func drawMoon(canvas gxui.Canvas, center math.Point, radius float32) { c := 40 p := make(gxui.Polygon, c*2) for i := 0; i < c; i++ { frac := float32(i) / float32(c) α := math.Lerpf(math.Pi*1.2, math.Pi*-0.2, frac) p[i] = gxui.PolygonVertex{ Position: math.Point{ X: center.X + int(radius*math.Sinf(α)), Y: center.Y + int(radius*math.Cosf(α)), }, RoundedRadius: 0, } } for i := 0; i < c; i++ { frac := float32(i) / float32(c) α := math.Lerpf(math.Pi*-0.2, math.Pi*1.2, frac) r := math.Lerpf(radius, radius*0.5, math.Sinf(frac*math.Pi)) p[i+c] = gxui.PolygonVertex{ Position: math.Point{ X: center.X + int(r*math.Sinf(α)), Y: center.Y + int(r*math.Cosf(α)), }, RoundedRadius: 0, } } canvas.DrawPolygon(p, gxui.CreatePen(3, gxui.Gray80), gxui.CreateBrush(gxui.Gray40)) }
func buildMoon(theme gxui.Theme, center math.Point, radius float32) gxui.Image { c := 40 p := make(gxui.Polygon, c*2) for i := 0; i < c; i++ { frac := float32(i) / float32(c) α := math.Lerpf(math.Pi*1.2, math.Pi*-0.2, frac) p[i] = gxui.PolygonVertex{ Position: math.Point{ X: center.X + int(radius*math.Sinf(α)), Y: center.Y + int(radius*math.Cosf(α)), }, RoundedRadius: 0, } } for i := 0; i < c; i++ { frac := float32(i) / float32(c) α := math.Lerpf(math.Pi*-0.2, math.Pi*1.2, frac) r := math.Lerpf(radius, radius*0.5, math.Sinf(frac*math.Pi)) p[i+c] = gxui.PolygonVertex{ Position: math.Point{ X: center.X + int(r*math.Sinf(α)), Y: center.Y + int(r*math.Cosf(α)), }, RoundedRadius: 0, } } image := theme.CreateImage() image.SetPolygon(p, gxui.CreatePen(3, gxui.Gray80), gxui.CreateBrush(gxui.Gray40)) return image }
func segment(penWidth, r float32, a, b, c math.Vec2, aIsLast bool, vsEdgePos []float32, fillEdge []math.Vec2) ([]float32, []math.Vec2) { ba, ca := a.Sub(b), a.Sub(c) baLen, caLen := ba.Len(), ca.Len() baDir, caDir := ba.DivS(baLen), ca.DivS(caLen) dp := baDir.Dot(caDir) if dp < -0.99999 { // Straight lines cause DBZs, special case inner := a.Sub(caDir.Tangent().MulS(penWidth)) vsEdgePos = appendVec2(vsEdgePos, a, inner) if fillEdge != nil /*&& i != 0*/ { fillEdge = append(fillEdge, inner) } return vsEdgePos, fillEdge } α := math.Acosf(dp) / 2 // ╔═══════════════════════════╦════════════════╗ // ║ ║ ║ // ║ A ║ ║ // ║ ╱:╲ ║ ║ // ║ ╱α:α╲ ║ A ║ // ║ ╱ : ╲ ║ |╲ ║ // ║ ╱ . d . ╲ ║ |α╲ ║ // ║ . : . ║ | ╲ ║ // ║ .P : Q. ║ | ╲ ║ // ║ ╱ X ╲ ║ | ╲ ║ // ║ ╱ . ┊ . ╲ ║ | ╲ ║ // ║ ╱ . r . ╲ ║ | ╲ ║ // ║ ╱ . ┊ . ╲ ║ |┐ β╲ ║ // ║ B ┊ C ║ P————————X ║ // ║ ║ ║ // ║ ^ ║ ║ // ║ ┊v ║ ║ // ║ ┊ u ║ ║ // ║ ┊—————> ║ ║ // ║ ║ ║ // ╚═══════════════════════════╩════════════════╝ v := baDir.Add(caDir).Normalize() u := v.Tangent() // // cos(2 • α) = dp // // cos⁻¹(dp) // α = ─────────── // 2 // // r // sin(α) = ─── // d // // r // d = ────── // sin(α) // d := r / math.Sinf(α) // X cannot be futher than half way along ab or ac dMax := math.Minf(baLen, caLen) / (2 * math.Cosf(α)) if d > dMax { // Adjust d and r to compensate d = dMax r = d * math.Sinf(α) } x := a.Sub(v.MulS(d)) convex := baDir.Tangent().Dot(caDir) <= 0 w := penWidth β := math.Pi/2 - α // Special case for convex vertices where the pen width is greater than // the rounding. Without dealing with this, we'd end up with the inner // vertices overlapping. Instead use a point calculated much the same as // x, but using the pen width. useFixedInnerPoint := convex && w > r fixedInnerPoint := a.Sub(v.MulS(math.Minf(w/math.Sinf(α), dMax))) // Concave vertices behave much the same as convex, but we have to flip // β as the sweep is reversed and w as we're extruding. if !convex { w, β = -w, -β } steps := 1 + int(d*α) if aIsLast { // No curvy edge required for the last vertex. // This is already done by the first vertex. steps = 1 } for j := 0; j < steps; j++ { γ := float32(0) if steps > 1 { γ = math.Lerpf(-β, β, float32(j)/float32(steps-1)) } dir := v.MulS(math.Cosf(γ)).Add(u.MulS(math.Sinf(γ))) va := x.Add(dir.MulS(r)) vb := va.Sub(dir.MulS(w)) if useFixedInnerPoint { vb = fixedInnerPoint } vsEdgePos = appendVec2(vsEdgePos, va, vb) if fillEdge != nil { fillEdge = append(fillEdge, vb) } } return vsEdgePos, fillEdge }