fixed circle.Intersect
This commit is contained in:
parent
5072f34b91
commit
ee24eeb67d
51
geometry.go
51
geometry.go
|
@ -345,7 +345,7 @@ func (c Circle) String() string {
|
||||||
return fmt.Sprintf("Circle(%.2f, %s)", c.Radius, c.Center)
|
return fmt.Sprintf("Circle(%.2f, %s)", c.Radius, c.Center)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Norm returns the Circle in normalized form - this is that the radius is set to an absolute version.
|
// Norm returns the Circle in normalized form - this sets the radius to its absolute value.
|
||||||
//
|
//
|
||||||
// c := pixel.C(-10, pixel.ZV)
|
// c := pixel.C(-10, pixel.ZV)
|
||||||
// c.Norm() // returns pixel.Circle{10, pixel.Vec{0, 0}}
|
// c.Norm() // returns pixel.Circle{10, pixel.Vec{0, 0}}
|
||||||
|
@ -374,7 +374,7 @@ func (c Circle) Moved(delta Vec) Circle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resized returns the Circle resized by the given delta.
|
// Resized returns the Circle resized by the given delta. The Circles center is use as the anchor.
|
||||||
//
|
//
|
||||||
// c := pixel.C(10, pixel.ZV)
|
// c := pixel.C(10, pixel.ZV)
|
||||||
// c.Resized(-5) // returns pixel.Circle{5, pixel.Vec{0, 0}}
|
// c.Resized(-5) // returns pixel.Circle{5, pixel.Vec{0, 0}}
|
||||||
|
@ -392,14 +392,26 @@ func (c Circle) Contains(u Vec) bool {
|
||||||
return c.Radius >= toCenter.Len()
|
return c.Radius >= toCenter.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MaxCircle will return the larger circle based on the radius.
|
||||||
|
func MaxCircle(c, d Circle) Circle {
|
||||||
|
if c.Radius < d.Radius {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinCircle will return the smaller circle based on the radius.
|
||||||
|
func MinCircle(c, d Circle) Circle {
|
||||||
|
if c.Radius < d.Radius {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
// Union returns the minimal Circle which covers both `c` and `d`.
|
// Union returns the minimal Circle which covers both `c` and `d`.
|
||||||
func (c Circle) Union(d Circle) Circle {
|
func (c Circle) Union(d Circle) Circle {
|
||||||
biggerC := c
|
biggerC := MaxCircle(c, d)
|
||||||
smallerC := d
|
smallerC := MinCircle(c, d)
|
||||||
if c.Radius < d.Radius {
|
|
||||||
biggerC = d
|
|
||||||
smallerC = c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get distance between centers
|
// Get distance between centers
|
||||||
dist := c.Center.To(d.Center).Len()
|
dist := c.Center.To(d.Center).Len()
|
||||||
|
@ -427,7 +439,28 @@ func (c Circle) Union(d Circle) Circle {
|
||||||
// If `c` and `d` don't overlap, this function returns a zero-sized circle at the centerpoint between the two Circle's
|
// If `c` and `d` don't overlap, this function returns a zero-sized circle at the centerpoint between the two Circle's
|
||||||
// centers.
|
// centers.
|
||||||
func (c Circle) Intersect(d Circle) Circle {
|
func (c Circle) Intersect(d Circle) Circle {
|
||||||
center := Lerp(c.Center, d.Center, 0.5)
|
// Check if one of the circles encompasses the other; if so, return that one
|
||||||
|
biggerC := MaxCircle(c, d)
|
||||||
|
smallerC := MinCircle(c, d)
|
||||||
|
|
||||||
|
if biggerC.Radius >= biggerC.Center.To(smallerC.Center).Len()+smallerC.Radius {
|
||||||
|
return biggerC
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the midpoint between the two radii
|
||||||
|
// Distance between centers
|
||||||
|
dist := c.Center.To(d.Center).Len()
|
||||||
|
// Difference between radii
|
||||||
|
diff := dist - (c.Radius + d.Radius)
|
||||||
|
// Distance from c.Center to the weighted midpoint
|
||||||
|
distToMidpoint := c.Radius + 0.5*diff
|
||||||
|
// Weighted midpoint
|
||||||
|
center := Lerp(c.Center, d.Center, distToMidpoint/dist)
|
||||||
|
|
||||||
|
// No need to calculate radius if the circles do not overlap
|
||||||
|
if c.Center.To(d.Center).Len() >= c.Radius+d.Radius {
|
||||||
|
return C(0, center)
|
||||||
|
}
|
||||||
|
|
||||||
radius := math.Min(0, c.Center.To(d.Center).Len()-(c.Radius+d.Radius))
|
radius := math.Min(0, c.Center.To(d.Center).Len()-(c.Radius+d.Radius))
|
||||||
|
|
||||||
|
|
|
@ -545,6 +545,24 @@ func TestCircle_Intersect(t *testing.T) {
|
||||||
args: args{d: pixel.C(1, pixel.V(3, 3))},
|
args: args{d: pixel.C(1, pixel.V(3, 3))},
|
||||||
want: pixel.C(0, pixel.V(1.5, 1.5)),
|
want: pixel.C(0, pixel.V(1.5, 1.5)),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Circle.Intersect(): first circle encompassing second",
|
||||||
|
fields: fields{radius: 10, center: pixel.V(0, 0)},
|
||||||
|
args: args{d: pixel.C(1, pixel.V(3, 3))},
|
||||||
|
want: pixel.C(10, pixel.V(0, 0)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Circle.Intersect(): second circle encompassing first",
|
||||||
|
fields: fields{radius: 1, center: pixel.V(-1, -4)},
|
||||||
|
args: args{d: pixel.C(10, pixel.V(0, 0))},
|
||||||
|
want: pixel.C(10, pixel.V(0, 0)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Circle.Intersect(): matching circles",
|
||||||
|
fields: fields{radius: 1, center: pixel.V(0, 0)},
|
||||||
|
args: args{d: pixel.C(1, pixel.V(0, 0))},
|
||||||
|
want: pixel.C(1, pixel.V(0, 0)),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
@ -558,3 +576,79 @@ func TestCircle_Intersect(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMaxCircle(t *testing.T) {
|
||||||
|
bigCircle := pixel.C(10, pixel.ZV)
|
||||||
|
smallCircle := pixel.C(1, pixel.ZV)
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
c pixel.Circle
|
||||||
|
d pixel.Circle
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want pixel.Circle
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "MaxCircle(): first bigger",
|
||||||
|
args: args{c: bigCircle, d: smallCircle},
|
||||||
|
want: bigCircle,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MaxCircle(): first smaller",
|
||||||
|
args: args{c: smallCircle, d: bigCircle},
|
||||||
|
want: bigCircle,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MaxCircle(): both same size",
|
||||||
|
args: args{c: smallCircle, d: smallCircle},
|
||||||
|
want: smallCircle,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := pixel.MaxCircle(tt.args.c, tt.args.d); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("MaxCircle() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMinCircle(t *testing.T) {
|
||||||
|
bigCircle := pixel.C(10, pixel.ZV)
|
||||||
|
smallCircle := pixel.C(1, pixel.ZV)
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
c pixel.Circle
|
||||||
|
d pixel.Circle
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want pixel.Circle
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "MinCircle(): first bigger",
|
||||||
|
args: args{c: bigCircle, d: smallCircle},
|
||||||
|
want: smallCircle,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MinCircle(): first smaller",
|
||||||
|
args: args{c: smallCircle, d: bigCircle},
|
||||||
|
want: smallCircle,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MinCircle(): both same size",
|
||||||
|
args: args{c: smallCircle, d: smallCircle},
|
||||||
|
want: smallCircle,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := pixel.MinCircle(tt.args.c, tt.args.d); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("MinCircle() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue