/fun/RUGL/src/com/rugl/geom/ShapeUtil.java

http://rugl.googlecode.com/ · Java · 938 lines · 539 code · 138 blank · 261 comment · 34 complexity · d19eea57e905a7e746e85f8907b9c71e MD5 · raw file

  1. package com.rugl.geom;
  2. import org.lwjgl.util.vector.Vector2f;
  3. import org.lwjgl.util.vector.Vector3f;
  4. import com.rugl.renderer.RenderUtils;
  5. import com.ryanm.util.geom.LineUtils;
  6. import com.ryanm.util.geom.VectorUtils;
  7. import com.ryanm.util.math.Trig;
  8. /**
  9. * Utility for constructing various {@link Shape}s
  10. *
  11. * @author ryanm
  12. */
  13. public class ShapeUtil
  14. {
  15. /**
  16. * Vertices will be in nbr, ntr, nbl, ntl, fbr, ftr, fbl, ftl
  17. * order, where near is -z, far is +z, bottom is -y, top is +y,
  18. * left is -x, right is +x
  19. *
  20. * @param minx
  21. * @param miny
  22. * @param minz
  23. * @param maxx
  24. * @param maxy
  25. * @param maxz
  26. * @return An axis-aligned cuboid
  27. */
  28. public static Shape cuboid( float minx, float miny, float minz, float maxx,
  29. float maxy, float maxz )
  30. {
  31. float[] verts = new float[ 8 * 3 ];
  32. int[] tris = new int[ 6 * 2 * 3 ];
  33. int vi = 0;
  34. verts[ vi++ ] = minx;
  35. verts[ vi++ ] = miny;
  36. verts[ vi++ ] = minz;
  37. verts[ vi++ ] = minx;
  38. verts[ vi++ ] = maxy;
  39. verts[ vi++ ] = minz;
  40. verts[ vi++ ] = maxx;
  41. verts[ vi++ ] = miny;
  42. verts[ vi++ ] = minz;
  43. verts[ vi++ ] = maxx;
  44. verts[ vi++ ] = maxy;
  45. verts[ vi++ ] = minz;
  46. verts[ vi++ ] = minx;
  47. verts[ vi++ ] = miny;
  48. verts[ vi++ ] = maxz;
  49. verts[ vi++ ] = minx;
  50. verts[ vi++ ] = maxy;
  51. verts[ vi++ ] = maxz;
  52. verts[ vi++ ] = maxx;
  53. verts[ vi++ ] = miny;
  54. verts[ vi++ ] = maxz;
  55. verts[ vi++ ] = maxx;
  56. verts[ vi++ ] = maxy;
  57. verts[ vi++ ] = maxz;
  58. int ti = 0;
  59. // near
  60. tris[ ti++ ] = 0;
  61. tris[ ti++ ] = 2;
  62. tris[ ti++ ] = 1;
  63. tris[ ti++ ] = 2;
  64. tris[ ti++ ] = 3;
  65. tris[ ti++ ] = 1;
  66. // bottom
  67. tris[ ti++ ] = 0;
  68. tris[ ti++ ] = 4;
  69. tris[ ti++ ] = 2;
  70. tris[ ti++ ] = 4;
  71. tris[ ti++ ] = 6;
  72. tris[ ti++ ] = 2;
  73. // right
  74. tris[ ti++ ] = 2;
  75. tris[ ti++ ] = 6;
  76. tris[ ti++ ] = 3;
  77. tris[ ti++ ] = 7;
  78. tris[ ti++ ] = 3;
  79. tris[ ti++ ] = 6;
  80. // top
  81. tris[ ti++ ] = 1;
  82. tris[ ti++ ] = 3;
  83. tris[ ti++ ] = 5;
  84. tris[ ti++ ] = 3;
  85. tris[ ti++ ] = 7;
  86. tris[ ti++ ] = 5;
  87. // left
  88. tris[ ti++ ] = 4;
  89. tris[ ti++ ] = 0;
  90. tris[ ti++ ] = 5;
  91. tris[ ti++ ] = 0;
  92. tris[ ti++ ] = 1;
  93. tris[ ti++ ] = 5;
  94. // far
  95. tris[ ti++ ] = 6;
  96. tris[ ti++ ] = 4;
  97. tris[ ti++ ] = 7;
  98. tris[ ti++ ] = 4;
  99. tris[ ti++ ] = 5;
  100. tris[ ti++ ] = 7;
  101. return new Shape( verts, tris );
  102. }
  103. /**
  104. * @param angle
  105. * the angle range through which to spiral
  106. * @param angleInc
  107. * granularity of spiral
  108. * @param width
  109. * The width of the line
  110. * @return A golden spiral at the origin
  111. */
  112. public static Shape goldenSpiral( float angle, float angleInc, float width )
  113. {
  114. return logSpiral( 1, 0.306349f, angle, angleInc, width );
  115. }
  116. /**
  117. * @param a
  118. * @param b
  119. * @param tRange
  120. * @param tStep
  121. * @param width
  122. * @return A logarithmic spiral
  123. */
  124. public static Shape logSpiral( float a, float b, float tRange, float tStep, float width )
  125. {
  126. assert tRange != 0;
  127. int points = ( int ) ( tRange / tStep ) + 2;
  128. tStep = tRange / points;
  129. float[] verts = new float[ points * 2 ];
  130. int vi = 0;
  131. for( int i = 0; i < points; i++ )
  132. {
  133. float t = i * tStep;
  134. float r = ( float ) ( a * Math.pow( Math.E, b * t ) );
  135. verts[ vi++ ] = r * Trig.cos( t );
  136. verts[ vi++ ] = r * Trig.sin( t );
  137. }
  138. return line( width, verts );
  139. }
  140. /**
  141. * @param legs
  142. * number of legs on each side
  143. * @param legLength
  144. * length of legs
  145. * @param legwidth
  146. * width of legs
  147. * @param sep
  148. * distance between core and legs
  149. * @return A chip icon shape
  150. */
  151. public static Shape chipIcon( int legs, float legLength, float legwidth, float sep )
  152. {
  153. float core = 1 - 2 * sep - 2 * legLength;
  154. Shape coreShape = filledQuad( 0, 0, core, core, 0 );
  155. coreShape.translate( legLength + sep, legLength + sep, 0 );
  156. Shape hLeg = filledQuad( 0, -legwidth / 2, legLength, legwidth / 2, 0 );
  157. Shape vLeg = filledQuad( -legwidth / 2, 0, legwidth / 2, legLength, 0 );
  158. ShapeBuilder b = new ShapeBuilder();
  159. b.addShape( coreShape );
  160. float legSep = core / legs;
  161. float p = legLength + sep + legSep / 2;
  162. for( int i = 0; i < legs; i++ )
  163. {
  164. b.addShape( hLeg.clone().translate( 0, p, 0 ) );
  165. b.addShape( hLeg.clone().translate( core + 2 * sep + legLength, p, 0 ) );
  166. b.addShape( vLeg.clone().translate( p, 0, 0 ) );
  167. b.addShape( vLeg.clone().translate( p, core + 2 * sep + legLength, 0 ) );
  168. p += legSep;
  169. }
  170. return b.compile();
  171. }
  172. /**
  173. * Constructs an arrow shape, such as you might find on a return
  174. * key
  175. *
  176. * @param posx
  177. * The lower-left corner of the bounding box
  178. * @param posy
  179. * @param width
  180. * The dimensions
  181. * @param height
  182. * @param arrowlength
  183. * The length of the arrowhead
  184. * @param thickness
  185. * The width of the shaft
  186. * @return An arrow shape
  187. */
  188. public static Shape retArrow( float posx, float posy, float width, float height,
  189. float arrowlength, float thickness )
  190. {
  191. float pointy = height / 2;
  192. float low = pointy - thickness / 2;
  193. float high = low + thickness;
  194. float[] verts =
  195. new float[] { 0, pointy, arrowlength, height, arrowlength, high,
  196. width - thickness, high, width - thickness, height, width, height,
  197. width, low, arrowlength, low, arrowlength, 0 };
  198. for( int i = 0; i < verts.length; i += 2 )
  199. {
  200. verts[ i ] += posx;
  201. verts[ i + 1 ] += posy;
  202. }
  203. int[] tris =
  204. new int[] { 0, 2, 1, 0, 7, 2, 0, 8, 7, 7, 6, 2, 2, 6, 3, 6, 4, 3, 6, 5, 4 };
  205. return new Shape( to3D( verts, 0 ), tris );
  206. }
  207. /**
  208. * Constructs a filled circle
  209. *
  210. * @param cx
  211. * The x coordinate of the center point
  212. * @param cy
  213. * The y coordinate of the center point
  214. * @param radius
  215. * The radius of the circle
  216. * @param maxSegment
  217. * The maximum length of the line segments making up the
  218. * circumference
  219. * @param z
  220. * The z coordinate of the circle
  221. * @return A filled circle {@link Shape}
  222. */
  223. public static Shape filledCircle( float cx, float cy, float radius, float maxSegment,
  224. float z )
  225. {
  226. assert radius >= 0;
  227. float c = ( float ) ( 2 * radius * Math.PI );
  228. int segs = ( int ) Math.ceil( c / maxSegment );
  229. segs = Math.max( segs, 3 );
  230. float angleIncrement = ( float ) ( 2 * Math.PI / segs );
  231. Vector3f[] verts = new Vector3f[ segs ];
  232. int[] tris = new int[ 3 * ( segs - 2 ) ];
  233. for( int i = 0; i < verts.length; i++ )
  234. {
  235. float a = i * angleIncrement;
  236. verts[ i ] =
  237. new Vector3f( ( float ) ( radius * Math.cos( a ) ),
  238. ( float ) ( radius * Math.sin( a ) ), z );
  239. verts[ i ].x += cx;
  240. verts[ i ].y += cy;
  241. }
  242. int ti = 0;
  243. for( int i = 0; i < segs - 2; i++ )
  244. {
  245. tris[ ti++ ] = 0;
  246. tris[ ti++ ] = i + 1;
  247. tris[ ti++ ] = i + 2;
  248. }
  249. return new Shape( Shape.extract( verts ), tris );
  250. }
  251. /**
  252. * Constructs a filled circle that has a central vertex
  253. *
  254. * @param cx
  255. * The x coordinate of the center point
  256. * @param cy
  257. * The y coordinate of the center point
  258. * @param radius
  259. * The radius of the circle
  260. * @param maxSegment
  261. * The maximum length of the line segments making up the
  262. * circumference
  263. * @param z
  264. * The z coordinate of the circle
  265. * @return A filled circle {@link Shape}. The center vertex is at
  266. * index 0
  267. */
  268. public static Shape filledCenteredCircle( float cx, float cy, float radius,
  269. float maxSegment, float z )
  270. {
  271. assert radius >= 0;
  272. float c = ( float ) ( 2 * radius * Math.PI );
  273. int segs = ( int ) Math.ceil( c / maxSegment );
  274. segs = Math.max( segs, 3 );
  275. float angleIncrement = ( float ) ( 2 * Math.PI / segs );
  276. Vector3f[] verts = new Vector3f[ segs + 1 ];
  277. int[] tris = new int[ 3 * segs ];
  278. verts[ 0 ] = new Vector3f( cx, cy, z );
  279. for( int i = 1; i < verts.length; i++ )
  280. {
  281. float a = i * angleIncrement;
  282. verts[ i ] =
  283. new Vector3f( ( float ) ( radius * Math.cos( a ) ),
  284. ( float ) ( radius * Math.sin( a ) ), z );
  285. verts[ i ].x += cx;
  286. verts[ i ].y += cy;
  287. }
  288. int ti = 0;
  289. int vi = 1;
  290. for( int i = 0; i < segs; i++ )
  291. {
  292. tris[ ti++ ] = 0;
  293. tris[ ti++ ] = vi;
  294. vi++;
  295. tris[ ti++ ] = vi;
  296. }
  297. tris[ tris.length - 1 ] = 1;
  298. return new Shape( Shape.extract( verts ), tris );
  299. }
  300. /**
  301. * Build a circle outline that entirely encompasses the specified
  302. * radius
  303. *
  304. * @param cx
  305. * The x coordinate of the center point
  306. * @param cy
  307. * The y coordinate of the center point
  308. * @param radius
  309. * The inner radius of the circle outline
  310. * @param width
  311. * The width of the line used to draw the circle
  312. * @param maxSegment
  313. * The maximum length of line segment used to draw the
  314. * circle
  315. * @param z
  316. * The z coordinate for the circle
  317. * @return A {@link Shape} that contains the circle's geometry
  318. */
  319. public static Shape outerCircle( float cx, float cy, float radius, float width,
  320. float maxSegment, float z )
  321. {
  322. return ShapeUtil.innerCircle( cx, cy, radius + width, width, maxSegment, z );
  323. }
  324. /**
  325. * Builds a circle outline that lies completely within the
  326. * specified radius
  327. *
  328. * @param cx
  329. * The x coordinate of the center point
  330. * @param cy
  331. * The y coordinate of the center point
  332. * @param radius
  333. * The outer radius of the circle outline
  334. * @param width
  335. * The width of the line used to draw the circle
  336. * @param maxSegment
  337. * The maximum length of line segment used to draw the
  338. * circle
  339. * @param z
  340. * The z coordinate for the circle
  341. * @return A {@link Shape} that contains the circle's geometry
  342. */
  343. public static Shape innerCircle( float cx, float cy, float radius, float width,
  344. float maxSegment, float z )
  345. {
  346. if( width > radius )
  347. {
  348. width = radius;
  349. }
  350. float inner = radius - width;
  351. float outer = radius;
  352. if( inner == 0 )
  353. {
  354. return filledCircle( cx, cy, radius, maxSegment, z );
  355. }
  356. int segments = ( int ) Math.ceil( Math.PI * 2 * outer / maxSegment );
  357. segments = Math.max( 3, segments );
  358. float angleIncrement = ( float ) ( Math.PI * 2 / segments );
  359. Vector3f[] verts = new Vector3f[ segments * 2 ];
  360. int[] indices = new int[ segments * 6 ];
  361. for( int i = 0; i < segments; i++ )
  362. {
  363. float cos = ( float ) Math.cos( angleIncrement * i );
  364. float sin = ( float ) Math.sin( angleIncrement * i );
  365. verts[ 2 * i ] = new Vector3f( cx + inner * cos, cy + inner * sin, z );
  366. verts[ 2 * i + 1 ] = new Vector3f( cx + outer * cos, cy + outer * sin, z );
  367. int ci = 2 * i;
  368. int co = 2 * i + 1;
  369. int ni = ( ci + 2 ) % verts.length;
  370. int no = ( co + 2 ) % verts.length;
  371. indices[ 6 * i ] = ci;
  372. indices[ 6 * i + 1 ] = co;
  373. indices[ 6 * i + 2 ] = ni;
  374. indices[ 6 * i + 3 ] = co;
  375. indices[ 6 * i + 4 ] = no;
  376. indices[ 6 * i + 5 ] = ni;
  377. }
  378. return new Shape( Shape.extract( verts ), indices );
  379. }
  380. /**
  381. * Creates a cross occupying the unit square
  382. *
  383. * @param width
  384. * The width of the arms
  385. * @return A cross shape
  386. */
  387. public static Shape cross( float width )
  388. {
  389. float n = 0.5f - width / 2;
  390. float f = n + width;
  391. float[] verts =
  392. new float[] { n, 0, n, n, 0, n, 0, f, n, f, n, 1, f, 1, f, f, 1, f, 1, n, f,
  393. n, f, 0 };
  394. int[] tris =
  395. new int[] { 1, 3, 2, 1, 4, 3, 4, 6, 5, 4, 7, 6, 7, 9, 8, 7, 10, 9, 10, 0, 11,
  396. 10, 1, 0, 1, 7, 4, 1, 10, 7 };
  397. return new Shape( to3D( verts, 0 ), tris );
  398. }
  399. /**
  400. * Takes a 2D vertex array and adds the z-coordinates
  401. *
  402. * @param verts
  403. * @param z
  404. * @return A 3D vertex array
  405. */
  406. public static float[] to3D( float[] verts, float z )
  407. {
  408. float[] tdv = new float[ verts.length / 2 * 3 ];
  409. int vi = 0;
  410. for( int i = 0; i < verts.length; i += 2 )
  411. {
  412. tdv[ vi++ ] = verts[ i ];
  413. tdv[ vi++ ] = verts[ i + 1 ];
  414. tdv[ vi++ ] = z;
  415. }
  416. return tdv;
  417. }
  418. /**
  419. * @param verts
  420. * @return the 2D projection of the 3D coordinates
  421. */
  422. public static float[] to2D( float[] verts )
  423. {
  424. float[] tdv = new float[ verts.length / 3 * 2 ];
  425. int vi = 0;
  426. for( int i = 0; i < verts.length; i += 3 )
  427. {
  428. tdv[ vi++ ] = verts[ i ];
  429. tdv[ vi++ ] = verts[ i + 1 ];
  430. }
  431. return tdv;
  432. }
  433. /**
  434. * Creates an upward-pointing arrow shape
  435. *
  436. * @param bx
  437. * The bottom corner
  438. * @param by
  439. * @param tx
  440. * The top corner
  441. * @param ty
  442. * @param cx
  443. * The inner corner nearest the bottom corner, in terms
  444. * of the bounding box
  445. * @param cy
  446. * @return An arrow shape
  447. */
  448. public static Shape arrow( float bx, float by, float tx, float ty, float cx, float cy )
  449. {
  450. float dx = tx - bx;
  451. float dy = ty - by;
  452. float ix = dx * cx;
  453. float iy = dy * cy;
  454. float near = bx + ix;
  455. float far = tx - ix;
  456. float peakx = ( bx + tx ) / 2;
  457. float[] verts =
  458. new float[] { near, by, near, iy, bx, iy, peakx, ty, tx, iy, far, iy, far, bx };
  459. int[] tris = new int[] { 1, 3, 2, 1, 5, 3, 5, 4, 3, 0, 5, 1, 0, 6, 5 };
  460. return new Shape( to3D( verts, 0 ), tris );
  461. }
  462. /**
  463. * Builds a simple filled quad. The vertices are in bl tl br tr
  464. * order
  465. *
  466. * @param px
  467. * corner x
  468. * @param py
  469. * corner y
  470. * @param qx
  471. * opposite corner x
  472. * @param qy
  473. * opposite corner y
  474. * @param z
  475. * The z coordinate of the quad
  476. * @return A filled quad
  477. */
  478. public static Shape filledQuad( float px, float py, float qx, float qy, float z )
  479. {
  480. if( px > qx )
  481. {
  482. float t = qx;
  483. qx = px;
  484. px = t;
  485. }
  486. if( py > qy )
  487. {
  488. float t = qy;
  489. qy = py;
  490. py = t;
  491. }
  492. Vector3f[] verts = new Vector3f[ 4 ];
  493. verts[ 0 ] = new Vector3f( px, py, z );
  494. verts[ 1 ] = new Vector3f( px, qy, z );
  495. verts[ 2 ] = new Vector3f( qx, py, z );
  496. verts[ 3 ] = new Vector3f( qx, qy, z );
  497. return new Shape( Shape.extract( verts ), RenderUtils.makeQuads( 4, 0, null, 0 ) );
  498. }
  499. /**
  500. * Builds a quad outline that lies completely within the specified
  501. * bounds
  502. *
  503. * @param px
  504. * corner x
  505. * @param py
  506. * corner y
  507. * @param qx
  508. * opposite corner x
  509. * @param qy
  510. * opposite corner y
  511. * @param width
  512. * the width of the outline
  513. * @param z
  514. * The z coordinate of the quad outline
  515. * @return A quad outline
  516. */
  517. public static Shape innerQuad( float px, float py, float qx, float qy, float width,
  518. float z )
  519. {
  520. if( px > qx )
  521. {
  522. float t = qx;
  523. qx = px;
  524. px = t;
  525. }
  526. if( py > qy )
  527. {
  528. float t = qy;
  529. qy = py;
  530. py = t;
  531. }
  532. Vector3f[] verts = new Vector3f[ 8 ];
  533. int[] tris = new int[ 8 * 3 ];
  534. float xwidth = width;
  535. float ywidth = width;
  536. if( Math.abs( qx - px ) < 2 * width )
  537. {
  538. xwidth = Math.abs( qx - px ) / 2.0f;
  539. }
  540. if( Math.abs( qy - py ) < 2 * width )
  541. {
  542. ywidth = Math.abs( qy - py ) / 2.0f;
  543. }
  544. int index = 0;
  545. verts[ index++ ] = new Vector3f( px, py, z );
  546. verts[ index++ ] = new Vector3f( px + xwidth, py + ywidth, z );
  547. verts[ index++ ] = new Vector3f( px, qy, z );
  548. verts[ index++ ] = new Vector3f( px + xwidth, qy - ywidth, z );
  549. verts[ index++ ] = new Vector3f( qx, qy, z );
  550. verts[ index++ ] = new Vector3f( qx - xwidth, qy - ywidth, z );
  551. verts[ index++ ] = new Vector3f( qx, py, z );
  552. verts[ index++ ] = new Vector3f( qx - xwidth, py + ywidth, z );
  553. index = 0;
  554. for( int i = 0; i < 4; i++ )
  555. {
  556. int a = i * 2;
  557. int b = a + 1;
  558. int c = ( b + 1 ) % verts.length;
  559. int d = ( c + 1 ) % verts.length;
  560. addTriangle( a, b, c, tris, index );
  561. index += 3;
  562. addTriangle( b, d, c, tris, index );
  563. index += 3;
  564. }
  565. return new Shape( Shape.extract( verts ), tris );
  566. }
  567. /**
  568. * Builds a quad outline that encompasses the supplied bounds
  569. *
  570. * @param px
  571. * corner x
  572. * @param py
  573. * corner y
  574. * @param qx
  575. * opposite corner x
  576. * @param qy
  577. * opposite corner y
  578. * @param width
  579. * The outline width
  580. * @param z
  581. * The outline's z-component
  582. * @return A quad outline
  583. */
  584. public static Shape outerQuad( float px, float py, float qx, float qy, float width,
  585. float z )
  586. {
  587. float minx, miny, maxx, maxy;
  588. if( px < qx )
  589. {
  590. minx = px;
  591. maxx = qx;
  592. }
  593. else
  594. {
  595. minx = qx;
  596. maxx = px;
  597. }
  598. if( py < qy )
  599. {
  600. miny = py;
  601. maxy = qy;
  602. }
  603. else
  604. {
  605. miny = qy;
  606. maxy = py;
  607. }
  608. return innerQuad( minx - width, miny - width, maxx + width, maxy + width, width, z );
  609. }
  610. static void addTriangle( int a, int b, int c, int[] array, int index )
  611. {
  612. array[ index ] = a;
  613. array[ index + 1 ] = b;
  614. array[ index + 2 ] = c;
  615. }
  616. /**
  617. * Creates a 2D triangle shape
  618. *
  619. * @param ax
  620. * @param ay
  621. * @param bx
  622. * @param by
  623. * @param cx
  624. * @param cy
  625. * @return The triangle abc
  626. */
  627. public static Shape triangle( int ax, int ay, int bx, int by, int cx, int cy )
  628. {
  629. float[] v = new float[] { ax, ay, 0, bx, by, 0, cx, cy, 0 };
  630. int[] t = new int[] { 0, 1, 2 };
  631. return new Shape( v, t );
  632. }
  633. /**
  634. * Returns the outline around some polygon
  635. *
  636. * @param width
  637. * The width of the outline
  638. * @param pos
  639. * The position of the input polygon faces relative to
  640. * the output outline edges 0 = on the right-hand edge,
  641. * 0.5 in the middle, 1 = on the left-hand edge
  642. * @param vin
  643. * The input polygon vertices, in x,y,x,y,x,y format.
  644. * Also assumed to be specified in clockwise order
  645. * @return The outline shape
  646. */
  647. public static Shape outline( float width, float pos, float... vin )
  648. {
  649. assert vin.length >= 6;
  650. Vector2f prevDir = new Vector2f(), nextDir = new Vector2f(), point = new Vector2f();
  651. Vector2f ta = new Vector2f(), tb = new Vector2f(), tc = new Vector2f(), td =
  652. new Vector2f();
  653. float[] vout = new float[ vin.length * 2 ];
  654. int vi = 0;
  655. for( int i = 1; i <= vin.length / 2; i++ )
  656. {
  657. int c = 2 * i % vin.length;
  658. int p = 2 * ( i - 1 ) % vin.length;
  659. int n = 2 * ( i + 1 ) % vin.length;
  660. prevDir.set( vin[ c ] - vin[ p ], vin[ c + 1 ] - vin[ p + 1 ] );
  661. prevDir.normalise();
  662. nextDir.set( vin[ n ] - vin[ c ], vin[ n + 1 ] - vin[ c + 1 ] );
  663. nextDir.normalise();
  664. prevDir.scale( width );
  665. nextDir.scale( width );
  666. VectorUtils.rotate90( prevDir );
  667. VectorUtils.rotate90( nextDir );
  668. // inner
  669. ta.set( vin[ p ] - pos * prevDir.x, vin[ p + 1 ] - pos * prevDir.y );
  670. tb.set( vin[ c ] - pos * prevDir.x, vin[ c + 1 ] - pos * prevDir.y );
  671. tc.set( vin[ c ] - pos * nextDir.x, vin[ c + 1 ] - pos * nextDir.y );
  672. td.set( vin[ n ] - pos * nextDir.x, vin[ n + 1 ] - pos * nextDir.y );
  673. LineUtils.lineIntersection( ta, tb, tc, td, point );
  674. vout[ vi++ ] = point.x;
  675. vout[ vi++ ] = point.y;
  676. // outer
  677. ta.set( vin[ p ] + ( 1 - pos ) * prevDir.x, vin[ p + 1 ] + ( 1 - pos )
  678. * prevDir.y );
  679. tb.set( vin[ c ] + ( 1 - pos ) * prevDir.x, vin[ c + 1 ] + ( 1 - pos )
  680. * prevDir.y );
  681. tc.set( vin[ c ] + ( 1 - pos ) * nextDir.x, vin[ c + 1 ] + ( 1 - pos )
  682. * nextDir.y );
  683. td.set( vin[ n ] + ( 1 - pos ) * nextDir.x, vin[ n + 1 ] + ( 1 - pos )
  684. * nextDir.y );
  685. LineUtils.lineIntersection( ta, tb, tc, td, point );
  686. vout[ vi++ ] = point.x;
  687. vout[ vi++ ] = point.y;
  688. }
  689. assert vi == vout.length : vi + " " + vout.length;
  690. int[] tris = new int[ 3 * vin.length ];
  691. int ti = 0;
  692. for( int i = 0; i < vin.length / 2; i++ )
  693. {
  694. tris[ ti++ ] = 2 * i;
  695. tris[ ti++ ] = 2 * i + 2;
  696. tris[ ti++ ] = 2 * i + 1;
  697. tris[ ti++ ] = 2 * i + 2;
  698. tris[ ti++ ] = 2 * i + 3;
  699. tris[ ti++ ] = 2 * i + 1;
  700. }
  701. for( int i = 0; i < tris.length; i++ )
  702. {
  703. tris[ i ] %= vout.length / 2;
  704. }
  705. return new Shape( to3D( vout, 0 ), tris );
  706. }
  707. /**
  708. * A simple line segment path
  709. *
  710. * @param width
  711. * @param vin
  712. * @return A line path of the specified width
  713. */
  714. public static Shape line( float width, float... vin )
  715. {
  716. assert vin.length >= 4;
  717. Vector2f prevDir = new Vector2f(), nextDir = new Vector2f(), point = new Vector2f();
  718. Vector2f ta = new Vector2f(), tb = new Vector2f(), tc = new Vector2f(), td =
  719. new Vector2f();
  720. float pos = 0.5f;
  721. float[] vout = new float[ vin.length * 2 ];
  722. int vi = 0;
  723. prevDir.set( vin[ 2 ] - vin[ 0 ], vin[ 3 ] - vin[ 1 ] );
  724. prevDir.normalise();
  725. prevDir.scale( width );
  726. VectorUtils.rotate90( prevDir );
  727. vout[ vi++ ] = vin[ 0 ] - pos * prevDir.x;
  728. vout[ vi++ ] = vin[ 1 ] - pos * prevDir.y;
  729. vout[ vi++ ] = vin[ 0 ] + ( 1 - pos ) * prevDir.x;
  730. vout[ vi++ ] = vin[ 1 ] + ( 1 - pos ) * prevDir.y;
  731. nextDir.set( prevDir );
  732. for( int i = 1; i < vin.length / 2 - 1; i++ )
  733. {
  734. int p = 2 * ( i - 1 );
  735. int c = 2 * i;
  736. int n = 2 * ( i + 1 );
  737. prevDir.set( vin[ c ] - vin[ p ], vin[ c + 1 ] - vin[ p + 1 ] );
  738. prevDir.normalise();
  739. nextDir.set( vin[ n ] - vin[ c ], vin[ n + 1 ] - vin[ c + 1 ] );
  740. nextDir.normalise();
  741. prevDir.scale( width );
  742. nextDir.scale( width );
  743. VectorUtils.rotate90( prevDir );
  744. VectorUtils.rotate90( nextDir );
  745. // inner
  746. ta.set( vin[ p ] - pos * prevDir.x, vin[ p + 1 ] - pos * prevDir.y );
  747. tb.set( vin[ c ] - pos * prevDir.x, vin[ c + 1 ] - pos * prevDir.y );
  748. tc.set( vin[ c ] - pos * nextDir.x, vin[ c + 1 ] - pos * nextDir.y );
  749. td.set( vin[ n ] - pos * nextDir.x, vin[ n + 1 ] - pos * nextDir.y );
  750. if( LineUtils.lineIntersection( ta, tb, tc, td, point ) == null )
  751. {
  752. // parallel segments
  753. vout[ vi++ ] = tb.x;
  754. vout[ vi++ ] = tb.y;
  755. }
  756. else
  757. {
  758. vout[ vi++ ] = point.x;
  759. vout[ vi++ ] = point.y;
  760. }
  761. // outer
  762. ta.set( vin[ p ] + ( 1 - pos ) * prevDir.x, vin[ p + 1 ] + ( 1 - pos )
  763. * prevDir.y );
  764. tb.set( vin[ c ] + ( 1 - pos ) * prevDir.x, vin[ c + 1 ] + ( 1 - pos )
  765. * prevDir.y );
  766. tc.set( vin[ c ] + ( 1 - pos ) * nextDir.x, vin[ c + 1 ] + ( 1 - pos )
  767. * nextDir.y );
  768. td.set( vin[ n ] + ( 1 - pos ) * nextDir.x, vin[ n + 1 ] + ( 1 - pos )
  769. * nextDir.y );
  770. if( LineUtils.lineIntersection( ta, tb, tc, td, point ) == null )
  771. {
  772. // parallel segments
  773. vout[ vi++ ] = tb.x;
  774. vout[ vi++ ] = tb.y;
  775. }
  776. else
  777. {
  778. vout[ vi++ ] = point.x;
  779. vout[ vi++ ] = point.y;
  780. }
  781. }
  782. vout[ vi++ ] = vin[ vin.length - 2 ] - pos * nextDir.x;
  783. vout[ vi++ ] = vin[ vin.length - 1 ] - pos * nextDir.y;
  784. vout[ vi++ ] = vin[ vin.length - 2 ] + ( 1 - pos ) * nextDir.x;
  785. vout[ vi++ ] = vin[ vin.length - 1 ] + ( 1 - pos ) * nextDir.y;
  786. assert vi == vout.length : vi + " " + vout.length;
  787. int[] tris = new int[ 3 * ( vin.length - 2 ) ];
  788. int ti = 0;
  789. for( int i = 0; i < vin.length / 2 - 1; i++ )
  790. {
  791. tris[ ti++ ] = 2 * i;
  792. tris[ ti++ ] = 2 * i + 2;
  793. tris[ ti++ ] = 2 * i + 1;
  794. tris[ ti++ ] = 2 * i + 2;
  795. tris[ ti++ ] = 2 * i + 3;
  796. tris[ ti++ ] = 2 * i + 1;
  797. }
  798. return new Shape( to3D( vout, 0 ), tris );
  799. }
  800. }