增加line cross

This commit is contained in:
unknown
2014-09-08 10:58:33 +08:00
parent c1d7138c68
commit e5d45a4572
21 changed files with 631 additions and 108 deletions

View File

@@ -3,8 +3,8 @@
<head>
<meta charset="UTF-8">
<title>二次贝塞尔曲线绘制</title>
<script src="http://s1.bdstatic.com/r/www/cache/ecom/esl/1-8-2/esl.js"></script>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.min.js"></script>
<script src="./lib/esl.js"></script>
<script src="./lib/jquery.min.js"></script>
<style>
body {
margin: 0;
@@ -16,7 +16,6 @@
height: 9px;
margin-left: -5px;
margin-top: -5px;
background: #CCC;
border: 1px solid red;
}
@@ -39,8 +38,8 @@
<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="500" height="500">
</canvas>
<div class="point" data-index="4"></div>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
require.config({
baseUrl: '../src',
@@ -51,6 +50,6 @@
define('jquery', $);
</script>
<script>require(['demo/quadraticBezier'])</script>
<script>require(['demo/bezierSegmentCross'])</script>
</body>
</html>

View File

@@ -3,8 +3,8 @@
<head>
<meta charset="UTF-8">
<title>glyf查看</title>
<script src="http://s1.bdstatic.com/r/www/cache/ecom/esl/1-8-2/esl.js"></script>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.min.js"></script>
<script src="./lib/esl.js"></script>
<script src="./lib/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="./css/glyf.css">
<style id="font-face"></style>
</head>

View File

@@ -3,8 +3,8 @@
<head>
<meta charset="UTF-8">
<title>glyf查看</title>
<script src="http://s1.bdstatic.com/r/www/cache/ecom/esl/1-8-2/esl.js"></script>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.min.js"></script>
<script src="./lib/esl.js"></script>
<script src="./lib/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="./css/glyf.css">
<style id="font-face"></style>
</head>

View File

@@ -0,0 +1,103 @@
/**
* @file quadraticBezier.js
* @author mengke01
* @date
* @description
* 二次贝塞尔直线相交演示
*/
define(
function(require) {
var isBezierSegmentCross = require('graphics/isBezierSegmentCross');
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":163,"y":136},
{"x":45,"y":54},
{"x":124,"y":177},
{"x":222,"y":122},
{"x":57,"y":122}
];
$('[data-index]').each(function(index, item) {
$(item).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='black';
ctx.moveTo(points[0].x, points[0].y);
ctx.quadraticCurveTo(points[1].x, points[1].y, points[2].x, points[2].y);
ctx.moveTo(points[3].x, points[3].y);
ctx.lineTo(points[4].x, points[4].y);
ctx.lineWidth = 1;
ctx.stroke();
//console.time('bezier');
var r = isBezierSegmentCross(points[0], points[1], points[2], points[3], points[4]);
//console.timeEnd('bezier');
console.log(r.length);
if(r) {
ctx.beginPath();
r.forEach(function(item) {
ctx.moveTo(item.x, item.y);
ctx.arc(item.x, item.y, 4, 0, Math.PI * 2, true);
});
ctx.fill();
}
}
draw();
}
};
entry.init();
return entry;
}
);

View File

