繪製鏡像線條瞭解一下?

功能需求:「用戶繪製一條線,而後鏡像出另一條線。」canvas

下面是我實現的過程,怕之後要用,就記下來了。bash

首先對問題的描述轉化了一下:「求p(x, y)點, 關於直線p1(x1, y1), p2(x2, y2)鏡像 獲得P(Px, Py)」 。動畫

帶着這個問題開始思考,也在網上找了一些數學計算的公式。ui

step1 分析

先求關於點p1,p2的直線方程。(直線的通常方程Ax + By + C = 0spa

關於直線p1,p2對稱有如下幾種形式:3d

  • 關於X軸的對稱code

    (p1.x = p2.x) 可知 直線(p1 p2) 與X軸垂直,獲得直線方程 x = p1.x;cdn

  • 關於Y軸的對稱blog

    (p1.y = p2.y) 可知 直線(p1 p2) 與Y軸垂直,獲得直線方程 y = p1.y;ip

  • 關於某直線(p1, p2)的對稱

    (p1.x != p2.x) && (p1.y != p2.y) 帶入兩點式 (y - p1.y) / (p2.y - p1.y) = (x - p1.x) / (p2.x - p1.x)

    直線(p1, p2)的斜率爲 k = (p2.y - p1.y) / (p2.x - p1.x)

    獲得直線方程:y = k * (x - p1.x) + p1.y;

step2 計算

求計算(p.x, p.y)關於直線Ax + By + C = 0對稱的點:

Px = p.x - 2 * A (A * p.x + B * p.y + C) / (A^2 + B^2);
Py = p.y - 2 * B (A * p.x + B * p.y + C) / (A^2 + B^2);
複製代碼
  • 關於X軸的對稱

    有直線方程 x - p1.x = 0A = 1, B = 0, C = -p1.x,帶入對稱點計算公式獲得

    Px = 2 * p1.x - p.x;
    
    Py = p.y;
    複製代碼
  • 關於Y軸的對稱

    有直線方程 y - p1.y = 0A = 0, B = 1, C = -p1.y,帶入對稱點計算公式獲得

    Px = p.x;
     
    Py = 2 * p1.y - p.y;
    複製代碼
  • 關於某直線(p1, p2)的對稱

    有直線方程 y = k * (x - p1.x) + p1.yA = k, B = -1, C = -k * p1.x + p1.y,帶入對稱點計算公式獲得

    Px = p.x - 2 * k * ( k * p.x - p.y + (-k * p1.x + p1.y)) / (Math.pow(k, 2) + 1);
     
     Py = p.y - 2 * -1 * ( k * p.x - p.y + (-k * p1.x + p1.y)) / (Math.pow(k, 2) + 1);
    複製代碼

step3 繪製

點的公式都獲得了,剩下的就是canvas繪製了。canvas的繪製這裏就很少講,直接寫個demo直觀點。

  • 創建直線
// 這裏我繪製三條軸的起始點和終點(兩點肯定一條直線),便於觀察繪製的圖形。

let canvasWidth = ctx.canvas.width;
let canvasHeight = ctx.canvas.height;
        
let axleX = {
    start: {
        x: canvasWidth,
        y: canvasHeight / 2
    },
    end: {
        x: 0,
        y: canvasHeight / 2
    }
};

let axleY = {
    start: {
        x: canvasWidth / 2,
        y: 0
    },
    end: {
        x: canvasWidth / 2,
        y: canvasHeight
    }
};

let axle = {
    start: {
        x: canvasWidth,
        y: 0
    },
    end: {
        x: 0,
        y: canvasHeight

    }
};

function drawAxle () {
    ctx.lineWidth = 1;
    ctx.lineJoin = ctx.lineCap = 'round';

    ctx.strokeStyle = '#000';

    ctx.beginPath();
    // x
    ctx.moveTo(axleX.start.x, axleX.start.y);
    ctx.lineTo(axleX.end.x, axleX.end.y);
    ctx.stroke();

    // y
    ctx.beginPath();
    ctx.moveTo(axleY.start.x, axleY.start.y);
    ctx.lineTo(axleY.end.x, axleY.end.y);
    ctx.stroke();

    // 對角
    ctx.beginPath();
    ctx.moveTo(axle.start.x, axle.start.y);
    ctx.lineTo(axle.end.x, axle.end.y);
    ctx.stroke();
};

drawAxle();

複製代碼

分別關於X軸、Y軸、任意線(這裏對角線爲例子)

圖一
  • 計算對稱點(核心)

    由上述獲得公式獲得。

    function calcSymmetryPoint (p1, p2, p) {
    
        if (p1.x  ==  p2.x) {
        	 // 關於Y軸鏡像
            return   {
                x: 2 * p1.x - p.x,
                y: p.y
            }
        } else if (p1.y  ==  p2.y) {
            // 關於X軸鏡像
            return   {
                x: p.x,
                y: 2 * p1.y - p.y
            }
        }
        
        //  關於任意直線鏡像
        let k1 = (p2.y - p1.y) / (p2.x - p1.x);
        
        let x = p.x - 2 * k1 * ( k1 * p.x - p.y + (-k1 * p1.x + p1.y))/ (Math.pow(k1, 2) + 1);
        let y = p.y - 2 * -1 * ( k1 * p.x - p.y + (-k1 * p1.x + p1.y))/ (Math.pow(k1, 2) + 1);
        
    	return   {
            x: x,
            y: y
        }
    }
    複製代碼
  • 繪製

    僞代碼以下:

    ...
    
      function mousemoveHandler (event) {
      	// 獲得須要鏡像的點(p.x, p.y)
      	let x = e.clientX - (w - canvasWidth) / 2;
      	let y = e.clientY - (h - canvasHeight) / 2;
      	  
      	// 獲得鏡像以後點的(Px,Py)
      	calcSymmetryPoint({x: axleX.start.x, y: axleX.start.y}, {x: axleX.end.x, y: axleX.end.y}, {x: x, y: y});
    
        calcSymmetryPoint({x: axleY.start.x, y: axleY.start.y}, {x: axleY.end.x, y: axleY.end.y}, {x: x, y: y});
    
        calcSymmetryPoint({x: axle.start.x, y: axle.start.y}, {x: axle.end.x, y: axle.end.y}, {x: x, y: y}); 
      	  
      	// 繪製.. 
      	// ctx.beginPath();
      	// ctx.moveTo(...);
      	// ctx.lineTo(...);
      	// ctx.stroke();
    }
    
    ...
    
    複製代碼

    效果演示以下:

圖二

這裏只是重點描述一下怎麼計算鏡像點的座標,(計算的方式還有矩陣)咱們能夠用這個來作不少有趣的效果動畫,另外也能夠拓展到3d空間裏去作鏡像。

參考:

斜率: zh.wikipedia.org/wiki/%E6%96…

直線方程:zh.wikipedia.org/wiki/%E7%9B…

直線方程:baike.baidu.com/item/%E7%9B…


my_blog: www.flowers1225.com/

相關文章
相關標籤/搜索