【问题标题】:Konva: get corners coordinate of a rotated rectangleKonva:获取旋转矩形的角坐标
【发布时间】:2019-11-29 03:25:35
【问题描述】:

如何获得旋转矩形的角坐标(以矩形的中心为轴心)?

我已经尝试了下面链接中的所有解决方案,但似乎没有任何运气。

Rotating a point about another point (2D)

Find corners of a rotated rectangle given its center point and rotation

https://gamedev.stackexchange.com/questions/86755/how-to-calculate-corner-positions-marks-of-a-rotated-tilted-rectangle

这是代码

// make a rectangle with zero rotation
const rect1 = new Konva.Rect({
  x: 200,
  y: 200,
  width: 100,
  height: 50,
  fill: "#00D2FF",
  draggable: true,
  rotation: 0,
  name: "rect"
});

// convert degree to rad
const degToRad = (deg: number) => deg * (Math.PI / 180);

// here's the code i use to rotate it around its center (from https://konvajs.org/docs/posts/Position_vs_Offset.html)

const rotateAroundCenter = (node: Rect, rotation: number) => {
     const topLeft = {
    x: -node.width() / 2,
    y: -node.height() / 2
  };
  console.log(`current X: ${node.x()}, current Y: ${node.y()},`)

  const currentRotatePoint = rotatePoint(topLeft, degToRad(node.rotation()));
  const afterRotatePoint = rotatePoint(topLeft, degToRad(rotation));
  const dx = afterRotatePoint.x - currentRotatePoint.x;
  const dy = afterRotatePoint.y - currentRotatePoint.y;

  node.rotation(rotation);
  node.x(node.x() + dx);
  node.y(node.y() + dy);
  layer.draw();

console.log(`the actual position x: ${node.x()}, y: ${node.y()}`);
};

// the code that i expected to give me the corner point

const computeCornerPoint = (r:Rect) => {
  // for now we want to compute top left corner point(as it's the easiest corner to get)
  let corner = {
     x: r.x(),
     y: r.y()
  };

  // the coordinate of rectangle's center (in stage coordinate)
  const cx = r.x() + r.width() / 2;
  const cy = r.y();

  // sine and cosine of the rectangle's rotation
  const s = Math.sin(degToRad(r.rotation()));
  const c = Math.cos(degToRad(r.rotation()));

  // rotate the corner point
  let xnew = c * (corner.x - cx) - s * (corner.y - cy) + cx;
  let ynew = s * (corner.x - cx) + c * (corner.y - cy) + cy;

  console.log(`based on this function calculation: xnew : ${xnew}, ynew: ${ynew}`);
  return [xnew, ynew];
}

根据上面的代码,如果初始旋转为0,我将矩形顺时针旋转30度, 那么实际位置将与来自 computeCornerPoint 的值相同,即 (219, 178),如果我再次将其顺时针旋转 30 度,则实际位置将是 (246, 169),而来自 computeCornerPoint 的值将是 (275 , 175)。

【问题讨论】:

  • 您好 - 如果我的答案是正确的,请您点击勾号将其标记为正确答案。标记正确或最​​有帮助的答案有助于其他人判断其相关性并将积分奖励给回答者。谢谢。

标签: javascript rotation rectangles konvajs


【解决方案1】:

生活在画布的世界中是矩形的,所以我们需要做的就是预测角的位置是知道左上角的形状和旋转角度,然后应用一些高中数学。旋转点的数学运算在函数 rotatePoint() 中。 sn-p 的其余部分是为其使用和结果说明而设置的。

在全屏模式下运行 sn-p 可能会更好。

// Function to rotate a point.
// pt = {x,y} of point to rotate, 
// o = {x, y} of rotation origin, 
// a = angle of rotation in degrees.
// returns {x, y} giving the new point.
function rotatePoint(pt, o, a){

  var angle = a * (Math.PI/180); // Convert to radians

  var rotatedX = Math.cos(angle) * (pt.x - o.x) - Math.sin(angle) * (pt.y - o.y) + o.x;

  var rotatedY = Math.sin(angle) * (pt.x - o.x) + Math.cos(angle) * (pt.y - o.y) + o.y;  

  return {x: rotatedX, y: rotatedY};

}

