增加 多路径求合并
This commit is contained in:
@@ -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).
|
||||
@@ -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}]]}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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
6
demo/js/contours-4.js
Normal 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
6
demo/js/contours-5.js
Normal 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
6
demo/js/contours-6.js
Normal 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
6
demo/js/contours-7.js
Normal 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}]]};
|
||||
}
|
||||
);
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
103
src/editor/command/shapes.js
Normal file
103
src/editor/command/shapes.js
Normal 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;
|
||||
}
|
||||
);
|
||||
@@ -261,6 +261,7 @@ define(
|
||||
};
|
||||
|
||||
lang.extend(support, require('./transform'));
|
||||
lang.extend(support, require('./shapes'));
|
||||
|
||||
return support;
|
||||
}
|
||||
|
||||
@@ -107,6 +107,17 @@ define(
|
||||
title: '复制'
|
||||
},
|
||||
|
||||
'join_shapes': {
|
||||
title: '结合'
|
||||
},
|
||||
|
||||
'intersect_shapes': {
|
||||
title: '相交'
|
||||
},
|
||||
|
||||
'tangency_shapes': {
|
||||
title: '相切'
|
||||
},
|
||||
|
||||
'rotate_left': {
|
||||
title: '向左旋转'
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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}]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; // 相切
|
||||
|
||||
@@ -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,
|
||||
|
||||
31
src/math/getBezierQ2Point.js
Normal file
31
src/math/getBezierQ2Point.js
Normal 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;
|
||||
}
|
||||
);
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user