change Rect to have Min, Max instead of Pos, Size

This commit is contained in:
faiface 2017-03-13 23:52:34 +01:00
parent bca582815c
commit 203adc914b
7 changed files with 128 additions and 100 deletions

24
data.go
View File

@ -134,8 +134,8 @@ type PictureData struct {
// MakePictureData creates a zero-initialized PictureData covering the given rectangle.
func MakePictureData(rect Rect) *PictureData {
w := int(math.Ceil(rect.Pos.X()+rect.Size.X())) - int(math.Floor(rect.Pos.X()))
h := int(math.Ceil(rect.Pos.Y()+rect.Size.Y())) - int(math.Floor(rect.Pos.Y()))
w := int(math.Ceil(rect.Max.X())) - int(math.Floor(rect.Min.X()))
h := int(math.Ceil(rect.Max.Y())) - int(math.Floor(rect.Min.Y()))
pd := &PictureData{
Stride: w,
Rect: rect,
@ -206,12 +206,12 @@ func PictureDataFromPicture(pic Picture) *PictureData {
pd := MakePictureData(bounds)
if pic, ok := pic.(PictureColor); ok {
for y := math.Floor(bounds.Pos.Y()); y < bounds.Pos.Y()+bounds.Size.Y(); y++ {
for x := math.Floor(bounds.Pos.X()); x < bounds.Pos.X()+bounds.Size.X(); x++ {
for y := math.Floor(bounds.Min.Y()); y < bounds.Max.Y(); y++ {
for x := math.Floor(bounds.Min.X()); x < bounds.Max.X(); x++ {
// this together with the Floor is a trick to get all of the pixels
at := V(
math.Max(x, bounds.Pos.X()),
math.Max(y, bounds.Pos.Y()),
math.Max(x, bounds.Min.X()),
math.Max(y, bounds.Min.Y()),
)
pd.SetColor(at, pic.Color(at))
}
@ -226,10 +226,10 @@ func PictureDataFromPicture(pic Picture) *PictureData {
// The resulting image.NRGBA's Bounds will be equivalent of the PictureData's Bounds.
func (pd *PictureData) Image() *image.NRGBA {
bounds := image.Rect(
int(math.Floor(pd.Rect.Pos.X())),
int(math.Floor(pd.Rect.Pos.Y())),
int(math.Ceil(pd.Rect.Pos.X()+pd.Rect.Size.X())),
int(math.Ceil(pd.Rect.Pos.Y()+pd.Rect.Size.Y())),
int(math.Floor(pd.Rect.Min.X())),
int(math.Floor(pd.Rect.Min.Y())),
int(math.Ceil(pd.Rect.Max.X())),
int(math.Ceil(pd.Rect.Max.Y())),
)
nrgba := image.NewNRGBA(bounds)
@ -251,7 +251,7 @@ func (pd *PictureData) Image() *image.NRGBA {
}
func (pd *PictureData) offset(at Vec) int {
at -= pd.Rect.Pos.Map(math.Floor)
at -= pd.Rect.Min.Map(math.Floor)
x, y := int(at.X()), int(at.Y())
return y*pd.Stride + x
}
@ -264,7 +264,7 @@ func (pd *PictureData) Bounds() Rect {
// Slice returns a sub-Picture of this PictureData inside the supplied rectangle.
func (pd *PictureData) Slice(r Rect) Picture {
return &PictureData{
Pix: pd.Pix[pd.offset(r.Pos):],
Pix: pd.Pix[pd.offset(r.Min):],
Stride: pd.Stride,
Rect: r,
Orig: pd.Orig,

View File

@ -83,6 +83,11 @@ func (u Vec) Scaled(c float64) Vec {
return u * V(c, 0)
}
// ScaledXY returns the vector u multiplied by vector v component-wise.
func (u Vec) ScaledXY(v Vec) Vec {
return V(u.X()*v.X(), u.Y()*v.Y())
}
// Rotated returns the vector u rotated by the given angle in radians.
func (u Vec) Rotated(angle float64) Vec {
sin, cos := math.Sincos(angle)
@ -117,19 +122,34 @@ func Lerp(a, b Vec, t float64) Vec {
return a.Scaled(1-t) + b.Scaled(t)
}
// Rect is a 2D rectangle aligned with the axes of the coordinate system. It has a position
// and a size.
// Rect is a 2D rectangle aligned with the axes of the coordinate system. It is defined by two
// points, Min and Max.
//
// You can manipulate the position and the size using the usual vector operations.
// The invariant should hold, that Max's components are greater or equal than Min's components
// respectively.
type Rect struct {
Pos, Size Vec
Min, Max Vec
}
// R returns a new Rect with given position (x, y) and size (w, h).
func R(x, y, w, h float64) Rect {
// R returns a new Rect with given the Min and Max coordinates.
func R(minX, minY, maxX, maxY float64) Rect {
return Rect{
Pos: V(x, y),
Size: V(w, h),
Min: V(minX, minY),
Max: V(maxX, maxY),
}.Norm()
}
// Norm returns the Rect in normal form, such that Max is component-wise greater or equal than Min.
func (r Rect) Norm() Rect {
return Rect{
Min: V(
math.Min(r.Min.X(), r.Max.X()),
math.Min(r.Min.Y(), r.Max.Y()),
),
Max: V(
math.Max(r.Min.X(), r.Max.X()),
math.Max(r.Min.Y(), r.Max.Y()),
),
}
}
@ -139,43 +159,63 @@ func R(x, y, w, h float64) Rect {
// r.String() // returns "Rect(100, 50, 200, 300)"
// fmt.Println(r) // Rect(100, 50, 200, 300)
func (r Rect) String() string {
return fmt.Sprintf("Rect(%v, %v, %v, %v)", r.X(), r.Y(), r.W(), r.H())
}
// X returns the x coordinate of the position of the rectangle.
func (r Rect) X() float64 {
return r.Pos.X()
}
// Y returns the y coordinate of the position of the rectangle
func (r Rect) Y() float64 {
return r.Pos.Y()
return fmt.Sprintf("Rect(%v, %v, %v, %v)", r.Min.X(), r.Min.Y(), r.Max.X(), r.Max.Y())
}
// W returns the width of the rectangle.
func (r Rect) W() float64 {
return r.Size.X()
return r.Max.X() - r.Min.X()
}
// H returns the height of the rectangle.
func (r Rect) H() float64 {
return r.Size.Y()
}
// XYWH returns all of the four components of the rectangle in four return values.
func (r Rect) XYWH() (x, y, w, h float64) {
return r.X(), r.Y(), r.W(), r.H()
return r.Max.Y() - r.Min.Y()
}
// Center returns the position of the center of the rectangle.
func (r Rect) Center() Vec {
return r.Pos + r.Size.Scaled(0.5)
return (r.Min + r.Max) / 2
}
// Moved returns the Rect moved (both Min and Max) by the given vector delta.
func (r Rect) Moved(delta Vec) Rect {
return Rect{
Min: r.Min + delta,
Max: r.Max + delta,
}
}
// Resized returns the Rect resized to the given size while keeping the position of the given anchor.
// r.Resized(r.Min, size) // resizes while keeping the position of the lower-left corner
// r.Resized(r.Max, size) // same with the top-right corner
// r.Resized(r.Center(), size) // resizes around the center
// This function does not make sense for size of zero area and will panic. Use ResizeMin in the case
// of zero area.
func (r Rect) Resized(anchor, size Vec) Rect {
if r.W()*r.H() == 0 || size.X()*size.Y() == 0 {
panic(fmt.Errorf("(%T).Resize: zero area", r))
}
fraction := size.ScaledXY(V(1/r.W(), 1/r.H()))
return Rect{
Min: anchor + (r.Min - anchor).ScaledXY(fraction),
Max: anchor + (r.Max - anchor).ScaledXY(fraction),
}
}
// ResizedMin returns the Rect resized to the given size while keeping the position of the Rect's
// Min.
//
// Sizes of zero area are safe here.
func (r Rect) ResizedMin(size Vec) Rect {
return Rect{
Min: r.Min,
Max: r.Min + size,
}
}
// Contains checks whether a vector u is contained within this Rect (including it's borders).
func (r Rect) Contains(u Vec) bool {
min, max := r.Pos, r.Pos+r.Size
return min.X() <= u.X() && u.X() <= max.X() && min.Y() <= u.Y() && u.Y() <= max.Y()
return r.Min.X() <= u.X() && u.X() <= r.Max.X() && r.Min.Y() <= u.Y() && u.Y() <= r.Max.Y()
}
// Matrix is a 3x3 transformation matrix that can be used for all kinds of spacial transforms, such
@ -200,8 +240,8 @@ func (m Matrix) Move(delta Vec) Matrix {
return Matrix(m3)
}
// ScaleXY scales everything around a given point by the scale factor in each axis respectively.
func (m Matrix) ScaleXY(around Vec, scale Vec) Matrix {
// ScaledXY scales everything around a given point by the scale factor in each axis respectively.
func (m Matrix) ScaledXY(around Vec, scale Vec) Matrix {
m3 := mgl64.Mat3(m)
m3 = mgl64.Translate2D((-around).XY()).Mul3(m3)
m3 = mgl64.Scale2D(scale.XY()).Mul3(m3)
@ -209,13 +249,13 @@ func (m Matrix) ScaleXY(around Vec, scale Vec) Matrix {
return Matrix(m3)
}
// Scale scales everything around a given point by the scale factor.
func (m Matrix) Scale(around Vec, scale float64) Matrix {
return m.ScaleXY(around, V(scale, scale))
// Scaled scales everything around a given point by the scale factor.
func (m Matrix) Scaled(around Vec, scale float64) Matrix {
return m.ScaledXY(around, V(scale, scale))
}
// Rotate rotates everything around a given point by the given angle in radians.
func (m Matrix) Rotate(around Vec, angle float64) Matrix {
// Rotated rotates everything around a given point by the given angle in radians.
func (m Matrix) Rotated(around Vec, angle float64) Matrix {
m3 := mgl64.Mat3(m)
m3 = mgl64.Translate2D((-around).XY()).Mul3(m3)
m3 = mgl64.Rotate3DZ(angle).Mul3(m3)

View File

@ -79,10 +79,10 @@ type IM struct {
}
type point struct {
position Vec
color NRGBA
picture Vec
intensity float64
pos Vec
col NRGBA
pic Vec
in float64
width float64
precision int
endshape EndShape
@ -130,25 +130,25 @@ func (im *IM) Draw(t Target) {
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)
point.pos = im.matrix.Project(pt)
point.col = im.mask.Mul(im.opts.col)
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)
im.opts.col = NRGBAModel.Convert(color).(NRGBA)
}
// Picture sets the Picture coordinates of the next Pushed points.
func (im *IM) Picture(pic Vec) {
im.opts.picture = pic
im.opts.pic = pic
}
// Intensity sets the picture Intensity of the next Pushed points.
func (im *IM) Intensity(in float64) {
im.opts.intensity = in
im.opts.in = in
}
// Width sets the with property of the next Pushed points.
@ -200,20 +200,20 @@ func (im *IM) FillConvexPolygon() {
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].Position = points[0].pos
(*im.tri)[i].Color = points[0].col
(*im.tri)[i].Picture = points[0].pic
(*im.tri)[i].Intensity = points[0].in
(*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+1].Position = points[j].pos
(*im.tri)[i+1].Color = points[j].col
(*im.tri)[i+1].Picture = points[j].pic
(*im.tri)[i+1].Intensity = points[j].in
(*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
(*im.tri)[i+2].Position = points[j+1].pos
(*im.tri)[i+2].Color = points[j+1].col
(*im.tri)[i+2].Picture = points[j+1].pic
(*im.tri)[i+2].Intensity = points[j+1].in
i += 3
}
@ -248,14 +248,14 @@ func (im *IM) FillEllipseArc(radius Vec, low, high float64) {
}
for _, pt := range points {
im.Push(pt.position) // center
im.Push(pt.pos) // 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(
im.tmp[i] = pt.pos + V(
radius.X()*cos,
radius.Y()*sin,
)
@ -265,7 +265,3 @@ func (im *IM) FillEllipseArc(radius Vec, low, high float64) {
im.FillConvexPolygon()
}
}
func (im *IM) Line() {
}

View File

@ -105,8 +105,8 @@ func (c *Canvas) MakePicture(p pixel.Picture) pixel.TargetPicture {
for y := 0; y < bh; y++ {
for x := 0; x < bw; x++ {
at := pixel.V(
math.Max(float64(bx+x), bounds.Pos.X()),
math.Max(float64(by+y), bounds.Pos.Y()),
math.Max(float64(bx+x), bounds.Min.X()),
math.Max(float64(by+y), bounds.Min.Y()),
)
color := p.Color(at)
pixels[(y*bw+x)*4+0] = uint8(color.R * 255)
@ -179,7 +179,7 @@ func (c *Canvas) SetBounds(bounds pixel.Rect) {
// preserve old content
if oldF != nil {
relBounds := bounds
relBounds.Pos -= c.orig.borders.Pos
relBounds = relBounds.Moved(-c.orig.borders.Min)
ox, oy, ow, oh := intBounds(relBounds)
oldF.Blit(
c.f,
@ -216,7 +216,7 @@ func (c *Canvas) Smooth() bool {
// must be manually called inside mainthread
func (c *Canvas) setGlhfBounds() {
bounds := c.bounds
bounds.Pos -= c.orig.borders.Pos
bounds.Moved(c.orig.borders.Min)
bx, by, bw, bh := intBounds(bounds)
glhf.Bounds(bx, by, bw, bh)
}
@ -311,8 +311,8 @@ func (ct *canvasTriangles) draw(tex *glhf.Texture, borders, bounds pixel.Rect) {
ct.dst.s.Begin()
ct.dst.s.SetUniformAttr(canvasBounds, mgl32.Vec4{
float32(ct.dst.bounds.X()),
float32(ct.dst.bounds.Y()),
float32(ct.dst.bounds.Min.X()),
float32(ct.dst.bounds.Min.Y()),
float32(ct.dst.bounds.W()),
float32(ct.dst.bounds.H()),
})
@ -327,14 +327,14 @@ func (ct *canvasTriangles) draw(tex *glhf.Texture, borders, bounds pixel.Rect) {
tex.Begin()
ct.dst.s.SetUniformAttr(canvasTexBorders, mgl32.Vec4{
float32(borders.X()),
float32(borders.Y()),
float32(borders.Min.X()),
float32(borders.Min.Y()),
float32(borders.W()),
float32(borders.H()),
})
ct.dst.s.SetUniformAttr(canvasTexBounds, mgl32.Vec4{
float32(bounds.X()),
float32(bounds.Y()),
float32(bounds.Min.X()),
float32(bounds.Min.Y()),
float32(bounds.W()),
float32(bounds.H()),
})

View File

@ -342,8 +342,8 @@ func (w *Window) initInput() {
w.window.SetCursorPosCallback(func(_ *glfw.Window, x, y float64) {
w.currInp.mouse = pixel.V(
x+w.bounds.X(),
(w.bounds.H()-y)+w.bounds.Y(),
x+w.bounds.Min.X(),
(w.bounds.H()-y)+w.bounds.Min.Y(),
)
})

View File

@ -7,9 +7,9 @@ import (
)
func intBounds(bounds pixel.Rect) (x, y, w, h int) {
x0 := int(math.Floor(bounds.Pos.X()))
y0 := int(math.Floor(bounds.Pos.Y()))
x1 := int(math.Ceil(bounds.Pos.X() + bounds.Size.X()))
y1 := int(math.Ceil(bounds.Pos.Y() + bounds.Size.Y()))
x0 := int(math.Floor(bounds.Min.X()))
y0 := int(math.Floor(bounds.Min.Y()))
x1 := int(math.Ceil(bounds.Max.X()))
y1 := int(math.Ceil(bounds.Max.Y()))
return x0, y0, x1 - x0, y1 - y0
}

View File

@ -2,7 +2,6 @@ package pixelgl
import (
"image/color"
"math"
"runtime"
"github.com/faiface/glhf"
@ -147,14 +146,7 @@ func (w *Window) Destroy() {
func (w *Window) Update() {
mainthread.Call(func() {
wi, hi := w.window.GetSize()
w.bounds.Size = pixel.V(float64(wi), float64(hi))
// fractional positions end up covering more pixels with less size
if w.bounds.X() != math.Floor(w.bounds.X()) {
w.bounds.Size -= pixel.V(1, 0)
}
if w.bounds.Y() != math.Floor(w.bounds.Y()) {
w.bounds.Size -= pixel.V(0, 1)
}
w.bounds = w.bounds.ResizedMin(pixel.V(float64(wi), float64(hi)))
})
w.canvas.SetBounds(w.Bounds())