class Vec2 { /** * @param {number} x * @param {number} y */ constructor(x, y) { this.x = x; this.y = y; } /** * @param {Vec2} b */ add(b) { let a = this; return new Vec2(a.x+b.x, a.y+b.y); } /** * @param {Vec2} b */ sub(b) { let a = this; return new Vec2(a.x-b.x, a.y-b.y); } copy() { return new Vec2(this.x, this.y); } /** * @param {number} s */ mul(s) { return new Vec2(s*this.x, s*this.y); } mag() { return sqrt(this.x ** 2 + this.y ** 2); } rotate(rad) { return new Vec2( this.x*cos(rad) - this.y*sin(rad), this.x*sin(rad) + this.y*cos(rad) ); } } class Ray2 { /** * @param {Vec2} pos このレイの始点の位置ベクトル. * @param {Vec2} way このレイの始点から伸びる方向ベクトル. */ constructor(pos, way) { this.pos = pos; this.way = way; } /** * @param {Vec2} begin * @param {Vec2} end */ static withPoints(begin, end) { return new Ray2(begin, end.sub(begin)); } get begin() { return this.pos; } get end() { return this.pos.add(this.way); } } let field = [ [1,1,1,0,1], [1,0,0,0,1], [0,0,1,0,1], [0,1,1,1,1], [0,0,0,0,1], ]; let fieldGet = (x,y) => x>=0 && x<5 && y>=0 && y<5 ? field[y][x] : 0; let intersection = (ray, field, tileWidth) => { //----- 交点を計算 let s = tileWidth; let t = (ray.end.y - ray.pos.y) / (ray.end.x - ray.pos.x); let y = (x) => t*x - t*ray.pos.x + ray.pos.y; let x = (y) => (y + t*ray.pos.x - ray.pos.y) / t; let rayLeftX = min(ray.pos.x, ray.end.x); let rayRightX = max(ray.pos.x, ray.end.x); // console.log(ray); strokeWeight(3); line(ray.pos.x, ray.pos.y, ray.end.x, ray.end.y); // 交点の候補 (x=0,1,2... と y=0,1,2... の交点) let xKouho = []; let yKouho = []; for(let i=0; i<5; i++) { xKouho.push([i*s, y(i*s)]); yKouho.push([x(i*s), i*s]); } // 候補から交点を計算する(壁にあたっているものだけに絞る) let xKouten = xKouho.filter( k => fieldGet(int(k[0]/s), int(k[1]/s)) === 1 || fieldGet(int(k[0]/s - 1), int(k[1]/s)) === 1 ); let yKouten = yKouho.filter( k => fieldGet(int(k[0]/s), int(k[1]/s)) === 1 || fieldGet(int(k[0]/s), int(k[1]/s) - 1) === 1 ); // xyバラバラになってる交点をまとめる let kouten = [...xKouten, ...yKouten]; // 線分の条件でフィルター kouten = kouten.filter(k => rayLeftX < k[0] && k[0] < rayRightX); // 交点がない if (kouten.length === 0) return null; // レイの始点に一番近い交点を返す let m = new Vec2(ray.begin.x, ray.begin.y); kouten.sort( (a,b) => { let p = new Vec2(...a).sub(m).mag(); let q = new Vec2(...b).sub(m).mag(); return p - q; } ); // レイの始点から一番近い(0番目の交点)を選ぶ let firstKouten = kouten[0]; return firstKouten; }; function setup() { createCanvas(640, 640); } function draw() { background(200); //drawIntersection(); //----- グリッド上にある壁と線分の交点のやつ let s = 120; /* let s = 120; let p = mouseX/320 - 1; let q = mouseY; let y = (x) => p*x + q; let x = (y) => (y-q)/p; */ strokeWeight(2); for(let j=0; j<5; j++) { for(let k=0; k<5; k++) { if (field[k][j] === 0) { noFill(); }else{ fill(255); } rect(s*j, s*k, s, s); } } //----- 交点を描画 let ray = Ray2.withPoints(new Vec2(430,180), new Vec2(mouseX, mouseY)); let kouten = intersection(ray, field, s); if (kouten !== null) { strokeWeight(18); point(...kouten); } } /* function drawIntersection() { let L1 = new Ray2(new Vec2(100,200), new Vec2(200,50)); let L2 = new Ray2(new Vec2(200,100), new Vec2(mouseX-200,mouseY-100)); //----- 交点を計算 let x1 = L1.pos.x;oo let y1 = L1.pos.y; let t1 = L1.way.y / L1.way.x; let left1 = min(L1.pos.add(L1.way).x, L1.pos.x); let right1 = max(L1.pos.add(L1.way).x, L1.pos.x); let x2 = L2.pos.x; let y2 = L2.pos.y; let t2 = L2.way.y / L2.way.x; let left2 = min(L2.pos.add(L2.way).x, L2.pos.x); let right2 = max(L2.pos.add(L2.way).x, L2.pos.x); let x = (t1*x1 - t2*x2 - y1 + y2) / (t1 - t2); let y = t1*(x - x1) + y1; //----- ここまで strokeWeight(4); line(L1.pos.x, L1.pos.y, L1.end.x, L1.end.y); line(L2.pos.x, L2.pos.y, L2.end.x, L2.end.y); strokeWeight(15); if (left1 < x && x < right1 && left2 < x && x < right2) { point(x, y); } }*/