add cubic bezier 2 quatic bezier
This commit is contained in:
54
demo/bezierCubic2Q2.html
Normal file
54
demo/bezierCubic2Q2.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>三次贝塞尔转二次</title>
|
||||
<script src="../dep/esl.js"></script>
|
||||
<script src="../dep/jquery.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.point {
|
||||
position: absolute;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
margin-left: -5px;
|
||||
margin-top: -5px;
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
#start-point {
|
||||
border-color: green;
|
||||
}
|
||||
|
||||
#end-point {
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
#control-point {
|
||||
border-color: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="start-point" class="point" data-index="0"></div>
|
||||
<div id="end-point" class="point" data-index="1"></div>
|
||||
<div id="control-point" class="point" data-index="2"></div>
|
||||
<div class="point" data-index="3"></div>
|
||||
<canvas id="canvas" width="800" height="800"></canvas>
|
||||
<script>
|
||||
require.config({
|
||||
baseUrl: '../src',
|
||||
paths: {
|
||||
demo: '../demo/js',
|
||||
}
|
||||
});
|
||||
define('jquery', $);
|
||||
</script>
|
||||
|
||||
<script>require(['demo/bezierCubic2Q2'])</script>
|
||||
</body>
|
||||
</html>
|
||||
92
demo/js/bezierCubic2Q2.js
Normal file
92
demo/js/bezierCubic2Q2.js
Normal file
@@ -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;
|
||||
}
|
||||
);
|
||||
@@ -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;
|
||||
|
||||
@@ -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') {
|
||||
|
||||
Reference in New Issue
Block a user