Flip images at 30 degrees?
Asked Answered
N

3

0

In canvas I know that I can flip images horizontally and vertically by doing this:

vc.scale(-1, -1)

But is there some way to flip an image along a 30 degree symmetry line?

Notus answered 28/2, 2017 at 12:9 Comment(1)
google -> canvas rotateJocelynjocelyne
J
1

Yes, you can accomplish this using the rotate() method of canvas :

var ctx = document.getElementById("myCanvas").getContext("2d");
var img = new Image();
img.src = "http://photos.the-scientist.com/articleImages/48000/48607-1-t.jpg";
img.onload = function() {
    ctx.rotate(30 * Math.PI / 180);
    ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
    ctx.scale(1, -1);
    ctx.drawImage(img, -65, 65);
}
<canvas id="myCanvas" width="300" height="300">
Johann answered 28/2, 2017 at 12:53 Comment(0)
V
3

Mirror along line

You can mirror along any line using the following function. It sets the transform so that you can render your scene as normal and it will be mirrored along the line.

function mirrorTransformLine(line){
    var x1 = line.x1;
    var y1 = line.y1;
    var x2 = line.x2;
    var y2 = line.y2;
    if(x1 > x2){  // to save some messing about with signs make the line always from left to right
        x2 = line.x1;
        y2 = line.y1;
        x1 = line.x2;
        y1 = line.y2;
    }
    var x = x2-x1;  // get the vector from line start to end
    var y = y2-y1; 
    var ox = -x1;  // get vector from line start to origin
    var oy = -y1;
    var len = Math.hypot(x,y); // get the length of the line
    var nx = x / len;  // normalise the line
    var ny = y / len;


    // We must find the mirrored origin
    // get the unit distance along the line where the mirrored y axis intercepts
    var u = (ox * x + oy * y)/(y * y + x * x);
    var dx = u * len; // get the x dist of the mirrored origin    
    var dy = Math.hypot(x1 + x * u, y1 + y * u); // get the mirrored y axis distance from line

    // the above code does not account for the direction of the origin. We don't know if its above or below the line
    // we can get the cross product of the mirror line and the vector to the origin. This will give us the sign (direction) to the origin
    dy *=  Math.sign(ox * y - oy * x); // flip the y distance if needed
    // calculate the  the location of the mirrored origin
    var mox = dx * nx - dy * ny + x1;
    var moy = dx * ny + dy * nx + y1;
    

    // Find the angle of the line to the x axis
    // var cross = 1 * ny - 0 * nx; // cross product give the sin of the angle between the line and the x axis
    // As the cross product is with 1,0 we can simplify
    var ang = Math.asin(ny); // with ny the cross product
    
    // now find the mirrored angle which is 2 times the angle to the x axis
    // use that angle to get the new x axis
    var axx = Math.cos(ang*2);
    var axy = Math.sin(ang*2);
    
    // this represents the x axis of the transform
    // you would normally rotate it clockwise 90 for the y axis 
    // to mirror its anticlockwise
    ctx.setTransform(axx,axy,axy,-axx,mox,moy);
}

Thus if you have a line at 30 deg

var line = {
     x1 : 100,
     y1 : 100,
     x2 : 100 + Math.cos((1/6)* Math.PI), // (1/6) *PI is 30 deg
     y2 : 100 + Math.sin((1/6)* Math.PI),
}

// draw the scene
ctx.fillRect(50,50,50,100);
mirrorTransformLine(line); // create the mirror transformation
ctx.fillRect(50,50,50,100); // draw the scene again this time its is mirrored
ctx.setTransform(1,0,0,1,0,0); // restore the transform to default

As a demo.

Use mouse to drag ends of red line to see it create the mirror transform. The scene is draw twice using the same coordinates. I have also clipped to the line so that the mirror does not overlap.

function mirrorTransformLine(line){
    var x1 = line.x1;
    var y1 = line.y1;
    var x2 = line.x2;
    var y2 = line.y2;
    if(x1 > x2){  // to save some messing about with signs make the line always from left to right
        x2 = line.x1;
        y2 = line.y1;
        x1 = line.x2;
        y1 = line.y2;
    }
    var x = x2-x1;  // get the vector from line start to end
    var y = y2-y1; 
    var ox = -x1;  // get vector from line start to origin
    var oy = -y1;
    var len = Math.hypot(x,y); // get the length of the line
    var nx = x / len;  // normalise the line
    var ny = y / len;


    // We must find the mirrored origin
    // get the unit distance along the line where the mirrored y axis intercepts
    var u = (ox * x + oy * y)/(y * y + x * x);
    var dx = u * len; // get the x dist of the mirrored origin    
    var dy = Math.hypot(x1 + x * u, y1 + y * u); // get the mirrored y axis distance from line

    // the above code does not acount for the direction of the origin. We dont know if its above or below the line
    // we can get the cross product of the mirror line and the vector to the origin. This will give us the sign (direction) to the origin
    dy *=  Math.sign(ox * y - oy * x);
    // calculate the  the location of the mirrored origin
    var mox = dx * nx - dy * ny + x1;
    var moy = dx * ny + dy * nx + y1;
    

    // Find the angle of the line to the x axis
    // var cross = 1 * ny - 0 * nx; // cross product give the sin of the angle between the line and the x axis
    // As the cross product is with 1,0 we can simplify
    var ang = Math.asin(ny); // with ny the cross product
    
    // now find the mirrored angle which is 2 time the angle to the x axis
    // use that angle to get the new x axis
    var axx = Math.cos(ang*2);
    var axy = Math.sin(ang*2);
    
    // this represents the x axis of the transform
    // you would normally rotate it clockwise 90 for the y axis 
    // to mirror its anticlockwise
    ctx.setTransform(axx,axy,axy,-axx,mox,moy);
}


