...

Source file src/oss.terrastruct.com/d2/lib/geo/segment.go

Documentation: oss.terrastruct.com/d2/lib/geo

     1  package geo
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  )
     7  
     8  type Intersectable interface {
     9  	Intersections(segment Segment) []*Point
    10  }
    11  
    12  type Segment struct {
    13  	Start *Point
    14  	End   *Point
    15  }
    16  
    17  func NewSegment(from, to *Point) *Segment {
    18  	return &Segment{from, to}
    19  }
    20  
    21  func (s Segment) Overlaps(otherS Segment, isHorizontal bool, buffer float64) bool {
    22  	if isHorizontal {
    23  		if math.Min(s.Start.Y, s.End.Y)-math.Max(otherS.Start.Y, otherS.End.Y) >= buffer {
    24  			return false
    25  		}
    26  		if math.Min(otherS.Start.Y, otherS.End.Y)-math.Max(s.Start.Y, s.End.Y) >= buffer {
    27  			return false
    28  		}
    29  		return true
    30  	} else {
    31  		if math.Min(s.Start.X, s.End.X)-math.Max(otherS.Start.X, otherS.End.X) >= buffer {
    32  			return false
    33  		}
    34  		if math.Min(otherS.Start.X, otherS.End.X)-math.Max(s.Start.X, s.End.X) >= buffer {
    35  			return false
    36  		}
    37  		return true
    38  	}
    39  }
    40  
    41  func (segment Segment) Intersects(otherSegment Segment) bool {
    42  	return IntersectionPoint(segment.Start, segment.End, otherSegment.Start, otherSegment.End) != nil
    43  }
    44  
    45  //nolint:unused
    46  func (s Segment) ToString() string {
    47  	return fmt.Sprintf("%v -> %v", s.Start.ToString(), s.End.ToString())
    48  }
    49  
    50  func (segment Segment) Intersections(otherSegment Segment) []*Point {
    51  	point := IntersectionPoint(segment.Start, segment.End, otherSegment.Start, otherSegment.End)
    52  	if point == nil {
    53  		return nil
    54  	}
    55  	return []*Point{point}
    56  }
    57  
    58  // getBounds takes a segment and returns the floor and ceil of where it can shift to
    59  // If there is no floor or ceiling, negative or positive infinity is used, respectively
    60  // The direction is inferred, e.g. b/c the passed in segment is vertical, it's inferred we want horizontal bounds
    61  // buffer says how close the segment can be, on both axes, to other segments given
    62  // .    │              │
    63  // .    │              │
    64  // .    │              │
    65  // .    │              │
    66  // .    │           non-overlap
    67  // .    │
    68  // .    │
    69  // .    │
    70  // .    │     segment
    71  // .    │       │
    72  // .    │       │         ceil
    73  // .    │       │            │
    74  // .            │            │
    75  // . floor      │            │
    76  // .                         │
    77  // .                         │
    78  // .                         │
    79  // .                         │
    80  // NOTE: the assumption is that all segments given are orthogonal
    81  func (segment *Segment) GetBounds(segments []*Segment, buffer float64) (float64, float64) {
    82  	ceil := math.Inf(1)
    83  	floor := math.Inf(-1)
    84  	if segment.Start.X == segment.End.X && segment.Start.Y == segment.End.Y {
    85  		// single point, no segment
    86  		return floor, ceil
    87  	}
    88  	isHorizontal := segment.Start.X == segment.End.X
    89  	for _, otherSegment := range segments {
    90  		if isHorizontal {
    91  			// Exclude segments that don't overlap (non-overlap in above diagram)
    92  			if otherSegment.End.Y < segment.Start.Y-buffer {
    93  				continue
    94  			}
    95  			if otherSegment.Start.Y > segment.End.Y+buffer {
    96  				continue
    97  			}
    98  			if otherSegment.Start.X <= segment.Start.X {
    99  				floor = math.Max(floor, otherSegment.Start.X)
   100  			}
   101  			if otherSegment.Start.X > segment.Start.X {
   102  				ceil = math.Min(ceil, otherSegment.Start.X)
   103  			}
   104  		} else {
   105  			if otherSegment.End.X < segment.Start.X-buffer {
   106  				continue
   107  			}
   108  			if otherSegment.Start.X > segment.End.X+buffer {
   109  				continue
   110  			}
   111  			if otherSegment.Start.Y <= segment.Start.Y {
   112  				floor = math.Max(floor, otherSegment.Start.Y)
   113  			}
   114  			if otherSegment.Start.Y > segment.Start.Y {
   115  				ceil = math.Min(ceil, otherSegment.Start.Y)
   116  			}
   117  		}
   118  	}
   119  	return floor, ceil
   120  }
   121  
   122  func (segment Segment) Length() float64 {
   123  	return EuclideanDistance(segment.Start.X, segment.Start.Y, segment.End.X, segment.End.Y)
   124  }
   125  
   126  func (segment Segment) ToVector() Vector {
   127  	return NewVector(segment.End.X-segment.Start.X, segment.End.Y-segment.Start.Y)
   128  }
   129  

View as plain text