确定选择框是否在旋转矩形上方 测试点测试边缘完成测试已添加示例

如何解决确定选择框是否在旋转矩形上方 测试点测试边缘完成测试已添加示例

我有一个用于绘制到 HTML Canvas 的 Rectangle 类。它有一个应用于其绘制方法的旋转属性。如果用户在画布内拖动,则会绘制一个选择选取框。当矩形位于选择选取框内使用数学时,如何将矩形的 active 属性设置为 true?这是我在另一种语言和上下文中遇到的一个问题,所以我没有所有可用的 Canvas 方法(例如 isPointInPath)。

我找到了一篇关于查找 Mouse position within rotated rectangle in HTML5 Canvas 的 StackOverflow 帖子,我正在 Rectangle 方法 checkHit 中实现它。但是,它不考虑选择选取框。它只是看着鼠标 X 和 Y,它仍然关闭。浅蓝色点是矩形旋转的原点。如果有任何不清楚的地方,请告诉我。谢谢。

class Rectangle
{
  constructor(x,y,width,height,rotation) {
    this.x = x;
    this.y = y;
    this.height = height;
    this.width = width;
    this.xOffset = this.x + this.width/2;
    this.yOffset = this.y + ((this.y+this.height)/2);
    this.rotation = rotation;
    this.active = false;
  }
  
  checkHit()
  {
    // translate mouse point values to origin
    let originX = this.xOffset;
    let originY = this.yOffset;
    let dx = marquee[2] - originX;
    let dy = marquee[3] - originY;
    // distance between the point and the center of the rectangle
    let h1 = Math.sqrt(dx*dx + dy*dy);
    let currA = Math.atan2(dy,dx);
    // Angle of point rotated around origin of rectangle in opposition
    let newA = currA - this.rotation;
    // New position of mouse point when rotated
    let x2 = Math.cos(newA) * h1;
    let y2 = Math.sin(newA) * h1;
    // Check relative to center of rectangle
    if (x2 > -0.5 * this.width && x2 < 0.5 * this.width && y2 > -0.5 * this.height && y2 < 0.5 * this.height){
      this.active = true;
    } else {
      this.active = false;    
    }
    
  }
  
  draw()
  {
    ctx.save();
    ctx.translate(this.xOffset,this.yOffset);
    ctx.fillStyle = 'rgba(255,255,1)';
    ctx.beginPath();
    ctx.arc(0,3,2 * Math.PI,true);
    ctx.fill();
    ctx.rotate(this.rotation * Math.PI / 180);
    ctx.translate(-this.xOffset,-this.yOffset);
    if (this.active)
    {
      ctx.fillStyle = 'rgba(255,0.5)';
    } else {
      ctx.fillStyle = 'rgba(0,0.5)';      
    }
    ctx.beginPath();
    ctx.fillRect(this.x,this.y,this.width,this.y+this.height);
    ctx.closePath();
    ctx.stroke();
    ctx.restore();
  }
}

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var raf;
var rect = new Rectangle(50,50,90,30,45);
var marquee = [-3,-3,-3];
var BB=canvas.getBoundingClientRect();
var offsetX=BB.left;
var offsetY=BB.top;
var start_x,start_y;

let draw = () => {
  ctx.clearRect(0,canvas.width,canvas.height);
  //rect.rotation+=1;
  rect.draw();
  ctx.fillStyle = "rgba(200,200,0.5)";
  ctx.fillRect(parseInt(marquee[0]),parseInt(marquee[1]),parseInt(marquee[2]),parseInt(marquee[3]))
  ctx.strokeStyle = "white"
  ctx.lineWidth = 1;
  ctx.rect(parseInt(marquee[0]),parseInt(marquee[3]))
  ctx.stroke()
  raf = window.requestAnimationFrame(draw);
}

let dragStart = (e) =>
{
  start_x = parseInt(e.clientX-offsetX);
  start_y = parseInt(e.clientY-offsetY);
  marquee = [start_x,start_y,0];
  canvas.addEventListener("mousemove",drag);
}

