processing.js 421 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292
  1. /*
  2. P R O C E S S I N G . J S - @VERSION@
  3. a port of the Processing visualization language
  4. License : MIT
  5. Developer : John Resig: http://ejohn.org
  6. Web Site : http://processingjs.org
  7. Java Version : http://processing.org
  8. Github Repo. : http://github.com/jeresig/processing-js
  9. Bug Tracking : http://processing-js.lighthouseapp.com
  10. Mozilla POW! : http://wiki.Mozilla.org/Education/Projects/ProcessingForTheWeb
  11. Maintained by : Seneca: http://zenit.senecac.on.ca/wiki/index.php/Processing.js
  12. Hyper-Metrix: http://hyper-metrix.com/#Processing
  13. BuildingSky: http://weare.buildingsky.net/pages/processing-js
  14. */
  15. (function() {
  16. var undef; // intentionally left undefined
  17. var ajax = function ajax(url) {
  18. var xhr = new XMLHttpRequest();
  19. xhr.open("GET", url, false);
  20. xhr.setRequestHeader("If-Modified-Since", "Fri, 1 Jan 1960 00:00:00 GMT");
  21. xhr.send(null);
  22. // failed request?
  23. if (xhr.status !== 200 && xhr.status !== 0) { throw ("XMLHttpRequest failed, status code " + xhr.status); }
  24. return xhr.responseText;
  25. };
  26. var PVector = function(x, y, z) {
  27. this.x = x || 0;
  28. this.y = y || 0;
  29. this.z = z || 0;
  30. },
  31. createPVectorMethod = function(method) {
  32. return function(v1, v2) {
  33. var v = v1.get();
  34. v[method](v2);
  35. return v;
  36. };
  37. },
  38. createSimplePVectorMethod = function(method) {
  39. return function(v1, v2) {
  40. return v1[method](v2);
  41. };
  42. },
  43. simplePVMethods = "dist dot cross".split(" "),
  44. method = simplePVMethods.length;
  45. PVector.angleBetween = function(v1, v2) {
  46. return Math.acos(v1.dot(v2) / (v1.mag() * v2.mag()));
  47. };
  48. // Common vector operations for PVector
  49. PVector.prototype = {
  50. set: function(v, y, z) {
  51. if (arguments.length === 1) {
  52. this.set(v.x || v[0], v.y || v[1], v.z || v[2]);
  53. } else {
  54. this.x = v;
  55. this.y = y;
  56. this.z = z;
  57. }
  58. },
  59. get: function() {
  60. return new PVector(this.x, this.y, this.z);
  61. },
  62. mag: function() {
  63. return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
  64. },
  65. add: function(v, y, z) {
  66. if (arguments.length === 3) {
  67. this.x += v;
  68. this.y += y;
  69. this.z += z;
  70. } else if (arguments.length === 1) {
  71. this.x += v.x;
  72. this.y += v.y;
  73. this.z += v.z;
  74. }
  75. },
  76. sub: function(v, y, z) {
  77. if (arguments.length === 3) {
  78. this.x -= v;
  79. this.y -= y;
  80. this.z -= z;
  81. } else if (arguments.length === 1) {
  82. this.x -= v.x;
  83. this.y -= v.y;
  84. this.z -= v.z;
  85. }
  86. },
  87. mult: function(v) {
  88. if (typeof v === 'number') {
  89. this.x *= v;
  90. this.y *= v;
  91. this.z *= v;
  92. } else if (typeof v === 'object') {
  93. this.x *= v.x;
  94. this.y *= v.y;
  95. this.z *= v.z;
  96. }
  97. },
  98. div: function(v) {
  99. if (typeof v === 'number') {
  100. this.x /= v;
  101. this.y /= v;
  102. this.z /= v;
  103. } else if (typeof v === 'object') {
  104. this.x /= v.x;
  105. this.y /= v.y;
  106. this.z /= v.z;
  107. }
  108. },
  109. dist: function(v) {
  110. var dx = this.x - v.x,
  111. dy = this.y - v.y,
  112. dz = this.z - v.z;
  113. return Math.sqrt(dx * dx + dy * dy + dz * dz);
  114. },
  115. dot: function(v, y, z) {
  116. if (arguments.length === 3) {
  117. return (this.x * v + this.y * y + this.z * z);
  118. } else if (arguments.length === 1) {
  119. return (this.x * v.x + this.y * v.y + this.z * v.z);
  120. }
  121. },
  122. cross: function(v) {
  123. return new PVector(this.y * v.z - v.y * this.z,
  124. this.z * v.x - v.z * this.x,
  125. this.x * v.y - v.x * this.y);
  126. },
  127. normalize: function() {
  128. var m = this.mag();
  129. if (m > 0) {
  130. this.div(m);
  131. }
  132. },
  133. limit: function(high) {
  134. if (this.mag() > high) {
  135. this.normalize();
  136. this.mult(high);
  137. }
  138. },
  139. heading2D: function() {
  140. return (-Math.atan2(-this.y, this.x));
  141. },
  142. toString: function() {
  143. return "[" + this.x + ", " + this.y + ", " + this.z + "]";
  144. },
  145. array: function() {
  146. return [this.x, this.y, this.z];
  147. }
  148. };
  149. while (method--) {
  150. PVector[simplePVMethods[method]] = createSimplePVectorMethod(simplePVMethods[method]);
  151. }
  152. for (method in PVector.prototype) {
  153. if (PVector.prototype.hasOwnProperty(method) && !PVector.hasOwnProperty(method)) {
  154. PVector[method] = createPVectorMethod(method);
  155. }
  156. }
  157. var Processing = this.Processing = function Processing(curElement, aCode) {
  158. var p = this;
  159. // Include Package Classes -- do this differently in the future.
  160. p.PVector = PVector;
  161. //p.PShapeSVG = PShapeSVG;
  162. //etc
  163. p.name = 'Processing.js Instance'; // Set Processing defaults / environment variables
  164. p.use3DContext = false; // default '2d' canvas context
  165. // PJS specific (non-p5) methods and properties to externalize
  166. p.externals = {
  167. canvas: curElement,
  168. context: undef,
  169. sketch: undef,
  170. onblur: function() {},
  171. onfocus: function() {}
  172. };
  173. // Glyph path storage for textFonts
  174. p.glyphTable = {};
  175. // Global vars for tracking mouse position
  176. p.pmouseX = 0;
  177. p.pmouseY = 0;
  178. p.mouseX = 0;
  179. p.mouseY = 0;
  180. p.mouseButton = 0;
  181. p.mouseScroll = 0;
  182. // Undefined event handlers to be replaced by user when needed
  183. p.mouseClicked = undef;
  184. p.mouseDragged = undef;
  185. p.mouseMoved = undef;
  186. p.mousePressed = undef;
  187. p.mouseReleased = undef;
  188. p.mouseScrolled = undef;
  189. p.key = undef;
  190. p.keyCode = undef;
  191. p.keyPressed = undef;
  192. p.keyReleased = undef;
  193. p.keyTyped = undef;
  194. p.draw = undef;
  195. p.setup = undef;
  196. // Remapped vars
  197. p.__mousePressed = false;
  198. p.__keyPressed = false;
  199. p.__frameRate = 0;
  200. // The current animation frame
  201. p.frameCount = 0;
  202. // The height/width of the canvas
  203. p.width = curElement.width - 0;
  204. p.height = curElement.height - 0;
  205. // Color modes
  206. p.RGB = 1;
  207. p.ARGB = 2;
  208. p.HSB = 3;
  209. p.ALPHA = 4;
  210. p.CMYK = 5;
  211. // Renderers
  212. p.P2D = 1;
  213. p.JAVA2D = 1;
  214. p.WEBGL = 2;
  215. p.P3D = 2;
  216. p.OPENGL = 2;
  217. p.EPSILON = 0.0001;
  218. p.MAX_FLOAT = 3.4028235e+38;
  219. p.MIN_FLOAT = -3.4028235e+38;
  220. p.MAX_INT = 2147483647;
  221. p.MIN_INT = -2147483648;
  222. p.PI = Math.PI;
  223. p.TWO_PI = 2 * p.PI;
  224. p.HALF_PI = p.PI / 2;
  225. p.THIRD_PI = p.PI / 3;
  226. p.QUARTER_PI = p.PI / 4;
  227. p.DEG_TO_RAD = p.PI / 180;
  228. p.RAD_TO_DEG = 180 / p.PI;
  229. p.WHITESPACE = " \t\n\r\f\u00A0";
  230. // Filter/convert types
  231. p.BLUR = 11;
  232. p.GRAY = 12;
  233. p.INVERT = 13;
  234. p.OPAQUE = 14;
  235. p.POSTERIZE = 15;
  236. p.THRESHOLD = 16;
  237. p.ERODE = 17;
  238. p.DILATE = 18;
  239. // Blend modes
  240. p.REPLACE = 0;
  241. p.BLEND = 1 << 0;
  242. p.ADD = 1 << 1;
  243. p.SUBTRACT = 1 << 2;
  244. p.LIGHTEST = 1 << 3;
  245. p.DARKEST = 1 << 4;
  246. p.DIFFERENCE = 1 << 5;
  247. p.EXCLUSION = 1 << 6;
  248. p.MULTIPLY = 1 << 7;
  249. p.SCREEN = 1 << 8;
  250. p.OVERLAY = 1 << 9;
  251. p.HARD_LIGHT = 1 << 10;
  252. p.SOFT_LIGHT = 1 << 11;
  253. p.DODGE = 1 << 12;
  254. p.BURN = 1 << 13;
  255. // Color component bit masks
  256. p.ALPHA_MASK = 0xff000000;
  257. p.RED_MASK = 0x00ff0000;
  258. p.GREEN_MASK = 0x0000ff00;
  259. p.BLUE_MASK = 0x000000ff;
  260. // Projection matrices
  261. p.CUSTOM = 0;
  262. p.ORTHOGRAPHIC = 2;
  263. p.PERSPECTIVE = 3;
  264. // Shapes
  265. p.POINT = 2;
  266. p.POINTS = 2;
  267. p.LINE = 4;
  268. p.LINES = 4;
  269. p.TRIANGLE = 8;
  270. p.TRIANGLES = 9;
  271. p.TRIANGLE_STRIP = 10;
  272. p.TRIANGLE_FAN = 11;
  273. p.QUAD = 16;
  274. p.QUADS = 16;
  275. p.QUAD_STRIP = 17;
  276. p.POLYGON = 20;
  277. p.PATH = 21;
  278. p.RECT = 30;
  279. p.ELLIPSE = 31;
  280. p.ARC = 32;
  281. p.SPHERE = 40;
  282. p.BOX = 41;
  283. p.GROUP = 0;
  284. p.PRIMITIVE = 1;
  285. p.PATH = 2;
  286. p.GEOMETRY = 3;
  287. p.breakShape = false;
  288. // Shape Vertex
  289. p.VERTEX = 0;
  290. p.BEZIER_VERTEX = 1;
  291. p.CURVE_VERTEX = 2;
  292. p.BREAK = 3;
  293. p.CLOSESHAPE = 4;
  294. // Shape closing modes
  295. p.OPEN = 1;
  296. p.CLOSE = 2;
  297. // Shape drawing modes
  298. p.CORNER = 0; // Draw mode convention to use (x, y) to (width, height)
  299. p.CORNERS = 1; // Draw mode convention to use (x1, y1) to (x2, y2) coordinates
  300. p.RADIUS = 2; // Draw mode from the center, and using the radius
  301. p.CENTER_RADIUS = 2; // Deprecated! Use RADIUS instead
  302. p.CENTER = 3; // Draw from the center, using second pair of values as the diameter
  303. p.DIAMETER = 3; // Synonym for the CENTER constant. Draw from the center
  304. p.CENTER_DIAMETER = 3; // Deprecated! Use DIAMETER instead
  305. // Text vertical alignment modes
  306. p.BASELINE = 0; // Default vertical alignment for text placement
  307. p.TOP = 101; // Align text to the top
  308. p.BOTTOM = 102; // Align text from the bottom, using the baseline
  309. // UV Texture coordinate modes
  310. p.NORMAL = 1;
  311. p.NORMALIZED = 1;
  312. p.IMAGE = 2;
  313. // Text placement modes
  314. p.MODEL = 4;
  315. p.SHAPE = 5;
  316. // Stroke modes
  317. p.SQUARE = 'butt';
  318. p.ROUND = 'round';
  319. p.PROJECT = 'square';
  320. p.MITER = 'miter';
  321. p.BEVEL = 'bevel';
  322. // Lighting modes
  323. p.AMBIENT = 0;
  324. p.DIRECTIONAL = 1;
  325. //POINT = 2; Shared with Shape constant
  326. p.SPOT = 3;
  327. // Key constants
  328. // Both key and keyCode will be equal to these values
  329. p.BACKSPACE = 8;
  330. p.TAB = 9;
  331. p.ENTER = 10;
  332. p.RETURN = 13;
  333. p.ESC = 27;
  334. p.DELETE = 127;
  335. p.CODED = 0xffff;
  336. // p.key will be CODED and p.keyCode will be this value
  337. p.SHIFT = 16;
  338. p.CONTROL = 17;
  339. p.ALT = 18;
  340. p.UP = 38;
  341. p.RIGHT = 39;
  342. p.DOWN = 40;
  343. p.LEFT = 37;
  344. var codedKeys = [p.SHIFT, p.CONTROL, p.ALT, p.UP, p.RIGHT, p.DOWN, p.LEFT];
  345. // Cursor types
  346. p.ARROW = 'default';
  347. p.CROSS = 'crosshair';
  348. p.HAND = 'pointer';
  349. p.MOVE = 'move';
  350. p.TEXT = 'text';
  351. p.WAIT = 'wait';
  352. p.NOCURSOR = "url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto";
  353. // Hints
  354. p.DISABLE_OPENGL_2X_SMOOTH = 1;
  355. p.ENABLE_OPENGL_2X_SMOOTH = -1;
  356. p.ENABLE_OPENGL_4X_SMOOTH = 2;
  357. p.ENABLE_NATIVE_FONTS = 3;
  358. p.DISABLE_DEPTH_TEST = 4;
  359. p.ENABLE_DEPTH_TEST = -4;
  360. p.ENABLE_DEPTH_SORT = 5;
  361. p.DISABLE_DEPTH_SORT = -5;
  362. p.DISABLE_OPENGL_ERROR_REPORT = 6;
  363. p.ENABLE_OPENGL_ERROR_REPORT = -6;
  364. p.ENABLE_ACCURATE_TEXTURES = 7;
  365. p.DISABLE_ACCURATE_TEXTURES = -7;
  366. p.HINT_COUNT = 10;
  367. // PJS defined constants
  368. p.SINCOS_LENGTH = parseInt(360 / 0.5, 10);
  369. p.PRECISIONB = 15; // fixed point precision is limited to 15 bits!!
  370. p.PRECISIONF = 1 << p.PRECISIONB;
  371. p.PREC_MAXVAL = p.PRECISIONF - 1;
  372. p.PREC_ALPHA_SHIFT = 24 - p.PRECISIONB;
  373. p.PREC_RED_SHIFT = 16 - p.PRECISIONB;
  374. p.NORMAL_MODE_AUTO = 0;
  375. p.NORMAL_MODE_SHAPE = 1;
  376. p.NORMAL_MODE_VERTEX = 2;
  377. p.MAX_LIGHTS = 8;
  378. p.focused = true;
  379. // "Private" variables used to maintain state
  380. var curContext,
  381. curSketch,
  382. online = true,
  383. doFill = true,
  384. fillStyle = [1.0, 1.0, 1.0, 1.0],
  385. currentFillColor = 0xFFFFFFFF,
  386. isFillDirty = true,
  387. doStroke = true,
  388. strokeStyle = [0.8, 0.8, 0.8, 1.0],
  389. currentStrokeColor = 0xFFFDFDFD,
  390. isStrokeDirty = true,
  391. lineWidth = 1,
  392. loopStarted = false,
  393. doLoop = true,
  394. looping = 0,
  395. curRectMode = p.CORNER,
  396. curEllipseMode = p.CENTER,
  397. normalX = 0,
  398. normalY = 0,
  399. normalZ = 0,
  400. normalMode = p.NORMAL_MODE_AUTO,
  401. inDraw = false,
  402. curFrameRate = 60,
  403. curCursor = p.ARROW,
  404. oldCursor = curElement.style.cursor,
  405. curMsPerFrame = 1,
  406. curShape = p.POLYGON,
  407. curShapeCount = 0,
  408. curvePoints = [],
  409. curTightness = 0,
  410. curveDet = 20,
  411. curveInited = false,
  412. bezDetail = 20,
  413. colorModeA = 255,
  414. colorModeX = 255,
  415. colorModeY = 255,
  416. colorModeZ = 255,
  417. pathOpen = false,
  418. mouseDragging = false,
  419. curColorMode = p.RGB,
  420. curTint = function() {},
  421. curTextSize = 12,
  422. curTextFont = "Arial",
  423. getLoaded = false,
  424. start = new Date().getTime(),
  425. timeSinceLastFPS = start,
  426. framesSinceLastFPS = 0,
  427. textcanvas,
  428. curveBasisMatrix,
  429. curveToBezierMatrix,
  430. curveDrawMatrix,
  431. bezierDrawMatrix,
  432. bezierBasisInverse,
  433. bezierBasisMatrix,
  434. // Shaders
  435. programObject3D,
  436. programObject2D,
  437. programObjectUnlitShape,
  438. boxBuffer,
  439. boxNormBuffer,
  440. boxOutlineBuffer,
  441. rectBuffer,
  442. rectNormBuffer,
  443. sphereBuffer,
  444. lineBuffer,
  445. fillBuffer,
  446. fillColorBuffer,
  447. strokeColorBuffer,
  448. pointBuffer,
  449. shapeTexVBO,
  450. curTexture = {width:0,height:0},
  451. curTextureMode = p.IMAGE,
  452. usingTexture = false,
  453. textBuffer,
  454. textureBuffer,
  455. indexBuffer,
  456. // Text alignment
  457. horizontalTextAlignment = p.LEFT,
  458. verticalTextAlignment = p.BASELINE,
  459. baselineOffset = 0.2, // percent
  460. // Pixels cache
  461. originalContext,
  462. proxyContext = null,
  463. isContextReplaced = false,
  464. setPixelsCached,
  465. maxPixelsCached = 1000;
  466. // Work-around for Minefield. using ctx.VERTEX_PROGRAM_POINT_SIZE
  467. // in Minefield does nothing and does not report any errors.
  468. var VERTEX_PROGRAM_POINT_SIZE = 0x8642;
  469. var POINT_SMOOTH = 0x0B10;
  470. // Get padding and border style widths for mouse offsets
  471. var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;
  472. if (document.defaultView && document.defaultView.getComputedStyle) {
  473. stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(curElement, null)['paddingLeft'], 10) || 0;
  474. stylePaddingTop = parseInt(document.defaultView.getComputedStyle(curElement, null)['paddingTop'], 10) || 0;
  475. styleBorderLeft = parseInt(document.defaultView.getComputedStyle(curElement, null)['borderLeftWidth'], 10) || 0;
  476. styleBorderTop = parseInt(document.defaultView.getComputedStyle(curElement, null)['borderTopWidth'], 10) || 0;
  477. }
  478. // User can only have MAX_LIGHTS lights
  479. var lightCount = 0;
  480. //sphere stuff
  481. var sphereDetailV = 0,
  482. sphereDetailU = 0,
  483. sphereX = [],
  484. sphereY = [],
  485. sphereZ = [],
  486. sinLUT = new Array(p.SINCOS_LENGTH),
  487. cosLUT = new Array(p.SINCOS_LENGTH),
  488. sphereVerts,
  489. sphereNorms;
  490. // Camera defaults and settings
  491. var cam,
  492. cameraInv,
  493. forwardTransform,
  494. reverseTransform,
  495. modelView,
  496. modelViewInv,
  497. userMatrixStack,
  498. inverseCopy,
  499. projection,
  500. manipulatingCamera = false,
  501. frustumMode = false,
  502. cameraFOV = 60 * (Math.PI / 180),
  503. cameraX = curElement.width / 2,
  504. cameraY = curElement.height / 2,
  505. cameraZ = cameraY / Math.tan(cameraFOV / 2),
  506. cameraNear = cameraZ / 10,
  507. cameraFar = cameraZ * 10,
  508. cameraAspect = curElement.width / curElement.height;
  509. var vertArray = [],
  510. curveVertArray = [],
  511. curveVertCount = 0,
  512. isCurve = false,
  513. isBezier = false,
  514. firstVert = true;
  515. //PShape stuff
  516. var curShapeMode = p.CORNER;
  517. var colors = {};
  518. colors.aliceblue = "#f0f8ff";
  519. colors.antiquewhite = "#faebd7";
  520. colors.aqua = "#00ffff";
  521. colors.aquamarine = "#7fffd4";
  522. colors.azure = "#f0ffff";
  523. colors.beige = "#f5f5dc";
  524. colors.bisque = "#ffe4c4";
  525. colors.black = "#000000";
  526. colors.blanchedalmond = "#ffebcd";
  527. colors.blue = "#0000ff";
  528. colors.blueviolet = "#8a2be2";
  529. colors.brown = "#a52a2a";
  530. colors.burlywood = "#deb887";
  531. colors.cadetblue = "#5f9ea0";
  532. colors.chartreuse = "#7fff00";
  533. colors.chocolate = "#d2691e";
  534. colors.coral = "#ff7f50";
  535. colors.cornflowerblue = "#6495ed";
  536. colors.cornsilk = "#fff8dc";
  537. colors.crimson = "#dc143c";
  538. colors.cyan = "#00ffff";
  539. colors.darkblue = "#00008b";
  540. colors.darkcyan = "#008b8b";
  541. colors.darkgoldenrod = "#b8860b";
  542. colors.darkgray = "#a9a9a9";
  543. colors.darkgreen = "#006400";
  544. colors.darkkhaki = "#bdb76b";
  545. colors.darkmagenta = "#8b008b";
  546. colors.darkolivegreen = "#556b2f";
  547. colors.darkorange = "#ff8c00";
  548. colors.darkorchid = "#9932cc";
  549. colors.darkred = "#8b0000";
  550. colors.darksalmon = "#e9967a";
  551. colors.darkseagreen = "#8fbc8f";
  552. colors.darkslateblue = "#483d8b";
  553. colors.darkslategray = "#2f4f4f";
  554. colors.darkturquoise = "#00ced1";
  555. colors.darkviolet = "#9400d3";
  556. colors.deeppink = "#ff1493";
  557. colors.deepskyblue = "#00bfff";
  558. colors.dimgray = "#696969";
  559. colors.dodgerblue = "#1e90ff";
  560. colors.firebrick = "#b22222";
  561. colors.floralwhite = "#fffaf0";
  562. colors.forestgreen = "#228b22";
  563. colors.fuchsia = "#ff00ff";
  564. colors.gainsboro = "#dcdcdc";
  565. colors.ghostwhite = "#f8f8ff";
  566. colors.gold = "#ffd700";
  567. colors.goldenrod = "#daa520";
  568. colors.gray = "#808080";
  569. colors.green = "#008000";
  570. colors.greenyellow = "#adff2f";
  571. colors.honeydew = "#f0fff0";
  572. colors.hotpink = "#ff69b4";
  573. colors.indianred = "#cd5c5c";
  574. colors.indigo = "#4b0082";
  575. colors.ivory = "#fffff0";
  576. colors.khaki = "#f0e68c";
  577. colors.lavender = "#e6e6fa";
  578. colors.lavenderblush = "#fff0f5";
  579. colors.lawngreen = "#7cfc00";
  580. colors.lemonchiffon = "#fffacd";
  581. colors.lightblue = "#add8e6";
  582. colors.lightcoral = "#f08080";
  583. colors.lightcyan = "#e0ffff";
  584. colors.lightgoldenrodyellow = "#fafad2";
  585. colors.lightgrey = "#d3d3d3";
  586. colors.lightgreen = "#90ee90";
  587. colors.lightpink = "#ffb6c1";
  588. colors.lightsalmon = "#ffa07a";
  589. colors.lightseagreen = "#20b2aa";
  590. colors.lightskyblue = "#87cefa";
  591. colors.lightslategray = "#778899";
  592. colors.lightsteelblue = "#b0c4de";
  593. colors.lightyellow = "#ffffe0";
  594. colors.lime = "#00ff00";
  595. colors.limegreen = "#32cd32";
  596. colors.linen = "#faf0e6";
  597. colors.magenta = "#ff00ff";
  598. colors.maroon = "#800000";
  599. colors.mediumaquamarine = "#66cdaa";
  600. colors.mediumblue = "#0000cd";
  601. colors.mediumorchid = "#ba55d3";
  602. colors.mediumpurple = "#9370d8";
  603. colors.mediumseagreen = "#3cb371";
  604. colors.mediumslateblue = "#7b68ee";
  605. colors.mediumspringgreen = "#00fa9a";
  606. colors.mediumturquoise = "#48d1cc";
  607. colors.mediumvioletred = "#c71585";
  608. colors.midnightblue = "#191970";
  609. colors.mintcream = "#f5fffa";
  610. colors.mistyrose = "#ffe4e1";
  611. colors.moccasin = "#ffe4b5";
  612. colors.navajowhite = "#ffdead";
  613. colors.navy = "#000080";
  614. colors.oldlace = "#fdf5e6";
  615. colors.olive = "#808000";
  616. colors.olivedrab = "#6b8e23";
  617. colors.orange = "#ffa500";
  618. colors.orangered = "#ff4500";
  619. colors.orchid = "#da70d6";
  620. colors.palegoldenrod = "#eee8aa";
  621. colors.palegreen = "#98fb98";
  622. colors.paleturquoise = "#afeeee";
  623. colors.palevioletred = "#d87093";
  624. colors.papayawhip = "#ffefd5";
  625. colors.peachpuff = "#ffdab9";
  626. colors.peru = "#cd853f";
  627. colors.pink = "#ffc0cb";
  628. colors.plum = "#dda0dd";
  629. colors.powderblue = "#b0e0e6";
  630. colors.purple = "#800080";
  631. colors.red = "#ff0000";
  632. colors.rosybrown = "#bc8f8f";
  633. colors.royalblue = "#4169e1";
  634. colors.saddlebrown = "#8b4513";
  635. colors.salmon = "#fa8072";
  636. colors.sandybrown = "#f4a460";
  637. colors.seagreen = "#2e8b57";
  638. colors.seashell = "#fff5ee";
  639. colors.sienna = "#a0522d";
  640. colors.silver = "#c0c0c0";
  641. colors.skyblue = "#87ceeb";
  642. colors.slateblue = "#6a5acd";
  643. colors.slategray = "#708090";
  644. colors.snow = "#fffafa";
  645. colors.springgreen = "#00ff7f";
  646. colors.steelblue = "#4682b4";
  647. colors.tan = "#d2b48c";
  648. colors.teal = "#008080";
  649. colors.thistle = "#d8bfd8";
  650. colors.tomato = "#ff6347";
  651. colors.turquoise = "#40e0d0";
  652. colors.violet = "#ee82ee";
  653. colors.wheat = "#f5deb3";
  654. colors.white = "#ffffff";
  655. colors.whitesmoke = "#f5f5f5";
  656. colors.yellow = "#ffff00";
  657. colors.yellowgreen = "#9acd32";
  658. // Stores states for pushStyle() and popStyle().
  659. var styleArray = new Array(0);
  660. // Vertices are specified in a counter-clockwise order
  661. // triangles are in this order: back, front, right, bottom, left, top
  662. var boxVerts = [0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5,
  663. -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5,
  664. -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5,
  665. 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5,
  666. 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5,
  667. -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5,
  668. -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5,
  669. -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5,
  670. -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5];
  671. var boxNorms = [0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
  672. 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
  673. 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
  674. 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,
  675. -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0,
  676. 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0];
  677. var boxOutlineVerts = [0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5,
  678. -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5,
  679. 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
  680. -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
  681. 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5,
  682. -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5];
  683. // These verts are used for the fill and stroke using TRIANGLE_FAN and LINE_LOOP
  684. var rectVerts = [0,0,0, 0,1,0, 1,1,0, 1,0,0];
  685. var rectNorms = [0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1];
  686. // Vertex shader for points and lines
  687. var vShaderSrcUnlitShape =
  688. "attribute vec3 aVertex;" +
  689. "attribute vec4 aColor;" +
  690. "uniform mat4 uView;" +
  691. "uniform mat4 uProjection;" +
  692. "void main(void) {" +
  693. " gl_FrontColor = aColor;" +
  694. " gl_Position = uProjection * uView * vec4(aVertex, 1.0);" +
  695. "}";
  696. var fShaderSrcUnlitShape =
  697. "void main(void){" +
  698. " gl_FragColor = gl_Color;" +
  699. "}";
  700. // Vertex shader for points and lines
  701. var vertexShaderSource2D =
  702. "attribute vec3 Vertex;" +
  703. "attribute vec2 aTextureCoord;" +
  704. "uniform vec4 color;" +
  705. "uniform mat4 model;" +
  706. "uniform mat4 view;" +
  707. "uniform mat4 projection;" +
  708. "uniform float pointSize;" +
  709. "varying vec2 vTextureCoord;"+
  710. "void main(void) {" +
  711. " gl_PointSize = pointSize;" +
  712. " gl_FrontColor = color;" +
  713. " gl_Position = projection * view * model * vec4(Vertex, 1.0);" +
  714. " vTextureCoord = aTextureCoord;" +
  715. "}";
  716. var fragmentShaderSource2D =
  717. "varying vec2 vTextureCoord;"+
  718. "uniform vec4 color;"+
  719. "uniform sampler2D uSampler;"+
  720. "uniform int picktype;"+
  721. "void main(void){" +
  722. " if(picktype==0){"+
  723. " gl_FragColor = color;" +
  724. " }else if(picktype==1){"+
  725. " float alpha = texture2D(uSampler,vTextureCoord).a;"+
  726. " gl_FragColor = vec4(color.rgb*alpha,alpha);\n"+
  727. " }"+
  728. "}";
  729. // Vertex shader for boxes and spheres
  730. var vertexShaderSource3D =
  731. "attribute vec3 Vertex;" +
  732. "attribute vec3 Normal;" +
  733. "attribute vec4 aColor;" +
  734. "attribute vec2 aTexture;" +
  735. "varying vec2 vTexture;" +
  736. "uniform vec4 color;" +
  737. "uniform bool usingMat;" +
  738. "uniform vec3 specular;" +
  739. "uniform vec3 mat_emissive;" +
  740. "uniform vec3 mat_ambient;" +
  741. "uniform vec3 mat_specular;" +
  742. "uniform float shininess;" +
  743. "uniform mat4 model;" +
  744. "uniform mat4 view;" +
  745. "uniform mat4 projection;" +
  746. "uniform mat4 normalTransform;" +
  747. "uniform int lightCount;" +
  748. "uniform vec3 falloff;" +
  749. "struct Light {" +
  750. " bool dummy;" +
  751. " int type;" +
  752. " vec3 color;" +
  753. " vec3 position;" +
  754. " vec3 direction;" +
  755. " float angle;" +
  756. " vec3 halfVector;" +
  757. " float concentration;" +
  758. "};" +
  759. "uniform Light lights[8];" +
  760. "void AmbientLight( inout vec3 totalAmbient, in vec3 ecPos, in Light light ) {" +
  761. // Get the vector from the light to the vertex
  762. // Get the distance from the current vector to the light position
  763. " float d = length( light.position - ecPos );" +
  764. " float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" + " totalAmbient += light.color * attenuation;" +
  765. "}" +
  766. "void DirectionalLight( inout vec3 col, in vec3 ecPos, inout vec3 spec, in vec3 vertNormal, in Light light ) {" +
  767. " float powerfactor = 0.0;" +
  768. " float nDotVP = max(0.0, dot( vertNormal, light.position ));" +
  769. " float nDotVH = max(0.0, dot( vertNormal, normalize( light.position-ecPos )));" +
  770. " if( nDotVP != 0.0 ){" +
  771. " powerfactor = pow( nDotVH, shininess );" +
  772. " }" +
  773. " col += light.color * nDotVP;" +
  774. " spec += specular * powerfactor;" +
  775. "}" +
  776. "void PointLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in vec3 eye, in Light light ) {" +
  777. " float powerfactor;" +
  778. // Get the vector from the light to the vertex
  779. " vec3 VP = light.position - ecPos;" +
  780. // Get the distance from the current vector to the light position
  781. " float d = length( VP ); " +
  782. // Normalize the light ray so it can be used in the dot product operation.
  783. " VP = normalize( VP );" +
  784. " float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" +
  785. " float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
  786. " vec3 halfVector = normalize( VP + eye );" +
  787. " float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
  788. " if( nDotVP == 0.0) {" +
  789. " powerfactor = 0.0;" +
  790. " }" +
  791. " else{" +
  792. " powerfactor = pow( nDotHV, shininess );" +
  793. " }" +
  794. " spec += specular * powerfactor * attenuation;" +
  795. " col += light.color * nDotVP * attenuation;" +
  796. "}" +
  797. /*
  798. */
  799. "void SpotLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in vec3 eye, in Light light ) {" +
  800. " float spotAttenuation;" +
  801. " float powerfactor;" +
  802. // calculate the vector from the current vertex to the light.
  803. " vec3 VP = light.position - ecPos; " +
  804. " vec3 ldir = normalize( light.direction );" +
  805. // get the distance from the spotlight and the vertex
  806. " float d = length( VP );" +
  807. " VP = normalize( VP );" +
  808. " float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ) );" +
  809. // dot product of the vector from vertex to light and light direction.
  810. " float spotDot = dot( VP, ldir );" +
  811. // if the vertex falls inside the cone
  812. " if( spotDot < cos( light.angle ) ) {" +
  813. " spotAttenuation = pow( spotDot, light.concentration );" +
  814. " }" +
  815. " else{" +
  816. " spotAttenuation = 1.0;" +
  817. " }" +
  818. " attenuation *= spotAttenuation;" +
  819. " float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
  820. " vec3 halfVector = normalize( VP + eye );" +
  821. " float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
  822. " if( nDotVP == 0.0 ) {" +
  823. " powerfactor = 0.0;" +
  824. " }" +
  825. " else {" +
  826. " powerfactor = pow( nDotHV, shininess );" +
  827. " }" +
  828. " spec += specular * powerfactor * attenuation;" +
  829. " col += light.color * nDotVP * attenuation;" +
  830. "}" +
  831. "void main(void) {" +
  832. " vec3 finalAmbient = vec3( 0.0, 0.0, 0.0 );" +
  833. " vec3 finalDiffuse = vec3( 0.0, 0.0, 0.0 );" +
  834. " vec3 finalSpecular = vec3( 0.0, 0.0, 0.0 );" +
  835. " vec4 col = color;" +
  836. " if(color[0] == -1.0){" +
  837. " col = aColor;" +
  838. " }" +
  839. " vec3 norm = vec3( normalTransform * vec4( Normal, 0.0 ) );" +
  840. " vec4 ecPos4 = view * model * vec4(Vertex,1.0);" +
  841. " vec3 ecPos = (vec3(ecPos4))/ecPos4.w;" +
  842. " vec3 eye = vec3( 0.0, 0.0, 1.0 );" +
  843. // If there were no lights this draw call, just use the
  844. // assigned fill color of the shape and the specular value
  845. " if( lightCount == 0 ) {" +
  846. " gl_FrontColor = col + vec4(mat_specular,1.0);" +
  847. " }" +
  848. " else {" +
  849. " for( int i = 0; i < lightCount; i++ ) {" +
  850. " if( lights[i].type == 0 ) {" +
  851. " AmbientLight( finalAmbient, ecPos, lights[i] );" +
  852. " }" +
  853. " else if( lights[i].type == 1 ) {" +
  854. " DirectionalLight( finalDiffuse,ecPos, finalSpecular, norm, lights[i] );" +
  855. " }" +
  856. " else if( lights[i].type == 2 ) {" +
  857. " PointLight( finalDiffuse, finalSpecular, norm, ecPos, eye, lights[i] );" +
  858. " }" +
  859. " else if( lights[i].type == 3 ) {" +
  860. " SpotLight( finalDiffuse, finalSpecular, norm, ecPos, eye, lights[i] );" +
  861. " }" +
  862. " }" +
  863. " if( usingMat == false ) {" +
  864. " gl_FrontColor = vec4( " +
  865. " vec3(col) * finalAmbient +" +
  866. " vec3(col) * finalDiffuse +" +
  867. " vec3(col) * finalSpecular," +
  868. " col[3] );" +
  869. " }" +
  870. " else{" +
  871. " gl_FrontColor = vec4( " +
  872. " mat_emissive + " +
  873. " (vec3(col) * mat_ambient * finalAmbient) + " +
  874. " (vec3(col) * finalDiffuse) + " +
  875. " (mat_specular * finalSpecular), " +
  876. " col[3] );" +
  877. " }" +
  878. " }" +
  879. " vTexture.xy = aTexture.xy;" +
  880. " gl_Position = projection * view * model * vec4( Vertex, 1.0 );" +
  881. "}";
  882. var fragmentShaderSource3D =
  883. "uniform sampler2D sampler;" +
  884. "uniform bool usingTexture;" +
  885. "varying vec2 vTexture;" +
  886. // In Processing, when a texture is used, the fill color is ignored
  887. "void main(void){" +
  888. " if(usingTexture){" +
  889. " gl_FragColor = vec4(texture2D(sampler, vTexture.xy));" +
  890. " }"+
  891. " else{" +
  892. " gl_FragColor = vec4(gl_Color);" +
  893. " }" +
  894. "}";
  895. ////////////////////////////////////////////////////////////////////////////
  896. // 3D Functions
  897. ////////////////////////////////////////////////////////////////////////////
  898. /*
  899. Sets the uniform variable 'varName' to the value specified by 'value'.
  900. Before calling this function, make sure the correct program object
  901. has been installed as part of the current rendering state.
  902. On some systems, if the variable exists in the shader but isn't used,
  903. the compiler will optimize it out and this function will fail.
  904. */
  905. function uniformf(programObj, varName, varValue) {
  906. var varLocation = curContext.getUniformLocation(programObj, varName);
  907. // the variable won't be found if it was optimized out.
  908. if (varLocation !== -1) {
  909. if (varValue.length === 4) {
  910. curContext.uniform4fv(varLocation, varValue);
  911. } else if (varValue.length === 3) {
  912. curContext.uniform3fv(varLocation, varValue);
  913. } else if (varValue.length === 2) {
  914. curContext.uniform2fv(varLocation, varValue);
  915. } else {
  916. curContext.uniform1f(varLocation, varValue);
  917. }
  918. }
  919. }
  920. function uniformi(programObj, varName, varValue) {
  921. var varLocation = curContext.getUniformLocation(programObj, varName);
  922. // the variable won't be found if it was optimized out.
  923. if (varLocation !== -1) {
  924. if (varValue.length === 4) {
  925. curContext.uniform4iv(varLocation, varValue);
  926. } else if (varValue.length === 3) {
  927. curContext.uniform3iv(varLocation, varValue);
  928. } else if (varValue.length === 2) {
  929. curContext.uniform2iv(varLocation, varValue);
  930. } else {
  931. curContext.uniform1i(varLocation, varValue);
  932. }
  933. }
  934. }
  935. function vertexAttribPointer(programObj, varName, size, VBO) {
  936. var varLocation = curContext.getAttribLocation(programObj, varName);
  937. if (varLocation !== -1) {
  938. curContext.bindBuffer(curContext.ARRAY_BUFFER, VBO);
  939. curContext.vertexAttribPointer(varLocation, size, curContext.FLOAT, false, 0, 0);
  940. curContext.enableVertexAttribArray(varLocation);
  941. }
  942. }
  943. function disableVertexAttribPointer(programObj, varName){
  944. var varLocation = curContext.getAttribLocation(programObj, varName);
  945. if (varLocation !== -1) {
  946. curContext.disableVertexAttribArray(varLocation);
  947. }
  948. }
  949. function uniformMatrix(programObj, varName, transpose, matrix) {
  950. var varLocation = curContext.getUniformLocation(programObj, varName);
  951. // the variable won't be found if it was optimized out.
  952. if (varLocation !== -1) {
  953. if (matrix.length === 16) {
  954. curContext.uniformMatrix4fv(varLocation, transpose, matrix);
  955. } else if (matrix.length === 9) {
  956. curContext.uniformMatrix3fv(varLocation, transpose, matrix);
  957. } else {
  958. curContext.uniformMatrix2fv(varLocation, transpose, matrix);
  959. }
  960. }
  961. }
  962. // Wrapper to easily deal with array names changes. TODO: Don't think we need this wrapper anymore consensus has been reached.
  963. var newWebGLArray = function(data) {
  964. return new WebGLFloatArray(data);
  965. };
  966. var imageModeCorner = function imageModeCorner(x, y, w, h, whAreSizes) {
  967. return {
  968. x: x,
  969. y: y,
  970. w: w,
  971. h: h
  972. };
  973. };
  974. var imageModeConvert = imageModeCorner;
  975. var imageModeCorners = function imageModeCorners(x, y, w, h, whAreSizes) {
  976. return {
  977. x: x,
  978. y: y,
  979. w: whAreSizes ? w : w - x,
  980. h: whAreSizes ? h : h - y
  981. };
  982. };
  983. var imageModeCenter = function imageModeCenter(x, y, w, h, whAreSizes) {
  984. return {
  985. x: x - w / 2,
  986. y: y - h / 2,
  987. w: w,
  988. h: h
  989. };
  990. };
  991. var createProgramObject = function(curContext, vetexShaderSource, fragmentShaderSource) {
  992. var vertexShaderObject = curContext.createShader(curContext.VERTEX_SHADER);
  993. curContext.shaderSource(vertexShaderObject, vetexShaderSource);
  994. curContext.compileShader(vertexShaderObject);
  995. if (!curContext.getShaderParameter(vertexShaderObject, curContext.COMPILE_STATUS)) {
  996. throw curContext.getShaderInfoLog(vertexShaderObject);
  997. }
  998. var fragmentShaderObject = curContext.createShader(curContext.FRAGMENT_SHADER);
  999. curContext.shaderSource(fragmentShaderObject, fragmentShaderSource);
  1000. curContext.compileShader(fragmentShaderObject);
  1001. if (!curContext.getShaderParameter(fragmentShaderObject, curContext.COMPILE_STATUS)) {
  1002. throw curContext.getShaderInfoLog(fragmentShaderObject);
  1003. }
  1004. var programObject = curContext.createProgram();
  1005. curContext.attachShader(programObject, vertexShaderObject);
  1006. curContext.attachShader(programObject, fragmentShaderObject);
  1007. curContext.linkProgram(programObject);
  1008. if (!curContext.getProgramParameter(programObject, curContext.LINK_STATUS)) {
  1009. throw "Error linking shaders.";
  1010. }
  1011. return programObject;
  1012. };
  1013. ////////////////////////////////////////////////////////////////////////////
  1014. // Char handling
  1015. ////////////////////////////////////////////////////////////////////////////
  1016. var charMap = {};
  1017. var Char = p.Character = function Char(chr) {
  1018. if (typeof chr === 'string' && chr.length === 1) {
  1019. this.code = chr.charCodeAt(0);
  1020. } else {
  1021. this.code = NaN;
  1022. }
  1023. return (charMap[this.code] === undef) ? charMap[this.code] = this : charMap[this.code];
  1024. };
  1025. Char.prototype.toString = function() {
  1026. return String.fromCharCode(this.code);
  1027. };
  1028. Char.prototype.valueOf = function() {
  1029. return this.code;
  1030. };
  1031. ////////////////////////////////////////////////////////////////////////////
  1032. // PShape
  1033. ////////////////////////////////////////////////////////////////////////////
  1034. var PShape = p.PShape = function(family) {
  1035. this.family = family || p.GROUP;
  1036. this.visible = true;
  1037. this.style = true;
  1038. this.children = [];
  1039. this.nameTable = [];
  1040. this.params = [];
  1041. this.name = "";
  1042. this.image = null; //type PImage
  1043. this.matrix = null;
  1044. this.kind = null;
  1045. this.close = null;
  1046. this.width = null;
  1047. this.height = null;
  1048. this.parent = null;
  1049. /* methods */
  1050. this.isVisible = function(){
  1051. return this.visible;
  1052. };
  1053. this.setVisible = function (visible){
  1054. this.visible = visible;
  1055. };
  1056. this.disableStyle = function(){
  1057. this.style = false;
  1058. for(var i = 0; i < this.children.length; i++)
  1059. {
  1060. this.children[i].disableStyle();
  1061. }
  1062. };
  1063. this.enableStyle = function(){
  1064. this.style = true;
  1065. for(var i = 0; i < this.children.length; i++)
  1066. {
  1067. this.children[i].enableStyle();
  1068. }
  1069. };
  1070. this.getFamily = function(){
  1071. return this.family;
  1072. };
  1073. this.getWidth = function(){
  1074. return this.width;
  1075. };
  1076. this.getHeight = function(){
  1077. return this.height;
  1078. };
  1079. this.setName = function(name){
  1080. this.name = name;
  1081. };
  1082. this.getName = function(){
  1083. return this.name;
  1084. };
  1085. this.draw = function(){
  1086. if (this.visible) {
  1087. this.pre();
  1088. this.drawImpl();
  1089. this.post();
  1090. }
  1091. };
  1092. this.drawImpl = function(){
  1093. if (this.family === p.GROUP) {
  1094. this.drawGroup();
  1095. } else if (this.family === p.PRIMITIVE) {
  1096. this.drawPrimitive();
  1097. } else if (this.family === p.GEOMETRY) {
  1098. this.drawGeometry();
  1099. } else if (this.family === p.PATH) {
  1100. this.drawPath();
  1101. }
  1102. };
  1103. this.drawPath = function(){
  1104. if (this.vertices.length === 0) { return; }
  1105. p.beginShape();
  1106. var i;
  1107. if (this.vertexCodes.length === 0) { // each point is a simple vertex
  1108. if (this.vertices[0].length === 2) { // drawing 2D vertices
  1109. for (i = 0; i < this.vertices.length; i++) {
  1110. p.vertex(this.vertices[i][0], this.vertices[i][1]);
  1111. }
  1112. } else { // drawing 3D vertices
  1113. for (i = 0; i < this.vertices.length; i++) {
  1114. p.vertex(this.vertices[i][0], this.vertices[i][1], this.vertices[i][2]);
  1115. }
  1116. }
  1117. } else { // coded set of vertices
  1118. var index = 0;
  1119. var j;
  1120. if (this.vertices[0].length === 2) { // drawing a 2D path
  1121. for (j = 0; j < this.vertexCodes.length; j++) {
  1122. switch (this.vertexCodes[j]) {
  1123. case p.VERTEX:
  1124. p.vertex(this.vertices[index][0], this.vertices[index][1]);
  1125. if ( this.vertices[index]["moveTo"] === true) {
  1126. vertArray[vertArray.length-1]["moveTo"] = true;
  1127. } else if ( this.vertices[index]["moveTo"] === false) {
  1128. vertArray[vertArray.length-1]["moveTo"] = false;
  1129. }
  1130. p.breakShape = false;
  1131. index++;
  1132. break;
  1133. case p.BEZIER_VERTEX:
  1134. p.bezierVertex(this.vertices[index+0][0], this.vertices[index+0][1],
  1135. this.vertices[index+1][0], this.vertices[index+1][1],
  1136. this.vertices[index+2][0], this.vertices[index+2][1]);
  1137. index += 3;
  1138. break;
  1139. case p.CURVE_VERTEX:
  1140. p.curveVertex(this.vertices[index][0], this.vertices[index][1]);
  1141. index++;
  1142. break;
  1143. case p.BREAK:
  1144. p.breakShape = true;
  1145. break;
  1146. }
  1147. }
  1148. } else { // drawing a 3D path
  1149. for (j = 0; j < this.vertexCodes.length; j++) {
  1150. switch (this.vertexCodes[j]) {
  1151. case p.VERTEX:
  1152. p.vertex(this.vertices[index][0], this.vertices[index][1], this.vertices[index][2]);
  1153. if (this.vertices[index]["moveTo"] === true) {
  1154. vertArray[vertArray.length-1]["moveTo"] = true;
  1155. } else if (this.vertices[index]["moveTo"] === false) {
  1156. vertArray[vertArray.length-1]["moveTo"] = false;
  1157. }
  1158. p.breakShape = false;
  1159. break;
  1160. case p.BEZIER_VERTEX:
  1161. p.bezierVertex(this.vertices[index+0][0], this.vertices[index+0][1], this.vertices[index+0][2],
  1162. this.vertices[index+1][0], this.vertices[index+1][1], this.vertices[index+1][2],
  1163. this.vertices[index+2][0], this.vertices[index+2][1], this.vertices[index+2][2]);
  1164. index += 3;
  1165. break;
  1166. case p.CURVE_VERTEX:
  1167. p.curveVertex(this.vertices[index][0], this.vertices[index][1], this.vertices[index][2]);
  1168. index++;
  1169. break;
  1170. case p.BREAK:
  1171. p.breakShape = true;
  1172. break;
  1173. }
  1174. }
  1175. }
  1176. }
  1177. p.endShape(this.close ? p.CLOSE : p.OPEN);
  1178. };
  1179. this.drawGeometry = function() {
  1180. p.beginShape(this.kind);
  1181. var i;
  1182. if (this.style) {
  1183. for (i = 0; i < this.vertices.length; i++) {
  1184. p.vertex(this.vertices[i]);
  1185. }
  1186. } else {
  1187. for (i = 0; i < this.vertices.length; i++) {
  1188. var vert = this.vertices[i];
  1189. if (vert[2] === 0) {
  1190. p.vertex(vert[0], vert[1]);
  1191. } else {
  1192. p.vertex(vert[0], vert[1], vert[2]);
  1193. }
  1194. }
  1195. }
  1196. p.endShape();
  1197. };
  1198. this.drawGroup = function() {
  1199. for (var i = 0; i < this.children.length; i++) {
  1200. this.children[i].draw();
  1201. }
  1202. };
  1203. this.drawPrimitive = function() {
  1204. switch (this.kind) {
  1205. case p.POINT:
  1206. p.point(this.params[0], this.params[1]);
  1207. break;
  1208. case p.LINE:
  1209. if (this.params.length === 4) { // 2D
  1210. p.line(this.params[0], this.params[1],
  1211. this.params[2], this.params[3]);
  1212. } else { // 3D
  1213. p.line(this.params[0], this.params[1], this.params[2],
  1214. this.params[3], this.params[4], this.params[5]);
  1215. }
  1216. break;
  1217. case p.TRIANGLE:
  1218. p.triangle(this.params[0], this.params[1],
  1219. this.params[2], this.params[3],
  1220. this.params[4], this.params[5]);
  1221. break;
  1222. case p.QUAD:
  1223. p.quad(this.params[0], this.params[1],
  1224. this.params[2], this.params[3],
  1225. this.params[4], this.params[5],
  1226. this.params[6], this.params[7]);
  1227. break;
  1228. case p.RECT:
  1229. if (this.image !== null) {
  1230. p.imageMode(p.CORNER);
  1231. p.image(this.image, this.params[0], this.params[1], this.params[2], this.params[3]);
  1232. } else {
  1233. p.rectMode(p.CORNER);
  1234. p.rect(this.params[0], this.params[1], this.params[2], this.params[3]);
  1235. }
  1236. break;
  1237. case p.ELLIPSE:
  1238. p.ellipseMode(p.CORNER);
  1239. p.ellipse(this.params[0], this.params[1], this.params[2], this.params[3]);
  1240. break;
  1241. case p.ARC:
  1242. p.ellipseMode(p.CORNER);
  1243. p.arc(this.params[0], this.params[1], this.params[2], this.params[3], this.params[4], this.params[5]);
  1244. break;
  1245. case p.BOX:
  1246. if (this.params.length === 1) {
  1247. p.box(this.params[0]);
  1248. } else {
  1249. p.box(this.params[0], this.params[1], this.params[2]);
  1250. }
  1251. break;
  1252. case p.SPHERE:
  1253. p.sphere(this.params[0]);
  1254. break;
  1255. }
  1256. };
  1257. this.pre = function() {
  1258. if (this.matrix) {
  1259. p.pushMatrix();
  1260. //this.applyMatrix( this.matrix ); //applyMatrix missing
  1261. }
  1262. if (this.style) {
  1263. p.pushStyle();
  1264. this.styles();
  1265. }
  1266. };
  1267. this.post = function() {
  1268. if (this.matrix) {
  1269. p.popMatrix();
  1270. }
  1271. if (this.style) {
  1272. p.popStyle();
  1273. }
  1274. };
  1275. this.styles = function() {
  1276. if (this.stroke) {
  1277. p.stroke(this.strokeColor);
  1278. p.strokeWeight(this.strokeWeight);
  1279. p.strokeCap(this.strokeCap);
  1280. p.strokeJoin(this.strokeJoin);
  1281. } else {
  1282. p.noStroke();
  1283. }
  1284. if (this.fill) {
  1285. p.fill(this.fillColor);
  1286. } else {
  1287. p.noFill();
  1288. }
  1289. };
  1290. // return the PShape at the specific index from the children array or
  1291. // return the Phape from a parent shape specified by its name
  1292. this.getChild = function(child) {
  1293. if (typeof child === 'number') {
  1294. return this.children[child];
  1295. } else {
  1296. var found,
  1297. i;
  1298. if(child === "" || this.name === child){
  1299. return this;
  1300. } else {
  1301. if(this.nameTable.length > 0)
  1302. {
  1303. for(i = 0; i < this.nameTable.length || found; i++)
  1304. {
  1305. if(this.nameTable[i].getName === child) {
  1306. found = this.nameTable[i];
  1307. }
  1308. }
  1309. if (found) { return found; }
  1310. }
  1311. for(i = 0; i < this.children.lenth; i++)
  1312. {
  1313. found = this.children[i].getChild(child);
  1314. if(found) { return found; }
  1315. }
  1316. }
  1317. return null;
  1318. }
  1319. };
  1320. this.getChildCount = function () {
  1321. return this.children.length;
  1322. };
  1323. this.addChild = function( child ) {
  1324. this.children.push(child);
  1325. child.parent = this;
  1326. if (child.getName() !== null) {
  1327. this.addName(child.getName(), child);
  1328. }
  1329. };
  1330. this.addName = function(name, shape) {
  1331. if (this.parent !== null) {
  1332. this.parent.addName( name, shape );
  1333. } else {
  1334. this.nameTable.push( [name, shape] );
  1335. }
  1336. };
  1337. // findChild not in yet
  1338. this.translate = function() {
  1339. if(arguments.length === 2)
  1340. {
  1341. this.checkMatrix(2);
  1342. this.matrix.translate(arguments[0], arguments[1]);
  1343. } else {
  1344. this.checkMatrix(3);
  1345. this.matrix.translate(arguments[0], arguments[1], 0);
  1346. }
  1347. };
  1348. this.checkMatrix = function(dimensions) {
  1349. if(this.matrix === null) {
  1350. if(dimensions === 2) {
  1351. this.matrix = new p.PMatrix2D();
  1352. } else {
  1353. this.matrix = new p.PMatrix3D();
  1354. }
  1355. }else if(dimensions === 3 && this.matrix instanceof p.PMatrix2D) {
  1356. this.matrix = new p.PMatrix3D();
  1357. }
  1358. };
  1359. this.rotateX = function(angle) {
  1360. this.rotate(angle, 1, 0, 0);
  1361. };
  1362. this.rotateY = function(angle) {
  1363. this.rotate(angle, 0, 1, 0);
  1364. };
  1365. this.rotateZ = function(angle) {
  1366. this.rotate(angle, 0, 0, 1);
  1367. };
  1368. this.rotate = function() {
  1369. if(arguments.length === 1){
  1370. this.checkMatrix(2);
  1371. this.matrix.rotate(arguments[0]);
  1372. } else {
  1373. this.checkMatrix(3);
  1374. this.matrix.rotate(arguments[0], arguments[1], arguments[2] ,arguments[3]);
  1375. }
  1376. };
  1377. this.scale = function() {
  1378. if(arguments.length === 2) {
  1379. this.checkMatrix(2);
  1380. this.matrix.scale(arguments[0], arguments[1]);
  1381. } else if (arguments.length === 3) {
  1382. this.checkMatrix(2);
  1383. this.matrix.scale(arguments[0], arguments[1], arguments[2]);
  1384. } else {
  1385. this.checkMatrix(2);
  1386. this.matrix.scale(arguments[0]);
  1387. }
  1388. };
  1389. this.resetMatrix = function() {
  1390. this.checkMatrix(2);
  1391. this.matrix.reset();
  1392. };
  1393. // applyMatrix missing
  1394. // apply missing
  1395. // contains missing
  1396. // find child missing
  1397. // getPrimitive missing
  1398. // getVertex , getVertexCount missing
  1399. // getVertexCode , getVertexCodes , getVertexCodeCount missing
  1400. // getVertexX, getVertexY, getVertexZ missing
  1401. };
  1402. p.shape = function(shape, x, y, width, height) {
  1403. if (arguments.length >= 1 && arguments[0] !== null) {
  1404. if (shape.isVisible()) {
  1405. p.pushMatrix();
  1406. if (curShapeMode === p.CENTER) {
  1407. if (arguments.length === 5) {
  1408. p.translate(x - width/2, y - height/2);
  1409. p.scale(width / shape.getWidth(), height / shape.getHeight());
  1410. } else if (arguments.length === 3) {
  1411. p.translate(x - shape.getWidth()/2, - shape.getHeight()/2);
  1412. } else {
  1413. p.translate(-shape.getWidth()/2, -shape.getHeight()/2);
  1414. }
  1415. } else if (curShapeMode === p.CORNER) {
  1416. if (arguments.length === 5) {
  1417. p.translate(x, y);
  1418. p.scale(width / shape.getWidth(), height / shape.getHeight());
  1419. } else if (arguments.length === 3) {
  1420. p.translate(x, y);
  1421. }
  1422. } else if (curShapeMode === p.CORNERS) {
  1423. if (arguments.length === 5) {
  1424. width -= x;
  1425. height -= y;
  1426. p.translate(x, y);
  1427. p.scale(width / shape.getWidth(), height / shape.getHeight());
  1428. } else if (arguments.length === 3) {
  1429. p.translate(x, y);
  1430. }
  1431. }
  1432. shape.draw();
  1433. if ((arguments.length === 1 && curShapeMode === p.CENTER ) || arguments.length > 1) {
  1434. p.popMatrix();
  1435. }
  1436. }
  1437. }
  1438. };
  1439. p.shapeMode = function (mode) {
  1440. curShapeMode = mode;
  1441. };
  1442. p.loadShape = function (filename) {
  1443. if (arguments.length === 1) {
  1444. if (filename.indexOf(".svg") > -1) {
  1445. return new p.PShapeSVG(null, filename);
  1446. }
  1447. }
  1448. return null;
  1449. };
  1450. var PShapeSVG = p.PShapeSVG = function() {
  1451. p.PShape.call( this ); // PShape is the base class.
  1452. if (arguments.length === 1) {
  1453. this.element = new p.XMLElement(arguments[0]);
  1454. // set values to their defaults according to the SVG spec
  1455. this.vertexCodes = [];
  1456. this.vertices = [];
  1457. this.opacity = 1;
  1458. this.stroke = false;
  1459. this.strokeColor = p.ALPHA_MASK;
  1460. this.strokeWeight = 1;
  1461. this.strokeCap = p.SQUARE; // equivalent to BUTT in svg spec
  1462. this.strokeJoin = p.MITER;
  1463. this.strokeGradient = null;
  1464. this.strokeGradientPaint = null;
  1465. this.strokeName = null;
  1466. this.strokeOpacity = 1;
  1467. this.fill = true;
  1468. this.fillColor = p.ALPHA_MASK;
  1469. this.fillGradient = null;
  1470. this.fillGradientPaint = null;
  1471. this.fillName = null;
  1472. this.fillOpacity = 1;
  1473. if (this.element.getName() !== "svg") {
  1474. throw("root is not <svg>, it's <" + this.element.getName() + ">");
  1475. }
  1476. }
  1477. else if (arguments.length === 2) {
  1478. if (typeof arguments[1] === 'string') {
  1479. if (arguments[1].indexOf(".svg") > -1) { //its a filename
  1480. this.element = new p.XMLElement(arguments[1]);
  1481. // set values to their defaults according to the SVG spec
  1482. this.vertexCodes = [];
  1483. this.vertices = [];
  1484. this.opacity = 1;
  1485. this.stroke = false;
  1486. this.strokeColor = p.ALPHA_MASK;
  1487. this.strokeWeight = 1;
  1488. this.strokeCap = p.SQUARE; // equivalent to BUTT in svg spec
  1489. this.strokeJoin = p.MITER;
  1490. this.strokeGradient = "";
  1491. this.strokeGradientPaint = "";
  1492. this.strokeName = "";
  1493. this.strokeOpacity = 1;
  1494. this.fill = true;
  1495. this.fillColor = p.ALPHA_MASK;
  1496. this.fillGradient = null;
  1497. this.fillGradientPaint = null;
  1498. this.fillOpacity = 1;
  1499. }
  1500. } else { // XMLElement
  1501. if (arguments[0]) { // PShapeSVG
  1502. this.element = arguments[1];
  1503. this.vertexCodes = arguments[0].vertexCodes.slice();
  1504. this.vertices = arguments[0].vertices.slice();
  1505. this.stroke = arguments[0].stroke;
  1506. this.strokeColor = arguments[0].strokeColor;
  1507. this.strokeWeight = arguments[0].strokeWeight;
  1508. this.strokeCap = arguments[0].strokeCap;
  1509. this.strokeJoin = arguments[0].strokeJoin;
  1510. this.strokeGradient = arguments[0].strokeGradient;
  1511. this.strokeGradientPaint = arguments[0].strokeGradientPaint;
  1512. this.strokeName = arguments[0].strokeName;
  1513. this.fill = arguments[0].fill;
  1514. this.fillColor = arguments[0].fillColor;
  1515. this.fillGradient = arguments[0].fillGradient;
  1516. this.fillGradientPaint = arguments[0].fillGradientPaint;
  1517. this.fillName = arguments[0].fillName;
  1518. this.strokeOpacity = arguments[0].strokeOpacity;
  1519. this.fillOpacity = arguments[0].fillOpacity;
  1520. this.opacity = arguments[0].opacity;
  1521. }
  1522. }
  1523. }
  1524. this.name = this.element.getStringAttribute("id");
  1525. var displayStr = this.element.getStringAttribute("display", "inline");
  1526. this.visible = displayStr !== "none";
  1527. var str = this.element.getAttribute("transform");
  1528. if (str) {
  1529. this.matrix = this.parseMatrix(str);
  1530. }
  1531. // not proper parsing of the viewBox, but will cover us for cases where
  1532. // the width and height of the object is not specified
  1533. var viewBoxStr = this.element.getStringAttribute("viewBox");
  1534. if ( viewBoxStr !== null ) {
  1535. var viewBox = viewBoxStr.split(" ");
  1536. this.width = viewBox[2];
  1537. this.height = viewBox[3];
  1538. }
  1539. // TODO if viewbox is not same as width/height, then use it to scale
  1540. // the original objects. for now, viewbox only used when width/height
  1541. // are empty values (which by the spec means w/h of "100%"
  1542. var unitWidth = this.element.getStringAttribute("width");
  1543. var unitHeight = this.element.getStringAttribute("height");
  1544. if (unitWidth !== null) {
  1545. this.width = this.parseUnitSize(unitWidth);
  1546. this.height = this.parseUnitSize(unitHeight);
  1547. } else {
  1548. if ((this.width === 0) || (this.height === 0)) {
  1549. // For the spec, the default is 100% and 100%. For purposes
  1550. // here, insert a dummy value because this is prolly just a
  1551. // font or something for which the w/h doesn't matter.
  1552. this.width = 1;
  1553. this.height = 1;
  1554. //show warning
  1555. throw("The width and/or height is not " +
  1556. "readable in the <svg> tag of this file.");
  1557. }
  1558. }
  1559. this.parseColors(this.element);
  1560. this.parseChildren(this.element);
  1561. };
  1562. PShapeSVG.prototype = {
  1563. // parseMatrix missing
  1564. // getChild missing
  1565. // print missing
  1566. parseMatrix: function(str) { },
  1567. parseChildren:function(element) {
  1568. var newelement = element.getChildren();
  1569. var children = new p.PShape();
  1570. for (var i = 0; i < newelement.length; i++) {
  1571. var kid = this.parseChild(newelement[i]);
  1572. if (kid) {
  1573. children.addChild(kid);
  1574. }
  1575. }
  1576. this.children.push(children);
  1577. },
  1578. getName: function() {
  1579. return this.name;
  1580. },
  1581. parseChild: function( elem ) {
  1582. var name = elem.getName();
  1583. var shape;
  1584. switch (name) {
  1585. case "g":
  1586. shape = new PShapeSVG(this, elem);
  1587. break;
  1588. case "defs":
  1589. // generally this will contain gradient info, so may
  1590. // as well just throw it into a group element for parsing
  1591. shape = new PShapeSVG(this, elem);
  1592. break;
  1593. case "line":
  1594. shape = new PShapeSVG(this, elem);
  1595. shape.parseLine();
  1596. break;
  1597. case "circle":
  1598. shape = new PShapeSVG(this, elem);
  1599. shape.parseEllipse(true);
  1600. break;
  1601. case "ellipse":
  1602. shape = new PShapeSVG(this, elem);
  1603. shape.parseEllipse(false);
  1604. break;
  1605. case "rect":
  1606. shape = new PShapeSVG(this, elem);
  1607. shape.parseRect();
  1608. break;
  1609. case "polygon":
  1610. shape = new PShapeSVG(this, elem);
  1611. shape.parsePoly(true);
  1612. break;
  1613. case "polyline":
  1614. shape = new PShapeSVG(this, elem);
  1615. shape.parsePoly(false);
  1616. break;
  1617. case "path":
  1618. shape = new PShapeSVG(this, elem);
  1619. shape.parsePath();
  1620. break;
  1621. case "radialGradient":
  1622. //return new RadialGradient(this, elem);
  1623. break;
  1624. case "linearGradient":
  1625. //return new LinearGradient(this, elem);
  1626. break;
  1627. case "text":
  1628. p.println("Text in SVG files is not currently supported, convert text to outlines instead." );
  1629. break;
  1630. case "filter":
  1631. p.println("Filters are not supported.");
  1632. break;
  1633. case "mask":
  1634. p.println("Masks are not supported.");
  1635. break;
  1636. default:
  1637. p.println("Ignoring <" + name + "> tag.");
  1638. break;
  1639. }
  1640. return shape;
  1641. },
  1642. parsePath: function(){
  1643. this.family = p.PATH;
  1644. this.kind = 0;
  1645. var c;
  1646. var pathData = p.trim(this.element.getStringAttribute("d").replace(/\s+/g,' '));
  1647. if (pathData === null) { return; }
  1648. var pathDataChars = pathData.toCharArray();
  1649. var pathBuffer = "";
  1650. var lastSeparate = false;
  1651. for (var i = 0; i < pathDataChars.length; i++) {
  1652. c = pathDataChars[i].toString();
  1653. var separate = false;
  1654. if (c === "M" || c === 'm' ||
  1655. c === 'L' || c === 'l' ||
  1656. c === 'H' || c === 'h' ||
  1657. c === 'V' || c === 'v' ||
  1658. c === 'C' || c === 'c' || // beziers
  1659. c === 'S' || c === 's' ||
  1660. c === 'Q' || c === 'q' || // quadratic beziers
  1661. c === 'T' || c === 't' ||
  1662. c === 'Z' || c === 'z' || // closepath
  1663. c === ',') {
  1664. separate = true;
  1665. if (i !== 0 ) {
  1666. pathBuffer +="|";
  1667. }
  1668. }
  1669. if (c === 'Z' || c === 'z') {
  1670. separate = false;
  1671. }
  1672. if (c === '-' && !lastSeparate) {
  1673. // allow for 'e' notation in numbers, e.g. 2.10e-9
  1674. // http://dev.processing.org/bugs/show_bug.cgi?id=1408
  1675. if (i === 0 || pathDataChars[i-1] !== 'e') {
  1676. pathBuffer +="|";
  1677. }
  1678. }
  1679. if (c !== ',') {
  1680. pathBuffer += c; //"" + pathDataBuffer.charAt(i));
  1681. }
  1682. if (separate && c !== ',' && c !== '-') {
  1683. pathBuffer +="|";
  1684. }
  1685. lastSeparate = separate;
  1686. }
  1687. // split into array
  1688. var pathDataKeys = pathBuffer.toString().split(/[|\s+]/g);
  1689. // loop through the array and remove spaces
  1690. for(i =0; i < pathDataKeys.length; i++){
  1691. if (pathDataKeys[i] === ""){
  1692. pathDataKeys.splice(i, 1);
  1693. }
  1694. }
  1695. var cx = 0,
  1696. cy = 0,
  1697. ctrlX = 0,
  1698. ctrlY = 0,
  1699. ctrlX1 = 0,
  1700. ctrlX2 = 0,
  1701. ctrlY1 = 0,
  1702. ctrlY2 = 0,
  1703. endX = 0,
  1704. endY = 0,
  1705. ppx = 0,
  1706. ppy = 0,
  1707. px = 0,
  1708. py = 0;
  1709. i = 0;
  1710. while (i < pathDataKeys.length) {
  1711. c = p.trim(pathDataKeys[i].charAt(0 ));
  1712. switch (c) {
  1713. case 'M': // M - move to (absolute)
  1714. cx = parseFloat(pathDataKeys[i + 1]);
  1715. cy = parseFloat(pathDataKeys[i + 2]);
  1716. this.parsePathMoveto(cx, cy);
  1717. i += 3;
  1718. break;
  1719. case 'm': // m - move to (relative)
  1720. cx = parseFloat(cx)+ parseFloat(pathDataKeys[i + 1]);
  1721. cy = parseFloat(cy)+ parseFloat(pathDataKeys[i + 2]);
  1722. this.parsePathMoveto(cx, cy);
  1723. i += 3;
  1724. break;
  1725. case 'L':
  1726. cx = parseFloat(pathDataKeys[i + 1]);
  1727. cy = parseFloat(pathDataKeys[i + 2]);
  1728. this.parsePathLineto(cx, cy);
  1729. i += 3;
  1730. break;
  1731. case 'l':
  1732. cx = parseFloat(cx) + parseFloat(pathDataKeys[i + 1]);
  1733. cy = parseFloat(cy) + parseFloat(pathDataKeys[i + 2]);
  1734. this.parsePathLineto(cx, cy);
  1735. i += 3;
  1736. break;
  1737. // horizontal lineto absolute
  1738. case 'H':
  1739. cx = parseFloat(pathDataKeys[i + 1]);
  1740. this.parsePathLineto(cx, cy);
  1741. i += 2;
  1742. break;
  1743. // horizontal lineto relative
  1744. case 'h':
  1745. cx = parseFloat(cx) + parseFloat(pathDataKeys[i + 1]);
  1746. this.parsePathLineto(cx, cy);
  1747. i += 2;
  1748. break;
  1749. case 'V':
  1750. cy = parseFloat(pathDataKeys[i + 1]);
  1751. this.parsePathLineto(cx, cy);
  1752. i += 2;
  1753. break;
  1754. case 'v':
  1755. cy = parseFloat(cy) + parseFloat(pathDataKeys[i + 1]);
  1756. this.parsePathLineto(cx, cy);
  1757. i += 2;
  1758. break;
  1759. // C - curve to (absolute)
  1760. case 'C':
  1761. ctrlX1 = parseFloat(pathDataKeys[i + 1]);
  1762. ctrlY1 = parseFloat(pathDataKeys[i + 2]);
  1763. ctrlX2 = parseFloat(pathDataKeys[i + 3]);
  1764. ctrlY2 = parseFloat(pathDataKeys[i + 4]);
  1765. endX = parseFloat(pathDataKeys[i + 5]);
  1766. endY = parseFloat(pathDataKeys[i + 6]);
  1767. this.parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY);
  1768. cx = endX;
  1769. cy = endY;
  1770. i += 7;
  1771. break;
  1772. // c - curve to (relative)
  1773. case 'c':
  1774. ctrlX1 = parseFloat(cx) + parseFloat(pathDataKeys[i + 1]);
  1775. ctrlY1 = parseFloat(cy) + parseFloat(pathDataKeys[i + 2]);
  1776. ctrlX2 = parseFloat(cx) + parseFloat(pathDataKeys[i + 3]);
  1777. ctrlY2 = parseFloat(cy) + parseFloat(pathDataKeys[i + 4]);
  1778. endX = parseFloat(cx) + parseFloat(pathDataKeys[i + 5]);
  1779. endY = parseFloat(cy) + parseFloat(pathDataKeys[i + 6]);
  1780. this.parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY);
  1781. cx = endX;
  1782. cy = endY;
  1783. i += 7;
  1784. break;
  1785. // S - curve to shorthand (absolute)
  1786. case 'S':
  1787. ppx = parseFloat(this.vertices[ this.vertices.length-2 ][0]);
  1788. ppy = parseFloat(this.vertices[ this.vertices.length-2 ][1]);
  1789. px = parseFloat(this.vertices[ this.vertices.length-1 ][0]);
  1790. py = parseFloat(this.vertices[ this.vertices.length-1 ][1]);
  1791. ctrlX1 = px + (px - ppx);
  1792. ctrlY1 = py + (py - ppy);
  1793. ctrlX2 = parseFloat(pathDataKeys[i + 1]);
  1794. ctrlY2 = parseFloat(pathDataKeys[i + 2]);
  1795. endX = parseFloat(pathDataKeys[i + 3]);
  1796. endY = parseFloat(pathDataKeys[i + 4]);
  1797. this.parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY);
  1798. cx = endX;
  1799. cy = endY;
  1800. i += 5;
  1801. break;
  1802. // s - curve to shorthand (relative)
  1803. case 's':
  1804. ppx = parseFloat(this.vertices[this.vertices.length-2][0]);
  1805. ppy = parseFloat(this.vertices[this.vertices.length-2][1]);
  1806. px = parseFloat(this.vertices[this.vertices.length-1][0]);
  1807. py = parseFloat(this.vertices[this.vertices.length-1][1]);
  1808. ctrlX1 = px + (px - ppx);
  1809. ctrlY1 = py + (py - ppy);
  1810. ctrlX2 = parseFloat(cx) + parseFloat(pathDataKeys[i + 1]);
  1811. ctrlY2 = parseFloat(cy) + parseFloat(pathDataKeys[i + 2]);
  1812. endX = parseFloat(cx) + parseFloat(pathDataKeys[i + 3]);
  1813. endY = parseFloat(cy) + parseFloat(pathDataKeys[i + 4]);
  1814. this.parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY);
  1815. cx = endX;
  1816. cy = endY;
  1817. i += 5;
  1818. break;
  1819. // Q - quadratic curve to (absolute)
  1820. case 'Q':
  1821. ctrlX = parseFloat(pathDataKeys[i + 1]);
  1822. ctrlY = parseFloat(pathDataKeys[i + 2]);
  1823. endX = parseFloat(pathDataKeys[i + 3]);
  1824. endY = parseFloat(pathDataKeys[i + 4]);
  1825. this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
  1826. cx = endX;
  1827. cy = endY;
  1828. i += 5;
  1829. break;
  1830. // q - quadratic curve to (relative)
  1831. case 'q':
  1832. ctrlX = parseFloat(cx) + parseFloat(pathDataKeys[i + 1]);
  1833. ctrlY = parseFloat(cy) + parseFloat(pathDataKeys[i + 2]);
  1834. endX = parseFloat(cx) + parseFloat(pathDataKeys[i + 3]);
  1835. endY = parseFloat(cy) + parseFloat(pathDataKeys[i + 4]);
  1836. this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
  1837. cx = endX;
  1838. cy = endY;
  1839. i += 5;
  1840. break;
  1841. // T - quadratic curve to shorthand (absolute)
  1842. // The control point is assumed to be the reflection of the
  1843. // control point on the previous command relative to the
  1844. // current point. (If there is no previous command or if the
  1845. // previous command was not a Q, q, T or t, assume the control
  1846. // point is coincident with the current point.)
  1847. case 'T':
  1848. ppx = this.vertices[this.vertices.length-2][0];
  1849. ppy = this.vertices[this.vertices.length-2][1];
  1850. px = this.vertices[this.vertices.length-1][0];
  1851. py = this.vertices[this.vertices.length-1][1];
  1852. ctrlX = px + (px - ppx);
  1853. ctrlY = py + (py - ppy);
  1854. endX = parseFloat(pathDataKeys[i + 1]);
  1855. endY = parseFloat(pathDataKeys[i + 2]);
  1856. this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
  1857. cx = endX;
  1858. cy = endY;
  1859. i += 3;
  1860. break;
  1861. // t - quadratic curve to shorthand (relative)
  1862. case 't':
  1863. ppx = this.vertices[this.vertices.length-2][0];
  1864. ppy = this.vertices[this.vertices.length-2][1];
  1865. px = this.vertices[this.vertices.length-1][0];
  1866. py = this.vertices[this.vertices.length-1][1];
  1867. ctrlX = px + (px - ppx);
  1868. ctrlY = py + (py - ppy);
  1869. endX = parseFloat(cx) + parseFloat(pathDataKeys[i + 1]);
  1870. endY = parseFloat(cy) + parseFloat(pathDataKeys[i + 2]);
  1871. this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
  1872. cx = endX;
  1873. cy = endY;
  1874. i += 3;
  1875. break;
  1876. case 'Z':
  1877. case 'z':
  1878. this.close = true;
  1879. i++;
  1880. break;
  1881. default:
  1882. i++;
  1883. /*String parsed =
  1884. PApplet.join(PApplet.subset(pathDataKeys, 0, i), ",");
  1885. String unparsed =
  1886. PApplet.join(PApplet.subset(pathDataKeys, i), ",");
  1887. System.err.println("parsed: " + parsed);
  1888. System.err.println("unparsed: " + unparsed);
  1889. if (pathDataKeys[i].equals("a") || pathDataKeys[i].equals("A")) {
  1890. String msg = "Sorry, elliptical arc support for SVG files " +
  1891. "is not yet implemented (See bug #996 for details)";
  1892. throw new RuntimeException(msg);
  1893. }
  1894. throw new RuntimeException("shape command not handled: " + pathDataKeys[i]);
  1895. }*/
  1896. }
  1897. }
  1898. },
  1899. parsePathQuadto: function(x1, y1, cx, cy, x2, y2) {
  1900. this.parsePathCode(p.BEZIER_VERTEX);
  1901. // x1/y1 already covered by last moveto, lineto, or curveto
  1902. this.parsePathVertex(x1 + ((cx-x1)*2/3), y1 + ((cy-y1)*2/3));
  1903. this.parsePathVertex(x2 + ((cx-x2)*2/3), y2 + ((cy-y2)*2/3));
  1904. this.parsePathVertex(x2, y2);
  1905. },
  1906. parsePathCurveto : function(x1, y1, x2, y2, x3, y3) {
  1907. this.parsePathCode(p.BEZIER_VERTEX );
  1908. this.parsePathVertex(x1, y1);
  1909. this.parsePathVertex(x2, y2);
  1910. this.parsePathVertex(x3, y3);
  1911. },
  1912. parsePathLineto: function(px, py) {
  1913. this.parsePathCode(p.VERTEX);
  1914. this.parsePathVertex(px, py);
  1915. // add property to distinguish between curContext.moveTo or curContext.lineTo
  1916. this.vertices[this.vertices.length-1]["moveTo"] = false;
  1917. },
  1918. parsePathMoveto: function(px, py) {
  1919. if (this.vertices.length > 0) {
  1920. this.parsePathCode(p.BREAK);
  1921. }
  1922. this.parsePathCode(p.VERTEX);
  1923. this.parsePathVertex(px, py);
  1924. // add property to distinguish between curContext.moveTo or curContext.lineTo
  1925. this.vertices[this.vertices.length-1]["moveTo"] = true;
  1926. },
  1927. parsePathVertex: function(x, y) {
  1928. var verts = [];
  1929. verts[0] = x;
  1930. verts[1] = y;
  1931. this.vertices.push(verts);
  1932. },
  1933. parsePathCode: function(what) {
  1934. this.vertexCodes.push(what);
  1935. },
  1936. parsePoly: function(val) {
  1937. this.family = p.PATH;
  1938. this.close = val;
  1939. var pointsAttr = p.trim(this.element.getStringAttribute("points").replace(/\s+/g,' '));
  1940. if (pointsAttr !== null) {
  1941. var pointsBuffer = pointsAttr.split(" ");
  1942. for (var i = 0; i < pointsBuffer.length; i++) {
  1943. var verts = [];
  1944. var pb = pointsBuffer[i].split(',');
  1945. verts[0] = pb[0];
  1946. verts[1] = pb[1];
  1947. this.vertices.push(verts);
  1948. }
  1949. }
  1950. },
  1951. parseRect: function() {
  1952. this.kind = p.RECT;
  1953. this.family = p.PRIMITIVE;
  1954. this.params = [];
  1955. this.params[0] = this.element.getFloatAttribute("x");
  1956. this.params[1] = this.element.getFloatAttribute("y");
  1957. this.params[2] = this.element.getFloatAttribute("width");
  1958. this.params[3] = this.element.getFloatAttribute("height");
  1959. },
  1960. parseEllipse: function(val) {
  1961. this.kind = p.ELLIPSE;
  1962. this.family = p.PRIMITIVE;
  1963. this.params = [];
  1964. this.params[0] = this.element.getFloatAttribute("cx");
  1965. this.params[1] = this.element.getFloatAttribute("cy");
  1966. var rx, ry;
  1967. if (val) {
  1968. rx = ry = this.element.getFloatAttribute("r");
  1969. } else {
  1970. rx = this.element.getFloatAttribute("rx");
  1971. ry = this.element.getFloatAttribute("ry");
  1972. }
  1973. this.params[0] -= rx;
  1974. this.params[1] -= ry;
  1975. this.params[2] = rx*2;
  1976. this.params[3] = ry*2;
  1977. },
  1978. parseLine: function() {
  1979. this.kind = p.LINE;
  1980. this.family = p.PRIMITIVE;
  1981. this.params = [];
  1982. this.params[0] = this.element.getFloatAttribute("x1");
  1983. this.params[1] = this.element.getFloatAttribute("y1");
  1984. this.params[2] = this.element.getFloatAttribute("x2");
  1985. this.params[3] = this.element.getFloatAttribute("y2");
  1986. },
  1987. parseColors: function(element) {
  1988. if (element.hasAttribute("opacity")) {
  1989. this.setOpacity(element.getAttribute("opacity"));
  1990. }
  1991. if (element.hasAttribute("stroke")) {
  1992. this.setStroke(element.getAttribute("stroke"));
  1993. }
  1994. if (element.hasAttribute("stroke-width")) {
  1995. // if NaN (i.e. if it's 'inherit') then default back to the inherit setting
  1996. this.setStrokeWeight(element.getAttribute("stroke-width"));
  1997. }
  1998. if (element.hasAttribute("stroke-linejoin") ) {
  1999. this.setStrokeJoin(element.getAttribute("stroke-linejoin"));
  2000. }
  2001. if (element.hasAttribute("stroke-linecap")) {
  2002. this.setStrokeCap(element.getStringAttribute("stroke-linecap"));
  2003. }
  2004. // fill defaults to black (though stroke defaults to "none")
  2005. // http://www.w3.org/TR/SVG/painting.html#FillProperties
  2006. if (element.hasAttribute("fill")) {
  2007. this.setFill(element.getStringAttribute("fill"));
  2008. }
  2009. if (element.hasAttribute("style")) {
  2010. var styleText = element.getStringAttribute("style");
  2011. var styleTokens = styleText.toString().split( ";" );
  2012. for (var i = 0; i < styleTokens.length; i++) {
  2013. var tokens = p.trim(styleTokens[i].split( ":" ));
  2014. switch(tokens[0]){
  2015. case "fill":
  2016. this.setFill(tokens[1]);
  2017. break;
  2018. case "fill-opacity":
  2019. this.setFillOpacity(tokens[1]);
  2020. break;
  2021. case "stroke":
  2022. this.setStroke(tokens[1]);
  2023. break;
  2024. case "stroke-width":
  2025. this.setStrokeWeight(tokens[1]);
  2026. break;
  2027. case "stroke-linecap":
  2028. this.setStrokeCap(tokens[1]);
  2029. break;
  2030. case "stroke-linejoin":
  2031. this.setStrokeJoin(tokens[1]);
  2032. break;
  2033. case "stroke-opacity":
  2034. this.setStrokeOpacity(tokens[1]);
  2035. break;
  2036. case "opacity":
  2037. this.setOpacity(tokens[1]);
  2038. break;
  2039. // Other attributes are not yet implemented
  2040. }
  2041. }
  2042. }
  2043. },
  2044. setFillOpacity: function(opacityText) {
  2045. this.fillOpacity = parseFloat(opacityText);
  2046. this.fillColor = (parseInt(this.fillOpacity * 255, 16)) << 24 | this.fillColor & 0xFFFFFF;
  2047. },
  2048. setFill: function (fillText) {
  2049. var opacityMask = this.fillColor & 0xFF000000;
  2050. if (fillText === "none") {
  2051. this.fill = false;
  2052. } else if (fillText.indexOf("#") === 0) {
  2053. this.fill = true;
  2054. this.fillColor = opacityMask | (parseInt(fillText.substring(1), 16 )) & 0xFFFFFF;
  2055. } else if (fillText.indexOf("rgb") === 0) {
  2056. this.fill = true;
  2057. this.fillColor = opacityMask | this.parseRGB(fillText);
  2058. } else if (fillText.indexOf("url(#") === 0) {
  2059. this.fillName = fillText.substring(5, fillText.length - 1 );
  2060. /*Object fillObject = findChild(fillName);
  2061. if (fillObject instanceof Gradient) {
  2062. fill = true;
  2063. fillGradient = (Gradient) fillObject;
  2064. fillGradientPaint = calcGradientPaint(fillGradient); //, opacity);
  2065. } else {
  2066. System.err.println("url " + fillName + " refers to unexpected data");
  2067. }*/
  2068. } else {
  2069. if (colors[fillText]) {
  2070. this.fill = true;
  2071. this.fillColor = opacityMask | (parseInt(colors[fillText].substring(1), 16)) & 0xFFFFFF;
  2072. }
  2073. }
  2074. },
  2075. setOpacity: function(opacity) {
  2076. this.strokeColor = (parseInt(opacity * 255, 16)) << 24 | this.strokeColor & 0xFFFFFF;
  2077. this.fillColor = (parseInt(opacity * 255, 16)) << 24 | this.fillColor & 0xFFFFFF;
  2078. },
  2079. setStroke: function(strokeText) {
  2080. var opacityMask = this.strokeColor & 0xFF000000;
  2081. if (strokeText === "none") {
  2082. this.stroke = false;
  2083. } else if (strokeText.charAt( 0 ) === "#") {
  2084. this.stroke = true;
  2085. this.strokeColor = opacityMask | (parseInt( strokeText.substring( 1 ), 16 )) & 0xFFFFFF;
  2086. } else if (strokeText.indexOf( "rgb" ) === 0 ) {
  2087. this.stroke = true;
  2088. this.strokeColor = opacityMask | this.parseRGB(strokeText);
  2089. } else if (strokeText.indexOf( "url(#" ) === 0) {
  2090. this.strokeName = strokeText.substring(5, strokeText.length - 1);
  2091. //this.strokeObject = findChild(strokeName);
  2092. /*if (strokeObject instanceof Gradient) {
  2093. strokeGradient = (Gradient) strokeObject;
  2094. strokeGradientPaint = calcGradientPaint(strokeGradient); //, opacity);
  2095. } else {
  2096. System.err.println("url " + strokeName + " refers to unexpected data");
  2097. }*/
  2098. } else {
  2099. if (colors[strokeText]){
  2100. this.stroke = true;
  2101. this.strokeColor = opacityMask | (parseInt(colors[strokeText].substring(1), 16)) & 0xFFFFFF;
  2102. }
  2103. }
  2104. },
  2105. setStrokeWeight: function(weight) {
  2106. this.strokeWeight = this.parseUnitSize(weight);
  2107. },
  2108. setStrokeJoin: function(linejoin) {
  2109. if (linejoin === "miter") {
  2110. this.strokeJoin = p.MITER;
  2111. } else if (linejoin === "round") {
  2112. this.strokeJoin = p.ROUND;
  2113. } else if (linejoin === "bevel") {
  2114. this.strokeJoin = p.BEVEL;
  2115. }
  2116. },
  2117. setStrokeCap: function (linecap) {
  2118. if (linecap === "butt") {
  2119. this.strokeCap = p.SQUARE;
  2120. } else if (linecap === "round") {
  2121. this.strokeCap = p.ROUND;
  2122. } else if (linecap === "square") {
  2123. this.strokeCap = p.PROJECT;
  2124. }
  2125. },
  2126. setStrokeOpacity: function (opacityText) {
  2127. this.strokeOpacity = parseFloat(opacityText);
  2128. this.strokeColor = (parseInt(this.strokeOpacity * 255, 16)) << 24 | this.strokeColor & 0xFFFFFF;
  2129. },
  2130. parseRGB: function(color) {
  2131. var sub = color.substring(color.indexOf('(') + 1, color.indexOf(')'));
  2132. var values = sub.split(", ");
  2133. return (values[0] << 16) | (values[1] << 8) | (values[2]);
  2134. },
  2135. parseUnitSize: function (text) {
  2136. var len = text.length - 2;
  2137. if (len < 0) { return text; }
  2138. if (text.indexOf("pt") === len) {
  2139. return parseFloat(text.substring(0, len)) * 1.25;
  2140. } else if (text.indexOf("pc") === len) {
  2141. return parseFloat( text.substring( 0, len)) * 15;
  2142. } else if (text.indexOf("mm") === len) {
  2143. return parseFloat( text.substring(0, len)) * 3.543307;
  2144. } else if (text.indexOf("cm") === len) {
  2145. return parseFloat(text.substring(0, len)) * 35.43307;
  2146. } else if (text.indexOf("in") === len) {
  2147. return parseFloat(text.substring(0, len)) * 90;
  2148. } else if (text.indexOf("px") === len) {
  2149. return parseFloat(text.substring(0, len));
  2150. } else {
  2151. return parseFloat(text);
  2152. }
  2153. }
  2154. };
  2155. ////////////////////////////////////////////////////////////////////////////
  2156. // XMLAttribute
  2157. ////////////////////////////////////////////////////////////////////////////
  2158. var XMLAttribute = p.XMLAttribute = function (fname, n, nameSpace, v, t){
  2159. this.fullName = fname || "";
  2160. this.name = n || "";
  2161. this.namespace = nameSpace || "";
  2162. this.value = v;
  2163. this.type = t;
  2164. };
  2165. XMLAttribute.prototype = {
  2166. getName: function() {
  2167. return this.name;
  2168. },
  2169. getFullName: function() {
  2170. return this.fullName;
  2171. },
  2172. getNamespace: function() {
  2173. return this.namespace;
  2174. },
  2175. getValue: function() {
  2176. return this.value;
  2177. },
  2178. getType: function() {
  2179. return this.type;
  2180. },
  2181. setValue: function(newval) {
  2182. this.value = newval;
  2183. }
  2184. };
  2185. ////////////////////////////////////////////////////////////////////////////
  2186. // XMLElement
  2187. ////////////////////////////////////////////////////////////////////////////
  2188. var XMLElement = p.XMLElement = function() {
  2189. if (arguments.length === 4) {
  2190. this.attributes = [];
  2191. this.children = [];
  2192. this.fullName = arguments[0] || "";
  2193. if (arguments[1]) {
  2194. this.name = arguments[1];
  2195. } else {
  2196. var index = this.fullName.indexOf(':');
  2197. if (index >= 0) {
  2198. this.name = this.fullName.substring(index + 1);
  2199. } else {
  2200. this.name = this.fullName;
  2201. }
  2202. }
  2203. this.namespace = arguments[1];
  2204. this.content = "";
  2205. this.lineNr = arguments[3];
  2206. this.systemID = arguments[2];
  2207. this.parent = null;
  2208. }
  2209. else if ((arguments.length === 1 && arguments[0].indexOf(".") > -1) || arguments.length === 2) { // filename or svg xml element
  2210. if (arguments[arguments.length -1].indexOf(".") > -1) { //its a filename
  2211. this.attributes = [];
  2212. this.children = [];
  2213. this.fullName = "";
  2214. this.name = "";
  2215. this.namespace = "";
  2216. this.content = "";
  2217. this.systemID = "";
  2218. this.lineNr = "";
  2219. this.parent = null;
  2220. this.parse(arguments[arguments.length -1]);
  2221. } else { //xml string
  2222. this.parse(arguments[arguments.length -1]);
  2223. }
  2224. }
  2225. else { //empty ctor
  2226. this.attributes = [];
  2227. this.children = [];
  2228. this.fullName = "";
  2229. this.name = "";
  2230. this.namespace = "";
  2231. this.content = "";
  2232. this.systemID = "";
  2233. this.lineNr = "";
  2234. this.parent = null;
  2235. }
  2236. return this;
  2237. };
  2238. /*XMLElement methods
  2239. missing: enumerateAttributeNames(), enumerateChildren(),
  2240. NOTE: parse does not work when a url is passed in
  2241. */
  2242. XMLElement.prototype = {
  2243. parse: function(filename) {
  2244. var xmlDoc;
  2245. try {
  2246. xmlDoc = new DOMParser().parseFromString(ajax(filename), "text/xml");
  2247. var elements = xmlDoc.documentElement;
  2248. if (elements) {
  2249. this.parseChildrenRecursive(null, elements);
  2250. } else {
  2251. throw ("Error loading document");
  2252. }
  2253. return this;
  2254. } catch(e) {
  2255. throw(e);
  2256. }
  2257. },
  2258. createElement: function () {
  2259. if (arguments.length === 2) {
  2260. return new XMLElement(arguments[0], arguments[1], null, null);
  2261. } else {
  2262. return new XMLElement(arguments[0], arguments[1], arguments[2], arguments[3]);
  2263. }
  2264. },
  2265. hasAttribute: function (name) {
  2266. return this.getAttribute(name) !== null;
  2267. //2 parameter call missing
  2268. },
  2269. createPCDataElement: function () {
  2270. return new XMLElement();
  2271. },
  2272. equals: function(object){
  2273. if (typeof object === "Object") {
  2274. return this.equalsXMLElement(object);
  2275. }
  2276. },
  2277. equalsXMLElement: function (object) {
  2278. if (object instanceof XMLElement) {
  2279. if (this.name !== object.getLocalName) { return false; }
  2280. if (this.attributes.length !== object.getAttributeCount()) { return false; }
  2281. for (var i = 0; i < this.attributes.length; i++){
  2282. if (! object.hasAttribute(this.attributes[i].getName(), this.attributes[i].getNamespace())) { return false; }
  2283. if (this.attributes[i].getValue() !== object.attributes[i].getValue()) { return false; }
  2284. if (this.attributes[i].getType() !== object.attributes[i].getType()) { return false; }
  2285. }
  2286. if (this.children.length !== object.getChildCount()) { return false; }
  2287. var child1, child2;
  2288. for (i = 0; i < this.children.length; i++) {
  2289. child1 = this.getChildAtIndex(i);
  2290. child2 = object.getChildAtIndex(i);
  2291. if (! child1.equalsXMLElement(child2)) { return false; }
  2292. }
  2293. return true;
  2294. }
  2295. },
  2296. getContent: function(){
  2297. return this.content;
  2298. },
  2299. getAttribute: function (){
  2300. var attribute;
  2301. if( arguments.length === 2 ){
  2302. attribute = this.findAttribute(arguments[0]);
  2303. if (attribute) {
  2304. return attribute.getValue();
  2305. } else {
  2306. return arguments[1];
  2307. }
  2308. } else if (arguments.length === 1) {
  2309. attribute = this.findAttribute(arguments[0]);
  2310. if (attribute) {
  2311. return attribute.getValue();
  2312. } else {
  2313. return null;
  2314. }
  2315. }
  2316. },
  2317. getStringAttribute: function() {
  2318. if (arguments.length === 1) {
  2319. return this.getAttribute(arguments[0]);
  2320. } else if (arguments.length === 2){
  2321. return this.getAttribute(arguments[0], arguments[1]);
  2322. } else {
  2323. return this.getAttribute(arguments[0], arguments[1],arguments[2]);
  2324. }
  2325. },
  2326. getFloatAttribute: function() {
  2327. if (arguments.length === 1 ) {
  2328. return this.getAttribute(arguments[0], 0);
  2329. } else if (arguments.length === 2 ){
  2330. return this.getAttribute(arguments[0], arguments[1]);
  2331. } else {
  2332. return this.getAttribute(arguments[0], arguments[1],arguments[2]);
  2333. }
  2334. },
  2335. getIntAttribute: function () {
  2336. if (arguments.length === 1) {
  2337. return this.getAttribute( arguments[0], 0 );
  2338. } else if (arguments.length === 2) {
  2339. return this.getAttribute(arguments[0], arguments[1]);
  2340. } else {
  2341. return this.getAttribute(arguments[0], arguments[1],arguments[2]);
  2342. }
  2343. },
  2344. hasChildren: function () {
  2345. return this.children.length > 0 ;
  2346. },
  2347. addChild: function (child) {
  2348. if (child !== null) {
  2349. child.parent = this;
  2350. this.children.push(child);
  2351. }
  2352. },
  2353. insertChild: function (child, index) {
  2354. if (child) {
  2355. if ((child.getLocalName() === null) && (! this.hasChildren())) {
  2356. var lastChild = this.children[this.children.length -1];
  2357. if (lastChild.getLocalName() === null) {
  2358. lastChild.setContent(lastChild.getContent() + child.getContent());
  2359. return;
  2360. }
  2361. }
  2362. child.parent = this;
  2363. this.children.splice(index,0,child);
  2364. }
  2365. },
  2366. getChild: function (index){
  2367. if (typeof index === "number") {
  2368. return this.children[index];
  2369. }
  2370. else if (index.indexOf('/') !== -1) { // path was given
  2371. this.getChildRecursive(index.split("/"), 0);
  2372. } else {
  2373. var kid, kidName;
  2374. for (var i = 0; i < this.getChildCount(); i++) {
  2375. kid = this.getChild(i);
  2376. kidName = kid.getName();
  2377. if (kidName !== null && kidName === index) {
  2378. return kid;
  2379. }
  2380. }
  2381. return null;
  2382. }
  2383. },
  2384. getChildren: function(){
  2385. if (arguments.length === 1) {
  2386. if (typeof arguments[0] === "number") {
  2387. return this.getChild( arguments[0]);
  2388. } else if (arguments[0].indexOf('/') !== -1) { // path was given
  2389. return this.getChildrenRecursive( arguments[0].split("/"), 0);
  2390. } else {
  2391. var matches = [];
  2392. var kid, kidName;
  2393. for (var i = 0; i < this.getChildCount(); i++) {
  2394. kid = this.getChild(i);
  2395. kidName = kid.getName();
  2396. if (kidName !== null && kidName === arguments[0]) {
  2397. matches.push(kid);
  2398. }
  2399. }
  2400. return matches;
  2401. }
  2402. }else {
  2403. return this.children;
  2404. }
  2405. },
  2406. getChildCount: function(){
  2407. return this.children.length;
  2408. },
  2409. getChildRecursive: function (items, offset) {
  2410. var kid, kidName;
  2411. for(var i = 0; i < this.getChildCount(); i++) {
  2412. kid = this.getChild(i);
  2413. kidName = kid.getName();
  2414. if (kidName !== null && kidName === items[offset]) {
  2415. if (offset === items.length-1) {
  2416. return kid;
  2417. } else {
  2418. offset += 1;
  2419. return kid.getChildRecursive(items, offset);
  2420. }
  2421. }
  2422. }
  2423. return null;
  2424. },
  2425. getChildrenRecursive: function (items, offset) {
  2426. if (offset === items.length-1) {
  2427. return this.getChildren(items[offset]);
  2428. }
  2429. var matches = this.getChildren(items[offset]);
  2430. var kidMatches;
  2431. for (var i = 0; i < matches.length; i++) {
  2432. kidMatches = matches[i].getChildrenRecursive(items, offset+1);
  2433. }
  2434. return kidMatches;
  2435. },
  2436. parseChildrenRecursive: function (parent , elementpath){
  2437. var xmlelement,
  2438. xmlattribute,
  2439. tmpattrib;
  2440. if (!parent) {
  2441. this.fullName = elementpath.localName;
  2442. this.name = elementpath.nodeName;
  2443. this.content = elementpath.textContent || "";
  2444. xmlelement = this;
  2445. } else { // a parent
  2446. xmlelement = new XMLElement(elementpath.localName, elementpath.nodeName, "", "");
  2447. xmlelement.content = elementpath.textContent || "";
  2448. xmlelement.parent = parent;
  2449. }
  2450. for (var l = 0; l < elementpath.attributes.length; l++) {
  2451. tmpattrib = elementpath.attributes[l];
  2452. xmlattribute = new XMLAttribute(tmpattrib.getname , tmpattrib.nodeName, tmpattrib.namespaceURI , tmpattrib.nodeValue , tmpattrib.nodeType);
  2453. xmlelement.attributes.push(xmlattribute);
  2454. }
  2455. for (var node in elementpath.childNodes){
  2456. if(elementpath.childNodes[node].nodeType === 1) { //ELEMENT_NODE type
  2457. xmlelement.children.push( xmlelement.parseChildrenRecursive(xmlelement, elementpath.childNodes[node]));
  2458. }
  2459. }
  2460. /*
  2461. for( var m = 0; m < elementpath.childElementCount; m++ ) {
  2462. xmlelement.children.push( xmlelement.parseChildrenRecursive(xmlelement, elementpath.childNodes[m]) );
  2463. }*/
  2464. return xmlelement;
  2465. },
  2466. isLeaf: function(){
  2467. return this.hasChildren();
  2468. },
  2469. listChildren: function() {
  2470. var arr = [];
  2471. for (var i = 0; i < this.children.length; i++) {
  2472. arr.push( this.getChild(i).getName());
  2473. }
  2474. return arr;
  2475. },
  2476. removeAttribute: function (name , namespace) {
  2477. this.namespace = namespace || "";
  2478. for (var i = 0; i < this.attributes.length; i++){
  2479. if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
  2480. this.attributes.splice(i, 0);
  2481. }
  2482. }
  2483. },
  2484. removeChild: function(child) {
  2485. if (child) {
  2486. for (var i = 0; i < this.children.length; i++) {
  2487. if (this.children[i].equalsXMLElement(child)) {
  2488. this.children.splice(i, 0);
  2489. }
  2490. }
  2491. }
  2492. },
  2493. removeChildAtIndex: function(index) {
  2494. if (this.children.length > index) { //make sure its not outofbounds
  2495. this.children.splice(index, 0);
  2496. }
  2497. },
  2498. findAttribute: function (name, namespace) {
  2499. this.namespace = namespace || "";
  2500. for (var i = 0; i < this.attributes.length; i++ ) {
  2501. if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
  2502. return this.attributes[i];
  2503. }
  2504. }
  2505. },
  2506. setAttribute: function() {
  2507. var attr;
  2508. if (arguments.length === 3) {
  2509. var index = arguments[0].indexOf(':');
  2510. var name = arguments[0].substring(index + 1);
  2511. attr = this.findAttribute( name, arguments[1] );
  2512. if (attr) {
  2513. attr.setValue(arguments[2]);
  2514. } else {
  2515. attr = new XMLAttribute(arguments[0], name, arguments[1], arguments[2], "CDATA");
  2516. this.attributes.addElement(attr);
  2517. }
  2518. } else {
  2519. attr = this.findAttribute(arguments[0]);
  2520. if (attr) {
  2521. attr.setValue(arguments[1]);
  2522. } else {
  2523. attr = new XMLAttribute(arguments[0], arguments[0], null, arguments[1], "CDATA");
  2524. this.attributes.addElement(attr);
  2525. }
  2526. }
  2527. },
  2528. setContent: function(content) {
  2529. this.content = content;
  2530. },
  2531. setName: function() {
  2532. if (arguments.length === 1) {
  2533. this.name = arguments[0];
  2534. this.fullName = arguments[0];
  2535. this.namespace = arguments[0];
  2536. } else {
  2537. var index = arguments[0].indexOf(':');
  2538. if ((arguments[1] === null) || (index < 0)) {
  2539. this.name = arguments[0];
  2540. } else {
  2541. this.name = arguments[0].substring(index + 1);
  2542. }
  2543. this.fullName = arguments[0];
  2544. this.namespace = arguments[1];
  2545. }
  2546. },
  2547. getName: function() {
  2548. return this.fullName;
  2549. }
  2550. };
  2551. ////////////////////////////////////////////////////////////////////////////
  2552. // 2D Matrix
  2553. ////////////////////////////////////////////////////////////////////////////
  2554. /*
  2555. Helper function for printMatrix(). Finds the largest scalar
  2556. in the matrix, then number of digits left of the decimal.
  2557. Call from PMatrix2D and PMatrix3D's print() function.
  2558. */
  2559. var printMatrixHelper = function printMatrixHelper(elements) {
  2560. var big = 0;
  2561. for (var i = 0; i < elements.length; i++) {
  2562. if (i !== 0) {
  2563. big = Math.max(big, Math.abs(elements[i]));
  2564. } else {
  2565. big = Math.abs(elements[i]);
  2566. }
  2567. }
  2568. var digits = (big + "").indexOf(".");
  2569. if (digits === 0) {
  2570. digits = 1;
  2571. } else if (digits === -1) {
  2572. digits = (big + "").length;
  2573. }
  2574. return digits;
  2575. };
  2576. var PMatrix2D = p.PMatrix2D = function() {
  2577. if (arguments.length === 0) {
  2578. this.reset();
  2579. } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
  2580. this.set(arguments[0].array());
  2581. } else if (arguments.length === 6) {
  2582. this.set(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
  2583. }
  2584. };
  2585. PMatrix2D.prototype = {
  2586. set: function() {
  2587. if (arguments.length === 6) {
  2588. var a = arguments;
  2589. this.set([a[0], a[1], a[2],
  2590. a[3], a[4], a[5]]);
  2591. } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
  2592. this.elements = arguments[0].array();
  2593. } else if (arguments.length === 1 && arguments[0] instanceof Array) {
  2594. this.elements = arguments[0].slice();
  2595. }
  2596. },
  2597. get: function() {
  2598. var outgoing = new PMatrix2D();
  2599. outgoing.set(this.elements);
  2600. return outgoing;
  2601. },
  2602. reset: function() {
  2603. this.set([1, 0, 0, 0, 1, 0]);
  2604. },
  2605. // Returns a copy of the element values.
  2606. array: function array() {
  2607. return this.elements.slice();
  2608. },
  2609. translate: function(tx, ty) {
  2610. this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2];
  2611. this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5];
  2612. },
  2613. transpose: function() {
  2614. // Does nothing in Processing.
  2615. },
  2616. mult: function(source, target) {
  2617. var x, y;
  2618. if (source instanceof PVector) {
  2619. x = source.x;
  2620. y = source.y;
  2621. if (!target) {
  2622. target = new PVector();
  2623. }
  2624. } else if (source instanceof Array) {
  2625. x = source[0];
  2626. y = source[1];
  2627. if (!target) {
  2628. target = [];
  2629. }
  2630. }
  2631. if (target instanceof Array) {
  2632. target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2];
  2633. target[1] = this.elements[3] * x + this.elements[4] * y + this.elements[5];
  2634. } else if (target instanceof PVector) {
  2635. target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2];
  2636. target.y = this.elements[3] * x + this.elements[4] * y + this.elements[5];
  2637. target.z = 0;
  2638. }
  2639. return target;
  2640. },
  2641. multX: function(x, y) {
  2642. return (x * this.elements[0] + y * this.elements[1] + this.elements[2]);
  2643. },
  2644. multY: function(x, y) {
  2645. return (x * this.elements[3] + y * this.elements[4] + this.elements[5]);
  2646. },
  2647. skewX: function(angle) {
  2648. this.apply(1, 0, 1, angle, 0, 0);
  2649. },
  2650. skewY: function(angle) {
  2651. this.apply(1, 0, 1, 0, angle, 0);
  2652. },
  2653. determinant: function() {
  2654. return (this.elements[0] * this.elements[4] - this.elements[1] * this.elements[3]);
  2655. },
  2656. invert: function() {
  2657. var d = this.determinant();
  2658. if ( Math.abs( d ) > p.FLOAT_MIN ) {
  2659. var old00 = this.elements[0];
  2660. var old01 = this.elements[1];
  2661. var old02 = this.elements[2];
  2662. var old10 = this.elements[3];
  2663. var old11 = this.elements[4];
  2664. var old12 = this.elements[5];
  2665. this.elements[0] = old11 / d;
  2666. this.elements[3] = -old10 / d;
  2667. this.elements[1] = -old01 / d;
  2668. this.elements[1] = old00 / d;
  2669. this.elements[2] = (old01 * old12 - old11 * old02) / d;
  2670. this.elements[5] = (old10 * old02 - old00 * old12) / d;
  2671. return true;
  2672. }
  2673. return false;
  2674. },
  2675. scale: function(sx, sy) {
  2676. if (sx && !sy) {
  2677. sy = sx;
  2678. }
  2679. if (sx && sy) {
  2680. this.elements[0] *= sx;
  2681. this.elements[1] *= sy;
  2682. this.elements[3] *= sx;
  2683. this.elements[4] *= sy;
  2684. }
  2685. },
  2686. apply: function() {
  2687. var source;
  2688. if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
  2689. source = arguments[0].array();
  2690. } else if (arguments.length === 6) {
  2691. source = Array.prototype.slice.call(arguments);
  2692. } else if (arguments.length === 1 && arguments[0] instanceof Array) {
  2693. source = arguments[0];
  2694. }
  2695. var result = [0, 0, this.elements[2],
  2696. 0, 0, this.elements[5]];
  2697. var e = 0;
  2698. for (var row = 0; row < 2; row++) {
  2699. for (var col = 0; col < 3; col++, e++) {
  2700. result[e] += this.elements[row * 3 + 0] * source[col + 0] +
  2701. this.elements[row * 3 + 1] * source[col + 3];
  2702. }
  2703. }
  2704. this.elements = result.slice();
  2705. },
  2706. preApply: function() {
  2707. var source;
  2708. if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
  2709. source = arguments[0].array();
  2710. } else if (arguments.length === 6) {
  2711. source = Array.prototype.slice.call(arguments);
  2712. } else if (arguments.length === 1 && arguments[0] instanceof Array) {
  2713. source = arguments[0];
  2714. }
  2715. var result = [0, 0, source[2],
  2716. 0, 0, source[5]];
  2717. result[2] = source[2] + this.elements[2] * source[0] + this.elements[5] * source[1];
  2718. result[5] = source[5] + this.elements[2] * source[3] + this.elements[5] * source[4];
  2719. result[0] = this.elements[0] * source[0] + this.elements[3] * source[1];
  2720. result[3] = this.elements[0] * source[3] + this.elements[3] * source[4];
  2721. result[1] = this.elements[1] * source[0] + this.elements[4] * source[1];
  2722. result[4] = this.elements[1] * source[3] + this.elements[4] * source[4];
  2723. this.elements = result.slice();
  2724. },
  2725. rotate: function(angle) {
  2726. var c = Math.cos(angle);
  2727. var s = Math.sin(angle);
  2728. var temp1 = this.elements[0];
  2729. var temp2 = this.elements[1];
  2730. this.elements[0] = c * temp1 + s * temp2;
  2731. this.elements[1] = -s * temp1 + c * temp2;
  2732. temp1 = this.elements[3];
  2733. temp2 = this.elements[4];
  2734. this.elements[3] = c * temp1 + s * temp2;
  2735. this.elements[4] = -s * temp1 + c * temp2;
  2736. },
  2737. rotateZ: function(angle) {
  2738. this.rotate(angle);
  2739. },
  2740. print: function() {
  2741. var digits = printMatrixHelper(this.elements);
  2742. var output = "" + p.nfs(this.elements[0], digits, 4) + " " +
  2743. p.nfs(this.elements[1], digits, 4) + " " +
  2744. p.nfs(this.elements[2], digits, 4) + "\n" +
  2745. p.nfs(this.elements[3], digits, 4) + " " +
  2746. p.nfs(this.elements[4], digits, 4) + " " +
  2747. p.nfs(this.elements[5], digits, 4) + "\n\n";
  2748. p.println(output);
  2749. }
  2750. };
  2751. ////////////////////////////////////////////////////////////////////////////
  2752. // PMatrix3D
  2753. ////////////////////////////////////////////////////////////////////////////
  2754. var PMatrix3D = p.PMatrix3D = function PMatrix3D() {
  2755. // When a matrix is created, it is set to an identity matrix
  2756. this.reset();
  2757. };
  2758. PMatrix3D.prototype = {
  2759. set: function() {
  2760. if (arguments.length === 16) {
  2761. this.elements = Array.prototype.slice.call(arguments);
  2762. } else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
  2763. this.elements = arguments[0].array();
  2764. } else if (arguments.length === 1 && arguments[0] instanceof Array) {
  2765. this.elements = arguments[0].slice();
  2766. }
  2767. },
  2768. get: function() {
  2769. var outgoing = new PMatrix3D();
  2770. outgoing.set(this.elements);
  2771. return outgoing;
  2772. },
  2773. reset: function() {
  2774. this.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
  2775. },
  2776. // Returns a copy of the element values.
  2777. array: function array() {
  2778. return this.elements.slice();
  2779. },
  2780. translate: function(tx, ty, tz) {
  2781. if (tz === undef) {
  2782. tz = 0;
  2783. }
  2784. this.elements[3] += tx * this.elements[0] + ty * this.elements[1] + tz * this.elements[2];
  2785. this.elements[7] += tx * this.elements[4] + ty * this.elements[5] + tz * this.elements[6];
  2786. this.elements[11] += tx * this.elements[8] + ty * this.elements[9] + tz * this.elements[10];
  2787. this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14];
  2788. },
  2789. transpose: function() {
  2790. var temp = this.elements.slice();
  2791. this.elements[0] = temp[0];
  2792. this.elements[1] = temp[4];
  2793. this.elements[2] = temp[8];
  2794. this.elements[3] = temp[12];
  2795. this.elements[4] = temp[1];
  2796. this.elements[5] = temp[5];
  2797. this.elements[6] = temp[9];
  2798. this.elements[7] = temp[13];
  2799. this.elements[8] = temp[2];
  2800. this.elements[9] = temp[6];
  2801. this.elements[10] = temp[10];
  2802. this.elements[11] = temp[14];
  2803. this.elements[12] = temp[3];
  2804. this.elements[13] = temp[7];
  2805. this.elements[14] = temp[11];
  2806. this.elements[15] = temp[15];
  2807. },
  2808. /*
  2809. You must either pass in two PVectors or two arrays,
  2810. don't mix between types. You may also omit a second
  2811. argument and simply read the result from the return.
  2812. */
  2813. mult: function(source, target) {
  2814. var x, y, z, w;
  2815. if (source instanceof PVector) {
  2816. x = source.x;
  2817. y = source.y;
  2818. z = source.z;
  2819. w = 1;
  2820. if (!target) {
  2821. target = new PVector();
  2822. }
  2823. } else if (source instanceof Array) {
  2824. x = source[0];
  2825. y = source[1];
  2826. z = source[2];
  2827. w = source[3] || 1;
  2828. if (!target || target.length !== 3 && target.length !== 4) {
  2829. target = [0, 0, 0];
  2830. }
  2831. }
  2832. if (target instanceof Array) {
  2833. if (target.length === 3) {
  2834. target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
  2835. target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
  2836. target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
  2837. } else if (target.length === 4) {
  2838. target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
  2839. target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
  2840. target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
  2841. target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
  2842. }
  2843. }
  2844. if (target instanceof PVector) {
  2845. target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
  2846. target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
  2847. target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
  2848. }
  2849. return target;
  2850. },
  2851. preApply: function() {
  2852. var source;
  2853. if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
  2854. source = arguments[0].array();
  2855. } else if (arguments.length === 16) {
  2856. source = Array.prototype.slice.call(arguments);
  2857. } else if (arguments.length === 1 && arguments[0] instanceof Array) {
  2858. source = arguments[0];
  2859. }
  2860. var result = [0, 0, 0, 0,
  2861. 0, 0, 0, 0,
  2862. 0, 0, 0, 0,
  2863. 0, 0, 0, 0];
  2864. var e = 0;
  2865. for (var row = 0; row < 4; row++) {
  2866. for (var col = 0; col < 4; col++, e++) {
  2867. result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] *
  2868. source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] +
  2869. this.elements[col + 12] * source[row * 4 + 3];
  2870. }
  2871. }
  2872. this.elements = result.slice();
  2873. },
  2874. apply: function() {
  2875. var source;
  2876. if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
  2877. source = arguments[0].array();
  2878. } else if (arguments.length === 16) {
  2879. source = Array.prototype.slice.call(arguments);
  2880. } else if (arguments.length === 1 && arguments[0] instanceof Array) {
  2881. source = arguments[0];
  2882. }
  2883. var result = [0, 0, 0, 0,
  2884. 0, 0, 0, 0,
  2885. 0, 0, 0, 0,
  2886. 0, 0, 0, 0];
  2887. var e = 0;
  2888. for (var row = 0; row < 4; row++) {
  2889. for (var col = 0; col < 4; col++, e++) {
  2890. result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] *
  2891. source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] +
  2892. this.elements[row * 4 + 3] * source[col + 12];
  2893. }
  2894. }
  2895. this.elements = result.slice();
  2896. },
  2897. rotate: function(angle, v0, v1, v2) {
  2898. if (!v1) {
  2899. this.rotateZ(angle);
  2900. } else {
  2901. // TODO should make sure this vector is normalized
  2902. var c = p.cos(angle);
  2903. var s = p.sin(angle);
  2904. var t = 1.0 - c;
  2905. this.apply((t * v0 * v0) + c,
  2906. (t * v0 * v1) - (s * v2),
  2907. (t * v0 * v2) + (s * v1),
  2908. 0,
  2909. (t * v0 * v1) + (s * v2),
  2910. (t * v1 * v1) + c,
  2911. (t * v1 * v2) - (s * v0),
  2912. 0,
  2913. (t * v0 * v2) - (s * v1),
  2914. (t * v1 * v2) + (s * v0),
  2915. (t * v2 * v2) + c,
  2916. 0, 0, 0, 0, 1);
  2917. }
  2918. },
  2919. invApply: function() {
  2920. if (inverseCopy === undef) {
  2921. inverseCopy = new PMatrix3D();
  2922. }
  2923. var a = arguments;
  2924. inverseCopy.set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8],
  2925. a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
  2926. if (!inverseCopy.invert()) {
  2927. return false;
  2928. }
  2929. this.preApply(inverseCopy);
  2930. return true;
  2931. },
  2932. rotateX: function(angle) {
  2933. var c = p.cos(angle);
  2934. var s = p.sin(angle);
  2935. this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
  2936. },
  2937. rotateY: function(angle) {
  2938. var c = p.cos(angle);
  2939. var s = p.sin(angle);
  2940. this.apply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
  2941. },
  2942. rotateZ: function(angle) {
  2943. var c = Math.cos(angle);
  2944. var s = Math.sin(angle);
  2945. this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
  2946. },
  2947. // Uniform scaling if only one value passed in
  2948. scale: function(sx, sy, sz) {
  2949. if (sx && !sy && !sz) {
  2950. sy = sz = sx;
  2951. } else if (sx && sy && !sz) {
  2952. sz = 1;
  2953. }
  2954. if (sx && sy && sz) {
  2955. this.elements[0] *= sx;
  2956. this.elements[1] *= sy;
  2957. this.elements[2] *= sz;
  2958. this.elements[4] *= sx;
  2959. this.elements[5] *= sy;
  2960. this.elements[6] *= sz;
  2961. this.elements[8] *= sx;
  2962. this.elements[9] *= sy;
  2963. this.elements[10] *= sz;
  2964. this.elements[12] *= sx;
  2965. this.elements[13] *= sy;
  2966. this.elements[14] *= sz;
  2967. }
  2968. },
  2969. skewX: function(angle) {
  2970. var t = Math.tan(angle);
  2971. this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
  2972. },
  2973. skewY: function(angle) {
  2974. var t = Math.tan(angle);
  2975. this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
  2976. },
  2977. multX: function(x, y, z, w) {
  2978. if (!z) {
  2979. return this.elements[0] * x + this.elements[1] * y + this.elements[3];
  2980. } else if (!w) {
  2981. return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
  2982. } else {
  2983. return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
  2984. }
  2985. },
  2986. multY: function(x, y, z, w) {
  2987. if (!z) {
  2988. return this.elements[4] * x + this.elements[5] * y + this.elements[7];
  2989. } else if (!w) {
  2990. return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
  2991. } else {
  2992. return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
  2993. }
  2994. },
  2995. multZ: function(x, y, z, w) {
  2996. if (!w) {
  2997. return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
  2998. } else {
  2999. return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
  3000. }
  3001. },
  3002. multW: function(x, y, z, w) {
  3003. if (!w) {
  3004. return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15];
  3005. } else {
  3006. return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
  3007. }
  3008. },
  3009. invert: function() {
  3010. var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4];
  3011. var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4];
  3012. var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4];
  3013. var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5];
  3014. var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5];
  3015. var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6];
  3016. var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12];
  3017. var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12];
  3018. var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12];
  3019. var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13];
  3020. var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13];
  3021. var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14];
  3022. // Determinant
  3023. var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
  3024. // Account for a very small value
  3025. // return false if not successful.
  3026. if (Math.abs(fDet) <= 1e-9) {
  3027. return false;
  3028. }
  3029. var kInv = [];
  3030. kInv[0] = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3;
  3031. kInv[4] = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1;
  3032. kInv[8] = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0;
  3033. kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0;
  3034. kInv[1] = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3;
  3035. kInv[5] = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1;
  3036. kInv[9] = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0;
  3037. kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0;
  3038. kInv[2] = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3;
  3039. kInv[6] = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1;
  3040. kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0;
  3041. kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0;
  3042. kInv[3] = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3;
  3043. kInv[7] = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1;
  3044. kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0;
  3045. kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0;
  3046. // Inverse using Determinant
  3047. var fInvDet = 1.0 / fDet;
  3048. kInv[0] *= fInvDet;
  3049. kInv[1] *= fInvDet;
  3050. kInv[2] *= fInvDet;
  3051. kInv[3] *= fInvDet;
  3052. kInv[4] *= fInvDet;
  3053. kInv[5] *= fInvDet;
  3054. kInv[6] *= fInvDet;
  3055. kInv[7] *= fInvDet;
  3056. kInv[8] *= fInvDet;
  3057. kInv[9] *= fInvDet;
  3058. kInv[10] *= fInvDet;
  3059. kInv[11] *= fInvDet;
  3060. kInv[12] *= fInvDet;
  3061. kInv[13] *= fInvDet;
  3062. kInv[14] *= fInvDet;
  3063. kInv[15] *= fInvDet;
  3064. this.elements = kInv.slice();
  3065. return true;
  3066. },
  3067. toString: function() {
  3068. var str = "";
  3069. for (var i = 0; i < 15; i++) {
  3070. str += this.elements[i] + ", ";
  3071. }
  3072. str += this.elements[15];
  3073. return str;
  3074. },
  3075. print: function() {
  3076. var digits = printMatrixHelper(this.elements);
  3077. var output = "" + p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) +
  3078. " " + p.nfs(this.elements[2], digits, 4) + " " + p.nfs(this.elements[3], digits, 4) +
  3079. "\n" + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) +
  3080. " " + p.nfs(this.elements[6], digits, 4) + " " + p.nfs(this.elements[7], digits, 4) +
  3081. "\n" + p.nfs(this.elements[8], digits, 4) + " " + p.nfs(this.elements[9], digits, 4) +
  3082. " " + p.nfs(this.elements[10], digits, 4) + " " + p.nfs(this.elements[11], digits, 4) +
  3083. "\n" + p.nfs(this.elements[12], digits, 4) + " " + p.nfs(this.elements[13], digits, 4) +
  3084. " " + p.nfs(this.elements[14], digits, 4) + " " + p.nfs(this.elements[15], digits, 4) + "\n\n";
  3085. p.println(output);
  3086. },
  3087. invTranslate: function(tx, ty, tz) {
  3088. this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1);
  3089. },
  3090. invRotateX: function(angle) {
  3091. var c = Math.cos(-angle);
  3092. var s = Math.sin(-angle);
  3093. this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
  3094. },
  3095. invRotateY: function(angle) {
  3096. var c = Math.cos(-angle);
  3097. var s = Math.sin(-angle);
  3098. this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
  3099. },
  3100. invRotateZ: function(angle) {
  3101. var c = Math.cos(-angle);
  3102. var s = Math.sin(-angle);
  3103. this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
  3104. },
  3105. invScale: function(x, y, z) {
  3106. this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]);
  3107. }
  3108. };
  3109. ////////////////////////////////////////////////////////////////////////////
  3110. // Matrix Stack
  3111. ////////////////////////////////////////////////////////////////////////////
  3112. var PMatrixStack = p.PMatrixStack = function PMatrixStack() {
  3113. this.matrixStack = [];
  3114. };
  3115. PMatrixStack.prototype.load = function load() {
  3116. var tmpMatrix;
  3117. if (p.use3DContext) {
  3118. tmpMatrix = new PMatrix3D();
  3119. } else {
  3120. tmpMatrix = new PMatrix2D();
  3121. }
  3122. if (arguments.length === 1) {
  3123. tmpMatrix.set(arguments[0]);
  3124. } else {
  3125. tmpMatrix.set(arguments);
  3126. }
  3127. this.matrixStack.push(tmpMatrix);
  3128. };
  3129. PMatrixStack.prototype.push = function push() {
  3130. this.matrixStack.push(this.peek());
  3131. };
  3132. PMatrixStack.prototype.pop = function pop() {
  3133. return this.matrixStack.pop();
  3134. };
  3135. PMatrixStack.prototype.peek = function peek() {
  3136. var tmpMatrix;
  3137. if (p.use3DContext) {
  3138. tmpMatrix = new PMatrix3D();
  3139. } else {
  3140. tmpMatrix = new PMatrix2D();
  3141. }
  3142. tmpMatrix.set(this.matrixStack[this.matrixStack.length - 1]);
  3143. return tmpMatrix;
  3144. };
  3145. PMatrixStack.prototype.mult = function mult(matrix) {
  3146. this.matrixStack[this.matrixStack.length - 1].apply(matrix);
  3147. };
  3148. ////////////////////////////////////////////////////////////////////////////
  3149. // Array handling
  3150. ////////////////////////////////////////////////////////////////////////////
  3151. p.split = function(str, delim) {
  3152. return str.split(delim);
  3153. };
  3154. p.splitTokens = function(str, tokens) {
  3155. if (arguments.length === 1) {
  3156. tokens = "\n\t\r\f ";
  3157. }
  3158. tokens = "[" + tokens + "]";
  3159. var ary = [];
  3160. var index = 0;
  3161. var pos = str.search(tokens);
  3162. while (pos >= 0) {
  3163. if (pos === 0) {
  3164. str = str.substring(1);
  3165. } else {
  3166. ary[index] = str.substring(0, pos);
  3167. index++;
  3168. str = str.substring(pos);
  3169. }
  3170. pos = str.search(tokens);
  3171. }
  3172. if (str.length > 0) {
  3173. ary[index] = str;
  3174. }
  3175. if (ary.length === 0) {
  3176. ary = undef;
  3177. }
  3178. return ary;
  3179. };
  3180. p.append = function(array, element) {
  3181. array[array.length] = element;
  3182. return array;
  3183. };
  3184. p.concat = function(array1, array2) {
  3185. return array1.concat(array2);
  3186. };
  3187. p.sort = function(array, numElem) {
  3188. var ret = [];
  3189. // depending on the type used (int, float) or string
  3190. // we'll need to use a different compare function
  3191. if (array.length > 0) {
  3192. // copy since we need to return another array
  3193. var elemsToCopy = numElem > 0 ? numElem : array.length;
  3194. for (var i = 0; i < elemsToCopy; i++) {
  3195. ret.push(array[i]);
  3196. }
  3197. if (typeof array[0] === "string") {
  3198. ret.sort();
  3199. }
  3200. // int or float
  3201. else {
  3202. ret.sort(function(a, b) {
  3203. return a - b;
  3204. });
  3205. }
  3206. // copy on the rest of the elements that were not sorted in case the user
  3207. // only wanted a subset of an array to be sorted.
  3208. if (numElem > 0) {
  3209. for (var j = ret.length; j < array.length; j++) {
  3210. ret.push(array[j]);
  3211. }
  3212. }
  3213. }
  3214. return ret;
  3215. };
  3216. /**
  3217. splice inserts "value" which can be either a scalar or an array
  3218. into "array" at position "index".
  3219. */
  3220. p.splice = function(array, value, index) {
  3221. // Trying to splice an empty array into "array" in P5 won't do
  3222. // anything, just return the original.
  3223. if(value.length === 0)
  3224. {
  3225. return array;
  3226. }
  3227. // If the second argument was an array, we'll need to iterate over all
  3228. // the "value" elements and add one by one because
  3229. // array.splice(index, 0, value);
  3230. // would create a multi-dimensional array which isn't what we want.
  3231. if(value instanceof Array) {
  3232. for(var i = 0, j = index; i < value.length; j++,i++) {
  3233. array.splice(j, 0, value[i]);
  3234. }
  3235. } else {
  3236. array.splice(index, 0, value);
  3237. }
  3238. return array;
  3239. };
  3240. p.subset = function(array, offset, length) {
  3241. if (arguments.length === 2) {
  3242. return array.slice(offset, array.length - offset);
  3243. } else if (arguments.length === 3) {
  3244. return array.slice(offset, offset + length);
  3245. }
  3246. };
  3247. p.join = function(array, seperator) {
  3248. return array.join(seperator);
  3249. };
  3250. p.shorten = function(ary) {
  3251. var newary = [];
  3252. // copy array into new array
  3253. var len = ary.length;
  3254. for (var i = 0; i < len; i++) {
  3255. newary[i] = ary[i];
  3256. }
  3257. newary.pop();
  3258. return newary;
  3259. };
  3260. p.expand = function(ary, newSize) {
  3261. var temp = ary.slice(0);
  3262. if (arguments.length === 1) {
  3263. // double size of array
  3264. temp.length = ary.length * 2;
  3265. return temp;
  3266. } else if (arguments.length === 2) {
  3267. // size is newSize
  3268. temp.length = newSize;
  3269. return temp;
  3270. }
  3271. };
  3272. p.arrayCopy = function() { // src, srcPos, dest, destPos, length) {
  3273. var src, srcPos = 0, dest, destPos = 0, length;
  3274. if (arguments.length === 2) {
  3275. // recall itself and copy src to dest from start index 0 to 0 of src.length
  3276. src = arguments[0];
  3277. dest = arguments[1];
  3278. length = src.length;
  3279. } else if (arguments.length === 3) {
  3280. // recall itself and copy src to dest from start index 0 to 0 of length
  3281. src = arguments[0];
  3282. dest = arguments[1];
  3283. length = arguments[2];
  3284. } else if (arguments.length === 5) {
  3285. src = arguments[0];
  3286. srcPos = arguments[1];
  3287. dest = arguments[2];
  3288. destPos = arguments[3];
  3289. length = arguments[4];
  3290. }
  3291. // copy src to dest from index srcPos to index destPos of length recursivly on objects
  3292. for (var i = srcPos, j = destPos; i < length + srcPos; i++, j++) {
  3293. if (dest[j] !== undef) {
  3294. dest[j] = src[i];
  3295. } else {
  3296. throw "array index out of bounds exception";
  3297. }
  3298. }
  3299. };
  3300. p.ArrayList = function() {
  3301. var createArrayList = function(args){
  3302. var array = [];
  3303. for (var i = 0; i < args[0]; i++){
  3304. array[i] = (args.length > 1 ? createArrayList(args.slice(1)) : 0 );
  3305. }
  3306. array.get = function(i) {
  3307. return this[i];
  3308. };
  3309. array.contains = function(item) {
  3310. return this.indexOf(item) !== -1;
  3311. };
  3312. array.add = function() {
  3313. if(arguments.length === 1) {
  3314. this.push(arguments[0]); // for add(Object)
  3315. } else if(arguments.length === 2) {
  3316. if (typeof arguments[0] === 'number') {
  3317. if (arguments[0] >= 0 && arguments[0] <= this.length) {
  3318. this.splice(arguments[0], 0, arguments[1]); // for add(i, Object)
  3319. } else {
  3320. throw(arguments[0] + " is not a valid index");
  3321. }
  3322. } else {
  3323. throw(typeof arguments[0] + " is not a number");
  3324. }
  3325. } else {
  3326. throw("Please use the proper number of parameters.");
  3327. }
  3328. };
  3329. array.set = function() {
  3330. if(arguments.length === 2) {
  3331. if (typeof arguments[0] === 'number') {
  3332. if (arguments[0] >= 0 && arguments[0] < this.length) {
  3333. this.splice(arguments[0], 1, arguments[1]);
  3334. } else {
  3335. throw(arguments[0] + " is not a valid index.");
  3336. }
  3337. } else {
  3338. throw(typeof arguments[0] + " is not a number");
  3339. }
  3340. } else {
  3341. throw("Please use the proper number of parameters.");
  3342. }
  3343. };
  3344. array.size = function() {
  3345. return this.length;
  3346. };
  3347. array.clear = function() {
  3348. this.length = 0;
  3349. };
  3350. array.remove = function(i) {
  3351. return this.splice(i, 1)[0];
  3352. };
  3353. array.isEmpty = function() {
  3354. return !!this.length;
  3355. };
  3356. array.clone = function() {
  3357. return this.slice(0);
  3358. };
  3359. array.toArray = function() {
  3360. return this.slice(0);
  3361. };
  3362. return array;
  3363. };
  3364. return createArrayList(Array.prototype.slice.call(arguments));
  3365. };
  3366. p.reverse = function(array) {
  3367. return array.reverse();
  3368. };
  3369. ////////////////////////////////////////////////////////////////////////////
  3370. // HashMap
  3371. ////////////////////////////////////////////////////////////////////////////
  3372. var virtHashCode = function virtHashCode(obj) {
  3373. if (obj.constructor === String) {
  3374. var hash = 0;
  3375. for (var i = 0; i < obj.length; ++i) {
  3376. hash = (hash * 31 + obj.charCodeAt(i)) & 0xFFFFFFFF;
  3377. }
  3378. return hash;
  3379. } else if (typeof(obj) !== "object") {
  3380. return obj & 0xFFFFFFFF;
  3381. } else if ("hashCode" in obj) {
  3382. return obj.hashCode.call(obj);
  3383. } else {
  3384. if (obj.$id === undef) {
  3385. obj.$id = ((Math.floor(Math.random() * 0x10000) - 0x8000) << 16) | Math.floor(Math.random() * 0x10000);
  3386. }
  3387. return obj.$id;
  3388. }
  3389. };
  3390. var virtEquals = function virtEquals(obj, other) {
  3391. if (obj === null || other === null) {
  3392. return (obj === null) && (other === null);
  3393. } else if (obj.constructor === String) {
  3394. return obj === other;
  3395. } else if (typeof(obj) !== "object") {
  3396. return obj === other;
  3397. } else if ("equals" in obj) {
  3398. return obj.equals.call(obj, other);
  3399. } else {
  3400. return obj === other;
  3401. }
  3402. };
  3403. p.HashMap = function HashMap() {
  3404. if (arguments.length === 1 && arguments[0].constructor === HashMap) {
  3405. return arguments[0].clone();
  3406. }
  3407. var initialCapacity = arguments.length > 0 ? arguments[0] : 16;
  3408. var loadFactor = arguments.length > 1 ? arguments[1] : 0.75;
  3409. var buckets = new Array(initialCapacity);
  3410. var count = 0;
  3411. var hashMap = this;
  3412. function ensureLoad() {
  3413. if (count <= loadFactor * buckets.length) {
  3414. return;
  3415. }
  3416. var allEntries = [];
  3417. for (var i = 0; i < buckets.length; ++i) {
  3418. if (buckets[i] !== undef) {
  3419. allEntries = allEntries.concat(buckets[i]);
  3420. }
  3421. }
  3422. buckets = new Array(buckets.length * 2);
  3423. for (var j = 0; j < allEntries.length; ++j) {
  3424. var index = virtHashCode(allEntries[j].key) % buckets.length;
  3425. var bucket = buckets[index];
  3426. if (bucket === undef) {
  3427. buckets[index] = bucket = [];
  3428. }
  3429. bucket.push(allEntries[j]);
  3430. }
  3431. }
  3432. function Iterator(conversion, removeItem) {
  3433. var bucketIndex = 0;
  3434. var itemIndex = -1;
  3435. var endOfBuckets = false;
  3436. function findNext() {
  3437. while (!endOfBuckets) {
  3438. ++itemIndex;
  3439. if (bucketIndex >= buckets.length) {
  3440. endOfBuckets = true;
  3441. } else if (buckets[bucketIndex] === undef || itemIndex >= buckets[bucketIndex].length) {
  3442. itemIndex = -1;
  3443. ++bucketIndex;
  3444. } else {
  3445. return;
  3446. }
  3447. }
  3448. }
  3449. this.hasNext = function() {
  3450. return !endOfBuckets;
  3451. };
  3452. this.next = function() {
  3453. var result = conversion(buckets[bucketIndex][itemIndex]);
  3454. findNext();
  3455. return result;
  3456. };
  3457. this.remove = function() {
  3458. removeItem(this.next());
  3459. --itemIndex;
  3460. };
  3461. findNext();
  3462. }
  3463. function Set(conversion, isIn, removeItem) {
  3464. this.clear = function() {
  3465. hashMap.clear();
  3466. };
  3467. this.contains = function(o) {
  3468. return isIn(o);
  3469. };
  3470. this.containsAll = function(o) {
  3471. var it = o.iterator();
  3472. while (it.hasNext()) {
  3473. if (!this.contains(it.next())) {
  3474. return false;
  3475. }
  3476. }
  3477. return true;
  3478. };
  3479. this.isEmpty = function() {
  3480. return hashMap.isEmpty();
  3481. };
  3482. this.iterator = function() {
  3483. return new Iterator(conversion, removeItem);
  3484. };
  3485. this.remove = function(o) {
  3486. if (this.contains(o)) {
  3487. removeItem(o);
  3488. return true;
  3489. }
  3490. return false;
  3491. };
  3492. this.removeAll = function(c) {
  3493. var it = c.iterator();
  3494. var changed = false;
  3495. while (it.hasNext()) {
  3496. var item = it.next();
  3497. if (this.contains(item)) {
  3498. removeItem(item);
  3499. changed = true;
  3500. }
  3501. }
  3502. return true;
  3503. };
  3504. this.retainAll = function(c) {
  3505. var it = this.iterator();
  3506. var toRemove = [];
  3507. while (it.hasNext()) {
  3508. var entry = it.next();
  3509. if (!c.contains(entry)) {
  3510. toRemove.push(entry);
  3511. }
  3512. }
  3513. for (var i = 0; i < toRemove.length; ++i) {
  3514. removeItem(toRemove[i]);
  3515. }
  3516. return toRemove.length > 0;
  3517. };
  3518. this.size = function() {
  3519. return hashMap.size();
  3520. };
  3521. this.toArray = function() {
  3522. var result = new p.ArrayList(0);
  3523. var it = this.iterator();
  3524. while (it.hasNext()) {
  3525. result.push(it.next());
  3526. }
  3527. return result;
  3528. };
  3529. }
  3530. function Entry(pair) {
  3531. this._isIn = function(map) {
  3532. return map === hashMap && (pair.removed === undef);
  3533. };
  3534. this.equals = function(o) {
  3535. return virtEquals(pair.key, o.getKey());
  3536. };
  3537. this.getKey = function() {
  3538. return pair.key;
  3539. };
  3540. this.getValue = function() {
  3541. return pair.value;
  3542. };
  3543. this.hashCode = function(o) {
  3544. return virtHashCode(pair.key);
  3545. };
  3546. this.setValue = function(value) {
  3547. var old = pair.value;
  3548. pair.value = value;
  3549. return old;
  3550. };
  3551. }
  3552. this.clear = function() {
  3553. count = 0;
  3554. buckets = new Array(initialCapacity);
  3555. };
  3556. this.clone = function() {
  3557. var map = new p.HashMap();
  3558. map.putAll(this);
  3559. return map;
  3560. };
  3561. this.containsKey = function(key) {
  3562. var index = virtHashCode(key) % buckets.length;
  3563. var bucket = buckets[index];
  3564. if (bucket === undef) {
  3565. return false;
  3566. }
  3567. for (var i = 0; i < bucket.length; ++i) {
  3568. if (virtEquals(bucket[i].key, key)) {
  3569. return true;
  3570. }
  3571. }
  3572. return false;
  3573. };
  3574. this.containsValue = function(value) {
  3575. for (var i = 0; i < buckets.length; ++i) {
  3576. var bucket = buckets[i];
  3577. if (bucket === undef) {
  3578. continue;
  3579. }
  3580. for (var j = 0; j < bucket.length; ++j) {
  3581. if (virtEquals(bucket[j].value, value)) {
  3582. return true;
  3583. }
  3584. }
  3585. }
  3586. return false;
  3587. };
  3588. this.entrySet = function() {
  3589. return new Set(
  3590. function(pair) {
  3591. return new Entry(pair);
  3592. },
  3593. function(pair) {
  3594. return pair.constructor === Entry && pair._isIn(hashMap);
  3595. },
  3596. function(pair) {
  3597. return hashMap.remove(pair.getKey());
  3598. });
  3599. };
  3600. this.get = function(key) {
  3601. var index = virtHashCode(key) % buckets.length;
  3602. var bucket = buckets[index];
  3603. if (bucket === undef) {
  3604. return null;
  3605. }
  3606. for (var i = 0; i < bucket.length; ++i) {
  3607. if (virtEquals(bucket[i].key, key)) {
  3608. return bucket[i].value;
  3609. }
  3610. }
  3611. return null;
  3612. };
  3613. this.isEmpty = function() {
  3614. return count === 0;
  3615. };
  3616. this.keySet = function() {
  3617. return new Set(
  3618. function(pair) {
  3619. return pair.key;
  3620. },
  3621. function(key) {
  3622. return hashMap.containsKey(key);
  3623. },
  3624. function(key) {
  3625. return hashMap.remove(key);
  3626. });
  3627. };
  3628. this.put = function(key, value) {
  3629. var index = virtHashCode(key) % buckets.length;
  3630. var bucket = buckets[index];
  3631. if (bucket === undef) {
  3632. ++count;
  3633. buckets[index] = [{
  3634. key: key,
  3635. value: value
  3636. }];
  3637. ensureLoad();
  3638. return null;
  3639. }
  3640. for (var i = 0; i < bucket.length; ++i) {
  3641. if (virtEquals(bucket[i].key, key)) {
  3642. var previous = bucket[i].value;
  3643. bucket[i].value = value;
  3644. return previous;
  3645. }
  3646. }
  3647. ++count;
  3648. bucket.push({
  3649. key: key,
  3650. value: value
  3651. });
  3652. ensureLoad();
  3653. return null;
  3654. };
  3655. this.putAll = function(m) {
  3656. var it = m.entrySet().iterator();
  3657. while (it.hasNext()) {
  3658. var entry = it.next();
  3659. this.put(entry.getKey(), entry.getValue());
  3660. }
  3661. };
  3662. this.remove = function(key) {
  3663. var index = virtHashCode(key) % buckets.length;
  3664. var bucket = buckets[index];
  3665. if (bucket === undef) {
  3666. return null;
  3667. }
  3668. for (var i = 0; i < bucket.length; ++i) {
  3669. if (virtEquals(bucket[i].key, key)) {
  3670. --count;
  3671. var previous = bucket[i].value;
  3672. bucket[i].removed = true;
  3673. if (bucket.length > 1) {
  3674. bucket.splice(i, 1);
  3675. } else {
  3676. buckets[index] = undef;
  3677. }
  3678. return previous;
  3679. }
  3680. }
  3681. return null;
  3682. };
  3683. this.size = function() {
  3684. return count;
  3685. };
  3686. this.values = function() {
  3687. var result = new p.ArrayList(0);
  3688. var it = this.entrySet().iterator();
  3689. while (it.hasNext()) {
  3690. var entry = it.next();
  3691. result.push(entry.getValue());
  3692. }
  3693. return result;
  3694. };
  3695. };
  3696. ////////////////////////////////////////////////////////////////////////////
  3697. // Color functions
  3698. ////////////////////////////////////////////////////////////////////////////
  3699. // helper functions for internal blending modes
  3700. p.mix = function(a, b, f) {
  3701. return a + (((b - a) * f) >> 8);
  3702. };
  3703. p.peg = function(n) {
  3704. return (n < 0) ? 0 : ((n > 255) ? 255 : n);
  3705. };
  3706. // blending modes
  3707. p.modes = {
  3708. replace: function(c1, c2) {
  3709. return c2;
  3710. },
  3711. blend: function(c1, c2) {
  3712. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3713. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3714. p.mix(c1 & p.RED_MASK, c2 & p.RED_MASK, f) & p.RED_MASK |
  3715. p.mix(c1 & p.GREEN_MASK, c2 & p.GREEN_MASK, f) & p.GREEN_MASK |
  3716. p.mix(c1 & p.BLUE_MASK, c2 & p.BLUE_MASK, f));
  3717. },
  3718. add: function(c1, c2) {
  3719. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3720. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3721. Math.min(((c1 & p.RED_MASK) + ((c2 & p.RED_MASK) >> 8) * f), p.RED_MASK) & p.RED_MASK |
  3722. Math.min(((c1 & p.GREEN_MASK) + ((c2 & p.GREEN_MASK) >> 8) * f), p.GREEN_MASK) & p.GREEN_MASK |
  3723. Math.min((c1 & p.BLUE_MASK) + (((c2 & p.BLUE_MASK) * f) >> 8), p.BLUE_MASK));
  3724. },
  3725. subtract: function(c1, c2) {
  3726. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3727. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3728. Math.max(((c1 & p.RED_MASK) - ((c2 & p.RED_MASK) >> 8) * f), p.GREEN_MASK) & p.RED_MASK |
  3729. Math.max(((c1 & p.GREEN_MASK) - ((c2 & p.GREEN_MASK) >> 8) * f), p.BLUE_MASK) & p.GREEN_MASK |
  3730. Math.max((c1 & p.BLUE_MASK) - (((c2 & p.BLUE_MASK) * f) >> 8), 0));
  3731. },
  3732. lightest: function(c1, c2) {
  3733. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3734. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3735. Math.max(c1 & p.RED_MASK, ((c2 & p.RED_MASK) >> 8) * f) & p.RED_MASK |
  3736. Math.max(c1 & p.GREEN_MASK, ((c2 & p.GREEN_MASK) >> 8) * f) & p.GREEN_MASK |
  3737. Math.max(c1 & p.BLUE_MASK, ((c2 & p.BLUE_MASK) * f) >> 8));
  3738. },
  3739. darkest: function(c1, c2) {
  3740. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3741. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3742. p.mix(c1 & p.RED_MASK, Math.min(c1 & p.RED_MASK, ((c2 & p.RED_MASK) >> 8) * f), f) & p.RED_MASK |
  3743. p.mix(c1 & p.GREEN_MASK, Math.min(c1 & p.GREEN_MASK, ((c2 & p.GREEN_MASK) >> 8) * f), f) & p.GREEN_MASK |
  3744. p.mix(c1 & p.BLUE_MASK, Math.min(c1 & p.BLUE_MASK, ((c2 & p.BLUE_MASK) * f) >> 8), f));
  3745. },
  3746. difference: function(c1, c2) {
  3747. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3748. var ar = (c1 & p.RED_MASK) >> 16;
  3749. var ag = (c1 & p.GREEN_MASK) >> 8;
  3750. var ab = (c1 & p.BLUE_MASK);
  3751. var br = (c2 & p.RED_MASK) >> 16;
  3752. var bg = (c2 & p.GREEN_MASK) >> 8;
  3753. var bb = (c2 & p.BLUE_MASK);
  3754. // formula:
  3755. var cr = (ar > br) ? (ar - br) : (br - ar);
  3756. var cg = (ag > bg) ? (ag - bg) : (bg - ag);
  3757. var cb = (ab > bb) ? (ab - bb) : (bb - ab);
  3758. // alpha blend (this portion will always be the same)
  3759. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3760. (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) |
  3761. (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) |
  3762. (p.peg(ab + (((cb - ab) * f) >> 8))));
  3763. },
  3764. exclusion: function(c1, c2) {
  3765. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3766. var ar = (c1 & p.RED_MASK) >> 16;
  3767. var ag = (c1 & p.GREEN_MASK) >> 8;
  3768. var ab = (c1 & p.BLUE_MASK);
  3769. var br = (c2 & p.RED_MASK) >> 16;
  3770. var bg = (c2 & p.GREEN_MASK) >> 8;
  3771. var bb = (c2 & p.BLUE_MASK);
  3772. // formula:
  3773. var cr = ar + br - ((ar * br) >> 7);
  3774. var cg = ag + bg - ((ag * bg) >> 7);
  3775. var cb = ab + bb - ((ab * bb) >> 7);
  3776. // alpha blend (this portion will always be the same)
  3777. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3778. (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) |
  3779. (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) |
  3780. (p.peg(ab + (((cb - ab) * f) >> 8))));
  3781. },
  3782. multiply: function(c1, c2) {
  3783. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3784. var ar = (c1 & p.RED_MASK) >> 16;
  3785. var ag = (c1 & p.GREEN_MASK) >> 8;
  3786. var ab = (c1 & p.BLUE_MASK);
  3787. var br = (c2 & p.RED_MASK) >> 16;
  3788. var bg = (c2 & p.GREEN_MASK) >> 8;
  3789. var bb = (c2 & p.BLUE_MASK);
  3790. // formula:
  3791. var cr = (ar * br) >> 8;
  3792. var cg = (ag * bg) >> 8;
  3793. var cb = (ab * bb) >> 8;
  3794. // alpha blend (this portion will always be the same)
  3795. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3796. (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) |
  3797. (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) |
  3798. (p.peg(ab + (((cb - ab) * f) >> 8))));
  3799. },
  3800. screen: function(c1, c2) {
  3801. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3802. var ar = (c1 & p.RED_MASK) >> 16;
  3803. var ag = (c1 & p.GREEN_MASK) >> 8;
  3804. var ab = (c1 & p.BLUE_MASK);
  3805. var br = (c2 & p.RED_MASK) >> 16;
  3806. var bg = (c2 & p.GREEN_MASK) >> 8;
  3807. var bb = (c2 & p.BLUE_MASK);
  3808. // formula:
  3809. var cr = 255 - (((255 - ar) * (255 - br)) >> 8);
  3810. var cg = 255 - (((255 - ag) * (255 - bg)) >> 8);
  3811. var cb = 255 - (((255 - ab) * (255 - bb)) >> 8);
  3812. // alpha blend (this portion will always be the same)
  3813. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3814. (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) |
  3815. (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) |
  3816. (p.peg(ab + (((cb - ab) * f) >> 8))));
  3817. },
  3818. hard_light: function(c1, c2) {
  3819. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3820. var ar = (c1 & p.RED_MASK) >> 16;
  3821. var ag = (c1 & p.GREEN_MASK) >> 8;
  3822. var ab = (c1 & p.BLUE_MASK);
  3823. var br = (c2 & p.RED_MASK) >> 16;
  3824. var bg = (c2 & p.GREEN_MASK) >> 8;
  3825. var bb = (c2 & p.BLUE_MASK);
  3826. // formula:
  3827. var cr = (br < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7));
  3828. var cg = (bg < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7));
  3829. var cb = (bb < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
  3830. // alpha blend (this portion will always be the same)
  3831. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3832. (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) |
  3833. (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) |
  3834. (p.peg(ab + (((cb - ab) * f) >> 8))));
  3835. },
  3836. soft_light: function(c1, c2) {
  3837. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3838. var ar = (c1 & p.RED_MASK) >> 16;
  3839. var ag = (c1 & p.GREEN_MASK) >> 8;
  3840. var ab = (c1 & p.BLUE_MASK);
  3841. var br = (c2 & p.RED_MASK) >> 16;
  3842. var bg = (c2 & p.GREEN_MASK) >> 8;
  3843. var bb = (c2 & p.BLUE_MASK);
  3844. // formula:
  3845. var cr = ((ar * br) >> 7) + ((ar * ar) >> 8) - ((ar * ar * br) >> 15);
  3846. var cg = ((ag * bg) >> 7) + ((ag * ag) >> 8) - ((ag * ag * bg) >> 15);
  3847. var cb = ((ab * bb) >> 7) + ((ab * ab) >> 8) - ((ab * ab * bb) >> 15);
  3848. // alpha blend (this portion will always be the same)
  3849. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3850. (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) |
  3851. (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) |
  3852. (p.peg(ab + (((cb - ab) * f) >> 8))));
  3853. },
  3854. overlay: function(c1, c2) {
  3855. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3856. var ar = (c1 & p.RED_MASK) >> 16;
  3857. var ag = (c1 & p.GREEN_MASK) >> 8;
  3858. var ab = (c1 & p.BLUE_MASK);
  3859. var br = (c2 & p.RED_MASK) >> 16;
  3860. var bg = (c2 & p.GREEN_MASK) >> 8;
  3861. var bb = (c2 & p.BLUE_MASK);
  3862. // formula:
  3863. var cr = (ar < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7));
  3864. var cg = (ag < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7));
  3865. var cb = (ab < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
  3866. // alpha blend (this portion will always be the same)
  3867. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3868. (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) |
  3869. (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) |
  3870. (p.peg(ab + (((cb - ab) * f) >> 8))));
  3871. },
  3872. dodge: function(c1, c2) {
  3873. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3874. var ar = (c1 & p.RED_MASK) >> 16;
  3875. var ag = (c1 & p.GREEN_MASK) >> 8;
  3876. var ab = (c1 & p.BLUE_MASK);
  3877. var br = (c2 & p.RED_MASK) >> 16;
  3878. var bg = (c2 & p.GREEN_MASK) >> 8;
  3879. var bb = (c2 & p.BLUE_MASK);
  3880. // formula:
  3881. var cr = (br === 255) ? 255 : p.peg((ar << 8) / (255 - br)); // division requires pre-peg()-ing
  3882. var cg = (bg === 255) ? 255 : p.peg((ag << 8) / (255 - bg)); // "
  3883. var cb = (bb === 255) ? 255 : p.peg((ab << 8) / (255 - bb)); // "
  3884. // alpha blend (this portion will always be the same)
  3885. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3886. (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) |
  3887. (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) |
  3888. (p.peg(ab + (((cb - ab) * f) >> 8))));
  3889. },
  3890. burn: function(c1, c2) {
  3891. var f = (c2 & p.ALPHA_MASK) >>> 24;
  3892. var ar = (c1 & p.RED_MASK) >> 16;
  3893. var ag = (c1 & p.GREEN_MASK) >> 8;
  3894. var ab = (c1 & p.BLUE_MASK);
  3895. var br = (c2 & p.RED_MASK) >> 16;
  3896. var bg = (c2 & p.GREEN_MASK) >> 8;
  3897. var bb = (c2 & p.BLUE_MASK);
  3898. // formula:
  3899. var cr = (br === 0) ? 0 : 255 - p.peg(((255 - ar) << 8) / br); // division requires pre-peg()-ing
  3900. var cg = (bg === 0) ? 0 : 255 - p.peg(((255 - ag) << 8) / bg); // "
  3901. var cb = (bb === 0) ? 0 : 255 - p.peg(((255 - ab) << 8) / bb); // "
  3902. // alpha blend (this portion will always be the same)
  3903. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  3904. (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) |
  3905. (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) |
  3906. (p.peg(ab + (((cb - ab) * f) >> 8))));
  3907. }
  3908. };
  3909. function color$4(aValue1, aValue2, aValue3, aValue4) {
  3910. var r, g, b, a;
  3911. if (curColorMode === p.HSB) {
  3912. var rgb = p.color.toRGB(aValue1, aValue2, aValue3);
  3913. r = rgb[0];
  3914. g = rgb[1];
  3915. b = rgb[2];
  3916. } else {
  3917. r = Math.round(255 * (aValue1 / colorModeX));
  3918. g = Math.round(255 * (aValue2 / colorModeY));
  3919. b = Math.round(255 * (aValue3 / colorModeZ));
  3920. }
  3921. a = Math.round(255 * (aValue4 / colorModeA));
  3922. // Limit values greater than 255
  3923. r = (r > 255) ? 255 : r;
  3924. g = (g > 255) ? 255 : g;
  3925. b = (b > 255) ? 255 : b;
  3926. a = (a > 255) ? 255 : a;
  3927. // Create color int
  3928. return (a << 24) & p.ALPHA_MASK | (r << 16) & p.RED_MASK | (g << 8) & p.GREEN_MASK | b & p.BLUE_MASK;
  3929. }
  3930. function color$2(aValue1, aValue2) {
  3931. var a;
  3932. // Color int and alpha
  3933. if (aValue1 & p.ALPHA_MASK) {
  3934. a = Math.round(255 * (aValue2 / colorModeA));
  3935. a = (a > 255) ? 255 : a;
  3936. return aValue1 - (aValue1 & p.ALPHA_MASK) + ((a << 24) & p.ALPHA_MASK);
  3937. }
  3938. // Grayscale and alpha
  3939. else {
  3940. if (curColorMode === p.RGB) {
  3941. return color$4(aValue1, aValue1, aValue1, aValue2);
  3942. } else if (curColorMode === p.HSB) {
  3943. return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, aValue2);
  3944. }
  3945. }
  3946. }
  3947. function color$1(aValue1) {
  3948. // Grayscale
  3949. if (aValue1 <= colorModeX && aValue1 >= 0) {
  3950. if (curColorMode === p.RGB) {
  3951. return color$4(aValue1, aValue1, aValue1, colorModeA);
  3952. } else if (curColorMode === p.HSB) {
  3953. return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, colorModeA);
  3954. }
  3955. }
  3956. // Color int
  3957. else if (aValue1) {
  3958. return aValue1;
  3959. }
  3960. }
  3961. p.color = function color(aValue1, aValue2, aValue3, aValue4) {
  3962. // 4 arguments: (R, G, B, A) or (H, S, B, A)
  3963. if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef && aValue4 !== undef) {
  3964. return color$4(aValue1, aValue2, aValue3, aValue4);
  3965. }
  3966. // 3 arguments: (R, G, B) or (H, S, B)
  3967. else if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef) {
  3968. return color$4(aValue1, aValue2, aValue3, colorModeA);
  3969. }
  3970. // 2 arguments: (Color, A) or (Grayscale, A)
  3971. else if (aValue1 !== undef && aValue2 !== undef) {
  3972. return color$2(aValue1, aValue2);
  3973. }
  3974. // 1 argument: (Grayscale) or (Color)
  3975. else if (typeof aValue1 === "number") {
  3976. return color$1(aValue1);
  3977. }
  3978. // Default
  3979. else {
  3980. return color$4(colorModeX, colorModeY, colorModeZ, colorModeA);
  3981. }
  3982. };
  3983. // Ease of use function to extract the colour bits into a string
  3984. p.color.toString = function(colorInt) {
  3985. return "rgba(" + ((colorInt & p.RED_MASK) >>> 16) + "," + ((colorInt & p.GREEN_MASK) >>> 8) +
  3986. "," + ((colorInt & p.BLUE_MASK)) + "," + ((colorInt & p.ALPHA_MASK) >>> 24) / 255 + ")";
  3987. };
  3988. // Easy of use function to pack rgba values into a single bit-shifted color int.
  3989. p.color.toInt = function(r, g, b, a) {
  3990. return (a << 24) & p.ALPHA_MASK | (r << 16) & p.RED_MASK | (g << 8) & p.GREEN_MASK | b & p.BLUE_MASK;
  3991. };
  3992. // Creates a simple array in [R, G, B, A] format, [255, 255, 255, 255]
  3993. p.color.toArray = function(colorInt) {
  3994. return [(colorInt & p.RED_MASK) >>> 16, (colorInt & p.GREEN_MASK) >>> 8,
  3995. colorInt & p.BLUE_MASK, (colorInt & p.ALPHA_MASK) >>> 24];
  3996. };
  3997. // Creates a WebGL color array in [R, G, B, A] format. WebGL wants the color ranges between 0 and 1, [1, 1, 1, 1]
  3998. p.color.toGLArray = function(colorInt) {
  3999. return [((colorInt & p.RED_MASK) >>> 16) / 255, ((colorInt & p.GREEN_MASK) >>> 8) / 255,
  4000. (colorInt & p.BLUE_MASK) / 255, ((colorInt & p.ALPHA_MASK) >>> 24) / 255];
  4001. };
  4002. // HSB conversion function from Mootools, MIT Licensed
  4003. p.color.toRGB = function(h, s, b) {
  4004. // Limit values greater than range
  4005. h = (h > colorModeX) ? colorModeX : h;
  4006. s = (s > colorModeY) ? colorModeY : s;
  4007. b = (b > colorModeZ) ? colorModeZ : b;
  4008. h = (h / colorModeX) * 360;
  4009. s = (s / colorModeY) * 100;
  4010. b = (b / colorModeZ) * 100;
  4011. var br = Math.round(b / 100 * 255);
  4012. if (s === 0) { // Grayscale
  4013. return [br, br, br];
  4014. } else {
  4015. var hue = h % 360;
  4016. var f = hue % 60;
  4017. var p = Math.round((b * (100 - s)) / 10000 * 255);
  4018. var q = Math.round((b * (6000 - s * f)) / 600000 * 255);
  4019. var t = Math.round((b * (6000 - s * (60 - f))) / 600000 * 255);
  4020. switch (Math.floor(hue / 60)) {
  4021. case 0:
  4022. return [br, t, p];
  4023. case 1:
  4024. return [q, br, p];
  4025. case 2:
  4026. return [p, br, t];
  4027. case 3:
  4028. return [p, q, br];
  4029. case 4:
  4030. return [t, p, br];
  4031. case 5:
  4032. return [br, p, q];
  4033. }
  4034. }
  4035. };
  4036. p.color.toHSB = function( colorInt ) {
  4037. var red, green, blue;
  4038. red = ((colorInt & p.RED_MASK) >>> 16) / 255;
  4039. green = ((colorInt & p.GREEN_MASK) >>> 8) / 255;
  4040. blue = (colorInt & p.BLUE_MASK) / 255;
  4041. var max = p.max(p.max(red,green), blue),
  4042. min = p.min(p.min(red,green), blue),
  4043. hue, saturation;
  4044. if (min === max) {
  4045. return [0, 0, max];
  4046. } else {
  4047. saturation = (max - min) / max;
  4048. if (red === max) {
  4049. hue = (green - blue) / (max - min);
  4050. } else if (green === max) {
  4051. hue = 2 + ((blue - red) / (max - min));
  4052. } else {
  4053. hue = 4 + ((red - green) / (max - min));
  4054. }
  4055. hue /= 6;
  4056. if (hue < 0) {
  4057. hue += 1;
  4058. } else if (hue > 1) {
  4059. hue -= 1;
  4060. }
  4061. }
  4062. return [hue*colorModeX, saturation*colorModeY, max*colorModeZ];
  4063. };
  4064. p.brightness = function(colInt){
  4065. return p.color.toHSB(colInt)[2];
  4066. };
  4067. p.saturation = function(colInt){
  4068. return p.color.toHSB(colInt)[1];
  4069. };
  4070. p.hue = function(colInt){
  4071. return p.color.toHSB(colInt)[0];
  4072. };
  4073. var verifyChannel = function verifyChannel(aColor) {
  4074. if (aColor.constructor === Array) {
  4075. return aColor;
  4076. } else {
  4077. return p.color(aColor);
  4078. }
  4079. };
  4080. p.red = function(aColor) {
  4081. return ((aColor & p.RED_MASK) >>> 16) / 255 * colorModeX;
  4082. };
  4083. p.green = function(aColor) {
  4084. return ((aColor & p.GREEN_MASK) >>> 8) / 255 * colorModeY;
  4085. };
  4086. p.blue = function(aColor) {
  4087. return (aColor & p.BLUE_MASK) / 255 * colorModeZ;
  4088. };
  4089. p.alpha = function(aColor) {
  4090. return ((aColor & p.ALPHA_MASK) >>> 24) / 255 * colorModeA;
  4091. };
  4092. p.lerpColor = function lerpColor(c1, c2, amt) {
  4093. // Get RGBA values for Color 1 to floats
  4094. var colorBits1 = p.color(c1);
  4095. var r1 = (colorBits1 & p.RED_MASK) >>> 16;
  4096. var g1 = (colorBits1 & p.GREEN_MASK) >>> 8;
  4097. var b1 = (colorBits1 & p.BLUE_MASK);
  4098. var a1 = ((colorBits1 & p.ALPHA_MASK) >>> 24) / colorModeA;
  4099. // Get RGBA values for Color 2 to floats
  4100. var colorBits2 = p.color(c2);
  4101. var r2 = (colorBits2 & p.RED_MASK) >>> 16;
  4102. var g2 = (colorBits2 & p.GREEN_MASK) >>> 8;
  4103. var b2 = (colorBits2 & p.BLUE_MASK);
  4104. var a2 = ((colorBits2 & p.ALPHA_MASK) >>> 24) / colorModeA;
  4105. // Return lerp value for each channel, INT for color, Float for Alpha-range
  4106. var r = parseInt(p.lerp(r1, r2, amt), 10);
  4107. var g = parseInt(p.lerp(g1, g2, amt), 10);
  4108. var b = parseInt(p.lerp(b1, b2, amt), 10);
  4109. var a = parseFloat(p.lerp(a1, a2, amt) * colorModeA);
  4110. return p.color.toInt(r, g, b, a);
  4111. };
  4112. // Forced default color mode for #aaaaaa style
  4113. p.defaultColor = function(aValue1, aValue2, aValue3) {
  4114. var tmpColorMode = curColorMode;
  4115. curColorMode = p.RGB;
  4116. var c = p.color(aValue1 / 255 * colorModeX, aValue2 / 255 * colorModeY, aValue3 / 255 * colorModeZ);
  4117. curColorMode = tmpColorMode;
  4118. return c;
  4119. };
  4120. p.colorMode = function colorMode() { // mode, range1, range2, range3, range4
  4121. curColorMode = arguments[0];
  4122. if (arguments.length > 1) {
  4123. colorModeX = arguments[1];
  4124. colorModeY = arguments[2] || arguments[1];
  4125. colorModeZ = arguments[3] || arguments[1];
  4126. colorModeA = arguments[4] || arguments[1];
  4127. }
  4128. };
  4129. p.blendColor = function(c1, c2, mode) {
  4130. var color = 0;
  4131. switch (mode) {
  4132. case p.REPLACE:
  4133. color = p.modes.replace(c1, c2);
  4134. break;
  4135. case p.BLEND:
  4136. color = p.modes.blend(c1, c2);
  4137. break;
  4138. case p.ADD:
  4139. color = p.modes.add(c1, c2);
  4140. break;
  4141. case p.SUBTRACT:
  4142. color = p.modes.subtract(c1, c2);
  4143. break;
  4144. case p.LIGHTEST:
  4145. color = p.modes.lightest(c1, c2);
  4146. break;
  4147. case p.DARKEST:
  4148. color = p.modes.darkest(c1, c2);
  4149. break;
  4150. case p.DIFFERENCE:
  4151. color = p.modes.difference(c1, c2);
  4152. break;
  4153. case p.EXCLUSION:
  4154. color = p.modes.exclusion(c1, c2);
  4155. break;
  4156. case p.MULTIPLY:
  4157. color = p.modes.multiply(c1, c2);
  4158. break;
  4159. case p.SCREEN:
  4160. color = p.modes.screen(c1, c2);
  4161. break;
  4162. case p.HARD_LIGHT:
  4163. color = p.modes.hard_light(c1, c2);
  4164. break;
  4165. case p.SOFT_LIGHT:
  4166. color = p.modes.soft_light(c1, c2);
  4167. break;
  4168. case p.OVERLAY:
  4169. color = p.modes.overlay(c1, c2);
  4170. break;
  4171. case p.DODGE:
  4172. color = p.modes.dodge(c1, c2);
  4173. break;
  4174. case p.BURN:
  4175. color = p.modes.burn(c1, c2);
  4176. break;
  4177. }
  4178. return color;
  4179. };
  4180. ////////////////////////////////////////////////////////////////////////////
  4181. // Canvas-Matrix manipulation
  4182. ////////////////////////////////////////////////////////////////////////////
  4183. function saveContext() {
  4184. curContext.save();
  4185. }
  4186. function restoreContext() {
  4187. curContext.restore();
  4188. isStrokeDirty = true;
  4189. isFillDirty = true;
  4190. }
  4191. p.printMatrix = function printMatrix() {
  4192. modelView.print();
  4193. };
  4194. p.translate = function translate(x, y, z) {
  4195. if (p.use3DContext) {
  4196. forwardTransform.translate(x, y, z);
  4197. reverseTransform.invTranslate(x, y, z);
  4198. } else {
  4199. curContext.translate(x, y);
  4200. }
  4201. };
  4202. p.scale = function scale(x, y, z) {
  4203. if (p.use3DContext) {
  4204. forwardTransform.scale(x, y, z);
  4205. reverseTransform.invScale(x, y, z);
  4206. } else {
  4207. curContext.scale(x, y || x);
  4208. }
  4209. };
  4210. p.pushMatrix = function pushMatrix() {
  4211. if (p.use3DContext) {
  4212. userMatrixStack.load(modelView);
  4213. } else {
  4214. saveContext();
  4215. }
  4216. };
  4217. p.popMatrix = function popMatrix() {
  4218. if (p.use3DContext) {
  4219. modelView.set(userMatrixStack.pop());
  4220. } else {
  4221. restoreContext();
  4222. }
  4223. };
  4224. p.resetMatrix = function resetMatrix() {
  4225. forwardTransform.reset();
  4226. reverseTransform.reset();
  4227. };
  4228. p.applyMatrix = function applyMatrix() {
  4229. var a = arguments;
  4230. if (!p.use3DContext) {
  4231. for (var cnt = a.length; cnt < 16; cnt++) {
  4232. a[cnt] = 0;
  4233. }
  4234. a[10] = a[15] = 1;
  4235. }
  4236. forwardTransform.apply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
  4237. reverseTransform.invApply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
  4238. };
  4239. p.rotateX = function(angleInRadians) {
  4240. forwardTransform.rotateX(angleInRadians);
  4241. reverseTransform.invRotateX(angleInRadians);
  4242. };
  4243. p.rotateZ = function(angleInRadians) {
  4244. forwardTransform.rotateZ(angleInRadians);
  4245. reverseTransform.invRotateZ(angleInRadians);
  4246. };
  4247. p.rotateY = function(angleInRadians) {
  4248. forwardTransform.rotateY(angleInRadians);
  4249. reverseTransform.invRotateY(angleInRadians);
  4250. };
  4251. p.rotate = function rotate(angleInRadians) {
  4252. if (p.use3DContext) {
  4253. forwardTransform.rotateZ(angleInRadians);
  4254. reverseTransform.invRotateZ(angleInRadians);
  4255. } else {
  4256. curContext.rotate(angleInRadians);
  4257. }
  4258. };
  4259. p.pushStyle = function pushStyle() {
  4260. // Save the canvas state.
  4261. saveContext();
  4262. p.pushMatrix();
  4263. var newState = {
  4264. 'doFill': doFill,
  4265. 'currentFillColor': currentFillColor,
  4266. 'doStroke': doStroke,
  4267. 'currentStrokeColor': currentStrokeColor,
  4268. 'curTint': curTint,
  4269. 'curRectMode': curRectMode,
  4270. 'curColorMode': curColorMode,
  4271. 'colorModeX': colorModeX,
  4272. 'colorModeZ': colorModeZ,
  4273. 'colorModeY': colorModeY,
  4274. 'colorModeA': colorModeA,
  4275. 'curTextFont': curTextFont,
  4276. 'curTextSize': curTextSize
  4277. };
  4278. styleArray.push(newState);
  4279. };
  4280. p.popStyle = function popStyle() {
  4281. var oldState = styleArray.pop();
  4282. if (oldState) {
  4283. restoreContext();
  4284. p.popMatrix();
  4285. doFill = oldState.doFill;
  4286. currentFillColor = oldState.currentFillColor;
  4287. doStroke = oldState.doStroke;
  4288. currentStrokeColor = oldState.currentStrokeColor;
  4289. curTint = oldState.curTint;
  4290. curRectMode = oldState.curRectmode;
  4291. curColorMode = oldState.curColorMode;
  4292. colorModeX = oldState.colorModeX;
  4293. colorModeZ = oldState.colorModeZ;
  4294. colorModeY = oldState.colorModeY;
  4295. colorModeA = oldState.colorModeA;
  4296. curTextFont = oldState.curTextFont;
  4297. curTextSize = oldState.curTextSize;
  4298. } else {
  4299. throw "Too many popStyle() without enough pushStyle()";
  4300. }
  4301. };
  4302. ////////////////////////////////////////////////////////////////////////////
  4303. // Time based functions
  4304. ////////////////////////////////////////////////////////////////////////////
  4305. p.year = function year() {
  4306. return new Date().getFullYear();
  4307. };
  4308. p.month = function month() {
  4309. return new Date().getMonth() + 1;
  4310. };
  4311. p.day = function day() {
  4312. return new Date().getDate();
  4313. };
  4314. p.hour = function hour() {
  4315. return new Date().getHours();
  4316. };
  4317. p.minute = function minute() {
  4318. return new Date().getMinutes();
  4319. };
  4320. p.second = function second() {
  4321. return new Date().getSeconds();
  4322. };
  4323. p.millis = function millis() {
  4324. return new Date().getTime() - start;
  4325. };
  4326. p.redraw = function redraw() {
  4327. var sec = (new Date().getTime() - timeSinceLastFPS) / 1000;
  4328. framesSinceLastFPS++;
  4329. var fps = framesSinceLastFPS / sec;
  4330. // recalculate FPS every half second for better accuracy.
  4331. if (sec > 0.5) {
  4332. timeSinceLastFPS = new Date().getTime();
  4333. framesSinceLastFPS = 0;
  4334. p.__frameRate = fps;
  4335. }
  4336. p.frameCount++;
  4337. inDraw = true;
  4338. if (p.use3DContext) {
  4339. // Delete all the lighting states and the materials the
  4340. // user set in the last draw() call.
  4341. p.noLights();
  4342. p.lightFalloff(1, 0, 0);
  4343. p.shininess(1);
  4344. p.ambient(255, 255, 255);
  4345. p.specular(0, 0, 0);
  4346. p.camera();
  4347. p.draw();
  4348. } else {
  4349. saveContext();
  4350. p.draw();
  4351. restoreContext();
  4352. }
  4353. inDraw = false;
  4354. };
  4355. p.noLoop = function noLoop() {
  4356. doLoop = false;
  4357. loopStarted = false;
  4358. clearInterval(looping);
  4359. };
  4360. p.loop = function loop() {
  4361. if (loopStarted) {
  4362. return;
  4363. }
  4364. looping = window.setInterval(function() {
  4365. try {
  4366. try {
  4367. p.focused = document.hasFocus();
  4368. } catch(e) {}
  4369. p.redraw();
  4370. } catch(e_loop) {
  4371. window.clearInterval(looping);
  4372. throw e_loop;
  4373. }
  4374. }, curMsPerFrame);
  4375. doLoop = true;
  4376. loopStarted = true;
  4377. };
  4378. p.frameRate = function frameRate(aRate) {
  4379. curFrameRate = aRate;
  4380. curMsPerFrame = 1000 / curFrameRate;
  4381. };
  4382. var eventHandlers = [];
  4383. p.exit = function exit() {
  4384. window.clearInterval(looping);
  4385. Processing.removeInstance(p.externals.canvas.id);
  4386. for (var i=0, ehl=eventHandlers.length; i<ehl; i++) {
  4387. var elem = eventHandlers[i][0],
  4388. type = eventHandlers[i][1],
  4389. fn = eventHandlers[i][2];
  4390. if (elem.removeEventListener) {
  4391. elem.removeEventListener(type, fn, false);
  4392. } else if (elem.detachEvent) {
  4393. elem.detachEvent("on" + type, fn);
  4394. }
  4395. }
  4396. };
  4397. ////////////////////////////////////////////////////////////////////////////
  4398. // MISC functions
  4399. ////////////////////////////////////////////////////////////////////////////
  4400. p.cursor = function cursor() {
  4401. if (arguments.length > 1 || (arguments.length === 1 && arguments[0] instanceof p.PImage)) {
  4402. var image = arguments[0],
  4403. x, y;
  4404. if (arguments.length >= 3) {
  4405. x = arguments[1];
  4406. y = arguments[2];
  4407. if (x < 0 || y < 0 || y >= image.height || x >= image.width) {
  4408. throw "x and y must be non-negative and less than the dimensions of the image";
  4409. }
  4410. } else {
  4411. x = image.width >>> 1;
  4412. y = image.height >>> 1;
  4413. }
  4414. // see https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property
  4415. var imageDataURL = image.toDataURL();
  4416. var style = "url(\"" + imageDataURL + "\") " + x + " " + y + ", default";
  4417. curCursor = curElement.style.cursor = style;
  4418. } else if (arguments.length === 1) {
  4419. var mode = arguments[0];
  4420. curCursor = curElement.style.cursor = mode;
  4421. } else {
  4422. curCursor = curElement.style.cursor = oldCursor;
  4423. }
  4424. };
  4425. p.noCursor = function noCursor() {
  4426. curCursor = curElement.style.cursor = p.NOCURSOR;
  4427. };
  4428. p.link = function(href, target) {
  4429. if (target !== undef) {
  4430. window.open(href, target);
  4431. } else {
  4432. window.location = href;
  4433. }
  4434. };
  4435. // PGraphics methods
  4436. // TODO: These functions are suppose to be called before any operations are called on the
  4437. // PGraphics object. They currently do nothing.
  4438. p.beginDraw = function beginDraw() {};
  4439. p.endDraw = function endDraw() {};
  4440. // Imports an external Processing.js library
  4441. p.Import = function Import(lib) {
  4442. // Replace evil-eval method with a DOM <script> tag insert method that
  4443. // binds new lib code to the Processing.lib names-space and the current
  4444. // p context. -F1LT3R
  4445. };
  4446. var contextMenu = function(e) {
  4447. e.preventDefault();
  4448. e.stopPropagation();
  4449. };
  4450. p.disableContextMenu = function disableContextMenu() {
  4451. curElement.addEventListener('contextmenu', contextMenu, false);
  4452. };
  4453. p.enableContextMenu = function enableContextMenu() {
  4454. curElement.removeEventListener('contextmenu', contextMenu, false);
  4455. };
  4456. p.status = function(text) {
  4457. window.status = text;
  4458. };
  4459. ////////////////////////////////////////////////////////////////////////////
  4460. // Binary Functions
  4461. ////////////////////////////////////////////////////////////////////////////
  4462. function decToBin(value, numBitsInValue) {
  4463. var mask = 1;
  4464. mask = mask << (numBitsInValue - 1);
  4465. var str = "";
  4466. for (var i = 0; i < numBitsInValue; i++) {
  4467. str += (mask & value) ? "1" : "0";
  4468. mask = mask >>> 1;
  4469. }
  4470. return str;
  4471. }
  4472. /*
  4473. This function does not always work when trying to convert
  4474. colors and bytes to binary values because the types passed in
  4475. cannot be determined.
  4476. */
  4477. p.binary = function(num, numBits) {
  4478. var numBitsInValue = 32;
  4479. // color, int, byte
  4480. if (typeof num === "number") {
  4481. if(numBits){
  4482. numBitsInValue = numBits;
  4483. }
  4484. return decToBin(num, numBitsInValue);
  4485. }
  4486. // char
  4487. if (num instanceof Char) {
  4488. num = num.toString().charCodeAt(0);
  4489. if (numBits) {
  4490. numBitsInValue = 32;
  4491. } else {
  4492. numBitsInValue = 16;
  4493. }
  4494. }
  4495. var str = decToBin(num, numBitsInValue);
  4496. // trim string if user wanted less chars
  4497. if (numBits) {
  4498. str = str.substr(-numBits);
  4499. }
  4500. return str;
  4501. };
  4502. p.unbinary = function unbinary(binaryString) {
  4503. var binaryPattern = new RegExp("^[0|1]{8}$");
  4504. var addUp = 0;
  4505. var i;
  4506. if (binaryString instanceof Array) {
  4507. var values = [];
  4508. for (i = 0; i < binaryString.length; i++) {
  4509. values[i] = p.unbinary(binaryString[i]);
  4510. }
  4511. return values;
  4512. } else {
  4513. if (isNaN(binaryString)) {
  4514. throw "NaN_Err";
  4515. } else {
  4516. if (arguments.length === 1 || binaryString.length === 8) {
  4517. if (binaryPattern.test(binaryString)) {
  4518. for (i = 0; i < 8; i++) {
  4519. addUp += (Math.pow(2, i) * parseInt(binaryString.charAt(7 - i), 10));
  4520. }
  4521. return addUp + "";
  4522. } else {
  4523. throw "notBinary: the value passed into unbinary was not an 8 bit binary number";
  4524. }
  4525. } else {
  4526. throw "longErr";
  4527. }
  4528. }
  4529. }
  4530. };
  4531. function nfCoreScalar(value, plus, minus, leftDigits, rightDigits, group) {
  4532. var sign = (value < 0) ? minus : plus;
  4533. var autoDetectDecimals = rightDigits === 0;
  4534. var rightDigitsOfDefault = (rightDigits === undef || rightDigits < 0) ? 0 : rightDigits;
  4535. // Change the way the number is 'floored' based on whether it is odd or even.
  4536. if (rightDigits < 0 && Math.floor(value) % 2 === 1) {
  4537. // Make sure 1.49 rounds to 1, but 1.5 rounds to 2.
  4538. if ((value - Math.floor(value)) >= 0.5) {
  4539. value += 1;
  4540. }
  4541. }
  4542. var absValue = Math.abs(value);
  4543. if(autoDetectDecimals) {
  4544. rightDigitsOfDefault = 1;
  4545. absValue *= 10;
  4546. while(Math.abs(Math.round(absValue) - absValue) > 1e-6 && rightDigitsOfDefault < 7) {
  4547. ++rightDigitsOfDefault;
  4548. absValue *= 10;
  4549. }
  4550. } else if (rightDigitsOfDefault !== 0) {
  4551. absValue *= Math.pow(10, rightDigitsOfDefault);
  4552. }
  4553. var number = Math.round(absValue);
  4554. var buffer = "";
  4555. var totalDigits = leftDigits + rightDigitsOfDefault;
  4556. while (totalDigits > 0 || number > 0) {
  4557. totalDigits--;
  4558. buffer = "" + (number % 10) + buffer;
  4559. number = Math.floor(number / 10);
  4560. }
  4561. if (group !== undef) {
  4562. var i = buffer.length - 3 - rightDigitsOfDefault;
  4563. while(i > 0) {
  4564. buffer = buffer.substring(0,i) + group + buffer.substring(i);
  4565. i-=3;
  4566. }
  4567. }
  4568. if (rightDigitsOfDefault > 0) {
  4569. return sign + buffer.substring(0, buffer.length - rightDigitsOfDefault) +
  4570. "." + buffer.substring(buffer.length - rightDigitsOfDefault, buffer.length);
  4571. } else {
  4572. return sign + buffer;
  4573. }
  4574. }
  4575. function nfCore(value, plus, minus, leftDigits, rightDigits, group) {
  4576. if (value instanceof Array) {
  4577. var arr = [];
  4578. for (var i = 0, len = value.length; i < len; i++) {
  4579. arr.push(nfCoreScalar(value[i], plus, minus, leftDigits, rightDigits, group));
  4580. }
  4581. return arr;
  4582. } else {
  4583. return nfCoreScalar(value, plus, minus, leftDigits, rightDigits, group);
  4584. }
  4585. }
  4586. // TODO: drop this and use nfCore (see below) code when we've fixed the rounding bug in nfCore
  4587. p.nf = function() {
  4588. var str, num, pad, arr, left, right, isNegative, test, i;
  4589. if (arguments.length === 2 && typeof arguments[0] === 'number' && typeof arguments[1] === 'number' && (arguments[0] + "").indexOf('.') === -1) {
  4590. num = arguments[0];
  4591. pad = arguments[1];
  4592. isNegative = num < 0;
  4593. if (isNegative) {
  4594. num = Math.abs(num);
  4595. }
  4596. str = "" + num;
  4597. for (i = pad - str.length; i > 0; i--) {
  4598. str = "0" + str;
  4599. }
  4600. if (isNegative) {
  4601. str = "-" + str;
  4602. }
  4603. } else if (arguments.length === 2 && typeof arguments[0] === 'object' && arguments[0].constructor === Array && typeof arguments[1] === 'number') {
  4604. arr = arguments[0];
  4605. pad = arguments[1];
  4606. str = new Array(arr.length);
  4607. for (i = 0; i < arr.length && str !== undef; i++) {
  4608. test = p.nf(arr[i], pad);
  4609. if (test === undef) {
  4610. str = undef;
  4611. } else {
  4612. str[i] = test;
  4613. }
  4614. }
  4615. } else if (arguments.length === 3 && typeof arguments[0] === 'number' && typeof arguments[1] === 'number' && typeof arguments[2] === 'number' && (arguments[0] + "").indexOf('.') >= 0) {
  4616. num = arguments[0];
  4617. left = arguments[1];
  4618. right = arguments[2];
  4619. isNegative = num < 0;
  4620. if (isNegative) {
  4621. num = Math.abs(num);
  4622. }
  4623. // Change the way the number is 'floored' based on whether it is odd or even.
  4624. if (right < 0 && Math.floor(num) % 2 === 1) {
  4625. // Make sure 1.49 rounds to 1, but 1.5 rounds to 2.
  4626. if ((num) - Math.floor(num) >= 0.5) {
  4627. num = num + 1;
  4628. }
  4629. }
  4630. str = "" + num;
  4631. for (i = left - str.indexOf('.'); i > 0; i--) {
  4632. str = "0" + str;
  4633. }
  4634. var numDec = str.length - str.indexOf('.') - 1;
  4635. if (numDec <= right) {
  4636. for (i = right - (str.length - str.indexOf('.') - 1); i > 0; i--) {
  4637. str = str + "0";
  4638. }
  4639. } else if (right > 0) {
  4640. str = str.substring(0, str.length - (numDec - right));
  4641. } else if (right < 0) {
  4642. str = str.substring(0, str.indexOf('.'));
  4643. }
  4644. if (isNegative) {
  4645. str = "-" + str;
  4646. }
  4647. } else if (arguments.length === 3 && typeof arguments[0] === 'object' && arguments[0].constructor === Array && typeof arguments[1] === 'number' && typeof arguments[2] === 'number') {
  4648. arr = arguments[0];
  4649. left = arguments[1];
  4650. right = arguments[2];
  4651. str = new Array(arr.length);
  4652. for (i = 0; i < arr.length && str !== undef; i++) {
  4653. test = p.nf(arr[i], left, right);
  4654. if (test === undef) {
  4655. str = undef;
  4656. } else {
  4657. str[i] = test;
  4658. }
  4659. }
  4660. }
  4661. return str;
  4662. };
  4663. // TODO: need to switch nf over to using nfCore...
  4664. // p.nf = function(value, leftDigits, rightDigits) { return nfCore(value, "", "-", leftDigits, rightDigits); };
  4665. p.nfs = function(value, leftDigits, rightDigits) { return nfCore(value, " ", "-", leftDigits, rightDigits); };
  4666. p.nfp = function(value, leftDigits, rightDigits) { return nfCore(value, "+", "-", leftDigits, rightDigits); };
  4667. p.nfc = function(value, leftDigits, rightDigits) { return nfCore(value, "", "-", leftDigits, rightDigits, ","); };
  4668. var decimalToHex = function decimalToHex(d, padding) {
  4669. //if there is no padding value added, default padding to 8 else go into while statement.
  4670. padding = (padding === undef || padding === null) ? padding = 8 : padding;
  4671. if (d < 0) {
  4672. d = 0xFFFFFFFF + d + 1;
  4673. }
  4674. var hex = Number(d).toString(16).toUpperCase();
  4675. while (hex.length < padding) {
  4676. hex = "0" + hex;
  4677. }
  4678. if (hex.length >= padding) {
  4679. hex = hex.substring(hex.length - padding, hex.length);
  4680. }
  4681. return hex;
  4682. };
  4683. // note: since we cannot keep track of byte, int types by default the returned string is 8 chars long
  4684. // if no 2nd argument is passed. closest compromise we can use to match java implementation Feb 5 2010
  4685. // also the char parser has issues with chars that are not digits or letters IE: !@#$%^&*
  4686. p.hex = function hex(value, len) {
  4687. if (arguments.length === 1) {
  4688. if (value instanceof Char) {
  4689. len = 4;
  4690. } else { // int or byte, indistinguishable at the moment, default to 8
  4691. len = 8;
  4692. }
  4693. }
  4694. return decimalToHex(value, len);
  4695. };
  4696. function unhexScalar(hex) {
  4697. var value = parseInt("0x" + hex, 16);
  4698. // correct for int overflow java expectation
  4699. if (value > 2147483647) {
  4700. value -= 4294967296;
  4701. }
  4702. return value;
  4703. }
  4704. p.unhex = function(hex) {
  4705. if (hex instanceof Array) {
  4706. var arr = [];
  4707. for (var i = 0; i < hex.length; i++) {
  4708. arr.push(unhexScalar(hex[i]));
  4709. }
  4710. return arr;
  4711. } else {
  4712. return unhexScalar(hex);
  4713. }
  4714. };
  4715. // Load a file or URL into strings
  4716. p.loadStrings = function loadStrings(url) {
  4717. return ajax(url).split("\n");
  4718. };
  4719. p.loadBytes = function loadBytes(url) {
  4720. var string = ajax(url);
  4721. var ret = [];
  4722. for (var i = 0; i < string.length; i++) {
  4723. ret.push(string.charCodeAt(i));
  4724. }
  4725. return ret;
  4726. };
  4727. ////////////////////////////////////////////////////////////////////////////
  4728. // String Functions
  4729. ////////////////////////////////////////////////////////////////////////////
  4730. p.matchAll = function matchAll(aString, aRegExp) {
  4731. var results = [],
  4732. latest;
  4733. var regexp = new RegExp(aRegExp, "g");
  4734. while ((latest = regexp.exec(aString)) !== null) {
  4735. results.push(latest);
  4736. if (latest[0].length === 0) {
  4737. ++regexp.lastIndex;
  4738. }
  4739. }
  4740. return results.length > 0 ? results : null;
  4741. };
  4742. String.prototype.replaceAll = function(re, replace) {
  4743. return this.replace(new RegExp(re, "g"), replace);
  4744. };
  4745. String.prototype.equals = function equals(str) {
  4746. return this.valueOf() === str.valueOf();
  4747. };
  4748. String.prototype.toCharArray = function() {
  4749. var chars = this.split("");
  4750. for (var i = chars.length - 1; i >= 0; i--) {
  4751. chars[i] = new Char(chars[i]);
  4752. }
  4753. return chars;
  4754. };
  4755. p.match = function(str, regexp) {
  4756. return str.match(regexp);
  4757. };
  4758. // tinylog lite JavaScript library
  4759. // http://purl.eligrey.com/tinylog/lite
  4760. /*global tinylog,print*/
  4761. var tinylogLite = (function() {
  4762. "use strict";
  4763. var tinylogLite = {},
  4764. undef = "undefined",
  4765. func = "function",
  4766. False = !1,
  4767. True = !0,
  4768. logLimit = 512,
  4769. log = "log";
  4770. if (typeof tinylog !== undef && typeof tinylog[log] === func) {
  4771. // pre-existing tinylog present
  4772. tinylogLite[log] = tinylog[log];
  4773. } else if (typeof document !== undef && !document.fake) {
  4774. (function() {
  4775. // DOM document
  4776. var doc = document,
  4777. $div = "div",
  4778. $style = "style",
  4779. $title = "title",
  4780. containerStyles = {
  4781. zIndex: 10000,
  4782. position: "fixed",
  4783. bottom: "0px",
  4784. width: "100%",
  4785. height: "15%",
  4786. fontFamily: "sans-serif",
  4787. color: "#ccc",
  4788. backgroundColor: "black"
  4789. },
  4790. outputStyles = {
  4791. position: "relative",
  4792. fontFamily: "monospace",
  4793. overflow: "auto",
  4794. height: "100%",
  4795. paddingTop: "5px"
  4796. },
  4797. resizerStyles = {
  4798. height: "5px",
  4799. marginTop: "-5px",
  4800. cursor: "n-resize",
  4801. backgroundColor: "darkgrey"
  4802. },
  4803. closeButtonStyles = {
  4804. position: "absolute",
  4805. top: "5px",
  4806. right: "20px",
  4807. color: "#111",
  4808. MozBorderRadius: "4px",
  4809. webkitBorderRadius: "4px",
  4810. borderRadius: "4px",
  4811. cursor: "pointer",
  4812. fontWeight: "normal",
  4813. textAlign: "center",
  4814. padding: "3px 5px",
  4815. backgroundColor: "#333",
  4816. fontSize: "12px"
  4817. },
  4818. entryStyles = {
  4819. //borderBottom: "1px solid #d3d3d3",
  4820. minHeight: "16px"
  4821. },
  4822. entryTextStyles = {
  4823. fontSize: "12px",
  4824. margin: "0 8px 0 8px",
  4825. maxWidth: "100%",
  4826. whiteSpace: "pre-wrap",
  4827. overflow: "auto"
  4828. },
  4829. view = doc.defaultView,
  4830. docElem = doc.documentElement,
  4831. docElemStyle = docElem[$style],
  4832. setStyles = function() {
  4833. var i = arguments.length,
  4834. elemStyle, styles, style;
  4835. while (i--) {
  4836. styles = arguments[i--];
  4837. elemStyle = arguments[i][$style];
  4838. for (style in styles) {
  4839. if (styles.hasOwnProperty(style)) {
  4840. elemStyle[style] = styles[style];
  4841. }
  4842. }
  4843. }
  4844. },
  4845. observer = function(obj, event, handler) {
  4846. if (obj.addEventListener) {
  4847. obj.addEventListener(event, handler, False);
  4848. } else if (obj.attachEvent) {
  4849. obj.attachEvent("on" + event, handler);
  4850. }
  4851. return [obj, event, handler];
  4852. },
  4853. unobserve = function(obj, event, handler) {
  4854. if (obj.removeEventListener) {
  4855. obj.removeEventListener(event, handler, False);
  4856. } else if (obj.detachEvent) {
  4857. obj.detachEvent("on" + event, handler);
  4858. }
  4859. },
  4860. clearChildren = function(node) {
  4861. var children = node.childNodes,
  4862. child = children.length;
  4863. while (child--) {
  4864. node.removeChild(children.item(0));
  4865. }
  4866. },
  4867. append = function(to, elem) {
  4868. return to.appendChild(elem);
  4869. },
  4870. createElement = function(localName) {
  4871. return doc.createElement(localName);
  4872. },
  4873. createTextNode = function(text) {
  4874. return doc.createTextNode(text);
  4875. },
  4876. createLog = tinylogLite[log] = function(message) {
  4877. // don't show output log until called once
  4878. var uninit,
  4879. originalPadding = docElemStyle.paddingBottom,
  4880. container = createElement($div),
  4881. containerStyle = container[$style],
  4882. resizer = append(container, createElement($div)),
  4883. output = append(container, createElement($div)),
  4884. closeButton = append(container, createElement($div)),
  4885. resizingLog = False,
  4886. previousHeight = False,
  4887. previousScrollTop = False,
  4888. messages = 0,
  4889. updateSafetyMargin = function() {
  4890. // have a blank space large enough to fit the output box at the page bottom
  4891. docElemStyle.paddingBottom = container.clientHeight + "px";
  4892. },
  4893. setContainerHeight = function(height) {
  4894. var viewHeight = view.innerHeight,
  4895. resizerHeight = resizer.clientHeight;
  4896. // constrain the container inside the viewport's dimensions
  4897. if (height < 0) {
  4898. height = 0;
  4899. } else if (height + resizerHeight > viewHeight) {
  4900. height = viewHeight - resizerHeight;
  4901. }
  4902. containerStyle.height = height / viewHeight * 100 + "%";
  4903. updateSafetyMargin();
  4904. },
  4905. observers = [
  4906. observer(doc, "mousemove", function(evt) {
  4907. if (resizingLog) {
  4908. setContainerHeight(view.innerHeight - evt.clientY);
  4909. output.scrollTop = previousScrollTop;
  4910. }
  4911. }),
  4912. observer(doc, "mouseup", function() {
  4913. if (resizingLog) {
  4914. resizingLog = previousScrollTop = False;
  4915. }
  4916. }),
  4917. observer(resizer, "dblclick", function(evt) {
  4918. evt.preventDefault();
  4919. if (previousHeight) {
  4920. setContainerHeight(previousHeight);
  4921. previousHeight = False;
  4922. } else {
  4923. previousHeight = container.clientHeight;
  4924. containerStyle.height = "0px";
  4925. }
  4926. }),
  4927. observer(resizer, "mousedown", function(evt) {
  4928. evt.preventDefault();
  4929. resizingLog = True;
  4930. previousScrollTop = output.scrollTop;
  4931. }),
  4932. observer(resizer, "contextmenu", function() {
  4933. resizingLog = False;
  4934. }),
  4935. observer(closeButton, "click", function() {
  4936. uninit();
  4937. })
  4938. ];
  4939. uninit = function() {
  4940. // remove observers
  4941. var i = observers.length;
  4942. while (i--) {
  4943. unobserve.apply(tinylogLite, observers[i]);
  4944. }
  4945. // remove tinylog lite from the DOM
  4946. docElem.removeChild(container);
  4947. docElemStyle.paddingBottom = originalPadding;
  4948. clearChildren(output);
  4949. clearChildren(container);
  4950. tinylogLite[log] = createLog;
  4951. };
  4952. setStyles(
  4953. container, containerStyles, output, outputStyles, resizer, resizerStyles, closeButton, closeButtonStyles);
  4954. closeButton[$title] = "Close Log";
  4955. append(closeButton, createTextNode("\u2716"));
  4956. resizer[$title] = "Double-click to toggle log minimization";
  4957. docElem.insertBefore(container, docElem.firstChild);
  4958. tinylogLite[log] = function(message) {
  4959. if (messages === logLimit) {
  4960. output.removeChild(output.firstChild);
  4961. } else {
  4962. messages++;
  4963. }
  4964. var entry = append(output, createElement($div)),
  4965. entryText = append(entry, createElement($div));
  4966. entry[$title] = (new Date()).toLocaleTimeString();
  4967. setStyles(
  4968. entry, entryStyles, entryText, entryTextStyles);
  4969. append(entryText, createTextNode(message));
  4970. output.scrollTop = output.scrollHeight;
  4971. };
  4972. tinylogLite[log](message);
  4973. };
  4974. }());
  4975. } else if (typeof print === func) { // JS shell
  4976. tinylogLite[log] = print;
  4977. }
  4978. return tinylogLite;
  4979. }()),
  4980. logBuffer = [];
  4981. p.console = window.console || tinylogLite;
  4982. p.println = function println(message) {
  4983. var bufferLen = logBuffer.length;
  4984. if (bufferLen) {
  4985. tinylogLite.log(logBuffer.join(""));
  4986. logBuffer.length = 0; // clear log buffer
  4987. }
  4988. if (arguments.length === 0 && bufferLen === 0) {
  4989. tinylogLite.log("");
  4990. } else if (arguments.length !== 0) {
  4991. tinylogLite.log(message);
  4992. }
  4993. };
  4994. p.print = function print(message) {
  4995. logBuffer.push(message);
  4996. };
  4997. // Alphanumeric chars arguments automatically converted to numbers when
  4998. // passed in, and will come out as numbers.
  4999. p.str = function str(val) {
  5000. if (val instanceof Array) {
  5001. var arr = [];
  5002. for (var i = 0; i < val.length; i++) {
  5003. arr.push(val[i] + "");
  5004. }
  5005. return arr;
  5006. } else {
  5007. return (val + "");
  5008. }
  5009. };
  5010. p.trim = function(str) {
  5011. if (str instanceof Array) {
  5012. var arr = [];
  5013. for (var i = 0; i < str.length; i++) {
  5014. arr.push(str[i].replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, ''));
  5015. }
  5016. return arr;
  5017. } else {
  5018. return str.replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, '');
  5019. }
  5020. };
  5021. // Conversion
  5022. function booleanScalar(val) {
  5023. if (typeof val === 'number') {
  5024. return val !== 0;
  5025. } else if (typeof val === 'boolean') {
  5026. return val;
  5027. } else if (typeof val === 'string') {
  5028. return val.toLowerCase() === 'true';
  5029. } else if (val instanceof Char) {
  5030. // 1, T or t
  5031. return val.code === 49 || val.code === 84 || val.code === 116;
  5032. }
  5033. }
  5034. p['boolean'] = function(val) {
  5035. if (val instanceof Array) {
  5036. var ret = [];
  5037. for (var i = 0; i < val.length; i++) {
  5038. ret.push(booleanScalar(val[i]));
  5039. }
  5040. return ret;
  5041. } else {
  5042. return booleanScalar(val);
  5043. }
  5044. };
  5045. // a byte is a number between -128 and 127
  5046. p['byte'] = function(aNumber) {
  5047. if (aNumber instanceof Array) {
  5048. var bytes = [];
  5049. for (var i = 0; i < aNumber.length; i++) {
  5050. bytes.push((0 - (aNumber[i] & 0x80)) | (aNumber[i] & 0x7F));
  5051. }
  5052. return bytes;
  5053. } else {
  5054. return (0 - (aNumber & 0x80)) | (aNumber & 0x7F);
  5055. }
  5056. };
  5057. p['char'] = function(key) {
  5058. if (typeof key === "number") {
  5059. return new Char(String.fromCharCode(key & 0xFFFF));
  5060. } else if (key instanceof Array) {
  5061. var ret = [];
  5062. for (var i = 0; i < key.length; i++) {
  5063. ret.push(new Char(String.fromCharCode(key[i] & 0xFFFF)));
  5064. }
  5065. return ret;
  5066. } else {
  5067. throw "char() may receive only one argument of type int, byte, int[], or byte[].";
  5068. }
  5069. };
  5070. // Processing doc claims good argument types are: int, char, byte, boolean,
  5071. // String, int[], char[], byte[], boolean[], String[].
  5072. // floats should not work. However, floats with only zeroes right of the
  5073. // decimal will work because JS converts those to int.
  5074. function floatScalar(val) {
  5075. if (typeof val === 'number') {
  5076. return val;
  5077. } else if (typeof val === 'boolean') {
  5078. return val ? 1 : 0;
  5079. } else if (typeof val === 'string') {
  5080. return parseFloat(val);
  5081. } else if (val instanceof Char) {
  5082. return val.code;
  5083. }
  5084. }
  5085. p['float'] = function(val) {
  5086. if (val instanceof Array) {
  5087. var ret = [];
  5088. for (var i = 0; i < val.length; i++) {
  5089. ret.push(floatScalar(val[i]));
  5090. }
  5091. return ret;
  5092. } else {
  5093. return floatScalar(val);
  5094. }
  5095. };
  5096. function intScalar(val) {
  5097. if (typeof val === 'number') {
  5098. return val & 0xFFFFFFFF;
  5099. } else if (typeof val === 'boolean') {
  5100. return val ? 1 : 0;
  5101. } else if (typeof val === 'string') {
  5102. var number = parseInt(val, 10); // Force decimal radix. Don't convert hex or octal (just like p5)
  5103. return number & 0xFFFFFFFF;
  5104. } else if (val instanceof Char) {
  5105. return val.code;
  5106. }
  5107. }
  5108. p['int'] = function(val) {
  5109. if (val instanceof Array) {
  5110. var ret = [];
  5111. for (var i = 0; i < val.length; i++) {
  5112. if (typeof val[i] === 'string' && !/^\s*[+\-]?\d+\s*$/.test(val[i])) {
  5113. ret.push(0);
  5114. } else {
  5115. ret.push(intScalar(val[i]));
  5116. }
  5117. }
  5118. return ret;
  5119. } else {
  5120. return intScalar(val);
  5121. }
  5122. };
  5123. ////////////////////////////////////////////////////////////////////////////
  5124. // Math functions
  5125. ////////////////////////////////////////////////////////////////////////////
  5126. // Calculation
  5127. p.abs = Math.abs;
  5128. p.ceil = Math.ceil;
  5129. p.constrain = function(aNumber, aMin, aMax) {
  5130. return aNumber > aMax ? aMax : aNumber < aMin ? aMin : aNumber;
  5131. };
  5132. p.dist = function() {
  5133. var dx, dy, dz;
  5134. if (arguments.length === 4) {
  5135. dx = arguments[0] - arguments[2];
  5136. dy = arguments[1] - arguments[3];
  5137. return Math.sqrt(dx * dx + dy * dy);
  5138. } else if (arguments.length === 6) {
  5139. dx = arguments[0] - arguments[3];
  5140. dy = arguments[1] - arguments[4];
  5141. dz = arguments[2] - arguments[5];
  5142. return Math.sqrt(dx * dx + dy * dy + dz * dz);
  5143. }
  5144. };
  5145. p.exp = Math.exp;
  5146. p.floor = Math.floor;
  5147. p.lerp = function(value1, value2, amt) {
  5148. return ((value2 - value1) * amt) + value1;
  5149. };
  5150. p.log = Math.log;
  5151. p.mag = function(a, b, c) {
  5152. if (arguments.length === 2) {
  5153. return Math.sqrt(a * a + b * b);
  5154. } else if (arguments.length === 3) {
  5155. return Math.sqrt(a * a + b * b + c * c);
  5156. }
  5157. };
  5158. p.map = function(value, istart, istop, ostart, ostop) {
  5159. return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
  5160. };
  5161. p.max = function() {
  5162. if (arguments.length === 2) {
  5163. return arguments[0] < arguments[1] ? arguments[1] : arguments[0];
  5164. } else {
  5165. var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
  5166. if (! ("length" in numbers && numbers.length > 0)) {
  5167. throw "Non-empty array is expected";
  5168. }
  5169. var max = numbers[0],
  5170. count = numbers.length;
  5171. for (var i = 1; i < count; ++i) {
  5172. if (max < numbers[i]) {
  5173. max = numbers[i];
  5174. }
  5175. }
  5176. return max;
  5177. }
  5178. };
  5179. p.min = function() {
  5180. if (arguments.length === 2) {
  5181. return arguments[0] < arguments[1] ? arguments[0] : arguments[1];
  5182. } else {
  5183. var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
  5184. if (! ("length" in numbers && numbers.length > 0)) {
  5185. throw "Non-empty array is expected";
  5186. }
  5187. var min = numbers[0],
  5188. count = numbers.length;
  5189. for (var i = 1; i < count; ++i) {
  5190. if (min > numbers[i]) {
  5191. min = numbers[i];
  5192. }
  5193. }
  5194. return min;
  5195. }
  5196. };
  5197. p.norm = function(aNumber, low, high) {
  5198. return (aNumber - low) / (high - low);
  5199. };
  5200. p.pow = Math.pow;
  5201. p.round = Math.round;
  5202. p.sq = function(aNumber) {
  5203. return aNumber * aNumber;
  5204. };
  5205. p.sqrt = Math.sqrt;
  5206. // Trigonometry
  5207. p.acos = Math.acos;
  5208. p.asin = Math.asin;
  5209. p.atan = Math.atan;
  5210. p.atan2 = Math.atan2;
  5211. p.cos = Math.cos;
  5212. p.degrees = function(aAngle) {
  5213. return (aAngle * 180) / Math.PI;
  5214. };
  5215. p.radians = function(aAngle) {
  5216. return (aAngle / 180) * Math.PI;
  5217. };
  5218. p.sin = Math.sin;
  5219. p.tan = Math.tan;
  5220. var currentRandom = Math.random;
  5221. p.random = function random() {
  5222. if(arguments.length === 0) {
  5223. return currentRandom();
  5224. } else if(arguments.length === 1) {
  5225. return currentRandom() * arguments[0];
  5226. } else {
  5227. var aMin = arguments[0], aMax = arguments[1];
  5228. return currentRandom() * (aMax - aMin) + aMin;
  5229. }
  5230. };
  5231. // Pseudo-random generator
  5232. function Marsaglia(i1, i2) {
  5233. // from http://www.math.uni-bielefeld.de/~sillke/ALGORITHMS/random/marsaglia-c
  5234. var z=i1 || 362436069, w= i2 || 521288629;
  5235. var nextInt = function() {
  5236. z=(36969*(z&65535)+(z>>>16)) & 0xFFFFFFFF;
  5237. w=(18000*(w&65535)+(w>>>16)) & 0xFFFFFFFF;
  5238. return (((z&0xFFFF)<<16) | (w&0xFFFF)) & 0xFFFFFFFF;
  5239. };
  5240. this.nextDouble = function() {
  5241. var i = nextInt() / 4294967296;
  5242. return i < 0 ? 1 + i : i;
  5243. };
  5244. this.nextInt = nextInt;
  5245. }
  5246. Marsaglia.createRandomized = function() {
  5247. var now = new Date();
  5248. return new Marsaglia((now / 60000) & 0xFFFFFFFF, now & 0xFFFFFFFF);
  5249. };
  5250. p.randomSeed = function(seed) {
  5251. currentRandom = (new Marsaglia(seed)).nextDouble;
  5252. };
  5253. // Random
  5254. p.Random = function(seed) {
  5255. var haveNextNextGaussian = false, nextNextGaussian, random;
  5256. this.nextGaussian = function() {
  5257. if (haveNextNextGaussian) {
  5258. haveNextNextGaussian = false;
  5259. return nextNextGaussian;
  5260. } else {
  5261. var v1, v2, s;
  5262. do {
  5263. v1 = 2 * random() - 1; // between -1.0 and 1.0
  5264. v2 = 2 * random() - 1; // between -1.0 and 1.0
  5265. s = v1 * v1 + v2 * v2;
  5266. }
  5267. while (s >= 1 || s === 0);
  5268. var multiplier = Math.sqrt(-2 * Math.log(s) / s);
  5269. nextNextGaussian = v2 * multiplier;
  5270. haveNextNextGaussian = true;
  5271. return v1 * multiplier;
  5272. }
  5273. };
  5274. // by default use standard random, otherwise seeded
  5275. random = (seed === undef) ? Math.random : (new Marsaglia(seed)).nextDouble;
  5276. };
  5277. // Noise functions and helpers
  5278. function PerlinNoise(seed) {
  5279. var rnd = seed !== undef ? new Marsaglia(seed) : Marsaglia.createRandomized();
  5280. var i, j;
  5281. // http://www.noisemachine.com/talk1/17b.html
  5282. // http://mrl.nyu.edu/~perlin/noise/
  5283. // generate permutation
  5284. var perm = new Array(512);
  5285. for(i=0;i<256;++i) { perm[i] = i; }
  5286. for(i=0;i<256;++i) { var t = perm[j = rnd.nextInt() & 0xFF]; perm[j] = perm[i]; perm[i] = t; }
  5287. // copy to avoid taking mod in perm[0];
  5288. for(i=0;i<256;++i) { perm[i + 256] = perm[i]; }
  5289. function grad3d(i,x,y,z) {
  5290. var h = i & 15; // convert into 12 gradient directions
  5291. var u = h<8 ? x : y,
  5292. v = h<4 ? y : h===12||h===14 ? x : z;
  5293. return ((h&1) === 0 ? u : -u) + ((h&2) === 0 ? v : -v);
  5294. }
  5295. function grad2d(i,x,y) {
  5296. var v = (i & 1) === 0 ? x : y;
  5297. return (i&2) === 0 ? -v : v;
  5298. }
  5299. function grad1d(i,x) {
  5300. return (i&1) === 0 ? -x : x;
  5301. }
  5302. function lerp(t,a,b) { return a + t * (b - a); }
  5303. this.noise3d = function(x, y, z) {
  5304. var X = Math.floor(x)&255, Y = Math.floor(y)&255, Z = Math.floor(z)&255;
  5305. x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z);
  5306. var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y, fz = (3-2*z)*z*z;
  5307. var p0 = perm[X]+Y, p00 = perm[p0] + Z, p01 = perm[p0 + 1] + Z,
  5308. p1 = perm[X + 1] + Y, p10 = perm[p1] + Z, p11 = perm[p1 + 1] + Z;
  5309. return lerp(fz,
  5310. lerp(fy, lerp(fx, grad3d(perm[p00], x, y, z), grad3d(perm[p10], x-1, y, z)),
  5311. lerp(fx, grad3d(perm[p01], x, y-1, z), grad3d(perm[p11], x-1, y-1,z))),
  5312. lerp(fy, lerp(fx, grad3d(perm[p00 + 1], x, y, z-1), grad3d(perm[p10 + 1], x-1, y, z-1)),
  5313. lerp(fx, grad3d(perm[p01 + 1], x, y-1, z-1), grad3d(perm[p11 + 1], x-1, y-1,z-1))));
  5314. };
  5315. this.noise2d = function(x, y) {
  5316. var X = Math.floor(x)&255, Y = Math.floor(y)&255;
  5317. x -= Math.floor(x); y -= Math.floor(y);
  5318. var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y;
  5319. var p0 = perm[X]+Y, p1 = perm[X + 1] + Y;
  5320. return lerp(fy,
  5321. lerp(fx, grad2d(perm[p0], x, y), grad2d(perm[p1], x-1, y)),
  5322. lerp(fx, grad2d(perm[p0 + 1], x, y-1), grad2d(perm[p1 + 1], x-1, y-1)));
  5323. };
  5324. this.noise1d = function(x) {
  5325. var X = Math.floor(x)&255;
  5326. x -= Math.floor(x);
  5327. var fx = (3-2*x)*x*x;
  5328. return lerp(fx, grad1d(perm[X], x), grad1d(perm[X+1], x-1));
  5329. };
  5330. }
  5331. // processing defaults
  5332. var noiseProfile = { generator: undef, octaves: 4, fallout: 0.5, seed: undef};
  5333. p.noise = function(x, y, z) {
  5334. if(noiseProfile.generator === undef) {
  5335. // caching
  5336. noiseProfile.generator = new PerlinNoise(noiseProfile.seed);
  5337. }
  5338. var generator = noiseProfile.generator;
  5339. var effect = 1, k = 1, sum = 0;
  5340. for(var i=0; i<noiseProfile.octaves; ++i) {
  5341. effect *= noiseProfile.fallout;
  5342. switch (arguments.length) {
  5343. case 1:
  5344. sum += effect * (1 + generator.noise1d(k*x))/2; break;
  5345. case 2:
  5346. sum += effect * (1 + generator.noise2d(k*x, k*y))/2; break;
  5347. case 3:
  5348. sum += effect * (1 + generator.noise3d(k*x, k*y, k*z))/2; break;
  5349. }
  5350. k *= 2;
  5351. }
  5352. return sum;
  5353. };
  5354. p.noiseDetail = function(octaves, fallout) {
  5355. noiseProfile.octaves = octaves;
  5356. if(fallout !== undef) {
  5357. noiseProfile.fallout = fallout;
  5358. }
  5359. };
  5360. p.noiseSeed = function(seed) {
  5361. noiseProfile.seed = seed;
  5362. noiseProfile.generator = undef;
  5363. };
  5364. // Set default background behavior for 2D and 3D contexts
  5365. var refreshBackground = function() {
  5366. if (curSketch.options.isOpaque) {
  5367. if (p.use3DContext) {
  5368. // fill background default opaque gray
  5369. curContext.clearColor(204 / 255, 204 / 255, 204 / 255, 1.0);
  5370. curContext.clear(curContext.COLOR_BUFFER_BIT | curContext.DEPTH_BUFFER_BIT);
  5371. } else {
  5372. // fill background default opaque gray
  5373. curContext.fillStyle = "rgb(204, 204, 204)";
  5374. curContext.fillRect(0, 0, p.width, p.height);
  5375. isFillDirty = true;
  5376. }
  5377. }
  5378. };
  5379. // Changes the size of the Canvas ( this resets context properties like 'lineCap', etc.
  5380. p.size = function size(aWidth, aHeight, aMode) {
  5381. if (aMode && (aMode === p.WEBGL)) {
  5382. // get the 3D rendering context
  5383. try {
  5384. // If the HTML <canvas> dimensions differ from the
  5385. // dimensions specified in the size() call in the sketch, for
  5386. // 3D sketches, browsers will either not render or render the
  5387. // scene incorrectly. To fix this, we need to adjust the
  5388. // width and height attributes of the canvas.
  5389. if (curElement.width !== aWidth || curElement.height !== aHeight) {
  5390. curElement.setAttribute("width", aWidth);
  5391. curElement.setAttribute("height", aHeight);
  5392. }
  5393. curContext = curElement.getContext("experimental-webgl");
  5394. p.use3DContext = true;
  5395. } catch(e_size) {
  5396. Processing.debug(e_size);
  5397. }
  5398. if (!curContext) {
  5399. throw "OPENGL 3D context is not supported on this browser.";
  5400. } else {
  5401. for (var i = 0; i < p.SINCOS_LENGTH; i++) {
  5402. sinLUT[i] = p.sin(i * (p.PI / 180) * 0.5);
  5403. cosLUT[i] = p.cos(i * (p.PI / 180) * 0.5);
  5404. }
  5405. // Set defaults
  5406. curContext.viewport(0, 0, curElement.width, curElement.height);
  5407. curContext.enable(curContext.DEPTH_TEST);
  5408. curContext.enable(curContext.BLEND);
  5409. curContext.blendFunc(curContext.SRC_ALPHA, curContext.ONE_MINUS_SRC_ALPHA);
  5410. refreshBackground(); // sets clearColor default;
  5411. // We declare our own constants since Minefield doesn't
  5412. // do anything when curContext.VERTEX_PROGRAM_POINT_SIZE is used.
  5413. curContext.enable(VERTEX_PROGRAM_POINT_SIZE);
  5414. curContext.enable(POINT_SMOOTH);
  5415. // Create the program objects to render 2D (points, lines) and
  5416. // 3D (spheres, boxes) shapes. Because 2D shapes are not lit,
  5417. // lighting calculations could be ommitted from that program object.
  5418. programObject2D = createProgramObject(curContext, vertexShaderSource2D, fragmentShaderSource2D);
  5419. // set the defaults
  5420. curContext.useProgram(programObject2D);
  5421. p.strokeWeight(1.0);
  5422. programObject3D = createProgramObject(curContext, vertexShaderSource3D, fragmentShaderSource3D);
  5423. programObjectUnlitShape = createProgramObject(curContext, vShaderSrcUnlitShape, fShaderSrcUnlitShape);
  5424. // Now that the programs have been compiled, we can set the default
  5425. // states for the lights.
  5426. curContext.useProgram(programObject3D);
  5427. // assume we aren't using textures by default
  5428. uniformi(programObject3D, "usingTexture", usingTexture);
  5429. p.lightFalloff(1, 0, 0);
  5430. p.shininess(1);
  5431. p.ambient(255, 255, 255);
  5432. p.specular(0, 0, 0);
  5433. // Create buffers for 3D primitives
  5434. boxBuffer = curContext.createBuffer();
  5435. curContext.bindBuffer(curContext.ARRAY_BUFFER, boxBuffer);
  5436. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(boxVerts), curContext.STATIC_DRAW);
  5437. boxNormBuffer = curContext.createBuffer();
  5438. curContext.bindBuffer(curContext.ARRAY_BUFFER, boxNormBuffer);
  5439. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(boxNorms), curContext.STATIC_DRAW);
  5440. boxOutlineBuffer = curContext.createBuffer();
  5441. curContext.bindBuffer(curContext.ARRAY_BUFFER, boxOutlineBuffer);
  5442. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(boxOutlineVerts), curContext.STATIC_DRAW);
  5443. // used to draw the rectangle and the outline
  5444. rectBuffer = curContext.createBuffer();
  5445. curContext.bindBuffer(curContext.ARRAY_BUFFER, rectBuffer);
  5446. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(rectVerts), curContext.STATIC_DRAW);
  5447. rectNormBuffer = curContext.createBuffer();
  5448. curContext.bindBuffer(curContext.ARRAY_BUFFER, rectNormBuffer);
  5449. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(rectNorms), curContext.STATIC_DRAW);
  5450. // The sphere vertices are specified dynamically since the user
  5451. // can change the level of detail. Everytime the user does that
  5452. // using sphereDetail(), the new vertices are calculated.
  5453. sphereBuffer = curContext.createBuffer();
  5454. lineBuffer = curContext.createBuffer();
  5455. // Shape buffers
  5456. fillBuffer = curContext.createBuffer();
  5457. fillColorBuffer = curContext.createBuffer();
  5458. strokeColorBuffer = curContext.createBuffer();
  5459. shapeTexVBO = curContext.createBuffer();
  5460. pointBuffer = curContext.createBuffer();
  5461. curContext.bindBuffer(curContext.ARRAY_BUFFER, pointBuffer);
  5462. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray([0, 0, 0]), curContext.STATIC_DRAW);
  5463. textBuffer = curContext.createBuffer();
  5464. curContext.bindBuffer(curContext.ARRAY_BUFFER, textBuffer );
  5465. curContext.bufferData(curContext.ARRAY_BUFFER, new WebGLFloatArray([1,1,0,-1,1,0,-1,-1,0,1,-1,0]), curContext.STATIC_DRAW);
  5466. textureBuffer = curContext.createBuffer();
  5467. curContext.bindBuffer(curContext.ARRAY_BUFFER, textureBuffer);
  5468. curContext.bufferData(curContext.ARRAY_BUFFER, new WebGLFloatArray([0,0,1,0,1,1,0,1]), curContext.STATIC_DRAW);
  5469. indexBuffer = curContext.createBuffer();
  5470. curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer);
  5471. curContext.bufferData(curContext.ELEMENT_ARRAY_BUFFER, new WebGLUnsignedShortArray([0,1,2,2,3,0]), curContext.STATIC_DRAW);
  5472. cam = new PMatrix3D();
  5473. cameraInv = new PMatrix3D();
  5474. forwardTransform = new PMatrix3D();
  5475. reverseTransform = new PMatrix3D();
  5476. modelView = new PMatrix3D();
  5477. modelViewInv = new PMatrix3D();
  5478. projection = new PMatrix3D();
  5479. p.camera();
  5480. p.perspective();
  5481. forwardTransform = modelView;
  5482. reverseTransform = modelViewInv;
  5483. userMatrixStack = new PMatrixStack();
  5484. // used by both curve and bezier, so just init here
  5485. curveBasisMatrix = new PMatrix3D();
  5486. curveToBezierMatrix = new PMatrix3D();
  5487. curveDrawMatrix = new PMatrix3D();
  5488. bezierDrawMatrix = new PMatrix3D();
  5489. bezierBasisInverse = new PMatrix3D();
  5490. bezierBasisMatrix = new PMatrix3D();
  5491. bezierBasisMatrix.set(-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0);
  5492. }
  5493. p.stroke(0);
  5494. p.fill(255);
  5495. } else {
  5496. if (curContext === undef) {
  5497. // size() was called without p.init() default context, ie. p.createGraphics()
  5498. curContext = curElement.getContext("2d");
  5499. p.use3DContext = false;
  5500. userMatrixStack = new PMatrixStack();
  5501. modelView = new PMatrix2D();
  5502. }
  5503. }
  5504. // The default 2d context has already been created in the p.init() stage if
  5505. // a 3d context was not specified. This is so that a 2d context will be
  5506. // available if size() was not called.
  5507. var props = {
  5508. fillStyle: curContext.fillStyle,
  5509. strokeStyle: curContext.strokeStyle,
  5510. lineCap: curContext.lineCap,
  5511. lineJoin: curContext.lineJoin
  5512. };
  5513. curElement.width = p.width = aWidth || 100;
  5514. curElement.height = p.height = aHeight || 100;
  5515. for (var j in props) {
  5516. if (props) {
  5517. curContext[j] = props[j];
  5518. }
  5519. }
  5520. // redraw the background if background was called before size
  5521. refreshBackground();
  5522. // set 5% for pixels to cache (or 1000)
  5523. maxPixelsCached = Math.max(1000, aWidth * aHeight * 0.05);
  5524. // Externalize the context
  5525. p.externals.context = curContext;
  5526. p.toImageData = function() {
  5527. if(!p.use3DContext){
  5528. return curContext.getImageData(0, 0, this.width, this.height);
  5529. } else {
  5530. var c = document.createElement("canvas");
  5531. var ctx = c.getContext("2d");
  5532. var obj = ctx.createImageData(this.width, this.height);
  5533. var uBuff = curContext.readPixels(0,0,this.width,this.height,curContext.RGBA,curContext.UNSIGNED_BYTE);
  5534. if(!uBuff){
  5535. uBuff = new WebGLUnsignedByteArray(this.width * this.height * 4);
  5536. curContext.readPixels(0,0,this.width,this.height,curContext.RGBA,curContext.UNSIGNED_BYTE, uBuff);
  5537. }
  5538. for(var i =0; i < uBuff.length; i++){
  5539. obj.data[i] = uBuff[(this.height - 1 - Math.floor(i / 4 / this.width)) * this.width * 4 + (i % (this.width * 4))];
  5540. }
  5541. return obj;
  5542. }
  5543. };
  5544. };
  5545. ////////////////////////////////////////////////////////////////////////////
  5546. // Lights
  5547. ////////////////////////////////////////////////////////////////////////////
  5548. p.ambientLight = function(r, g, b, x, y, z) {
  5549. if (p.use3DContext) {
  5550. if (lightCount === p.MAX_LIGHTS) {
  5551. throw "can only create " + p.MAX_LIGHTS + " lights";
  5552. }
  5553. var pos = new PVector(x, y, z);
  5554. var view = new PMatrix3D();
  5555. view.scale(1, -1, 1);
  5556. view.apply(modelView.array());
  5557. view.mult(pos, pos);
  5558. curContext.useProgram(programObject3D);
  5559. uniformf(programObject3D, "lights[" + lightCount + "].color", [r / 255, g / 255, b / 255]);
  5560. uniformf(programObject3D, "lights[" + lightCount + "].position", pos.array());
  5561. uniformi(programObject3D, "lights[" + lightCount + "].type", 0);
  5562. uniformi(programObject3D, "lightCount", ++lightCount);
  5563. }
  5564. };
  5565. p.directionalLight = function(r, g, b, nx, ny, nz) {
  5566. if (p.use3DContext) {
  5567. if (lightCount === p.MAX_LIGHTS) {
  5568. throw "can only create " + p.MAX_LIGHTS + " lights";
  5569. }
  5570. curContext.useProgram(programObject3D);
  5571. // Less code than manually multiplying, but I'll fix
  5572. // this when I have more time.
  5573. var dir = [nx, ny, nz, 0.0000001];
  5574. var view = new PMatrix3D();
  5575. view.scale(1, -1, 1);
  5576. view.apply(modelView.array());
  5577. view.mult(dir, dir);
  5578. uniformf(programObject3D, "lights[" + lightCount + "].color", [r / 255, g / 255, b / 255]);
  5579. uniformf(programObject3D, "lights[" + lightCount + "].position", [-dir[0], -dir[1], -dir[2]]);
  5580. uniformi(programObject3D, "lights[" + lightCount + "].type", 1);
  5581. uniformi(programObject3D, "lightCount", ++lightCount);
  5582. }
  5583. };
  5584. p.lightFalloff = function lightFalloff(constant, linear, quadratic) {
  5585. if (p.use3DContext) {
  5586. curContext.useProgram(programObject3D);
  5587. uniformf(programObject3D, "falloff", [constant, linear, quadratic]);
  5588. }
  5589. };
  5590. p.lightSpecular = function lightSpecular(r, g, b) {
  5591. if (p.use3DContext) {
  5592. curContext.useProgram(programObject3D);
  5593. uniformf(programObject3D, "specular", [r / 255, g / 255, b / 255]);
  5594. }
  5595. };
  5596. /*
  5597. Sets the default ambient light, directional light,
  5598. falloff, and specular values. P5 Documentation says specular()
  5599. is set, but the code calls lightSpecular().
  5600. */
  5601. p.lights = function lights() {
  5602. p.ambientLight(128, 128, 128);
  5603. p.directionalLight(128, 128, 128, 0, 0, -1);
  5604. p.lightFalloff(1, 0, 0);
  5605. p.lightSpecular(0, 0, 0);
  5606. };
  5607. p.pointLight = function(r, g, b, x, y, z) {
  5608. if (p.use3DContext) {
  5609. if (lightCount === p.MAX_LIGHTS) {
  5610. throw "can only create " + p.MAX_LIGHTS + " lights";
  5611. }
  5612. // place the point in view space once instead of once per vertex
  5613. // in the shader.
  5614. var pos = new PVector(x, y, z);
  5615. var view = new PMatrix3D();
  5616. view.scale(1, -1, 1);
  5617. view.apply(modelView.array());
  5618. view.mult(pos, pos);
  5619. curContext.useProgram(programObject3D);
  5620. uniformf(programObject3D, "lights[" + lightCount + "].color", [r / 255, g / 255, b / 255]);
  5621. uniformf(programObject3D, "lights[" + lightCount + "].position", pos.array());
  5622. uniformi(programObject3D, "lights[" + lightCount + "].type", 2);
  5623. uniformi(programObject3D, "lightCount", ++lightCount);
  5624. }
  5625. };
  5626. /*
  5627. Disables lighting so the all shapes drawn after this
  5628. will not be lit.
  5629. */
  5630. p.noLights = function noLights() {
  5631. if (p.use3DContext) {
  5632. lightCount = 0;
  5633. curContext.useProgram(programObject3D);
  5634. uniformi(programObject3D, "lightCount", lightCount);
  5635. }
  5636. };
  5637. /*
  5638. r,g,b - Color of the light
  5639. x,y,z - position of the light in modeling space
  5640. nx,ny,nz - direction of the spotlight
  5641. angle - in radians
  5642. concentration -
  5643. */
  5644. p.spotLight = function spotLight(r, g, b, x, y, z, nx, ny, nz, angle, concentration) {
  5645. if (p.use3DContext) {
  5646. if (lightCount === p.MAX_LIGHTS) {
  5647. throw "can only create " + p.MAX_LIGHTS + " lights";
  5648. }
  5649. curContext.useProgram(programObject3D);
  5650. // place the point in view space once instead of once per vertex
  5651. // in the shader.
  5652. var pos = new PVector(x, y, z);
  5653. var view = new PMatrix3D();
  5654. view.scale(1, -1, 1);
  5655. view.apply(modelView.array());
  5656. view.mult(pos, pos);
  5657. // transform the spotlight's direction
  5658. // need to find a solution for this one. Maybe manual mult?
  5659. var dir = [nx, ny, nz, 0.0000001];
  5660. view = new PMatrix3D();
  5661. view.scale(1, -1, 1);
  5662. view.apply(modelView.array());
  5663. view.mult(dir, dir);
  5664. uniformf(programObject3D, "lights[" + lightCount + "].color", [r / 255, g / 255, b / 255]);
  5665. uniformf(programObject3D, "lights[" + lightCount + "].position", pos.array());
  5666. uniformf(programObject3D, "lights[" + lightCount + "].direction", [dir[0], dir[1], dir[2]]);
  5667. uniformf(programObject3D, "lights[" + lightCount + "].concentration", concentration);
  5668. uniformf(programObject3D, "lights[" + lightCount + "].angle", angle);
  5669. uniformi(programObject3D, "lights[" + lightCount + "].type", 3);
  5670. uniformi(programObject3D, "lightCount", ++lightCount);
  5671. }
  5672. };
  5673. ////////////////////////////////////////////////////////////////////////////
  5674. // Camera functions
  5675. ////////////////////////////////////////////////////////////////////////////
  5676. p.beginCamera = function beginCamera() {
  5677. if (manipulatingCamera) {
  5678. throw ("You cannot call beginCamera() again before calling endCamera()");
  5679. } else {
  5680. manipulatingCamera = true;
  5681. forwardTransform = cameraInv;
  5682. reverseTransform = cam;
  5683. }
  5684. };
  5685. p.endCamera = function endCamera() {
  5686. if (!manipulatingCamera) {
  5687. throw ("You cannot call endCamera() before calling beginCamera()");
  5688. } else {
  5689. modelView.set(cam);
  5690. modelViewInv.set(cameraInv);
  5691. forwardTransform = modelView;
  5692. reverseTransform = modelViewInv;
  5693. manipulatingCamera = false;
  5694. }
  5695. };
  5696. p.camera = function camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
  5697. if (arguments.length === 0) {
  5698. //in case canvas is resized
  5699. cameraX = curElement.width / 2;
  5700. cameraY = curElement.height / 2;
  5701. cameraZ = cameraY / Math.tan(cameraFOV / 2);
  5702. eyeX = cameraX;
  5703. eyeY = cameraY;
  5704. eyeZ = cameraZ;
  5705. centerX = cameraX;
  5706. centerY = cameraY;
  5707. centerZ = 0;
  5708. upX = 0;
  5709. upY = 1;
  5710. upZ = 0;
  5711. }
  5712. var z = new p.PVector(eyeX - centerX, eyeY - centerY, eyeZ - centerZ);
  5713. var y = new p.PVector(upX, upY, upZ);
  5714. var transX, transY, transZ;
  5715. z.normalize();
  5716. var x = p.PVector.cross(y, z);
  5717. y = p.PVector.cross(z, x);
  5718. x.normalize();
  5719. y.normalize();
  5720. cam.set(x.x, x.y, x.z, 0, y.x, y.y, y.z, 0, z.x, z.y, z.z, 0, 0, 0, 0, 1);
  5721. cam.translate(-eyeX, -eyeY, -eyeZ);
  5722. cameraInv.reset();
  5723. cameraInv.invApply(x.x, x.y, x.z, 0, y.x, y.y, y.z, 0, z.x, z.y, z.z, 0, 0, 0, 0, 1);
  5724. cameraInv.translate(eyeX, eyeY, eyeZ);
  5725. modelView.set(cam);
  5726. modelViewInv.set(cameraInv);
  5727. };
  5728. p.perspective = function perspective(fov, aspect, near, far) {
  5729. if (arguments.length === 0) {
  5730. //in case canvas is resized
  5731. cameraY = curElement.height / 2;
  5732. cameraZ = cameraY / Math.tan(cameraFOV / 2);
  5733. cameraNear = cameraZ / 10;
  5734. cameraFar = cameraZ * 10;
  5735. cameraAspect = curElement.width / curElement.height;
  5736. fov = cameraFOV;
  5737. aspect = cameraAspect;
  5738. near = cameraNear;
  5739. far = cameraFar;
  5740. }
  5741. var yMax, yMin, xMax, xMin;
  5742. yMax = near * Math.tan(fov / 2);
  5743. yMin = -yMax;
  5744. xMax = yMax * aspect;
  5745. xMin = yMin * aspect;
  5746. p.frustum(xMin, xMax, yMin, yMax, near, far);
  5747. };
  5748. p.frustum = function frustum(left, right, bottom, top, near, far) {
  5749. frustumMode = true;
  5750. projection = new PMatrix3D();
  5751. projection.set((2 * near) / (right - left), 0, (right + left) / (right - left),
  5752. 0, 0, (2 * near) / (top - bottom), (top + bottom) / (top - bottom),
  5753. 0, 0, 0, -(far + near) / (far - near), -(2 * far * near) / (far - near),
  5754. 0, 0, -1, 0);
  5755. };
  5756. p.ortho = function ortho(left, right, bottom, top, near, far) {
  5757. if (arguments.length === 0) {
  5758. left = 0;
  5759. right = p.width;
  5760. bottom = 0;
  5761. top = p.height;
  5762. near = -10;
  5763. far = 10;
  5764. }
  5765. var x = 2 / (right - left);
  5766. var y = 2 / (top - bottom);
  5767. var z = -2 / (far - near);
  5768. var tx = -(right + left) / (right - left);
  5769. var ty = -(top + bottom) / (top - bottom);
  5770. var tz = -(far + near) / (far - near);
  5771. projection = new PMatrix3D();
  5772. projection.set(x, 0, 0, tx, 0, y, 0, ty, 0, 0, z, tz, 0, 0, 0, 1);
  5773. frustumMode = false;
  5774. };
  5775. p.printProjection = function() {
  5776. projection.print();
  5777. };
  5778. p.printCamera = function() {
  5779. cam.print();
  5780. };
  5781. ////////////////////////////////////////////////////////////////////////////
  5782. // Shapes
  5783. ////////////////////////////////////////////////////////////////////////////
  5784. p.box = function(w, h, d) {
  5785. if (p.use3DContext) {
  5786. // user can uniformly scale the box by
  5787. // passing in only one argument.
  5788. if (!h || !d) {
  5789. h = d = w;
  5790. }
  5791. // Modeling transformation
  5792. var model = new PMatrix3D();
  5793. model.scale(w, h, d);
  5794. model.transpose();
  5795. // viewing transformation needs to have Y flipped
  5796. // becuase that's what Processing does.
  5797. var view = new PMatrix3D();
  5798. view.scale(1, -1, 1);
  5799. view.apply(modelView.array());
  5800. view.transpose();
  5801. var proj = new PMatrix3D();
  5802. proj.set(projection);
  5803. proj.transpose();
  5804. if (doFill === true) {
  5805. curContext.useProgram(programObject3D);
  5806. disableVertexAttribPointer(programObject3D, "aTexture");
  5807. uniformMatrix(programObject3D, "model", false, model.array());
  5808. uniformMatrix(programObject3D, "view", false, view.array());
  5809. uniformMatrix(programObject3D, "projection", false, proj.array());
  5810. // fix stitching problems. (lines get occluded by triangles
  5811. // since they share the same depth values). This is not entirely
  5812. // working, but it's a start for drawing the outline. So
  5813. // developers can start playing around with styles.
  5814. curContext.enable(curContext.POLYGON_OFFSET_FILL);
  5815. curContext.polygonOffset(1, 1);
  5816. uniformf(programObject3D, "color", fillStyle);
  5817. // Create the normal transformation matrix
  5818. var v = new PMatrix3D();
  5819. v.set(view);
  5820. var m = new PMatrix3D();
  5821. m.set(model);
  5822. v.mult(m);
  5823. var normalMatrix = new PMatrix3D();
  5824. normalMatrix.set(v);
  5825. normalMatrix.invert();
  5826. uniformMatrix(programObject3D, "normalTransform", false, normalMatrix.array());
  5827. vertexAttribPointer(programObject3D, "Vertex", 3, boxBuffer);
  5828. vertexAttribPointer(programObject3D, "Normal", 3, boxNormBuffer);
  5829. // Ugly hack. Can't simply disable the vertex attribute
  5830. // array. No idea why, so I'm passing in dummy data.
  5831. vertexAttribPointer(programObject3D, "aColor", 3, boxNormBuffer);
  5832. curContext.drawArrays(curContext.TRIANGLES, 0, boxVerts.length / 3);
  5833. curContext.disable(curContext.POLYGON_OFFSET_FILL);
  5834. }
  5835. if (lineWidth > 0 && doStroke) {
  5836. curContext.useProgram(programObject2D);
  5837. uniformMatrix(programObject2D, "model", false, model.array());
  5838. uniformMatrix(programObject2D, "view", false, view.array());
  5839. uniformMatrix(programObject2D, "projection", false, proj.array());
  5840. uniformf(programObject2D, "color", strokeStyle);
  5841. uniformi(programObject2D, "picktype", 0);
  5842. vertexAttribPointer(programObject2D, "Vertex", 3, boxOutlineBuffer);
  5843. disableVertexAttribPointer(programObject2D, "aTextureCoord");
  5844. curContext.lineWidth(lineWidth);
  5845. curContext.drawArrays(curContext.LINES, 0, boxOutlineVerts.length / 3);
  5846. }
  5847. }
  5848. };
  5849. var initSphere = function() {
  5850. var i;
  5851. sphereVerts = [];
  5852. for (i = 0; i < sphereDetailU; i++) {
  5853. sphereVerts.push(0);
  5854. sphereVerts.push(-1);
  5855. sphereVerts.push(0);
  5856. sphereVerts.push(sphereX[i]);
  5857. sphereVerts.push(sphereY[i]);
  5858. sphereVerts.push(sphereZ[i]);
  5859. }
  5860. sphereVerts.push(0);
  5861. sphereVerts.push(-1);
  5862. sphereVerts.push(0);
  5863. sphereVerts.push(sphereX[0]);
  5864. sphereVerts.push(sphereY[0]);
  5865. sphereVerts.push(sphereZ[0]);
  5866. var v1, v11, v2;
  5867. // middle rings
  5868. var voff = 0;
  5869. for (i = 2; i < sphereDetailV; i++) {
  5870. v1 = v11 = voff;
  5871. voff += sphereDetailU;
  5872. v2 = voff;
  5873. for (var j = 0; j < sphereDetailU; j++) {
  5874. sphereVerts.push(parseFloat(sphereX[v1]));
  5875. sphereVerts.push(parseFloat(sphereY[v1]));
  5876. sphereVerts.push(parseFloat(sphereZ[v1++]));
  5877. sphereVerts.push(parseFloat(sphereX[v2]));
  5878. sphereVerts.push(parseFloat(sphereY[v2]));
  5879. sphereVerts.push(parseFloat(sphereZ[v2++]));
  5880. }
  5881. // close each ring
  5882. v1 = v11;
  5883. v2 = voff;
  5884. sphereVerts.push(parseFloat(sphereX[v1]));
  5885. sphereVerts.push(parseFloat(sphereY[v1]));
  5886. sphereVerts.push(parseFloat(sphereZ[v1]));
  5887. sphereVerts.push(parseFloat(sphereX[v2]));
  5888. sphereVerts.push(parseFloat(sphereY[v2]));
  5889. sphereVerts.push(parseFloat(sphereZ[v2]));
  5890. }
  5891. // add the northern cap
  5892. for (i = 0; i < sphereDetailU; i++) {
  5893. v2 = voff + i;
  5894. sphereVerts.push(parseFloat(sphereX[v2]));
  5895. sphereVerts.push(parseFloat(sphereY[v2]));
  5896. sphereVerts.push(parseFloat(sphereZ[v2]));
  5897. sphereVerts.push(0);
  5898. sphereVerts.push(1);
  5899. sphereVerts.push(0);
  5900. }
  5901. sphereVerts.push(parseFloat(sphereX[voff]));
  5902. sphereVerts.push(parseFloat(sphereY[voff]));
  5903. sphereVerts.push(parseFloat(sphereZ[voff]));
  5904. sphereVerts.push(0);
  5905. sphereVerts.push(1);
  5906. sphereVerts.push(0);
  5907. //set the buffer data
  5908. curContext.bindBuffer(curContext.ARRAY_BUFFER, sphereBuffer);
  5909. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(sphereVerts), curContext.STATIC_DRAW);
  5910. };
  5911. p.sphereDetail = function sphereDetail(ures, vres) {
  5912. var i;
  5913. if (arguments.length === 1) {
  5914. ures = vres = arguments[0];
  5915. }
  5916. if (ures < 3) {
  5917. ures = 3;
  5918. } // force a minimum res
  5919. if (vres < 2) {
  5920. vres = 2;
  5921. } // force a minimum res
  5922. // if it hasn't changed do nothing
  5923. if ((ures === sphereDetailU) && (vres === sphereDetailV)) {
  5924. return;
  5925. }
  5926. var delta = p.SINCOS_LENGTH / ures;
  5927. var cx = new Array(ures);
  5928. var cz = new Array(ures);
  5929. // calc unit circle in XZ plane
  5930. for (i = 0; i < ures; i++) {
  5931. cx[i] = cosLUT[parseInt((i * delta) % p.SINCOS_LENGTH, 10)];
  5932. cz[i] = sinLUT[parseInt((i * delta) % p.SINCOS_LENGTH, 10)];
  5933. }
  5934. // computing vertexlist
  5935. // vertexlist starts at south pole
  5936. var vertCount = ures * (vres - 1) + 2;
  5937. var currVert = 0;
  5938. // re-init arrays to store vertices
  5939. sphereX = new Array(vertCount);
  5940. sphereY = new Array(vertCount);
  5941. sphereZ = new Array(vertCount);
  5942. var angle_step = (p.SINCOS_LENGTH * 0.5) / vres;
  5943. var angle = angle_step;
  5944. // step along Y axis
  5945. for (i = 1; i < vres; i++) {
  5946. var curradius = sinLUT[parseInt(angle % p.SINCOS_LENGTH, 10)];
  5947. var currY = -cosLUT[parseInt(angle % p.SINCOS_LENGTH, 10)];
  5948. for (var j = 0; j < ures; j++) {
  5949. sphereX[currVert] = cx[j] * curradius;
  5950. sphereY[currVert] = currY;
  5951. sphereZ[currVert++] = cz[j] * curradius;
  5952. }
  5953. angle += angle_step;
  5954. }
  5955. sphereDetailU = ures;
  5956. sphereDetailV = vres;
  5957. // make the sphere verts and norms
  5958. initSphere();
  5959. };
  5960. p.sphere = function() {
  5961. if (p.use3DContext) {
  5962. var sRad = arguments[0], c;
  5963. if ((sphereDetailU < 3) || (sphereDetailV < 2)) {
  5964. p.sphereDetail(30);
  5965. }
  5966. // Modeling transformation
  5967. var model = new PMatrix3D();
  5968. model.scale(sRad, sRad, sRad);
  5969. // viewing transformation needs to have Y flipped
  5970. // becuase that's what Processing does.
  5971. var view = new PMatrix3D();
  5972. view.scale(1, -1, 1);
  5973. view.apply(modelView.array());
  5974. view.transpose();
  5975. var proj = new PMatrix3D();
  5976. proj.set(projection);
  5977. proj.transpose();
  5978. if (doFill === true) {
  5979. // Create a normal transformation matrix
  5980. var v = new PMatrix3D();
  5981. v.set(view);
  5982. var m = new PMatrix3D();
  5983. m.set(model);
  5984. v.mult(m);
  5985. var normalMatrix = new PMatrix3D();
  5986. normalMatrix.set(v);
  5987. normalMatrix.invert();
  5988. curContext.useProgram(programObject3D);
  5989. disableVertexAttribPointer(programObject3D, "aTexture");
  5990. uniformMatrix(programObject3D, "model", false, model.array());
  5991. uniformMatrix(programObject3D, "view", false, view.array());
  5992. uniformMatrix(programObject3D, "projection", false, proj.array());
  5993. uniformMatrix(programObject3D, "normalTransform", false, normalMatrix.array());
  5994. vertexAttribPointer(programObject3D, "Vertex", 3, sphereBuffer);
  5995. vertexAttribPointer(programObject3D, "Normal", 3, sphereBuffer);
  5996. // Ugly hack. Can't simply disable the vertex attribute
  5997. // array. No idea why, so I'm passing in dummy data.
  5998. vertexAttribPointer(programObject3D, "aColor", 3, sphereBuffer);
  5999. // fix stitching problems. (lines get occluded by triangles
  6000. // since they share the same depth values). This is not entirely
  6001. // working, but it's a start for drawing the outline. So
  6002. // developers can start playing around with styles.
  6003. curContext.enable(curContext.POLYGON_OFFSET_FILL);
  6004. curContext.polygonOffset(1, 1);
  6005. uniformf(programObject3D, "color", fillStyle);
  6006. curContext.drawArrays(curContext.TRIANGLE_STRIP, 0, sphereVerts.length / 3);
  6007. curContext.disable(curContext.POLYGON_OFFSET_FILL);
  6008. }
  6009. if (lineWidth > 0 && doStroke) {
  6010. curContext.useProgram(programObject2D);
  6011. uniformMatrix(programObject2D, "model", false, model.array());
  6012. uniformMatrix(programObject2D, "view", false, view.array());
  6013. uniformMatrix(programObject2D, "projection", false, proj.array());
  6014. vertexAttribPointer(programObject2D, "Vertex", 3, sphereBuffer);
  6015. disableVertexAttribPointer(programObject2D, "aTextureCoord");
  6016. uniformf(programObject2D, "color", strokeStyle);
  6017. uniformi(programObject2D, "picktype", 0);
  6018. curContext.lineWidth(lineWidth);
  6019. curContext.drawArrays(curContext.LINE_STRIP, 0, sphereVerts.length / 3);
  6020. }
  6021. }
  6022. };
  6023. ////////////////////////////////////////////////////////////////////////////
  6024. // Coordinates
  6025. ////////////////////////////////////////////////////////////////////////////
  6026. p.modelX = function modelX(x, y, z) {
  6027. var mv = modelView.array();
  6028. var ci = cameraInv.array();
  6029. var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
  6030. var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
  6031. var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
  6032. var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
  6033. var ox = ci[0] * ax + ci[1] * ay + ci[2] * az + ci[3] * aw;
  6034. var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
  6035. return (ow !== 0) ? ox / ow : ox;
  6036. };
  6037. p.modelY = function modelY(x, y, z) {
  6038. var mv = modelView.array();
  6039. var ci = cameraInv.array();
  6040. var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
  6041. var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
  6042. var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
  6043. var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
  6044. var oy = ci[4] * ax + ci[5] * ay + ci[6] * az + ci[7] * aw;
  6045. var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
  6046. return (ow !== 0) ? oy / ow : oy;
  6047. };
  6048. p.modelZ = function modelZ(x, y, z) {
  6049. var mv = modelView.array();
  6050. var ci = cameraInv.array();
  6051. var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
  6052. var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
  6053. var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
  6054. var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
  6055. var oz = ci[8] * ax + ci[9] * ay + ci[10] * az + ci[11] * aw;
  6056. var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
  6057. return (ow !== 0) ? oz / ow : oz;
  6058. };
  6059. ////////////////////////////////////////////////////////////////////////////
  6060. // Material Properties
  6061. ////////////////////////////////////////////////////////////////////////////
  6062. p.ambient = function ambient() {
  6063. // create an alias to shorten code
  6064. var a = arguments;
  6065. // either a shade of gray or a 'color' object.
  6066. if (p.use3DContext) {
  6067. curContext.useProgram(programObject3D);
  6068. uniformi(programObject3D, "usingMat", true);
  6069. if (a.length === 1) {
  6070. // color object was passed in
  6071. if (typeof a[0] === "string") {
  6072. var c = a[0].slice(5, -1).split(",");
  6073. uniformf(programObject3D, "mat_ambient", [c[0] / 255, c[1] / 255, c[2] / 255]);
  6074. }
  6075. // else a single number was passed in for gray shade
  6076. else {
  6077. uniformf(programObject3D, "mat_ambient", [a[0] / 255, a[0] / 255, a[0] / 255]);
  6078. }
  6079. }
  6080. // Otherwise three values were provided (r,g,b)
  6081. else {
  6082. uniformf(programObject3D, "mat_ambient", [a[0] / 255, a[1] / 255, a[2] / 255]);
  6083. }
  6084. }
  6085. };
  6086. p.emissive = function emissive() {
  6087. // create an alias to shorten code
  6088. var a = arguments;
  6089. if (p.use3DContext) {
  6090. curContext.useProgram(programObject3D);
  6091. uniformi(programObject3D, "usingMat", true);
  6092. // If only one argument was provided, the user either gave us a
  6093. // shade of gray or a 'color' object.
  6094. if (a.length === 1) {
  6095. // color object was passed in
  6096. if (typeof a[0] === "string") {
  6097. var c = a[0].slice(5, -1).split(",");
  6098. uniformf(programObject3D, "mat_emissive", [c[0] / 255, c[1] / 255, c[2] / 255]);
  6099. }
  6100. // else a regular number was passed in for gray shade
  6101. else {
  6102. uniformf(programObject3D, "mat_emissive", [a[0] / 255, a[0] / 255, a[0] / 255]);
  6103. }
  6104. }
  6105. // Otherwise three values were provided (r,g,b)
  6106. else {
  6107. uniformf(programObject3D, "mat_emissive", [a[0] / 255, a[1] / 255, a[2] / 255]);
  6108. }
  6109. }
  6110. };
  6111. p.shininess = function shininess(shine) {
  6112. if (p.use3DContext) {
  6113. curContext.useProgram(programObject3D);
  6114. uniformi(programObject3D, "usingMat", true);
  6115. uniformf(programObject3D, "shininess", shine);
  6116. }
  6117. };
  6118. /*
  6119. Documentation says the following calls are valid, but the
  6120. Processing throws exceptions:
  6121. specular(gray, alpha)
  6122. specular(v1, v2, v3, alpha)
  6123. So we don't support them either
  6124. <corban> I dont think this matters so much, let us let color handle it. alpha values are not sent anyways.
  6125. */
  6126. p.specular = function specular() {
  6127. var c = p.color.apply(this, arguments);
  6128. if (p.use3DContext) {
  6129. curContext.useProgram(programObject3D);
  6130. uniformi(programObject3D, "usingMat", true);
  6131. uniformf(programObject3D, "mat_specular", p.color.toGLArray(c).slice(0, 3));
  6132. }
  6133. };
  6134. ////////////////////////////////////////////////////////////////////////////
  6135. // Coordinates
  6136. ////////////////////////////////////////////////////////////////////////////
  6137. p.screenX = function screenX( x, y, z ) {
  6138. var mv = modelView.array();
  6139. var pj = projection.array();
  6140. var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
  6141. var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
  6142. var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
  6143. var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
  6144. var ox = pj[ 0]*ax + pj[ 1]*ay + pj[ 2]*az + pj[ 3]*aw;
  6145. var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
  6146. if ( ow !== 0 ){
  6147. ox /= ow;
  6148. }
  6149. return p.width * ( 1 + ox ) / 2.0;
  6150. };
  6151. p.screenY = function screenY( x, y, z ) {
  6152. var mv = modelView.array();
  6153. var pj = projection.array();
  6154. var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
  6155. var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
  6156. var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
  6157. var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
  6158. var oy = pj[ 4]*ax + pj[ 5]*ay + pj[ 6]*az + pj[ 7]*aw;
  6159. var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
  6160. if ( ow !== 0 ){
  6161. oy /= ow;
  6162. }
  6163. return p.height * ( 1 + oy ) / 2.0;
  6164. };
  6165. p.screenZ = function screenZ( x, y, z ) {
  6166. var mv = modelView.array();
  6167. var pj = projection.array();
  6168. var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
  6169. var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
  6170. var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
  6171. var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
  6172. var oz = pj[ 8]*ax + pj[ 9]*ay + pj[10]*az + pj[11]*aw;
  6173. var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
  6174. if ( ow !== 0 ) {
  6175. oz /= ow;
  6176. }
  6177. return ( oz + 1 ) / 2.0;
  6178. };
  6179. ////////////////////////////////////////////////////////////////////////////
  6180. // Style functions
  6181. ////////////////////////////////////////////////////////////////////////////
  6182. p.fill = function fill() {
  6183. var color = p.color(arguments[0], arguments[1], arguments[2], arguments[3]);
  6184. if(color === currentFillColor && doFill) {
  6185. return;
  6186. }
  6187. doFill = true;
  6188. currentFillColor = color;
  6189. if (p.use3DContext) {
  6190. fillStyle = p.color.toGLArray(color);
  6191. } else {
  6192. isFillDirty = true;
  6193. }
  6194. };
  6195. function executeContextFill() {
  6196. if(doFill) {
  6197. if(isFillDirty) {
  6198. curContext.fillStyle = p.color.toString(currentFillColor);
  6199. isFillDirty = false;
  6200. }
  6201. curContext.fill();
  6202. }
  6203. }
  6204. p.noFill = function noFill() {
  6205. doFill = false;
  6206. };
  6207. p.stroke = function stroke() {
  6208. var color = p.color(arguments[0], arguments[1], arguments[2], arguments[3]);
  6209. if(color === currentStrokeColor && doStroke) {
  6210. return;
  6211. }
  6212. doStroke = true;
  6213. currentStrokeColor = color;
  6214. if (p.use3DContext) {
  6215. strokeStyle = p.color.toGLArray(color);
  6216. } else {
  6217. isStrokeDirty = true;
  6218. }
  6219. };
  6220. function executeContextStroke() {
  6221. if(doStroke) {
  6222. if(isStrokeDirty) {
  6223. curContext.strokeStyle = p.color.toString(currentStrokeColor);
  6224. isStrokeDirty = false;
  6225. }
  6226. curContext.stroke();
  6227. }
  6228. }
  6229. p.noStroke = function noStroke() {
  6230. doStroke = false;
  6231. };
  6232. p.strokeWeight = function strokeWeight(w) {
  6233. lineWidth = w;
  6234. if (p.use3DContext) {
  6235. curContext.useProgram(programObject2D);
  6236. uniformf(programObject2D, "pointSize", w);
  6237. } else {
  6238. curContext.lineWidth = w;
  6239. }
  6240. };
  6241. p.strokeCap = function strokeCap(value) {
  6242. curContext.lineCap = value;
  6243. };
  6244. p.strokeJoin = function strokeJoin(value) {
  6245. curContext.lineJoin = value;
  6246. };
  6247. p.smooth = function() {
  6248. if (!p.use3DContext) {
  6249. if ("mozImageSmoothingEnabled" in curContext) {
  6250. curElement.style.setProperty("image-rendering", "optimizeQuality", "important");
  6251. curContext.mozImageSmoothingEnabled = true;
  6252. }
  6253. }
  6254. };
  6255. p.noSmooth = function() {
  6256. if (!p.use3DContext) {
  6257. if ("mozImageSmoothingEnabled" in curContext) {
  6258. curElement.style.setProperty("image-rendering", "optimizeQuality", "important");
  6259. curContext.mozImageSmoothingEnabled = false;
  6260. }
  6261. }
  6262. };
  6263. ////////////////////////////////////////////////////////////////////////////
  6264. // Vector drawing functions
  6265. ////////////////////////////////////////////////////////////////////////////
  6266. function colorBlendWithAlpha(c1, c2, k) {
  6267. var f = 0|(k * ((c2 & p.ALPHA_MASK) >>> 24));
  6268. return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
  6269. p.mix(c1 & p.RED_MASK, c2 & p.RED_MASK, f) & p.RED_MASK |
  6270. p.mix(c1 & p.GREEN_MASK, c2 & p.GREEN_MASK, f) & p.GREEN_MASK |
  6271. p.mix(c1 & p.BLUE_MASK, c2 & p.BLUE_MASK, f));
  6272. }
  6273. p.point = function point(x, y, z) {
  6274. if (p.use3DContext) {
  6275. var model = new PMatrix3D();
  6276. // move point to position
  6277. model.translate(x, y, z || 0);
  6278. model.transpose();
  6279. var view = new PMatrix3D();
  6280. view.scale(1, -1, 1);
  6281. view.apply(modelView.array());
  6282. view.transpose();
  6283. var proj = new PMatrix3D();
  6284. proj.set(projection);
  6285. proj.transpose();
  6286. curContext.useProgram(programObject2D);
  6287. uniformMatrix(programObject2D, "model", false, model.array());
  6288. uniformMatrix(programObject2D, "view", false, view.array());
  6289. uniformMatrix(programObject2D, "projection", false, proj.array());
  6290. if (lineWidth > 0 && doStroke) {
  6291. // this will be replaced with the new bit shifting color code
  6292. uniformf(programObject2D, "color", strokeStyle);
  6293. uniformi(programObject2D, "picktype", 0);
  6294. vertexAttribPointer(programObject2D, "Vertex", 3, pointBuffer);
  6295. disableVertexAttribPointer(programObject2D, "aTextureCoord");
  6296. curContext.drawArrays(curContext.POINTS, 0, 1);
  6297. }
  6298. } else {
  6299. if (doStroke) {
  6300. // TODO if strokeWeight > 1, do circle
  6301. if (curSketch.options.crispLines) {
  6302. var alphaOfPointWeight = Math.PI / 4; // TODO dependency of strokeWeight
  6303. var c = p.get(x, y);
  6304. p.set(x, y, colorBlendWithAlpha(c, currentStrokeColor, alphaOfPointWeight));
  6305. } else {
  6306. if (lineWidth > 1){
  6307. curContext.fillStyle = p.color.toString(currentStrokeColor);
  6308. isFillDirty = true;
  6309. curContext.beginPath();
  6310. curContext.arc(x, y, lineWidth / 2, 0, p.TWO_PI, false);
  6311. curContext.fill();
  6312. curContext.closePath();
  6313. } else {
  6314. curContext.fillStyle = p.color.toString(currentStrokeColor);
  6315. curContext.fillRect(Math.round(x), Math.round(y), 1, 1);
  6316. isFillDirty = true;
  6317. }
  6318. }
  6319. }
  6320. }
  6321. };
  6322. p.beginShape = function beginShape(type) {
  6323. curShape = type;
  6324. curShapeCount = 0;
  6325. curvePoints = [];
  6326. //textureImage = null;
  6327. vertArray = [];
  6328. if(p.use3DContext)
  6329. {
  6330. //normalMode = NORMAL_MODE_AUTO;
  6331. }
  6332. };
  6333. p.vertex = function vertex() {
  6334. if(firstVert){ firstVert = false; }
  6335. var vert = [];
  6336. if(arguments.length === 4){ //x, y, u, v
  6337. vert[0] = arguments[0];
  6338. vert[1] = arguments[1];
  6339. vert[2] = 0;
  6340. vert[3] = arguments[2];
  6341. vert[4] = arguments[3];
  6342. }
  6343. else{ // x, y, z, u, v
  6344. vert[0] = arguments[0];
  6345. vert[1] = arguments[1];
  6346. vert[2] = arguments[2] || 0;
  6347. vert[3] = arguments[3] || 0;
  6348. vert[4] = arguments[4] || 0;
  6349. }
  6350. // fill rgba
  6351. vert[5] = fillStyle[0];
  6352. vert[6] = fillStyle[1];
  6353. vert[7] = fillStyle[2];
  6354. vert[8] = fillStyle[3];
  6355. // stroke rgba
  6356. vert[9] = strokeStyle[0];
  6357. vert[10] = strokeStyle[1];
  6358. vert[11] = strokeStyle[2];
  6359. vert[12] = strokeStyle[3];
  6360. //normals
  6361. vert[13] = normalX;
  6362. vert[14] = normalY;
  6363. vert[15] = normalZ;
  6364. vert["isVert"] = true;
  6365. vertArray.push(vert);
  6366. };
  6367. /*
  6368. Draw 3D points created from calls to vertex:
  6369. beginShape(POINT);
  6370. vertex(x, y, 0);
  6371. ...
  6372. endShape();
  6373. */
  6374. var point3D = function point3D(vArray, cArray){
  6375. var view = new PMatrix3D();
  6376. view.scale(1, -1, 1);
  6377. view.apply(modelView.array());
  6378. view.transpose();
  6379. var proj = new PMatrix3D();
  6380. proj.set(projection);
  6381. proj.transpose();
  6382. curContext.useProgram(programObjectUnlitShape);
  6383. uniformMatrix(programObjectUnlitShape, "uView", false, view.array());
  6384. uniformMatrix(programObjectUnlitShape, "uProjection", false, proj.array());
  6385. vertexAttribPointer(programObjectUnlitShape, "aVertex", 3, pointBuffer);
  6386. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(vArray), curContext.STREAM_DRAW);
  6387. vertexAttribPointer(programObjectUnlitShape, "aColor", 4, fillColorBuffer);
  6388. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(cArray), curContext.STREAM_DRAW);
  6389. curContext.drawArrays(curContext.POINTS, 0, vArray.length/3);
  6390. };
  6391. /*
  6392. Draw 3D lines created from calls to beginShape/vertex/endShape
  6393. LINES, LINE_LOOP, etc.
  6394. */
  6395. var line3D = function line3D(vArray, mode, cArray){
  6396. var ctxMode;
  6397. if (mode === "LINES"){
  6398. ctxMode = curContext.LINES;
  6399. }
  6400. else if(mode === "LINE_LOOP"){
  6401. ctxMode = curContext.LINE_LOOP;
  6402. }
  6403. else{
  6404. ctxMode = curContext.LINE_STRIP;
  6405. }
  6406. var view = new PMatrix3D();
  6407. view.scale(1, -1, 1);
  6408. view.apply(modelView.array());
  6409. view.transpose();
  6410. var proj = new PMatrix3D();
  6411. proj.set(projection);
  6412. proj.transpose();
  6413. curContext.useProgram(programObjectUnlitShape);
  6414. uniformMatrix(programObjectUnlitShape, "uView", false, view.array());
  6415. uniformMatrix(programObjectUnlitShape, "uProjection", false, proj.array());
  6416. vertexAttribPointer(programObjectUnlitShape, "aVertex", 3, lineBuffer);
  6417. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(vArray), curContext.STREAM_DRAW);
  6418. vertexAttribPointer(programObjectUnlitShape, "aColor", 4, strokeColorBuffer);
  6419. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(cArray), curContext.STREAM_DRAW);
  6420. curContext.lineWidth(lineWidth);
  6421. curContext.drawArrays(ctxMode, 0, vArray.length/3);
  6422. };
  6423. var fill3D = function fill3D(vArray, mode, cArray, tArray){
  6424. var ctxMode;
  6425. if(mode === "TRIANGLES"){
  6426. ctxMode = curContext.TRIANGLES;
  6427. }
  6428. else if(mode === "TRIANGLE_FAN"){
  6429. ctxMode = curContext.TRIANGLE_FAN;
  6430. }
  6431. else{
  6432. ctxMode = curContext.TRIANGLE_STRIP;
  6433. }
  6434. var view = new PMatrix3D();
  6435. view.scale(1, -1, 1);
  6436. view.apply(modelView.array());
  6437. view.transpose();
  6438. var proj = new PMatrix3D();
  6439. proj.set(projection);
  6440. proj.transpose();
  6441. curContext.useProgram( programObject3D );
  6442. uniformMatrix( programObject3D, "model", false, [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1] );
  6443. uniformMatrix( programObject3D, "view", false, view.array() );
  6444. uniformMatrix( programObject3D, "projection", false, proj.array() );
  6445. curContext.enable( curContext.POLYGON_OFFSET_FILL );
  6446. curContext.polygonOffset( 1, 1 );
  6447. uniformf(programObject3D, "color", [-1,0,0,0]);
  6448. vertexAttribPointer(programObject3D, "Vertex", 3, fillBuffer);
  6449. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(vArray), curContext.STREAM_DRAW);
  6450. vertexAttribPointer(programObject3D, "aColor", 4, fillColorBuffer);
  6451. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(cArray), curContext.STREAM_DRAW);
  6452. // No support for lights....yet
  6453. disableVertexAttribPointer(programObject3D, "Normal");
  6454. var i;
  6455. if(usingTexture){
  6456. if(curTextureMode === p.IMAGE){
  6457. for(i = 0; i < tArray.length; i += 2){
  6458. tArray[i] = tArray[i]/curTexture.width;
  6459. tArray[i+1] /= curTexture.height;
  6460. }
  6461. }
  6462. // hack to handle when users specifies values
  6463. // greater than 1.0 for texture coords.
  6464. for(i = 0; i < tArray.length; i += 2){
  6465. if( tArray[i+0] > 1.0 ){ tArray[i+0] -= (tArray[i+0] - 1.0);}
  6466. if( tArray[i+1] > 1.0 ){ tArray[i+1] -= (tArray[i+1] - 1.0);}
  6467. }
  6468. uniformi(programObject3D, "usingTexture", usingTexture);
  6469. vertexAttribPointer(programObject3D, "aTexture", 2, shapeTexVBO);
  6470. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(tArray), curContext.STREAM_DRAW);
  6471. }
  6472. curContext.drawArrays( ctxMode, 0, vArray.length/3 );
  6473. curContext.disable( curContext.POLYGON_OFFSET_FILL );
  6474. };
  6475. p.endShape = function endShape(close){
  6476. var lineVertArray = [];
  6477. var fillVertArray = [];
  6478. var colorVertArray = [];
  6479. var strokeVertArray = [];
  6480. var texVertArray = [];
  6481. firstVert = true;
  6482. var i, j, k;
  6483. var last = vertArray.length - 1;
  6484. for(i = 0; i < vertArray.length; i++){
  6485. for(j = 0; j < 3; j++){
  6486. fillVertArray.push(vertArray[i][j]);
  6487. }
  6488. }
  6489. // 5,6,7,8
  6490. // R,G,B,A
  6491. for(i = 0; i < vertArray.length; i++){
  6492. for(j = 5; j < 9; j++){
  6493. colorVertArray.push(vertArray[i][j]);
  6494. }
  6495. }
  6496. // 9,10,11,12
  6497. // R, G, B, A
  6498. for(i = 0; i < vertArray.length; i++){
  6499. for(j = 9; j < 13; j++){
  6500. strokeVertArray.push(vertArray[i][j]);
  6501. }
  6502. }
  6503. for(i = 0; i < vertArray.length; i++){
  6504. texVertArray.push(vertArray[i][3]);
  6505. texVertArray.push(vertArray[i][4]);
  6506. }
  6507. if(!close){
  6508. p.CLOSE = false;
  6509. }
  6510. else{
  6511. p.CLOSE = true;
  6512. fillVertArray.push(vertArray[0][0]);
  6513. fillVertArray.push(vertArray[0][1]);
  6514. fillVertArray.push(vertArray[0][2]);
  6515. for(i = 5; i < 9; i++){
  6516. colorVertArray.push(vertArray[0][i]);
  6517. }
  6518. for(i = 9; i < 13; i++){
  6519. strokeVertArray.push(vertArray[0][i]);
  6520. }
  6521. texVertArray.push(vertArray[0][3]);
  6522. texVertArray.push(vertArray[0][4]);
  6523. }
  6524. if(isCurve && curShape === p.POLYGON || isCurve && curShape === undef){
  6525. if(p.use3DContext){
  6526. lineVertArray = fillVertArray;
  6527. if(doStroke){
  6528. line3D(lineVertArray, null, strokeVertArray);
  6529. }
  6530. if(doFill){
  6531. fill3D(fillVertArray, null, colorVertArray); // fill isn't working in 3d curveVertex
  6532. }
  6533. }
  6534. else{
  6535. if(vertArray.length > 3){
  6536. var b = [],
  6537. s = 1 - curTightness;
  6538. curContext.beginPath();
  6539. curContext.moveTo(vertArray[1][0], vertArray[1][1]);
  6540. /*
  6541. * Matrix to convert from Catmull-Rom to cubic Bezier
  6542. * where t = curTightness
  6543. * |0 1 0 0 |
  6544. * |(t-1)/6 1 (1-t)/6 0 |
  6545. * |0 (1-t)/6 1 (t-1)/6 |
  6546. * |0 0 0 0 |
  6547. */
  6548. for(i = 1; (i+2) < vertArray.length; i++){
  6549. b[0] = [vertArray[i][0], vertArray[i][1]];
  6550. b[1] = [vertArray[i][0] + (s * vertArray[i+1][0] - s * vertArray[i-1][0]) / 6,
  6551. vertArray[i][1] + (s * vertArray[i+1][1] - s * vertArray[i-1][1]) / 6];
  6552. b[2] = [vertArray[i+1][0] + (s * vertArray[i][0] - s * vertArray[i+2][0]) / 6,
  6553. vertArray[i+1][1] + (s * vertArray[i][1] - s * vertArray[i+2][1]) / 6];
  6554. b[3] = [vertArray[i+1][0], vertArray[i+1][1]];
  6555. curContext.bezierCurveTo(b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]);
  6556. }
  6557. executeContextFill();
  6558. executeContextStroke();
  6559. curContext.closePath();
  6560. }
  6561. }
  6562. }
  6563. else if(isBezier && curShape === p.POLYGON || isBezier && curShape === undef){
  6564. if(p.use3DContext){
  6565. lineVertArray = fillVertArray;
  6566. lineVertArray.splice(lineVertArray.length - 3);
  6567. strokeVertArray.splice(strokeVertArray.length - 4);
  6568. if(doStroke){
  6569. line3D(lineVertArray, null, strokeVertArray);
  6570. }
  6571. if(doFill){
  6572. fill3D(fillVertArray, "TRIANGLES", colorVertArray);
  6573. }
  6574. // TODO: Fill not properly working yet, will fix later
  6575. /*fillVertArray = [];
  6576. colorVertArray = [];
  6577. tempArray.reverse();
  6578. for(i = 0; (i+1) < 10; i++){
  6579. for(j = 0; j < 3; j++){
  6580. fillVertArray.push(tempArray[i][j]);
  6581. }
  6582. for(j = 5; j < 9; j++){
  6583. colorVertArray.push(tempArray[i][j]);
  6584. }
  6585. for(j = 0; j < 3; j++){
  6586. fillVertArray.push(vertArray[i][j]);
  6587. }
  6588. for(j = 5; j < 9; j++){
  6589. colorVertArray.push(vertArray[i][j]);
  6590. }
  6591. for(j = 0; j < 3; j++){
  6592. fillVertArray.push(vertArray[i+1][j]);
  6593. }
  6594. for(j = 5; j < 9; j++){
  6595. colorVertArray.push(vertArray[i][j]);
  6596. }
  6597. }
  6598. strokeVertArray = [];
  6599. for(i = 0; i < tempArray.length/3; i++){
  6600. strokeVertArray.push(255);
  6601. strokeVertArray.push(0);
  6602. strokeVertArray.push(0);
  6603. strokeVertArray.push(255);
  6604. }
  6605. point3D(tempArray, strokeVertArray);*/
  6606. }
  6607. else{
  6608. curContext.beginPath();
  6609. for(i = 0; i < vertArray.length; i++){
  6610. if( vertArray[i]["isVert"] === true ){ //if it is a vertex move to the position
  6611. if ( vertArray[i]["moveTo"] === true) {
  6612. curContext.moveTo(vertArray[i][0], vertArray[i][1]);
  6613. } else if (vertArray[i]["moveTo"] === false){
  6614. curContext.lineTo(vertArray[i][0], vertArray[i][1]);
  6615. } else {
  6616. curContext.moveTo(vertArray[i][0], vertArray[i][1]);
  6617. }
  6618. } else { //otherwise continue drawing bezier
  6619. curContext.bezierCurveTo(vertArray[i][0], vertArray[i][1], vertArray[i][2], vertArray[i][3], vertArray[i][4], vertArray[i][5]);
  6620. }
  6621. }
  6622. executeContextFill();
  6623. executeContextStroke();
  6624. curContext.closePath();
  6625. }
  6626. }
  6627. else{
  6628. if(p.use3DContext){ // 3D context
  6629. if (curShape === p.POINTS){
  6630. for(i = 0; i < vertArray.length; i++){
  6631. for(j = 0; j < 3; j++){
  6632. lineVertArray.push(vertArray[i][j]);
  6633. }
  6634. }
  6635. point3D(lineVertArray, strokeVertArray);
  6636. }
  6637. else if(curShape === p.LINES){
  6638. for(i = 0; i < vertArray.length; i++){
  6639. for(j = 0; j < 3; j++){
  6640. lineVertArray.push(vertArray[i][j]);
  6641. }
  6642. }
  6643. for(i = 0; i < vertArray.length; i++){
  6644. for(j = 5; j < 9; j++){
  6645. colorVertArray.push(vertArray[i][j]);
  6646. }
  6647. }
  6648. line3D(lineVertArray, "LINES", strokeVertArray);
  6649. }
  6650. else if(curShape === p.TRIANGLES){
  6651. if(vertArray.length > 2){
  6652. for(i = 0; (i+2) < vertArray.length; i+=3){
  6653. fillVertArray = [];
  6654. texVertArray = [];
  6655. lineVertArray = [];
  6656. colorVertArray = [];
  6657. strokeVertArray = [];
  6658. for(j = 0; j < 3; j++){
  6659. for(k = 0; k < 3; k++){
  6660. lineVertArray.push(vertArray[i+j][k]);
  6661. fillVertArray.push(vertArray[i+j][k]);
  6662. }
  6663. }
  6664. for(j = 0; j < 3; j++){
  6665. for(k = 3; k < 5; k++){
  6666. texVertArray.push(vertArray[i+j][k]);
  6667. }
  6668. }
  6669. for(j = 0; j < 3; j++){
  6670. for(k = 5; k < 9; k++){
  6671. colorVertArray.push(vertArray[i+j][k]);
  6672. strokeVertArray.push(vertArray[i+j][k+4]);
  6673. }
  6674. }
  6675. if(doStroke){
  6676. line3D(lineVertArray, "LINE_LOOP", strokeVertArray );
  6677. }
  6678. if(doFill || usingTexture){
  6679. fill3D(fillVertArray, "TRIANGLES", colorVertArray, texVertArray);
  6680. }
  6681. }
  6682. }
  6683. }
  6684. else if(curShape === p.TRIANGLE_STRIP){
  6685. if(vertArray.length > 2){
  6686. for(i = 0; (i+2) < vertArray.length; i++){
  6687. lineVertArray = [];
  6688. fillVertArray = [];
  6689. strokeVertArray = [];
  6690. colorVertArray = [];
  6691. texVertArray = [];
  6692. for(j = 0; j < 3; j++){
  6693. for(k = 0; k < 3; k++){
  6694. lineVertArray.push(vertArray[i+j][k]);
  6695. fillVertArray.push(vertArray[i+j][k]);
  6696. }
  6697. }
  6698. for(j = 0; j < 3; j++){
  6699. for(k = 3; k < 5; k++){
  6700. texVertArray.push(vertArray[i+j][k]);
  6701. }
  6702. }
  6703. for(j = 0; j < 3; j++){
  6704. for(k = 5; k < 9; k++){
  6705. strokeVertArray.push(vertArray[i+j][k+4]);
  6706. colorVertArray.push(vertArray[i+j][k]);
  6707. }
  6708. }
  6709. if(doFill || usingTexture){
  6710. fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
  6711. }
  6712. if(doStroke){
  6713. line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
  6714. }
  6715. }
  6716. }
  6717. }
  6718. else if(curShape === p.TRIANGLE_FAN){
  6719. if(vertArray.length > 2){
  6720. for(i = 0; i < 3; i++){
  6721. for(j = 0; j < 3; j++){
  6722. lineVertArray.push(vertArray[i][j]);
  6723. }
  6724. }
  6725. for(i = 0; i < 3; i++){
  6726. for(j = 9; j < 13; j++){
  6727. strokeVertArray.push(vertArray[i][j]);
  6728. }
  6729. }
  6730. if(doStroke){
  6731. line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
  6732. }
  6733. for(i = 2; (i+1) < vertArray.length; i++){
  6734. lineVertArray = [];
  6735. strokeVertArray = [];
  6736. lineVertArray.push(vertArray[0][0]);
  6737. lineVertArray.push(vertArray[0][1]);
  6738. lineVertArray.push(vertArray[0][2]);
  6739. strokeVertArray.push(vertArray[0][9]);
  6740. strokeVertArray.push(vertArray[0][10]);
  6741. strokeVertArray.push(vertArray[0][11]);
  6742. strokeVertArray.push(vertArray[0][12]);
  6743. for(j = 0; j < 2; j++){
  6744. for(k = 0; k < 3; k++){
  6745. lineVertArray.push(vertArray[i+j][k]);
  6746. }
  6747. }
  6748. for(j = 0; j < 2; j++){
  6749. for(k = 9; k < 13; k++){
  6750. strokeVertArray.push(vertArray[i+j][k]);
  6751. }
  6752. }
  6753. if(doStroke){
  6754. line3D(lineVertArray, "LINE_STRIP",strokeVertArray);
  6755. }
  6756. }
  6757. if(doFill || usingTexture){
  6758. fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
  6759. }
  6760. }
  6761. }
  6762. else if(curShape === p.QUADS){
  6763. for(i = 0; (i + 3) < vertArray.length; i+=4){
  6764. lineVertArray = [];
  6765. for(j = 0; j < 4; j++){
  6766. for(k = 0; k < 3; k++){
  6767. lineVertArray.push(vertArray[i+j][k]);
  6768. }
  6769. }
  6770. if(doStroke){
  6771. line3D(lineVertArray, "LINE_LOOP",strokeVertArray);
  6772. }
  6773. if(doFill){
  6774. fillVertArray = [];
  6775. colorVertArray = [];
  6776. texVertArray = [];
  6777. for(j = 0; j < 3; j++){
  6778. fillVertArray.push(vertArray[i][j]);
  6779. }
  6780. for(j = 5; j < 9; j++){
  6781. colorVertArray.push(vertArray[i][j]);
  6782. }
  6783. for(j = 0; j < 3; j++){
  6784. fillVertArray.push(vertArray[i+1][j]);
  6785. }
  6786. for(j = 5; j < 9; j++){
  6787. colorVertArray.push(vertArray[i+1][j]);
  6788. }
  6789. for(j = 0; j < 3; j++){
  6790. fillVertArray.push(vertArray[i+3][j]);
  6791. }
  6792. for(j = 5; j < 9; j++){
  6793. colorVertArray.push(vertArray[i+3][j]);
  6794. }
  6795. for(j = 0; j < 3; j++){
  6796. fillVertArray.push(vertArray[i+2][j]);
  6797. }
  6798. for(j = 5; j < 9; j++){
  6799. colorVertArray.push(vertArray[i+2][j]);
  6800. }
  6801. if(usingTexture){
  6802. texVertArray.push(vertArray[i+0][3]);
  6803. texVertArray.push(vertArray[i+0][4]);
  6804. texVertArray.push(vertArray[i+1][3]);
  6805. texVertArray.push(vertArray[i+1][4]);
  6806. texVertArray.push(vertArray[i+3][3]);
  6807. texVertArray.push(vertArray[i+3][4]);
  6808. texVertArray.push(vertArray[i+2][3]);
  6809. texVertArray.push(vertArray[i+2][4]);
  6810. }
  6811. fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
  6812. }
  6813. }
  6814. }
  6815. else if(curShape === p.QUAD_STRIP){
  6816. var tempArray = [];
  6817. if(vertArray.length > 3){
  6818. for(i = 0; i < 2; i++){
  6819. for(j = 0; j < 3; j++){
  6820. lineVertArray.push(vertArray[i][j]);
  6821. }
  6822. }
  6823. for(i = 0; i < 2; i++){
  6824. for(j = 9; j < 13; j++){
  6825. strokeVertArray.push(vertArray[i][j]);
  6826. }
  6827. }
  6828. line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
  6829. if(vertArray.length > 4 && vertArray.length % 2 > 0){
  6830. tempArray = fillVertArray.splice(fillVertArray.length - 3);
  6831. vertArray.pop();
  6832. }
  6833. for(i = 0; (i+3) < vertArray.length; i+=2){
  6834. lineVertArray = [];
  6835. strokeVertArray = [];
  6836. for(j = 0; j < 3; j++){
  6837. lineVertArray.push(vertArray[i+1][j]);
  6838. }
  6839. for(j = 0; j < 3; j++){
  6840. lineVertArray.push(vertArray[i+3][j]);
  6841. }
  6842. for(j = 0; j < 3; j++){
  6843. lineVertArray.push(vertArray[i+2][j]);
  6844. }
  6845. for(j = 0; j < 3; j++){
  6846. lineVertArray.push(vertArray[i+0][j]);
  6847. }
  6848. for(j = 9; j < 13; j++){
  6849. strokeVertArray.push(vertArray[i+1][j]);
  6850. }
  6851. for(j = 9; j < 13; j++){
  6852. strokeVertArray.push(vertArray[i+3][j]);
  6853. }
  6854. for(j = 9; j < 13; j++){
  6855. strokeVertArray.push(vertArray[i+2][j]);
  6856. }
  6857. for(j = 9; j < 13; j++){
  6858. strokeVertArray.push(vertArray[i+0][j]);
  6859. }
  6860. if(doStroke){
  6861. line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
  6862. }
  6863. }
  6864. if(doFill || usingTexture){
  6865. fill3D(fillVertArray, "TRIANGLE_LIST", colorVertArray, texVertArray);
  6866. }
  6867. }
  6868. }
  6869. // If the user didn't specify a type (LINES, TRIANGLES, etc)
  6870. else{
  6871. // If only one vertex was specified, it must be a point
  6872. if(vertArray.length === 1){
  6873. for(j = 0; j < 3; j++){
  6874. lineVertArray.push(vertArray[0][j]);
  6875. }
  6876. for(j = 9; j < 13; j++){
  6877. strokeVertArray.push(vertArray[0][j]);
  6878. }
  6879. point3D(lineVertArray,strokeVertArray);
  6880. }
  6881. else{
  6882. for(i = 0; i < vertArray.length; i++){
  6883. for(j = 0; j < 3; j++){
  6884. lineVertArray.push(vertArray[i][j]);
  6885. }
  6886. for(j = 5; j < 9; j++){
  6887. strokeVertArray.push(vertArray[i][j]);
  6888. }
  6889. }
  6890. if(p.CLOSE){
  6891. line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
  6892. }
  6893. else{
  6894. line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
  6895. }
  6896. // fill is ignored if textures are used
  6897. if(doFill || usingTexture){
  6898. fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
  6899. }
  6900. }
  6901. }
  6902. // everytime beginShape is followed by a call to
  6903. // texture(), texturing it turned back on. We do this to
  6904. // figure out if the shape should be textured or filled
  6905. // with a color.
  6906. usingTexture = false;
  6907. curContext.useProgram(programObject3D);
  6908. uniformi(programObject3D, "usingTexture", usingTexture);
  6909. }
  6910. // 2D context
  6911. else{
  6912. if (curShape === p.POINTS){
  6913. for(i = 0; i < vertArray.length; i++){
  6914. p.point(vertArray[i][0], vertArray[i][1]);
  6915. }
  6916. }
  6917. else if(curShape === p.LINES){
  6918. for(i = 0; (i + 1) < vertArray.length; i+=2){
  6919. p.line(vertArray[i][0], vertArray[i][1], vertArray[i+1][0], vertArray[i+1][1]);
  6920. }
  6921. }
  6922. else if(curShape === p.TRIANGLES){
  6923. for(i = 0; (i + 2) < vertArray.length; i+=3){
  6924. curContext.beginPath();
  6925. curContext.moveTo(vertArray[i][0], vertArray[i][1]);
  6926. curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
  6927. curContext.lineTo(vertArray[i+2][0], vertArray[i+2][1]);
  6928. curContext.lineTo(vertArray[i][0], vertArray[i][1]);
  6929. executeContextFill();
  6930. executeContextStroke();
  6931. curContext.closePath();
  6932. }
  6933. }
  6934. else if(curShape === p.TRIANGLE_STRIP){
  6935. if(vertArray.length > 2){
  6936. curContext.beginPath();
  6937. curContext.moveTo(vertArray[0][0], vertArray[0][1]);
  6938. curContext.lineTo(vertArray[1][0], vertArray[1][1]);
  6939. for(i = 2; i < vertArray.length; i++){
  6940. curContext.lineTo(vertArray[i][0], vertArray[i][1]);
  6941. curContext.lineTo(vertArray[i-2][0], vertArray[i-2][1]);
  6942. executeContextFill();
  6943. executeContextStroke();
  6944. curContext.moveTo(vertArray[i][0],vertArray[i][1]);
  6945. }
  6946. curContext.closePath();
  6947. }
  6948. }
  6949. else if(curShape === p.TRIANGLE_FAN){
  6950. if(vertArray.length > 2){
  6951. curContext.beginPath();
  6952. curContext.moveTo(vertArray[0][0], vertArray[0][1]);
  6953. curContext.lineTo(vertArray[1][0], vertArray[1][1]);
  6954. curContext.lineTo(vertArray[2][0], vertArray[2][1]);
  6955. executeContextFill();
  6956. executeContextStroke();
  6957. for(i = 3; i < vertArray.length; i++){
  6958. curContext.moveTo(vertArray[0][0], vertArray[0][1]);
  6959. curContext.lineTo(vertArray[i-1][0], vertArray[i-1][1]);
  6960. curContext.lineTo(vertArray[i][0], vertArray[i][1]);
  6961. executeContextFill();
  6962. executeContextStroke();
  6963. }
  6964. curContext.closePath();
  6965. }
  6966. }
  6967. else if(curShape === p.QUADS){
  6968. for(i = 0; (i + 3) < vertArray.length; i+=4){
  6969. curContext.beginPath();
  6970. curContext.moveTo(vertArray[i][0], vertArray[i][1]);
  6971. for(j = 1; j < 4; j++){
  6972. curContext.lineTo(vertArray[i+j][0], vertArray[i+j][1]);
  6973. }
  6974. curContext.lineTo(vertArray[i][0], vertArray[i][1]);
  6975. executeContextFill();
  6976. executeContextStroke();
  6977. curContext.closePath();
  6978. }
  6979. }
  6980. else if(curShape === p.QUAD_STRIP){
  6981. if(vertArray.length > 3){
  6982. curContext.beginPath();
  6983. curContext.moveTo(vertArray[0][0], vertArray[0][1]);
  6984. curContext.lineTo(vertArray[1][0], vertArray[1][1]);
  6985. for(i = 2; (i+1) < vertArray.length; i++){
  6986. if((i % 2) === 0){
  6987. curContext.moveTo(vertArray[i-2][0], vertArray[i-2][1]);
  6988. curContext.lineTo(vertArray[i][0], vertArray[i][1]);
  6989. curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
  6990. curContext.lineTo(vertArray[i-1][0], vertArray[i-1][1]);
  6991. executeContextFill();
  6992. executeContextStroke();
  6993. }
  6994. }
  6995. curContext.closePath();
  6996. }
  6997. }
  6998. else{
  6999. curContext.beginPath();
  7000. curContext.moveTo(vertArray[0][0], vertArray[0][1]);
  7001. for(i = 1; i < vertArray.length; i++){
  7002. curContext.lineTo(vertArray[i][0], vertArray[i][1]);
  7003. }
  7004. if(p.CLOSE){
  7005. curContext.lineTo(vertArray[0][0], vertArray[0][1]);
  7006. }
  7007. executeContextFill();
  7008. executeContextStroke();
  7009. curContext.closePath();
  7010. }
  7011. }
  7012. }
  7013. isCurve = false;
  7014. isBezier = false;
  7015. curveVertArray = [];
  7016. curveVertCount = 0;
  7017. };
  7018. //used by both curveDetail and bezierDetail
  7019. var splineForward = function(segments, matrix) {
  7020. var f = 1.0 / segments;
  7021. var ff = f * f;
  7022. var fff = ff * f;
  7023. matrix.set(0, 0, 0, 1, fff, ff, f, 0, 6 * fff, 2 * ff, 0, 0, 6 * fff, 0, 0, 0);
  7024. };
  7025. //internal curveInit
  7026. //used by curveDetail, curveTightness
  7027. var curveInit = function() {
  7028. // allocate only if/when used to save startup time
  7029. if (!curveDrawMatrix) {
  7030. curveBasisMatrix = new PMatrix3D();
  7031. curveDrawMatrix = new PMatrix3D();
  7032. curveInited = true;
  7033. }
  7034. var s = curTightness;
  7035. curveBasisMatrix.set(((s - 1) / 2).toFixed(2), ((s + 3) / 2).toFixed(2),
  7036. ((-3 - s) / 2).toFixed(2), ((1 - s) / 2).toFixed(2),
  7037. (1 - s), ((-5 - s) / 2).toFixed(2), (s + 2), ((s - 1) / 2).toFixed(2),
  7038. ((s - 1) / 2).toFixed(2), 0, ((1 - s) / 2).toFixed(2), 0, 0, 1, 0, 0);
  7039. splineForward(curveDet, curveDrawMatrix);
  7040. if (!bezierBasisInverse) {
  7041. //bezierBasisInverse = bezierBasisMatrix.get();
  7042. //bezierBasisInverse.invert();
  7043. curveToBezierMatrix = new PMatrix3D();
  7044. }
  7045. // TODO only needed for PGraphicsJava2D? if so, move it there
  7046. // actually, it's generally useful for other renderers, so keep it
  7047. // or hide the implementation elsewhere.
  7048. curveToBezierMatrix.set(curveBasisMatrix);
  7049. curveToBezierMatrix.preApply(bezierBasisInverse);
  7050. // multiply the basis and forward diff matrices together
  7051. // saves much time since this needn't be done for each curve
  7052. curveDrawMatrix.apply(curveBasisMatrix);
  7053. };
  7054. p.bezierVertex = function bezierVertex() {
  7055. isBezier = true;
  7056. var vert = [];
  7057. if (firstVert) {
  7058. throw ("vertex() must be used at least once before calling bezierVertex()");
  7059. } else {
  7060. if (arguments.length === 9) {
  7061. if (p.use3DContext) {
  7062. if (bezierDrawMatrix === undef) {
  7063. bezierDrawMatrix = new PMatrix3D();
  7064. }
  7065. // setup matrix for forward differencing to speed up drawing
  7066. var lastPoint = vertArray.length - 1;
  7067. splineForward( bezDetail, bezierDrawMatrix );
  7068. bezierDrawMatrix.apply( bezierBasisMatrix );
  7069. var draw = bezierDrawMatrix.array();
  7070. var x1 = vertArray[lastPoint][0],
  7071. y1 = vertArray[lastPoint][1],
  7072. z1 = vertArray[lastPoint][2];
  7073. var xplot1 = draw[4] * x1 + draw[5] * arguments[0] + draw[6] * arguments[3] + draw[7] * arguments[6];
  7074. var xplot2 = draw[8] * x1 + draw[9] * arguments[0] + draw[10]* arguments[3] + draw[11]* arguments[6];
  7075. var xplot3 = draw[12]* x1 + draw[13]* arguments[0] + draw[14]* arguments[3] + draw[15]* arguments[6];
  7076. var yplot1 = draw[4] * y1 + draw[5] * arguments[1] + draw[6] * arguments[4] + draw[7] * arguments[7];
  7077. var yplot2 = draw[8] * y1 + draw[9] * arguments[1] + draw[10]* arguments[4] + draw[11]* arguments[7];
  7078. var yplot3 = draw[12]* y1 + draw[13]* arguments[1] + draw[14]* arguments[4] + draw[15]* arguments[7];
  7079. var zplot1 = draw[4] * z1 + draw[5] * arguments[2] + draw[6] * arguments[5] + draw[7] * arguments[8];
  7080. var zplot2 = draw[8] * z1 + draw[9] * arguments[2] + draw[10]* arguments[5] + draw[11]* arguments[8];
  7081. var zplot3 = draw[12]* z1 + draw[13]* arguments[2] + draw[14]* arguments[5] + draw[15]* arguments[8];
  7082. for (var j = 0; j < bezDetail; j++) {
  7083. x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
  7084. y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
  7085. z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
  7086. p.vertex(x1, y1, z1);
  7087. }
  7088. p.vertex(arguments[6], arguments[7], arguments[8]);
  7089. }
  7090. } else {
  7091. for (var i = 0; i < arguments.length; i++) {
  7092. vert[i] = arguments[i];
  7093. }
  7094. vertArray.push(vert);
  7095. vertArray[vertArray.length -1]["isVert"] = false;
  7096. }
  7097. }
  7098. };
  7099. p.texture = function(pimage){
  7100. if(!pimage.__texture)
  7101. {
  7102. var texture = curContext.createTexture();
  7103. pimage.__texture = texture;
  7104. var cvs = document.createElement('canvas');
  7105. cvs.width = pimage.width;
  7106. cvs.height = pimage.height;
  7107. var ctx = cvs.getContext('2d');
  7108. var textureImage = ctx.createImageData(cvs.width, cvs.height);
  7109. var imgData = pimage.toImageData();
  7110. for (var i = 0; i < cvs.width; i += 1) {
  7111. for (var j = 0; j < cvs.height; j += 1) {
  7112. var index = (j * cvs.width + i) * 4;
  7113. textureImage.data[index + 0] = imgData.data[index + 0];
  7114. textureImage.data[index + 1] = imgData.data[index + 1];
  7115. textureImage.data[index + 2] = imgData.data[index + 2];
  7116. textureImage.data[index + 3] = 255;
  7117. }
  7118. }
  7119. ctx.putImageData(textureImage, 0, 0);
  7120. pimage.__cvs = cvs;
  7121. curContext.bindTexture(curContext.TEXTURE_2D, pimage.__texture);
  7122. curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR_MIPMAP_LINEAR);
  7123. curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
  7124. curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE);
  7125. curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE);
  7126. curContext.texImage2D(curContext.TEXTURE_2D, 0, pimage.__cvs, false);
  7127. curContext.generateMipmap(curContext.TEXTURE_2D);
  7128. }
  7129. else{
  7130. curContext.bindTexture(curContext.TEXTURE_2D, pimage.__texture);
  7131. }
  7132. curTexture.width = pimage.width;
  7133. curTexture.height = pimage.height;
  7134. usingTexture = true;
  7135. curContext.useProgram(programObject3D);
  7136. uniformi(programObject3D, "usingTexture", usingTexture);
  7137. };
  7138. p.textureMode = function(mode){
  7139. curTextureMode = mode;
  7140. };
  7141. var curveVertexSegment = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) {
  7142. var x0 = x2;
  7143. var y0 = y2;
  7144. var z0 = z2;
  7145. var draw = curveDrawMatrix.array();
  7146. var xplot1 = draw[4] * x1 + draw[5] * x2 + draw[6] * x3 + draw[7] * x4;
  7147. var xplot2 = draw[8] * x1 + draw[9] * x2 + draw[10] * x3 + draw[11] * x4;
  7148. var xplot3 = draw[12] * x1 + draw[13] * x2 + draw[14] * x3 + draw[15] * x4;
  7149. var yplot1 = draw[4] * y1 + draw[5] * y2 + draw[6] * y3 + draw[7] * y4;
  7150. var yplot2 = draw[8] * y1 + draw[9] * y2 + draw[10] * y3 + draw[11] * y4;
  7151. var yplot3 = draw[12] * y1 + draw[13] * y2 + draw[14] * y3 + draw[15] * y4;
  7152. var zplot1 = draw[4] * z1 + draw[5] * z2 + draw[6] * z3 + draw[7] * z4;
  7153. var zplot2 = draw[8] * z1 + draw[9] * z2 + draw[10] * z3 + draw[11] * z4;
  7154. var zplot3 = draw[12] * z1 + draw[13] * z2 + draw[14] * z3 + draw[15] * z4;
  7155. p.vertex(x0, y0, z0);
  7156. for (var j = 0; j < curveDet; j++) {
  7157. x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
  7158. y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
  7159. z0 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
  7160. p.vertex(x0, y0, z0);
  7161. }
  7162. };
  7163. p.curveVertex = function(x, y, z) {
  7164. isCurve = true;
  7165. if(p.use3DContext){
  7166. if (!curveInited){
  7167. curveInit();
  7168. }
  7169. var vert = [];
  7170. vert[0] = x;
  7171. vert[1] = y;
  7172. vert[2] = z;
  7173. curveVertArray.push(vert);
  7174. curveVertCount++;
  7175. if (curveVertCount > 3){
  7176. curveVertexSegment( curveVertArray[curveVertCount-4][0],
  7177. curveVertArray[curveVertCount-4][1],
  7178. curveVertArray[curveVertCount-4][2],
  7179. curveVertArray[curveVertCount-3][0],
  7180. curveVertArray[curveVertCount-3][1],
  7181. curveVertArray[curveVertCount-3][2],
  7182. curveVertArray[curveVertCount-2][0],
  7183. curveVertArray[curveVertCount-2][1],
  7184. curveVertArray[curveVertCount-2][2],
  7185. curveVertArray[curveVertCount-1][0],
  7186. curveVertArray[curveVertCount-1][1],
  7187. curveVertArray[curveVertCount-1][2] );
  7188. }
  7189. }
  7190. else{
  7191. p.vertex(x, y, z);
  7192. }
  7193. };
  7194. p.curve = function curve() {
  7195. if (arguments.length === 8) // curve(x1, y1, x2, y2, x3, y3, x4, y4)
  7196. {
  7197. p.beginShape();
  7198. p.curveVertex(arguments[0], arguments[1]);
  7199. p.curveVertex(arguments[2], arguments[3]);
  7200. p.curveVertex(arguments[4], arguments[5]);
  7201. p.curveVertex(arguments[6], arguments[7]);
  7202. p.endShape();
  7203. } else { // curve( x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4);
  7204. if (p.use3DContext) {
  7205. p.beginShape();
  7206. p.curveVertex(arguments[0], arguments[1], arguments[2]);
  7207. p.curveVertex(arguments[3], arguments[4], arguments[5]);
  7208. p.curveVertex(arguments[6], arguments[7], arguments[8]);
  7209. p.curveVertex(arguments[9], arguments[10], arguments[11]);
  7210. p.endShape();
  7211. }
  7212. }
  7213. };
  7214. p.curveTightness = function(tightness) {
  7215. curTightness = tightness;
  7216. };
  7217. p.curveDetail = function curveDetail( detail ) {
  7218. curveDet = detail;
  7219. curveInit();
  7220. };
  7221. p.rectMode = function rectMode(aRectMode) {
  7222. curRectMode = aRectMode;
  7223. };
  7224. p.imageMode = function(mode) {
  7225. switch (mode) {
  7226. case p.CORNER:
  7227. imageModeConvert = imageModeCorner;
  7228. break;
  7229. case p.CORNERS:
  7230. imageModeConvert = imageModeCorners;
  7231. break;
  7232. case p.CENTER:
  7233. imageModeConvert = imageModeCenter;
  7234. break;
  7235. default:
  7236. throw "Invalid imageMode";
  7237. }
  7238. };
  7239. p.ellipseMode = function ellipseMode(aEllipseMode) {
  7240. curEllipseMode = aEllipseMode;
  7241. };
  7242. p.arc = function arc(x, y, width, height, start, stop) {
  7243. if (width <= 0) {
  7244. return;
  7245. }
  7246. if (curEllipseMode === p.CORNER) {
  7247. x += width / 2;
  7248. y += height / 2;
  7249. }
  7250. curContext.moveTo(x, y);
  7251. curContext.beginPath();
  7252. curContext.arc(x, y, curEllipseMode === p.CENTER_RADIUS ? width : width / 2, start, stop, false);
  7253. executeContextStroke();
  7254. curContext.lineTo(x, y);
  7255. executeContextFill();
  7256. curContext.closePath();
  7257. };
  7258. p.line = function line() {
  7259. var x1, y1, z1, x2, y2, z2;
  7260. if (p.use3DContext) {
  7261. if (arguments.length === 6) {
  7262. x1 = arguments[0];
  7263. y1 = arguments[1];
  7264. z1 = arguments[2];
  7265. x2 = arguments[3];
  7266. y2 = arguments[4];
  7267. z2 = arguments[5];
  7268. } else if (arguments.length === 4) {
  7269. x1 = arguments[0];
  7270. y1 = arguments[1];
  7271. z1 = 0;
  7272. x2 = arguments[2];
  7273. y2 = arguments[3];
  7274. z2 = 0;
  7275. }
  7276. var lineVerts = [x1, y1, z1, x2, y2, z2];
  7277. var view = new PMatrix3D();
  7278. view.scale(1, -1, 1);
  7279. view.apply(modelView.array());
  7280. view.transpose();
  7281. var proj = new PMatrix3D();
  7282. proj.set(projection);
  7283. proj.transpose();
  7284. if (lineWidth > 0 && doStroke) {
  7285. curContext.useProgram(programObject2D);
  7286. uniformMatrix(programObject2D, "model", false, [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]);
  7287. uniformMatrix(programObject2D, "view", false, view.array());
  7288. uniformMatrix(programObject2D, "projection", false, proj.array());
  7289. uniformf(programObject2D, "color", strokeStyle);
  7290. uniformi(programObject2D, "picktype", 0);
  7291. curContext.lineWidth(lineWidth);
  7292. vertexAttribPointer(programObject2D, "Vertex", 3, lineBuffer);
  7293. disableVertexAttribPointer(programObject2D, "aTextureCoord");
  7294. curContext.bufferData(curContext.ARRAY_BUFFER, newWebGLArray(lineVerts), curContext.STREAM_DRAW);
  7295. curContext.drawArrays(curContext.LINES, 0, 2);
  7296. }
  7297. } else {
  7298. x1 = arguments[0];
  7299. y1 = arguments[1];
  7300. x2 = arguments[2];
  7301. y2 = arguments[3];
  7302. // if line is parallel to axis and lineWidth is less than 1px, trying to do it "crisp"
  7303. if ((x1 === x2 || y1 === y2) && lineWidth <= 1.0 && doStroke && curSketch.options.crispLines) {
  7304. var temp;
  7305. if(x1 === x2) {
  7306. if(y1 > y2) { temp = y1; y1 = y2; y2 = temp; }
  7307. for(var y=y1;y<=y2;++y) {
  7308. p.set(x1, y, currentStrokeColor);
  7309. }
  7310. } else {
  7311. if(x1 > x2) { temp = x1; x1 = x2; x2 = temp; }
  7312. for(var x=x1;x<=x2;++x) {
  7313. p.set(x, y1, currentStrokeColor);
  7314. }
  7315. }
  7316. return;
  7317. }
  7318. if (doStroke) {
  7319. curContext.beginPath();
  7320. curContext.moveTo(x1 || 0, y1 || 0);
  7321. curContext.lineTo(x2 || 0, y2 || 0);
  7322. executeContextStroke();
  7323. curContext.closePath();
  7324. }
  7325. }
  7326. };
  7327. p.bezier = function bezier() {
  7328. if( arguments.length === 8 && !p.use3DContext ){
  7329. p.beginShape();
  7330. p.vertex( arguments[0], arguments[1] );
  7331. p.bezierVertex( arguments[2], arguments[3],
  7332. arguments[4], arguments[5],
  7333. arguments[6], arguments[7] );
  7334. p.endShape();
  7335. }
  7336. else if( arguments.length === 12 && p.use3DContext ){
  7337. p.beginShape();
  7338. p.vertex( arguments[0], arguments[1], arguments[2] );
  7339. p.bezierVertex( arguments[3], arguments[4], arguments[5],
  7340. arguments[6], arguments[7], arguments[8],
  7341. arguments[9], arguments[10], arguments[11] );
  7342. p.endShape();
  7343. }
  7344. else {
  7345. throw("Please use the proper parameters!");
  7346. }
  7347. };
  7348. p.bezierDetail = function bezierDetail( detail ){
  7349. bezDetail = detail;
  7350. };
  7351. p.bezierPoint = function bezierPoint(a, b, c, d, t) {
  7352. return (1 - t) * (1 - t) * (1 - t) * a + 3 * (1 - t) * (1 - t) * t * b + 3 * (1 - t) * t * t * c + t * t * t * d;
  7353. };
  7354. p.bezierTangent = function bezierTangent(a, b, c, d, t) {
  7355. return (3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b));
  7356. };
  7357. p.curvePoint = function curvePoint(a, b, c, d, t) {
  7358. return 0.5 * ((2 * b) + (-a + c) * t + (2 * a - 5 * b + 4 * c - d) * t * t + (-a + 3 * b - 3 * c + d) * t * t * t);
  7359. };
  7360. p.curveTangent = function curveTangent(a, b, c, d, t) {
  7361. return 0.5 * ((-a + c) + 2 * (2 * a - 5 * b + 4 * c - d) * t + 3 * (-a + 3 * b - 3 * c + d) * t * t);
  7362. };
  7363. p.triangle = function triangle(x1, y1, x2, y2, x3, y3) {
  7364. p.beginShape(p.TRIANGLES);
  7365. p.vertex(x1, y1, 0);
  7366. p.vertex(x2, y2, 0);
  7367. p.vertex(x3, y3, 0);
  7368. p.endShape();
  7369. };
  7370. p.quad = function quad(x1, y1, x2, y2, x3, y3, x4, y4) {
  7371. p.beginShape(p.QUADS);
  7372. p.vertex(x1, y1, 0);
  7373. p.vertex(x2, y2, 0);
  7374. p.vertex(x3, y3, 0);
  7375. p.vertex(x4, y4, 0);
  7376. p.endShape();
  7377. };
  7378. p.rect = function rect(x, y, width, height) {
  7379. if (p.use3DContext) {
  7380. // Modeling transformation
  7381. var model = new PMatrix3D();
  7382. model.translate(x, y, 0);
  7383. model.scale(width, height, 1);
  7384. // viewing transformation needs to have Y flipped
  7385. // becuase that's what Processing does.
  7386. var view = new PMatrix3D();
  7387. view.scale(1, -1, 1);
  7388. view.apply(modelView.array());
  7389. if (lineWidth > 0 && doStroke) {
  7390. curContext.useProgram(programObject2D);
  7391. uniformMatrix(programObject2D, "model", true, model.array());
  7392. uniformMatrix(programObject2D, "view", true, view.array());
  7393. uniformMatrix(programObject2D, "projection", true, projection.array());
  7394. uniformf(programObject2D, "color", strokeStyle);
  7395. uniformi(programObject2D, "picktype", 0);
  7396. vertexAttribPointer(programObject2D, "Vertex", 3, rectBuffer);
  7397. disableVertexAttribPointer(programObject2D, "aTextureCoord");
  7398. curContext.lineWidth(lineWidth);
  7399. curContext.drawArrays(curContext.LINE_LOOP, 0, rectVerts.length / 3);
  7400. }
  7401. if (doFill) {
  7402. curContext.useProgram(programObject3D);
  7403. uniformMatrix(programObject3D, "model", true, model.array());
  7404. uniformMatrix(programObject3D, "view", true, view.array());
  7405. uniformMatrix(programObject3D, "projection", true, projection.array());
  7406. // fix stitching problems. (lines get occluded by triangles
  7407. // since they share the same depth values). This is not entirely
  7408. // working, but it's a start for drawing the outline. So
  7409. // developers can start playing around with styles.
  7410. curContext.enable(curContext.POLYGON_OFFSET_FILL);
  7411. curContext.polygonOffset(1, 1);
  7412. uniformf(programObject3D, "color", fillStyle);
  7413. var v = new PMatrix3D();
  7414. v.set(view);
  7415. var m = new PMatrix3D();
  7416. m.set(model);
  7417. v.mult(m);
  7418. var normalMatrix = new PMatrix3D();
  7419. normalMatrix.set(v);
  7420. normalMatrix.invert();
  7421. uniformMatrix(programObject3D, "normalTransform", false, normalMatrix.array());
  7422. vertexAttribPointer(programObject3D, "Vertex", 3, rectBuffer);
  7423. vertexAttribPointer(programObject3D, "Normal", 3, rectNormBuffer);
  7424. curContext.drawArrays(curContext.TRIANGLE_FAN, 0, rectVerts.length / 3);
  7425. curContext.disable(curContext.POLYGON_OFFSET_FILL);
  7426. }
  7427. }
  7428. else{
  7429. if (!width && !height) {
  7430. return;
  7431. }
  7432. // if only stroke is enabled, do it "crisp"
  7433. if (doStroke && !doFill && lineWidth <= 1.0 && curSketch.options.crispLines) {
  7434. var i, x2 = x + width - 1, y2 = y + height - 1;
  7435. for(i=0;i<width;++i) {
  7436. p.set(x + i, y, currentStrokeColor);
  7437. p.set(x + i, y2, currentStrokeColor);
  7438. }
  7439. for(i=0;i<height;++i) {
  7440. p.set(x, y + i, currentStrokeColor);
  7441. p.set(x2, y + i, currentStrokeColor);
  7442. }
  7443. return;
  7444. }
  7445. curContext.beginPath();
  7446. var offsetStart = 0;
  7447. var offsetEnd = 0;
  7448. if (curRectMode === p.CORNERS) {
  7449. width -= x;
  7450. height -= y;
  7451. }
  7452. if (curRectMode === p.RADIUS) {
  7453. width *= 2;
  7454. height *= 2;
  7455. }
  7456. if (curRectMode === p.CENTER || curRectMode === p.RADIUS) {
  7457. x -= width / 2;
  7458. y -= height / 2;
  7459. }
  7460. curContext.rect(
  7461. Math.round(x) - offsetStart, Math.round(y) - offsetStart, Math.round(width) + offsetEnd, Math.round(height) + offsetEnd);
  7462. executeContextFill();
  7463. executeContextStroke();
  7464. curContext.closePath();
  7465. }
  7466. };
  7467. p.ellipse = function ellipse(x, y, width, height) {
  7468. x = x || 0;
  7469. y = y || 0;
  7470. if (width <= 0 && height <= 0) {
  7471. return;
  7472. }
  7473. if (curEllipseMode === p.RADIUS) {
  7474. width *= 2;
  7475. height *= 2;
  7476. }
  7477. if (curEllipseMode === p.CORNERS) {
  7478. width = width - x;
  7479. height = height - y;
  7480. }
  7481. if (curEllipseMode === p.CORNER || curEllipseMode === p.CORNERS) {
  7482. x += width / 2;
  7483. y += height / 2;
  7484. }
  7485. var offsetStart = 0;
  7486. // Shortcut for drawing a 2D circle
  7487. if ((!p.use3DContext) && (width === height)) {
  7488. curContext.beginPath();
  7489. curContext.arc(x - offsetStart, y - offsetStart, width / 2, 0, p.TWO_PI, false);
  7490. executeContextFill();
  7491. executeContextStroke();
  7492. curContext.closePath();
  7493. }
  7494. else {
  7495. var w = width / 2,
  7496. h = height / 2,
  7497. C = 0.5522847498307933;
  7498. var c_x = C * w,
  7499. c_y = C * h;
  7500. if(!p.use3DContext){
  7501. // TODO: Audit
  7502. p.beginShape();
  7503. p.vertex(x + w, y);
  7504. p.bezierVertex(x + w, y - c_y, x + c_x, y - h, x, y - h);
  7505. p.bezierVertex(x - c_x, y - h, x - w, y - c_y, x - w, y);
  7506. p.bezierVertex(x - w, y + c_y, x - c_x, y + h, x, y + h);
  7507. p.bezierVertex(x + c_x, y + h, x + w, y + c_y, x + w, y);
  7508. p.endShape();
  7509. }
  7510. else{
  7511. p.beginShape();
  7512. p.vertex(x + w, y);
  7513. p.bezierVertex(x + w, y - c_y, 0, x + c_x, y - h, 0, x, y - h, 0);
  7514. p.bezierVertex(x - c_x, y - h, 0, x - w, y - c_y, 0, x - w, y, 0);
  7515. p.bezierVertex(x - w, y + c_y, 0, x - c_x, y + h, 0, x, y + h, 0);
  7516. p.bezierVertex(x + c_x, y + h, 0, x + w, y + c_y, 0, x + w, y, 0);
  7517. p.endShape();
  7518. //temporary workaround to not working fills for bezier -- will fix later
  7519. var xAv = 0, yAv = 0, i, j;
  7520. for(i = 0; i < vertArray.length; i++){
  7521. xAv += vertArray[i][0];
  7522. yAv += vertArray[i][1];
  7523. }
  7524. xAv /= vertArray.length;
  7525. yAv /= vertArray.length;
  7526. var vert = [],
  7527. fillVertArray = [],
  7528. colorVertArray = [];
  7529. vert[0] = xAv;
  7530. vert[1] = yAv;
  7531. vert[2] = 0;
  7532. vert[3] = 0;
  7533. vert[4] = 0;
  7534. vert[5] = fillStyle[0];
  7535. vert[6] = fillStyle[1];
  7536. vert[7] = fillStyle[2];
  7537. vert[8] = fillStyle[3];
  7538. vert[9] = strokeStyle[0];
  7539. vert[10] = strokeStyle[1];
  7540. vert[11] = strokeStyle[2];
  7541. vert[12] = strokeStyle[3];
  7542. vert[13] = normalX;
  7543. vert[14] = normalY;
  7544. vert[15] = normalZ;
  7545. vertArray.unshift(vert);
  7546. for(i = 0; i < vertArray.length; i++){
  7547. for(j = 0; j < 3; j++){
  7548. fillVertArray.push(vertArray[i][j]);
  7549. }
  7550. for(j = 5; j < 9; j++){
  7551. colorVertArray.push(vertArray[i][j]);
  7552. }
  7553. }
  7554. fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray);
  7555. }
  7556. }
  7557. };
  7558. p.normal = function normal(nx, ny, nz) {
  7559. if (arguments.length !== 3 || !(typeof nx === "number" && typeof ny === "number" && typeof nz === "number")) {
  7560. throw "normal() requires three numeric arguments.";
  7561. }
  7562. normalX = nx;
  7563. normalY = ny;
  7564. normalZ = nz;
  7565. if (curShape !== 0) {
  7566. if (normalMode === p.NORMAL_MODE_AUTO) {
  7567. normalMode = p.NORMAL_MODE_SHAPE;
  7568. } else if (normalMode === p.NORMAL_MODE_SHAPE) {
  7569. normalMode = p.NORMAL_MODE_VERTEX;
  7570. }
  7571. }
  7572. };
  7573. ////////////////////////////////////////////////////////////////////////////
  7574. // Raster drawing functions
  7575. ////////////////////////////////////////////////////////////////////////////
  7576. p.save = function save(file, img) {
  7577. // file is unused at the moment
  7578. // may implement this differently in later release
  7579. if (img !== undef) {
  7580. return window.open(img.toDataURL(),"_blank");
  7581. } else {
  7582. return window.open(p.externals.canvas.toDataURL(),"_blank");
  7583. }
  7584. };
  7585. var canvasDataCache = [undef, undef, undef]; // we need three for now
  7586. function getCanvasData(obj, w, h) {
  7587. var canvasData = canvasDataCache.shift();
  7588. if(canvasData === undef) {
  7589. canvasData = {};
  7590. canvasData.canvas = document.createElement("canvas");
  7591. canvasData.context = canvasData.canvas.getContext('2d');
  7592. }
  7593. canvasDataCache.push(canvasData);
  7594. var canvas = canvasData.canvas, context = canvasData.context,
  7595. width = w || obj.width, height = h || obj.height;
  7596. canvas.width = width; canvas.height = height;
  7597. if(!obj) {
  7598. context.clearRect(0, 0, width, height);
  7599. } else if("data" in obj) { // ImageData
  7600. context.putImageData(obj, 0, 0);
  7601. } else {
  7602. context.clearRect(0, 0, width, height);
  7603. context.drawImage(obj, 0, 0, width, height);
  7604. }
  7605. return canvasData;
  7606. }
  7607. var PImage = function PImage(aWidth, aHeight, aFormat) {
  7608. this.get = function(x, y, w, h) {
  7609. if (!arguments.length) {
  7610. return p.get(this);
  7611. } else if (arguments.length === 2) {
  7612. return p.get(x, y, this);
  7613. } else if (arguments.length === 4) {
  7614. return p.get(x, y, w, h, this);
  7615. }
  7616. };
  7617. this.set = function(x, y, c) {
  7618. p.set(x, y, c, this);
  7619. };
  7620. this.blend = function(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE) {
  7621. if (arguments.length === 9) {
  7622. p.blend(this, srcImg, x, y, width, height, dx, dy, dwidth, dheight, this);
  7623. } else if (arguments.length === 10) {
  7624. p.blend(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE, this);
  7625. }
  7626. };
  7627. this.copy = function(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight) {
  7628. if (arguments.length === 8) {
  7629. p.blend(this, srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, p.REPLACE, this);
  7630. } else if (arguments.length === 9) {
  7631. p.blend(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight, p.REPLACE, this);
  7632. }
  7633. };
  7634. this.filter = function(mode, param) {
  7635. if (arguments.length === 2) {
  7636. p.filter(mode, param, this);
  7637. } else if (arguments.length === 1) {
  7638. // no param specified, send null to show its invalid
  7639. p.filter(mode, null, this);
  7640. }
  7641. };
  7642. this.save = function(file){
  7643. p.save(file,this);
  7644. };
  7645. this.resize = function(w, h) {
  7646. if (this.width !== 0 || this.height !== 0) {
  7647. // make aspect ratio if w or h is 0
  7648. if (w === 0 && h !== 0) {
  7649. w = this.width / this.height * h;
  7650. } else if (h === 0 && w !== 0) {
  7651. h = w / (this.width / this.height);
  7652. }
  7653. // put 'this.imageData' into a new canvas
  7654. var canvas = getCanvasData(this.imageData).canvas;
  7655. // pull imageData object out of canvas into ImageData object
  7656. var imageData = getCanvasData(canvas, w, h).context.getImageData(0, 0, w, h);
  7657. // set this as new pimage
  7658. this.fromImageData(imageData);
  7659. }
  7660. };
  7661. this.mask = function(mask) {
  7662. this._mask = undef;
  7663. if (mask instanceof PImage) {
  7664. if (mask.width === this.width && mask.height === this.height) {
  7665. this._mask = mask;
  7666. } else {
  7667. throw "mask must have the same dimensions as PImage.";
  7668. }
  7669. } else if (typeof mask === "object" && mask.constructor === Array) { // this is a pixel array
  7670. // mask pixel array needs to be the same length as this.pixels
  7671. // how do we update this for 0.9 this.imageData holding pixels ^^
  7672. // mask.constructor ? and this.pixels.length = this.imageData.data.length instead ?
  7673. if (this.pixels.length === mask.length) {
  7674. this._mask = mask;
  7675. } else {
  7676. throw "mask array must be the same length as PImage pixels array.";
  7677. }
  7678. }
  7679. };
  7680. // handle the sketch code for pixels[] and pixels.length
  7681. // parser code converts pixels[] to getPixels()
  7682. // or setPixels(), .length becomes getLength()
  7683. this.pixels = {
  7684. getLength: (function(aImg) {
  7685. return function() {
  7686. return aImg.imageData.data.length ? aImg.imageData.data.length/4 : 0;
  7687. };
  7688. }(this)),
  7689. getPixel: (function(aImg) {
  7690. return function(i) {
  7691. var offset = i*4;
  7692. return p.color.toInt(aImg.imageData.data[offset], aImg.imageData.data[offset+1],
  7693. aImg.imageData.data[offset+2], aImg.imageData.data[offset+3]);
  7694. };
  7695. }(this)),
  7696. setPixel: (function(aImg) {
  7697. return function(i,c) {
  7698. var offset = i*4;
  7699. aImg.imageData.data[offset] = (c & p.RED_MASK) >>> 16;
  7700. aImg.imageData.data[offset+1] = (c & p.GREEN_MASK) >>> 8;
  7701. aImg.imageData.data[offset+2] = (c & p.BLUE_MASK);
  7702. aImg.imageData.data[offset+3] = (c & p.ALPHA_MASK) >>> 24;
  7703. };
  7704. }(this)),
  7705. set: function(arr) {
  7706. for (var i = 0, aL = arr.length; i < aL; i++) {
  7707. this.setPixel(i, arr[i]);
  7708. }
  7709. }
  7710. };
  7711. // These are intentionally left blank for PImages, we work live with pixels and draw as necessary
  7712. this.loadPixels = function() {};
  7713. this.updatePixels = function() {};
  7714. this.toImageData = function() {
  7715. if (this.isRemote) { // Remote images cannot access imageData, send source image instead
  7716. return this.sourceImg;
  7717. } else {
  7718. var canvasData = getCanvasData(this.imageData);
  7719. return canvasData.context.getImageData(0, 0, this.width, this.height);
  7720. }
  7721. };
  7722. this.toDataURL = function() {
  7723. var canvasData = getCanvasData(this.imageData);
  7724. return canvasData.canvas.toDataURL();
  7725. };
  7726. this.fromImageData = function(canvasImg) {
  7727. this.width = canvasImg.width;
  7728. this.height = canvasImg.height;
  7729. this.imageData = canvasImg;
  7730. // changed for 0.9
  7731. this.format = p.ARGB;
  7732. };
  7733. this.fromHTMLImageData = function(htmlImg) {
  7734. // convert an <img> to a PImage
  7735. var canvasData = getCanvasData(htmlImg);
  7736. try {
  7737. var imageData = canvasData.context.getImageData(0, 0, htmlImg.width, htmlImg.height);
  7738. this.fromImageData(imageData);
  7739. } catch(e) {
  7740. if (htmlImg.width && htmlImg.height) {
  7741. this.isRemote = true;
  7742. this.width = htmlImg.width;
  7743. this.height = htmlImg.height;
  7744. }
  7745. }
  7746. this.sourceImg = htmlImg;
  7747. };
  7748. if (arguments.length === 1) {
  7749. // convert an <img> to a PImage
  7750. this.fromHTMLImageData(arguments[0]);
  7751. } else if (arguments.length === 2 || arguments.length === 3) {
  7752. this.width = aWidth || 1;
  7753. this.height = aHeight || 1;
  7754. // changed for 0.9
  7755. this.imageData = curContext.createImageData(this.width, this.height);
  7756. this.format = (aFormat === p.ARGB || aFormat === p.ALPHA) ? aFormat : p.RGB;
  7757. } else {
  7758. this.width = 0;
  7759. this.height = 0;
  7760. this.imageData = curContext.createImageData(1, 1);
  7761. this.format = p.ARGB;
  7762. }
  7763. };
  7764. p.PImage = PImage;
  7765. try {
  7766. // Opera createImageData fix
  7767. if (! ("createImageData" in CanvasRenderingContext2D.prototype)) {
  7768. CanvasRenderingContext2D.prototype.createImageData = function(sw, sh) {
  7769. return this.getImageData(0, 0, sw, sh);
  7770. };
  7771. }
  7772. } catch(e) {}
  7773. p.createImage = function createImage(w, h, mode) {
  7774. // changed for 0.9
  7775. return new PImage(w,h,mode);
  7776. };
  7777. // Loads an image for display. Type is an extension. Callback is fired on load.
  7778. p.loadImage = function loadImage(file, type, callback) {
  7779. // if type is specified add it with a . to file to make the filename
  7780. if (type) {
  7781. file = file + "." + type;
  7782. }
  7783. // if image is in the preloader cache return a new PImage
  7784. if (curSketch.imageCache.images[file]) {
  7785. return new PImage(curSketch.imageCache.images[file]);
  7786. }
  7787. // else aysnc load it
  7788. else {
  7789. var pimg = new PImage(0, 0, p.ARGB);
  7790. var img = document.createElement('img');
  7791. pimg.sourceImg = img;
  7792. img.onload = (function(aImage, aPImage, aCallback) {
  7793. var image = aImage;
  7794. var pimg = aPImage;
  7795. var callback = aCallback;
  7796. return function() {
  7797. // change the <img> object into a PImage now that its loaded
  7798. pimg.fromHTMLImageData(image);
  7799. pimg.loaded = true;
  7800. if (callback) {
  7801. callback();
  7802. }
  7803. };
  7804. }(img, pimg, callback));
  7805. img.src = file; // needs to be called after the img.onload function is declared or it wont work in opera
  7806. return pimg;
  7807. }
  7808. };
  7809. // async loading of large images, same functionality as loadImage above
  7810. p.requestImage = p.loadImage;
  7811. function get$0() {
  7812. //return a PImage of curContext
  7813. var c = new PImage(p.width, p.height, p.RGB);
  7814. c.fromImageData(curContext.getImageData(0, 0, p.width, p.height));
  7815. return c;
  7816. }
  7817. function get$2(x,y) {
  7818. var data;
  7819. // return the color at x,y (int) of curContext
  7820. // create a PImage object of size 1x1 and return the int of the pixels array element 0
  7821. if (x < p.width && x >= 0 && y >= 0 && y < p.height) {
  7822. if(isContextReplaced) {
  7823. var offset = ((0|x) + p.width * (0|y))*4;
  7824. data = p.imageData.data;
  7825. return p.color.toInt(data[offset], data[offset+1],
  7826. data[offset+2], data[offset+3]);
  7827. }
  7828. // x,y is inside canvas space
  7829. data = curContext.getImageData(0|x, 0|y, 1, 1).data;
  7830. // changed for 0.9
  7831. return p.color.toInt(data[0], data[1], data[2], data[3]);
  7832. } else {
  7833. // x,y is outside image return transparent black
  7834. return 0;
  7835. }
  7836. }
  7837. function get$3(x,y,img) {
  7838. // PImage.get(x,y) was called, return the color (int) at x,y of img
  7839. // changed in 0.9
  7840. var offset = y * img.width * 4 + (x * 4);
  7841. return p.color.toInt(img.imageData.data[offset],
  7842. img.imageData.data[offset + 1],
  7843. img.imageData.data[offset + 2],
  7844. img.imageData.data[offset + 3]);
  7845. }
  7846. function get$4(x, y, w, h) {
  7847. // return a PImage of w and h from cood x,y of curContext
  7848. var c = new PImage(w, h, p.RGB);
  7849. c.fromImageData(curContext.getImageData(x, y, w, h));
  7850. return c;
  7851. }
  7852. function get$5(x, y, w, h, img) {
  7853. // PImage.get(x,y,w,h) was called, return x,y,w,h PImage of img
  7854. // changed for 0.9, offset start point needs to be *4
  7855. var start = y * img.width * 4 + (x*4);
  7856. var end = (y + h) * img.width * 4 + ((x + w) * 4);
  7857. var c = new PImage(w, h, p.RGB);
  7858. for (var i = start, j = 0; i < end; i++, j++) {
  7859. // changed in 0.9
  7860. c.imageData.data[j] = img.imageData.data[i];
  7861. if ((j+1) % (w*4) === 0) {
  7862. //completed one line, increment i by offset
  7863. i += (img.width - w) * 4;
  7864. }
  7865. }
  7866. return c;
  7867. }
  7868. // Gets a single pixel or block of pixels from the current Canvas Context or a PImage
  7869. p.get = function get(x, y, w, h, img) {
  7870. // for 0 2 and 4 arguments use curContext, otherwise PImage.get was called
  7871. if (arguments.length === 2) {
  7872. return get$2(x, y);
  7873. } else if (arguments.length === 0) {
  7874. return get$0();
  7875. } else if (arguments.length === 5) {
  7876. return get$5(x, y, w, h, img);
  7877. } else if (arguments.length === 4) {
  7878. return get$4(x, y, w, h);
  7879. } else if (arguments.length === 3) {
  7880. return get$3(x, y, w);
  7881. } else if (arguments.length === 1) {
  7882. // PImage.get() was called, return the PImage
  7883. return x;
  7884. }
  7885. };
  7886. // Creates a new Processing instance and passes it back for... processing
  7887. p.createGraphics = function createGraphics(w, h, render) {
  7888. var canvas = document.createElement("canvas");
  7889. var pg = new Processing(canvas);
  7890. pg.size(w, h, render);
  7891. pg.canvas = canvas;
  7892. //Processing.addInstance(pg); // TODO: this function does not exist in this scope
  7893. return pg;
  7894. };
  7895. // pixels caching
  7896. function resetContext() {
  7897. if(isContextReplaced) {
  7898. curContext = originalContext;
  7899. isContextReplaced = false;
  7900. p.updatePixels();
  7901. }
  7902. }
  7903. function SetPixelContextWrapper() {
  7904. function wrapFunction(newContext, name) {
  7905. function wrapper() {
  7906. resetContext();
  7907. curContext[name].apply(curContext, arguments);
  7908. }
  7909. newContext[name] = wrapper;
  7910. }
  7911. function wrapProperty(newContext, name) {
  7912. function getter() {
  7913. resetContext();
  7914. return curContext[name];
  7915. }
  7916. function setter(value) {
  7917. resetContext();
  7918. curContext[name] = value;
  7919. }
  7920. newContext.__defineGetter__(name, getter);
  7921. newContext.__defineSetter__(name, setter);
  7922. }
  7923. for(var n in curContext) {
  7924. if(typeof curContext[n] === 'function') {
  7925. wrapFunction(this, n);
  7926. } else {
  7927. wrapProperty(this, n);
  7928. }
  7929. }
  7930. }
  7931. function replaceContext() {
  7932. if(isContextReplaced) {
  7933. return;
  7934. }
  7935. p.loadPixels();
  7936. if(proxyContext === null) {
  7937. originalContext = curContext;
  7938. proxyContext = new SetPixelContextWrapper();
  7939. }
  7940. isContextReplaced = true;
  7941. curContext = proxyContext;
  7942. setPixelsCached = 0;
  7943. }
  7944. function set$3(x, y, c) {
  7945. if (x < p.width && x >= 0 && y >= 0 && y < p.height) {
  7946. replaceContext();
  7947. p.pixels.setPixel((0|x)+p.width*(0|y), c);
  7948. if(++setPixelsCached > maxPixelsCached) {
  7949. resetContext();
  7950. }
  7951. }
  7952. }
  7953. function set$4(x, y, obj, img) {
  7954. var c = p.color.toArray(obj);
  7955. var offset = y * img.width * 4 + (x*4);
  7956. var data = img.imageData.data;
  7957. data[offset] = c[0];
  7958. data[offset+1] = c[1];
  7959. data[offset+2] = c[2];
  7960. data[offset+3] = c[3];
  7961. }
  7962. // Paints a pixel array into the canvas
  7963. p.set = function set(x, y, obj, img) {
  7964. var color, oldFill;
  7965. if (arguments.length === 3) {
  7966. // called p.set(), was it with a color or a img ?
  7967. if (typeof obj === "number") {
  7968. set$3(x, y, obj);
  7969. } else if (obj instanceof PImage) {
  7970. p.image(obj, x, y);
  7971. }
  7972. } else if (arguments.length === 4) {
  7973. // PImage.set(x,y,c) was called, set coordinate x,y color to c of img
  7974. set$4(x, y, obj, img);
  7975. }
  7976. };
  7977. p.imageData = {};
  7978. // handle the sketch code for pixels[]
  7979. // parser code converts pixels[] to getPixels()
  7980. // or setPixels(), .length becomes getLength()
  7981. p.pixels = {
  7982. getLength: function() { return p.imageData.data.length ? p.imageData.data.length/4 : 0; },
  7983. getPixel: function(i) {
  7984. var offset = i*4;
  7985. return (p.imageData.data[offset+3] << 24) & 0xff000000 |
  7986. (p.imageData.data[offset+0] << 16) & 0x00ff0000 |
  7987. (p.imageData.data[offset+1] << 8) & 0x0000ff00 |
  7988. p.imageData.data[offset+2] & 0x000000ff;
  7989. },
  7990. setPixel: function(i,c) {
  7991. var offset = i*4;
  7992. p.imageData.data[offset+0] = (c & 0x00ff0000) >>> 16; // RED_MASK
  7993. p.imageData.data[offset+1] = (c & 0x0000ff00) >>> 8; // GREEN_MASK
  7994. p.imageData.data[offset+2] = (c & 0x000000ff); // BLUE_MASK
  7995. p.imageData.data[offset+3] = (c & 0xff000000) >>> 24; // ALPHA_MASK
  7996. },
  7997. set: function(arr) {
  7998. for (var i = 0, aL = arr.length; i < aL; i++) {
  7999. this.setPixel(i, arr[i]);
  8000. }
  8001. }
  8002. };
  8003. // Gets a 1-Dimensional pixel array from Canvas
  8004. p.loadPixels = function() {
  8005. // changed in 0.9
  8006. p.imageData = curContext.getImageData(0, 0, p.width, p.height);
  8007. };
  8008. // Draws a 1-Dimensional pixel array to Canvas
  8009. p.updatePixels = function() {
  8010. // changed in 0.9
  8011. if (p.imageData) {
  8012. curContext.putImageData(p.imageData, 0, 0);
  8013. }
  8014. };
  8015. p.hint = function hint(which) {
  8016. if (which === p.DISABLE_DEPTH_TEST) {
  8017. curContext.disable(curContext.DEPTH_TEST);
  8018. curContext.depthMask(false);
  8019. curContext.clear(curContext.DEPTH_BUFFER_BIT);
  8020. }
  8021. else if (which === p.ENABLE_DEPTH_TEST) {
  8022. curContext.enable(curContext.DEPTH_TEST);
  8023. curContext.depthMask(true);
  8024. }
  8025. };
  8026. // Draw an image or a color to the background
  8027. p.background = function background() {
  8028. var color, a, img;
  8029. // background params are either a color or a PImage
  8030. if (typeof arguments[0] === 'number') {
  8031. color = p.color.apply(this, arguments);
  8032. // override alpha value, processing ignores the alpha for background color
  8033. if (curSketch.options.isOpaque) {
  8034. color = color | p.ALPHA_MASK;
  8035. }
  8036. } else if (arguments.length === 1 && arguments[0] instanceof PImage) {
  8037. img = arguments[0];
  8038. if (!img.pixels || img.width !== p.width || img.height !== p.height) {
  8039. throw "Background image must be the same dimensions as the canvas.";
  8040. }
  8041. } else {
  8042. throw "Incorrect background parameters.";
  8043. }
  8044. if (p.use3DContext) {
  8045. if (color !== undef) {
  8046. var c = p.color.toGLArray(color);
  8047. refreshBackground = function() {
  8048. curContext.clearColor(c[0], c[1], c[2], c[3]);
  8049. curContext.clear(curContext.COLOR_BUFFER_BIT | curContext.DEPTH_BUFFER_BIT);
  8050. };
  8051. } else {
  8052. // Handle image background for 3d context. not done yet.
  8053. refreshBackground = function() {};
  8054. }
  8055. } else { // 2d context
  8056. if (color !== undef) {
  8057. refreshBackground = function() {
  8058. curContext.fillStyle = p.color.toString(color);
  8059. curContext.fillRect(0, 0, p.width, p.height);
  8060. isFillDirty = true;
  8061. };
  8062. } else {
  8063. refreshBackground = function() {
  8064. p.image(img, 0, 0);
  8065. };
  8066. }
  8067. }
  8068. refreshBackground();
  8069. };
  8070. // Draws an image to the Canvas
  8071. p.image = function image(img, x, y, w, h) {
  8072. if (img.width > 0) {
  8073. var bounds = imageModeConvert(x || 0, y || 0, w || img.width, h || img.height, arguments.length < 4);
  8074. var obj = img.toImageData();
  8075. if (img._mask) {
  8076. var j, size;
  8077. if (img._mask instanceof PImage) {
  8078. var objMask = img._mask.toImageData();
  8079. for (j = 2, size = img.width * img.height * 4; j < size; j += 4) {
  8080. // using it as an alpha channel
  8081. obj.data[j + 1] = objMask.data[j];
  8082. // but only the blue color channel
  8083. }
  8084. } else {
  8085. for (j = 0, size = img._mask.length; j < size; ++j) {
  8086. obj.data[(j << 2) + 3] = img._mask[j];
  8087. }
  8088. }
  8089. }
  8090. // draw the image
  8091. curTint(obj);
  8092. curContext.drawImage(getCanvasData(obj).canvas, 0, 0, img.width, img.height,
  8093. bounds.x, bounds.y, bounds.w, bounds.h);
  8094. }
  8095. };
  8096. // Clears a rectangle in the Canvas element or the whole Canvas
  8097. p.clear = function clear(x, y, width, height) {
  8098. if (arguments.length === 0) {
  8099. curContext.clearRect(0, 0, p.width, p.height);
  8100. } else {
  8101. curContext.clearRect(x, y, width, height);
  8102. }
  8103. };
  8104. p.tint = function tint() {
  8105. var tintColor = p.color.apply(this, arguments);
  8106. var r = p.red(tintColor) / colorModeX;
  8107. var g = p.green(tintColor) / colorModeY;
  8108. var b = p.blue(tintColor) / colorModeZ;
  8109. var a = p.alpha(tintColor) / colorModeA;
  8110. curTint = function(obj) {
  8111. var data = obj.data,
  8112. length = 4 * obj.width * obj.height;
  8113. for (var i = 0; i < length;) {
  8114. data[i++] *= r;
  8115. data[i++] *= g;
  8116. data[i++] *= b;
  8117. data[i++] *= a;
  8118. }
  8119. };
  8120. };
  8121. p.noTint = function noTint() {
  8122. curTint = function() {};
  8123. };
  8124. p.copy = function copy(src, sx, sy, sw, sh, dx, dy, dw, dh) {
  8125. if (arguments.length === 8) {
  8126. // shift everything, and introduce p
  8127. dh = dw;
  8128. dw = dy;
  8129. dy = dx;
  8130. dx = sh;
  8131. sh = sw;
  8132. sw = sy;
  8133. sy = sx;
  8134. sx = src;
  8135. src = p;
  8136. }
  8137. p.blend(src, sx, sy, sw, sh, dx, dy, dw, dh, p.REPLACE);
  8138. };
  8139. p.blend = function blend(src, sx, sy, sw, sh, dx, dy, dw, dh, mode, pimgdest) {
  8140. if (arguments.length === 9) {
  8141. // shift everything, and introduce p
  8142. mode = dh;
  8143. dh = dw;
  8144. dw = dy;
  8145. dy = dx;
  8146. dx = sh;
  8147. sh = sw;
  8148. sw = sy;
  8149. sy = sx;
  8150. sx = src;
  8151. src = p;
  8152. }
  8153. var sx2 = sx + sw;
  8154. var sy2 = sy + sh;
  8155. var dx2 = dx + dw;
  8156. var dy2 = dy + dh;
  8157. var dest;
  8158. // check if pimgdest is there and pixels, if so this was a call from pimg.blend
  8159. if (arguments.length === 10 || arguments.length === 9) {
  8160. p.loadPixels();
  8161. dest = p;
  8162. } else if (arguments.length === 11 && pimgdest && pimgdest.imageData) {
  8163. dest = pimgdest;
  8164. }
  8165. if (src === p) {
  8166. if (p.intersect(sx, sy, sx2, sy2, dx, dy, dx2, dy2)) {
  8167. p.blit_resize(p.get(sx, sy, sx2 - sx, sy2 - sy), 0, 0, sx2 - sx - 1, sy2 - sy - 1,
  8168. dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
  8169. } else {
  8170. // same as below, except skip the loadPixels() because it'd be redundant
  8171. p.blit_resize(src, sx, sy, sx2, sy2, dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
  8172. }
  8173. } else {
  8174. src.loadPixels();
  8175. p.blit_resize(src, sx, sy, sx2, sy2, dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
  8176. }
  8177. if (arguments.length === 10) {
  8178. p.updatePixels();
  8179. }
  8180. };
  8181. // helper function for filter()
  8182. var buildBlurKernel = function buildBlurKernel(r) {
  8183. var radius = p.floor(r * 3.5), i, radiusi;
  8184. radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248);
  8185. if (p.shared.blurRadius !== radius) {
  8186. p.shared.blurRadius = radius;
  8187. p.shared.blurKernelSize = 1 + (p.shared.blurRadius<<1);
  8188. p.shared.blurKernel = new Array(p.shared.blurKernelSize);
  8189. // init blurKernel
  8190. for (i = 0; i < p.shared.blurKernelSize; i++) {
  8191. p.shared.blurKernel[i] = 0;
  8192. }
  8193. for (i = 1, radiusi = radius - 1; i < radius; i++) {
  8194. p.shared.blurKernel[radius+i] = p.shared.blurKernel[radiusi] = radiusi * radiusi;
  8195. }
  8196. p.shared.blurKernel[radius] = radius * radius;
  8197. }
  8198. };
  8199. var blurARGB = function blurARGB(r, aImg) {
  8200. var sum, cr, cg, cb, ca, c, m;
  8201. var read, ri, ym, ymi, bk0;
  8202. var wh = aImg.pixels.getLength();
  8203. var r2 = new Array(wh);
  8204. var g2 = new Array(wh);
  8205. var b2 = new Array(wh);
  8206. var a2 = new Array(wh);
  8207. var yi = 0;
  8208. var x, y, i;
  8209. buildBlurKernel(r);
  8210. for (y = 0; y < aImg.height; y++) {
  8211. for (x = 0; x < aImg.width; x++) {
  8212. cb = cg = cr = ca = sum = 0;
  8213. read = x - p.shared.blurRadius;
  8214. if (read<0) {
  8215. bk0 = -read;
  8216. read = 0;
  8217. } else {
  8218. if (read >= aImg.width) {
  8219. break;
  8220. }
  8221. bk0=0;
  8222. }
  8223. for (i = bk0; i < p.shared.blurKernelSize; i++) {
  8224. if (read >= aImg.width) {
  8225. break;
  8226. }
  8227. c = aImg.pixels.getPixel(read + yi);
  8228. m = p.shared.blurKernel[i];
  8229. ca += m * ((c & p.ALPHA_MASK) >>> 24);
  8230. cr += m * ((c & p.RED_MASK) >> 16);
  8231. cg += m * ((c & p.GREEN_MASK) >> 8);
  8232. cb += m * (c & p.BLUE_MASK);
  8233. sum += m;
  8234. read++;
  8235. }
  8236. ri = yi + x;
  8237. a2[ri] = ca / sum;
  8238. r2[ri] = cr / sum;
  8239. g2[ri] = cg / sum;
  8240. b2[ri] = cb / sum;
  8241. }
  8242. yi += aImg.width;
  8243. }
  8244. yi = 0;
  8245. ym = -p.shared.blurRadius;
  8246. ymi = ym*aImg.width;
  8247. for (y = 0; y < aImg.height; y++) {
  8248. for (x = 0; x < aImg.width; x++) {
  8249. cb = cg = cr = ca = sum = 0;
  8250. if (ym<0) {
  8251. bk0 = ri = -ym;
  8252. read = x;
  8253. } else {
  8254. if (ym >= aImg.height) {
  8255. break;
  8256. }
  8257. bk0 = 0;
  8258. ri = ym;
  8259. read = x + ymi;
  8260. }
  8261. for (i = bk0; i < p.shared.blurKernelSize; i++) {
  8262. if (ri >= aImg.height) {
  8263. break;
  8264. }
  8265. m = p.shared.blurKernel[i];
  8266. ca += m * a2[read];
  8267. cr += m * r2[read];
  8268. cg += m * g2[read];
  8269. cb += m * b2[read];
  8270. sum += m;
  8271. ri++;
  8272. read += aImg.width;
  8273. }
  8274. aImg.pixels.setPixel(x+yi, ((ca/sum)<<24 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum)));
  8275. }
  8276. yi += aImg.width;
  8277. ymi += aImg.width;
  8278. ym++;
  8279. }
  8280. };
  8281. // helper funtion for ERODE and DILATE modes of filter()
  8282. var dilate = function dilate(isInverted, aImg) {
  8283. var currIdx = 0;
  8284. var maxIdx = aImg.pixels.getLength();
  8285. var out = new Array(maxIdx);
  8286. var currRowIdx, maxRowIdx, colOrig, colOut, currLum;
  8287. var idxRight, idxLeft, idxUp, idxDown,
  8288. colRight, colLeft, colUp, colDown,
  8289. lumRight, lumLeft, lumUp, lumDown;
  8290. if (!isInverted) {
  8291. // erosion (grow light areas)
  8292. while (currIdx<maxIdx) {
  8293. currRowIdx = currIdx;
  8294. maxRowIdx = currIdx + aImg.width;
  8295. while (currIdx < maxRowIdx) {
  8296. colOrig = colOut = aImg.pixels.getPixel(currIdx);
  8297. idxLeft = currIdx - 1;
  8298. idxRight = currIdx + 1;
  8299. idxUp = currIdx - aImg.width;
  8300. idxDown = currIdx + aImg.width;
  8301. if (idxLeft < currRowIdx) {
  8302. idxLeft = currIdx;
  8303. }
  8304. if (idxRight >= maxRowIdx) {
  8305. idxRight = currIdx;
  8306. }
  8307. if (idxUp < 0) {
  8308. idxUp = 0;
  8309. }
  8310. if (idxDown >= maxIdx) {
  8311. idxDown = currIdx;
  8312. }
  8313. colUp = aImg.pixels.getPixel(idxUp);
  8314. colLeft = aImg.pixels.getPixel(idxLeft);
  8315. colDown = aImg.pixels.getPixel(idxDown);
  8316. colRight = aImg.pixels.getPixel(idxRight);
  8317. // compute luminance
  8318. currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff);
  8319. lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
  8320. lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
  8321. lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
  8322. lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
  8323. if (lumLeft > currLum) {
  8324. colOut = colLeft;
  8325. currLum = lumLeft;
  8326. }
  8327. if (lumRight > currLum) {
  8328. colOut = colRight;
  8329. currLum = lumRight;
  8330. }
  8331. if (lumUp > currLum) {
  8332. colOut = colUp;
  8333. currLum = lumUp;
  8334. }
  8335. if (lumDown > currLum) {
  8336. colOut = colDown;
  8337. currLum = lumDown;
  8338. }
  8339. out[currIdx++] = colOut;
  8340. }
  8341. }
  8342. } else {
  8343. // dilate (grow dark areas)
  8344. while (currIdx < maxIdx) {
  8345. currRowIdx = currIdx;
  8346. maxRowIdx = currIdx + aImg.width;
  8347. while (currIdx < maxRowIdx) {
  8348. colOrig = colOut = aImg.pixels.getPixel(currIdx);
  8349. idxLeft = currIdx - 1;
  8350. idxRight = currIdx + 1;
  8351. idxUp = currIdx - aImg.width;
  8352. idxDown = currIdx + aImg.width;
  8353. if (idxLeft < currRowIdx) {
  8354. idxLeft = currIdx;
  8355. }
  8356. if (idxRight >= maxRowIdx) {
  8357. idxRight = currIdx;
  8358. }
  8359. if (idxUp < 0) {
  8360. idxUp = 0;
  8361. }
  8362. if (idxDown >= maxIdx) {
  8363. idxDown = currIdx;
  8364. }
  8365. colUp = aImg.pixels.getPixel(idxUp);
  8366. colLeft = aImg.pixels.getPixel(idxLeft);
  8367. colDown = aImg.pixels.getPixel(idxDown);
  8368. colRight = aImg.pixels.getPixel(idxRight);
  8369. // compute luminance
  8370. currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff);
  8371. lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
  8372. lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
  8373. lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
  8374. lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
  8375. if (lumLeft < currLum) {
  8376. colOut = colLeft;
  8377. currLum = lumLeft;
  8378. }
  8379. if (lumRight < currLum) {
  8380. colOut = colRight;
  8381. currLum = lumRight;
  8382. }
  8383. if (lumUp < currLum) {
  8384. colOut = colUp;
  8385. currLum = lumUp;
  8386. }
  8387. if (lumDown < currLum) {
  8388. colOut = colDown;
  8389. currLum = lumDown;
  8390. }
  8391. out[currIdx++]=colOut;
  8392. }
  8393. }
  8394. }
  8395. aImg.pixels.set(out);
  8396. //p.arraycopy(out,0,pixels,0,maxIdx);
  8397. };
  8398. p.filter = function filter(kind, param, aImg){
  8399. var img, col, lum, i;
  8400. if (arguments.length === 3) {
  8401. aImg.loadPixels();
  8402. img = aImg;
  8403. } else {
  8404. p.loadPixels();
  8405. img = p;
  8406. }
  8407. if (param === undef) {
  8408. param = null;
  8409. }
  8410. var imglen = img.pixels.getLength();
  8411. switch (kind) {
  8412. case p.BLUR:
  8413. var radius = param || 1; // if no param specified, use 1 (default for p5)
  8414. blurARGB(radius, img);
  8415. break;
  8416. case p.GRAY:
  8417. if (img.format === p.ALPHA) { //trouble
  8418. // for an alpha image, convert it to an opaque grayscale
  8419. for (i = 0; i < imglen; i++) {
  8420. col = 255 - img.pixels.getPixel(i);
  8421. img.pixels.setPixel(i,(0xff000000 | (col << 16) | (col << 8) | col));
  8422. }
  8423. img.format = p.RGB; //trouble
  8424. } else {
  8425. for (i = 0; i < imglen; i++) {
  8426. col = img.pixels.getPixel(i);
  8427. lum = (77*(col>>16&0xff) + 151*(col>>8&0xff) + 28*(col&0xff))>>8;
  8428. img.pixels.setPixel(i,((col & p.ALPHA_MASK) | lum<<16 | lum<<8 | lum));
  8429. }
  8430. }
  8431. break;
  8432. case p.INVERT:
  8433. for (i = 0; i < imglen; i++) {
  8434. img.pixels.setPixel(i, (img.pixels.getPixel(i) ^ 0xffffff));
  8435. }
  8436. break;
  8437. case p.POSTERIZE:
  8438. if(param === null) {
  8439. throw "Use filter(POSTERIZE, int levels) instead of filter(POSTERIZE)";
  8440. }
  8441. var levels = p.floor(param);
  8442. if ((levels < 2) || (levels > 255)) {
  8443. throw "Levels must be between 2 and 255 for filter(POSTERIZE, levels)";
  8444. }
  8445. var levels1 = levels - 1;
  8446. for (i = 0; i < imglen; i++) {
  8447. var rlevel = (img.pixels.getPixel(i) >> 16) & 0xff;
  8448. var glevel = (img.pixels.getPixel(i) >> 8) & 0xff;
  8449. var blevel = img.pixels.getPixel(i) & 0xff;
  8450. rlevel = (((rlevel * levels) >> 8) * 255) / levels1;
  8451. glevel = (((glevel * levels) >> 8) * 255) / levels1;
  8452. blevel = (((blevel * levels) >> 8) * 255) / levels1;
  8453. img.pixels.setPixel(i, ((0xff000000 & img.pixels.getPixel(i)) | (rlevel << 16) | (glevel << 8) | blevel));
  8454. }
  8455. break;
  8456. case p.OPAQUE:
  8457. for (i = 0; i < imglen; i++) {
  8458. img.pixels.setPixel(i, (img.pixels.getPixel(i) | 0xff000000));
  8459. }
  8460. img.format = p.RGB; //trouble
  8461. break;
  8462. case p.THRESHOLD:
  8463. if (param === null) {
  8464. param = 0.5;
  8465. }
  8466. if ((param < 0) || (param > 1)) {
  8467. throw "Level must be between 0 and 1 for filter(THRESHOLD, level)";
  8468. }
  8469. var thresh = p.floor(param * 255);
  8470. for (i = 0; i < imglen; i++) {
  8471. var max = p.max((img.pixels.getPixel(i) & p.RED_MASK) >> 16,
  8472. p.max((img.pixels.getPixel(i) & p.GREEN_MASK) >> 8,
  8473. (img.pixels.getPixel(i) & p.BLUE_MASK)));
  8474. img.pixels.setPixel(i, ((img.pixels.getPixel(i) & p.ALPHA_MASK) |
  8475. ((max < thresh) ? 0x000000 : 0xffffff)));
  8476. }
  8477. break;
  8478. case p.ERODE:
  8479. dilate(true, img);
  8480. break;
  8481. case p.DILATE:
  8482. dilate(false, img);
  8483. break;
  8484. }
  8485. img.updatePixels();
  8486. };
  8487. // shared variables for blit_resize(), filter_new_scanline(), filter_bilinear(), filter()
  8488. // change this in the future to not be exposed to p
  8489. p.shared = {
  8490. fracU: 0,
  8491. ifU: 0,
  8492. fracV: 0,
  8493. ifV: 0,
  8494. u1: 0,
  8495. u2: 0,
  8496. v1: 0,
  8497. v2: 0,
  8498. sX: 0,
  8499. sY: 0,
  8500. iw: 0,
  8501. iw1: 0,
  8502. ih1: 0,
  8503. ul: 0,
  8504. ll: 0,
  8505. ur: 0,
  8506. lr: 0,
  8507. cUL: 0,
  8508. cLL: 0,
  8509. cUR: 0,
  8510. cLR: 0,
  8511. srcXOffset: 0,
  8512. srcYOffset: 0,
  8513. r: 0,
  8514. g: 0,
  8515. b: 0,
  8516. a: 0,
  8517. srcBuffer: null,
  8518. blurRadius: 0,
  8519. blurKernelSize: 0,
  8520. blurKernel: null
  8521. };
  8522. p.intersect = function intersect(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2) {
  8523. var sw = sx2 - sx1 + 1;
  8524. var sh = sy2 - sy1 + 1;
  8525. var dw = dx2 - dx1 + 1;
  8526. var dh = dy2 - dy1 + 1;
  8527. if (dx1 < sx1) {
  8528. dw += dx1 - sx1;
  8529. if (dw > sw) {
  8530. dw = sw;
  8531. }
  8532. } else {
  8533. var w = sw + sx1 - dx1;
  8534. if (dw > w) {
  8535. dw = w;
  8536. }
  8537. }
  8538. if (dy1 < sy1) {
  8539. dh += dy1 - sy1;
  8540. if (dh > sh) {
  8541. dh = sh;
  8542. }
  8543. } else {
  8544. var h = sh + sy1 - dy1;
  8545. if (dh > h) {
  8546. dh = h;
  8547. }
  8548. }
  8549. return ! (dw <= 0 || dh <= 0);
  8550. };
  8551. p.filter_new_scanline = function filter_new_scanline() {
  8552. p.shared.sX = p.shared.srcXOffset;
  8553. p.shared.fracV = p.shared.srcYOffset & p.PREC_MAXVAL;
  8554. p.shared.ifV = p.PREC_MAXVAL - p.shared.fracV;
  8555. p.shared.v1 = (p.shared.srcYOffset >> p.PRECISIONB) * p.shared.iw;
  8556. p.shared.v2 = Math.min((p.shared.srcYOffset >> p.PRECISIONB) + 1, p.shared.ih1) * p.shared.iw;
  8557. };
  8558. p.filter_bilinear = function filter_bilinear() {
  8559. p.shared.fracU = p.shared.sX & p.PREC_MAXVAL;
  8560. p.shared.ifU = p.PREC_MAXVAL - p.shared.fracU;
  8561. p.shared.ul = (p.shared.ifU * p.shared.ifV) >> p.PRECISIONB;
  8562. p.shared.ll = (p.shared.ifU * p.shared.fracV) >> p.PRECISIONB;
  8563. p.shared.ur = (p.shared.fracU * p.shared.ifV) >> p.PRECISIONB;
  8564. p.shared.lr = (p.shared.fracU * p.shared.fracV) >> p.PRECISIONB;
  8565. p.shared.u1 = (p.shared.sX >> p.PRECISIONB);
  8566. p.shared.u2 = Math.min(p.shared.u1 + 1, p.shared.iw1);
  8567. // get color values of the 4 neighbouring texels
  8568. // changed for 0.9
  8569. var cULoffset = (p.shared.v1 + p.shared.u1) * 4;
  8570. var cURoffset = (p.shared.v1 + p.shared.u2) * 4;
  8571. var cLLoffset = (p.shared.v2 + p.shared.u1) * 4;
  8572. var cLRoffset = (p.shared.v2 + p.shared.u2) * 4;
  8573. p.shared.cUL = p.color.toInt(p.shared.srcBuffer[cULoffset], p.shared.srcBuffer[cULoffset+1],
  8574. p.shared.srcBuffer[cULoffset+2], p.shared.srcBuffer[cULoffset+3]);
  8575. p.shared.cUR = p.color.toInt(p.shared.srcBuffer[cURoffset], p.shared.srcBuffer[cURoffset+1],
  8576. p.shared.srcBuffer[cURoffset+2], p.shared.srcBuffer[cURoffset+3]);
  8577. p.shared.cLL = p.color.toInt(p.shared.srcBuffer[cLLoffset], p.shared.srcBuffer[cLLoffset+1],
  8578. p.shared.srcBuffer[cLLoffset+2], p.shared.srcBuffer[cLLoffset+3]);
  8579. p.shared.cLR = p.color.toInt(p.shared.srcBuffer[cLRoffset], p.shared.srcBuffer[cLRoffset+1],
  8580. p.shared.srcBuffer[cLRoffset+2], p.shared.srcBuffer[cLRoffset+3]);
  8581. p.shared.r = ((p.shared.ul * ((p.shared.cUL & p.RED_MASK) >> 16) + p.shared.ll *
  8582. ((p.shared.cLL & p.RED_MASK) >> 16) + p.shared.ur * ((p.shared.cUR & p.RED_MASK) >> 16) +
  8583. p.shared.lr * ((p.shared.cLR & p.RED_MASK) >> 16)) << p.PREC_RED_SHIFT) & p.RED_MASK;
  8584. p.shared.g = ((p.shared.ul * (p.shared.cUL & p.GREEN_MASK) + p.shared.ll * (p.shared.cLL & p.GREEN_MASK) +
  8585. p.shared.ur * (p.shared.cUR & p.GREEN_MASK) + p.shared.lr *
  8586. (p.shared.cLR & p.GREEN_MASK)) >>> p.PRECISIONB) & p.GREEN_MASK;
  8587. p.shared.b = (p.shared.ul * (p.shared.cUL & p.BLUE_MASK) + p.shared.ll * (p.shared.cLL & p.BLUE_MASK) +
  8588. p.shared.ur * (p.shared.cUR & p.BLUE_MASK) + p.shared.lr * (p.shared.cLR & p.BLUE_MASK)) >>> p.PRECISIONB;
  8589. p.shared.a = ((p.shared.ul * ((p.shared.cUL & p.ALPHA_MASK) >>> 24) + p.shared.ll *
  8590. ((p.shared.cLL & p.ALPHA_MASK) >>> 24) + p.shared.ur * ((p.shared.cUR & p.ALPHA_MASK) >>> 24) +
  8591. p.shared.lr * ((p.shared.cLR & p.ALPHA_MASK) >>> 24)) << p.PREC_ALPHA_SHIFT) & p.ALPHA_MASK;
  8592. return p.shared.a | p.shared.r | p.shared.g | p.shared.b;
  8593. };
  8594. p.blit_resize = function blit_resize(img, srcX1, srcY1, srcX2, srcY2, destPixels,
  8595. screenW, screenH, destX1, destY1, destX2, destY2, mode) {
  8596. var x, y; // iterator vars
  8597. if (srcX1 < 0) {
  8598. srcX1 = 0;
  8599. }
  8600. if (srcY1 < 0) {
  8601. srcY1 = 0;
  8602. }
  8603. if (srcX2 >= img.width) {
  8604. srcX2 = img.width - 1;
  8605. }
  8606. if (srcY2 >= img.height) {
  8607. srcY2 = img.height - 1;
  8608. }
  8609. var srcW = srcX2 - srcX1;
  8610. var srcH = srcY2 - srcY1;
  8611. var destW = destX2 - destX1;
  8612. var destH = destY2 - destY1;
  8613. var smooth = true; // may as well go with the smoothing these days
  8614. if (!smooth) {
  8615. srcW++;
  8616. srcH++;
  8617. }
  8618. if (destW <= 0 || destH <= 0 || srcW <= 0 || srcH <= 0 || destX1 >= screenW ||
  8619. destY1 >= screenH || srcX1 >= img.width || srcY1 >= img.height) {
  8620. return;
  8621. }
  8622. var dx = Math.floor(srcW / destW * p.PRECISIONF);
  8623. var dy = Math.floor(srcH / destH * p.PRECISIONF);
  8624. p.shared.srcXOffset = Math.floor(destX1 < 0 ? -destX1 * dx : srcX1 * p.PRECISIONF);
  8625. p.shared.srcYOffset = Math.floor(destY1 < 0 ? -destY1 * dy : srcY1 * p.PRECISIONF);
  8626. if (destX1 < 0) {
  8627. destW += destX1;
  8628. destX1 = 0;
  8629. }
  8630. if (destY1 < 0) {
  8631. destH += destY1;
  8632. destY1 = 0;
  8633. }
  8634. destW = Math.min(destW, screenW - destX1);
  8635. destH = Math.min(destH, screenH - destY1);
  8636. // changed in 0.9, TODO
  8637. var destOffset = destY1 * screenW + destX1;
  8638. var destColor;
  8639. p.shared.srcBuffer = img.imageData.data;
  8640. if (smooth) {
  8641. // use bilinear filtering
  8642. p.shared.iw = img.width;
  8643. p.shared.iw1 = img.width - 1;
  8644. p.shared.ih1 = img.height - 1;
  8645. switch (mode) {
  8646. case p.BLEND:
  8647. for (y = 0; y < destH; y++) {
  8648. p.filter_new_scanline();
  8649. for (x = 0; x < destW; x++) {
  8650. // changed for 0.9
  8651. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8652. destPixels[((destOffset + x) * 4) + 1],
  8653. destPixels[((destOffset + x) * 4) + 2],
  8654. destPixels[((destOffset + x) * 4) + 3]);
  8655. destColor = p.color.toArray(p.modes.blend(destColor, p.filter_bilinear()));
  8656. //destPixels[destOffset + x] = p.modes.blend(destPixels[destOffset + x], p.filter_bilinear());
  8657. destPixels[(destOffset + x) * 4] = destColor[0];
  8658. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8659. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8660. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8661. p.shared.sX += dx;
  8662. }
  8663. destOffset += screenW;
  8664. p.shared.srcYOffset += dy;
  8665. }
  8666. break;
  8667. case p.ADD:
  8668. for (y = 0; y < destH; y++) {
  8669. p.filter_new_scanline();
  8670. for (x = 0; x < destW; x++) {
  8671. // changed for 0.9
  8672. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8673. destPixels[((destOffset + x) * 4) + 1],
  8674. destPixels[((destOffset + x) * 4) + 2],
  8675. destPixels[((destOffset + x) * 4) + 3]);
  8676. destColor = p.color.toArray(p.modes.add(destColor, p.filter_bilinear()));
  8677. destColor = p.color.toArray(p.modes.add(destColor, p.filter_bilinear()));
  8678. //destPixels[destOffset + x] = p.modes.add(destPixels[destOffset + x], p.filter_bilinear());
  8679. destPixels[(destOffset + x) * 4] = destColor[0];
  8680. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8681. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8682. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8683. p.shared.sX += dx;
  8684. }
  8685. destOffset += screenW;
  8686. p.shared.srcYOffset += dy;
  8687. }
  8688. break;
  8689. case p.SUBTRACT:
  8690. for (y = 0; y < destH; y++) {
  8691. p.filter_new_scanline();
  8692. for (x = 0; x < destW; x++) {
  8693. // changed for 0.9
  8694. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8695. destPixels[((destOffset + x) * 4) + 1],
  8696. destPixels[((destOffset + x) * 4) + 2],
  8697. destPixels[((destOffset + x) * 4) + 3]);
  8698. destColor = p.color.toArray(p.modes.subtract(destColor, p.filter_bilinear()));
  8699. //destPixels[destOffset + x] = p.modes.subtract(destPixels[destOffset + x], p.filter_bilinear());
  8700. destPixels[(destOffset + x) * 4] = destColor[0];
  8701. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8702. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8703. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8704. p.shared.sX += dx;
  8705. }
  8706. destOffset += screenW;
  8707. p.shared.srcYOffset += dy;
  8708. }
  8709. break;
  8710. case p.LIGHTEST:
  8711. for (y = 0; y < destH; y++) {
  8712. p.filter_new_scanline();
  8713. for (x = 0; x < destW; x++) {
  8714. // changed for 0.9
  8715. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8716. destPixels[((destOffset + x) * 4) + 1],
  8717. destPixels[((destOffset + x) * 4) + 2],
  8718. destPixels[((destOffset + x) * 4) + 3]);
  8719. destColor = p.color.toArray(p.modes.lightest(destColor, p.filter_bilinear()));
  8720. //destPixels[destOffset + x] = p.modes.lightest(destPixels[destOffset + x], p.filter_bilinear());
  8721. destPixels[(destOffset + x) * 4] = destColor[0];
  8722. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8723. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8724. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8725. p.shared.sX += dx;
  8726. }
  8727. destOffset += screenW;
  8728. p.shared.srcYOffset += dy;
  8729. }
  8730. break;
  8731. case p.DARKEST:
  8732. for (y = 0; y < destH; y++) {
  8733. p.filter_new_scanline();
  8734. for (x = 0; x < destW; x++) {
  8735. // changed for 0.9
  8736. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8737. destPixels[((destOffset + x) * 4) + 1],
  8738. destPixels[((destOffset + x) * 4) + 2],
  8739. destPixels[((destOffset + x) * 4) + 3]);
  8740. destColor = p.color.toArray(p.modes.darkest(destColor, p.filter_bilinear()));
  8741. //destPixels[destOffset + x] = p.modes.darkest(destPixels[destOffset + x], p.filter_bilinear());
  8742. destPixels[(destOffset + x) * 4] = destColor[0];
  8743. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8744. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8745. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8746. p.shared.sX += dx;
  8747. }
  8748. destOffset += screenW;
  8749. p.shared.srcYOffset += dy;
  8750. }
  8751. break;
  8752. case p.REPLACE:
  8753. for (y = 0; y < destH; y++) {
  8754. p.filter_new_scanline();
  8755. for (x = 0; x < destW; x++) {
  8756. // changed for 0.9
  8757. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8758. destPixels[((destOffset + x) * 4) + 1],
  8759. destPixels[((destOffset + x) * 4) + 2],
  8760. destPixels[((destOffset + x) * 4) + 3]);
  8761. destColor = p.color.toArray(p.filter_bilinear());
  8762. //destPixels[destOffset + x] = p.filter_bilinear();
  8763. destPixels[(destOffset + x) * 4] = destColor[0];
  8764. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8765. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8766. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8767. p.shared.sX += dx;
  8768. }
  8769. destOffset += screenW;
  8770. p.shared.srcYOffset += dy;
  8771. }
  8772. break;
  8773. case p.DIFFERENCE:
  8774. for (y = 0; y < destH; y++) {
  8775. p.filter_new_scanline();
  8776. for (x = 0; x < destW; x++) {
  8777. // changed for 0.9
  8778. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8779. destPixels[((destOffset + x) * 4) + 1],
  8780. destPixels[((destOffset + x) * 4) + 2],
  8781. destPixels[((destOffset + x) * 4) + 3]);
  8782. destColor = p.color.toArray(p.modes.difference(destColor, p.filter_bilinear()));
  8783. //destPixels[destOffset + x] = p.modes.difference(destPixels[destOffset + x], p.filter_bilinear());
  8784. destPixels[(destOffset + x) * 4] = destColor[0];
  8785. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8786. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8787. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8788. p.shared.sX += dx;
  8789. }
  8790. destOffset += screenW;
  8791. p.shared.srcYOffset += dy;
  8792. }
  8793. break;
  8794. case p.EXCLUSION:
  8795. for (y = 0; y < destH; y++) {
  8796. p.filter_new_scanline();
  8797. for (x = 0; x < destW; x++) {
  8798. // changed for 0.9
  8799. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8800. destPixels[((destOffset + x) * 4) + 1],
  8801. destPixels[((destOffset + x) * 4) + 2],
  8802. destPixels[((destOffset + x) * 4) + 3]);
  8803. destColor = p.color.toArray(p.modes.exclusion(destColor, p.filter_bilinear()));
  8804. //destPixels[destOffset + x] = p.modes.exclusion(destPixels[destOffset + x], p.filter_bilinear());
  8805. destPixels[(destOffset + x) * 4] = destColor[0];
  8806. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8807. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8808. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8809. p.shared.sX += dx;
  8810. }
  8811. destOffset += screenW;
  8812. p.shared.srcYOffset += dy;
  8813. }
  8814. break;
  8815. case p.MULTIPLY:
  8816. for (y = 0; y < destH; y++) {
  8817. p.filter_new_scanline();
  8818. for (x = 0; x < destW; x++) {
  8819. // changed for 0.9
  8820. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8821. destPixels[((destOffset + x) * 4) + 1],
  8822. destPixels[((destOffset + x) * 4) + 2],
  8823. destPixels[((destOffset + x) * 4) + 3]);
  8824. destColor = p.color.toArray(p.modes.multiply(destColor, p.filter_bilinear()));
  8825. //destPixels[destOffset + x] = p.modes.multiply(destPixels[destOffset + x], p.filter_bilinear());
  8826. destPixels[(destOffset + x) * 4] = destColor[0];
  8827. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8828. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8829. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8830. p.shared.sX += dx;
  8831. }
  8832. destOffset += screenW;
  8833. p.shared.srcYOffset += dy;
  8834. }
  8835. break;
  8836. case p.SCREEN:
  8837. for (y = 0; y < destH; y++) {
  8838. p.filter_new_scanline();
  8839. for (x = 0; x < destW; x++) {
  8840. // changed for 0.9
  8841. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8842. destPixels[((destOffset + x) * 4) + 1],
  8843. destPixels[((destOffset + x) * 4) + 2],
  8844. destPixels[((destOffset + x) * 4) + 3]);
  8845. destColor = p.color.toArray(p.modes.screen(destColor, p.filter_bilinear()));
  8846. //destPixels[destOffset + x] = p.modes.screen(destPixels[destOffset + x], p.filter_bilinear());
  8847. destPixels[(destOffset + x) * 4] = destColor[0];
  8848. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8849. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8850. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8851. p.shared.sX += dx;
  8852. }
  8853. destOffset += screenW;
  8854. p.shared.srcYOffset += dy;
  8855. }
  8856. break;
  8857. case p.OVERLAY:
  8858. for (y = 0; y < destH; y++) {
  8859. p.filter_new_scanline();
  8860. for (x = 0; x < destW; x++) {
  8861. // changed for 0.9
  8862. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8863. destPixels[((destOffset + x) * 4) + 1],
  8864. destPixels[((destOffset + x) * 4) + 2],
  8865. destPixels[((destOffset + x) * 4) + 3]);
  8866. destColor = p.color.toArray(p.modes.overlay(destColor, p.filter_bilinear()));
  8867. //destPixels[destOffset + x] = p.modes.overlay(destPixels[destOffset + x], p.filter_bilinear());
  8868. destPixels[(destOffset + x) * 4] = destColor[0];
  8869. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8870. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8871. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8872. p.shared.sX += dx;
  8873. }
  8874. destOffset += screenW;
  8875. p.shared.srcYOffset += dy;
  8876. }
  8877. break;
  8878. case p.HARD_LIGHT:
  8879. for (y = 0; y < destH; y++) {
  8880. p.filter_new_scanline();
  8881. for (x = 0; x < destW; x++) {
  8882. // changed for 0.9
  8883. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8884. destPixels[((destOffset + x) * 4) + 1],
  8885. destPixels[((destOffset + x) * 4) + 2],
  8886. destPixels[((destOffset + x) * 4) + 3]);
  8887. destColor = p.color.toArray(p.modes.hard_light(destColor, p.filter_bilinear()));
  8888. //destPixels[destOffset + x] = p.modes.hard_light(destPixels[destOffset + x], p.filter_bilinear());
  8889. destPixels[(destOffset + x) * 4] = destColor[0];
  8890. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8891. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8892. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8893. p.shared.sX += dx;
  8894. }
  8895. destOffset += screenW;
  8896. p.shared.srcYOffset += dy;
  8897. }
  8898. break;
  8899. case p.SOFT_LIGHT:
  8900. for (y = 0; y < destH; y++) {
  8901. p.filter_new_scanline();
  8902. for (x = 0; x < destW; x++) {
  8903. // changed for 0.9
  8904. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8905. destPixels[((destOffset + x) * 4) + 1],
  8906. destPixels[((destOffset + x) * 4) + 2],
  8907. destPixels[((destOffset + x) * 4) + 3]);
  8908. destColor = p.color.toArray(p.modes.soft_light(destColor, p.filter_bilinear()));
  8909. //destPixels[destOffset + x] = p.modes.soft_light(destPixels[destOffset + x], p.filter_bilinear());
  8910. destPixels[(destOffset + x) * 4] = destColor[0];
  8911. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8912. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8913. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8914. p.shared.sX += dx;
  8915. }
  8916. destOffset += screenW;
  8917. p.shared.srcYOffset += dy;
  8918. }
  8919. break;
  8920. case p.DODGE:
  8921. for (y = 0; y < destH; y++) {
  8922. p.filter_new_scanline();
  8923. for (x = 0; x < destW; x++) {
  8924. // changed for 0.9
  8925. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8926. destPixels[((destOffset + x) * 4) + 1],
  8927. destPixels[((destOffset + x) * 4) + 2],
  8928. destPixels[((destOffset + x) * 4) + 3]);
  8929. destColor = p.color.toArray(p.modes.dodge(destColor, p.filter_bilinear()));
  8930. //destPixels[destOffset + x] = p.modes.dodge(destPixels[destOffset + x], p.filter_bilinear());
  8931. destPixels[(destOffset + x) * 4] = destColor[0];
  8932. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8933. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8934. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8935. p.shared.sX += dx;
  8936. }
  8937. destOffset += screenW;
  8938. p.shared.srcYOffset += dy;
  8939. }
  8940. break;
  8941. case p.BURN:
  8942. for (y = 0; y < destH; y++) {
  8943. p.filter_new_scanline();
  8944. for (x = 0; x < destW; x++) {
  8945. // changed for 0.9
  8946. destColor = p.color.toInt(destPixels[(destOffset + x) * 4],
  8947. destPixels[((destOffset + x) * 4) + 1],
  8948. destPixels[((destOffset + x) * 4) + 2],
  8949. destPixels[((destOffset + x) * 4) + 3]);
  8950. destColor = p.color.toArray(p.modes.burn(destColor, p.filter_bilinear()));
  8951. //destPixels[destOffset + x] = p.modes.burn(destPixels[destOffset + x], p.filter_bilinear());
  8952. destPixels[(destOffset + x) * 4] = destColor[0];
  8953. destPixels[(destOffset + x) * 4 + 1] = destColor[1];
  8954. destPixels[(destOffset + x) * 4 + 2] = destColor[2];
  8955. destPixels[(destOffset + x) * 4 + 3] = destColor[3];
  8956. p.shared.sX += dx;
  8957. }
  8958. destOffset += screenW;
  8959. p.shared.srcYOffset += dy;
  8960. }
  8961. break;
  8962. }
  8963. }
  8964. };
  8965. ////////////////////////////////////////////////////////////////////////////
  8966. // Font handling
  8967. ////////////////////////////////////////////////////////////////////////////
  8968. // Loads a font from an SVG or Canvas API
  8969. p.loadFont = function loadFont(name) {
  8970. if (name.indexOf(".svg") === -1) {
  8971. return {
  8972. name: "\"" + name + "\", sans-serif",
  8973. width: function(str) {
  8974. if ("measureText" in curContext) {
  8975. return curContext.measureText(typeof str === "number" ? String.fromCharCode(str) : str).width / curTextSize;
  8976. } else if ("mozMeasureText" in curContext) {
  8977. return curContext.mozMeasureText(typeof str === "number" ? String.fromCharCode(str) : str) / curTextSize;
  8978. } else {
  8979. return 0;
  8980. }
  8981. }
  8982. };
  8983. } else {
  8984. // If the font is a glyph, calculate by SVG table
  8985. var font = p.loadGlyphs(name);
  8986. return {
  8987. name: name,
  8988. glyph: true,
  8989. units_per_em: font.units_per_em,
  8990. horiz_adv_x: 1 / font.units_per_em * font.horiz_adv_x,
  8991. ascent: font.ascent,
  8992. descent: font.descent,
  8993. width: function(str) {
  8994. var width = 0;
  8995. var len = str.length;
  8996. for (var i = 0; i < len; i++) {
  8997. try {
  8998. width += parseFloat(p.glyphLook(p.glyphTable[name], str[i]).horiz_adv_x);
  8999. }
  9000. catch(e) {
  9001. Processing.debug(e);
  9002. }
  9003. }
  9004. return width / p.glyphTable[name].units_per_em;
  9005. }
  9006. };
  9007. }
  9008. };
  9009. p.createFont = function(name, size) {};
  9010. // Sets a 'current font' for use
  9011. p.textFont = function textFont(name, size) {
  9012. curTextFont = name;
  9013. p.textSize(size);
  9014. };
  9015. // Sets the font size
  9016. p.textSize = function textSize(size) {
  9017. if (size) {
  9018. curTextSize = size;
  9019. }
  9020. };
  9021. p.textAlign = function textAlign() {
  9022. if(arguments.length === 1) {
  9023. horizontalTextAlignment = arguments[0];
  9024. } else if(arguments.length === 2) {
  9025. horizontalTextAlignment = arguments[0];
  9026. verticalTextAlignment = arguments[1];
  9027. }
  9028. };
  9029. p.textWidth = function textWidth(str) {
  9030. if(p.use3DContext){
  9031. if(textcanvas === undef){
  9032. textcanvas = document.createElement("canvas");
  9033. }
  9034. var oldContext = curContext;
  9035. curContext = textcanvas.getContext("2d");
  9036. curContext.font = curContext.mozTextStyle = curTextSize + "px " + curTextFont.name;
  9037. if ("fillText" in curContext) {
  9038. textcanvas.width = curContext.measureText(str).width;
  9039. } else if ("mozDrawText" in curContext) {
  9040. textcanvas.width = curContext.mozMeasureText(str);
  9041. }
  9042. curContext = oldContext;
  9043. return textcanvas.width;
  9044. }
  9045. else{
  9046. curContext.font = curTextSize + "px " + curTextFont.name;
  9047. if ("fillText" in curContext) {
  9048. return curContext.measureText(str).width;
  9049. } else if ("mozDrawText" in curContext) {
  9050. return curContext.mozMeasureText(str);
  9051. }
  9052. }
  9053. };
  9054. // A lookup table for characters that can not be referenced by Object
  9055. p.glyphLook = function glyphLook(font, chr) {
  9056. try {
  9057. switch (chr) {
  9058. case "1":
  9059. return font.one;
  9060. case "2":
  9061. return font.two;
  9062. case "3":
  9063. return font.three;
  9064. case "4":
  9065. return font.four;
  9066. case "5":
  9067. return font.five;
  9068. case "6":
  9069. return font.six;
  9070. case "7":
  9071. return font.seven;
  9072. case "8":
  9073. return font.eight;
  9074. case "9":
  9075. return font.nine;
  9076. case "0":
  9077. return font.zero;
  9078. case " ":
  9079. return font.space;
  9080. case "$":
  9081. return font.dollar;
  9082. case "!":
  9083. return font.exclam;
  9084. case '"':
  9085. return font.quotedbl;
  9086. case "#":
  9087. return font.numbersign;
  9088. case "%":
  9089. return font.percent;
  9090. case "&":
  9091. return font.ampersand;
  9092. case "'":
  9093. return font.quotesingle;
  9094. case "(":
  9095. return font.parenleft;
  9096. case ")":
  9097. return font.parenright;
  9098. case "*":
  9099. return font.asterisk;
  9100. case "+":
  9101. return font.plus;
  9102. case ",":
  9103. return font.comma;
  9104. case "-":
  9105. return font.hyphen;
  9106. case ".":
  9107. return font.period;
  9108. case "/":
  9109. return font.slash;
  9110. case "_":
  9111. return font.underscore;
  9112. case ":":
  9113. return font.colon;
  9114. case ";":
  9115. return font.semicolon;
  9116. case "<":
  9117. return font.less;
  9118. case "=":
  9119. return font.equal;
  9120. case ">":
  9121. return font.greater;
  9122. case "?":
  9123. return font.question;
  9124. case "@":
  9125. return font.at;
  9126. case "[":
  9127. return font.bracketleft;
  9128. case "\\":
  9129. return font.backslash;
  9130. case "]":
  9131. return font.bracketright;
  9132. case "^":
  9133. return font.asciicircum;
  9134. case "`":
  9135. return font.grave;
  9136. case "{":
  9137. return font.braceleft;
  9138. case "|":
  9139. return font.bar;
  9140. case "}":
  9141. return font.braceright;
  9142. case "~":
  9143. return font.asciitilde;
  9144. // If the character is not 'special', access it by object reference
  9145. default:
  9146. return font[chr];
  9147. }
  9148. } catch(e) {
  9149. Processing.debug(e);
  9150. }
  9151. };
  9152. function toP5String(obj) {
  9153. if(obj instanceof String) {
  9154. return obj;
  9155. } else if(typeof obj === 'number') {
  9156. // check if an int
  9157. if(obj === (0 | obj)) {
  9158. return obj.toString();
  9159. } else {
  9160. return p.nf(obj, 0, 3);
  9161. }
  9162. } else if(obj === null || obj === undef) {
  9163. return "";
  9164. } else {
  9165. return obj.toString();
  9166. }
  9167. }
  9168. // Print some text to the Canvas
  9169. function text$line(str, x, y, z, align) {
  9170. var textWidth = 0, xOffset = 0;
  9171. // If the font is a standard Canvas font...
  9172. if (!curTextFont.glyph) {
  9173. if (str && ("fillText" in curContext || "mozDrawText" in curContext)) {
  9174. curContext.font = curContext.mozTextStyle = curTextSize + "px " + curTextFont.name;
  9175. if (isFillDirty) {
  9176. curContext.fillStyle = p.color.toString(currentFillColor);
  9177. isFillDirty = false;
  9178. }
  9179. // horizontal offset/alignment
  9180. if(align === p.RIGHT || align === p.CENTER) {
  9181. if ("fillText" in curContext) {
  9182. textWidth = curContext.measureText(str).width;
  9183. } else if ("mozDrawText" in curContext) {
  9184. textWidth = curContext.mozMeasureText(str);
  9185. }
  9186. if(align === p.RIGHT) {
  9187. xOffset = -textWidth;
  9188. } else { // if(align === p.CENTER)
  9189. xOffset = -textWidth/2;
  9190. }
  9191. }
  9192. if ("fillText" in curContext) {
  9193. curContext.fillText(str, x+xOffset, y);
  9194. } else if ("mozDrawText" in curContext) {
  9195. saveContext();
  9196. curContext.translate(x+xOffset, y);
  9197. curContext.mozDrawText(str);
  9198. restoreContext();
  9199. }
  9200. }
  9201. } else {
  9202. // If the font is a Batik SVG font...
  9203. var font = p.glyphTable[curTextFont.name];
  9204. saveContext();
  9205. curContext.translate(x, y + curTextSize);
  9206. // horizontal offset/alignment
  9207. if(align === p.RIGHT || align === p.CENTER) {
  9208. textWidth = font.width(str);
  9209. if(align === p.RIGHT) {
  9210. xOffset = -textWidth;
  9211. } else { // if(align === p.CENTER)
  9212. xOffset = -textWidth/2;
  9213. }
  9214. }
  9215. var upem = font.units_per_em,
  9216. newScale = 1 / upem * curTextSize;
  9217. curContext.scale(newScale, newScale);
  9218. for (var i=0, len=str.length; i < len; i++) {
  9219. // Test character against glyph table
  9220. try {
  9221. p.glyphLook(font, str[i]).draw();
  9222. } catch(e) {
  9223. Processing.debug(e);
  9224. }
  9225. }
  9226. restoreContext();
  9227. }
  9228. }
  9229. function text$line$3d(str, x, y, z, align) {
  9230. // handle case for 3d text
  9231. if (textcanvas === undef) {
  9232. textcanvas = document.createElement("canvas");
  9233. }
  9234. var oldContext = curContext;
  9235. curContext = textcanvas.getContext("2d");
  9236. curContext.font = curContext.mozTextStyle = curTextSize + "px " + curTextFont.name;
  9237. var textWidth = 0;
  9238. if ("fillText" in curContext) {
  9239. textWidth = curContext.measureText(str).width;
  9240. } else if ("mozDrawText" in curContext) {
  9241. textWidth = curContext.mozMeasureText(str);
  9242. }
  9243. textcanvas.width = textWidth;
  9244. textcanvas.height = curTextSize;
  9245. curContext = textcanvas.getContext("2d"); // refreshes curContext
  9246. curContext.font = curContext.mozTextStyle = curTextSize + "px " + curTextFont.name;
  9247. curContext.textBaseline="top";
  9248. // paint on 2D canvas
  9249. text$line(str,0,0,0,p.LEFT);
  9250. // use it as a texture
  9251. var aspect = textcanvas.width/textcanvas.height;
  9252. curContext = oldContext;
  9253. curContext.texImage2D(curContext.TEXTURE_2D, 0, textcanvas, false, true);
  9254. curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
  9255. curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR_MIPMAP_LINEAR);
  9256. curContext.generateMipmap(curContext.TEXTURE_2D);
  9257. // horizontal offset/alignment
  9258. var xOffset = 0;
  9259. if(align === p.RIGHT) {
  9260. xOffset = -textWidth;
  9261. } else if(align === p.CENTER) {
  9262. xOffset = -textWidth/2;
  9263. }
  9264. var model = new PMatrix3D();
  9265. var scalefactor = curTextSize * 0.5;
  9266. model.translate(x+xOffset-scalefactor/2, y-scalefactor, z);
  9267. model.scale(-aspect*scalefactor, -scalefactor, scalefactor);
  9268. model.translate(-1, -1, -1);
  9269. var view = new PMatrix3D();
  9270. view.scale(1, -1, 1);
  9271. view.apply(modelView.array());
  9272. curContext.useProgram(programObject2D);
  9273. vertexAttribPointer(programObject2D, "Vertex", 3, textBuffer);
  9274. vertexAttribPointer(programObject2D, "aTextureCoord", 2, textureBuffer);
  9275. uniformi(programObject2D, "uSampler", [0]);
  9276. uniformi(programObject2D, "picktype", 1);
  9277. uniformMatrix( programObject2D, "model", true, model.array() );
  9278. uniformMatrix( programObject2D, "view", true, view.array() );
  9279. uniformMatrix( programObject2D, "projection", true, projection.array() );
  9280. uniformf(programObject2D, "color", fillStyle);
  9281. curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer);
  9282. curContext.drawElements(curContext.TRIANGLES, 6, curContext.UNSIGNED_SHORT, 0);
  9283. }
  9284. function text$4(str, x, y, z) {
  9285. var lineFunction = p.use3DContext ? text$line$3d : text$line;
  9286. var lines, linesCount;
  9287. if(str.indexOf('\n') < 0) {
  9288. lines = [str];
  9289. linesCount = 1;
  9290. } else {
  9291. lines = str.split(/\r?\n/g);
  9292. linesCount = lines.length;
  9293. }
  9294. // handle text line-by-line
  9295. var yOffset;
  9296. if(verticalTextAlignment === p.TOP) {
  9297. yOffset = (1-baselineOffset) * curTextSize;
  9298. } else if(verticalTextAlignment === p.CENTER) {
  9299. yOffset = (1-baselineOffset - linesCount/2) * curTextSize;
  9300. } else if(verticalTextAlignment === p.BOTTOM) {
  9301. yOffset = (1-baselineOffset - linesCount) * curTextSize;
  9302. } else { // if(verticalTextAlignment === p.BASELINE) {
  9303. yOffset = (1 - linesCount) * curTextSize;
  9304. }
  9305. for(var i=0;i<linesCount;++i) {
  9306. var line = lines[i];
  9307. lineFunction(line, x, y + yOffset, z, horizontalTextAlignment);
  9308. yOffset += curTextSize;
  9309. }
  9310. }
  9311. function text$6(str, x, y, width, height, z) {
  9312. if (str.length === 0) { // is empty string
  9313. return;
  9314. }
  9315. if(curTextSize > height) { // is text height larger than box
  9316. return;
  9317. }
  9318. var spaceMark = -1;
  9319. var start = 0;
  9320. var lineWidth = 0;
  9321. var textboxWidth = width;
  9322. var yOffset = 0;
  9323. curContext.font = curTextSize + "px " + curTextFont.name;
  9324. var drawCommands = [];
  9325. var hadSpaceBefore = false;
  9326. for (var j=0, len=str.length; j < len; j++) {
  9327. var currentChar = str[j];
  9328. var letterWidth;
  9329. if ("fillText" in curContext) {
  9330. letterWidth = curContext.measureText(currentChar).width;
  9331. } else if ("mozDrawText" in curContext) {
  9332. letterWidth = curContext.mozMeasureText(currentChar);
  9333. }
  9334. if (currentChar !== "\n" && (currentChar === " " || (hadSpaceBefore && str[j + 1] === " ") ||
  9335. lineWidth + 2 * letterWidth < textboxWidth)) { // check a line of text
  9336. if (currentChar === " ") {
  9337. spaceMark = j;
  9338. }
  9339. lineWidth += letterWidth;
  9340. } else { // draw a line of text
  9341. if (start === spaceMark + 1) { // in case a whole line without a space
  9342. spaceMark = j;
  9343. }
  9344. if (str[j] === "\n") {
  9345. drawCommands.push({text:str.substring(start, j), width: lineWidth, offset: yOffset});
  9346. start = j + 1;
  9347. } else {
  9348. drawCommands.push({text:str.substring(start, spaceMark + 1), width: lineWidth, offset: yOffset});
  9349. start = spaceMark + 1;
  9350. }
  9351. yOffset += curTextSize;
  9352. lineWidth = 0;
  9353. j = start - 1;
  9354. }
  9355. hadSpaceBefore = currentChar === " ";
  9356. } // for (var j=
  9357. if (start < len) { // draw the last line
  9358. drawCommands.push({text:str.substring(start), width: lineWidth, offset: yOffset});
  9359. yOffset += curTextSize;
  9360. }
  9361. // actual draw
  9362. var lineFunction = p.use3DContext ? text$line$3d : text$line;
  9363. var xOffset = 0;
  9364. if(horizontalTextAlignment === p.CENTER) {
  9365. xOffset = width / 2;
  9366. } else if(horizontalTextAlignment === p.RIGHT) {
  9367. xOffset = width;
  9368. }
  9369. // offsets for alignment
  9370. var boxYOffset1 = (1-baselineOffset) * curTextSize, boxYOffset2 = 0;
  9371. if(verticalTextAlignment === p.BOTTOM) {
  9372. boxYOffset2 = height-yOffset;
  9373. } else if(verticalTextAlignment === p.CENTER) {
  9374. boxYOffset2 = (height-yOffset) / 2;
  9375. }
  9376. for(var il=0,ll=drawCommands.length; il<ll; ++il) {
  9377. var command = drawCommands[il];
  9378. if(command.offset + boxYOffset2 < 0) {
  9379. continue; // skip if not inside box yet
  9380. }
  9381. if(command.offset + boxYOffset2 + curTextSize > height) {
  9382. break; // stop if no enough space for one more line draw
  9383. }
  9384. lineFunction(command.text, x + xOffset, y + command.offset + boxYOffset1 + boxYOffset2,
  9385. z, horizontalTextAlignment);
  9386. }
  9387. }
  9388. p.text = function text() {
  9389. if (arguments.length === 3) { // for text( str, x, y)
  9390. text$4(toP5String(arguments[0]), arguments[1], arguments[2], 0);
  9391. } else if (arguments.length === 4) { // for text( str, x, y, z)
  9392. text$4(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3]);
  9393. } else if (arguments.length === 5) { // for text( str, x, y , width, height)
  9394. text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], 0);
  9395. } else if (arguments.length === 6) { // for text( stringdata, x, y , width, height, z)
  9396. text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
  9397. }
  9398. };
  9399. // Load Batik SVG Fonts and parse to pre-def objects for quick rendering
  9400. p.loadGlyphs = function loadGlyph(url) {
  9401. var x, y, cx, cy, nx, ny, d, a, lastCom, lenC, horiz_adv_x, getXY = '[0-9\\-]+', path;
  9402. // Return arrays of SVG commands and coords
  9403. // get this to use p.matchAll() - will need to work around the lack of null return
  9404. var regex = function regex(needle, hay) {
  9405. var i = 0,
  9406. results = [],
  9407. latest, regexp = new RegExp(needle, "g");
  9408. latest = results[i] = regexp.exec(hay);
  9409. while (latest) {
  9410. i++;
  9411. latest = results[i] = regexp.exec(hay);
  9412. }
  9413. return results;
  9414. };
  9415. var buildPath = function buildPath(d) {
  9416. var c = regex("[A-Za-z][0-9\\- ]+|Z", d);
  9417. // Begin storing path object
  9418. path = "var path={draw:function(){saveContext();curContext.beginPath();";
  9419. x = 0;
  9420. y = 0;
  9421. cx = 0;
  9422. cy = 0;
  9423. nx = 0;
  9424. ny = 0;
  9425. d = 0;
  9426. a = 0;
  9427. lastCom = "";
  9428. lenC = c.length - 1;
  9429. // Loop through SVG commands translating to canvas eqivs functions in path object
  9430. for (var j = 0; j < lenC; j++) {
  9431. var com = c[j][0], xy = regex(getXY, com);
  9432. switch (com[0]) {
  9433. case "M":
  9434. //curContext.moveTo(x,-y);
  9435. x = parseFloat(xy[0][0]);
  9436. y = parseFloat(xy[1][0]);
  9437. path += "curContext.moveTo(" + x + "," + (-y) + ");";
  9438. break;
  9439. case "L":
  9440. //curContext.lineTo(x,-y);
  9441. x = parseFloat(xy[0][0]);
  9442. y = parseFloat(xy[1][0]);
  9443. path += "curContext.lineTo(" + x + "," + (-y) + ");";
  9444. break;
  9445. case "H":
  9446. //curContext.lineTo(x,-y)
  9447. x = parseFloat(xy[0][0]);
  9448. path += "curContext.lineTo(" + x + "," + (-y) + ");";
  9449. break;
  9450. case "V":
  9451. //curContext.lineTo(x,-y);
  9452. y = parseFloat(xy[0][0]);
  9453. path += "curContext.lineTo(" + x + "," + (-y) + ");";
  9454. break;
  9455. case "T":
  9456. //curContext.quadraticCurveTo(cx,-cy,nx,-ny);
  9457. nx = parseFloat(xy[0][0]);
  9458. ny = parseFloat(xy[1][0]);
  9459. if (lastCom === "Q" || lastCom === "T") {
  9460. d = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(cy - y, 2));
  9461. a = Math.PI + Math.atan2(cx - x, cy - y);
  9462. cx = x + (Math.sin(a) * (d));
  9463. cy = y + (Math.cos(a) * (d));
  9464. } else {
  9465. cx = x;
  9466. cy = y;
  9467. }
  9468. path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
  9469. x = nx;
  9470. y = ny;
  9471. break;
  9472. case "Q":
  9473. //curContext.quadraticCurveTo(cx,-cy,nx,-ny);
  9474. cx = parseFloat(xy[0][0]);
  9475. cy = parseFloat(xy[1][0]);
  9476. nx = parseFloat(xy[2][0]);
  9477. ny = parseFloat(xy[3][0]);
  9478. path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
  9479. x = nx;
  9480. y = ny;
  9481. break;
  9482. case "Z":
  9483. //curContext.closePath();
  9484. path += "curContext.closePath();";
  9485. break;
  9486. }
  9487. lastCom = com[0];
  9488. }
  9489. path += "executeContextFill();executeContextStroke();";
  9490. path += "restoreContext();";
  9491. path += "curContext.translate(" + horiz_adv_x + ",0);";
  9492. path += "}}";
  9493. return path;
  9494. };
  9495. // Parse SVG font-file into block of Canvas commands
  9496. var parseSVGFont = function parseSVGFontse(svg) {
  9497. // Store font attributes
  9498. var font = svg.getElementsByTagName("font");
  9499. p.glyphTable[url].horiz_adv_x = font[0].getAttribute("horiz-adv-x");
  9500. var font_face = svg.getElementsByTagName("font-face")[0];
  9501. p.glyphTable[url].units_per_em = parseFloat(font_face.getAttribute("units-per-em"));
  9502. p.glyphTable[url].ascent = parseFloat(font_face.getAttribute("ascent"));
  9503. p.glyphTable[url].descent = parseFloat(font_face.getAttribute("descent"));
  9504. var glyph = svg.getElementsByTagName("glyph"),
  9505. len = glyph.length;
  9506. // Loop through each glyph in the SVG
  9507. for (var i = 0; i < len; i++) {
  9508. // Store attributes for this glyph
  9509. var unicode = glyph[i].getAttribute("unicode");
  9510. var name = glyph[i].getAttribute("glyph-name");
  9511. horiz_adv_x = glyph[i].getAttribute("horiz-adv-x");
  9512. if (horiz_adv_x === null) {
  9513. horiz_adv_x = p.glyphTable[url].horiz_adv_x;
  9514. }
  9515. d = glyph[i].getAttribute("d");
  9516. // Split path commands in glpyh
  9517. if (d !== undef) {
  9518. path = buildPath(d);
  9519. eval(path);
  9520. // Store glyph data to table object
  9521. p.glyphTable[url][name] = {
  9522. name: name,
  9523. unicode: unicode,
  9524. horiz_adv_x: horiz_adv_x,
  9525. draw: path.draw
  9526. };
  9527. }
  9528. } // finished adding glyphs to table
  9529. };
  9530. // Load and parse Batik SVG font as XML into a Processing Glyph object
  9531. var loadXML = function loadXML() {
  9532. var xmlDoc;
  9533. try {
  9534. xmlDoc = document.implementation.createDocument("", "", null);
  9535. }
  9536. catch(e_fx_op) {
  9537. Processing.debug(e_fx_op.message);
  9538. return;
  9539. }
  9540. try {
  9541. xmlDoc.async = false;
  9542. xmlDoc.load(url);
  9543. parseSVGFont(xmlDoc.getElementsByTagName("svg")[0]);
  9544. }
  9545. catch(e_sf_ch) {
  9546. // Google Chrome, Safari etc.
  9547. Processing.debug(e_sf_ch);
  9548. try {
  9549. var xmlhttp = new window.XMLHttpRequest();
  9550. xmlhttp.open("GET", url, false);
  9551. xmlhttp.send(null);
  9552. parseSVGFont(xmlhttp.responseXML.documentElement);
  9553. }
  9554. catch(e) {
  9555. Processing.debug(e_sf_ch);
  9556. }
  9557. }
  9558. };
  9559. // Create a new object in glyphTable to store this font
  9560. p.glyphTable[url] = {};
  9561. // Begin loading the Batik SVG font...
  9562. loadXML(url);
  9563. // Return the loaded font for attribute grabbing
  9564. return p.glyphTable[url];
  9565. };
  9566. ////////////////////////////////////////////////////////////////////////////
  9567. // Class methods
  9568. ////////////////////////////////////////////////////////////////////////////
  9569. p.extendClass = function extendClass(subClass, baseClass) {
  9570. function extendGetterSetter(propertyName) {
  9571. subClass.__defineGetter__(propertyName, function() {
  9572. return baseClass[propertyName];
  9573. });
  9574. subClass.__defineSetter__(propertyName, function(v) {
  9575. baseClass[propertyName]=v;
  9576. });
  9577. }
  9578. for (var propertyName in baseClass) {
  9579. if (subClass[propertyName] === undef) {
  9580. if (typeof baseClass[propertyName] === 'function') {
  9581. subClass[propertyName] = baseClass[propertyName];
  9582. } else {
  9583. extendGetterSetter(propertyName);
  9584. }
  9585. }
  9586. }
  9587. };
  9588. p.addMethod = function addMethod(object, name, fn, superAccessor) {
  9589. if (object[name]) {
  9590. var args = fn.length,
  9591. oldfn = object[name];
  9592. object[name] = function() {
  9593. if (arguments.length === args) {
  9594. return fn.apply(this, arguments);
  9595. } else {
  9596. return oldfn.apply(this, arguments);
  9597. }
  9598. };
  9599. } else {
  9600. object[name] = fn;
  9601. }
  9602. };
  9603. //////////////////////////////////////////////////////////////////////////
  9604. // Event handling
  9605. //////////////////////////////////////////////////////////////////////////
  9606. function attach(elem, type, fn) {
  9607. if (elem.addEventListener) {
  9608. elem.addEventListener(type, fn, false);
  9609. } else {
  9610. elem.attachEvent("on" + type, fn);
  9611. }
  9612. eventHandlers.push([elem, type, fn]);
  9613. }
  9614. attach(curElement, "mousemove", function(e) {
  9615. var element = curElement, offsetX = 0, offsetY = 0;
  9616. p.pmouseX = p.mouseX;
  9617. p.pmouseY = p.mouseY;
  9618. if (element.offsetParent) {
  9619. do {
  9620. offsetX += element.offsetLeft;
  9621. offsetY += element.offsetTop;
  9622. } while ((element = element.offsetParent));
  9623. }
  9624. // Add padding and border style widths to offset
  9625. offsetX += stylePaddingLeft;
  9626. offsetY += stylePaddingTop;
  9627. offsetX += styleBorderLeft;
  9628. offsetY += styleBorderTop;
  9629. // Dropping support for IE clientX and clientY, switching to pageX and pageY so we don't have to calculate scroll offset.
  9630. // Removed in ticket #184. See rev: 2f106d1c7017fed92d045ba918db47d28e5c16f4
  9631. p.mouseX = e.pageX - offsetX;
  9632. p.mouseY = e.pageY - offsetY;
  9633. if (typeof p.mouseMoved === "function" && !p.__mousePressed) {
  9634. p.mouseMoved();
  9635. }
  9636. if (typeof p.mouseDragged === "function" && p.__mousePressed) {
  9637. p.mouseDragged();
  9638. p.mouseDragging = true;
  9639. }
  9640. });
  9641. attach(curElement, "mouseout", function(e) {
  9642. });
  9643. attach(curElement, "mousedown", function(e) {
  9644. p.__mousePressed = true;
  9645. p.mouseDragging = false;
  9646. switch (e.which) {
  9647. case 1:
  9648. p.mouseButton = p.LEFT;
  9649. break;
  9650. case 2:
  9651. p.mouseButton = p.CENTER;
  9652. break;
  9653. case 3:
  9654. p.mouseButton = p.RIGHT;
  9655. break;
  9656. }
  9657. if (typeof p.mousePressed === "function") {
  9658. p.mousePressed();
  9659. }
  9660. });
  9661. attach(curElement, "mouseup", function(e) {
  9662. p.__mousePressed = false;
  9663. if (typeof p.mouseClicked === "function" && !p.mouseDragging) {
  9664. p.mouseClicked();
  9665. }
  9666. if (typeof p.mouseReleased === "function") {
  9667. p.mouseReleased();
  9668. }
  9669. });
  9670. var mouseWheelHandler = function(e) {
  9671. var delta = 0;
  9672. if (e.wheelDelta) {
  9673. delta = e.wheelDelta / 120;
  9674. if (window.opera) {
  9675. delta = -delta;
  9676. }
  9677. } else if (e.detail) {
  9678. delta = -e.detail / 3;
  9679. }
  9680. p.mouseScroll = delta;
  9681. if (delta && typeof p.mouseScrolled === 'function') {
  9682. p.mouseScrolled();
  9683. }
  9684. };
  9685. // Support Gecko and non-Gecko scroll events
  9686. attach(document, 'DOMMouseScroll', mouseWheelHandler);
  9687. attach(document, 'mousewheel', mouseWheelHandler);
  9688. //////////////////////////////////////////////////////////////////////////
  9689. // Keyboard Events
  9690. //////////////////////////////////////////////////////////////////////////
  9691. function keyCodeMap(code, shift) {
  9692. // Letters
  9693. if (code >= 65 && code <= 90) { // A-Z
  9694. // Keys return ASCII for upcased letters.
  9695. // Convert to downcase if shiftKey is not pressed.
  9696. if (shift) {
  9697. return code;
  9698. }
  9699. else {
  9700. return code + 32;
  9701. }
  9702. }
  9703. // Numbers and their shift-symbols
  9704. else if (code >= 48 && code <= 57) { // 0-9
  9705. if (shift) {
  9706. switch (code) {
  9707. case 49:
  9708. return 33; // !
  9709. case 50:
  9710. return 64; // @
  9711. case 51:
  9712. return 35; // #
  9713. case 52:
  9714. return 36; // $
  9715. case 53:
  9716. return 37; // %
  9717. case 54:
  9718. return 94; // ^
  9719. case 55:
  9720. return 38; // &
  9721. case 56:
  9722. return 42; // *
  9723. case 57:
  9724. return 40; // (
  9725. case 48:
  9726. return 41; // )
  9727. }
  9728. }
  9729. }
  9730. // Coded keys
  9731. else if (codedKeys.indexOf(code) >= 0) { // SHIFT, CONTROL, ALT, LEFT, RIGHT, UP, DOWN
  9732. p.keyCode = code;
  9733. return p.CODED;
  9734. }
  9735. // Symbols and their shift-symbols
  9736. else {
  9737. if (shift) {
  9738. switch (code) {
  9739. case 107:
  9740. return 43; // +
  9741. case 219:
  9742. return 123; // {
  9743. case 221:
  9744. return 125; // }
  9745. case 222:
  9746. return 34; // "
  9747. }
  9748. } else {
  9749. switch (code) {
  9750. case 188:
  9751. return 44; // ,
  9752. case 109:
  9753. return 45; // -
  9754. case 190:
  9755. return 46; // .
  9756. case 191:
  9757. return 47; // /
  9758. case 192:
  9759. return 96; // ~
  9760. case 219:
  9761. return 91; // [
  9762. case 220:
  9763. return 92; // \
  9764. case 221:
  9765. return 93; // ]
  9766. case 222:
  9767. return 39; // '
  9768. }
  9769. }
  9770. }
  9771. return code;
  9772. }
  9773. attach(document, "keydown", function(e) {
  9774. p.__keyPressed = true;
  9775. p.keyCode = null;
  9776. p.key = keyCodeMap(e.keyCode, e.shiftKey);
  9777. if (typeof p.keyPressed === "function") {
  9778. p.keyPressed();
  9779. }
  9780. });
  9781. attach(document, "keyup", function(e) {
  9782. p.keyCode = null;
  9783. p.key = keyCodeMap(e.keyCode, e.shiftKey);
  9784. //TODO: This needs to only be made false if all keys have been released.
  9785. p.__keyPressed = false;
  9786. if (typeof p.keyReleased === "function") {
  9787. p.keyReleased();
  9788. }
  9789. });
  9790. attach(document, "keypress", function (e) {
  9791. // In Firefox, e.keyCode is not currently set with keypress.
  9792. //
  9793. // keypress will always happen after a keydown, so p.keyCode and p.key
  9794. // should remain correct. Some browsers (chrome) refire keydown when
  9795. // key repeats happen, others (firefox) don't. Either way keyCode and
  9796. // key should remain correct.
  9797. if (p.keyTyped) {
  9798. p.keyTyped();
  9799. }
  9800. });
  9801. // Place-holder for debugging function
  9802. Processing.debug = function(e) {};
  9803. // Get the DOM element if string was passed
  9804. if (typeof curElement === "string") {
  9805. curElement = document.getElementById(curElement);
  9806. }
  9807. // Send aCode Processing syntax to be converted to JavaScript
  9808. if (aCode) {
  9809. if(aCode instanceof Processing.Sketch) {
  9810. // Use sketch as is
  9811. curSketch = aCode;
  9812. } else if(typeof aCode === "function") {
  9813. // Wrap function with default sketch parameters
  9814. curSketch = new Processing.Sketch(aCode);
  9815. } else {
  9816. // Compile the code
  9817. curSketch = Processing.compile(aCode);
  9818. }
  9819. // Expose internal field for diagnostics and testing
  9820. p.externals.sketch = curSketch;
  9821. p.use3DContext = curSketch.use3DContext;
  9822. if ("mozOpaque" in curElement) {
  9823. curElement.mozOpaque = curSketch.options.isOpaque;
  9824. }
  9825. // Initialize the onfocus and onblur event handler externals
  9826. if (curSketch.options.pauseOnBlur) {
  9827. p.externals.onfocus = function() {
  9828. if (doLoop) {
  9829. p.loop();
  9830. }
  9831. };
  9832. p.externals.onblur = function() {
  9833. if (doLoop && loopStarted) {
  9834. p.noLoop();
  9835. doLoop = true; // make sure to keep this true after the noLoop call
  9836. }
  9837. };
  9838. }
  9839. if (!curSketch.use3DContext) {
  9840. // Setup default 2d canvas context.
  9841. curContext = curElement.getContext('2d');
  9842. // Externalize the default context
  9843. p.externals.context = curContext;
  9844. modelView = new PMatrix2D();
  9845. // Canvas has trouble rendering single pixel stuff on whole-pixel
  9846. // counts, so we slightly offset it (this is super lame).
  9847. curContext.translate(0.5, 0.5);
  9848. curContext.lineCap = 'round';
  9849. // Set default stroke and fill color
  9850. p.stroke(0);
  9851. p.fill(255);
  9852. p.noSmooth();
  9853. p.disableContextMenu();
  9854. }
  9855. // Step through the libraries that were attached at doc load...
  9856. for (var i in Processing.lib) {
  9857. if (Processing.lib) {
  9858. // Init the libraries in the context of this p_instance
  9859. Processing.lib[i].call(this);
  9860. }
  9861. }
  9862. var executeSketch = function(processing) {
  9863. // Don't start until all specified images in the cache are preloaded
  9864. if (!curSketch.imageCache.pending) {
  9865. curSketch.attach(processing);
  9866. // Run void setup()
  9867. if (processing.setup) {
  9868. processing.setup();
  9869. }
  9870. // some pixels can be cached, flushing
  9871. resetContext();
  9872. if (processing.draw) {
  9873. if (!doLoop) {
  9874. processing.redraw();
  9875. } else {
  9876. processing.loop();
  9877. }
  9878. }
  9879. } else {
  9880. window.setTimeout(executeSketch, 10, processing);
  9881. }
  9882. };
  9883. // The parser adds custom methods to the processing context
  9884. // this renames p to processing so these methods will run
  9885. executeSketch(p);
  9886. } else {
  9887. // No executable sketch was specified
  9888. // or called via createGraphics
  9889. curSketch = new Processing.Sketch();
  9890. curSketch.options.isOpaque = false;
  9891. }
  9892. };
  9893. Processing.version = "@VERSION@";
  9894. // Share lib space
  9895. Processing.lib = {};
  9896. // Processing global methods and constants for the parser
  9897. function getGlobalMembers() {
  9898. var names =
  9899. ["abs","acos","ADD","alpha","ALPHA","ALT","ambient","ambientLight","append","applyMatrix","arc",
  9900. "ARGB","arrayCopy","ArrayList","ARROW","asin","atan","atan2","background","BACKSPACE","beginCamera",
  9901. "beginDraw","beginShape","BEVEL","bezier","bezierDetail","bezierPoint","bezierTangent","bezierVertex","binary",
  9902. "blend","BLEND","blendColor","blue","BLUE_MASK","BLUR","boolean", "BOTTOM", "box","brightness","BURN","byte","camera","ceil",
  9903. "CENTER","CENTER_RADIUS","char","Character","clear","CLOSE","CMYK","CODED","color","colorMode","concat",
  9904. "console","constrain","CONTROL","copy","CORNER","CORNERS","cos","createFont","createGraphics",
  9905. "createImage","CROSS","cursor","curve","curveDetail","curvePoint","curveTangent","curveTightness",
  9906. "curveVertex","curveVertexSegment","DARKEST","day","defaultColor","degrees","DELETE","DIFFERENCE",
  9907. "DILATE","directionalLight","disableContextMenu","DISABLE_DEPTH_TEST","dist","DODGE","DOWN","draw","ellipse","ellipseMode",
  9908. "emissive","enableContextMenu","ENABLE_DEPTH_TEST","endCamera","endDraw","endShape","ENTER","ERODE","ESC","EXCLUSION","externals",
  9909. "exit","exp","expand","fill","filter","filter_bilinear","filter_new_scanline","float","floor","focused",
  9910. "frameCount","frameRate","frustum","get","glyphLook","glyphTable","GRAY","green","GREEN_MASK",
  9911. "HALF_PI","HAND","HARD_LIGHT","HashMap","height","hex","hint","hour","HSB","hue","image","IMAGE","imageMode",
  9912. "Import","int","intersect","INVERT","JAVA2D","join","key","keyPressed","keyReleased","LEFT","lerp",
  9913. "lerpColor","LIGHTEST","lightFalloff","lights","lightSpecular","line","LINES","link","loadBytes",
  9914. "loadFont","loadGlyphs","loadImage","loadPixels","loadShape","loadStrings","log","loop","mag","map","match",
  9915. "matchAll","max","MAX_FLOAT","MAX_INT","MAX_LIGHTS","millis","min","MIN_FLOAT","MIN_INT","minute",
  9916. "MITER","mix","modelX","modelY","modelZ","modes","month","mouseButton","mouseClicked","mouseDown",
  9917. "mouseDragged","mouseMoved","mousePressed","mouseReleased","mouseScroll","mouseScrolled","mouseX",
  9918. "mouseY","MOVE","MULTIPLY","nf","nfc","nfp","nfs","noCursor","NOCURSOR","noFill","noise","noiseDetail","noiseSeed",
  9919. "noLights","noLoop","norm","normal","NORMAL_MODE_AUTO","NORMALIZED","NORMAL_MODE_SHAPE","NORMAL_MODE_VERTEX",
  9920. "noSmooth","noStroke","noTint","OPAQUE","OPENGL","OVERLAY","P3D","peg","perspective","PI","PImage","pixels",
  9921. "PMatrix2D", "PMatrix3D", "PMatrixStack",
  9922. "pmouseX","pmouseY","point","Point","pointLight","POINTS","POLYGON","popMatrix","popStyle","POSTERIZE",
  9923. "pow","PREC_ALPHA_SHIFT","PRECISIONB","PRECISIONF","PREC_MAXVAL","PREC_RED_SHIFT","print",
  9924. "printCamera","println","printMatrix","printProjection","PROJECT","pushMatrix","pushStyle",
  9925. "PVector","quad","QUADS","QUAD_STRIP","radians","RADIUS","random","Random","randomSeed", "rect",
  9926. "rectMode","red","RED_MASK","redraw","REPLACE","requestImage","resetMatrix","RETURN","reverse","RGB",
  9927. "RIGHT","rotate","rotateX","rotateY","rotateZ","round","ROUND","saturation","save","scale","SCREEN",
  9928. "second","set","setup","shape", "shapeMode","shared","SHIFT","shininess","shorten","sin","SINCOS_LENGTH","size",
  9929. "smooth","SOFT_LIGHT","sort","specular","sphere","sphereDetail","splice","split","splitTokens",
  9930. "spotLight","sq","sqrt","SQUARE","status","str","stroke","strokeCap","strokeJoin","strokeWeight",
  9931. "subset","SUBTRACT","TAB","tan","text","TEXT","textAlign","textAscent","textDescent","textFont",
  9932. "textSize","textureMode","texture","textWidth","THRESHOLD","tint", "TOP",
  9933. "translate","triangle","TRIANGLE_FAN","TRIANGLES","TRIANGLE_STRIP","trim","TWO_PI","unbinary",
  9934. "unhex","UP","updatePixels","use3DContext","vertex","WAIT","width","XMLAttrbute","XMLElement","year",
  9935. "__frameRate","__mousePressed","__keyPressed"];
  9936. var members = {};
  9937. var i, l;
  9938. for(i=0,l=names.length;i<l;++i) {
  9939. members[names[i]] = null;
  9940. }
  9941. for(var lib in Processing.lib) {
  9942. if(Processing.lib[lib] && Processing.lib[lib].exports) {
  9943. var exportedNames = Processing.lib[lib].exports;
  9944. for(i=0,l=exportedNames.length;i<l;++i) {
  9945. members[exportedNames[i]] = null;
  9946. }
  9947. }
  9948. }
  9949. return members;
  9950. }
  9951. // Parser starts
  9952. function parseProcessing(code) {
  9953. var globalMembers = getGlobalMembers();
  9954. function splitToAtoms(code) {
  9955. var atoms = [];
  9956. var items = code.split(/([\{\[\(\)\]\}])/);
  9957. var result = items[0];
  9958. var stack = [];
  9959. for(var i=1; i < items.length; i += 2) {
  9960. var item = items[i];
  9961. if(item === '[' || item === '{' || item === '(') {
  9962. stack.push(result); result = item;
  9963. } else if(item === ']' || item === '}' || item === ')') {
  9964. var kind = item === '}' ? 'A' : item === ')' ? 'B' : 'C';
  9965. var index = atoms.length; atoms.push(result + item);
  9966. result = stack.pop() + '"' + kind + (index + 1) + '"';
  9967. }
  9968. result += items[i + 1];
  9969. }
  9970. atoms.unshift(result);
  9971. return atoms;
  9972. }
  9973. function injectStrings(code, strings) {
  9974. return code.replace(/'(\d+)'/g, function(all, index) {
  9975. var val = strings[index];
  9976. if(val.charAt(0) === "/") {
  9977. return val;
  9978. } else {
  9979. return (/^'((?:[^'\\\n])|(?:\\.[0-9A-Fa-f]*))'$/).test(val) ? "(new processing.Character(" + val + "))" : val;
  9980. }
  9981. });
  9982. }
  9983. function trimSpaces(string) {
  9984. var m1 = /^\s*/.exec(string), result;
  9985. if(m1[0].length === string.length) {
  9986. result = {left: m1[0], middle: "", right: ""};
  9987. } else {
  9988. var m2 = /\s*$/.exec(string);
  9989. result = {left: m1[0], middle: string.substring(m1[0].length, m2.index), right: m2[0]};
  9990. }
  9991. result.untrim = function(t) { return this.left + t + this.right; };
  9992. return result;
  9993. }
  9994. function trim(string) {
  9995. return string.replace(/^\s+/,'').replace(/\s+$/,'');
  9996. }
  9997. function appendToLookupTable(table, array) {
  9998. for(var i=0,l=array.length;i<l;++i) {
  9999. table[array[i]] = null;
  10000. }
  10001. return table;
  10002. }
  10003. function isLookupTableEmpty(table) {
  10004. for(var i in table) {
  10005. if(table.hasOwnProperty(i)) {
  10006. return false;
  10007. }
  10008. }
  10009. return true;
  10010. }
  10011. function getAtomIndex(templ) { return templ.substring(2, templ.length - 1); }
  10012. var codeWoExtraCr = code.replace(/\r\n?|\n\r/g, "\n");
  10013. var strings = [];
  10014. var codeWoStrings = codeWoExtraCr.replace(/("(?:[^"\\\n]|\\.)*")|('(?:[^'\\\n]|\\.)*')|(([\[\(=|&!\^:?]\s*)(\/(?![*\/])(?:[^\/\\\n]|\\.)*\/[gim]*)\b)|(\/\/[^\n]*\n)|(\/\*(?:(?!\*\/)(?:.|\n))*\*\/)/g,
  10015. function(all, quoted, aposed, regexCtx, prefix, regex, singleComment, comment) {
  10016. var index;
  10017. if(quoted || aposed) { // replace strings
  10018. index = strings.length; strings.push(all);
  10019. return "'" + index + "'";
  10020. } else if(regexCtx) { // replace RegExps
  10021. index = strings.length; strings.push(regex);
  10022. return prefix + "'" + index + "'";
  10023. } else { // kill comments
  10024. return comment !== "" ? " " : "\n";
  10025. }
  10026. });
  10027. var atoms = splitToAtoms(codeWoStrings);
  10028. var replaceContext;
  10029. var declaredClasses = {}, currentClassId, classIdSeed = 0;
  10030. function addAtom(text, type) {
  10031. var lastIndex = atoms.length;
  10032. atoms.push(text);
  10033. return '"' + type + lastIndex + '"';
  10034. }
  10035. function generateClassId() {
  10036. return "class" + (++classIdSeed);
  10037. }
  10038. function appendClass(class_, classId, scopeId) {
  10039. class_.classId = classId;
  10040. class_.scopeId = scopeId;
  10041. declaredClasses[classId] = class_;
  10042. }
  10043. // function defined below
  10044. var transformClassBody, transformStatementsBlock, transformStatements, transformMain, transformExpression;
  10045. var classesRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)(class|interface)\s+([A-Za-z_$][\w$]*\b)(\s+extends\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\b)?(\s+implements\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\b)*)?\s*("A\d+")/g;
  10046. var methodsRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)((?!(?:else|new|return|throw|function|public|private|protected)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*([A-Za-z_$][\w$]*\b)\s*("B\d+")(\s*throws\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)*)?\s*("A\d+"|;)/g;
  10047. var fieldTest = /^((?:(?:public|private|final|protected|static)\s+)*)((?!(?:else|new|return|throw)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*([A-Za-z_$][\w$]*\b)\s*(?:"C\d+"\s*)*([=,]|$)/;
  10048. var cstrsRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)((?!(?:new|return|throw)\b)[A-Za-z_$][\w$]*\b)\s*("B\d+")(\s*throws\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)*)?\s*("A\d+")/g;
  10049. var attrAndTypeRegex = /^((?:(?:public|private|final|protected|static)\s+)*)((?!(?:new|return|throw)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*/;
  10050. var functionsRegex = /\bfunction(?:\s+([A-Za-z_$][\w$]*))?\s*("B\d+")\s*("A\d+")/g;
  10051. function extractClassesAndMethods(code) {
  10052. var s = code;
  10053. s = s.replace(classesRegex, function(all) {
  10054. return addAtom(all, 'E');
  10055. });
  10056. s = s.replace(methodsRegex, function(all) {
  10057. return addAtom(all, 'D');
  10058. });
  10059. s = s.replace(functionsRegex, function(all) {
  10060. return addAtom(all, 'H');
  10061. });
  10062. return s;
  10063. }
  10064. function extractConstructors(code, className) {
  10065. var result = code.replace(cstrsRegex, function(all, attr, name, params, throws_, body) {
  10066. if(name !== className) {
  10067. return all;
  10068. } else {
  10069. return addAtom(all, 'G');
  10070. }
  10071. });
  10072. return result;
  10073. }
  10074. function AstParam(name) {
  10075. this.name = name;
  10076. }
  10077. AstParam.prototype.toString = function() {
  10078. return this.name;
  10079. };
  10080. function AstParams(params) {
  10081. this.params = params;
  10082. }
  10083. AstParams.prototype.getNames = function() {
  10084. var names = [];
  10085. for(var i=0,l=this.params.length;i<l;++i) {
  10086. names.push(this.params[i].name);
  10087. }
  10088. return names;
  10089. };
  10090. AstParams.prototype.toString = function() {
  10091. if(this.params.length === 0) {
  10092. return "()";
  10093. }
  10094. var result = "(";
  10095. for(var i=0,l=this.params.length;i<l;++i) {
  10096. result += this.params[i] + ", ";
  10097. }
  10098. return result.substring(0, result.length - 2) + ")";
  10099. };
  10100. function transformParams(params) {
  10101. var paramsWoPars = trim(params.substring(1, params.length - 1));
  10102. var result = [];
  10103. if(paramsWoPars !== "") {
  10104. var paramList = paramsWoPars.split(",");
  10105. for(var i=0; i < paramList.length; ++i) {
  10106. var param = /\b([A-Za-z_$][\w$]*\b)\s*("[ABC][\d]*")?$/.exec(paramList[i]);
  10107. result.push(new AstParam(param[1]));
  10108. }
  10109. }
  10110. return new AstParams(result);
  10111. }
  10112. function preExpressionTransform(expr) {
  10113. var s = expr;
  10114. // new type[] {...} --> {...}
  10115. s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s*"C\d+")+\s*("A\d+")/g, function(all, type, init) {
  10116. return init;
  10117. });
  10118. // new Runnable() {...} --> "F???"
  10119. s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s*"B\d+")\s*("A\d+")/g, function(all, type, init) {
  10120. return addAtom(all, 'F');
  10121. });
  10122. // function(...) { } --> "H???"
  10123. s = s.replace(functionsRegex, function(all) {
  10124. return addAtom(all, 'H');
  10125. });
  10126. // new type[?] --> new ArrayList(?)
  10127. s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)\s*("C\d+"(?:\s*"C\d+")*)/g, function(all, type, index) {
  10128. var args = index.replace(/"C(\d+)"/g, function(all, j) { return atoms[j]; }).
  10129. replace(/\[\s*\]/g, "[0]").replace(/\s*\]\s*\[\s*/g, ", ");
  10130. var arrayInitializer = "(" + args.substring(1, args.length - 1) + ")";
  10131. return 'new ArrayList' + addAtom(arrayInitializer, 'B');
  10132. });
  10133. // .length() --> .length
  10134. s = s.replace(/(\.\s*length)\s*"B\d+"/g, "$1");
  10135. // #000000 --> 0x000000
  10136. s = s.replace(/#([0-9A-Fa-f]+)/g, function(all, digits) {
  10137. return digits.length < 6 ? "0x" + digits : "0xFF000000".substring(0, 10 - digits.length) + digits;
  10138. });
  10139. // delete (type)???, (int)??? -> 0|???
  10140. s = s.replace(/"B(\d+)"(\s*(?:[\w$']|"B))/g, function(all, index, next) {
  10141. var atom = atoms[index];
  10142. if(!/^\(\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\s*(?:"C\d+"\s*)*\)$/.test(atom)) {
  10143. return all;
  10144. } else if(/^\(\s*int\s*\)$/.test(atom)) {
  10145. return "0|" + next;
  10146. } else {
  10147. var indexParts = atom.split(/"C(\d+)"/g);
  10148. if(indexParts.length > 1) {
  10149. // even items contains atom numbers, can check only first
  10150. if(! /^\[\s*\]$/.test(atoms[indexParts[1]])) {
  10151. return all; // fallback - not a cast
  10152. }
  10153. }
  10154. return "" + next;
  10155. }
  10156. });
  10157. // super() -> $superCstr(), super. -> $super.;
  10158. s = s.replace(/\bsuper(\s*"B\d+")/g, "$$superCstr$1").replace(/\bsuper(\s*\.)/g, "$$super$1");
  10159. // 3.0f -> 3.0
  10160. s = s.replace(/\b(\.?\d+)[fF]/g, "$1");
  10161. // Weird (?) parsing errors with %
  10162. s = s.replace(/([^\s])%([^=\s])/g, "$1 % $2");
  10163. // Since frameRate() and frameRate are different things,
  10164. // we need to differentiate them somehow. So when we parse
  10165. // the Processing.js source, replace frameRate so it isn't
  10166. // confused with frameRate(), as well as keyPressed and mousePressed
  10167. s = s.replace(/\b(frameRate|keyPressed|mousePressed)\b(?!\s*"B)/g, "__$1");
  10168. // "pixels" replacements:
  10169. // pixels[i] = c => pixels.setPixel(i,c) | pixels[i] => pixels.getPixel(i)
  10170. // pixels.length => pixels.getLength()
  10171. // pixels = ar => pixels.set(ar) | pixels => pixels.toArray()
  10172. s = s.replace(/\bpixels\s*(("C(\d+)")|\.length)?(\s*=(?!=)([^,\]\)\}\?\:]+))?/g,
  10173. function(all, indexOrLength, index, atomIndex, equalsPart, rightSide) {
  10174. if(index) {
  10175. var atom = atoms[atomIndex];
  10176. if(equalsPart) {
  10177. return "pixels.setPixel" + addAtom("(" +atom.substring(1, atom.length - 1) +
  10178. "," + rightSide + ")", 'B');
  10179. } else {
  10180. return "pixels.getPixel" + addAtom("(" + atom.substring(1, atom.length - 1) +
  10181. ")", 'B');
  10182. }
  10183. } else if(indexOrLength) {
  10184. // length
  10185. return "pixels.getLength" + addAtom("()", 'B');
  10186. } else {
  10187. if(equalsPart) {
  10188. return "pixels.set" + addAtom("(" + rightSide + ")", 'B');
  10189. } else {
  10190. return "pixels.toArray" + addAtom("()", 'B');
  10191. }
  10192. }
  10193. });
  10194. // this() -> $constr()
  10195. s = s.replace(/\bthis(\s*"B\d+")/g, "$$constr$1");
  10196. return s;
  10197. }
  10198. function AstInlineClass(baseInterfaceName, body) {
  10199. this.baseInterfaceName = baseInterfaceName;
  10200. this.body = body;
  10201. body.owner = this;
  10202. }
  10203. AstInlineClass.prototype.toString = function() {
  10204. return "new (function() {\n" + this.body + "})";
  10205. };
  10206. function transformInlineClass(class_) {
  10207. var m = new RegExp(/\bnew\s*(Runnable)\s*"B\d+"\s*"A(\d+)"/).exec(class_);
  10208. if(m === null) {
  10209. return "null";
  10210. } else {
  10211. var oldClassId = currentClassId, newClassId = generateClassId();
  10212. currentClassId = newClassId;
  10213. // only Runnable supported
  10214. var inlineClass = new AstInlineClass("Runnable", transformClassBody(atoms[m[2]], m[1]));
  10215. appendClass(inlineClass, newClassId, oldClassId);
  10216. currentClassId = oldClassId;
  10217. return inlineClass;
  10218. }
  10219. }
  10220. function AstFunction(name, params, body) {
  10221. this.name = name;
  10222. this.params = params;
  10223. this.body = body;
  10224. }
  10225. AstFunction.prototype.toString = function() {
  10226. var oldContext = replaceContext;
  10227. // saving "this." and parameters
  10228. var names = appendToLookupTable({"this":null}, this.params.getNames());
  10229. replaceContext = function(name) {
  10230. return name in names ? name : oldContext(name);
  10231. };
  10232. var result = "function";
  10233. if(this.name) {
  10234. result += " " + this.name;
  10235. }
  10236. result += this.params + " " + this.body;
  10237. replaceContext = oldContext;
  10238. return result;
  10239. };
  10240. function transformFunction(class_) {
  10241. var m = new RegExp(/\b([A-Za-z_$][\w$]*)\s*"B(\d+)"\s*"A(\d+)"/).exec(class_);
  10242. return new AstFunction( m[1] !== "function" ? m[1] : null,
  10243. transformParams(atoms[m[2]]), transformStatementsBlock(atoms[m[3]]));
  10244. }
  10245. function AstInlineObject(members) {
  10246. this.members = members;
  10247. }
  10248. AstInlineObject.prototype.toString = function() {
  10249. var oldContext = replaceContext;
  10250. replaceContext = function(name) {
  10251. return name === "this"? name : oldContext(name); // saving "this."
  10252. };
  10253. var result = "";
  10254. for(var i=0,l=this.members.length;i<l;++i) {
  10255. if(this.members[i].label) {
  10256. result += this.members[i].label + ": ";
  10257. }
  10258. result += this.members[i].value.toString() + ", ";
  10259. }
  10260. replaceContext = oldContext;
  10261. return result.substring(0, result.length - 2);
  10262. };
  10263. function transformInlineObject(obj) {
  10264. var members = obj.split(',');
  10265. for(var i=0; i < members.length; ++i) {
  10266. var label = members[i].indexOf(':');
  10267. if(label < 0) {
  10268. members[i] = { value: transformExpression(members[i]) };
  10269. } else {
  10270. members[i] = { label: trim(members[i].substring(0, label)),
  10271. value: transformExpression( trim(members[i].substring(label + 1)) ) };
  10272. }
  10273. }
  10274. return new AstInlineObject(members);
  10275. }
  10276. function expandExpression(expr) {
  10277. if(expr.charAt(0) === '(' || expr.charAt(0) === '[') {
  10278. return expr.charAt(0) + expandExpression(expr.substring(1, expr.length - 1)) + expr.charAt(expr.length - 1);
  10279. } else if(expr.charAt(0) === '{') {
  10280. if(/^\{\s*(?:[A-Za-z_$][\w$]*|'\d+')\s*:/.test(expr)) {
  10281. return "{" + addAtom(expr.substring(1, expr.length - 1), 'I') + "}";
  10282. } else {
  10283. return "[" + expandExpression(expr.substring(1, expr.length - 1)) + "]";
  10284. }
  10285. } else {
  10286. var trimmed = trimSpaces(expr);
  10287. var result = preExpressionTransform(trimmed.middle);
  10288. result = result.replace(/"[ABC](\d+)"/g, function(all, index) {
  10289. return expandExpression(atoms[index]);
  10290. });
  10291. return trimmed.untrim(result);
  10292. }
  10293. }
  10294. function replaceContextInVars(expr) {
  10295. return expr.replace(/(\.\s*)?(\b[A-Za-z_$][\w$]*\b)/g,
  10296. function(all, memberAccessSign, identifier) {
  10297. if(memberAccessSign) {
  10298. return all;
  10299. } else {
  10300. return replaceContext(identifier);
  10301. }
  10302. });
  10303. }
  10304. function AstExpression(expr, transforms) {
  10305. this.expr = expr;
  10306. this.transforms = transforms;
  10307. }
  10308. AstExpression.prototype.toString = function() {
  10309. var transforms = this.transforms;
  10310. var expr = replaceContextInVars(this.expr);
  10311. return expr.replace(/"!(\d+)"/g, function(all, index) {
  10312. return transforms[index].toString();
  10313. });
  10314. };
  10315. transformExpression = function(expr) {
  10316. var transforms = [];
  10317. var s = expandExpression(expr);
  10318. s = s.replace(/"H(\d+)"/g, function(all, index) {
  10319. transforms.push(transformFunction(atoms[index]));
  10320. return '"!' + (transforms.length - 1) + '"';
  10321. });
  10322. s = s.replace(/"F(\d+)"/g, function(all, index) {
  10323. transforms.push(transformInlineClass(atoms[index]));
  10324. return '"!' + (transforms.length - 1) + '"';
  10325. });
  10326. s = s.replace(/"I(\d+)"/g, function(all, index) {
  10327. transforms.push(transformInlineObject(atoms[index]));
  10328. return '"!' + (transforms.length - 1) + '"';
  10329. });
  10330. return new AstExpression(s, transforms);
  10331. };
  10332. function AstVarDefinition(name, value, isDefault) {
  10333. this.name = name;
  10334. this.value = value;
  10335. this.isDefault = isDefault;
  10336. }
  10337. AstVarDefinition.prototype.toString = function() {
  10338. return this.name + ' = ' + this.value;
  10339. };
  10340. function transformVarDefinition(def, defaultTypeValue) {
  10341. var eqIndex = def.indexOf("=");
  10342. var name, value, isDefault;
  10343. if(eqIndex < 0) {
  10344. name = def;
  10345. value = defaultTypeValue;
  10346. isDefault = true;
  10347. } else {
  10348. name = def.substring(0, eqIndex);
  10349. value = transformExpression(def.substring(eqIndex + 1));
  10350. isDefault = false;
  10351. }
  10352. return new AstVarDefinition( trim(name.replace(/(\s*"C\d+")+/g, "")),
  10353. value, isDefault);
  10354. }
  10355. function getDefaultValueForType(type) {
  10356. if(type === "int" || type === "float") {
  10357. return "0";
  10358. } else if(type === "boolean") {
  10359. return "false";
  10360. } else if(type === "color") {
  10361. return "0x00000000";
  10362. } else {
  10363. return "null";
  10364. }
  10365. }
  10366. function AstVar(definitions, varType) {
  10367. this.definitions = definitions;
  10368. this.varType = varType;
  10369. }
  10370. AstVar.prototype.getNames = function() {
  10371. var names = [];
  10372. for(var i=0,l=this.definitions.length;i<l;++i) {
  10373. names.push(this.definitions[i].name);
  10374. }
  10375. return names;
  10376. };
  10377. AstVar.prototype.toString = function() {
  10378. return "var " + this.definitions.join(",");
  10379. };
  10380. function AstStatement(expression) {
  10381. this.expression = expression;
  10382. }
  10383. AstStatement.prototype.toString = function() {
  10384. return this.expression.toString();
  10385. };
  10386. function transformStatement(statement) {
  10387. if(fieldTest.test(statement)) {
  10388. var attrAndType = attrAndTypeRegex.exec(statement);
  10389. var definitions = statement.substring(attrAndType[0].length).split(",");
  10390. var defaultTypeValue = getDefaultValueForType(attrAndType[2]);
  10391. for(var i=0; i < definitions.length; ++i) {
  10392. definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue);
  10393. }
  10394. return new AstVar(definitions, attrAndType[2]);
  10395. } else {
  10396. return new AstStatement(transformExpression(statement));
  10397. }
  10398. }
  10399. function AstForExpression(initStatement, condition, step) {
  10400. this.initStatement = initStatement;
  10401. this.condition = condition;
  10402. this.step = step;
  10403. }
  10404. AstForExpression.prototype.toString = function() {
  10405. return "(" + this.initStatement + "; " + this.condition + "; " + this.step + ")";
  10406. };
  10407. function AstForInExpression(initStatement, container) {
  10408. this.initStatement = initStatement;
  10409. this.container = container;
  10410. }
  10411. AstForInExpression.prototype.toString = function() {
  10412. var init = this.initStatement.toString();
  10413. if(init.indexOf("=") >= 0) { // can be without var declaration
  10414. init = init.substring(0, init.indexOf("="));
  10415. }
  10416. return "(" + init + " in " + this.container + ")";
  10417. };
  10418. function transformForExpression(expr) {
  10419. var content;
  10420. if(/\bin\b/.test(expr)) {
  10421. content = expr.substring(1, expr.length - 1).split(/\bin\b/g);
  10422. return new AstForInExpression( transformStatement(trim(content[0])),
  10423. transformExpression(content[1]));
  10424. } else {
  10425. content = expr.substring(1, expr.length - 1).split(";");
  10426. return new AstForExpression( transformStatement(trim(content[0])),
  10427. transformExpression(content[1]), transformExpression(content[2]));
  10428. }
  10429. }
  10430. function AstInnerInterface(name) {
  10431. this.name = name;
  10432. }
  10433. AstInnerInterface.prototype.toString = function() {
  10434. return "this." + this.name + " = function " + this.name + "() { "+
  10435. "throw 'This is an interface'; };";
  10436. };
  10437. function AstInnerClass(name, body) {
  10438. this.name = name;
  10439. this.body = body;
  10440. body.owner = this;
  10441. }
  10442. AstInnerClass.prototype.toString = function() {
  10443. return "this." + this.name + " = function " + this.name + "() {\n" +
  10444. this.body + "};";
  10445. };
  10446. function transformInnerClass(class_) {
  10447. var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body
  10448. classesRegex.lastIndex = 0;
  10449. var body = atoms[getAtomIndex(m[6])];
  10450. if(m[2] === "interface") {
  10451. return new AstInnerInterface(m[3]);
  10452. } else {
  10453. var oldClassId = currentClassId, newClassId = generateClassId();
  10454. currentClassId = newClassId;
  10455. var innerClass = new AstInnerClass(m[3], transformClassBody(body, m[3], m[4], m[5]));
  10456. appendClass(innerClass, newClassId, oldClassId);
  10457. currentClassId = oldClassId;
  10458. return innerClass;
  10459. }
  10460. }
  10461. function AstClassMethod(name, params, body) {
  10462. this.name = name;
  10463. this.params = params;
  10464. this.body = body;
  10465. }
  10466. AstClassMethod.prototype.toString = function(){
  10467. var thisReplacement = replaceContext("this");
  10468. var paramNames = appendToLookupTable({}, this.params.getNames());
  10469. var oldContext = replaceContext;
  10470. replaceContext = function(name) {
  10471. return name in paramNames ? name : oldContext(name);
  10472. };
  10473. var result = "processing.addMethod(" + thisReplacement + ", '" + this.name + "', function " + this.params + " " +
  10474. this.body +");";
  10475. replaceContext = oldContext;
  10476. return result;
  10477. };
  10478. function transformClassMethod(method) {
  10479. var m = methodsRegex.exec(method);
  10480. methodsRegex.lastIndex = 0;
  10481. return new AstClassMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]),
  10482. transformStatementsBlock(atoms[getAtomIndex(m[6])]) );
  10483. }
  10484. function AstClassField(definitions, fieldType, isStatic) {
  10485. this.definitions = definitions;
  10486. this.fieldType = fieldType;
  10487. this.isStatic = isStatic;
  10488. }
  10489. AstClassField.prototype.getNames = function() {
  10490. var names = [];
  10491. for(var i=0,l=this.definitions.length;i<l;++i) {
  10492. names.push(this.definitions[i].name);
  10493. }
  10494. return names;
  10495. };
  10496. AstClassField.prototype.toString = function() {
  10497. var thisPrefix = replaceContext("this") + ".";
  10498. if(this.isStatic) {
  10499. var className = this.owner.name;
  10500. var staticDeclarations = [];
  10501. for(var i=0,l=this.definitions.length;i<l;++i) {
  10502. var definition = this.definitions[i];
  10503. var name = definition.name, staticName = className + "." + name;
  10504. var declaration = "if(" + staticName + " === void(0)) {\n" +
  10505. " " + staticName + " = " + definition.value + "; }\n" +
  10506. thisPrefix + "__defineGetter__('" + name + "',function(){return " + staticName + ";});\n" +
  10507. thisPrefix + "__defineSetter__('" + name + "',function(val){" + staticName + " = val;});\n";
  10508. staticDeclarations.push(declaration);
  10509. }
  10510. return staticDeclarations.join("");
  10511. } else {
  10512. return thisPrefix + this.definitions.join("; " + thisPrefix);
  10513. }
  10514. };
  10515. function transformClassField(statement) {
  10516. var attrAndType = attrAndTypeRegex.exec(statement);
  10517. var isStatic = attrAndType[1].indexOf("static") >= 0;
  10518. var definitions = statement.substring(attrAndType[0].length).split(/,\s*/g);
  10519. var defaultTypeValue = getDefaultValueForType(attrAndType[2]);
  10520. for(var i=0; i < definitions.length; ++i) {
  10521. definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue);
  10522. }
  10523. return new AstClassField(definitions, attrAndType[2], isStatic);
  10524. }
  10525. function AstConstructor(params, body) {
  10526. this.params = params;
  10527. this.body = body;
  10528. }
  10529. AstConstructor.prototype.toString = function() {
  10530. var paramNames = appendToLookupTable({}, this.params.getNames());
  10531. var oldContext = replaceContext;
  10532. replaceContext = function(name) {
  10533. return name in paramNames ? name : oldContext(name);
  10534. };
  10535. var prefix = "function $constr_" + this.params.params.length + this.params.toString();
  10536. var body = this.body.toString();
  10537. if(!/\$(superCstr|constr)\b/.test(body)) {
  10538. body = "{\n$superCstr();\n" + body.substring(1);
  10539. }
  10540. replaceContext = oldContext;
  10541. return prefix + body + "\n";
  10542. };
  10543. function transformConstructor(cstr) {
  10544. var m = new RegExp(/"B(\d+)"\s*"A(\d+)"/).exec(cstr);
  10545. var params = transformParams(atoms[m[1]]);
  10546. return new AstConstructor(params, transformStatementsBlock(atoms[m[2]]));
  10547. }
  10548. function AstClassBody(name, baseClassName, functions, methods, fields, cstrs, innerClasses, misc) {
  10549. var i,l;
  10550. this.name = name;
  10551. this.baseClassName = baseClassName;
  10552. this.functions = functions;
  10553. this.methods = methods;
  10554. this.fields = fields;
  10555. this.cstrs = cstrs;
  10556. this.innerClasses = innerClasses;
  10557. this.misc = misc;
  10558. for(i=0,l=fields.length; i<l; ++i) {
  10559. fields[i].owner = this;
  10560. }
  10561. }
  10562. AstClassBody.prototype.getMembers = function() {
  10563. var members;
  10564. if(this.owner.base) {
  10565. members = this.owner.base.body.getMembers();
  10566. } else {
  10567. members = { fields: [], methods: [], innerClasses: [] };
  10568. }
  10569. var i, j, l, m;
  10570. for(i=0,l=this.fields.length;i<l;++i) {
  10571. members.fields = members.fields.concat(this.fields[i].getNames());
  10572. }
  10573. for(i=0,l=this.methods.length;i<l;++i) {
  10574. var method = this.methods[i];
  10575. members.methods.push(method.name);
  10576. }
  10577. for(i=0,l=this.innerClasses.length;i<l;++i) {
  10578. var innerClass = this.innerClasses[i];
  10579. members.innerClasses.push(innerClass.name);
  10580. }
  10581. return members;
  10582. };
  10583. AstClassBody.prototype.toString = function() {
  10584. function getScopeLevel(p) {
  10585. var i = 0;
  10586. while(p) {
  10587. ++i;
  10588. p=p.scope;
  10589. }
  10590. return i;
  10591. }
  10592. var scopeLevel = getScopeLevel(this.owner);
  10593. var selfId = "$this_" + scopeLevel;
  10594. var result = "var " + selfId + " = this;\n";
  10595. var members = this.getMembers();
  10596. var thisClassFields = appendToLookupTable({}, members.fields),
  10597. thisClassMethods = appendToLookupTable({}, members.methods),
  10598. thisClassInners = appendToLookupTable({}, members.innerClasses);
  10599. var oldContext = replaceContext;
  10600. replaceContext = function(name) {
  10601. if(name === "this") {
  10602. return selfId;
  10603. } else if(name in thisClassFields || name in thisClassInners) {
  10604. return selfId + "." + name;
  10605. } else if(name in thisClassMethods) {
  10606. return "this." + name;
  10607. }
  10608. return oldContext(name);
  10609. };
  10610. if(this.baseClassName) {
  10611. result += "var $super = {};\n";
  10612. result += "function $superCstr(){\n" +
  10613. this.baseClassName + ".prototype.constructor.apply($super, arguments);\n" +
  10614. "processing.extendClass(" + selfId + ", $super); }\n";
  10615. } else {
  10616. result += "function $superCstr() { }\n";
  10617. }
  10618. result += this.functions.join('\n') + '\n';
  10619. result += this.innerClasses.join('\n');
  10620. result += this.fields.join(";\n") + ";\n";
  10621. result += this.methods.join('\n') + '\n';
  10622. result += this.misc.tail;
  10623. result += this.cstrs.join('\n') + '\n';
  10624. result += "function $constr() {\n";
  10625. var cstrsIfs = [];
  10626. for(var i=0,l=this.cstrs.length;i<l;++i) {
  10627. var paramsLength = this.cstrs[i].params.params.length;
  10628. cstrsIfs.push("if(arguments.length === " + paramsLength + ") { " +
  10629. "$constr_" + paramsLength + ".apply(" + selfId + ", arguments); }");
  10630. }
  10631. if(cstrsIfs.length > 0) {
  10632. result += cstrsIfs.join(" else ") + " else ";
  10633. }
  10634. // ??? add check if length is 0, otherwise fail
  10635. result += "$superCstr(); }\n";
  10636. result += "$constr.apply(null, arguments);\n";
  10637. replaceContext = oldContext;
  10638. return result;
  10639. };
  10640. transformClassBody = function(body, name, baseName, impls) {
  10641. var declarations = body.substring(1, body.length - 1);
  10642. declarations = extractClassesAndMethods(declarations);
  10643. declarations = extractConstructors(declarations, name);
  10644. var methods = [], classes = [], cstrs = [], functions = [];
  10645. declarations = declarations.replace(/"([DEGH])(\d+)"/g, function(all, type, index) {
  10646. if(type === 'D') { methods.push(index); }
  10647. else if(type === 'E') { classes.push(index); }
  10648. else if(type === 'H') { functions.push(index); }
  10649. else { cstrs.push(index); }
  10650. return "";
  10651. });
  10652. var fields = declarations.split(';');
  10653. var baseClassName;
  10654. var i;
  10655. if(baseName !== undef) {
  10656. baseClassName = baseName.replace(/^\s*extends\s+([A-Za-z_$][\w$]*)\s*$/g, "$1");
  10657. }
  10658. for(i = 0; i < functions.length; ++i) {
  10659. functions[i] = transformFunction(atoms[functions[i]]);
  10660. }
  10661. for(i = 0; i < methods.length; ++i) {
  10662. methods[i] = transformClassMethod(atoms[methods[i]]);
  10663. }
  10664. for(i = 0; i < fields.length - 1; ++i) {
  10665. var field = trimSpaces(fields[i]);
  10666. fields[i] = transformClassField(field.middle);
  10667. }
  10668. var tail = fields.pop();
  10669. for(i = 0; i < cstrs.length; ++i) {
  10670. cstrs[i] = transformConstructor(atoms[cstrs[i]]);
  10671. }
  10672. for(i = 0; i < classes.length; ++i) {
  10673. classes[i] = transformInnerClass(atoms[classes[i]]);
  10674. }
  10675. return new AstClassBody(name, baseClassName, functions, methods, fields, cstrs,
  10676. classes, { tail: tail });
  10677. };
  10678. function AstInterface(name) {
  10679. this.name = name;
  10680. }
  10681. AstInterface.prototype.toString = function() {
  10682. return "function " + this.name + "() { throw 'This is an interface'; }\n" +
  10683. "processing." + this.name + " = " + this.name + ";";
  10684. };
  10685. function AstClass(name, body) {
  10686. this.name = name;
  10687. this.body = body;
  10688. body.owner = this;
  10689. }
  10690. AstClass.prototype.toString = function() {
  10691. var staticVars = "";
  10692. for (var i = 0, l = this.body.fields.length; i < l; i++) {
  10693. if (this.body.fields[i].isStatic) {
  10694. for (var x = 0, xl = this.body.fields[i].definitions.length; x < xl; x++) {
  10695. staticVars += "var " + this.body.fields[i].definitions[x].name + " = " + this.body.name + "." + this.body.fields[i].definitions[x] + ";";
  10696. }
  10697. }
  10698. }
  10699. return "function " + this.name + "() {\n" + this.body + "}\n" +
  10700. staticVars + "\n" +
  10701. "processing." + this.name + " = " + this.name + ";";
  10702. };
  10703. function transformGlobalClass(class_) {
  10704. var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body
  10705. classesRegex.lastIndex = 0;
  10706. var body = atoms[getAtomIndex(m[6])];
  10707. if(m[2] === "interface") {
  10708. return new AstInterface(m[3]);
  10709. } else {
  10710. var oldClassId = currentClassId, newClassId = generateClassId();
  10711. currentClassId = newClassId;
  10712. var globalClass = new AstClass(m[3], transformClassBody(body, m[3], m[4], m[5]) );
  10713. appendClass(globalClass, newClassId, oldClassId);
  10714. currentClassId = oldClassId;
  10715. return globalClass;
  10716. }
  10717. }
  10718. function AstMethod(name, params, body) {
  10719. this.name = name;
  10720. this.params = params;
  10721. this.body = body;
  10722. }
  10723. AstMethod.prototype.toString = function(){
  10724. var paramNames = appendToLookupTable({}, this.params.getNames());
  10725. var oldContext = replaceContext;
  10726. replaceContext = function(name) {
  10727. return name in paramNames ? name : oldContext(name);
  10728. };
  10729. var result = "function " + this.name + this.params + " " + this.body + "\n" +
  10730. "processing." + this.name + " = " + this.name + ";";
  10731. replaceContext = oldContext;
  10732. return result;
  10733. };
  10734. function transformGlobalMethod(method) {
  10735. var m = methodsRegex.exec(method);
  10736. var result =
  10737. methodsRegex.lastIndex = 0;
  10738. return new AstMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]),
  10739. transformStatementsBlock(atoms[getAtomIndex(m[6])]));
  10740. }
  10741. function preStatementsTransform(statements) {
  10742. var s = statements;
  10743. s = s.replace(/\b(catch\s*"B\d+"\s*"A\d+")(\s*catch\s*"B\d+"\s*"A\d+")+/g, "$1");
  10744. return s;
  10745. }
  10746. function AstForStatement(argument, misc) {
  10747. this.argument = argument;
  10748. this.misc = misc;
  10749. }
  10750. AstForStatement.prototype.toString = function() {
  10751. return this.misc.prefix + this.argument.toString();
  10752. };
  10753. function AstCatchStatement(argument, misc) {
  10754. this.argument = argument;
  10755. this.misc = misc;
  10756. }
  10757. AstCatchStatement.prototype.toString = function() {
  10758. return this.misc.prefix + this.argument.toString();
  10759. };
  10760. function AstPrefixStatement(name, argument, misc) {
  10761. this.name = name;
  10762. this.argument = argument;
  10763. this.misc = misc;
  10764. }
  10765. AstPrefixStatement.prototype.toString = function() {
  10766. var result = this.misc.prefix;
  10767. if(this.argument !== undef) {
  10768. result += this.argument.toString();
  10769. }
  10770. return result;
  10771. };
  10772. function AstLabel(label) {
  10773. this.label = label;
  10774. }
  10775. AstLabel.prototype.toString = function() {
  10776. return this.label;
  10777. };
  10778. transformStatements = function(statements, transformMethod, transformClass) {
  10779. var nextStatement = new RegExp(/\b(catch|for|if|switch|while|with)\s*"B(\d+)"|\b(do|else|finally|return|throw|try|break|continue)\b|("[ADEH](\d+)")|\b((?:case\s[^:]+|[A-Za-z_$][\w$]*\s*):)|(;)/g);
  10780. var res = [];
  10781. statements = preStatementsTransform(statements);
  10782. var lastIndex = 0, m, space;
  10783. while((m = nextStatement.exec(statements)) !== null) {
  10784. if(m[1] !== undef) { // catch, for ...
  10785. var i = statements.lastIndexOf('"B', nextStatement.lastIndex);
  10786. var statementsPrefix = statements.substring(lastIndex, i);
  10787. if(m[1] === "for") {
  10788. res.push(new AstForStatement(transformForExpression(atoms[m[2]]),
  10789. { prefix: statementsPrefix }) );
  10790. } else if(m[1] === "catch") {
  10791. res.push(new AstCatchStatement(transformParams(atoms[m[2]]),
  10792. { prefix: statementsPrefix }) );
  10793. } else {
  10794. res.push(new AstPrefixStatement(m[1], transformExpression(atoms[m[2]]),
  10795. { prefix: statementsPrefix }) );
  10796. }
  10797. } else if(m[3] !== undef) { // do, else, ...
  10798. res.push(new AstPrefixStatement(m[3], undef,
  10799. { prefix: statements.substring(lastIndex, nextStatement.lastIndex) }) );
  10800. } else if(m[4] !== undef) { // block, class and methods
  10801. space = statements.substring(lastIndex, nextStatement.lastIndex - m[4].length);
  10802. if(trim(space).length !== 0) { continue; } // avoiding new type[] {} construct
  10803. res.push(space);
  10804. var kind = m[4].charAt(1), atomIndex = m[5];
  10805. if(kind === 'D') {
  10806. res.push(transformMethod(atoms[atomIndex]));
  10807. } else if(kind === 'E') {
  10808. res.push(transformClass(atoms[atomIndex]));
  10809. } else if(kind === 'H') {
  10810. res.push(transformFunction(atoms[atomIndex]));
  10811. } else {
  10812. res.push(transformStatementsBlock(atoms[atomIndex]));
  10813. }
  10814. } else if(m[6] !== undef) { // label
  10815. space = statements.substring(lastIndex, nextStatement.lastIndex - m[6].length);
  10816. if(trim(space).length !== 0) { continue; } // avoiding ?: construct
  10817. res.push(new AstLabel(statements.substring(lastIndex, nextStatement.lastIndex)) );
  10818. } else { // semicolon
  10819. var statement = trimSpaces(statements.substring(lastIndex, nextStatement.lastIndex - 1));
  10820. res.push(statement.left);
  10821. res.push(transformStatement(statement.middle));
  10822. res.push(statement.right + ";");
  10823. }
  10824. lastIndex = nextStatement.lastIndex;
  10825. }
  10826. var statementsTail = trimSpaces(statements.substring(lastIndex));
  10827. res.push(statementsTail.left);
  10828. if(statementsTail.middle !== "") {
  10829. res.push(transformStatement(statementsTail.middle));
  10830. res.push(";" + statementsTail.right);
  10831. }
  10832. return res;
  10833. };
  10834. function getLocalNames(statements) {
  10835. var localNames = [];
  10836. for(var i=0,l=statements.length;i<l;++i) {
  10837. var statement = statements[i];
  10838. if(statement instanceof AstVar) {
  10839. localNames = localNames.concat(statement.getNames());
  10840. } else if(statement instanceof AstForStatement &&
  10841. statement.argument.initStatement instanceof AstVar) {
  10842. localNames = localNames.concat(statement.argument.initStatement.getNames());
  10843. }
  10844. }
  10845. return appendToLookupTable({}, localNames);
  10846. }
  10847. function AstStatementsBlock(statements) {
  10848. this.statements = statements;
  10849. }
  10850. AstStatementsBlock.prototype.toString = function() {
  10851. var localNames = getLocalNames(this.statements);
  10852. var oldContext = replaceContext;
  10853. // replacing context only when necessary
  10854. if(!isLookupTableEmpty(localNames)) {
  10855. replaceContext = function(name) {
  10856. return name in localNames ? name : oldContext(name);
  10857. };
  10858. }
  10859. var result = "{\n" + this.statements.join('') + "\n}";
  10860. replaceContext = oldContext;
  10861. return result;
  10862. };
  10863. transformStatementsBlock = function(block) {
  10864. var content = trimSpaces(block.substring(1, block.length - 1));
  10865. return new AstStatementsBlock(transformStatements(content.middle));
  10866. };
  10867. function AstRoot(statements) {
  10868. this.statements = statements;
  10869. }
  10870. AstRoot.prototype.toString = function() {
  10871. var localNames = getLocalNames(this.statements);
  10872. replaceContext = function(name) {
  10873. if(name in globalMembers && !(name in localNames)) {
  10874. return "processing." + name;
  10875. }
  10876. return name;
  10877. };
  10878. var result = "// this code was autogenerated from PJS\n" +
  10879. "(function(processing) {\n" +
  10880. this.statements.join('') + "\n})";
  10881. replaceContext = null;
  10882. return result;
  10883. };
  10884. transformMain = function() {
  10885. var statements = extractClassesAndMethods(atoms[0]);
  10886. statements = statements.replace(/\bimport\s+[^;]+;/g, "");
  10887. return new AstRoot( transformStatements(statements,
  10888. transformGlobalMethod, transformGlobalClass) );
  10889. };
  10890. function generateMetadata(ast) {
  10891. var globalScope = {};
  10892. var id, class_;
  10893. for(id in declaredClasses) {
  10894. if(declaredClasses.hasOwnProperty(id)) {
  10895. class_ = declaredClasses[id];
  10896. var scopeId = class_.scopeId, name = class_.name;
  10897. if(scopeId) {
  10898. var scope = declaredClasses[scopeId];
  10899. class_.scope = scope;
  10900. if(scope.inScope === undef) {
  10901. scope.inScope = {};
  10902. }
  10903. scope.inScope[name] = class_;
  10904. } else {
  10905. globalScope[name] = class_;
  10906. }
  10907. }
  10908. }
  10909. function findInScopes(class_, name) {
  10910. var parts = name.split('.');
  10911. var currentScope = class_.scope, found;
  10912. while(currentScope) {
  10913. if(parts[0] in currentScope) {
  10914. found = currentScope[parts[0]]; break;
  10915. }
  10916. currentScope = currentScope.scope;
  10917. }
  10918. if(found === undef) {
  10919. found = globalScope[parts[0]];
  10920. }
  10921. for(var i=1,l=parts.length;i<l && found;++i) {
  10922. found = found.inScope[parts[i]];
  10923. }
  10924. return found;
  10925. }
  10926. for(id in declaredClasses) {
  10927. if(declaredClasses.hasOwnProperty(id)) {
  10928. class_ = declaredClasses[id];
  10929. var baseClassName = class_.body.baseClassName;
  10930. if(baseClassName) {
  10931. class_.base = findInScopes(class_, baseClassName);
  10932. }
  10933. }
  10934. }
  10935. }
  10936. var transformed = transformMain();
  10937. generateMetadata(transformed);
  10938. // remove empty extra lines with space
  10939. var redendered = transformed.toString();
  10940. redendered = redendered.replace(/\s*\n(?:[\t ]*\n)+/g, "\n\n");
  10941. return injectStrings(redendered, strings);
  10942. }// Parser ends
  10943. function preprocessCode(aCode, sketch) {
  10944. // Parse out @pjs directive, if any.
  10945. var dm = /\/\*\s*@pjs\s+((?:[^\*]|\*+[^\*\/])*)\*\//g.exec(aCode);
  10946. if (dm && dm.length === 2) {
  10947. var directives = dm.splice(1, 2)[0].replace('\n', '').replace('\r', '').split(';');
  10948. // We'll L/RTrim, and also remove any surrounding double quotes (e.g., just take string contents)
  10949. var clean = function(s) {
  10950. return s.replace(/^\s*\"?/, '').replace(/\"?\s*$/, '');
  10951. };
  10952. for (var i = 0, dl = directives.length; i < dl; i++) {
  10953. var pair = directives[i].split('=');
  10954. if (pair && pair.length === 2) {
  10955. var key = clean(pair[0]);
  10956. var value = clean(pair[1]);
  10957. // A few directives require work beyond storying key/value pairings
  10958. if (key === "preload") {
  10959. var list = value.split(',');
  10960. // All pre-loaded images will get put in imageCache, keyed on filename
  10961. for (var j = 0, ll = list.length; j < ll; j++) {
  10962. var imageName = clean(list[j]);
  10963. sketch.imageCache.add(imageName);
  10964. }
  10965. } else if (key === "opaque") {
  10966. sketch.options.isOpaque = value === "true";
  10967. } else if (key === "crisp") {
  10968. sketch.options.crispLines = value === "true";
  10969. } else if (key === "pauseOnBlur") {
  10970. sketch.options.pauseOnBlur = value === "true";
  10971. } else {
  10972. sketch.options[key] = value;
  10973. }
  10974. }
  10975. }
  10976. }
  10977. // Check if 3D context is invoked -- this is not the best way to do this.
  10978. var codeWoStrings = aCode.replace(/("(?:[^"\\\n]|\\.)*")|('(?:[^'\\\n]|\\.)*')|(([\[\(=|&!\^:?]\s*)(\/(?![*\/])(?:[^\/\\\n]|\\.)*\/[gim]*)\b)|(\/\/[^\n]*\n)|(\/\*(?:(?!\*\/)(?:.|\n))*\*\/)/g, "");
  10979. if (codeWoStrings.match(/\bsize\((?:.+),(?:.+),\s*(OPENGL|P3D)\s*\);/)) {
  10980. sketch.use3DContext = true;
  10981. }
  10982. return aCode;
  10983. }
  10984. // Parse/compiles Processing (Java-like) syntax to JavaScript syntax
  10985. Processing.compile = function(pdeCode) {
  10986. var sketch = new Processing.Sketch();
  10987. var code = preprocessCode(pdeCode, sketch);
  10988. var compiledPde = parseProcessing(code);
  10989. sketch.sourceCode = compiledPde;
  10990. return sketch;
  10991. };
  10992. Error.prototype.printStackTrace = function() {
  10993. return this.toString();
  10994. };
  10995. Processing.version = "@VERSION@";
  10996. // Share lib space
  10997. Processing.lib = {};
  10998. // Store Processing instances
  10999. Processing.instances = [];
  11000. Processing.instanceIds = {};
  11001. Processing.removeInstance = function(id) {
  11002. Processing.instances.splice(Processing.instanceIds[id], 1);
  11003. delete Processing.instanceIds[id];
  11004. };
  11005. Processing.addInstance = function(processing) {
  11006. if (processing.externals.canvas.id === undef || !processing.externals.canvas.id.length) {
  11007. processing.externals.canvas.id = "__processing" + Processing.instances.length;
  11008. }
  11009. Processing.instanceIds[processing.externals.canvas.id] = Processing.instances.length;
  11010. Processing.instances.push(processing);
  11011. };
  11012. Processing.getInstanceById = function(name) {
  11013. return Processing.instances[Processing.instanceIds[name]];
  11014. };
  11015. Processing.Sketch = function(attachFunction) {
  11016. this.attachFunction = attachFunction; // can be optional
  11017. this.use3DContext = false;
  11018. this.options = {
  11019. isOpaque: true,
  11020. crispLines: false,
  11021. pauseOnBlur: false
  11022. };
  11023. this.imageCache = {
  11024. pending: 0,
  11025. images: {},
  11026. add: function(href) {
  11027. var img = new Image();
  11028. img.onload = (function(owner) {
  11029. return function() {
  11030. owner.pending--;
  11031. };
  11032. }(this));
  11033. this.pending++;
  11034. this.images[href] = img;
  11035. img.src = href;
  11036. }
  11037. };
  11038. this.sourceCode = undefined;
  11039. this.attach = function(processing) {
  11040. // either attachFunction or sourceCode must be present on attach
  11041. if(typeof this.attachFunction === "function") {
  11042. this.attachFunction(processing);
  11043. } else if(this.sourceCode) {
  11044. var func = eval(this.sourceCode);
  11045. func(processing);
  11046. this.attachFunction = func;
  11047. } else {
  11048. throw "Unable to attach sketch to the processing instance";
  11049. }
  11050. };
  11051. this.toString = function() {
  11052. return this.sourceCode || "[attach: " + this.attachFunction + "]";
  11053. };
  11054. this.onblur = function() {};
  11055. this.onfocus = function() {};
  11056. };
  11057. // Automatic Initialization Method
  11058. var init = function() {
  11059. var canvas = document.getElementsByTagName('canvas');
  11060. for (var i = 0, l = canvas.length; i < l; i++) {
  11061. // datasrc and data-src are deprecated.
  11062. var processingSources = canvas[i].getAttribute('data-processing-sources');
  11063. if (processingSources === null) {
  11064. // Temporary fallback for datasrc and data-src
  11065. processingSources = canvas[i].getAttribute('data-src');
  11066. if (processingSources === null) {
  11067. processingSources = canvas[i].getAttribute('datasrc');
  11068. }
  11069. }
  11070. if (processingSources) {
  11071. // The problem: if the HTML canvas dimensions differ from the
  11072. // dimensions specified in the size() call in the sketch, for
  11073. // 3D sketches, browsers will either not render or render the
  11074. // scene incorrectly. To fix this, we need to adjust the attributes
  11075. // of the canvas width and height.
  11076. // Get the source, we'll need to find what the user has used in size()
  11077. var filenames = processingSources.split(' ');
  11078. var code = "";
  11079. for (var j = 0, fl = filenames.length; j < fl; j++) {
  11080. if (filenames[j]) {
  11081. var block = ajax(filenames[j]);
  11082. if (block !== false) {
  11083. code += ";\n" + block;
  11084. }
  11085. }
  11086. }
  11087. Processing.addInstance(new Processing(canvas[i], code));
  11088. }
  11089. }
  11090. };
  11091. document.addEventListener('DOMContentLoaded', function() {
  11092. init();
  11093. }, false);
  11094. // pauseOnBlur handling
  11095. window.addEventListener('blur', function() {
  11096. for (var i = 0; i < Processing.instances.length; i++) {
  11097. Processing.instances[i].externals.onblur();
  11098. }
  11099. }, false);
  11100. window.addEventListener('focus', function() {
  11101. for (var i = 0; i < Processing.instances.length; i++) {
  11102. Processing.instances[i].externals.onfocus();
  11103. }
  11104. }, false);
  11105. }());