babylon.lines2d.ts 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102
  1. module BABYLON {
  2. export class Lines2DRenderCache extends ModelRenderCache {
  3. effectsReady: boolean = false;
  4. fillVB: WebGLBuffer = null;
  5. fillIB: WebGLBuffer = null;
  6. fillIndicesCount: number = 0;
  7. instancingFillAttributes: InstancingAttributeInfo[] = null;
  8. effectFill: Effect = null;
  9. effectFillInstanced: Effect = null;
  10. borderVB: WebGLBuffer = null;
  11. borderIB: WebGLBuffer = null;
  12. borderIndicesCount: number = 0;
  13. instancingBorderAttributes: InstancingAttributeInfo[] = null;
  14. effectBorder: Effect = null;
  15. effectBorderInstanced: Effect = null;
  16. constructor(engine: Engine, modelKey: string) {
  17. super(engine, modelKey);
  18. }
  19. render(instanceInfo: GroupInstanceInfo, context: Render2DContext): boolean {
  20. // Do nothing if the shader is still loading/preparing
  21. if (!this.effectsReady) {
  22. if ((this.effectFill && (!this.effectFill.isReady() || (this.effectFillInstanced && !this.effectFillInstanced.isReady()))) ||
  23. (this.effectBorder && (!this.effectBorder.isReady() || (this.effectBorderInstanced && !this.effectBorderInstanced.isReady())))) {
  24. return false;
  25. }
  26. this.effectsReady = true;
  27. }
  28. var engine = instanceInfo.owner.owner.engine;
  29. let depthFunction = 0;
  30. if (this.effectFill && this.effectBorder) {
  31. depthFunction = engine.getDepthFunction();
  32. engine.setDepthFunctionToLessOrEqual();
  33. }
  34. let curAlphaMode = engine.getAlphaMode();
  35. if (this.effectFill) {
  36. let partIndex = instanceInfo.partIndexFromId.get(Shape2D.SHAPE2D_FILLPARTID.toString());
  37. let pid = context.groupInfoPartData[partIndex];
  38. if (context.renderMode !== Render2DContext.RenderModeOpaque) {
  39. engine.setAlphaMode(Engine.ALPHA_COMBINE);
  40. }
  41. let effect = context.useInstancing ? this.effectFillInstanced : this.effectFill;
  42. engine.enableEffect(effect);
  43. engine.bindBuffersDirectly(this.fillVB, this.fillIB, [2], 2*4, effect);
  44. if (context.useInstancing) {
  45. if (!this.instancingFillAttributes) {
  46. this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, effect);
  47. }
  48. engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingFillAttributes);
  49. engine.draw(true, 0, this.fillIndicesCount, pid._partData.usedElementCount);
  50. engine.unbindInstanceAttributes();
  51. } else {
  52. for (let i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
  53. this.setupUniforms(effect, partIndex, pid._partData, i);
  54. engine.draw(true, 0, this.fillIndicesCount);
  55. }
  56. }
  57. }
  58. if (this.effectBorder) {
  59. let partIndex = instanceInfo.partIndexFromId.get(Shape2D.SHAPE2D_BORDERPARTID.toString());
  60. let pid = context.groupInfoPartData[partIndex];
  61. if (context.renderMode !== Render2DContext.RenderModeOpaque) {
  62. engine.setAlphaMode(Engine.ALPHA_COMBINE);
  63. }
  64. let effect = context.useInstancing ? this.effectBorderInstanced : this.effectBorder;
  65. engine.enableEffect(effect);
  66. engine.bindBuffersDirectly(this.borderVB, this.borderIB, [2], 2 * 4, effect);
  67. if (context.useInstancing) {
  68. if (!this.instancingBorderAttributes) {
  69. this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, effect);
  70. }
  71. engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingBorderAttributes);
  72. engine.draw(true, 0, this.borderIndicesCount, pid._partData.usedElementCount);
  73. engine.unbindInstanceAttributes();
  74. } else {
  75. for (let i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
  76. this.setupUniforms(effect, partIndex, pid._partData, i);
  77. engine.draw(true, 0, this.borderIndicesCount);
  78. }
  79. }
  80. }
  81. engine.setAlphaMode(curAlphaMode);
  82. if (this.effectFill && this.effectBorder) {
  83. engine.setDepthFunction(depthFunction);
  84. }
  85. return true;
  86. }
  87. public dispose(): boolean {
  88. if (!super.dispose()) {
  89. return false;
  90. }
  91. if (this.fillVB) {
  92. this._engine._releaseBuffer(this.fillVB);
  93. this.fillVB = null;
  94. }
  95. if (this.fillIB) {
  96. this._engine._releaseBuffer(this.fillIB);
  97. this.fillIB = null;
  98. }
  99. if (this.effectFill) {
  100. this._engine._releaseEffect(this.effectFill);
  101. this.effectFill = null;
  102. }
  103. if (this.effectFillInstanced) {
  104. this._engine._releaseEffect(this.effectFillInstanced);
  105. this.effectFillInstanced = null;
  106. }
  107. if (this.borderVB) {
  108. this._engine._releaseBuffer(this.borderVB);
  109. this.borderVB = null;
  110. }
  111. if (this.borderIB) {
  112. this._engine._releaseBuffer(this.borderIB);
  113. this.borderIB = null;
  114. }
  115. if (this.effectBorder) {
  116. this._engine._releaseEffect(this.effectBorder);
  117. this.effectBorder = null;
  118. }
  119. if (this.effectBorderInstanced) {
  120. this._engine._releaseEffect(this.effectBorderInstanced);
  121. this.effectBorderInstanced = null;
  122. }
  123. return true;
  124. }
  125. }
  126. export class Lines2DInstanceData extends Shape2DInstanceData {
  127. constructor(partId: number) {
  128. super(partId, 1);
  129. }
  130. @instanceData()
  131. get boundingMin(): Vector2 {
  132. return null;
  133. }
  134. @instanceData()
  135. get boundingMax(): Vector2 {
  136. return null;
  137. }
  138. }
  139. @className("Lines2D")
  140. export class Lines2D extends Shape2D {
  141. static get NoCap () { return Lines2D._noCap; }
  142. static get RoundCap () { return Lines2D._roundCap; }
  143. static get TriangleCap () { return Lines2D._triangleCap; }
  144. static get SquareAnchorCap () { return Lines2D._squareAnchorCap; }
  145. static get RoundAnchorCap () { return Lines2D._roundAnchorCap; }
  146. static get DiamondAnchorCap () { return Lines2D._diamondAnchorCap; }
  147. static get ArrowCap () { return Lines2D._arrowCap; }
  148. public static pointsProperty: Prim2DPropInfo;
  149. public static fillThicknessProperty: Prim2DPropInfo;
  150. public static closedProperty: Prim2DPropInfo;
  151. public static startCapProperty: Prim2DPropInfo;
  152. public static endCapProperty: Prim2DPropInfo;
  153. public get actualSize(): Size {
  154. return this.size;
  155. }
  156. @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 1, pi => Lines2D.pointsProperty = pi)
  157. public get points(): Vector2[] {
  158. return this._points;
  159. }
  160. public set points(value: Vector2[]) {
  161. this._points = value;
  162. this._levelBoundingInfoDirty = true;
  163. }
  164. @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 2, pi => Lines2D.fillThicknessProperty = pi)
  165. public get fillThickness(): number {
  166. return this._fillThickness;
  167. }
  168. public set fillThickness(value: number) {
  169. this._fillThickness = value;
  170. }
  171. @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 3, pi => Lines2D.closedProperty = pi)
  172. public get closed(): boolean {
  173. return this._closed;
  174. }
  175. public set closed(value: boolean) {
  176. this._closed = value;
  177. }
  178. @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 4, pi => Lines2D.startCapProperty = pi)
  179. public get startCap(): number {
  180. return this._startCap;
  181. }
  182. public set startCap(value: number) {
  183. this._startCap = value;
  184. }
  185. @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 5, pi => Lines2D.endCapProperty = pi)
  186. public get endCap(): number {
  187. return this._endCap;
  188. }
  189. public set endCap(value: number) {
  190. this._endCap = value;
  191. }
  192. private static _prevA: Vector2 = Vector2.Zero();
  193. private static _prevB: Vector2 = Vector2.Zero();
  194. private static _curA: Vector2 = Vector2.Zero();
  195. private static _curB: Vector2 = Vector2.Zero();
  196. protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
  197. let pl = this.points.length;
  198. let l = this.closed ? pl + 1 : pl;
  199. let p = intersectInfo._localPickPosition;
  200. this.transformPointWithOriginToRef(this._contour[0], null, Lines2D._prevA);
  201. this.transformPointWithOriginToRef(this._contour[1], null, Lines2D._prevB);
  202. for (let i = 1; i < l; i++) {
  203. this.transformPointWithOriginToRef(this._contour[(i % pl) * 2 + 0], null, Lines2D._curA);
  204. this.transformPointWithOriginToRef(this._contour[(i % pl) * 2 + 1], null, Lines2D._curB);
  205. if (Vector2.PointInTriangle(p, Lines2D._prevA, Lines2D._prevB, Lines2D._curA)) {
  206. return true;
  207. }
  208. if (Vector2.PointInTriangle(p, Lines2D._curA, Lines2D._prevB, Lines2D._curB)) {
  209. return true;
  210. }
  211. Lines2D._prevA.x = Lines2D._curA.x;
  212. Lines2D._prevA.y = Lines2D._curA.y;
  213. Lines2D._prevB.x = Lines2D._curB.x;
  214. Lines2D._prevB.y = Lines2D._curB.y;
  215. }
  216. let capIntersect = (tri: number[], points: number[]): boolean => {
  217. let l = tri.length;
  218. for (let i = 0; i < l; i += 3) {
  219. Lines2D._curA.x = points[tri[i + 0] * 2 + 0];
  220. Lines2D._curA.y = points[tri[i + 0] * 2 + 1];
  221. this.transformPointWithOriginToRef(Lines2D._curA, null, Lines2D._curB);
  222. Lines2D._curA.x = points[tri[i + 1] * 2 + 0];
  223. Lines2D._curA.y = points[tri[i + 1] * 2 + 1];
  224. this.transformPointWithOriginToRef(Lines2D._curA, null, Lines2D._prevA);
  225. Lines2D._curA.x = points[tri[i + 2] * 2 + 0];
  226. Lines2D._curA.y = points[tri[i + 2] * 2 + 1];
  227. this.transformPointWithOriginToRef(Lines2D._curA, null, Lines2D._prevB);
  228. if (Vector2.PointInTriangle(p, Lines2D._prevA, Lines2D._prevB, Lines2D._curB)) {
  229. return true;
  230. }
  231. }
  232. return false;
  233. }
  234. if (this._startCapTriIndices) {
  235. if (capIntersect(this._startCapTriIndices, this._startCapContour)) {
  236. return true;
  237. }
  238. if (capIntersect(this._endCapTriIndices, this._endCapContour)) {
  239. return true;
  240. }
  241. }
  242. return false;
  243. }
  244. protected get size(): Size {
  245. return this._size;
  246. }
  247. protected get boundingMin(): Vector2 {
  248. return this._boundingMin;
  249. }
  250. protected get boundingMax(): Vector2 {
  251. return this._boundingMax;
  252. }
  253. protected getUsedShaderCategories(dataPart: InstanceDataBase): string[] {
  254. let res = super.getUsedShaderCategories(dataPart);
  255. // Remove the BORDER category, we don't use it in the VertexShader
  256. let i = res.indexOf(Shape2D.SHAPE2D_CATEGORY_BORDER);
  257. if (i !== -1) {
  258. res.splice(i, 1);
  259. }
  260. return res;
  261. }
  262. protected updateLevelBoundingInfo() {
  263. BoundingInfo2D.CreateFromMinMaxToRef(this._boundingMin.x, this._boundingMax.x, this._boundingMin.y, this._boundingMax.y, this._levelBoundingInfo, this.origin);
  264. }
  265. protected setupLines2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, points: Vector2[], fillThickness: number, startCap: number, endCap: number, fill: IBrush2D, border: IBrush2D, borderThickness: number, closed: boolean, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, vAlignment: number, hAlignment: number) {
  266. this.setupShape2D(owner, parent, id, position, origin, isVisible, fill, border, borderThickness, marginTop, marginLeft, marginRight, marginBottom, hAlignment, vAlignment);
  267. this.fillThickness = fillThickness;
  268. this.startCap = startCap;
  269. this.endCap = endCap;
  270. this.points = points;
  271. this.closed = closed;
  272. this._size = Size.Zero();
  273. this._boundingMin = Vector2.Zero();
  274. this._boundingMax = Vector2.Zero();
  275. }
  276. /**
  277. * Create an 2D Lines Shape primitive. The defined lines may be opened or closed (see below)
  278. * @param parent the parent primitive, must be a valid primitive (or the Canvas)
  279. * @param points an array that describe the points to use to draw the line, must contain at least two entries.
  280. * options:
  281. * - id a text identifier, for information purpose
  282. * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
  283. * - origin: define the normalized origin point location, default [0.5;0.5]
  284. * - fillThickness: the thickness of the fill part of the line, can be null to draw nothing (but a border brush must be given), default is 1.
  285. * - closed: if false the lines are said to be opened, the first point and the latest DON'T connect. if true the lines are said to be closed, the first and last point will be connected by a line. For instance you can define the 4 points of a rectangle, if you set closed to true a 4 edges rectangle will be drawn. If you set false, only three edges will be drawn, the edge formed by the first and last point won't exist. Default is false.
  286. * - Draw a cap of the given type at the start of the first line, you can't define a Cap if the Lines2D is closed. Default is Lines2D.NoCap.
  287. * - Draw a cap of the given type at the end of the last line, you can't define a Cap if the Lines2D is closed. Default is Lines2D.NoCap.
  288. * - fill: the brush used to draw the fill content of the lines, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white.
  289. * - border: the brush used to draw the border of the lines, you can set null to draw nothing (but you will have to set a fill brush), default is null.
  290. * - borderThickness: the thickness of the drawn border, default is 1.
  291. * - isVisible: true if the primitive must be visible, false for hidden. Default is true.
  292. * - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
  293. * - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
  294. * - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
  295. */
  296. public static Create(parent: Prim2DBase, points: Vector2[], options: { id?: string, position?: Vector2, x?: number, y?: number, origin?: Vector2, fillThickness?: number, closed?: boolean, startCap?: number, endCap?: number, fill?: IBrush2D, border?: IBrush2D, borderThickness?: number, isVisible?: boolean, marginTop?: number, marginLeft?: number, marginRight?: number, marginBottom?: number, vAlignment?: number, hAlignment?: number }): Lines2D {
  297. Prim2DBase.CheckParent(parent);
  298. let lines = new Lines2D();
  299. if (!options) {
  300. lines.setupLines2D(parent.owner, parent, null, Vector2.Zero(), null, points, 1, 0, 0, Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF"), null, 1, false, true, null, null, null, null, null, null);
  301. } else {
  302. let fill: IBrush2D;
  303. if (options.fill === undefined) {
  304. fill = Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
  305. } else {
  306. fill = options.fill;
  307. }
  308. let pos = options.position || new Vector2(options.x || 0, options.y || 0);
  309. lines.setupLines2D
  310. (
  311. parent.owner,
  312. parent,
  313. options.id || null,
  314. pos,
  315. options.origin || null,
  316. points,
  317. (options.fillThickness == null) ? 1 : options.fillThickness,
  318. (options.startCap == null) ? 0 : options.startCap,
  319. (options.endCap == null) ? 0 : options.endCap,
  320. fill,
  321. options.border || null,
  322. (options.borderThickness == null) ? 1 : options.borderThickness,
  323. (options.closed == null) ? false : options.closed,
  324. (options.isVisible == null) ? true : options.isVisible,
  325. options.marginTop || null,
  326. options.marginLeft || null,
  327. options.marginRight || null,
  328. options.marginBottom || null,
  329. options.vAlignment || null,
  330. options.hAlignment || null);
  331. }
  332. return lines;
  333. }
  334. protected createModelRenderCache(modelKey: string): ModelRenderCache {
  335. let renderCache = new Lines2DRenderCache(this.owner.engine, modelKey);
  336. return renderCache;
  337. }
  338. protected setupModelRenderCache(modelRenderCache: ModelRenderCache) {
  339. let renderCache = <Lines2DRenderCache>modelRenderCache;
  340. let engine = this.owner.engine;
  341. // Init min/max because their being computed here
  342. this._boundingMin = new Vector2(Number.MAX_VALUE, Number.MAX_VALUE);
  343. this._boundingMax = new Vector2(Number.MIN_VALUE, Number.MIN_VALUE);
  344. let perp = (v: Vector2, res: Vector2) => {
  345. res.x = v.y;
  346. res.y = -v.x;
  347. };
  348. let direction = (a: Vector2, b: Vector2, res: Vector2) => {
  349. a.subtractToRef(b, res);
  350. res.normalize();
  351. }
  352. let tps = Vector2.Zero();
  353. let computeMiter = (tangent: Vector2, miter: Vector2, a: Vector2, b: Vector2): number => {
  354. a.addToRef(b, tangent);
  355. tangent.normalize();
  356. miter.x = -tangent.y;
  357. miter.y = tangent.x;
  358. tps.x = -a.y;
  359. tps.y = a.x;
  360. return 1 / Vector2.Dot(miter, tps);
  361. }
  362. let intersect = (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number): boolean => {
  363. let d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
  364. if (d === 0) return false;
  365. let xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d; // Intersection point is xi/yi, just in case...
  366. //let yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d; // That's why I left it commented
  367. if (xi < Math.min(x1, x2) || xi > Math.max(x1, x2)) return false;
  368. if (xi < Math.min(x3, x4) || xi > Math.max(x3, x4)) return false;
  369. return true;
  370. }
  371. let startDir: Vector2 = Vector2.Zero();
  372. let endDir: Vector2 = Vector2.Zero();
  373. let updateMinMax = (array: Float32Array, offset: number) => {
  374. if (offset >= array.length) {
  375. return;
  376. }
  377. this._boundingMin.x = Math.min(this._boundingMin.x, array[offset]);
  378. this._boundingMax.x = Math.max(this._boundingMax.x, array[offset]);
  379. this._boundingMin.y = Math.min(this._boundingMin.y, array[offset+1]);
  380. this._boundingMax.y = Math.max(this._boundingMax.y, array[offset+1]);
  381. }
  382. let store = (array: Float32Array, contour: Vector2[], index: number, max: number, p: Vector2, n: Vector2, halfThickness: number, borderThickness: number, detectFlip?: number) => {
  383. let borderMode = borderThickness != null && !isNaN(borderThickness);
  384. let off = index * (borderMode ? 8 : 4);
  385. // 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)
  386. if (off >= array.length) {
  387. return;
  388. }
  389. // Store start/end normal, we need it for the cap construction
  390. if (index === 0) {
  391. perp(n, startDir);
  392. } else if (index === max - 1) {
  393. perp(n, endDir);
  394. endDir.x *= -1;
  395. endDir.y *= -1;
  396. }
  397. let swap = false;
  398. array[off + 0] = p.x + n.x * halfThickness;
  399. array[off + 1] = p.y + n.y * halfThickness;
  400. array[off + 2] = p.x + n.x * -halfThickness;
  401. array[off + 3] = p.y + n.y * -halfThickness;
  402. updateMinMax(array, off);
  403. updateMinMax(array, off + 2);
  404. // If an index is given we check if the two segments formed between [index+0;detectFlip+0] and [index+2;detectFlip+2] intersect themselves.
  405. // 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
  406. if (detectFlip !== undefined) {
  407. // Flip if intersect
  408. let flipOff = detectFlip * (borderMode ? 8 : 4);
  409. 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])) {
  410. swap = true;
  411. let tps = array[off + 0];
  412. array[off + 0] = array[off + 2];
  413. array[off + 2] = tps;
  414. tps = array[off + 1];
  415. array[off + 1] = array[off + 3];
  416. array[off + 3] = tps;
  417. }
  418. }
  419. if (borderMode) {
  420. let t = halfThickness + borderThickness;
  421. array[off + 4] = p.x + n.x * (swap ? -t : t);
  422. array[off + 5] = p.y + n.y * (swap ? -t : t);
  423. array[off + 6] = p.x + n.x * (swap ? t : -t);
  424. array[off + 7] = p.y + n.y * (swap ? t : -t);
  425. updateMinMax(array, off + 4);
  426. updateMinMax(array, off + 6);
  427. }
  428. if (contour) {
  429. off += borderMode ? 4 : 0;
  430. contour.push(new Vector2(array[off + 0], array[off + 1]));
  431. contour.push(new Vector2(array[off + 2], array[off + 3]));
  432. }
  433. }
  434. let sd = Lines2D._roundCapSubDiv;
  435. let getCapSize = (type: number, border: boolean = false): { vbsize: number; ibsize: number } => {
  436. // If no array given, we call this to get the size
  437. let vbsize: number = 0, ibsize: number = 0;
  438. switch (type) {
  439. case Lines2D.NoCap:
  440. // If the line is not close and we're computing border, we add the size to generate the edge border
  441. if (!this.closed && border) {
  442. vbsize = 4;
  443. ibsize = 6;
  444. } else {
  445. vbsize = ibsize = 0;
  446. }
  447. break;
  448. case Lines2D.RoundCap:
  449. if (border) {
  450. vbsize = sd;
  451. ibsize = (sd-2) * 3;
  452. } else {
  453. vbsize = (sd / 2) + 1;
  454. ibsize = (sd / 2) * 3;
  455. }
  456. break;
  457. case Lines2D.ArrowCap:
  458. if (border) {
  459. vbsize = 12;
  460. ibsize = 24;
  461. } else {
  462. vbsize = 3;
  463. ibsize = 3;
  464. }
  465. break;
  466. case Lines2D.TriangleCap:
  467. if (border) {
  468. vbsize = 6;
  469. ibsize = 12;
  470. } else {
  471. vbsize = 3;
  472. ibsize = 3;
  473. }
  474. break;
  475. case Lines2D.DiamondAnchorCap:
  476. if (border) {
  477. vbsize = 10;
  478. ibsize = 24;
  479. } else {
  480. vbsize = 5;
  481. ibsize = 9;
  482. }
  483. break;
  484. case Lines2D.SquareAnchorCap:
  485. if (border) {
  486. vbsize = 12;
  487. ibsize = 30;
  488. } else {
  489. vbsize = 4;
  490. ibsize = 6;
  491. }
  492. break;
  493. case Lines2D.RoundAnchorCap:
  494. if (border) {
  495. vbsize = sd*2;
  496. ibsize = (sd - 1) * 6;
  497. } else {
  498. vbsize = sd + 1;
  499. ibsize = (sd + 1) * 3;
  500. }
  501. break;
  502. }
  503. return { vbsize: vbsize*2, ibsize: ibsize };
  504. }
  505. let v = Vector2.Zero();
  506. let storeVertex = (vb: Float32Array, baseOffset: number, index: number, basePos: Vector2, rotation: number, vertex: Vector2, contour: number[]): number => {
  507. let c = Math.cos(rotation);
  508. let s = Math.sin(rotation);
  509. v.x = (c * vertex.x) + (-s * vertex.y) + basePos.x;
  510. v.y = (s * vertex.x) + ( c * vertex.y) + basePos.y;
  511. let offset = baseOffset + (index*2);
  512. vb[offset + 0] = v.x;
  513. vb[offset + 1] = v.y;
  514. if (contour) {
  515. contour.push(v.x);
  516. contour.push(v.y);
  517. }
  518. updateMinMax(vb, offset);
  519. return (baseOffset + index*2) / 2;
  520. }
  521. let storeIndex = (ib: Float32Array, baseOffset: number, index: number, vertexIndex: number) => {
  522. ib[baseOffset + index] = vertexIndex;
  523. }
  524. let buildCap = (vb: Float32Array, vbi: number, ib: Float32Array, ibi: number, pos: Vector2, thickness: number, borderThickness: number, type: number, capDir: Vector2, contour: number[]): { vbsize: number; ibsize: number } => {
  525. // 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
  526. let dir = new Vector2(1, 0);
  527. let angle = Math.atan2(capDir.y, capDir.x) - Math.atan2(dir.y, dir.x);
  528. let ht = thickness / 2;
  529. let t = thickness;
  530. let borderMode = borderThickness != null;
  531. let bt = borderThickness;
  532. switch (type) {
  533. case Lines2D.NoCap:
  534. if (borderMode && !this.closed) {
  535. let vi = 0;
  536. let ii = 0;
  537. let v1 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, ht + bt), contour);
  538. let v2 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(bt, ht + bt), contour);
  539. let v3 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(bt, -(ht + bt)), contour);
  540. let v4 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -(ht + bt)), contour);
  541. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v3);
  542. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v4);
  543. }
  544. break;
  545. case Lines2D.ArrowCap:
  546. ht *= 2;
  547. case Lines2D.TriangleCap:
  548. {
  549. if (borderMode) {
  550. let f = type===Lines2D.TriangleCap ? bt : Math.sqrt(bt * bt * 2);
  551. let v1 = storeVertex(vb, vbi, 0, pos, angle, new Vector2(0, ht), null);
  552. let v2 = storeVertex(vb, vbi, 1, pos, angle, new Vector2(ht, 0), null);
  553. let v3 = storeVertex(vb, vbi, 2, pos, angle, new Vector2(0, -ht), null);
  554. let v4 = storeVertex(vb, vbi, 3, pos, angle, new Vector2(0, ht + f), contour);
  555. let v5 = storeVertex(vb, vbi, 4, pos, angle, new Vector2(ht + f, 0), contour);
  556. let v6 = storeVertex(vb, vbi, 5, pos, angle, new Vector2(0, -(ht + f)), contour);
  557. let ii = 0;
  558. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v4); storeIndex(ib, ibi, ii++, v5);
  559. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v5); storeIndex(ib, ibi, ii++, v2);
  560. storeIndex(ib, ibi, ii++, v6); storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v2);
  561. storeIndex(ib, ibi, ii++, v6); storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v5);
  562. if (type === Lines2D.ArrowCap) {
  563. let rht = thickness / 2;
  564. let v10 = storeVertex(vb, vbi, 9, pos, angle, new Vector2(0, -(rht + bt)), null);
  565. let v12 = storeVertex(vb, vbi, 11, pos, angle, new Vector2(-bt, -(ht + f)), contour);
  566. let v11 = storeVertex(vb, vbi, 10, pos, angle, new Vector2(-bt, -(rht + bt)), contour);
  567. let v7 = storeVertex(vb, vbi, 6, pos, angle, new Vector2(0, rht + bt), null);
  568. let v8 = storeVertex(vb, vbi, 7, pos, angle, new Vector2(-bt, rht + bt), contour);
  569. let v9 = storeVertex(vb, vbi, 8, pos, angle, new Vector2(-bt, ht + f), contour);
  570. storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v8); storeIndex(ib, ibi, ii++, v9);
  571. storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v9); storeIndex(ib, ibi, ii++, v4);
  572. storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v12); storeIndex(ib, ibi, ii++, v11);
  573. storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v6); storeIndex(ib, ibi, ii++, v12);
  574. }
  575. } else {
  576. let v1 = storeVertex(vb, vbi, 0, pos, angle, new Vector2(0, ht), contour);
  577. let v2 = storeVertex(vb, vbi, 1, pos, angle, new Vector2(ht, 0), contour);
  578. let v3 = storeVertex(vb, vbi, 2, pos, angle, new Vector2(0, -ht), contour);
  579. storeIndex(ib, ibi, 0, v1);
  580. storeIndex(ib, ibi, 1, v2);
  581. storeIndex(ib, ibi, 2, v3);
  582. }
  583. break;
  584. }
  585. case Lines2D.RoundCap:
  586. {
  587. if (borderMode) {
  588. let curA = -Math.PI / 2;
  589. let incA = Math.PI / (sd / 2 - 1);
  590. let ii = 0;
  591. for (let i = 0; i < (sd / 2); i++) {
  592. let v1 = storeVertex(vb, vbi, i*2 + 0, pos, angle, new Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht), null);
  593. let v2 = storeVertex(vb, vbi, i * 2 + 1, pos, angle, new Vector2(Math.cos(curA) * (ht + bt), Math.sin(curA) * (ht + bt)), contour);
  594. if (i > 0) {
  595. storeIndex(ib, ibi, ii++, v1 - 2);
  596. storeIndex(ib, ibi, ii++, v2 - 2);
  597. storeIndex(ib, ibi, ii++, v2);
  598. storeIndex(ib, ibi, ii++, v1 - 2);
  599. storeIndex(ib, ibi, ii++, v2);
  600. storeIndex(ib, ibi, ii++, v1);
  601. }
  602. curA += incA;
  603. }
  604. } else {
  605. let c = storeVertex(vb, vbi, 0, pos, angle, new Vector2(0, 0), null);
  606. let curA = -Math.PI / 2;
  607. let incA = Math.PI / (sd / 2 - 1);
  608. storeVertex(vb, vbi, 1, pos, angle, new Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht), null);
  609. curA += incA;
  610. for (let i = 1; i < (sd / 2); i++) {
  611. let v2 = storeVertex(vb, vbi, i + 1, pos, angle, new Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht), contour);
  612. storeIndex(ib, ibi, i * 3 + 0, c);
  613. storeIndex(ib, ibi, i * 3 + 1, v2 - 1);
  614. storeIndex(ib, ibi, i * 3 + 2, v2);
  615. curA += incA;
  616. }
  617. }
  618. break;
  619. }
  620. case Lines2D.SquareAnchorCap:
  621. {
  622. let vi = 0;
  623. let c = borderMode ? null : contour;
  624. let v1 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, t), c);
  625. let v2 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2, t), c);
  626. let v3 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2, -t), c);
  627. let v4 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -t), c);
  628. if (borderMode) {
  629. let v5 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, ht+bt), null);
  630. let v6 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, ht+bt), contour);
  631. let v7 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, t + bt), contour);
  632. let v8 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2 + bt, t + bt), contour);
  633. let v9 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2 + bt, -(t + bt)), contour);
  634. let v10 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, -(t + bt)), contour);
  635. let v11 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, -(ht + bt)), contour);
  636. let v12 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -(ht+bt)), null);
  637. let ii = 0;
  638. storeIndex(ib, ibi, ii++, v6); storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v5);
  639. storeIndex(ib, ibi, ii++, v6); storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v1);
  640. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v8);
  641. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v8); storeIndex(ib, ibi, ii++, v2);
  642. storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v8); storeIndex(ib, ibi, ii++, v9);
  643. storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v9); storeIndex(ib, ibi, ii++, v3);
  644. storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v9); storeIndex(ib, ibi, ii++, v10);
  645. storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v4);
  646. storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v11); storeIndex(ib, ibi, ii++, v4);
  647. storeIndex(ib, ibi, ii++, v11); storeIndex(ib, ibi, ii++, v12); storeIndex(ib, ibi, ii++, v4);
  648. } else {
  649. storeIndex(ib, ibi, 0, v1);
  650. storeIndex(ib, ibi, 1, v2);
  651. storeIndex(ib, ibi, 2, v3);
  652. storeIndex(ib, ibi, 3, v1);
  653. storeIndex(ib, ibi, 4, v3);
  654. storeIndex(ib, ibi, 5, v4);
  655. }
  656. break;
  657. }
  658. case Lines2D.RoundAnchorCap:
  659. {
  660. let cpos = Math.sqrt(t * t - ht * ht);
  661. let center = new Vector2(cpos, 0);
  662. let curA = Tools.ToRadians(-150);
  663. let incA = Tools.ToRadians(300) / (sd - 1);
  664. if (borderMode) {
  665. let ii = 0;
  666. for (let i = 0; i < sd; i++) {
  667. let v1 = storeVertex(vb, vbi, i * 2 + 0, pos, angle, new Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t), null);
  668. let v2 = storeVertex(vb, vbi, i * 2 + 1, pos, angle, new Vector2(cpos + Math.cos(curA) * (t + bt), Math.sin(curA) * (t + bt)), contour);
  669. if (i > 0) {
  670. storeIndex(ib, ibi, ii++, v1 - 2);
  671. storeIndex(ib, ibi, ii++, v2 - 2);
  672. storeIndex(ib, ibi, ii++, v2);
  673. storeIndex(ib, ibi, ii++, v1 - 2);
  674. storeIndex(ib, ibi, ii++, v2);
  675. storeIndex(ib, ibi, ii++, v1);
  676. }
  677. curA += incA;
  678. }
  679. } else {
  680. let c = storeVertex(vb, vbi, 0, pos, angle, center, null);
  681. storeVertex(vb, vbi, 1, pos, angle, new Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t), null); // contour maybe TODO
  682. curA += incA;
  683. for (let i = 1; i < sd; i++) {
  684. let v2 = storeVertex(vb, vbi, i + 1, pos, angle, new Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t), contour);
  685. storeIndex(ib, ibi, i * 3 + 0, c);
  686. storeIndex(ib, ibi, i * 3 + 1, v2 - 1);
  687. storeIndex(ib, ibi, i * 3 + 2, v2);
  688. curA += incA;
  689. }
  690. storeIndex(ib, ibi, sd * 3 + 0, c);
  691. storeIndex(ib, ibi, sd * 3 + 1, c + 1);
  692. storeIndex(ib, ibi, sd * 3 + 2, c + sd);
  693. }
  694. break;
  695. }
  696. case Lines2D.DiamondAnchorCap:
  697. {
  698. let vi = 0;
  699. let c = borderMode ? null : contour;
  700. let v1 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, ht), c);
  701. let v2 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht, t), c);
  702. let v3 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht * 3, 0), c);
  703. let v4 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht, -t), c);
  704. let v5 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -ht), c);
  705. if (borderMode) {
  706. let f = Math.sqrt(bt * bt * 2);
  707. let v6 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-f, ht), contour);
  708. let v7 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht, t + f), contour);
  709. let v8 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht * 3 + f, 0), contour);
  710. let v9 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht, -(t + f)), contour);
  711. let v10 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-f, -ht), contour);
  712. let ii = 0;
  713. storeIndex(ib, ibi, ii++, v6); storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v1);
  714. storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v2);
  715. storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v8);
  716. storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v8); storeIndex(ib, ibi, ii++, v3);
  717. storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v8); storeIndex(ib, ibi, ii++, v9);
  718. storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v9); storeIndex(ib, ibi, ii++, v4);
  719. storeIndex(ib, ibi, ii++, v4); storeIndex(ib, ibi, ii++, v9); storeIndex(ib, ibi, ii++, v10);
  720. storeIndex(ib, ibi, ii++, v4); storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v5);
  721. } else {
  722. storeIndex(ib, ibi, 0, v1); storeIndex(ib, ibi, 1, v2); storeIndex(ib, ibi, 2, v3);
  723. storeIndex(ib, ibi, 3, v1); storeIndex(ib, ibi, 4, v3); storeIndex(ib, ibi, 5, v5);
  724. storeIndex(ib, ibi, 6, v5); storeIndex(ib, ibi, 7, v3); storeIndex(ib, ibi, 8, v4);
  725. }
  726. break;
  727. }
  728. }
  729. return null;
  730. }
  731. let buildLine = (vb: Float32Array, contour: Vector2[], ht: number, bt?: number) => {
  732. let lineA = Vector2.Zero();
  733. let lineB = Vector2.Zero();
  734. let tangent = Vector2.Zero();
  735. let miter = Vector2.Zero();
  736. let curNormal: Vector2 = null;
  737. if (this.closed) {
  738. this.points.push(this.points[0]);
  739. }
  740. var total = this.points.length;
  741. for (let i = 1; i < total; i++) {
  742. let last = this.points[i - 1];
  743. let cur = this.points[i];
  744. let next = (i < (this.points.length - 1)) ? this.points[i + 1] : null;
  745. direction(cur, last, lineA);
  746. if (!curNormal) {
  747. curNormal = Vector2.Zero();
  748. perp(lineA, curNormal);
  749. }
  750. if (i === 1) {
  751. store(vb, contour, 0, total, this.points[0], curNormal, ht, bt);
  752. }
  753. if (!next) {
  754. perp(lineA, curNormal);
  755. store(vb, contour, i, total, this.points[i], curNormal, ht, bt, i - 1);
  756. } else {
  757. direction(next, cur, lineB);
  758. var miterLen = computeMiter(tangent, miter, lineA, lineB);
  759. store(vb, contour, i, total, this.points[i], miter, miterLen*ht, miterLen*bt, i - 1);
  760. }
  761. }
  762. if (this.points.length > 2 && this.closed) {
  763. let last2 = this.points[total - 2];
  764. let cur2 = this.points[0];
  765. let next2 = this.points[1];
  766. direction(cur2, last2, lineA);
  767. direction(next2, cur2, lineB);
  768. perp(lineA, curNormal);
  769. var miterLen2 = computeMiter(tangent, miter, lineA, lineB);
  770. store(vb, null, 0, total, this.points[0], miter, miterLen2 * ht, miterLen2 * bt, 1);
  771. // Patch contour
  772. if (contour) {
  773. let off = (bt == null) ? 0 : 4;
  774. contour[0].x = vb[off + 0];
  775. contour[0].y = vb[off + 1];
  776. contour[1].x = vb[off + 2];
  777. contour[1].y = vb[off + 3];
  778. }
  779. }
  780. // Remove the point we added at the beginning
  781. if (this.closed) {
  782. this.points.splice(total - 1);
  783. }
  784. }
  785. let contour = new Array<Vector2>();
  786. let startCapContour = new Array<number>();
  787. let endCapContour = new Array<number>();
  788. // Need to create WebGL resources for fill part?
  789. if (this.fill) {
  790. let startCapInfo = getCapSize(this.startCap);
  791. let endCapInfo = getCapSize(this.endCap);
  792. let count = this.points.length;
  793. let vbSize = (count * 2 * 2) + startCapInfo.vbsize + endCapInfo.vbsize;
  794. let vb = new Float32Array(vbSize);
  795. let ht = this.fillThickness / 2;
  796. let total = this.points.length;
  797. buildLine(vb, this.border ? null : contour, ht);
  798. let max = total * 2;
  799. let triCount = (count - (this.closed ? 0 : 1)) * 2;
  800. let ib = new Float32Array(triCount * 3 + startCapInfo.ibsize + endCapInfo.ibsize);
  801. for (let i = 0; i < triCount; i+=2) {
  802. ib[i * 3 + 0] = i + 0;
  803. ib[i * 3 + 1] = i + 1;
  804. ib[i * 3 + 2] = (i + 2) % max;
  805. ib[i * 3 + 3] = i + 1;
  806. ib[i * 3 + 4] = (i + 3) % max;
  807. ib[i * 3 + 5] = (i + 2) % max;
  808. }
  809. buildCap(vb, count * 2 * 2, ib, triCount * 3, this.points[0], this.fillThickness, null, this.startCap, startDir, this.border ? null : startCapContour);
  810. buildCap(vb, (count * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, null, this.endCap, endDir, this.border ? null : startCapContour);
  811. renderCache.fillVB = engine.createVertexBuffer(vb);
  812. renderCache.fillIB = engine.createIndexBuffer(ib);
  813. renderCache.fillIndicesCount = ib.length;
  814. // Get the instanced version of the effect, if the engine does not support it, null is return and we'll only draw on by one
  815. let ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_FILLPARTID, ["position"], true);
  816. if (ei) {
  817. renderCache.effectFillInstanced = engine.createEffect("lines2d", ei.attributes, ei.uniforms, [], ei.defines, null);
  818. }
  819. // Get the non instanced version
  820. ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_FILLPARTID, ["position"], false);
  821. renderCache.effectFill = engine.createEffect("lines2d", ei.attributes, ei.uniforms, [], ei.defines, null);
  822. }
  823. // Need to create WebGL resources for border part?
  824. if (this.border) {
  825. let startCapInfo = getCapSize(this.startCap, true);
  826. let endCapInfo = getCapSize(this.endCap, true);
  827. let count = this.points.length;
  828. let vbSize = (count * 2 * 2 * 2) + startCapInfo.vbsize + endCapInfo.vbsize;
  829. let vb = new Float32Array(vbSize);
  830. let ht = this.fillThickness / 2;
  831. let bt = this.borderThickness;
  832. let total = this.points.length;
  833. buildLine(vb, contour, ht, bt);
  834. let max = total * 2 * 2;
  835. let triCount = (count - (this.closed ? 0 : 1)) * 2 * 2;
  836. let ib = new Float32Array(triCount * 3 + startCapInfo.ibsize + endCapInfo.ibsize);
  837. for (let i = 0; i < triCount; i += 4) {
  838. ib[i * 3 + 0] = i + 0;
  839. ib[i * 3 + 1] = i + 2;
  840. ib[i * 3 + 2] = (i + 6) % max;
  841. ib[i * 3 + 3] = i + 0;
  842. ib[i * 3 + 4] = (i + 6) % max;
  843. ib[i * 3 + 5] = (i + 4) % max;
  844. ib[i * 3 + 6] = i + 3;
  845. ib[i * 3 + 7] = i + 1;
  846. ib[i * 3 + 8] = (i + 5) % max;
  847. ib[i * 3 + 9] = i + 3;
  848. ib[i * 3 + 10] = (i + 5) % max;
  849. ib[i * 3 + 11] = (i + 7) % max;
  850. }
  851. buildCap(vb, count * 2 * 2 * 2, ib, triCount * 3, this.points[0], this.fillThickness, this.borderThickness, this.startCap, startDir, startCapContour);
  852. buildCap(vb, (count * 2 * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, this.borderThickness, this.endCap, endDir, endCapContour);
  853. renderCache.borderVB = engine.createVertexBuffer(vb);
  854. renderCache.borderIB = engine.createIndexBuffer(ib);
  855. renderCache.borderIndicesCount = ib.length;
  856. // Get the instanced version of the effect, if the engine does not support it, null is return and we'll only draw on by one
  857. let ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_BORDERPARTID, ["position"], true);
  858. if (ei) {
  859. renderCache.effectBorderInstanced = engine.createEffect({ vertex: "lines2d", fragment: "lines2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
  860. }
  861. // Get the non instanced version
  862. ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_BORDERPARTID, ["position"], false);
  863. renderCache.effectBorder = engine.createEffect({ vertex: "lines2d", fragment: "lines2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
  864. }
  865. this._contour = contour;
  866. if (startCapContour.length > 0) {
  867. let startCapTri = Earcut.earcut(startCapContour, null, 2);
  868. this._startCapTriIndices = startCapTri;
  869. this._startCapContour = startCapContour;
  870. }
  871. if (endCapContour.length > 0) {
  872. let endCapTri = Earcut.earcut(endCapContour, null, 2);
  873. this._endCapContour = endCapContour;
  874. this._endCapTriIndices = endCapTri;
  875. }
  876. let bs = this._boundingMax.subtract(this._boundingMin);
  877. this._size.width = bs.x;
  878. this._size.height = bs.y;
  879. return renderCache;
  880. }
  881. protected createInstanceDataParts(): InstanceDataBase[] {
  882. var res = new Array<InstanceDataBase>();
  883. if (this.border) {
  884. res.push(new Lines2DInstanceData(Shape2D.SHAPE2D_BORDERPARTID));
  885. }
  886. if (this.fill) {
  887. res.push(new Lines2DInstanceData(Shape2D.SHAPE2D_FILLPARTID));
  888. }
  889. return res;
  890. }
  891. protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
  892. if (!super.refreshInstanceDataPart(part)) {
  893. return false;
  894. }
  895. if (part.id === Shape2D.SHAPE2D_BORDERPARTID) {
  896. let d = <Lines2DInstanceData>part;
  897. d.boundingMin = this.boundingMin;
  898. d.boundingMax = this.boundingMax;
  899. }
  900. else if (part.id === Shape2D.SHAPE2D_FILLPARTID) {
  901. let d = <Lines2DInstanceData>part;
  902. d.boundingMin = this.boundingMin;
  903. d.boundingMax = this.boundingMax;
  904. }
  905. return true;
  906. }
  907. private static _noCap = 0;
  908. private static _roundCap = 1;
  909. private static _triangleCap = 2;
  910. private static _squareAnchorCap = 3;
  911. private static _roundAnchorCap = 4;
  912. private static _diamondAnchorCap = 5;
  913. private static _arrowCap = 6;
  914. private static _roundCapSubDiv = 36;
  915. private _boundingMin: Vector2;
  916. private _boundingMax: Vector2;
  917. private _size: Size;
  918. private _contour: Vector2[];
  919. private _startCapContour: number[];
  920. private _startCapTriIndices: number[];
  921. private _endCapContour: number[];
  922. private _endCapTriIndices: number[];
  923. private _closed: boolean;
  924. private _startCap: number;
  925. private _endCap: number;
  926. private _fillThickness: number;
  927. private _points: Vector2[];
  928. }
  929. }