2016-07-08 6 views
3

Ich versuche zu testen, ob ein Punkt innerhalb eines rechteckigen Bereichs ist, der sich um einen Winkel (x, y) dreht, wie im Bild unten. Dies ist sprachunabhängiges Problem, aber ich arbeite jetzt mit HTML5 Canvas.Wie testen, ob ein Punkt in einem rechteckigen Bereich ist, der einen Winkel dreht?

Angenommen, der zu testende Punkt ist (x1, y1), ist die Breite des Rechtecks ​​100 und die Höhe 60. Im normalen kartesischen Koordinatensystem ist das Rechteck ABCD oben links Punkt A (canvas.width/2, canvas.height/2 -rect.height/2). Ich nehme an, dass (canvas.width/2, canvas.height/2) in der Mitte der Linie AB ist, wo B (canvas.width/2, canvas.height/2 + rect.height /2) ist.

Ich habe einige Ressourcen here gelesen und schrieb ein Testprojekt, aber es testet nicht den richtigen Bereich. In meinem Testprojekt möchte ich den folgenden Effekt:

Wenn sich die Maus an einem Punkt befindet, der innerhalb des Bereichs des Test-Rechtecks ​​liegt, wird ein Punkt um die Maus herum angezeigt. Wenn es außerhalb des Rechtecks ​​liegt, wird nichts angezeigt.

aber mein Testprojekt wie folgt aussieht: (Beachten Sie, dass, obwohl ich die Vektor-basierte Technik verwendet, um den Punkt in einem gedrehten rechteckigen Bereich zu testen, die Testfläche bleibt das Rechteck vor der Rotation)

// Detecting a point is in a rotated rectangle area 
 
// using vector based method 
 
const canvas = document.getElementById('canvas'); 
 
canvas.width = window.innerWidth; 
 
canvas.height = window.innerHeight; 
 
const ctx = canvas.getContext('2d'); 
 

 
class Rectangle { 
 
\t constructor(x, y, width, height) { 
 
    \t this.x = x; 
 
    this.y = y; 
 
    this.width = width; 
 
    this.height = height; 
 
    this.searchPoint = { x: 0, y: 0}; 
 
    this.binding(); 
 
    } 
 
    
 
    binding() { 
 
    \t let self = this; 
 
    window.addEventListener('mousemove', e => { 
 
     if (!e) return; 
 
     let rect = canvas.getBoundingClientRect(); 
 
     let mx = e.clientX - rect.left - canvas.clientLeft; 
 
     let my = e.clientY - rect.top - canvas.clientTop; 
 
     self.searchPoint = { x: mx, y: my }; 
 
    }); 
 
\t } 
 
} 
 

 
let rect = new Rectangle(canvas.width /2, canvas.height /2 - 30, 100, 60); 
 

 
function vector(p1, p2) { 
 
    return { 
 
      x: (p2.x - p1.x), 
 
      y: (p2.y - p1.y) 
 
    }; 
 
} 
 

 
function point(x, y) { 
 
\t return { x, y }; 
 
} 
 

 
// Vector dot operation 
 
