Skip to content

Commit

Permalink
feature 优化性能修复bug
Browse files Browse the repository at this point in the history
  • Loading branch information
SimingLiu committed Dec 6, 2024
1 parent 99f2018 commit 991b79d
Show file tree
Hide file tree
Showing 5 changed files with 355 additions and 194 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# rtreego
https://github.com/dhconnelly/rtreego 精度进行了更改,改为float32
https://github.com/dhconnelly/rtreego 精度进行了更改,改为float32。依据版本是主线分支 2024-02-18 版本。此版本对1.2进行了bug修复。而1.2 对1.1进行了性能提升。

98 changes: 55 additions & 43 deletions geom.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
)

// DimError represents a failure due to mismatched dimensions.
//
// 表示由于维度不匹配导致的失败。
type DimError struct {
Expected int
Actual int
Expand All @@ -22,46 +24,60 @@ func (err DimError) Error() string {

// DistError is an improper distance measurement. It implements the error
// and is generated when a distance-related assertion fails.
//
// 表示不正确的距离测量。它实现了 `error` 接口,并在距离相关的断言失败时生成。
type DistError float32

func (err DistError) Error() string {
return "rtreego: improper distance"
}

// Point represents a point in n-dimensional Euclidean space.
//
// 表示 n 维欧几里得空间中的一个点。
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.
//
// 计算 p 和 q 两点之间的欧几里得距离。就是两点间 每个维度上的差值平方和的平方根
func (p Point) dist(q Point) float32 {
if len(p) != len(q) {
panic(DimError{len(p), len(q)})
}
sum := 0.0
sum := float32(0.0)
for i := range p {
dx := p[i] - q[i]
sum += float64(dx * dx)
sum += dx * dx
}
return float32(math.Sqrt(sum))
return float32(math.Sqrt(float64(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) float64 {
if len(p) != len(r.p) {
panic(DimError{len(p), len(r.p)})
}

sum := float32(0.0)
sum := 0.0
for i, pi := range p {
if pi < r.p[i] {
d := pi - r.p[i]
sum += d * d
sum += float64(d) * float64(d)
} else if pi > r.q[i] {
d := pi - r.q[i]
sum += d * d
sum += float64(d) * float64(d)
} else {
sum += 0
}
Expand All @@ -75,7 +91,9 @@ 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 {
//
// 计算从`p`到`r`上各点的最大距离中的最小值。如果`r`是某些几何对象的边界框,那么在`r`中至少有一个对象位于`p`的minMaxDist(p, r)范围内
func (p Point) minMaxDist(r Rect) float64 {
if len(p) != len(r.p) {
panic(DimError{len(p), len(r.p)})
}
Expand All @@ -101,18 +119,18 @@ func (p Point) minMaxDist(r *Rect) float32 {
// This formula can be computed in linear time by precomputing
// S = sum{1<=i<=n}(|pi - rMi|^2).

S := float32(0.0)
S := 0.0
for i := range p {
d := p[i] - rM(i)
S += d * d
S += float64(d) * float64(d)
}

// Compute MinMaxDist using the precomputed S.
min := float32(math.MaxFloat32)
min := math.MaxFloat64
for k := range p {
d1 := p[k] - rM(k)
d2 := p[k] - rm(k)
d := S - d1*d1 + d2*d2
d := S - float64(d1)*float64(d1) + float64(d2)*float64(d2)
if d < min {
min = d
}
Expand All @@ -128,17 +146,19 @@ type Rect struct {
}

// PointCoord returns the coordinate of the point of the rectangle at i
func (r *Rect) PointCoord(i int) float32 {
//
// 返回矩形在索引 i 处的坐标。
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 @@ -152,7 +172,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 @@ -164,8 +184,7 @@ 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) {
r = new(Rect)
func NewRect(p Point, lengths []float32) (r Rect, err error) {
r.p = p
if len(p) != len(lengths) {
err = &DimError{len(p), len(lengths)}
Expand All @@ -183,26 +202,32 @@ func NewRect(p Point, lengths []float32) (r *Rect, err error) {
}

// 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
}

//checking that min and max points is swapping
// check that min and max point coordinates require swapping
copied := false
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 @@ -212,7 +237,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 @@ -230,7 +255,7 @@ func (r *Rect) margin() float32 {
}

// 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 @@ -247,7 +272,7 @@ 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)})
}
Expand All @@ -257,6 +282,7 @@ func (r *Rect) containsRect(r2 *Rect) bool {
// enforced by constructor: a1 <= b1 and a2 <= b2.
// so containment holds if and only if a1 <= a2 <= b2 <= b1
// for every dimension.
// float32精度不够高,精确到第六位就行了
if a1-a2 > 0.000001 || b2-b1 > 0.000001 {
return false
}
Expand All @@ -267,7 +293,7 @@ func (r *Rect) containsRect(r2 *Rect) bool {

// 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 @@ -312,19 +338,18 @@ 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) {
bb = new(Rect)
func boundingBox(r1, r2 Rect) (bb Rect) {
dim := len(r1.p)
bb.p = make([]float32, dim)
bb.q = make([]float32, dim)
Expand All @@ -345,16 +370,3 @@ 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
}
33 changes: 9 additions & 24 deletions geom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,13 @@ func TestNewRectFromPointsWithSwapPoints(t *testing.T) {

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

// 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 := p.dist(rect.p); d > EPS {
t.Errorf("Expected p == rect.")
}
if d := q.dist(rect.p); d > EPS {
if d := q.dist(rect.q); d > EPS {
t.Errorf("Expected q == rect.q")
}
}
Expand Down Expand Up @@ -148,9 +147,9 @@ func TestRectMargin(t *testing.T) {
p := Point{1.0, -2.5, 3.0}
lengths := []float32{2.5, 8.0, 1.5}
rect, _ := NewRect(p, lengths)
size := float32(4*2.5 + 4*8.0 + 4*1.5)
size := 4*2.5 + 4*8.0 + 4*1.5
actual := rect.margin()
if size != actual {
if float32(size) != actual {
t.Errorf("Expected %v.margin() == %v, got %v", rect, size, actual)
}
}
Expand Down Expand Up @@ -336,20 +335,6 @@ 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 @@ -360,16 +345,16 @@ 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 {
if d := p.minDist(r); math.Abs(d-float64(expected)) > EPS {
t.Errorf("Expected %v.minDist(%v) == %v, got %v", p, r, expected, d)
}
}

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}
Expand Down
Loading

0 comments on commit 991b79d

Please sign in to comment.