babylon.lines2d.ts 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. module BABYLON {
  2. export class Lines2DRenderCache extends ModelRenderCache {
  3. fillVB: WebGLBuffer;
  4. fillIB: WebGLBuffer;
  5. fillIndicesCount: number;
  6. instancingFillAttributes: InstancingAttributeInfo[];
  7. effectFill: Effect;
  8. borderVB: WebGLBuffer;
  9. borderIB: WebGLBuffer;
  10. borderIndicesCount: number;
  11. instancingBorderAttributes: InstancingAttributeInfo[];
  12. effectBorder: Effect;
  13. constructor(engine: Engine, modelKey: string, isTransparent: boolean) {
  14. super(engine, modelKey, isTransparent);
  15. }
  16. render(instanceInfo: GroupInstanceInfo, context: Render2DContext): boolean {
  17. // Do nothing if the shader is still loading/preparing
  18. if ((this.effectFill && !this.effectFill.isReady()) || (this.effectBorder && !this.effectBorder.isReady())) {
  19. return false;
  20. }
  21. var engine = instanceInfo._owner.owner.engine;
  22. let depthFunction = 0;
  23. if (this.effectFill && this.effectBorder) {
  24. depthFunction = engine.getDepthFunction();
  25. engine.setDepthFunctionToLessOrEqual();
  26. }
  27. var cur: number;
  28. if (this.isTransparent) {
  29. cur = engine.getAlphaMode();
  30. engine.setAlphaMode(Engine.ALPHA_COMBINE);
  31. }
  32. if (this.effectFill) {
  33. let partIndex = instanceInfo._partIndexFromId.get(Shape2D.SHAPE2D_FILLPARTID.toString());
  34. engine.enableEffect(this.effectFill);
  35. engine.bindBuffers(this.fillVB, this.fillIB, [2], 2*4, this.effectFill);
  36. let count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
  37. if (instanceInfo._owner.owner.supportInstancedArray) {
  38. if (!this.instancingFillAttributes) {
  39. // Compute the offset locations of the attributes in the vertex shader that will be mapped to the instance buffer data
  40. this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, this.effectFill);
  41. }
  42. engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingFillAttributes);
  43. engine.draw(true, 0, this.fillIndicesCount, count);
  44. engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], this.instancingFillAttributes);
  45. } else {
  46. for (let i = 0; i < count; i++) {
  47. this.setupUniforms(this.effectFill, partIndex, instanceInfo._instancesPartsData[partIndex], i);
  48. engine.draw(true, 0, this.fillIndicesCount);
  49. }
  50. }
  51. }
  52. if (this.effectBorder) {
  53. let partIndex = instanceInfo._partIndexFromId.get(Shape2D.SHAPE2D_BORDERPARTID.toString());
  54. engine.enableEffect(this.effectBorder);
  55. engine.bindBuffers(this.borderVB, this.borderIB, [2], 2 * 4, this.effectBorder);
  56. let count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
  57. if (instanceInfo._owner.owner.supportInstancedArray) {
  58. if (!this.instancingBorderAttributes) {
  59. this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, this.effectBorder);
  60. }
  61. engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingBorderAttributes);
  62. engine.draw(true, 0, this.borderIndicesCount, count);
  63. engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], this.instancingBorderAttributes);
  64. } else {
  65. for (let i = 0; i < count; i++) {
  66. this.setupUniforms(this.effectBorder, partIndex, instanceInfo._instancesPartsData[partIndex], i);
  67. engine.draw(true, 0, this.borderIndicesCount);
  68. }
  69. }
  70. }
  71. if (this.isTransparent) {
  72. engine.setAlphaMode(cur);
  73. }
  74. if (this.effectFill && this.effectBorder) {
  75. engine.setDepthFunction(depthFunction);
  76. }
  77. return true;
  78. }
  79. public dispose(): boolean {
  80. if (!super.dispose()) {
  81. return false;
  82. }
  83. if (this.fillVB) {
  84. this._engine._releaseBuffer(this.fillVB);
  85. this.fillVB = null;
  86. }
  87. if (this.fillIB) {
  88. this._engine._releaseBuffer(this.fillIB);
  89. this.fillIB = null;
  90. }
  91. if (this.effectFill) {
  92. this._engine._releaseEffect(this.effectFill);
  93. this.effectFill = null;
  94. }
  95. if (this.borderVB) {
  96. this._engine._releaseBuffer(this.borderVB);
  97. this.borderVB = null;
  98. }
  99. if (this.borderIB) {
  100. this._engine._releaseBuffer(this.borderIB);
  101. this.borderIB = null;
  102. }
  103. if (this.effectBorder) {
  104. this._engine._releaseEffect(this.effectBorder);
  105. this.effectBorder = null;
  106. }
  107. return true;
  108. }
  109. }
  110. export class Lines2DInstanceData extends Shape2DInstanceData {
  111. constructor(partId: number) {
  112. super(partId, 1);
  113. }
  114. @instanceData()
  115. get boundingMin(): Vector2 {
  116. return null;
  117. }
  118. @instanceData()
  119. get boundingMax(): Vector2 {
  120. return null;
  121. }
  122. }
  123. @className("Lines2D")
  124. export class Lines2D extends Shape2D {
  125. static get NoCap () { return Lines2D._noCap; }
  126. static get RoundCap () { return Lines2D._roundCap; }
  127. static get TriangleCap () { return Lines2D._triangleCap; }
  128. static get SquareAnchorCap () { return Lines2D._squareAnchorCap; }
  129. static get RoundAnchorCap () { return Lines2D._roundAnchorCap; }
  130. static get DiamondAnchorCap () { return Lines2D._diamondAnchorCap; }
  131. static get ArrowCap () { return Lines2D._arrowCap; }
  132. public static pointsProperty: Prim2DPropInfo;
  133. public static fillThicknessProperty: Prim2DPropInfo;
  134. public static closedProperty: Prim2DPropInfo;
  135. public static startCapProperty: Prim2DPropInfo;
  136. public static endCapProperty: Prim2DPropInfo;
  137. public get actualSize(): Size {
  138. return this.size;
  139. }
  140. @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 1, pi => Lines2D.pointsProperty = pi)
  141. public get points(): Vector2[] {
  142. return this._points;
  143. }
  144. public set points(value: Vector2[]) {
  145. this._points = value;
  146. this._levelBoundingInfoDirty = true;
  147. }
  148. @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 2, pi => Lines2D.fillThicknessProperty = pi)
  149. public get fillThickness(): number {
  150. return this._fillThickness;
  151. }
  152. public set fillThickness(value: number) {
  153. this._fillThickness = value;
  154. }
  155. @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 3, pi => Lines2D.closedProperty = pi)
  156. public get closed(): boolean {
  157. return this._closed;
  158. }
  159. public set closed(value: boolean) {
  160. this._closed = value;
  161. }
  162. @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 4, pi => Lines2D.startCapProperty = pi)
  163. public get startCap(): number {
  164. return this._startCap;
  165. }
  166. public set startCap(value: number) {
  167. this._startCap = value;
  168. }
  169. @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 5, pi => Lines2D.endCapProperty = pi)
  170. public get endCap(): number {
  171. return this._endCap;
  172. }
  173. public set endCap(value: number) {
  174. this._endCap = value;
  175. }
  176. protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
  177. let pl = this.points.length;
  178. let l = this.closed ? pl + 1 : pl;
  179. let originOffset = new Vector2(-0.5, -0.5);
  180. let p = intersectInfo._localPickPosition;
  181. let prevA = this.transformPointWithOrigin(this._contour[0], originOffset);
  182. let prevB = this.transformPointWithOrigin(this._contour[1], originOffset);
  183. for (let i = 1; i < l; i++) {
  184. let curA = this.transformPointWithOrigin(this._contour[(i % pl) * 2 + 0], originOffset);
  185. let curB = this.transformPointWithOrigin(this._contour[(i % pl) * 2 + 1], originOffset);
  186. if (Vector2.PointInTriangle(p, prevA, prevB, curA)) {
  187. return true;
  188. }
  189. if (Vector2.PointInTriangle(p, curA, prevB, curB)) {
  190. return true;
  191. }
  192. prevA = curA;
  193. prevB = curB;
  194. }
  195. return false;
  196. }
  197. protected get size(): Size {
  198. return this._size;
  199. }
  200. protected get boundingMin(): Vector2 {
  201. return this._boundingMin;
  202. }
  203. protected get boundingMax(): Vector2 {
  204. return this._boundingMax;
  205. }
  206. protected getUsedShaderCategories(dataPart: InstanceDataBase): string[] {
  207. let res = super.getUsedShaderCategories(dataPart);
  208. // Remove the BORDER category, we don't use it in the VertexShader
  209. let i = res.indexOf(Shape2D.SHAPE2D_CATEGORY_BORDER);
  210. if (i !== -1) {
  211. res.splice(i, 1);
  212. }
  213. return res;
  214. }
  215. protected updateLevelBoundingInfo() {
  216. BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
  217. }
  218. protected setupLines2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, points: Vector2[], fillThickness: number, startCap: number, endCap: number, fill?: IBrush2D, border?: IBrush2D, borderThickness: number = 1) {
  219. this.setupShape2D(owner, parent, id, position, true, fill, border, borderThickness);
  220. this.fillThickness = fillThickness;
  221. this.startCap = startCap;
  222. this.endCap = endCap;
  223. this.points = points;
  224. this.closed = false;
  225. this._size = Size.Zero();
  226. this._boundingMin = Vector2.Zero();
  227. this._boundingMax = Vector2.Zero();
  228. }
  229. public static Create(parent: Prim2DBase, id: string, x: number, y: number, points: Vector2[], fillThickness: number, startCap: number = Lines2D.NoCap, endCap: number = Lines2D.NoCap, fill?: IBrush2D, border?: IBrush2D, borderThickness?: number): Lines2D {
  230. Prim2DBase.CheckParent(parent);
  231. let lines = new Lines2D();
  232. lines.setupLines2D(parent.owner, parent, id, new Vector2(x, y), points, fillThickness, startCap, endCap, fill, border, borderThickness);
  233. return lines;
  234. }
  235. protected createModelRenderCache(modelKey: string, isTransparent: boolean): ModelRenderCache {
  236. let renderCache = new Lines2DRenderCache(this.owner.engine, modelKey, isTransparent);
  237. return renderCache;
  238. }
  239. protected setupModelRenderCache(modelRenderCache: ModelRenderCache) {
  240. let renderCache = <Lines2DRenderCache>modelRenderCache;
  241. let engine = this.owner.engine;
  242. // Init min/max because their being computed here
  243. this.boundingMin = new Vector2(Number.MAX_VALUE, Number.MAX_VALUE);
  244. this.boundingMax = new Vector2(Number.MIN_VALUE, Number.MIN_VALUE);
  245. let perp = (v: Vector2, res: Vector2) => {
  246. res.x = v.y;
  247. res.y = -v.x;
  248. };
  249. let direction = (a: Vector2, b: Vector2, res: Vector2) => {
  250. a.subtractToRef(b, res);
  251. res.normalize();
  252. }
  253. let tps = Vector2.Zero();
  254. let computeMiter = (tangent: Vector2, miter: Vector2, a: Vector2, b: Vector2): number => {
  255. a.addToRef(b, tangent);
  256. tangent.normalize();
  257. miter.x = -tangent.y;
  258. miter.y = tangent.x;
  259. tps.x = -a.y;
  260. tps.y = a.x;
  261. return 1 / Vector2.Dot(miter, tps);
  262. }
  263. let intersect = (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number): boolean => {
  264. let d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
  265. if (d === 0) return false;
  266. let xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d; // Intersection point is xi/yi, just in case...
  267. //let yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d; // That's why I left it commented
  268. if (xi < Math.min(x1, x2) || xi > Math.max(x1, x2)) return false;
  269. if (xi < Math.min(x3, x4) || xi > Math.max(x3, x4)) return false;
  270. return true;
  271. }
  272. let startDir: Vector2 = Vector2.Zero();
  273. let endDir: Vector2 = Vector2.Zero();
  274. let updateMinMax = (array: Float32Array, offset: number) => {
  275. if (offset >= array.length) {
  276. return;
  277. }
  278. this._boundingMin.x = Math.min(this._boundingMin.x, array[offset]);
  279. this._boundingMax.x = Math.max(this._boundingMax.x, array[offset]);
  280. this._boundingMin.y = Math.min(this._boundingMin.y, array[offset+1]);
  281. this._boundingMax.y = Math.max(this._boundingMax.y, array[offset+1]);
  282. }
  283. let store = (array: Float32Array, contour: Vector2[], index: number, max: number, p: Vector2, n: Vector2, halfThickness: number, borderThickness: number, detectFlip?: number) => {
  284. let borderMode = borderThickness != null && !isNaN(borderThickness);
  285. let off = index * (borderMode ? 8 : 4);
  286. // Mandatory because we'll be out of bound in case of closed line, for the very last point (which is a duplicate of the first that we don't store in the vb)
  287. if (off >= array.length) {
  288. return;
  289. }
  290. // Store start/end normal, we need it for the cap construction
  291. if (index === 0) {
  292. perp(n, startDir);
  293. } else if (index === max - 1) {
  294. perp(n, endDir);
  295. endDir.x *= -1;
  296. endDir.y *= -1;
  297. }
  298. let swap = false;
  299. array[off + 0] = p.x + n.x * halfThickness;
  300. array[off + 1] = p.y + n.y * halfThickness;
  301. array[off + 2] = p.x + n.x * -halfThickness;
  302. array[off + 3] = p.y + n.y * -halfThickness;
  303. updateMinMax(array, off);
  304. updateMinMax(array, off + 2);
  305. // If an index is given we check if the two segments formed between [index+0;detectFlip+0] and [index+2;detectFlip+2] intersect themselves.
  306. // It should not be the case, they should be parallel, so if they cross, we switch the order of storage to ensure we'll have parallel lines
  307. if (detectFlip !== undefined) {
  308. // Flip if intersect
  309. let flipOff = detectFlip * (borderMode ? 8 : 4);
  310. if (intersect(array[off + 0], array[off + 1], array[flipOff + 0], array[flipOff + 1], array[off + 2], array[off + 3], array[flipOff + 2], array[flipOff + 3])) {
  311. swap = true;
  312. let tps = array[off + 0];
  313. array[off + 0] = array[off + 2];
  314. array[off + 2] = tps;
  315. tps = array[off + 1];
  316. array[off + 1] = array[off + 3];
  317. array[off + 3] = tps;
  318. }
  319. }
  320. if (borderMode) {
  321. let t = halfThickness + borderThickness;
  322. array[off + 4] = p.x + n.x * (swap ? -t : t);
  323. array[off + 5] = p.y + n.y * (swap ? -t : t);
  324. array[off + 6] = p.x + n.x * (swap ? t : -t);
  325. array[off + 7] = p.y + n.y * (swap ? t : -t);
  326. updateMinMax(array, off + 4);
  327. updateMinMax(array, off + 6);
  328. }
  329. if (contour) {
  330. off += borderMode ? 4 : 0;
  331. contour.push(new Vector2(array[off + 0], array[off + 1]));
  332. contour.push(new Vector2(array[off + 2], array[off + 3]));
  333. }
  334. }
  335. let sd = Lines2D._roundCapSubDiv;
  336. let getCapSize = (type: number, border: boolean = false): { vbsize: number; ibsize: number } => {
  337. // If no array given, we call this to get the size
  338. let vbsize: number = 0, ibsize: number = 0;
  339. switch (type) {
  340. case Lines2D.NoCap:
  341. // If the line is not close and we're computing border, we add the size to generate the edge border
  342. if (!this.closed && border) {
  343. vbsize = 4;
  344. ibsize = 6;
  345. } else {
  346. vbsize = ibsize = 0;
  347. }
  348. break;
  349. case Lines2D.RoundCap:
  350. if (border) {
  351. vbsize = sd;
  352. ibsize = (sd-2) * 3;
  353. } else {
  354. vbsize = (sd / 2) + 1;
  355. ibsize = (sd / 2) * 3;
  356. }
  357. break;
  358. case Lines2D.ArrowCap:
  359. if (border) {
  360. vbsize = 12;
  361. ibsize = 24;
  362. } else {
  363. vbsize = 3;
  364. ibsize = 3;
  365. }
  366. break;
  367. case Lines2D.TriangleCap:
  368. if (border) {
  369. vbsize = 6;
  370. ibsize = 12;
  371. } else {
  372. vbsize = 3;
  373. ibsize = 3;
  374. }
  375. break;
  376. case Lines2D.DiamondAnchorCap:
  377. if (border) {
  378. vbsize = 10;
  379. ibsize = 24;
  380. } else {
  381. vbsize = 5;
  382. ibsize = 9;
  383. }
  384. break;
  385. case Lines2D.SquareAnchorCap:
  386. if (border) {
  387. vbsize = 12;
  388. ibsize = 30;
  389. } else {
  390. vbsize = 4;
  391. ibsize = 6;
  392. }
  393. break;
  394. case Lines2D.RoundAnchorCap:
  395. if (border) {
  396. vbsize = sd*2;
  397. ibsize = (sd - 1) * 6;
  398. } else {
  399. vbsize = sd + 1;
  400. ibsize = (sd + 1) * 3;
  401. }
  402. break;
  403. }
  404. return { vbsize: vbsize*2, ibsize: ibsize };
  405. }
  406. let v = Vector2.Zero();
  407. let storeVertex = (vb: Float32Array, baseOffset: number, index: number, basePos: Vector2, rotation: number, vertex: Vector2): number => {
  408. let c = Math.cos(rotation);
  409. let s = Math.sin(rotation);
  410. v.x = (c * vertex.x) + (-s * vertex.y) + basePos.x;
  411. v.y = (s * vertex.x) + ( c * vertex.y) + basePos.y;
  412. let offset = baseOffset + (index*2);
  413. vb[offset + 0] = v.x;
  414. vb[offset + 1] = v.y;
  415. updateMinMax(vb, offset);
  416. return (baseOffset + index*2) / 2;
  417. }
  418. let storeIndex = (ib: Float32Array, baseOffset: number, index: number, vertexIndex: number) => {
  419. ib[baseOffset + index] = vertexIndex;
  420. }
  421. let buildCap = (vb: Float32Array, vbi: number, ib: Float32Array, ibi: number, pos: Vector2, thickness: number, borderThickness: number, type: number, capDir: Vector2): { vbsize: number; ibsize: number } => {
  422. // Compute the transformation from the direction of the cap to build relative to our default orientation [1;0] (our cap are by default pointing toward right, horizontal
  423. let dir = new Vector2(1, 0);
  424. let angle = Math.atan2(capDir.y, capDir.x) - Math.atan2(dir.y, dir.x);
  425. let ht = thickness / 2;
  426. let t = thickness;
  427. let borderMode = borderThickness != null;
  428. let bt = borderThickness;
  429. switch (type) {
  430. case Lines2D.NoCap:
  431. if (borderMode && !this.closed) {
  432. let vi = 0;
  433. let ii = 0;
  434. let v1 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, ht + bt));
  435. let v2 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(bt, ht + bt));
  436. let v3 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(bt, -(ht + bt)));
  437. let v4 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -(ht + bt)));
  438. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v3);
  439. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v4);
  440. }
  441. break;
  442. case Lines2D.ArrowCap:
  443. ht *= 2;
  444. case Lines2D.TriangleCap:
  445. {
  446. if (borderMode) {
  447. let f = type===Lines2D.TriangleCap ? bt : Math.sqrt(bt * bt * 2);
  448. let v1 = storeVertex(vb, vbi, 0, pos, angle, new Vector2(0, ht));
  449. let v2 = storeVertex(vb, vbi, 1, pos, angle, new Vector2(ht, 0));
  450. let v3 = storeVertex(vb, vbi, 2, pos, angle, new Vector2(0, -ht));
  451. let v4 = storeVertex(vb, vbi, 3, pos, angle, new Vector2(0, ht+f));
  452. let v5 = storeVertex(vb, vbi, 4, pos, angle, new Vector2(ht+f, 0));
  453. let v6 = storeVertex(vb, vbi, 5, pos, angle, new Vector2(0, -(ht+f)));
  454. let ii = 0;
  455. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v4); storeIndex(ib, ibi, ii++, v5);
  456. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v5); storeIndex(ib, ibi, ii++, v2);
  457. storeIndex(ib, ibi, ii++, v6); storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v2);
  458. storeIndex(ib, ibi, ii++, v6); storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v5);
  459. if (type === Lines2D.ArrowCap) {
  460. let rht = thickness / 2;
  461. let v7 = storeVertex(vb, vbi, 6, pos, angle, new Vector2(0, rht+bt));
  462. let v8 = storeVertex(vb, vbi, 7, pos, angle, new Vector2(-bt, rht+bt));
  463. let v9 = storeVertex(vb, vbi, 8, pos, angle, new Vector2(-bt, ht+f));
  464. let v10 = storeVertex(vb, vbi, 9, pos, angle, new Vector2(0, -(rht+bt)));
  465. let v11 = storeVertex(vb, vbi, 10, pos, angle, new Vector2(-bt, -(rht+bt)));
  466. let v12 = storeVertex(vb, vbi, 11, pos, angle, new Vector2(-bt, -(ht+f)));
  467. storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v8); storeIndex(ib, ibi, ii++, v9);
  468. storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v9); storeIndex(ib, ibi, ii++, v4);
  469. storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v12); storeIndex(ib, ibi, ii++, v11);
  470. storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v6); storeIndex(ib, ibi, ii++, v12);
  471. }
  472. } else {
  473. let v1 = storeVertex(vb, vbi, 0, pos, angle, new Vector2(0, ht));
  474. let v2 = storeVertex(vb, vbi, 1, pos, angle, new Vector2(ht, 0));
  475. let v3 = storeVertex(vb, vbi, 2, pos, angle, new Vector2(0, -ht));
  476. storeIndex(ib, ibi, 0, v1);
  477. storeIndex(ib, ibi, 1, v2);
  478. storeIndex(ib, ibi, 2, v3);
  479. }
  480. break;
  481. }
  482. case Lines2D.RoundCap:
  483. {
  484. if (borderMode) {
  485. let curA = -Math.PI / 2;
  486. let incA = Math.PI / (sd / 2 - 1);
  487. let ii = 0;
  488. for (let i = 0; i < (sd / 2); i++) {
  489. let v1 = storeVertex(vb, vbi, i*2 + 0, pos, angle, new Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht));
  490. let v2 = storeVertex(vb, vbi, i*2 + 1, pos, angle, new Vector2(Math.cos(curA) * (ht+bt), Math.sin(curA) * (ht+bt)));
  491. if (i > 0) {
  492. storeIndex(ib, ibi, ii++, v1 - 2);
  493. storeIndex(ib, ibi, ii++, v2 - 2);
  494. storeIndex(ib, ibi, ii++, v2);
  495. storeIndex(ib, ibi, ii++, v1 - 2);
  496. storeIndex(ib, ibi, ii++, v2);
  497. storeIndex(ib, ibi, ii++, v1);
  498. }
  499. curA += incA;
  500. }
  501. } else {
  502. let c = storeVertex(vb, vbi, 0, pos, angle, new Vector2(0, 0));
  503. let curA = -Math.PI / 2;
  504. let incA = Math.PI / (sd / 2 - 1);
  505. storeVertex(vb, vbi, 1, pos, angle, new Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht));
  506. curA += incA;
  507. for (let i = 1; i < (sd / 2); i++) {
  508. let v2 = storeVertex(vb, vbi, i + 1, pos, angle, new Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht));
  509. storeIndex(ib, ibi, i * 3 + 0, c);
  510. storeIndex(ib, ibi, i * 3 + 1, v2 - 1);
  511. storeIndex(ib, ibi, i * 3 + 2, v2);
  512. curA += incA;
  513. }
  514. }
  515. break;
  516. }
  517. case Lines2D.SquareAnchorCap:
  518. {
  519. let vi = 0;
  520. let v1 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, t));
  521. let v2 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2, t));
  522. let v3 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2, -t));
  523. let v4 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -t));
  524. if (borderMode) {
  525. let v5 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, ht+bt));
  526. let v6 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, ht+bt));
  527. let v7 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, t + bt));
  528. let v8 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2 + bt, t + bt));
  529. let v9 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2 + bt, -(t+bt)));
  530. let v10 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, -(t + bt)));
  531. let v11 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, -(ht+bt)));
  532. let v12 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -(ht+bt)));
  533. let ii = 0;
  534. storeIndex(ib, ibi, ii++, v6); storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v5);
  535. storeIndex(ib, ibi, ii++, v6); storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v1);
  536. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v8);
  537. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v8); storeIndex(ib, ibi, ii++, v2);
  538. storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v8); storeIndex(ib, ibi, ii++, v9);
  539. storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v9); storeIndex(ib, ibi, ii++, v3);
  540. storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v9); storeIndex(ib, ibi, ii++, v10);
  541. storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v4);
  542. storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v11); storeIndex(ib, ibi, ii++, v4);
  543. storeIndex(ib, ibi, ii++, v11); storeIndex(ib, ibi, ii++, v12); storeIndex(ib, ibi, ii++, v4);
  544. } else {
  545. storeIndex(ib, ibi, 0, v1);
  546. storeIndex(ib, ibi, 1, v2);
  547. storeIndex(ib, ibi, 2, v3);
  548. storeIndex(ib, ibi, 3, v1);
  549. storeIndex(ib, ibi, 4, v3);
  550. storeIndex(ib, ibi, 5, v4);
  551. }
  552. break;
  553. }
  554. case Lines2D.RoundAnchorCap:
  555. {
  556. let cpos = Math.sqrt(t * t - ht * ht);
  557. let center = new Vector2(cpos, 0);
  558. let curA = Tools.ToRadians(-150);
  559. let incA = Tools.ToRadians(300) / (sd - 1);
  560. if (borderMode) {
  561. let ii = 0;
  562. for (let i = 0; i < sd; i++) {
  563. let v1 = storeVertex(vb, vbi, i * 2 + 0, pos, angle, new Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t));
  564. let v2 = storeVertex(vb, vbi, i * 2 + 1, pos, angle, new Vector2(cpos + Math.cos(curA) * (t + bt), Math.sin(curA) * (t + bt)));
  565. if (i > 0) {
  566. storeIndex(ib, ibi, ii++, v1 - 2);
  567. storeIndex(ib, ibi, ii++, v2 - 2);
  568. storeIndex(ib, ibi, ii++, v2);
  569. storeIndex(ib, ibi, ii++, v1 - 2);
  570. storeIndex(ib, ibi, ii++, v2);
  571. storeIndex(ib, ibi, ii++, v1);
  572. }
  573. curA += incA;
  574. }
  575. } else {
  576. let c = storeVertex(vb, vbi, 0, pos, angle, center);
  577. storeVertex(vb, vbi, 1, pos, angle, new Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t));
  578. curA += incA;
  579. for (let i = 1; i < sd; i++) {
  580. let v2 = storeVertex(vb, vbi, i + 1, pos, angle, new Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t));
  581. storeIndex(ib, ibi, i * 3 + 0, c);
  582. storeIndex(ib, ibi, i * 3 + 1, v2 - 1);
  583. storeIndex(ib, ibi, i * 3 + 2, v2);
  584. curA += incA;
  585. }
  586. storeIndex(ib, ibi, sd * 3 + 0, c);
  587. storeIndex(ib, ibi, sd * 3 + 1, c + 1);
  588. storeIndex(ib, ibi, sd * 3 + 2, c + sd);
  589. }
  590. break;
  591. }
  592. case Lines2D.DiamondAnchorCap:
  593. {
  594. let vi = 0;
  595. let v1 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, ht));
  596. let v2 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht, t));
  597. let v3 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht * 3, 0));
  598. let v4 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht, -t));
  599. let v5 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -ht));
  600. if (borderMode) {
  601. let f = Math.sqrt(bt * bt * 2);
  602. let v6 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-f,ht));
  603. let v7 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht,t+f));
  604. let v8 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht*3+f,0));
  605. let v9 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht,-(t+f)));
  606. let v10 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-f, -ht));
  607. let ii = 0;
  608. storeIndex(ib, ibi, ii++, v6); storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v1);
  609. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v2);
  610. storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v8);
  611. storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v8); storeIndex(ib, ibi, ii++, v3);
  612. storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v8); storeIndex(ib, ibi, ii++, v9);
  613. storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v9); storeIndex(ib, ibi, ii++, v4);
  614. storeIndex(ib, ibi, ii++, v4); storeIndex(ib, ibi, ii++, v9); storeIndex(ib, ibi, ii++, v10);
  615. storeIndex(ib, ibi, ii++, v4); storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v5);
  616. } else {
  617. storeIndex(ib, ibi, 0, v1); storeIndex(ib, ibi, 1, v2); storeIndex(ib, ibi, 2, v3);
  618. storeIndex(ib, ibi, 3, v1); storeIndex(ib, ibi, 4, v3); storeIndex(ib, ibi, 5, v5);
  619. storeIndex(ib, ibi, 6, v5); storeIndex(ib, ibi, 7, v3); storeIndex(ib, ibi, 8, v4);
  620. }
  621. break;
  622. }
  623. }
  624. return null;
  625. }
  626. let buildLine = (vb: Float32Array, contour: Vector2[], ht: number, bt?: number) => {
  627. let lineA = Vector2.Zero();
  628. let lineB = Vector2.Zero();
  629. let tangent = Vector2.Zero();
  630. let miter = Vector2.Zero();
  631. let curNormal: Vector2 = null;
  632. if (this.closed) {
  633. this.points.push(this.points[0]);
  634. }
  635. var total = this.points.length;
  636. for (let i = 1; i < total; i++) {
  637. let last = this.points[i - 1];
  638. let cur = this.points[i];
  639. let next = (i < (this.points.length - 1)) ? this.points[i + 1] : null;
  640. direction(cur, last, lineA);
  641. if (!curNormal) {
  642. curNormal = Vector2.Zero();
  643. perp(lineA, curNormal);
  644. }
  645. if (i === 1) {
  646. store(vb, contour, 0, total, this.points[0], curNormal, ht, bt);
  647. }
  648. if (!next) {
  649. perp(lineA, curNormal);
  650. store(vb, contour, i, total, this.points[i], curNormal, ht, bt, i - 1);
  651. } else {
  652. direction(next, cur, lineB);
  653. var miterLen = computeMiter(tangent, miter, lineA, lineB);
  654. store(vb, contour, i, total, this.points[i], miter, miterLen*ht, miterLen*bt, i - 1);
  655. }
  656. }
  657. if (this.points.length > 2 && this.closed) {
  658. let last2 = this.points[total - 2];
  659. let cur2 = this.points[0];
  660. let next2 = this.points[1];
  661. direction(cur2, last2, lineA);
  662. direction(next2, cur2, lineB);
  663. perp(lineA, curNormal);
  664. var miterLen2 = computeMiter(tangent, miter, lineA, lineB);
  665. store(vb, null, 0, total, this.points[0], miter, miterLen2 * ht, miterLen2 * bt, 1);
  666. // Patch contour
  667. if (contour) {
  668. let off = (bt == null) ? 0 : 4;
  669. contour[0].x = vb[off + 0];
  670. contour[0].y = vb[off + 1];
  671. contour[1].x = vb[off + 2];
  672. contour[1].y = vb[off + 3];
  673. }
  674. }
  675. // Remove the point we added at the beginning
  676. if (this.closed) {
  677. this.points.splice(total - 1);
  678. }
  679. }
  680. let contour = new Array<Vector2>();
  681. // Need to create WebGL resources for fill part?
  682. if (this.fill) {
  683. let startCapInfo = getCapSize(this.startCap);
  684. let endCapInfo = getCapSize(this.endCap);
  685. let count = this.points.length;
  686. let vbSize = (count * 2 * 2) + startCapInfo.vbsize + endCapInfo.vbsize;
  687. let vb = new Float32Array(vbSize);
  688. let ht = this.fillThickness / 2;
  689. let total = this.points.length;
  690. buildLine(vb, this.border ? null : contour, ht);
  691. let max = total * 2;
  692. let triCount = (count - (this.closed ? 0 : 1)) * 2;
  693. let ib = new Float32Array(triCount * 3 + startCapInfo.ibsize + endCapInfo.ibsize);
  694. for (let i = 0; i < triCount; i+=2) {
  695. ib[i * 3 + 0] = i + 0;
  696. ib[i * 3 + 1] = i + 1;
  697. ib[i * 3 + 2] = (i + 2) % max;
  698. ib[i * 3 + 3] = i + 1;
  699. ib[i * 3 + 4] = (i + 3) % max;
  700. ib[i * 3 + 5] = (i + 2) % max;
  701. }
  702. buildCap(vb, count * 2 * 2, ib, triCount * 3, this.points[0], this.fillThickness, null, this.startCap, startDir);
  703. buildCap(vb, (count * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, null, this.endCap, endDir);
  704. renderCache.fillVB = engine.createVertexBuffer(vb);
  705. renderCache.fillIB = engine.createIndexBuffer(ib);
  706. renderCache.fillIndicesCount = ib.length;
  707. let ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_FILLPARTID, ["position"]);
  708. renderCache.effectFill = engine.createEffect({ vertex: "lines2d", fragment: "lines2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
  709. }
  710. // Need to create WebGL resources for border part?
  711. if (this.border) {
  712. let startCapInfo = getCapSize(this.startCap, true);
  713. let endCapInfo = getCapSize(this.endCap, true);
  714. let count = this.points.length;
  715. let vbSize = (count * 2 * 2 * 2) + startCapInfo.vbsize + endCapInfo.vbsize;
  716. let vb = new Float32Array(vbSize);
  717. let ht = this.fillThickness / 2;
  718. let bt = this.borderThickness;
  719. let total = this.points.length;
  720. buildLine(vb, contour, ht, bt);
  721. let max = total * 2 * 2;
  722. let triCount = (count - (this.closed ? 0 : 1)) * 2 * 2;
  723. let ib = new Float32Array(triCount * 3 + startCapInfo.ibsize + endCapInfo.ibsize);
  724. for (let i = 0; i < triCount; i += 4) {
  725. ib[i * 3 + 0] = i + 0;
  726. ib[i * 3 + 1] = i + 2;
  727. ib[i * 3 + 2] = (i + 6) % max;
  728. ib[i * 3 + 3] = i + 0;
  729. ib[i * 3 + 4] = (i + 6) % max;
  730. ib[i * 3 + 5] = (i + 4) % max;
  731. ib[i * 3 + 6] = i + 3;
  732. ib[i * 3 + 7] = i + 1;
  733. ib[i * 3 + 8] = (i + 5) % max;
  734. ib[i * 3 + 9] = i + 3;
  735. ib[i * 3 + 10] = (i + 5) % max;
  736. ib[i * 3 + 11] = (i + 7) % max;
  737. }
  738. buildCap(vb, count * 2 * 2 * 2, ib, triCount * 3, this.points[0], this.fillThickness, this.borderThickness, this.startCap, startDir);
  739. buildCap(vb, (count * 2 * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, this.borderThickness, this.endCap, endDir);
  740. renderCache.borderVB = engine.createVertexBuffer(vb);
  741. renderCache.borderIB = engine.createIndexBuffer(ib);
  742. renderCache.borderIndicesCount = ib.length;
  743. let ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_BORDERPARTID, ["position"]);
  744. renderCache.effectBorder = engine.createEffect({ vertex: "lines2d", fragment: "lines2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
  745. }
  746. this._contour = contour;
  747. let bs = this._boundingMax.subtract(this._boundingMin);
  748. this._size.width = bs.x;
  749. this._size.height = bs.y;
  750. return renderCache;
  751. }
  752. protected createInstanceDataParts(): InstanceDataBase[] {
  753. var res = new Array<InstanceDataBase>();
  754. if (this.border) {
  755. res.push(new Lines2DInstanceData(Shape2D.SHAPE2D_BORDERPARTID));
  756. }
  757. if (this.fill) {
  758. res.push(new Lines2DInstanceData(Shape2D.SHAPE2D_FILLPARTID));
  759. }
  760. return res;
  761. }
  762. protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
  763. if (!super.refreshInstanceDataPart(part)) {
  764. return false;
  765. }
  766. if (part.id === Shape2D.SHAPE2D_BORDERPARTID) {
  767. let d = <Lines2DInstanceData>part;
  768. d.boundingMin = this.boundingMin;
  769. d.boundingMax = this.boundingMax;
  770. }
  771. else if (part.id === Shape2D.SHAPE2D_FILLPARTID) {
  772. let d = <Lines2DInstanceData>part;
  773. d.boundingMin = this.boundingMin;
  774. d.boundingMax = this.boundingMax;
  775. }
  776. return true;
  777. }
  778. private static _noCap = 0;
  779. private static _roundCap = 1;
  780. private static _triangleCap = 2;
  781. private static _squareAnchorCap = 3;
  782. private static _roundAnchorCap = 4;
  783. private static _diamondAnchorCap = 5;
  784. private static _arrowCap = 6;
  785. private static _roundCapSubDiv = 36;
  786. private _boundingMin: Vector2;
  787. private _boundingMax: Vector2;
  788. private _size: Size;
  789. private _contour: Vector2[];
  790. private _closed: boolean;
  791. private _startCap: number;
  792. private _endCap: number;
  793. private _fillThickness: number;
  794. private _points: Vector2[];
  795. }
  796. }