A relative rotation in an animation - javascript

I have a problem with Raphael.js. I want to rotate the "compassScale" - set in the following code - in a relative manner.
This works for the paths, but all the texts "animate" to the absolute rotation of 30 degree. I want them to rotate to the 30 degrees relative from their actual positions.
var compassScale = paper.set();
var centerX = 200;
var centerY = 200;
var radius = 195;
var compasCircle = paper.circle(centerX, centerY, radius);
for(var i = 0; i < 360; i++) {
var winkelRad = i * (Math.PI/180)
var xStart = centerX + Math.sin(winkelRad) * radius;
var yStart = centerY + Math.cos(winkelRad) * radius;
var diff = 6;
if(i % 10 === 0){
compassScale.push(paper.text(centerX, centerY - radius + 18, i).rotate(i, centerX, centerY, true));
diff = 12;
} else if(i % 5 === 0) {
diff = 8;
}
var xEnd = centerX + Math.sin(winkelRad) * (radius - diff);
var yEnd = centerY + Math.cos(winkelRad) * (radius - diff);
compassScale.push(paper.path("M" + xStart + " " + yStart + " L" + xEnd + " " + yEnd));
}
compassScale.animate({rotation:"30 " + centerX + " " + centerY}, 5000);

Like you said, the problem is that you're animating all elements to 30 degrees, not their current rotation + 30 degrees. It's actually quite simple once you think of it that way. Here is your revised code that works:
var compassScale = paper.set();
var texts = []; // array to hold the text elements
var centerX = 200;
var centerY = 200;
var radius = 195;
var compasCircle = paper.circle(centerX, centerY, radius);
for(var i = 0; i < 360; i++) {
var winkelRad = i * (Math.PI/180)
var xStart = centerX + Math.sin(winkelRad) * radius;
var yStart = centerY + Math.cos(winkelRad) * radius;
var diff = 6;
if(i % 10 === 0){
texts.push(paper.text(centerX, centerY - radius + 18, i).rotate(i, centerX, centerY, true));
diff = 12;
} else if(i % 5 === 0) {
diff = 8;
}
var xEnd = centerX + Math.sin(winkelRad) * (radius - diff);
var yEnd = centerY + Math.cos(winkelRad) * (radius - diff);
compassScale.push(paper.path("M" + xStart + " " + yStart + " L" + xEnd + " " + yEnd));
}
compassScale.animate({rotation:"30 " + centerX + " " + centerY}, 5000);
// loop through the text elements, adjusting their rotation by adding 30 to their individual rotation
for (var i = 0, l = texts.length; i < l; i += 1) {
// node.attr("rotation") returns something like 50 200 200, so we have to split the string and grab the first number with shift
texts[i].animate({rotation: (30 + +texts[i].attr("rotation").split(" ").shift()) + " " + centerX + " " + centerY}, 5000);
}

Just a quick observation:
Looks like "Rotation" isn't part of the Atrr anymore since ver 2, so you can't use it in "animate", but you can replace that with "transform: "r" + (some degree)"..
eg:
element.animate( {transform: "r" + (-90)}, 2000, 'bounce');

Related

Honeycomb hexagonal grid?

