Search code examples
gosvgrotationimage-rotation

Rotated rectangle is shifted (SVG - rotation matrix)


I use SVG to work with some formulars. When I rotate a rectangle, the rectangle is shifted...

package main

import (
    "bytes"
    "fmt"
    "log"
    "math"
    "os"
)
type Point struct {
    X, Y float64
}

/* With SVG path the points are
    A B
    D C
*/
type Rect struct {
    A Point
    B Point
    C Point
    D Point
}

func (r *Rect) Rotate(a float64) {
    r.A.X = r.A.X * math.Cos(dToR(a)) - r.A.Y * math.Sin(dToR(a)) 
    r.A.Y = r.A.X * math.Sin(dToR(a)) + r.A.Y * math.Cos(dToR(a))

    r.B.X = r.B.X * math.Cos(dToR(a)) - r.B.Y * math.Sin(dToR(a)) 
    r.B.Y = r.B.X * math.Sin(dToR(a)) + r.B.Y * math.Cos(dToR(a))

    r.C.X = r.C.X * math.Cos(dToR(a)) - r.C.Y * math.Sin(dToR(a)) 
    r.C.Y = r.C.X * math.Sin(dToR(a)) + r.C.Y * math.Cos(dToR(a))

    r.D.X = r.D.X * math.Cos(dToR(a)) - r.D.Y * math.Sin(dToR(a)) 
    r.D.Y = r.D.X * math.Sin(dToR(a)) + r.D.Y * math.Cos(dToR(a))
}

// degree to radian
func dToR(deg float64) float64 {
    return deg * (math.Pi / 180.0)
}

// radian to degree
func rToD(rad float64) float64 {
    return rad * (180.0 / math.Pi)
}

func writeSvg(data []byte) error {
    f, err := os.OpenFile("test.svg", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
    if err != nil {
        return err
    }
    defer f.Close()

    _, err = f.Write(data)
    if err != nil {
        return err
    }

    return nil
}


func main() {

    var b bytes.Buffer
    b.WriteString("<svg width=\"600\" height=\"600\" xmlns=\"http://www.w3.org/2000/svg\">")
    b.WriteString("<g stroke=\"black\" stroke-width=\"2.5\" fill=\"none\">")
    b.WriteString("<path d=\"M")
    
    r := &Rect {
        Point{-200, -100},
        Point{200, -100},
        Point{200, 100},
        Point{-200, 100},
    }
    
    moveX := 300.0
    moveY := 300.0
    
    r.Rotate(30)
    
    b.WriteString(fmt.Sprintf("%.14f %.14f L", moveX+r.A.X, moveY+r.A.Y))           
    b.WriteString(fmt.Sprintf("%.14f %.14f ", moveX+r.B.X, moveY+r.B.Y))  
    b.WriteString(fmt.Sprintf("%.14f %.14f ", moveX+r.C.X, moveY+r.C.Y))   
    b.WriteString(fmt.Sprintf("%.14f %.14f Z", moveX+r.D.X, moveY+r.D.Y))

    b.WriteString("\"/>\n")   
    b.WriteString("</g></svg>")


    err := writeSvg(b.Bytes())
    if err != nil {
        log.Fatal(err)
    }
}

The results look like this (black rectangle)

enter image description here

The rectangle is rotated, but angles are not 90 degree ... it's a simple and well documented formula, but i can't find my bug.

(There are other ways in SVG to draw and rotate things, but I work with the formulars.)


Solution

  • In your Rotate function you calculate the new x, y coords incorrectly. You calculate r.A.X based on the current value of r.A.X and r.A.Y and then you calculate r.A.Y based on the new value of r.A.X, and that throws you off.

    You need to know the original x, y values and then calculate the new values based on those.

    rad := dToR(a)
    
    x, y := r.A.X, r.A.Y
    r.A.X = x*math.Cos(rad) - y*math.Sin(rad)
    r.A.Y = x*math.Sin(rad) + y*math.Cos(rad)