Skip to content

Commit

Permalink
feat: 上一个版本不符,本次是基于1.10版本的精度修改
Browse files Browse the repository at this point in the history
  • Loading branch information
peifengll committed Nov 7, 2023
1 parent 503c937 commit 99f2018
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 341 deletions.
106 changes: 38 additions & 68 deletions geom.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,31 +31,25 @@ func (err DistError) Error() string {
// Point represents a point in n-dimensional Euclidean space.
type Point []float32

func (p Point) Copy() Point {
result := make(Point, len(p))
copy(result, p)
return result
}

// Dist computes the Euclidean distance between two points p and q.
func (p Point) dist(q Point) float32 {
if len(p) != len(q) {
panic(DimError{len(p), len(q)})
}
sum := float32(0.0)
sum := 0.0
for i := range p {
dx := p[i] - q[i]
sum += dx * dx
sum += float64(dx * dx)
}
return float32(math.Sqrt(float64(sum)))
return float32(math.Sqrt(sum))
}

// minDist computes the square of the distance from a point to a rectangle.
// If the point is contained in the rectangle then the distance is zero.
//
// Implemented per Definition 2 of "Nearest Neighbor Queries" by
// N. Roussopoulos, S. Kelley and F. Vincent, ACM SIGMOD, pages 71-79, 1995.
func (p Point) minDist(r Rect) float32 {
func (p Point) minDist(r *Rect) float32 {
if len(p) != len(r.p) {
panic(DimError{len(p), len(r.p)})
}
Expand All @@ -81,7 +75,7 @@ func (p Point) minDist(r Rect) float32 {
//
// Implemented per Definition 4 of "Nearest Neighbor Queries" by
// N. Roussopoulos, S. Kelley and F. Vincent, ACM SIGMOD, pages 71-79, 1995.
func (p Point) minMaxDist(r Rect) float32 {
func (p Point) minMaxDist(r *Rect) float32 {
if len(p) != len(r.p) {
panic(DimError{len(p), len(r.p)})
}
Expand Down Expand Up @@ -113,7 +107,6 @@ func (p Point) minMaxDist(r Rect) float32 {
S += d * d
}

// todo 这里的精度问题?
// Compute MinMaxDist using the precomputed S.
min := float32(math.MaxFloat32)
for k := range p {
Expand All @@ -134,36 +127,18 @@ type Rect struct {
p, q Point // Enforced by NewRect: p[i] <= q[i] for all i.
}

// 详细输出 rect
func (r Rect) RectDetail() {
if r.p != nil && len(r.p) != 0 {
// 就输出
for i, v := range r.p {
fmt.Printf("p[%d]: %v ", i, v)
}
}
fmt.Println()
if r.q != nil && len(r.q) != 0 {
// 就输出
for i, v := range r.q {
fmt.Printf("q[%d]: %v ", i, v)
}
}
fmt.Println()
}

// PointCoord returns the coordinate of the point of the rectangle at i
func (r Rect) PointCoord(i int) float32 {
func (r *Rect) PointCoord(i int) float32 {
return r.p[i]
}

// LengthsCoord returns the coordinate of the lengths of the rectangle at i
func (r Rect) LengthsCoord(i int) float32 {
func (r *Rect) LengthsCoord(i int) float32 {
return r.q[i] - r.p[i]
}

// Equal returns true if the two rectangles are equal
func (r Rect) Equal(other Rect) bool {
func (r *Rect) Equal(other *Rect) bool {
for i, e := range r.p {
if e != other.p[i] {
return false
Expand All @@ -177,7 +152,7 @@ func (r Rect) Equal(other Rect) bool {
return true
}

func (r Rect) String() string {
func (r *Rect) String() string {
s := make([]string, len(r.p))
for i, a := range r.p {
b := r.q[i]
Expand All @@ -189,55 +164,45 @@ func (r Rect) String() string {
// NewRect constructs and returns a pointer to a Rect given a corner point and
// the lengths of each dimension. The point p should be the most-negative point
// on the rectangle (in every dimension) and every length should be positive.
func NewRect(p Point, lengths []float32) (r Rect, err error) {
func NewRect(p Point, lengths []float32) (r *Rect, err error) {
r = new(Rect)
r.p = p
if len(p) != len(lengths) {
err = &DimError{len(p), len(lengths)}
return
}
r.q = make([]float32, len(p))
//fmt.Println(" test")
for i := range p {
if lengths[i] <= 0 {
err = DistError(lengths[i])
return
}
r.q[i] = p[i] + lengths[i]
//fmt.Printf("q: %v, p:%v, length: %v ", r.q[i], p[i], lengths[i])
}
//fmt.Println(" test")
//fmt.Println("new rect:")
//r.RectDetail()
return
}

// NewRectFromPoints constructs and returns a pointer to a Rect given a corner points.
func NewRectFromPoints(minPoint, maxPoint Point) (r Rect, err error) {
func NewRectFromPoints(minPoint, maxPoint Point) (r *Rect, err error) {
if len(minPoint) != len(maxPoint) {
err = &DimError{len(minPoint), len(maxPoint)}
return
}

// check that min and max point coordinates require swapping
copied := false
//checking that min and max points is swapping
for i, p := range minPoint {
if minPoint[i] > maxPoint[i] {
if !copied {
minPoint = minPoint.Copy()
maxPoint = maxPoint.Copy()
copied = true
}
minPoint[i] = maxPoint[i]
maxPoint[i] = p
}
}

r = Rect{p: minPoint, q: maxPoint}
r = &Rect{p: minPoint, q: maxPoint}
return
}

// Size computes the measure of a rectangle (the product of its side lengths).
func (r Rect) Size() float32 {
func (r *Rect) Size() float32 {
size := float32(1.0)
for i, a := range r.p {
b := r.q[i]
Expand All @@ -247,7 +212,7 @@ func (r Rect) Size() float32 {
}

// margin computes the sum of the edge lengths of a rectangle.
func (r Rect) margin() float32 {
func (r *Rect) margin() float32 {
// The number of edges in an n-dimensional rectangle is n * 2^(n-1)
// (http://en.wikipedia.org/wiki/Hypercube_graph). Thus the number
// of edges of length (ai - bi), where the rectangle is determined
Expand All @@ -261,11 +226,11 @@ func (r Rect) margin() float32 {
b := r.q[i]
sum += b - a
}
return float32(math.Pow(2, float64(dim-1))) * sum
return float32(math.Pow(2, float64(dim-1)) * float64(sum))
}

// containsPoint tests whether p is located inside or on the boundary of r.
func (r Rect) containsPoint(p Point) bool {
func (r *Rect) containsPoint(p Point) bool {
if len(p) != len(r.p) {
panic(DimError{len(r.p), len(p)})
}
Expand All @@ -282,36 +247,27 @@ func (r Rect) containsPoint(p Point) bool {
}

// containsRect tests whether r2 is is located inside r1.
func (r Rect) containsRect(r2 Rect) bool {
func (r *Rect) containsRect(r2 *Rect) bool {
if len(r.p) != len(r2.p) {
panic(DimError{len(r.p), len(r2.p)})
}
//fmt.Printf("rect1: %v \n rect2 %v\n", r, r2)
//fmt.Printf("key:%v\n", r.q[1])
//fmt.Printf("key2:%v\n", r2.q[1])

for i, a1 := range r.p {
b1, a2, b2 := r.q[i], r2.p[i], r2.q[i]
// enforced by constructor: a1 <= b1 and a2 <= b2.
// so containment holds if and only if a1 <= a2 <= b2 <= b1
// for every dimension.
//fmt.Printf("%v %v %v %v i:%d \n", a1, r2.p[i], r.q[i], r2.q[i], i)

// 精度检验
if a1-a2 > 0.000001 || b2-b1 > 0.000001 {
return false
}
// 原本的
//if a1 > a2 || b2 > b1 {
// return false
//}
}

return true
}

// intersect computes the intersection of two rectangles. If no intersection
// exists, the intersection is nil.
func intersect(r1, r2 Rect) bool {
func intersect(r1, r2 *Rect) bool {
dim := len(r1.p)
if len(r2.p) != dim {
panic(DimError{dim, len(r2.p)})
Expand Down Expand Up @@ -356,18 +312,19 @@ func intersect(r1, r2 Rect) bool {
}

// ToRect constructs a rectangle containing p with side lengths 2*tol.
func (p Point) ToRect(tol float32) Rect {
func (p Point) ToRect(tol float32) *Rect {
dim := len(p)
a, b := make([]float32, dim), make([]float32, dim)
for i := range p {
a[i] = p[i] - tol
b[i] = p[i] + tol
}
return Rect{a, b}
return &Rect{a, b}
}

// boundingBox constructs the smallest rectangle containing both r1 and r2.
func boundingBox(r1, r2 Rect) (bb Rect) {
func boundingBox(r1, r2 *Rect) (bb *Rect) {
bb = new(Rect)
dim := len(r1.p)
bb.p = make([]float32, dim)
bb.q = make([]float32, dim)
Expand All @@ -388,3 +345,16 @@ func boundingBox(r1, r2 Rect) (bb Rect) {
}
return
}

// boundingBoxN constructs the smallest rectangle containing all of r...
func boundingBoxN(rects ...*Rect) (bb *Rect) {
if len(rects) == 1 {
bb = rects[0]
return
}
bb = boundingBox(rects[0], rects[1])
for _, rect := range rects[2:] {
bb = boundingBox(bb, rect)
}
return
}
43 changes: 26 additions & 17 deletions geom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,14 @@ func TestNewRectFromPointsWithSwapPoints(t *testing.T) {

rect, err := NewRectFromPoints(q, p)
if err != nil {
t.Errorf("Error on NewRect(%v, %v): %v", q, p, err)
t.Errorf("Error on NewRect(%v, %v): %v", p, q, err)
}

if d := p.dist(rect.p); d > EPS {
t.Errorf("Expected p == rect.")
// we must swap p and q because in function it was swapped
if d := p.dist(rect.q); d > EPS {
t.Errorf("Expected p == rect.p")
}
if d := q.dist(rect.q); d > EPS {
if d := q.dist(rect.p); d > EPS {
t.Errorf("Expected q == rect.q")
}
}
Expand Down Expand Up @@ -150,7 +151,6 @@ func TestRectMargin(t *testing.T) {
size := float32(4*2.5 + 4*8.0 + 4*1.5)
actual := rect.margin()
if size != actual {
t.Errorf("self : Expected %f.margin() == %f, got %f", rect, size, actual)
t.Errorf("Expected %v.margin() == %v, got %v", rect, size, actual)
}
}
Expand Down Expand Up @@ -185,11 +185,7 @@ func TestContainsRect(t *testing.T) {
q := Point{4.1, -1.9, 1.0}
lengths2 := []float32{3.2, 0.6, 3.7}
rect2, _ := NewRect(q, lengths2)
//fmt.Println("rect1")
//rect1.RectDetail()
//fmt.Println("rect2")
//rect2.RectDetail()
//fmt.Println("+==========")

if yes := rect1.containsRect(rect2); !yes {
t.Errorf("Expected %v.containsRect(%v", rect1, rect2)
}
Expand Down Expand Up @@ -298,7 +294,6 @@ func TestToRect(t *testing.T) {
q := Point{3.75, -2.35, 0.05}
d1 := p.dist(rect.p)
d2 := q.dist(rect.q)
//fmt.Printf("d1: %f , d2: %f\n", d1, d2)
if d1 > EPS || d2 > EPS {
t.Errorf("Expected %v.ToRect(%v) == %v, %v, got %v", x, tol, p, q, rect)
}
Expand Down Expand Up @@ -341,6 +336,20 @@ func TestBoundingBoxContains(t *testing.T) {
}
}

func TestBoundingBoxN(t *testing.T) {
rect1, _ := NewRect(Point{0, 0}, []float32{1, 1})
rect2, _ := NewRect(Point{0, 1}, []float32{1, 1})
rect3, _ := NewRect(Point{1, 0}, []float32{1, 1})

exp, _ := NewRect(Point{0, 0}, []float32{2, 2})
bb := boundingBoxN(rect1, rect2, rect3)
d1 := bb.p.dist(exp.p)
d2 := bb.q.dist(exp.q)
if d1 > EPS || d2 > EPS {
t.Errorf("boundingBoxN(%v, %v, %v) != %v, got %v", rect1, rect2, rect3, exp, bb)
}
}

func TestMinDistZero(t *testing.T) {
p := Point{1, 2, 3}
r := p.ToRect(1)
Expand All @@ -351,7 +360,7 @@ func TestMinDistZero(t *testing.T) {

func TestMinDistPositive(t *testing.T) {
p := Point{1, 2, 3}
r := Rect{Point{-1, -4, 7}, Point{2, -2, 9}}
r := &Rect{Point{-1, -4, 7}, Point{2, -2, 9}}
expected := float32((-2-2)*(-2-2) + (7-3)*(7-3))
if d := p.minDist(r); math.Abs(float64(d-expected)) > EPS {
t.Errorf("Expected %v.minDist(%v) == %v, got %v", p, r, expected, d)
Expand All @@ -360,18 +369,18 @@ func TestMinDistPositive(t *testing.T) {

func TestMinMaxdist(t *testing.T) {
p := Point{-3, -2, -1}
r := Rect{Point{0, 0, 0}, Point{1, 2, 3}}
r := &Rect{Point{0, 0, 0}, Point{1, 2, 3}}

// furthest points from p on the faces closest to p in each dimension
q1 := Point{0, 2, 3}
q2 := Point{1, 0, 3}
q3 := Point{1, 2, 0}

// find the closest distance from p to one of these furthest points
d1 := p.dist(q1)
d2 := p.dist(q2)
d3 := p.dist(q3)
expected := math.Min(float64(d1*d1), math.Min(float64(d2*d2), float64(d3*d3)))
d1 := float64(p.dist(q1))
d2 := float64(p.dist(q2))
d3 := float64(p.dist(q3))
expected := math.Min(d1*d1, math.Min(d2*d2, d3*d3))

if d := p.minMaxDist(r); math.Abs(float64(d)-expected) > EPS {
t.Errorf("Expected %v.minMaxDist(%v) == %v, got %v", p, r, expected, d)
Expand Down
Loading

0 comments on commit 99f2018

Please sign in to comment.