https://jsfiddle.net/7cbymps6/
tooltip: {
positioner: function () {
if(true) {
return {
x: this.chart.plotWidth / 1.5,
y: this.chart.plotHeight / 2
};
} else {
return {
/*x: defaultX ??,
y: defaultY ?? */
};
}
}
},
In else statement I want the tooltip position as it is. But in case of no return statement, it gives error. If I return an empty object, this time it puts the tooltip on the upper left hand side of the chart. How kann I return previous x and y values? Thanks
You have to compute the default position using callback properties: point, labelWidth and labelHeight like that:
tooltip: {
positioner: function(labelWidth, labelHeight, point) {
if (false) {
return {
x: this.chart.plotWidth / 1.5,
y: this.chart.plotHeight / 2
};
} else {
return {
x: point.plotX + this.chart.plotLeft - labelWidth / 2,
y: point.plotY + this.chart.plotTop - labelHeight - 10
};
}
}
}
Demo:
https://jsfiddle.net/BlackLabel/jo0eka18/
Related
I have the following objects:
const circle = { center: { x: 0, y: 0 }, radius: 10 };
const point = { x: 0, y: 0 };
And I need to determine whether point lies in a circle. My code is:
function isInsideCircle(circle, point) {
if (((point.x - circle.center.x) * (point.x - circle.center.x) + (point.y - circle.center.y) * (point.y - circle.center.y)) <= circle.radius * circle.radius) return true;
return false;
}
But this does not work at all. The testing environment says, that it returns wrong boolean, so I suppose it doesn't calculate at all. Am I accessing objects properties wrongly?
I've been trying to use the tooltip positioner to get the tooltip at the middle of each stacked bar instead of the right side but I've not been able to find any variable that could be use to calculate the x of the appropiate point.
tooltip: {
positioner: function (labelWidth, labelHeight,point) {
return {
x: point.plotX - this.chart.hoverPoint.pointWidth,
y: point.plotY + this.chart.plotTop - labelHeight
};
}
}
Here is a codepen that show how it fails on the last point:
http://jsfiddle.net/vw7ebd4k/1/
To calculate the tooltip position you can use point.h and labelWidth. Try something like that:
tooltip: {
positioner: function (labelWidth, labelHeight, point) {
return {
x: point.plotX - point.h/2 + labelWidth/2,
y: point.plotY
};
}
}
To remove the unnecessary line between tooltip and point you can use tooltip.shape property.
shape: 'rect'
I have a grid with items inside of it with x and y co-orditantes. I am trying to write a function (with lodash) to determine where is the first empty spot where the top most left most spot is the first position.
I am trying to do this by iterating over each spot until I find the first empty spot. It is only a 2 column layout so I work through them in a pattern like so - x: 0, y:0 -> x:1, y:0 -> x:0, y:1 -> x:1, y:1 ... and then checking all the items along the way to see if there is not a match, so I then know if there is an opening. My attempt looks like so :
function fillEmptySpace(isFilled, startX, startY) {
if (!isFilled) {
_.forEach(items, function(item, i) {
if (!_.isMatch(item, {
'x': startX
}) && !_.isMatch(item, {
'y': startY
})
) {
console.log("empty spot at", startX, startY);
isFilled = true;
} else if (!_.isMatch(item, {
'x': startX + 1
}) && !_.isMatch(item, {
'y': startY
})) {
console.log("empty spot at", startX + 1, startY);
isFilled = true;
}
});
startY += 1;
fillEmptySpace(isFilled, startX, startY);
}
}
fillEmptySpace(false, 0, 0);
The data looks like so :
var items = [{
i: 'a',
x: 0,
y: 0,
w: 1,
h: 1,
maxW: 2
}, {
i: 'b',
x: 1,
y: 4,
w: 1,
h: 1,
maxW: 2
}, {
i: 'c',
x: 0,
y: 1,
w: 1,
h: 1,
maxW: 2
}, {
i: 'd',
x: 0,
y: 2,
w: 1,
h: 1,
maxW: 2
}];
And here is the fiddle I have been fooling around in : https://jsfiddle.net/alexjm/ugpy13xd/38/
I can't seem to get this logic quite right, I am not sure a this point where I am getting it wrong. Any input would be greatly appreciated!
Just as a note : with the provided data it should identify the first empty space as x:1, y:0, however right now it is saying empty spot at 0 0, which cannot be correct. Thanks!
When it comes to 2D arrays, the 1D index can be calculated with x + y * width. If we then sort the 1D indexes, we can create an O(nlogn) solution:
function findEmptySpace(grid, width) {
var index = _(grid)
.map(function(p) { return p.x + p.y * width })
.sortBy()
.findIndex(_.negate(_.eq));
if (index < 0) index = grid.length;
return {
x: index % width,
y: index / width >> 0 // ">> 0" has the same result as "Math.floor"
};
}
var items = [{x:0,y:0},{x:0,y:4},{x:0,y:1},{x:0,y:2}];
function findEmptySpace(grid, width) {
var index = _(grid)
.map(function(p) { return p.x + p.y * width; })
.sortBy()
.findIndex(_.negate(_.eq));
if (index < 0) index = grid.length;
return {
x: index % width,
y: index / width >> 0 // ">> 0" has the same result as "Math.floor"
};
}
document.getElementById('btn').onclick = function() {
var space = findEmptySpace(items, 2);
items.push(space);
console.log(space);
};
#btn { font-size: 14pt }
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>
<button id="btn">Fill the Empty Space</button>
If you pre-sort the array, the solution would be worst-case O(n).
May I suggest checking to see if the point exists vs checking to see if it doesn't. Iterate over each item in the list to see if it exists if it does set a flag, then increment positions through your grid. Keep in mind this will not account for coords less than your intial value of "startY". Consider the following code:
function findEmptySpace(startX, startY) {
var isFilled = false;
_.forEach(items, function(item, i) {
if (_.isMatch(item, { 'x': startX }) && _.isMatch(item, { 'y': startY }) {
// this spot is filled check next x
isFilled = true;
continue;
}
}
if (isFilled == true) {
// we need to recursively call our function but I don't know the value of x
(startX == 0) ? findEmptySpace(1, startY): findEmptySpace(0, startY + 1);
} else {
console.log("Congrats, we found a spot", startX, startY);
}
}
It looks like you're always going to find a match at 0,0 since your logic is finding if there is any item in the list that is not on 0,0, instead of if there is an item in the list on 0,0
What you really want to do is stop checking once you've found an item in the current x,y (and, additionally, check both x and y in your isMatch). You can use your existing routine and your existing isFilled check:
function fillEmptySpace(isFilled, startX, startY) {
_.forEach(items, function(item, i) {
if (!isFilled) {
if (!_.isMatch(item, {'x': startX, 'y': startY})) {
console.log("empty spot at", startX, startY);
isFilled = true;
} else if (!_.isMatch(item, {'x': startX + 1, 'y': startY})) {
console.log("empty spot at", startX + 1, startY);
isFilled = true;
}
}
});
if (!isFilled) {
startY += 1;
fillEmptySpace(isFilled, startX, startY);
}
}
I have been trying to make highchart tooltip to show the nearest point incase the x-axis value aren't align in different series.
This is what I got so far
http://jsfiddle.net/Yw8hb/5/
Highcharts.wrap(Highcharts.Tooltip.prototype, 'refresh', function (proceed) {
var args = arguments,
points = args[1],
point = points[0],
chart = point.series.chart;
// Loop over all the series of the chart
Highcharts.each(chart.series, function(series) {
// This one already exist
if (series == point.series) return;
var current,
dist,
distance = Number.MAX_VALUE;
// Loop over all the points
Highcharts.each(series.points, function(p) {
// use the distance in X to determine the closest point
dist = Math.abs(p.x - point.x);
if (dist < distance) {
distance = dist;
current = p;
}
});
// Add the closest point to the array
points.push(current);
});
proceed.apply(this, [].slice.call(args, 1));
});
It seems to be working half way there however when you hover in some spot it shows duplicated series. I have spent hours trying to figure this out any help would be very appreciated.
Before insertion, check whether points array contains the current point in your refresh callback function.
// Add the closest point to the array
if(points.indexOf(current)==-1)
points.push(current);
Highcharts.wrap(Highcharts.Tooltip.prototype, 'refresh', function (proceed) {
var args = arguments,
points = args[1],
point = points[0],
chart = point.series.chart;
// Loop over all the series of the chart
Highcharts.each(chart.series, function(series) {
// This one already exist
if (series == point.series) return;
var current,
dist,
distance = Number.MAX_VALUE;
// Loop over all the points
Highcharts.each(series.points, function(p) {
// use the distance in X to determine the closest point
dist = Math.abs(p.x - point.x);
if (dist < distance) {
distance = dist;
current = p;
}
});
// Add the closest point to the array
if(points.indexOf(current)==-1)
points.push(current);
});
proceed.apply(this, [].slice.call(args, 1));
});
$('#container').highcharts({
tooltip: {
shared: true
},
xAxis: {
crosshair: {
color: '#F70000'
}
},
series: [{
data: [{
x: 0.0,
y: 1
}, {
x: 1.0,
y: 2
}, {
x: 2.0,
y: 3
}, {
x: 3.0,
y: 2
}, {
x: 4.0,
y: 1
}]
}, {
data: [{
x: 0.2,
y: 0
}, {
x: 1.2,
y: 1
}, {
x: 2.2,
y: 1
}, {
x: 3.2,
y: 1
}, {
x: 4.2,
y: 2
}]
}, {
data: [{
x: 0.2,
y: 5
}, {
x: 1.2,
y: 9
}, {
x: 2.2,
y: 4
}, {
x: 3.2,
y: 5
}, {
x: 4.2,
y: 3
}]
}]
});
#container {
min-width: 300px;
max-width: 800px;
height: 300px;
margin: 1em auto;
}
<script src="http://code.jquery.com/jquery-git.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/modules/exporting.js"></script>
<div id="container"></div>
If you want to show visible series' in the tooltip only, change
// This one already exist
if (series == point.series) return;
to
// This one already exist
if (series == point.series || series.visible==false) return;
Thanks for you solution!!!
for constant order the tooltips
Highcharts.wrap(Highcharts.Tooltip.prototype, 'refresh', function (proceed) {
var args = arguments,
points = args[1],
point = points[0],
chart = point.series.chart;
// Loop over all the series of the chart
Highcharts.each(chart.series, function (series) {
// This one already exist
if (series == point.series || series.visible == false)
return;
var current,
dist,
distance = Number.MAX_VALUE;
// Loop over all the points
Highcharts.each(series.points, function (p) {
// use the distance in X to determine the closest point
dist = Math.abs(p.x - point.x);
if (dist < distance) {
distance = dist;
current = p;
return;
}
});
// Add the closest point to the array
if (points.indexOf(current) == -1)
points.push(current);
});
// for not changing the tooltip series order
var tt = [].slice.call(args, 1);
tt[0].sort(function (a, b) {
if (a.color < b.color)
return -1;
if (a.color > b.color)
return 1;
return 0;
});
proceed.apply(this, tt);
});
Don't forget tooltip option shared!
options = {
tooltip: {
shared: true,
....
I'm unsure if this is possible, given that the order rotations are applied can affect the form of the rotational matrix; but I'd like get the Euler angles from a CSS matrix3d Transform. I'm finding a dearth of documentation on the format of the matrix3d and how transformations are applied. Here's my code so far:
getRotation: function (el) {
var matrix = Esprit.getTransform(el);
// 2d matrix
if (matrix.length === 6) {
return {
x: 0,
y: 0,
z: Math.round(Math.atan2(matrix[1], matrix[0]) * (180 / Math.PI))
};
}
// 3d matrix
else {
// incorrect calculations
// only work for a single rotation
// return {
// x: Math.round(Math.atan2(matrix[6], matrix[5]) * (180/Math.PI)),
// y: Math.round(Math.atan2(-matrix[2], matrix[0]) * (180/Math.PI)),
// z: Math.round(Math.atan2(matrix[1], matrix[0]) * (180/Math.PI))
// };
// convert from string to number
// for (var i = 0, len = matrix.length; i < len; i++) {
// matrix[i] = Number(matrix[i]);
// }
// gimball lock for positive 90 degrees
if (matrix[2] === 1) {
return {
x: Esprit.toDegrees(Math.atan2(matrix[0], matrix[1])),
y: Esprit.toDegrees(Math.PI / 2),
z: 0
}
}
// gimball lock for negative 90 degrees
else if (matrix[2] === -1) {
return {
x: Esprit.toDegrees(-Math.atan2(matrix[0], matrix[1])),
y: Esprit.toDegrees(-Math.PI / 2),
z: 0
}
}
// no gimball lock
else {
return {
x: Esprit.toDegrees(Math.atan2(matrix[6], matrix[10])),
y: Esprit.toDegrees(Math.asin(matrix[2])),
z: Esprit.toDegrees(Math.atan2(-matrix[1], matrix[0]))
}
}
}
},
getTransform: function (el) {
var transform = getComputedStyle(el).webkitTransform;
return transform !== 'none' ? transform.split('(')[1].split(')')[0].split(',') : Esprit.create3dMatrix();
},
toDegrees: function (radians) {
return Math.round(radians * 180 / Math.PI);
}
Any help or ideas would be greatly appreciated. Thanks!
John Schulz (#jfsiii) posted this response:
https://gist.github.com/4119165