function dot(a, b) { 
 
    return a.x * b.x + a.y * b.y; 
 
} 
 

 
function pointInRect(p, rect, angle) { 
 
\t let a = newPointTurningAngle(0, -rect.height/2, angle); 
 
\t let b = newPointTurningAngle(0, rect.height/2, angle); 
 
    let c = newPointTurningAngle(rect.width, rect.height/2, angle); 
 
\t let AB = vector(a, b); 
 
    let AM = vector(a, p); 
 
    let BC = vector(b, c); 
 
    let BM = vector(b, p); 
 
    let dotABAM = dot(AB, AM); 
 
    let dotABAB = dot(AB, AB); 
 
    let dotBCBM = dot(BC, BM); 
 
    let dotBCBC = dot(BC, BC); 
 
    
 
    return 0 <= dotABAM && dotABAM <= dotABAB && 0 <= dotBCBM && dotBCBM <= dotBCBC; 
 
} 
 

 
function drawLine(x, y) { 
 
\t ctx.strokeStyle = 'black'; 
 
\t ctx.lineTo(x, y); 
 
    ctx.stroke(); 
 
} 
 

 
function text(text, x, y) { 
 
\t ctx.font = "18px serif"; 
 
    ctx.fillText(text, x, y); 
 
} 
 

 
function newPointTurningAngle(nx, ny, angle) { 
 
\t return { 
 
    \t x: nx * Math.cos(angle) - ny * Math.sin(angle), 
 
    y: nx * Math.sin(angle) + ny * Math.cos(angle) 
 
    }; 
 
} 
 

 
function animate() { 
 
\t ctx.clearRect(0, 0, canvas.width, canvas.height); 
 
\t ctx.setTransform(1, 0, 0, 1, 0, 0); 
 
\t ctx.moveTo(canvas.width/2, 0); 
 
    drawLine(canvas.width /2, canvas.height/2); 
 
    
 
    ctx.moveTo(0, canvas.height/2); 
 
    drawLine(canvas.width/2, canvas.height /2); 
 
    
 
\t let angle = -Math.PI/4; 
 
\t ctx.setTransform(Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle), canvas.width/2, canvas.height/2); 
 
    //ctx.setTransform(1, 0, 0, 1, canvas.width/2, canvas.height/2); 
 
\t ctx.strokeStyle = 'red'; 
 
    ctx.strokeRect(0, -rect.height/2, rect.width, rect.height); 
 
    
 
\t let p = newPointTurningAngle(rect.searchPoint.x - canvas.width/2, rect.searchPoint.y - canvas.height/2, angle); 
 

 
\t let testResult = pointInRect(p, rect, angle); 
 
\t if (testResult) { 
 
    \t ctx.setTransform(1, 0, 0, 1, 0, 0); 
 
    ctx.beginPath(); 
 
    \t ctx.fillStyle = 'black'; 
 
    \t ctx.arc(rect.searchPoint.x, rect.searchPoint.y, 5, 0, Math.PI * 2); 
 
    ctx.fill(); 
 
    } 
 
    
 
    ctx.setTransform(1, 0, 0, 1, 0, 0); 
 
    text('searchPoint x: ' + rect.searchPoint.x + ', y: ' + rect.searchPoint.y, 60, 430); 
 
    text('x: ' + canvas.width/2 + ', y: ' + canvas.height/2, 60, 480); 
 
    
 
    requestAnimationFrame(animate); 
 
} 
 

 
animate();
<canvas id='canvas'></canvas>

Lösung Aktualisiert

ich noch die Vektor-basierte Methode bin wie folgt:

Jetzt habe ich den gedrehten Winkel des Punktes und die Eckpunktkoordinaten geändert, so dass der Punkt im Rechteck erkannt werden kann. Die Eckpunkte befinden sich bereits im gedrehten Koordinatensystem, sodass sie nicht übersetzt werden müssen. Der Punkt der Mausposition muss jedoch übersetzt werden, bevor sie im Rechteckbereich getestet wird.

In setTransform Verfahren rotierte der Winkel positiv ist, wenn im Uhrzeigersinn gedreht wird, die Form ist:

ctx.setTransform(angle_cosine, angle_sine, -angle_sine, angle_cosine, x, y);

So, wenn der Punkt des neuen Berechnungskoordinaten nach einem Winkel dreht, die Formel, dies ändern muß, so dass der Winkel ist positiv, wenn auch im Uhrzeigersinn gedreht:

new_x = x * angle_cosine + y * angle_sine; 
new_y = -x * angle_sine + y * angle_cos; 

// Detecting a point is in a rotated rectangle area 
 
// using vector based method 
 
const canvas = document.getElementById('canvas'); 
 
