Skip to content

Commit

Permalink
Update axis ticks and scale when paths are loaded.
Browse files Browse the repository at this point in the history
This addresses an issue of paths loading and one or more of the axes showing only tick 0, with all the paths connecting to it.  This was working but a recent commit disconnected the update, as has happened several times before.
Use CP dependencies to update the axis ticks and scale.

draw-map.js : add to axisApi : cmNameAdd, makeMapChrName, axisIDAdd.

axis-1d.js : added CPs : dataBlocksDomains, referenceBlock, blocksDomains, blocksDomain, blocksDomainEffect, domainEffect.

models/block.js : add CP : featuresDomain.
feature.js : add CP : valueOrdered.
data/block.js : use cmNameAdd().  (viewed() may not be updating - trialling an added dependency and changed use of blockValues()).

draw/axes-1d.hbs and axis-1d.hbs : commented-out display of values which may be changed during the render (refn emberjs/ember.js#13948) :  axis.view, currentPosition.yDomain.  Use blocksDomainEffect.

utility-chromosome.js : factor copyChrData() out of chrData(); add cmNameAdd() based on draw-map.js:receiveChr().

frontend/app/ :
 6e37a2f 223853 Sep 10 20:25  components/draw-map.js
 62cdfc0  22369 Sep 10 21:12  components/draw/axis-1d.js
 2b94ee2  24542 Sep 10 20:25  components/draw/block-adj.js
 563096e   2943 Sep 10 16:51  mixins/axis-position.js
 06c02ae   6638 Sep 10 18:30  models/block.js
 930fc67   1021 Sep  9 16:59  models/feature.js
 4e7d202  14286 Sep 10 15:14  services/data/block.js
 cf7716e    356 Sep 10 17:24  templates/components/draw/axes-1d.hbs
 ed752af    578 Sep 10 18:42  templates/components/draw/axis-1d.hbs
 8c18360   3946 Sep 10 15:36  utils/utility-chromosome.js

added :
 998f0c9   1623 Sep 10 18:30  utils/interval-calcs.js
  • Loading branch information
Don-Isdale committed Sep 10, 2019
1 parent 2bf1414 commit 52ff438
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 26 deletions.
24 changes: 20 additions & 4 deletions frontend/app/components/draw-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ scheduleIntoAnimationFrame = scheduleFrame.default;

import config from '../config/environment';
import { EventedListener } from '../utils/eventedListener';
import { chrData } from '../utils/utility-chromosome';
import { chrData, cmNameAdd } from '../utils/utility-chromosome';
import { eltWidthResizable, eltResizeToAvailableWidth, noShiftKeyfilter, eltClassName, tabActive, inputRangeValue, expRange } from '../utils/domElements';
import { /*fromSelectionArray,*/ logSelectionLevel, logSelection, logSelectionNodes, selectImmediateChildNodes } from '../utils/log-selection';
import { parseOptions } from '../utils/common/strings';
Expand Down Expand Up @@ -484,7 +484,10 @@ export default Ember.Component.extend(Ember.Evented, {
axisName2MapChr,
axisStackChanged,
axisScaleChanged,
axisRange2Domain
axisRange2Domain,
cmNameAdd,
makeMapChrName,
axisIDAdd
};
console.log('draw-map stacks', stacks);
this.set('stacks', stacks);
Expand Down Expand Up @@ -1176,8 +1179,21 @@ export default Ember.Component.extend(Ember.Evented, {
sBlock = oa.stacks.blocks[d],
addedBlock = ! sBlock;
if (! sBlock) {
oa.stacks.blocks[d] = sBlock = new Block(dBlock);
dBlock.set('view', sBlock);
/** sBlock may already be associated with dBlock */
let view = dBlock.get('view');
sBlock = view || new Block(dBlock);
oa.stacks.blocks[d] = sBlock;
if (! view) {
/* this .set() was getting assertion fail (https://github.com/emberjs/ember.js/issues/13948),
* hence the catch and trace; this has been resolved by not displaying .view in .hbs
*/
try {
dBlock.set('view', sBlock);
}
catch (exc) {
console.log('ensureAxis', d, dBlock, sBlock, addedBlock, view, oa.stacks.blocks, exc.stack || exc);
}
}
}
let s = Stacked.getStack(d);
if (trace_stack > 1)
Expand Down
97 changes: 87 additions & 10 deletions frontend/app/components/draw/axis-1d.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import { selectAxis } from '../../utils/draw/stacksAxes';
import { breakPoint } from '../../utils/breakPoint';
import { configureHorizTickHover } from '../../utils/hover';
import { getAttrOrCP } from '../../utils/ember-devel';
import { intervalExtent } from '../../utils/interval-calcs';
import { updateDomain } from '../../utils/stacksLayout';


/* global d3 */
/* global require */
Expand Down Expand Up @@ -310,6 +313,75 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, AxisPosition, {
}
return dataBlocks;
}),
/** @return the domains of the data blocks of this axis.
* The result does not contain a domain for data blocks with no features loaded.
*/
dataBlocksDomains : Ember.computed('dataBlocks.@each.featuresDomain', function () {
let dataBlocks = this.get('dataBlocks'),
dataBlockDomains = dataBlocks.map(function (b) { return b.get('featuresDomain'); } )
/* featuresDomain() will return undefined when block has no features loaded. */
.filter(d => d !== undefined);
return dataBlockDomains;
}),
referenceBlock : Ember.computed.alias('axisS.referenceBlock'),
/** @return the domains of all the blocks of this axis, including the reference block if any.
* @description related @see axesDomains() (draw/block-adj)
*/
blocksDomains : Ember.computed('dataBlocksDomains.[]', 'referenceBlock.range', function () {
let
/* alternative :
* dataBlocksMap = this.get('blockService.dataBlocks'),
* axisId = this.get('axis.id'),
* datablocks = dataBlocksMap.get(axisId),
*/
/** see also domainCalc(), blocksUpdateDomain() */
blocksDomains = this.get('dataBlocksDomains'),
/** equivalent : Stacked:referenceDomain() */
referenceRange = this.get('referenceBlock.range');
if (referenceRange) {
console.log('referenceRange', referenceRange, blocksDomains);
blocksDomains.push(referenceRange);
}
return blocksDomains;
}),
/** @return the union of blocksDomains[], i.e. the interval which contains all
* the blocksDomains intervals.
*/
blocksDomain : Ember.computed('blocksDomains.[]', function () {
let
blocksDomains = this.get('blocksDomains'),
domain = intervalExtent(blocksDomains);
console.log('blocksDomain', blocksDomains, domain);
return domain;
}),
blocksDomainEffect : Ember.computed('blocksDomain', function () {
let domain = this.get('blocksDomain'),
/** if domain is [0,0] or [false, false] then consider that undefined. */
domainDefined = domain && domain.length && (domain[0] || domain[1]);
if (domainDefined && ! this.get('zoomed'))
/* defer setting yDomain to the end of this render, to avoid assert fail
* re. change of domainChanged, refn issues/13948;
* that also breaks progressive loading and axis & path updates from zoom.
*/
Ember.run.later(() => {
this.setDomain(domain);
});
}),
/** same as domainChanged, not used. */
domainEffect : Ember.computed('domain', function () {
let domain = this.get('domain');
if (domain) {
/* Similar to this.updateDomain(), defined in axis-position.js, */
let axisS = this.get('axisS');
console.log('domainEffect', domain, axisS);
if (axisS) {
let y = axisS.getY(), ys = axisS.ys;
updateDomain(axisS.y, axisS.ys, axisS);
}
}
return domain;
}),

/** count of features of .dataBlocks */
featureLength : Ember.computed('dataBlocks.@each.featuresLength', function () {
let dataBlocks = this.get('dataBlocks'),
Expand All @@ -324,6 +396,7 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, AxisPosition, {
*/
featureLengthEffect : Ember.computed('featureLength', 'axisS', function () {
let featureLength = this.get('featureLength');

this.renderTicksDebounce();
let axisApi = stacks.oa.axisApi,
/** defined after first brushHelper() call. */
Expand Down Expand Up @@ -394,20 +467,24 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, AxisPosition, {
/** position as of the last zoom. */
domain : Ember.computed.alias('currentPosition.yDomain'),

/** this is an alias of .domain, but it updates when the array elements update. */
/** Updates when the array elements of .domain[] update.
* @return undefined; value is unused.
*/
domainChanged : Ember.computed(
'domain.0', 'domain.1',
function () {
let domain = this.get('domain');
// use the VLinePosition:toString() for the position-s
console.log('domainChanged', domain, this.get('axisS'), ''+this.get('currentPosition'), ''+this.get('lastDrawn'));
// this.notifyChanges();
if (! this.get('axisS'))
console.log('domainChanged() no axisS yet', domain, this.get('axis.id'));
else
this.updateAxis();

return domain;
// domain is initially undefined
if (domain) {
// use the VLinePosition:toString() for the position-s
console.log('domainChanged', domain, this.get('axisS'), ''+this.get('currentPosition'), ''+this.get('lastDrawn'));
// this.notifyChanges();
if (! this.get('axisS'))
console.log('domainChanged() no axisS yet', domain, this.get('axis.id'));
else
this.updateAxis();
}
return undefined;
}),
notifyChanges() {
let axisID = this.get('axis.id');
Expand Down
22 changes: 22 additions & 0 deletions frontend/app/components/draw/block-adj.js
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,20 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, {
.attr("d", function(d) { return d.pathU() /*get('pathU')*/; });
},

/** Call updateAxis() for the axes which bound this block-adj.
* See comment in updatePathsPositionDebounce().
*/
updateAxesScale() {
let
axes = this.get('axes'),
/** reference blocks */
axesBlocks = axes.mapBy('blocks');
console.log('updateAxesScale', axesBlocks.map((blocks) => blocks.mapBy('axisName')));
axesBlocks.forEach(function (blocks) {
blocks[0].axis.axis1d.updateAxis();
});
},

/*--------------------------------------------------------------------------*/

axesDomains : Ember.computed.alias('blockAdj.axesDomains'),
Expand All @@ -419,6 +433,14 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, {
domainsChanged = this.get('axesDomains');
console.log('updatePathsPositionDebounce', this.get('blockAdjId'), heightChanged, count, domainsChanged);
this.updatePathsPosition();

/* this update is an alternative trigger for updating the axes ticks and
* scale when their domains change, e.g. when loaded features extend a
* block's domain. The solution used instead is the ComputedProperty
* side-effect axis-1d : domainChanged(), which is a similar approach, but
* it localises the dependencies to a single axis whereas this would
* duplicate updates. */
// this.updateAxesScale();
return count;
}),

Expand Down
1 change: 1 addition & 0 deletions frontend/app/mixins/axis-position.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export default Mixin.create({
if (! axisS) {
/** This replicates the role of axis-1d.js:axisS(); this will be solved
* when Stacked is created and owned by axis-1d.
* (also : now using ensureAxis() in data/block.js : axesBlocks())
*/
let axisName = this.get('axis.id');
axisS = Stacked.getAxis(axisName);
Expand Down
18 changes: 18 additions & 0 deletions frontend/app/models/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import DS from 'ember-data';
import attr from 'ember-data/attr';
// import { PartialModel, partial } from 'ember-data-partial-model/utils/model';

import { intervalMerge } from '../utils/interval-calcs';


export default DS.Model.extend({
datasetId: DS.belongsTo('dataset'),
annotations: DS.hasMany('annotation', { async: false }),
Expand Down Expand Up @@ -71,6 +74,21 @@ export default DS.Model.extend({
console.log('featuresLength', featuresLength, this.get('id'));
return featuresLength;
}),
/** @return undefined if ! features.length,
* otherwise [min, max] of block's feature.value
*/
featuresDomain : Ember.computed('features.[]', function () {
let featuresDomain, features = this.get('features');
if (features.length) {
featuresDomain = features
.mapBy('value')
.reduce(intervalMerge, []);

console.log('featuresDomain', featuresDomain, this.get('id'));
}
return featuresDomain;
}),


isChartable : Ember.computed('datasetId.tags', function () {
let tags = this.get('datasetId.tags'),
Expand Down
19 changes: 18 additions & 1 deletion frontend/app/models/feature.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { computed } from '@ember/object';
import DS from 'ember-data';
import attr from 'ember-data/attr';
//import Fragment from 'model-fragments/fragment';
Expand All @@ -10,5 +11,21 @@ export default DS.Model.extend({
value: attr(),
range: attr(),
parentId: DS.belongsTo('feature', {inverse: 'features'}),
features: DS.hasMany('feature', {inverse: 'parentId'})
features: DS.hasMany('feature', {inverse: 'parentId'}),

/*--------------------------------------------------------------------------*/

/** feature can have a direction, i.e. (value[0] > value[1])
* For domain calculation, the ordered value is required.
*/
valueOrdered : computed('value', function () {
let value = this.get('value');
if (value[0] > value[1]) {
let value = [value[1], value[0]];
}
return value;
})

/*--------------------------------------------------------------------------*/

});
7 changes: 5 additions & 2 deletions frontend/app/services/data/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,10 @@ export default Service.extend(Ember.Evented, {
return records; // .toArray()
}),
viewed: Ember.computed(
'blockValues.[]',
'blockValues.@each.isViewed',
function() {
let records = this.get('blockValues')
let records = this.get('store').peekAll('block') // this.get('blockValues')
.filterBy('isViewed', true);
if (trace_block)
console.log('viewed', records.toArray());
Expand Down Expand Up @@ -374,8 +375,10 @@ export default Service.extend(Ember.Evented, {
function (map, block) {
let axis = block.get('axis');
if (! axis) {
let oa = stacks.oa, axisApi = oa.axisApi;
axisApi.cmNameAdd(oa, block);
console.log('axesBlocks ensureAxis', block.get('id'));
stacks.oa.axisApi.ensureAxis(block.get('id'));
axisApi.ensureAxis(block.get('id'));
stacks.forEach(function(s){s.log();});
axis = block.get('axis');
console.log('axesBlocks', axis);
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/templates/components/draw/axes-1d.hbs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div>axesP : {{axesP.length}}</div>
{{#each axesP as |axis axisIndex|}}
<div>axis : {{axisIndex}}, {{axis.view}} {{axis.view.axisName}}, {{axis.extended}}</div>
<div>axis : {{axisIndex}}, {{axis.extended}}</div>
{{#draw/axis-1d drawMap=drawMap axis=axis axes2d=axes2d as |axis1d|}}
{{draw/axis-ticks-selected axis1d=axis1d axisId=axis.id
drawMap=drawMap
Expand Down
5 changes: 4 additions & 1 deletion frontend/app/templates/components/draw/axis-1d.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
axis-1d: {{ extended }} {{ axis.id }} {{ domainChanged }} , {{ position }}, {{ currentPosition }}, {{ lastDrawn }}, {{ currentPosition.yDomain }} {{ zoomed }}, {{ dataBlocks.length }} {{ dataBlocks.0.features.length }} {{ featureLengthEffect }}
{{!-- commented-out values may be changed during the render which
will cause displaying their value to produce an exception,
refn https://github.com/emberjs/ember.js/issues/13948 --}}
axis-1d: {{ extended }} {{ axis.id }}, {{!-- axis.view}} {{axis.view.axisName , --}} {{ domainChanged }} , {{ position }}, {{ currentPosition }}, {{ lastDrawn }}, {{!-- currentPosition.yDomain --}} {{ zoomed }}, {{ dataBlocks.length }} {{ dataBlocks.0.features.length }} {{ featureLengthEffect }} {{ blocksDomainEffect }}
{{ log 'axis-1d rendered domainContents' position }}
{{yield this }}
52 changes: 52 additions & 0 deletions frontend/app/utils/interval-calcs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

/*----------------------------------------------------------------------------*/

/* global d3 */

/*----------------------------------------------------------------------------*/

const
intervalLimit = [d3.min, d3.max],
/** Choose the outside values, as with d3.extent()
* true if value a is outside the domain limit b.
*/
intervalOutside = [(a, b) => (a < b),
(a, b) => (a > b),
];

/** Merge the given interval v into the domain, so that the result domain
* contains the interval.
*
* Used within .reduce(), e.g. :
* intervals.reduce(intervalMerge, []);
* @param domain result of merging the intervals.
* form is [min, max].
* @param v a single interval (feature value). can be either direction, i.e. doesn't assume f[0] < f[1]
* @see intervalExtent()
*/
function intervalMerge(domain, v) {
// let v = f.get('valueOrdered');

[0, 1].forEach(function (i) {
/** the limit value of the interval v, in the direction i.
* The result domain is ordered [min, max] whereas the input values v are
* not; this translates the unordered value to the ordered result.
*/
let limit = intervalLimit[i](v);
if ((domain[i] === undefined) || intervalOutside[i](limit, domain[i]))
domain[i] = limit;
});

return domain;
}

/** Calculate the union of the given intervals.
*/
function intervalExtent(intervals) {
let extent = intervals.reduce(intervalMerge, []);
return extent;
}

/*----------------------------------------------------------------------------*/

export { intervalLimit, intervalOutside, intervalMerge, intervalExtent };

0 comments on commit 52ff438

Please sign in to comment.