github.com/df-mc/dragonfly@v0.9.13/server/block/cube/trace/trace.go (about)

     1  package trace
     2  
     3  import (
     4  	"github.com/df-mc/dragonfly/server/block/cube"
     5  	"github.com/go-gl/mathgl/mgl64"
     6  	"math"
     7  )
     8  
     9  // TraverseBlocks performs a ray trace between the start and end coordinates.
    10  // A function 'f' is passed which is called for each voxel, if f returns false, the function will return.
    11  // TraverseBlocks panics if the start and end positions are the same.
    12  func TraverseBlocks(start, end mgl64.Vec3, f func(pos cube.Pos) (con bool)) {
    13  	dir := end.Sub(start)
    14  	if mgl64.FloatEqual(dir.LenSqr(), 0) {
    15  		panic("start and end points are the same, giving a zero direction vector")
    16  	}
    17  	dir = dir.Normalize()
    18  
    19  	b := cube.PosFromVec3(start)
    20  
    21  	step := signVec3(dir)
    22  	stepX, stepY, stepZ := int(step[0]), int(step[1]), int(step[2])
    23  	max := boundaryVec3(start, dir)
    24  
    25  	delta := safeDivideVec3(step, dir)
    26  
    27  	r := start.Sub(end).Len()
    28  	for {
    29  		if !f(b) {
    30  			return
    31  		}
    32  
    33  		if max[0] < max[1] && max[0] < max[2] {
    34  			if max[0] > r {
    35  				return
    36  			}
    37  			b[0] += stepX
    38  			max[0] += delta[0]
    39  		} else if max[1] < max[2] {
    40  			if max[1] > r {
    41  				return
    42  			}
    43  			b[1] += stepY
    44  			max[1] += delta[1]
    45  		} else {
    46  			if max[2] > r {
    47  				return
    48  			}
    49  			b[2] += stepZ
    50  			max[2] += delta[2]
    51  		}
    52  	}
    53  }
    54  
    55  // safeDivideVec3 ...
    56  func safeDivideVec3(dividend, divisor mgl64.Vec3) mgl64.Vec3 {
    57  	return mgl64.Vec3{
    58  		safeDivide(dividend[0], divisor[0]),
    59  		safeDivide(dividend[1], divisor[1]),
    60  		safeDivide(dividend[2], divisor[2]),
    61  	}
    62  }
    63  
    64  // safeDivide divides the dividend by the divisor, but if the divisor is 0, it returns 0.
    65  func safeDivide(dividend, divisor float64) float64 {
    66  	if divisor == 0.0 {
    67  		return 0.0
    68  	}
    69  	return dividend / divisor
    70  }
    71  
    72  // boundaryVec3 ...
    73  func boundaryVec3(v1, v2 mgl64.Vec3) mgl64.Vec3 {
    74  	return mgl64.Vec3{boundary(v1[0], v2[0]), boundary(v1[1], v2[1]), boundary(v1[2], v2[2])}
    75  }
    76  
    77  // boundary returns the distance that must be travelled on an axis from the start point with the direction vector
    78  // component to cross a block boundary.
    79  func boundary(start, dir float64) float64 {
    80  	if dir == 0.0 {
    81  		return math.Inf(1)
    82  	}
    83  
    84  	if dir < 0.0 {
    85  		start, dir = -start, -dir
    86  		if math.Floor(start) == start {
    87  			return 0.0
    88  		}
    89  	}
    90  
    91  	return (1 - (start - math.Floor(start))) / dir
    92  }
    93  
    94  // signVec3 ...
    95  func signVec3(v1 mgl64.Vec3) mgl64.Vec3 {
    96  	return mgl64.Vec3{sign(v1[0]), sign(v1[1]), sign(v1[2])}
    97  }
    98  
    99  // sign ...
   100  func sign(f float64) float64 {
   101  	switch {
   102  	case f > 0.0:
   103  		return 1.0
   104  	case f < 0.0:
   105  		return -1.0
   106  	}
   107  	return 0.0
   108  }