canvas.width = window.innerWidth; 
 
canvas.height = window.innerHeight; 
 
const ctx = canvas.getContext('2d'); 
 

 
class Rectangle { 
 
\t constructor(x, y, width, height) { 
 
    \t this.x = x; 
 
    this.y = y; 
 
    this.width = width; 
 
    this.height = height; 
 
    this.searchPoint = { x: 0, y: 0}; 
 
    this.binding(); 
 
    } 
 
    
 
    binding() { 
 
    \t let self = this; 
 
    window.addEventListener('mousemove', e => { 
 
     if (!e) return; 
 
     let rect = canvas.getBoundingClientRect(); 
 
     let mx = e.clientX - rect.left - canvas.clientLeft; 
 
     let my = e.clientY - rect.top - canvas.clientTop; 
 
     self.searchPoint = { x: mx, y: my }; 
 
    }); 
 
\t } 
 
} 
 

 
let rect = new Rectangle(canvas.width /2, canvas.height /2 - 30, 100, 60); 
 

 
function vector(p1, p2) { 
 
    return { 
 
      x: (p2.x - p1.x), 
 
      y: (p2.y - p1.y) 
 
    }; 
 
} 
 

 
function point(x, y) { 
 
\t return { x, y }; 
 
} 
 

 
// Vector dot operation 
 
function dot(a, b) { 
 
    return a.x * b.x + a.y * b.y; 
 
} 
 

 
function pointInRect(p, rect) { 
 
    let a = { x: 0, y: -rect.height/2}; 
 
    let b = { x: 0, y: rect.height/2}; 
 
    let c = { x: rect.width, y: rect.height/2}; 
 
    text('P x: ' + p.x.toFixed() + ', y: ' + p.y.toFixed(), 60, 430); 
 
    text('A x: ' + a.x.toFixed() + ', y: ' + a.y.toFixed(), 60, 455); 
 
    text('B x: ' + b.x.toFixed() + ', y: ' + b.y.toFixed(), 60, 480); 
 
\t let AB = vector(a, b); 
 
    let AM = vector(a, p); 
 
    let BC = vector(b, c); 
 
    let BM = vector(b, p); 
 
    let dotABAM = dot(AB, AM); 
 
    let dotABAB = dot(AB, AB); 
 
    let dotBCBM = dot(BC, BM); 
 
    let dotBCBC = dot(BC, BC); 
 
    
 
    return 0 <= dotABAM && dotABAM <= dotABAB && 0 <= dotBCBM && dotBCBM <= dotBCBC; 
 
} 
 

 
function drawLine(x, y) { 
 
\t ctx.strokeStyle = 'black'; 
 
\t ctx.lineTo(x, y); 
 
    ctx.stroke(); 
 
} 
 

 
function text(text, x, y) { 
 
\t ctx.font = "18px serif"; 
 
    ctx.fillText(text, x, y); 
 
} 
 

 
function newPointTurningAngle(nx, ny, angle) { 
 
\t let cos = Math.cos(angle); 
 
    let sin = Math.sin(angle); 
 
\t return { 
 
    \t x: nx * cos + ny * sin, 
 
    y: -nx * sin + ny * cos 
 
    }; 
 
} 
 

 
function animate() { 
 
\t ctx.clearRect(0, 0, canvas.width, canvas.height); 
 
\t ctx.setTransform(1, 0, 0, 1, 0, 0); 
 
\t ctx.moveTo(canvas.width/2, 0); 
 
    drawLine(canvas.width /2, canvas.height/2); 
 
    
 
    ctx.moveTo(0, canvas.height/2); 
 
    drawLine(canvas.width/2, canvas.height /2); 
 
    
 
    let angle = - Math.PI/4; 
 
\t ctx.setTransform(Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle), canvas.width/2, canvas.height/2); 
 
