vector: Add Dir

Updates #844
This commit is contained in:
Hajime Hoshi 2021-07-17 16:41:12 +09:00
parent 70ef5e7d5b
commit a9241a45c6
2 changed files with 74 additions and 30 deletions

View File

@ -175,7 +175,7 @@ func drawArc(screen *ebiten.Image, count int) {
theta2 := math.Pi * float64(count) / 180 / 3 theta2 := math.Pi * float64(count) / 180 / 3
path.MoveTo(550, 100) path.MoveTo(550, 100)
path.Arc(550, 100, 50, float32(theta1), float32(theta2)) path.Arc(550, 100, 50, float32(theta1), float32(theta2), vector.Clockwise)
op := &ebiten.DrawTrianglesOptions{ op := &ebiten.DrawTrianglesOptions{
EvenOdd: true, EvenOdd: true,

View File

@ -23,6 +23,14 @@ import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
) )
// Direction represents clockwise or countercolockwise.
type Direction int
const (
Clockwise Direction = iota
CounterClockwise
)
type point struct { type point struct {
x float32 x float32
y float32 y float32
@ -139,6 +147,10 @@ func normalize(x, y float32) (float32, float32) {
return x / len, y / len return x / len, y / len
} }
func cross(x0, y0, x1, y1 float32) float32 {
return x0*y1 - x1*y0
}
// ArcTo adds an arc curve to the path. (x1, y1) is the control point, and (x2, y2) is the destination. // ArcTo adds an arc curve to the path. (x1, y1) is the control point, and (x2, y2) is the destination.
// //
// ArcTo updates the current position to (x2, y2). // ArcTo updates the current position to (x2, y2).
@ -165,21 +177,22 @@ func (p *Path) ArcTo(x1, y1, x2, y2, radius float32) {
ax0 := x1 + dx0*dist ax0 := x1 + dx0*dist
ay0 := y1 + dy0*dist ay0 := y1 + dy0*dist
// (ax1, ay1) is the end of the arc. var cx, cy, a0, a1 float32
ax1 := x1 + dx1*dist var dir Direction
ay1 := y1 + dy1*dist if cross(dx0, dy0, dx1, dy1) >= 0 {
cx = ax0 - dy0*radius
p.LineTo(ax0, ay0) cy = ay0 + dx0*radius
a0 = float32(math.Atan2(float64(-dx0), float64(dy0)))
// Calculate the control points for an approximated Bézier curve. a1 = float32(math.Atan2(float64(dx1), float64(-dy1)))
// See https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/curves/beziers. dir = CounterClockwise
alpha := math.Pi - theta } else {
l := radius * float32(math.Tan(alpha/4)*4/3) cx = ax0 + dy0*radius
cx0 := ax0 + l*(-dx0) cy = ay0 - dx0*radius
cy0 := ay0 + l*(-dy0) a0 = float32(math.Atan2(float64(dx0), float64(-dy0)))
cx1 := ax1 + l*(-dx1) a1 = float32(math.Atan2(float64(-dx1), float64(dy1)))
cy1 := ay1 + l*(-dy1) dir = Clockwise
p.CubicTo(cx0, cy0, cx1, cy1, ax1, ay1) }
p.Arc(cx, cy, radius, a0, a1, dir)
p.LineTo(x2, y2) p.LineTo(x2, y2)
} }
@ -188,28 +201,51 @@ func (p *Path) ArcTo(x1, y1, x2, y2, radius float32) {
// (x, y) is the center of the arc. // (x, y) is the center of the arc.
// //
// Arc updates the current position to the end of the arc. // Arc updates the current position to the end of the arc.
func (p *Path) Arc(x, y, radius, startAngle, endAngle float32) { func (p *Path) Arc(x, y, radius, startAngle, endAngle float32, dir Direction) {
// Adjust the angles. // Adjust the angles.
var da float64
if dir == Clockwise {
for startAngle > endAngle { for startAngle > endAngle {
endAngle += 2 * math.Pi endAngle += 2 * math.Pi
} }
da := float64(endAngle - startAngle) da = float64(endAngle - startAngle)
} else {
for startAngle < endAngle {
startAngle += 2 * math.Pi
}
da = float64(startAngle - endAngle)
}
if da >= 2*math.Pi { if da >= 2*math.Pi {
da = 2 * math.Pi da = 2 * math.Pi
endAngle = startAngle + float32(da) if dir == Clockwise {
endAngle = startAngle + 2*math.Pi
} else {
startAngle = endAngle + 2*math.Pi
}
} }
// If the angle is big, splict this into multiple Arc calls. // If the angle is big, splict this into multiple Arc calls.
if da > math.Pi/2 { if da > math.Pi/2 {
const delta = math.Pi / 3 const delta = math.Pi / 3
a := float64(startAngle) a := float64(startAngle)
if dir == Clockwise {
for { for {
p.Arc(x, y, radius, float32(a), float32(math.Min(a+delta, float64(endAngle)))) p.Arc(x, y, radius, float32(a), float32(math.Min(a+delta, float64(endAngle))), dir)
if a+delta >= float64(endAngle) { if a+delta >= float64(endAngle) {
break break
} }
a += delta a += delta
} }
} else {
for {
p.Arc(x, y, radius, float32(a), float32(math.Max(a-delta, float64(endAngle))), dir)
if a-delta <= float64(endAngle) {
break
}
a -= delta
}
}
return return
} }
@ -225,10 +261,18 @@ func (p *Path) Arc(x, y, radius, startAngle, endAngle float32) {
// Calculate the control points for an approximated Bézier curve. // Calculate the control points for an approximated Bézier curve.
// See https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/curves/beziers. // See https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/curves/beziers.
l := radius * float32(math.Tan(da/4)*4/3) l := radius * float32(math.Tan(da/4)*4/3)
cx0 := x0 + l*float32(-sin0) var cx0, cy0, cx1, cy1 float32
cy0 := y0 + l*float32(cos0) if dir == Clockwise {
cx1 := x1 + l*float32(sin1) cx0 = x0 + l*float32(-sin0)
cy1 := y1 + l*float32(-cos1) cy0 = y0 + l*float32(cos0)
cx1 = x1 + l*float32(sin1)
cy1 = y1 + l*float32(-cos1)
} else {
cx0 = x0 + l*float32(sin0)
cy0 = y0 + l*float32(-cos0)
cx1 = x1 + l*float32(-sin1)
cy1 = y1 + l*float32(cos1)
}
p.CubicTo(cx0, cy0, cx1, cy1, x1, y1) p.CubicTo(cx0, cy0, cx1, cy1, x1, y1)
} }