// This is just about drawing the circles at the corners.
function drawCorners(rect, angle){

  var rectPos = rect.position();
  
  var x = 0, y = 0;
  for (var i = 0; i < 4; i = i + 1){

  switch (i){
    
    case 0: 
      x = rectPos.x; y = rectPos.y;
      break;

    case 1: 
      x = rectPos.x + rect.width(); y = rectPos.y;
      break;

    case 2: 
      x = rectPos.x + rect.width(); y = rectPos.y + rect.height();
      break;

    case 3: 
      x = rectPos.x; y = rectPos.y + rect.height();
      break;

     }

    var pt = rotatePoint({x: x, y: y}, {x: rectPos.x, y: rectPos.y}, angle)
    circles[i].position(pt)

  }
 }


// rotate and redraw the rectangle
function rotateUnderMouse(){


  // Get the stage position of the mouse
  var mousePos = stage.getPointerPosition();

  // get the stage position of the mouse
  var shapePos = rect.position();

  // compute the vector for the difference
  var rel = {x: mousePos.x - shapePos.x, y: mousePos.y - shapePos.y} 

  // Now apply the rotation
  angle = angle + 90;

  circle.position({x: mousePos.x, y: mousePos.y});
  circle.show();
  
  // and reposition the shape to keep the same point in the shape under the mouse 
  var newPos = ({x: mousePos.x  + rel.y , y: mousePos.y - rel.x}) 

  rect.position(newPos);
  rect.rotation(angle);

  // re-calculate and draw the circle positions.
  drawCorners(rect, angle)

  stage.draw()
}





function setup() {

// Set up a stage and a shape
stage = new Konva.Stage({
  container: 'canvas-container',
  width: 650,
  height: 300
});


layer = new Konva.Layer();
stage.add(layer);

newPos = {x: 80, y: 100};
rect = new Konva.Rect({
   width: 140, height: 50, x: newPos.x, y: newPos.y, draggable: true, stroke: 'silver', fill: 'cyan'
  })

// not very dry, setting up the corner circles.
circle = new Konva.Circle({x: newPos.x, y: newPos.y, radius: 10, fill: 'magenta'}) 
circles[0] = circle.clone();
circles[0].fill('lime')
layer.add(circles[0]);
circles[1] = circle.clone();
circles[1].fill('gold')
layer.add(circles[1]);
circles[2] = circle.clone();
circles[2].fill('blue')
layer.add(circles[2]);
circles[3] = circle.clone();
circles[3].fill('darkviolet')

layer.add(circles[3]);

layer.add(rect);
layer.add(circle);
circle.hide()

drawCorners(rect, 0)

stage.draw()

rect.on('mousedown', function(){
  rotateUnderMouse()
})

}

var stage, layer, rect, circles = [], angle = 0;

setup()
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/4.0.13/konva.js"></script>

<p>Click the rectangle - it will rotate 90 degrees clockwise under the mouse and coloured circles will be drawn consistently at the corners. These circles have their position calculated rather than derived from the visual rectangle corner positions. NB: No dragging !</p>

<div id="canvas-container"></div>

