diff --git a/polydodo/src/d3/evolving_chart/evolving_chart.js b/polydodo/src/d3/evolving_chart/evolving_chart.js index 95216a6a..ac5822ce 100644 --- a/polydodo/src/d3/evolving_chart/evolving_chart.js +++ b/polydodo/src/d3/evolving_chart/evolving_chart.js @@ -48,7 +48,7 @@ const createDrawingGroup = (svg) => svg.append('g').attr('transform', `translate const bindAnnotationsToRects = (g, annotations) => g.selectAll('.rect').data(annotations).enter().append('rect').attr('class', 'rect-stacked'); const createEvolvingChart = (containerNode, data) => { - const svg = d3.select(containerNode).attr('width', CANVAS_DIMENSION.WIDTH).attr('height', CANVAS_DIMENSION.HEIGHT); + const svg = d3.select(containerNode).attr('viewBox', `0, 0, ${CANVAS_DIMENSION.WIDTH}, ${CANVAS_DIMENSION.HEIGHT}`); const { xTime, xLinear, y, colors } = initializeScales(); const { xTimeAxis, xLinearAxis, yAxis } = initializeAxes(xTime, xLinear, y); const g = createDrawingGroup(svg); diff --git a/polydodo/src/d3/hypnogram/hypnogram.js b/polydodo/src/d3/hypnogram/hypnogram.js index ffd3685a..8262f863 100644 --- a/polydodo/src/d3/hypnogram/hypnogram.js +++ b/polydodo/src/d3/hypnogram/hypnogram.js @@ -40,7 +40,7 @@ const setDomainOnScales = (x, y, colors, data) => { }; const createHypnogram = (containerNode, data, chartTitle, hypnogramNames, comparativeColors) => { - const svg = d3.select(containerNode).attr('width', CANVAS_DIMENSION.WIDTH).attr('height', CANVAS_DIMENSION.HEIGHT); + const svg = d3.select(containerNode).attr('viewBox', `0, 0, ${CANVAS_DIMENSION.WIDTH}, ${CANVAS_DIMENSION.HEIGHT}`); const { x, y, colors } = initializeScales(comparativeColors); const { xAxis, yAxis } = initializeAxes(x, y); const g = createDrawingGroup(svg); diff --git a/polydodo/src/d3/spectrogram/constants.js b/polydodo/src/d3/spectrogram/constants.js index 50b919d5..5ae945a1 100644 --- a/polydodo/src/d3/spectrogram/constants.js +++ b/polydodo/src/d3/spectrogram/constants.js @@ -1,32 +1,13 @@ -import _ from 'lodash'; - -const PADDING = 100; +export const PADDING = 100; export const NB_SPECTROGRAM = 2; export const FREQUENCY_KEY = 'frequencies'; export const NB_POINTS_COLOR_INTERPOLATION = 3; export const TITLE_FONT_SIZE = '18px'; - +export const CANVAS_WIDTH_TO_HEIGHT_RATIO = 700 / 1000; // width to height ratio +export const CANVAS_HEIGHT_WINDOW_FACTOR = 0.8; export const MARGIN = { TOP: 50, RIGHT: 120, BOTTOM: 50, LEFT: 70, }; -export const CANVAS_DIMENSION = { - WIDTH: 1000, - HEIGHT: 700, -}; -export const DIMENSION = { - WIDTH: CANVAS_DIMENSION.WIDTH - MARGIN.LEFT - MARGIN.RIGHT, - HEIGHT: CANVAS_DIMENSION.HEIGHT - MARGIN.BOTTOM - MARGIN.TOP, -}; -export const SPECTROGRAM_CANVAS_HEIGTH = _.range(NB_SPECTROGRAM).map((x) => { - let height = DIMENSION.HEIGHT / NB_SPECTROGRAM; - if (x === 0) { - height += MARGIN.TOP; - } else if (x === NB_SPECTROGRAM - 1) { - height += MARGIN.BOTTOM; - } - return height; -}); -export const SPECTROGRAM_HEIGHT = (CANVAS_DIMENSION.HEIGHT - MARGIN.BOTTOM - MARGIN.TOP - PADDING) / NB_SPECTROGRAM; diff --git a/polydodo/src/d3/spectrogram/legend.js b/polydodo/src/d3/spectrogram/legend.js index 41d0136e..f8a7ad5c 100644 --- a/polydodo/src/d3/spectrogram/legend.js +++ b/polydodo/src/d3/spectrogram/legend.js @@ -1,8 +1,8 @@ import * as d3 from 'd3'; import _ from 'lodash'; -import { MARGIN, SPECTROGRAM_HEIGHT, NB_POINTS_COLOR_INTERPOLATION, TITLE_FONT_SIZE } from './constants'; +import { MARGIN, NB_POINTS_COLOR_INTERPOLATION, TITLE_FONT_SIZE } from './constants'; -export const createLegend = (svg, color, y) => { +export const createLegend = (svg, color, y, spectrogramHeight) => { const interpolate = d3.interpolate(color.domain()[0], color.domain()[1]); const colors = _.map(_.range(NB_POINTS_COLOR_INTERPOLATION + 1), (x) => color(interpolate(x / NB_POINTS_COLOR_INTERPOLATION))); @@ -23,14 +23,13 @@ export const createLegend = (svg, color, y) => { .append('stop') .attr('stop-color', (d) => d) .attr('offset', (_, i) => i / (colors.length - 1)); - svg .append('rect') .attr('fill', `url(#${GRADIENT_ID})`) .attr('x', MARGIN.RIGHT / 10) .attr('y', 0) .attr('width', MARGIN.RIGHT / 6) - .attr('height', SPECTROGRAM_HEIGHT); + .attr('height', spectrogramHeight); const yAxis = d3.axisRight(y).ticks(5, 's'); svg @@ -45,7 +44,7 @@ export const createLegend = (svg, color, y) => { .attr('class', 'y axis') .attr('transform', 'rotate(90)') .attr('y', -MARGIN.RIGHT) - .attr('x', SPECTROGRAM_HEIGHT / 2) + .attr('x', spectrogramHeight / 2) .attr('dy', '1em') .attr('fill', 'currentColor') .style('text-anchor', 'middle') diff --git a/polydodo/src/d3/spectrogram/spectrogram.js b/polydodo/src/d3/spectrogram/spectrogram.js index 1a639ee8..fef0b5b3 100644 --- a/polydodo/src/d3/spectrogram/spectrogram.js +++ b/polydodo/src/d3/spectrogram/spectrogram.js @@ -1,16 +1,24 @@ import * as d3 from 'd3'; import _ from 'lodash'; -import { DIMENSION, MARGIN, CANVAS_DIMENSION, SPECTROGRAM_CANVAS_HEIGTH, SPECTROGRAM_HEIGHT, FREQUENCY_KEY, TITLE_FONT_SIZE } from './constants'; +import { + MARGIN, + CANVAS_WIDTH_TO_HEIGHT_RATIO, + FREQUENCY_KEY, + TITLE_FONT_SIZE, + NB_SPECTROGRAM, + PADDING, + CANVAS_HEIGHT_WINDOW_FACTOR, +} from './constants'; import { EPOCH_DURATION_SEC } from '../constants'; import { createLegend } from './legend'; -const initializeScales = () => +const initializeScales = ({ spectrogramWidth, singleSpectrogramHeight }) => Object({ - x: d3.scaleLinear([0, DIMENSION.WIDTH]), - yLinear: d3.scaleLinear([SPECTROGRAM_HEIGHT, 0]), - yBand: d3.scaleBand([SPECTROGRAM_HEIGHT, 0]), - yColor: d3.scaleLinear([SPECTROGRAM_HEIGHT, 0]), + x: d3.scaleLinear([0, spectrogramWidth]), + yLinear: d3.scaleLinear([singleSpectrogramHeight, 0]), + yBand: d3.scaleBand([singleSpectrogramHeight, 0]), + yColor: d3.scaleLinear([singleSpectrogramHeight, 0]), color: d3.scaleSequential().interpolator(d3.interpolatePlasma), }); @@ -39,18 +47,16 @@ const preprocessData = (powerAmplitudesByTimestamp, frequencies) => ), ); -const getHoursFromIndex = (idx) => { - return (idx * EPOCH_DURATION_SEC) / 60.0 / 60; -}; +const getHoursFromIndex = (idx) => (idx * EPOCH_DURATION_SEC) / 3600; -const createDrawingGroups = (g) => +const createDrawingGroups = (g, spectrogramWidth) => Object({ spectrogramDrawingGroup: g.append('g').attr('transform', `translate(${MARGIN.LEFT}, ${MARGIN.TOP})`), - legendDrawingGroup: g.append('g').attr('transform', `translate(${MARGIN.LEFT + DIMENSION.WIDTH}, ${MARGIN.TOP})`), + legendDrawingGroup: g.append('g').attr('transform', `translate(${MARGIN.LEFT + spectrogramWidth}, ${MARGIN.TOP})`), }); -const getScalesAndAxes = (data, channel) => { - const { x, yLinear, yBand, yColor, color } = initializeScales(); +const getScalesAndAxes = (data, channel, dimensions) => { + const { x, yLinear, yBand, yColor, color } = initializeScales(dimensions); const { xAxis, yAxis } = initializeAxes(x, yLinear); const preprocessedData = preprocessData(data[channel], data.frequencies); @@ -59,11 +65,11 @@ const getScalesAndAxes = (data, channel) => { return { data: preprocessedData, x, yBand, yColor, color, xAxis, yAxis }; }; -const createAxes = (g, xAxis, yAxis) => { +const createAxes = (g, xAxis, yAxis, singleSpectrogramHeight, spectrogramWidth) => { g.append('text') .attr('class', 'x axis') - .attr('y', SPECTROGRAM_HEIGHT + MARGIN.BOTTOM) - .attr('x', DIMENSION.WIDTH / 2) + .attr('y', singleSpectrogramHeight + MARGIN.BOTTOM) + .attr('x', spectrogramWidth / 2) .attr('fill', 'currentColor') .style('text-anchor', 'middle') .text('Time'); @@ -72,7 +78,7 @@ const createAxes = (g, xAxis, yAxis) => { .attr('class', 'y axis') .attr('transform', 'rotate(-90)') .attr('y', -MARGIN.LEFT) - .attr('x', -SPECTROGRAM_HEIGHT / 2) + .attr('x', -singleSpectrogramHeight / 2) .attr('dy', '1em') .attr('fill', 'currentColor') .style('text-anchor', 'middle') @@ -80,7 +86,7 @@ const createAxes = (g, xAxis, yAxis) => { g.append('g') .attr('class', 'x axis') - .attr('transform', `translate(0, ${SPECTROGRAM_HEIGHT})`) + .attr('transform', `translate(0, ${singleSpectrogramHeight})`) .call(xAxis) .selectAll('text') .style('font-size', TITLE_FONT_SIZE); @@ -88,21 +94,21 @@ const createAxes = (g, xAxis, yAxis) => { g.append('g').attr('class', 'y axis').call(yAxis).selectAll('text').style('font-size', TITLE_FONT_SIZE); }; -const createTitle = (g, channelName) => +const createTitle = (g, channelName, spectrogramWidth) => g .append('text') - .attr('x', DIMENSION.WIDTH / 2) + .attr('x', spectrogramWidth / 2) .attr('y', -MARGIN.TOP / 3) .style('text-anchor', 'middle') .style('font-size', TITLE_FONT_SIZE) .text(`Spectrogram of channel ${channelName}`); -const createSpectrogramRectangles = (canvas, scalesAndAxesBySpectrogram) => { +const createSpectrogramRectangles = (canvas, scalesAndAxesBySpectrogram, { singleSpectrogramCanvasHeight }) => { const context = canvas.node().getContext('2d'); _.each(scalesAndAxesBySpectrogram, ({ x, yBand, color, data }, index) => { context.resetTransform(); - context.translate(MARGIN.LEFT, MARGIN.TOP + index * SPECTROGRAM_CANVAS_HEIGTH[index]); + context.translate(MARGIN.LEFT, MARGIN.TOP + index * singleSpectrogramCanvasHeight[index]); _.each(data, ({ Timestamp, Frequency, Intensity }) => { context.beginPath(); @@ -114,19 +120,35 @@ const createSpectrogramRectangles = (canvas, scalesAndAxesBySpectrogram) => { }); }; -const createSpectrogramAxesAndLegend = (svg, scalesAndAxesBySpectrogram, channelNames) => +const createSpectrogramAxesAndLegend = ( + svg, + scalesAndAxesBySpectrogram, + channelNames, + { canvasWidth, spectrogramWidth, singleSpectrogramCanvasHeight, singleSpectrogramHeight }, +) => _.forEach(_.zip(scalesAndAxesBySpectrogram, channelNames), ([{ xAxis, yAxis, color, yColor }, channel], index) => { const currentSpectrogramDrawingGroup = svg .append('g') - .attr('transform', `translate(0, ${index * SPECTROGRAM_CANVAS_HEIGTH[index]})`) - .attr('width', CANVAS_DIMENSION.WIDTH) - .attr('height', SPECTROGRAM_CANVAS_HEIGTH[index]); + .attr('transform', `translate(0, ${index * singleSpectrogramCanvasHeight[index]})`) + .attr('width', canvasWidth) + .attr('height', singleSpectrogramCanvasHeight[index]); + + const { spectrogramDrawingGroup, legendDrawingGroup } = createDrawingGroups(currentSpectrogramDrawingGroup, spectrogramWidth); - const { spectrogramDrawingGroup, legendDrawingGroup } = createDrawingGroups(currentSpectrogramDrawingGroup); + createTitle(spectrogramDrawingGroup, channel, spectrogramWidth); + createAxes(spectrogramDrawingGroup, xAxis, yAxis, singleSpectrogramHeight, spectrogramWidth); + createLegend(legendDrawingGroup, color, yColor, singleSpectrogramHeight); + }); - createTitle(spectrogramDrawingGroup, channel); - createAxes(spectrogramDrawingGroup, xAxis, yAxis); - createLegend(legendDrawingGroup, color, yColor); +const getSpectrogramCanvasHeight = (spectrogramHeight) => + _.range(NB_SPECTROGRAM).map((x) => { + let height = spectrogramHeight / NB_SPECTROGRAM; + if (x === 0) { + height += MARGIN.TOP; + } else if (x === NB_SPECTROGRAM - 1) { + height += MARGIN.BOTTOM; + } + return height; }); const createSpectrogram = (containerNode, data) => { @@ -140,18 +162,29 @@ const createSpectrogram = (containerNode, data) => { setting the first element's position, in this case the canvas, to absolute. */ const parentDiv = d3.select(containerNode); + const canvasWidth = parentDiv.node().getBoundingClientRect().width; + const canvasHeight = Math.min(canvasWidth * CANVAS_WIDTH_TO_HEIGHT_RATIO, window.innerHeight * CANVAS_HEIGHT_WINDOW_FACTOR); + const dimensions = { + canvasWidth: canvasWidth, + canvasHeight: canvasHeight, + spectrogramWidth: canvasWidth - MARGIN.LEFT - MARGIN.RIGHT, + spectrogramsHeight: canvasHeight - MARGIN.TOP - MARGIN.BOTTOM, + singleSpectrogramCanvasHeight: getSpectrogramCanvasHeight(canvasHeight - MARGIN.TOP - MARGIN.BOTTOM), + singleSpectrogramHeight: (canvasHeight - MARGIN.BOTTOM - MARGIN.TOP - PADDING) / NB_SPECTROGRAM, + }; + const channelNames = _.filter(_.keys(data), (keyName) => keyName !== FREQUENCY_KEY); - const scalesAndAxesBySpectrogram = _.map(channelNames, (name) => getScalesAndAxes(data, name)); + const scalesAndAxesBySpectrogram = _.map(channelNames, (name) => getScalesAndAxes(data, name, dimensions)); const canvas = parentDiv .append('canvas') - .attr('width', CANVAS_DIMENSION.WIDTH) - .attr('height', CANVAS_DIMENSION.HEIGHT) + .attr('width', dimensions.canvasWidth) + .attr('height', dimensions.canvasHeight) .style('position', 'absolute'); - const svg = parentDiv.append('svg').attr('width', CANVAS_DIMENSION.WIDTH).attr('height', CANVAS_DIMENSION.HEIGHT); + const svg = parentDiv.append('svg').attr('width', dimensions.canvasWidth).attr('height', dimensions.canvasHeight); - createSpectrogramRectangles(canvas, scalesAndAxesBySpectrogram); - createSpectrogramAxesAndLegend(svg, scalesAndAxesBySpectrogram, channelNames); + createSpectrogramRectangles(canvas, scalesAndAxesBySpectrogram, dimensions); + createSpectrogramAxesAndLegend(svg, scalesAndAxesBySpectrogram, channelNames, dimensions); }; export default createSpectrogram; diff --git a/polydodo/src/views/sleep_analysis/sleep_analysis.js b/polydodo/src/views/sleep_analysis/sleep_analysis.js index cc036c4b..cf37cffc 100644 --- a/polydodo/src/views/sleep_analysis/sleep_analysis.js +++ b/polydodo/src/views/sleep_analysis/sleep_analysis.js @@ -53,6 +53,7 @@ const SleepAnalysis = () => {
  • Routine: going to sleep about at the same time, in a darkened and quiet environment.
  • Routine: going to sleep about at the same time, in a darkened and quiet environment.
  • +
  • Routine: going to sleep about at the same time, in a darkened and quiet environment.
  • Although we’ve looked at many aspects of your night’s sleep, we haven’t properly looked at your sleep dynamics, whereas how your sleep diff --git a/polydodo/yarn.lock b/polydodo/yarn.lock index 31a33821..c87a0aab 100644 --- a/polydodo/yarn.lock +++ b/polydodo/yarn.lock @@ -7960,10 +7960,10 @@ mixin-object@^2.0.1: dependencies: minimist "^1.2.5" -moment@2.24.0: - version "2.24.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" - integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== +moment@^2.27.0: + version "2.27.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d" + integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ== moment@^2.27.0: version "2.27.0"