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], } }
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 }
// 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() }
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} }
// 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 }
// // Tessellate a NURBS surface on equal spaced intervals in the parametric domain // // **params** // + NurbsSurfaceData object // + number of divisions in the u direction // + number of divisions in the v direction // // **returns** // + MeshData object // func (this *NurbsSurface) tessellateNaive(divsU, divsV int) *Mesh { if divsU < 1 { divsU = 1 } if divsV < 1 { divsV = 1 } //degreeU, degreeV := this.DegreeU, this.DegreeV //controlPoints := this.ControlPoints knotsU, knotsV := this.knotsU, this.knotsV uSpan := knotsU[len(knotsU)-1] - knotsU[0] vSpan := knotsV[len(knotsV)-1] - knotsV[0] spanU := uSpan / float64(divsU) spanV := vSpan / float64(divsV) numPoints := (divsU + 1) * (divsV + 1) points := make([]vec3.T, numPoints) uvs := make([]UV, numPoints) normals := make([]vec3.T, 0, numPoints) var counter int for i := 0; i <= divsU; i++ { for j := 0; j <= divsV; j++ { uv := UV{float64(i) * spanU, float64(j) * spanV} uvs[counter] = uv derivs := this.Derivatives(uv, 1) pt := derivs[0][0] points[counter] = pt normal := vec3.Cross(&derivs[1][0], &derivs[0][1]) normals = append(normals, *normal.Normalize()) counter++ } } faces := make([]Tri, 0, 2*divsU*divsV) for i := 0; i < divsU; i++ { for j := 0; j < divsV; j++ { ai := i*(divsV+1) + j bi := (i+1)*(divsV+1) + j ci := bi + 1 di := ai + 1 abc := Tri{ai, bi, ci} acd := Tri{ai, ci, di} faces = append(faces, abc, acd) } } return &Mesh{faces, points, normals, uvs} }
// // Get triangle normal // // **params** // + array of length 3 arrays of numbers representing the points // + length 3 array of point indices for the triangle // // **returns** // + a normal vector represented by an array of length 3 // func TriangleNormal(points []vec3.T, tri *verb.Tri) vec3.T { v0 := points[tri[0]] v1 := points[tri[1]] v2 := points[tri[2]] v1.Sub(&v0) v2.Sub(&v0) n := vec3.Cross(&v1, &v2) return *n.Normalize() }
func (this *adaptiveRefinementNode) EvalSrf(uv UV) *SurfacePoint { derivs := this.srf.Derivatives(uv, 1) pt := derivs[0][0] norm := vec3.Cross(&derivs[0][1], &derivs[1][0]) degen := true for _, compon := range norm { if math.Abs(compon) > this.tolerance { degen = false break } } if !degen { norm.Normalize() } return &SurfacePoint{uv, &pt, &norm, -1, degen} }
// Cross returns the cross product of two vectors. func Cross(a, b *T) T { a3 := a.Vec3DividedByW() b3 := b.Vec3DividedByW() c3 := vec3.Cross(&a3, &b3) return T{c3[0], c3[1], c3[2], 1} }
// Compute the derivatives at a point on a NURBS surface // // **params** // + NurbsSurfaceData object representing the surface // + u parameter // + v parameter // // **returns** // + a Vector represented by an array of length (dim) func (this *NurbsSurface) Normal(uv UV) vec3.T { derivs := this.Derivatives(uv, 1) return vec3.Cross(&derivs[1][0], &derivs[0][1]) }
// // Divide a NURBS surface int equal spaced intervals in the parametric domain as AdaptiveRefinementNodes // // **params** // + NurbsSurfaceData object // + SurfaceDivideOptions object // // **returns** // + MeshData object // func (this *NurbsSurface) adaptiveDivisions(options *adaptiveRefinementOptions) []*adaptiveRefinementNode { if options == nil { options = &defaultAdaptiveRefinementOptions } minU := (len(this.controlPoints) - 1) * 2 minV := (len(this.controlPoints[0]) - 1) * 2 var divsU, divsV int if options.MinDivsU > minU { divsU = options.MinDivsU } else { divsU = minU } if options.MinDivsU > minV { divsV = options.MinDivsV } else { divsV = minV } // get necessary intervals umax := this.knotsU[len(this.knotsU)-1] umin := this.knotsU[0] vmax := this.knotsV[len(this.knotsV)-1] vmin := this.knotsV[0] du := (umax - umin) / float64(divsU) dv := (vmax - vmin) / float64(divsV) pts := make([][]*SurfacePoint, divsV+1) // 1) evaluate all of the corners for i := range pts { ptrow := make([]*SurfacePoint, divsU+1) for j := range ptrow { uv := UV{umin + du*float64(j), vmin + dv*float64(i)} // todo: make this faster by specifying n,m ds := this.Derivatives(uv, 1) norm := vec3.Cross(&ds[0][1], &ds[1][0]) norm.Normalize() degen := true for _, compon := range norm { if math.Abs(compon) > Tolerance { degen = false break } } ptrow[j] = &SurfacePoint{uv, &ds[0][0], &norm, -1, degen} } pts[i] = ptrow } divs := make([]*adaptiveRefinementNode, divsU*divsV) // 2) make all of the nodes var divsI int for i := 0; i < divsV; i++ { for j := 0; j < divsU; j++ { corners := [4]*SurfacePoint{ pts[divsV-i-1][j], pts[divsV-i-1][j+1], pts[divsV-i][j+1], pts[divsV-i][j], } divs[divsI] = newAdaptiveRefinementNode(this, &corners, nil) divsI++ } } if !options.Refine { return divs } // 3) assign all of the neighbors and divide for i := 0; i < divsV; i++ { for j := 0; j < divsU; j++ { ci := i*divsU + j n := north(ci, i, j, divsU, divsV, divs) e := east(ci, i, j, divsU, divsV, divs) s := south(ci, i, j, divsU, divsV, divs) w := west(ci, i, j, divsU, divsV, divs) divs[ci].Neighbors = [4]*adaptiveRefinementNode{s, e, n, w} divs[ci].Divide(options) } } return divs }
// 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()) }