add path constructor

This commit is contained in:
mkwiser
2014-09-03 00:22:02 +08:00
parent afdfa966a2
commit e5f1c82b1e
12 changed files with 346 additions and 114 deletions

View File

@@ -13,6 +13,9 @@ define(
var shape_baidu = require('./shape-baidu');
var shape_bdjk = require('./shape-bdjk');
var glyfAdjust = require('ttf/util/glyfAdjust');
var glyf2path = require('ttf/util/glyf2path');
var lang = require('common/lang');
var currentRender;
var entry = {
@@ -66,16 +69,35 @@ define(
level: 10
});
shape_baidu = glyfAdjust(shape_baidu, 100 / 512);
shape_baidu.x = 50;
shape_baidu.y = 50;
font.addShape('font', shape_baidu);
shape_bdjk = glyfAdjust(shape_bdjk, 100 / 512);
shape_bdjk.x = 444;
shape_bdjk.y = 255;
font.addShape('font', shape_bdjk);
var pathPanel = currentRender.addLayer('path', {
level: 11
});
var pathArray = glyf2path(shape_baidu, {
scale: 120 / 512
});
var computeBoundingBox = require('render/util/computeBoundingBox');
var pathAdjust = require('render/util/pathAdjust');
pathArray.forEach(function(path) {
var shape = {};
shape.points = path;
shape.bound = computeBoundingBox.computePath(path);
shape.x = 200;
shape.y = 200;
shape.width = shape.bound.width;
shape.height = shape.bound.height;
pathPanel.addShape('path', shape);
});
currentRender.refresh();
// cover.removeShape(0);

View File

@@ -135,6 +135,9 @@ define(
else if(typeof shape === 'number') {
return this.shapes[shape];
}
else if(typeof shape === 'object') {
return shape;
}
},
/**
@@ -212,6 +215,31 @@ define(
}
},
/**
* 移动到指定的偏移
* @param {number} x 偏移
* @param {number} y 偏移
* @param {shape} shape对象
*/
move: function(x, y, shape) {
shape = this.getShape(shape);
var shapes = [];
if(shape) {
shapes.push(shape);
}
else {
shapes = this.shapes;
}
shapes.forEach(function(shape) {
var _shape = shape['_shape'];
_shape.x = _shape.x + x;
_shape.y= _shape.y + y;
});
return this;
},
/**
* 注销本对象
*/

View File

@@ -136,6 +136,9 @@ define(
else if(typeof layerId === 'number') {
return this.layers[layerId];
}
else if(typeof layerId === 'object') {
return layerId;
}
},
/**
@@ -217,6 +220,29 @@ define(
}
},
/**
* 移动到指定的偏移
* @param {number} x 偏移
* @param {number} y 偏移
* @param {Layer} layerId对象
*/
move: function(x, y, layerId) {
var layer = this.getLayer(layerId);
var layers = [];
if(layer) {
layers.push(layer);
}
else {
layers = this.layers;
}
layers.forEach(function(layer) {
layer.move(x, y);
});
return this;
},
/**
* 注销本对象
*/

View File

@@ -61,36 +61,44 @@ define(
me.camera.scale *= ratio;
me.painter.refresh();
console.log(me.camera.scale);
me.camera.ratio = 1;
});
this.capture.on('dragstart', function(e) {
var shape = me.painter.getShapeIn(e);
if(shape) {
var _shape = shape['_shape'];
_shape._x = _shape.x;
_shape._y= _shape.y;
}
me.selectedShape = shape;
}
me.camera.mx = e.x;
me.camera.my = e.y;
});
this.capture.on('drag', function(e) {
var shape = me.selectedShape;
if(shape) {
var _shape = shape['_shape'];
_shape.x = _shape._x + e.deltaX;
_shape.y = _shape._y + e.deltaY;
me.painter.getLayer(shape.layerId).refresh();
me.painter.getLayer(shape.layerId)
.move(e.x - me.camera.mx, e.y - me.camera.my, shape)
.refresh();
}
else {
me.painter.move(e.x - me.camera.mx, e.y - me.camera.my)
.refresh();
}
me.camera.mx = e.x;
me.camera.my = e.y;
});
this.capture.on('dragend', function(e) {
var shape = me.selectedShape;
if(shape) {
me.painter.getLayer(shape.layerId).refresh();
me.painter.getLayer(shape.layerId)
.move(e.x - me.camera.mx, e.y - me.camera.my, shape)
.refresh();
me.selectedShape = null;
}
else {
me.painter.refresh();
}
});
}

View File