I'm trying to draw a hexagonal grid in a honeycomb shape.
So far I'm able to draw it in a rectangular shape but I don't know how to convert my for loop to make a honeycomb shape instead.
This is what I currently have
<html>
<body>
<canvas width='1080' height='720' id='hexmap'></canvas>
</body>
<script>
window.addEventListener('DOMContentLoaded', (event) => {
var canvas = document.getElementById('hexmap');
var hexHeight,
hexRadius,
hexRectangleHeight,
hexRectangleWidth,
hexagonAngle = 0.523598776, // 30 degrees in radians
sideLength = 36,
boardWidth = 10,
boardHeight = 10;
hexHeight = Math.sin(hexagonAngle) * sideLength;
hexRadius = Math.cos(hexagonAngle) * sideLength;
hexRectangleHeight = sideLength + 2 * hexHeight;
hexRectangleWidth = 2 * hexRadius;
var ctx = canvas.getContext('2d');
ctx.fillStyle = "#000000";
ctx.strokeStyle = "#CCCCCC";
ctx.lineWidth = 1;
drawBoard(ctx, boardWidth, boardHeight);
function drawBoard(canvasContext, width, height) {
var i,j;
//this loop generates a rectangular hexagon grid
for(i = 0; i < width; ++i) {
for(j = 0; j < height; ++j) {
drawHexagon(
ctx,
i * hexRectangleWidth + ((j % 2) * hexRadius),
j * (sideLength + hexHeight),
false
);
}
}
}
function drawHexagon(canvasContext, x, y, fill) {
var fill = fill || false;
canvasContext.beginPath();
canvasContext.moveTo(x + hexRadius, y);
canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight);
canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight + sideLength);
canvasContext.lineTo(x + hexRadius, y + hexRectangleHeight);
canvasContext.lineTo(x, y + sideLength + hexHeight);
canvasContext.lineTo(x, y + hexHeight);
canvasContext.closePath();
if(fill) {
canvasContext.fill();
} else {
canvasContext.stroke();
}
}
})
</script>
</html>
Which results in this shape
What I'd like to achieve though is a shape like this
I was able to do it using like 13 separate for loops, shifting the hexagon over manually each time but it wasn't very practical nor automated.
If we set some conditions, we can derive an algorithm quite easily.
Let the conditions be:
width & height have to be equal
width & height have to be odd numbers
Now let's look at your shape, which meets the condition as it's width & height is 13. A closer look reveals that we have 7 hexagons in the first row, 8 in the second, 9 in the third and so on up to 13 hexagons at row 7. Afterwards the number of hexagons decreases by one per row until reaching the last row 13.
So the number of hexagons per row can be expressed as:
hexagons = width - (Math.abs(Math.floor(width / 2) - i));
Where i is the row.
Likewise the horizontal starting position of each row decrements by half a hexagon's width until reaching the center.
xStart = (width - 3) % 4 == 0 ? Math.ceil((width - hexagons) / 2) : Math.floor((width - hexagons) / 2);
Now all that's left to do is modifying your for-loop to start at xStart up to xStart+hexagons.
for (j = xStart; j < xStart+hexagons; j++)
Here's a complete example:
var canvas = document.getElementById('hexmap');
var hexHeight,
hexRadius,
hexRectangleHeight,
hexRectangleWidth,
hexagonAngle = 0.523598776, // 30 degrees in radians
sideLength = 9,
boardWidth = 13,
boardHeight = 13;
hexHeight = Math.sin(hexagonAngle) * sideLength;
hexRadius = Math.cos(hexagonAngle) * sideLength;
hexRectangleHeight = sideLength + 2 * hexHeight;
hexRectangleWidth = 2 * hexRadius;
var ctx = canvas.getContext('2d');
ctx.fillStyle = "#000000";
ctx.strokeStyle = "#CCCCCC";
ctx.lineWidth = 1;
drawBoard(ctx, boardWidth, boardHeight);
function drawBoard(canvasContext, width, height) {
var i, j, hexagons, xStart;
//this loop generates a rectangular hexagon grid
for (i = 0; i < height; i++) {
hexagons = width - (Math.abs(Math.floor(width / 2) - i));
xStart = (width - 3) % 4 == 0 ? Math.ceil((width - hexagons) / 2) : Math.floor((width - hexagons) / 2);
for (j = xStart; j < xStart + hexagons; j++) {
drawHexagon(
ctx,
j * hexRectangleWidth + ((i % 2) * hexRadius),
i * (sideLength + hexHeight),
false
);
}
}
}
function drawHexagon(canvasContext, x, y, fill) {
var fill = fill || false;
canvasContext.beginPath();
canvasContext.moveTo(x + hexRadius, y);
canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight);
canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight + sideLength);
canvasContext.lineTo(x + hexRadius, y + hexRectangleHeight);
canvasContext.lineTo(x, y + sideLength + hexHeight);
canvasContext.lineTo(x, y + hexHeight);
canvasContext.closePath();
if (fill) {
canvasContext.fill();
} else {
canvasContext.stroke();
}
}
document.getElementById("slider").oninput = (e) => {
ctx.clearRect(0, 0, canvas.width, canvas.height)
drawBoard(ctx, e.target.value, e.target.value);
}
<input type="range" min="3" max="27" value="13" step="2" id="slider"><br>
<canvas width='400' height='300' id='hexmap'></canvas>

How to draw multiple self-edges in a node-link diagram in D3

