-
Notifications
You must be signed in to change notification settings - Fork 55
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
Fix invert point scale logic to get option closest to mouse pointer #150
Changes from all commits
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 |
---|---|---|
|
@@ -117,28 +117,12 @@ export function scaleEqual(scaleA, scaleB) { | |
_.isEqual(scaleA.range(), scaleB.range()); | ||
} | ||
|
||
export function indexOfClosestLeftNumberInList(number, sortedList) { | ||
if (sortedList.length <= 1) { | ||
return 0; | ||
} | ||
|
||
const isDescending = sortedList[0] > sortedList[1]; | ||
// _.sortedIndex works on ascending lists only | ||
// so reverse if working with descending list | ||
const listCopy = isDescending ? sortedList.slice().reverse() : sortedList; | ||
|
||
// Get lower bound of where number falls | ||
let indexForNumber = _.sortedIndex(listCopy, number); | ||
if (isDescending && indexForNumber < sortedList.length) { | ||
indexForNumber += 1; | ||
} else if (!isDescending && indexForNumber > 0) { | ||
indexForNumber -= 1; | ||
} | ||
|
||
return isDescending | ||
? // If descending, remap indexForNumber to work with descending list | ||
Math.abs(indexForNumber - sortedList.length) | ||
: indexForNumber; | ||
export function indexOfClosestNumberInList(number, list) { | ||
return list.reduce((closestI, current, i) => { | ||
return Math.abs(current - number) < Math.abs(list[closestI] - number) | ||
? i | ||
: closestI; | ||
}, 0); | ||
} | ||
|
||
export function invertPointScale(scale, rangeValue) { | ||
|
@@ -147,10 +131,21 @@ export function invertPointScale(scale, rangeValue) { | |
// shim until d3.scalePoint.invert() is implemented for real | ||
// given a value from the output range, returns the *nearest* corresponding value in the input domain | ||
const rangePoints = domain.map(domainValue => scale(domainValue)); | ||
const nearestLeftPointIndex = indexOfClosestLeftNumberInList( | ||
rangeValue, | ||
rangePoints | ||
); | ||
|
||
return domain[nearestLeftPointIndex]; | ||
if (rangePoints.length <= 1) { | ||
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. is there a use case where there is a 0? is the undefined caught in this use case? 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. inspecting d3's code domain looks like it'll always return an array https://github.com/d3/d3-scale/blob/master/src/continuous.js#L91 |
||
return domain[0]; | ||
} | ||
|
||
const isDescending = rangePoints[0] > rangePoints[1]; | ||
|
||
// _.sortedIndex works on ascending lists only | ||
// so reverse if working with descending list | ||
if (isDescending) { | ||
domain.reverse(); | ||
rangePoints.reverse(); | ||
} | ||
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. FWIW I don't think you need this descending/reverse logic anymore since you're not assuming anything about sort order. 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. that's a good point, will actually opt to update the code above and just use lodash's sortedIndex |
||
|
||
const nearestPointIndex = indexOfClosestNumberInList(rangeValue, rangePoints); | ||
|
||
return domain[nearestPointIndex]; | ||
} |
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.
Hi! Thanks for keeping the Reactochart dream alive!
Just FYI - the original reason i wrote it <-- that way was because we were assuming the input data would be sorted (by domain, either ascending or descending). With this assumption, you can use
_.sortedIndex
to do a binary search inO(log n)
time. With ^the new method here, it works for all data regardless of sort order, but always searches every data point, so it'sO(n)
which can get slow for large datasets.IIRC, the plan was to eventually allow a
sorted
flag (on theXYPlot
i guess?) that would tell Reactochart whether the data was sorted or not - it would default false, but iftrue
would use tricks like this to get better performance for sorted data. Might be a nice enhancement for the future.Hope all is well with y'all :)