@@ -45,6 +45,8 @@ define(
_shape.xMin = shape.xMin * scale;
_shape.yMin = shape.yMin * scale;
shape.width = shape.width * scale;
shape.height = shape.height * scale;
},
/**
@@ -72,9 +74,9 @@ define(
*/
isIn: function(shape, x, y) {
return x <= shape.x + shape.width
&& x >= shape.x - shape.width
&& x >= shape.x
&& y <= shape.y + shape.height
&& y >= shape.y - shape.height;
&& y >= shape.y;
},
/**

View File

@@ -9,10 +9,25 @@
define(
function(require) {
var isInsidePolygon = require('../util/isInsidePolygon');
var proto = {
type: 'path',
/**
* 对形状进行缩放平移调整
*
* @param {Object} shape shape对象
* @param {Object} camera camera对象
* @return {Object} shape对象
*/
adjust: function(shape, camera) {
},
/**
* 获取shape的矩形区域
*
@@ -37,7 +52,24 @@ define(
* @param {boolean} 是否
*/
isIn: function(shape, x, y) {
return Math.pow(shape.x - x, 2) + Math.pow(shape.y - y, 2) <= Math.pow(shape.r, 2);
x = x - shape.bound.x;
y = y - shape.bound.y;
if(
x <= shape.x + shape.width
&& x >= shape.x
&& y <= shape.y + shape.height
&& y >= shape.y
) {
var points = [];
shape.points.forEach(function(point) {
if(point.c == 'Q') {
points.push(point.p1);
}
points.push(point.p);
});
return isInsidePolygon(points, x, y);
}
return false;
},
/**
@@ -47,7 +79,32 @@ define(
* @param {Object} shape shape数据
*/
draw: function(ctx, shape) {
var x = shape.x || 0;
var y = shape.y || 0;
ctx.translate(x, y);
var i = -1;
var points = shape.points;
var l = points.length;
var point;
while (++i < l) {
point = points[i];
switch (point.c) {
case 'M':
ctx.moveTo(point.p.x, point.p.y);
break;
case 'L':
ctx.lineTo(point.p.x, point.p.y);
break;
case 'Q':
ctx.quadraticCurveTo(point.p1.x, point.p1.y, point.p.x, point.p.y);
break;
case 'Z':
ctx.lineTo(point.p.x, point.p.y);
break;
}
}
ctx.translate(-x, -y);
}
};

View File

