GameDev – Intersection of Two Lines (Giao điểm của hai đường thẳng)

HTML5 - Canvas -  Intersection of Two LinesTừ hai đoạn thẳng (hoặc vector) trong mặt phẳng 2D, ta có thể tìm được giao điểm của chúng để tính toán các góc phản xạ và hướng di chuyển.

Xem Demo.

Tạo phương trình đường thẳng từ đoạn thẳng

Ta có hai điểm A(x1,y1) và B(x2,y2) tạo thành một đoạn thẳng, để tạo được một phương trình đường thẳng từ hai điểm này, ta cần tính được độ nghiêng của đường thẳng (slope) theo công thức:

a = (y2 – y1)/(x2 – x1)

Thay thế x2, y2 bằng hai biến x,y:

a = (y – y1)/(x – x1)
=> y – y1 = a(x – x1)

Hay:
y = ax + (y1 – ax1)
Đặt b = y1 – ax1, ta có:

y = ax + b

Tính giao điểm của hai đường thẳng

Ta có hai phương trình đường thẳng

(1): y = a1x + b1
(2): y = a2x + b2

Ta có thể tính được giao điểm của chúng bằng cách tìm giao điểm x trước:

a1x + b1 = a2x + b2;

=> x(a1 – a2) = b2 – b1
=> x = (b2 – b1)/(a1 – a2)

Khi đã có x, ta sẽ thế vào (1) hoặc (2) để tính được y.
Để kiểm tra hai đoạn thẳng có cắt nhau không, ta chỉ cần kiểm tra điểm {x,y} thuộc cả hai đoạn thẳng hay không.
Ngoài ra ta cần loại trừ trường hợp hai đường thẳng song song hoặc trùng nhau, khi đó giá trị slope của chúng sẽ bằng nhau.

Minh họa với HTML5 Canvas

HTML5 - Canvas - Intersection of Two Lines
<html>
<head>
  <script>
 /***************** Circle ***************/

function Circle(x,y) {
    this.x = x;
    this.y = y;
	this.radius = 5;
}

Circle.prototype.draw = function(context) {
    context.beginPath();
    context.fillStyle = "black";
    context.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
    context.closePath();
    context.fill();
}
Circle.prototype.setPosition = function(x, y) {
    this.x = x + this.radius;
    this.y = y + this.radius;
}
Circle.prototype.contains = function(x, y) {
    var dx = this.x - x;
    var dy = this.y - y;

    return Math.sqrt(dx * dx + dy * dy) <= this.radius;
}

/***************** Line ***************/

function Line(x1,y1,x2,y2,color,hasHandles) {
    this.color = color;
	this.p1 = new Circle(x1,y1,5);
	this.p2 = new Circle(x2,y2,5);
	this.hasHandles = hasHandles;
}
Line.prototype.captureMouse = function(x,y) {
	if(this.p1.contains(x,y))
		return this.p1;
	if(this.p2.contains(x,y))
		return this.p2;
}
Line.prototype.getVector = function() {
	var x = this.p2.x-this.p1.x;
	var y = this.p2.y-this.p1.y;

	return {
		x: x,
		y: y,
		root: this.p1,
		length: Math.sqrt(x*x+y*y)
	};
}
Line.prototype.draw = function(context) {

	context.beginPath();
	context.strokeStyle = this.color;
	context.lineWidth = this.width;

    context.moveTo(this.p1.x, this.p1.y);
    context.lineTo(this.p2.x, this.p2.y);
	context.closePath();
	context.stroke();

	if(this.hasHandles){
		this.p1.draw(context);
		this.p2.draw(context);
	}
}
 /************** Main *************/

var _canvas;
var _context;
var _movingShape = false;
var _line1;
var _line2;

function canvas_mousedown(e) {

    var x = e.pageX - _canvas.offsetLeft;
    var y = e.pageY - _canvas.offsetTop;

    _movingShape = _line1.captureMouse(x, y);
    if(!_movingShape)
		_movingShape = _line2.captureMouse(x, y);
}

function canvas_mousemove(e) {
	var x = e.pageX - _canvas.offsetLeft;
	var y = e.pageY - _canvas.offsetTop;

	if (_movingShape) {
        _movingShape.setPosition(x, y);
		update();
    }
}

function canvas_mouseup(e) {
    _movingShape = null;
}

function clear() {
    _context.clearRect(0, 0, _canvas.width, _canvas.height);
}
function update(){

	var a1 = (_line1.p2.y-_line1.p1.y)/(_line1.p2.x-_line1.p1.x);
	var b1 = _line1.p2.y - a1*_line1.p2.x;

	var a2 = (_line2.p2.y-_line2.p1.y)/(_line2.p2.x-_line2.p1.x);
	var b2 = _line2.p2.y - a2*_line2.p2.x;

	// find intersection point
	// y = a1x + b1
	// y = a2x + b2
	// a1x + b1 = a2x + b2
	// (a1 - a2)x = b2 - b1
	var x = (b2 - b1)/(a1 - a2);
	var y = a1*x+b1;

	// draw

	clear();

    _line1.draw(_context);
	_line2.draw(_context);

	_context.fillText(genFormula(a1,b1),10,20);
	_context.fillText(genFormula(a2,b2),10,40);

	_context.beginPath();
    _context.fillStyle = "black";
    _context.arc(x, y, 10, 0, Math.PI * 2, true);
    _context.closePath();
    _context.fill();
}
function genFormula(a,b)
{
	a = floor(a);
	b = floor(b);
	var s = "y = ";
	if(a!=0)
		s += (a==1?"":a)+"x";
	if(b!=0)
		s += (b>0?(a==0?"":"+"):"")+b;
	return s;
}
function floor(number)
{
	return Math.floor(number*10)/10;
}

function distance(x,y,xx,yy){
	var dx = xx-x;
	var dy = yy-y;
	return Math.sqrt(dx*dx+dy*dy);
}

window.onload = function(){
	_canvas = document.getElementById("canvas");
	_context = _canvas.getContext("2d");
	_context.font = "16px Arial";
	_context.lineWidth = 3;

	_line1 = new Line(100, 100, 150, 150,"red",true);
	_line2 = new Line(100, 200, 300, 200,"green",true);
	_canvas.onmousedown = canvas_mousedown;
	_canvas.onmousemove = canvas_mousemove;
	_canvas.onmouseup = canvas_mouseup;

	update();

}
  </script>
</head>
<body>
<canvas id="canvas" width="400px" height="400px" style="border: 1px solid"></canvas>
</body>
</html>

YinYangIt's Blog

Advertisements

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Đăng xuất / Thay đổi )

Connecting to %s