add IM with several methods

This commit is contained in:
faiface 2017-03-10 14:51:55 +01:00
parent dcf70e5a26
commit f93f599d7f
1 changed files with 195 additions and 0 deletions

View File

@ -1,5 +1,10 @@
package pixel
import (
"image/color"
"math"
)
// Sprite is a drawable Picture. It's always anchored by the center of it's Picture.
type Sprite struct {
tri *TrianglesData
@ -59,3 +64,193 @@ func (s *Sprite) Picture() Picture {
func (s *Sprite) Draw(t Target) {
s.d.Draw(t)
}
// IM is an immediate-like-mode shape drawer.
//
// TODO: mode doc
type IM struct {
points []point
opts point
matrix Matrix
mask NRGBA
tri *TrianglesData
d Drawer
tmp []Vec
}
type point struct {
position Vec
color NRGBA
picture Vec
intensity float64
width float64
precision int
endshape EndShape
}
// EndShape specifies the shape of an end of a line or a curve.
type EndShape int
const (
// RoundEndShape is a circular end shape.
RoundEndShape EndShape = iota
// SharpEndShape is a square end shape.
SharpEndShape
)
// NewIM creates a new empty IM. An optional Picture can be used to draw with a Picture.
//
// If you just want to draw primitive shapes, pass nil as the Picture.
func NewIM(pic Picture) *IM {
tri := &TrianglesData{}
im := &IM{
tri: tri,
d: Drawer{Triangles: tri, Picture: pic},
}
im.Precision(64)
im.SetMatrix(ZM)
im.SetColorMask(NRGBA{1, 1, 1, 1})
return im
}
// Clear removes all drawn shapes from the IM. This does not remove Pushed points.
func (im *IM) Clear() {
im.tri.SetLen(0)
im.d.Dirty()
}
// Draw draws all currently drawn shapes inside the IM onto another Target.
func (im *IM) Draw(t Target) {
im.d.Draw(t)
}
// Push adds some points to the IM queue. All Pushed points will have the same properties except for
// the position.
func (im *IM) Push(pts ...Vec) {
point := im.opts
for _, pt := range pts {
point.position = im.matrix.Project(pt)
point.color = im.mask.Mul(im.opts.color)
im.points = append(im.points, point)
}
}
// Color sets the color of the next Pushed points.
func (im *IM) Color(color color.Color) {
im.opts.color = NRGBAModel.Convert(color).(NRGBA)
}
// Picture sets the Picture coordinates of the next Pushed points.
func (im *IM) Picture(pic Vec) {
im.opts.picture = pic
}
// Intensity sets the picture Intensity of the next Pushed points.
func (im *IM) Intensity(in float64) {
im.opts.intensity = in
}
// Width sets the with property of the next Pushed points.
//
// Note that this property does not apply to filled shapes.
func (im *IM) Width(w float64) {
im.opts.width = w
}
// Precision sets the curve/circle drawing precision of the next Pushed points.
//
// It is the number of segments per 360 degrees.
func (im *IM) Precision(p int) {
im.opts.precision = p
if p+1 > len(im.tmp) {
im.tmp = append(im.tmp, make([]Vec, p+1-len(im.tmp))...)
}
if p+1 < len(im.tmp) {
im.tmp = im.tmp[:p+1]
}
}
// EndShape sets the endshape of the next Pushed points.
func (im *IM) EndShape(es EndShape) {
im.opts.endshape = es
}
// SetMatrix sets a Matrix that all further points will be transformed by.
func (im *IM) SetMatrix(m Matrix) {
im.matrix = m
}
// SetColorMask sets a color that all futher point's color will be multiplied by.
func (im *IM) SetColorMask(color color.Color) {
im.mask = NRGBAModel.Convert(color).(NRGBA)
}
// FillConvexPolygon takes all points Pushed into the IM's queue and fills the convex polygon formed
// by them.
//
// It empties the queue after.
func (im *IM) FillConvexPolygon() {
points := im.points
im.points = nil
if len(points) < 3 {
return
}
i := im.tri.Len()
im.tri.SetLen(im.tri.Len() + 3*(len(points)-2))
for j := 1; j+1 < len(points); j++ {
(*im.tri)[i].Position = points[0].position
(*im.tri)[i].Color = points[0].color
(*im.tri)[i].Picture = points[0].picture
(*im.tri)[i].Intensity = points[0].intensity
(*im.tri)[i+1].Position = points[j].position
(*im.tri)[i+1].Color = points[j].color
(*im.tri)[i+1].Picture = points[j].picture
(*im.tri)[i+1].Intensity = points[j].intensity
(*im.tri)[i+2].Position = points[j+1].position
(*im.tri)[i+2].Color = points[j+1].color
(*im.tri)[i+2].Picture = points[j+1].picture
(*im.tri)[i+2].Intensity = points[j+1].intensity
i += 3
}
im.d.Dirty()
}
// FillEllipseArc draws an ellipse arc around each point in the IM's queue. Low and high angles are
// in radians.
//
// It empties the queue after.
func (im *IM) FillEllipseArc(radius Vec, low, high float64) {
points := im.points
im.points = nil
// normalize high
if math.Abs(high-low) > 2*math.Pi {
high = low + math.Mod(high-low, 2*math.Pi)
}
for _, pt := range points {
im.Push(pt.position) // center
num := math.Ceil(math.Abs(high-low) / (2 * math.Pi) * float64(pt.precision))
delta := (high - low) / num
for i := range im.tmp[:int(num)+1] {
angle := low + float64(i)*delta
sin, cos := math.Sincos(angle)
im.tmp[i] = pt.position + V(
radius.X()*cos,
radius.Y()*sin,
)
}
im.Push(im.tmp[:int(num)+1]...)
im.FillConvexPolygon()
}
}