Exemple #1
0
func (this *Mesh) TriangleUvFromPoint(faceIndex int, f *vec3.T) verb.UV {
	tri := this.Faces[faceIndex]

	p0 := this.Points[tri[0]]
	p1 := this.Points[tri[1]]
	p2 := this.Points[tri[2]]

	uv0 := this.UVs[tri[0]]
	uv1 := this.UVs[tri[1]]
	uv2 := this.UVs[tri[2]]

	f0 := vec3.Sub(&p0, f)
	f1 := vec3.Sub(&p1, f)
	f2 := vec3.Sub(&p2, f)

	// calculate the areas and factors (order of parameters doesn't matter):
	p1.Sub(&p0)
	p2.Sub(&p0)
	aVec := vec3.Cross(&p1, &p2)
	a := aVec.Length()

	a0Vec := vec3.Cross(&f1, &f2)
	a1Vec := vec3.Cross(&f2, &f0)
	a2Vec := vec3.Cross(&f0, &f1)

	a0 := a0Vec.Length() / a
	a1 := a1Vec.Length() / a
	a2 := a2Vec.Length() / a

	// find the uv corresponding to point f (uv1/uv2/uv3 are associated to p1/p2/p3):
	return verb.UV{
		a0*uv0[0] + a1*uv1[0] + a2*uv2[0],
		a0*uv0[1] + a1*uv1[1] + a2*uv2[1],
	}
}
Exemple #2
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}}
	}
}
Exemple #3
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
}
Exemple #4
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
}
Exemple #5
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
}
Exemple #6
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
}
Exemple #7
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)
}
Exemple #8
0
func (this *adaptiveRefinementNode) ShouldDivide(options *adaptiveRefinementOptions, currentDepth int) bool {
	if currentDepth < options.MinDepth {
		return true
	}
	if currentDepth >= options.MaxDepth {
		return false
	}

	if this.HasBadNormals() {
		this.FixNormals()
		// don't divide any further when encountering a degenerate normal
		return false
	}

	normDiff01 := vec3.Sub(this.corners[0].Normal, this.corners[1].Normal)
	normDiff23 := vec3.Sub(this.corners[2].Normal, this.corners[3].Normal)
	this.splitVert = normDiff01.LengthSqr() > options.NormTol || normDiff23.LengthSqr() > options.NormTol

	normDiff12 := vec3.Sub(this.corners[1].Normal, this.corners[2].Normal)
	normDiff30 := vec3.Sub(this.corners[3].Normal, this.corners[0].Normal)
	this.splitHoriz = normDiff12.LengthSqr() > options.NormTol || normDiff30.LengthSqr() > options.NormTol

	if this.splitVert || this.splitHoriz {
		return true
	}

	center := this.Center()

	for _, corner := range this.corners {
		diffVec := vec3.Sub(center.Normal, corner.Normal)
		if diffVec.LengthSqr() > options.NormTol {
			return true
		}
	}
	return false
}
Exemple #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)),
	}
}
Exemple #10
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
}
Exemple #11
0
// Generate the control points, weights, and knots of an elliptical arc
//
// **params**
// + the center
// + the scaled x axis
// + the scaled y axis
// + start angle of the ellipse arc, between 0 and 2pi, where 0 points at the xaxis
// + end angle of the arc, between 0 and 2pi, greater than the start angle
//
// **returns**
// + a NurbsCurveData object representing a NURBS curve
func EllipseArc(center *vec3.T, xaxis, yaxis *vec3.T, startAngle, endAngle float64) *verb.NurbsCurve {
	xradius, yradius := xaxis.Length(), yaxis.Length()

	xaxisNorm, yaxisNorm := xaxis.Normalized(), yaxis.Normalized()

	// if the end angle is less than the start angle, do a circle
	if endAngle < startAngle {
		endAngle = 2.0*math.Pi + startAngle
	}

	theta := endAngle - startAngle

	// how many arcs?
	var numArcs int
	if theta <= math.Pi/2 {
		numArcs = 1
	} else {
		if theta <= math.Pi {
			numArcs = 2
		} else if theta <= 3*math.Pi/2 {
			numArcs = 3
		} else {
			numArcs = 4
		}
	}

	dtheta := theta / float64(numArcs)
	w1 := math.Cos(dtheta / 2)

	xCompon := xaxisNorm.Scaled(xradius * math.Cos(startAngle))
	yCompon := yaxisNorm.Scaled(yradius * math.Sin(startAngle))
	P0 := vec3.Add(&xCompon, &yCompon)

	temp0 := yaxisNorm.Scaled(math.Cos(startAngle))
	temp1 := xaxisNorm.Scaled(math.Sin(startAngle))
	T0 := vec3.Sub(&temp0, &temp1)

	controlPoints := make([]vec3.T, 2*numArcs+1)
	knots := make([]float64, 2*numArcs+3)
	index := 0
	angle := startAngle
	weights := make([]float64, numArcs*2)

	controlPoints[0] = P0
	weights[0] = 1.0

	for i := 1; i <= numArcs; i++ {
		angle += dtheta
		xCompon = xaxisNorm.Scaled(xradius * math.Cos(angle))
		yCompon = yaxisNorm.Scaled(yradius * math.Sin(angle))
		offset := vec3.Add(&xCompon, &yCompon)
		P2 := vec3.Add(center, &offset)

		weights[index+2] = 1
		controlPoints[index+2] = P2

		temp0 := yaxisNorm.Scaled(math.Cos(angle))
		temp1 := xaxisNorm.Scaled(math.Sin(angle))
		T2 := vec3.Sub(&temp0, &temp1)

		T0Norm := T0.Normalized()
		T2Norm := T2.Normalized()
		inters := intersect.Rays(&P0, &T0Norm, &P2, &T2Norm)

		T0Scaled := T0.Scaled(inters.U0)
		P1 := vec3.Add(&P0, &T0Scaled)

		weights[index+1] = w1
		controlPoints[index+1] = P1

		index += 2

		if i < numArcs {
			P0 = P2
			T0 = T2
		}
	}

	j := 2*numArcs + 1

	for i := 0; i < 3; i++ {
		knots[i] = 0.0
		knots[i+j] = 1.0
	}

	switch numArcs {
	case 2:
		knots[3] = 0.5
		knots[4] = 0.5
	case 3:
		knots[3] = 1 / 3
		knots[4] = 1 / 3

		knots[5] = 2 / 3
		knots[6] = 2 / 3
	case 4:
		knots[3] = 0.25
		knots[4] = 0.25

		knots[5] = 0.5
		knots[6] = 0.5

		knots[7] = 0.75
		knots[8] = 0.75
	}

	return verb.NewNurbsCurveUnchecked(2, controlPoints, weights, knots)
}
Exemple #12
0
// Generate the control points, weights, and knots of a revolved surface
// (Corresponds to Algorithm A7.1 from Piegl & Tiller)
//
// **params**
// + center of the rotation axis
// + axis of the rotation axis
// + angle to revolve around axis
// + degree of the generatrix
// + control points of the generatrix
// + weights of the generatrix
//
// **returns**
// + an object with the following properties: controlPoints, weights, knots, degree
func RevolvedSurface(profile *verb.NurbsCurve, center *vec3.T, axis *vec3.T, theta float64) *verb.NurbsSurface {
	prof_controlPoints := profile.ControlPoints()
	prof_weights := profile.Weights()

	var narcs int
	var knotsU []float64

	switch {
	case theta <= math.Pi/2:
		{ // less than 90
			narcs = 1
			knotsU = make([]float64, 6+2*(narcs-1))
		}
	case theta <= math.Pi:
		{ // between 90 and 180
			narcs = 2
			knotsU = make([]float64, 6+2*(narcs-1))
			knotsU[3], knotsU[4] = 0.5, 0.5
		}
	case theta <= 3*math.Pi/2:
		{ // between 180 and 270
			narcs = 3
			knotsU = make([]float64, 6+2*(narcs-1))
			knotsU[3], knotsU[4] = 1/3, 1/3
			knotsU[5], knotsU[6] = 2/3, 2/3
		}
	default:
		{ // between 270 and 360
			narcs = 4
			knotsU = make([]float64, 6+2*(narcs-1))
			knotsU[3], knotsU[4] = 1/4, 1/4
			knotsU[5], knotsU[6] = 1/2, 1/2
			knotsU[7], knotsU[8] = 3/4, 3/4
		}
	}

	dtheta := theta / float64(narcs) // divide the interval into several points
	j := 3 + 2*(narcs-1)

	// initialize the start and end knots
	// keep in mind that we only return the knot vector for thes
	for i := 0; i < 3; i++ {
		knotsU[j+i] = 1
	}

	// do some initialization
	wm := math.Cos(dtheta / 2)
	sines, cosines := make([]float64, narcs+1), make([]float64, narcs+1)

	controlPoints := make([][]vec3.T, 2*narcs+1)
	for i := range controlPoints {
		controlPoints[i] = make([]vec3.T, len(prof_controlPoints))
	}

	weights := make([][]float64, 2*narcs+1)
	for i := range weights {
		weights[i] = make([]float64, len(prof_controlPoints))
	}

	// initialize the sines and cosines
	var angle float64
	for i := 1; i <= narcs; i++ {
		angle += dtheta
		cosines[i] = math.Cos(angle)
		sines[i] = math.Sin(angle)
	}

	// for each pt in the generatrix
	// i.e. for each row of the 2d knot vectors
	for j := range prof_controlPoints {
		// get the closest point of the generatrix point on the axis
		O := rayClosestPoint(prof_controlPoints[j], center, axis)
		// X is the vector from the axis to generatrix control pt
		X := vec3.Sub(&prof_controlPoints[j], &O)
		// radius at that height
		r := X.Length()
		// Y is perpendicular to X and axis, and complete the coordinate system
		Y := vec3.Cross(axis, &X)

		if r > internal.Epsilon {
			X.Scale(1 / r)
			Y.Scale(1 / r)
		}

		// the first row of controlPoints and weights is just the generatrix
		controlPoints[0][j] = prof_controlPoints[j]
		P0 := prof_controlPoints[j]
		weights[0][j] = prof_weights[j]

		// store T0 as the Y vector
		var T0 = Y
		var index int

		// proceed around the circle
		for i := 1; i <= narcs; i++ {
			// O + r * cos(theta) * X + r * sin(theta) * Y
			// rotated generatrix pt
			var P2 vec3.T
			if r == 0 {
				P2 = O
			} else {
				xCompon := X.Scaled(r * cosines[i])
				yCompon := Y.Scaled(r * sines[i])
				offset := xCompon.Add(&yCompon)
				P2 = vec3.Add(&O, offset)
			}

			controlPoints[index+2][j] = P2
			weights[index+2][j] = prof_weights[j]

			// construct the vector tangent to the rotation
			temp0 := Y.Scaled(cosines[i])
			temp1 := X.Scaled(sines[i])
			T2 := temp0.Sub(&temp1)

			// construct the next control pt
			if r == 0 {
				controlPoints[index+1][j] = O
			} else {
				T0Norm := T0.Normalized()
				T2Norm := T2.Normalized()
				inters := intersect.Rays(&P0, &T0Norm, &P2, &T2Norm)

				T0Scaled := T0.Scaled(inters.U0)
				P1 := T0Scaled.Add(&P0)

				controlPoints[index+1][j] = *P1
			}

			weights[index+1][j] = wm * prof_weights[j]

			index += 2

			if i < narcs {
				P0 = P2
				T0 = *T2
			}
		}
	}

	return verb.NewNurbsSurfaceUnchecked(2, profile.Degree(), controlPoints, weights, knotsU, profile.Knots())
}