商域无疆 (http://blog.csdn.net/omni360/)
本文遵循“署名-非商业用途-保持一致”创作公用协议
转载请保留此句: 商域无疆 - 本博客专注于 敏捷开发及移动和物联设备研究:数据可视化、GOLANG、Html5、WEBGL、THREE.JS , 否则,出自本博客的文章拒绝转载或再转载,谢谢合作。
俺也是刚开始学,好多地儿肯定不对还请见谅.
以下代码是THREE.JS 源码文件中extras/core/Shape.js文件的注释.
更多更新在 : https://github.com/omni360/three.js.sourcecode
/**
* @author zz85 / http://www.lab4games.net/zz85/blog
* Defines a 2d shape plane using paths.
**/
// STEP 1 Create a path.
// 1. 创建路径
// STEP 2 Turn path into shape.
// 2. 将路径变成截面
// STEP 3 ExtrudeGeometry takes in Shape/Shapes
// 3. 将截面拉伸成几何体
// STEP 3a - Extract points from each shape, turn to vertices
// 3a. 导出所有的截面顶点到vertices属性中
// STEP 3b - Triangulate each shape, add faces.
// 3b. 组织所有的顶点为三角面.
/*
///Shape对象将二维平面生成图形对象的抽象基类.
///
*/
///Shape
THREE.Shape = function () {
THREE.Path.apply( this, arguments ); //调用Path对象的call方法,将原本属于Path的方法交给当前对象Shape来使用.
this.holes = []; //将孔洞存放到holes数组中.
};
/*************************************************
****下面是Shape对象的方法属性定义,继承自Path对象.
**************************************************/
THREE.Shape.prototype = Object.create( THREE.Path.prototype );
/*
///extrude生成拉伸几何体的便利方法.
///
/// parameters = {
///
/// curveSegments: , // number of points on the curves 曲线上的顶点数量
/// steps: , // number of points for z-side extrusions / used for subdividing segements of extrude spline too 步数,曲线拉伸的细分线段数
/// amount: , // Depth to extrude the shape 拉伸线段的厚度.
///
/// bevelEnabled: , // turn on bevel 是否启用倒角
/// bevelThickness: , // how deep into the original shape bevel goes 倒角的厚度
/// bevelSize: , // how far from shape outline is bevel 从截面外轮廓倒角的尺寸.
/// bevelSegments: , // number of bevel layers 倒角部分的细分线段数.
///
/// extrudePath: // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined) 截面拉伸的路径,3d的spline对象.
/// frames: // containing arrays of tangents, normals, binormals 包含三角形,法线,副法线数组.
///
/// material: // material index for front and back faces 正面和背面材质索引
/// extrudeMaterial: // material index for extrusion and beveled faces 拉伸体和斜面的材质索引
/// uvGenerator: // object that provides UV generator functions UV坐标生成函数.
///
/// }
*/
///extrude
/// 参数选项
///拉伸几何体.
// Convenience method to return ExtrudeGeometry
// 生成拉伸几何体的便利方法.
THREE.Shape.prototype.extrude = function ( options ) {
var extruded = new THREE.ExtrudeGeometry( this, options );
return extruded;
};
/*
///makeGeometry创建图形几何体的便利方法
///
/// parameters = {
///
/// curveSegments: , // number of points on the curves. NOT USED AT THE MOMENT. 曲线上的顶点数量
///
/// material: // material index for front and back faces 正面和背面材质索引
/// uvGenerator: // object that provides UV generator functions UV坐标生成函数
///
/// }
*/
///makeGeometry
/// 参数选项
///拉伸几何体.
// Convenience method to return ShapeGeometry
// 创建图形几何体的便利方法.
THREE.Shape.prototype.makeGeometry = function ( options ) {
var geometry = new THREE.ShapeGeometry( this, options );
return geometry;
};
/*
///getPointsHoles方法根据divisions将孔洞等分,获得在孔洞对象上等分点的点序列.如果没有设置参数divisions.返回对应等分孔洞顶点的坐标数组.
///定量等分孔洞
*/
///getPointsHoles
/// 根据divisions将孔洞等分,获得在孔洞对象上等分点的点序列.如果没有设置参数divisions.
///返回对应等分孔洞顶点的坐标数组.
// Get points of holes
// 定量等分,获得所有孔洞的顶点
THREE.Shape.prototype.getPointsHoles = function ( divisions ) {
var i, il = this.holes.length, holesPts = [];
for ( i = 0; i < il; i ++ ) {
holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends );
}
return holesPts; //返回对应等分孔洞顶点的坐标数组
};
/*
///getSpacedPointsHoles方法根据divisions将孔洞等分,获得在孔洞对象上等分点的点序列.如果没有设置参数divisions.返回对应等分孔洞顶点的坐标数组.
///定距等分孔洞
*/
///getSpacedPointsHoles
/// 根据divisions将孔洞等分,获得在孔洞对象上等分点的点序列.如果没有设置参数divisions.
///返回对应等分孔洞顶点的坐标数组.
// Get points of holes (spaced by regular distance)
// 定距等分,获得所有孔洞的顶点.
THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) {
var i, il = this.holes.length, holesPts = [];
for ( i = 0; i < il; i ++ ) {
holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends );
}
return holesPts; //返回对应等分孔洞顶点的坐标数组
};
/*
///extractAllPoints方法根据divisions将孔洞等分,获得在所有界面和孔洞对象上等分点的点序列.如果没有设置参数divisions.返回所有界面和孔洞等分顶点的坐标数组.
///定量等分孔洞
*/
///extractAllPoints
/// 根据divisions将所有界面和孔洞等分,获得在所有界面和孔洞对象上等分点的点序列.如果没有设置参数divisions.
///返回所有界面和孔洞等分顶点的坐标数组.
// Get points of shape and holes (keypoints based on segments parameter)
// 定量等分,获得所有界面和孔洞的顶点.
THREE.Shape.prototype.extractAllPoints = function ( divisions ) {
return {
shape: this.getTransformedPoints( divisions ),
holes: this.getPointsHoles( divisions )
};
};
/*
///extractPoints方法根据divisions将孔洞等分,获得在所有界面和孔洞对象上等分点的点序列.如果没有设置参数divisions.返回所有界面和孔洞等分顶点的坐标数组.
///
*/
///extractPoints
/// 根据divisions将所有界面和孔洞等分,获得在所有界面和孔洞对象上等分点的点序列.如果没有设置参数divisions.
///返回所有界面和孔洞等分顶点的坐标数组.
// 等分所有界面和孔洞,获得的顶点
THREE.Shape.prototype.extractPoints = function ( divisions ) {
if (this.useSpacedPoints) {
return this.extractAllSpacedPoints(divisions);
}
return this.extractAllPoints(divisions);
};
//
// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) {
//
// return {
//
// shape: this.transform( bend, divisions ),
// holes: this.getPointsHoles( divisions, bend )
//
// };
//
// };
/*
///extractAllSpacedPoints方法根据divisions将孔洞等分,获得在所有界面和孔洞对象上等分点的点序列.如果没有设置参数divisions.返回所有界面和孔洞等分顶点的坐标数组.
///定距等分孔洞
*/
///extractAllSpacedPoints
/// 根据divisions将所有界面和孔洞等分,获得在所有界面和孔洞对象上等分点的点序列.如果没有设置参数divisions.
///返回所有界面和孔洞等分顶点的坐标数组.
// Get points of shape and holes (spaced by regular distance)
// 定距等分,获得所有界面和孔洞的顶点.
THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) {
return {
shape: this.getTransformedSpacedPoints( divisions ),
holes: this.getSpacedPointsHoles( divisions )
};
};
/**************************************************************
* Utils shape对象的工具集
**************************************************************/
THREE.Shape.Utils = {
/*
///triangulateShape方法将传递的顶点数组(参数contour)和镂空(孔洞)数组(参数holes)三角化.
*/
///triangulateShape
/// 拉伸几何体的顶点数据.
/// 镂空(孔洞)顶点数据.
///返回围绕形状的顶点索引.
triangulateShape: function ( contour, holes ) {
function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) {
// inOtherPt needs to be colinear to the inSegment
if ( inSegPt1.x != inSegPt2.x ) {
if ( inSegPt1.x < inSegPt2.x ) {
return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) );
} else {
return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) );
}
} else {
if ( inSegPt1.y < inSegPt2.y ) {
return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) );
} else {
return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) );
}
}
}
/*
///intersect_segments_2D方法返回两条线段的交点.
*/
///intersect_segments_2D
/// 要检查交点的第一条线的起始点.
/// 要检查交点的第一条线的结束点.
/// 要检查交点的第二条线的起始点.
/// 要检查交点的第二条线的结束点.
/// 是否排除相邻的线段.
///二维向量数组.
function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) {
var EPSILON = 0.0000000001;
var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y;
var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y;
var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x;
var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y;
var limit = seg1dy * seg2dx - seg1dx * seg2dy;
var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy;
if ( Math.abs(limit) > EPSILON ) { // not parallel //两条线不平行
var perpSeg2;
if ( limit > 0 ) {
if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; //返回空数组
perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; //返回空数组
} else {
if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; //返回空数组
perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; //返回空数组
}
// i.e. to reduce rounding errors
// intersection at endpoint of segment#1?
// 交点位于第一条线的端点
if ( perpSeg2 == 0 ) {
if ( ( inExcludeAdjacentSegs ) &&
( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return []; //返回空数组
return [ inSeg1Pt1 ];
}
if ( perpSeg2 == limit ) {
if ( ( inExcludeAdjacentSegs ) &&
( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return []; //返回空数组
return [ inSeg1Pt2 ];
}
// intersection at endpoint of segment#2?
// 交点位于第二条线的端点
if ( perpSeg1 == 0 ) return [ inSeg2Pt1 ];
if ( perpSeg1 == limit ) return [ inSeg2Pt2 ];
// return real intersection point
// 返回真正的交点
var factorSeg1 = perpSeg2 / limit;
return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx,
y: inSeg1Pt1.y + factorSeg1 * seg1dy } ];
} else { // parallel or colinear 平行或共线
if ( ( perpSeg1 != 0 ) ||
( seg2dy * seg1seg2dx != seg2dx * seg1seg2dy ) ) return []; //返回空数组
// they are collinear or degenerate 两条线共线或则无效
var seg1Pt = ( (seg1dx == 0) && (seg1dy == 0) ); // segment1 ist just a point? 第一条线只是一个点
var seg2Pt = ( (seg2dx == 0) && (seg2dy == 0) ); // segment2 ist just a point? 第二条线只是一个点
// both segments are points 两条线都是点
if ( seg1Pt && seg2Pt ) {
if ( (inSeg1Pt1.x != inSeg2Pt1.x) ||
(inSeg1Pt1.y != inSeg2Pt1.y) ) return []; // they are distinct points 两个点不共点,返回空数组
return [ inSeg1Pt1 ]; // they are the same point 共点
}
// segment#1 is a single point 第一条线段是一个点
if ( seg1Pt ) {
if (! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 不在第二条线段内,返回空数组
return [ inSeg1Pt1 ];
}
// segment#2 is a single point 第二条线是一个点
if ( seg2Pt ) {
if (! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 不在第一条线段内,返回空数组
return [ inSeg2Pt1 ];
}
// they are collinear segments, which might overlap 两条线共线,有可能重叠.
var seg1min, seg1max, seg1minVal, seg1maxVal;
var seg2min, seg2max, seg2minVal, seg2maxVal;
if (seg1dx != 0) { // the segments are NOT on a vertical line 线不是垂直线
if ( inSeg1Pt1.x < inSeg1Pt2.x ) {
seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x;
seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x;
} else {
seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x;
seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x;
}
if ( inSeg2Pt1.x < inSeg2Pt2.x ) {
seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x;
seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x;
} else {
seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x;
seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x;
}
} else { // the segments are on a vertical line
if ( inSeg1Pt1.y < inSeg1Pt2.y ) {
seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y;
seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y;
} else {
seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y;
seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y;
}
if ( inSeg2Pt1.y < inSeg2Pt2.y ) {
seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y;
seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y;
} else {
seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y;
seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y;
}
}
if ( seg1minVal <= seg2minVal ) {
if ( seg1maxVal < seg2minVal ) return [];
if ( seg1maxVal == seg2minVal ) {
if ( inExcludeAdjacentSegs ) return [];
return [ seg2min ];
}
if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ];
return [ seg2min, seg2max ];
} else {
if ( seg1minVal > seg2maxVal ) return [];
if ( seg1minVal == seg2maxVal ) {
if ( inExcludeAdjacentSegs ) return [];
return [ seg1min ];
}
if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ];
return [ seg1min, seg2max ];
}
}
}
/*
///isPointInsideAngle方法判断第四个参数是否在前三个参数组成的三角形内.
*/
///isPointInsideAngle
/// 顶点索引.
/// 上一个顶点索引.
/// 下一个顶点索引.
/// 孔洞顶点索引.
///true 或者 false.
function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) {
// The order of legs is important 参数的排列顺序非常重要.
var EPSILON = 0.0000000001;
// translation of all points, so that Vertex is at (0,0)
var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y;
var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y;
var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y;
// main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg.
var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX;
var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX;
if ( Math.abs(from2toAngle) > EPSILON ) { // angle != 180 deg.
var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX;
// console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle );
if ( from2toAngle > 0 ) { // main angle < 180 deg.
return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) );
} else { // main angle > 180 deg.
return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) );
}
} else { // angle == 180 deg.
// console.log( "from2to: 180 deg., from2other: " + from2otherAngle );
return ( from2otherAngle > 0 );
}
}
/*
///removeHoles方法从拉伸几何体中删除孔洞.
*/
///removeHoles
/// 拉伸几何体的顶点数据.
/// 镂空(孔洞)顶点数据.
///返回没有镂空(孔洞)的拉伸几何体.
function removeHoles( contour, holes ) {
var shape = contour.concat(); // work on this shape
var hole;
/*
///isCutLineInsideAngles方法返回当前索引所指的图形顶点在镂空顶点,以及前一个顶点,后一个顶点组成的三角形内.
// 或者当前索引所指的镂空顶点在图形顶点,以及前一个顶点,后一个顶点组成的三角形内,true为真.
*/
///isCutLineInsideAngles
/// 拉伸几何体的顶点数据.
/// 镂空(孔洞)顶点数据.
///true 或者 false.
function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) {
// Check if hole point lies within angle around shape point
// 检查镂空(孔洞)的顶点在
var lastShapeIdx = shape.length - 1;
var prevShapeIdx = inShapeIdx - 1;
if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx;
var nextShapeIdx = inShapeIdx + 1;
if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0;
var insideAngle = isPointInsideAngle( shape[inShapeIdx], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[inHoleIdx] );
if (! insideAngle ) {
// console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y );
return false;
}
// Check if shape point lies within angle around hole point
// 检查图形顶点位于环绕镂空(孔洞)的三角形内.
var lastHoleIdx = hole.length - 1;
var prevHoleIdx = inHoleIdx - 1;
if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx;
var nextHoleIdx = inHoleIdx + 1;
if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0;
insideAngle = isPointInsideAngle( hole[inHoleIdx], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[inShapeIdx] );
if (! insideAngle ) {
// console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y );
return false;
}
return true; //
}
/*
///intersectsShapeEdge方法检查镂空(孔洞)与形状边界是否有交点,true为真.
*/
///isCutLineInsideAngles
/// 拉伸几何体的顶点数据.
/// 镂空(孔洞)顶点数据.
///true 或者 false.
function intersectsShapeEdge( inShapePt, inHolePt ) {
// checks for intersections with shape edges
// 检查镂空(孔洞)与形状边界是否有交点.
var sIdx, nextIdx, intersection;
for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) {
nextIdx = sIdx+1; nextIdx %= shape.length;
intersection = intersect_segments_2D( inShapePt, inHolePt, shape[sIdx], shape[nextIdx], true );
if ( intersection.length > 0 ) return true;
}
return false;
}
var indepHoles = [];
/*
///intersectsShapeEdge方法检查当前的镂空(孔洞)是否是否与其它镂空(孔洞)边界相交,true为真.
*/
///isCutLineInsideAngles
/// 拉伸几何体的顶点数据.
/// 镂空(孔洞)顶点数据.
///true 或者 false.
function intersectsHoleEdge( inShapePt, inHolePt ) {
// checks for intersections with hole edges
// 检查当前的镂空(孔洞)是否是否与其它镂空(孔洞)边界相交.
var ihIdx, chkHole,
hIdx, nextIdx, intersection;
for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) {
chkHole = holes[indepHoles[ihIdx]];
for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) {
nextIdx = hIdx+1; nextIdx %= chkHole.length;
intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[hIdx], chkHole[nextIdx], true );
if ( intersection.length > 0 ) return true;
}
}
return false;
}
var holeIndex, shapeIndex,
shapePt, holePt,
holeIdx, cutKey, failedCuts = [],
tmpShape1, tmpShape2,
tmpHole1, tmpHole2;
for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
indepHoles.push( h );
}
var minShapeIndex = 0;
var counter = indepHoles.length * 2;
while ( indepHoles.length > 0 ) {
counter --;
if ( counter < 0 ) {
console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" );
break;
}
// search for shape-vertex and hole-vertex,
// 搜索形状的顶点和镂空(孔洞)顶点
// which can be connected without intersections
// 哪些可以连接并无交点.
for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) {
shapePt = shape[ shapeIndex ];
holeIndex = - 1;
// search for hole which can be reached without intersections
// 搜索镂空(孔洞)的顶点,哪些可以到达并没有交点.
for ( var h = 0; h < indepHoles.length; h ++ ) {
holeIdx = indepHoles[h];
// prevent multiple checks
// 避免多次检查
cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx;
if ( failedCuts[cutKey] !== undefined ) continue;
hole = holes[holeIdx];
for ( var h2 = 0; h2 < hole.length; h2 ++ ) {
holePt = hole[ h2 ];
if (! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; //如果孔洞顶点不在切线内
if ( intersectsShapeEdge( shapePt, holePt ) ) continue; //如果与图形的边相交.
if ( intersectsHoleEdge( shapePt, holePt ) ) continue; //如果与镂空(空洞)的边相交.
holeIndex = h2;
indepHoles.splice(h,1);
tmpShape1 = shape.slice( 0, shapeIndex+1 );
tmpShape2 = shape.slice( shapeIndex );
tmpHole1 = hole.slice( holeIndex );
tmpHole2 = hole.slice( 0, holeIndex+1 );
shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
minShapeIndex = shapeIndex;
// Debug only, to show the selected cuts
// glob_CutLines.push( [ shapePt, holePt ] );
break;
}
if ( holeIndex >= 0 ) break; // hole-vertex found 找到镂空顶点
failedCuts[cutKey] = true; // remember failure //添加切割线顶点索引
}
if ( holeIndex >= 0 ) break; // hole-vertex found //找到镂空(孔洞)顶点
}
}
return shape; /* shape with no holes */ // 返回不包含镂空(孔洞)的形状.
}
var i, il, f, face,
key, index,
allPointsMap = {};
// To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first.
// 将孔洞的顶点按照图形原来坐标顺序,偏移索引,这是首先能做的.
var allpoints = contour.concat(); //声明数组,存放合并后的顶点数组.
for ( var h = 0, hl = holes.length; h < hl; h ++ ) { //遍历镂空(孔洞)的顶点
Array.prototype.push.apply( allpoints, holes[h] ); //将镂空(孔洞)的顶点压入所有顶点数组中.
}
//console.log( "allpoints",allpoints, allpoints.length );
// prepare all points map 准备所有的顶点的哈希表.
for ( i = 0, il = allpoints.length; i < il; i ++ ) {
key = allpoints[ i ].x + ":" + allpoints[ i ].y;
if ( allPointsMap[ key ] !== undefined ) {
console.log( "Duplicate point", key );
}
allPointsMap[ key ] = i;
}
// remove holes by cutting paths to holes and adding them to the shape
// 删除镂空(孔洞),并将孔洞的作为实体的一部分.
var shapeWithoutHoles = removeHoles( contour, holes ); //调用removeHoles方法.
var triangles = THREE.FontUtils.Triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape 真正返回围绕形状的顶点索引.
//console.log( "triangles",triangles, triangles.length );
// check all face vertices against all points map
// 检查所有的面顶点顺序与所有顶点的哈希表一致.
for ( i = 0, il = triangles.length; i < il; i ++ ) {
face = triangles[ i ];
for ( f = 0; f < 3; f ++ ) {
key = face[ f ].x + ":" + face[ f ].y;
index = allPointsMap[ key ];
if ( index !== undefined ) {
face[ f ] = index;
}
}
}
return triangles.concat(); //返回围绕形状的顶点索引.
},
/*
///isClockWise方法判断顶点坐标数组的顺序是否是顺时针.
*/
///isClockWise
/// 顶点坐标数组
///返回true 或者 false.
isClockWise: function ( pts ) {
return THREE.FontUtils.Triangulate.area( pts ) < 0;
},
// Bezier Curves formulas obtained from
// http://en.wikipedia.org/wiki/B%C3%A9zier_curve
// Quad Bezier Functions
// 二次贝塞尔方程.
b2p0: function ( t, p ) {
var k = 1 - t;
return k * k * p;
},
b2p1: function ( t, p ) {
return 2 * ( 1 - t ) * t * p;
},
b2p2: function ( t, p ) {
return t * t * p;
},
b2: function ( t, p0, p1, p2 ) {
return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 );
},
// Cubic Bezier Functions
// 三次贝塞尔取信方程.
b3p0: function ( t, p ) {
var k = 1 - t;
return k * k * k * p;
},
b3p1: function ( t, p ) {
var k = 1 - t;
return 3 * k * k * t * p;
},
b3p2: function ( t, p ) {
var k = 1 - t;
return 3 * k * t * t * p;
},
b3p3: function ( t, p ) {
return t * t * t * p;
},
b3: function ( t, p0, p1, p2, p3 ) {
return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 );
}
};
商域无疆 (http://blog.csdn.net/omni360/)
本文遵循“署名-非商业用途-保持一致”创作公用协议
转载请保留此句: 商域无疆 - 本博客专注于 敏捷开发及移动和物联设备研究:数据可视化、GOLANG、Html5、WEBGL、THREE.JS , 否则,出自本博客的文章拒绝转载或再转载,谢谢合作。
以下代码是THREE.JS 源码文件中extras/core/Shape.js文件的注释.
更多更新在 : https://github.com/omni360/three.js.sourcecode