let drag = (e) =>
{
  let mouseX = parseInt(e.clientX-offsetX);
  let mouseY = parseInt(e.clientY-offsetY);
  marquee[2] = mouseX - start_x;
    marquee[3] = mouseY - start_y;
  rect.checkHit();
}

let dragEnd = (e) =>
{
  marquee = [-10,-10,-10];
  canvas.removeEventListener("mousemove",drag);
}

canvas.addEventListener('mousedown',dragStart);
canvas.addEventListener('mouseup',dragEnd);

raf = window.requestAnimationFrame(draw);
body
{
  margin:0;  
}

#canvas
{
  width: 360px;
  height: 180px;
  border: 1px solid grey;
  background-color: grey;
}
<canvas id="canvas" width="360" height="180"></canvas>

解决方法

凸多边形是否重叠

矩形是凸多边形。

Rectanglemarquee 各有 4 个点(角),定义了连接这些点的 4 条边(线段)。

此解决方案适用于所有具有 3 个或更多边的凸不规则多边形。

点和边必须是连续的或顺时针 CW 或 Count Clockwise CCW

测试点

如果一个多边形的任何一点在另一个多边形内,那么它们必须重叠。查看示例函数 isInside

要检查点是否在多边形内,求叉积,边起点为向量,边为向量。

如果所有叉积 >= 0(左侧),则存在重叠(对于 CW 多边形)。如果多边形是逆时针,那么如果所有叉积都

可以在另一个多边形内没有任何点的情况下重叠。

测试边缘

如果一个多边形的任何边与另一个多边形的任何边交叉,则必须有重叠。如果两条线段截取,则函数 doLinesIntercept 返回 true。

完成测试

如果两个多边形重叠,函数 isPolyOver(poly1,poly2) 将返回 true

多边形由一组连接点的 PointLines 定义。

多边形可以是不规则的,也就是说每条边可以是任意长度 > 0

不要传递边长度为 === 0 的多边形,否则将不起作用。

已添加

我添加了函数 Rectangle.toPoints 来转换矩形并返回一组 4 个点(角)。

示例

示例是使用上述方法工作的代码副本。

canvas.addEventListener('mousedown',dragStart);
canvas.addEventListener('mouseup',dragEnd);
requestAnimationFrame(draw);

const Point = (x = 0,y = 0) => ({x,y,set(x,y){ this.x = x; this.y = y }});
const Line = (p1,p2) => ({p1,p2});
const selector = { points: [Point(),Point(),Point()] }
selector.lines = [
    Line(selector.points[0],selector.points[1]),Line(selector.points[1],selector.points[2]),Line(selector.points[2],selector.points[3]),Line(selector.points[3],selector.points[0])
];
const rectangle = { points: [Point(),Point()] }
rectangle.lines = [
    Line(rectangle.points[0],rectangle.points[1]),Line(rectangle.points[1],rectangle.points[2]),Line(rectangle.points[2],rectangle.points[3]),Line(rectangle.points[3],rectangle.points[0])
];

function isInside(point,points) {
    var i = 0,p1 = points[points.length - 1];
    while (i < points.length) {
        const p2 = points[i++];
        if ((p2.x - p1.x) * (point.y - p1.y) - (p2.y - p1.y) * (point.x - p1.x) < 0) { return false }
        p1 = p2;
    }
    return true;
}
function doLinesIntercept(l1,l2) { 
    const v1x = l1.p2.x - l1.p1.x;
    const v1y = l1.p2.y - l1.p1.y;
    const v2x = l2.p2.x - l2.p1.x;
    const v2y = l2.p2.y - l2.p1.y;
    const c = v1x * v2y - v1y * v2x;
    if(c !== 0){
        const u = (v2x * (l1.p1.y - l2.p1.y) - v2y * (l1.p1.x - l2.p1.x)) / c;
        if(u >= 0 && u <= 1){
            const u = (v1x * (l1.p1.y - l2.p1.y) - v1y * (l1.p1.x - l2.p1.x)) / c;
            return  u >= 0 && u <= 1;
        }
    }
    return false;
}   
function isPolyOver(p1,p2) { // is poly p2 under any part of poly p1
    if (p2.points.some(p => isInside(p,p1.points))) { return true };
    if (p1.points.some(p => isInside(p,p2.points))) { return true };
    return p1.lines.some(l1 => p2.lines.some(l2 => doLinesIntercept(l1,l2)));
}
    