function clipToLine(line){
    var x =line.x2-line.x1;  // get the vector from line start to end
    var y =line.y2-line.y1; 

    var len = Math.hypot(x,y); // get the length of the line
    var nx = x / len;  // normalise the line
    var ny = y / len;
    // from 1000 px before start to 1000 px after end create dividing line
    ctx.beginPath();
    ctx.moveTo(line.x1 - nx * 1000, line.y1 - ny * 1000);
    ctx.lineTo(line.x2 + nx * 1000, line.y2 + ny * 1000);
    ctx.lineTo(line.x2 + nx * 1000 - ny * 1000, line.y2 + ny * 1000 + nx * 1000);
    ctx.lineTo(line.x1 - nx * 1000 - ny * 1000, line.y1 - ny * 1000 + nx * 1000);
    ctx.clip();
    
    
}

var line;
var onResize = function(){  // this is called at start
    line = {
        x1 : 10, 
        y1 : canvas.height /2,
        x2 : canvas.width -10,
        y2 : canvas.height /2,
    };
    ctx.font = Math.floor(canvas.height /10) + "px arial";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
}
function drawLine(line,sCol,width){
    ctx.strokeStyle = sCol;
    ctx.lineWidth = width;
    ctx.beginPath();
    ctx.moveTo(line.x1,line.y1);
    ctx.lineTo(line.x2,line.y2);
    ctx.stroke();
}
function drawCircle(x,y,r,fCol,sCol,width){
    ctx.fillStyle = fCol;
    ctx.strokeStyle = sCol;
    ctx.lineWidth = width;
    ctx.beginPath();
    ctx.arc(x,y,r,0,Math.PI*2);
    if(fCol) {ctx.fill()}
    if(sCol) {ctx.stroke()}
    
}
var dragging = 0;
function display() { 
    ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
    ctx.globalAlpha = 1; // reset alpha
    ctx.clearRect(0, 0, w, h);
    var dist = Math.hypot(line.x1 - mouse.x, line.y1 - mouse.y);
    var dist1 = Math.hypot(line.x2 - mouse.x, line.y2 - mouse.y);
    var col1 = "blue";
    var col2 = "blue";
    if(dragging){
        if(dragging === 1){
            line.x1 = mouse.x;
            line.y1 = mouse.y;
            canvas.style.cursor = "move";
        }else{
            line.x2 = mouse.x;
            line.y2 = mouse.y;
            canvas.style.cursor = "move";
        }
        if(mouse.buttonRaw !== 1){
            dragging = 0;
            canvas.style.cursor = "default";
        }
    }else if(dist1 < dist && dist1 < 40){
        col2 = "red";
        canvas.style.cursor = "move";
        if(mouse.buttonRaw === 1){
            dragging = 2;
        }
    }else
    if(dist < dist1 && dist < 40){
        col1 = "red"
        canvas.style.cursor = "move";
        if(mouse.buttonRaw === 1){
            dragging = 1;
        }
    }else{
        canvas.style.cursor = "default";
        
    }
    ctx.save();
    clipToLine(line);
    drawCircle(canvas.width /2, canvas.height / 4 , canvas.height / 5, "Green","Blue",10);
    drawCircle(canvas.width /2, canvas.height * (3/4) , canvas.height / 5, "Blue","Green",10);
    ctx.fillStyle = "black";
    ctx.fillText("Mirror about the line.",canvas.width / 2, canvas.height / 2);
    ctx.restore();
    mirrorTransformLine(line);
    ctx.save();
    clipToLine(line);
    drawCircle(canvas.width /2, canvas.height / 4 , canvas.height / 5, "Green","Blue",10);
    drawCircle(canvas.width /2, canvas.height * (3/4) , canvas.height / 5, "Blue","Green",10);
    ctx.fillStyle = "#444";
    ctx.fillText("Mirror about the line.",canvas.width / 2, canvas.height / 2);
    ctx.restore();

    drawLine(line,"red",4);
    drawCircle(line.x1,line.y1,10,"white",col1,4);
    drawCircle(line.x2,line.y2,10,"white",col2,4);

}