@@ -39,9 +39,9 @@ define(
*/
isIn: function(shape, x, y) {
return x <= shape.x + shape.width
&& x >= shape.x - shape.width
&& x >= shape.x
&& y <= shape.y + shape.height
&& y >= shape.y - shape.height;
&& y >= shape.y;
},
/**

View File

@@ -12,7 +12,8 @@ define(
var support = {
circle: require('./Circle'),
rect: require('./Rect'),
font: require('./Font')
font: require('./Font'),
path: require('./Path')
};
return support;
}

View File

@@ -10,96 +10,135 @@
* https://github.com/ecomfe/zrender/blob/master/src/tool/computeBoundingBox.js
*/
define(
function(require) {
/**
* 计算包围盒
*/
function computeBoundingBox(points, min, max) {
function computeBoundingBox(points) {
if (points.length === 0) {
return;
}
var left = points[0][0];
var right = points[0][0];
var top = points[0][1];
var bottom = points[0][1];
var left = points[0].x;
var right = points[0].x;
var top = points[0].y;
var bottom = points[0].y;
for (var i = 1; i < points.length; i++) {
var p = points[i];
if (p[0] < left) {
left = p[0];
if (p.x < left) {
left = p.x;
}
if (p[0] > right) {
right = p[0];
if (p.x > right) {
right = p.x;
}
if (p[1] < top) {
top = p[1];
if (p.y < top) {
top = p.y;
}
if (p[1] > bottom) {
bottom = p[1];
if (p.y > bottom) {
bottom = p.y;
}
}
min[0] = left;
min[1] = top;
max[0] = right;
max[1] = bottom;
return {
x: left,
y: top,
width: right - left,
height: bottom - top
}
}
/**
* 计算二阶贝塞尔曲线的包围盒
* http://pissang.net/blog/?p=91
*/
function computeQuadraticBezierBoundingBox(p0, p1, p2, min, max) {
function computeQuadraticBezierBoundingBox(p0, p1, p2) {
// Find extremities, where derivative in x dim or y dim is zero
var tmp = (p0[0] + p2[0] - 2 * p1[0]);
var tmp = (p0.x + p2.x - 2 * p1.x);
// p1 is center of p0 and p2 in x dim
var t1;
if (tmp === 0) {
t1 = 0.5;
} else {
t1 = (p0[0] - p1[0]) / tmp;
t1 = (p0.x - p1.x) / tmp;
}
tmp = (p0[1] + p2[1] - 2 * p1[1]);
tmp = (p0.y + p2.y - 2 * p1.y);
// p1 is center of p0 and p2 in y dim
var t2;
if (tmp === 0) {
t2 = 0.5;
} else {
t2 = (p0[1] - p1[1]) / tmp;
t2 = (p0.y - p1.y) / tmp;
}
t1 = Math.max(Math.min(t1, 1), 0);
t2 = Math.max(Math.min(t2, 1), 0);
var ct1 = 1-t1;
var ct2 = 1-t2;
var ct1 = 1 - t1;
var ct2 = 1 - t2;
var x1 = ct1 * ct1 * p0[0]
+ 2 * ct1 * t1 * p1[0]
+ t1 * t1 * p2[0];
var y1 = ct1 * ct1 * p0[1]
+ 2 * ct1 * t1 * p1[1]
+ t1 * t1 * p2[1];
var x1 = ct1 * ct1 * p0.x + 2 * ct1 * t1 * p1.x + t1 * t1 * p2.x;
var y1 = ct1 * ct1 * p0.y + 2 * ct1 * t1 * p1.y + t1 * t1 * p2.y;
var x2 = ct2 * ct2 * p0[0]
+ 2 * ct2 * t2 * p1[0]
+ t2 * t2 * p2[0];
var y2 = ct2 * ct2 * p0[1]
+ 2 * ct2 * t2 * p1[1]
+ t2 * t2 * p2[1];
var x2 = ct2 * ct2 * p0.x + 2 * ct2 * t2 * p1.x + t2 * t2 * p2.x;
var y2 = ct2 * ct2 * p0.y + 2 * ct2 * t2 * p1.y + t2 * t2 * p2.y;
return computeBoundingBox(
[p0.slice(), p2.slice(), [x1, y1], [x2, y2]],
min, max
);
return computeBoundingBox([p0, p2, {
x: x1,
y: y1
},
{
x: x2,
y: y2
}]);
}
/**
* 计算曲线包围盒
*
* @private
* @param {path} path对象
*
* @return {Object} x,y,width,height
*/
function computePathBoundingBox(path) {
var l = path.length;
var xMax = xMin = yMax = yMin = 0;
var i = -1;
var points = [];
while (++i < l) {
var point = path[i];
switch (point.c) {
case 'M':
case 'L':
points.push(point.p);
break;
case 'Q':
var p0 = path[i - 1].p;
var bound = computeQuadraticBezierBoundingBox(p0, point.p1, point.p);
points.push(bound);
points.push({
x: bound.x,
y: bound.y + bound.height
});
points.push({
x: bound.x + bound.width,
y: bound.y
});
points.push({
x: bound.x + bound.width,
y: bound.y + bound.height
});
break;
}
}
return computeBoundingBox(points);
}
return {
quadraticBezier: computeQuadraticBezierBoundingBox
computeBounding: computeBoundingBox,
quadraticBezier: computeQuadraticBezierBoundingBox,
computePath: computePathBoundingBox
};
}
);

View File

@@ -17,7 +17,7 @@ define(
* 多边形包含判断
* 警告:下面这段代码会很难看,建议跳过~
*/
function _isInsidePolygon(pointList, x, y) {
function isInsidePolygon(pointList, x, y) {
/**
* 射线判别法
* 如果一个点在多边形内部,任意角度做射线肯定会与多边形要么有一个交点,要么有与多边形边界线重叠
@@ -34,7 +34,7 @@ define(
for (i = 0; i < N; ++i) {
// 是否在顶点上
if (polygon[i][0] == x && polygon[i][1] == y ) {
if (polygon[i].x == x && polygon[i].y == y ) {
redo = false;
inside = true;
break;
@@ -45,14 +45,14 @@ define(
redo = false;
inside = false;
for (i = 0,j = N - 1; i < N; j = i++) {
if ((polygon[i][1] < y && y < polygon[j][1])
|| (polygon[j][1] < y && y < polygon[i][1])
if ((polygon[i].y < y && y < polygon[j].y)
|| (polygon[j].y < y && y < polygon[i].y)
) {
if (x <= polygon[i][0] || x <= polygon[j][0]) {
v = (y - polygon[i][1])
* (polygon[j][0] - polygon[i][0])
/ (polygon[j][1] - polygon[i][1])
+ polygon[i][0];
if (x <= polygon[i].x || x <= polygon[j].x) {
v = (y - polygon[i].y)
* (polygon[j].x - polygon[i].x)
/ (polygon[j].y - polygon[i].y)
+ polygon[i].x;
if (x < v) { // 在线的左侧
inside = !inside;
}
@@ -62,17 +62,17 @@ define(
}
}
}
else if (y == polygon[i][1]) {
if (x < polygon[i][0]) { // 交点在顶点上
polygon[i][1] > polygon[j][1] ? --y : ++y;
else if (y == polygon[i].y) {
if (x < polygon[i].x) { // 交点在顶点上
polygon[i].y > polygon[j].y ? --y : ++y;
//redo = true;
break;
}
}
else if (polygon[i][1] == polygon[j][1] // 在水平的边界线上
&& y == polygon[i][1]
&& ((polygon[i][0] < x && x < polygon[j][0])
|| (polygon[j][0] < x && x < polygon[i][0]))
else if (polygon[i].y == polygon[j].y // 在水平的边界线上
&& y == polygon[i].y
&& ((polygon[i].x < x && x < polygon[j].x)
|| (polygon[j].x < x && x < polygon[i].x))
) {
inside = true;
break;
@@ -82,21 +82,6 @@ define(
return inside;
}
/**
* 判断点是否在折线内部
*
* @param {Object} path path对象
* @param {Object} p 点对象
* @return {boolean} 是否
*/
function isInsidePolygon(pointList, p) {
var list = [];
pointList.forEach(function(item) {
list.push([item.x, item.y]);
});
return _isInsidePolygon(list, p.x, p.y);
}
return isInsidePolygon;
}
);

View File

@@ -0,0 +1,49 @@
/**
* @file pathAdjust.js
* @author mengke01
* @date
* @description
* 路径调整
*/
define(
function(require) {
/**
* 对path坐标进行调整
*
* @param {Object} path path数据结构
* @param {number} scale 缩放比例
* @param {number} offsetX x偏移
* @param {number} offsetY y偏移
* @return {number} path数据结构
*/
function pathAdjust(path, scale, offsetX, offsetY) {
var scale = scale || 1;
var x = offsetX || 0;
var y = offsetY || 0;
var l = path.length;
var i = -1;
while (++i < l) {
var point = path[i];
switch (point.c) {
case 'M':
case 'L':
point.p.x = scale * (point.p.x + x);
point.p.y = scale * (point.p.y + y);
break;
case 'Q':
point.p.x = scale * (point.p.x + x);
point.p.y = scale * (point.p.y + y);
point.p1.x = scale * (point.p1.x + x);
point.p1.y = scale * (point.p1.y + y);
break;
}
}
return path;
}
return pathAdjust;
}
);

View File

@@ -44,18 +44,22 @@ var glyfAdjust = require('ttf/util/glyfAdjust');
var commandQueue = [];
var curBezier = null;
var currentPoint = null;
var prevPoint = null;
var nextPoint = null;
// 处理glyf坐标
for ( var endPts = glyf.endPtsOfContours[i]; currentPts < endPts + 1; currentPts++) {
var currentPoint = coordinates[currentPts];
var prevPoint = (currentPts === startPts)
currentPoint = coordinates[currentPts];
prevPoint = (currentPts === startPts)
? coordinates[endPts]
: coordinates[currentPts - 1];
var nextPoint = (currentPts === endPts)
nextPoint = (currentPts === endPts)
? coordinates[startPts]
: coordinates[currentPts + 1];
if (currentPoint == undefined) {
if (!currentPoint) {
continue;
}
@@ -70,7 +74,7 @@ var glyfAdjust = require('ttf/util/glyfAdjust');
}
});
}
// 起始点不在曲线上
// 起始点不在曲线上则中间点为bezier曲线的起始点
else {
var midPoint = {
@@ -96,15 +100,11 @@ var glyfAdjust = require('ttf/util/glyfAdjust');
// 直线
if (
currentPoint != undefined
currentPoint
&& currentPoint.isOnCurve
&& prevPoint != undefined
&& prevPoint
&& prevPoint.isOnCurve
) {
if(curBezier) {
curBezier.p = prevPoint;
curBezier = null;
}
commandQueue.push({
c: 'L',
p: {
@@ -113,11 +113,26 @@ var glyfAdjust = require('ttf/util/glyfAdjust');
}
});
}
// 当前在曲线上,并且上一个不在曲线上
// 则当前为bezier终点
else if (
currentPoint.isOnCurve
&& prevPoint
&& !prevPoint.isOnCurve
) {
if(curBezier) {
curBezier.p = {
x: currentPoint.x,
y: currentPoint.y
};
curBezier = null;
}
}
// 当前点不在曲线上,并且上一个点不在曲线上
// 贝塞尔曲线的连续情况
// 贝塞尔曲线的连续情况,需要求中间点为终点和起点
else if (
!currentPoint.isOnCurve
&& prevPoint != undefined
&& prevPoint
&& !prevPoint.isOnCurve
) {
@@ -153,13 +168,13 @@ var glyfAdjust = require('ttf/util/glyfAdjust');
}
}
// 处理最后一个点
// 最后一个点不在曲线上
if (
!currentPoint.isOnCurve
&& coordinates[startPts] != undefined
&& coordinates[startPts]
) {
// 轮廓起始点在曲线上
// 轮廓起始点在曲线上则起始点为bezier曲线终点
if (coordinates[startPts].isOnCurve) {
if(curBezier) {
curBezier.p = {