\t ctx.strokeStyle = 'red'; 
 
    ctx.strokeRect(0, -rect.height/2, rect.width, rect.height); 
 
    
 
    \t let p = newPointTurningAngle(rect.searchPoint.x - canvas.width/2, rect.searchPoint.y - canvas.height/2, angle); 
 
    
 
    ctx.setTransform(1, 0, 0, 1, 0, 0); 
 
\t let testResult = pointInRect(p, rect); 
 
    
 
\t if (testResult) { 
 
    ctx.beginPath(); 
 
    \t ctx.fillStyle = 'black'; 
 
    \t ctx.arc(rect.searchPoint.x, rect.searchPoint.y, 5, 0, Math.PI * 2); 
 
    ctx.fill(); 
 
    } 
 
    
 
    ctx.setTransform(1, 0, 0, 1, 0, 0); 
 
    text('searchPoint x: ' + rect.searchPoint.x + ', y: ' + rect.searchPoint.y, 60, 412); 
 
    text('x: ' + canvas.width/2 + ', y: ' + canvas.height/2, 60, 510); 
 
    
 
    requestAnimationFrame(animate); 
 
} 
 

 
animate();
<canvas id='canvas'></canvas>

+4

roate Punkt und Rechteck rückwärts vor der Berechnung? – wutzebaer

+0

@wutzebaer. Drehen Sie entweder den Mauszeiger oder das Rechteck, aber nicht beide. ;-) – markE

Antwort

3

Angenommen, Sie wissen, wie ein Punkt im Rechteck ist, ist der Lösungsansatz, alles (Punkt und Rechteck) zu drehen und in ein "normalisiertes" Koordinatensystem zu übersetzen (kartesisches Koordinatensystem, das uns vertraut ist)) und dann, um es trivial zu überprüfen.

Für weitere Informationen sollten Sie Affine Transformationen überprüfen. Die gute Link, wo Sie anfangen könnte, ist

http://www.mathworks.com/discovery/affine-transformation.html?requestedDomain=www.mathworks.com

2

Der Browser immer Mausposition untransformierten (== nicht gedrehten) berichten.

So zu testen, ob die Maus in einem gedrehten Rechteck ist, können Sie:

  • die ungedrehten Mausposition von der Mausereignis (bezogen auf die Leinwand) Holen.
  • Drehen Sie die Maus x, y gegen den Drehpunkt um die gleiche Drehung wie das Rechteck.
  • Testen Sie, ob sich die Maus innerhalb des Rechtecks ​​befindet. Nun, da sowohl die Rect- als auch die Mausposition in ähnlicher Weise gedreht wurden, können Sie einfach so testen, als ob Maus und Rect nicht rotiert wären.

Kommentierte Code und ein Demo:

var canvas=document.getElementById("canvas"); 
 
var ctx=canvas.getContext("2d"); 
 
var cw=canvas.width; 
 
var ch=canvas.height; 
 
function reOffset(){ 
 
    var BB=canvas.getBoundingClientRect(); 
 
    offsetX=BB.left; 
 
    offsetY=BB.top;   
 
} 
 
var offsetX,offsetY; 
 
reOffset(); 
 
window.onscroll=function(e){ reOffset(); } 
 
window.onresize=function(e){ reOffset(); } 
 

 
var isDown=false; 
 
