// 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) }
// 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()) }