增加 多路径求合并

This commit is contained in:
mkwiser
2014-10-28 02:19:53 +08:00
parent 8a3feb2acc
commit 4637e9a3bf
19 changed files with 447 additions and 113 deletions

View File

@@ -5,4 +5,6 @@ fonteditor 在线ttf字体编辑器
1. 2014-10-7 ttf管理器发布[试玩版地址](http://mkwiser.sinaapp.com/fonteditor/ttf.html).
2. 2014-10-19 ttf编辑器发布[试玩版地址](http://mkwiser.sinaapp.com/fonteditor/index.html).
2. 2014-10-19 ttf编辑器发布[试玩版地址](http://mkwiser.sinaapp.com/fonteditor/index.html).
3. 2014-10-28 ttf编辑器增加路径合集、交集、差集[试玩版地址](http://mkwiser.sinaapp.com/fonteditor/index.html).

View File

@@ -1,6 +1,6 @@
define(
function(require) {
return {"xMin":26,"yMin":-10,"xMax":601,"yMax":212,"unicode":[57357],"advanceWidth":633,"leftSideBearing":26,"name":"uniE00D","contours":[[{"x":230,"y":159},{"x":230,"y":84},{"x":283,"y":31},{"x":357,"y":31},{"x":410,"y":84},{"x":410,"y":159},{"x":357,"y":212},{"x":283,"y":212}],[{"x":597,"y":61,"onCurve":true},{"x":597,"y":62},{"x":595,"y":63},{"x":594,"y":64,"onCurve":true},{"x":586,"y":78},{"x":572,"y":84,"onCurve":true},{"x":459,"y":177},{"x":312,"y":177,"onCurve":true},{"x":163,"y":177},{"x":49,"y":81,"onCurve":true},{"x":42,"y":76},{"x":37,"y":70,"onCurve":true},{"x":36,"y":69},{"x":33,"y":66},{"x":33,"y":65,"onCurve":true},{"x":26,"y":53},{"x":26,"y":39,"onCurve":true},{"x":26,"y":19},{"x":55,"y":-10},{"x":75,"y":-10,"onCurve":true},{"x":91,"y":-10},{"x":105,"y":0,"onCurve":true},{"x":193,"y":80},{"x":312,"y":80,"onCurve":true},{"x":435,"y":80},{"x":524,"y":-4,"onCurve":true},{"x":526,"y":-2,"onCurve":true},{"x":538,"y":-10},{"x":552,"y":-10,"onCurve":true},{"x":573,"y":-10},{"x":601,"y":19},{"x":601,"y":39,"onCurve":true},{"x":601,"y":50},{"x":596,"y":60,"onCurve":true}]]};
return {"xMin":37,"yMin":78,"xMax":439,"yMax":390,"unicode":[57357],"advanceWidth":633,"leftSideBearing":37,"name":"uniE00D","contours":[[{"x":74,"y":289,"onCurve":true},{"x":98,"y":277},{"x":128,"y":277,"onCurve":true},{"x":160,"y":277},{"x":186,"y":292,"onCurve":true},{"x":225,"y":254},{"x":281,"y":254,"onCurve":true},{"x":337,"y":254},{"x":377,"y":292,"onCurve":true},{"x":416,"y":329},{"x":416,"y":383,"onCurve":true},{"x":416,"y":387},{"x":416,"y":390,"onCurve":true},{"x":428,"y":390,"onCurve":true},{"x":428,"y":78,"onCurve":true},{"x":74,"y":78,"onCurve":true}],[{"x":439,"y":210,"onCurve":true},{"x":437,"y":259},{"x":400,"y":295,"onCurve":true},{"x":360,"y":332},{"x":304,"y":332,"onCurve":true},{"x":250,"y":332},{"x":212,"y":298,"onCurve":true},{"x":185,"y":314},{"x":151,"y":314,"onCurve":true},{"x":103,"y":314},{"x":37,"y":251},{"x":37,"y":160},{"x":70,"y":129,"onCurve":true},{"x":83,"y":117},{"x":97,"y":109,"onCurve":true},{"x":97,"y":210,"onCurve":true}]]}
}
);

View File

@@ -1,6 +1,6 @@
define(
function(require) {
return {"xMin":0,"yMin":-32,"xMax":512,"yMax":480,"unicode":[57358],"advanceWidth":512,"leftSideBearing":0,"name":"uniE00E","contours":[[{"x":362,"y":480},{"x":150,"y":480},{"x":0,"y":330},{"x":0,"y":118},{"x":150,"y":-32},{"x":362,"y":-32},{"x":512,"y":118},{"x":512,"y":330}],[{"x":360,"y":148,"onCurve":true},{"x":365,"y":143},{"x":365,"y":127},{"x":360,"y":122,"onCurve":true},{"x":351,"y":113,"onCurve":true},{"x":345,"y":108},{"x":330,"y":108},{"x":325,"y":113,"onCurve":true},{"x":254,"y":184,"onCurve":true},{"x":183,"y":114,"onCurve":true},{"x":178,"y":108},{"x":163,"y":108},{"x":157,"y":114,"onCurve":true},{"x":148,"y":122,"onCurve":true},{"x":143,"y":128},{"x":143,"y":143},{"x":148,"y":148,"onCurve":true},{"x":219,"y":219,"onCurve":true},{"x":148,"y":290,"onCurve":true},{"x":143,"y":295},{"x":143,"y":311},{"x":148,"y":316,"onCurve":true},{"x":157,"y":325,"onCurve":true},{"x":162,"y":330},{"x":177,"y":330},{"x":183,"y":325,"onCurve":true},{"x":254,"y":254,"onCurve":true},{"x":325,"y":325,"onCurve":true},{"x":330,"y":330},{"x":346,"y":330},{"x":351,"y":325,"onCurve":true},{"x":360,"y":316,"onCurve":true},{"x":365,"y":311},{"x":365,"y":295},{"x":360,"y":290,"onCurve":true},{"x":289,"y":219,"onCurve":true}]]};
return {"xMin":58,"yMin":142,"xMax":504,"yMax":446,"unicode":[57357],"advanceWidth":633,"leftSideBearing":58,"name":"uniE00D","contours":[[{"x":404.20977,"y":306,"onCurve":true},{"x":587,"y":306,"onCurve":true},{"x":587,"y":580,"onCurve":true},{"x":314,"y":580,"onCurve":true},{"x":314,"y":353.71808,"onCurve":true},{"x":323.41730917023847,"y":355},{"x":334,"y":355,"onCurve":true},{"x":364,"y":355},{"x":386,"y":344,"onCurve":true},{"x":407,"y":333},{"x":407,"y":317,"onCurve":true},{"x":407,"y":311.1678292421482}],[{"x":659,"y":337},{"x":683,"y":328,"onCurve":true},{"x":707,"y":319},{"x":707,"y":306,"onCurve":true},{"x":707,"y":294},{"x":683,"y":285,"onCurve":true},{"x":659,"y":276},{"x":625,"y":276,"onCurve":true},{"x":591,"y":276},{"x":568,"y":285,"onCurve":true},{"x":545,"y":294},{"x":545,"y":306,"onCurve":true},{"x":545,"y":319},{"x":568,"y":328,"onCurve":true},{"x":591,"y":337},{"x":625,"y":337,"onCurve":true}]]};
}
);

6
demo/js/contours-4.js Normal file
View File

@@ -0,0 +1,6 @@
define(
function(require) {
return {"xMin":63,"yMin":176,"xMax":278,"yMax":406,"unicode":[57357],"advanceWidth":633,"leftSideBearing":63,"name":"uniE00D","contours":[[{"x":198,"y":176},{"x":214,"y":243},{"x":187,"y":337},{"x":131,"y":404},{"x":79,"y":404},{"x":63,"y":337},{"x":90,"y":243},{"x":146,"y":176}],[{"x":197,"y":177},{"x":247,"y":244},{"x":278,"y":339},{"x":272,"y":406},{"x":231,"y":406},{"x":181,"y":339},{"x":150,"y":244},{"x":156,"y":177}]]}
}
);

6
demo/js/contours-5.js Normal file
View File

@@ -0,0 +1,6 @@
define(
function(require) {
return {"xMin":114,"yMin":-6,"xMax":486,"yMax":430,"unicode":[57357],"advanceWidth":633,"leftSideBearing":114,"name":"uniE00D","contours":[[{"x":226,"y":255},{"x":272,"y":301},{"x":272,"y":367},{"x":226,"y":413},{"x":160,"y":413},{"x":114,"y":367},{"x":114,"y":301},{"x":160,"y":255}],[{"x":431,"y":242},{"x":486,"y":297},{"x":486,"y":375},{"x":431,"y":430},{"x":353,"y":430},{"x":298,"y":375},{"x":298,"y":297},{"x":353,"y":242}],[{"x":326,"y":-6},{"x":380,"y":48},{"x":380,"y":124},{"x":326,"y":178},{"x":249,"y":178},{"x":196,"y":124},{"x":196,"y":48},{"x":249,"y":-6}],[{"x":178,"y":340,"onCurve":true},{"x":423,"y":340,"onCurve":true},{"x":423,"y":113,"onCurve":true},{"x":178,"y":113,"onCurve":true}]]}
}
);

6
demo/js/contours-6.js Normal file
View File

@@ -0,0 +1,6 @@
define(
function(require) {
return {"xMin":55,"yMin":-7,"xMax":494,"yMax":430,"unicode":[57357],"advanceWidth":633,"leftSideBearing":55,"name":"uniE00D","contours":[[{"x":319,"y":259},{"x":342,"y":282,"onCurve":true},{"x":365,"y":305},{"x":365,"y":338,"onCurve":true},{"x":365,"y":371},{"x":342,"y":394,"onCurve":true},{"x":319,"y":417},{"x":286,"y":417,"onCurve":true},{"x":253,"y":417},{"x":230,"y":394,"onCurve":true},{"x":207,"y":371},{"x":207,"y":338,"onCurve":true},{"x":207,"y":305},{"x":230,"y":282,"onCurve":true},{"x":253,"y":259},{"x":286,"y":259,"onCurve":true}],[{"x":431,"y":242},{"x":459,"y":270,"onCurve":true},{"x":486,"y":297},{"x":486,"y":336,"onCurve":true},{"x":486,"y":375},{"x":459,"y":403,"onCurve":true},{"x":431,"y":430},{"x":392,"y":430,"onCurve":true},{"x":353,"y":430},{"x":326,"y":403,"onCurve":true},{"x":298,"y":375},{"x":298,"y":336,"onCurve":true},{"x":298,"y":297},{"x":326,"y":270,"onCurve":true},{"x":353,"y":242},{"x":392,"y":242,"onCurve":true}],[{"x":185,"y":6},{"x":212,"y":33,"onCurve":true},{"x":239,"y":60},{"x":239,"y":98,"onCurve":true},{"x":239,"y":136},{"x":212,"y":163,"onCurve":true},{"x":185,"y":190},{"x":147,"y":190,"onCurve":true},{"x":108,"y":190},{"x":82,"y":163,"onCurve":true},{"x":55,"y":136},{"x":55,"y":98,"onCurve":true},{"x":55,"y":60},{"x":82,"y":33,"onCurve":true},{"x":108,"y":6},{"x":147,"y":6,"onCurve":true}],[{"x":178,"y":340,"onCurve":true},{"x":423,"y":340,"onCurve":true},{"x":423,"y":113,"onCurve":true},{"x":178,"y":113,"onCurve":true}],[{"x":435,"y":-7},{"x":465,"y":23,"onCurve":true},{"x":494,"y":52},{"x":494,"y":94,"onCurve":true},{"x":494,"y":135},{"x":465,"y":165,"onCurve":true},{"x":435,"y":194},{"x":393,"y":194,"onCurve":true},{"x":351,"y":194},{"x":322,"y":165,"onCurve":true},{"x":293,"y":135},{"x":293,"y":94,"onCurve":true},{"x":293,"y":52},{"x":322,"y":23,"onCurve":true},{"x":351,"y":-7},{"x":393,"y":-7,"onCurve":true}]]}
}
);

6
demo/js/contours-7.js Normal file
View File

@@ -0,0 +1,6 @@
define(
function(require) {
return {"xMin":63,"yMin":176,"xMax":214,"yMax":407,"unicode":[57357],"advanceWidth":633,"leftSideBearing":63,"name":"uniE00D","contours":[[{"x":198,"y":176},{"x":206,"y":210,"onCurve":true},{"x":214,"y":243},{"x":201,"y":290,"onCurve":true},{"x":187,"y":337},{"x":159,"y":371,"onCurve":true},{"x":131,"y":404},{"x":105,"y":404,"onCurve":true},{"x":79,"y":404},{"x":71,"y":371,"onCurve":true},{"x":63,"y":337},{"x":77,"y":290,"onCurve":true},{"x":90,"y":243},{"x":118,"y":210,"onCurve":true},{"x":146,"y":176},{"x":172,"y":176,"onCurve":true}],[{"x":128,"y":178},{"x":153,"y":212,"onCurve":true},{"x":178,"y":245},{"x":194,"y":293,"onCurve":true},{"x":209,"y":340},{"x":206,"y":374,"onCurve":true},{"x":203,"y":407},{"x":183,"y":407,"onCurve":true},{"x":162,"y":407},{"x":137,"y":374,"onCurve":true},{"x":112,"y":340},{"x":97,"y":293,"onCurve":true},{"x":81,"y":245},{"x":84,"y":212,"onCurve":true},{"x":87,"y":178},{"x":108,"y":178,"onCurve":true}]]};
}
);

View File

@@ -11,7 +11,7 @@ define(
var lang = require('common/lang');
var editor = require('editor/main');
var shape_baidu = require('./contours-3');
var shape_baidu = require('./contours-1');
var isPathCross = require('graphics/isPathCross');
var pathJoin = require('graphics/pathJoin');
var util = require('graphics/util');
@@ -27,52 +27,51 @@ define(
var clonedShape = lang.clone(shape_baidu);
//clonedShape.contours[1].reverse();
// 插值
clonedShape.contours[0] = util.interpolate(clonedShape.contours[0]);
clonedShape.contours[1] = util.interpolate(clonedShape.contours[1]);
currentEditor = editor.create($('#render-view').get(0));
window.editor = currentEditor = editor.create($('#render-view').get(0));
currentEditor.setFont(clonedShape);
var jointLayer = currentEditor.fontLayer;
// var jointLayer = currentEditor.fontLayer;
var path0 = currentEditor.fontLayer.shapes[0].points;
var path1 = currentEditor.fontLayer.shapes[1].points;
var result = isPathCross(path0, path1);
// var paths = currentEditor.fontLayer.shapes.map(function(shape) {
// return shape.points;
// });
// // var result = isPathCross(path0, path1);
if (result && result.length) {
result.forEach(function(p, index){
jointLayer.addShape({
type: 'point',
x: p.x,
y: p.y,
style: {
fill: true,
fillColor: 'red'
}
});
});
// // if (result && result.length) {
// // result.forEach(function(p, index){
// // jointLayer.addShape({
// // type: 'point',
// // x: p.x,
// // y: p.y,
// // style: {
// // fill: true,
// // fillColor: 'red'
// // }
// // });
// // });
jointLayer.refresh();
}
// // jointLayer.refresh();
// // }
var paths = pathJoin(path0, path1, 4);
// var newPaths = pathJoin(paths, 4);
paths.forEach(function(p, index) {
jointLayer.addShape('path', {
points: lang.clone(p),
style: {
lineWidth: 2,
fill:true,
fillColor: index % 2 ? 'red' : 'blue'
}
});
});
// newPaths.forEach(function(p, index) {
// jointLayer.addShape('path', {
// points: lang.clone(p),
// style: {
// lineWidth: 2,
// fill:true,
// fillColor: index % 2 ? 'red' : 'blue'
// }
// });
// });
jointLayer.refresh();
// jointLayer.refresh();
}
};

View File

@@ -0,0 +1,103 @@
/**
* @file shapes.js
* @author mengke01
* @date
* @description
* 轮廓合并操作
*/
define(
function(require) {
var pathJoin = require('graphics/pathJoin');
var lang = require('common/lang');
// shape的组合操作
function combineShape(shapes, relation) {
var pathList = shapes.map(function(path) {
return path.points;
});
var result = pathJoin(pathList, relation);
// 检查有shapes没有改变过
var changed = false;
if (result.length === pathList.length) {
for (var i = 0, l = result.length; i < l; i ++ ) {
if (result[i] !== pathList[i]) {
changed = true;
break;
}
}
}
else {
changed = true;
}
// 有改变则更新节点集合
if (changed) {
var fontLayer = this.fontLayer;
var resultLength = result.length;
var shapesLength = shapes.length;
var length = Math.min(resultLength, shapesLength);
// 替换原来位置的
for (var i = 0; i < length; i++) {
shapes[i].points = lang.clone(result[i]);
}
// 移除多余的
if (shapesLength > length) {
for (var i = length; i < shapesLength; i++) {
fontLayer.removeShape(shapes[i]);
}
shapes.splice(length, shapesLength - length);
}
// 添加新的shape
if (resultLength > length) {
for (var i = length; i < resultLength; i++) {
var shape = fontLayer.addShape('path', {
points: result[i]
});
shapes.push(shape);
}
}
fontLayer.refresh();
}
return shapes;
}
var support = {
/**
* 结合
*/
joinshapes: function(shapes) {
combineShape.call(this, shapes, pathJoin.JOIN);
},
/**
* 相交
*/
intersectshapes: function(shapes) {
combineShape.call(this, shapes, pathJoin.INTERSECT);
},
/**
* 相切
*/
tangencyshapes: function(shapes) {
combineShape.call(this, shapes, pathJoin.TANGENCY);
}
};
return support;
}
);

View File

@@ -261,6 +261,7 @@ define(
};
lang.extend(support, require('./transform'));
lang.extend(support, require('./shapes'));
return support;
}

View File

@@ -107,6 +107,17 @@ define(
title: '复制'
},
'join_shapes': {
title: '结合'
},
'intersect_shapes': {
title: '相交'
},
'tangency_shapes': {
title: '相切'
},
'rotate_left': {
title: '向左旋转'

View File

@@ -72,6 +72,24 @@ define(
else if (command == 'down') {
this.execCommand('downshape', shape);
}
// 相交,结合和相切
else if (command == 'join_shapes') {
this.execCommand('joinshapes', shapes);
this.currentGroup.setShapes(shapes);
this.currentGroup.refresh();
}
else if (command == 'intersect_shapes') {
this.execCommand('intersectshapes', shapes);
this.currentGroup.setShapes(shapes);
this.currentGroup.refresh();
}
else if (command == 'tangency_shapes') {
this.execCommand('tangencyshapes', shapes);
this.currentGroup.setShapes(shapes);
this.currentGroup.refresh();
}
else if (command == 'rotate_left') {
this.execCommand('rotateleft', shapes);
this.currentGroup.setShapes(shapes);
@@ -141,7 +159,7 @@ define(
var shapeIndex = this.currentGroup.shapes.indexOf(shape);
if(shapeIndex >= 0) {
if (e.ctrlKey) {
if (e.ctrlKey && !e.altKey) {
this.currentGroup.shapes.splice(shapeIndex, 1);
this.currentGroup.setShapes(this.currentGroup.shapes);
this.currentGroup.refresh();

View File

@@ -13,16 +13,7 @@ define(
yMin:0,
xMax: 300,
yMax: 300,
points:[
{x:212, y: 300},
{x:300, y: 212},
{x:300, y: 88},
{x:212, y: 0},
{x:87, y: 0},
{x:0, y: 88},
{x:0, y: 212},
{x:87, y: 300}
]
points:[{"x":87,"y":300},{"x":0,"y":212},{"x":0,"y":88},{"x":87,"y":0},{"x":212,"y":0},{"x":300,"y":88},{"x":300,"y":212},{"x":212,"y":300}]
};
}
);

View File

@@ -18,7 +18,7 @@ define(
* @return {Array|boolean} 交点数组或者false
*/
function isBezierRayCross(p0, p1, p2, p) {
// 3点都在同一侧
if(0 === ((p0.y > p.y) + (p1.y > p.y) + (p2.y > p.y)) % 3) {
return false;

View File

@@ -88,6 +88,10 @@ define(
return joint.length ? joint : false;
}
function hashcode(p) {
return p.x / 7 + p.y / 13 + (p.x + p.y) / 17;
}
/**
* 判断x轴射线是否穿过线段
*
@@ -117,6 +121,23 @@ define(
}
}
else {
// 对结果集合进行筛选,去除重复点
var hash = {};
for (var i = result.length - 1; i >= 0; i--) {
var p = result[i];
if (hash[hashcode(p)]) {
result.splice(i, 1);
}
else {
hash[hashcode(p)] = true;
}
}
if (result.length === 1) {
return false;
}
return result;
}
}

View File

@@ -13,7 +13,9 @@ define(
var isInsidePath = require('./isInsidePath');
var bezierQ2Split = require('math/bezierQ2Split');
var getBezierQ2T = require('math/getBezierQ2T');
var getBezierQ2Point = require('math/getBezierQ2Point');
var util = require('./util');
var splice = Array.prototype.splice;
/**
* 按索引号排序相交点,这里需要处理曲线段上有多个交点的问题
@@ -43,7 +45,7 @@ define(
var prev = p0.index == 0 ? path[length - 1] : path[p0.index - 1];
var t1 = getBezierQ2T(prev, cur, next, p0);
var t2 = getBezierQ2T(prev, cur, next, p1);
return t1 < t2 ? -1 : 1;
return t1 == t2 ? 0 : t1 < t2 ? -1 : 1;
}
}
@@ -72,7 +74,6 @@ define(
function splitPath(path, joint) {
joint = sortJoint(path, joint);
var jointOffset = 0; // 用来标记插入点产生的偏移
// 插入分割点
@@ -99,54 +100,69 @@ define(
else if (!path[cur].onCurve) {
var prev = cur == 0 ? length - 1 : cur - 1;
var bezierArray = bezierQ2Split(path[prev], path[cur], path[next], p);
if (!bezierArray) {
throw 'can\'t split path';
}
path.splice(cur, 1,
bezierArray[0][1],
{
x: p.x,
y: p.y,
onCurve: true
},
bezierArray[1][1]
);
// 端点情况
if (bezierArray.length === 1) {
p.index = bezierArray[0] === 0 ? prev : next;
}
else {
path.splice(cur, 1,
bezierArray[0][1],
{
x: p.x,
y: p.y,
onCurve: true
},
bezierArray[1][1]
);
p.index = cur + 1;
jointOffset += 2;
p.index = cur + 1;
jointOffset += 2;
}
}
else {
throw 'can\'t get here';
}
}
// 这里需要重新筛选排序
joint.sort(function(p0, p1) {
return p0.index - p1.index;
});
// 分割曲线
var splitPaths = [];
var splitedPaths = [];
var start;
var end;
for (var i = 0, l = joint.length - 1; i < l; i++) {
start = joint[i];
end = joint[i + 1];
splitPaths.push(path.slice(start.index, end.index + 1));
splitedPaths.push(path.slice(start.index, end.index + 1));
}
// 闭合轮廓
start = end;
end = joint[0];
splitPaths.push(path.slice(start.index).concat(path.slice(0, end.index + 1)));
return splitPaths;
splitedPaths.push(path.slice(start.index).concat(path.slice(0, end.index + 1)));
return splitedPaths;
}
/**
* 获取分割的路径hash
*
* @param {Array} splitPath 分割的路径
* @param {Array} splitedPath 分割的路径
* @return {Object} 哈希值
*/
function getSplitPathHash(splitPath) {
function getSplitedPathHash(splitedPath) {
var splitHash = {};
var code;
// 根据起始点创建hash
splitPath.forEach(function(path) {
splitedPath.forEach(function(path) {
// 开始点
code = hashcode(path[0]);
if (!splitHash[code]) {
@@ -168,15 +184,15 @@ define(
/**
* 组合路径
*
* @param {Array} splitPaths0 分割后的路径1
* @param {Array} splitPaths1 分割后的路径2
* @param {Array} splitedPaths0 分割后的路径1
* @param {Array} splitedPaths1 分割后的路径2
* @param {number} relation 分割关系
* @return {Array} 组合后的路径
*/
function combinePath(splitPaths0, splitPaths1, relation) {
function combinePath(splitedPaths0, splitedPaths1, relation) {
var direction0 = splitedPaths0.direction;
var direction1 = splitedPaths1.direction;
var newPaths = [];
var splice = Array.prototype.splice;
// 过滤路径
var filterPath = function(path){
@@ -191,32 +207,38 @@ define(
}
};
splitPaths0 = splitPaths0.filter(filterPath);
splitPaths1 = splitPaths1.filter(filterPath);
splitedPaths0 = splitedPaths0.filter(filterPath);
splitedPaths1 = splitedPaths1.filter(filterPath);
// 计算哈希,用来辅助组合点
var splitHash0 = getSplitPathHash(splitPaths0);
var splitHash1 = getSplitPathHash(splitPaths1);
var splitHash0 = getSplitedPathHash(splitedPaths0);
var splitHash1 = getSplitedPathHash(splitedPaths1);
for (var i = 0; i < splitPaths0.length; i++) {
var length = splitPaths0[i].length;
var newPath = splitPaths0[i].slice(0, length - 1);
var start = splitPaths0[i][0];
var end = splitPaths0[i][length - 1];
for (var i = 0; i < splitedPaths0.length; i++) {
var curPath = splitedPaths0[i];
var cross = curPath.cross; // 起始cross
var length = curPath.length;
// 对于求相切的情况,外轮廓不需要处理,内轮廓需要根据另一个轮廓的方向调整反向
if (relation == pathJoin.TANGENCY && cross && direction0 == direction1) {
curPath = curPath.reverse();
}
var newPath = curPath.slice(0, length - 1);
var start = curPath[0];
var end = curPath[length - 1];
var nextHash = splitHash1;
var cross = splitPaths0[i].cross; // 起始cross
var loops = 0; // 防止死循环最多组合100个路径段
while (loops++ < 100 && (Math.abs(start.x - end.x) > 0.0001 || Math.abs(start.y - end.y) > 0.0001)) {
while (++loops < 100 && (Math.abs(start.x - end.x) > 0.001 || Math.abs(start.y - end.y) > 0.001)) {
var paths = nextHash[hashcode(end)];
// 选取异向
var p = paths[0];
if (relation == pathJoin.TANGENCY && cross == p.cross) {
if (relation == pathJoin.TANGENCY && cross == p.cross && paths.length > 1) {
p = paths[1];
}
@@ -236,14 +258,16 @@ define(
}
else {
nextHash = splitHash1;
// 这里需要去掉已经使用的splitPaths0上的点
splitPaths0.splice(splitPaths0.indexOf(p), 1);
// 这里需要去掉已经使用的splitedPaths0上的点
splitedPaths0.splice(splitedPaths0.indexOf(p), 1);
}
}
newPaths.push(newPath);
}
if (loops >= 100) {
throw '没有找到可组合的轮廓!';
}
if (newPaths.length) {
return newPaths.map(function(path){
@@ -265,7 +289,7 @@ define(
* @param {number} relation 关系
* @return {Array} 新的路径集合
*/
function pathJoin(path0, path1, relation) {
function join2Path(path0, path1, relation) {
// 方向, 0 顺时针, 1 逆时针
var direction0 = util.isClockWise(path0);
var direction1 = util.isClockWise(path1);
@@ -277,28 +301,49 @@ define(
// 获取组合后的路径
var getJoinPaths = function(joint) {
var splitPaths0 = splitPath(newPath0, joint.map(function(p) {
var splitedPaths0 = splitPath(newPath0, joint.map(function(p) {
p.index = p.index0;
return p;
}));
// 求路径是否在另一个路径内
var inPath = isInsidePath(path1, splitPaths0[0][1]);
splitPaths0 = splitPaths0.map(function(path) {
path.direction = direction0;
path.cross = inPath;
inPath = !inPath;
return path;
var inPath = false;
splitedPaths0 = splitedPaths0.map(function(splitedPath) {
splitedPath.cross = isInsidePath(
path1,
splitedPath[1].onCurve
? splitedPath[1]
: getBezierQ2Point(splitedPath[0], splitedPath[1], splitedPath[2], 0.5)
);
if (splitedPath.cross) {
inPath = true;
}
return splitedPath;
});
var splitPaths1 = splitPath(newPath1, joint.map(function(p) {
// 只有相交的点,没有相交的轮廓
if (!inPath) {
if (relation == pathJoin.JOIN || relation == pathJoin.TANGENCY) {
return [path0, path1];
}
else if (relation == pathJoin.INTERSECT) {
return [];
}
}
var splitedPaths1 = splitPath(newPath1, joint.map(function(p) {
p.index = p.index1;
return p;
}));
inPath = isInsidePath(path0, splitPaths1[0][1]);
splitPaths1 = splitPaths1.map(function(path) {
path.direction = direction1;
var splitedPath = splitedPaths1[0];
inPath = isInsidePath(
path0,
splitedPath[1].onCurve
? splitedPath[1]
: getBezierQ2Point(splitedPath[0], splitedPath[1], splitedPath[2], 0.5)
);
splitedPaths1 = splitedPaths1.map(function(path) {
path.cross = inPath;
inPath = !inPath;
return path;
@@ -313,7 +358,10 @@ define(
return [];
}
return combinePath(splitPaths0, splitPaths1, relation);
splitedPaths0.direction = direction0;
splitedPaths1.direction = direction1;
return combinePath(splitedPaths0, splitedPaths1, relation);
};
@@ -373,6 +421,85 @@ define(
return [path0, path1];
}
/**
* 求路径交集、并集、差集
*
* @param {Array} paths 路径集合
* @param {number} relation 关系
* @return {Array} 合并后的路径
*/
function pathJoin(paths, relation) {
if (paths.length == 1) {
if (relation == pathJoin.INTERSECT) {
return [];
}
else {
return paths;
}
}
else if (paths.length == 2) {
return join2Path(paths[0], paths[1], relation);
}
else {
// 算法描述:
// 1. 第一个路径为已选集合,后面依次为待选集合
// 2. 从已选集合中和待选集合中各取一个轮廓求pathJoin
// 3. 如果有合并项目则除去已选集合和待选集合中的路径把新路径加入待选集合继续2
// 否则将待选集合中的当前路径加入已选集合继续2
// 4. 继续2、3步骤直到待选集合为空
// 已选集合
var startPaths = [paths[0]];
var leftPath = paths.slice(1);
var curPath;
var joinFlag = 0; // 两个轮廓如果是由同一个合并生成的,则不需要进行比较了
while (curPath = leftPath.shift()) {
for (var i = 0, l = startPaths.length; i < l; i++) {
if (curPath.joinFlag && startPaths[i].joinFlag && (curPath.joinFlag & startPaths[i].joinFlag)) {
continue;
}
var result = join2Path(curPath, startPaths[i], relation);
// 相交关系,有个无交集则返回空
if (relation == pathJoin.INTERSECT && !result.length) {
return [];
}
// 原样返回,继续比较
if (result.length == 2 && result[0] === curPath && result[1] === startPaths[i]) {
continue;
}
// 否则分割出来的点,加入待选集合
else {
startPaths.splice(i, 1);
joinFlag ++;
result.forEach(function(path) {
path.joinFlag = (path.joinFlag || 0) + (1 << joinFlag);
leftPath.push(path);
});
break;
}
}
// 没有找到
if (i == l) {
startPaths.push(curPath);
}
else if (!startPaths.length) {
startPaths.push(leftPath.shift());
}
}
return startPaths;
}
}
pathJoin.JOIN = 1; // 并集
pathJoin.INTERSECT = 2; // 交集
pathJoin.TANGENCY = 4; // 相切

View File

@@ -11,14 +11,7 @@ define(
function(require) {
var getBezierQ2T = require('./getBezierQ2T');
// 获取贝塞尔曲线上的点
function getPoint(p0, p1, p2, t) {
return {
x: p0.x * Math.pow(1 - t, 2) + 2 * p1.x * t * (1-t) + p2.x * Math.pow(t, 2),
y: p0.y * Math.pow(1 - t, 2) + 2 * p1.y * t * (1-t) + p2.y * Math.pow(t, 2)
};
}
var getPoint = require('./getBezierQ2Point');
/**
* 分割贝塞尔曲线
@@ -45,6 +38,10 @@ define(
}
}
if (t == 0 || t == 1) {
return [t];
}
return [
[
p0,

View File

@@ -0,0 +1,31 @@
/**
* @file getBezierQ2Point.js
* @author mengke01
* @date
* @description
* 获取贝塞尔曲线上的点
*/
define(
function(require) {
/**
* 获取贝塞尔曲线上的点
*
* @param {Object} p0 p0
* @param {Object} p1 p1
* @param {Object} p2 p2
* @param {number} t
* @return {Object} 点
*/
function getPoint(p0, p1, p2, t) {
return {
x: p0.x * Math.pow(1 - t, 2) + 2 * p1.x * t * (1-t) + p2.x * Math.pow(t, 2),
y: p0.y * Math.pow(1 - t, 2) + 2 * p1.y * t * (1-t) + p2.y * Math.pow(t, 2)
}
}
return getPoint;
}
);

View File

@@ -10,6 +10,7 @@ define(
function(require) {
var bezierQ2Equation = require('math/bezierQ2Equation');
var getPoint = require('./getBezierQ2Point');
/**
* 分割贝塞尔曲线
@@ -22,6 +23,14 @@ define(
*/
function getBezierQ2T(p0, p1, p2, p) {
// 极端情况
if (Math.abs(p.x - p0.x) < 0.001 && Math.abs(p.y - p0.y) < 0.001) {
return 0;
}
else if (Math.abs(p.x - p2.x) < 0.001 && Math.abs(p.y - p2.y) < 0.001) {
return 1;
}
var result = bezierQ2Equation(
p0.x + p2.x - 2 * p1.x + p0.y + p2.y - 2 * p1.y,
2 * (p1.x - p0.x) + 2 * (p1.y - p0.y),
@@ -39,7 +48,7 @@ define(
}
else {
var pt = getPoint(p0, p1, p2, result[0]);
if (Math.abs(pt.x - p.x) < 0.01 && Math.abs(pt.y - p.y) < 0.001) {
if (Math.abs(pt.x - p.x) < 0.001 && Math.abs(pt.y - p.y) < 0.001) {
t = result[0];
}
else {