var startX,startY; 
 

 
var rect=makeRect(50,20,35,20,Math.PI/4,60,30); 
 

 
function makeRect(x,y,w,h,angle,rotationPointX,rotationPointY){ 
 
    return({ 
 
     x:x,y:y,width:w,height:h, 
 
     rotation:angle,rotationPoint:{x:rotationPointX,y:rotationPointY}, 
 
    }); 
 
} 
 

 
drawRect(rect); 
 

 
$("#canvas").mousedown(function(e){handleMouseDown(e);}); 
 

 
function drawRect(r){ 
 
    var rx=r.rotationPoint.x; 
 
    var ry=r.rotationPoint.y; 
 
    // demo only, draw the rotation point 
 
    dot(rx,ry,'blue'); 
 
    // draw the rotated rect 
 
    ctx.translate(rx,ry); 
 
    ctx.rotate(r.rotation); 
 
    ctx.strokeRect(rect.x-rx,rect.y-ry,r.width,r.height); 
 
    // always clean up, undo the transformations (in reverse order) 
 
    ctx.rotate(-r.rotation); 
 
    ctx.translate(-rx,-ry); 
 
} 
 

 
function dot(x,y,fill){ 
 
    ctx.fillStyle=fill; 
 
    ctx.beginPath(); 
 
    ctx.arc(x,y,3,0,Math.PI*2); 
 
    ctx.fill(); 
 
} 
 

 
function handleMouseDown(e){ 
 
    // tell the browser we're handling this event 
 
    e.preventDefault(); 
 
    e.stopPropagation(); 
 
    // get mouse position relative to canvas 
 
    mouseX=parseInt(e.clientX-offsetX); 
 
    mouseY=parseInt(e.clientY-offsetY); 
 
    // rotate the mouse position versus the rotationPoint 
 
    var dx=mouseX-rect.rotationPoint.x; 
 
    var dy=mouseY-rect.rotationPoint.y; 
 
    var mouseAngle=Math.atan2(dy,dx); 
 
    var mouseDistance=Math.sqrt(dx*dx+dy*dy); 
 
    var rotatedMouseX=rect.rotationPoint.x+mouseDistance*Math.cos(mouseAngle-rect.rotation); 
 
    var rotatedMouseY=rect.rotationPoint.y+mouseDistance*Math.sin(mouseAngle-rect.rotation); 
 
    // test if rotated mouse is inside rotated rect 
 
    var mouseIsInside=rotatedMouseX>rect.x && 
 
     rotatedMouseX<rect.x+rect.width && 
 
     rotatedMouseY>rect.y && 
 
     rotatedMouseY<rect.y+rect.height; 
 
    // draw a dot at the unrotated mouse position 
 
    // green if inside rect, otherwise red 
 
    var hitColor=mouseIsInside?'green':'red'; 
 
    dot(mouseX,mouseY,hitColor); 
 
}
body{ background-color: ivory; } 
 
#canvas{border:1px solid red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> 
 
<h4>Clicks inside rect are green, otherwise red.</h4> 
 
<canvas id="canvas" width=512 height=512></canvas>

+0

Diese Lösung funktioniert aber ich bin mir nicht sicher, was 'mouseAngle-rect.rotation' bedeutet. Ich habe eine Revision meines Codes veröffentlicht, in der ich immer noch die Methode befolge, die ich verwende, so dass es für mich einfacher ist zu verstehen, wie es funktioniert. – newguy

+0

** Erklären 'mouseAngle-rect.rotation': ** Wenn Sie klicken, befindet sich die Maus bei' mouseAngle' am Rotationspunkt. Beginnend mit dem Nullwinkel bis zum Rotationspunkt drehen wir zuerst zu dem "mouseAngle" und zweitens drehen wir um den Rotationswinkel des Rect. Das richtet die Maus drehend auf das Rect aus. Viel Glück mit Ihrem Projekt! :-) – markE

2

Wie Sie auf dieser Codepen sehen ich habe (zu erkennen 2 drehen rect kollidieren).

Sie haben 2 Projektionen Ihres Punktes (in meinem Fall der 4 Punkte eines rect) zu überprüfen und schauen, ob die Vorsprünge auf dem anderen rect sind

Sie haben die gleiche Sache zu behandeln, sondern nur für einen Punkt und eine rect statt 2 Rects

alle Projektionen nicht kollidierende enter image description here

alle Projektionen sind kollidierende

required code for codepen link 
+1

Das ist eine sehr schöne Technik. Ich denke, es ist ähnlich wie in der Testprojektlösung. – newguy

Verwandte Themen