@@ -24,19 +24,6 @@ define(
var width = canvas.offsetWidth;
var height = canvas.offsetHeight;
// var points = [];
// [0, 1, 2, 3].forEach(function(i) {
// var p = {
// x: Math.floor(Math.random() * (width - 100) + 50),
// y: Math.floor(Math.random() * (height - 100) + 50)
// }
// points[i] = p;
// $($('.point').get(i)).css({
// left: p.x,
// top: p.y
// });
// });
var points = [
{
x: 130,

99
demo/js/segmentCross.js Normal file
View File

@@ -0,0 +1,99 @@
/**
* @file quadraticBezier.js
* @author mengke01
* @date
* @description
* 二次贝塞尔直线相交演示
*/
define(
function(require) {
var isSegmentCross = require('graphics/isSegmentCross');
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":50,"y":200},{"x":97,"y":98},{"x":58,"y":94},{"x":105,"y":201}];
$('[data-index]').each(function(index, item) {
$(item).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='black';
ctx.moveTo(points[0].x, points[0].y);
ctx.lineTo(points[1].x, points[1].y);
ctx.moveTo(points[2].x, points[2].y);
ctx.lineTo(points[3].x, points[3].y);
ctx.lineWidth = 1;
ctx.stroke();
//console.time('bezier');
var r = isSegmentCross(points[0], points[1], points[2], points[3]);
//console.timeEnd('bezier');
console.log(r);
if(r) {
ctx.beginPath();
r.forEach(function(item) {
ctx.moveTo(item.x, item.y);
ctx.arc(item.x, item.y, 4, 0, Math.PI * 2, true);
});
ctx.fill();
}
}
draw();
}
};
entry.init();
return entry;
}
);

1
demo/lib/esl.js Normal file

File diff suppressed because one or more lines are too long

3
demo/lib/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -3,8 +3,8 @@
<head>
<meta charset="UTF-8">
<title>测试render</title>
<script src="http://s1.bdstatic.com/r/www/cache/ecom/esl/1-8-2/esl.js"></script>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.min.js"></script>
<script src="./lib/esl.js"></script>
<script src="./lib/jquery.min.js"></script>
</head>
<body>
<script>

View File

@@ -3,8 +3,8 @@
<head>
<meta charset="UTF-8">
<title>二次贝塞尔求交</title>
<script src="http://s1.bdstatic.com/r/www/cache/ecom/esl/1-8-2/esl.js"></script>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.min.js"></script>
<script src="./lib/esl.js"></script>
<script src="./lib/jquery.min.js"></script>
<style>
body {
margin: 0;
@@ -16,7 +16,6 @@
height: 9px;
margin-left: -5px;
margin-top: -5px;
background: #CCC;
border: 1px solid red;
}

View File

@@ -3,8 +3,8 @@
<head>
<meta charset="UTF-8">
<title>测试render</title>
<script src="http://s1.bdstatic.com/r/www/cache/ecom/esl/1-8-2/esl.js"></script>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.min.js"></script>
<script src="./lib/esl.js"></script>
<script src="./lib/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="./css/render.css">
<style id="font-face"></style>
</head>

45
demo/segmentCross.html Normal file
View File

@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>线段相交</title>
<script src="./lib/esl.js"></script>
<script src="./lib/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;
}
[data-index="0"], [data-index="1"] {
border-color: green
}
</style>
</head>
<body>
<div class="point" data-index="0"></div>
<div class="point" data-index="1"></div>
<div class="point" data-index="2"></div>
<div class="point" data-index="3"></div>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
require.config({
baseUrl: '../src',
paths: {
demo: '../demo/js',
}
});
define('jquery', $);
</script>
<script>require(['demo/segmentCross'])</script>
</body>
</html>

View File

@@ -3,10 +3,13 @@
<head>
<meta charset="UTF-8">
<title>font编辑器</title>
<script src="http://s1.bdstatic.com/r/www/cache/ecom/esl/1-8-2/esl.js"></script>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.min.js"></script>
<script src="./lib/esl.js"></script>
<script src="./lib/jquery.min.js"></script>
</head>
<body>
<input id="upload-file" type="file">
<script>
require.config({
baseUrl: '../src',
@@ -17,8 +20,6 @@
define('jquery', $);
</script>
<input id="upload-file" type="file">
<script>
require(['demo/ttfparse']);
</script>

View File

@@ -14,45 +14,11 @@ define(
var bezierQ2Equation = require('../math/bezierQ2Equation');
var bezierQ4Equation = require('../math/bezierQ4Equation');
var bezierCubeEquation = require('../math/bezierCubeEquation');
/**
* 矩阵乘
*
* @param {Array.<number>} matrix 一维矩阵
* @param {number} a 乘数算子
* @return {Array.<number>} 数组
*/
function multi(matrix, a) {
return matrix.map(function(item) {
return item * a;
});
}
/**
* 矩阵减
*
* @param {Array.<number>} matrix1 一维矩阵
* @param {Array.<number>} matrix2 一维矩阵
* @return {Array.<number>} 数组
*/
function minus(matrix1, matrix2) {
return matrix1.map(function(item, index) {
return item - matrix2[index];
});
}
/**
* 矩阵加
*
* @param {Array.<number>} matrix1 一维矩阵
* @param {Array.<number>} matrix2 一维矩阵
* @return {Array.<number>} 数组
*/
function plus(matrix1, matrix2) {
return matrix1.map(function(item, index) {
return item + matrix2[index];
});
}
var matrix = require('./matrix');
var multi = matrix.multi;
var minus = matrix.minus;
var plus = matrix.plus;
/**
* 求两个bezier曲线的交点

View File

@@ -9,21 +9,71 @@
define(
function(require) {
var bezierQ2Equation = require('../math/bezierQ2Equation');
/**
* 判断贝塞尔曲线与直线相交
*
* @param {Object} p0 起点
* @param {Object} p1 控制点
* @param {Object} p2 终点
* @param {Object} s1 直线点1
* @param {Object} s2 直线点2
* @param {Object} s0 直线点1
* @param {Object} s1 直线点2
* @return {boolean|Object} 是否相交
*/
function isBezierLineCross(p0, p1, p2, s1, s2) {
function isBezierLineCross(p0, p1, p2, s0, s1) {
// y = kx + b
// x = at^2 + bt + c
// y = bt^2 + et + f
// y = dt^2 + et + f
//(ka-d)t^2 + (kb-e)t + (kc+b-f) = 0
var result;
// 垂直x
if (s0.y == s1.y) {
result = bezierQ2Equation(
p0.y + p2.y - 2 * p1.y,
2 * (p1.y - p0.y),
p0.y - s0.y
);
}
else if (s0.x == s1.x) {
result = bezierQ2Equation(
p0.x + p2.x - 2 * p1.x,
2 * (p1.x - p0.x),
p0.x - s0.x
);
}
else {
var k = (s1.y - s0.y) / (s1.x - s0.x);
var b1 = s0.y - k * s0.x;
var a = p0.x + p2.x - 2 * p1.x;
var b = 2 * (p1.x - p0.x);
var c = p0.x;
var d = p0.y + p2.y - 2 * p1.y;
var e = 2 * (p1.y - p0.y);
var f = p0.y;
result = bezierQ2Equation(
k * a - d,
k * b - e,
k * c + b1 - f
);
}
if(result) {
return result.map(function(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 false;
}
return isBezierLineCross;

View File

@@ -8,19 +8,41 @@
define(
function(require) {
var computeBoundingBox = require('./computeBoundingBox');
var isBezierLineCross = require('./isBezierLineCross');
var isBoundingBoxCross = require('./isBoundingBoxCross');
var isPointInBound = require('./util').isPointInBound;
/**
* 判断贝塞尔曲线与线段相交
*
* @param {Object} p0 起点
* @param {Object} p1 控制点
* @param {Object} p2 终点
* @param {Object} s1 线段点1
* @param {Object} s2 线段点2
* @param {Object} s0 线段点1
* @param {Object} s1 线段点2
* @return {boolean|Object} 是否相交
*/
function isBezierSegmentCross(p0, p1, p2, s1, s2) {
function isBezierSegmentCross(p0, p1, p2, s0, s1) {
var b1 = computeBoundingBox.quadraticBezier(p0, p1, p2);
var bound = {
x: Math.min(s0.x, s1.x),
y: Math.min(s0.y, s1.y),
width: Math.abs(s0.x - s1.x),
height: Math.abs(s0.y - s1.y)
};
if (isBoundingBoxCross(b1, bound)) {
var result = isBezierLineCross(p0, p1, p2, s0, s1);
if (result) {
return result.filter(function(p) {
return p.x >= s0.x && p.x <= s1.x
|| p.x >= s1.x && p.x <= s0.x;
});
}
}
return false;
}
return isBezierSegmentCross;

View File

@@ -9,18 +9,9 @@
define(
function(require) {
/**
* 判断点是否在bounding box内部
*
* @return {boolean} 是否
*/
function isPointInBounding(bound, p) {
return p.x <= bound.x + bound.width
&& p.x >= bound.x
&& p.y <= bound.y + bound.height
&& p.y >= bound.y
}
var isPointInBound = require('./util').isPointInBound;
/**
* 两个boundingbox的关系
@@ -30,20 +21,20 @@ define(
* @return {number} 包含关系
*/
function isBoundingBoxCross(b1, b2) {
var b1_lt = isPointInBounding(b2, b1); // 左上
var b1_rt = isPointInBounding(b2, {x: b1.x + b1.width, y: b1.y}); // 右上
var b1_lb = isPointInBounding(b2, {x: b1.x, y: b1.y + b1.height}); // 左下
var b1_rb = isPointInBounding(b2, {x: b1.x + b1.width, y: b1.y + b1.height}); //右下
var b1_lt = isPointInBound(b2, b1); // 左上
var b1_rt = isPointInBound(b2, {x: b1.x + b1.width, y: b1.y}); // 右上
var b1_lb = isPointInBound(b2, {x: b1.x, y: b1.y + b1.height}); // 左下
var b1_rb = isPointInBound(b2, {x: b1.x + b1.width, y: b1.y + b1.height}); //右下
// b2 包含 b1
if(b1_lt && b1_rt && b1_lb && b1_rb) {
return 2;
}
var b2_lt = isPointInBounding(b1, b2); // 左上
var b2_rt = isPointInBounding(b1, {x: b2.x + b2.width, y: b2.y}); // 右上
var b2_lb = isPointInBounding(b1, {x: b2.x, y: b2.y + b2.height}); // 左下
var b2_rb = isPointInBounding(b1, {x: b2.x + b2.width, y: b2.y + b2.height}); //右下
var b2_lt = isPointInBound(b1, b2); // 左上
var b2_rt = isPointInBound(b1, {x: b2.x + b2.width, y: b2.y}); // 右上
var b2_lb = isPointInBound(b1, {x: b2.x, y: b2.y + b2.height}); // 左下
var b2_rb = isPointInBound(b1, {x: b2.x + b2.width, y: b2.y + b2.height}); //右下
// b1 包含 b2
if(b2_lt && b2_rt && b2_lb && b2_rb) {
@@ -54,8 +45,8 @@ define(
if(false == (b1_lt || b1_rt || b1_lb || b1_rb || b2_lt || b2_rt || b2_lb || b2_rb)) {
// 判断十字架
if(
(b1.x >= b2.x && b1.x <= b2.x + b2.width)
|| (b1.y >= b2.y && b1.y <= b2.y + b2.height)
(b1.x > b2.x && b1.x < b2.x + b2.width)
|| (b1.y > b2.y && b1.y < b2.y + b2.height)
) {
return 1;
}

View File

@@ -0,0 +1,28 @@
/**
* @file isBoundingBoxSegmentCross.js
* @author mengke01
* @date
* @description
* boundingbox和线段的关系
*/
define(
function(require) {
var isPointInBound = require('./util').isPointInBound;
/**
* boundingbox和线段的关系
*
* @param {Object} bound bounding 1
* @param {Object} s0 线段点1
* @param {Object} s1 线段点2
* @return {number} 包含关系
*/
function isBoundingBoxSegmentCross(bound, s0 s1) {
}
return isBoundingBoxSegmentCross;
}
);

View File

@@ -9,17 +9,144 @@
define(
function(require) {
var isBoundingBoxCross = require('./isBoundingBoxCross');
var matrix = require('./matrix');
var multi = matrix.multi;
var minus = matrix.minus;
var plus = matrix.plus;
/**
* 判断点是否在bounding box内部
*
* @return {boolean} 是否
*/
function isPointInBound(bound, p) {
return p.x <= bound.x + bound.width
&& p.x >= bound.x
&& p.y <= bound.y + bound.height
&& p.y >= bound.y
}
/**
* 过滤相交重叠线段上的点
*/
function filter(s0, s1, t0, t1, axis) {
var sorted = [s0, s1, t0, t1].sort(function(a, b) {
return a[axis] < b[axis] ? -1 : 1;
});
var points = [];
if(sorted[0][axis] == sorted[1][axis]) {
points.push(sorted[0]);
points.push(sorted[2]);
}
else if(sorted[2][axis] == sorted[3][axis]) {
points.push(sorted[1]);
points.push(sorted[3]);
}
else {
points.push(sorted[1]);
if(sorted[1][axis] != sorted[2][axis]) {
points.push(sorted[2]);
}
}
return points;
}
/**
* 线段是否相交
*
* @param {Object} s1 点1
* @param {Object} s2 点2
* @param {Object} t1 点1
* @param {Object} s0 点1
* @param {Object} s1 点2
* @param {Object} t0 点1
* @param {Object} t1 点2
* @return {boolean|Object} 是否相交
*/
function isSegmentCross(s1, s2, t1, t2) {
function isSegmentCross(s0, s1, t0, t1) {
var p, x, y;
if (s1.x < s0.x ) {
p = s1;
s1 = s0;
s0 = p;
}
if (t1.x < t0.x) {
p = t1;
t1 = t0;
t0 = p;
}
var b1 = {
x: Math.min(s0.x, s1.x),
y: Math.min(s0.y, s1.y),
width: Math.abs(s0.x - s1.x),
height: Math.abs(s0.y - s1.y)
};
var b2 = {
x: Math.min(t0.x, t1.x),
y: Math.min(t0.y, t1.y),
width: Math.abs(t0.x - t1.x),
height: Math.abs(t0.y - t1.y)
};
if (isBoundingBoxCross(b1, b2)) {
// 参数方程 Ax + By + C = 0
var seg1 = [s1.y - s0.y, -(s1.x - s0.x), s0.y * (s1.x - s0.x) - s0.x *(s1.y - s0.y)];
var seg2 = [t1.y - t0.y, -(t1.x - t0.x), t0.y * (t1.x - t0.x) - t0.x *(t1.y - t0.y)];
// x轴重叠
if (seg1[1] == seg2[1] && seg1[1] == 0) {
if (s1.x == t1.x) {
return filter(s0, s1, t0, t1, 'y');
}
else {
return false;
}
}
// y轴重叠
else if(seg1[0] == seg2[0] && seg1[0] == 0) {
if (s1.y == t1.y) {
return filter(s0, s1, t0, t1, 'x');
}
else {
return false;
}
}
// 平行
else if (seg1[0] * seg2[1] == seg1[1] * seg2[0]) {
// 重叠
if (seg1[1] * seg2[2] == seg1[2] * seg2[1]) {
return filter(s0, s1, t0, t1, 'x');
}
else {
return false;
}
}
else {
var tmp = minus(multi(seg1, seg2[0]), multi(seg2, seg1[0]));
y = - tmp[2] / tmp[1];
if(seg1[0] != 0) {
x = - (seg1[2] + seg1[1] * y) / seg1[0];
}
else {
x = - (seg2[2] + seg2[1] * y) / seg2[0];
}
p = {
x: x,
y: y
};
if(isPointInBound(b1, p) && isPointInBound(b2, p)) {
return [p];
}
}
}
return false;
}
return isSegmentCross;

59
src/graphics/matrix.js Normal file
View File

@@ -0,0 +1,59 @@
/**
* @file matrix.js
* @author mengke01
* @date
* @description
* 一维矩阵操作
*/
define(
function(require) {
/**
* 矩阵乘系数
*
* @param {Array.<number>} matrix 一维矩阵
* @param {number} a 乘数算子
* @return {Array.<number>} 数组
*/
function multi(matrix, a) {
return matrix.map(function(item) {
return item * a;
});
}
/**
* 矩阵减
*
* @param {Array.<number>} matrix1 一维矩阵
* @param {Array.<number>} matrix2 一维矩阵
* @return {Array.<number>} 数组
*/
function minus(matrix1, matrix2) {
return matrix1.map(function(item, index) {
return item - matrix2[index];
});
}
/**
* 矩阵加
*
* @param {Array.<number>} matrix1 一维矩阵
* @param {Array.<number>} matrix2 一维矩阵
* @return {Array.<number>} 数组
*/
function plus(matrix1, matrix2) {
return matrix1.map(function(item, index) {
return item + matrix2[index];
});
}
return {
multi: multi,
minus: minus,
plus: plus
};
}
);

43
src/graphics/util.js Normal file
View File

@@ -0,0 +1,43 @@
/**
* @file matrix.js
* @author mengke01
* @date
* @description
* 相关工具箱
*/
define(
function(require) {
/**
* 将点进行误差舍入
*
* @return {Object} 点
*/
function ceil(p) {
p.x = Math.ceil(p.x * 100000) / 100000;
p.y = Math.ceil(p.y * 100000) / 100000
return p;
}
/**
* 判断点是否在bounding box内部
* @param {Object} bound bounding box对象
* @param {Object} p 点对象
* @return {boolean} 是否
*/
function isPointInBound(bound, p, fixed) {
p = fixed ? ceil(p) : p;
return p.x <= bound.x + bound.width
&& p.x >= bound.x
&& p.y <= bound.y + bound.height
&& p.y >= bound.y
}
return {
ceil: ceil,
isPointInBound: isPointInBound
};
}
);