未验证 提交 d0e39c4c 编写于 作者: M Mr.doob 提交者: GitHub

Merge pull request #13919 from ndebeiss/SVGEllipticalArc

SVGLoader: adding support for elliptical arc
......@@ -221,14 +221,17 @@ THREE.SVGLoader.prototype = {
break;
case 'A':
console.warn( command );
var numbers = parseFloats( data );
for ( var j = 0, jl = numbers.length; j < jl; j += 7 ) {
// TODO
point.x = numbers[ j + 5 ];
point.y = numbers[ j + 6 ];
var radius = { x: numbers[ j ], y: numbers[ j + 1 ] };
var x_axis_rotation = numbers[ j + 2 ] * Math.PI / 180;
svgEllipsisToThreeEllipsis( path, control, radius, x_axis_rotation, numbers[ j + 3 ], numbers[ j + 4 ], point );
control.x = point.x;
control.y = point.y;
}
break;
......@@ -372,6 +375,64 @@ THREE.SVGLoader.prototype = {
}
/**
* https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
* https://mortoray.com/2017/02/16/rendering-an-svg-elliptical-arc-as-bezier-curves/ Appendix: Endpoint to center arc conversion
* From
* rx ry x-axis-rotation large-arc-flag sweep-flag x y
* To
* aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation
*/
function svgEllipsisToThreeEllipsis( path, start, radius, x_axis_rotation, large_arc_flag, sweep_flag, end ) {
//F.6.6 Correction of out-of-range radii
//Step 2: Ensure radii are positive
var rX = Math.abs( radius.x );
var rY = Math.abs( radius.y );
// Step 1: Compute (x1′, y1′)
var midDist = new THREE.Vector2().subVectors( start, end ).multiplyScalar( 0.5 );
var x1p = Math.cos( x_axis_rotation ) * midDist.x + Math.sin( x_axis_rotation ) * midDist.y;
var y1p = - Math.sin( x_axis_rotation ) * midDist.x + Math.cos( x_axis_rotation ) * midDist.y;
// Step 2: Compute (cx′, cy′)
var rxs = rX * rX;
var rys = rY * rY;
var x1ps = x1p * x1p;
var y1ps = y1p * y1p;
//Step 3: Ensure radii are large enough
var cr = x1ps / rxs + y1ps / rys;
if (cr > 1) {
//scale up rX,rY equally so cr == 1
var s = Math.sqrt(cr);
rX = s * rX;
rY = s * rY;
rxs = rX * rX;
rys = rY * rY;
}
var dq = ( rxs * y1ps + rys * x1ps );
var pq = ( rxs * rys - dq ) / dq;
var q = Math.sqrt( pq );
if ( large_arc_flag === sweep_flag ) q = - q;
var cxp = q * rX * y1p / rY;
var cyp = - q * rY * x1p / rX;
// Step 3: Compute (cx, cy) from (cx′, cy′)
var cx = Math.cos( x_axis_rotation ) * cxp - Math.sin( x_axis_rotation ) * cyp + ( start.x + end.x ) / 2;
var cy = Math.sin( x_axis_rotation ) * cxp + Math.cos( x_axis_rotation ) * cyp + ( start.y + end.y ) / 2;
// Step 4: Compute θ1 and Δθ
var startAngle = new THREE.Vector2( ( x1p - cxp ) / rX, ( y1p - cyp ) / rY ).angle();
var endAngle = new THREE.Vector2( ( - x1p - cxp ) / rX, ( - y1p - cyp ) / rY ).angle();
if ( ! sweep_flag ) endAngle -= 2 * Math.PI;
path.currentPath.absellipse( cx, cy, rX, rY, startAngle, endAngle, endAngle > startAngle, x_axis_rotation );
}
/*
* According to https://www.w3.org/TR/SVG/shapes.html#RectElementRXAttribute
* rounded corner should be rendered to elliptical arc, but bezier curve does the job well enough
......@@ -586,4 +647,4 @@ THREE.SVGLoader.prototype = {
}
};
};
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册