Improve normal calculations

Soooo. It turns out that the bunch of smallish (~4-5% of runtime)
loads associated with Len(), Unit(), Rotated(), and so on... Were
actually more like 15% or more of computational effort. I first
figured this out by creating:

	func (u Vec) Normal(v Vec) Vec

which gives you a vector normal to u->v. That consumed a lot
of CPU time, and was followed by .Unit().Scaled(imd.thickness / 2),
which consumed a bit more CPU time.

After some poking, and in the interests of avoiding UI cruft,
the final selection is
	func (u Vec) Normal() Vec

This returns the vector rotated 90 degrees, which turns out to
be the most common problem.
This commit is contained in:
Seebs 2017-06-09 21:42:20 -05:00
parent 678da34fc3
commit daedc45ea9
2 changed files with 20 additions and 8 deletions

View File

@ -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}

View File

@ -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])