Drawing a single self-link on a node in a node-link diagram can be done as described here: D3 Force Layout Self-Linking Node
What would you change if you need to draw multiple links on the same node?
I tried to add a 'rotation' to it based on the number of self-links that exist.
Given the code from the linked example I made the following changes:
function tick() {
link.attr("d", function(d) {
var x1 = d.source.x,
y1 = d.source.y,
x2 = d.target.x,
y2 = d.target.y,
dx = x2 - x1,
dy = y2 - y1,
dr = Math.sqrt(dx * dx + dy * dy),
// Defaults for normal edge.
drx = dr,
dry = dr,
xRotation = 0, // degrees
largeArc = 0, // 1 or 0
sweep = 1; // 1 or 0
// Self edge.
if ( x1 === x2 && y1 === y2 ) {
// Fiddle with this angle to get loop oriented.
var index = getIndexOfDuplicateEdge();
var degree = 360 / numberOfDuplicateEdges();
var degreeForIndex = degree * index;
xRotation = degreeForIndex; // Previously: -45;
// Needs to be 1.
largeArc = 1;
// Change sweep to change orientation of loop.
//sweep = 0; // I also tried to change it based on index % 2
// Make drx and dry different to get an ellipse
// instead of a circle.
drx = 30;
dry = 20;
// For whatever reason the arc collapses to a point if the beginning
// and ending points of the arc are the same, so kludge it.
x2 = x2 + 1;
y2 = y2 + 1;
}
return "M" + x1 + "," + y1 + "A" + drx + "," + dry + " " + xRotation + "," + largeArc + "," + sweep + " " + x2 + "," + y2;
});
This won't draw my ellipses as expected and I cannot find a way to handle this. Based on SVG from Mozilla the large-arc has to be 1. Sweep can be 0 or 1 and will 'mirror' my ellipsis. I can use xRotation between 90-180 with sweep 0/1 which will cover 180 degrees of my circle. However, i do not find a way to draw the ellipsis at the other 180 degree positions.
The number of self-links can vary, and I always want to have the 'best' distribution between ellipsis.
Ideally, it should look like:
The idea is to divide the circle into as many segments as petals your flower has. Then calculate the start- and end points for each petal on the circle and fitting an elipse on those points.
You can use the following code snippet to do achieve this: (the function assumes you have a svg element with the id "svgthing")
function radtodeg(angle) {
return angle * (180/Math.PI);
}
function flower( center_x, center_y, num_self_edges, start_angle, end_angle, radius, length ) {
var angle_sector = end_angle - start_angle;
var num_points = num_self_edges * 2;
var angle_per_point = angle_sector / num_points;
var angle_per_sector = angle_per_point * 2;
var str_builder = [];
for( var angle = start_angle; angle < end_angle; angle += angle_per_sector ) {
var start_sector_angle = angle;
var end_sector_angle = angle + angle_per_point;
var mid_sector_angle = angle + angle_per_point / 2;
var start_x = center_x + (radius * Math.cos(start_sector_angle));
var start_y = center_y + (radius * Math.sin(start_sector_angle));
var end_x = center_x + (radius * Math.cos(end_sector_angle));
var end_y = center_y + (radius * Math.sin(end_sector_angle));
var mid_x = center_x + (radius * Math.cos(mid_sector_angle));
var mid_y = center_y + (radius * Math.sin(mid_sector_angle));
str_builder.push("<path d='");
str_builder.push("M" + start_x + " " + start_y + ",");
str_builder.push("A " + length + " 1 " + radtodeg(mid_sector_angle) + " 0 1 " + end_x + " " + end_y);
str_builder.push("'/>\n");
str_builder.push("<circle cx='" + start_x + "' cy='" + start_y + "' r='5' />\n");
str_builder.push("<circle cx='" + end_x + "' cy='" + end_y + "' r='5'/>\n");
str_builder.push("<circle cx='" + mid_x + "' cy='" + mid_y + "' r='5'/>\n");
}
str_builder.push("<circle cx='" + center_x + "' cy='" + center_y + "' r='" + radius + "' />\n");
$("#svgthing").html(str_builder.join(""));
}
flower(60, 50, 8, 0, 2 * Math.PI, 50, 10);
The example call will generate a flower with 8 petals.

Positioning mesh in three.js

I m trying to understand how i can positioning my cubes in the canvas.
But i don't understand how positioning work.
I m looking a way to detect if my mesh meet the limit of the canvas. But what is the unit of position.x or position.y ?
And what is the relation between the canvas width , height and meshs on in the canvas?
Thanks.
var geometry = new Array(),material = new Array(),cube = new Array();
var i = 0;
for(i=0; i < 10;i++){
geometry[i] = new THREE.BoxGeometry(1,1,1);
material[i] = new THREE.MeshBasicMaterial({ color: 0x33FF99 });
cube[i] = new THREE.Mesh(geometry[i], material[i]);
scene.add(cube[i]);
}
camera.position.z = 5;
var render = function () {
requestAnimationFrame(render);
var xRandom = 0;
var yRandom = 0;
var zRandom = 0;
var sens = 1;
for (i = 0; i < cube.length ; i++) {
document.getElementById('widthHeight').innerHTML = " " + window.innerHeight + " " + window.innerWidth + "<br> x:" + cube[i].position.x + " <br> y:" + cube[i].position.y + " <br> z:" + cube[i].position.z;
xRandom = (Math.random() * 0.010 + 0.001) * sens;
yRandom = (Math.random() * 0.010 + 0.001) * sens;
zRandom = (Math.random() * 0.010 + 0.001) * sens;
cube[i].position.setX(xRandom + cube[i].position.x);
cube[i].position.setY(yRandom + cube[i].position.y);
cube[i].position.setZ(zRandom + cube[i].position.z);
cube[i].rotation.x += 0.0191;
cube[i].rotation.y += 0.0198;
}
renderer.render(scene, camera);
};
render();
i added a PlaneGeometry and some tests to detect if cubes reach limit x or y of the new PlaneGeometry.
var geometry = new Array(),material = new Array(),cube = new Array();
var i = 0;
var planeMap = new THREE.PlaneGeometry(100, 100);
var materialMap = new THREE.MeshBasicMaterial({ color: 0xCE0F0F });
var map = new THREE.Mesh(planeMap,materialMap);
scene.add(map);
for(i=0; i < 5; i++){
geometry[i] = new THREE.BoxGeometry(3,3,3);
material[i] = new THREE.MeshBasicMaterial({ color: 0x336699 });
cube[i] = new THREE.Mesh(geometry[i], material[i]);
scene.add(cube[i]);
}
camera.position.z = 100;
var render = function () {
requestAnimationFrame(render);
var xRandom = 0,yRandom = 0,zRandom = 0,x=0,y=0,z=0;
var sensX = 1, sensY = 1, sensZ = 1;
var widthHeight = document.getElementById('widthHeight');
for (i = 0; i < cube.length ; i++) {
if (cube[i].geometry.type == "BoxGeometry") {
widthHeight.innerHTML = " " + window.innerHeight + " " + window.innerWidth + "<br> x:" + cube[i].position.x + " <br> y:" + cube[i].position.y + " <br> z:" + cube[i].position.z;
var currentCube = cube[i].position;
var widthCube = cube[i].geometry.parameters.width;
var heightCube = cube[i].geometry.parameters.height;
x = currentCube.x;
y = currentCube.y;
z = currentCube.z;
if (x >= ((map.geometry.parameters.width / 2) - widthCube)) {
sensX = -1;
}
if (x <= ((map.geometry.parameters.width / 2) - widthCube)*-1) {
sensX = 1;
}
if (y >= ((map.geometry.parameters.height / 2) - heightCube)) {
sensY = -1;
}
if (y <= ((map.geometry.parameters.height / 2) - heightCube)*-1) {
sensY = 1;
}
xRandom = (Math.random() * 0.650 + 0.001) * sensX * (i + 1);
yRandom = (Math.random() * 0.850 + 0.001) * sensY * (i + 1);
//zRandom = (Math.random() * 0.450 + 0.001) * sensZ * (i + 1);
cube[i].position.setX(xRandom + x);
cube[i].position.setY(yRandom + y);
cube[i].position.setZ(zRandom + z);
cube[i].rotation.x += 0.01;
cube[i].rotation.y += 0.01;
}
}
In three.js you are working with a 3D space, the scene. The x,y,z coordinates are in that space. Similar to the real world, you can think of it as a room with some objects in it.
What gets shown on the canvas in 2D is how your camera sees a particular view to that scene. You can observe this by modifying the z coordinate of the camera position which you have in your code. This is again similar to taking a photo with a real camera of a room with some objects.
So the relation of the 3D object position and where they show in the canvas depend on the camera view. Three.js can tell you where some 3D position is projected in the 2D view -- for details and an example, see the answer there: Converting 3D position to 2d screen position [r69!]

javascript raphael.js multiple objects and events

i'm trying to link an event to a raphael.js object through hover event, but it doesnt work. Here is my code :
var paper = Raphael('menu', 400, 400);
for(var i = 0; i < 6; i++) {
var x = 200,
y = 200;
var rx = Math.sin(i / 6 * 2 * Math.PI),
ry = Math.cos(i / 6 * 2 * Math.PI);
var hx = x + rx * 100,
hy = y + ry * 100;
var hexa = polygon(hx, hy, 6, 50);
hexa.attr("fill", "rgb(212, 212, 212)");
hexa.attr("stroke-width", 0);
var hoverTitle = paper.text(hx + rx * 70, hy + ry * 70, "foo " + i);
var hoverIn = function() {
this.animate({fill: "rgb(247,245,240)"}, 300, '<>');
hoverTitle.show();
hoverTitle.animate({opacity:1}, 200, '<>');
}
var hoverOut = function() {
this.animate({fill: "rgb(212, 212, 212)"}, 300, '<>');
hoverTitle.animate({opacity:0}, 200, '<>');
hoverTitle.hide();
}
hexa.hover(hoverIn, hoverOut, hexa, hexa);
}
function polygon(x, y, N, side) {
var path = "", n, temp_x, temp_y, angle;
for(n = 0; n <= N; n++) {
angle = n / N * 2 * Math.PI;
temp_x = x + Math.cos(angle) * side;
temp_y = y + Math.sin(angle) * side;
path += (n === 0 ? "M" : "L") + temp_x + "," + temp_y;
}
return paper.path(path);
}
i want each hexagon to display it's foo when hovered, but i dont understand why it's always refering to the last foo... Should i declare each one separately ?
Here is a fiddle
This happening because you define hoverTitle as global variable, so when you try to manipulate with it in callbacks you always use last one.
The solution is to define hoverTitle as local property for each hexagon, for example like this:
hexa.hoverTitle = paper.text(hx + rx * 70, hy + ry * 70, "foo " + i);
And then manipulate with this property in callbacks:
this.hoverTitle.show();
this.hoverTitle.animate({opacity:1}, 200, '<>');
Fiddle

Create line between two images in jQuery

Is there any way to write with jQuery in html. Hence when a user click any one of the shown image, a line between them will be appeared. I found a lot of info about " how to do it in canvas",and I found it is possible to just use the images in html file.
For example when clicked on id1 and the id4 a line appears that connects them.
Thanks to #LastCoder:
This won't work in IE8 or below due to CSS restrictions. It uses pure Javascript, no need for jQuery.
function getOffset( el ) { // return element top, left, width, height
var _x = 0;
var _y = 0;
var _w = el.offsetWidth|0;
var _h = el.offsetHeight|0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
_x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return { top: _y, left: _x, width: _w, height: _h };
}
function connect(div1, div2, color, thickness) { // draw a line connecting elements
var off1 = getOffset(div1);
var off2 = getOffset(div2);
// bottom right
var x1 = off1.left + off1.width;
var y1 = off1.top + off1.height;
// top right
var x2 = off2.left + off2.width;
var y2 = off2.top;
// distance
var length = Math.sqrt(((x2-x1) * (x2-x1)) + ((y2-y1) * (y2-y1)));
// center
var cx = ((x1 + x2) / 2) - (length / 2);
var cy = ((y1 + y2) / 2) - (thickness / 2);
// angle
var angle = Math.atan2((y1-y2),(x1-x2))*(180/Math.PI);
// make hr
var htmlLine = "<div style='padding:0px; margin:0px; height:" + thickness + "px; background-color:" + color + "; line-height:1px; position:absolute; left:" + cx + "px; top:" + cy + "px; width:" + length + "px; -moz-transform:rotate(" + angle + "deg); -webkit-transform:rotate(" + angle + "deg); -o-transform:rotate(" + angle + "deg); -ms-transform:rotate(" + angle + "deg); transform:rotate(" + angle + "deg);' />";
//
// alert(htmlLine);
document.body.innerHTML += htmlLine;
}
Simply call it by using something like:
<a onclick="testIt();">Draw line</a>
window.testIt = function() {
var div1 = document.getElementById('div1');
var div2 = document.getElementById('div2')
connect(div1, div2, "#0F0", 2);
}
Here is the working example.
I think Using HTML-5 you can do this.Please try this link.Html-5

Categories

Resources