
Printed from www.flong.com/texts/code/shapers_circ/
Contents © 2020 Golan Levin and Collaborators
Golan Levin and Collaborators
Code
- Peer-Reviewed Publications
- Essays and Statements
- Interviews and Dialogues
- Catalogues and Lists
- Project Reports
- Press Clippings
- Lectures
- Code
- Misc.
- 07 2006. Shaping Functions: Polynomial
- 07 2006. Shaping Functions: Exponential
- 07 2006. Shaping Functions: Circular and Elliptical
- 07 2006. Shaping Functions: Bezier and Parametric
Circular & Elliptical Shaping Functions
- Circular Interpolation: Ease-In and Ease-Out
- Double-Circle Seat
- Double-Circle Sigmoid
- Double-Elliptic Seat
- Double-Elliptic Sigmoid
- Double-Linear with Circular Fillet
- Circular Arc Through a Given Point
Circular Interpolation: Ease-In and Ease-Out
A circular arc offers a quick and easy-to-code method for easing in or out of the unit square. The computational efficiency of the function is diminished by its use of a square root, however.
Ease-In interpolation:
Ease-Out interpolation:
//------------------------------ float circularEaseIn (float x){ float y = 1 - sqrt(1 - x*x); return y; } //------------------------------ float circularEaseOut (float x){ float y = sqrt(1 - sq(1 - x)); return y; }
Double-Circle Seat
This shaping function is formed by the meeting of two circular arcs, which join with a horizontal tangent. The parameter a, in the range [0...1], governs the location of the curve's inflection point along the diagonal of the unit square.
//---------------------------------------- float doubleCircleSeat (float x, float a){ float min_param_a = 0.0; float max_param_a = 1.0; a = max(min_param_a, min(max_param_a, a)); float y = 0; if (x<=a){ y = sqrt(sq(a) - sq(x-a)); } else { y = 1 - sqrt(sq(1-a) - sq(x-a)); } return y; }
Double-Circle Sigmoid
This sigmoidal shaping function is formed by the meeting of two circular arcs, which join with a vertical tangent. The parameter a, in the range [0...1], governs the location of the curve's inflection point along the diagonal of the unit square.
//------------------------------------------- float doubleCircleSigmoid (float x, float a){ float min_param_a = 0.0; float max_param_a = 1.0; a = max(min_param_a, min(max_param_a, a)); float y = 0; if (x<=a){ y = a - sqrt(a*a - x*x); } else { y = a + sqrt(sq(1-a) - sq(x-1)); } return y; }
Double-Elliptic Seat
This seat-shaped function is created by the joining of two elliptical arcs, and is a generalization of the Double-Circle Seat. The two arcs meet at the coordinate (a,b) with a horizontal tangent.
//--------------------------------------------------- float doubleEllipticSeat (float x, float a, float b){ float epsilon = 0.00001; float min_param_a = 0.0 + epsilon; float max_param_a = 1.0 - epsilon; float min_param_b = 0.0; float max_param_b = 1.0; a = max(min_param_a, min(max_param_a, a)); b = max(min_param_b, min(max_param_b, b)); float y = 0; if (x<=a){ y = (b/a) * sqrt(sq(a) - sq(x-a)); } else { y = 1- ((1-b)/(1-a))*sqrt(sq(1-a) - sq(x-a)); } return y; }
Double-Elliptic Sigmoid
This sigmoid-shaped function is created by the joining of two elliptical arcs, and is a generalization of the Double-Circle Sigmoid. The arcs meet at the coordinate (a, b) in the unit square with a vertical tangent.
//------------------------------------------------------ float doubleEllipticSigmoid (float x, float a, float b){ float epsilon = 0.00001; float min_param_a = 0.0 + epsilon; float max_param_a = 1.0 - epsilon; float min_param_b = 0.0; float max_param_b = 1.0; a = max(min_param_a, min(max_param_a, a)); b = max(min_param_b, min(max_param_b, b)); float y = 0; if (x<=a){ y = b * (1 - (sqrt(sq(a) - sq(x))/a)); } else { y = b + ((1-b)/(1-a))*sqrt(sq(1-a) - sq(x-1)); } return y; }
Double-Linear with Circular Fillet
This pattern joins two straight lines with a circular arc whose radius is adjustable. The user specifies the fillet's radius (with parameter c) and the coordinate in the unit square where the lines would otherwise intersect (with parameters a and b). This pattern is adapted from Robert D. Miller's "Joining Two Lines with a Circular Arc Fillet", which appears in Graphics Gems III.
//-------------------------------------------------------- // Joining Two Lines with a Circular Arc Fillet // Adapted from Robert D. Miller / Graphics Gems III. float arcStartAngle; float arcEndAngle; float arcStartX, arcStartY; float arcEndX, arcEndY; float arcCenterX, arcCenterY; float arcRadius; //-------------------------------------------------------- float circularFillet (float x, float a, float b, float R){ float epsilon = 0.00001; float min_param_a = 0.0 + epsilon; float max_param_a = 1.0 - epsilon; float min_param_b = 0.0 + epsilon; float max_param_b = 1.0 - epsilon; a = max(min_param_a, min(max_param_a, a)); b = max(min_param_b, min(max_param_b, b)); computeFilletParameters (0,0, a,b, a,b, 1,1, R); float t = 0; float y = 0; x = max(0, min(1, x)); if (x <= arcStartX){ t = x / arcStartX; y = t * arcStartY; } else if (x >= arcEndX){ t = (x - arcEndX)/(1 - arcEndX); y = arcEndY + t*(1 - arcEndY); } else { if (x >= arcCenterX){ y = arcCenterY - sqrt(sq(arcRadius) - sq(x-arcCenterX)); } else{ y = arcCenterY + sqrt(sq(arcRadius) - sq(x-arcCenterX)); } } return y; } //------------------------------------------ // Return signed distance from line Ax + By + C = 0 to point P. float linetopoint (float a, float b, float c, float ptx, float pty){ float lp = 0.0; float d = sqrt((a*a)+(b*b)); if (d != 0.0){ lp = (a*ptx + b*pty + c)/d; } return lp; } //------------------------------------------ // Compute the parameters of a circular arc // fillet between lines L1 (p1 to p2) and // L2 (p3 to p4) with radius R. // void computeFilletParameters ( float p1x, float p1y, float p2x, float p2y, float p3x, float p3y, float p4x, float p4y, float r){ float c1 = p2x*p1y - p1x*p2y; float a1 = p2y-p1y; float b1 = p1x-p2x; float c2 = p4x*p3y - p3x*p4y; float a2 = p4y-p3y; float b2 = p3x-p4x; if ((a1*b2) == (a2*b1)){ /* Parallel or coincident lines */ return; } float d1, d2; float mPx, mPy; mPx = (p3x + p4x)/2.0; mPy = (p3y + p4y)/2.0; d1 = linetopoint(a1,b1,c1,mPx,mPy); /* Find distance p1p2 to p3 */ if (d1 == 0.0) { return; } mPx = (p1x + p2x)/2.0; mPy = (p1y + p2y)/2.0; d2 = linetopoint(a2,b2,c2,mPx,mPy); /* Find distance p3p4 to p2 */ if (d2 == 0.0) { return; } float c1p, c2p, d; float rr = r; if (d1 <= 0.0) { rr= -rr; } c1p = c1 - rr*sqrt((a1*a1)+(b1*b1)); /* Line parallel l1 at d */ rr = r; if (d2 <= 0.0){ rr = -rr; } c2p = c2 - rr*sqrt((a2*a2)+(b2*b2)); /* Line parallel l2 at d */ d = (a1*b2)-(a2*b1); float pCx = (c2p*b1-c1p*b2)/d; /* Intersect constructed lines */ float pCy = (c1p*a2-c2p*a1)/d; /* to find center of arc */ float pAx = 0; float pAy = 0; float pBx = 0; float pBy = 0; float dP,cP; dP = (a1*a1) + (b1*b1); /* Clip or extend lines as required */ if (dP != 0.0){ cP = a1*pCy - b1*pCx; pAx = (-a1*c1 - b1*cP)/dP; pAy = ( a1*cP - b1*c1)/dP; } dP = (a2*a2) + (b2*b2); if (dP != 0.0){ cP = a2*pCy - b2*pCx; pBx = (-a2*c2 - b2*cP)/dP; pBy = ( a2*cP - b2*c2)/dP; } float gv1x = pAx-pCx; float gv1y = pAy-pCy; float gv2x = pBx-pCx; float gv2y = pBy-pCy; float arcStart = (float) atan2(gv1y,gv1x); float arcAngle = 0.0; float dd = (float) sqrt(((gv1x*gv1x)+(gv1y*gv1y)) * ((gv2x*gv2x)+(gv2y*gv2y))); if (dd != (float) 0.0){ arcAngle = (acos((gv1x*gv2x + gv1y*gv2y)/dd)); } float crossProduct = (gv1x*gv2y - gv2x*gv1y); if (crossProduct < 0.0){ arcStart -= arcAngle; } float arc1 = arcStart; float arc2 = arcStart + arcAngle; if (crossProduct < 0.0){ arc1 = arcStart + arcAngle; arc2 = arcStart; } arcCenterX = pCx; arcCenterY = pCy; arcStartAngle = arc1; arcEndAngle = arc2; arcRadius = r; arcStartX = arcCenterX + arcRadius*cos(arcStartAngle); arcStartY = arcCenterY + arcRadius*sin(arcStartAngle); arcEndX = arcCenterX + arcRadius*cos(arcEndAngle); arcEndY = arcCenterY + arcRadius*sin(arcEndAngle); }
Circular Arc Through a Given Point
This function defines a circular arc which passes through a user-specified point in the unit square. Unfortunately, not every location in the unit square lends itself to defining a circle which also is confined to the unit square; the user-supplied point must inhabit a zone close to the main (Identity) diagonal. This pattern is adapted from Paul Bourke's Equation of a Circle From 3 Points.
//--------------------------------------------------------- // Adapted from Paul Bourke float m_Centerx; float m_Centery; float m_dRadius; float circularArcThroughAPoint (float x, float a, float b){ float epsilon = 0.00001; float min_param_a = 0.0 + epsilon; float max_param_a = 1.0 - epsilon; float min_param_b = 0.0 + epsilon; float max_param_b = 1.0 - epsilon; a = min(max_param_a, max(min_param_a, a)); b = min(max_param_b, max(min_param_b, b)); x = min(1.0-epsilon, max(0.0+epsilon, x)); float pt1x = 0; float pt1y = 0; float pt2x = a; float pt2y = b; float pt3x = 1; float pt3y = 1; if (!IsPerpendicular(pt1x,pt1y, pt2x,pt2y, pt3x,pt3y)) calcCircleFrom3Points (pt1x,pt1y, pt2x,pt2y, pt3x,pt3y); else if (!IsPerpendicular(pt1x,pt1y, pt3x,pt3y, pt2x,pt2y)) calcCircleFrom3Points (pt1x,pt1y, pt3x,pt3y, pt2x,pt2y); else if (!IsPerpendicular(pt2x,pt2y, pt1x,pt1y, pt3x,pt3y)) calcCircleFrom3Points (pt2x,pt2y, pt1x,pt1y, pt3x,pt3y); else if (!IsPerpendicular(pt2x,pt2y, pt3x,pt3y, pt1x,pt1y)) calcCircleFrom3Points (pt2x,pt2y, pt3x,pt3y, pt1x,pt1y); else if (!IsPerpendicular(pt3x,pt3y, pt2x,pt2y, pt1x,pt1y)) calcCircleFrom3Points (pt3x,pt3y, pt2x,pt2y, pt1x,pt1y); else if (!IsPerpendicular(pt3x,pt3y, pt1x,pt1y, pt2x,pt2y)) calcCircleFrom3Points (pt3x,pt3y, pt1x,pt1y, pt2x,pt2y); else { return 0; } // constrain if ((m_Centerx > 0) && (m_Centerx < 1)){ if (a < m_Centerx){ m_Centerx = 1; m_Centery = 0; m_dRadius = 1; } else { m_Centerx = 0; m_Centery = 1; m_dRadius = 1; } } float y = 0; if (x >= m_Centerx){ y = m_Centery - sqrt(sq(m_dRadius) - sq(x-m_Centerx)); } else { y = m_Centery + sqrt(sq(m_dRadius) - sq(x-m_Centerx)); } return y; } //---------------------- boolean IsPerpendicular( float pt1x, float pt1y, float pt2x, float pt2y, float pt3x, float pt3y) { // Check the given point are perpendicular to x or y axis float yDelta_a = pt2y - pt1y; float xDelta_a = pt2x - pt1x; float yDelta_b = pt3y - pt2y; float xDelta_b = pt3x - pt2x; float epsilon = 0.000001; // checking whether the line of the two pts are vertical if (abs(xDelta_a) <= epsilon && abs(yDelta_b) <= epsilon){ return false; } if (abs(yDelta_a) <= epsilon){ return true; } else if (abs(yDelta_b) <= epsilon){ return true; } else if (abs(xDelta_a)<= epsilon){ return true; } else if (abs(xDelta_b)<= epsilon){ return true; } else return false; } //-------------------------- void calcCircleFrom3Points ( float pt1x, float pt1y, float pt2x, float pt2y, float pt3x, float pt3y) { float yDelta_a = pt2y - pt1y; float xDelta_a = pt2x - pt1x; float yDelta_b = pt3y - pt2y; float xDelta_b = pt3x - pt2x; float epsilon = 0.000001; if (abs(xDelta_a) <= epsilon && abs(yDelta_b) <= epsilon){ m_Centerx = 0.5*(pt2x + pt3x); m_Centery = 0.5*(pt1y + pt2y); m_dRadius = sqrt(sq(m_Centerx-pt1x) + sq(m_Centery-pt1y)); return; } // IsPerpendicular() assure that xDelta(s) are not zero float aSlope = yDelta_a / xDelta_a; float bSlope = yDelta_b / xDelta_b; if (abs(aSlope-bSlope) <= epsilon){ // checking whether the given points are colinear. return; } // calc center m_Centerx = ( aSlope*bSlope*(pt1y - pt3y) + bSlope*(pt1x + pt2x) - aSlope*(pt2x+pt3x) ) /(2* (bSlope-aSlope) ); m_Centery = -1*(m_Centerx - (pt1x+pt2x)/2)/aSlope + (pt1y+pt2y)/2; m_dRadius = sqrt(sq(m_Centerx-pt1x) + sq(m_Centery-pt1y)); }