Ejemplo n.º 1
0
//
// Sample a NURBS curve at 3 points, facilitating adaptive sampling
//
// **params**
// + NurbsCurveData object
// + start parameter for sampling
// + end parameter for sampling
// + whether to prefix the point with the parameter
//
// **returns**
// + an array of dim + 1 length where the first element is the param where it was sampled and the remaining the pt
//
func (this *NurbsCurve) adaptiveSampleRange(start, end, tol float64, includeU bool) []CurvePoint {
	// sample curve at three pts
	p1, p3 := this.Point(start), this.Point(end)
	t := 0.5 + 0.2*rand.Float64()
	mid := start + (end-start)*t
	p2 := this.Point(mid)

	// if the two end control points are coincident, the three point test will always return 0, let's split the curve
	diff := vec3.Sub(&p1, &p3)
	diff2 := vec3.Sub(&p1, &p2)

	// the first condition checks if the curve makes up a loop, if so, we will need to continue evaluation
	if (vec3.Dot(&diff, &diff) < tol && vec3.Dot(&diff2, &diff2) > tol) || !threePointsAreCollinear(&p1, &p2, &p3, tol) {

		// get the exact middle
		exact_mid := start + (end-start)*0.5

		// recurse on the two halves
		left_pts := this.adaptiveSampleRange(start, exact_mid, tol, includeU)
		right_pts := this.adaptiveSampleRange(exact_mid, end, tol, includeU)

		// concatenate the two
		leftEnd := len(left_pts) - 1
		return append(left_pts[:leftEnd:leftEnd], right_pts...)

	} else {
		return []CurvePoint{{start, p1}, {end, p3}}
	}
}
Ejemplo n.º 2
0
// Find the closest point on a ray
//
// **params**
// + point to project
// + origin for ray
// + direction of ray 1, assumed normalized
//
// **returns**
// + pt
func (this Ray) ClosestPoint(pt vec3.T) vec3.T {
	o2pt := vec3.Sub(&pt, &this.Origin)
	do2ptr := vec3.Dot(&o2pt, &this.Dir)
	dirScaled := this.Dir.Scaled(do2ptr)
	proj := vec3.Add(&this.Origin, &dirScaled)

	return proj
}
Ejemplo n.º 3
0
// Vec3Diff returns the rotation quaternion between two vectors.
func Vec3Diff(a, b *vec3.T) T {
	cr := vec3.Cross(a, b)
	sr := math.Sqrt(2 * (1 + vec3.Dot(a, b)))
	oosr := 1 / sr

	q := T{cr[0] * oosr, cr[1] * oosr, cr[2] * oosr, sr * 0.5}
	return q.Normalized()
}
Ejemplo n.º 4
0
func (this Triangle) Plane() Plane {
	v0, v1, v2 := this[0], this[1], this[2]
	normal := vec3.Cross(v1.Sub(&v0), (v2.Sub(&v0)))

	invNormal := normal.Scaled(-1)
	offset := vec3.Dot(&invNormal, &v0)

	return Plane{normal, offset}
}
Ejemplo n.º 5
0
// Determine if three points form a straight line within a given tolerance for their 2 * squared area
//
//          * p2
//         / \
//        /   \
//       /     \
//      /       \
//     * p1 ---- * p3
//
// The area metric is 2 * the squared norm of the cross product of two edges, requiring no square roots and no divisions
//
// **params**
// + p1
// + p2
// + p3
// + The tolerance
//
// **returns**
// + Whether the triangle passes the test
//
func threePointsAreCollinear(p1, p2, p3 *vec3.T, tol float64) bool {
	// find the area of the triangle without using a square root
	p2mp1 := vec3.Sub(p2, p1)
	p3mp1 := vec3.Sub(p3, p1)
	norm := vec3.Cross(&p2mp1, &p3mp1)
	area := vec3.Dot(&norm, &norm)

	return area < tol
}
Ejemplo n.º 6
0
func (this Line) SameSide(p0, p1 vec3.T) bool {
	lineVec := vec3.Sub(&this[1], &this[0])
	p0Vec := vec3.Sub(&p0, &this[0])
	p1Vec := vec3.Sub(&p1, &this[0])

	cp0 := vec3.Cross(&p0Vec, &lineVec)
	cp1 := vec3.Cross(&p1Vec, &lineVec)

	return vec3.Dot(&cp0, &cp1) >= 0
}
Ejemplo n.º 7
0
func (this Plane) IntersectLine(line Line) *vec3.T {
	lineVec := vec3.Sub(&line[1], &line[0])
	invLineVec := vec3.Sub(&line[0], &line[1])

	denom := vec3.Dot(&this.Normal, &invLineVec)
	if math.Abs(denom) < epsilon {
		// Line is parallel to plane
		return nil
	}

	numer := vec3.Dot(&this.Normal, &line[0]) + this.Offset
	t := numer / denom
	if t < 0 || t > 1 {
		return nil
	}

	lineVec.Scale(t)
	intersectPt := vec3.Add(&lineVec, &line[0])
	return &intersectPt
}
Ejemplo n.º 8
0
func distToSegment(a, b, c *vec3.T) float64 {
	// check if ac is zero length
	acv := vec3.Sub(c, a)
	acl := acv.Length()

	// subtract b from a
	var bma = vec3.Sub(b, a)

	if acl < Tolerance {
		return bma.Length()
	}

	// normalize acv
	acv.Normalize()

	// project b - a to acv = p
	p := vec3.Dot(&bma, &acv)

	// multiply ac by d = acd
	acv.Scale(p).Add(a)

	return vec3.Distance(&acv, b)
}
Ejemplo n.º 9
0
// Find the closest point on a segment
//
// **params**
// + point to project
// + first point of segment
// + second point of segment
// + first param of segment
// + second param of segment
//
// **returns**
// + *Object* with u and pt properties
func segmentClosestPoint(pt, segpt0, segpt1 *vec3.T, u0, u1 float64) CurvePoint {
	dif := vec3.Sub(segpt1, segpt0)
	l := dif.Length()

	if l < Epsilon {
		return CurvePoint{u0, *segpt0}
	}

	o := segpt0
	r := dif.Normalize()
	o2pt := vec3.Sub(pt, o)
	do2ptr := vec3.Dot(&o2pt, r)

	if do2ptr < 0 {
		return CurvePoint{u0, *segpt0}
	} else if do2ptr > l {
		return CurvePoint{u1, *segpt1}
	}

	return CurvePoint{
		u0 + (u1-u0)*do2ptr/l,
		vec3.Add(o, r.Scale(do2ptr)),
	}
}
Ejemplo n.º 10
0
func (this Plane) TriangleCrosses(tri Triangle) bool {
	sign1 := sign(vec3.Dot(&this.Normal, &tri[0]) + this.Offset)
	sign2 := sign(vec3.Dot(&this.Normal, &tri[1]) + this.Offset)
	sign3 := sign(vec3.Dot(&this.Normal, &tri[2]) + this.Offset)
	return !((sign1 == sign2 && sign2 == sign3) && sign1 != 0)
}
Ejemplo n.º 11
0
func (this *NurbsCurve) ClosestParam(p vec3.T) float64 {
	//  We want to solve:
	//
	//   C'(u) * ( C(u) - P ) = 0 = f(u)
	//
	//  C(u) is the curve, p is the point, * is a dot product
	//
	// We'll use newton's method:
	//
	// 	 u* = u - f / f'
	//
	// We use the product rule in order to form the derivative, f':
	//
	//	f' = C"(u) * ( C(u) - p ) + C'(u) * C'(u)
	//
	// What is the conversion criteria? (Piegl & Tiller suggest)
	//
	// |C(u) - p| < e1
	//
	// |C'(u)*(C(u) - P)|
	// ------------------  < e2
	// |C'(u)| |C(u) - P|
	//
	//  1) first check 2 & 3
	// 	2) if at least one of these is not, compute new value, otherwise halt
	// 	3) ensure the parameter stays within range
	// 			* if not closed, don't allow outside of range a-b
	// 			* if closed (e.g. circle), allow to move back to beginning
	//  4)  if |(u* - u)C'(u)| < e1, halt
	//

	min := math.MaxFloat64
	var u float64

	pts := this.regularSample(len(this.controlPoints) * this.degree)

	for i := 0; i < len(pts)-1; i++ {
		u0, u1 := pts[i].U, pts[i+1].U

		p0 := pts[i].Pt
		p1 := pts[i+1].Pt

		proj := segmentClosestPoint(&p, &p0, &p1, u0, u1)
		dv := vec3.Sub(&p, &proj.Pt)
		d := dv.Length()

		if d < min {
			min = d
			u = proj.U
		}
	}

	maxits := 5
	var i int
	var e []vec3.T
	eps1, eps2 := 0.0001, 0.0005
	var dif vec3.T
	minu, maxu := this.knots[0], this.knots[len(this.knots)-1]

	firstCtrlPt := this.controlPoints[0].Dehomogenized()
	lastCtrlPt := this.controlPoints[len(this.controlPoints)-1].Dehomogenized()
	closed := vec3.SquareDistance(&firstCtrlPt, &lastCtrlPt) < Epsilon

	cu := u

	f := func(u float64) []vec3.T {
		return this.Derivatives(u, 2)
	}

	n := func(u float64, e []vec3.T, d vec3.T) float64 {
		//   C'(u) * ( C(u) - P ) = 0 = f(u)
		f := vec3.Dot(&e[1], &d)

		//	f' = C"(u) * ( C(u) - p ) + C'(u) * C'(u)
		s0 := vec3.Dot(&e[2], &d)
		s1 := vec3.Dot(&e[1], &e[1])
		df := s0 + s1

		return u - f/df
	}

	for i < maxits {
		e = f(cu)
		dif = vec3.Sub(&e[0], &p)

		// |C(u) - p| < e1
		c1v := dif.Length()

		// C'(u) * (C(u) - P)
		// ------------------ < e2
		// |C'(u)| |C(u) - P|
		c2n := vec3.Dot(&e[1], &dif)
		c2d := e[1].Length() * c1v

		c2v := c2n / c2d

		c1 := c1v < eps1
		c2 := math.Abs(c2v) < eps2

		// if both tolerances are met
		if c1 && c2 {
			return cu
		}

		ct := n(cu, e, dif)

		// are we outside of the bounds of the curve?
		if ct < minu {
			if closed {
				ct = maxu - (ct - minu)
			} else {
				ct = minu
			}
		} else if ct > maxu {
			if closed {
				ct = minu + (ct - maxu)
			} else {
				ct = maxu
			}
		}

		// will our next step force us out of the curve?
		c3vv := e[1].Scaled(ct - cu)
		c3v := c3vv.Length()

		if c3v < eps1 {
			return cu
		}

		cu = ct
		i++

	}

	return cu
}
Ejemplo n.º 12
0
// Dot returns the dot product of two (dived by w) vectors.
func Dot(a, b *T) float64 {
	a3 := a.Vec3DividedByW()
	b3 := b.Vec3DividedByW()
	return vec3.Dot(&a3, &b3)
}
Ejemplo n.º 13
0
func (this *NurbsSurface) ClosestParam(p vec3.T) UV {
	// for surfaces, we try to minimize the following:
	//
	// f = Su(u,v) * r = 0
	// g = Sv(u,v) * r = 0
	//
	//  where r = S(u,v) - P
	//
	// Again, this requires newton iteration, but this time our objective function is vector valued
	//
	//    J d = k
	//
	//      d =   [ u* - u, v* - v ]
	//		k = - [ f(u,v), g(u,v) ]
	//		J =
	//          |Su|^2   +  Suu * r       Su*Sv  +  Suv * r
	//		     Su*Sv   +  Svu * r      |Sv|^2  +  Svv * r
	//
	//
	// 	we have similar halting conditions:
	//
	//  point coincidence
	//
	//		|S(u,v) - p| < e1
	//
	//  cosine
	//
	//   |Su(u,v)*(S(u,v) - P)|
	//   ----------------------  < e2
	//   |Su(u,v)| |S(u,v) - P|
	//
	//   |Sv(u,v)*(S(u,v) - P)|
	//   ----------------------  < e2
	//   |Sv(u,v)| |S(u,v) - P|
	//
	//  1) first check 2 & 3
	// 	2) if at least one of these is not, compute new value, otherwise halt
	// 	3) ensure the parameter stays within range
	// 			* if not closed, don't allow outside of range a-b
	// 			* if closed (e.g. circle), allow to move back to beginning
	//  4)  if |(u* - u)C'(u)| < e1, halt
	//

	maxits := 5
	var i int
	var e [][]vec3.T
	eps1, eps2 := 0.0001, 0.0005
	var dif vec3.T
	minu, maxu := this.knotsU[0], this.knotsU[len(this.knotsU)-1]
	minv, maxv := this.knotsV[0], this.knotsV[len(this.knotsV)-1]
	closedu, closedv := this.isClosed(true), this.isClosed(false)
	var cuv UV

	// TODO divide surface instead of a full on tessellation

	// approximate closest point with tessellation
	tess := this.tessellateAdaptive(&defaultAdaptiveRefinementOptions)

	dmin := math.MaxFloat64

	for i, x := range tess.Points {
		d := vec3.SquareDistance(&p, &x)

		if d < dmin {
			dmin = d
			cuv = tess.UVs[i]
		}
	}

	f := func(uv UV) [][]vec3.T {
		return this.Derivatives(uv, 2)
	}

	n := func(uv UV, e [][]vec3.T, r vec3.T) UV {
		// f = Su(u,v) * r = 0
		// g = Sv(u,v) * r = 0

		Su, Sv := e[1][0], e[0][1]
		Suu, Svv := e[2][0], e[0][2]
		Suv, Svu := e[1][1], e[1][1]

		f := vec3.Dot(&Su, &r)
		g := vec3.Dot(&Sv, &r)

		k := [2]float64{-f, -g}

		J00 := vec3.Dot(&Su, &Su) + vec3.Dot(&Suu, &r)
		J01 := vec3.Dot(&Su, &Sv) + vec3.Dot(&Suv, &r)
		J10 := vec3.Dot(&Su, &Sv) + vec3.Dot(&Svu, &r)
		J11 := vec3.Dot(&Sv, &Sv) + vec3.Dot(&Svv, &r)

		//J := [2][2]float64{{J00, J01}, {J10, J11}}
		//J := Mat2{J00, J01, J10, J11}

		//    d =   [ u* - u, v* - v ]
		//		k = - [ f(u,v), g(u,v) ]
		//		J =
		//          |Su|^2   +  Suu * r       Su*Sv  +  Suv * r
		//		     Su*Sv   +  Svu * r      |Sv|^2  +  Svv * r
		//

		//d := J.Solve(k)
		x, y := Mat2Solve(J00, J01, J10, J11, k[0], k[1])

		//return UV{d[0] + uv[0], d[1] + uv[1]}
		return UV{x + uv[0], y + uv[1]}
	}

	for i < maxits {
		e = f(cuv)

		//  point coincidence
		//
		//		|S(u,v) - p| < e1
		c1v := vec3.Distance(&e[0][0], &p)

		//
		//  cosine
		//
		//   |Su(u,v)*(S(u,v) - P)|
		//   ----------------------  < e2
		//   |Su(u,v)| |S(u,v) - P|
		//
		//   |Sv(u,v)*(S(u,v) - P)|
		//   ----------------------  < e2
		//   |Sv(u,v)| |S(u,v) - P|
		//
		c2an := vec3.Dot(&e[1][0], &dif)
		c2ad := e[1][0].Length() * c1v

		c2bn := vec3.Dot(&e[0][1], &dif)
		c2bd := e[0][1].Length() * c1v

		c2av := c2an / c2ad
		c2bv := c2bn / c2bd

		c1 := c1v < eps1
		c2a := c2av < eps2
		c2b := c2bv < eps2

		// if all of the tolerance are met, we're done
		if c1 && c2a && c2b {
			return cuv
		}

		// otherwise, take a step
		ct := n(cuv, e, dif)

		// correct for exceeding bounds
		if ct[0] < minu {
			if closedu {
				ct = UV{maxu - (ct[0] - minu), ct[1]}
			} else {
				ct = UV{minu + Epsilon, ct[1]}
			}
		} else if ct[0] > maxu {
			if closedu {
				ct = UV{minu + (ct[0] - maxu), ct[1]}
			} else {
				ct = UV{maxu - Epsilon, ct[1]}
			}
		}

		if ct[1] < minv {
			if closedv {
				ct = UV{ct[0], maxv - (ct[1] - minv)}
			} else {
				ct = UV{ct[0], minv + Epsilon}
			}
		} else if ct[1] > maxv {
			if closedv {
				ct = UV{ct[0], minv + (ct[0] - maxv)}
			} else {
				ct = UV{ct[0], maxv - Epsilon}
			}
		}

		// if |(u* - u) C'(u)| < e1, halt
		c3v0v := e[1][0].Scaled(ct[0] - cuv[0])
		c3v0 := c3v0v.Length()
		c3v1v := e[0][1].Scaled(ct[1] - cuv[1])
		c3v1 := c3v1v.Length()

		if c3v0+c3v1 < eps1 {
			return cuv
		}

		cuv = ct
		i++

	}

	return cuv
}