-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Constraint-type contours for regular contour (not on a carpet) #2270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
3d17792
de15ad1
78c5964
8134a61
a69c3ad
59af6a2
2f5eae1
7c7a011
7e837ef
db70bca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/** | ||
* Copyright 2012-2018, Plotly, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
module.exports = { | ||
COMPARISON_OPS: ['=', '!=', '<', '>=', '>', '<='], | ||
COMPARISON_OPS2: ['=', '<', '>=', '>', '<='], | ||
INTERVAL_OPS: ['[]', '()', '[)', '(]', '][', ')(', '](', ')['], | ||
SET_OPS: ['{}', '}{'], | ||
CONSTRAINT_REDUCTION: { | ||
// for contour constraints, open/closed endpoints are equivalent | ||
'=': '=', | ||
|
||
'<': '<', | ||
'<=': '<', | ||
|
||
'>': '>', | ||
'>=': '>', | ||
|
||
'[]': '[]', | ||
'()': '[]', | ||
'[)': '[]', | ||
'(]': '[]', | ||
|
||
'][': '][', | ||
')(': '][', | ||
'](': '][', | ||
')[': '][' | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,10 @@ var dash = require('../../components/drawing/attributes').dash; | |
var fontAttrs = require('../../plots/font_attributes'); | ||
var extendFlat = require('../../lib/extend').extendFlat; | ||
|
||
var filterOps = require('../../constants/filter_ops'); | ||
var COMPARISON_OPS2 = filterOps.COMPARISON_OPS2; | ||
var INTERVAL_OPS = filterOps.INTERVAL_OPS; | ||
|
||
var scatterLineAttrs = scatterAttrs.line; | ||
|
||
module.exports = extendFlat({ | ||
|
@@ -34,6 +38,17 @@ module.exports = extendFlat({ | |
|
||
connectgaps: heatmapAttrs.connectgaps, | ||
|
||
fillcolor: { | ||
valType: 'color', | ||
role: 'style', | ||
editType: 'calc', | ||
description: [ | ||
'Sets the fill color if `contours.type` is *constraint*.', | ||
'Defaults to a half-transparent variant of the line color,', | ||
'marker color, or marker line color, whichever is available.' | ||
].join(' ') | ||
}, | ||
|
||
autocontour: { | ||
valType: 'boolean', | ||
dflt: true, | ||
|
@@ -67,6 +82,19 @@ module.exports = extendFlat({ | |
}, | ||
|
||
contours: { | ||
type: { | ||
valType: 'enumerated', | ||
values: ['levels', 'constraint'], | ||
dflt: 'levels', | ||
role: 'info', | ||
editType: 'calc', | ||
description: [ | ||
'If `levels`, the data is represented as a contour plot with multiple', | ||
'levels displayed. If `constraint`, the data is represented as constraints', | ||
'with the invalid region shaded as specified by the `operation` and', | ||
'`value` parameters.' | ||
].join(' ') | ||
}, | ||
start: { | ||
valType: 'number', | ||
dflt: null, | ||
|
@@ -155,6 +183,53 @@ module.exports = extendFlat({ | |
'https://github.com/d3/d3-format/blob/master/README.md#locale_format.' | ||
].join(' ') | ||
}, | ||
operation: { | ||
valType: 'enumerated', | ||
values: [].concat(COMPARISON_OPS2).concat(INTERVAL_OPS), | ||
role: 'info', | ||
dflt: '=', | ||
editType: 'calc', | ||
description: [ | ||
'Sets the filter operation.', | ||
|
||
'*=* keeps items equal to `value`', | ||
|
||
'*<* and *<=* keep items less than `value`', | ||
|
||
'*>* and *>=* keep items greater than `value`', | ||
|
||
'*[]*, *()*, *[)*, and *(]* keep items inside `value[0]` to `value[1]`', | ||
|
||
'*][*, *)(*, *](*, *)[* keep items outside `value[0]` to value[1]`', | ||
|
||
'Open vs. closed intervals make no difference to constraint display, but', | ||
'all versions are allowed for consistency with filter transforms.' | ||
].join(' ') | ||
}, | ||
value: { | ||
valType: 'any', | ||
dflt: 0, | ||
role: 'info', | ||
editType: 'calc', | ||
description: [ | ||
'Sets the value or values by which to filter by.', | ||
|
||
'Values are expected to be in the same type as the data linked', | ||
'to *target*.', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm. I think this was copied from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good catch - 7c7a011 |
||
|
||
'When `operation` is set to one of the comparison values', | ||
'(' + COMPARISON_OPS2 + ')', | ||
'*value* is expected to be a number or a string.', | ||
|
||
'When `operation` is set to one of the interval value', | ||
'(' + INTERVAL_OPS + ')', | ||
'*value* is expected to be 2-item array where the first item', | ||
'is the lower bound and the second item is the upper bound.', | ||
|
||
'Open vs. closed intervals make no difference to constraint display, but', | ||
'all versions are allowed for consistency with filter transforms.' | ||
].join(' ') | ||
}, | ||
editType: 'calc', | ||
impliedEdits: {'autocontour': false} | ||
}, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,94 +9,14 @@ | |
|
||
'use strict'; | ||
|
||
var Axes = require('../../plots/cartesian/axes'); | ||
var extendFlat = require('../../lib').extendFlat; | ||
var heatmapCalc = require('../heatmap/calc'); | ||
|
||
var setContours = require('./set_contours'); | ||
|
||
// most is the same as heatmap calc, then adjust it | ||
// though a few things inside heatmap calc still look for | ||
// contour maps, because the makeBoundArray calls are too entangled | ||
module.exports = function calc(gd, trace) { | ||
var cd = heatmapCalc(gd, trace), | ||
contours = trace.contours; | ||
|
||
// check if we need to auto-choose contour levels | ||
if(trace.autocontour !== false) { | ||
var dummyAx = autoContours(trace.zmin, trace.zmax, trace.ncontours); | ||
|
||
contours.size = dummyAx.dtick; | ||
|
||
contours.start = Axes.tickFirst(dummyAx); | ||
dummyAx.range.reverse(); | ||
contours.end = Axes.tickFirst(dummyAx); | ||
|
||
if(contours.start === trace.zmin) contours.start += contours.size; | ||
if(contours.end === trace.zmax) contours.end -= contours.size; | ||
|
||
// if you set a small ncontours, *and* the ends are exactly on zmin/zmax | ||
// there's an edge case where start > end now. Make sure there's at least | ||
// one meaningful contour, put it midway between the crossed values | ||
if(contours.start > contours.end) { | ||
contours.start = contours.end = (contours.start + contours.end) / 2; | ||
} | ||
|
||
// copy auto-contour info back to the source data. | ||
// previously we copied the whole contours object back, but that had | ||
// other info (coloring, showlines) that should be left to supplyDefaults | ||
if(!trace._input.contours) trace._input.contours = {}; | ||
extendFlat(trace._input.contours, { | ||
start: contours.start, | ||
end: contours.end, | ||
size: contours.size | ||
}); | ||
trace._input.autocontour = true; | ||
} | ||
else { | ||
// sanity checks on manually-supplied start/end/size | ||
var start = contours.start, | ||
end = contours.end, | ||
inputContours = trace._input.contours; | ||
|
||
if(start > end) { | ||
contours.start = inputContours.start = end; | ||
end = contours.end = inputContours.end = start; | ||
start = contours.start; | ||
} | ||
|
||
if(!(contours.size > 0)) { | ||
var sizeOut; | ||
if(start === end) sizeOut = 1; | ||
else sizeOut = autoContours(start, end, trace.ncontours).dtick; | ||
|
||
inputContours.size = contours.size = sizeOut; | ||
} | ||
} | ||
|
||
var cd = heatmapCalc(gd, trace); | ||
setContours(trace); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lovely ♻️ |
||
return cd; | ||
}; | ||
|
||
/* | ||
* autoContours: make a dummy axis object with dtick we can use | ||
* as contours.size, and if needed we can use Axes.tickFirst | ||
* with this axis object to calculate the start and end too | ||
* | ||
* start: the value to start the contours at | ||
* end: the value to end at (must be > start) | ||
* ncontours: max number of contours to make, like roughDTick | ||
* | ||
* returns: an axis object | ||
*/ | ||
function autoContours(start, end, ncontours) { | ||
var dummyAx = { | ||
type: 'linear', | ||
range: [start, end] | ||
}; | ||
|
||
Axes.autoTicks( | ||
dummyAx, | ||
(end - start) / (ncontours || 15) | ||
); | ||
|
||
return dummyAx; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,9 +11,11 @@ | |
module.exports = function(pathinfo, operation, perimeter, trace) { | ||
// Abandon all hope, ye who enter here. | ||
var i, v1, v2; | ||
var na = trace.a.length; | ||
var nb = trace.b.length; | ||
var z = trace.z; | ||
var pi0 = pathinfo[0]; | ||
var na = pi0.x.length; | ||
var nb = pi0.y.length; | ||
var z = pi0.z; | ||
var contours = trace.contours; | ||
|
||
var boundaryMax = -Infinity; | ||
var boundaryMin = Infinity; | ||
|
@@ -32,36 +34,31 @@ module.exports = function(pathinfo, operation, perimeter, trace) { | |
boundaryMax = Math.max(boundaryMax, z[nb - 1][i]); | ||
} | ||
|
||
pi0.prefixBoundary = false; | ||
|
||
switch(operation) { | ||
case '>': | ||
case '>=': | ||
if(trace.contours.value > boundaryMax) { | ||
pathinfo[0].prefixBoundary = true; | ||
if(contours.value > boundaryMax) { | ||
pi0.prefixBoundary = true; | ||
} | ||
break; | ||
case '<': | ||
case '<=': | ||
if(trace.contours.value < boundaryMin) { | ||
pathinfo[0].prefixBoundary = true; | ||
if(contours.value < boundaryMin) { | ||
pi0.prefixBoundary = true; | ||
} | ||
break; | ||
case '[]': | ||
case '()': | ||
v1 = Math.min.apply(null, trace.contours.value); | ||
v2 = Math.max.apply(null, trace.contours.value); | ||
if(v2 < boundaryMin) { | ||
pathinfo[0].prefixBoundary = true; | ||
} | ||
if(v1 > boundaryMax) { | ||
pathinfo[0].prefixBoundary = true; | ||
v1 = Math.min.apply(null, contours.value); | ||
v2 = Math.max.apply(null, contours.value); | ||
if(v2 < boundaryMin || v1 > boundaryMax) { | ||
pi0.prefixBoundary = true; | ||
} | ||
break; | ||
case '][': | ||
case ')(': | ||
v1 = Math.min.apply(null, trace.contours.value); | ||
v2 = Math.max.apply(null, trace.contours.value); | ||
v1 = Math.min.apply(null, contours.value); | ||
v2 = Math.max.apply(null, contours.value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if(v1 < boundaryMin && v2 > boundaryMax) { | ||
pathinfo[0].prefixBoundary = true; | ||
pi0.prefixBoundary = true; | ||
} | ||
break; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/** | ||
* Copyright 2012-2018, Plotly, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
|
||
'use strict'; | ||
|
||
var handleConstraintValueDefaults = require('./constraint_value_defaults'); | ||
var handleLabelDefaults = require('./label_defaults'); | ||
var Color = require('../../components/color'); | ||
var addOpacity = Color.addOpacity; | ||
var opacity = Color.opacity; | ||
var CONSTRAINT_REDUCTION = require('../../constants/filter_ops').CONSTRAINT_REDUCTION; | ||
|
||
module.exports = function handleConstraintDefaults(traceIn, traceOut, coerce, layout, defaultColor, opts) { | ||
var contours = traceOut.contours; | ||
var showLines, lineColor, fillColor; | ||
|
||
var operation = coerce('contours.operation'); | ||
contours._operation = CONSTRAINT_REDUCTION[operation]; | ||
|
||
handleConstraintValueDefaults(coerce, contours); | ||
|
||
if(operation === '=') { | ||
showLines = contours.showlines = true; | ||
} | ||
else { | ||
showLines = coerce('contours.showlines'); | ||
fillColor = coerce('fillcolor', addOpacity( | ||
(traceIn.line || {}).color || defaultColor, 0.5 | ||
)); | ||
} | ||
|
||
if(showLines) { | ||
var lineDfltColor = fillColor && opacity(fillColor) ? | ||
addOpacity(traceOut.fillcolor, 1) : | ||
defaultColor; | ||
lineColor = coerce('line.color', lineDfltColor); | ||
coerce('line.width', 2); | ||
coerce('line.dash'); | ||
} | ||
|
||
coerce('line.smoothing'); | ||
|
||
handleLabelDefaults(coerce, layout, lineColor, opts); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❤️