#version 3.8; // Tangents between circles by analytical geometry // Bill Walker "Bald Eagle" October 2019 // Major.Minor.Patch http://semver.org/ // Version 0.1.0 // "minimal" scene for demonstration purposes. // BW ---> jr global_settings { assumed_gamma 1.0 } // Epsilon - for fixing various issues in need of "nudging" #declare E = 0.000001; #declare _Eps = E; // just a 4-digit name for neatness in array definitions // {x, y, z, rad} #declare CircleArray = array [3][4]{ // x y z r { 0.000, 0, -0.25, 0.20}, {-1.000, 0, -0.10, 0.10}, { 0.500, 0, 0.50, 0.10} } // DS_parameter indicates dimension_size (1 to N), the number of items in an array // EL_parameter indicates element number (0 to N-1), the position of the item in the array #declare DS_Circles = dimension_size (CircleArray, 1); #declare EL_Circles = DS_Circles-1; //######################################################################### #include "colors.inc" #include "math.inc" #include "shapes.inc" #include "shapes2.inc" #include "shapes3.inc" #declare Aspect = image_width/image_height; //######################################################################### #declare Zoom = 400; camera { orthographic right x*image_width/Zoom up y*image_height/Zoom location <0, 8, -0.1> look_at <0, 0, 0.0> } //######################################################################### light_source {<0, 25, 0> color White} // radius of lines drawn in scene #declare Line = 0.017/4; plane {y, -Line*5 pigment {White*0.1}} cylinder {<-10, 0, 0>, <10, 0, 0> Line pigment {Red*0.2}} cylinder {<0, 0, -10>, <0, 0, 10> Line pigment {Blue*0.2}} // Define pigments and textures //------------------------------------------------------------------------ // P_parameter indicates pigment // T_parameter indicates texture // CM_parameter indicates color_map #declare LinePigment = pigment {Yellow+White/2} #declare Tan = texture {pigment {rgb <0.92, 0.60, 0.20>} finish {diffuse 0.7 specular 0.2 reflection 0.02}} #declare Brn = texture {pigment {rgb <0.92, 0.60, 0.20>*0.5} finish {diffuse 0.7 specular 0.2 reflection 0.02}} // Define arrays and macros //------------------------------------------------------------------------ // array for displaying signed values in #debug output #declare Sign = array [3] {"-", "0", "+"}; // shorthand macro for sending numeric output to #debug stream #macro N (_N) str(_N, 0, 2) #end #macro Angle2D (vA, vB) // from Tor Olav Kristensen // http://news.povray.org/povray.newusers/message/%3Cweb.5d9f7b4becd82d81ed4479290%40news.povray.org%3E/#%3Cweb.5d9f7b4becd82d81ed4479290%40news.povray.org%3E #local AngleA = atan2 (vA.z, vA.x); #local AngleB = atan2 (vB.z, vB.x); #local dAngle = AngleB - AngleA; // degrees(dAngle) // For result from -180 to +180 degrees degrees (mod (dAngle + 2*pi, 2*pi)) // For result from 0 to +360 degrees #end // macro Angle2D #macro ArcAngle (_Center, _T1, _T4) // This macro should not be invoked until the tangents are chosen, // since there are 4 tangents, and this macro only uses 2 points #local _T1vector = _T1 - _Center; #local _T4vector = _T4 - _Center; #local _Start = atan2 (_T1vector.z, _T1vector.x); #local _End = atan2 (_T4vector.z, _T4vector.x); #local _CCWArc = degrees (mod (_End - _Start + 2*pi, 2*pi)); #local _S = degrees (mod (_Start + 2*pi, 2*pi)); #local _E = degrees (mod (_End + 2*pi, 2*pi)); #ifdef (Verbose) #debug concat ( "Counterclockwise Calculated Angle = ", str (_CCWArc, 0, 2), "\n") #end array [3] {_S, _E, _CCWArc} // start, end, counterclockwise angle #end #macro BezierArcs (_Center, _T1, _T4) #local _Angle = ArcAngle (_Center, _T1, _T4) #local NumSegments = ceil (abs(_Angle)/90); #ifdef (Verbose) #debug concat ( " NumSegments = ", str (NumSegments, 0, 1), "\n") #end #local Segments = array; //[NumSegments*4] #local PCurrent = 0; #for (S, 0, NumSegments-1) #local Quadrant = S*90; #debug concat ( " Quadrant = ", str (Quadrant, 0, 1), "\n") #if (S = NumSegments-1) #local T4 = _T4; #else #local End = transform {translate -Center rotate -y*(Quadrant+90) translate Center } #local T4 = vtransform (_T1, End); #end #local Start = transform {translate -Center rotate -y*Quadrant translate Center } #local T1 = vtransform (_T1, Start); // Equations are in terms of x-y, so swap prism z values into y. ;) #local x1 = T1.x; #local y1 = T1.z; #local x4 = T4.x; #local y4 = T4.z; #ifdef (Verbose) #debug concat ( " x1 = ", str (x1, 0, 2), "\n") #debug concat ( " y1 = ", str (y1, 0, 2), "\n") #debug concat ( " x4 = ", str (x4, 0, 2), "\n") #debug concat ( " y4 = ", str (y4, 0, 2), "\n") #end // https://pdfs.semanticscholar.org/1639/0db1a470bd13fe428e0896671a9a5745070a.pdf #local ax = x1 - xc; #local ay = y1 - yc; #local bx = x4 - xc; #local by = y4 - yc; #local q1 = ax*ax + ay*ay; #local q2 = q1 + ax*bx + ay*by; #local k2 = 4/3 * (sqrt(2*q1*q2)-q2)/(ax*by-ay*bx); // (4/3) // original equations // #local x2 = xc + x1 - k2*y1; // #local y2 = yc + y1 + k2*x1; // #local x3 = xc + x4 - k2*y4; // #local y4 = yc + y4 + k2*x4; // corrections to original equations from // https://hansmuller-flex.blogspot.com/2011/10/more-about-approximating-circular-arcs.html?showComment=1498749617507#c2109832351939371205 #local x2 = xc + ax - k2*ay; #local y2 = yc + ay + k2*ax; #local x3 = xc + bx + k2*by; // needed to change sign from: bx - k2*by #local y3 = yc + by - k2*bx; // needed to change sign from: by + k2*bx #debug concat ( " x2 = ", str (x2, 0, 2), "\n") #debug concat ( " y2 = ", str (y2, 0, 2), "\n") #debug concat ( " x3 = ", str (x3, 0, 2), "\n") #debug concat ( " y3 = ", str (y3, 0, 2), "\n") #local P1 = ; #local P2 = ; #local P3 = ; #local P4 = ; #local Segments[PCurrent] = P1; #local PCurrent = PCurrent +1; #local Segments[PCurrent] = P2; #local PCurrent = PCurrent +1; #local Segments[PCurrent] = P3; #local PCurrent = PCurrent +1; #local Segments[PCurrent] = P4; #local PCurrent = PCurrent +1; #end #end #macro AnalyticalTangents (_C1, _R1, _C2, _R2) // http://ambrsoft.com/TrigoCalc/Circles2/Circles2Tangent_.htm // http://ambrsoft.com/Equations/Examples/AnalyticGeometry/AnalyticGeometry.htm#ex2 #local a = _C1.x; #local b = _C1.z; #local r0 = _R1; #local c = _C2.x; #local d = _C2.z; #local r1 = _R2; #local _vector = _C2 - _C1; #local _D = vlength (_vector); #local OFlag = 1; // this gets set to 0 for nonexistant outer tangents #local IFlag = 1; // this gets set to 0 for nonexistant inner tangents // initialize variables to avoid errors on macro exit if calculations are skipped #local Xpo = 1; #local Ypo = 1; #local Xto1 = 1; #local Yto1 = 1; #local Xto2 = 1; #local Yto2 = 1; #local Xto3 = 1; #local Yto3 = 1; #local Xto4 = 1; #local Yto4 = 1; #local Xpi = 1; #local Ypi = 1; #local Xti1 = 1; #local Yti1 = 1; #local Xti2 = 1; #local Yti2 = 1; #local Xti3 = 1; #local Yti3 = 1; #local Xti4 = 1; #local Yti4 = 1; #if (r0 = r1) #local r0 = r0 + E; // this skirts addressing tangent calculations for equal radius circles #end // display equations #ifdef (Verbose) #if (_D < abs(r0 - r1) ) #debug "One circle is interior to the other - no tangents \n" #end #if (_D = abs(r0 - r1) ) #debug "One circle is interior and tangent to the other - one tangent \n" #end #if (_D > abs(r0 - r1) & _D < r0+r1 ) #debug "Circle overlap each other - two tangents \n" #end #if (_D = r0 + r1 ) #debug "Circles are exterior and tangent to each other - 3 tangents \n" #end #if (_D > r0 + r1 ) #debug "Circles are exterior to each other - 4 tangents \n" #end #debug "Calculating tangents for circles \n" #debug concat ("pow (x", Sign[-sgn(a)+1], N(abs(a)), ", 2) + " ) #debug concat ("pow (y", Sign[-sgn(b)+1], N(abs(b)), ", 2) = " ) #debug concat ("pow (", N(r0), ", 2) \n" ) #debug " and \n" #debug concat ("pow (x", Sign[-sgn(c)+1], N(abs(c)), ", 2) + " ) #debug concat ("pow (y", Sign[-sgn(d)+1], N(abs(d)), ", 2) = " ) #debug concat ("pow (", N(r1), ", 2) \n\n" ) /* #debug "Outer tangent lines intersect at point p" #debug concat ("Xp = c * r0 - a * r1 / r0 - r1 = ") #debug concat (N(c), "*", N(r0), " - ", N(a), "*", N(r1), " / ", N(r0), " - ", N(r1), " = ") #debug N((c*r0 - a*r1) / (r0-r1) ) #debug "\n" #debug concat ("Yp = d * r0 - b * r1 / r0 - r1 = ") #debug concat (N(d), "*", N(r0), " - ", N(b), "*", N(r1), " / ", N(r0), " - ", N(r1), " = ") #debug N((d*r0 - b*r1) / (r0-r1) ) #debug "\n" */ #end // check for exceptions and exclude calculations for nonexistant tangents. #if (_D < abs(r0 - r1) ) #local OFlag = 0; #local IFlag = 0; #end // 0 #if (_D = abs(r0 - r1) ) #local OFlag = 0; #local IFlag = 0; #end // 1 // calculate single tangent and export data to #debug stream #if (_D > abs(r0 - r1) & _D < r0+r1 ) #local IFlag = 0; #end // 2 #if (_D = r0 + r1 ) #local IFlag = 0; #end // 3 // calculate third tangent and export data to #debug stream #if (OFlag) // compute coordinates for the intersection of outer tangent lines #local Xpo = (c*r0 - a*r1) / (r0-r1); #local Ypo = (d*r0 - b*r1) / (r0-r1); // compute values for tangents // Outer tangents on r0 #local Xto1 = (pow(r0,2)*(Xpo-a) + r0*(Ypo-b)*sqrt(pow(Xpo-a,2)+pow(Ypo-b,2)-pow(r0,2)) ) / (pow(Xpo-a,2)+pow(Ypo-b,2))+a; #local Xto2 = (pow(r0,2)*(Xpo-a) - r0*(Ypo-b)*sqrt(pow(Xpo-a,2)+pow(Ypo-b,2)-pow(r0,2)) ) / (pow(Xpo-a,2)+pow(Ypo-b,2))+a; #local Yto1 = (pow(r0,2)*(Ypo-b) + r0*(Xpo-a)*sqrt(pow(Xpo-a,2)+pow(Ypo-b,2)-pow(r0,2)) ) / (pow(Xpo-a,2)+pow(Ypo-b,2))+b; #local Yto2 = (pow(r0,2)*(Ypo-b) - r0*(Xpo-a)*sqrt(pow(Xpo-a,2)+pow(Ypo-b,2)-pow(r0,2)) ) / (pow(Xpo-a,2)+pow(Ypo-b,2))+b; #local s = (b-Yto1)*(Ypo-Yto1)/(Xto1-a)*(Xto1-Xpo); #if (s != 1) // check if correct coordinates are paired - if not, swap tangent y-values #local TempY = Yto1; #local Yto1 = Yto2; #local Yto2 = TempY; #end // Outer tangents on r1 #local Xto3 = (pow(r1,2)*(Xpo-c) + r1*(Ypo-d)*sqrt(pow(Xpo-c,2)+pow(Ypo-d,2)-pow(r1,2)) ) / (pow(Xpo-c,2)+pow(Ypo-d,2))+c; #local Xto4 = (pow(r1,2)*(Xpo-c) - r1*(Ypo-d)*sqrt(pow(Xpo-c,2)+pow(Ypo-d,2)-pow(r1,2)) ) / (pow(Xpo-c,2)+pow(Ypo-d,2))+c; #local Yto3 = (pow(r1,2)*(Ypo-d) + r1*(Xpo-c)*sqrt(pow(Xpo-c,2)+pow(Ypo-d,2)-pow(r1,2)) ) / (pow(Xpo-c,2)+pow(Ypo-d,2))+d; #local Yto4 = (pow(r1,2)*(Ypo-d) - r1*(Xpo-c)*sqrt(pow(Xpo-c,2)+pow(Ypo-d,2)-pow(r1,2)) ) / (pow(Xpo-c,2)+pow(Ypo-d,2))+d; #local s = (b-Yto1)*(Ypo-Yto1)/(Xto1-a)*(Xto1-Xpo); #if (s != 1) // check if correct coordinates are paired - if not, swap tangent y-values #local TempY = Yto3; #local Yto3 = Yto4; #local Yto4 = TempY; #end #ifdef (Verbose) #debug "Points of tangency are: \n" #debug concat ( "t1 = ", vstr(3, , ", ", 0, 2), " \n") #debug concat ( "t2 = ", vstr(3, , ", ", 0, 2), " \n") #debug concat ( "t3 = ", vstr(3, , ", ", 0, 2), " \n") #debug concat ( "t4 = ", vstr(3, , ", ", 0, 2), " \n") #debug concat ("s-check: \n") #end #else #warning "No outer tangents exist. Skipping calculations. \n" #end // end if OFlag #if (IFlag) // Inner tangents // compute coordinates for the intersection of inner tangent lines #local Xpi = (c*r0 + a*r1) / (r0+r1); #local Ypi = (d*r0 + b*r1) / (r0+r1); // compute values for tangents // Inner tangents on r0 #local Xti1 = (pow(r0,2)*(Xpi-a) + r0*(Ypi-b)*sqrt(pow(Xpi-a,2)+pow(Ypi-b,2)-pow(r0,2)) ) / (pow(Xpi-a,2)+pow(Ypi-b,2))+a; #local Xti2 = (pow(r0,2)*(Xpi-a) - r0*(Ypi-b)*sqrt(pow(Xpi-a,2)+pow(Ypi-b,2)-pow(r0,2)) ) / (pow(Xpi-a,2)+pow(Ypi-b,2))+a; #local Yti1 = (pow(r0,2)*(Ypi-b) + r0*(Xpi-a)*sqrt(pow(Xpi-a,2)+pow(Ypi-b,2)-pow(r0,2)) ) / (pow(Xpi-a,2)+pow(Ypi-b,2))+b; #local Yti2 = (pow(r0,2)*(Ypi-b) - r0*(Xpi-a)*sqrt(pow(Xpi-a,2)+pow(Ypi-b,2)-pow(r0,2)) ) / (pow(Xpi-a,2)+pow(Ypi-b,2))+b; #local s = (b-Yti1)*(Ypo-Yti1)/(Xti1-a)*(Xti1-Xpo); #if (s != 1) // check if correct coordinates are paired - if not, swap tangent y-values #local TempY = Yti1; #local Yti1 = Yti2; #local Yti2 = TempY; #end // Inner tangents on r1 #local Xti3 = (pow(r1,2)*(Xpi-c) + r1*(Ypi-d)*sqrt(pow(Xpi-c,2)+pow(Ypi-d,2)-pow(r1,2)) ) / (pow(Xpi-c,2)+pow(Ypi-d,2))+c; #local Xti4 = (pow(r1,2)*(Xpi-c) - r1*(Ypi-d)*sqrt(pow(Xpi-c,2)+pow(Ypi-d,2)-pow(r1,2)) ) / (pow(Xpi-c,2)+pow(Ypi-d,2))+c; #local Yti3 = (pow(r1,2)*(Ypi-d) + r1*(Xpi-c)*sqrt(pow(Xpi-c,2)+pow(Ypi-d,2)-pow(r1,2)) ) / (pow(Xpi-c,2)+pow(Ypi-d,2))+d; #local Yti4 = (pow(r1,2)*(Ypi-d) - r1*(Xpi-c)*sqrt(pow(Xpi-c,2)+pow(Ypi-d,2)-pow(r1,2)) ) / (pow(Xpi-c,2)+pow(Ypi-d,2))+d; #local s = (b-Yti1)*(Ypo-Yti1)/(Xti1-a)*(Xti1-Xpo); #if (s != 1) // check if correct coordinates are paired - if not, swap tangent y-values #local TempY = Yti3; #local Yti3 = Yti4; #local Yti4 = TempY; #end #else #warning "No inner tangents exist. Skipping calculations. \n" #end // end if IFlag // Only if the circles are exterior are there all 4 tangents. // if the circles touch, then there are no interior tangents - set a flag with the y component // and issue a warning // but there is also the tangent perpendicular to the line connecting the radii // export an equation or some endpoints to the debug stream // if the circles intersect, then there are no interior tangents - set a flag with the y component // and issue a warning // if the circle is interior and tangent, then there is only the perpendicular tangent // if one circle is interior, then warning & skip // output of macro is in the form of an array: array [10] { // Outer tangent data // Xto[N] "X tangent {point}, outer #N" 1 ( B ) 2 , // | 6 | 7 | , // | \|/ | , // -------|------- , // | /|\ | // Inner tangent data | 9 | 8 | , // Yto[N] "Y tangent {point}, outer #N" 3 ( A ) 4 , // , // , // // } #end #macro BezierArc (_Center, _T1, _T4) // Input: center of circle and 2 endpoints to make an arc between - going counterclockwise (LH system) // output: 4 points per 90 degree or less segment. #declare xc = _Center.x; #declare yc = _Center.z; #local CurrentAngle = ArcAngle (_Center, _T1, _T4); #local CA = CurrentAngle [2]; #local NumSegments = ceil (abs(CA)/90); #ifdef (Verbose) #debug concat ( " NumSegments = ", str (NumSegments, 0, 1), "\n") #end #local Partial = abs(CA)/90 - int (abs(CA)/90); #local Segments = array; //[NumSegments*4] #local PCurrent = 0; #for (S, 0, NumSegments-1) #local Quadrant = S*90; // multiples of 90 degrees: 0, 1, 2, 3 #ifdef (Verbose) #debug concat ( " Quadrant = ", str (Quadrant, 0, 1), "\n") #end #if (S = NumSegments-1) // if we're in the last segment, then T4 for this 4-point spline is the specified endpoint _T4 #local T4 = _T4; #else // if we're NOT in the last segment, then T4 for this 4-point spline is 90 degrees // from this 90-degree quadrant's starting point - which is a multiple of 90 deg from _T1 #local End = transform {translate -_Center rotate -y*(Quadrant+90) translate _Center } #local T4 = vtransform (_T1, End); #end // for any quadrant, the starting point is a multiple of 90 degrees from _T1 #local Start = transform {translate -_Center rotate -y*Quadrant translate _Center } #local T1 = vtransform (_T1, Start); // I'm thinking in x-y, so swap z values into y. ;) #local x1 = T1.x; #local y1 = T1.z; #local x4 = T4.x; #local y4 = T4.z; #ifdef (Verbose) #debug concat ( " x1 = ", str (x1, 0, 2), "\n") #debug concat ( " y1 = ", str (y1, 0, 2), "\n") #debug concat ( " x4 = ", str (x4, 0, 2), "\n") #debug concat ( " y4 = ", str (y4, 0, 2), "\n") #end // source: // https://pdfs.semanticscholar.org/1639/0db1a470bd13fe428e0896671a9a5745070a.pdf #local ax = x1 - xc; #local ay = y1 - yc; #local bx = x4 - xc; #local by = y4 - yc; #local q1 = ax*ax + ay*ay; #local q2 = q1 + ax*bx + ay*by; #local k2 = 4/3 * (sqrt(2*q1*q2)-q2)/(ax*by-ay*bx); // (4/3) // original equations // #local x2 = xc + x1 - k2*y1; // #local y2 = yc + y1 + k2*x1; // #local x3 = xc + x4 - k2*y4; // #local y4 = yc + y4 + k2*x4; // corrections to original equations from // https://hansmuller-flex.blogspot.com/2011/10/more-about-approximating-circular-arcs.html?showComment=1498749617507#c2109832351939371205 #local x2 = xc + ax - k2*ay; #local y2 = yc + ay + k2*ax; #local x3 = xc + bx + k2*by; // needed to change sign from: bx - k2*by | because of POV-Ray's #local y3 = yc + by - k2*bx; // needed to change sign from: by + k2*bx | LH coordinate system? #ifdef (Verbose) #debug concat ( " x2 = ", str (x2, 0, 2), "\n") #debug concat ( " y2 = ", str (y2, 0, 2), "\n") #debug concat ( " x3 = ", str (x3, 0, 2), "\n") #debug concat ( " y3 = ", str (y3, 0, 2), "\n") #end // These are the coordinates for the current 4-point segment of the loop #local P1 = ; #local P2 = ; #local P3 = ; #local P4 = ; #local Segments[PCurrent] = P1; #local PCurrent = PCurrent +1; #local Segments[PCurrent] = P2; #local PCurrent = PCurrent +1; #local Segments[PCurrent] = P3; #local PCurrent = PCurrent +1; #local Segments[PCurrent] = P4; #local PCurrent = PCurrent +1; #end Segments #end #macro LinearSegment (_Point1, _Point2) #local Vector = _Point2 - _Point1; #local Length = vlength (Vector); #local NVector = vnormalize (Vector); array [4] { _Point1 + 0/3*Vector, _Point1 + 1/3*Vector, _Point1 + 2/3*Vector, _Point1 + 3/3*Vector } #end // Draw elements composing the prism outline #for (Tangent, 0, EL_Circles) #local ElementL = Tangent - 1; //mod(Tangent-1, DS_Circles); #if (ElementL < 0) #local ElementL = EL_Circles; #end #local ElementA = mod(Tangent , DS_Circles+1); #local ElementB = mod(Tangent+1, DS_Circles+1); #if (ElementB > EL_Circles) #local ElementB = 0; #end #local CenterL = ; #local RadiusL = CircleArray [ElementL][3]; #local CenterA = ; #local RadiusA = CircleArray [ElementA][3]; #local CenterB = ; #local RadiusB = CircleArray [ElementB][3]; // get data for 4 tangent lines from #debug concat ( "\n\n ***** TANGENTS FROM CIRCLE ", str (ElementL, 2, 0), " to ", str (ElementA, 2, 0), " ***** \n\n") #local LastTangent = AnalyticalTangents (CenterL, RadiusL, CenterA, RadiusA); #debug concat ( "\n\n ***** TANGENTS FROM CIRCLE ", str (ElementA, 2, 0), " to ", str (ElementB, 2, 0), " ***** \n\n") #local CurrentTangent = AnalyticalTangents (CenterA, RadiusA, CenterB, RadiusB); #local OFlag = CurrentTangent [0].y; #local IFlag = CurrentTangent [5].y; // intersection, Outer tangent 1, 2, 3, 4, intersection, inner tangent 1, 2, 3, 4 union { // Arc angles are calculated counterclockwise. // this is right for "concave" arcs, but reversed for "convex" ones // use selected tangent colors for circles A and B to determine // endpoints of arc, and added flags for _which arc_ and direction // no tangents are chosen so there are no ARCS to choose and apply a color to torus {RadiusA, Line translate CenterA no_shadow} // 0 1 2 3 4 5 6 7 8 9 // {x, y, z, rad, # sides, rotation of polygon, Tangent by color, dir, Arc by color, dir} // ***** Code to include Bezier Arc points into prism definition ***** #if (OFlag) //------------------------------------------------------------------ #declare TVector = CurrentTangent[3] - CurrentTangent[1]; #declare TLength = vlength (TVector); #declare TAngle = Angle2D (x, TVector); cylinder {0, <1, 0, 0> Line pigment {LinePigment} no_shadow scale rotate -y*TAngle translate CurrentTangent[1]*<1, 0, 1> // <----- } //------------------------------------------------------------------ #declare TVector = CurrentTangent[4] - CurrentTangent[2]; #declare TLength = vlength (TVector); #declare TAngle = Angle2D (x, TVector); cylinder {0, <1, 0, 0> Line pigment {LinePigment} no_shadow scale rotate -y*TAngle translate CurrentTangent[2]*<1, 0, 1> // <----- } #else #warning "Outer tangents do not exist - not drawn." #end // end if OFlag #if (IFlag) //------------------------------------------------------------------ #declare TVector = CurrentTangent[8] - CurrentTangent[6]; #declare TLength = vlength (TVector); #declare TAngle = Angle2D (x, TVector); cylinder {0, <1, 0, 0> Line pigment {LinePigment} no_shadow scale rotate -y*TAngle translate CurrentTangent[6]*<1, 0, 1> // <----- } //------------------------------------------------------------------ #declare TVector = CurrentTangent[9] - CurrentTangent[7]; #declare TLength = vlength (TVector); #declare TAngle = Angle2D (x, TVector); cylinder {0, <1, 0, 0> Line pigment {LinePigment} no_shadow scale rotate -y*TAngle translate CurrentTangent[7]*<1, 0, 1> // <----- } //------------------------------------------------------------------ #else #warning "Inner tangents do not exist - not drawn." #end // end if IFlag pigment {LinePigment} } #end