diff --git a/src/App.re b/src/App.re index 058f25fa..e2526c25 100644 --- a/src/App.re +++ b/src/App.re @@ -78,7 +78,7 @@ module Menu = { {"Dist Builder" |> E.ste} - + {"Dist Builder 2" |> E.ste} ; diff --git a/src/components/DistBuilder2.re b/src/components/DistBuilder2.re index b077f57b..767c13af 100644 --- a/src/components/DistBuilder2.re +++ b/src/components/DistBuilder2.re @@ -38,7 +38,7 @@ module DemoDist = { let continuous: DistTypes.xyShape = {xs, ys}; E.ste}>
- + ; }; }; diff --git a/src/components/charts/DistPlusPlot.re b/src/components/charts/DistPlusPlot.re index 9ea8d889..1c957dda 100644 --- a/src/components/charts/DistPlusPlot.re +++ b/src/components/charts/DistPlusPlot.re @@ -208,4 +208,4 @@ let make = (~distPlus: DistTypes.distPlus) => { );
chart chart2 {table(distPlus, x)}
; // chart -}; \ No newline at end of file +}; diff --git a/src/components/charts/DistributionPlot/distPlotD3.js b/src/components/charts/DistributionPlot/distPlotD3.js index 47f39133..73f0caa1 100644 --- a/src/components/charts/DistributionPlot/distPlotD3.js +++ b/src/components/charts/DistributionPlot/distPlotD3.js @@ -12,15 +12,14 @@ export class CdfChartD3 { this.attrs = { svgWidth: 400, svgHeight: 400, - marginTop: 5, marginBottom: 5, marginRight: 50, marginLeft: 5, container: null, - minX: false, - maxX: false, + minX: null, + maxX: null, scale: 'linear', timeScale: null, showDistributionLines: true, @@ -54,11 +53,20 @@ export class CdfChartD3 { this.formatDates = this.formatDates.bind(this); } + /** + * @param {string} name + * @param value + * @returns {CdfChartD3} + */ set(name, value) { _.set(this.attrs, [name], value); return this; } + /** + * @param data + * @returns {CdfChartD3} + */ data(data) { this.attrs.data = data; this.attrs.data.continuous = data.continuous || { @@ -78,6 +86,42 @@ export class CdfChartD3 { console.error('Container for D3 is not defined.'); return; } + + if (!['log', 'linear'].includes(this.attrs.scale)) { + console.error('Scale should be either "log" or "linear".'); + return; + } + + // Log Scale. + if (this.attrs.scale === 'log') { + this.logFilter('continuous'); + this.logFilter('discrete'); + } + if ( + this.attrs.scale === 'log' + && this.attrs.minX !== null + && this.attrs.minX < 0 + ) { + console.warn('minX should be positive.'); + this.attrs.minX = undefined; + } + + // Fields. + const fields = [ + 'marginLeft', 'marginRight', + 'marginTop', 'marginBottom', + 'svgWidth', 'svgHeight', + 'yMaxContinuousDomainFactor', + 'yMaxDiscreteDomainFactor', + 'logBase', + ]; + for (const field of fields) { + if (!_.isNumber(this.attrs[field])) { + console.error(`${field} should be a number.`); + return; + } + } + // Sets the width from the DOM element. const containerRect = this._container.node().getBoundingClientRect(); if (containerRect.width > 0) { @@ -134,6 +178,12 @@ export class CdfChartD3 { const yMin = d3.min(this.attrs.data.continuous.ys); const yMax = d3.max(this.attrs.data.continuous.ys); + // Errors. + if (!_.isFinite(xMin)) return console.error('xMin is undefined'); + if (!_.isFinite(xMax)) return console.error('xMax is undefined'); + if (!_.isFinite(yMin)) return console.error('yMin is undefined'); + if (!_.isFinite(yMax)) return console.error('yMax is undefined'); + // X-domains. const yMaxDomainFactor = _.get(this.attrs, 'yMaxContinuousDomainFactor', 1); const xMinDomain = xMin; @@ -142,7 +192,7 @@ export class CdfChartD3 { const yMaxDomain = yMax * yMaxDomainFactor; // X-scale. - let xScale = this.attrs.scale === 'linear' + const xScale = this.attrs.scale === 'linear' ? d3.scaleLinear() .domain([xMinDomain, xMaxDomain]) .range([0, this.calc.chartWidth]) @@ -210,16 +260,6 @@ export class CdfChartD3 { // Y-axis. const yAxis = d3.axisRight(yScale); - // Objects. - const line = d3.line() - .x(d => xScale(d.x)) - .y(d => yScale(d.y)); - - const area = d3.area() - .x(d => xScale(d.x)) - .y1(d => yScale(d.y)) - .y0(this.calc.chartHeight); - // Add axis. this.chart .createObject({ tag: 'g', selector: 'x-axis' }) @@ -233,6 +273,11 @@ export class CdfChartD3 { } // Draw area. + const area = d3.area() + .x(d => xScale(d.x)) + .y1(d => yScale(d.y)) + .y0(this.calc.chartHeight); + this.chart .createObjectsWithData({ tag: 'path', @@ -245,6 +290,10 @@ export class CdfChartD3 { // Draw line. if (this.attrs.showDistributionLines) { + const line = d3.line() + .x(d => xScale(d.x)) + .y(d => yScale(d.y)); + this.chart .createObjectsWithData({ tag: 'path', @@ -454,7 +503,7 @@ export class CdfChartD3 { } /** - * @param {name} key + * @param {string} key * @returns {{x: number[], y: number[]}} */ getDataPoints(key) { @@ -473,6 +522,29 @@ export class CdfChartD3 { return dt; } + /** + * @param {string} key + * @returns {{x: number[], y: number[]}} + */ + logFilter(key) { + const xs = []; + const ys = []; + const emptyShape = { xs: [], ys: [] }; + const data = _.get(this.attrs.data, key, emptyShape); + + for (let i = 0, len = data.xs.length; i < len; i++) { + const x = data.xs[i]; + const y = data.ys[i]; + if (x > 0) { + xs.push(x); + ys.push(y); + } + } + + _.set(this.attrs.data, [key, 'xs'], xs); + _.set(this.attrs.data, [key, 'ys'], ys); + } + /** * @param {string} key * @returns {boolean} diff --git a/src/components/charts/DistributionPlot/distPlotReact.js b/src/components/charts/DistributionPlot/distPlotReact.js index 9bd9deee..1220b8fe 100644 --- a/src/components/charts/DistributionPlot/distPlotReact.js +++ b/src/components/charts/DistributionPlot/distPlotReact.js @@ -46,13 +46,13 @@ function CdfChartReact(props) { .set('marginTop', 5) .set('showDistributionLines', props.showDistributionLines) .set('showDistributionYAxis', props.showDistributionYAxis) - .set('verticalLine', props.verticalLine) + .set('verticalLine', props.verticalLine || 110) .set('showVerticalLine', props.showVerticalLine) .set('container', containerRef.current) .set('scale', scale) .set('timeScale', props.timeScale) - .set('yMaxContinuousDomainFactor', props.yMaxContinuousDomainFactor) - .set('yMaxDiscreteDomainFactor', props.yMaxDiscreteDomainFactor) + .set('yMaxContinuousDomainFactor', props.yMaxContinuousDomainFactor || 1) + .set('yMaxDiscreteDomainFactor', props.yMaxDiscreteDomainFactor || 1) .data({ continuous: props.continuous, discrete: props.discrete,