function merge_object(obj1, obj2){
var obj3 = {};
for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
return obj3;
};
Math.radians = function(degrees) {
return degrees * Math.PI / 180;
};
Math.degrees = function(radians) {
return radians * 180 / Math.PI;
};
var collide = {
PROJECT_ID : 'collide',
PROJECT_SPEED : 50,
FIXED_SQUARE_SIZE : 100,
FIXED_SQUARE_X : 250,
FIXED_SQUARE_Y : 250,
CURSOR_SQUARE_SIZE : 50,
$el : null,
$ctx : null,
project_pos_top : null,
project_pos_left : null,
width : null,
height : null,
cursor_x : 0,
cursor_y : 0,
fixed_angle : 0,
fixed_rotaton_speed : null,
cursor_angle : 0,
cursor_rotaton_speed : null,
draw_approx : true,
fixed_projections : {},
cursor_projections : {},
__init : function( options ){
collide.$el = $('#' + collide.PROJECT_ID);
collide.$ctx = collide.$el[0].getContext('2d');
var offset = collide.$el.offset();
collide.project_pos_top = offset.top;
collide.project_pos_left = offset.left;
collide.width = collide.$el.width();
collide.height = collide.$el.height();
collide.$el.mousemove(function(e){
collide.cursor_x = e.pageX - collide.project_pos_left;
collide.cursor_y = e.pageY - collide.project_pos_top;
});
$('#cursor_set_angle').change(function(){
collide.cursor_angle = parseInt($(this).val());
});
$('#fixed_set_angle').change(function(){
collide.fixed_angle = parseInt($(this).val());
});
collide.run();
},
run : function(){
$('.corners, .functions, .projections').remove();
collide.fixed_rotaton_speed = parseInt($('#fixed_rotation_speed').val());
collide.cursor_rotaton_speed = parseInt($('#cursor_rotation_speed').val());
collide.fixed_angle = collide.fixed_angle+collide.fixed_rotaton_speed;
if(collide.fixed_angle > 360) {collide.fixed_angle -= 360};
if(collide.fixed_angle < 0) {collide.fixed_angle += 360};
$('#fixed_angle').val(collide.fixed_angle);
collide.cursor_angle = collide.cursor_angle+collide.cursor_rotaton_speed;
if(collide.cursor_angle > 360) {collide.cursor_angle -= 360};
if(collide.cursor_angle < 0) {collide.cursor_angle += 360};
$('#cursor_angle').val(collide.cursor_angle);
if(collide.is_approx_collide()){
collide.draw_approx = false;
var fixer_corner = collide.get_corners(
collide.FIXED_SQUARE_X, collide.FIXED_SQUARE_Y,
collide.FIXED_SQUARE_SIZE, collide.fixed_angle);
var cursor_corner = collide.get_corners(
collide.cursor_x, collide.cursor_y,
collide.CURSOR_SQUARE_SIZE, collide.cursor_angle);
collide.cursor_projections = collide.get_projections(
collide.FIXED_SQUARE_X, collide.FIXED_SQUARE_Y,
collide.fixed_angle,cursor_corner);
collide.fixed_projections = collide.get_projections(
collide.cursor_x, collide.cursor_y,
collide.cursor_angle, fixer_corner);
}else{
collide.draw_approx = true;
}
collide.draw();
setTimeout(function(){
collide.run();
}, collide.PROJECT_SPEED);
},
draw : function(){
collide.$ctx.clearRect(0, 0, collide.width, collide.height);
collide.$ctx.setTransform(1, 0, 0, 1, 0, 0);
collide.$ctx.save();
collide.draw_square(collide.FIXED_SQUARE_X, collide.FIXED_SQUARE_Y,
collide.FIXED_SQUARE_SIZE, collide.fixed_angle, '0,0,255');
collide.draw_square(collide.cursor_x, collide.cursor_y,
collide.CURSOR_SQUARE_SIZE, collide.cursor_angle, '255,128,0');
if(collide.draw_approx){
collide.draw_approx_square(collide.FIXED_SQUARE_X, collide.FIXED_SQUARE_Y,
collide.FIXED_SQUARE_SIZE, '0,0,255');
collide.draw_approx_square(collide.cursor_x, collide.cursor_y,
collide.CURSOR_SQUARE_SIZE, '255,128,0');
}
else{
collide.draw_axe(collide.FIXED_SQUARE_X, collide.FIXED_SQUARE_Y,
collide.fixed_angle, '0,0,255');
collide.draw_axe(collide.cursor_x, collide.cursor_y,
collide.cursor_angle, '255,128,0');
collide.draw_projections( collide.fixed_projections, '0,0,255');
collide.draw_projections( collide.cursor_projections, '255,128,0');
}
},
draw_square : function(center_x, center_y, size, angle, rgb){
if(collide.is_approx_collide() && collide.is_collide()){ rgb = "255,0,0"; }
collide.$ctx.save();
collide.$ctx.translate(center_x, center_y);
collide.$ctx.rotate( Math.radians( angle ) );
collide.$ctx.fillStyle = 'rgba('+rgb+',.2)';
collide.$ctx.fillRect(size / -2, size / -2, size, size);
collide.$ctx.translate(size/2, size/2);
collide.$ctx.beginPath();
collide.$ctx.fillStyle = 'rgba('+rgb+',1)';
collide.$ctx.arc(0, 0, 2, 0, Math.PI*2, true);
collide.$ctx.closePath();
collide.$ctx.fill();
collide.$ctx.translate(-size, 0);
collide.$ctx.beginPath();
collide.$ctx.fillStyle = 'rgba('+rgb+',1)';
collide.$ctx.arc(0, 0, 2, 0, Math.PI*2, true);
collide.$ctx.closePath();
collide.$ctx.fill();
collide.$ctx.translate(0, -size);
collide.$ctx.beginPath();
collide.$ctx.fillStyle = 'rgba('+rgb+',1)';
collide.$ctx.arc(0, 0, 2, 0, Math.PI*2, true);
collide.$ctx.closePath();
collide.$ctx.fill();
collide.$ctx.translate(size,0);
collide.$ctx.beginPath();
collide.$ctx.fillStyle = 'rgba('+rgb+',1)';
collide.$ctx.arc(0, 0, 2, 0, Math.PI*2, true);
collide.$ctx.closePath();
collide.$ctx.fill();
collide.$ctx.restore();
},
draw_approx_square : function(center_x, center_y, size, rgb){
collide.$ctx.save();
collide.$ctx.translate(center_x, center_y);
collide.$ctx.strokeStyle = 'rgba('+rgb+',1)';
collide.$ctx.lineWidth = 1;
collide.$ctx.strokeRect(-size, -size, 2*size, 2*size);
collide.$ctx.restore();
},
draw_axe : function (center_x, center_y, angle, rgb){
collide.$ctx.save();
collide.$ctx.translate(center_x, center_y);
collide.$ctx.rotate( Math.radians( angle ) );
collide.$ctx.beginPath();
collide.$ctx.strokeStyle = 'rgba('+rgb+',1)';
collide.$ctx.moveTo(-500, 0);
collide.$ctx.lineTo(500,0);
collide.$ctx.stroke();
collide.$ctx.restore();
collide.$ctx.save();
collide.$ctx.translate(center_x, center_y);
collide.$ctx.rotate( Math.radians( angle+90 ) );
collide.$ctx.beginPath();
collide.$ctx.strokeStyle = 'rgba('+rgb+',1)';
collide.$ctx.moveTo(-500,0);
collide.$ctx.lineTo(500,0);
collide.$ctx.stroke();
collide.$ctx.restore();
},
draw_projections : function(projections, rgb){
for(axe in projections){
for(key in ['min', 'max']){
var projection = projections[axe][key==0?"min":"max"];
collide.$ctx.save();
collide.$ctx.translate(projection.x, projection.y);
collide.$ctx.beginPath();
collide.$ctx.fillStyle = 'rgba('+rgb+',1)';
collide.$ctx.arc(0, 0, 2, 0, Math.PI*2, true);
collide.$ctx.closePath();
collide.$ctx.fill();
collide.$ctx.beginPath();
collide.$ctx.strokeStyle = 'rgba('+rgb+',.2)';
collide.$ctx.moveTo(00,0);
collide.$ctx.lineTo(projection.corner.x - projection.x,
projection.corner.y - projection.y);
collide.$ctx.stroke();
collide.$ctx.restore();
}
collide.$ctx.save();
collide.$ctx.beginPath();
collide.$ctx.strokeStyle = (projections[axe].is_collide)? 'rgba(255,0,0,1)' : 'rgba('+rgb+',1)';
collide.$ctx.lineWidth = 2;
collide.$ctx.moveTo(projections[axe].min.x, projections[axe].min.y);
collide.$ctx.lineTo(projections[axe].max.x,projections[axe].max.y);
collide.$ctx.stroke();
collide.$ctx.restore();
}
},
get_corners : function(x, y, size, angle){
var corners = [];
var radius = parseInt(Math.sqrt(2*size*size)/2);
for(var i_angle=0; i_angle<=270; i_angle+=90){
var corner = {
x : parseInt(x + radius * Math.cos(Math.radians(angle + 45 + i_angle) )),
y : parseInt(y + radius * Math.sin(Math.radians(angle + 45 + i_angle) ))
};
corners.push(corner);
}
return corners;
},
get_functions : function(x, y, size, angle){
var functions = {
x : Math.tan(Math.radians( - angle%90)),
y : Math.tan(Math.radians(90 - angle%90))
};
return functions;
},
get_projections : function(center_x, center_y,angle, corners){
var projections = {
"x": {
'min' : null,
'max' : null,
'distance' : null
},
"y": {
'min' : null,
'max' : null,
'distance' : null
}
};
for(i in corners){
var corner = corners[i];
var projection_x = {}, projection_y = {};
var angle90 = -(angle%90);
var distance_corner_center = Math.floor(Math.sqrt((center_x-corner.x)*(center_x-corner.x) + (center_y-corner.y)*(center_y-corner.y)));
var angle_with_axeX = -Math.floor(Math.degrees(Math.atan((corner.y-center_y) / (corner.x-center_x))));
if(angle_with_axeX > 0) {angle_with_axeX -= 180;}
if(corner.y < center_y || (corner.y == center_y && corner.x < center_x) ){angle_with_axeX -= 180;}
var delta_angle = angle_with_axeX - angle90;
if(delta_angle < -180){delta_angle += 360;}
var distance_center_projection_x = Math.floor(distance_corner_center * Math.cos(Math.radians( delta_angle )));
projection_x.x = Math.floor(center_x + distance_center_projection_x * Math.cos(Math.radians( -angle90 )));
projection_x.y = Math.floor(center_y + distance_center_projection_x * Math.sin(Math.radians( -angle90 )));
if(projections.x.min == null
|| distance_center_projection_x < projections.x.min.distance){
projections.x.min = projection_x;
projections.x.min.distance = distance_center_projection_x;
projections.x.min.corner = corner;
}
if(projections.x.max == null
|| distance_center_projection_x > projections.x.max.distance){
projections.x.max = projection_x;
projections.x.max.distance = distance_center_projection_x;
projections.x.max.corner = corner;
}
var distance_center_projection_y = Math.floor(distance_corner_center * Math.cos(Math.radians( delta_angle-90 )));
projection_y.x = Math.floor(center_x + distance_center_projection_y * Math.cos(Math.radians( -angle90 -90 )));
projection_y.y = Math.floor(center_y + distance_center_projection_y * Math.sin(Math.radians( -angle90 -90)));
if(projections.y.min == null
|| distance_center_projection_y < projections.y.min.distance){
projections.y.min = projection_y;
projections.y.min.distance = distance_center_projection_y;
projections.y.min.corner = corner;
}
if(projections.y.max == null
|| distance_center_projection_y > projections.y.max.distance){
projections.y.max = projection_y;
projections.y.max.distance = distance_center_projection_y;
projections.y.max.corner = corner;
}
}
return projections;
},
is_approx_collide : function(){
return (collide.FIXED_SQUARE_X + collide.FIXED_SQUARE_SIZE + collide.CURSOR_SQUARE_SIZE >= collide.cursor_x
&& collide.FIXED_SQUARE_Y + collide.FIXED_SQUARE_SIZE + collide.CURSOR_SQUARE_SIZE >= collide.cursor_y
&& collide.FIXED_SQUARE_X <= collide.cursor_x + collide.FIXED_SQUARE_SIZE + collide.CURSOR_SQUARE_SIZE
&& collide.FIXED_SQUARE_Y <= collide.cursor_y + collide.FIXED_SQUARE_SIZE + collide.CURSOR_SQUARE_SIZE) ? true : false;
},
is_collide : function(){
collide.fixed_projections.x.is_collide =
( (collide.fixed_projections.x.min.distance <= -collide.CURSOR_SQUARE_SIZE/2 && collide.fixed_projections.x.max.distance >= -collide.CURSOR_SQUARE_SIZE/2 )
|| (collide.fixed_projections.x.min.distance <= collide.CURSOR_SQUARE_SIZE/2 && collide.fixed_projections.x.max.distance >= collide.CURSOR_SQUARE_SIZE/2 )
|| (collide.fixed_projections.x.min.distance >= -collide.CURSOR_SQUARE_SIZE/2 && collide.fixed_projections.x.max.distance <= collide.CURSOR_SQUARE_SIZE/2 )) ? true : false;
collide.fixed_projections.y.is_collide =
( (collide.fixed_projections.y.min.distance <= -collide.CURSOR_SQUARE_SIZE/2 && collide.fixed_projections.y.max.distance >= -collide.CURSOR_SQUARE_SIZE/2 )
|| (collide.fixed_projections.y.min.distance <= collide.CURSOR_SQUARE_SIZE/2 && collide.fixed_projections.y.max.distance >= collide.CURSOR_SQUARE_SIZE/2 )
|| (collide.fixed_projections.y.min.distance >= -collide.CURSOR_SQUARE_SIZE/2 && collide.fixed_projections.y.max.distance <= collide.CURSOR_SQUARE_SIZE/2 )) ? true : false;
collide.cursor_projections.x.is_collide =
( (collide.cursor_projections.x.min.distance <= -collide.FIXED_SQUARE_SIZE/2 && collide.cursor_projections.x.max.distance >= -collide.FIXED_SQUARE_SIZE/2 )
|| (collide.cursor_projections.x.min.distance <= collide.FIXED_SQUARE_SIZE/2 && collide.cursor_projections.x.max.distance >= collide.FIXED_SQUARE_SIZE/2 )
|| (collide.cursor_projections.x.min.distance >= -collide.FIXED_SQUARE_SIZE/2 && collide.cursor_projections.x.max.distance <= collide.FIXED_SQUARE_SIZE/2 )) ? true : false;
collide.cursor_projections.y.is_collide =
( (collide.cursor_projections.y.min.distance <= -collide.FIXED_SQUARE_SIZE/2 && collide.cursor_projections.y.max.distance >= -collide.FIXED_SQUARE_SIZE/2 )
|| (collide.cursor_projections.y.min.distance <= collide.FIXED_SQUARE_SIZE/2 && collide.cursor_projections.y.max.distance >= collide.FIXED_SQUARE_SIZE/2 )
|| (collide.cursor_projections.y.min.distance >= -collide.FIXED_SQUARE_SIZE/2 && collide.cursor_projections.y.max.distance <= collide.FIXED_SQUARE_SIZE/2 )) ? true : false;
return (collide.fixed_projections.x.is_collide
&& collide.fixed_projections.y.is_collide
&& collide.cursor_projections.x.is_collide
&& collide.cursor_projections.y.is_collide) ? true : false;
}
};
$(document).ready(function(){
collide.__init();
});
var debug = function(attr){
if($('.debugjs').length == 0){
$('<span class="debugjs"></span>').appendTo('body');
}
$('.debugjs').text(attr);
}
#collide {
margin: 25px;
border: 1px solid black;
float: left;
}
#form {
margin-left: 550px;
padding-top: 2px;
}
#fixed_form h2 {
color: #0033FF;
}
#cursor_form h2 {
color: #FF8800;
}
#fixed_form input, #cursor_form input {
margin-left: 25px;
width: 50px;
}
#fixed_rotation_speed, #cursor_rotation_speed {
margin-right: 150px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="collide" width="500" height="500"></canvas>
<div id="form">
<h3>Understand how to detect if 2 rotates squares collide.</h3>
<p>The 2 rotates squares collide only when all the projection of a square hit the second one.</p>
<div id="fixed_form">
<h2>Fixed square :</h2>
Rotation speed : <input type="number" id="fixed_rotation_speed" value="0"/>
Angle : <input type="number" id="fixed_angle" value="0"/>
(Set : <input type="number" id="fixed_set_angle" value="0"/>)
</div>
<div id="cursor_form">
<h2>Cursor square :</h2>
Rotation speed : <input type="number" id="cursor_rotation_speed" value="-1"/>
Angle : <input type="number" id="cursor_angle" value="0"/>
(Set : <input type="number" id="cursor_set_angle" value="0"/>)
</div>
</div>