Moving Element in Raphael and check if it collides with other Element - path

I want to check if an animated circle element collides (overlaps) with an animated Path Element in a set of Path Elements.
To make the example easier to understand I animated circles and want you to show me, how you can make the green circle appear red, as soon one of the black circles collides with it:
http://jsfiddle.net/329pK/2/
The Code from the Fiddle:
JS
var paper = Raphael("canvas", 800, 800);
var cx = 400;
var cy = 400;
// Helpers
function rand(min, max) {
return Math.random() * (max - min) + min;
}
var bigCircle = paper.circle(cx, cy, 500);
function flyMeteor(){
var ptOnCircle = bigCircle.getPointAtLength(rand(1,bigCircle.getTotalLength()));
var anim = Raphael.animation({
fill: 'black',
opacity: 0,
cx: ptOnCircle.x,
cy: ptOnCircle.y,
stroke: 0,
r: 0
},1200,function(){
this.remove();
});
var circle = paper.circle(cx, cy, 4).attr({
fill:'black',
stroke: 0
}).animate(anim);
};
setInterval(function(){
flyMeteor();
},200);
var circle = paper.circle(250, 250, 80).attr({
fill:'green',
stroke: 0
});
HTML
<div id="canvas"></div>
Thank you very much for your help!

I'm not sure if there's an easy answer to this one. There are a few SVG methods like referenced here Hit-testing SVG shapes? but I'm not sure they could really be used for this. It also depends if its just circles, or if the circles are just placeholders for something more complex. If its circles, you could follow collision detection like Circle Collision Javascript which I've used in an example below...
For checking, I'm checking all objects outside the animation. This feels a bit clunky, and it would be better to have inside the animation somehow, but haven't seen a way one can do this (you could do your own animate func).
fiddle here http://jsfiddle.net/RvvXL/1/
circle.data('myAnim', anim);
....
function collision (p1x, p1y, r1, p2x, p2y, r2) {
var a;
var x;
var y;
a = r1 + r2;
x = p1x - p2x;
y = p1y - p2y;
if ( a > Math.sqrt( (x*x) + (y*y) ) ) {
return true;
} else {
return false;
}
}
....
setInterval( function() { // maybe use requestAnimationFrame
paper.forEach( function(el) {
if( el.type=='circle' ) {
if( collision( el.attr('cx'), el.attr('cy'), el.attr('r'), bigCircle.attr('cx'), bigCircle.attr('cy'), bigCircle.attr('r') ) ) {
if( el.data('myAnim') ) {
el.stop( el.data('myAnim') );
el.remove();
}
};
}
} );

Related

Logic for jumping in webgl with physics enabled

Most important code just for example. Func UPDATE is called in every draw frame.
I want implement jumping virtualJumpActive == true logic.
I invert procedure for virtualJumpActive == true to make scene object and camera position set by cannonjs position but just in some short time.
My camera.posY follow fixed values for now.
I wanna only Y (for cannonjs Z / jumping ) to be navigated by physics and other XY
to be navigated by camera (WASD user control)...
It is little intricate situation, i have 3 factors
position of camera
position of player (hands)
position of player collision object
In normal regime player collision object follow camera and player.
In jump moment i wanna apply by vertical force and make camera follow collision object. Also i wanna that always Y component depens on physics data. In that way i will have jumping on top of other objects.
var playerUpdater = {
UPDATE: () => {
App.scene[objName].rotation.rotateY(
matrixEngine.Events.camera.yaw + 180)
var detPitch;
var limit = 2;
if(matrixEngine.Events.camera.pitch < limit &&
matrixEngine.Events.camera.pitch > -limit) {
detPitch = matrixEngine.Events.camera.pitch * 2;
} else if(matrixEngine.Events.camera.pitch > limit) {
detPitch = limit * 2;
} else if(matrixEngine.Events.camera.pitch < -(limit + 2)) {
detPitch = -(limit + 2) * 2;
}
if(matrixEngine.Events.camera.virtualJumpActive == true) {
// invert logic
// Scene object set
App.scene[objName].rotation.rotateX(-detPitch);
var detPitchPos = matrixEngine.Events.camera.pitch;
if(detPitchPos > 4) detPitchPos = 4;
App.scene[objName].position.setPosition(
App.scene.playerCollisonBox.physics.currentBody.position.x,
App.scene.playerCollisonBox.physics.currentBody.position.z,
App.scene.playerCollisonBox.physics.currentBody.position.y
)
// Cannonjs object set
// Switched Z - Y
matrixEngine.Events.camera.xPos = App.scene.playerCollisonBox.physics.currentBody.position.x;
matrixEngine.Events.camera.zPos = App.scene.playerCollisonBox.physics.currentBody.position.y;
matrixEngine.Events.camera.yPos = App.scene.playerCollisonBox.physics.currentBody.position.z;
// App.scene.playerCollisonBox.
// physics.currentBody.velocity.set(0, 0, 0);
App.scene.playerCollisonBox.
physics.currentBody.angularVelocity.set(0, 0, 0);
setTimeout(() => {
matrixEngine.Events.camera.virtualJumpActive = false;
matrixEngine.Events.camera.virtualJumpY = 2;
}, 350);
} else {
// Scene object set
App.scene[objName].rotation.rotateX(-detPitch);
var detPitchPos = matrixEngine.Events.camera.pitch;
if(detPitchPos > 4) detPitchPos = 4;
App.scene[objName].position.setPosition(
matrixEngine.Events.camera.xPos,
matrixEngine.Events.camera.yPos - 0.3 + detPitchPos / 50,
matrixEngine.Events.camera.zPos,
)
// Cannonjs object set
// Switched Z - Y
App.scene.playerCollisonBox.
physics.currentBody.position.set(
matrixEngine.Events.camera.xPos,
matrixEngine.Events.camera.zPos,
matrixEngine.Events.camera.yPos);
App.scene.playerCollisonBox.
physics.currentBody.velocity.set(0, 0, 0);
App.scene.playerCollisonBox.
physics.currentBody.angularVelocity.set(0, 0, 0);
}
}
};
.full {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
<object class="full" data="https://fps-matrix-engine.vercel.app"/>
Any suggestion ?
More data:
Stackoverflow Question ref code =>
https://codepen.io/zlatnaspirala/pen/eYKrmdM?editors=0010
Source code github
Last update
https://fps-matrix-engine.vercel.app

Highcharts sankey node without links

I have a highcharts sankey diagram with two sides:
There are situations where some of my nodes have empty links (=with 0 weight). I would like the node to being displayed despite having no link from or to it.
Any chance I can achieve this?
I read on this thread that I have to fake it with weight=1 connexions, I could make the link transparent, and twitch the tooltip to hide those, but that's very painful for something that feels pretty basic.
Maybe a custom call of the generateNode call or something?
Thanks for the help
You can use the following wrap to show a node when the weight is 0.
const isObject = Highcharts.isObject,
merge = Highcharts.merge
function getDLOptions(
params
) {
const optionsPoint = (
isObject(params.optionsPoint) ?
params.optionsPoint.dataLabels : {}
),
optionsLevel = (
isObject(params.level) ?
params.level.dataLabels : {}
),
options = merge({
style: {}
}, optionsLevel, optionsPoint);
return options;
}
Highcharts.wrap(
Highcharts.seriesTypes.sankey.prototype,
'translateNode',
function(proceed, node, column) {
var translationFactor = this.translationFactor,
series = this,
chart = this.chart,
options = this.options,
sum = node.getSum(),
nodeHeight = Math.max(Math.round(sum * translationFactor),
this.options.minLinkWidth),
nodeWidth = Math.round(this.nodeWidth),
crisp = Math.round(options.borderWidth) % 2 / 2,
nodeOffset = column.sankeyColumn.offset(node,
translationFactor),
fromNodeTop = Math.floor(Highcharts.pick(nodeOffset.absoluteTop, (column.sankeyColumn.top(translationFactor) +
nodeOffset.relativeTop))) + crisp,
left = Math.floor(this.colDistance * node.column +
options.borderWidth / 2) + Highcharts.relativeLength(node.options.offsetHorizontal || 0,
nodeWidth) +
crisp,
nodeLeft = chart.inverted ?
chart.plotSizeX - left :
left;
node.sum = sum;
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
if (1) {
// Draw the node
node.shapeType = 'rect';
node.nodeX = nodeLeft;
node.nodeY = fromNodeTop;
let x = nodeLeft,
y = fromNodeTop,
width = node.options.width || options.width || nodeWidth,
height = node.options.height || options.height || nodeHeight;
if (chart.inverted) {
x = nodeLeft - nodeWidth;
y = chart.plotSizeY - fromNodeTop - nodeHeight;
width = node.options.height || options.height || nodeWidth;
height = node.options.width || options.width || nodeHeight;
}
// Calculate data label options for the point
node.dlOptions = getDLOptions({
level: (this.mapOptionsToLevel)[node.level],
optionsPoint: node.options
});
// Pass test in drawPoints
node.plotX = 1;
node.plotY = 1;
// Set the anchor position for tooltips
node.tooltipPos = chart.inverted ? [
(chart.plotSizeY) - y - height / 2,
(chart.plotSizeX) - x - width / 2
] : [
x + width / 2,
y + height / 2
];
node.shapeArgs = {
x,
y,
width,
height,
display: node.hasShape() ? '' : 'none'
};
} else {
node.dlOptions = {
enabled: false
};
}
}
);
Demo:
http://jsfiddle.net/BlackLabel/uh6fp89j/
In the above solution, another node arrangement would be difficult to achieve and may require a lot of modifications beyond our scope of support.
You can consider using mentioned "tricky solution", since might return a better positioning result. This solution is based on changing 0 weight nodes on the chart.load() event and converting the tooltip as well, so it may require adjustment to your project.
chart: {
events: {
load() {
this.series[0].points.forEach(point => {
if (point.weight === 0) {
point.update({
weight: 0.1,
color: 'transparent'
})
}
})
}
}
},
tooltip: {
nodeFormatter: function() {
return `${this.name}: <b>${Math.floor(this.sum)}</b><br/>`
},
pointFormatter: function() {
return `${this.fromNode.name} → ${this.toNode.name}: <b>${Math.floor(this.weight)}</b><br/>`
}
},
Demo:
http://jsfiddle.net/BlackLabel/0dqpabku/

How to get Node Coordinates Data in Sankey Graph

I'm trying to create something similar to the image below. Where each column has a heading with it.
I know that chart.renderer.text can be used to create & place custom text on chart. However, I'm unable to find a way to fetch the column/node coordinates data(x,y) which would help me place them.
Also is there a programmatic way to do this task. For example, a function that fetches all the columns coordinates and populates all the headings from an existing list.
To summarize:
How to fetch a columns (x,y) Coordinates?
How to dynamically place headings for all columns from a list?
Image
You can get the required coordinates and place the headers in render event, for example:
events: {
render: function() {
var chart = this,
series = chart.series[0],
columns = series.nodeColumns,
isFirst,
isLast,
xPos;
if (!series.customHeaders) {
series.customHeaders = [];
}
columns.forEach(function(column, i) {
xPos = column[0].nodeX + chart.plotLeft;
isFirst = i === 0;
isLast = i === columns.length - 1;
if (!series.customHeaders[i]) {
series.customHeaders.push(
chart.renderer.text(
headers[i],
xPos,
80
).attr({
translateX: isFirst ? 0 : (
isLast ?
series.options.nodeWidth :
series.options.nodeWidth / 2
),
align: isFirst ? 'left' : (
isLast ? 'right' : 'center'
)
}).add()
)
} else {
series.customHeaders[i].attr({
x: xPos
});
}
});
}
}
Live demo: https://jsfiddle.net/BlackLabel/6Lvdufbp/
API Reference:
https://api.highcharts.com/highcharts/chart.events.render
https://api.highcharts.com/class-reference/Highcharts.SVGRenderer#text

Highcharts: plotbands in gauge charts

I'm using plotbands in a gauge chart to represent angle ranges. But I'm facing a problem with the plotband angles when the "from" value is higher than the "to" value.
JSFiddle
As you can see, the plotband is set as from: 270, to: 45 but it really is rendered as if it was set as from: 45, to: 270. That renders exactly the oposite angle range that I need.
The only way that I can find to do that is setting two plotbands, one from 270 to 360 and another one from 0 to 45, but that seems very unconvenient.
Is there any easy way to achieve what I'm trying to do?
As I have mentioned in my comment, I think that you should be able to override getPlotBand method in your code for enabling plotBands with bigger from value than to value:
(function(H) {
H.wrap(H.Axis.prototype, 'init', function(proceed, chart, userOptions) {
this.getPlotBandPath = function(from, to, options) {
var center = this.center,
startAngleRad = this.startAngleRad,
pick = H.pick,
map = H.map,
pInt = H.pInt,
fullRadius = center[2] / 2,
radii = [
pick(options.outerRadius, '100%'),
options.innerRadius,
pick(options.thickness, 10)
],
offset = Math.min(this.offset, 0),
percentRegex = /%$/,
start,
end,
open,
isCircular = this.isCircular, // X axis in a polar chart
ret;
// Polygonal plot bands
if (this.options.gridLineInterpolation === 'polygon') {
ret = this.getPlotLinePath(from).concat(this.getPlotLinePath(to, true));
// Circular grid bands
} else {
// Keep within bounds
from = Math.max(from, this.min);
to = Math.min(to, this.max);
// Plot bands on Y axis (radial axis) - inner and outer radius depend on to and from
if (!isCircular) {
radii[0] = this.translate(from);
radii[1] = this.translate(to);
}
// Convert percentages to pixel values
radii = map(radii, function(radius) {
if (percentRegex.test(radius)) {
radius = (pInt(radius, 10) * fullRadius) / 100;
}
return radius;
});
// Handle full circle
if (options.shape === 'circle' || !isCircular) {
start = -Math.PI / 2;
end = Math.PI * 1.5;
open = true;
} else {
start = startAngleRad + this.translate(from);
end = startAngleRad + this.translate(to);
}
radii[0] -= offset; // #5283
radii[2] -= offset; // #5283
ret = this.chart.renderer.symbols.arc(
this.left + center[0],
this.top + center[1],
radii[0],
radii[0], {
start: start, // Math is for reversed yAxis (#3606)
end: end,
innerR: pick(radii[1], radii[0] - radii[2]),
open: open
}
);
}
return ret;
}
proceed.call(this, chart, userOptions);
});
}(Highcharts))
Live example:
http://jsfiddle.net/2Ljk7usL/9/

Revert or execute function in jQuery UI

I'm looking to revert the element to it's original position if it hasn't been dragged by more than a certain distance.
If the element has been dragged by more than the set distance on axis X I want to execute a function that will slide the dragged element out of the viewport and slide it back in. I got it to track the distance but now it somehow won't return function as true or false if the condition is not met... I get an error with D undefined. Does anyone know what could be wrong?
I have tried different solutions, but couldn't get neither one of them to work and wasn't able to find an answer on the web.
This what I have done so far: http://jsfiddle.net/DzEu2/4/
$(".video").draggable({
revert: $("#video").mousedown(function (e) {
$(this).data('p0', {
x: e.pageX,
y: e.pageY
});
}).mouseup(function (e) {
p0 = $(this).data('p0'),
p1 = {
x: e.pageX,
y: e.pageY
},
d = Math.sqrt(Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2));
});
if (d > 100) {
return false;
} else {
return true;
}
start: function (event) {
x = event.originalEvent.pageX;
y = event.originalEvent.pageY;
console.log(x, y);
},
drag: function (event) {
if (x && y) {
axis = Math.abs(event.originalEvent.pageX - x) > Math.abs(event.originalEvent.pageY - y) ? 'x' : 'y';
$(".video").draggable('option', 'axis', axis);
x = y = null;
}
},
stop: function () {
x = y = null;
$(".video").draggable('option', 'axis', false);
},
distance: 20,
});
You are massively overdoing it. Measuring distance travelled on a single axis is rather trivial:
revert: function() {
var orig = $(this).data("uiDraggable").originalPosition.left;
var curr = $(this).data("uiDraggable").position.left;
return curr - orig > 100;
}
See a working update on your fiddle
Note that the name of the data object is different for different versions of jQueryUI. For the one you selected in the fiddle, it's draggable, for newer ones it's uiDraggable.
There are a number of other errors in your code, which I commented out in the fiddle:
You haven't actually surrounded the code you meant to put in revert in a function(){}
Event binders and asynchronous callbacks don't work like what you assume in mousedown and mouseup. The d variable in your d>100 condition will always be undefined
It's generally bad form to use a selector (".video") inside an event handler, when you can get the same object with this or event.currentTarget.
Don't end the last property in an object with a comma. Internet explorer randomly decides to hate that.
Summary? Code reviewing is your friend, don't forget about him! :)

Resources