diff --git a/rectangle.go b/rectangle.go deleted file mode 100644 index 5881da2..0000000 --- a/rectangle.go +++ /dev/null @@ -1,284 +0,0 @@ -package pixel - -import ( - "fmt" - "math" -) - -// Rect is a 2D rectangle aligned with the axes of the coordinate system. It is defined by two -// points, Min and Max. -// -// The invariant should hold, that Max's components are greater or equal than Min's components -// respectively. -type Rect struct { - Min, Max Vec -} - -// ZR is a zero rectangle. -var ZR = Rect{Min: ZV, Max: ZV} - -// R returns a new Rect with given the Min and Max coordinates. -// -// Note that the returned rectangle is not automatically normalized. -func R(minX, minY, maxX, maxY float64) Rect { - return Rect{ - Min: Vec{minX, minY}, - Max: Vec{maxX, maxY}, - } -} - -// String returns the string representation of the Rect. -// -// r := pixel.R(100, 50, 200, 300) -// 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.Min.X, r.Min.Y, r.Max.X, r.Max.Y) -} - -// 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: Vec{ - math.Min(r.Min.X, r.Max.X), - math.Min(r.Min.Y, r.Max.Y), - }, - Max: Vec{ - math.Max(r.Min.X, r.Max.X), - math.Max(r.Min.Y, r.Max.Y), - }, - } -} - -// W returns the width of the Rect. -func (r Rect) W() float64 { - return r.Max.X - r.Min.X -} - -// H returns the height of the Rect. -func (r Rect) H() float64 { - return r.Max.Y - r.Min.Y -} - -// Size returns the vector of width and height of the Rect. -func (r Rect) Size() Vec { - return V(r.W(), r.H()) -} - -// Area returns the area of r. If r is not normalized, area may be negative. -func (r Rect) Area() float64 { - return r.W() * r.H() -} - -// Edges will return the four lines which make up the edges of the rectangle. -func (r Rect) Edges() [4]Line { - corners := r.Vertices() - - return [4]Line{ - {A: corners[0], B: corners[1]}, - {A: corners[1], B: corners[2]}, - {A: corners[2], B: corners[3]}, - {A: corners[3], B: corners[0]}, - } -} - -// Anchor is a vector used to define anchors, such as `Center`, `Top`, `TopRight`, etc. -type Anchor Vec - -var ( - Center = Anchor{0.5, 0.5} - Top = Anchor{0.5, 0} - TopRight = Anchor{0, 0} - Right = Anchor{0, 0.5} - BottomRight = Anchor{0, 1} - Bottom = Anchor{0.5, 1} - BottomLeft = Anchor{1, 1} - Left = Anchor{1, 0.5} - TopLeft = Anchor{1, 0} -) - -var anchorStrings map[Anchor]string = map[Anchor]string{ - Center: "center", - Top: "top", - TopRight: "top-right", - Right: "right", - BottomRight: "bottom-right", - Bottom: "bottom", - BottomLeft: "bottom-left", - Left: "left", - TopLeft: "top-left", -} - -// String returns the string representation of an anchor. -func (anchor Anchor) String() string { - return anchorStrings[anchor] -} - -var oppositeAnchors map[Anchor]Anchor = map[Anchor]Anchor{ - Center: Center, - Top: Bottom, - Bottom: Top, - Right: Left, - Left: Right, - TopRight: BottomLeft, - BottomLeft: TopRight, - BottomRight: TopLeft, - TopLeft: BottomRight, -} - -// Opposite returns the opposite position of the anchor (ie. Top -> Bottom; BottomLeft -> TopRight, etc.). -func (anchor Anchor) Opposite() Anchor { - return oppositeAnchors[anchor] -} - -// AnchorPos returns the relative position of the given anchor. -func (r Rect) AnchorPos(anchor Anchor) Vec { - return r.Size().ScaledXY(V(0, 0).Sub(Vec(anchor))) -} - -// AlignedTo returns the rect moved by the given anchor. -func (rect Rect) AlignedTo(anchor Anchor) Rect { - return rect.Moved(rect.AnchorPos(anchor)) -} - -// Center returns the position of the center of the Rect. -// `rect.Center()` is equivalent to `rect.Anchor(pixel.Anchor.Center)` -func (r Rect) Center() Vec { - return Lerp(r.Min, r.Max, 0.5) -} - -// 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.Add(delta), - Max: r.Max.Add(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 resizing a rectangle of zero area and will panic. Use -// ResizedMin in the case of zero area. -func (r Rect) Resized(anchor, size Vec) Rect { - if r.W()*r.H() == 0 { - panic(fmt.Errorf("(%T).Resize: zero area", r)) - } - fraction := Vec{size.X / r.W(), size.Y / r.H()} - return Rect{ - Min: anchor.Add(r.Min.Sub(anchor).ScaledXY(fraction)), - Max: anchor.Add(r.Max.Sub(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.Add(size), - } -} - -// Contains checks whether a vector u is contained within this Rect (including it's borders). -func (r Rect) Contains(u Vec) bool { - return r.Min.X <= u.X && u.X <= r.Max.X && r.Min.Y <= u.Y && u.Y <= r.Max.Y -} - -// Union returns the minimal Rect which covers both r and s. Rects r and s must be normalized. -func (r Rect) Union(s Rect) Rect { - return R( - math.Min(r.Min.X, s.Min.X), - math.Min(r.Min.Y, s.Min.Y), - math.Max(r.Max.X, s.Max.X), - math.Max(r.Max.Y, s.Max.Y), - ) -} - -// Intersect returns the maximal Rect which is covered by both r and s. Rects r and s must be normalized. -// -// If r and s don't overlap, this function returns a zero-rectangle. -func (r Rect) Intersect(s Rect) Rect { - t := R( - math.Max(r.Min.X, s.Min.X), - math.Max(r.Min.Y, s.Min.Y), - math.Min(r.Max.X, s.Max.X), - math.Min(r.Max.Y, s.Max.Y), - ) - if t.Min.X >= t.Max.X || t.Min.Y >= t.Max.Y { - return ZR - } - return t -} - -// Intersects returns whether or not the given Rect intersects at any point with this Rect. -// -// This function is overall about 5x faster than Intersect, so it is better -// to use if you have no need for the returned Rect from Intersect. -func (r Rect) Intersects(s Rect) bool { - return !(s.Max.X <= r.Min.X || - s.Min.X >= r.Max.X || - s.Max.Y <= r.Min.Y || - s.Min.Y >= r.Max.Y) -} - -// IntersectCircle returns a minimal required Vector, such that moving the rect by that vector would stop the Circle -// and the Rect intersecting. This function returns a zero-vector if the Circle and Rect do not overlap, and if only -// the perimeters touch. -// -// This function will return a non-zero vector if: -// - The Rect contains the Circle, partially or fully -// - The Circle contains the Rect, partially of fully -func (r Rect) IntersectCircle(c Circle) Vec { - return c.IntersectRect(r).Scaled(-1) -} - -// IntersectLine will return the shortest Vec such that if the Rect is moved by the Vec returned, the Line and Rect no -// longer intersect. -func (r Rect) IntersectLine(l Line) Vec { - return l.IntersectRect(r).Scaled(-1) -} - -// IntersectionPoints returns all the points where the Rect intersects with the line provided. This can be zero, one or -// two points, depending on the location of the shapes. The points of intersection will be returned in order of -// closest-to-l.A to closest-to-l.B. -func (r Rect) IntersectionPoints(l Line) []Vec { - // Use map keys to ensure unique points - pointMap := make(map[Vec]struct{}) - - for _, edge := range r.Edges() { - if intersect, ok := l.Intersect(edge); ok { - pointMap[intersect] = struct{}{} - } - } - - points := make([]Vec, 0, len(pointMap)) - for point := range pointMap { - points = append(points, point) - } - - // Order the points - if len(points) == 2 { - if points[1].To(l.A).Len() < points[0].To(l.A).Len() { - return []Vec{points[1], points[0]} - } - } - - return points -} - -// Vertices returns a slice of the four corners which make up the rectangle. -func (r Rect) Vertices() [4]Vec { - return [4]Vec{ - r.Min, - V(r.Min.X, r.Max.Y), - r.Max, - V(r.Max.X, r.Min.Y), - } -}