diff --git a/demo/bezierCubic2Q2.html b/demo/bezierCubic2Q2.html
new file mode 100644
index 0000000..c9e4a4e
--- /dev/null
+++ b/demo/bezierCubic2Q2.html
@@ -0,0 +1,54 @@
+
+
+
+
+ 三次贝塞尔转二次
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/js/bezierCubic2Q2.js b/demo/js/bezierCubic2Q2.js
new file mode 100644
index 0000000..231170f
--- /dev/null
+++ b/demo/js/bezierCubic2Q2.js
@@ -0,0 +1,92 @@
+/**
+ * @file quadraticBezier.js
+ * @author mengke01
+ * @date
+ * @description
+ * 三次贝塞尔转二次
+ */
+
+define(
+ function(require) {
+
+ var bezierCubic2Q2 = require('math/bezierCubic2Q2');
+
+ var entry = {
+
+ /**
+ * 初始化
+ */
+ init: function() {
+ var canvas = $('#canvas').get(0);
+ var ctx = canvas.getContext('2d');
+ var width = canvas.offsetWidth;
+ var height = canvas.offsetHeight;
+
+ var points =[{"x":167,"y":449},{"x":233,"y":331},{"x":431,"y":332},{"x":481,"y":417}];
+
+ $(points).each(function(index, item) {
+ $('[data-index="'+index+'"]').css({
+ left: points[index].x,
+ top: points[index].y
+ })
+ });
+
+ var point;
+
+ $('.point').on('mousedown', function(e) {
+ point = $(this);
+ });
+
+ $(document.body).on('mousemove', onMove);
+ $(document.body).on('mouseup', function(e) {
+ onMove.call(this, e);
+ point = null;
+ });
+
+ function onMove(e) {
+ var px = e.pageX;
+ var py = e.pageY;
+ if(point) {
+ point.css({
+ left: px,
+ top: py
+ });
+ var p = points[+point.attr('data-index')];
+ p.x = px;
+ p.y = py;
+ draw();
+ }
+ }
+
+ function draw() {
+
+
+ ctx.clearRect(0, 0, width, height);
+ //绘制2次贝塞尔曲线
+ ctx.beginPath();
+ ctx.strokeStyle='red';
+
+ ctx.moveTo(points[0].x, points[0].y);
+ ctx.bezierCurveTo(points[1].x,points[1].y,points[2].x,points[2].y,points[3].x,points[3].y);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.strokeStyle='blue';
+ bezierCubic2Q2.apply(null, points).forEach(function(bezier) {
+ ctx.moveTo(bezier[0].x, bezier[0].y);
+ ctx.quadraticCurveTo(bezier[1].x, bezier[1].y, bezier[2].x, bezier[2].y);
+ });
+
+ ctx.stroke();
+
+ }
+
+ draw();
+ }
+ };
+
+ entry.init();
+
+ return entry;
+ }
+);
\ No newline at end of file
diff --git a/src/math/bezierCubic2Q2.js b/src/math/bezierCubic2Q2.js
index 3d6abb7..3efd836 100644
--- a/src/math/bezierCubic2Q2.js
+++ b/src/math/bezierCubic2Q2.js
@@ -14,6 +14,15 @@
define(
function(require) {
+
+ function toQuad(p1, c1, c2, p2) {
+ // Quad control point is (3*c2 - p2 + 3*c1 - p1)/4
+ var x = (3 * c2.x - p2.x + 3 * c1.x - p1.x) / 4;
+ var y = (3 * c2.y - p2.y + 3 * c1.y - p1.y) / 4;
+ return [p1, {x: x, y: y}, p2];
+ }
+
+
/**
* 三次贝塞尔转二次贝塞尔
*
@@ -24,8 +33,48 @@ define(
* @return {Array} 二次贝塞尔控制点
*/
function bezierCubic2Q2(p1, c1, c2, p2) {
-
- return [];
+
+ var mx = p2.x - 3 * c2.x + 3 * c1.x - p1.x;
+ var my = p2.y - 3 * c2.y + 3 * c1.y - p1.y;
+
+ // control points near
+ if (mx * mx + my * my <= 4) {
+ return [ toQuad(p1, c1, c2, p2) ];
+ }
+
+ // Split to 2 qubic beziers by midpoints
+ // (p2 + 3*c2 + 3*c1 + p1)/8
+ var mp = {
+ x: (p2.x + 3 * c2.x + 3 * c1.x + p1.x) / 8,
+ y: (p2.y + 3 * c2.y + 3 * c1.y + p1.y) / 8
+ };
+
+ return [
+ toQuad(
+ p1,
+ {
+ x: (p1.x + c1.x) / 2,
+ y: (p1.y + c1.y) / 2
+ },
+ {
+ x: (p1.x + 2 * c1.x + c2.x) / 4,
+ y: (p1.y + 2 * c1.y + c2.y) / 4
+ },
+ mp
+ ),
+ toQuad(
+ mp,
+ {
+ x: (p2.x + c1.x + 2 * c2.x) / 4,
+ y: (p2.y + c1.y + 2 * c2.y) / 4
+ },
+ {
+ x: (p2.x + c2.x) / 2,
+ y: (p2.y + c2.y) / 2
+ },
+ p2
+ )
+ ];
}
return bezierCubic2Q2;
diff --git a/src/ttf/util/svg2contours.js b/src/ttf/util/svg2contours.js
index 0869c1b..5fdd8f7 100644
--- a/src/ttf/util/svg2contours.js
+++ b/src/ttf/util/svg2contours.js
@@ -10,6 +10,53 @@
define(
function(require) {
+ var bezierCubic2Q2 = require('math/bezierCubic2Q2');
+
+ function cubic2Points(cubicList, contour) {
+ // 三次贝塞尔转二次
+ var q2List = [];
+ cubicList.forEach(function(c) {
+ q2List = q2List.concat(bezierCubic2Q2(c[0], c[1], c[2], c[3]));
+ });
+
+ var q2, prevq2;
+ for (var i = 0, l = q2List.length; i < l; i++) {
+ q2 = q2List[i];
+ if (i == 0) {
+ contour.push({
+ x: q2[1].x,
+ y: q2[1].y
+ });
+ contour.push({
+ x: q2[2].x,
+ y: q2[2].y,
+ onCurve: true
+ });
+ }
+ else {
+ prevq2 = q2List[i - 1];
+ // 检查是否存在切线点
+ if (prevq2[1].x + q2[1].x == 2 * q2[0].x && prevq2[1].y + q2[1].y == 2 * q2[0].y) {
+ contour.pop();
+ }
+ contour.push({
+ x: q2[1].x,
+ y: q2[1].y
+ });
+ }
+ }
+
+ contour.push({
+ x: q2[2].x,
+ y: q2[2].y,
+ onCurve: true
+ });
+
+ return contour;
+ }
+
+
+
/**
* svg path 转 contours
*
@@ -65,7 +112,7 @@ define(
segments.push({cmd:'Z'});
// 解析segments
- var contours = [], contour = [], prevX = 0, prevY = 0;
+ var contours = [], contour = [], prevX = 0, prevY = 0, prevc1;
for (var i = 0, l = segments.length;i < l; i++) {
segment = segments[i];
@@ -147,14 +194,79 @@ define(
y: prevY,
onCurve: true
});
+
}
// 三次贝塞尔
else if (segment.cmd === 'C') {
- throw 'not support cubic bezier';
+
+ // 这里可能会连续绘制,最后一个是终点
+ var q = 0, ql = segment.args.length - 2;
+ var cubicList = [];
+
+ for (; q < ql ; q += 4) {
+ var cubic = [];
+ var c1 = {
+ x: prevX + segment.args[q],
+ y: prevY + segment.args[q + 1]
+ };
+ var c2 = {
+ x: prevX + segment.args[q + 2],
+ y: prevY + segment.args[q + 3]
+ };
+
+ // 计算中间点
+ if (q == 0) {
+ cubic.push({x: prevX, y: prevY});
+ cubic.push(c1);
+ cubic.push(c2);
+ }
+ else {
+ var prevC2 = cubicList[cubicList.length - 1][2];
+ var p1 = {
+ x: (prevC2.x + c1.x) / 2,
+ y: (prevC2.y + c1.y) / 2
+ }
+ cubicList[cubicList.length - 1][3] = p1;
+
+ cubic.push(p1);
+ cubic.push(c1);
+ cubic.push(c2);
+ }
+
+ cubicList.push(cubic);
+ }
+
+ prevX += segment.args[ql];
+ prevY += segment.args[ql + 1];
+
+ cubicList[cubicList.length - 1].push({x: prevX, y: prevY});
+ cubic2Points(cubicList, contour);
+ prevc1 = cubicList[cubicList.length - 1][2];
}
// 三次贝塞尔平滑
else if (segment.cmd === 'S') {
- throw 'not support cubic bezier';
+
+ // 这里需要移除上一个曲线的终点
+ var p1 = contour.pop();
+ var c1 = {
+ x: 2 * p1.x - prevc1.x,
+ y: 2 * p1.y - prevc1.y
+ };
+ var c2 = {
+ x: prevX + segment.args[0],
+ y: prevX + segment.args[1]
+ };
+
+ prevX += segment.args[2];
+ prevY += segment.args[3];
+
+ var p2 = {
+ x: prevX,
+ y: prevY
+ };
+
+ cubic2Points([[p1, c1, c2, p2]], contour);
+ prevc1 = c2;
}
// 圆弧
else if (segment.cmd === 'A') {