【讨论】:

    【解决方案2】:

    我最近了解到一种新的、更简单的方法来使用内置的 Konva 函数来实现这一点。 node.getTransform 及其密切关系 node.getAbsoluteTransform 方法将检索应用于节点(形状)的变换。绝对版本获取包含父变换的变换,而普通 getTransform 获取相对于节点父级的变换。

    两者都返回一个 Konva.Trasform 对象,该对象本身具有 point() 方法,该方法将采用给定的 {x, y} 对象并将转换应用于它。

    使用应用于形状的变换意味着我们不必关心如何模拟该变换的步骤 - 我们只需要求将相同的变换应用于我们的点。

    这意味着我们可以做到这一点......

    // assuming we have a Konva rect already...
    let rect = new Konva.Rect({
      x: 100,
      y: 80,
      width: 60,
      height: 20,
      fill: 'cyan'
      rotation: 45    
    })
    
    let corners = [],
        size = rect.size();
    
    // Now get the 4 corner points
    corners[0] = {x: 0, y: 0 }; // top left
    corners[1] = {x: size.width, y: 0 }; // top right
    corners[2] = {x: size.width, y: size.height }; // bottom right
    corners[4] = {x: 0, y: size.height }; // bottom left
    
    // And rotate the corners using the same transform as the rect.
    for (let i = 0; i < 4; i++){
      // Here be the magic
      corners[i] = rect.getAbsoluteTransform().point(corners[i]); // top left
    }
    
    // At this point we have the rotated positions of the corners.
    

    重要提示

    你会看到上面代码中矩形的角是相对于原点设置的,而不是相对于矩形位置的。换句话说,左上角是 {x: 0, y: 0} 而不是 {x: rect.x(), y: rect.y()},右下角是 {x: rect.width , y: rect.height}。这是因为矩形的变换是:

    1. 移动到(x, y)
    2. 旋转(角度)

    如果我们在决定未旋转的角点时不否定 moveto,那么它们似乎经历了 2 次 moveto 变换。

    moveto 变换并不明显——它是在 shape 的初始声明中设置 shape.x() 和 shape.y() 的效果。

    这是一个使用 getAbsoluteTransform() 方法的工作 sn-p。

    // Function to rotate a point.
    // node = the shape we are using to deterine the transform of the pt.
    // pt = {x,y} of point to rotate, 
    // returns {x, y} giving the new point.
    function rotatePoint(node, pt){
    
      return node.getAbsoluteTransform().point(pt);
    }
    
    // This is just about drawing the circles at the corners.
    function drawCorners(rect, angle){
    
      var rectPos = rect.position();
      
      var x = 0, y = 0;
      for (var i = 0; i < 4; i = i + 1){
    
      switch (i){
        
        case 0: 
          x = 0; y = 0;
          break;
    
        case 1: 
          x = rect.width(); y = 0;
          break;
    
        case 2: 
          x = rect.width(); y = rect.height();
          break;
    
        case 3: 
          x = 0; y = rect.height();
          break;
    
         }
    
        var pt = rotatePoint(rect, {x: x, y: y})
        circles[i].position(pt)
    
      }
     }
    
    
    // rotate and redraw the rectangle
    function rotateUnderMouse(){
    
    
      // Get the stage position of the mouse
      var mousePos = stage.getPointerPosition();
    
      // get the stage position of the mouse
      var shapePos = rect.position();
    
      // compute the vector for the difference
      var rel = {x: mousePos.x - shapePos.x, y: mousePos.y - shapePos.y} 
    
      // Now apply the rotation
      angle = angle + 90;
    
      circle.position({x: mousePos.x, y: mousePos.y});
      circle.show();
      
      // and reposition the shape to keep the same point in the shape under the mouse 
      var newPos = ({x: mousePos.x  + rel.y , y: mousePos.y - rel.x}) 
    
      rect.position(newPos);
      rect.rotation(angle);
    
      // re-calculate and draw the circle positions.
      drawCorners(rect, angle)
    
      stage.draw()
    }
    
    
    
    
    
    function setup() {
    
    // Set up a stage and a shape
    stage = new Konva.Stage({
      container: 'canvas-container',
      width: 650,
      height: 300
    });
    
    
    layer = new Konva.Layer();
    stage.add(layer);
    
    newPos = {x: 80, y: 100};
    rect = new Konva.Rect({
       width: 140, height: 50, x: newPos.x, y: newPos.y, draggable: true, stroke: 'silver', fill: 'cyan'
      })
    
    // not very dry, setting up the corner circles.
    circle = new Konva.Circle({x: newPos.x, y: newPos.y, radius: 10, fill: 'magenta', listening: false}) 
    circles[0] = circle.clone();
    circles[0].fill('lime')
    layer.add(circles[0]);
    circles[1] = circle.clone();
    circles[1].fill('gold')
    layer.add(circles[1]);
    circles[2] = circle.clone();
    circles[2].fill('blue')
    layer.add(circles[2]);
    circles[3] = circle.clone();
    circles[3].fill('darkviolet')
    
    layer.add(circles[3]);
    
    layer.add(rect);
    layer.add(circle);
    circle.hide()
    
    drawCorners(rect, 0)
    
    stage.draw()
    
    rect.on('mousedown', function(){
      rotateUnderMouse()
    })
    
    }
    
    var stage, layer, rect, circles = [], angle = 0;
    
    setup()
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/konva/4.0.13/konva.js"></script>
    
    <p>Click the rectangle - it will rotate 90 degrees clockwise under the mouse and coloured circles will be drawn consistently at the corners. These circles have their position derived via the rect.transform - not calculated. NB: No dragging !</p>
    
    <div id="canvas-container"></div>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-21
      • 2021-05-31
      • 2013-09-04
      • 1970-01-01
      • 2018-12-09
      相关资源
      最近更新 更多