const ctx = canvas.getContext("2d");
var dragging = false;

const marquee = [0,0];
const rotate = 0.01;
var startX,startY,hasSize = false;
const BB = canvas.getBoundingClientRect();
const offsetX = BB.left;
const offsetY = BB.top;
class Rectangle {
    constructor(x,width,height,rotation) {
        this.x = x;
        this.y = y;
        this.height = height;
        this.width = width;
        this.rotation = rotation;
        this.active = false;
    }
    toPoints(points = [Point(),Point()]) {
        const xAx = Math.cos(this.rotation) / 2;
        const xAy = Math.sin(this.rotation) / 2;
        const x = this.x,y = this.y;
        const w = this.width,h = this.height;
        points[0].set(-w * xAx + h * xAy + x,-w * xAy - h * xAx + y);
        points[1].set( w * xAx + h * xAy + x,w * xAy - h * xAx + y);
        points[2].set( w * xAx - h * xAy + x,w * xAy + h * xAx + y);
        points[3].set(-w * xAx - h * xAy + x,-w * xAy + h * xAx + y);
    }
    draw() {
        ctx.setTransform(1,1,this.x,this.y);
        ctx.fillStyle = 'rgba(255,255,1)';
        ctx.strokeStyle = this.active ? 'rgba(255,1)' : 'rgba(0,1)';
        ctx.lineWidth = this.active ? 3 : 1;
        
        ctx.beginPath();
        ctx.arc(0,3,2 * Math.PI,true);
        ctx.fill();
        ctx.rotate(this.rotation);
        
        ctx.beginPath();
        ctx.rect(-this.width / 2,- this.height / 2,this.width,this.height);
        ctx.stroke();
    }
}
function draw(){
    rect.rotation += rotate;
    ctx.setTransform(1,0);
    ctx.clearRect(0,canvas.width,canvas.height);
    rect.draw();
    drawSelector();
    requestAnimationFrame(draw);
}
function drawSelector() {
    if (dragging && hasSize) {
        rect.toPoints(rectangle.points);
        rect.active = isPolyOver(selector,rectangle);
        ctx.setTransform(1,0);
        ctx.fillStyle = "rgba(200,200,0.5)";
        ctx.strokeStyle = "white";
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.rect(...marquee);
        ctx.fill();
        ctx.stroke();
    
    } else {
        rect.active = false;
    }

 
}
function dragStart(e) {
    startX = e.clientX - offsetX;
    startY = e.clientY - offsetY;
    drag(e);
    canvas.addEventListener("mousemove",drag);
    
}
function drag(e) {
    dragging = true;
    const x = e.clientX - offsetX;
    const y = e.clientY - offsetY;
    const left = Math.min(startX,x);
    const top = Math.min(startY,y);
    const w = Math.max(startX,x) - left;
    const h = Math.max(startY,y) - top;
    marquee[0] = left;
    marquee[1] = top;
    marquee[2] = w;
    marquee[3] = h;
    if (w > 0 || h > 0) {
        hasSize = true;
        selector.points[0].set(left,top);
        selector.points[1].set(left + w,top);
        selector.points[2].set(left + w,top + h);
        selector.points[3].set(left,top + h);
        
    } else {
        hasSize = false;
    }
}
function dragEnd(e) {
    dragging = false;
    rect.active = false;
    canvas.removeEventListener("mousemove",drag);
}

const rect = new Rectangle(canvas.width / 2,canvas.height / 2,90,Math.PI / 4);
body
{
  margin:0;  
}

#canvas
{
  width: 360px;
  height: 180px;
  border: 1px solid grey;
  background-color: grey;
}
<canvas id="canvas" width="360" height="180"></canvas>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res