//===========================================================================
// Boilerplat code from here down not part of answer
var w, h, cw, ch, canvas, ctx, mouse, globalTime = 0, firstRun = true;



;(function(){
    const RESIZE_DEBOUNCE_TIME = 100;
    var  createCanvas, resizeCanvas, setGlobals, resizeCount = 0;
    createCanvas = function () {
        var c,
        cs;
        cs = (c = document.createElement("canvas")).style;
        cs.position = "absolute";
        cs.top = cs.left = "0px";
        cs.zIndex = 1000;
        document.body.appendChild(c);
        return c;
    }
    resizeCanvas = function () {
        if (canvas === undefined) {
            canvas = createCanvas();
        }
        canvas.width = innerWidth;
        canvas.height = innerHeight;
        ctx = canvas.getContext("2d");
        if (typeof setGlobals === "function") {
            setGlobals();
        }
        if (typeof onResize === "function") {
            if(firstRun){
                onResize();
                firstRun = false;
            }else{
                resizeCount += 1;
                setTimeout(debounceResize, RESIZE_DEBOUNCE_TIME);
            }
        }
    }
    function debounceResize() {
        resizeCount -= 1;
        if (resizeCount <= 0) {
            onResize();
        }
    }
    setGlobals = function () {
        cw = (w = canvas.width) / 2;
        ch = (h = canvas.height) / 2;
    }
    mouse = (function () {
        function preventDefault(e) {
            e.preventDefault();
        }
        var mouse = {
            x : 0,
            y : 0,
            w : 0,
            alt : false,
            shift : false,
            ctrl : false,
            buttonRaw : 0,
            over : false,
            bm : [1, 2, 4, 6, 5, 3],
            active : false,
            bounds : null,
            crashRecover : null,
            mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
        };
        var m = mouse;
        function mouseMove(e) {
            var t = e.type;
            m.bounds = m.element.getBoundingClientRect();
            m.x = e.pageX - m.bounds.left;
            m.y = e.pageY - m.bounds.top;
            m.alt = e.altKey;
            m.shift = e.shiftKey;
            m.ctrl = e.ctrlKey;
            if (t === "mousedown") {
                m.buttonRaw |= m.bm[e.which - 1];
            } else if (t === "mouseup") {
                m.buttonRaw &= m.bm[e.which + 2];
            } else if (t === "mouseout") {
                m.buttonRaw = 0;
                m.over = false;
            } else if (t === "mouseover") {
                m.over = true;
            } else if (t === "mousewheel") {
                m.w = e.wheelDelta;
            } else if (t === "DOMMouseScroll") {
                m.w = -e.detail;
            }
            if (m.callbacks) {
                m.callbacks.forEach(c => c(e));
            }
            e.preventDefault();
        }
        m.addCallback = function (callback) {
            if (typeof callback === "function") {
                if (m.callbacks === undefined) {
                    m.callbacks = [callback];
                } else {
                    m.callbacks.push(callback);
                }
            }
        }
        m.start = function (element) {
            if (m.element !== undefined) {
                m.removeMouse();
            }
            m.element = element === undefined ? document : element;
            m.mouseEvents.forEach(n => {
                m.element.addEventListener(n, mouseMove);
            });
            m.element.addEventListener("contextmenu", preventDefault, false);
            m.active = true;
        }
        m.remove = function () {
            if (m.element !== undefined) {
                m.mouseEvents.forEach(n => {
                    m.element.removeEventListener(n, mouseMove);
                });
                m.element.removeEventListener("contextmenu", preventDefault);
                m.element = m.callbacks = undefined;
                m.active = false;
            }
        }
        return mouse;
    })();



    function update(timer) { // Main update loop
        if(ctx === undefined){
            return;
        }
        globalTime = timer;
        display(); // call demo code
        requestAnimationFrame(update);
    }
    setTimeout(function(){
        resizeCanvas();
        mouse.start(canvas, true);
        window.addEventListener("resize", resizeCanvas);
        requestAnimationFrame(update);
    },0);
})();
/** SimpleFullCanvasMouse.js end **/
Verse answered 28/2, 2017 at 15:39 Comment(1)
Just what I was looking for. Works great!Susumu
D
1

Rotate image 30 degrees, flip image, rotate -30 degrees.

Dolphin answered 28/2, 2017 at 12:11 Comment(0)
J
1

Yes, you can accomplish this using the rotate() method of canvas :

var ctx = document.getElementById("myCanvas").getContext("2d");
var img = new Image();
img.src = "http://photos.the-scientist.com/articleImages/48000/48607-1-t.jpg";
img.onload = function() {
    ctx.rotate(30 * Math.PI / 180);
    ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
    ctx.scale(1, -1);
    ctx.drawImage(img, -65, 65);
}
<canvas id="myCanvas" width="300" height="300">
Johann answered 28/2, 2017 at 12:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.