DxfLoader.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. import * as THREE from "../../libs/three.js/build/three.module.js";
  2. import '../../libs/three.js/loaders/dxf/dxf-parser.js'
  3. export default class DxfLoader extends THREE.EventDispatcher{
  4. constructor(){
  5. super()
  6. this.parser = new Potree.DxfParser
  7. }
  8. load(url,done, options={}){
  9. Potree.loadFile(url, {returnText:true} , (data)=>{
  10. var dxfJson = this.parser.parseSync(data);
  11. console.log(dxfJson)
  12. let model = this.createModel(dxfJson, options)
  13. done(model)
  14. })
  15. }
  16. createModel(data, options={}){
  17. let root = new THREE.Object3D;
  18. var i, entity, obj, min_x, min_y, min_z, max_x, max_y, max_z;
  19. /* var dims = {
  20. min: { x: false, y: false, z: false},
  21. max: { x: false, y: false, z: false}
  22. }; */
  23. //let bound = THREE.Box3
  24. for(i = 0; i < data.entities.length; i++) {
  25. entity = data.entities[i];
  26. obj = drawEntity(entity, data, options.unsupportTypes );
  27. if (obj) {
  28. var bbox = new THREE.Box3().setFromObject(obj);
  29. /* if (bbox.min.x && ((dims.min.x === false) || (dims.min.x > bbox.min.x))) dims.min.x = bbox.min.x;
  30. if (bbox.min.y && ((dims.min.y === false) || (dims.min.y > bbox.min.y))) dims.min.y = bbox.min.y;
  31. if (bbox.min.z && ((dims.min.z === false) || (dims.min.z > bbox.min.z))) dims.min.z = bbox.min.z;
  32. if (bbox.max.x && ((dims.max.x === false) || (dims.max.x < bbox.max.x))) dims.max.x = bbox.max.x;
  33. if (bbox.max.y && ((dims.max.y === false) || (dims.max.y < bbox.max.y))) dims.max.y = bbox.max.y;
  34. if (bbox.max.z && ((dims.max.z === false) || (dims.max.z < bbox.max.z))) dims.max.z = bbox.max.z; */
  35. root.add(obj);
  36. }
  37. obj = null;
  38. }
  39. return root
  40. }
  41. }
  42. function drawEntity(entity, data, unsupportTypes, group) {
  43. var mesh;
  44. if(unsupportTypes && unsupportTypes.includes(entity.type)){
  45. console.warn('遇到一个dxf不会绘制的type:', entity.type)
  46. return //add 不绘制该项
  47. }
  48. if(entity.type === 'CIRCLE' || entity.type === 'ARC') {
  49. mesh = drawArc(entity, data, unsupportTypes, group );
  50. } else if(entity.type === 'LWPOLYLINE' || entity.type === 'LINE' || entity.type === 'POLYLINE') {
  51. mesh = drawLine(entity, data, unsupportTypes, group );
  52. } else if(entity.type === 'TEXT') {//"MTEXT" text: "{\fKaiTi|b0|i0|c134|p49;糕点制作台}"
  53. mesh = drawText(entity, data, unsupportTypes, group);
  54. } else if(entity.type === 'SOLID') {
  55. mesh = drawSolid(entity, data);
  56. } else if(entity.type === 'POINT') {
  57. mesh = drawPoint(entity, data );
  58. } else if(entity.type === 'INSERT') {//嵌套其他group
  59. mesh = drawBlock(entity, data, unsupportTypes, group );
  60. } else if(entity.type === 'SPLINE') {
  61. mesh = drawSpline(entity, data);
  62. } else if(entity.type === 'MTEXT') {
  63. mesh = drawMtext(entity, data, unsupportTypes, group );
  64. } else if(entity.type === 'ELLIPSE') {
  65. mesh = drawEllipse(entity, data, unsupportTypes, group );
  66. } else if(entity.type === 'DIMENSION') {
  67. var dimTypeEnum = entity.dimensionType & 7;
  68. if(dimTypeEnum === 0) {
  69. mesh = drawDimension(entity, data);
  70. } else {
  71. console.log("Unsupported Dimension type: " + dimTypeEnum);
  72. }
  73. }
  74. else {
  75. console.log("Unsupported Entity Type: " + entity.type);
  76. }
  77. if(entity.type === 'LWPOLYLINE' || entity.type === 'LINE' || entity.type === 'POLYLINE') {
  78. mesh = drawLine(entity, data);
  79. }
  80. /* if(entity.type === 'INSERT') {
  81. mesh = drawBlock(entity, data); //group
  82. } */
  83. /*
  84. 门都是arc
  85. 似乎墙壁都是不是insert类型,但窗户是
  86. */
  87. mesh && (mesh.dxfInfo = entity) //add
  88. return mesh;
  89. }
  90. function drawEllipse(entity, data) {
  91. var color = getColor(entity, data);
  92. var xrad = Math.sqrt(Math.pow(entity.majorAxisEndPoint.x,2) + Math.pow(entity.majorAxisEndPoint.y,2));
  93. var yrad = xrad*entity.axisRatio;
  94. var rotation = Math.atan2(entity.majorAxisEndPoint.y, entity.majorAxisEndPoint.x);
  95. var curve = new THREE.EllipseCurve(
  96. entity.center.x, entity.center.y,
  97. xrad, yrad,
  98. entity.startAngle, entity.endAngle,
  99. false, // Always counterclockwise
  100. rotation
  101. );
  102. var points = curve.getPoints( 50 );
  103. var geometry = new THREE.BufferGeometry().setFromPoints( points );
  104. var material = new THREE.LineBasicMaterial( { linewidth: 1, color : color } );
  105. // Create the final object to add to the scene
  106. var ellipse = new THREE.Line( geometry, material );
  107. return ellipse;
  108. }
  109. function drawMtext(entity, data) {
  110. var color = getColor(entity, data);
  111. //if (!font) { return console.log('font parameter not set. Ignoring text entity.')}
  112. var geometry = new THREE.TextGeometry( entity.text, {
  113. //font: font,
  114. size: entity.height * (4/5),
  115. height: 1
  116. });
  117. var material = new THREE.MeshBasicMaterial( {color: color} );
  118. var text = new THREE.Mesh( geometry, material );
  119. // Measure what we rendered.
  120. var measure = new THREE.Box3();
  121. measure.setFromObject( text );
  122. var textWidth = measure.max.x - measure.min.x;
  123. // If the text ends up being wider than the box, it's supposed
  124. // to be multiline. Doing that in threeJS is overkill.
  125. if (textWidth > entity.width) {
  126. console.log("Can't render this multipline MTEXT entity, sorry.", entity);
  127. return undefined;
  128. }
  129. text.position.z = 0;
  130. switch (entity.attachmentPoint) {
  131. case 1:
  132. // Top Left
  133. text.position.x = entity.position.x;
  134. text.position.y = entity.position.y - entity.height;
  135. break;
  136. case 2:
  137. // Top Center
  138. text.position.x = entity.position.x - textWidth/2;
  139. text.position.y = entity.position.y - entity.height;
  140. break;
  141. case 3:
  142. // Top Right
  143. text.position.x = entity.position.x - textWidth;
  144. text.position.y = entity.position.y - entity.height;
  145. break;
  146. case 4:
  147. // Middle Left
  148. text.position.x = entity.position.x;
  149. text.position.y = entity.position.y - entity.height/2;
  150. break;
  151. case 5:
  152. // Middle Center
  153. text.position.x = entity.position.x - textWidth/2;
  154. text.position.y = entity.position.y - entity.height/2;
  155. break;
  156. case 6:
  157. // Middle Right
  158. text.position.x = entity.position.x - textWidth;
  159. text.position.y = entity.position.y - entity.height/2;
  160. break;
  161. case 7:
  162. // Bottom Left
  163. text.position.x = entity.position.x;
  164. text.position.y = entity.position.y;
  165. break;
  166. case 8:
  167. // Bottom Center
  168. text.position.x = entity.position.x - textWidth/2;
  169. text.position.y = entity.position.y;
  170. break;
  171. case 9:
  172. // Bottom Right
  173. text.position.x = entity.position.x - textWidth;
  174. text.position.y = entity.position.y;
  175. break;
  176. default:
  177. return undefined;
  178. };
  179. return text;
  180. }
  181. function drawSpline(entity, data) {
  182. var color = getColor(entity, data);
  183. var points = entity.controlPoints.map(function(vec) {
  184. return new THREE.Vector2(vec.x, vec.y);
  185. });
  186. var interpolatedPoints = [];
  187. var curve;
  188. if (entity.degreeOfSplineCurve === 2 || entity.degreeOfSplineCurve === 3) {
  189. for(var i = 0; i + 2 < points.length; i = i + 2) {
  190. if (entity.degreeOfSplineCurve === 2) {
  191. curve = new THREE.QuadraticBezierCurve(points[i], points[i + 1], points[i + 2]);
  192. } else {
  193. curve = new THREE.QuadraticBezierCurve3(points[i], points[i + 1], points[i + 2]);
  194. }
  195. interpolatedPoints.push.apply(interpolatedPoints, curve.getPoints(50));
  196. }
  197. } else {
  198. curve = new THREE.SplineCurve(points);
  199. interpolatedPoints = curve.getPoints( 100 );
  200. }
  201. var geometry = new THREE.BufferGeometry().setFromPoints( interpolatedPoints );
  202. var material = new THREE.LineBasicMaterial( { linewidth: 1, color : color } );
  203. var splineObject = new THREE.Line( geometry, material );
  204. return splineObject;
  205. }
  206. function drawLine(entity, data) {
  207. var geometry = new THREE.Geometry(),
  208. color = getColor(entity, data),
  209. material, lineType, vertex, startPoint, endPoint, bulgeGeometry,
  210. bulge, i, line;
  211. if (!entity.vertices) return console.log('entity missing vertices.');
  212. // create geometry
  213. for(i = 0; i < entity.vertices.length; i++) {
  214. if(entity.vertices[i].bulge) {
  215. bulge = entity.vertices[i].bulge;
  216. startPoint = entity.vertices[i];
  217. endPoint = i + 1 < entity.vertices.length ? entity.vertices[i + 1] : geometry.vertices[0];
  218. bulgeGeometry = new THREEx.BulgeGeometry(startPoint, endPoint, bulge);
  219. geometry.vertices.push.apply(geometry.vertices, bulgeGeometry.vertices);
  220. } else {
  221. vertex = entity.vertices[i];
  222. geometry.vertices.push(new THREE.Vector3(vertex.x, vertex.y, 0));
  223. }
  224. }
  225. if(entity.shape) geometry.vertices.push(geometry.vertices[0]);
  226. // set material
  227. if(entity.lineType) {
  228. lineType = data.tables.lineType.lineTypes[entity.lineType];
  229. }
  230. if(lineType && lineType.pattern && lineType.pattern.length !== 0) {
  231. material = new THREE.LineDashedMaterial({ color: color, gapSize: 4, dashSize: 4});
  232. } else {
  233. material = new THREE.LineBasicMaterial({ linewidth: 1, color: color });
  234. }
  235. // if(lineType && lineType.pattern && lineType.pattern.length !== 0) {
  236. // geometry.computeLineDistances();
  237. // // Ugly hack to add diffuse to this. Maybe copy the uniforms object so we
  238. // // don't add diffuse to a material.
  239. // lineType.material.uniforms.diffuse = { type: 'c', value: new THREE.Color(color) };
  240. // material = new THREE.ShaderMaterial({
  241. // uniforms: lineType.material.uniforms,
  242. // vertexShader: lineType.material.vertexShader,
  243. // fragmentShader: lineType.material.fragmentShader
  244. // });
  245. // }else {
  246. // material = new THREE.LineBasicMaterial({ linewidth: 1, color: color });
  247. // }
  248. line = new THREE.Line(geometry, material);
  249. return line;
  250. }
  251. function drawArc(entity, data) {
  252. var startAngle, endAngle;
  253. if (entity.type === 'CIRCLE') {
  254. startAngle = entity.startAngle || 0;
  255. endAngle = startAngle + 2 * Math.PI;
  256. } else {
  257. startAngle = entity.startAngle;
  258. endAngle = entity.endAngle;
  259. }
  260. var curve = new THREE.ArcCurve(
  261. 0, 0,
  262. entity.radius,
  263. startAngle,
  264. endAngle);
  265. var points = curve.getPoints( 32 );
  266. var geometry = new THREE.BufferGeometry().setFromPoints( points );
  267. var material = new THREE.LineBasicMaterial({ color: getColor(entity, data) });
  268. var arc = new THREE.Line(geometry, material);
  269. arc.position.x = entity.center.x;
  270. arc.position.y = entity.center.y;
  271. arc.position.z = entity.center.z;
  272. return arc;
  273. }
  274. function drawSolid(entity, data) {
  275. var material, mesh, verts,
  276. geometry = new THREE.Geometry();
  277. verts = geometry.vertices;
  278. verts.push(new THREE.Vector3(entity.points[0].x, entity.points[0].y, entity.points[0].z));
  279. verts.push(new THREE.Vector3(entity.points[1].x, entity.points[1].y, entity.points[1].z));
  280. verts.push(new THREE.Vector3(entity.points[2].x, entity.points[2].y, entity.points[2].z));
  281. verts.push(new THREE.Vector3(entity.points[3].x, entity.points[3].y, entity.points[3].z));
  282. // Calculate which direction the points are facing (clockwise or counter-clockwise)
  283. var vector1 = new THREE.Vector3();
  284. var vector2 = new THREE.Vector3();
  285. vector1.subVectors(verts[1], verts[0]);
  286. vector2.subVectors(verts[2], verts[0]);
  287. vector1.cross(vector2);
  288. // If z < 0 then we must draw these in reverse order
  289. if(vector1.z < 0) {
  290. geometry.faces.push(new THREE.Face3(2, 1, 0));
  291. geometry.faces.push(new THREE.Face3(2, 3, 1));
  292. } else {
  293. geometry.faces.push(new THREE.Face3(0, 1, 2));
  294. geometry.faces.push(new THREE.Face3(1, 3, 2));
  295. }
  296. material = new THREE.MeshBasicMaterial({ color: getColor(entity, data) });
  297. return new THREE.Mesh(geometry, material);
  298. }
  299. function drawText(entity, data) {
  300. var geometry, material, text;
  301. //必须要font才能画出文字
  302. /* if(!font)
  303. return console.warn('Text is not supported without a Three.js font loaded with THREE.FontLoader! Load a font of your choice and pass this into the constructor. See the sample for this repository or Three.js examples at http://threejs.org/examples/?q=text#webgl_geometry_text for more details.');
  304. */
  305. geometry = new THREE.TextGeometry(entity.text, {/* font: font, */ height: 0, size: entity.textHeight || 12 });
  306. if (entity.rotation) {
  307. var zRotation = entity.rotation * Math.PI / 180;
  308. geometry.rotateZ(zRotation);
  309. }
  310. material = new THREE.MeshBasicMaterial({ color: getColor(entity, data) });
  311. text = new THREE.Mesh(geometry, material);
  312. text.position.x = entity.startPoint.x;
  313. text.position.y = entity.startPoint.y;
  314. text.position.z = entity.startPoint.z;
  315. return text;
  316. }
  317. function drawPoint(entity, data) {
  318. var geometry, material, point;
  319. geometry = new THREE.Geometry();
  320. geometry.vertices.push(new THREE.Vector3(entity.position.x, entity.position.y, entity.position.z));
  321. // TODO: could be more efficient. PointCloud per layer?
  322. var numPoints = 1;
  323. var color = getColor(entity, data);
  324. var colors = new Float32Array( numPoints*3 );
  325. colors[0] = color.r;
  326. colors[1] = color.g;
  327. colors[2] = color.b;
  328. geometry.colors = colors;
  329. geometry.computeBoundingBox();
  330. material = new THREE.PointsMaterial( { size: 0.05, vertexColors: THREE.VertexColors } );
  331. point = new THREE.Points(geometry, material);
  332. //scene.add(point);
  333. return point
  334. }
  335. function drawDimension(entity, data, unsupportTypes, parentGroup) {
  336. return drawBlock(entity, data, unsupportTypes, parentGroup, true)
  337. //改
  338. /* var block = data.blocks[entity.block];
  339. if (!block || !block.entities) return null;
  340. var group = new THREE.Object3D();
  341. // if(entity.anchorPoint) {
  342. // group.position.x = entity.anchorPoint.x;
  343. // group.position.y = entity.anchorPoint.y;
  344. // group.position.z = entity.anchorPoint.z;
  345. // }
  346. for(var i = 0; i < block.entities.length; i++) {
  347. var childEntity = drawEntity(block.entities[i], data, group);
  348. if(childEntity && !group.children.includes(childEntity)) group.add(childEntity);
  349. }
  350. return group; */
  351. }
  352. function drawBlock(entity, data, unsupportTypes, parentGroup, isDimension) {
  353. var block = data.blocks[entity.name];
  354. if (!block.entities) return null;
  355. var group = new THREE.Object3D()
  356. parentGroup && parentGroup.add(group)//提前添加 为了获得matrixWorld
  357. if(!isDimension){
  358. if(entity.position) {
  359. group.position.x = entity.position.x;
  360. group.position.y = entity.position.y;
  361. group.position.z = entity.position.z;
  362. }
  363. if(entity.xScale){
  364. group.scale.x = entity.xScale;
  365. }
  366. if(entity.yScale){
  367. group.scale.y = entity.yScale;
  368. }
  369. if(entity.rotation) {
  370. group.rotation.z = entity.rotation * Math.PI / 180;
  371. }
  372. //add: 经常看到有的会脱离主区域很远 add zScale
  373. var flag;
  374. if(entity.zScale != void 0 && entity.extrusionDirection){//see (005.dxf、 others/01.dxf) extrusionDirection ={x:0,y:0,z:-1}
  375. group.position.x *= Math.sign(entity.zScale)
  376. group.scale.x *= Math.sign(entity.zScale);
  377. group.rotation.z *= Math.sign(entity.zScale)
  378. flag = 1;
  379. //检查下其他的extrusionDirection
  380. }
  381. }else{
  382. /* if(entity.anchorPoint) {
  383. group.position.x = entity.anchorPoint.x;
  384. group.position.y = entity.anchorPoint.y;
  385. group.position.z = entity.anchorPoint.z;
  386. } */
  387. }
  388. //add:
  389. group.updateMatrixWorld()
  390. var visiEntities = block.entities.filter(e=>e.visible !== false)
  391. //console.log("block的可见children个数"+block.entities.length)
  392. for(var i = 0; i < visiEntities.length; i++) {
  393. var childEntity = drawEntity(visiEntities[i], data, unsupportTypes, group );
  394. if(childEntity && !group.children.includes(childEntity)) group.add(childEntity);
  395. }
  396. return group;
  397. }
  398. function getColor(entity, data) {
  399. var color = 0x000000; //default
  400. if(entity.color) color = entity.color;
  401. else if(data.tables && data.tables.layer && data.tables.layer.layers[entity.layer])
  402. color = data.tables.layer.layers[entity.layer].color;
  403. if(color == null || color === 0xffffff) {
  404. color = 0x000000;
  405. }
  406. return color;
  407. }
  408. function createLineTypeShaders(data) {
  409. var ltype, type;
  410. if(!data.tables || !data.tables.lineType) return;
  411. var ltypes = data.tables.lineType.lineTypes;
  412. for(type in ltypes) {
  413. ltype = ltypes[type];
  414. if(!ltype.pattern) continue;
  415. ltype.material = createDashedLineShader(ltype.pattern);
  416. }
  417. }
  418. function createDashedLineShader(pattern) {
  419. var i,
  420. dashedLineShader = {},
  421. totalLength = 0.0;
  422. for(i = 0; i < pattern.length; i++) {
  423. totalLength += Math.abs(pattern[i]);
  424. }
  425. dashedLineShader.uniforms = THREE.UniformsUtils.merge([
  426. THREE.UniformsLib[ 'common' ],
  427. THREE.UniformsLib[ 'fog' ],
  428. {
  429. 'pattern': { type: 'fv1', value: pattern },
  430. 'patternLength': { type: 'f', value: totalLength }
  431. }
  432. ]);
  433. dashedLineShader.vertexShader = [
  434. 'attribute float lineDistance;',
  435. 'varying float vLineDistance;',
  436. THREE.ShaderChunk[ 'color_pars_vertex' ],
  437. 'void main() {',
  438. THREE.ShaderChunk[ 'color_vertex' ],
  439. 'vLineDistance = lineDistance;',
  440. 'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
  441. '}'
  442. ].join('\n');
  443. dashedLineShader.fragmentShader = [
  444. 'uniform vec3 diffuse;',
  445. 'uniform float opacity;',
  446. 'uniform float pattern[' + pattern.length + '];',
  447. 'uniform float patternLength;',
  448. 'varying float vLineDistance;',
  449. THREE.ShaderChunk[ 'color_pars_fragment' ],
  450. THREE.ShaderChunk[ 'fog_pars_fragment' ],
  451. 'void main() {',
  452. 'float pos = mod(vLineDistance, patternLength);',
  453. 'for ( int i = 0; i < ' + pattern.length + '; i++ ) {',
  454. 'pos = pos - abs(pattern[i]);',
  455. 'if( pos < 0.0 ) {',
  456. 'if( pattern[i] > 0.0 ) {',
  457. 'gl_FragColor = vec4(1.0, 0.0, 0.0, opacity );',
  458. 'break;',
  459. '}',
  460. 'discard;',
  461. '}',
  462. '}',
  463. THREE.ShaderChunk[ 'color_fragment' ],
  464. THREE.ShaderChunk[ 'fog_fragment' ],
  465. '}'
  466. ].join('\n');
  467. return dashedLineShader;
  468. }
  469. function findExtents(scene) {
  470. for(var child of scene.children) {
  471. var minX, maxX, minY, maxY;
  472. if(child.position) {
  473. minX = Math.min(child.position.x, minX);
  474. minY = Math.min(child.position.y, minY);
  475. maxX = Math.max(child.position.x, maxX);
  476. maxY = Math.max(child.position.y, maxY);
  477. }
  478. }
  479. return { min: { x: minX, y: minY }, max: { x: maxX, y: maxY }};
  480. }
  481. // Three.js extension functions. Webpack doesn't seem to like it if we modify the THREE object directly.
  482. var THREEx = { Math: {} };
  483. /**
  484. * Returns the angle in radians of the vector (p1,p2). In other words, imagine
  485. * putting the base of the vector at coordinates (0,0) and finding the angle
  486. * from vector (1,0) to (p1,p2).
  487. * @param {Object} p1 start point of the vector
  488. * @param {Object} p2 end point of the vector
  489. * @return {Number} the angle
  490. */
  491. THREEx.Math.angle2 = function(p1, p2) {
  492. var v1 = new THREE.Vector2(p1.x, p1.y);
  493. var v2 = new THREE.Vector2(p2.x, p2.y);
  494. v2.sub(v1); // sets v2 to be our chord
  495. v2.normalize();
  496. if(v2.y < 0) return -Math.acos(v2.x);
  497. return Math.acos(v2.x);
  498. };
  499. THREEx.Math.polar = function(point, distance, angle) {
  500. var result = {};
  501. result.x = point.x + distance * Math.cos(angle);
  502. result.y = point.y + distance * Math.sin(angle);
  503. return result;
  504. };
  505. /**
  506. * Calculates points for a curve between two points
  507. * @param startPoint - the starting point of the curve
  508. * @param endPoint - the ending point of the curve
  509. * @param bulge - a value indicating how much to curve
  510. * @param segments - number of segments between the two given points
  511. */
  512. THREEx.BulgeGeometry = function ( startPoint, endPoint, bulge, segments ) {
  513. var vertex, i,
  514. center, p0, p1, angle,
  515. radius, startAngle,
  516. thetaAngle;
  517. THREE.Geometry.call( this );
  518. this.startPoint = p0 = startPoint ? new THREE.Vector2(startPoint.x, startPoint.y) : new THREE.Vector2(0,0);
  519. this.endPoint = p1 = endPoint ? new THREE.Vector2(endPoint.x, endPoint.y) : new THREE.Vector2(1,0);
  520. this.bulge = bulge = bulge || 1;
  521. angle = 4 * Math.atan(bulge);
  522. radius = p0.distanceTo(p1) / 2 / Math.sin(angle/2);
  523. center = THREEx.Math.polar(startPoint, radius, THREEx.Math.angle2(p0,p1) + (Math.PI / 2 - angle/2));
  524. this.segments = segments = segments || Math.max( Math.abs(Math.ceil(angle/(Math.PI/18))), 6); // By default want a segment roughly every 10 degrees
  525. startAngle = THREEx.Math.angle2(center, p0);
  526. thetaAngle = angle / segments;
  527. this.vertices.push(new THREE.Vector3(p0.x, p0.y, 0));
  528. for(i = 1; i <= segments - 1; i++) {
  529. vertex = THREEx.Math.polar(center, Math.abs(radius), startAngle + thetaAngle * i);
  530. this.vertices.push(new THREE.Vector3(vertex.x, vertex.y, 0));
  531. }
  532. };
  533. THREEx.BulgeGeometry.prototype = Object.create( THREE.Geometry.prototype );
  534. /* -----note--------------
  535. */