babylon.prim2dBase.ts 186 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600
  1. module BABYLON {
  2. export class PrepareRender2DContext {
  3. constructor() {
  4. this.forceRefreshPrimitive = false;
  5. }
  6. /**
  7. * True if the primitive must be refreshed no matter what
  8. * This mode is needed because sometimes the primitive doesn't change by itself, but external changes make a refresh of its InstanceData necessary
  9. */
  10. forceRefreshPrimitive: boolean;
  11. }
  12. export class Render2DContext {
  13. constructor(renderMode: number) {
  14. this._renderMode = renderMode;
  15. this.useInstancing = false;
  16. this.groupInfoPartData = null;
  17. this.partDataStartIndex = this.partDataEndIndex = null;
  18. this.instancedBuffers = null;
  19. }
  20. /**
  21. * Define which render Mode should be used to render the primitive: one of Render2DContext.RenderModeXxxx property
  22. */
  23. get renderMode(): number {
  24. return this._renderMode;
  25. }
  26. /**
  27. * If true hardware instancing is supported and must be used for the rendering. The groupInfoPartData._partBuffer must be used.
  28. * If false rendering on a per primitive basis must be made. The following properties must be used
  29. * - groupInfoPartData._partData: contains the primitive instances data to render
  30. * - partDataStartIndex: the index into instanceArrayData of the first instance to render.
  31. * - partDataCount: the number of primitive to render
  32. */
  33. useInstancing: boolean;
  34. /**
  35. * If specified, must take precedence from the groupInfoPartData. partIndex is the same as groupInfoPardData
  36. */
  37. instancedBuffers: WebGLBuffer[];
  38. /**
  39. * To use when instancedBuffers is specified, gives the count of instances to draw
  40. */
  41. instancesCount: number;
  42. /**
  43. * Contains the data related to the primitives instances to render
  44. */
  45. groupInfoPartData: GroupInfoPartData[];
  46. /**
  47. * The index into groupInfoPartData._partData of the first primitive to render. This is an index, not an offset: it represent the nth primitive which is the first to render.
  48. */
  49. partDataStartIndex: number;
  50. /**
  51. * The exclusive end index, you have to render the primitive instances until you reach this one, but don't render this one!
  52. */
  53. partDataEndIndex: number;
  54. /**
  55. * The set of primitives to render is opaque.
  56. * This is the first rendering pass. All Opaque primitives are rendered. Depth Compare and Write are both enabled.
  57. */
  58. public static get RenderModeOpaque(): number {
  59. return Render2DContext._renderModeOpaque;
  60. }
  61. /**
  62. * The set of primitives to render is using Alpha Test (aka masking).
  63. * Alpha Blend is enabled, the AlphaMode must be manually set, the render occurs after the RenderModeOpaque and is depth independent (i.e. primitives are not sorted by depth). Depth Compare and Write are both enabled.
  64. */
  65. public static get RenderModeAlphaTest(): number {
  66. return Render2DContext._renderModeAlphaTest;
  67. }
  68. /**
  69. * The set of primitives to render is transparent.
  70. * Alpha Blend is enabled, the AlphaMode must be manually set, the render occurs after the RenderModeAlphaTest and is depth dependent (i.e. primitives are stored by depth and rendered back to front). Depth Compare is on, but Depth write is Off.
  71. */
  72. public static get RenderModeTransparent(): number {
  73. return Render2DContext._renderModeTransparent;
  74. }
  75. private static _renderModeOpaque: number = 1;
  76. private static _renderModeAlphaTest: number = 2;
  77. private static _renderModeTransparent: number = 3;
  78. private _renderMode: number;
  79. }
  80. /**
  81. * This class store information for the pointerEventObservable Observable.
  82. * The Observable is divided into many sub events (using the Mask feature of the Observable pattern): PointerOver, PointerEnter, PointerDown, PointerMouseWheel, PointerMove, PointerUp, PointerDown, PointerLeave, PointerGotCapture and PointerLostCapture.
  83. */
  84. export class PrimitivePointerInfo {
  85. private static _pointerOver = 0x0001;
  86. private static _pointerEnter = 0x0002;
  87. private static _pointerDown = 0x0004;
  88. private static _pointerMouseWheel = 0x0008;
  89. private static _pointerMove = 0x0010;
  90. private static _pointerUp = 0x0020;
  91. private static _pointerOut = 0x0040;
  92. private static _pointerLeave = 0x0080;
  93. private static _pointerGotCapture = 0x0100;
  94. private static _pointerLostCapture = 0x0200;
  95. private static _mouseWheelPrecision = 3.0;
  96. // The behavior is based on the HTML specifications of the Pointer Events (https://www.w3.org/TR/pointerevents/#list-of-pointer-events). This is not 100% compliant and not meant to be, but still, it's based on these specs for most use cases to be programmed the same way (as closest as possible) as it would have been in HTML.
  97. /**
  98. * This event type is raised when a pointing device is moved into the hit test boundaries of a primitive.
  99. * Bubbles: yes
  100. */
  101. public static get PointerOver(): number {
  102. return PrimitivePointerInfo._pointerOver;
  103. }
  104. /**
  105. * This event type is raised when a pointing device is moved into the hit test boundaries of a primitive or one of its descendants.
  106. * Bubbles: no
  107. */
  108. public static get PointerEnter(): number {
  109. return PrimitivePointerInfo._pointerEnter;
  110. }
  111. /**
  112. * This event type is raised when a pointer enters the active button state (non-zero value in the buttons property). For mouse it's when the device transitions from no buttons depressed to at least one button depressed. For touch/pen this is when a physical contact is made.
  113. * Bubbles: yes
  114. */
  115. public static get PointerDown(): number {
  116. return PrimitivePointerInfo._pointerDown;
  117. }
  118. /**
  119. * This event type is raised when the pointer is a mouse and it's wheel is rolling
  120. * Bubbles: yes
  121. */
  122. public static get PointerMouseWheel(): number {
  123. return PrimitivePointerInfo._pointerMouseWheel;
  124. }
  125. /**
  126. * This event type is raised when a pointer change coordinates or when a pointer changes button state, pressure, tilt, or contact geometry and the circumstances produce no other pointers events.
  127. * Bubbles: yes
  128. */
  129. public static get PointerMove(): number {
  130. return PrimitivePointerInfo._pointerMove;
  131. }
  132. /**
  133. * This event type is raised when the pointer leaves the active buttons states (zero value in the buttons property). For mouse, this is when the device transitions from at least one button depressed to no buttons depressed. For touch/pen, this is when physical contact is removed.
  134. * Bubbles: yes
  135. */
  136. public static get PointerUp(): number {
  137. return PrimitivePointerInfo._pointerUp;
  138. }
  139. /**
  140. * This event type is raised when a pointing device is moved out of the hit test the boundaries of a primitive.
  141. * Bubbles: yes
  142. */
  143. public static get PointerOut(): number {
  144. return PrimitivePointerInfo._pointerOut;
  145. }
  146. /**
  147. * This event type is raised when a pointing device is moved out of the hit test boundaries of a primitive and all its descendants.
  148. * Bubbles: no
  149. */
  150. public static get PointerLeave(): number {
  151. return PrimitivePointerInfo._pointerLeave;
  152. }
  153. /**
  154. * This event type is raised when a primitive receives the pointer capture. This event is fired at the element that is receiving pointer capture. Subsequent events for that pointer will be fired at this element.
  155. * Bubbles: yes
  156. */
  157. public static get PointerGotCapture(): number {
  158. return PrimitivePointerInfo._pointerGotCapture;
  159. }
  160. /**
  161. * This event type is raised after pointer capture is released for a pointer.
  162. * Bubbles: yes
  163. */
  164. public static get PointerLostCapture(): number {
  165. return PrimitivePointerInfo._pointerLostCapture;
  166. }
  167. public static get MouseWheelPrecision(): number {
  168. return PrimitivePointerInfo._mouseWheelPrecision;
  169. }
  170. /**
  171. * Event Type, one of the static PointerXXXX property defined above (PrimitivePointerInfo.PointerOver to PrimitivePointerInfo.PointerLostCapture)
  172. */
  173. eventType: number;
  174. /**
  175. * Position of the pointer relative to the bottom/left of the Canvas
  176. */
  177. canvasPointerPos: Vector2;
  178. /**
  179. * Position of the pointer relative to the bottom/left of the primitive that registered the Observer
  180. */
  181. primitivePointerPos: Vector2;
  182. /**
  183. * The primitive where the event was initiated first (in case of bubbling)
  184. */
  185. relatedTarget: Prim2DBase;
  186. /**
  187. * Position of the pointer relative to the bottom/left of the relatedTarget
  188. */
  189. relatedTargetPointerPos: Vector2;
  190. /**
  191. * An observable can set this property to true to stop bubbling on the upper levels
  192. */
  193. cancelBubble: boolean;
  194. /**
  195. * True if the Control keyboard key is down
  196. */
  197. ctrlKey: boolean;
  198. /**
  199. * true if the Shift keyboard key is down
  200. */
  201. shiftKey: boolean;
  202. /**
  203. * true if the Alt keyboard key is down
  204. */
  205. altKey: boolean;
  206. /**
  207. * true if the Meta keyboard key is down
  208. */
  209. metaKey: boolean;
  210. /**
  211. * For button, buttons, refer to https://www.w3.org/TR/pointerevents/#button-states
  212. */
  213. button: number;
  214. /**
  215. * For button, buttons, refer to https://www.w3.org/TR/pointerevents/#button-states
  216. */
  217. buttons: number;
  218. /**
  219. * The amount of mouse wheel rolled
  220. */
  221. mouseWheelDelta: number;
  222. /**
  223. * Id of the Pointer involved in the event
  224. */
  225. pointerId: number;
  226. width: number;
  227. height: number;
  228. presssure: number;
  229. tilt: Vector2;
  230. /**
  231. * true if the involved pointer is captured for a particular primitive, false otherwise.
  232. */
  233. isCaptured: boolean;
  234. constructor() {
  235. this.primitivePointerPos = Vector2.Zero();
  236. this.tilt = Vector2.Zero();
  237. this.cancelBubble = false;
  238. }
  239. updateRelatedTarget(prim: Prim2DBase, primPointerPos: Vector2) {
  240. this.relatedTarget = prim;
  241. this.relatedTargetPointerPos = primPointerPos;
  242. }
  243. public static getEventTypeName(mask: number): string {
  244. switch (mask) {
  245. case PrimitivePointerInfo.PointerOver: return "PointerOver";
  246. case PrimitivePointerInfo.PointerEnter: return "PointerEnter";
  247. case PrimitivePointerInfo.PointerDown: return "PointerDown";
  248. case PrimitivePointerInfo.PointerMouseWheel: return "PointerMouseWheel";
  249. case PrimitivePointerInfo.PointerMove: return "PointerMove";
  250. case PrimitivePointerInfo.PointerUp: return "PointerUp";
  251. case PrimitivePointerInfo.PointerOut: return "PointerOut";
  252. case PrimitivePointerInfo.PointerLeave: return "PointerLeave";
  253. case PrimitivePointerInfo.PointerGotCapture: return "PointerGotCapture";
  254. case PrimitivePointerInfo.PointerLostCapture: return "PointerLostCapture";
  255. }
  256. }
  257. }
  258. /**
  259. * Defines the horizontal and vertical alignment information for a Primitive.
  260. */
  261. @className("PrimitiveAlignment", "BABYLON")
  262. export class PrimitiveAlignment {
  263. constructor(changeCallback?: () => void) {
  264. this._changedCallback = changeCallback;
  265. this._horizontal = PrimitiveAlignment.AlignLeft;
  266. this._vertical = PrimitiveAlignment.AlignBottom;
  267. }
  268. /**
  269. * Alignment is made relative to the left edge of the Primitive. Valid for horizontal alignment only.
  270. */
  271. public static get AlignLeft(): number { return PrimitiveAlignment._AlignLeft; }
  272. /**
  273. * Alignment is made relative to the top edge of the Primitive. Valid for vertical alignment only.
  274. */
  275. public static get AlignTop(): number { return PrimitiveAlignment._AlignTop; }
  276. /**
  277. * Alignment is made relative to the right edge of the Primitive. Valid for horizontal alignment only.
  278. */
  279. public static get AlignRight(): number { return PrimitiveAlignment._AlignRight; }
  280. /**
  281. * Alignment is made relative to the bottom edge of the Primitive. Valid for vertical alignment only.
  282. */
  283. public static get AlignBottom(): number { return PrimitiveAlignment._AlignBottom; }
  284. /**
  285. * Alignment is made to center the content from equal distance to the opposite edges of the Primitive
  286. */
  287. public static get AlignCenter(): number { return PrimitiveAlignment._AlignCenter; }
  288. /**
  289. * The content is stretched toward the opposite edges of the Primitive
  290. */
  291. public static get AlignStretch(): number { return PrimitiveAlignment._AlignStretch; }
  292. private static _AlignLeft = 1;
  293. private static _AlignTop = 1; // Same as left
  294. private static _AlignRight = 2;
  295. private static _AlignBottom = 2; // Same as right
  296. private static _AlignCenter = 3;
  297. private static _AlignStretch = 4;
  298. /**
  299. * Get/set the horizontal alignment. Use one of the AlignXXX static properties of this class
  300. */
  301. public get horizontal(): number {
  302. return this._horizontal;
  303. }
  304. public set horizontal(value: number) {
  305. if (this._horizontal === value) {
  306. return;
  307. }
  308. this._horizontal = value;
  309. this.onChangeCallback();
  310. }
  311. /**
  312. * Get/set the vertical alignment. Use one of the AlignXXX static properties of this class
  313. */
  314. public get vertical(): number {
  315. return this._vertical;
  316. }
  317. public set vertical(value: number) {
  318. if (this._vertical === value) {
  319. return;
  320. }
  321. this._vertical = value;
  322. this.onChangeCallback();
  323. }
  324. private onChangeCallback() {
  325. if (this._changedCallback) {
  326. this._changedCallback();
  327. }
  328. }
  329. private _changedCallback: () => void;
  330. private _horizontal: number;
  331. private _vertical: number;
  332. /**
  333. * Set the horizontal alignment from a string value.
  334. * @param text can be either: 'left','right','center','stretch'
  335. */
  336. setHorizontal(text: string) {
  337. let v = text.trim().toLocaleLowerCase();
  338. switch (v) {
  339. case "left":
  340. this.horizontal = PrimitiveAlignment.AlignLeft;
  341. return;
  342. case "right":
  343. this.horizontal = PrimitiveAlignment.AlignRight;
  344. return;
  345. case "center":
  346. this.horizontal = PrimitiveAlignment.AlignCenter;
  347. return;
  348. case "stretch":
  349. this.horizontal = PrimitiveAlignment.AlignStretch;
  350. return;
  351. }
  352. }
  353. /**
  354. * Set the vertical alignment from a string value.
  355. * @param text can be either: 'top','bottom','center','stretch'
  356. */
  357. setVertical(text: string) {
  358. let v = text.trim().toLocaleLowerCase();
  359. switch (v) {
  360. case "top":
  361. this.vertical = PrimitiveAlignment.AlignTop;
  362. return;
  363. case "bottom":
  364. this.vertical = PrimitiveAlignment.AlignBottom;
  365. return;
  366. case "center":
  367. this.vertical = PrimitiveAlignment.AlignCenter;
  368. return;
  369. case "stretch":
  370. this.vertical = PrimitiveAlignment.AlignStretch;
  371. return;
  372. }
  373. }
  374. /**
  375. * Set the horizontal and or vertical alignments from a string value.
  376. * @param text can be: [<h:|horizontal:><left|right|center|stretch>], [<v:|vertical:><top|bottom|center|stretch>]
  377. */
  378. fromString(value: string) {
  379. let m = value.trim().split(",");
  380. if (m.length === 1) {
  381. this.setHorizontal(m[0]);
  382. this.setVertical(m[0]);
  383. } else {
  384. for (let v of m) {
  385. v = v.toLocaleLowerCase().trim();
  386. // Horizontal
  387. let i = v.indexOf("h:");
  388. if (i === -1) {
  389. i = v.indexOf("horizontal:");
  390. }
  391. if (i !== -1) {
  392. v = v.substr(v.indexOf(":") + 1);
  393. this.setHorizontal(v);
  394. continue;
  395. }
  396. // Vertical
  397. i = v.indexOf("v:");
  398. if (i === -1) {
  399. i = v.indexOf("vertical:");
  400. }
  401. if (i !== -1) {
  402. v = v.substr(v.indexOf(":") + 1);
  403. this.setVertical(v);
  404. continue;
  405. }
  406. }
  407. }
  408. }
  409. copyFrom(pa: PrimitiveAlignment) {
  410. this._horizontal = pa._horizontal;
  411. this._vertical = pa._vertical;
  412. this.onChangeCallback();
  413. }
  414. clone(): PrimitiveAlignment {
  415. let pa = new PrimitiveAlignment();
  416. pa._horizontal = this._horizontal;
  417. pa._vertical = this._vertical;
  418. return pa;
  419. }
  420. public get isDefault(): boolean {
  421. return this.horizontal === PrimitiveAlignment.AlignLeft && this.vertical === PrimitiveAlignment.AlignBottom;
  422. }
  423. }
  424. /**
  425. * Stores information about a Primitive that was intersected
  426. */
  427. export class PrimitiveIntersectedInfo {
  428. constructor(public prim: Prim2DBase, public intersectionLocation: Vector2) {
  429. }
  430. }
  431. /**
  432. * Define a thickness toward every edges of a Primitive to allow margin and padding.
  433. * The thickness can be expressed as pixels, percentages, inherit the value of the parent primitive or be auto.
  434. */
  435. @className("PrimitiveThickness", "BABYLON")
  436. export class PrimitiveThickness {
  437. constructor(parentAccess: () => PrimitiveThickness, changedCallback?: () => void) {
  438. this._parentAccess = parentAccess;
  439. this._changedCallback = changedCallback;
  440. this._pixels = new Array<number>(4);
  441. this._percentages = new Array<number>(4);
  442. this._setType(0, PrimitiveThickness.Auto);
  443. this._setType(1, PrimitiveThickness.Auto);
  444. this._setType(2, PrimitiveThickness.Auto);
  445. this._setType(3, PrimitiveThickness.Auto);
  446. this._pixels[0] = 0;
  447. this._pixels[1] = 0;
  448. this._pixels[2] = 0;
  449. this._pixels[3] = 0;
  450. }
  451. /**
  452. * Set the thickness from a string value
  453. * @param thickness format is "top: <value>, left:<value>, right:<value>, bottom:<value>" or "<value>" (same for all edges) each are optional, auto will be set if it's omitted.
  454. * Values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels.
  455. */
  456. public fromString(thickness: string) {
  457. this._clear();
  458. let m = thickness.trim().split(",");
  459. // Special case, one value to apply to all edges
  460. if (m.length === 1 && thickness.indexOf(":") === -1) {
  461. this._setStringValue(m[0], 0, false);
  462. this._setStringValue(m[0], 1, false);
  463. this._setStringValue(m[0], 2, false);
  464. this._setStringValue(m[0], 3, false);
  465. this.onChangeCallback();
  466. return;
  467. }
  468. let res = false;
  469. for (let cm of m) {
  470. res = this._extractString(cm, false) || res;
  471. }
  472. if (!res) {
  473. throw new Error("Can't parse the string to create a PrimitiveMargin object, format must be: 'top: <value>, left:<value>, right:<value>, bottom:<value>");
  474. }
  475. // Check the margin that weren't set and set them in auto
  476. if ((this._flags & 0x000F) === 0) this._flags |= PrimitiveThickness.Pixel << 0;
  477. if ((this._flags & 0x00F0) === 0) this._flags |= PrimitiveThickness.Pixel << 4;
  478. if ((this._flags & 0x0F00) === 0) this._flags |= PrimitiveThickness.Pixel << 8;
  479. if ((this._flags & 0xF000) === 0) this._flags |= PrimitiveThickness.Pixel << 12;
  480. this.onChangeCallback();
  481. }
  482. /**
  483. * Set the thickness from multiple string
  484. * Possible values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels.
  485. * @param top the top thickness to set
  486. * @param left the left thickness to set
  487. * @param right the right thickness to set
  488. * @param bottom the bottom thickness to set
  489. */
  490. public fromStrings(top: string, left: string, right: string, bottom: string): PrimitiveThickness {
  491. this._clear();
  492. this._setStringValue(top, 0, false);
  493. this._setStringValue(left, 1, false);
  494. this._setStringValue(right, 2, false);
  495. this._setStringValue(bottom, 3, false);
  496. this.onChangeCallback();
  497. return this;
  498. }
  499. /**
  500. * Set the thickness from pixel values
  501. * @param top the top thickness in pixels to set
  502. * @param left the left thickness in pixels to set
  503. * @param right the right thickness in pixels to set
  504. * @param bottom the bottom thickness in pixels to set
  505. */
  506. public fromPixels(top: number, left: number, right: number, bottom: number): PrimitiveThickness {
  507. this._clear();
  508. this._pixels[0] = top;
  509. this._pixels[1] = left;
  510. this._pixels[2] = right;
  511. this._pixels[3] = bottom;
  512. this.onChangeCallback();
  513. return this;
  514. }
  515. /**
  516. * Apply the same pixel value to all edges
  517. * @param margin the value to set, in pixels.
  518. */
  519. public fromUniformPixels(margin: number): PrimitiveThickness {
  520. this._clear();
  521. this._pixels[0] = margin;
  522. this._pixels[1] = margin;
  523. this._pixels[2] = margin;
  524. this._pixels[3] = margin;
  525. this.onChangeCallback();
  526. return this;
  527. }
  528. public copyFrom(pt: PrimitiveThickness) {
  529. this._clear();
  530. for (let i = 0; i < 4; i++) {
  531. this._pixels[i] = pt._pixels[i];
  532. this._percentages[i] = pt._percentages[i];
  533. }
  534. this._flags = pt._flags;
  535. this.onChangeCallback();
  536. }
  537. /**
  538. * Set all edges in auto
  539. */
  540. public auto(): PrimitiveThickness {
  541. this._clear();
  542. this._flags = (PrimitiveThickness.Auto << 0) | (PrimitiveThickness.Auto << 4) | (PrimitiveThickness.Auto << 8) | (PrimitiveThickness.Auto << 12);
  543. this._pixels[0] = 0;
  544. this._pixels[1] = 0;
  545. this._pixels[2] = 0;
  546. this._pixels[3] = 0;
  547. this.onChangeCallback();
  548. return this;
  549. }
  550. private _clear() {
  551. this._flags = 0;
  552. this._pixels[0] = 0;
  553. this._pixels[1] = 0;
  554. this._pixels[2] = 0;
  555. this._pixels[3] = 0;
  556. this._percentages[0] = null;
  557. this._percentages[1] = null;
  558. this._percentages[2] = null;
  559. this._percentages[3] = null;
  560. }
  561. private _extractString(value: string, emitChanged: boolean): boolean {
  562. let v = value.trim().toLocaleLowerCase();
  563. if (v.indexOf("top:") === 0) {
  564. v = v.substr(4).trim();
  565. return this._setStringValue(v, 0, emitChanged);
  566. }
  567. if (v.indexOf("left:") === 0) {
  568. v = v.substr(5).trim();
  569. return this._setStringValue(v, 1, emitChanged);
  570. }
  571. if (v.indexOf("right:") === 0) {
  572. v = v.substr(6).trim();
  573. return this._setStringValue(v, 2, emitChanged);
  574. }
  575. if (v.indexOf("bottom:") === 0) {
  576. v = v.substr(7).trim();
  577. return this._setStringValue(v, 3, emitChanged);
  578. }
  579. return false;
  580. }
  581. private _setStringValue(value: string, index: number, emitChanged: boolean): boolean {
  582. // Check for auto
  583. let v = value.trim().toLocaleLowerCase();
  584. if (v === "auto") {
  585. if (this._isType(index, PrimitiveThickness.Auto)) {
  586. return true;
  587. }
  588. this._setType(index, PrimitiveThickness.Auto);
  589. this._pixels[index] = 0;
  590. if (emitChanged) {
  591. this.onChangeCallback();
  592. }
  593. } else if (v === "inherit") {
  594. if (this._isType(index, PrimitiveThickness.Inherit)) {
  595. return true;
  596. }
  597. this._setType(index, PrimitiveThickness.Inherit);
  598. this._pixels[index] = null;
  599. if (emitChanged) {
  600. this.onChangeCallback();
  601. }
  602. } else {
  603. let pI = v.indexOf("%");
  604. // Check for percentage
  605. if (pI !== -1) {
  606. let n = v.substr(0, pI);
  607. let number = Math.round(Number(n)) / 100; // Normalize the percentage to [0;1] with a 0.01 precision
  608. if (this._isType(index, PrimitiveThickness.Percentage) && (this._percentages[index] === number)) {
  609. return true;
  610. }
  611. this._setType(index, PrimitiveThickness.Percentage);
  612. if (isNaN(number)) {
  613. return false;
  614. }
  615. this._percentages[index] = number;
  616. if (emitChanged) {
  617. this.onChangeCallback();
  618. }
  619. return true;
  620. }
  621. // Check for pixel
  622. let n: string;
  623. pI = v.indexOf("px");
  624. if (pI !== -1) {
  625. n = v.substr(0, pI).trim();
  626. } else {
  627. n = v;
  628. }
  629. let number = Number(n);
  630. if (this._isType(index, PrimitiveThickness.Pixel) && (this._pixels[index] === number)) {
  631. return true;
  632. }
  633. if (isNaN(number)) {
  634. return false;
  635. }
  636. this._pixels[index] = number;
  637. this._setType(index, PrimitiveThickness.Pixel);
  638. if (emitChanged) {
  639. this.onChangeCallback();
  640. }
  641. return true;
  642. }
  643. }
  644. private _setPixels(value: number, index: number, emitChanged: boolean) {
  645. // Round the value because, well, it's the thing to do! Otherwise we'll have sub-pixel stuff, and the no change comparison just below will almost never work for PrimitiveThickness values inside a hierarchy of Primitives
  646. value = Math.round(value);
  647. if (this._isType(index, PrimitiveThickness.Pixel) && this._pixels[index] === value) {
  648. return;
  649. }
  650. this._setType(index, PrimitiveThickness.Pixel);
  651. this._pixels[index] = value;
  652. if (emitChanged) {
  653. this.onChangeCallback();
  654. }
  655. }
  656. private _setPercentage(value: number, index: number, emitChanged: boolean) {
  657. // Clip Value to bounds
  658. value = Math.min(1, value);
  659. value = Math.max(0, value);
  660. value = Math.round(value * 100) / 100; // 0.01 precision
  661. if (this._isType(index, PrimitiveThickness.Percentage) && this._percentages[index] === value) {
  662. return;
  663. }
  664. this._setType(index, PrimitiveThickness.Percentage);
  665. this._percentages[index] = value;
  666. if (emitChanged) {
  667. this.onChangeCallback();
  668. }
  669. }
  670. private _getStringValue(index: number): string {
  671. let f = (this._flags >> (index * 4)) & 0xF;
  672. switch (f) {
  673. case PrimitiveThickness.Auto:
  674. return "auto";
  675. case PrimitiveThickness.Pixel:
  676. return `${this._pixels[index]}px`;
  677. case PrimitiveThickness.Percentage:
  678. return `${this._percentages[index] * 100}%`;
  679. case PrimitiveThickness.Inherit:
  680. return "inherit";
  681. }
  682. return "";
  683. }
  684. private _isType(index: number, type: number): boolean {
  685. let f = (this._flags >> (index * 4)) & 0xF;
  686. return f === type;
  687. }
  688. private _getType(index: number, processInherit: boolean): number {
  689. let t = (this._flags >> (index * 4)) & 0xF;
  690. if (processInherit && (t === PrimitiveThickness.Inherit)) {
  691. let p = this._parentAccess();
  692. if (p) {
  693. return p._getType(index, true);
  694. }
  695. return PrimitiveThickness.Auto;
  696. }
  697. return t;
  698. }
  699. private _setType(index: number, type: number) {
  700. this._flags &= ~(0xF << (index * 4));
  701. this._flags |= type << (index * 4);
  702. }
  703. public setTop(value: number | string) {
  704. if (typeof value === "string") {
  705. this._setStringValue(value, 0, true);
  706. } else {
  707. this.topPixels = value;
  708. }
  709. }
  710. public setLeft(value: number | string) {
  711. if (typeof value === "string") {
  712. this._setStringValue(value, 1, true);
  713. } else {
  714. this.leftPixels = value;
  715. }
  716. }
  717. public setRight(value: number | string) {
  718. if (typeof value === "string") {
  719. this._setStringValue(value, 2, true);
  720. } else {
  721. this.rightPixels = value;
  722. }
  723. }
  724. public setBottom(value: number | string) {
  725. if (typeof value === "string") {
  726. this._setStringValue(value, 3, true);
  727. } else {
  728. this.bottomPixels = value;
  729. }
  730. }
  731. /**
  732. * Get/set the top thickness. Possible values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels.
  733. */
  734. public get top(): string {
  735. return this._getStringValue(0);
  736. }
  737. public set top(value: string) {
  738. this._setStringValue(value, 0, true);
  739. }
  740. /**
  741. * Get/set the left thickness. Possible values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels.
  742. */
  743. public get left(): string {
  744. return this._getStringValue(1);
  745. }
  746. public set left(value: string) {
  747. this._setStringValue(value, 1, true);
  748. }
  749. /**
  750. * Get/set the right thickness. Possible values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels.
  751. */
  752. public get right(): string {
  753. return this._getStringValue(2);
  754. }
  755. public set right(value: string) {
  756. this._setStringValue(value, 2, true);
  757. }
  758. /**
  759. * Get/set the bottom thickness. Possible values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels.
  760. */
  761. public get bottom(): string {
  762. return this._getStringValue(3);
  763. }
  764. public set bottom(value: string) {
  765. this._setStringValue(value, 3, true);
  766. }
  767. /**
  768. * Get/set the top thickness in pixel.
  769. */
  770. public get topPixels(): number {
  771. return this._pixels[0];
  772. }
  773. public set topPixels(value: number) {
  774. this._setPixels(value, 0, true);
  775. }
  776. /**
  777. * Get/set the left thickness in pixel.
  778. */
  779. public get leftPixels(): number {
  780. return this._pixels[1];
  781. }
  782. public set leftPixels(value: number) {
  783. this._setPixels(value, 1, true);
  784. }
  785. /**
  786. * Get/set the right thickness in pixel.
  787. */
  788. public get rightPixels(): number {
  789. return this._pixels[2];
  790. }
  791. public set rightPixels(value: number) {
  792. this._setPixels(value, 2, true);
  793. }
  794. /**
  795. * Get/set the bottom thickness in pixel.
  796. */
  797. public get bottomPixels(): number {
  798. return this._pixels[3];
  799. }
  800. public set bottomPixels(value: number) {
  801. this._setPixels(value, 3, true);
  802. }
  803. /**
  804. * Get/set the top thickness in percentage.
  805. * The get will return a valid value only if the edge type is percentage.
  806. * The Set will change the edge mode if needed
  807. */
  808. public get topPercentage(): number {
  809. return this._percentages[0];
  810. }
  811. public set topPercentage(value: number) {
  812. this._setPercentage(value, 0, true);
  813. }
  814. /**
  815. * Get/set the left thickness in percentage.
  816. * The get will return a valid value only if the edge mode is percentage.
  817. * The Set will change the edge mode if needed
  818. */
  819. public get leftPercentage(): number {
  820. return this._percentages[1];
  821. }
  822. public set leftPercentage(value: number) {
  823. this._setPercentage(value, 1, true);
  824. }
  825. /**
  826. * Get/set the right thickness in percentage.
  827. * The get will return a valid value only if the edge mode is percentage.
  828. * The Set will change the edge mode if needed
  829. */
  830. public get rightPercentage(): number {
  831. return this._percentages[2];
  832. }
  833. public set rightPercentage(value: number) {
  834. this._setPercentage(value, 2, true);
  835. }
  836. /**
  837. * Get/set the bottom thickness in percentage.
  838. * The get will return a valid value only if the edge mode is percentage.
  839. * The Set will change the edge mode if needed
  840. */
  841. public get bottomPercentage(): number {
  842. return this._percentages[3];
  843. }
  844. public set bottomPercentage(value: number) {
  845. this._setPercentage(value, 3, true);
  846. }
  847. /**
  848. * Get/set the top mode. The setter shouldn't be used, other setters with value should be preferred
  849. */
  850. public get topMode(): number {
  851. return this._getType(0, false);
  852. }
  853. public set topMode(mode: number) {
  854. this._setType(0, mode);
  855. }
  856. /**
  857. * Get/set the left mode. The setter shouldn't be used, other setters with value should be preferred
  858. */
  859. public get leftMode(): number {
  860. return this._getType(1, false);
  861. }
  862. public set leftMode(mode: number) {
  863. this._setType(1, mode);
  864. }
  865. /**
  866. * Get/set the right mode. The setter shouldn't be used, other setters with value should be preferred
  867. */
  868. public get rightMode(): number {
  869. return this._getType(2, false);
  870. }
  871. public set rightMode(mode: number) {
  872. this._setType(2, mode);
  873. }
  874. /**
  875. * Get/set the bottom mode. The setter shouldn't be used, other setters with value should be preferred
  876. */
  877. public get bottomMode(): number {
  878. return this._getType(3, false);
  879. }
  880. public set bottomMode(mode: number) {
  881. this._setType(3, mode);
  882. }
  883. public get isDefault(): boolean {
  884. return this._flags === 0x1111;
  885. }
  886. private _parentAccess: () => PrimitiveThickness;
  887. private _changedCallback: () => void;
  888. private _pixels: number[];
  889. private _percentages: number[]; // Percentages are in fact stored in a normalized range [0;1] with a 0.01 precision
  890. private _flags: number;
  891. public static Auto = 0x1;
  892. public static Inherit = 0x2;
  893. public static Percentage = 0x4;
  894. public static Pixel = 0x8;
  895. public static ComputeH = 0x1;
  896. public static ComputeV = 0x2;
  897. public static ComputeAll = 0x03;
  898. private _computePixels(index: number, sourceArea: Size, emitChanged: boolean) {
  899. let type = this._getType(index, false);
  900. if (type === PrimitiveThickness.Inherit) {
  901. this._parentAccess()._computePixels(index, sourceArea, emitChanged);
  902. return;
  903. }
  904. if (type !== PrimitiveThickness.Percentage) {
  905. return;
  906. }
  907. let pixels = ((index === 0 || index === 3) ? sourceArea.height : sourceArea.width) * this._percentages[index];
  908. this._pixels[index] = pixels;
  909. if (emitChanged) {
  910. this.onChangeCallback();
  911. }
  912. }
  913. private onChangeCallback() {
  914. if (this._changedCallback) {
  915. this._changedCallback();
  916. }
  917. }
  918. /**
  919. * Compute the positioning/size of an area considering the thickness of this object and a given alignment
  920. * @param sourceArea the source area where the content must be sized/positioned
  921. * @param contentSize the content size to position/resize
  922. * @param alignment the alignment setting
  923. * @param dstOffset the position of the content, x, y, z, w are left, bottom, right, top
  924. * @param dstArea the new size of the content
  925. */
  926. public computeWithAlignment(sourceArea: Size, contentSize: Size, alignment: PrimitiveAlignment, contentScale: Vector2, dstOffset: Vector4, dstArea: Size, computeLayoutArea = false, computeAxis = PrimitiveThickness.ComputeAll) {
  927. // Fetch some data
  928. let topType = this._getType(0, true);
  929. let leftType = this._getType(1, true);
  930. let rightType = this._getType(2, true);
  931. let bottomType = this._getType(3, true);
  932. let hasWidth = contentSize && (contentSize.width != null);
  933. let hasHeight = contentSize && (contentSize.height != null);
  934. let sx = contentScale.x;
  935. let sy = contentScale.y;
  936. let isx = 1 / sx;
  937. let isy = 1 / sy;
  938. let width = hasWidth ? contentSize.width : 0;
  939. let height = hasHeight ? contentSize.height : 0;
  940. let isTopAuto = topType === PrimitiveThickness.Auto;
  941. let isLeftAuto = leftType === PrimitiveThickness.Auto;
  942. let isRightAuto = rightType === PrimitiveThickness.Auto;
  943. let isBottomAuto = bottomType === PrimitiveThickness.Auto;
  944. if (computeAxis & PrimitiveThickness.ComputeH) {
  945. switch (alignment.horizontal) {
  946. case PrimitiveAlignment.AlignLeft:
  947. {
  948. let leftPixels = 0;
  949. if (!isLeftAuto) {
  950. this._computePixels(1, sourceArea, true);
  951. leftPixels = this.leftPixels;
  952. }
  953. dstOffset.x = leftPixels;
  954. dstArea.width = width * isx;
  955. dstOffset.z = sourceArea.width - (width * sx + leftPixels);
  956. if (computeLayoutArea) {
  957. let rightPixels = 0;
  958. if (!isRightAuto) {
  959. this._computePixels(2, sourceArea, true);
  960. rightPixels = this.rightPixels;
  961. }
  962. dstArea.width += (leftPixels + rightPixels) * isx;
  963. }
  964. break;
  965. }
  966. case PrimitiveAlignment.AlignRight:
  967. {
  968. let rightPixels = 0;
  969. if (!isRightAuto) {
  970. this._computePixels(2, sourceArea, true);
  971. rightPixels = this.rightPixels;
  972. }
  973. dstOffset.x = sourceArea.width - (width * sx + rightPixels);
  974. dstArea.width = width * isx;
  975. dstOffset.z = rightPixels;
  976. if (computeLayoutArea) {
  977. let leftPixels = 0;
  978. if (!isLeftAuto) {
  979. this._computePixels(1, sourceArea, true);
  980. leftPixels = this.leftPixels;
  981. }
  982. dstArea.width += (leftPixels + rightPixels) * isx;
  983. }
  984. break;
  985. }
  986. case PrimitiveAlignment.AlignStretch:
  987. {
  988. if (isLeftAuto) {
  989. dstOffset.x = 0;
  990. } else {
  991. this._computePixels(1, sourceArea, true);
  992. dstOffset.x = this.leftPixels;
  993. }
  994. let rightPixels = 0;
  995. if (!isRightAuto) {
  996. this._computePixels(2, sourceArea, true);
  997. rightPixels = this.rightPixels;
  998. }
  999. if (computeLayoutArea) {
  1000. dstArea.width = sourceArea.width * isx;
  1001. } else {
  1002. dstArea.width = (sourceArea.width * isx) - (dstOffset.x + rightPixels) * isx;
  1003. }
  1004. dstOffset.z = this.rightPixels;
  1005. break;
  1006. }
  1007. case PrimitiveAlignment.AlignCenter:
  1008. {
  1009. let leftPixels = 0;
  1010. if (!isLeftAuto) {
  1011. this._computePixels(1, sourceArea, true);
  1012. leftPixels = this.leftPixels;
  1013. }
  1014. let rightPixels = 0;
  1015. if (!isRightAuto) {
  1016. this._computePixels(2, sourceArea, true);
  1017. rightPixels = this.rightPixels;
  1018. }
  1019. let center = ((sourceArea.width - (width * sx)) / 2);
  1020. dstOffset.x = center + (leftPixels - rightPixels);
  1021. if (computeLayoutArea) {
  1022. dstArea.width = (width * isx) + (this.leftPixels + this.rightPixels) * isx;
  1023. } else {
  1024. dstArea.width = (width * isx);
  1025. }
  1026. dstOffset.z = rightPixels + center;
  1027. break;
  1028. }
  1029. }
  1030. }
  1031. if (computeAxis & PrimitiveThickness.ComputeV) {
  1032. switch (alignment.vertical) {
  1033. case PrimitiveAlignment.AlignBottom:
  1034. {
  1035. let bottomPixels = 0;
  1036. if (!isBottomAuto) {
  1037. this._computePixels(3, sourceArea, true);
  1038. bottomPixels = this.bottomPixels;
  1039. }
  1040. dstOffset.y = bottomPixels;
  1041. dstArea.height = height * isy;
  1042. dstOffset.w = sourceArea.height - (height * sy + bottomPixels);
  1043. if (computeLayoutArea) {
  1044. let topPixels = 0;
  1045. if (!isTopAuto) {
  1046. this._computePixels(0, sourceArea, true);
  1047. topPixels = this.topPixels;
  1048. }
  1049. dstArea.height += (bottomPixels + topPixels) * isy;
  1050. }
  1051. break;
  1052. }
  1053. case PrimitiveAlignment.AlignTop:
  1054. {
  1055. let topPixels = 0;
  1056. if (!isTopAuto) {
  1057. this._computePixels(0, sourceArea, true);
  1058. topPixels = this.topPixels;
  1059. }
  1060. dstOffset.y = sourceArea.height - ((height * sy) + topPixels);
  1061. dstArea.height = height * isy;
  1062. dstOffset.w = topPixels;
  1063. if (computeLayoutArea) {
  1064. let bottomPixels = 0;
  1065. if (!isBottomAuto) {
  1066. this._computePixels(3, sourceArea, true);
  1067. bottomPixels = this.bottomPixels;
  1068. }
  1069. dstArea.height += (bottomPixels + topPixels) * isy;
  1070. }
  1071. break;
  1072. }
  1073. case PrimitiveAlignment.AlignStretch:
  1074. {
  1075. let bottom = 0;
  1076. if (!isBottomAuto) {
  1077. this._computePixels(3, sourceArea, true);
  1078. bottom = this.bottomPixels;
  1079. }
  1080. dstOffset.y = bottom;
  1081. let top = 0;
  1082. if (!isTopAuto) {
  1083. this._computePixels(0, sourceArea, true);
  1084. top = this.topPixels;
  1085. }
  1086. dstOffset.w = top;
  1087. if (computeLayoutArea) {
  1088. dstArea.height = sourceArea.height * isy;
  1089. } else {
  1090. dstArea.height = (sourceArea.height * isy) - (top + bottom) * isy;
  1091. }
  1092. break;
  1093. }
  1094. case PrimitiveAlignment.AlignCenter:
  1095. {
  1096. let bottomPixels = 0;
  1097. if (!isBottomAuto) {
  1098. this._computePixels(3, sourceArea, true);
  1099. bottomPixels = this.bottomPixels;
  1100. }
  1101. let topPixels = 0;
  1102. if (!isTopAuto) {
  1103. this._computePixels(0, sourceArea, true);
  1104. topPixels = this.topPixels;
  1105. }
  1106. let center = ((sourceArea.height - (height * sy)) / 2);
  1107. dstOffset.y = center + (bottomPixels - topPixels);
  1108. if (computeLayoutArea) {
  1109. dstArea.height = (height * isy) + (bottomPixels + topPixels) * isy;
  1110. } else {
  1111. dstArea.height = (height * isy);
  1112. }
  1113. dstOffset.w = topPixels + center;
  1114. break;
  1115. }
  1116. }
  1117. }
  1118. }
  1119. /**
  1120. * Compute an area and its position considering this thickness properties based on a given source area
  1121. * @param sourceArea the source area
  1122. * @param dstOffset the position of the resulting area
  1123. * @param dstArea the size of the resulting area
  1124. */
  1125. public compute(sourceArea: Size, dstOffset: Vector4, dstArea: Size, computeLayoutArea = false) {
  1126. this._computePixels(0, sourceArea, true);
  1127. this._computePixels(1, sourceArea, true);
  1128. this._computePixels(2, sourceArea, true);
  1129. this._computePixels(3, sourceArea, true);
  1130. dstOffset.x = this.leftPixels;
  1131. if (computeLayoutArea) {
  1132. dstArea.width = (sourceArea.width) + (dstOffset.x + this.rightPixels);
  1133. } else {
  1134. dstArea.width = (sourceArea.width) - (dstOffset.x + this.rightPixels);
  1135. }
  1136. dstOffset.y = this.bottomPixels;
  1137. if (computeLayoutArea) {
  1138. dstArea.height = (sourceArea.height) + (dstOffset.y + this.topPixels);
  1139. } else {
  1140. dstArea.height = (sourceArea.height) - (dstOffset.y + this.topPixels);
  1141. }
  1142. dstOffset.z = this.rightPixels;
  1143. dstOffset.w = this.topPixels;
  1144. }
  1145. /**
  1146. * Compute an area considering this thickness properties based on a given source area
  1147. * @param sourceArea the source area
  1148. * @param result the resulting area
  1149. */
  1150. computeArea(sourceArea: Size, sourceScale: Vector2, result: Size) {
  1151. this._computePixels(0, sourceArea, true);
  1152. this._computePixels(1, sourceArea, true);
  1153. this._computePixels(2, sourceArea, true);
  1154. this._computePixels(3, sourceArea, true);
  1155. result.width = this.leftPixels + (sourceArea.width * sourceScale.x) + this.rightPixels;
  1156. result.height = this.bottomPixels + (sourceArea.height * sourceScale.y) + this.topPixels;
  1157. }
  1158. enlarge(sourceArea: Size, sourceScale: Vector2, dstOffset: Vector4, enlargedArea: Size) {
  1159. this._computePixels(0, sourceArea, true);
  1160. this._computePixels(1, sourceArea, true);
  1161. this._computePixels(2, sourceArea, true);
  1162. this._computePixels(3, sourceArea, true);
  1163. dstOffset.x = this.leftPixels;
  1164. enlargedArea.width = (sourceArea.width * sourceScale.x) + (dstOffset.x + this.rightPixels);
  1165. dstOffset.y = this.bottomPixels;
  1166. enlargedArea.height = (sourceArea.height * sourceScale.y) + (dstOffset.y + this.topPixels);
  1167. dstOffset.z = this.rightPixels;
  1168. dstOffset.w = this.topPixels;
  1169. }
  1170. }
  1171. /**
  1172. * Main class used for the Primitive Intersection API
  1173. */
  1174. export class IntersectInfo2D {
  1175. constructor() {
  1176. this.findFirstOnly = false;
  1177. this.intersectHidden = false;
  1178. this.pickPosition = Vector2.Zero();
  1179. }
  1180. // Input settings, to setup before calling an intersection related method
  1181. /**
  1182. * Set the pick position, relative to the primitive where the intersection test is made
  1183. */
  1184. public pickPosition: Vector2;
  1185. /**
  1186. * If true the intersection will stop at the first hit, if false all primitives will be tested and the intersectedPrimitives array will be filled accordingly (false default)
  1187. */
  1188. public findFirstOnly: boolean;
  1189. /**
  1190. * If true the intersection test will also be made on hidden primitive (false default)
  1191. */
  1192. public intersectHidden: boolean;
  1193. // Intermediate data, don't use!
  1194. public _globalPickPosition: Vector2;
  1195. public _localPickPosition: Vector2;
  1196. // Output settings, up to date in return of a call to an intersection related method
  1197. /**
  1198. * The topmost intersected primitive
  1199. */
  1200. public topMostIntersectedPrimitive: PrimitiveIntersectedInfo;
  1201. /**
  1202. * The array containing all intersected primitive, in no particular order.
  1203. */
  1204. public intersectedPrimitives: Array<PrimitiveIntersectedInfo>;
  1205. /**
  1206. * true if at least one primitive intersected during the test
  1207. */
  1208. public get isIntersected(): boolean {
  1209. return this.intersectedPrimitives && this.intersectedPrimitives.length > 0;
  1210. }
  1211. public isPrimIntersected(prim: Prim2DBase): Vector2 {
  1212. for (let cur of this.intersectedPrimitives) {
  1213. if (cur.prim === prim) {
  1214. return cur.intersectionLocation;
  1215. }
  1216. }
  1217. return null;
  1218. }
  1219. // Internals, don't use
  1220. public _exit(firstLevel: boolean) {
  1221. if (firstLevel) {
  1222. this._globalPickPosition = null;
  1223. }
  1224. }
  1225. }
  1226. @className("Prim2DBase", "BABYLON")
  1227. /**
  1228. * Base class for a Primitive of the Canvas2D feature
  1229. */
  1230. export class Prim2DBase extends SmartPropertyPrim {
  1231. static PRIM2DBASE_PROPCOUNT: number = 25;
  1232. public static _bigInt = Math.pow(2, 30);
  1233. constructor(settings: {
  1234. parent ?: Prim2DBase,
  1235. id ?: string,
  1236. children ?: Array<Prim2DBase>,
  1237. position ?: Vector2,
  1238. x ?: number,
  1239. y ?: number,
  1240. rotation ?: number,
  1241. scale ?: number,
  1242. scaleX ?: number,
  1243. scaleY ?: number,
  1244. dontInheritParentScale ?: boolean,
  1245. alignToPixel ?: boolean,
  1246. opacity ?: number,
  1247. zOrder ?: number,
  1248. origin ?: Vector2,
  1249. layoutEngine ?: LayoutEngineBase | string,
  1250. isVisible ?: boolean,
  1251. isPickable ?: boolean,
  1252. isContainer ?: boolean,
  1253. childrenFlatZOrder ?: boolean,
  1254. levelCollision ?: boolean,
  1255. deepCollision ?: boolean,
  1256. layoutData ?: ILayoutData,
  1257. marginTop ?: number | string,
  1258. marginLeft ?: number | string,
  1259. marginRight ?: number | string,
  1260. marginBottom ?: number | string,
  1261. margin ?: number | string,
  1262. marginHAlignment ?: number,
  1263. marginVAlignment ?: number,
  1264. marginAlignment ?: string,
  1265. paddingTop ?: number | string,
  1266. paddingLeft ?: number | string,
  1267. paddingRight ?: number | string,
  1268. paddingBottom ?: number | string,
  1269. padding ?: number | string,
  1270. }) {
  1271. // Avoid checking every time if the object exists
  1272. if (settings == null) {
  1273. settings = {};
  1274. }
  1275. // BASE CLASS CALL
  1276. super();
  1277. // Fetch the owner, parent. There're many ways to do it and we can end up with nothing for both
  1278. let owner: Canvas2D;
  1279. let parent: Prim2DBase;
  1280. if (Prim2DBase._isCanvasInit) {
  1281. owner = <Canvas2D><any>this;
  1282. parent = null;
  1283. this._canvasPreInit(settings);
  1284. } else {
  1285. if (settings.parent != null) {
  1286. parent = settings.parent;
  1287. owner = settings.parent.owner;
  1288. if (!owner) {
  1289. throw new Error(`Parent ${parent.id} of ${settings.id} doesn't have a valid owner!`);
  1290. }
  1291. if (!(this instanceof Group2D) && !(this instanceof Sprite2D && settings.id != null && settings.id.indexOf("__cachedSpriteOfGroup__") === 0) && (owner.cachingStrategy === Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) && (parent === owner)) {
  1292. throw new Error("Can't create a primitive with the canvas as direct parent when the caching strategy is TOPLEVELGROUPS. You need to create a Group below the canvas and use it as the parent for the primitive");
  1293. }
  1294. }
  1295. }
  1296. // Fields initialization
  1297. this._layoutEngine = CanvasLayoutEngine.Singleton;
  1298. this._size = null; //Size.Zero();
  1299. this._scale = new Vector2(1, 1);
  1300. this._postScale = new Vector2(1, 1);
  1301. this._actualSize = null;
  1302. this._internalSize = Size.Zero();
  1303. this._layoutArea = null;
  1304. this._layoutAreaPos = null;
  1305. this._layoutBoundingInfo = null;
  1306. this._marginOffset = Vector4.Zero();
  1307. this._paddingOffset = Vector4.Zero();
  1308. this._parentPaddingOffset = Vector2.Zero();
  1309. this._parentContentArea = Size.Zero();
  1310. this._lastAutoSizeArea = Size.Zero();
  1311. this._contentArea = Size.Zero();
  1312. this._pointerEventObservable = new Observable<PrimitivePointerInfo>();
  1313. this._owner = owner;
  1314. this._parent = null;
  1315. this._margin = null;
  1316. this._padding = null;
  1317. this._marginAlignment = null;
  1318. this._id = settings.id;
  1319. this._children = new Array<Prim2DBase>();
  1320. this._localTransform = new Matrix2D();
  1321. this._localLayoutTransform = new Matrix2D();
  1322. this._globalTransform = null;
  1323. this._invGlobalTransform = null;
  1324. this._globalTransformProcessStep = 0;
  1325. this._globalTransformStep = 0;
  1326. this._prepareProcessStep = 0;
  1327. this._updateCachesProcessStep = 0;
  1328. this._renderGroup = null;
  1329. this._primLinearPosition = 0;
  1330. this._manualZOrder = null;
  1331. this._zOrder = 0;
  1332. this._zMax = 0;
  1333. this._firstZDirtyIndex = Prim2DBase._bigInt;
  1334. this._actualOpacity = 0;
  1335. this._actualScale = Vector2.Zero();
  1336. this._displayDebugAreas = false;
  1337. this._debugAreaGroup = null;
  1338. this._primTriArray = null;
  1339. this._primTriArrayDirty = true;
  1340. if (owner) {
  1341. this.onSetOwner();
  1342. }
  1343. this._levelBoundingInfo.worldMatrixAccess = () => this.globalTransform;
  1344. this._boundingInfo.worldMatrixAccess = () => this.globalTransform;
  1345. let isPickable = true;
  1346. let isContainer = true;
  1347. if (settings.isPickable !== undefined) {
  1348. isPickable = settings.isPickable;
  1349. }
  1350. if (settings.isContainer !== undefined) {
  1351. isContainer = settings.isContainer;
  1352. }
  1353. if (settings.dontInheritParentScale) {
  1354. this._setFlags(SmartPropertyPrim.flagDontInheritParentScale);
  1355. }
  1356. if (settings.alignToPixel) {
  1357. this.alignToPixel = true;
  1358. }
  1359. this._setFlags((isPickable ? SmartPropertyPrim.flagIsPickable : 0) | SmartPropertyPrim.flagBoundingInfoDirty | SmartPropertyPrim.flagActualOpacityDirty | (isContainer ? SmartPropertyPrim.flagIsContainer : 0) | SmartPropertyPrim.flagActualScaleDirty | SmartPropertyPrim.flagLayoutBoundingInfoDirty);
  1360. if (settings.opacity != null) {
  1361. this._opacity = settings.opacity;
  1362. } else {
  1363. this._opacity = 1;
  1364. }
  1365. this._updateRenderMode();
  1366. if (settings.childrenFlatZOrder) {
  1367. this._setFlags(SmartPropertyPrim.flagChildrenFlatZOrder);
  1368. }
  1369. // If the parent is given, initialize the hierarchy/owner related data
  1370. if (parent != null) {
  1371. parent.addChild(this);
  1372. this._hierarchyDepth = parent._hierarchyDepth + 1;
  1373. this._patchHierarchy(parent.owner);
  1374. }
  1375. // If it's a group, detect its own states
  1376. if (this.owner && this instanceof Group2D) {
  1377. var group: any = this;
  1378. group.detectGroupStates();
  1379. }
  1380. // Time to insert children if some are specified
  1381. if (settings.children != null) {
  1382. for (let child of settings.children) {
  1383. this.addChild(child);
  1384. // Good time to patch the hierarchy, it won't go very far if there's no need to
  1385. if (this.owner != null && this._hierarchyDepth != null) {
  1386. child._patchHierarchy(this.owner);
  1387. }
  1388. }
  1389. }
  1390. if (settings.zOrder != null) {
  1391. this.zOrder = settings.zOrder;
  1392. }
  1393. // Set the model related properties
  1394. if (settings.position != null) {
  1395. this.position = settings.position;
  1396. }
  1397. else if (settings.x != null || settings.y != null) {
  1398. this.position = new Vector2(settings.x || 0, settings.y || 0);
  1399. } else {
  1400. this._position = null;
  1401. }
  1402. this.rotation = (settings.rotation == null) ? 0 : settings.rotation;
  1403. if (settings.scale != null) {
  1404. this.scale = settings.scale;
  1405. } else {
  1406. if (settings.scaleX != null) {
  1407. this.scaleX = settings.scaleX;
  1408. }
  1409. if (settings.scaleY != null) {
  1410. this.scaleY = settings.scaleY;
  1411. }
  1412. }
  1413. this.levelVisible = (settings.isVisible == null) ? true : settings.isVisible;
  1414. this.origin = settings.origin || new Vector2(0.5, 0.5);
  1415. // Layout Engine
  1416. if (settings.layoutEngine != null) {
  1417. if (typeof settings.layoutEngine === "string") {
  1418. let name = (<string>settings.layoutEngine).toLocaleLowerCase().trim();
  1419. if (name === "canvas" || name === "canvaslayoutengine") {
  1420. this.layoutEngine = CanvasLayoutEngine.Singleton;
  1421. } else if (name.indexOf("stackpanel") === 0 || name.indexOf("horizontalstackpanel") === 0) {
  1422. this.layoutEngine = StackPanelLayoutEngine.Horizontal;
  1423. } else if (name.indexOf("verticalstackpanel") === 0) {
  1424. this.layoutEngine = StackPanelLayoutEngine.Vertical;
  1425. }
  1426. } else if (settings.layoutEngine instanceof LayoutEngineBase) {
  1427. this.layoutEngine = <LayoutEngineBase>settings.layoutEngine;
  1428. }
  1429. }
  1430. // Set the layout/margin stuffs
  1431. if (settings.marginTop) {
  1432. this.margin.setTop(settings.marginTop);
  1433. }
  1434. if (settings.marginLeft) {
  1435. this.margin.setLeft(settings.marginLeft);
  1436. }
  1437. if (settings.marginRight) {
  1438. this.margin.setRight(settings.marginRight);
  1439. }
  1440. if (settings.marginBottom) {
  1441. this.margin.setBottom(settings.marginBottom);
  1442. }
  1443. if (settings.margin) {
  1444. if (typeof settings.margin === "string") {
  1445. this.margin.fromString(<string>settings.margin);
  1446. } else {
  1447. this.margin.fromUniformPixels(<number>settings.margin);
  1448. }
  1449. }
  1450. if (settings.marginHAlignment) {
  1451. this.marginAlignment.horizontal = settings.marginHAlignment;
  1452. }
  1453. if (settings.marginVAlignment) {
  1454. this.marginAlignment.vertical = settings.marginVAlignment;
  1455. }
  1456. if (settings.marginAlignment) {
  1457. this.marginAlignment.fromString(settings.marginAlignment);
  1458. }
  1459. if (settings.paddingTop) {
  1460. this.padding.setTop(settings.paddingTop);
  1461. }
  1462. if (settings.paddingLeft) {
  1463. this.padding.setLeft(settings.paddingLeft);
  1464. }
  1465. if (settings.paddingRight) {
  1466. this.padding.setRight(settings.paddingRight);
  1467. }
  1468. if (settings.paddingBottom) {
  1469. this.padding.setBottom(settings.paddingBottom);
  1470. }
  1471. if (settings.padding) {
  1472. if (typeof settings.padding === "string") {
  1473. this.padding.fromString(<string>settings.padding);
  1474. } else {
  1475. this.padding.fromUniformPixels(<number>settings.padding);
  1476. }
  1477. }
  1478. if (settings.layoutData) {
  1479. this.layoutData = settings.layoutData;
  1480. }
  1481. this._updatePositioningState();
  1482. // Dirty layout and positioning
  1483. this._parentLayoutDirty();
  1484. this._positioningDirty();
  1485. // Add in the PCM
  1486. if (settings.levelCollision || settings.deepCollision) {
  1487. this._actorInfo = this.owner._primitiveCollisionManager._addActor(this, settings.deepCollision === true);
  1488. this._setFlags(SmartPropertyPrim.flagCollisionActor);
  1489. } else {
  1490. this._actorInfo = null;
  1491. }
  1492. }
  1493. /**
  1494. * Return the ChangedDictionary observable of the StringDictionary containing the primitives intersecting with this one
  1495. */
  1496. public get intersectWithObservable(): Observable<DictionaryChanged<ActorInfoBase>> {
  1497. if (!this._actorInfo) {
  1498. return null;
  1499. }
  1500. return this._actorInfo.intersectWith.dictionaryChanged;
  1501. }
  1502. /**
  1503. * Return the ObservableStringDictionary containing all the primitives intersecting with this one.
  1504. * The key is the primitive uid, the value is the ActorInfo object
  1505. * @returns {}
  1506. */
  1507. public get intersectWith(): ObservableStringDictionary<ActorInfoBase> {
  1508. if (!this._actorInfo) {
  1509. return null;
  1510. }
  1511. return this._actorInfo.intersectWith;
  1512. }
  1513. public get actionManager(): ActionManager {
  1514. if (!this._actionManager) {
  1515. this._actionManager = new ActionManager(this.owner.scene);
  1516. }
  1517. return this._actionManager;
  1518. }
  1519. /**
  1520. * From 'this' primitive, traverse up (from parent to parent) until the given predicate is true
  1521. * @param predicate the predicate to test on each parent
  1522. * @return the first primitive where the predicate was successful
  1523. */
  1524. public traverseUp(predicate: (p: Prim2DBase) => boolean): Prim2DBase {
  1525. let p: Prim2DBase = this;
  1526. while (p != null) {
  1527. if (predicate(p)) {
  1528. return p;
  1529. }
  1530. p = p._parent;
  1531. }
  1532. return null;
  1533. }
  1534. /**
  1535. * Retrieve the owner Canvas2D
  1536. */
  1537. public get owner(): Canvas2D {
  1538. return this._owner;
  1539. }
  1540. /**
  1541. * Get the parent primitive (can be the Canvas, only the Canvas has no parent)
  1542. */
  1543. public get parent(): Prim2DBase {
  1544. return this._parent;
  1545. }
  1546. /**
  1547. * The array of direct children primitives
  1548. */
  1549. public get children(): Prim2DBase[] {
  1550. return this._children;
  1551. }
  1552. /**
  1553. * The identifier of this primitive, may not be unique, it's for information purpose only
  1554. */
  1555. public get id(): string {
  1556. return this._id;
  1557. }
  1558. public set id(value: string) {
  1559. if (this._id === value) {
  1560. return;
  1561. }
  1562. let oldValue = this._id;
  1563. this.onPropertyChanged("id", oldValue, this._id);
  1564. }
  1565. /**
  1566. * Metadata of the position property
  1567. */
  1568. public static positionProperty: Prim2DPropInfo;
  1569. /**
  1570. * Metadata of the left property
  1571. */
  1572. public static xProperty: Prim2DPropInfo;
  1573. /**
  1574. * Metadata of the bottom property
  1575. */
  1576. public static yProperty: Prim2DPropInfo;
  1577. /**
  1578. * Metadata of the actualPosition property
  1579. */
  1580. public static actualPositionProperty: Prim2DPropInfo;
  1581. /**
  1582. * Metadata of the actualX (Left) property
  1583. */
  1584. public static actualXProperty: Prim2DPropInfo;
  1585. /**
  1586. * Metadata of the actualY (Bottom) property
  1587. */
  1588. public static actualYProperty: Prim2DPropInfo;
  1589. /**
  1590. * Metadata of the size property
  1591. */
  1592. public static sizeProperty: Prim2DPropInfo;
  1593. /**
  1594. * Metadata of the width property
  1595. */
  1596. public static widthProperty: Prim2DPropInfo;
  1597. /**
  1598. * Metadata of the height property
  1599. */
  1600. public static heightProperty: Prim2DPropInfo;
  1601. /**
  1602. * Metadata of the rotation property
  1603. */
  1604. public static rotationProperty: Prim2DPropInfo;
  1605. /**
  1606. * Metadata of the scale property
  1607. */
  1608. public static scaleProperty: Prim2DPropInfo;
  1609. /**
  1610. * Metadata of the actualSize property
  1611. */
  1612. public static actualSizeProperty: Prim2DPropInfo;
  1613. /**
  1614. * Metadata of the actualWidth property
  1615. */
  1616. public static actualWidthProperty: Prim2DPropInfo;
  1617. /**
  1618. * Metadata of the actualHeight property
  1619. */
  1620. public static actualHeightProperty: Prim2DPropInfo;
  1621. /**
  1622. * Metadata of the origin property
  1623. */
  1624. public static originProperty: Prim2DPropInfo;
  1625. /**
  1626. * Metadata of the levelVisible property
  1627. */
  1628. public static levelVisibleProperty: Prim2DPropInfo;
  1629. /**
  1630. * Metadata of the isVisible property
  1631. */
  1632. public static isVisibleProperty: Prim2DPropInfo;
  1633. /**
  1634. * Metadata of the zOrder property
  1635. */
  1636. public static zOrderProperty: Prim2DPropInfo;
  1637. /**
  1638. * Metadata of the margin property
  1639. */
  1640. public static marginProperty: Prim2DPropInfo;
  1641. /**
  1642. * Metadata of the margin property
  1643. */
  1644. public static paddingProperty: Prim2DPropInfo;
  1645. /**
  1646. * Metadata of the marginAlignment property
  1647. */
  1648. public static marginAlignmentProperty: Prim2DPropInfo;
  1649. /**
  1650. * Metadata of the opacity property
  1651. */
  1652. public static opacityProperty: Prim2DPropInfo;
  1653. /**
  1654. * Metadata of the scaleX property
  1655. */
  1656. public static scaleXProperty: Prim2DPropInfo;
  1657. /**
  1658. * Metadata of the scaleY property
  1659. */
  1660. public static scaleYProperty: Prim2DPropInfo;
  1661. /**
  1662. * Metadata of the actualScale property
  1663. */
  1664. public static actualScaleProperty: Prim2DPropInfo;
  1665. @logProp(null, false, false, false)
  1666. @instanceLevelProperty(1, pi => Prim2DBase.actualPositionProperty = pi, false, false, true)
  1667. /**
  1668. * Return the position where the primitive is rendered in the Canvas, this position may be different than the one returned by the position property due to layout/alignment/margin/padding computing.
  1669. * BEWARE: don't change this value, it's read-only!
  1670. */
  1671. public get actualPosition(): Vector2 {
  1672. // If we don't use positioning engine the actual position is the position
  1673. if (!this._isFlagSet(SmartPropertyPrim.flagUsePositioning)) {
  1674. return this.position;
  1675. }
  1676. // We use the positioning engine, if the variable is fetched, it's up to date, return it
  1677. if (this._actualPosition != null) {
  1678. return this._actualPosition;
  1679. }
  1680. this._updatePositioning();
  1681. return this._actualPosition;
  1682. }
  1683. private static _nullPosition = Vector2.Zero();
  1684. private static _nullSize = Size.Zero();
  1685. /**
  1686. * DO NOT INVOKE for internal purpose only
  1687. */
  1688. public set actualPosition(val: Vector2) {
  1689. if (!this._actualPosition) {
  1690. this._actualPosition = val.clone();
  1691. } else {
  1692. this._actualPosition.copyFrom(val);
  1693. }
  1694. }
  1695. /**
  1696. * Shortcut to actualPosition.x
  1697. */
  1698. @instanceLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 1, pi => Prim2DBase.actualXProperty = pi, false, false, true)
  1699. public get actualX(): number {
  1700. return this.actualPosition.x;
  1701. }
  1702. /**
  1703. * DO NOT INVOKE for internal purpose only
  1704. */
  1705. public set actualX(val: number) {
  1706. this._actualPosition.x = val;
  1707. this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, this._actualPosition);
  1708. }
  1709. /**
  1710. * Shortcut to actualPosition.y
  1711. */
  1712. @instanceLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 2, pi => Prim2DBase.actualYProperty = pi, false, false, true)
  1713. public get actualY(): number {
  1714. return this.actualPosition.y;
  1715. }
  1716. /**
  1717. * DO NOT INVOKE for internal purpose only
  1718. */
  1719. public set actualY(val: number) {
  1720. this._actualPosition.y = val;
  1721. this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, this._actualPosition);
  1722. }
  1723. /**
  1724. * Position of the primitive, relative to its parent.
  1725. * BEWARE: if you change only position.x or y it won't trigger a property change and you won't have the expected behavior.
  1726. * Use this property to set a new Vector2 object, otherwise to change only the x/y use Prim2DBase.x or y properties.
  1727. * Setting this property may have no effect is specific alignment are in effect.
  1728. */
  1729. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 3, pi => Prim2DBase.positionProperty = pi, false, false, true)
  1730. public get position(): Vector2 {
  1731. if (!this._position) {
  1732. this._position = Vector2.Zero();
  1733. }
  1734. return this._position;
  1735. }
  1736. public set position(value: Vector2) {
  1737. //if (!this._checkPositionChange()) {
  1738. // return;
  1739. //}
  1740. if (this._checkUseMargin()) {
  1741. switch (this.marginAlignment.horizontal) {
  1742. case PrimitiveAlignment.AlignLeft:
  1743. case PrimitiveAlignment.AlignStretch:
  1744. case PrimitiveAlignment.AlignCenter:
  1745. this.margin.leftPixels = value.x;
  1746. break;
  1747. case PrimitiveAlignment.AlignRight:
  1748. this.margin.rightPixels = value.x;
  1749. break;
  1750. }
  1751. switch (this.marginAlignment.vertical) {
  1752. case PrimitiveAlignment.AlignBottom:
  1753. case PrimitiveAlignment.AlignStretch:
  1754. case PrimitiveAlignment.AlignCenter:
  1755. this.margin.bottomPixels = value.y;
  1756. break;
  1757. case PrimitiveAlignment.AlignTop:
  1758. this.margin.topPixels = value.y;
  1759. break;
  1760. }
  1761. return;
  1762. } else {
  1763. if (!value) {
  1764. this._position = null;
  1765. } else {
  1766. if (!this._position) {
  1767. this._position = value.clone();
  1768. } else {
  1769. this._position.copyFrom(value);
  1770. }
  1771. }
  1772. this._actualPosition = null;
  1773. this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, value);
  1774. }
  1775. }
  1776. /**
  1777. * Direct access to the position.x value of the primitive
  1778. * Use this property when you only want to change one component of the position property
  1779. */
  1780. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 4, pi => Prim2DBase.xProperty = pi, false, false, true)
  1781. public get x(): number {
  1782. if (!this._position) {
  1783. return null;
  1784. }
  1785. return this._position.x;
  1786. }
  1787. public set x(value: number) {
  1788. //if (!this._checkPositionChange()) {
  1789. // return;
  1790. //}
  1791. if (value == null) {
  1792. throw new Error(`Can't set a null x in primitive ${this.id}, only the position can be turned to null`);
  1793. }
  1794. if (this._checkUseMargin()) {
  1795. switch (this.marginAlignment.horizontal) {
  1796. case PrimitiveAlignment.AlignLeft:
  1797. case PrimitiveAlignment.AlignStretch:
  1798. case PrimitiveAlignment.AlignCenter:
  1799. this.margin.leftPixels = value;
  1800. break;
  1801. case PrimitiveAlignment.AlignRight:
  1802. this.margin.rightPixels = value;
  1803. break;
  1804. }
  1805. return;
  1806. } else {
  1807. if (!this._position) {
  1808. this._position = Vector2.Zero();
  1809. }
  1810. if (this._position.x === value) {
  1811. return;
  1812. }
  1813. this._position.x = value;
  1814. this._actualPosition = null;
  1815. this._triggerPropertyChanged(Prim2DBase.positionProperty, value);
  1816. this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, value);
  1817. }
  1818. }
  1819. /**
  1820. * Direct access to the position.y value of the primitive
  1821. * Use this property when you only want to change one component of the position property
  1822. */
  1823. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 5, pi => Prim2DBase.yProperty = pi, false, false, true)
  1824. public get y(): number {
  1825. if (!this._position) {
  1826. return null;
  1827. }
  1828. return this._position.y;
  1829. }
  1830. public set y(value: number) {
  1831. //if (!this._checkPositionChange()) {
  1832. // return;
  1833. //}
  1834. if (value == null) {
  1835. throw new Error(`Can't set a null y in primitive ${this.id}, only the position can be turned to null`);
  1836. }
  1837. if (this._checkUseMargin()) {
  1838. switch (this.marginAlignment.vertical) {
  1839. case PrimitiveAlignment.AlignBottom:
  1840. case PrimitiveAlignment.AlignStretch:
  1841. case PrimitiveAlignment.AlignCenter:
  1842. this.margin.bottomPixels = value;
  1843. break;
  1844. case PrimitiveAlignment.AlignTop:
  1845. this.margin.topPixels = value;
  1846. break;
  1847. }
  1848. return;
  1849. } else {
  1850. if (!this._position) {
  1851. this._position = Vector2.Zero();
  1852. }
  1853. if (this._position.y === value) {
  1854. return;
  1855. }
  1856. this._position.y = value;
  1857. this._actualPosition = null;
  1858. this._triggerPropertyChanged(Prim2DBase.positionProperty, value);
  1859. this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, value);
  1860. }
  1861. }
  1862. private static boundinbBoxReentrency: number = -1;
  1863. protected static nullSize = Size.Zero();
  1864. @logProp()
  1865. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 6, pi => Prim2DBase.sizeProperty = pi, false, true)
  1866. /**
  1867. * Size of the primitive or its bounding area
  1868. * BEWARE: if you change only size.width or height it won't trigger a property change and you won't have the expected behavior.
  1869. * Use this property to set a new Size object, otherwise to change only the width/height use Prim2DBase.width or height properties.
  1870. */
  1871. public get size(): Size {
  1872. return this.internalGetSize();
  1873. }
  1874. @logMethod()
  1875. protected internalGetSize(): Size {
  1876. if (!this._size || this._size.width == null || this._size.height == null) {
  1877. let bbr = Prim2DBase.boundinbBoxReentrency;
  1878. if (bbr !== -1 && bbr <= (this.hierarchyDepth || 0)) {
  1879. C2DLogging.setPostMessage(() => "re entrancy detected");
  1880. return Prim2DBase.nullSize;
  1881. }
  1882. if (!this._isFlagSet(SmartPropertyPrim.flagLayoutBoundingInfoDirty)) {
  1883. C2DLogging.setPostMessage(() => "cache hit");
  1884. return this._internalSize;
  1885. }
  1886. C2DLogging.setPostMessage(() => "cache miss");
  1887. Prim2DBase.boundinbBoxReentrency = this.hierarchyDepth || 0;
  1888. let b = this.boundingInfo;
  1889. Prim2DBase.boundinbBoxReentrency = -1;
  1890. Prim2DBase._size.copyFrom(this._internalSize);
  1891. b.sizeToRef(this._internalSize);
  1892. if (!this._internalSize.equals(Prim2DBase._size)) {
  1893. this._triggerPropertyChanged(Prim2DBase.sizeProperty, this._internalSize);
  1894. this._positioningDirty();
  1895. }
  1896. return this._internalSize || Prim2DBase._nullSize;
  1897. } else {
  1898. C2DLogging.setPostMessage(() => "user set size");
  1899. }
  1900. return this._size || Prim2DBase._nullSize;
  1901. }
  1902. public set size(value: Size) {
  1903. this.internalSetSize(value);
  1904. }
  1905. @logMethod()
  1906. protected internalSetSize(value: Size) {
  1907. if (!value) {
  1908. this._size = null;
  1909. } else {
  1910. if (!this._size) {
  1911. this._size = value.clone();
  1912. } else {
  1913. this._size.copyFrom(value);
  1914. }
  1915. }
  1916. this._actualSize = null;
  1917. this._updatePositioningState();
  1918. this._positioningDirty();
  1919. }
  1920. @logProp()
  1921. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 7, pi => Prim2DBase.widthProperty = pi, false, true)
  1922. /**
  1923. * Direct access to the size.width value of the primitive
  1924. * Use this property when you only want to change one component of the size property
  1925. */
  1926. public get width(): number {
  1927. if (!this.size) {
  1928. return null;
  1929. }
  1930. return this.size.width;
  1931. }
  1932. public set width(value: number) {
  1933. if (this.size && this.size.width === value) {
  1934. return;
  1935. }
  1936. if (!this.size) {
  1937. this.size = new Size(value, 0);
  1938. } else {
  1939. this.size.width = value;
  1940. }
  1941. this._actualSize = null;
  1942. this._triggerPropertyChanged(Prim2DBase.sizeProperty, value);
  1943. this._positioningDirty();
  1944. }
  1945. @logProp()
  1946. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 8, pi => Prim2DBase.heightProperty = pi, false, true)
  1947. /**
  1948. * Direct access to the size.height value of the primitive
  1949. * Use this property when you only want to change one component of the size property
  1950. */
  1951. public get height(): number {
  1952. if (!this.size) {
  1953. return null;
  1954. }
  1955. return this.size.height;
  1956. }
  1957. public set height(value: number) {
  1958. if (this.size && this.size.height === value) {
  1959. return;
  1960. }
  1961. if (!this.size) {
  1962. this.size = new Size(0, value);
  1963. } else {
  1964. this.size.height = value;
  1965. }
  1966. this._actualSize = null;
  1967. this._triggerPropertyChanged(Prim2DBase.sizeProperty, value);
  1968. this._positioningDirty();
  1969. }
  1970. @logProp()
  1971. @instanceLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 9, pi => Prim2DBase.rotationProperty = pi, false, true)
  1972. /**
  1973. * Rotation of the primitive, in radian, along the Z axis
  1974. */
  1975. public get rotation(): number {
  1976. return this._rotation;
  1977. }
  1978. public set rotation(value: number) {
  1979. this._rotation = value;
  1980. if (this._hasMargin) {
  1981. this._positioningDirty();
  1982. }
  1983. }
  1984. @logProp()
  1985. @instanceLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 10, pi => Prim2DBase.scaleProperty = pi, false, true)
  1986. /**
  1987. * Uniform scale applied on the primitive. If a non-uniform scale is applied through scaleX/scaleY property the getter of this property will return scaleX.
  1988. */
  1989. public set scale(value: number) {
  1990. if (value <= 0) {
  1991. throw new Error("You can't set the scale to less or equal to 0");
  1992. }
  1993. this._scale.x = this._scale.y = value;
  1994. this._setFlags(SmartPropertyPrim.flagActualScaleDirty);
  1995. this._spreadActualScaleDirty();
  1996. this._positioningDirty();
  1997. }
  1998. public get scale(): number {
  1999. return this._scale.x;
  2000. }
  2001. @logProp()
  2002. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 11, pi => Prim2DBase.actualSizeProperty = pi, false, true)
  2003. /**
  2004. * Return the size of the primitive as it's being rendered into the target.
  2005. * This value may be different of the size property when layout/alignment is used or specific primitive types can implement a custom logic through this property.
  2006. * BEWARE: don't use the setter, it's for internal purpose only
  2007. * Note to implementers: you have to override this property and declare if necessary a @xxxxInstanceLevel decorator
  2008. */
  2009. public get actualSize(): Size {
  2010. // If we don't use positioning engine the actual size is the size
  2011. if (!this._isFlagSet(SmartPropertyPrim.flagUsePositioning)) {
  2012. return this.size;
  2013. }
  2014. // We use the positioning engine, if the variable is fetched, it's up to date, return it
  2015. if (this._actualSize) {
  2016. return this._actualSize;
  2017. }
  2018. this._updatePositioning();
  2019. return this._actualSize;
  2020. }
  2021. public set actualSize(value: Size) {
  2022. if (this._actualSize && this._actualSize.equals(value)) {
  2023. return;
  2024. }
  2025. if (!this._actualSize) {
  2026. this._actualSize = value.clone();
  2027. } else {
  2028. this._actualSize.copyFrom(value);
  2029. }
  2030. }
  2031. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 12, pi => Prim2DBase.actualWidthProperty = pi, false, true)
  2032. /**
  2033. * Shortcut to actualSize.width
  2034. */
  2035. public get actualWidth(): number {
  2036. return this.actualSize.width;
  2037. }
  2038. public set actualWidth(val: number) {
  2039. this._actualSize.width = val;
  2040. this._triggerPropertyChanged(Prim2DBase.actualSizeProperty, this._actualSize);
  2041. }
  2042. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 13, pi => Prim2DBase.actualHeightProperty = pi, false, true)
  2043. /**
  2044. * Shortcut to actualPosition.height
  2045. */
  2046. public get actualHeight(): number {
  2047. return this.actualSize.height;
  2048. }
  2049. public set actualHeight(val: number) {
  2050. this._actualSize.height = val;
  2051. this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, this._actualSize);
  2052. }
  2053. public get actualZOffset(): number {
  2054. if (this._manualZOrder!=null) {
  2055. return this._manualZOrder;
  2056. }
  2057. if (this._isFlagSet(SmartPropertyPrim.flagZOrderDirty)) {
  2058. this._updateZOrder();
  2059. }
  2060. return (1 - this._zOrder);
  2061. }
  2062. /**
  2063. * Get or set the minimal size the Layout Engine should respect when computing the primitive's actualSize.
  2064. * The Primitive's size won't be less than specified.
  2065. * The default value depends of the Primitive type
  2066. */
  2067. public get minSize(): Size {
  2068. return this._minSize;
  2069. }
  2070. public set minSize(value: Size) {
  2071. if (this._minSize && value && this._minSize.equals(value)) {
  2072. return;
  2073. }
  2074. if (!this._minSize) {
  2075. this._minSize = value.clone();
  2076. } else {
  2077. this._minSize.copyFrom(value);
  2078. }
  2079. this._parentLayoutDirty();
  2080. }
  2081. /**
  2082. * Get or set the maximal size the Layout Engine should respect when computing the primitive's actualSize.
  2083. * The Primitive's size won't be more than specified.
  2084. * The default value depends of the Primitive type
  2085. */
  2086. public get maxSize(): Size {
  2087. return this._maxSize;
  2088. }
  2089. public set maxSize(value: Size) {
  2090. if (this._maxSize && value && this._maxSize.equals(value)) {
  2091. return;
  2092. }
  2093. if (!this._maxSize) {
  2094. this._maxSize = value.clone();
  2095. } else {
  2096. this._maxSize.copyFrom(value);
  2097. }
  2098. this._parentLayoutDirty();
  2099. }
  2100. /**
  2101. * The origin defines the normalized coordinate of the center of the primitive, from the bottom/left corner.
  2102. * The origin is used only to compute transformation of the primitive, it has no meaning in the primitive local frame of reference
  2103. * For instance:
  2104. * 0,0 means the center is bottom/left. Which is the default for Canvas2D instances
  2105. * 0.5,0.5 means the center is at the center of the primitive, which is default of all types of Primitives
  2106. * 0,1 means the center is top/left
  2107. * @returns The normalized center.
  2108. */
  2109. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 14, pi => Prim2DBase.originProperty = pi, false, true)
  2110. public get origin(): Vector2 {
  2111. return this._origin;
  2112. }
  2113. public set origin(value: Vector2) {
  2114. if (!this._origin) {
  2115. this._origin = value.clone();
  2116. } else {
  2117. this._origin.copyFrom(value);
  2118. }
  2119. }
  2120. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 15, pi => Prim2DBase.levelVisibleProperty = pi)
  2121. /**
  2122. * Let the user defines if the Primitive is hidden or not at its level. As Primitives inherit the hidden status from their parent, only the isVisible property give properly the real visible state.
  2123. * Default is true, setting to false will hide this primitive and its children.
  2124. */
  2125. public get levelVisible(): boolean {
  2126. return this._isFlagSet(SmartPropertyPrim.flagLevelVisible);
  2127. }
  2128. public set levelVisible(value: boolean) {
  2129. this._changeFlags(SmartPropertyPrim.flagLevelVisible, value);
  2130. }
  2131. @instanceLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 16, pi => Prim2DBase.isVisibleProperty = pi)
  2132. /**
  2133. * Use ONLY THE GETTER to determine if the primitive is visible or not.
  2134. * The Setter is for internal purpose only!
  2135. */
  2136. public get isVisible(): boolean {
  2137. return this._isFlagSet(SmartPropertyPrim.flagIsVisible);
  2138. }
  2139. public set isVisible(value: boolean) {
  2140. this._changeFlags(SmartPropertyPrim.flagIsVisible, value);
  2141. }
  2142. @instanceLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 17, pi => Prim2DBase.zOrderProperty = pi)
  2143. /**
  2144. * You can override the default Z Order through this property, but most of the time the default behavior is acceptable
  2145. */
  2146. public get zOrder(): number {
  2147. return this._manualZOrder;
  2148. }
  2149. public set zOrder(value: number) {
  2150. if (this._manualZOrder === value) {
  2151. return;
  2152. }
  2153. this._manualZOrder = value;
  2154. this.onZOrderChanged();
  2155. if (this._actualZOrderChangedObservable && this._actualZOrderChangedObservable.hasObservers()) {
  2156. this._actualZOrderChangedObservable.notifyObservers(value);
  2157. }
  2158. }
  2159. public get isManualZOrder(): boolean {
  2160. return this._manualZOrder != null;
  2161. }
  2162. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 18, pi => Prim2DBase.marginProperty = pi)
  2163. /**
  2164. * You can get/set a margin on the primitive through this property
  2165. * @returns the margin object, if there was none, a default one is created and returned
  2166. */
  2167. public get margin(): PrimitiveThickness {
  2168. if (!this._margin) {
  2169. this._margin = new PrimitiveThickness(() => {
  2170. if (!this.parent) {
  2171. return null;
  2172. }
  2173. return this.parent.margin;
  2174. }, () => {
  2175. this._positioningDirty();
  2176. this._updatePositioningState();
  2177. });
  2178. this._updatePositioningState();
  2179. }
  2180. return this._margin;
  2181. }
  2182. public set margin(value: PrimitiveThickness) {
  2183. if (!value) {
  2184. this._margin = null;
  2185. } else {
  2186. this.margin.copyFrom(value);
  2187. }
  2188. this._updatePositioningState();
  2189. }
  2190. /**
  2191. * Set the margin from a string value
  2192. * @param value is "top: <value>, left:<value>, right:<value>, bottom:<value>" or "<value>" (same for all edges) each are optional, auto will be set if it's omitted.
  2193. * Values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels.
  2194. */
  2195. public setMargin(value: string) {
  2196. this.margin.fromString(value);
  2197. this._updatePositioningState();
  2198. }
  2199. /**
  2200. * Check for both margin and marginAlignment, return true if at least one of them is specified with a non default value
  2201. */
  2202. public get _hasMargin(): boolean {
  2203. return (this._margin !== null && !this._margin.isDefault) || (this._marginAlignment !== null && !this._marginAlignment.isDefault);
  2204. }
  2205. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 19, pi => Prim2DBase.paddingProperty = pi)
  2206. /**
  2207. * You can get/set a margin on the primitive through this property
  2208. * @returns the margin object, if there was none, a default one is created and returned
  2209. */
  2210. public get padding(): PrimitiveThickness {
  2211. if (!this._padding) {
  2212. this._padding = new PrimitiveThickness(() => {
  2213. if (!this.parent) {
  2214. return null;
  2215. }
  2216. return this.parent.padding;
  2217. }, () => this._positioningDirty());
  2218. this._updatePositioningState();
  2219. }
  2220. return this._padding;
  2221. }
  2222. public set padding(value: PrimitiveThickness) {
  2223. if (!value) {
  2224. this._padding = null;
  2225. } else {
  2226. this.padding.copyFrom(value);
  2227. }
  2228. this._updatePositioningState();
  2229. }
  2230. /**
  2231. * Set the padding from a string value
  2232. * @param value is "top: <value>, left:<value>, right:<value>, bottom:<value>" or "<value>" (same for all edges) each are optional, auto will be set if it's omitted.
  2233. * Values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels. */
  2234. public setPadding(value: string) {
  2235. this.padding.fromString(value);
  2236. this._updatePositioningState();
  2237. }
  2238. private get _hasPadding(): boolean {
  2239. return this._padding !== null && !this._padding.isDefault;
  2240. }
  2241. @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 20, pi => Prim2DBase.marginAlignmentProperty = pi)
  2242. /**
  2243. * You can get/set the margin alignment through this property
  2244. */
  2245. public get marginAlignment(): PrimitiveAlignment {
  2246. if (!this._marginAlignment) {
  2247. this._marginAlignment = new PrimitiveAlignment(() => {
  2248. this._positioningDirty();
  2249. this._updatePositioningState();
  2250. });
  2251. this._updatePositioningState();
  2252. }
  2253. return this._marginAlignment;
  2254. }
  2255. public set marginAlignment(value: PrimitiveAlignment) {
  2256. if (!value) {
  2257. this._marginAlignment = null;
  2258. } else {
  2259. this.marginAlignment.copyFrom(value);
  2260. }
  2261. this._updatePositioningState();
  2262. }
  2263. /**
  2264. * Set the margin's horizontal and or vertical alignments from a string value.
  2265. * @param value can be: [<h:|horizontal:><left|right|center|stretch>], [<v:|vertical:><top|bottom|center|stretch>]
  2266. */
  2267. public setMarginalignment(value: string) {
  2268. this.marginAlignment.fromString(value);
  2269. this._updatePositioningState();
  2270. }
  2271. /**
  2272. * Check if there a marginAlignment specified (non null and not default)
  2273. */
  2274. public get _hasMarginAlignment(): boolean {
  2275. return (this._marginAlignment !== null && !this._marginAlignment.isDefault);
  2276. }
  2277. protected _updatePositioningState() {
  2278. let value = this._hasMargin || this._hasPadding || this.isSizeAuto;
  2279. // console.log(`${this.id} with parent ${this._parent ? this._parent.id : "[none]"} state: ${value} `);
  2280. this._changeFlags(SmartPropertyPrim.flagUsePositioning, value);
  2281. }
  2282. @instanceLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 21, pi => Prim2DBase.opacityProperty = pi)
  2283. /**
  2284. * Get/set the opacity of the whole primitive
  2285. */
  2286. public get opacity(): number {
  2287. return this._opacity;
  2288. }
  2289. public set opacity(value: number) {
  2290. if (value < 0) {
  2291. value = 0;
  2292. } else if (value > 1) {
  2293. value = 1;
  2294. }
  2295. if (this._opacity === value) {
  2296. return;
  2297. }
  2298. this._opacity = value;
  2299. this._setFlags(SmartPropertyPrim.flagActualOpacityDirty);
  2300. this._spreadActualOpacityChanged();
  2301. this._updateRenderMode();
  2302. }
  2303. @instanceLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 22, pi => Prim2DBase.scaleXProperty = pi, false, true)
  2304. /**
  2305. * Scale applied on the X axis of the primitive
  2306. */
  2307. public set scaleX(value: number) {
  2308. if (value <= 0) {
  2309. throw new Error("You can't set the scaleX to less or equal to 0");
  2310. }
  2311. this._scale.x = value;
  2312. this._setFlags(SmartPropertyPrim.flagActualScaleDirty);
  2313. this._spreadActualScaleDirty();
  2314. this._positioningDirty();
  2315. }
  2316. public get scaleX(): number {
  2317. return this._scale.x;
  2318. }
  2319. @instanceLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 23, pi => Prim2DBase.scaleYProperty = pi, false, true)
  2320. /**
  2321. * Scale applied on the Y axis of the primitive
  2322. */
  2323. public set scaleY(value: number) {
  2324. if (value <= 0) {
  2325. throw new Error("You can't set the scaleY to less or equal to 0");
  2326. }
  2327. this._scale.y = value;
  2328. this._setFlags(SmartPropertyPrim.flagActualScaleDirty);
  2329. this._spreadActualScaleDirty();
  2330. this._positioningDirty();
  2331. }
  2332. public get scaleY(): number {
  2333. return this._scale.y;
  2334. }
  2335. protected _spreadActualScaleDirty() {
  2336. for (let child of this._children) {
  2337. child._setFlags(SmartPropertyPrim.flagActualScaleDirty);
  2338. child._spreadActualScaleDirty();
  2339. }
  2340. }
  2341. /**
  2342. * Returns the actual scale of this Primitive, the value is computed from the scale property of this primitive, multiplied by the actualScale of its parent one (if any). The Vector2 object returned contains the scale for both X and Y axis
  2343. */
  2344. @instanceLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 24, pi => Prim2DBase.actualScaleProperty = pi, false, true)
  2345. public get actualScale(): Vector2 {
  2346. if (this._isFlagSet(SmartPropertyPrim.flagActualScaleDirty)) {
  2347. let cur = this._isFlagSet(SmartPropertyPrim.flagDontInheritParentScale) ? null : this.parent;
  2348. let sx = this.scaleX;
  2349. let sy = this.scaleY;
  2350. while (cur) {
  2351. sx *= cur.scaleX;
  2352. sy *= cur.scaleY;
  2353. cur = cur._isFlagSet(SmartPropertyPrim.flagDontInheritParentScale) ? null : cur.parent;
  2354. }
  2355. this._actualScale.copyFromFloats(sx, sy);
  2356. this._clearFlags(SmartPropertyPrim.flagActualScaleDirty);
  2357. }
  2358. return this._actualScale;
  2359. }
  2360. /**
  2361. * Get the actual Scale of the X axis, shortcut for this.actualScale.x
  2362. */
  2363. public get actualScaleX(): number {
  2364. return this.actualScale.x;
  2365. }
  2366. /**
  2367. * This method stores the actual global scale (including DesignMode and DPR related scales) in the given Vector2
  2368. * @param res the object that will receive the actual global scale: this is actualScale * DPRScale * DesignModeScale
  2369. */
  2370. public getActualGlobalScaleToRef(res: Vector2) {
  2371. let as = this.actualScale;
  2372. let cls = this.owner._canvasLevelScale || Prim2DBase._iv2;
  2373. res.x = as.x * cls.x;
  2374. res.y = as.y * cls.y;
  2375. }
  2376. /**
  2377. * Get the actual Scale of the Y axis, shortcut for this.actualScale.y
  2378. */
  2379. public get actualScaleY(): number {
  2380. return this.actualScale.y;
  2381. }
  2382. /**
  2383. * Get the actual opacity level, this property is computed from the opacity property, multiplied by the actualOpacity of its parent (if any)
  2384. */
  2385. public get actualOpacity(): number {
  2386. if (this._isFlagSet(SmartPropertyPrim.flagActualOpacityDirty)) {
  2387. let cur = this.parent;
  2388. let op = this.opacity;
  2389. while (cur) {
  2390. op *= cur.opacity;
  2391. cur = cur.parent;
  2392. }
  2393. this._actualOpacity = op;
  2394. this._clearFlags(SmartPropertyPrim.flagActualOpacityDirty);
  2395. }
  2396. return this._actualOpacity;
  2397. }
  2398. /**
  2399. * Get/set the layout engine to use for this primitive.
  2400. * The default layout engine is the CanvasLayoutEngine.
  2401. */
  2402. public get layoutEngine(): LayoutEngineBase {
  2403. if (!this._layoutEngine) {
  2404. this._layoutEngine = CanvasLayoutEngine.Singleton;
  2405. }
  2406. return this._layoutEngine;
  2407. }
  2408. public set layoutEngine(value: LayoutEngineBase) {
  2409. if (this._layoutEngine === value) {
  2410. return;
  2411. }
  2412. this._changeLayoutEngine(value);
  2413. }
  2414. /**
  2415. * Get/set the layout are of this primitive.
  2416. * The Layout area is the zone allocated by the Layout Engine for this particular primitive. Margins/Alignment will be computed based on this area.
  2417. * The setter should only be called by a Layout Engine class.
  2418. */
  2419. public get layoutArea(): Size {
  2420. return this._layoutArea;
  2421. }
  2422. public set layoutArea(val: Size) {
  2423. if (this._layoutArea && this._layoutArea.equals(val)) {
  2424. return;
  2425. }
  2426. this._positioningDirty();
  2427. this._setFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty);
  2428. if (this.parent) {
  2429. this.parent._setFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty | SmartPropertyPrim.flagGlobalTransformDirty);
  2430. }
  2431. if (!this._layoutArea) {
  2432. this._layoutArea = val.clone();
  2433. } else {
  2434. this._layoutArea.copyFrom(val);
  2435. }
  2436. }
  2437. /**
  2438. * Get/set the layout area position (relative to the parent primitive).
  2439. * The setter should only be called by a Layout Engine class.
  2440. */
  2441. public get layoutAreaPos(): Vector2 {
  2442. return this._layoutAreaPos;
  2443. }
  2444. public set layoutAreaPos(val: Vector2) {
  2445. if (this._layoutAreaPos && this._layoutAreaPos.equals(val)) {
  2446. return;
  2447. }
  2448. if (this.parent) {
  2449. this.parent._setFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty | SmartPropertyPrim.flagGlobalTransformDirty);
  2450. }
  2451. this._positioningDirty();
  2452. if (!this._layoutAreaPos) {
  2453. this._layoutAreaPos = val.clone();
  2454. } else {
  2455. this._layoutAreaPos.copyFrom(val);
  2456. }
  2457. this._setFlags(SmartPropertyPrim.flagLocalTransformDirty);
  2458. }
  2459. /**
  2460. * Define if the Primitive can be subject to intersection test or not (default is true)
  2461. */
  2462. public get isPickable(): boolean {
  2463. return this._isFlagSet(SmartPropertyPrim.flagIsPickable);
  2464. }
  2465. public set isPickable(value: boolean) {
  2466. this._changeFlags(SmartPropertyPrim.flagIsPickable, value);
  2467. }
  2468. /**
  2469. * Define if the Primitive acts as a container or not
  2470. * A container will encapsulate its children for interaction event.
  2471. * If it's not a container events will be process down to children if the primitive is not pickable.
  2472. * Default value is true
  2473. */
  2474. public get isContainer(): boolean {
  2475. return this._isFlagSet(SmartPropertyPrim.flagIsContainer);
  2476. }
  2477. public set isContainer(value: boolean) {
  2478. this._changeFlags(SmartPropertyPrim.flagIsContainer, value);
  2479. }
  2480. /**
  2481. * Return the depth level of the Primitive into the Canvas' Graph. A Canvas will be 0, its direct children 1, and so on.
  2482. */
  2483. public get hierarchyDepth(): number {
  2484. return this._hierarchyDepth;
  2485. }
  2486. /**
  2487. * Retrieve the Group that is responsible to render this primitive
  2488. */
  2489. public get renderGroup(): Group2D {
  2490. return this._renderGroup;
  2491. }
  2492. /**
  2493. * Get the global transformation matrix of the primitive
  2494. */
  2495. public get globalTransform(): Matrix2D {
  2496. if (!this._globalTransform || (this._globalTransformProcessStep !== this.owner._globalTransformProcessStep)) {
  2497. this.updateCachedStates(false);
  2498. }
  2499. return this._globalTransform;
  2500. }
  2501. /**
  2502. * return the global position of the primitive, relative to its canvas
  2503. */
  2504. public getGlobalPosition(): Vector2 {
  2505. let v = new Vector2(0, 0);
  2506. this.getGlobalPositionByRef(v);
  2507. return v;
  2508. }
  2509. /**
  2510. * return the global position of the primitive, relative to its canvas
  2511. * @param v the valid Vector2 object where the global position will be stored
  2512. */
  2513. public getGlobalPositionByRef(v: Vector2) {
  2514. v.x = this.globalTransform.m[4];
  2515. v.y = this.globalTransform.m[5];
  2516. }
  2517. /**
  2518. * Get invert of the global transformation matrix of the primitive
  2519. */
  2520. public get invGlobalTransform(): Matrix2D {
  2521. this._updateLocalTransform();
  2522. return this._invGlobalTransform;
  2523. }
  2524. /**
  2525. * Get the local transformation of the primitive
  2526. */
  2527. public get localTransform(): Matrix2D {
  2528. this._updateLocalTransform();
  2529. return this._localTransform;
  2530. }
  2531. public get localLayoutTransform(): Matrix2D {
  2532. this._updateLocalTransform();
  2533. return this._localLayoutTransform;
  2534. }
  2535. /**
  2536. * Get/set if the sprite rendering should be aligned to the target rendering device pixel or not
  2537. */
  2538. public get alignToPixel(): boolean {
  2539. return this._isFlagSet(SmartPropertyPrim.flagAlignPrimitive);
  2540. }
  2541. public set alignToPixel(value: boolean) {
  2542. this._changeFlags(SmartPropertyPrim.flagAlignPrimitive, value);
  2543. }
  2544. private static _bMinMax = Vector4.Zero();
  2545. private static _bMax = Vector2.Zero();
  2546. private static _bSize = Size.Zero();
  2547. private static _tpsBB = new BoundingInfo2D();
  2548. private static _tpsBB2 = new BoundingInfo2D();
  2549. /**
  2550. * Get the boundingInfo associated to the primitive and its children.
  2551. */
  2552. @logProp("", true)
  2553. public get boundingInfo(): BoundingInfo2D {
  2554. // Check if we must update the boundingInfo
  2555. if (this._isFlagSet(SmartPropertyPrim.flagBoundingInfoDirty)) {
  2556. if (this.owner) {
  2557. this.owner.boundingInfoRecomputeCounter.addCount(1, false);
  2558. }
  2559. C2DLogging.setPostMessage(() => "cache miss");
  2560. let sizedByContent = this.isSizedByContent;
  2561. if (sizedByContent) {
  2562. this._boundingInfo.clear();
  2563. } else {
  2564. this._boundingInfo.copyFrom(this.levelBoundingInfo);
  2565. }
  2566. if (this._children.length > 0) {
  2567. var contentBI = new BoundingInfo2D();
  2568. var tps = Prim2DBase._tpsBB2;
  2569. for (let curChild of this._children) {
  2570. if (curChild._isFlagSet(SmartPropertyPrim.flagNoPartOfLayout)) {
  2571. continue;
  2572. }
  2573. let bb = curChild.layoutBoundingInfo;
  2574. bb.transformToRef(curChild.localLayoutTransform, tps);
  2575. contentBI.unionToRef(tps, contentBI);
  2576. }
  2577. // Apply padding
  2578. if (this._hasPadding) {
  2579. let padding = this.padding;
  2580. let minmax = Prim2DBase._bMinMax;
  2581. contentBI.minMaxToRef(minmax);
  2582. this._paddingOffset.copyFromFloats(padding.leftPixels, padding.bottomPixels, padding.rightPixels, padding.topPixels);
  2583. let size = Prim2DBase._size2;
  2584. contentBI.sizeToRef(size);
  2585. this._getActualSizeFromContentToRef(size, this._paddingOffset, size);
  2586. minmax.z += this._paddingOffset.z + this._paddingOffset.x;
  2587. minmax.w += this._paddingOffset.w + this._paddingOffset.y;
  2588. BoundingInfo2D.CreateFromMinMaxToRef(minmax.x, minmax.z, minmax.y, minmax.w, contentBI);
  2589. } else {
  2590. this._paddingOffset.copyFromFloats(0, 0, 0, 0);
  2591. }
  2592. this._boundingInfo.unionToRef(contentBI, this._boundingInfo);
  2593. }
  2594. if (sizedByContent || !this._isFlagSet(SmartPropertyPrim.flagLevelBoundingInfoDirty)) {
  2595. this._clearFlags(SmartPropertyPrim.flagBoundingInfoDirty);
  2596. }
  2597. } else {
  2598. C2DLogging.setPostMessage(() => "cache hit");
  2599. }
  2600. return this._boundingInfo;
  2601. }
  2602. /**
  2603. * Get the boundingInfo of the primitive's content arranged by a layout Engine
  2604. * If a particular child is not arranged by layout, it's boundingInfo is used instead to produce something as accurate as possible
  2605. */
  2606. @logProp("", true)
  2607. public get layoutBoundingInfo(): BoundingInfo2D {
  2608. let usePositioning = this._isFlagSet(SmartPropertyPrim.flagUsePositioning);
  2609. if (this._isFlagSet(SmartPropertyPrim.flagLayoutBoundingInfoDirty)) {
  2610. C2DLogging.setPostMessage(() => "cache miss");
  2611. if (this._owner) {
  2612. this._owner.addLayoutBoundingInfoUpdateCounter(1);
  2613. }
  2614. if (this._isFlagSet(SmartPropertyPrim.flagLayoutDirty)) {
  2615. if (this._owner) {
  2616. this._owner.addUpdateLayoutCounter(1);
  2617. }
  2618. this._layoutEngine.updateLayout(this);
  2619. this._clearFlags(SmartPropertyPrim.flagLayoutDirty);
  2620. }
  2621. if (usePositioning) {
  2622. if (this._isFlagSet(SmartPropertyPrim.flagPositioningDirty)) {
  2623. this._updatePositioning();
  2624. }
  2625. // Safety check, code re entrance is a PITA in this part of the code
  2626. if (!this._layoutBoundingInfo) {
  2627. C2DLogging.setPostMessage(() => "re entrance detected, boundingInfo returned");
  2628. return this.boundingInfo;
  2629. }
  2630. if (this._isFlagSet(SmartPropertyPrim.flagPositioningDirty)) {
  2631. C2DLogging.setPostMessage(() => "couldn't compute positioning, boundingInfo returned");
  2632. return this.boundingInfo;
  2633. }
  2634. }
  2635. if (!usePositioning) {
  2636. let bi = this.boundingInfo;
  2637. if (!this._isFlagSet(SmartPropertyPrim.flagBoundingInfoDirty)) {
  2638. this._clearFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty);
  2639. }
  2640. return bi;
  2641. }
  2642. this._clearFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty);
  2643. } else {
  2644. C2DLogging.setPostMessage(() => "cache hit");
  2645. }
  2646. return usePositioning ? this._layoutBoundingInfo : this.boundingInfo;
  2647. }
  2648. /**
  2649. * Determine if the size is automatically computed or fixed because manually specified.
  2650. * Use the actualSize property to get the final/real size of the primitive
  2651. * @returns true if the size is automatically computed, false if it were manually specified.
  2652. */
  2653. public get isSizeAuto(): boolean {
  2654. let size = this._size;
  2655. return size == null || (size.width==null && size.height==null);
  2656. }
  2657. /**
  2658. * Determine if the horizontal size is automatically computed or fixed because manually specified.
  2659. * Use the actualSize property to get the final/real size of the primitive
  2660. * @returns true if the horizontal size is automatically computed, false if it were manually specified.
  2661. */
  2662. public get isHorizontalSizeAuto(): boolean {
  2663. let size = this._size;
  2664. return size == null || size.width == null;
  2665. }
  2666. /**
  2667. * Determine if the vertical size is automatically computed or fixed because manually specified.
  2668. * Use the actualSize property to get the final/real size of the primitive
  2669. * @returns true if the vertical size is automatically computed, false if it were manually specified.
  2670. */
  2671. public get isVerticalSizeAuto(): boolean {
  2672. let size = this._size;
  2673. return size == null || size.height == null;
  2674. }
  2675. /**
  2676. * Return true if this prim has an auto size which is set by the children's global bounding box
  2677. */
  2678. public get isSizedByContent(): boolean {
  2679. return (this._size == null) && (this._children.length > 0);
  2680. }
  2681. /**
  2682. * Determine if the position is automatically computed or fixed because manually specified.
  2683. * Use the actualPosition property to get the final/real position of the primitive
  2684. * @returns true if the position is automatically computed, false if it were manually specified.
  2685. */
  2686. public get isPositionAuto(): boolean {
  2687. return this._position == null;
  2688. }
  2689. /**
  2690. * Interaction with the primitive can be create using this Observable. See the PrimitivePointerInfo class for more information
  2691. */
  2692. public get pointerEventObservable(): Observable<PrimitivePointerInfo> {
  2693. return this._pointerEventObservable;
  2694. }
  2695. public get zActualOrderChangedObservable(): Observable<number> {
  2696. if (!this._actualZOrderChangedObservable) {
  2697. this._actualZOrderChangedObservable = new Observable<number>();
  2698. }
  2699. return this._actualZOrderChangedObservable;
  2700. }
  2701. public get displayDebugAreas(): boolean {
  2702. return this._displayDebugAreas;
  2703. }
  2704. public set displayDebugAreas(value: boolean) {
  2705. if (this._displayDebugAreas === value) {
  2706. return;
  2707. }
  2708. if (value === false) {
  2709. this._debugAreaGroup.dispose();
  2710. this._debugAreaGroup = null;
  2711. } else {
  2712. let layoutFill = "#F0808040"; // Red - Layout area
  2713. let layoutBorder = "#F08080FF";
  2714. let marginFill = "#F0F04040"; // Yellow - Margin area
  2715. let marginBorder = "#F0F040FF";
  2716. let paddingFill = "#F040F040"; // Magenta - Padding Area
  2717. let paddingBorder = "#F040F0FF";
  2718. let contentFill = "#40F0F040"; // Cyan - Content area
  2719. let contentBorder = "#40F0F0FF";
  2720. let s = new Size(10, 10);
  2721. let p = Vector2.Zero();
  2722. this._debugAreaGroup = new Group2D
  2723. (
  2724. { /*dontInheritParentScale: true,*/
  2725. parent: (this.parent!=null) ? this.parent : this, id: "###DEBUG AREA GROUP###", children:
  2726. [
  2727. new Group2D({
  2728. id: "###Layout Area###", position: p, size: s, children:
  2729. [
  2730. new Rectangle2D({ id: "###Layout Frame###", position: Vector2.Zero(), size: s, fill: null, border: layoutBorder }),
  2731. new Rectangle2D({ id: "###Layout Top###", position: Vector2.Zero(), size: s, fill: layoutFill }),
  2732. new Rectangle2D({ id: "###Layout Left###", position: Vector2.Zero(), size: s, fill: layoutFill }),
  2733. new Rectangle2D({ id: "###Layout Right###", position: Vector2.Zero(), size: s, fill: layoutFill }),
  2734. new Rectangle2D({ id: "###Layout Bottom###", position: Vector2.Zero(), size: s, fill: layoutFill })
  2735. ]
  2736. }),
  2737. new Group2D({
  2738. id: "###Margin Area###", position: p, size: s, children:
  2739. [
  2740. new Rectangle2D({ id: "###Margin Frame###", position: Vector2.Zero(), size: s, fill: null, border: marginBorder }),
  2741. new Rectangle2D({ id: "###Margin Top###", position: Vector2.Zero(), size: s, fill: marginFill }),
  2742. new Rectangle2D({ id: "###Margin Left###", position: Vector2.Zero(), size: s, fill: marginFill }),
  2743. new Rectangle2D({ id: "###Margin Right###", position: Vector2.Zero(), size: s, fill: marginFill }),
  2744. new Rectangle2D({ id: "###Margin Bottom###", position: Vector2.Zero(), size: s, fill: marginFill })
  2745. ]
  2746. }),
  2747. new Group2D({
  2748. id: "###Padding Area###", position: p, size: s, children:
  2749. [
  2750. new Rectangle2D({ id: "###Padding Frame###", position: Vector2.Zero(), size: s, fill: null, border: paddingBorder }),
  2751. new Rectangle2D({ id: "###Padding Top###", position: Vector2.Zero(), size: s, fill: paddingFill }),
  2752. new Rectangle2D({ id: "###Padding Left###", position: Vector2.Zero(), size: s, fill: paddingFill }),
  2753. new Rectangle2D({ id: "###Padding Right###", position: Vector2.Zero(), size: s, fill: paddingFill }),
  2754. new Rectangle2D({ id: "###Padding Bottom###", position: Vector2.Zero(), size: s, fill: paddingFill })
  2755. ]
  2756. }),
  2757. new Group2D({
  2758. id: "###Content Area###", position: p, size: s, children:
  2759. [
  2760. new Rectangle2D({ id: "###Content Frame###", position: Vector2.Zero(), size: s, fill: null, border: contentBorder }),
  2761. new Rectangle2D({ id: "###Content Top###", position: Vector2.Zero(), size: s, fill: contentFill }),
  2762. new Rectangle2D({ id: "###Content Left###", position: Vector2.Zero(), size: s, fill: contentFill }),
  2763. new Rectangle2D({ id: "###Content Right###", position: Vector2.Zero(), size: s, fill: contentFill }),
  2764. new Rectangle2D({ id: "###Content Bottom###", position: Vector2.Zero(), size: s, fill: contentFill })
  2765. ]
  2766. })
  2767. ]
  2768. }
  2769. );
  2770. this._debugAreaGroup._setFlags(SmartPropertyPrim.flagNoPartOfLayout);
  2771. this._updateDebugArea();
  2772. }
  2773. this._displayDebugAreas = value;
  2774. }
  2775. private static _updatingDebugArea = false;
  2776. private _updateDebugArea() {
  2777. if (Prim2DBase._updatingDebugArea === true) {
  2778. return;
  2779. }
  2780. Prim2DBase._updatingDebugArea = true;
  2781. let areaNames = ["Layout", "Margin", "Padding", "Content"];
  2782. let areaZones = ["Area", "Frame", "Top", "Left", "Right", "Bottom"];
  2783. let prims = new Array<Array<Prim2DBase>>(4);
  2784. // Get all the primitives used to display the areas
  2785. for (let i = 0; i < 4; i++) {
  2786. prims[i] = new Array<Prim2DBase>(6);
  2787. for (let j = 0; j < 6; j++) {
  2788. prims[i][j] = this._debugAreaGroup.findById(`###${areaNames[i]} ${areaZones[j]}###`);
  2789. if (j > 1) {
  2790. prims[i][j].levelVisible = false;
  2791. }
  2792. }
  2793. }
  2794. // Update the visibility status of layout/margin/padding
  2795. let hasLayout = this._layoutAreaPos != null;
  2796. let hasPos = (this.actualPosition.x!==0) || (this.actualPosition.y!==0);
  2797. let hasMargin = this._hasMargin;
  2798. let hasPadding = this._hasPadding;
  2799. prims[0][0].levelVisible = hasLayout;
  2800. prims[1][0].levelVisible = hasMargin;
  2801. prims[2][0].levelVisible = hasPadding;
  2802. prims[3][0].levelVisible = true;
  2803. // Current offset
  2804. let curOffset = Vector2.Zero();
  2805. // Store the area info of the layout area
  2806. let curAreaIndex = 0;
  2807. // Store data about each area
  2808. let areaInfo = new Array<{ off: Vector2, size: Size, min: Vector2, max: Vector2 }>(4);
  2809. let storeAreaInfo = (pos: Vector2, size: Size) => {
  2810. let min = pos.clone();
  2811. let max = pos.clone();
  2812. if (size.width > 0) {
  2813. max.x += size.width;
  2814. }
  2815. if (size.height > 0) {
  2816. max.y += size.height;
  2817. }
  2818. areaInfo[curAreaIndex++] = { off: pos, size: size, min: min, max: max };
  2819. }
  2820. let isCanvas = this instanceof Canvas2D;
  2821. let marginH = this._marginOffset.x + this._marginOffset.z;
  2822. let marginV = this._marginOffset.y + this._marginOffset.w;
  2823. let actualSize = this.actualSize.multiplyByFloats(isCanvas ? 1 : this.scaleX, isCanvas ? 1 : this.scaleY);
  2824. let w = hasLayout ? (this.layoutAreaPos.x + this.layoutArea.width) : (marginH + actualSize.width);
  2825. let h = hasLayout ? (this.layoutAreaPos.y + this.layoutArea.height) : (marginV + actualSize.height);
  2826. let pos = (!hasLayout && !hasMargin && !hasPadding && hasPos) ? this.actualPosition : Vector2.Zero();
  2827. storeAreaInfo(pos, new Size(w, h));
  2828. // Compute the layout related data
  2829. if (hasLayout) {
  2830. let layoutOffset = this.layoutAreaPos.clone();
  2831. storeAreaInfo(layoutOffset, (hasMargin || hasPadding) ? this.layoutArea.clone() : actualSize.clone());
  2832. curOffset = layoutOffset.clone();
  2833. }
  2834. // Compute margin data
  2835. if (hasMargin) {
  2836. let marginOffset = curOffset.clone();
  2837. marginOffset.x += this._marginOffset.x;
  2838. marginOffset.y += this._marginOffset.y;
  2839. let marginArea = actualSize;
  2840. storeAreaInfo(marginOffset, marginArea);
  2841. curOffset = marginOffset.clone();
  2842. }
  2843. if (hasPadding) {
  2844. let contentOffset = curOffset.clone();
  2845. contentOffset.x += this._paddingOffset.x;
  2846. contentOffset.y += this._paddingOffset.y;
  2847. let contentArea = this.contentArea;
  2848. storeAreaInfo(contentOffset, contentArea);
  2849. curOffset = curOffset.add(contentOffset);
  2850. }
  2851. // Helper function that set the pos and size of a given prim
  2852. let setArea = (i: number, j: number, pos: Vector2, size: Size) => {
  2853. prims[i][j].position = pos;
  2854. prims[i][j].size = size;
  2855. }
  2856. let setFullRect = (i: number, pos: Vector2, size: Size) => {
  2857. let plist = prims[i];
  2858. plist[2].levelVisible = true;
  2859. plist[3].levelVisible = false;
  2860. plist[4].levelVisible = false;
  2861. plist[5].levelVisible = false;
  2862. setArea(i, 1, pos, size);
  2863. setArea(i, 2, pos, size);
  2864. }
  2865. let setQuadRect = (i: number, areaIndex: number) => {
  2866. let plist = prims[i];
  2867. plist[2].levelVisible = true;
  2868. plist[3].levelVisible = true;
  2869. plist[4].levelVisible = true;
  2870. plist[5].levelVisible = true;
  2871. let ca = areaInfo[areaIndex];
  2872. let na = areaInfo[areaIndex + 1];
  2873. let tp = new Vector2(ca.min.x, na.max.y);
  2874. let ts = new Size(ca.size.width, ca.max.y - tp.y);
  2875. let lp = new Vector2(ca.min.x, na.min.y);
  2876. let ls = new Size(na.min.x - ca.min.x, na.max.y - na.min.y);
  2877. let rp = new Vector2(na.max.x, na.min.y);
  2878. let rs = new Size(ca.max.x - na.max.x, na.max.y - na.min.y);
  2879. let bp = new Vector2(ca.min.x, ca.min.y);
  2880. let bs = new Size(ca.size.width, na.min.y - ca.min.y);
  2881. // Frame
  2882. plist[1].position = ca.off;
  2883. plist[1].size = ca.size;
  2884. // Top rect
  2885. plist[2].position = tp;
  2886. plist[2].size = ts;
  2887. // Left rect
  2888. plist[3].position = lp;
  2889. plist[3].size = ls;
  2890. // Right rect
  2891. plist[4].position = rp;
  2892. plist[4].size = rs;
  2893. // Bottom rect
  2894. plist[5].position = bp;
  2895. plist[5].size = bs;
  2896. }
  2897. let areaCount = curAreaIndex;
  2898. curAreaIndex = 0;
  2899. // Available zones
  2900. let availableZones = [false, hasLayout, hasMargin, hasPadding, true];
  2901. for (let k = 1; k < 5; k++) {
  2902. if (availableZones[k]) {
  2903. let ai = areaInfo[curAreaIndex];
  2904. setArea(k-1, 0, Vector2.Zero(), ai.size);
  2905. // setArea(k-1, 1, Vector2.Zero(), ai.size);
  2906. if (k === 4) {
  2907. setFullRect(k-1, ai.off, ai.size);
  2908. } else {
  2909. setQuadRect(k-1, curAreaIndex);
  2910. }
  2911. ++curAreaIndex;
  2912. }
  2913. }
  2914. Prim2DBase._updatingDebugArea = false;
  2915. }
  2916. public findById(id: string): Prim2DBase {
  2917. if (this._id === id) {
  2918. return this;
  2919. }
  2920. for (let child of this._children) {
  2921. let r = child.findById(id);
  2922. if (r != null) {
  2923. return r;
  2924. }
  2925. }
  2926. }
  2927. protected onZOrderChanged() {
  2928. }
  2929. protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
  2930. return false;
  2931. }
  2932. /**
  2933. * Capture all the Events of the given PointerId for this primitive.
  2934. * Don't forget to call releasePointerEventsCapture when done.
  2935. * @param pointerId the Id of the pointer to capture the events from.
  2936. */
  2937. public setPointerEventCapture(pointerId: number): boolean {
  2938. return this.owner._setPointerCapture(pointerId, this);
  2939. }
  2940. /**
  2941. * Release a captured pointer made with setPointerEventCapture.
  2942. * @param pointerId the Id of the pointer to release the capture from.
  2943. */
  2944. public releasePointerEventsCapture(pointerId: number): boolean {
  2945. return this.owner._releasePointerCapture(pointerId, this);
  2946. }
  2947. private static _bypassGroup2DExclusion = false;
  2948. /**
  2949. * Make an intersection test with the primitive, all inputs/outputs are stored in the IntersectInfo2D class, see its documentation for more information.
  2950. * @param intersectInfo contains the settings of the intersection to perform, to setup before calling this method as well as the result, available after a call to this method.
  2951. */
  2952. public intersect(intersectInfo: IntersectInfo2D): boolean {
  2953. if (!intersectInfo) {
  2954. return false;
  2955. }
  2956. // If this is null it means this method is call for the first level, initialize stuffs
  2957. let firstLevel = !intersectInfo._globalPickPosition;
  2958. if (firstLevel) {
  2959. // Compute the pickPosition in global space and use it to find the local position for each level down, always relative from the world to get the maximum accuracy (and speed). The other way would have been to compute in local every level down relative to its parent's local, which wouldn't be as accurate (even if javascript number is 80bits accurate).
  2960. intersectInfo._globalPickPosition = Vector2.Zero();
  2961. this.globalTransform.transformPointToRef(intersectInfo.pickPosition, intersectInfo._globalPickPosition);
  2962. intersectInfo._localPickPosition = intersectInfo.pickPosition.clone();
  2963. intersectInfo.intersectedPrimitives = new Array<PrimitiveIntersectedInfo>();
  2964. intersectInfo.topMostIntersectedPrimitive = null;
  2965. }
  2966. if (!Prim2DBase._bypassGroup2DExclusion && this instanceof Group2D && (<Group2D><any>this).isCachedGroup && !(<Group2D><any>this).isRenderableGroup) {
  2967. // Important to call this before each return to allow a good recursion next time this intersectInfo is reused
  2968. intersectInfo._exit(firstLevel);
  2969. return false;
  2970. }
  2971. if (!intersectInfo.intersectHidden && !this.isVisible) {
  2972. // Important to call this before each return to allow a good recursion next time this intersectInfo is reused
  2973. intersectInfo._exit(firstLevel);
  2974. return false;
  2975. }
  2976. let id = this.id;
  2977. if (id != null && id.indexOf("__cachedSpriteOfGroup__") === 0) {
  2978. try {
  2979. Prim2DBase._bypassGroup2DExclusion = true;
  2980. let ownerGroup = this.getExternalData<Group2D>("__cachedGroup__");
  2981. if (!ownerGroup) {
  2982. return false;
  2983. }
  2984. return ownerGroup.intersect(intersectInfo);
  2985. } finally {
  2986. Prim2DBase._bypassGroup2DExclusion = false;
  2987. }
  2988. }
  2989. // If we're testing a cachedGroup, we must reject pointer outside its levelBoundingInfo because children primitives could be partially clipped outside so we must not accept them as intersected when it's the case (because they're not visually visible).
  2990. let isIntersectionTest = false;
  2991. if (this instanceof Group2D) {
  2992. let g = <Group2D><any>this;
  2993. isIntersectionTest = g.isCachedGroup;
  2994. }
  2995. if (isIntersectionTest && !this.levelBoundingInfo.doesIntersect(intersectInfo._localPickPosition)) {
  2996. // Important to call this before each return to allow a good recursion next time this intersectInfo is reused
  2997. intersectInfo._exit(firstLevel);
  2998. return false;
  2999. }
  3000. // Fast rejection test with boundingInfo
  3001. let boundingIntersected = true;
  3002. if (this.isPickable && !this.boundingInfo.doesIntersect(intersectInfo._localPickPosition)) {
  3003. if (this.isContainer) {
  3004. // Important to call this before each return to allow a good recursion next time this intersectInfo is reused
  3005. intersectInfo._exit(firstLevel);
  3006. return false;
  3007. }
  3008. boundingIntersected = false;
  3009. }
  3010. // We hit the boundingInfo that bounds this primitive and its children, now we have to test on the primitive of this level
  3011. let levelIntersectRes = false;
  3012. if (this.isPickable) {
  3013. levelIntersectRes = boundingIntersected && this.levelIntersect(intersectInfo);
  3014. if (levelIntersectRes) {
  3015. let pii = new PrimitiveIntersectedInfo(this, intersectInfo._localPickPosition.clone());
  3016. intersectInfo.intersectedPrimitives.push(pii);
  3017. if (!intersectInfo.topMostIntersectedPrimitive || (intersectInfo.topMostIntersectedPrimitive.prim.actualZOffset > pii.prim.actualZOffset)) {
  3018. intersectInfo.topMostIntersectedPrimitive = pii;
  3019. }
  3020. // If we must stop at the first intersection, we're done, quit!
  3021. if (intersectInfo.findFirstOnly) {
  3022. intersectInfo._exit(firstLevel);
  3023. return true;
  3024. }
  3025. }
  3026. }
  3027. // Recurse to children if needed
  3028. if (!levelIntersectRes || !intersectInfo.findFirstOnly) {
  3029. for (let curChild of this._children) {
  3030. // Don't test primitive not pick able or if it's hidden and we don't test hidden ones
  3031. if ((!curChild.isPickable && curChild.isContainer) || (!intersectInfo.intersectHidden && !curChild.isVisible)) {
  3032. continue;
  3033. }
  3034. // Must compute the localPickLocation for the children level
  3035. curChild.invGlobalTransform.transformPointToRef(intersectInfo._globalPickPosition, intersectInfo._localPickPosition);
  3036. // If we got an intersection with the child and we only need to find the first one, quit!
  3037. if (curChild.intersect(intersectInfo) && intersectInfo.findFirstOnly) {
  3038. intersectInfo._exit(firstLevel);
  3039. return true;
  3040. }
  3041. }
  3042. }
  3043. intersectInfo._exit(firstLevel);
  3044. return intersectInfo.isIntersected;
  3045. }
  3046. public intersectOtherPrim(other: Prim2DBase): boolean {
  3047. let setA = this.triList;
  3048. let setB = other.triList;
  3049. return Tri2DArray.doesIntersect(setA, setB, other.globalTransform.multiply(this.globalTransform.clone().invert()));
  3050. }
  3051. public get triList(): Tri2DArray {
  3052. if (this._primTriArrayDirty) {
  3053. this.updateTriArray();
  3054. this._primTriArrayDirty = false;
  3055. }
  3056. return this._primTriArray;
  3057. }
  3058. // This is the worst implementation, if the top level primitive doesn't override this method we will just store a quad that defines the bounding rect of the prim
  3059. protected updateTriArray() {
  3060. if (this._primTriArray == null) {
  3061. this._primTriArray = new Tri2DArray(2);
  3062. } else {
  3063. this._primTriArray.clear(2);
  3064. }
  3065. let size = this.actualSize;
  3066. let lb = new Vector2(0, 0);
  3067. let rt = new Vector2(size.width, size.height);
  3068. let lt = new Vector2(0, size.height);
  3069. let rb = new Vector2(size.width, 0);
  3070. this._primTriArray.storeTriangle(0, lb, lt, rt);
  3071. this._primTriArray.storeTriangle(1, lb, rt, rb);
  3072. }
  3073. /**
  3074. * Move a child object into a new position regarding its siblings to change its rendering order.
  3075. * You can also use the shortcut methods to move top/bottom: moveChildToTop, moveChildToBottom, moveToTop, moveToBottom.
  3076. * @param child the object to move
  3077. * @param previous the object which will be before "child", if child has to be the first among sibling, set "previous" to null.
  3078. */
  3079. public moveChild(child: Prim2DBase, previous: Prim2DBase): boolean {
  3080. if (child.parent !== this) {
  3081. return false;
  3082. }
  3083. let childIndex = this._children.indexOf(child);
  3084. let prevIndex = previous ? this._children.indexOf(previous) : -1;
  3085. if (!this._isFlagSet(SmartPropertyPrim.flagChildrenFlatZOrder)) {
  3086. this._setFlags(SmartPropertyPrim.flagZOrderDirty);
  3087. this._firstZDirtyIndex = Math.min(this._firstZDirtyIndex, prevIndex+1);
  3088. }
  3089. this._children.splice(prevIndex + 1, 0, this._children.splice(childIndex, 1)[0]);
  3090. return true;
  3091. }
  3092. /**
  3093. * Move the given child so it's displayed on the top of all its siblings
  3094. * @param child the primitive to move to the top
  3095. */
  3096. public moveChildToTop(child: Prim2DBase): boolean {
  3097. return this.moveChild(child, this._children[this._children.length - 1]);
  3098. }
  3099. /**
  3100. * Move the given child so it's displayed on the bottom of all its siblings
  3101. * @param child the primitive to move to the top
  3102. */
  3103. public moveChildToBottom(child: Prim2DBase): boolean {
  3104. return this.moveChild(child, null);
  3105. }
  3106. /**
  3107. * Move this primitive to be at the top among all its sibling
  3108. */
  3109. public moveToTop(): boolean {
  3110. if (this.parent == null) {
  3111. return false;
  3112. }
  3113. return this.parent.moveChildToTop(this);
  3114. }
  3115. /**
  3116. * Move this primitive to be at the bottom among all its sibling
  3117. */
  3118. public moveToBottom() {
  3119. if (this.parent == null) {
  3120. return false;
  3121. }
  3122. return this.parent.moveChildToBottom(this);
  3123. }
  3124. private addChild(child: Prim2DBase) {
  3125. child._parent = this;
  3126. this._boundingBoxDirty();
  3127. let flat = this._isFlagSet(SmartPropertyPrim.flagChildrenFlatZOrder);
  3128. if (flat) {
  3129. child._setFlags(SmartPropertyPrim.flagChildrenFlatZOrder);
  3130. child._setZOrder(this._zOrder, true);
  3131. child._zMax = this._zOrder;
  3132. } else {
  3133. this._setFlags(SmartPropertyPrim.flagZOrderDirty);
  3134. }
  3135. let length = this._children.push(child);
  3136. this._firstZDirtyIndex = Math.min(this._firstZDirtyIndex, length - 1);
  3137. child._setFlags(SmartPropertyPrim.flagActualOpacityDirty);
  3138. }
  3139. /**
  3140. * Dispose the primitive, remove it from its parent.
  3141. */
  3142. public dispose(): boolean {
  3143. if (!super.dispose()) {
  3144. return false;
  3145. }
  3146. if (this._isFlagSet(SmartPropertyPrim.flagCollisionActor)) {
  3147. this.owner._primitiveCollisionManager._removeActor(this);
  3148. this._actorInfo = null;
  3149. }
  3150. if (this._pointerEventObservable) {
  3151. this._pointerEventObservable.clear();
  3152. this._pointerEventObservable = null;
  3153. }
  3154. if (this._actionManager) {
  3155. this._actionManager.dispose();
  3156. this._actionManager = null;
  3157. }
  3158. this.owner.scene.stopAnimation(this);
  3159. // If there's a parent, remove this object from its parent list
  3160. if (this._parent) {
  3161. if (this instanceof Group2D) {
  3162. let g = <Group2D><any>this;
  3163. if (g.isRenderableGroup) {
  3164. let parentRenderable = <Group2D>this.parent.traverseUp(p => (p instanceof Group2D && p.isRenderableGroup));
  3165. if (parentRenderable != null) {
  3166. let l = parentRenderable._renderableData._childrenRenderableGroups;
  3167. let i = l.indexOf(g);
  3168. if (i !== -1) {
  3169. l.splice(i, 1);
  3170. }
  3171. }
  3172. }
  3173. }
  3174. let i = this._parent._children.indexOf(this);
  3175. if (i !== undefined) {
  3176. this._parent._children.splice(i, 1);
  3177. }
  3178. this._parent = null;
  3179. }
  3180. // Recurse dispose to children
  3181. if (this._children) {
  3182. while (this._children.length > 0) {
  3183. this._children[this._children.length - 1].dispose();
  3184. }
  3185. }
  3186. return true;
  3187. }
  3188. protected onPrimBecomesDirty() {
  3189. if (this._renderGroup && !this._isFlagSet(SmartPropertyPrim.flagPrimInDirtyList)) {
  3190. this._renderGroup._addPrimToDirtyList(this);
  3191. this._setFlags(SmartPropertyPrim.flagPrimInDirtyList);
  3192. }
  3193. }
  3194. public _needPrepare(): boolean {
  3195. return this._areSomeFlagsSet(SmartPropertyPrim.flagVisibilityChanged | SmartPropertyPrim.flagModelDirty | SmartPropertyPrim.flagModelUpdate | SmartPropertyPrim.flagNeedRefresh) || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep);
  3196. }
  3197. public _prepareRender(context: PrepareRender2DContext) {
  3198. let globalTransformStep = this.owner._globalTransformStep;
  3199. if (this._prepareProcessStep < globalTransformStep) {
  3200. this._prepareRenderPre(context);
  3201. this._prepareRenderPost(context);
  3202. this._prepareProcessStep = globalTransformStep;
  3203. }
  3204. }
  3205. public _prepareRenderPre(context: PrepareRender2DContext) {
  3206. }
  3207. public _prepareRenderPost(context: PrepareRender2DContext) {
  3208. // Don't recurse if it's a renderable group, the content will be processed by the group itself
  3209. if (this instanceof Group2D) {
  3210. var self: any = this;
  3211. if (self.isRenderableGroup) {
  3212. return;
  3213. }
  3214. }
  3215. // Check if we need to recurse the prepare to children primitives
  3216. // - must have children
  3217. // - the global transform of this level have changed, or
  3218. // - the visible state of primitive has changed
  3219. if (this._children.length > 0 && ((this._globalTransformProcessStep !== this._globalTransformStep) ||
  3220. this.checkPropertiesDirty(Prim2DBase.isVisibleProperty.flagId))) {
  3221. this._children.forEach(c => {
  3222. // As usual stop the recursion if we meet a renderable group
  3223. if (!(c instanceof Group2D && c.isRenderableGroup)) {
  3224. c._prepareRender(context);
  3225. }
  3226. });
  3227. }
  3228. // Finally reset the dirty flags as we've processed everything
  3229. this._clearFlags(SmartPropertyPrim.flagModelDirty);
  3230. this._instanceDirtyFlags = 0;
  3231. }
  3232. protected _canvasPreInit(settings: any) {
  3233. }
  3234. protected static _isCanvasInit: boolean = false;
  3235. protected static CheckParent(parent: Prim2DBase) { // TODO remove
  3236. //if (!Prim2DBase._isCanvasInit && !parent) {
  3237. // throw new Error("A Primitive needs a valid Parent, it can be any kind of Primitives based types, even the Canvas (with the exception that only Group2D can be direct child of a Canvas if the cache strategy used is TOPLEVELGROUPS)");
  3238. //}
  3239. }
  3240. protected updateCachedStatesOf(list: Prim2DBase[], recurse: boolean) {
  3241. for (let cur of list) {
  3242. cur.updateCachedStates(recurse);
  3243. }
  3244. }
  3245. private _parentLayoutDirty() {
  3246. if (!this._parent || this._parent.isDisposed) {
  3247. return;
  3248. }
  3249. this._parent._setLayoutDirty();
  3250. }
  3251. @logMethod("", true)
  3252. protected _setLayoutDirty() {
  3253. this.onPrimBecomesDirty();
  3254. this._setFlags(SmartPropertyPrim.flagLayoutDirty);
  3255. }
  3256. //private _checkPositionChange(): boolean {
  3257. // if (this.parent && this.parent.layoutEngine.isChildPositionAllowed === false) {
  3258. // console.log(`Can't manually set the position of ${this.id}, the Layout Engine of its parent doesn't allow it`);
  3259. // return false;
  3260. // }
  3261. // if (this._isFlagSet(SmartPropertyPrim.flagUsePositioning)) {
  3262. // if (<any>this instanceof Group2D && (<Group2D><any>this).trackedNode == null) {
  3263. // console.log(`You can't set the position/x/y of ${this.id} properties while positioning engine is used (margin, margin alignment and/or padding are set`);
  3264. // return false;
  3265. // }
  3266. // }
  3267. // return true;
  3268. //}
  3269. private _checkUseMargin(): boolean {
  3270. // Special cae: tracked node
  3271. if (<any>this instanceof Group2D && (<Group2D><any>this).trackedNode != null) {
  3272. return false;
  3273. }
  3274. return this._isFlagSet(SmartPropertyPrim.flagUsePositioning);
  3275. }
  3276. @logMethod("", true)
  3277. protected _positioningDirty() {
  3278. if (!this._isFlagSet(SmartPropertyPrim.flagUsePositioning)) {
  3279. return;
  3280. }
  3281. this.onPrimBecomesDirty();
  3282. this._setFlags(SmartPropertyPrim.flagPositioningDirty);
  3283. }
  3284. protected _spreadActualOpacityChanged() {
  3285. for (let child of this._children) {
  3286. child._setFlags(SmartPropertyPrim.flagActualOpacityDirty);
  3287. child._updateRenderMode();
  3288. child.onPrimBecomesDirty();
  3289. child._spreadActualOpacityChanged();
  3290. }
  3291. }
  3292. private _changeLayoutEngine(engine: LayoutEngineBase) {
  3293. this._layoutEngine = engine;
  3294. }
  3295. private static _t0: Matrix2D = new Matrix2D();
  3296. private static _t1: Matrix2D = new Matrix2D();
  3297. private static _t2: Matrix2D = new Matrix2D();
  3298. private static _v0: Vector2 = Vector2.Zero(); // Must stay with the value 0,0
  3299. private static _v30: Vector3 = Vector3.Zero(); // Must stay with the value 0,0,0
  3300. private static _iv2: Vector2 = new Vector2(1,1); // Must stay identity vector
  3301. private static _ts0 = Size.Zero();
  3302. private _updateLocalTransform(): boolean {
  3303. let tflags = Prim2DBase.actualPositionProperty.flagId | Prim2DBase.rotationProperty.flagId | Prim2DBase.scaleProperty.flagId | Prim2DBase.scaleXProperty.flagId | Prim2DBase.scaleYProperty.flagId | Prim2DBase.originProperty.flagId;
  3304. if (this.checkPropertiesDirty(tflags) || this._areSomeFlagsSet(SmartPropertyPrim.flagLocalTransformDirty | SmartPropertyPrim.flagPositioningDirty)) {
  3305. if (this.owner) {
  3306. this.owner.addupdateLocalTransformCounter(1);
  3307. }
  3308. // Check for positioning update
  3309. if (this._isFlagSet(SmartPropertyPrim.flagPositioningDirty)) {
  3310. this._updatePositioning();
  3311. }
  3312. var rot = this._rotation;
  3313. var local: Matrix2D;
  3314. let pos = this._position ? this.position : (this.layoutAreaPos || Prim2DBase._v0);
  3315. let postScale = this._postScale;
  3316. let canvasScale = Prim2DBase._iv2;
  3317. //let hasCanvasScale = false;
  3318. //if (this._parent instanceof Canvas2D) {
  3319. // hasCanvasScale = true;
  3320. // canvasScale = (this._parent as Canvas2D)._canvasLevelScale || Prim2DBase._iv2;
  3321. //}
  3322. let globalScale = this._scale.multiplyByFloats(/*postScale.x**/canvasScale.x, /*postScale.y**/canvasScale.y);
  3323. if ((this._origin.x === 0 && this._origin.y === 0) || this._hasMargin) {
  3324. local = Matrix2D.Compose(globalScale, rot, new Vector2(pos.x + this._marginOffset.x, pos.y + this._marginOffset.y));
  3325. this._localTransform = local;
  3326. this._localLayoutTransform = Matrix2D.Compose(globalScale, rot, new Vector2(pos.x, pos.y));
  3327. } else {
  3328. // -Origin offset
  3329. let t0 = Prim2DBase._t0;
  3330. let t1 = Prim2DBase._t1;
  3331. let t2 = Prim2DBase._t2;
  3332. let as = Prim2DBase._ts0;
  3333. as.copyFrom(this.actualSize);
  3334. //as.width /= postScale.x;
  3335. //as.height /= postScale.y;
  3336. Matrix2D.TranslationToRef((-as.width * this._origin.x), (-as.height * this._origin.y), t0);
  3337. // -Origin * rotation
  3338. Matrix2D.RotationToRef(rot, t1);
  3339. t0.multiplyToRef(t1, t2);
  3340. Matrix2D.ScalingToRef(this._scale.x, this._scale.y, t0);
  3341. t2.multiplyToRef(t0, t1);
  3342. Matrix2D.TranslationToRef((as.width * this._origin.x), (as.height * this._origin.y), t2);
  3343. t1.multiplyToRef(t2, t0);
  3344. Matrix2D.ScalingToRef(postScale.x, postScale.y, t1);
  3345. t0.multiplyToRef(t1, t2);
  3346. Matrix2D.TranslationToRef(pos.x + this._marginOffset.x, pos.y + this._marginOffset.y, t0);
  3347. t2.multiplyToRef(t0, this._localTransform);
  3348. //if (hasCanvasScale) {
  3349. // Matrix2D.ScalingToRef(canvasScale.x, canvasScale.y, Prim2DBase._t1);
  3350. // this._localTransform.multiplyToRef(Prim2DBase._t1, this._localTransform);
  3351. //}
  3352. this._localLayoutTransform = Matrix2D.Compose(globalScale, rot, pos);
  3353. }
  3354. this.clearPropertiesDirty(tflags);
  3355. this._setFlags(SmartPropertyPrim.flagGlobalTransformDirty);
  3356. this._clearFlags(SmartPropertyPrim.flagLocalTransformDirty);
  3357. return true;
  3358. }
  3359. return false;
  3360. }
  3361. private static _transMtx = Matrix2D.Zero();
  3362. @logMethod()
  3363. protected updateCachedStates(recurse: boolean) {
  3364. if (this.isDisposed) {
  3365. C2DLogging.setPostMessage(() => "disposed");
  3366. return;
  3367. }
  3368. let ownerProcessStep = this.owner._globalTransformProcessStep;
  3369. if (this._updateCachesProcessStep === ownerProcessStep) {
  3370. return;
  3371. }
  3372. this._updateCachesProcessStep = ownerProcessStep;
  3373. this.owner.addUpdateCachedStateCounter(1);
  3374. // Check if the parent is synced
  3375. if (this._parent && ((this._parent._globalTransformProcessStep !== this.owner._globalTransformProcessStep) || this._parent._areSomeFlagsSet(SmartPropertyPrim.flagLayoutDirty | SmartPropertyPrim.flagPositioningDirty | SmartPropertyPrim.flagZOrderDirty))) {
  3376. this._parent.updateCachedStates(false);
  3377. }
  3378. // Update Z-Order if needed
  3379. if (this._isFlagSet(SmartPropertyPrim.flagZOrderDirty)) {
  3380. this._updateZOrder();
  3381. }
  3382. // Update actualSize only if there' not positioning to recompute and the size changed
  3383. // Otherwise positioning will take care of it.
  3384. let sizeDirty = this.checkPropertiesDirty(Prim2DBase.sizeProperty.flagId);
  3385. if (!this._isFlagSet(SmartPropertyPrim.flagLayoutDirty) && !this._isFlagSet(SmartPropertyPrim.flagPositioningDirty) && sizeDirty) {
  3386. let size = this.size;
  3387. this.onPropertyChanged("actualSize", size, size, Prim2DBase.actualSizeProperty.flagId);
  3388. this.clearPropertiesDirty(Prim2DBase.sizeProperty.flagId);
  3389. }
  3390. let positioningDirty = this._isFlagSet(SmartPropertyPrim.flagPositioningDirty);
  3391. let positioningComputed = positioningDirty && !this._isFlagSet(SmartPropertyPrim.flagPositioningDirty);
  3392. // Check for layout update
  3393. if (this._isFlagSet(SmartPropertyPrim.flagLayoutDirty)) {
  3394. this.owner.addUpdateLayoutCounter(1);
  3395. this._layoutEngine.updateLayout(this);
  3396. this._clearFlags(SmartPropertyPrim.flagLayoutDirty);
  3397. }
  3398. let autoContentChanged = false;
  3399. if (this.isSizeAuto) {
  3400. if (!this._lastAutoSizeArea) {
  3401. autoContentChanged = this.actualSize!==null;
  3402. } else {
  3403. autoContentChanged = (!this._lastAutoSizeArea.equals(this.actualSize));
  3404. }
  3405. }
  3406. // Check for positioning update
  3407. if (!positioningComputed && (autoContentChanged || sizeDirty || this._isFlagSet(SmartPropertyPrim.flagPositioningDirty) || (this._parent && !this._parent.contentArea.equals(this._parentContentArea)))) {
  3408. this._updatePositioning();
  3409. if (sizeDirty) {
  3410. this.clearPropertiesDirty(Prim2DBase.sizeProperty.flagId);
  3411. }
  3412. positioningComputed = true;
  3413. }
  3414. if (positioningComputed && this._parent) {
  3415. this._parentContentArea.copyFrom(this._parent.contentArea);
  3416. }
  3417. // Check if we must update this prim
  3418. if (!this._globalTransform || (this._globalTransformProcessStep !== this.owner._globalTransformProcessStep) || (this._areSomeFlagsSet(SmartPropertyPrim.flagGlobalTransformDirty))) {
  3419. this.owner.addUpdateGlobalTransformCounter(1);
  3420. let curVisibleState = this.isVisible;
  3421. this.isVisible = (!this._parent || this._parent.isVisible) && this.levelVisible;
  3422. // Detect a change of visibility
  3423. this._changeFlags(SmartPropertyPrim.flagVisibilityChanged, curVisibleState !== this.isVisible);
  3424. // Get/compute the localTransform
  3425. let localDirty = this._updateLocalTransform();
  3426. let parentPaddingChanged = false;
  3427. let parentPaddingOffset: Vector2 = Prim2DBase._v0;
  3428. if (this._parent) {
  3429. parentPaddingOffset = new Vector2(this._parent._paddingOffset.x, this._parent._paddingOffset.y);
  3430. parentPaddingChanged = !parentPaddingOffset.equals(this._parentPaddingOffset);
  3431. }
  3432. // Check if there are changes in the parent that will force us to update the global matrix
  3433. let parentDirty = (this._parent != null) ? (this._parent._globalTransformStep !== this._parentTransformStep) : false;
  3434. // Check if we have to update the globalTransform
  3435. if (!this._globalTransform || localDirty || parentDirty || parentPaddingChanged || this._areSomeFlagsSet(SmartPropertyPrim.flagGlobalTransformDirty)) {
  3436. let globalTransform = this._parent ? this._parent._globalTransform : null;
  3437. let localTransform: Matrix2D;
  3438. Prim2DBase._transMtx.copyFrom(this._localTransform);
  3439. Prim2DBase._transMtx.m[4] += parentPaddingOffset.x;
  3440. Prim2DBase._transMtx.m[5] += parentPaddingOffset.y;
  3441. localTransform = Prim2DBase._transMtx;
  3442. this._globalTransform = this._parent ? localTransform.multiply(globalTransform) : localTransform.clone();
  3443. this._invGlobalTransform = Matrix2D.Invert(this._globalTransform);
  3444. this._levelBoundingInfo.dirtyWorldAABB();
  3445. this._boundingInfo.dirtyWorldAABB();
  3446. this._globalTransformStep = this.owner._globalTransformProcessStep + 1;
  3447. this._parentTransformStep = this._parent ? this._parent._globalTransformStep : 0;
  3448. this._clearFlags(SmartPropertyPrim.flagGlobalTransformDirty);
  3449. }
  3450. this._globalTransformProcessStep = this.owner._globalTransformProcessStep;
  3451. }
  3452. if (recurse) {
  3453. for (let child of this._children) {
  3454. // Stop the recursion if we meet a renderable group
  3455. child.updateCachedStates(!(child instanceof Group2D && child.isRenderableGroup));
  3456. }
  3457. }
  3458. }
  3459. private static _icPos = Vector2.Zero();
  3460. private static _icZone = Vector4.Zero();
  3461. private static _icArea = Size.Zero();
  3462. private static _size = Size.Zero();
  3463. private static _size2 = Size.Zero();
  3464. private static _size3 = Size.Zero();
  3465. private static _size4 = Size.Zero();
  3466. private static _pv0 = Vector2.Zero();
  3467. private static _curContentArea = Size.Zero();
  3468. private static _piv = new Vector2(1, 1);
  3469. private static _tbi = new BoundingInfo2D();
  3470. private static _pv1 = Vector2.Zero();
  3471. private static _pv2 = Vector2.Zero();
  3472. @logMethod()
  3473. private _updatePositioning() {
  3474. if (!this._isFlagSet(SmartPropertyPrim.flagUsePositioning)) {
  3475. C2DLogging.setPostMessage(() => "Not using positioning engine");
  3476. // Just in case, if may happen and if we don't clear some computation will keep going on forever
  3477. this._clearFlags(SmartPropertyPrim.flagPositioningDirty);
  3478. return;
  3479. }
  3480. let success = true;
  3481. // Check if re-entrance is occurring
  3482. if (this._isFlagSet(SmartPropertyPrim.flagComputingPositioning)/* || (hasMargin && !this._layoutArea)*/) {
  3483. if (!this._actualSize) {
  3484. this._actualSize = this.size.clone() || Size.Zero();
  3485. this._contentArea.copyFrom(this._actualSize);
  3486. }
  3487. if (!this._actualPosition) {
  3488. this._actualPosition = Vector2.Zero();
  3489. }
  3490. C2DLogging.setPostMessage(() => "Re entrance detected");
  3491. return;
  3492. }
  3493. if (this.owner) {
  3494. this.owner.addUpdatePositioningCounter(1);
  3495. }
  3496. // Set the flag to avoid re-entrance
  3497. this._setFlags(SmartPropertyPrim.flagComputingPositioning);
  3498. try {
  3499. let isSizeAuto = this.isSizeAuto;
  3500. let isVSizeAuto = this.isVerticalSizeAuto;
  3501. let isHSizeAuto = this.isHorizontalSizeAuto;
  3502. let ma = this._marginAlignment ? this._marginAlignment.clone() : new PrimitiveAlignment();
  3503. let levelScale = this._scale;
  3504. let primSize = this.size;
  3505. // If the primitive has no size and is autoSized without margin, then set a Stretch/Stretch margin alignment for the primitive to take all the available space
  3506. if (!this._hasMarginAlignment && (isSizeAuto && (primSize===Prim2DBase.nullSize || (primSize===this._internalSize && primSize.width===0 && primSize.height===0)))) {
  3507. if (isSizeAuto || this.actualSize.width == null) {
  3508. ma.horizontal = PrimitiveAlignment.AlignStretch;
  3509. }
  3510. if (isSizeAuto || this.actualSize.height == null) {
  3511. ma.vertical = PrimitiveAlignment.AlignStretch;
  3512. }
  3513. }
  3514. let transformedBSize = Prim2DBase._size3;
  3515. let bSize = Prim2DBase._size4;
  3516. let bi = this.boundingInfo;
  3517. if (this._isFlagSet(SmartPropertyPrim.flagBoundingInfoDirty)) {
  3518. success = false;
  3519. }
  3520. let tbi = Prim2DBase._tbi;
  3521. bi.transformToRef(Matrix2D.Rotation(this.rotation), tbi);
  3522. tbi.sizeToRef(transformedBSize);
  3523. bi.sizeToRef(bSize);
  3524. bi.extent.subtractToRef(bi.center, Prim2DBase._pv1);
  3525. tbi.center.subtractToRef(tbi.extent, Prim2DBase._pv2);
  3526. let transbi = Prim2DBase._pv2.add(Prim2DBase._pv1);
  3527. let setSize = false;
  3528. let hasMargin = (this._margin !== null && !this._margin.isDefault) || (ma !== null && !ma.isDefault);
  3529. let primNewSize: Size = Prim2DBase._size;
  3530. let hasH = false;
  3531. let hasV = false;
  3532. //let paddingApplied = false;
  3533. let hasPadding = this._hasPadding;
  3534. // Compute the size
  3535. // The size is the size of the prim or the computed one if there's a marginAlignment of Stretch
  3536. if (hasMargin) {
  3537. let layoutArea = this.layoutArea;
  3538. if (layoutArea /*&& layoutArea.width >= size.width */&& ma.horizontal === PrimitiveAlignment.AlignStretch) {
  3539. this.margin.computeWithAlignment(layoutArea, primSize, ma, levelScale, this._marginOffset, primNewSize, false, PrimitiveThickness.ComputeH);
  3540. hasH = true;
  3541. setSize = true;
  3542. }
  3543. if (layoutArea /*&& layoutArea.height >= size.height */&& ma.vertical === PrimitiveAlignment.AlignStretch) {
  3544. this.margin.computeWithAlignment(layoutArea, primSize, ma, levelScale, this._marginOffset, primNewSize, false, PrimitiveThickness.ComputeV);
  3545. hasV = true;
  3546. setSize = true;
  3547. }
  3548. }
  3549. if (!hasH) {
  3550. // If the Horizontal size is Auto, we have to compute it from its content and padding
  3551. if (isHSizeAuto) {
  3552. primNewSize.width = bSize.width;
  3553. setSize = true;
  3554. } else {
  3555. primNewSize.width = primSize.width;
  3556. }
  3557. }
  3558. if (!hasV) {
  3559. // If the Vertical size is Auto, we have to compute it from its content and padding
  3560. if (isVSizeAuto) {
  3561. primNewSize.height = bSize.height;
  3562. setSize = true;
  3563. } else {
  3564. primNewSize.height = primSize.height;
  3565. }
  3566. }
  3567. Prim2DBase._curContentArea.copyFrom(this._contentArea);
  3568. if (hasPadding) {
  3569. let area = Prim2DBase._icArea;
  3570. let zone = Prim2DBase._icZone;
  3571. this._getInitialContentAreaToRef(primNewSize, zone, area);
  3572. area.width = Math.max(0, area.width);
  3573. area.height = Math.max(0, area.height);
  3574. this.padding.compute(area, this._paddingOffset, Prim2DBase._size2);
  3575. if (!isHSizeAuto) {
  3576. this._paddingOffset.x += zone.x;
  3577. this._paddingOffset.z -= zone.z;
  3578. this._contentArea.width = Prim2DBase._size2.width;
  3579. }
  3580. if (!isVSizeAuto) {
  3581. this._paddingOffset.y += zone.y;
  3582. this._paddingOffset.w -= zone.w;
  3583. this._contentArea.height = Prim2DBase._size2.height;
  3584. }
  3585. } else {
  3586. this._contentArea.copyFrom(primNewSize);
  3587. }
  3588. if (!Prim2DBase._curContentArea.equals(this._contentArea)) {
  3589. this._setLayoutDirty();
  3590. }
  3591. // Finally we apply margin to determine the position
  3592. if (hasMargin) {
  3593. let layoutArea = this.layoutArea;
  3594. let mo = this._marginOffset;
  3595. let margin = this.margin;
  3596. // We compute margin only if the layoutArea is "real": a valid object with dimensions greater than 0
  3597. // otherwise sometimes this code would be triggered with and invalid layoutArea, resulting to an invalid positioning
  3598. // So we make sure with compute alignment only if the layoutArea is good
  3599. if (layoutArea && layoutArea.width > 0 && layoutArea.height > 0) {
  3600. margin.computeWithAlignment(layoutArea, transformedBSize, ma, levelScale, mo, Prim2DBase._size2);
  3601. } else {
  3602. mo.copyFromFloats(0, 0, 0, 0);
  3603. }
  3604. let tbi = Prim2DBase._tpsBB;
  3605. tbi.copyFrom(bi);
  3606. let minmax = Prim2DBase._bMinMax;
  3607. tbi.minMaxToRef(minmax);
  3608. minmax.z += margin.leftPixels + margin.rightPixels;
  3609. minmax.w += margin.topPixels + margin.bottomPixels;
  3610. BoundingInfo2D.CreateFromMinMaxToRef(minmax.x, minmax.z, minmax.y, minmax.w, tbi);
  3611. // Check if the layoutBoundingInfo changed
  3612. let changed = false;
  3613. if (!this._layoutBoundingInfo) {
  3614. this._layoutBoundingInfo = tbi.clone();
  3615. changed = true;
  3616. } else if (!this._layoutBoundingInfo.equals(tbi)) {
  3617. this._layoutBoundingInfo.copyFrom(tbi);
  3618. changed = true;
  3619. }
  3620. if (changed) {
  3621. let p = this._parent;
  3622. while (p) {
  3623. if (p.isSizedByContent) {
  3624. p._setFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty);
  3625. p.onPrimitivePropertyDirty(Prim2DBase.actualSizeProperty.flagId);
  3626. } else {
  3627. break;
  3628. }
  3629. p = p._parent;
  3630. }
  3631. this.onPrimitivePropertyDirty(Prim2DBase.actualSizeProperty.flagId);
  3632. }
  3633. }
  3634. let lap = this.layoutAreaPos;
  3635. this._marginOffset.x -= transbi.x * levelScale.x;
  3636. this._marginOffset.y -= transbi.y * levelScale.y;
  3637. this.actualPosition = new Vector2(this._marginOffset.x + (lap ? lap.x : 0), this._marginOffset.y + (lap ? lap.y : 0));
  3638. // if (setSize) {
  3639. this.actualSize = primNewSize.clone();
  3640. // }
  3641. this._setFlags(SmartPropertyPrim.flagLocalTransformDirty);
  3642. if (isSizeAuto) {
  3643. this._lastAutoSizeArea = this.actualSize;
  3644. }
  3645. if (this.displayDebugAreas) {
  3646. this._updateDebugArea();
  3647. }
  3648. } finally {
  3649. C2DLogging.setPostMessage(() => "Succeeded");
  3650. this._clearFlags(SmartPropertyPrim.flagComputingPositioning);
  3651. // Remove dirty flag
  3652. if (success) {
  3653. this._clearFlags(SmartPropertyPrim.flagPositioningDirty);
  3654. }
  3655. }
  3656. }
  3657. /**
  3658. * Get the content are of this primitive, this area is computed the primitive size and using the padding property.
  3659. * Children of this primitive will be positioned relative to the bottom/left corner of this area.
  3660. */
  3661. public get contentArea(): Size {
  3662. if (this._isFlagSet(SmartPropertyPrim.flagUsePositioning)) {
  3663. if (this._isFlagSet(SmartPropertyPrim.flagPositioningDirty)) {
  3664. this._updatePositioning();
  3665. }
  3666. return this._contentArea;
  3667. } else {
  3668. return this.size;
  3669. }
  3670. }
  3671. public _patchHierarchy(owner: Canvas2D) {
  3672. if (this._owner == null) {
  3673. this._owner = owner;
  3674. this.onSetOwner();
  3675. this._setFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty);
  3676. }
  3677. // The only place we initialize the _renderGroup is this method, if it's set, we already been there, no need to execute more
  3678. if (this._renderGroup != null) {
  3679. return;
  3680. }
  3681. if (this instanceof Group2D) {
  3682. var group: any = this;
  3683. group.detectGroupStates();
  3684. if (group._trackedNode && !group._isFlagSet(SmartPropertyPrim.flagTrackedGroup)) {
  3685. group.owner._registerTrackedNode(this);
  3686. }
  3687. }
  3688. this._renderGroup = <Group2D>this.traverseUp(p => p instanceof Group2D && p.isRenderableGroup);
  3689. if (this._parent) {
  3690. this._parentLayoutDirty();
  3691. }
  3692. // Make sure the prim is in the dirtyList if it should be
  3693. if (this._renderGroup && this.isDirty) {
  3694. let list = this._renderGroup._renderableData._primDirtyList;
  3695. let i = list.indexOf(this);
  3696. if (i === -1) {
  3697. this._setFlags(SmartPropertyPrim.flagPrimInDirtyList);
  3698. list.push(this);
  3699. }
  3700. }
  3701. // Recurse
  3702. for (let child of this._children) {
  3703. child._hierarchyDepth = this._hierarchyDepth + 1;
  3704. child._patchHierarchy(owner);
  3705. }
  3706. }
  3707. protected onSetOwner() {
  3708. }
  3709. private static _zOrderChangedNotifList = new Array<Prim2DBase>();
  3710. private static _zRebuildReentrency = false;
  3711. private _updateZOrder() {
  3712. let prevLinPos = this._primLinearPosition;
  3713. let startI = 0;
  3714. let startZ = this._zOrder;
  3715. // We must start rebuilding Z-Order from the Prim before the first one that changed, because we know its Z-Order is correct, so are its children, but it's better to recompute everything from this point instead of finding the last valid children
  3716. let childrenCount = this._children.length;
  3717. if (this._firstZDirtyIndex > 0) {
  3718. if ((this._firstZDirtyIndex - 1) < childrenCount) {
  3719. let prevPrim = this._children[this._firstZDirtyIndex - 1];
  3720. prevLinPos = prevPrim._primLinearPosition;
  3721. startI = this._firstZDirtyIndex - 1;
  3722. startZ = prevPrim._zOrder;
  3723. }
  3724. }
  3725. let startPos = prevLinPos;
  3726. // Update the linear position of the primitive from the first one to the last inside this primitive, compute the total number of prim traversed
  3727. Prim2DBase._totalCount = 0;
  3728. for (let i = startI; i < childrenCount; i++) {
  3729. let child = this._children[i];
  3730. prevLinPos = child._updatePrimitiveLinearPosition(prevLinPos);
  3731. }
  3732. // Compute the new Z-Order for all the primitives
  3733. // Add 20% to the current total count to reserve space for future insertions, except if we're rebuilding due to a zMinDelta reached
  3734. let zDelta = (this._zMax - startZ) / (Prim2DBase._totalCount * (Prim2DBase._zRebuildReentrency ? 1 : 1.2));
  3735. // If the computed delta is less than the smallest allowed by the depth buffer, we rebuild the Z-Order from the very beginning of the primitive's children (that is, the first) to redistribute uniformly the Z.
  3736. if (zDelta < Canvas2D._zMinDelta) {
  3737. // Check for re-entrance, if the flag is true we already attempted a rebuild but couldn't get a better zDelta, go up in the hierarchy to rebuilt one level up, hoping to get this time a decent delta, otherwise, recurse until we got it or when no parent is reached, which would mean the canvas would have more than 16 millions of primitives...
  3738. if (Prim2DBase._zRebuildReentrency) {
  3739. let p = this._parent;
  3740. if (p == null) {
  3741. // Can't find a good Z delta and we're in the canvas, which mean we're dealing with too many objects (which should never happen, but well...)
  3742. console.log(`Can't compute Z-Order for ${this.id}'s children, zDelta is too small, Z-Order is now in an unstable state`);
  3743. Prim2DBase._zRebuildReentrency = false;
  3744. return;
  3745. }
  3746. p._firstZDirtyIndex = 0;
  3747. return p._updateZOrder();
  3748. }
  3749. Prim2DBase._zRebuildReentrency = true;
  3750. this._firstZDirtyIndex = 0;
  3751. this._updateZOrder();
  3752. Prim2DBase._zRebuildReentrency = false;
  3753. }
  3754. for (let i = startI; i < childrenCount; i++) {
  3755. let child = this._children[i];
  3756. child._updatePrimitiveZOrder(startPos, startZ, zDelta);
  3757. }
  3758. // Notify the Observers that we found during the Z change (we do it after to avoid any kind of re-entrance)
  3759. for (let p of Prim2DBase._zOrderChangedNotifList) {
  3760. p._actualZOrderChangedObservable.notifyObservers(p.actualZOffset);
  3761. }
  3762. Prim2DBase._zOrderChangedNotifList.splice(0);
  3763. this._firstZDirtyIndex = Prim2DBase._bigInt;
  3764. this._clearFlags(SmartPropertyPrim.flagZOrderDirty);
  3765. }
  3766. private static _totalCount: number = 0;
  3767. private _updatePrimitiveLinearPosition(prevLinPos: number): number {
  3768. if (this.isManualZOrder) {
  3769. return prevLinPos;
  3770. }
  3771. this._primLinearPosition = ++prevLinPos;
  3772. Prim2DBase._totalCount++;
  3773. // Check for the FlatZOrder, which means the children won't have a dedicated Z-Order but will all share the same (unique) one.
  3774. if (!this._isFlagSet(SmartPropertyPrim.flagChildrenFlatZOrder)) {
  3775. for (let child of this._children) {
  3776. prevLinPos = child._updatePrimitiveLinearPosition(prevLinPos);
  3777. }
  3778. }
  3779. return prevLinPos;
  3780. }
  3781. private _updatePrimitiveZOrder(startPos: number, startZ: number, deltaZ: number): number {
  3782. if (this.isManualZOrder) {
  3783. return null;
  3784. }
  3785. let newZ = startZ + ((this._primLinearPosition - startPos) * deltaZ);
  3786. let isFlat = this._isFlagSet(SmartPropertyPrim.flagChildrenFlatZOrder);
  3787. this._setZOrder(newZ, false);
  3788. if (this._isFlagSet(SmartPropertyPrim.flagZOrderDirty)) {
  3789. this._firstZDirtyIndex = Prim2DBase._bigInt;
  3790. this._clearFlags(SmartPropertyPrim.flagZOrderDirty);
  3791. }
  3792. let curZ: number = newZ;
  3793. // Check for the FlatZOrder, which means the children won't have a dedicated Z-Order but will all share the same (unique) one.
  3794. if (isFlat) {
  3795. if (this._children.length > 0) {
  3796. //let childrenZOrder = startZ + ((this._children[0]._primLinearPosition - startPos) * deltaZ);
  3797. for (let child of this._children) {
  3798. child._updatePrimitiveFlatZOrder(this._zOrder);
  3799. }
  3800. }
  3801. } else {
  3802. for (let child of this._children) {
  3803. let r = child._updatePrimitiveZOrder(startPos, startZ, deltaZ);
  3804. if (r != null) {
  3805. curZ = r;
  3806. }
  3807. }
  3808. }
  3809. this._zMax = isFlat ? newZ : (curZ + deltaZ);
  3810. return curZ;
  3811. }
  3812. private _updatePrimitiveFlatZOrder(newZ: number) {
  3813. if (this.isManualZOrder) {
  3814. return;
  3815. }
  3816. this._setZOrder(newZ, false);
  3817. this._zMax = newZ;
  3818. if (this._isFlagSet(SmartPropertyPrim.flagZOrderDirty)) {
  3819. this._firstZDirtyIndex = Prim2DBase._bigInt;
  3820. this._clearFlags(SmartPropertyPrim.flagZOrderDirty);
  3821. }
  3822. for (let child of this._children) {
  3823. child._updatePrimitiveFlatZOrder(newZ);
  3824. }
  3825. }
  3826. private _setZOrder(newZ: number, directEmit: boolean) {
  3827. if (newZ !== this._zOrder) {
  3828. this._zOrder = newZ;
  3829. this.onPrimBecomesDirty();
  3830. this.onZOrderChanged();
  3831. if (this._actualZOrderChangedObservable && this._actualZOrderChangedObservable.hasObservers()) {
  3832. if (directEmit) {
  3833. this._actualZOrderChangedObservable.notifyObservers(newZ);
  3834. } else {
  3835. Prim2DBase._zOrderChangedNotifList.push(this);
  3836. }
  3837. }
  3838. }
  3839. }
  3840. protected _updateRenderMode() {
  3841. }
  3842. /**
  3843. * This method is used to alter the contentArea of the Primitive before margin is applied.
  3844. * In most of the case you won't need to override this method, but it can prove some usefulness, check the Rectangle2D class for a concrete application.
  3845. * @param primSize the current size of the primitive
  3846. * @param initialContentPosition the position of the initial content area to compute, a valid object is passed, you have to set its properties. PLEASE ROUND the values, we're talking about pixels and fraction of them is not a good thing! x, y, z, w area left, bottom, right, top
  3847. * @param initialContentArea the size of the initial content area to compute, a valid object is passed, you have to set its properties. PLEASE ROUND the values, we're talking about pixels and fraction of them is not a good thing!
  3848. */
  3849. protected _getInitialContentAreaToRef(primSize: Size, initialContentPosition: Vector4, initialContentArea: Size) {
  3850. initialContentArea.copyFrom(primSize);
  3851. initialContentPosition.x = initialContentPosition.y = initialContentPosition.z = initialContentPosition.w = 0;
  3852. }
  3853. /**
  3854. * This method is used to calculate the new size of the primitive based on the content which must stay the same
  3855. * Check the Rectangle2D implementation for a concrete application.
  3856. * @param primSize the current size of the primitive
  3857. * @param newPrimSize the new size of the primitive. PLEASE ROUND THE values, we're talking about pixels and fraction of them are not our friends!
  3858. */
  3859. protected _getActualSizeFromContentToRef(primSize: Size, paddingOffset: Vector4, newPrimSize: Size) {
  3860. newPrimSize.copyFrom(primSize);
  3861. }
  3862. /**
  3863. * Get/set the layout data to use for this primitive.
  3864. */
  3865. public get layoutData(): ILayoutData {
  3866. return this._layoutData;
  3867. }
  3868. public set layoutData(value: ILayoutData) {
  3869. if (this._layoutData === value) {
  3870. return;
  3871. }
  3872. this._layoutData = value;
  3873. }
  3874. private _owner: Canvas2D;
  3875. private _parent: Prim2DBase;
  3876. private _actionManager: ActionManager;
  3877. protected _children: Array<Prim2DBase>;
  3878. private _renderGroup: Group2D;
  3879. protected _hierarchyDepth: number;
  3880. protected _zOrder: number;
  3881. private _manualZOrder: number;
  3882. protected _zMax: number;
  3883. private _firstZDirtyIndex: number;
  3884. private _primLinearPosition: number;
  3885. private _margin: PrimitiveThickness;
  3886. private _padding: PrimitiveThickness;
  3887. private _marginAlignment: PrimitiveAlignment;
  3888. public _pointerEventObservable: Observable<PrimitivePointerInfo>;
  3889. private _actualZOrderChangedObservable: Observable<number>;
  3890. private _id: string;
  3891. private _position: Vector2;
  3892. private _actualPosition: Vector2;
  3893. protected _size: Size;
  3894. protected _actualSize: Size;
  3895. private _internalSize: Size;
  3896. protected _minSize: Size;
  3897. protected _maxSize: Size;
  3898. protected _desiredSize: Size;
  3899. private _layoutEngine: LayoutEngineBase;
  3900. private _marginOffset: Vector4;
  3901. private _paddingOffset: Vector4;
  3902. private _parentPaddingOffset: Vector2;
  3903. private _parentContentArea: Size;
  3904. private _lastAutoSizeArea: Size;
  3905. private _layoutAreaPos: Vector2;
  3906. private _layoutArea: Size;
  3907. private _layoutData: ILayoutData;
  3908. private _contentArea: Size;
  3909. private _rotation: number;
  3910. private _scale: Vector2;
  3911. protected _postScale: Vector2;
  3912. private _origin: Vector2;
  3913. protected _opacity: number;
  3914. private _actualOpacity: number;
  3915. private _actualScale : Vector2;
  3916. private _displayDebugAreas: boolean;
  3917. private _debugAreaGroup: Group2D;
  3918. private _actorInfo: ActorInfoBase;
  3919. // Stores the step of the parent for which the current global transform was computed
  3920. // If the parent has a new step, it means this prim's global transform must be updated
  3921. protected _parentTransformStep: number;
  3922. // Stores the step corresponding of the global transform for this prim
  3923. // If a child prim has an older _parentTransformStep it means the child's transform should be updated
  3924. protected _globalTransformStep: number;
  3925. // Stores the previous
  3926. protected _globalTransformProcessStep: number;
  3927. protected _prepareProcessStep: number;
  3928. protected _updateCachesProcessStep: number;
  3929. protected _localTransform: Matrix2D;
  3930. protected _localLayoutTransform: Matrix2D;
  3931. protected _globalTransform: Matrix2D;
  3932. protected _invGlobalTransform: Matrix2D;
  3933. // Intersection related data
  3934. protected _primTriArrayDirty: boolean;
  3935. protected _primTriArray: Tri2DArray;
  3936. }
  3937. }