diff --git a/geometry.go b/geometry.go
index 13a574d42f7df8b8fe6f9a0a5c1e1383f5cf7640..02a8b5199016a373956c74d9f5622512c855881b 100644
--- a/geometry.go
+++ b/geometry.go
@@ -49,6 +49,11 @@ func (u Vec) XY() (x, y float64) {
 	return u.X, u.Y
 }
 
+// Normal returns a vector normal to u (rotated by math.pi/2)
+func (u Vec) Normal() Vec {
+	return Vec{X: u.Y, Y: -u.X}
+}
+
 // Add returns the sum of vectors u and v.
 func (u Vec) Add(v Vec) Vec {
 	return Vec{
@@ -65,6 +70,14 @@ func (u Vec) Sub(v Vec) Vec {
 	}
 }
 
+// To returns the vector from vector u to vector v, equivalent to v.Sub(u).
+func (u Vec) To(v Vec) Vec {
+	return Vec{
+		v.X - u.X,
+		v.Y - u.Y,
+	}
+}
+
 // Scaled returns the vector u multiplied by c.
 func (u Vec) Scaled(c float64) Vec {
 	return Vec{u.X * c, u.Y * c}
diff --git a/imdraw/imdraw.go b/imdraw/imdraw.go
index ab72ea9222339219639090af0899eb7ac2ce7546..7f72a4d5b2d993095b42cff68dc95eac4e19658c 100644
--- a/imdraw/imdraw.go
+++ b/imdraw/imdraw.go
@@ -495,12 +495,12 @@ func (imd *IMDraw) outlineEllipseArc(radius pixel.Vec, low, high, thickness floa
 				thick := pixel.V(thickness/2, 0).Rotated(normalLow)
 				imd.pushPt(lowCenter.Add(thick), pt)
 				imd.pushPt(lowCenter.Sub(thick), pt)
-				imd.pushPt(lowCenter.Sub(thick.Rotated(math.Pi/2*orientation)), pt)
+				imd.pushPt(lowCenter.Sub(thick.Normal().Scaled(orientation)), pt)
 				imd.fillPolygon()
 				thick = pixel.V(thickness/2, 0).Rotated(normalHigh)
 				imd.pushPt(highCenter.Add(thick), pt)
 				imd.pushPt(highCenter.Sub(thick), pt)
-				imd.pushPt(highCenter.Add(thick.Rotated(math.Pi/2*orientation)), pt)
+				imd.pushPt(highCenter.Add(thick.Normal().Scaled(orientation)), pt)
 				imd.fillPolygon()
 			case RoundEndShape:
 				imd.pushPt(lowCenter, pt)
@@ -528,7 +528,7 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) {
 
 	// first point
 	j, i := 0, 1
-	ijNormal := points[1].pos.Sub(points[0].pos).Rotated(math.Pi / 2).Unit().Scaled(thickness / 2)
+	ijNormal := points[0].pos.To(points[1].pos).Normal().Unit().Scaled(thickness / 2)
 
 	if !closed {
 		switch points[j].endshape {
@@ -537,7 +537,7 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) {
 		case SharpEndShape:
 			imd.pushPt(points[j].pos.Add(ijNormal), points[j])
 			imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
-			imd.pushPt(points[j].pos.Add(ijNormal.Rotated(math.Pi/2)), points[j])
+			imd.pushPt(points[j].pos.Add(ijNormal.Normal()), points[j])
 			imd.fillPolygon()
 		case RoundEndShape:
 			imd.pushPt(points[j].pos, points[j])
@@ -549,7 +549,6 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) {
 	imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
 
 	// middle points
-	// compute "previous" normal:
 	for i := 0; i < len(points); i++ {
 		j, k := i+1, i+2
 
@@ -565,7 +564,7 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) {
 			k %= len(points)
 		}
 
-		jkNormal := points[k].pos.Sub(points[j].pos).Rotated(math.Pi / 2).Unit().Scaled(thickness / 2)
+		jkNormal := points[j].pos.To(points[k].pos).Normal().Unit().Scaled(thickness / 2)
 
 		orientation := 1.0
 		if ijNormal.Cross(jkNormal) > 0 {
@@ -601,7 +600,7 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) {
 
 	// last point
 	i, j = len(points)-2, len(points)-1
-	ijNormal = points[j].pos.Sub(points[i].pos).Rotated(math.Pi / 2).Unit().Scaled(thickness / 2)
+	ijNormal = points[i].pos.To(points[j].pos).Normal().Unit().Scaled(thickness / 2)
 
 	imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
 	imd.pushPt(points[j].pos.Add(ijNormal), points[j])
@@ -614,7 +613,7 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) {
 		case SharpEndShape:
 			imd.pushPt(points[j].pos.Add(ijNormal), points[j])
 			imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
-			imd.pushPt(points[j].pos.Add(ijNormal.Rotated(-math.Pi/2)), points[j])
+			imd.pushPt(points[j].pos.Add(ijNormal.Normal().Scaled(-1)), points[j])
 			imd.fillPolygon()
 		case RoundEndShape:
 			imd.pushPt(points[j].pos, points[j])