protovis-d3.2.js 516 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919
  1. // 21a9062b1af7e0328b11351e63de4f904867ec28
  2. /**
  3. * @class The built-in Array class.
  4. * @name Array
  5. */
  6. /**
  7. * Creates a new array with the results of calling a provided function on every
  8. * element in this array. Implemented in Javascript 1.6.
  9. *
  10. * @function
  11. * @name Array.prototype.map
  12. * @see <a
  13. * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/Map">map</a>
  14. * documentation.
  15. * @param {function} f function that produces an element of the new Array from
  16. * an element of the current one.
  17. * @param [o] object to use as <tt>this</tt> when executing <tt>f</tt>.
  18. */
  19. if (!Array.prototype.map) Array.prototype.map = function(f, o) {
  20. var n = this.length;
  21. var result = new Array(n);
  22. for (var i = 0; i < n; i++) {
  23. if (i in this) {
  24. result[i] = f.call(o, this[i], i, this);
  25. }
  26. }
  27. return result;
  28. };
  29. /**
  30. * Creates a new array with all elements that pass the test implemented by the
  31. * provided function. Implemented in Javascript 1.6.
  32. *
  33. * @function
  34. * @name Array.prototype.filter
  35. * @see <a
  36. * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/filter">filter</a>
  37. * documentation.
  38. * @param {function} f function to test each element of the array.
  39. * @param [o] object to use as <tt>this</tt> when executing <tt>f</tt>.
  40. */
  41. if (!Array.prototype.filter) Array.prototype.filter = function(f, o) {
  42. var n = this.length;
  43. var result = new Array();
  44. for (var i = 0; i < n; i++) {
  45. if (i in this) {
  46. var v = this[i];
  47. if (f.call(o, v, i, this)) result.push(v);
  48. }
  49. }
  50. return result;
  51. };
  52. /**
  53. * Executes a provided function once per array element. Implemented in
  54. * Javascript 1.6.
  55. *
  56. * @function
  57. * @name Array.prototype.forEach
  58. * @see <a
  59. * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/ForEach">forEach</a>
  60. * documentation.
  61. * @param {function} f function to execute for each element.
  62. * @param [o] object to use as <tt>this</tt> when executing <tt>f</tt>.
  63. */
  64. if (!Array.prototype.forEach) Array.prototype.forEach = function(f, o) {
  65. var n = this.length >>> 0;
  66. for (var i = 0; i < n; i++) {
  67. if (i in this) f.call(o, this[i], i, this);
  68. }
  69. };
  70. /**
  71. * Apply a function against an accumulator and each value of the array (from
  72. * left-to-right) as to reduce it to a single value. Implemented in Javascript
  73. * 1.8.
  74. *
  75. * @function
  76. * @name Array.prototype.reduce
  77. * @see <a
  78. * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/Reduce">reduce</a>
  79. * documentation.
  80. * @param {function} f function to execute on each value in the array.
  81. * @param [v] object to use as the first argument to the first call of
  82. * <tt>t</tt>.
  83. */
  84. if (!Array.prototype.reduce) Array.prototype.reduce = function(f, v) {
  85. var len = this.length;
  86. if (!len && (arguments.length == 1)) {
  87. throw new Error("reduce: empty array, no initial value");
  88. }
  89. var i = 0;
  90. if (arguments.length < 2) {
  91. while (true) {
  92. if (i in this) {
  93. v = this[i++];
  94. break;
  95. }
  96. if (++i >= len) {
  97. throw new Error("reduce: no values, no initial value");
  98. }
  99. }
  100. }
  101. for (; i < len; i++) {
  102. if (i in this) {
  103. v = f(v, this[i], i, this);
  104. }
  105. }
  106. return v;
  107. };
  108. /**
  109. * The top-level Protovis namespace. All public methods and fields should be
  110. * registered on this object. Note that core Protovis source is surrounded by an
  111. * anonymous function, so any other declared globals will not be visible outside
  112. * of core methods. This also allows multiple versions of Protovis to coexist,
  113. * since each version will see their own <tt>pv</tt> namespace.
  114. *
  115. * @namespace The top-level Protovis namespace, <tt>pv</tt>.
  116. */
  117. var pv = {};
  118. /**
  119. * Protovis major and minor version numbers.
  120. *
  121. * @namespace Protovis major and minor version numbers.
  122. */
  123. pv.version = {
  124. /**
  125. * The major version number.
  126. *
  127. * @type number
  128. * @constant
  129. */
  130. major: 3,
  131. /**
  132. * The minor version number.
  133. *
  134. * @type number
  135. * @constant
  136. */
  137. minor: 2
  138. };
  139. /**
  140. * Returns the passed-in argument, <tt>x</tt>; the identity function. This method
  141. * is provided for convenience since it is used as the default behavior for a
  142. * number of property functions.
  143. *
  144. * @param x a value.
  145. * @returns the value <tt>x</tt>.
  146. */
  147. pv.identity = function(x) { return x; };
  148. /**
  149. * Returns <tt>this.index</tt>. This method is provided for convenience for use
  150. * with scales. For example, to color bars by their index, say:
  151. *
  152. * <pre>.fillStyle(pv.Colors.category10().by(pv.index))</pre>
  153. *
  154. * This method is equivalent to <tt>function() this.index</tt>, but more
  155. * succinct. Note that the <tt>index</tt> property is also supported for
  156. * accessor functions with {@link pv.max}, {@link pv.min} and other array
  157. * utility methods.
  158. *
  159. * @see pv.Scale
  160. * @see pv.Mark#index
  161. */
  162. pv.index = function() { return this.index; };
  163. /**
  164. * Returns <tt>this.childIndex</tt>. This method is provided for convenience for
  165. * use with scales. For example, to color bars by their child index, say:
  166. *
  167. * <pre>.fillStyle(pv.Colors.category10().by(pv.child))</pre>
  168. *
  169. * This method is equivalent to <tt>function() this.childIndex</tt>, but more
  170. * succinct.
  171. *
  172. * @see pv.Scale
  173. * @see pv.Mark#childIndex
  174. */
  175. pv.child = function() { return this.childIndex; };
  176. /**
  177. * Returns <tt>this.parent.index</tt>. This method is provided for convenience
  178. * for use with scales. This method is provided for convenience for use with
  179. * scales. For example, to color bars by their parent index, say:
  180. *
  181. * <pre>.fillStyle(pv.Colors.category10().by(pv.parent))</pre>
  182. *
  183. * Tthis method is equivalent to <tt>function() this.parent.index</tt>, but more
  184. * succinct.
  185. *
  186. * @see pv.Scale
  187. * @see pv.Mark#index
  188. */
  189. pv.parent = function() { return this.parent.index; };
  190. /**
  191. * Stores the current event. This field is only set within event handlers.
  192. *
  193. * @type Event
  194. * @name pv.event
  195. */
  196. /**
  197. * @private Returns a prototype object suitable for extending the given class
  198. * <tt>f</tt>. Rather than constructing a new instance of <tt>f</tt> to serve as
  199. * the prototype (which unnecessarily runs the constructor on the created
  200. * prototype object, potentially polluting it), an anonymous function is
  201. * generated internally that shares the same prototype:
  202. *
  203. * <pre>function g() {}
  204. * g.prototype = f.prototype;
  205. * return new g();</pre>
  206. *
  207. * For more details, see Douglas Crockford's essay on prototypal inheritance.
  208. *
  209. * @param {function} f a constructor.
  210. * @returns a suitable prototype object.
  211. * @see Douglas Crockford's essay on <a
  212. * href="http://javascript.crockford.com/prototypal.html">prototypal
  213. * inheritance</a>.
  214. */
  215. pv.extend = function(f) {
  216. function g() {}
  217. g.prototype = f.prototype || f;
  218. return new g();
  219. };
  220. try {
  221. eval("pv.parse = function(x) x;"); // native support
  222. } catch (e) {
  223. /**
  224. * @private Parses a Protovis specification, which may use JavaScript 1.8
  225. * function expresses, replacing those function expressions with proper
  226. * functions such that the code can be run by a JavaScript 1.6 interpreter. This
  227. * hack only supports function expressions (using clumsy regular expressions, no
  228. * less), and not other JavaScript 1.8 features such as let expressions.
  229. *
  230. * @param {string} s a Protovis specification (i.e., a string of JavaScript 1.8
  231. * source code).
  232. * @returns {string} a conformant JavaScript 1.6 source code.
  233. */
  234. pv.parse = function(js) { // hacky regex support
  235. var re = new RegExp("function\\s*(\\b\\w+)?\\s*\\([^)]*\\)\\s*", "mg"), m, d, i = 0, s = "";
  236. while (m = re.exec(js)) {
  237. var j = m.index + m[0].length;
  238. if (js.charAt(j) != '{') {
  239. s += js.substring(i, j) + "{return ";
  240. i = j;
  241. for (var p = 0; p >= 0 && j < js.length; j++) {
  242. var c = js.charAt(j);
  243. switch (c) {
  244. case '"': case '\'': {
  245. while (++j < js.length && (d = js.charAt(j)) != c) {
  246. if (d == '\\') j++;
  247. }
  248. break;
  249. }
  250. case '[': case '(': p++; break;
  251. case ']': case ')': p--; break;
  252. case ';':
  253. case ',': if (p == 0) p--; break;
  254. }
  255. }
  256. s += pv.parse(js.substring(i, --j)) + ";}";
  257. i = j;
  258. }
  259. re.lastIndex = j;
  260. }
  261. s += js.substring(i);
  262. return s;
  263. };
  264. }
  265. /**
  266. * @private Computes the value of the specified CSS property <tt>p</tt> on the
  267. * specified element <tt>e</tt>.
  268. *
  269. * @param {string} p the name of the CSS property.
  270. * @param e the element on which to compute the CSS property.
  271. */
  272. pv.css = function(e, p) {
  273. return window.getComputedStyle
  274. ? window.getComputedStyle(e, null).getPropertyValue(p)
  275. : e.currentStyle[p];
  276. };
  277. /**
  278. * @private Reports the specified error to the JavaScript console. Mozilla only
  279. * allows logging to the console for privileged code; if the console is
  280. * unavailable, the alert dialog box is used instead.
  281. *
  282. * @param e the exception that triggered the error.
  283. */
  284. pv.error = function(e) {
  285. (typeof console == "undefined") ? alert(e) : console.error(e);
  286. };
  287. /**
  288. * @private Registers the specified listener for events of the specified type on
  289. * the specified target. For standards-compliant browsers, this method uses
  290. * <tt>addEventListener</tt>; for Internet Explorer, <tt>attachEvent</tt>.
  291. *
  292. * @param target a DOM element.
  293. * @param {string} type the type of event, such as "click".
  294. * @param {function} the event handler callback.
  295. */
  296. pv.listen = function(target, type, listener) {
  297. listener = pv.listener(listener);
  298. return target.addEventListener
  299. ? target.addEventListener(type, listener, false)
  300. : target.attachEvent("on" + type, listener);
  301. };
  302. /**
  303. * @private Returns a wrapper for the specified listener function such that the
  304. * {@link pv.event} is set for the duration of the listener's invocation. The
  305. * wrapper is cached on the returned function, such that duplicate registrations
  306. * of the wrapped event handler are ignored.
  307. *
  308. * @param {function} f an event handler.
  309. * @returns {function} the wrapped event handler.
  310. */
  311. pv.listener = function(f) {
  312. return f.$listener || (f.$listener = function(e) {
  313. try {
  314. pv.event = e || window.event;
  315. return f.call(this, pv.event);
  316. } finally {
  317. delete pv.event;
  318. }
  319. });
  320. };
  321. /**
  322. * @private Returns true iff <i>a</i> is an ancestor of <i>e</i>. This is useful
  323. * for ignoring mouseout and mouseover events that are contained within the
  324. * target element.
  325. */
  326. pv.ancestor = function(a, e) {
  327. while (e) {
  328. if (e == a) return true;
  329. e = e.parentNode;
  330. }
  331. return false;
  332. };
  333. /** @private Returns a locally-unique positive id. */
  334. pv.id = function() {
  335. var id = 1; return function() { return id++; };
  336. }();
  337. /** @private Returns a function wrapping the specified constant. */
  338. pv.functor = function(v) {
  339. return typeof v == "function" ? v : function() { return v; };
  340. };
  341. /*
  342. * Parses the Protovis specifications on load, allowing the use of JavaScript
  343. * 1.8 function expressions on browsers that only support JavaScript 1.6.
  344. *
  345. * @see pv.parse
  346. */
  347. pv.listen(window, "load", function() {
  348. /*
  349. * Note: in Firefox any variables declared here are visible to the eval'd
  350. * script below. Even worse, any global variables declared by the script
  351. * could overwrite local variables here (such as the index, `i`)! To protect
  352. * against this, all variables are explicitly scoped on a pv.$ object.
  353. */
  354. pv.$ = {i:0, x:document.getElementsByTagName("script")};
  355. for (; pv.$.i < pv.$.x.length; pv.$.i++) {
  356. pv.$.s = pv.$.x[pv.$.i];
  357. if (pv.$.s.type == "text/javascript+protovis") {
  358. try {
  359. window.eval(pv.parse(pv.$.s.text));
  360. } catch (e) {
  361. pv.error(e);
  362. }
  363. }
  364. }
  365. delete pv.$;
  366. });
  367. /**
  368. * Abstract; see an implementing class.
  369. *
  370. * @class Represents an abstract text formatter and parser. A <i>format</i> is a
  371. * function that converts an object of a given type, such as a <tt>Date</tt>, to
  372. * a human-readable string representation. The format may also have a
  373. * {@link #parse} method for converting a string representation back to the
  374. * given object type.
  375. *
  376. * <p>Because formats are themselves functions, they can be used directly as
  377. * mark properties. For example, if the data associated with a label are dates,
  378. * a date format can be used as label text:
  379. *
  380. * <pre> .text(pv.Format.date("%m/%d/%y"))</pre>
  381. *
  382. * And as with scales, if the format is used in multiple places, it can be
  383. * convenient to declare it as a global variable and then reference it from the
  384. * appropriate property functions. For example, if the data has a <tt>date</tt>
  385. * attribute, and <tt>format</tt> references a given date format:
  386. *
  387. * <pre> .text(function(d) format(d.date))</pre>
  388. *
  389. * Similarly, to parse a string into a date:
  390. *
  391. * <pre>var date = format.parse("4/30/2010");</pre>
  392. *
  393. * Not all format implementations support parsing. See the implementing class
  394. * for details.
  395. *
  396. * @see pv.Format.date
  397. * @see pv.Format.number
  398. * @see pv.Format.time
  399. */
  400. pv.Format = {};
  401. /**
  402. * Formats the specified object, returning the string representation.
  403. *
  404. * @function
  405. * @name pv.Format.prototype.format
  406. * @param {object} x the object to format.
  407. * @returns {string} the formatted string.
  408. */
  409. /**
  410. * Parses the specified string, returning the object representation.
  411. *
  412. * @function
  413. * @name pv.Format.prototype.parse
  414. * @param {string} x the string to parse.
  415. * @returns {object} the parsed object.
  416. */
  417. /**
  418. * @private Given a string that may be used as part of a regular expression,
  419. * this methods returns an appropriately quoted version of the specified string,
  420. * with any special characters escaped.
  421. *
  422. * @param {string} s a string to quote.
  423. * @returns {string} the quoted string.
  424. */
  425. pv.Format.re = function(s) {
  426. return s.replace(/[\\\^\$\*\+\?\[\]\(\)\.\{\}]/g, "\\$&");
  427. };
  428. /**
  429. * @private Optionally pads the specified string <i>s</i> so that it is at least
  430. * <i>n</i> characters long, using the padding character <i>c</i>.
  431. *
  432. * @param {string} c the padding character.
  433. * @param {number} n the minimum string length.
  434. * @param {string} s the string to pad.
  435. * @returns {string} the padded string.
  436. */
  437. pv.Format.pad = function(c, n, s) {
  438. var m = n - String(s).length;
  439. return (m < 1) ? s : new Array(m + 1).join(c) + s;
  440. };
  441. /**
  442. * Constructs a new date format with the specified string pattern.
  443. *
  444. * @class The format string is in the same format expected by the
  445. * <tt>strftime</tt> function in C. The following conversion specifications are
  446. * supported:<ul>
  447. *
  448. * <li>%a - abbreviated weekday name.</li>
  449. * <li>%A - full weekday name.</li>
  450. * <li>%b - abbreviated month names.</li>
  451. * <li>%B - full month names.</li>
  452. * <li>%c - locale's appropriate date and time.</li>
  453. * <li>%C - century number.</li>
  454. * <li>%d - day of month [01,31] (zero padded).</li>
  455. * <li>%D - same as %m/%d/%y.</li>
  456. * <li>%e - day of month [ 1,31] (space padded).</li>
  457. * <li>%h - same as %b.</li>
  458. * <li>%H - hour (24-hour clock) [00,23] (zero padded).</li>
  459. * <li>%I - hour (12-hour clock) [01,12] (zero padded).</li>
  460. * <li>%m - month number [01,12] (zero padded).</li>
  461. * <li>%M - minute [0,59] (zero padded).</li>
  462. * <li>%n - newline character.</li>
  463. * <li>%p - locale's equivalent of a.m. or p.m.</li>
  464. * <li>%r - same as %I:%M:%S %p.</li>
  465. * <li>%R - same as %H:%M.</li>
  466. * <li>%S - second [00,61] (zero padded).</li>
  467. * <li>%t - tab character.</li>
  468. * <li>%T - same as %H:%M:%S.</li>
  469. * <li>%x - same as %m/%d/%y.</li>
  470. * <li>%X - same as %I:%M:%S %p.</li>
  471. * <li>%y - year with century [00,99] (zero padded).</li>
  472. * <li>%Y - year including century.</li>
  473. * <li>%% - %.</li>
  474. *
  475. * </ul>The following conversion specifications are currently <i>unsupported</i>
  476. * for formatting:<ul>
  477. *
  478. * <li>%j - day number [1,366].</li>
  479. * <li>%u - weekday number [1,7].</li>
  480. * <li>%U - week number [00,53].</li>
  481. * <li>%V - week number [01,53].</li>
  482. * <li>%w - weekday number [0,6].</li>
  483. * <li>%W - week number [00,53].</li>
  484. * <li>%Z - timezone name or abbreviation.</li>
  485. *
  486. * </ul>In addition, the following conversion specifications are currently
  487. * <i>unsupported</i> for parsing:<ul>
  488. *
  489. * <li>%a - day of week, either abbreviated or full name.</li>
  490. * <li>%A - same as %a.</li>
  491. * <li>%c - locale's appropriate date and time.</li>
  492. * <li>%C - century number.</li>
  493. * <li>%D - same as %m/%d/%y.</li>
  494. * <li>%I - hour (12-hour clock) [1,12].</li>
  495. * <li>%n - any white space.</li>
  496. * <li>%p - locale's equivalent of a.m. or p.m.</li>
  497. * <li>%r - same as %I:%M:%S %p.</li>
  498. * <li>%R - same as %H:%M.</li>
  499. * <li>%t - same as %n.</li>
  500. * <li>%T - same as %H:%M:%S.</li>
  501. * <li>%x - locale's equivalent to %m/%d/%y.</li>
  502. * <li>%X - locale's equivalent to %I:%M:%S %p.</li>
  503. *
  504. * </ul>
  505. *
  506. * @see <a
  507. * href="http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html">strftime</a>
  508. * documentation.
  509. * @see <a
  510. * href="http://www.opengroup.org/onlinepubs/007908799/xsh/strptime.html">strptime</a>
  511. * documentation.
  512. * @extends pv.Format
  513. * @param {string} pattern the format pattern.
  514. */
  515. pv.Format.date = function(pattern) {
  516. var pad = pv.Format.pad;
  517. /** @private */
  518. function format(d) {
  519. return pattern.replace(/%[a-zA-Z0-9]/g, function(s) {
  520. switch (s) {
  521. case '%a': return [
  522. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  523. ][d.getDay()];
  524. case '%A': return [
  525. "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
  526. "Saturday"
  527. ][d.getDay()];
  528. case '%h':
  529. case '%b': return [
  530. "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
  531. "Oct", "Nov", "Dec"
  532. ][d.getMonth()];
  533. case '%B': return [
  534. "January", "February", "March", "April", "May", "June", "July",
  535. "August", "September", "October", "November", "December"
  536. ][d.getMonth()];
  537. case '%c': return d.toLocaleString();
  538. case '%C': return pad("0", 2, Math.floor(d.getFullYear() / 100) % 100);
  539. case '%d': return pad("0", 2, d.getDate());
  540. case '%x':
  541. case '%D': return pad("0", 2, d.getMonth() + 1)
  542. + "/" + pad("0", 2, d.getDate())
  543. + "/" + pad("0", 2, d.getFullYear() % 100);
  544. case '%e': return pad(" ", 2, d.getDate());
  545. case '%H': return pad("0", 2, d.getHours());
  546. case '%I': {
  547. var h = d.getHours() % 12;
  548. return h ? pad("0", 2, h) : 12;
  549. }
  550. // TODO %j: day of year as a decimal number [001,366]
  551. case '%m': return pad("0", 2, d.getMonth() + 1);
  552. case '%M': return pad("0", 2, d.getMinutes());
  553. case '%n': return "\n";
  554. case '%p': return d.getHours() < 12 ? "AM" : "PM";
  555. case '%T':
  556. case '%X':
  557. case '%r': {
  558. var h = d.getHours() % 12;
  559. return (h ? pad("0", 2, h) : 12)
  560. + ":" + pad("0", 2, d.getMinutes())
  561. + ":" + pad("0", 2, d.getSeconds())
  562. + " " + (d.getHours() < 12 ? "AM" : "PM");
  563. }
  564. case '%R': return pad("0", 2, d.getHours()) + ":" + pad("0", 2, d.getMinutes());
  565. case '%S': return pad("0", 2, d.getSeconds());
  566. case '%Q': return pad("0", 3, d.getMilliseconds());
  567. case '%t': return "\t";
  568. case '%u': {
  569. var w = d.getDay();
  570. return w ? w : 1;
  571. }
  572. // TODO %U: week number (sunday first day) [00,53]
  573. // TODO %V: week number (monday first day) [01,53] ... with weirdness
  574. case '%w': return d.getDay();
  575. // TODO %W: week number (monday first day) [00,53] ... with weirdness
  576. case '%y': return pad("0", 2, d.getFullYear() % 100);
  577. case '%Y': return d.getFullYear();
  578. // TODO %Z: timezone name or abbreviation
  579. case '%%': return "%";
  580. }
  581. return s;
  582. });
  583. }
  584. /**
  585. * Converts a date to a string using the associated formatting pattern.
  586. *
  587. * @function
  588. * @name pv.Format.date.prototype.format
  589. * @param {Date} date a date to format.
  590. * @returns {string} the formatted date as a string.
  591. */
  592. format.format = format;
  593. /**
  594. * Parses a date from a string using the associated formatting pattern.
  595. *
  596. * @function
  597. * @name pv.Format.date.prototype.parse
  598. * @param {string} s the string to parse as a date.
  599. * @returns {Date} the parsed date.
  600. */
  601. format.parse = function(s) {
  602. var year = 1970, month = 0, date = 1, hour = 0, minute = 0, second = 0;
  603. var fields = [function() {}];
  604. /* Register callbacks for each field in the format pattern. */
  605. var re = pv.Format.re(pattern).replace(/%[a-zA-Z0-9]/g, function(s) {
  606. switch (s) {
  607. // TODO %a: day of week, either abbreviated or full name
  608. // TODO %A: same as %a
  609. case '%b': {
  610. fields.push(function(x) { month = {
  611. Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5, Jul: 6, Aug: 7,
  612. Sep: 8, Oct: 9, Nov: 10, Dec: 11
  613. }[x]; });
  614. return "([A-Za-z]+)";
  615. }
  616. case '%h':
  617. case '%B': {
  618. fields.push(function(x) { month = {
  619. January: 0, February: 1, March: 2, April: 3, May: 4, June: 5,
  620. July: 6, August: 7, September: 8, October: 9, November: 10,
  621. December: 11
  622. }[x]; });
  623. return "([A-Za-z]+)";
  624. }
  625. // TODO %c: locale's appropriate date and time
  626. // TODO %C: century number[0,99]
  627. case '%e':
  628. case '%d': {
  629. fields.push(function(x) { date = x; });
  630. return "([0-9]+)";
  631. }
  632. // TODO %D: same as %m/%d/%y
  633. case '%I':
  634. case '%H': {
  635. fields.push(function(x) { hour = x; });
  636. return "([0-9]+)";
  637. }
  638. // TODO %j: day number [1,366]
  639. case '%m': {
  640. fields.push(function(x) { month = x - 1; });
  641. return "([0-9]+)";
  642. }
  643. case '%M': {
  644. fields.push(function(x) { minute = x; });
  645. return "([0-9]+)";
  646. }
  647. // TODO %n: any white space
  648. // TODO %p: locale's equivalent of a.m. or p.m.
  649. case '%p': { // TODO this is a hack
  650. fields.push(function(x) {
  651. if (hour == 12) {
  652. if (x == "am") hour = 0;
  653. } else if (x == "pm") {
  654. hour = Number(hour) + 12;
  655. }
  656. });
  657. return "(am|pm)";
  658. }
  659. // TODO %r: %I:%M:%S %p
  660. // TODO %R: %H:%M
  661. case '%S': {
  662. fields.push(function(x) { second = x; });
  663. return "([0-9]+)";
  664. }
  665. // TODO %t: any white space
  666. // TODO %T: %H:%M:%S
  667. // TODO %U: week number [00,53]
  668. // TODO %w: weekday [0,6]
  669. // TODO %W: week number [00, 53]
  670. // TODO %x: locale date (%m/%d/%y)
  671. // TODO %X: locale time (%I:%M:%S %p)
  672. case '%y': {
  673. fields.push(function(x) {
  674. x = Number(x);
  675. year = x + (((0 <= x) && (x < 69)) ? 2000
  676. : (((x >= 69) && (x < 100) ? 1900 : 0)));
  677. });
  678. return "([0-9]+)";
  679. }
  680. case '%Y': {
  681. fields.push(function(x) { year = x; });
  682. return "([0-9]+)";
  683. }
  684. case '%%': {
  685. fields.push(function() {});
  686. return "%";
  687. }
  688. }
  689. return s;
  690. });
  691. var match = s.match(re);
  692. if (match) match.forEach(function(m, i) { fields[i](m); });
  693. return new Date(year, month, date, hour, minute, second);
  694. };
  695. return format;
  696. };
  697. /**
  698. * Returns a time format of the given type, either "short" or "long".
  699. *
  700. * @class Represents a time format, converting between a <tt>number</tt>
  701. * representing a duration in milliseconds, and a <tt>string</tt>. Two types of
  702. * time formats are supported: "short" and "long". The <i>short</i> format type
  703. * returns a string such as "3.3 days" or "12.1 minutes", while the <i>long</i>
  704. * format returns "13:04:12" or similar.
  705. *
  706. * @extends pv.Format
  707. * @param {string} type the type; "short" or "long".
  708. */
  709. pv.Format.time = function(type) {
  710. var pad = pv.Format.pad;
  711. /*
  712. * MILLISECONDS = 1
  713. * SECONDS = 1e3
  714. * MINUTES = 6e4
  715. * HOURS = 36e5
  716. * DAYS = 864e5
  717. * WEEKS = 6048e5
  718. * MONTHS = 2592e6
  719. * YEARS = 31536e6
  720. */
  721. /** @private */
  722. function format(t) {
  723. t = Number(t); // force conversion from Date
  724. switch (type) {
  725. case "short": {
  726. if (t >= 31536e6) {
  727. return (t / 31536e6).toFixed(1) + " years";
  728. } else if (t >= 6048e5) {
  729. return (t / 6048e5).toFixed(1) + " weeks";
  730. } else if (t >= 864e5) {
  731. return (t / 864e5).toFixed(1) + " days";
  732. } else if (t >= 36e5) {
  733. return (t / 36e5).toFixed(1) + " hours";
  734. } else if (t >= 6e4) {
  735. return (t / 6e4).toFixed(1) + " minutes";
  736. }
  737. return (t / 1e3).toFixed(1) + " seconds";
  738. }
  739. case "long": {
  740. var a = [],
  741. s = ((t % 6e4) / 1e3) >> 0,
  742. m = ((t % 36e5) / 6e4) >> 0;
  743. a.push(pad("0", 2, s));
  744. if (t >= 36e5) {
  745. var h = ((t % 864e5) / 36e5) >> 0;
  746. a.push(pad("0", 2, m));
  747. if (t >= 864e5) {
  748. a.push(pad("0", 2, h));
  749. a.push(Math.floor(t / 864e5).toFixed());
  750. } else {
  751. a.push(h.toFixed());
  752. }
  753. } else {
  754. a.push(m.toFixed());
  755. }
  756. return a.reverse().join(":");
  757. }
  758. }
  759. }
  760. /**
  761. * Formats the specified time, returning the string representation.
  762. *
  763. * @function
  764. * @name pv.Format.time.prototype.format
  765. * @param {number} t the duration in milliseconds. May also be a <tt>Date</tt>.
  766. * @returns {string} the formatted string.
  767. */
  768. format.format = format;
  769. /**
  770. * Parses the specified string, returning the time in milliseconds.
  771. *
  772. * @function
  773. * @name pv.Format.time.prototype.parse
  774. * @param {string} s a formatted string.
  775. * @returns {number} the parsed duration in milliseconds.
  776. */
  777. format.parse = function(s) {
  778. switch (type) {
  779. case "short": {
  780. var re = /([0-9,.]+)\s*([a-z]+)/g, a, t = 0;
  781. while (a = re.exec(s)) {
  782. var f = parseFloat(a[0].replace(",", "")), u = 0;
  783. switch (a[2].toLowerCase()) {
  784. case "year": case "years": u = 31536e6; break;
  785. case "week": case "weeks": u = 6048e5; break;
  786. case "day": case "days": u = 864e5; break;
  787. case "hour": case "hours": u = 36e5; break;
  788. case "minute": case "minutes": u = 6e4; break;
  789. case "second": case "seconds": u = 1e3; break;
  790. }
  791. t += f * u;
  792. }
  793. return t;
  794. }
  795. case "long": {
  796. var a = s.replace(",", "").split(":").reverse(), t = 0;
  797. if (a.length) t += parseFloat(a[0]) * 1e3;
  798. if (a.length > 1) t += parseFloat(a[1]) * 6e4;
  799. if (a.length > 2) t += parseFloat(a[2]) * 36e5;
  800. if (a.length > 3) t += parseFloat(a[3]) * 864e5;
  801. return t;
  802. }
  803. }
  804. }
  805. return format;
  806. };
  807. /**
  808. * Returns a default number format.
  809. *
  810. * @class Represents a number format, converting between a <tt>number</tt> and a
  811. * <tt>string</tt>. This class allows numbers to be formatted with variable
  812. * precision (both for the integral and fractional part of the number), optional
  813. * thousands grouping, and optional padding. The thousands (",") and decimal
  814. * (".") separator can be customized.
  815. *
  816. * @returns {pv.Format.number} a number format.
  817. */
  818. pv.Format.number = function() {
  819. var mini = 0, // default minimum integer digits
  820. maxi = Infinity, // default maximum integer digits
  821. mins = 0, // mini, including group separators
  822. minf = 0, // default minimum fraction digits
  823. maxf = 0, // default maximum fraction digits
  824. maxk = 1, // 10^maxf
  825. padi = "0", // default integer pad
  826. padf = "0", // default fraction pad
  827. padg = true, // whether group separator affects integer padding
  828. decimal = ".", // default decimal separator
  829. group = ","; // default group separator
  830. /** @private */
  831. function format(x) {
  832. /* Round the fractional part, and split on decimal separator. */
  833. if (Infinity > maxf) x = Math.round(x * maxk) / maxk;
  834. var s = String(Math.abs(x)).split(".");
  835. /* Pad, truncate and group the integral part. */
  836. var i = s[0], n = (x < 0) ? "-" : "";
  837. if (i.length > maxi) i = i.substring(i.length - maxi);
  838. if (padg && (i.length < mini)) i = n + new Array(mini - i.length + 1).join(padi) + i;
  839. if (i.length > 3) i = i.replace(/\B(?=(?:\d{3})+(?!\d))/g, group);
  840. if (!padg && (i.length < mins)) i = new Array(mins - i.length + 1).join(padi) + n + i;
  841. s[0] = i;
  842. /* Pad the fractional part. */
  843. var f = s[1] || "";
  844. if (f.length < minf) s[1] = f + new Array(minf - f.length + 1).join(padf);
  845. return s.join(decimal);
  846. }
  847. /**
  848. * @function
  849. * @name pv.Format.number.prototype.format
  850. * @param {number} x
  851. * @returns {string}
  852. */
  853. format.format = format;
  854. /**
  855. * Parses the specified string as a number. Before parsing, leading and
  856. * trailing padding is removed. Group separators are also removed, and the
  857. * decimal separator is replaced with the standard point ("."). The integer
  858. * part is truncated per the maximum integer digits, and the fraction part is
  859. * rounded per the maximum fraction digits.
  860. *
  861. * @function
  862. * @name pv.Format.number.prototype.parse
  863. * @param {string} x the string to parse.
  864. * @returns {number} the parsed number.
  865. */
  866. format.parse = function(x) {
  867. var re = pv.Format.re;
  868. /* Remove leading and trailing padding. Split on the decimal separator. */
  869. var s = String(x)
  870. .replace(new RegExp("^(" + re(padi) + ")*"), "")
  871. .replace(new RegExp("(" + re(padf) + ")*$"), "")
  872. .split(decimal);
  873. /* Remove grouping and truncate the integral part. */
  874. var i = s[0].replace(new RegExp(re(group), "g"), "");
  875. if (i.length > maxi) i = i.substring(i.length - maxi);
  876. /* Round the fractional part. */
  877. var f = s[1] ? Number("0." + s[1]) : 0;
  878. if (Infinity > maxf) f = Math.round(f * maxk) / maxk;
  879. return Math.round(i) + f;
  880. };
  881. /**
  882. * Sets or gets the minimum and maximum number of integer digits. This
  883. * controls the number of decimal digits to display before the decimal
  884. * separator for the integral part of the number. If the number of digits is
  885. * smaller than the minimum, the digits are padded; if the number of digits is
  886. * larger, the digits are truncated, showing only the lower-order digits. The
  887. * default range is [0, Infinity].
  888. *
  889. * <p>If only one argument is specified to this method, this value is used as
  890. * both the minimum and maximum number. If no arguments are specified, a
  891. * two-element array is returned containing the minimum and the maximum.
  892. *
  893. * @function
  894. * @name pv.Format.number.prototype.integerDigits
  895. * @param {number} [min] the minimum integer digits.
  896. * @param {number} [max] the maximum integer digits.
  897. * @returns {pv.Format.number} <tt>this</tt>, or the current integer digits.
  898. */
  899. format.integerDigits = function(min, max) {
  900. if (arguments.length) {
  901. mini = Number(min);
  902. maxi = (arguments.length > 1) ? Number(max) : mini;
  903. mins = mini + Math.floor(mini / 3) * group.length;
  904. return this;
  905. }
  906. return [mini, maxi];
  907. };
  908. /**
  909. * Sets or gets the minimum and maximum number of fraction digits. The
  910. * controls the number of decimal digits to display after the decimal
  911. * separator for the fractional part of the number. If the number of digits is
  912. * smaller than the minimum, the digits are padded; if the number of digits is
  913. * larger, the fractional part is rounded, showing only the higher-order
  914. * digits. The default range is [0, 0].
  915. *
  916. * <p>If only one argument is specified to this method, this value is used as
  917. * both the minimum and maximum number. If no arguments are specified, a
  918. * two-element array is returned containing the minimum and the maximum.
  919. *
  920. * @function
  921. * @name pv.Format.number.prototype.fractionDigits
  922. * @param {number} [min] the minimum fraction digits.
  923. * @param {number} [max] the maximum fraction digits.
  924. * @returns {pv.Format.number} <tt>this</tt>, or the current fraction digits.
  925. */
  926. format.fractionDigits = function(min, max) {
  927. if (arguments.length) {
  928. minf = Number(min);
  929. maxf = (arguments.length > 1) ? Number(max) : minf;
  930. maxk = Math.pow(10, maxf);
  931. return this;
  932. }
  933. return [minf, maxf];
  934. };
  935. /**
  936. * Sets or gets the character used to pad the integer part. The integer pad is
  937. * used when the number of integer digits is smaller than the minimum. The
  938. * default pad character is "0" (zero).
  939. *
  940. * @param {string} [x] the new pad character.
  941. * @returns {pv.Format.number} <tt>this</tt> or the current pad character.
  942. */
  943. format.integerPad = function(x) {
  944. if (arguments.length) {
  945. padi = String(x);
  946. padg = /\d/.test(padi);
  947. return this;
  948. }
  949. return padi;
  950. };
  951. /**
  952. * Sets or gets the character used to pad the fration part. The fraction pad
  953. * is used when the number of fraction digits is smaller than the minimum. The
  954. * default pad character is "0" (zero).
  955. *
  956. * @param {string} [x] the new pad character.
  957. * @returns {pv.Format.number} <tt>this</tt> or the current pad character.
  958. */
  959. format.fractionPad = function(x) {
  960. if (arguments.length) {
  961. padf = String(x);
  962. return this;
  963. }
  964. return padf;
  965. };
  966. /**
  967. * Sets or gets the character used as the decimal point, separating the
  968. * integer and fraction parts of the number. The default decimal point is ".".
  969. *
  970. * @param {string} [x] the new decimal separator.
  971. * @returns {pv.Format.number} <tt>this</tt> or the current decimal separator.
  972. */
  973. format.decimal = function(x) {
  974. if (arguments.length) {
  975. decimal = String(x);
  976. return this;
  977. }
  978. return decimal;
  979. };
  980. /**
  981. * Sets or gets the character used as the group separator, grouping integer
  982. * digits by thousands. The default decimal point is ",". Grouping can be
  983. * disabled by using "" for the separator.
  984. *
  985. * @param {string} [x] the new group separator.
  986. * @returns {pv.Format.number} <tt>this</tt> or the current group separator.
  987. */
  988. format.group = function(x) {
  989. if (arguments.length) {
  990. group = x ? String(x) : "";
  991. mins = mini + Math.floor(mini / 3) * group.length;
  992. return this;
  993. }
  994. return group;
  995. };
  996. return format;
  997. };
  998. /**
  999. * @private A private variant of Array.prototype.map that supports the index
  1000. * property.
  1001. */
  1002. pv.map = function(array, f) {
  1003. var o = {};
  1004. return f
  1005. ? array.map(function(d, i) { o.index = i; return f.call(o, d); })
  1006. : array.slice();
  1007. };
  1008. /**
  1009. * Concatenates the specified array with itself <i>n</i> times. For example,
  1010. * <tt>pv.repeat([1, 2])</tt> returns [1, 2, 1, 2].
  1011. *
  1012. * @param {array} a an array.
  1013. * @param {number} [n] the number of times to repeat; defaults to two.
  1014. * @returns {array} an array that repeats the specified array.
  1015. */
  1016. pv.repeat = function(array, n) {
  1017. if (arguments.length == 1) n = 2;
  1018. return pv.blend(pv.range(n).map(function() { return array; }));
  1019. };
  1020. /**
  1021. * Given two arrays <tt>a</tt> and <tt>b</tt>, <style
  1022. * type="text/css">sub{line-height:0}</style> returns an array of all possible
  1023. * pairs of elements [a<sub>i</sub>, b<sub>j</sub>]. The outer loop is on array
  1024. * <i>a</i>, while the inner loop is on <i>b</i>, such that the order of
  1025. * returned elements is [a<sub>0</sub>, b<sub>0</sub>], [a<sub>0</sub>,
  1026. * b<sub>1</sub>], ... [a<sub>0</sub>, b<sub>m</sub>], [a<sub>1</sub>,
  1027. * b<sub>0</sub>], [a<sub>1</sub>, b<sub>1</sub>], ... [a<sub>1</sub>,
  1028. * b<sub>m</sub>], ... [a<sub>n</sub>, b<sub>m</sub>]. If either array is empty,
  1029. * an empty array is returned.
  1030. *
  1031. * @param {array} a an array.
  1032. * @param {array} b an array.
  1033. * @returns {array} an array of pairs of elements in <tt>a</tt> and <tt>b</tt>.
  1034. */
  1035. pv.cross = function(a, b) {
  1036. var array = [];
  1037. for (var i = 0, n = a.length, m = b.length; i < n; i++) {
  1038. for (var j = 0, x = a[i]; j < m; j++) {
  1039. array.push([x, b[j]]);
  1040. }
  1041. }
  1042. return array;
  1043. };
  1044. /**
  1045. * Given the specified array of arrays, concatenates the arrays into a single
  1046. * array. If the individual arrays are explicitly known, an alternative to blend
  1047. * is to use JavaScript's <tt>concat</tt> method directly. These two equivalent
  1048. * expressions:<ul>
  1049. *
  1050. * <li><tt>pv.blend([[1, 2, 3], ["a", "b", "c"]])</tt>
  1051. * <li><tt>[1, 2, 3].concat(["a", "b", "c"])</tt>
  1052. *
  1053. * </ul>return [1, 2, 3, "a", "b", "c"].
  1054. *
  1055. * @param {array[]} arrays an array of arrays.
  1056. * @returns {array} an array containing all the elements of each array in
  1057. * <tt>arrays</tt>.
  1058. */
  1059. pv.blend = function(arrays) {
  1060. return Array.prototype.concat.apply([], arrays);
  1061. };
  1062. /**
  1063. * Given the specified array of arrays, <style
  1064. * type="text/css">sub{line-height:0}</style> transposes each element
  1065. * array<sub>ij</sub> with array<sub>ji</sub>. If the array has dimensions
  1066. * <i>n</i>&times;<i>m</i>, it will have dimensions <i>m</i>&times;<i>n</i>
  1067. * after this method returns. This method transposes the elements of the array
  1068. * in place, mutating the array, and returning a reference to the array.
  1069. *
  1070. * @param {array[]} arrays an array of arrays.
  1071. * @returns {array[]} the passed-in array, after transposing the elements.
  1072. */
  1073. pv.transpose = function(arrays) {
  1074. var n = arrays.length, m = pv.max(arrays, function(d) { return d.length; });
  1075. if (m > n) {
  1076. arrays.length = m;
  1077. for (var i = n; i < m; i++) {
  1078. arrays[i] = new Array(n);
  1079. }
  1080. for (var i = 0; i < n; i++) {
  1081. for (var j = i + 1; j < m; j++) {
  1082. var t = arrays[i][j];
  1083. arrays[i][j] = arrays[j][i];
  1084. arrays[j][i] = t;
  1085. }
  1086. }
  1087. } else {
  1088. for (var i = 0; i < m; i++) {
  1089. arrays[i].length = n;
  1090. }
  1091. for (var i = 0; i < n; i++) {
  1092. for (var j = 0; j < i; j++) {
  1093. var t = arrays[i][j];
  1094. arrays[i][j] = arrays[j][i];
  1095. arrays[j][i] = t;
  1096. }
  1097. }
  1098. }
  1099. arrays.length = m;
  1100. for (var i = 0; i < m; i++) {
  1101. arrays[i].length = n;
  1102. }
  1103. return arrays;
  1104. };
  1105. /**
  1106. * Returns a normalized copy of the specified array, such that the sum of the
  1107. * returned elements sum to one. If the specified array is not an array of
  1108. * numbers, an optional accessor function <tt>f</tt> can be specified to map the
  1109. * elements to numbers. For example, if <tt>array</tt> is an array of objects,
  1110. * and each object has a numeric property "foo", the expression
  1111. *
  1112. * <pre>pv.normalize(array, function(d) d.foo)</pre>
  1113. *
  1114. * returns a normalized array on the "foo" property. If an accessor function is
  1115. * not specified, the identity function is used. Accessor functions can refer to
  1116. * <tt>this.index</tt>.
  1117. *
  1118. * @param {array} array an array of objects, or numbers.
  1119. * @param {function} [f] an optional accessor function.
  1120. * @returns {number[]} an array of numbers that sums to one.
  1121. */
  1122. pv.normalize = function(array, f) {
  1123. var norm = pv.map(array, f), sum = pv.sum(norm);
  1124. for (var i = 0; i < norm.length; i++) norm[i] /= sum;
  1125. return norm;
  1126. };
  1127. /**
  1128. * Returns a permutation of the specified array, using the specified array of
  1129. * indexes. The returned array contains the corresponding element in
  1130. * <tt>array</tt> for each index in <tt>indexes</tt>, in order. For example,
  1131. *
  1132. * <pre>pv.permute(["a", "b", "c"], [1, 2, 0])</pre>
  1133. *
  1134. * returns <tt>["b", "c", "a"]</tt>. It is acceptable for the array of indexes
  1135. * to be a different length from the array of elements, and for indexes to be
  1136. * duplicated or omitted. The optional accessor function <tt>f</tt> can be used
  1137. * to perform a simultaneous mapping of the array elements. Accessor functions
  1138. * can refer to <tt>this.index</tt>.
  1139. *
  1140. * @param {array} array an array.
  1141. * @param {number[]} indexes an array of indexes into <tt>array</tt>.
  1142. * @param {function} [f] an optional accessor function.
  1143. * @returns {array} an array of elements from <tt>array</tt>; a permutation.
  1144. */
  1145. pv.permute = function(array, indexes, f) {
  1146. if (!f) f = pv.identity;
  1147. var p = new Array(indexes.length), o = {};
  1148. indexes.forEach(function(j, i) { o.index = j; p[i] = f.call(o, array[j]); });
  1149. return p;
  1150. };
  1151. /**
  1152. * Returns a map from key to index for the specified <tt>keys</tt> array. For
  1153. * example,
  1154. *
  1155. * <pre>pv.numerate(["a", "b", "c"])</pre>
  1156. *
  1157. * returns <tt>{a: 0, b: 1, c: 2}</tt>. Note that since JavaScript maps only
  1158. * support string keys, <tt>keys</tt> must contain strings, or other values that
  1159. * naturally map to distinct string values. Alternatively, an optional accessor
  1160. * function <tt>f</tt> can be specified to compute the string key for the given
  1161. * element. Accessor functions can refer to <tt>this.index</tt>.
  1162. *
  1163. * @param {array} keys an array, usually of string keys.
  1164. * @param {function} [f] an optional key function.
  1165. * @returns a map from key to index.
  1166. */
  1167. pv.numerate = function(keys, f) {
  1168. if (!f) f = pv.identity;
  1169. var map = {}, o = {};
  1170. keys.forEach(function(x, i) { o.index = i; map[f.call(o, x)] = i; });
  1171. return map;
  1172. };
  1173. /**
  1174. * Returns the unique elements in the specified array, in the order they appear.
  1175. * Note that since JavaScript maps only support string keys, <tt>array</tt> must
  1176. * contain strings, or other values that naturally map to distinct string
  1177. * values. Alternatively, an optional accessor function <tt>f</tt> can be
  1178. * specified to compute the string key for the given element. Accessor functions
  1179. * can refer to <tt>this.index</tt>.
  1180. *
  1181. * @param {array} array an array, usually of string keys.
  1182. * @param {function} [f] an optional key function.
  1183. * @returns {array} the unique values.
  1184. */
  1185. pv.uniq = function(array, f) {
  1186. if (!f) f = pv.identity;
  1187. var map = {}, keys = [], o = {}, y;
  1188. array.forEach(function(x, i) {
  1189. o.index = i;
  1190. y = f.call(o, x);
  1191. if (!(y in map)) map[y] = keys.push(y);
  1192. });
  1193. return keys;
  1194. };
  1195. /**
  1196. * The comparator function for natural order. This can be used in conjunction with
  1197. * the built-in array <tt>sort</tt> method to sort elements by their natural
  1198. * order, ascending. Note that if no comparator function is specified to the
  1199. * built-in <tt>sort</tt> method, the default order is lexicographic, <i>not</i>
  1200. * natural!
  1201. *
  1202. * @see <a
  1203. * href="http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/sort">Array.sort</a>.
  1204. * @param a an element to compare.
  1205. * @param b an element to compare.
  1206. * @returns {number} negative if a &lt; b; positive if a &gt; b; otherwise 0.
  1207. */
  1208. pv.naturalOrder = function(a, b) {
  1209. return (a < b) ? -1 : ((a > b) ? 1 : 0);
  1210. };
  1211. /**
  1212. * The comparator function for reverse natural order. This can be used in
  1213. * conjunction with the built-in array <tt>sort</tt> method to sort elements by
  1214. * their natural order, descending. Note that if no comparator function is
  1215. * specified to the built-in <tt>sort</tt> method, the default order is
  1216. * lexicographic, <i>not</i> natural!
  1217. *
  1218. * @see #naturalOrder
  1219. * @param a an element to compare.
  1220. * @param b an element to compare.
  1221. * @returns {number} negative if a &lt; b; positive if a &gt; b; otherwise 0.
  1222. */
  1223. pv.reverseOrder = function(b, a) {
  1224. return (a < b) ? -1 : ((a > b) ? 1 : 0);
  1225. };
  1226. /**
  1227. * Searches the specified array of numbers for the specified value using the
  1228. * binary search algorithm. The array must be sorted (as by the <tt>sort</tt>
  1229. * method) prior to making this call. If it is not sorted, the results are
  1230. * undefined. If the array contains multiple elements with the specified value,
  1231. * there is no guarantee which one will be found.
  1232. *
  1233. * <p>The <i>insertion point</i> is defined as the point at which the value
  1234. * would be inserted into the array: the index of the first element greater than
  1235. * the value, or <tt>array.length</tt>, if all elements in the array are less
  1236. * than the specified value. Note that this guarantees that the return value
  1237. * will be nonnegative if and only if the value is found.
  1238. *
  1239. * @param {number[]} array the array to be searched.
  1240. * @param {number} value the value to be searched for.
  1241. * @returns the index of the search value, if it is contained in the array;
  1242. * otherwise, (-(<i>insertion point</i>) - 1).
  1243. * @param {function} [f] an optional key function.
  1244. */
  1245. pv.search = function(array, value, f) {
  1246. if (!f) f = pv.identity;
  1247. var low = 0, high = array.length - 1;
  1248. while (low <= high) {
  1249. var mid = (low + high) >> 1, midValue = f(array[mid]);
  1250. if (midValue < value) low = mid + 1;
  1251. else if (midValue > value) high = mid - 1;
  1252. else return mid;
  1253. }
  1254. return -low - 1;
  1255. };
  1256. pv.search.index = function(array, value, f) {
  1257. var i = pv.search(array, value, f);
  1258. return (i < 0) ? (-i - 1) : i;
  1259. };
  1260. /**
  1261. * Returns an array of numbers, starting at <tt>start</tt>, incrementing by
  1262. * <tt>step</tt>, until <tt>stop</tt> is reached. The stop value is
  1263. * exclusive. If only a single argument is specified, this value is interpeted
  1264. * as the <i>stop</i> value, with the <i>start</i> value as zero. If only two
  1265. * arguments are specified, the step value is implied to be one.
  1266. *
  1267. * <p>The method is modeled after the built-in <tt>range</tt> method from
  1268. * Python. See the Python documentation for more details.
  1269. *
  1270. * @see <a href="http://docs.python.org/library/functions.html#range">Python range</a>
  1271. * @param {number} [start] the start value.
  1272. * @param {number} stop the stop value.
  1273. * @param {number} [step] the step value.
  1274. * @returns {number[]} an array of numbers.
  1275. */
  1276. pv.range = function(start, stop, step) {
  1277. if (arguments.length == 1) {
  1278. stop = start;
  1279. start = 0;
  1280. }
  1281. if (step == undefined) step = 1;
  1282. if ((stop - start) / step == Infinity) throw new Error("range must be finite");
  1283. var array = [], i = 0, j;
  1284. if (step < 0) {
  1285. while ((j = start + step * i++) > stop) {
  1286. array.push(j);
  1287. }
  1288. } else {
  1289. while ((j = start + step * i++) < stop) {
  1290. array.push(j);
  1291. }
  1292. }
  1293. return array;
  1294. };
  1295. /**
  1296. * Returns a random number in the range [<tt>start</tt>, <tt>stop</tt>) that is
  1297. * a multiple of <tt>step</tt>. More specifically, the returned number is of the
  1298. * form <tt>start</tt> + <i>n</i> * <tt>step</tt>, where <i>n</i> is a
  1299. * nonnegative integer. If <tt>step</tt> is not specified, it defaults to 1,
  1300. * returning a random integer if <tt>start</tt> is also an integer.
  1301. *
  1302. * @param {number} [start] the start value value.
  1303. * @param {number} stop the stop value.
  1304. * @param {number} [step] the step value.
  1305. * @returns {number} a random number between <i>start</i> and <i>stop</i>.
  1306. */
  1307. pv.random = function(start, stop, step) {
  1308. if (arguments.length == 1) {
  1309. stop = start;
  1310. start = 0;
  1311. }
  1312. if (step == undefined) step = 1;
  1313. return step
  1314. ? (Math.floor(Math.random() * (stop - start) / step) * step + start)
  1315. : (Math.random() * (stop - start) + start);
  1316. };
  1317. /**
  1318. * Returns the sum of the specified array. If the specified array is not an
  1319. * array of numbers, an optional accessor function <tt>f</tt> can be specified
  1320. * to map the elements to numbers. See {@link #normalize} for an example.
  1321. * Accessor functions can refer to <tt>this.index</tt>.
  1322. *
  1323. * @param {array} array an array of objects, or numbers.
  1324. * @param {function} [f] an optional accessor function.
  1325. * @returns {number} the sum of the specified array.
  1326. */
  1327. pv.sum = function(array, f) {
  1328. var o = {};
  1329. return array.reduce(f
  1330. ? function(p, d, i) { o.index = i; return p + f.call(o, d); }
  1331. : function(p, d) { return p + d; }, 0);
  1332. };
  1333. /**
  1334. * Returns the maximum value of the specified array. If the specified array is
  1335. * not an array of numbers, an optional accessor function <tt>f</tt> can be
  1336. * specified to map the elements to numbers. See {@link #normalize} for an
  1337. * example. Accessor functions can refer to <tt>this.index</tt>.
  1338. *
  1339. * @param {array} array an array of objects, or numbers.
  1340. * @param {function} [f] an optional accessor function.
  1341. * @returns {number} the maximum value of the specified array.
  1342. */
  1343. pv.max = function(array, f) {
  1344. if (f == pv.index) return array.length - 1;
  1345. return Math.max.apply(null, f ? pv.map(array, f) : array);
  1346. };
  1347. /**
  1348. * Returns the index of the maximum value of the specified array. If the
  1349. * specified array is not an array of numbers, an optional accessor function
  1350. * <tt>f</tt> can be specified to map the elements to numbers. See
  1351. * {@link #normalize} for an example. Accessor functions can refer to
  1352. * <tt>this.index</tt>.
  1353. *
  1354. * @param {array} array an array of objects, or numbers.
  1355. * @param {function} [f] an optional accessor function.
  1356. * @returns {number} the index of the maximum value of the specified array.
  1357. */
  1358. pv.max.index = function(array, f) {
  1359. if (!array.length) return -1;
  1360. if (f == pv.index) return array.length - 1;
  1361. if (!f) f = pv.identity;
  1362. var maxi = 0, maxx = -Infinity, o = {};
  1363. for (var i = 0; i < array.length; i++) {
  1364. o.index = i;
  1365. var x = f.call(o, array[i]);
  1366. if (x > maxx) {
  1367. maxx = x;
  1368. maxi = i;
  1369. }
  1370. }
  1371. return maxi;
  1372. }
  1373. /**
  1374. * Returns the minimum value of the specified array of numbers. If the specified
  1375. * array is not an array of numbers, an optional accessor function <tt>f</tt>
  1376. * can be specified to map the elements to numbers. See {@link #normalize} for
  1377. * an example. Accessor functions can refer to <tt>this.index</tt>.
  1378. *
  1379. * @param {array} array an array of objects, or numbers.
  1380. * @param {function} [f] an optional accessor function.
  1381. * @returns {number} the minimum value of the specified array.
  1382. */
  1383. pv.min = function(array, f) {
  1384. if (f == pv.index) return 0;
  1385. return Math.min.apply(null, f ? pv.map(array, f) : array);
  1386. };
  1387. /**
  1388. * Returns the index of the minimum value of the specified array. If the
  1389. * specified array is not an array of numbers, an optional accessor function
  1390. * <tt>f</tt> can be specified to map the elements to numbers. See
  1391. * {@link #normalize} for an example. Accessor functions can refer to
  1392. * <tt>this.index</tt>.
  1393. *
  1394. * @param {array} array an array of objects, or numbers.
  1395. * @param {function} [f] an optional accessor function.
  1396. * @returns {number} the index of the minimum value of the specified array.
  1397. */
  1398. pv.min.index = function(array, f) {
  1399. if (!array.length) return -1;
  1400. if (f == pv.index) return 0;
  1401. if (!f) f = pv.identity;
  1402. var mini = 0, minx = Infinity, o = {};
  1403. for (var i = 0; i < array.length; i++) {
  1404. o.index = i;
  1405. var x = f.call(o, array[i]);
  1406. if (x < minx) {
  1407. minx = x;
  1408. mini = i;
  1409. }
  1410. }
  1411. return mini;
  1412. }
  1413. /**
  1414. * Returns the arithmetic mean, or average, of the specified array. If the
  1415. * specified array is not an array of numbers, an optional accessor function
  1416. * <tt>f</tt> can be specified to map the elements to numbers. See
  1417. * {@link #normalize} for an example. Accessor functions can refer to
  1418. * <tt>this.index</tt>.
  1419. *
  1420. * @param {array} array an array of objects, or numbers.
  1421. * @param {function} [f] an optional accessor function.
  1422. * @returns {number} the mean of the specified array.
  1423. */
  1424. pv.mean = function(array, f) {
  1425. return pv.sum(array, f) / array.length;
  1426. };
  1427. /**
  1428. * Returns the median of the specified array. If the specified array is not an
  1429. * array of numbers, an optional accessor function <tt>f</tt> can be specified
  1430. * to map the elements to numbers. See {@link #normalize} for an example.
  1431. * Accessor functions can refer to <tt>this.index</tt>.
  1432. *
  1433. * @param {array} array an array of objects, or numbers.
  1434. * @param {function} [f] an optional accessor function.
  1435. * @returns {number} the median of the specified array.
  1436. */
  1437. pv.median = function(array, f) {
  1438. if (f == pv.index) return (array.length - 1) / 2;
  1439. array = pv.map(array, f).sort(pv.naturalOrder);
  1440. if (array.length % 2) return array[Math.floor(array.length / 2)];
  1441. var i = array.length / 2;
  1442. return (array[i - 1] + array[i]) / 2;
  1443. };
  1444. /**
  1445. * Returns the unweighted variance of the specified array. If the specified
  1446. * array is not an array of numbers, an optional accessor function <tt>f</tt>
  1447. * can be specified to map the elements to numbers. See {@link #normalize} for
  1448. * an example. Accessor functions can refer to <tt>this.index</tt>.
  1449. *
  1450. * @param {array} array an array of objects, or numbers.
  1451. * @param {function} [f] an optional accessor function.
  1452. * @returns {number} the variance of the specified array.
  1453. */
  1454. pv.variance = function(array, f) {
  1455. if (array.length < 1) return NaN;
  1456. if (array.length == 1) return 0;
  1457. var mean = pv.mean(array, f), sum = 0, o = {};
  1458. if (!f) f = pv.identity;
  1459. for (var i = 0; i < array.length; i++) {
  1460. o.index = i;
  1461. var d = f.call(o, array[i]) - mean;
  1462. sum += d * d;
  1463. }
  1464. return sum;
  1465. };
  1466. /**
  1467. * Returns an unbiased estimation of the standard deviation of a population,
  1468. * given the specified random sample. If the specified array is not an array of
  1469. * numbers, an optional accessor function <tt>f</tt> can be specified to map the
  1470. * elements to numbers. See {@link #normalize} for an example. Accessor
  1471. * functions can refer to <tt>this.index</tt>.
  1472. *
  1473. * @param {array} array an array of objects, or numbers.
  1474. * @param {function} [f] an optional accessor function.
  1475. * @returns {number} the standard deviation of the specified array.
  1476. */
  1477. pv.deviation = function(array, f) {
  1478. return Math.sqrt(pv.variance(array, f) / (array.length - 1));
  1479. };
  1480. /**
  1481. * Returns the logarithm with a given base value.
  1482. *
  1483. * @param {number} x the number for which to compute the logarithm.
  1484. * @param {number} b the base of the logarithm.
  1485. * @returns {number} the logarithm value.
  1486. */
  1487. pv.log = function(x, b) {
  1488. return Math.log(x) / Math.log(b);
  1489. };
  1490. /**
  1491. * Computes a zero-symmetric logarithm. Computes the logarithm of the absolute
  1492. * value of the input, and determines the sign of the output according to the
  1493. * sign of the input value.
  1494. *
  1495. * @param {number} x the number for which to compute the logarithm.
  1496. * @param {number} b the base of the logarithm.
  1497. * @returns {number} the symmetric log value.
  1498. */
  1499. pv.logSymmetric = function(x, b) {
  1500. return (x == 0) ? 0 : ((x < 0) ? -pv.log(-x, b) : pv.log(x, b));
  1501. };
  1502. /**
  1503. * Computes a zero-symmetric logarithm, with adjustment to values between zero
  1504. * and the logarithm base. This adjustment introduces distortion for values less
  1505. * than the base number, but enables simultaneous plotting of log-transformed
  1506. * data involving both positive and negative numbers.
  1507. *
  1508. * @param {number} x the number for which to compute the logarithm.
  1509. * @param {number} b the base of the logarithm.
  1510. * @returns {number} the adjusted, symmetric log value.
  1511. */
  1512. pv.logAdjusted = function(x, b) {
  1513. if (!isFinite(x)) return x;
  1514. var negative = x < 0;
  1515. if (x < b) x += (b - x) / b;
  1516. return negative ? -pv.log(x, b) : pv.log(x, b);
  1517. };
  1518. /**
  1519. * Rounds an input value down according to its logarithm. The method takes the
  1520. * floor of the logarithm of the value and then uses the resulting value as an
  1521. * exponent for the base value.
  1522. *
  1523. * @param {number} x the number for which to compute the logarithm floor.
  1524. * @param {number} b the base of the logarithm.
  1525. * @returns {number} the rounded-by-logarithm value.
  1526. */
  1527. pv.logFloor = function(x, b) {
  1528. return (x > 0)
  1529. ? Math.pow(b, Math.floor(pv.log(x, b)))
  1530. : -Math.pow(b, -Math.floor(-pv.log(-x, b)));
  1531. };
  1532. /**
  1533. * Rounds an input value up according to its logarithm. The method takes the
  1534. * ceiling of the logarithm of the value and then uses the resulting value as an
  1535. * exponent for the base value.
  1536. *
  1537. * @param {number} x the number for which to compute the logarithm ceiling.
  1538. * @param {number} b the base of the logarithm.
  1539. * @returns {number} the rounded-by-logarithm value.
  1540. */
  1541. pv.logCeil = function(x, b) {
  1542. return (x > 0)
  1543. ? Math.pow(b, Math.ceil(pv.log(x, b)))
  1544. : -Math.pow(b, -Math.ceil(-pv.log(-x, b)));
  1545. };
  1546. (function() {
  1547. var radians = Math.PI / 180,
  1548. degrees = 180 / Math.PI;
  1549. /** Returns the number of radians corresponding to the specified degrees. */
  1550. pv.radians = function(degrees) { return radians * degrees; };
  1551. /** Returns the number of degrees corresponding to the specified radians. */
  1552. pv.degrees = function(radians) { return degrees * radians; };
  1553. })();
  1554. /**
  1555. * Returns all of the property names (keys) of the specified object (a map). The
  1556. * order of the returned array is not defined.
  1557. *
  1558. * @param map an object.
  1559. * @returns {string[]} an array of strings corresponding to the keys.
  1560. * @see #entries
  1561. */
  1562. pv.keys = function(map) {
  1563. var array = [];
  1564. for (var key in map) {
  1565. array.push(key);
  1566. }
  1567. return array;
  1568. };
  1569. /**
  1570. * Returns all of the entries (key-value pairs) of the specified object (a
  1571. * map). The order of the returned array is not defined. Each key-value pair is
  1572. * represented as an object with <tt>key</tt> and <tt>value</tt> attributes,
  1573. * e.g., <tt>{key: "foo", value: 42}</tt>.
  1574. *
  1575. * @param map an object.
  1576. * @returns {array} an array of key-value pairs corresponding to the keys.
  1577. */
  1578. pv.entries = function(map) {
  1579. var array = [];
  1580. for (var key in map) {
  1581. array.push({ key: key, value: map[key] });
  1582. }
  1583. return array;
  1584. };
  1585. /**
  1586. * Returns all of the values (attribute values) of the specified object (a
  1587. * map). The order of the returned array is not defined.
  1588. *
  1589. * @param map an object.
  1590. * @returns {array} an array of objects corresponding to the values.
  1591. * @see #entries
  1592. */
  1593. pv.values = function(map) {
  1594. var array = [];
  1595. for (var key in map) {
  1596. array.push(map[key]);
  1597. }
  1598. return array;
  1599. };
  1600. /**
  1601. * Returns a map constructed from the specified <tt>keys</tt>, using the
  1602. * function <tt>f</tt> to compute the value for each key. The single argument to
  1603. * the value function is the key. The callback is invoked only for indexes of
  1604. * the array which have assigned values; it is not invoked for indexes which
  1605. * have been deleted or which have never been assigned values.
  1606. *
  1607. * <p>For example, this expression creates a map from strings to string length:
  1608. *
  1609. * <pre>pv.dict(["one", "three", "seventeen"], function(s) s.length)</pre>
  1610. *
  1611. * The returned value is <tt>{one: 3, three: 5, seventeen: 9}</tt>. Accessor
  1612. * functions can refer to <tt>this.index</tt>.
  1613. *
  1614. * @param {array} keys an array.
  1615. * @param {function} f a value function.
  1616. * @returns a map from keys to values.
  1617. */
  1618. pv.dict = function(keys, f) {
  1619. var m = {}, o = {};
  1620. for (var i = 0; i < keys.length; i++) {
  1621. if (i in keys) {
  1622. var k = keys[i];
  1623. o.index = i;
  1624. m[k] = f.call(o, k);
  1625. }
  1626. }
  1627. return m;
  1628. };
  1629. /**
  1630. * Returns a {@link pv.Dom} operator for the given map. This is a convenience
  1631. * factory method, equivalent to <tt>new pv.Dom(map)</tt>. To apply the operator
  1632. * and retrieve the root node, call {@link pv.Dom#root}; to retrieve all nodes
  1633. * flattened, use {@link pv.Dom#nodes}.
  1634. *
  1635. * @see pv.Dom
  1636. * @param map a map from which to construct a DOM.
  1637. * @returns {pv.Dom} a DOM operator for the specified map.
  1638. */
  1639. pv.dom = function(map) {
  1640. return new pv.Dom(map);
  1641. };
  1642. /**
  1643. * Constructs a DOM operator for the specified map. This constructor should not
  1644. * be invoked directly; use {@link pv.dom} instead.
  1645. *
  1646. * @class Represets a DOM operator for the specified map. This allows easy
  1647. * transformation of a hierarchical JavaScript object (such as a JSON map) to a
  1648. * W3C Document Object Model hierarchy. For more information on which attributes
  1649. * and methods from the specification are supported, see {@link pv.Dom.Node}.
  1650. *
  1651. * <p>Leaves in the map are determined using an associated <i>leaf</i> function;
  1652. * see {@link #leaf}. By default, leaves are any value whose type is not
  1653. * "object", such as numbers or strings.
  1654. *
  1655. * @param map a map from which to construct a DOM.
  1656. */
  1657. pv.Dom = function(map) {
  1658. this.$map = map;
  1659. };
  1660. /** @private The default leaf function. */
  1661. pv.Dom.prototype.$leaf = function(n) {
  1662. return typeof n != "object";
  1663. };
  1664. /**
  1665. * Sets or gets the leaf function for this DOM operator. The leaf function
  1666. * identifies which values in the map are leaves, and which are internal nodes.
  1667. * By default, objects are considered internal nodes, and primitives (such as
  1668. * numbers and strings) are considered leaves.
  1669. *
  1670. * @param {function} f the new leaf function.
  1671. * @returns the current leaf function, or <tt>this</tt>.
  1672. */
  1673. pv.Dom.prototype.leaf = function(f) {
  1674. if (arguments.length) {
  1675. this.$leaf = f;
  1676. return this;
  1677. }
  1678. return this.$leaf;
  1679. };
  1680. /**
  1681. * Applies the DOM operator, returning the root node.
  1682. *
  1683. * @returns {pv.Dom.Node} the root node.
  1684. * @param {string} [nodeName] optional node name for the root.
  1685. */
  1686. pv.Dom.prototype.root = function(nodeName) {
  1687. var leaf = this.$leaf, root = recurse(this.$map);
  1688. /** @private */
  1689. function recurse(map) {
  1690. var n = new pv.Dom.Node();
  1691. for (var k in map) {
  1692. var v = map[k];
  1693. n.appendChild(leaf(v) ? new pv.Dom.Node(v) : recurse(v)).nodeName = k;
  1694. }
  1695. return n;
  1696. }
  1697. root.nodeName = nodeName;
  1698. return root;
  1699. };
  1700. /**
  1701. * Applies the DOM operator, returning the array of all nodes in preorder
  1702. * traversal.
  1703. *
  1704. * @returns {array} the array of nodes in preorder traversal.
  1705. */
  1706. pv.Dom.prototype.nodes = function() {
  1707. return this.root().nodes();
  1708. };
  1709. /**
  1710. * Constructs a DOM node for the specified value. Instances of this class are
  1711. * not typically created directly; instead they are generated from a JavaScript
  1712. * map using the {@link pv.Dom} operator.
  1713. *
  1714. * @class Represents a <tt>Node</tt> in the W3C Document Object Model.
  1715. */
  1716. pv.Dom.Node = function(value) {
  1717. this.nodeValue = value;
  1718. this.childNodes = [];
  1719. };
  1720. /**
  1721. * The node name. When generated from a map, the node name corresponds to the
  1722. * key at the given level in the map. Note that the root node has no associated
  1723. * key, and thus has an undefined node name (and no <tt>parentNode</tt>).
  1724. *
  1725. * @type string
  1726. * @field pv.Dom.Node.prototype.nodeName
  1727. */
  1728. /**
  1729. * The node value. When generated from a map, node value corresponds to the leaf
  1730. * value for leaf nodes, and is undefined for internal nodes.
  1731. *
  1732. * @field pv.Dom.Node.prototype.nodeValue
  1733. */
  1734. /**
  1735. * The array of child nodes. This array is empty for leaf nodes. An easy way to
  1736. * check if child nodes exist is to query <tt>firstChild</tt>.
  1737. *
  1738. * @type array
  1739. * @field pv.Dom.Node.prototype.childNodes
  1740. */
  1741. /**
  1742. * The parent node, which is null for root nodes.
  1743. *
  1744. * @type pv.Dom.Node
  1745. */
  1746. pv.Dom.Node.prototype.parentNode = null;
  1747. /**
  1748. * The first child, which is null for leaf nodes.
  1749. *
  1750. * @type pv.Dom.Node
  1751. */
  1752. pv.Dom.Node.prototype.firstChild = null;
  1753. /**
  1754. * The last child, which is null for leaf nodes.
  1755. *
  1756. * @type pv.Dom.Node
  1757. */
  1758. pv.Dom.Node.prototype.lastChild = null;
  1759. /**
  1760. * The previous sibling node, which is null for the first child.
  1761. *
  1762. * @type pv.Dom.Node
  1763. */
  1764. pv.Dom.Node.prototype.previousSibling = null;
  1765. /**
  1766. * The next sibling node, which is null for the last child.
  1767. *
  1768. * @type pv.Dom.Node
  1769. */
  1770. pv.Dom.Node.prototype.nextSibling = null;
  1771. /**
  1772. * Removes the specified child node from this node.
  1773. *
  1774. * @throws Error if the specified child is not a child of this node.
  1775. * @returns {pv.Dom.Node} the removed child.
  1776. */
  1777. pv.Dom.Node.prototype.removeChild = function(n) {
  1778. var i = this.childNodes.indexOf(n);
  1779. if (i == -1) throw new Error("child not found");
  1780. this.childNodes.splice(i, 1);
  1781. if (n.previousSibling) n.previousSibling.nextSibling = n.nextSibling;
  1782. else this.firstChild = n.nextSibling;
  1783. if (n.nextSibling) n.nextSibling.previousSibling = n.previousSibling;
  1784. else this.lastChild = n.previousSibling;
  1785. delete n.nextSibling;
  1786. delete n.previousSibling;
  1787. delete n.parentNode;
  1788. return n;
  1789. };
  1790. /**
  1791. * Appends the specified child node to this node. If the specified child is
  1792. * already part of the DOM, the child is first removed before being added to
  1793. * this node.
  1794. *
  1795. * @returns {pv.Dom.Node} the appended child.
  1796. */
  1797. pv.Dom.Node.prototype.appendChild = function(n) {
  1798. if (n.parentNode) n.parentNode.removeChild(n);
  1799. n.parentNode = this;
  1800. n.previousSibling = this.lastChild;
  1801. if (this.lastChild) this.lastChild.nextSibling = n;
  1802. else this.firstChild = n;
  1803. this.lastChild = n;
  1804. this.childNodes.push(n);
  1805. return n;
  1806. };
  1807. /**
  1808. * Inserts the specified child <i>n</i> before the given reference child
  1809. * <i>r</i> of this node. If <i>r</i> is null, this method is equivalent to
  1810. * {@link #appendChild}. If <i>n</i> is already part of the DOM, it is first
  1811. * removed before being inserted.
  1812. *
  1813. * @throws Error if <i>r</i> is non-null and not a child of this node.
  1814. * @returns {pv.Dom.Node} the inserted child.
  1815. */
  1816. pv.Dom.Node.prototype.insertBefore = function(n, r) {
  1817. if (!r) return this.appendChild(n);
  1818. var i = this.childNodes.indexOf(r);
  1819. if (i == -1) throw new Error("child not found");
  1820. if (n.parentNode) n.parentNode.removeChild(n);
  1821. n.parentNode = this;
  1822. n.nextSibling = r;
  1823. n.previousSibling = r.previousSibling;
  1824. if (r.previousSibling) {
  1825. r.previousSibling.nextSibling = n;
  1826. } else {
  1827. if (r == this.lastChild) this.lastChild = n;
  1828. this.firstChild = n;
  1829. }
  1830. this.childNodes.splice(i, 0, n);
  1831. return n;
  1832. };
  1833. /**
  1834. * Replaces the specified child <i>r</i> of this node with the node <i>n</i>. If
  1835. * <i>n</i> is already part of the DOM, it is first removed before being added.
  1836. *
  1837. * @throws Error if <i>r</i> is not a child of this node.
  1838. */
  1839. pv.Dom.Node.prototype.replaceChild = function(n, r) {
  1840. var i = this.childNodes.indexOf(r);
  1841. if (i == -1) throw new Error("child not found");
  1842. if (n.parentNode) n.parentNode.removeChild(n);
  1843. n.parentNode = this;
  1844. n.nextSibling = r.nextSibling;
  1845. n.previousSibling = r.previousSibling;
  1846. if (r.previousSibling) r.previousSibling.nextSibling = n;
  1847. else this.firstChild = n;
  1848. if (r.nextSibling) r.nextSibling.previousSibling = n;
  1849. else this.lastChild = n;
  1850. this.childNodes[i] = n;
  1851. return r;
  1852. };
  1853. /**
  1854. * Visits each node in the tree in preorder traversal, applying the specified
  1855. * function <i>f</i>. The arguments to the function are:<ol>
  1856. *
  1857. * <li>The current node.
  1858. * <li>The current depth, starting at 0 for the root node.</ol>
  1859. *
  1860. * @param {function} f a function to apply to each node.
  1861. */
  1862. pv.Dom.Node.prototype.visitBefore = function(f) {
  1863. function visit(n, i) {
  1864. f(n, i);
  1865. for (var c = n.firstChild; c; c = c.nextSibling) {
  1866. visit(c, i + 1);
  1867. }
  1868. }
  1869. visit(this, 0);
  1870. };
  1871. /**
  1872. * Visits each node in the tree in postorder traversal, applying the specified
  1873. * function <i>f</i>. The arguments to the function are:<ol>
  1874. *
  1875. * <li>The current node.
  1876. * <li>The current depth, starting at 0 for the root node.</ol>
  1877. *
  1878. * @param {function} f a function to apply to each node.
  1879. */
  1880. pv.Dom.Node.prototype.visitAfter = function(f) {
  1881. function visit(n, i) {
  1882. for (var c = n.firstChild; c; c = c.nextSibling) {
  1883. visit(c, i + 1);
  1884. }
  1885. f(n, i);
  1886. }
  1887. visit(this, 0);
  1888. };
  1889. /**
  1890. * Sorts child nodes of this node, and all descendent nodes recursively, using
  1891. * the specified comparator function <tt>f</tt>. The comparator function is
  1892. * passed two nodes to compare.
  1893. *
  1894. * <p>Note: during the sort operation, the comparator function should not rely
  1895. * on the tree being well-formed; the values of <tt>previousSibling</tt> and
  1896. * <tt>nextSibling</tt> for the nodes being compared are not defined during the
  1897. * sort operation.
  1898. *
  1899. * @param {function} f a comparator function.
  1900. * @returns this.
  1901. */
  1902. pv.Dom.Node.prototype.sort = function(f) {
  1903. if (this.firstChild) {
  1904. this.childNodes.sort(f);
  1905. var p = this.firstChild = this.childNodes[0], c;
  1906. delete p.previousSibling;
  1907. for (var i = 1; i < this.childNodes.length; i++) {
  1908. p.sort(f);
  1909. c = this.childNodes[i];
  1910. c.previousSibling = p;
  1911. p = p.nextSibling = c;
  1912. }
  1913. this.lastChild = p;
  1914. delete p.nextSibling;
  1915. p.sort(f);
  1916. }
  1917. return this;
  1918. };
  1919. /**
  1920. * Reverses all sibling nodes.
  1921. *
  1922. * @returns this.
  1923. */
  1924. pv.Dom.Node.prototype.reverse = function() {
  1925. var childNodes = [];
  1926. this.visitAfter(function(n) {
  1927. while (n.lastChild) childNodes.push(n.removeChild(n.lastChild));
  1928. for (var c; c = childNodes.pop();) n.insertBefore(c, n.firstChild);
  1929. });
  1930. return this;
  1931. };
  1932. /** Returns all descendants of this node in preorder traversal. */
  1933. pv.Dom.Node.prototype.nodes = function() {
  1934. var array = [];
  1935. /** @private */
  1936. function flatten(node) {
  1937. array.push(node);
  1938. node.childNodes.forEach(flatten);
  1939. }
  1940. flatten(this, array);
  1941. return array;
  1942. };
  1943. /**
  1944. * Toggles the child nodes of this node. If this node is not yet toggled, this
  1945. * method removes all child nodes and appends them to a new <tt>toggled</tt>
  1946. * array attribute on this node. Otherwise, if this node is toggled, this method
  1947. * re-adds all toggled child nodes and deletes the <tt>toggled</tt> attribute.
  1948. *
  1949. * <p>This method has no effect if the node has no child nodes.
  1950. *
  1951. * @param {boolean} [recursive] whether the toggle should apply to descendants.
  1952. */
  1953. pv.Dom.Node.prototype.toggle = function(recursive) {
  1954. if (recursive) return this.toggled
  1955. ? this.visitBefore(function(n) { if (n.toggled) n.toggle(); })
  1956. : this.visitAfter(function(n) { if (!n.toggled) n.toggle(); });
  1957. var n = this;
  1958. if (n.toggled) {
  1959. for (var c; c = n.toggled.pop();) n.appendChild(c);
  1960. delete n.toggled;
  1961. } else if (n.lastChild) {
  1962. n.toggled = [];
  1963. while (n.lastChild) n.toggled.push(n.removeChild(n.lastChild));
  1964. }
  1965. };
  1966. /**
  1967. * Given a flat array of values, returns a simple DOM with each value wrapped by
  1968. * a node that is a child of the root node.
  1969. *
  1970. * @param {array} values.
  1971. * @returns {array} nodes.
  1972. */
  1973. pv.nodes = function(values) {
  1974. var root = new pv.Dom.Node();
  1975. for (var i = 0; i < values.length; i++) {
  1976. root.appendChild(new pv.Dom.Node(values[i]));
  1977. }
  1978. return root.nodes();
  1979. };
  1980. /**
  1981. * Returns a {@link pv.Tree} operator for the specified array. This is a
  1982. * convenience factory method, equivalent to <tt>new pv.Tree(array)</tt>.
  1983. *
  1984. * @see pv.Tree
  1985. * @param {array} array an array from which to construct a tree.
  1986. * @returns {pv.Tree} a tree operator for the specified array.
  1987. */
  1988. pv.tree = function(array) {
  1989. return new pv.Tree(array);
  1990. };
  1991. /**
  1992. * Constructs a tree operator for the specified array. This constructor should
  1993. * not be invoked directly; use {@link pv.tree} instead.
  1994. *
  1995. * @class Represents a tree operator for the specified array. The tree operator
  1996. * allows a hierarchical map to be constructed from an array; it is similar to
  1997. * the {@link pv.Nest} operator, except the hierarchy is derived dynamically
  1998. * from the array elements.
  1999. *
  2000. * <p>For example, given an array of size information for ActionScript classes:
  2001. *
  2002. * <pre>{ name: "flare.flex.FlareVis", size: 4116 },
  2003. * { name: "flare.physics.DragForce", size: 1082 },
  2004. * { name: "flare.physics.GravityForce", size: 1336 }, ...</pre>
  2005. *
  2006. * To facilitate visualization, it may be useful to nest the elements by their
  2007. * package hierarchy:
  2008. *
  2009. * <pre>var tree = pv.tree(classes)
  2010. * .keys(function(d) d.name.split("."))
  2011. * .map();</pre>
  2012. *
  2013. * The resulting tree is:
  2014. *
  2015. * <pre>{ flare: {
  2016. * flex: {
  2017. * FlareVis: {
  2018. * name: "flare.flex.FlareVis",
  2019. * size: 4116 } },
  2020. * physics: {
  2021. * DragForce: {
  2022. * name: "flare.physics.DragForce",
  2023. * size: 1082 },
  2024. * GravityForce: {
  2025. * name: "flare.physics.GravityForce",
  2026. * size: 1336 } },
  2027. * ... } }</pre>
  2028. *
  2029. * By specifying a value function,
  2030. *
  2031. * <pre>var tree = pv.tree(classes)
  2032. * .keys(function(d) d.name.split("."))
  2033. * .value(function(d) d.size)
  2034. * .map();</pre>
  2035. *
  2036. * we can further eliminate redundant data:
  2037. *
  2038. * <pre>{ flare: {
  2039. * flex: {
  2040. * FlareVis: 4116 },
  2041. * physics: {
  2042. * DragForce: 1082,
  2043. * GravityForce: 1336 },
  2044. * ... } }</pre>
  2045. *
  2046. * For visualizations with large data sets, performance improvements may be seen
  2047. * by storing the data in a tree format, and then flattening it into an array at
  2048. * runtime with {@link pv.Flatten}.
  2049. *
  2050. * @param {array} array an array from which to construct a tree.
  2051. */
  2052. pv.Tree = function(array) {
  2053. this.array = array;
  2054. };
  2055. /**
  2056. * Assigns a <i>keys</i> function to this operator; required. The keys function
  2057. * returns an array of <tt>string</tt>s for each element in the associated
  2058. * array; these keys determine how the elements are nested in the tree. The
  2059. * returned keys should be unique for each element in the array; otherwise, the
  2060. * behavior of this operator is undefined.
  2061. *
  2062. * @param {function} k the keys function.
  2063. * @returns {pv.Tree} this.
  2064. */
  2065. pv.Tree.prototype.keys = function(k) {
  2066. this.k = k;
  2067. return this;
  2068. };
  2069. /**
  2070. * Assigns a <i>value</i> function to this operator; optional. The value
  2071. * function specifies an optional transformation of the element in the array
  2072. * before it is inserted into the map. If no value function is specified, it is
  2073. * equivalent to using the identity function.
  2074. *
  2075. * @param {function} k the value function.
  2076. * @returns {pv.Tree} this.
  2077. */
  2078. pv.Tree.prototype.value = function(v) {
  2079. this.v = v;
  2080. return this;
  2081. };
  2082. /**
  2083. * Returns a hierarchical map of values. The hierarchy is determined by the keys
  2084. * function; the values in the map are determined by the value function.
  2085. *
  2086. * @returns a hierarchical map of values.
  2087. */
  2088. pv.Tree.prototype.map = function() {
  2089. var map = {}, o = {};
  2090. for (var i = 0; i < this.array.length; i++) {
  2091. o.index = i;
  2092. var value = this.array[i], keys = this.k.call(o, value), node = map;
  2093. for (var j = 0; j < keys.length - 1; j++) {
  2094. node = node[keys[j]] || (node[keys[j]] = {});
  2095. }
  2096. node[keys[j]] = this.v ? this.v.call(o, value) : value;
  2097. }
  2098. return map;
  2099. };
  2100. /**
  2101. * Returns a {@link pv.Nest} operator for the specified array. This is a
  2102. * convenience factory method, equivalent to <tt>new pv.Nest(array)</tt>.
  2103. *
  2104. * @see pv.Nest
  2105. * @param {array} array an array of elements to nest.
  2106. * @returns {pv.Nest} a nest operator for the specified array.
  2107. */
  2108. pv.nest = function(array) {
  2109. return new pv.Nest(array);
  2110. };
  2111. /**
  2112. * Constructs a nest operator for the specified array. This constructor should
  2113. * not be invoked directly; use {@link pv.nest} instead.
  2114. *
  2115. * @class Represents a {@link Nest} operator for the specified array. Nesting
  2116. * allows elements in an array to be grouped into a hierarchical tree
  2117. * structure. The levels in the tree are specified by <i>key</i> functions. The
  2118. * leaf nodes of the tree can be sorted by value, while the internal nodes can
  2119. * be sorted by key. Finally, the tree can be returned either has a
  2120. * multidimensional array via {@link #entries}, or as a hierarchical map via
  2121. * {@link #map}. The {@link #rollup} routine similarly returns a map, collapsing
  2122. * the elements in each leaf node using a summary function.
  2123. *
  2124. * <p>For example, consider the following tabular data structure of Barley
  2125. * yields, from various sites in Minnesota during 1931-2:
  2126. *
  2127. * <pre>{ yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
  2128. * { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
  2129. * { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" }, ...</pre>
  2130. *
  2131. * To facilitate visualization, it may be useful to nest the elements first by
  2132. * year, and then by variety, as follows:
  2133. *
  2134. * <pre>var nest = pv.nest(yields)
  2135. * .key(function(d) d.year)
  2136. * .key(function(d) d.variety)
  2137. * .entries();</pre>
  2138. *
  2139. * This returns a nested array. Each element of the outer array is a key-values
  2140. * pair, listing the values for each distinct key:
  2141. *
  2142. * <pre>{ key: 1931, values: [
  2143. * { key: "Manchuria", values: [
  2144. * { yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
  2145. * { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
  2146. * { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" },
  2147. * ...
  2148. * ] },
  2149. * { key: "Glabron", values: [
  2150. * { yield: 43.07, variety: "Glabron", year: 1931, site: "University Farm" },
  2151. * { yield: 55.20, variety: "Glabron", year: 1931, site: "Waseca" },
  2152. * ...
  2153. * ] },
  2154. * ] },
  2155. * { key: 1932, values: ... }</pre>
  2156. *
  2157. * Further details, including sorting and rollup, is provided below on the
  2158. * corresponding methods.
  2159. *
  2160. * @param {array} array an array of elements to nest.
  2161. */
  2162. pv.Nest = function(array) {
  2163. this.array = array;
  2164. this.keys = [];
  2165. };
  2166. /**
  2167. * Nests using the specified key function. Multiple keys may be added to the
  2168. * nest; the array elements will be nested in the order keys are specified.
  2169. *
  2170. * @param {function} key a key function; must return a string or suitable map
  2171. * key.
  2172. * @returns {pv.Nest} this.
  2173. */
  2174. pv.Nest.prototype.key = function(key) {
  2175. this.keys.push(key);
  2176. return this;
  2177. };
  2178. /**
  2179. * Sorts the previously-added keys. The natural sort order is used by default
  2180. * (see {@link pv.naturalOrder}); if an alternative order is desired,
  2181. * <tt>order</tt> should be a comparator function. If this method is not called
  2182. * (i.e., keys are <i>unsorted</i>), keys will appear in the order they appear
  2183. * in the underlying elements array. For example,
  2184. *
  2185. * <pre>pv.nest(yields)
  2186. * .key(function(d) d.year)
  2187. * .key(function(d) d.variety)
  2188. * .sortKeys()
  2189. * .entries()</pre>
  2190. *
  2191. * groups yield data by year, then variety, and sorts the variety groups
  2192. * lexicographically (since the variety attribute is a string).
  2193. *
  2194. * <p>Key sort order is only used in conjunction with {@link #entries}, which
  2195. * returns an array of key-values pairs. If the nest is used to construct a
  2196. * {@link #map} instead, keys are unsorted.
  2197. *
  2198. * @param {function} [order] an optional comparator function.
  2199. * @returns {pv.Nest} this.
  2200. */
  2201. pv.Nest.prototype.sortKeys = function(order) {
  2202. this.keys[this.keys.length - 1].order = order || pv.naturalOrder;
  2203. return this;
  2204. };
  2205. /**
  2206. * Sorts the leaf values. The natural sort order is used by default (see
  2207. * {@link pv.naturalOrder}); if an alternative order is desired, <tt>order</tt>
  2208. * should be a comparator function. If this method is not called (i.e., values
  2209. * are <i>unsorted</i>), values will appear in the order they appear in the
  2210. * underlying elements array. For example,
  2211. *
  2212. * <pre>pv.nest(yields)
  2213. * .key(function(d) d.year)
  2214. * .key(function(d) d.variety)
  2215. * .sortValues(function(a, b) a.yield - b.yield)
  2216. * .entries()</pre>
  2217. *
  2218. * groups yield data by year, then variety, and sorts the values for each
  2219. * variety group by yield.
  2220. *
  2221. * <p>Value sort order, unlike keys, applies to both {@link #entries} and
  2222. * {@link #map}. It has no effect on {@link #rollup}.
  2223. *
  2224. * @param {function} [order] an optional comparator function.
  2225. * @returns {pv.Nest} this.
  2226. */
  2227. pv.Nest.prototype.sortValues = function(order) {
  2228. this.order = order || pv.naturalOrder;
  2229. return this;
  2230. };
  2231. /**
  2232. * Returns a hierarchical map of values. Each key adds one level to the
  2233. * hierarchy. With only a single key, the returned map will have a key for each
  2234. * distinct value of the key function; the correspond value with be an array of
  2235. * elements with that key value. If a second key is added, this will be a nested
  2236. * map. For example:
  2237. *
  2238. * <pre>pv.nest(yields)
  2239. * .key(function(d) d.variety)
  2240. * .key(function(d) d.site)
  2241. * .map()</pre>
  2242. *
  2243. * returns a map <tt>m</tt> such that <tt>m[variety][site]</tt> is an array, a subset of
  2244. * <tt>yields</tt>, with each element having the given variety and site.
  2245. *
  2246. * @returns a hierarchical map of values.
  2247. */
  2248. pv.Nest.prototype.map = function() {
  2249. var map = {}, values = [];
  2250. /* Build the map. */
  2251. for (var i, j = 0; j < this.array.length; j++) {
  2252. var x = this.array[j];
  2253. var m = map;
  2254. for (i = 0; i < this.keys.length - 1; i++) {
  2255. var k = this.keys[i](x);
  2256. if (!m[k]) m[k] = {};
  2257. m = m[k];
  2258. }
  2259. k = this.keys[i](x);
  2260. if (!m[k]) {
  2261. var a = [];
  2262. values.push(a);
  2263. m[k] = a;
  2264. }
  2265. m[k].push(x);
  2266. }
  2267. /* Sort each leaf array. */
  2268. if (this.order) {
  2269. for (var i = 0; i < values.length; i++) {
  2270. values[i].sort(this.order);
  2271. }
  2272. }
  2273. return map;
  2274. };
  2275. /**
  2276. * Returns a hierarchical nested array. This method is similar to
  2277. * {@link pv.entries}, but works recursively on the entire hierarchy. Rather
  2278. * than returning a map like {@link #map}, this method returns a nested
  2279. * array. Each element of the array has a <tt>key</tt> and <tt>values</tt>
  2280. * field. For leaf nodes, the <tt>values</tt> array will be a subset of the
  2281. * underlying elements array; for non-leaf nodes, the <tt>values</tt> array will
  2282. * contain more key-values pairs.
  2283. *
  2284. * <p>For an example usage, see the {@link Nest} constructor.
  2285. *
  2286. * @returns a hierarchical nested array.
  2287. */
  2288. pv.Nest.prototype.entries = function() {
  2289. /** Recursively extracts the entries for the given map. */
  2290. function entries(map) {
  2291. var array = [];
  2292. for (var k in map) {
  2293. var v = map[k];
  2294. array.push({ key: k, values: (v instanceof Array) ? v : entries(v) });
  2295. };
  2296. return array;
  2297. }
  2298. /** Recursively sorts the values for the given key-values array. */
  2299. function sort(array, i) {
  2300. var o = this.keys[i].order;
  2301. if (o) array.sort(function(a, b) { return o(a.key, b.key); });
  2302. if (++i < this.keys.length) {
  2303. for (var j = 0; j < array.length; j++) {
  2304. sort.call(this, array[j].values, i);
  2305. }
  2306. }
  2307. return array;
  2308. }
  2309. return sort.call(this, entries(this.map()), 0);
  2310. };
  2311. /**
  2312. * Returns a rollup map. The behavior of this method is the same as
  2313. * {@link #map}, except that the leaf values are replaced with the return value
  2314. * of the specified rollup function <tt>f</tt>. For example,
  2315. *
  2316. * <pre>pv.nest(yields)
  2317. * .key(function(d) d.site)
  2318. * .rollup(function(v) pv.median(v, function(d) d.yield))</pre>
  2319. *
  2320. * first groups yield data by site, and then returns a map from site to median
  2321. * yield for the given site.
  2322. *
  2323. * @see #map
  2324. * @param {function} f a rollup function.
  2325. * @returns a hierarchical map, with the leaf values computed by <tt>f</tt>.
  2326. */
  2327. pv.Nest.prototype.rollup = function(f) {
  2328. /** Recursively descends to the leaf nodes (arrays) and does rollup. */
  2329. function rollup(map) {
  2330. for (var key in map) {
  2331. var value = map[key];
  2332. if (value instanceof Array) {
  2333. map[key] = f(value);
  2334. } else {
  2335. rollup(value);
  2336. }
  2337. }
  2338. return map;
  2339. }
  2340. return rollup(this.map());
  2341. };
  2342. /**
  2343. * Returns a {@link pv.Flatten} operator for the specified map. This is a
  2344. * convenience factory method, equivalent to <tt>new pv.Flatten(map)</tt>.
  2345. *
  2346. * @see pv.Flatten
  2347. * @param map a map to flatten.
  2348. * @returns {pv.Flatten} a flatten operator for the specified map.
  2349. */
  2350. pv.flatten = function(map) {
  2351. return new pv.Flatten(map);
  2352. };
  2353. /**
  2354. * Constructs a flatten operator for the specified map. This constructor should
  2355. * not be invoked directly; use {@link pv.flatten} instead.
  2356. *
  2357. * @class Represents a flatten operator for the specified array. Flattening
  2358. * allows hierarchical maps to be flattened into an array. The levels in the
  2359. * input tree are specified by <i>key</i> functions.
  2360. *
  2361. * <p>For example, consider the following hierarchical data structure of Barley
  2362. * yields, from various sites in Minnesota during 1931-2:
  2363. *
  2364. * <pre>{ 1931: {
  2365. * Manchuria: {
  2366. * "University Farm": 27.00,
  2367. * "Waseca": 48.87,
  2368. * "Morris": 27.43,
  2369. * ... },
  2370. * Glabron: {
  2371. * "University Farm": 43.07,
  2372. * "Waseca": 55.20,
  2373. * ... } },
  2374. * 1932: {
  2375. * ... } }</pre>
  2376. *
  2377. * To facilitate visualization, it may be useful to flatten the tree into a
  2378. * tabular array:
  2379. *
  2380. * <pre>var array = pv.flatten(yields)
  2381. * .key("year")
  2382. * .key("variety")
  2383. * .key("site")
  2384. * .key("yield")
  2385. * .array();</pre>
  2386. *
  2387. * This returns an array of object elements. Each element in the array has
  2388. * attributes corresponding to this flatten operator's keys:
  2389. *
  2390. * <pre>{ site: "University Farm", variety: "Manchuria", year: 1931, yield: 27 },
  2391. * { site: "Waseca", variety: "Manchuria", year: 1931, yield: 48.87 },
  2392. * { site: "Morris", variety: "Manchuria", year: 1931, yield: 27.43 },
  2393. * { site: "University Farm", variety: "Glabron", year: 1931, yield: 43.07 },
  2394. * { site: "Waseca", variety: "Glabron", year: 1931, yield: 55.2 }, ...</pre>
  2395. *
  2396. * <p>The flatten operator is roughly the inverse of the {@link pv.Nest} and
  2397. * {@link pv.Tree} operators.
  2398. *
  2399. * @param map a map to flatten.
  2400. */
  2401. pv.Flatten = function(map) {
  2402. this.map = map;
  2403. this.keys = [];
  2404. };
  2405. /**
  2406. * Flattens using the specified key function. Multiple keys may be added to the
  2407. * flatten; the tiers of the underlying tree must correspond to the specified
  2408. * keys, in order. The order of the returned array is undefined; however, you
  2409. * can easily sort it.
  2410. *
  2411. * @param {string} key the key name.
  2412. * @param {function} [f] an optional value map function.
  2413. * @returns {pv.Nest} this.
  2414. */
  2415. pv.Flatten.prototype.key = function(key, f) {
  2416. this.keys.push({name: key, value: f});
  2417. delete this.$leaf;
  2418. return this;
  2419. };
  2420. /**
  2421. * Flattens using the specified leaf function. This is an alternative to
  2422. * specifying an explicit set of keys; the tiers of the underlying tree will be
  2423. * determined dynamically by recursing on the values, and the resulting keys
  2424. * will be stored in the entries <tt>keys</tt> attribute. The leaf function must
  2425. * return true for leaves, and false for internal nodes.
  2426. *
  2427. * @param {function} f a leaf function.
  2428. * @returns {pv.Nest} this.
  2429. */
  2430. pv.Flatten.prototype.leaf = function(f) {
  2431. this.keys.length = 0;
  2432. this.$leaf = f;
  2433. return this;
  2434. };
  2435. /**
  2436. * Returns the flattened array. Each entry in the array is an object; each
  2437. * object has attributes corresponding to this flatten operator's keys.
  2438. *
  2439. * @returns an array of elements from the flattened map.
  2440. */
  2441. pv.Flatten.prototype.array = function() {
  2442. var entries = [], stack = [], keys = this.keys, leaf = this.$leaf;
  2443. /* Recursively visit using the leaf function. */
  2444. if (leaf) {
  2445. function recurse(value, i) {
  2446. if (leaf(value)) {
  2447. entries.push({keys: stack.slice(), value: value});
  2448. } else {
  2449. for (var key in value) {
  2450. stack.push(key);
  2451. recurse(value[key], i + 1);
  2452. stack.pop();
  2453. }
  2454. }
  2455. }
  2456. recurse(this.map, 0);
  2457. return entries;
  2458. }
  2459. /* Recursively visits the specified value. */
  2460. function visit(value, i) {
  2461. if (i < keys.length - 1) {
  2462. for (var key in value) {
  2463. stack.push(key);
  2464. visit(value[key], i + 1);
  2465. stack.pop();
  2466. }
  2467. } else {
  2468. entries.push(stack.concat(value));
  2469. }
  2470. }
  2471. visit(this.map, 0);
  2472. return entries.map(function(stack) {
  2473. var m = {};
  2474. for (var i = 0; i < keys.length; i++) {
  2475. var k = keys[i], v = stack[i];
  2476. m[k.name] = k.value ? k.value.call(null, v) : v;
  2477. }
  2478. return m;
  2479. });
  2480. };
  2481. /**
  2482. * Returns a {@link pv.Vector} for the specified <i>x</i> and <i>y</i>
  2483. * coordinate. This is a convenience factory method, equivalent to <tt>new
  2484. * pv.Vector(x, y)</tt>.
  2485. *
  2486. * @see pv.Vector
  2487. * @param {number} x the <i>x</i> coordinate.
  2488. * @param {number} y the <i>y</i> coordinate.
  2489. * @returns {pv.Vector} a vector for the specified coordinates.
  2490. */
  2491. pv.vector = function(x, y) {
  2492. return new pv.Vector(x, y);
  2493. };
  2494. /**
  2495. * Constructs a {@link pv.Vector} for the specified <i>x</i> and <i>y</i>
  2496. * coordinate. This constructor should not be invoked directly; use
  2497. * {@link pv.vector} instead.
  2498. *
  2499. * @class Represents a two-dimensional vector; a 2-tuple <i>&#x27e8;x,
  2500. * y&#x27e9;</i>. The intent of this class is to simplify vector math. Note that
  2501. * in performance-sensitive cases it may be more efficient to represent 2D
  2502. * vectors as simple objects with <tt>x</tt> and <tt>y</tt> attributes, rather
  2503. * than using instances of this class.
  2504. *
  2505. * @param {number} x the <i>x</i> coordinate.
  2506. * @param {number} y the <i>y</i> coordinate.
  2507. */
  2508. pv.Vector = function(x, y) {
  2509. this.x = x;
  2510. this.y = y;
  2511. };
  2512. /**
  2513. * Returns a vector perpendicular to this vector: <i>&#x27e8;-y, x&#x27e9;</i>.
  2514. *
  2515. * @returns {pv.Vector} a perpendicular vector.
  2516. */
  2517. pv.Vector.prototype.perp = function() {
  2518. return new pv.Vector(-this.y, this.x);
  2519. };
  2520. /**
  2521. * Returns a normalized copy of this vector: a vector with the same direction,
  2522. * but unit length. If this vector has zero length this method returns a copy of
  2523. * this vector.
  2524. *
  2525. * @returns {pv.Vector} a unit vector.
  2526. */
  2527. pv.Vector.prototype.norm = function() {
  2528. var l = this.length();
  2529. return this.times(l ? (1 / l) : 1);
  2530. };
  2531. /**
  2532. * Returns the magnitude of this vector, defined as <i>sqrt(x * x + y * y)</i>.
  2533. *
  2534. * @returns {number} a length.
  2535. */
  2536. pv.Vector.prototype.length = function() {
  2537. return Math.sqrt(this.x * this.x + this.y * this.y);
  2538. };
  2539. /**
  2540. * Returns a scaled copy of this vector: <i>&#x27e8;x * k, y * k&#x27e9;</i>.
  2541. * To perform the equivalent divide operation, use <i>1 / k</i>.
  2542. *
  2543. * @param {number} k the scale factor.
  2544. * @returns {pv.Vector} a scaled vector.
  2545. */
  2546. pv.Vector.prototype.times = function(k) {
  2547. return new pv.Vector(this.x * k, this.y * k);
  2548. };
  2549. /**
  2550. * Returns this vector plus the vector <i>v</i>: <i>&#x27e8;x + v.x, y +
  2551. * v.y&#x27e9;</i>. If only one argument is specified, it is interpreted as the
  2552. * vector <i>v</i>.
  2553. *
  2554. * @param {number} x the <i>x</i> coordinate to add.
  2555. * @param {number} y the <i>y</i> coordinate to add.
  2556. * @returns {pv.Vector} a new vector.
  2557. */
  2558. pv.Vector.prototype.plus = function(x, y) {
  2559. return (arguments.length == 1)
  2560. ? new pv.Vector(this.x + x.x, this.y + x.y)
  2561. : new pv.Vector(this.x + x, this.y + y);
  2562. };
  2563. /**
  2564. * Returns this vector minus the vector <i>v</i>: <i>&#x27e8;x - v.x, y -
  2565. * v.y&#x27e9;</i>. If only one argument is specified, it is interpreted as the
  2566. * vector <i>v</i>.
  2567. *
  2568. * @param {number} x the <i>x</i> coordinate to subtract.
  2569. * @param {number} y the <i>y</i> coordinate to subtract.
  2570. * @returns {pv.Vector} a new vector.
  2571. */
  2572. pv.Vector.prototype.minus = function(x, y) {
  2573. return (arguments.length == 1)
  2574. ? new pv.Vector(this.x - x.x, this.y - x.y)
  2575. : new pv.Vector(this.x - x, this.y - y);
  2576. };
  2577. /**
  2578. * Returns the dot product of this vector and the vector <i>v</i>: <i>x * v.x +
  2579. * y * v.y</i>. If only one argument is specified, it is interpreted as the
  2580. * vector <i>v</i>.
  2581. *
  2582. * @param {number} x the <i>x</i> coordinate to dot.
  2583. * @param {number} y the <i>y</i> coordinate to dot.
  2584. * @returns {number} a dot product.
  2585. */
  2586. pv.Vector.prototype.dot = function(x, y) {
  2587. return (arguments.length == 1)
  2588. ? this.x * x.x + this.y * x.y
  2589. : this.x * x + this.y * y;
  2590. };
  2591. /**
  2592. * Returns a new identity transform.
  2593. *
  2594. * @class Represents a transformation matrix. The transformation matrix is
  2595. * limited to expressing translate and uniform scale transforms only; shearing,
  2596. * rotation, general affine, and other transforms are not supported.
  2597. *
  2598. * <p>The methods on this class treat the transform as immutable, returning a
  2599. * copy of the transformation matrix with the specified transform applied. Note,
  2600. * alternatively, that the matrix fields can be get and set directly.
  2601. */
  2602. pv.Transform = function() {};
  2603. pv.Transform.prototype = {k: 1, x: 0, y: 0};
  2604. /**
  2605. * The scale magnitude; defaults to 1.
  2606. *
  2607. * @type number
  2608. * @name pv.Transform.prototype.k
  2609. */
  2610. /**
  2611. * The x-offset; defaults to 0.
  2612. *
  2613. * @type number
  2614. * @name pv.Transform.prototype.x
  2615. */
  2616. /**
  2617. * The y-offset; defaults to 0.
  2618. *
  2619. * @type number
  2620. * @name pv.Transform.prototype.y
  2621. */
  2622. /**
  2623. * @private The identity transform.
  2624. *
  2625. * @type pv.Transform
  2626. */
  2627. pv.Transform.identity = new pv.Transform();
  2628. // k 0 x 1 0 a k 0 ka+x
  2629. // 0 k y * 0 1 b = 0 k kb+y
  2630. // 0 0 1 0 0 1 0 0 1
  2631. /**
  2632. * Returns a translated copy of this transformation matrix.
  2633. *
  2634. * @param {number} x the x-offset.
  2635. * @param {number} y the y-offset.
  2636. * @returns {pv.Transform} the translated transformation matrix.
  2637. */
  2638. pv.Transform.prototype.translate = function(x, y) {
  2639. var v = new pv.Transform();
  2640. v.k = this.k;
  2641. v.x = this.k * x + this.x;
  2642. v.y = this.k * y + this.y;
  2643. return v;
  2644. };
  2645. // k 0 x d 0 0 kd 0 x
  2646. // 0 k y * 0 d 0 = 0 kd y
  2647. // 0 0 1 0 0 1 0 0 1
  2648. /**
  2649. * Returns a scaled copy of this transformation matrix.
  2650. *
  2651. * @param {number} k
  2652. * @returns {pv.Transform} the scaled transformation matrix.
  2653. */
  2654. pv.Transform.prototype.scale = function(k) {
  2655. var v = new pv.Transform();
  2656. v.k = this.k * k;
  2657. v.x = this.x;
  2658. v.y = this.y;
  2659. return v;
  2660. };
  2661. /**
  2662. * Returns the inverse of this transformation matrix.
  2663. *
  2664. * @returns {pv.Transform} the inverted transformation matrix.
  2665. */
  2666. pv.Transform.prototype.invert = function() {
  2667. var v = new pv.Transform(), k = 1 / this.k;
  2668. v.k = k;
  2669. v.x = -this.x * k;
  2670. v.y = -this.y * k;
  2671. return v;
  2672. };
  2673. // k 0 x d 0 a kd 0 ka+x
  2674. // 0 k y * 0 d b = 0 kd kb+y
  2675. // 0 0 1 0 0 1 0 0 1
  2676. /**
  2677. * Returns this matrix post-multiplied by the specified matrix <i>m</i>.
  2678. *
  2679. * @param {pv.Transform} m
  2680. * @returns {pv.Transform} the post-multiplied transformation matrix.
  2681. */
  2682. pv.Transform.prototype.times = function(m) {
  2683. var v = new pv.Transform();
  2684. v.k = this.k * m.k;
  2685. v.x = this.k * m.x + this.x;
  2686. v.y = this.k * m.y + this.y;
  2687. return v;
  2688. };
  2689. /**
  2690. * Abstract; see the various scale implementations.
  2691. *
  2692. * @class Represents a scale; a function that performs a transformation from
  2693. * data domain to visual range. For quantitative and quantile scales, the domain
  2694. * is expressed as numbers; for ordinal scales, the domain is expressed as
  2695. * strings (or equivalently objects with unique string representations). The
  2696. * "visual range" may correspond to pixel space, colors, font sizes, and the
  2697. * like.
  2698. *
  2699. * <p>Note that scales are functions, and thus can be used as properties
  2700. * directly, assuming that the data associated with a mark is a number. While
  2701. * this is convenient for single-use scales, frequently it is desirable to
  2702. * define scales globally:
  2703. *
  2704. * <pre>var y = pv.Scale.linear(0, 100).range(0, 640);</pre>
  2705. *
  2706. * The <tt>y</tt> scale can now be equivalently referenced within a property:
  2707. *
  2708. * <pre> .height(function(d) y(d))</pre>
  2709. *
  2710. * Alternatively, if the data are not simple numbers, the appropriate value can
  2711. * be passed to the <tt>y</tt> scale (e.g., <tt>d.foo</tt>). The {@link #by}
  2712. * method similarly allows the data to be mapped to a numeric value before
  2713. * performing the linear transformation.
  2714. *
  2715. * @see pv.Scale.quantitative
  2716. * @see pv.Scale.quantile
  2717. * @see pv.Scale.ordinal
  2718. * @extends function
  2719. */
  2720. pv.Scale = function() {};
  2721. /**
  2722. * @private Returns a function that interpolators from the start value to the
  2723. * end value, given a parameter <i>t</i> in [0, 1].
  2724. *
  2725. * @param start the start value.
  2726. * @param end the end value.
  2727. */
  2728. pv.Scale.interpolator = function(start, end) {
  2729. if (typeof start == "number") {
  2730. return function(t) {
  2731. return t * (end - start) + start;
  2732. };
  2733. }
  2734. /* For now, assume color. */
  2735. start = pv.color(start).rgb();
  2736. end = pv.color(end).rgb();
  2737. return function(t) {
  2738. var a = start.a * (1 - t) + end.a * t;
  2739. if (a < 1e-5) a = 0; // avoid scientific notation
  2740. return (start.a == 0) ? pv.rgb(end.r, end.g, end.b, a)
  2741. : ((end.a == 0) ? pv.rgb(start.r, start.g, start.b, a)
  2742. : pv.rgb(
  2743. Math.round(start.r * (1 - t) + end.r * t),
  2744. Math.round(start.g * (1 - t) + end.g * t),
  2745. Math.round(start.b * (1 - t) + end.b * t), a));
  2746. };
  2747. };
  2748. /**
  2749. * Returns a view of this scale by the specified accessor function <tt>f</tt>.
  2750. * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
  2751. * <tt>function(d) y(d.foo)</tt>.
  2752. *
  2753. * <p>This method is provided for convenience, such that scales can be
  2754. * succinctly defined inline. For example, given an array of data elements that
  2755. * have a <tt>score</tt> attribute with the domain [0, 1], the height property
  2756. * could be specified as:
  2757. *
  2758. * <pre> .height(pv.Scale.linear().range(0, 480).by(function(d) d.score))</pre>
  2759. *
  2760. * This is equivalent to:
  2761. *
  2762. * <pre> .height(function(d) d.score * 480)</pre>
  2763. *
  2764. * This method should be used judiciously; it is typically more clear to invoke
  2765. * the scale directly, passing in the value to be scaled.
  2766. *
  2767. * @function
  2768. * @name pv.Scale.prototype.by
  2769. * @param {function} f an accessor function.
  2770. * @returns {pv.Scale} a view of this scale by the specified accessor function.
  2771. */
  2772. /**
  2773. * Returns a default quantitative, linear, scale for the specified domain. The
  2774. * arguments to this constructor are optional, and equivalent to calling
  2775. * {@link #domain}. The default domain and range are [0,1].
  2776. *
  2777. * <p>This constructor is typically not used directly; see one of the
  2778. * quantitative scale implementations instead.
  2779. *
  2780. * @class Represents an abstract quantitative scale; a function that performs a
  2781. * numeric transformation. This class is typically not used directly; see one of
  2782. * the quantitative scale implementations (linear, log, root, etc.)
  2783. * instead. <style type="text/css">sub{line-height:0}</style> A quantitative
  2784. * scale represents a 1-dimensional transformation from a numeric domain of
  2785. * input data [<i>d<sub>0</sub></i>, <i>d<sub>1</sub></i>] to a numeric range of
  2786. * pixels [<i>r<sub>0</sub></i>, <i>r<sub>1</sub></i>]. In addition to
  2787. * readability, scales offer several useful features:
  2788. *
  2789. * <p>1. The range can be expressed in colors, rather than pixels. For example:
  2790. *
  2791. * <pre> .fillStyle(pv.Scale.linear(0, 100).range("red", "green"))</pre>
  2792. *
  2793. * will fill the marks "red" on an input value of 0, "green" on an input value
  2794. * of 100, and some color in-between for intermediate values.
  2795. *
  2796. * <p>2. The domain and range can be subdivided for a non-uniform
  2797. * transformation. For example, you may want a diverging color scale that is
  2798. * increasingly red for negative values, and increasingly green for positive
  2799. * values:
  2800. *
  2801. * <pre> .fillStyle(pv.Scale.linear(-1, 0, 1).range("red", "white", "green"))</pre>
  2802. *
  2803. * The domain can be specified as a series of <i>n</i> monotonically-increasing
  2804. * values; the range must also be specified as <i>n</i> values, resulting in
  2805. * <i>n - 1</i> contiguous linear scales.
  2806. *
  2807. * <p>3. Quantitative scales can be inverted for interaction. The
  2808. * {@link #invert} method takes a value in the output range, and returns the
  2809. * corresponding value in the input domain. This is frequently used to convert
  2810. * the mouse location (see {@link pv.Mark#mouse}) to a value in the input
  2811. * domain. Note that inversion is only supported for numeric ranges, and not
  2812. * colors.
  2813. *
  2814. * <p>4. A scale can be queried for reasonable "tick" values. The {@link #ticks}
  2815. * method provides a convenient way to get a series of evenly-spaced rounded
  2816. * values in the input domain. Frequently these are used in conjunction with
  2817. * {@link pv.Rule} to display tick marks or grid lines.
  2818. *
  2819. * <p>5. A scale can be "niced" to extend the domain to suitable rounded
  2820. * numbers. If the minimum and maximum of the domain are messy because they are
  2821. * derived from data, you can use {@link #nice} to round these values down and
  2822. * up to even numbers.
  2823. *
  2824. * @param {number...} domain... optional domain values.
  2825. * @see pv.Scale.linear
  2826. * @see pv.Scale.log
  2827. * @see pv.Scale.root
  2828. * @extends pv.Scale
  2829. */
  2830. pv.Scale.quantitative = function() {
  2831. var d = [0, 1], // default domain
  2832. l = [0, 1], // default transformed domain
  2833. r = [0, 1], // default range
  2834. i = [pv.identity], // default interpolators
  2835. type = Number, // default type
  2836. n = false, // whether the domain is negative
  2837. f = pv.identity, // default forward transform
  2838. g = pv.identity, // default inverse transform
  2839. tickFormat = String; // default tick formatting function
  2840. /** @private */
  2841. function newDate(x) {
  2842. return new Date(x);
  2843. }
  2844. /** @private */
  2845. function scale(x) {
  2846. var j = pv.search(d, x);
  2847. if (j < 0) j = -j - 2;
  2848. j = Math.max(0, Math.min(i.length - 1, j));
  2849. return i[j]((f(x) - l[j]) / (l[j + 1] - l[j]));
  2850. }
  2851. /** @private */
  2852. scale.transform = function(forward, inverse) {
  2853. /** @ignore */ f = function(x) { return n ? -forward(-x) : forward(x); };
  2854. /** @ignore */ g = function(y) { return n ? -inverse(-y) : inverse(y); };
  2855. l = d.map(f);
  2856. return this;
  2857. };
  2858. /**
  2859. * Sets or gets the input domain. This method can be invoked several ways:
  2860. *
  2861. * <p>1. <tt>domain(min, ..., max)</tt>
  2862. *
  2863. * <p>Specifying the domain as a series of numbers is the most explicit and
  2864. * recommended approach. Most commonly, two numbers are specified: the minimum
  2865. * and maximum value. However, for a diverging scale, or other subdivided
  2866. * non-uniform scales, multiple values can be specified. Values can be derived
  2867. * from data using {@link pv.min} and {@link pv.max}. For example:
  2868. *
  2869. * <pre> .domain(0, pv.max(array))</pre>
  2870. *
  2871. * An alternative method for deriving minimum and maximum values from data
  2872. * follows.
  2873. *
  2874. * <p>2. <tt>domain(array, minf, maxf)</tt>
  2875. *
  2876. * <p>When both the minimum and maximum value are derived from data, the
  2877. * arguments to the <tt>domain</tt> method can be specified as the array of
  2878. * data, followed by zero, one or two accessor functions. For example, if the
  2879. * array of data is just an array of numbers:
  2880. *
  2881. * <pre> .domain(array)</pre>
  2882. *
  2883. * On the other hand, if the array elements are objects representing stock
  2884. * values per day, and the domain should consider the stock's daily low and
  2885. * daily high:
  2886. *
  2887. * <pre> .domain(array, function(d) d.low, function(d) d.high)</pre>
  2888. *
  2889. * The first method of setting the domain is preferred because it is more
  2890. * explicit; setting the domain using this second method should be used only
  2891. * if brevity is required.
  2892. *
  2893. * <p>3. <tt>domain()</tt>
  2894. *
  2895. * <p>Invoking the <tt>domain</tt> method with no arguments returns the
  2896. * current domain as an array of numbers.
  2897. *
  2898. * @function
  2899. * @name pv.Scale.quantitative.prototype.domain
  2900. * @param {number...} domain... domain values.
  2901. * @returns {pv.Scale.quantitative} <tt>this</tt>, or the current domain.
  2902. */
  2903. scale.domain = function(array, min, max) {
  2904. if (arguments.length) {
  2905. var o; // the object we use to infer the domain type
  2906. if (array instanceof Array) {
  2907. if (arguments.length < 2) min = pv.identity;
  2908. if (arguments.length < 3) max = min;
  2909. o = array.length && min(array[0]);
  2910. d = array.length ? [pv.min(array, min), pv.max(array, max)] : [];
  2911. } else {
  2912. o = array;
  2913. d = Array.prototype.slice.call(arguments).map(Number);
  2914. }
  2915. if (!d.length) d = [-Infinity, Infinity];
  2916. else if (d.length == 1) d = [d[0], d[0]];
  2917. n = (d[0] || d[d.length - 1]) < 0;
  2918. l = d.map(f);
  2919. type = (o instanceof Date) ? newDate : Number;
  2920. return this;
  2921. }
  2922. return d.map(type);
  2923. };
  2924. /**
  2925. * Sets or gets the output range. This method can be invoked several ways:
  2926. *
  2927. * <p>1. <tt>range(min, ..., max)</tt>
  2928. *
  2929. * <p>The range may be specified as a series of numbers or colors. Most
  2930. * commonly, two numbers are specified: the minimum and maximum pixel values.
  2931. * For a color scale, values may be specified as {@link pv.Color}s or
  2932. * equivalent strings. For a diverging scale, or other subdivided non-uniform
  2933. * scales, multiple values can be specified. For example:
  2934. *
  2935. * <pre> .range("red", "white", "green")</pre>
  2936. *
  2937. * <p>Currently, only numbers and colors are supported as range values. The
  2938. * number of range values must exactly match the number of domain values, or
  2939. * the behavior of the scale is undefined.
  2940. *
  2941. * <p>2. <tt>range()</tt>
  2942. *
  2943. * <p>Invoking the <tt>range</tt> method with no arguments returns the current
  2944. * range as an array of numbers or colors.
  2945. *
  2946. * @function
  2947. * @name pv.Scale.quantitative.prototype.range
  2948. * @param {...} range... range values.
  2949. * @returns {pv.Scale.quantitative} <tt>this</tt>, or the current range.
  2950. */
  2951. scale.range = function() {
  2952. if (arguments.length) {
  2953. r = Array.prototype.slice.call(arguments);
  2954. if (!r.length) r = [-Infinity, Infinity];
  2955. else if (r.length == 1) r = [r[0], r[0]];
  2956. i = [];
  2957. for (var j = 0; j < r.length - 1; j++) {
  2958. i.push(pv.Scale.interpolator(r[j], r[j + 1]));
  2959. }
  2960. return this;
  2961. }
  2962. return r;
  2963. };
  2964. /**
  2965. * Inverts the specified value in the output range, returning the
  2966. * corresponding value in the input domain. This is frequently used to convert
  2967. * the mouse location (see {@link pv.Mark#mouse}) to a value in the input
  2968. * domain. Inversion is only supported for numeric ranges, and not colors.
  2969. *
  2970. * <p>Note that this method does not do any rounding or bounds checking. If
  2971. * the input domain is discrete (e.g., an array index), the returned value
  2972. * should be rounded. If the specified <tt>y</tt> value is outside the range,
  2973. * the returned value may be equivalently outside the input domain.
  2974. *
  2975. * @function
  2976. * @name pv.Scale.quantitative.prototype.invert
  2977. * @param {number} y a value in the output range (a pixel location).
  2978. * @returns {number} a value in the input domain.
  2979. */
  2980. scale.invert = function(y) {
  2981. var j = pv.search(r, y);
  2982. if (j < 0) j = -j - 2;
  2983. j = Math.max(0, Math.min(i.length - 1, j));
  2984. return type(g(l[j] + (y - r[j]) / (r[j + 1] - r[j]) * (l[j + 1] - l[j])));
  2985. };
  2986. /**
  2987. * Returns an array of evenly-spaced, suitably-rounded values in the input
  2988. * domain. This method attempts to return between 5 and 10 tick values. These
  2989. * values are frequently used in conjunction with {@link pv.Rule} to display
  2990. * tick marks or grid lines.
  2991. *
  2992. * @function
  2993. * @name pv.Scale.quantitative.prototype.ticks
  2994. * @param {number} [m] optional number of desired ticks.
  2995. * @returns {number[]} an array input domain values to use as ticks.
  2996. */
  2997. scale.ticks = function(m) {
  2998. var start = d[0],
  2999. end = d[d.length - 1],
  3000. reverse = end < start,
  3001. min = reverse ? end : start,
  3002. max = reverse ? start : end,
  3003. span = max - min;
  3004. /* Special case: empty, invalid or infinite span. */
  3005. if (!span || !isFinite(span)) {
  3006. if (type == newDate) tickFormat = pv.Format.date("%x");
  3007. return [type(min)];
  3008. }
  3009. /* Special case: dates. */
  3010. if (type == newDate) {
  3011. /* Floor the date d given the precision p. */
  3012. function floor(d, p) {
  3013. switch (p) {
  3014. case 31536e6: d.setMonth(0);
  3015. case 2592e6: d.setDate(1);
  3016. case 6048e5: if (p == 6048e5) d.setDate(d.getDate() - d.getDay());
  3017. case 864e5: d.setHours(0);
  3018. case 36e5: d.setMinutes(0);
  3019. case 6e4: d.setSeconds(0);
  3020. case 1e3: d.setMilliseconds(0);
  3021. }
  3022. }
  3023. var precision, format, increment, step = 1;
  3024. if (span >= 3 * 31536e6) {
  3025. precision = 31536e6;
  3026. format = "%Y";
  3027. /** @ignore */ increment = function(d) { d.setFullYear(d.getFullYear() + step); };
  3028. } else if (span >= 3 * 2592e6) {
  3029. precision = 2592e6;
  3030. format = "%m/%Y";
  3031. /** @ignore */ increment = function(d) { d.setMonth(d.getMonth() + step); };
  3032. } else if (span >= 3 * 6048e5) {
  3033. precision = 6048e5;
  3034. format = "%m/%d";
  3035. /** @ignore */ increment = function(d) { d.setDate(d.getDate() + 7 * step); };
  3036. } else if (span >= 3 * 864e5) {
  3037. precision = 864e5;
  3038. format = "%m/%d";
  3039. /** @ignore */ increment = function(d) { d.setDate(d.getDate() + step); };
  3040. } else if (span >= 3 * 36e5) {
  3041. precision = 36e5;
  3042. format = "%I:%M %p";
  3043. /** @ignore */ increment = function(d) { d.setHours(d.getHours() + step); };
  3044. } else if (span >= 3 * 6e4) {
  3045. precision = 6e4;
  3046. format = "%I:%M %p";
  3047. /** @ignore */ increment = function(d) { d.setMinutes(d.getMinutes() + step); };
  3048. } else if (span >= 3 * 1e3) {
  3049. precision = 1e3;
  3050. format = "%I:%M:%S";
  3051. /** @ignore */ increment = function(d) { d.setSeconds(d.getSeconds() + step); };
  3052. } else {
  3053. precision = 1;
  3054. format = "%S.%Qs";
  3055. /** @ignore */ increment = function(d) { d.setTime(d.getTime() + step); };
  3056. }
  3057. tickFormat = pv.Format.date(format);
  3058. var date = new Date(min), dates = [];
  3059. floor(date, precision);
  3060. /* If we'd generate too many ticks, skip some!. */
  3061. var n = span / precision;
  3062. if (n > 10) {
  3063. switch (precision) {
  3064. case 36e5: {
  3065. step = (n > 20) ? 6 : 3;
  3066. date.setHours(Math.floor(date.getHours() / step) * step);
  3067. break;
  3068. }
  3069. case 2592e6: {
  3070. step = 3; // seasons
  3071. date.setMonth(Math.floor(date.getMonth() / step) * step);
  3072. break;
  3073. }
  3074. case 6e4: {
  3075. step = (n > 30) ? 15 : ((n > 15) ? 10 : 5);
  3076. date.setMinutes(Math.floor(date.getMinutes() / step) * step);
  3077. break;
  3078. }
  3079. case 1e3: {
  3080. step = (n > 90) ? 15 : ((n > 60) ? 10 : 5);
  3081. date.setSeconds(Math.floor(date.getSeconds() / step) * step);
  3082. break;
  3083. }
  3084. case 1: {
  3085. step = (n > 1000) ? 250 : ((n > 200) ? 100 : ((n > 100) ? 50 : ((n > 50) ? 25 : 5)));
  3086. date.setMilliseconds(Math.floor(date.getMilliseconds() / step) * step);
  3087. break;
  3088. }
  3089. default: {
  3090. step = pv.logCeil(n / 15, 10);
  3091. if (n / step < 2) step /= 5;
  3092. else if (n / step < 5) step /= 2;
  3093. date.setFullYear(Math.floor(date.getFullYear() / step) * step);
  3094. break;
  3095. }
  3096. }
  3097. }
  3098. while (true) {
  3099. increment(date);
  3100. if (date > max) break;
  3101. dates.push(new Date(date));
  3102. }
  3103. return reverse ? dates.reverse() : dates;
  3104. }
  3105. /* Normal case: numbers. */
  3106. if (!arguments.length) m = 10;
  3107. var step = pv.logFloor(span / m, 10),
  3108. err = m / (span / step);
  3109. if (err <= .15) step *= 10;
  3110. else if (err <= .35) step *= 5;
  3111. else if (err <= .75) step *= 2;
  3112. var start = Math.ceil(min / step) * step,
  3113. end = Math.floor(max / step) * step;
  3114. tickFormat = pv.Format.number()
  3115. .fractionDigits(Math.max(0, -Math.floor(pv.log(step, 10) + .01)));
  3116. var ticks = pv.range(start, end + step, step);
  3117. return reverse ? ticks.reverse() : ticks;
  3118. };
  3119. /**
  3120. * Formats the specified tick value using the appropriate precision, based on
  3121. * the step interval between tick marks. If {@link #ticks} has not been called,
  3122. * the argument is converted to a string, but no formatting is applied.
  3123. *
  3124. * @function
  3125. * @name pv.Scale.quantitative.prototype.tickFormat
  3126. * @param {number} t a tick value.
  3127. * @returns {string} a formatted tick value.
  3128. */
  3129. scale.tickFormat = function (t) { return tickFormat(t); };
  3130. /**
  3131. * "Nices" this scale, extending the bounds of the input domain to
  3132. * evenly-rounded values. Nicing is useful if the domain is computed
  3133. * dynamically from data, and may be irregular. For example, given a domain of
  3134. * [0.20147987687960267, 0.996679553296417], a call to <tt>nice()</tt> might
  3135. * extend the domain to [0.2, 1].
  3136. *
  3137. * <p>This method must be invoked each time after setting the domain.
  3138. *
  3139. * @function
  3140. * @name pv.Scale.quantitative.prototype.nice
  3141. * @returns {pv.Scale.quantitative} <tt>this</tt>.
  3142. */
  3143. scale.nice = function() {
  3144. if (d.length != 2) return this; // TODO support non-uniform domains
  3145. var start = d[0],
  3146. end = d[d.length - 1],
  3147. reverse = end < start,
  3148. min = reverse ? end : start,
  3149. max = reverse ? start : end,
  3150. span = max - min;
  3151. /* Special case: empty, invalid or infinite span. */
  3152. if (!span || !isFinite(span)) return this;
  3153. var step = Math.pow(10, Math.round(Math.log(span) / Math.log(10)) - 1);
  3154. d = [Math.floor(min / step) * step, Math.ceil(max / step) * step];
  3155. if (reverse) d.reverse();
  3156. l = d.map(f);
  3157. return this;
  3158. };
  3159. /**
  3160. * Returns a view of this scale by the specified accessor function <tt>f</tt>.
  3161. * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
  3162. * <tt>function(d) y(d.foo)</tt>.
  3163. *
  3164. * <p>This method is provided for convenience, such that scales can be
  3165. * succinctly defined inline. For example, given an array of data elements
  3166. * that have a <tt>score</tt> attribute with the domain [0, 1], the height
  3167. * property could be specified as:
  3168. *
  3169. * <pre> .height(pv.Scale.linear().range(0, 480).by(function(d) d.score))</pre>
  3170. *
  3171. * This is equivalent to:
  3172. *
  3173. * <pre> .height(function(d) d.score * 480)</pre>
  3174. *
  3175. * This method should be used judiciously; it is typically more clear to
  3176. * invoke the scale directly, passing in the value to be scaled.
  3177. *
  3178. * @function
  3179. * @name pv.Scale.quantitative.prototype.by
  3180. * @param {function} f an accessor function.
  3181. * @returns {pv.Scale.quantitative} a view of this scale by the specified
  3182. * accessor function.
  3183. */
  3184. scale.by = function(f) {
  3185. function by() { return scale(f.apply(this, arguments)); }
  3186. for (var method in scale) by[method] = scale[method];
  3187. return by;
  3188. };
  3189. scale.domain.apply(scale, arguments);
  3190. return scale;
  3191. };
  3192. /**
  3193. * Returns a linear scale for the specified domain. The arguments to this
  3194. * constructor are optional, and equivalent to calling {@link #domain}.
  3195. * The default domain and range are [0,1].
  3196. *
  3197. * @class Represents a linear scale; a function that performs a linear
  3198. * transformation. <style type="text/css">sub{line-height:0}</style> Most
  3199. * commonly, a linear scale represents a 1-dimensional linear transformation
  3200. * from a numeric domain of input data [<i>d<sub>0</sub></i>,
  3201. * <i>d<sub>1</sub></i>] to a numeric range of pixels [<i>r<sub>0</sub></i>,
  3202. * <i>r<sub>1</sub></i>]. The equation for such a scale is:
  3203. *
  3204. * <blockquote><i>f(x) = (x - d<sub>0</sub>) / (d<sub>1</sub> - d<sub>0</sub>) *
  3205. * (r<sub>1</sub> - r<sub>0</sub>) + r<sub>0</sub></i></blockquote>
  3206. *
  3207. * For example, a linear scale from the domain [0, 100] to range [0, 640]:
  3208. *
  3209. * <blockquote><i>f(x) = (x - 0) / (100 - 0) * (640 - 0) + 0</i><br>
  3210. * <i>f(x) = x / 100 * 640</i><br>
  3211. * <i>f(x) = x * 6.4</i><br>
  3212. * </blockquote>
  3213. *
  3214. * Thus, saying
  3215. *
  3216. * <pre> .height(function(d) d * 6.4)</pre>
  3217. *
  3218. * is identical to
  3219. *
  3220. * <pre> .height(pv.Scale.linear(0, 100).range(0, 640))</pre>
  3221. *
  3222. * Note that the scale is itself a function, and thus can be used as a property
  3223. * directly, assuming that the data associated with a mark is a number. While
  3224. * this is convenient for single-use scales, frequently it is desirable to
  3225. * define scales globally:
  3226. *
  3227. * <pre>var y = pv.Scale.linear(0, 100).range(0, 640);</pre>
  3228. *
  3229. * The <tt>y</tt> scale can now be equivalently referenced within a property:
  3230. *
  3231. * <pre> .height(function(d) y(d))</pre>
  3232. *
  3233. * Alternatively, if the data are not simple numbers, the appropriate value can
  3234. * be passed to the <tt>y</tt> scale (e.g., <tt>d.foo</tt>). The {@link #by}
  3235. * method similarly allows the data to be mapped to a numeric value before
  3236. * performing the linear transformation.
  3237. *
  3238. * @param {number...} domain... optional domain values.
  3239. * @extends pv.Scale.quantitative
  3240. */
  3241. pv.Scale.linear = function() {
  3242. var scale = pv.Scale.quantitative();
  3243. scale.domain.apply(scale, arguments);
  3244. return scale;
  3245. };
  3246. /**
  3247. * Returns a log scale for the specified domain. The arguments to this
  3248. * constructor are optional, and equivalent to calling {@link #domain}.
  3249. * The default domain is [1,10] and the default range is [0,1].
  3250. *
  3251. * @class Represents a log scale. <style
  3252. * type="text/css">sub{line-height:0}</style> Most commonly, a log scale
  3253. * represents a 1-dimensional log transformation from a numeric domain of input
  3254. * data [<i>d<sub>0</sub></i>, <i>d<sub>1</sub></i>] to a numeric range of
  3255. * pixels [<i>r<sub>0</sub></i>, <i>r<sub>1</sub></i>]. The equation for such a
  3256. * scale is:
  3257. *
  3258. * <blockquote><i>f(x) = (log(x) - log(d<sub>0</sub>)) / (log(d<sub>1</sub>) -
  3259. * log(d<sub>0</sub>)) * (r<sub>1</sub> - r<sub>0</sub>) +
  3260. * r<sub>0</sub></i></blockquote>
  3261. *
  3262. * where <i>log(x)</i> represents the zero-symmetric logarthim of <i>x</i> using
  3263. * the scale's associated base (default: 10, see {@link pv.logSymmetric}). For
  3264. * example, a log scale from the domain [1, 100] to range [0, 640]:
  3265. *
  3266. * <blockquote><i>f(x) = (log(x) - log(1)) / (log(100) - log(1)) * (640 - 0) + 0</i><br>
  3267. * <i>f(x) = log(x) / 2 * 640</i><br>
  3268. * <i>f(x) = log(x) * 320</i><br>
  3269. * </blockquote>
  3270. *
  3271. * Thus, saying
  3272. *
  3273. * <pre> .height(function(d) Math.log(d) * 138.974)</pre>
  3274. *
  3275. * is equivalent to
  3276. *
  3277. * <pre> .height(pv.Scale.log(1, 100).range(0, 640))</pre>
  3278. *
  3279. * Note that the scale is itself a function, and thus can be used as a property
  3280. * directly, assuming that the data associated with a mark is a number. While
  3281. * this is convenient for single-use scales, frequently it is desirable to
  3282. * define scales globally:
  3283. *
  3284. * <pre>var y = pv.Scale.log(1, 100).range(0, 640);</pre>
  3285. *
  3286. * The <tt>y</tt> scale can now be equivalently referenced within a property:
  3287. *
  3288. * <pre> .height(function(d) y(d))</pre>
  3289. *
  3290. * Alternatively, if the data are not simple numbers, the appropriate value can
  3291. * be passed to the <tt>y</tt> scale (e.g., <tt>d.foo</tt>). The {@link #by}
  3292. * method similarly allows the data to be mapped to a numeric value before
  3293. * performing the log transformation.
  3294. *
  3295. * @param {number...} domain... optional domain values.
  3296. * @extends pv.Scale.quantitative
  3297. */
  3298. pv.Scale.log = function() {
  3299. var scale = pv.Scale.quantitative(1, 10),
  3300. b, // logarithm base
  3301. p, // cached Math.log(b)
  3302. /** @ignore */ log = function(x) { return Math.log(x) / p; },
  3303. /** @ignore */ pow = function(y) { return Math.pow(b, y); };
  3304. /**
  3305. * Returns an array of evenly-spaced, suitably-rounded values in the input
  3306. * domain. These values are frequently used in conjunction with
  3307. * {@link pv.Rule} to display tick marks or grid lines.
  3308. *
  3309. * @function
  3310. * @name pv.Scale.log.prototype.ticks
  3311. * @returns {number[]} an array input domain values to use as ticks.
  3312. */
  3313. scale.ticks = function() {
  3314. // TODO support non-uniform domains
  3315. var d = scale.domain(),
  3316. n = d[0] < 0,
  3317. i = Math.floor(n ? -log(-d[0]) : log(d[0])),
  3318. j = Math.ceil(n ? -log(-d[1]) : log(d[1])),
  3319. ticks = [];
  3320. if (n) {
  3321. ticks.push(-pow(-i));
  3322. for (; i++ < j;) for (var k = b - 1; k > 0; k--) ticks.push(-pow(-i) * k);
  3323. } else {
  3324. for (; i < j; i++) for (var k = 1; k < b; k++) ticks.push(pow(i) * k);
  3325. ticks.push(pow(i));
  3326. }
  3327. for (i = 0; ticks[i] < d[0]; i++); // strip small values
  3328. for (j = ticks.length; ticks[j - 1] > d[1]; j--); // strip big values
  3329. return ticks.slice(i, j);
  3330. };
  3331. /**
  3332. * Formats the specified tick value using the appropriate precision, assuming
  3333. * base 10.
  3334. *
  3335. * @function
  3336. * @name pv.Scale.log.prototype.tickFormat
  3337. * @param {number} t a tick value.
  3338. * @returns {string} a formatted tick value.
  3339. */
  3340. scale.tickFormat = function(t) {
  3341. return t.toPrecision(1);
  3342. };
  3343. /**
  3344. * "Nices" this scale, extending the bounds of the input domain to
  3345. * evenly-rounded values. This method uses {@link pv.logFloor} and
  3346. * {@link pv.logCeil}. Nicing is useful if the domain is computed dynamically
  3347. * from data, and may be irregular. For example, given a domain of
  3348. * [0.20147987687960267, 0.996679553296417], a call to <tt>nice()</tt> might
  3349. * extend the domain to [0.1, 1].
  3350. *
  3351. * <p>This method must be invoked each time after setting the domain (and
  3352. * base).
  3353. *
  3354. * @function
  3355. * @name pv.Scale.log.prototype.nice
  3356. * @returns {pv.Scale.log} <tt>this</tt>.
  3357. */
  3358. scale.nice = function() {
  3359. // TODO support non-uniform domains
  3360. var d = scale.domain();
  3361. return scale.domain(pv.logFloor(d[0], b), pv.logCeil(d[1], b));
  3362. };
  3363. /**
  3364. * Sets or gets the logarithm base. Defaults to 10.
  3365. *
  3366. * @function
  3367. * @name pv.Scale.log.prototype.base
  3368. * @param {number} [v] the new base.
  3369. * @returns {pv.Scale.log} <tt>this</tt>, or the current base.
  3370. */
  3371. scale.base = function(v) {
  3372. if (arguments.length) {
  3373. b = Number(v);
  3374. p = Math.log(b);
  3375. scale.transform(log, pow); // update transformed domain
  3376. return this;
  3377. }
  3378. return b;
  3379. };
  3380. scale.domain.apply(scale, arguments);
  3381. return scale.base(10);
  3382. };
  3383. /**
  3384. * Returns a root scale for the specified domain. The arguments to this
  3385. * constructor are optional, and equivalent to calling {@link #domain}.
  3386. * The default domain and range are [0,1].
  3387. *
  3388. * @class Represents a root scale; a function that performs a power
  3389. * transformation. <style type="text/css">sub{line-height:0}</style> Most
  3390. * commonly, a root scale represents a 1-dimensional root transformation from a
  3391. * numeric domain of input data [<i>d<sub>0</sub></i>, <i>d<sub>1</sub></i>] to
  3392. * a numeric range of pixels [<i>r<sub>0</sub></i>, <i>r<sub>1</sub></i>].
  3393. *
  3394. * <p>Note that the scale is itself a function, and thus can be used as a
  3395. * property directly, assuming that the data associated with a mark is a
  3396. * number. While this is convenient for single-use scales, frequently it is
  3397. * desirable to define scales globally:
  3398. *
  3399. * <pre>var y = pv.Scale.root(0, 100).range(0, 640);</pre>
  3400. *
  3401. * The <tt>y</tt> scale can now be equivalently referenced within a property:
  3402. *
  3403. * <pre> .height(function(d) y(d))</pre>
  3404. *
  3405. * Alternatively, if the data are not simple numbers, the appropriate value can
  3406. * be passed to the <tt>y</tt> scale (e.g., <tt>d.foo</tt>). The {@link #by}
  3407. * method similarly allows the data to be mapped to a numeric value before
  3408. * performing the root transformation.
  3409. *
  3410. * @param {number...} domain... optional domain values.
  3411. * @extends pv.Scale.quantitative
  3412. */
  3413. pv.Scale.root = function() {
  3414. var scale = pv.Scale.quantitative();
  3415. /**
  3416. * Sets or gets the exponent; defaults to 2.
  3417. *
  3418. * @function
  3419. * @name pv.Scale.root.prototype.power
  3420. * @param {number} [v] the new exponent.
  3421. * @returns {pv.Scale.root} <tt>this</tt>, or the current base.
  3422. */
  3423. scale.power = function(v) {
  3424. if (arguments.length) {
  3425. var b = Number(v), p = 1 / b;
  3426. scale.transform(
  3427. function(x) { return Math.pow(x, p); },
  3428. function(y) { return Math.pow(y, b); });
  3429. return this;
  3430. }
  3431. return b;
  3432. };
  3433. scale.domain.apply(scale, arguments);
  3434. return scale.power(2);
  3435. };
  3436. /**
  3437. * Returns an ordinal scale for the specified domain. The arguments to this
  3438. * constructor are optional, and equivalent to calling {@link #domain}.
  3439. *
  3440. * @class Represents an ordinal scale. <style
  3441. * type="text/css">sub{line-height:0}</style> An ordinal scale represents a
  3442. * pairwise mapping from <i>n</i> discrete values in the input domain to
  3443. * <i>n</i> discrete values in the output range. For example, an ordinal scale
  3444. * might map a domain of species ["setosa", "versicolor", "virginica"] to colors
  3445. * ["red", "green", "blue"]. Thus, saying
  3446. *
  3447. * <pre> .fillStyle(function(d) {
  3448. * switch (d.species) {
  3449. * case "setosa": return "red";
  3450. * case "versicolor": return "green";
  3451. * case "virginica": return "blue";
  3452. * }
  3453. * })</pre>
  3454. *
  3455. * is equivalent to
  3456. *
  3457. * <pre> .fillStyle(pv.Scale.ordinal("setosa", "versicolor", "virginica")
  3458. * .range("red", "green", "blue")
  3459. * .by(function(d) d.species))</pre>
  3460. *
  3461. * If the mapping from species to color does not need to be specified
  3462. * explicitly, the domain can be omitted. In this case it will be inferred
  3463. * lazily from the data:
  3464. *
  3465. * <pre> .fillStyle(pv.colors("red", "green", "blue")
  3466. * .by(function(d) d.species))</pre>
  3467. *
  3468. * When the domain is inferred, the first time the scale is invoked, the first
  3469. * element from the range will be returned. Subsequent calls with unique values
  3470. * will return subsequent elements from the range. If the inferred domain grows
  3471. * larger than the range, range values will be reused. However, it is strongly
  3472. * recommended that the domain and the range contain the same number of
  3473. * elements.
  3474. *
  3475. * <p>A range can be discretized from a continuous interval (e.g., for pixel
  3476. * positioning) by using {@link #split}, {@link #splitFlush} or
  3477. * {@link #splitBanded} after the domain has been set. For example, if
  3478. * <tt>states</tt> is an array of the fifty U.S. state names, the state name can
  3479. * be encoded in the left position:
  3480. *
  3481. * <pre> .left(pv.Scale.ordinal(states)
  3482. * .split(0, 640)
  3483. * .by(function(d) d.state))</pre>
  3484. *
  3485. * <p>N.B.: ordinal scales are not invertible (at least not yet), since the
  3486. * domain and range and discontinuous. A workaround is to use a linear scale.
  3487. *
  3488. * @param {...} domain... optional domain values.
  3489. * @extends pv.Scale
  3490. * @see pv.colors
  3491. */
  3492. pv.Scale.ordinal = function() {
  3493. var d = [], i = {}, r = [], band = 0;
  3494. /** @private */
  3495. function scale(x) {
  3496. if (!(x in i)) i[x] = d.push(x) - 1;
  3497. return r[i[x] % r.length];
  3498. }
  3499. /**
  3500. * Sets or gets the input domain. This method can be invoked several ways:
  3501. *
  3502. * <p>1. <tt>domain(values...)</tt>
  3503. *
  3504. * <p>Specifying the domain as a series of values is the most explicit and
  3505. * recommended approach. However, if the domain values are derived from data,
  3506. * you may find the second method more appropriate.
  3507. *
  3508. * <p>2. <tt>domain(array, f)</tt>
  3509. *
  3510. * <p>Rather than enumerating the domain values as explicit arguments to this
  3511. * method, you can specify a single argument of an array. In addition, you can
  3512. * specify an optional accessor function to extract the domain values from the
  3513. * array.
  3514. *
  3515. * <p>3. <tt>domain()</tt>
  3516. *
  3517. * <p>Invoking the <tt>domain</tt> method with no arguments returns the
  3518. * current domain as an array.
  3519. *
  3520. * @function
  3521. * @name pv.Scale.ordinal.prototype.domain
  3522. * @param {...} domain... domain values.
  3523. * @returns {pv.Scale.ordinal} <tt>this</tt>, or the current domain.
  3524. */
  3525. scale.domain = function(array, f) {
  3526. if (arguments.length) {
  3527. array = (array instanceof Array)
  3528. ? ((arguments.length > 1) ? pv.map(array, f) : array)
  3529. : Array.prototype.slice.call(arguments);
  3530. /* Filter the specified ordinals to their unique values. */
  3531. d = [];
  3532. var seen = {};
  3533. for (var j = 0; j < array.length; j++) {
  3534. var o = array[j];
  3535. if (!(o in seen)) {
  3536. seen[o] = true;
  3537. d.push(o);
  3538. }
  3539. }
  3540. i = pv.numerate(d);
  3541. return this;
  3542. }
  3543. return d;
  3544. };
  3545. /**
  3546. * Sets or gets the output range. This method can be invoked several ways:
  3547. *
  3548. * <p>1. <tt>range(values...)</tt>
  3549. *
  3550. * <p>Specifying the range as a series of values is the most explicit and
  3551. * recommended approach. However, if the range values are derived from data,
  3552. * you may find the second method more appropriate.
  3553. *
  3554. * <p>2. <tt>range(array, f)</tt>
  3555. *
  3556. * <p>Rather than enumerating the range values as explicit arguments to this
  3557. * method, you can specify a single argument of an array. In addition, you can
  3558. * specify an optional accessor function to extract the range values from the
  3559. * array.
  3560. *
  3561. * <p>3. <tt>range()</tt>
  3562. *
  3563. * <p>Invoking the <tt>range</tt> method with no arguments returns the
  3564. * current range as an array.
  3565. *
  3566. * @function
  3567. * @name pv.Scale.ordinal.prototype.range
  3568. * @param {...} range... range values.
  3569. * @returns {pv.Scale.ordinal} <tt>this</tt>, or the current range.
  3570. */
  3571. scale.range = function(array, f) {
  3572. if (arguments.length) {
  3573. r = (array instanceof Array)
  3574. ? ((arguments.length > 1) ? pv.map(array, f) : array)
  3575. : Array.prototype.slice.call(arguments);
  3576. if (typeof r[0] == "string") r = r.map(pv.color);
  3577. return this;
  3578. }
  3579. return r;
  3580. };
  3581. /**
  3582. * Sets the range from the given continuous interval. The interval
  3583. * [<i>min</i>, <i>max</i>] is subdivided into <i>n</i> equispaced points,
  3584. * where <i>n</i> is the number of (unique) values in the domain. The first
  3585. * and last point are offset from the edge of the range by half the distance
  3586. * between points.
  3587. *
  3588. * <p>This method must be called <i>after</i> the domain is set.
  3589. *
  3590. * @function
  3591. * @name pv.Scale.ordinal.prototype.split
  3592. * @param {number} min minimum value of the output range.
  3593. * @param {number} max maximum value of the output range.
  3594. * @returns {pv.Scale.ordinal} <tt>this</tt>.
  3595. * @see #splitFlush
  3596. * @see #splitBanded
  3597. */
  3598. scale.split = function(min, max) {
  3599. var step = (max - min) / this.domain().length;
  3600. r = pv.range(min + step / 2, max, step);
  3601. return this;
  3602. };
  3603. /**
  3604. * Sets the range from the given continuous interval. The interval
  3605. * [<i>min</i>, <i>max</i>] is subdivided into <i>n</i> equispaced points,
  3606. * where <i>n</i> is the number of (unique) values in the domain. The first
  3607. * and last point are exactly on the edge of the range.
  3608. *
  3609. * <p>This method must be called <i>after</i> the domain is set.
  3610. *
  3611. * @function
  3612. * @name pv.Scale.ordinal.prototype.splitFlush
  3613. * @param {number} min minimum value of the output range.
  3614. * @param {number} max maximum value of the output range.
  3615. * @returns {pv.Scale.ordinal} <tt>this</tt>.
  3616. * @see #split
  3617. */
  3618. scale.splitFlush = function(min, max) {
  3619. var n = this.domain().length, step = (max - min) / (n - 1);
  3620. r = (n == 1) ? [(min + max) / 2]
  3621. : pv.range(min, max + step / 2, step);
  3622. return this;
  3623. };
  3624. /**
  3625. * Sets the range from the given continuous interval. The interval
  3626. * [<i>min</i>, <i>max</i>] is subdivided into <i>n</i> equispaced bands,
  3627. * where <i>n</i> is the number of (unique) values in the domain. The first
  3628. * and last band are offset from the edge of the range by the distance between
  3629. * bands.
  3630. *
  3631. * <p>The band width argument, <tt>band</tt>, is typically in the range [0, 1]
  3632. * and defaults to 1. This fraction corresponds to the amount of space in the
  3633. * range to allocate to the bands, as opposed to padding. A value of 0.5 means
  3634. * that the band width will be equal to the padding width. The computed
  3635. * absolute band width can be retrieved from the range as
  3636. * <tt>scale.range().band</tt>.
  3637. *
  3638. * <p>If the band width argument is negative, this method will allocate bands
  3639. * of a <i>fixed</i> width <tt>-band</tt>, rather than a relative fraction of
  3640. * the available space.
  3641. *
  3642. * <p>Tip: to inset the bands by a fixed amount <tt>p</tt>, specify a minimum
  3643. * value of <tt>min + p</tt> (or simply <tt>p</tt>, if <tt>min</tt> is
  3644. * 0). Then set the mark width to <tt>scale.range().band - p</tt>.
  3645. *
  3646. * <p>This method must be called <i>after</i> the domain is set.
  3647. *
  3648. * @function
  3649. * @name pv.Scale.ordinal.prototype.splitBanded
  3650. * @param {number} min minimum value of the output range.
  3651. * @param {number} max maximum value of the output range.
  3652. * @param {number} [band] the fractional band width in [0, 1]; defaults to 1.
  3653. * @returns {pv.Scale.ordinal} <tt>this</tt>.
  3654. * @see #split
  3655. */
  3656. scale.splitBanded = function(min, max, band) {
  3657. if (arguments.length < 3) band = 1;
  3658. if (band < 0) {
  3659. var n = this.domain().length,
  3660. total = -band * n,
  3661. remaining = max - min - total,
  3662. padding = remaining / (n + 1);
  3663. r = pv.range(min + padding, max, padding - band);
  3664. r.band = -band;
  3665. } else {
  3666. var step = (max - min) / (this.domain().length + (1 - band));
  3667. r = pv.range(min + step * (1 - band), max, step);
  3668. r.band = step * band;
  3669. }
  3670. return this;
  3671. };
  3672. /**
  3673. * Returns a view of this scale by the specified accessor function <tt>f</tt>.
  3674. * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
  3675. * <tt>function(d) y(d.foo)</tt>. This method should be used judiciously; it
  3676. * is typically more clear to invoke the scale directly, passing in the value
  3677. * to be scaled.
  3678. *
  3679. * @function
  3680. * @name pv.Scale.ordinal.prototype.by
  3681. * @param {function} f an accessor function.
  3682. * @returns {pv.Scale.ordinal} a view of this scale by the specified accessor
  3683. * function.
  3684. */
  3685. scale.by = function(f) {
  3686. function by() { return scale(f.apply(this, arguments)); }
  3687. for (var method in scale) by[method] = scale[method];
  3688. return by;
  3689. };
  3690. scale.domain.apply(scale, arguments);
  3691. return scale;
  3692. };
  3693. /**
  3694. * Constructs a default quantile scale. The arguments to this constructor are
  3695. * optional, and equivalent to calling {@link #domain}. The default domain is
  3696. * the empty set, and the default range is [0,1].
  3697. *
  3698. * @class Represents a quantile scale; a function that maps from a value within
  3699. * a sortable domain to a quantized numeric range. Typically, the domain is a
  3700. * set of numbers, but any sortable value (such as strings) can be used as the
  3701. * domain of a quantile scale. The range defaults to [0,1], with 0 corresponding
  3702. * to the smallest value in the domain, 1 the largest, .5 the median, etc.
  3703. *
  3704. * <p>By default, the number of quantiles in the range corresponds to the number
  3705. * of values in the domain. The {@link #quantiles} method can be used to specify
  3706. * an explicit number of quantiles; for example, <tt>quantiles(4)</tt> produces
  3707. * a standard quartile scale. A quartile scale's range is a set of four discrete
  3708. * values, such as [0, 1/3, 2/3, 1]. Calling the {@link #range} method will
  3709. * scale these discrete values accordingly, similar to {@link
  3710. * pv.Scale.ordinal#splitFlush}.
  3711. *
  3712. * <p>For example, given the strings ["c", "a", "b"], a default quantile scale:
  3713. *
  3714. * <pre>pv.Scale.quantile("c", "a", "b")</pre>
  3715. *
  3716. * will return 0 for "a", .5 for "b", and 1 for "c".
  3717. *
  3718. * @extends pv.Scale
  3719. */
  3720. pv.Scale.quantile = function() {
  3721. var n = -1, // number of quantiles
  3722. j = -1, // max quantile index
  3723. q = [], // quantile boundaries
  3724. d = [], // domain
  3725. y = pv.Scale.linear(); // range
  3726. /** @private */
  3727. function scale(x) {
  3728. return y(Math.max(0, Math.min(j, pv.search.index(q, x) - 1)) / j);
  3729. }
  3730. /**
  3731. * Sets or gets the quantile boundaries. By default, each element in the
  3732. * domain is in its own quantile. If the argument to this method is a number,
  3733. * it specifies the number of equal-sized quantiles by which to divide the
  3734. * domain.
  3735. *
  3736. * <p>If no arguments are specified, this method returns the quantile
  3737. * boundaries; the first element is always the minimum value of the domain,
  3738. * and the last element is the maximum value of the domain. Thus, the length
  3739. * of the returned array is always one greater than the number of quantiles.
  3740. *
  3741. * @function
  3742. * @name pv.Scale.quantile.prototype.quantiles
  3743. * @param {number} x the number of quantiles.
  3744. */
  3745. scale.quantiles = function(x) {
  3746. if (arguments.length) {
  3747. n = Number(x);
  3748. if (n < 0) {
  3749. q = [d[0]].concat(d);
  3750. j = d.length - 1;
  3751. } else {
  3752. q = [];
  3753. q[0] = d[0];
  3754. for (var i = 1; i <= n; i++) {
  3755. q[i] = d[~~(i * (d.length - 1) / n)];
  3756. }
  3757. j = n - 1;
  3758. }
  3759. return this;
  3760. }
  3761. return q;
  3762. };
  3763. /**
  3764. * Sets or gets the input domain. This method can be invoked several ways:
  3765. *
  3766. * <p>1. <tt>domain(values...)</tt>
  3767. *
  3768. * <p>Specifying the domain as a series of values is the most explicit and
  3769. * recommended approach. However, if the domain values are derived from data,
  3770. * you may find the second method more appropriate.
  3771. *
  3772. * <p>2. <tt>domain(array, f)</tt>
  3773. *
  3774. * <p>Rather than enumerating the domain values as explicit arguments to this
  3775. * method, you can specify a single argument of an array. In addition, you can
  3776. * specify an optional accessor function to extract the domain values from the
  3777. * array.
  3778. *
  3779. * <p>3. <tt>domain()</tt>
  3780. *
  3781. * <p>Invoking the <tt>domain</tt> method with no arguments returns the
  3782. * current domain as an array.
  3783. *
  3784. * @function
  3785. * @name pv.Scale.quantile.prototype.domain
  3786. * @param {...} domain... domain values.
  3787. * @returns {pv.Scale.quantile} <tt>this</tt>, or the current domain.
  3788. */
  3789. scale.domain = function(array, f) {
  3790. if (arguments.length) {
  3791. d = (array instanceof Array)
  3792. ? pv.map(array, f)
  3793. : Array.prototype.slice.call(arguments);
  3794. d.sort(pv.naturalOrder);
  3795. scale.quantiles(n); // recompute quantiles
  3796. return this;
  3797. }
  3798. return d;
  3799. };
  3800. /**
  3801. * Sets or gets the output range. This method can be invoked several ways:
  3802. *
  3803. * <p>1. <tt>range(min, ..., max)</tt>
  3804. *
  3805. * <p>The range may be specified as a series of numbers or colors. Most
  3806. * commonly, two numbers are specified: the minimum and maximum pixel values.
  3807. * For a color scale, values may be specified as {@link pv.Color}s or
  3808. * equivalent strings. For a diverging scale, or other subdivided non-uniform
  3809. * scales, multiple values can be specified. For example:
  3810. *
  3811. * <pre> .range("red", "white", "green")</pre>
  3812. *
  3813. * <p>Currently, only numbers and colors are supported as range values. The
  3814. * number of range values must exactly match the number of domain values, or
  3815. * the behavior of the scale is undefined.
  3816. *
  3817. * <p>2. <tt>range()</tt>
  3818. *
  3819. * <p>Invoking the <tt>range</tt> method with no arguments returns the current
  3820. * range as an array of numbers or colors.
  3821. *
  3822. * @function
  3823. * @name pv.Scale.quantile.prototype.range
  3824. * @param {...} range... range values.
  3825. * @returns {pv.Scale.quantile} <tt>this</tt>, or the current range.
  3826. */
  3827. scale.range = function() {
  3828. if (arguments.length) {
  3829. y.range.apply(y, arguments);
  3830. return this;
  3831. }
  3832. return y.range();
  3833. };
  3834. /**
  3835. * Returns a view of this scale by the specified accessor function <tt>f</tt>.
  3836. * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
  3837. * <tt>function(d) y(d.foo)</tt>.
  3838. *
  3839. * <p>This method is provided for convenience, such that scales can be
  3840. * succinctly defined inline. For example, given an array of data elements
  3841. * that have a <tt>score</tt> attribute with the domain [0, 1], the height
  3842. * property could be specified as:
  3843. *
  3844. * <pre>.height(pv.Scale.linear().range(0, 480).by(function(d) d.score))</pre>
  3845. *
  3846. * This is equivalent to:
  3847. *
  3848. * <pre>.height(function(d) d.score * 480)</pre>
  3849. *
  3850. * This method should be used judiciously; it is typically more clear to
  3851. * invoke the scale directly, passing in the value to be scaled.
  3852. *
  3853. * @function
  3854. * @name pv.Scale.quantile.prototype.by
  3855. * @param {function} f an accessor function.
  3856. * @returns {pv.Scale.quantile} a view of this scale by the specified
  3857. * accessor function.
  3858. */
  3859. scale.by = function(f) {
  3860. function by() { return scale(f.apply(this, arguments)); }
  3861. for (var method in scale) by[method] = scale[method];
  3862. return by;
  3863. };
  3864. scale.domain.apply(scale, arguments);
  3865. return scale;
  3866. };
  3867. /**
  3868. * Returns a histogram operator for the specified data, with an optional
  3869. * accessor function. If the data specified is not an array of numbers, an
  3870. * accessor function must be specified to map the data to numeric values.
  3871. *
  3872. * @class Represents a histogram operator.
  3873. *
  3874. * @param {array} data an array of numbers or objects.
  3875. * @param {function} [f] an optional accessor function.
  3876. */
  3877. pv.histogram = function(data, f) {
  3878. var frequency = true;
  3879. return {
  3880. /**
  3881. * Returns the computed histogram bins. An optional array of numbers,
  3882. * <tt>ticks</tt>, may be specified as the break points. If the ticks are
  3883. * not specified, default ticks will be computed using a linear scale on the
  3884. * data domain.
  3885. *
  3886. * <p>The returned array contains {@link pv.histogram.Bin}s. The <tt>x</tt>
  3887. * attribute corresponds to the bin's start value (inclusive), while the
  3888. * <tt>dx</tt> attribute stores the bin size (end - start). The <tt>y</tt>
  3889. * attribute stores either the frequency count or probability, depending on
  3890. * how the histogram operator has been configured.
  3891. *
  3892. * <p>The {@link pv.histogram.Bin} objects are themselves arrays, containing
  3893. * the data elements present in each bin, i.e., the elements in the
  3894. * <tt>data</tt> array (prior to invoking the accessor function, if any).
  3895. * For example, if the data represented countries, and the accessor function
  3896. * returned the GDP of each country, the returned bins would be arrays of
  3897. * countries (not GDPs).
  3898. *
  3899. * @function
  3900. * @name pv.histogram.prototype.bins
  3901. * @param {array} [ticks]
  3902. * @returns {array}
  3903. */ /** @private */
  3904. bins: function(ticks) {
  3905. var x = pv.map(data, f), bins = [];
  3906. /* Initialize default ticks. */
  3907. if (!arguments.length) ticks = pv.Scale.linear(x).ticks();
  3908. /* Initialize the bins. */
  3909. for (var i = 0; i < ticks.length - 1; i++) {
  3910. var bin = bins[i] = [];
  3911. bin.x = ticks[i];
  3912. bin.dx = ticks[i + 1] - ticks[i];
  3913. bin.y = 0;
  3914. }
  3915. /* Count the number of samples per bin. */
  3916. for (var i = 0; i < x.length; i++) {
  3917. var j = pv.search.index(ticks, x[i]) - 1,
  3918. bin = bins[Math.max(0, Math.min(bins.length - 1, j))];
  3919. bin.y++;
  3920. bin.push(data[i]);
  3921. }
  3922. /* Convert frequencies to probabilities. */
  3923. if (!frequency) for (var i = 0; i < bins.length; i++) {
  3924. bins[i].y /= x.length;
  3925. }
  3926. return bins;
  3927. },
  3928. /**
  3929. * Sets or gets whether this histogram operator returns frequencies or
  3930. * probabilities.
  3931. *
  3932. * @function
  3933. * @name pv.histogram.prototype.frequency
  3934. * @param {boolean} [x]
  3935. * @returns {pv.histogram} this.
  3936. */ /** @private */
  3937. frequency: function(x) {
  3938. if (arguments.length) {
  3939. frequency = Boolean(x);
  3940. return this;
  3941. }
  3942. return frequency;
  3943. }
  3944. };
  3945. };
  3946. /**
  3947. * @class Represents a bin returned by the {@link pv.histogram} operator. Bins
  3948. * are themselves arrays containing the data elements present in the given bin
  3949. * (prior to the accessor function being invoked to convert the data object to a
  3950. * numeric value). These bin arrays have additional attributes with meta
  3951. * information about the bin.
  3952. *
  3953. * @name pv.histogram.Bin
  3954. * @extends array
  3955. * @see pv.histogram
  3956. */
  3957. /**
  3958. * The start value of the bin's range.
  3959. *
  3960. * @type number
  3961. * @name pv.histogram.Bin.prototype.x
  3962. */
  3963. /**
  3964. * The magnitude value of the bin's range; end - start.
  3965. *
  3966. * @type number
  3967. * @name pv.histogram.Bin.prototype.dx
  3968. */
  3969. /**
  3970. * The frequency or probability of the bin, depending on how the histogram
  3971. * operator was configured.
  3972. *
  3973. * @type number
  3974. * @name pv.histogram.Bin.prototype.y
  3975. */
  3976. /**
  3977. * Returns the {@link pv.Color} for the specified color format string. Colors
  3978. * may have an associated opacity, or alpha channel. Color formats are specified
  3979. * by CSS Color Modular Level 3, using either in RGB or HSL color space. For
  3980. * example:<ul>
  3981. *
  3982. * <li>#f00 // #rgb
  3983. * <li>#ff0000 // #rrggbb
  3984. * <li>rgb(255, 0, 0)
  3985. * <li>rgb(100%, 0%, 0%)
  3986. * <li>hsl(0, 100%, 50%)
  3987. * <li>rgba(0, 0, 255, 0.5)
  3988. * <li>hsla(120, 100%, 50%, 1)
  3989. *
  3990. * </ul>The SVG 1.0 color keywords names are also supported, such as "aliceblue"
  3991. * and "yellowgreen". The "transparent" keyword is supported for fully-
  3992. * transparent black.
  3993. *
  3994. * <p>If the <tt>format</tt> argument is already an instance of <tt>Color</tt>,
  3995. * the argument is returned with no further processing.
  3996. *
  3997. * @param {string} format the color specification string, such as "#f00".
  3998. * @returns {pv.Color} the corresponding <tt>Color</tt>.
  3999. * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color
  4000. * keywords</a>
  4001. * @see <a href="http://www.w3.org/TR/css3-color/">CSS3 color module</a>
  4002. */
  4003. pv.color = function(format) {
  4004. if (format.rgb) return format.rgb();
  4005. /* Handle hsl, rgb. */
  4006. var m1 = /([a-z]+)\((.*)\)/i.exec(format);
  4007. if (m1) {
  4008. var m2 = m1[2].split(","), a = 1;
  4009. switch (m1[1]) {
  4010. case "hsla":
  4011. case "rgba": {
  4012. a = parseFloat(m2[3]);
  4013. if (!a) return pv.Color.transparent;
  4014. break;
  4015. }
  4016. }
  4017. switch (m1[1]) {
  4018. case "hsla":
  4019. case "hsl": {
  4020. var h = parseFloat(m2[0]), // degrees
  4021. s = parseFloat(m2[1]) / 100, // percentage
  4022. l = parseFloat(m2[2]) / 100; // percentage
  4023. return (new pv.Color.Hsl(h, s, l, a)).rgb();
  4024. }
  4025. case "rgba":
  4026. case "rgb": {
  4027. function parse(c) { // either integer or percentage
  4028. var f = parseFloat(c);
  4029. return (c[c.length - 1] == '%') ? Math.round(f * 2.55) : f;
  4030. }
  4031. var r = parse(m2[0]), g = parse(m2[1]), b = parse(m2[2]);
  4032. return pv.rgb(r, g, b, a);
  4033. }
  4034. }
  4035. }
  4036. /* Named colors. */
  4037. var named = pv.Color.names[format];
  4038. if (named) return named;
  4039. /* Hexadecimal colors: #rgb and #rrggbb. */
  4040. if (format.charAt(0) == "#") {
  4041. var r, g, b;
  4042. if (format.length == 4) {
  4043. r = format.charAt(1); r += r;
  4044. g = format.charAt(2); g += g;
  4045. b = format.charAt(3); b += b;
  4046. } else if (format.length == 7) {
  4047. r = format.substring(1, 3);
  4048. g = format.substring(3, 5);
  4049. b = format.substring(5, 7);
  4050. }
  4051. return pv.rgb(parseInt(r, 16), parseInt(g, 16), parseInt(b, 16), 1);
  4052. }
  4053. /* Otherwise, pass-through unsupported colors. */
  4054. return new pv.Color(format, 1);
  4055. };
  4056. /**
  4057. * Constructs a color with the specified color format string and opacity. This
  4058. * constructor should not be invoked directly; use {@link pv.color} instead.
  4059. *
  4060. * @class Represents an abstract (possibly translucent) color. The color is
  4061. * divided into two parts: the <tt>color</tt> attribute, an opaque color format
  4062. * string, and the <tt>opacity</tt> attribute, a float in [0, 1]. The color
  4063. * space is dependent on the implementing class; all colors support the
  4064. * {@link #rgb} method to convert to RGB color space for interpolation.
  4065. *
  4066. * <p>See also the <a href="../../api/Color.html">Color guide</a>.
  4067. *
  4068. * @param {string} color an opaque color format string, such as "#f00".
  4069. * @param {number} opacity the opacity, in [0,1].
  4070. * @see pv.color
  4071. */
  4072. pv.Color = function(color, opacity) {
  4073. /**
  4074. * An opaque color format string, such as "#f00".
  4075. *
  4076. * @type string
  4077. * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color
  4078. * keywords</a>
  4079. * @see <a href="http://www.w3.org/TR/css3-color/">CSS3 color module</a>
  4080. */
  4081. this.color = color;
  4082. /**
  4083. * The opacity, a float in [0, 1].
  4084. *
  4085. * @type number
  4086. */
  4087. this.opacity = opacity;
  4088. };
  4089. /**
  4090. * Returns a new color that is a brighter version of this color. The behavior of
  4091. * this method may vary slightly depending on the underlying color space.
  4092. * Although brighter and darker are inverse operations, the results of a series
  4093. * of invocations of these two methods might be inconsistent because of rounding
  4094. * errors.
  4095. *
  4096. * @param [k] {number} an optional scale factor; defaults to 1.
  4097. * @see #darker
  4098. * @returns {pv.Color} a brighter color.
  4099. */
  4100. pv.Color.prototype.brighter = function(k) {
  4101. return this.rgb().brighter(k);
  4102. };
  4103. /**
  4104. * Returns a new color that is a brighter version of this color. The behavior of
  4105. * this method may vary slightly depending on the underlying color space.
  4106. * Although brighter and darker are inverse operations, the results of a series
  4107. * of invocations of these two methods might be inconsistent because of rounding
  4108. * errors.
  4109. *
  4110. * @param [k] {number} an optional scale factor; defaults to 1.
  4111. * @see #brighter
  4112. * @returns {pv.Color} a darker color.
  4113. */
  4114. pv.Color.prototype.darker = function(k) {
  4115. return this.rgb().darker(k);
  4116. };
  4117. /**
  4118. * Constructs a new RGB color with the specified channel values.
  4119. *
  4120. * @param {number} r the red channel, an integer in [0,255].
  4121. * @param {number} g the green channel, an integer in [0,255].
  4122. * @param {number} b the blue channel, an integer in [0,255].
  4123. * @param {number} [a] the alpha channel, a float in [0,1].
  4124. * @returns pv.Color.Rgb
  4125. */
  4126. pv.rgb = function(r, g, b, a) {
  4127. return new pv.Color.Rgb(r, g, b, (arguments.length == 4) ? a : 1);
  4128. };
  4129. /**
  4130. * Constructs a new RGB color with the specified channel values.
  4131. *
  4132. * @class Represents a color in RGB space.
  4133. *
  4134. * @param {number} r the red channel, an integer in [0,255].
  4135. * @param {number} g the green channel, an integer in [0,255].
  4136. * @param {number} b the blue channel, an integer in [0,255].
  4137. * @param {number} a the alpha channel, a float in [0,1].
  4138. * @extends pv.Color
  4139. */
  4140. pv.Color.Rgb = function(r, g, b, a) {
  4141. pv.Color.call(this, a ? ("rgb(" + r + "," + g + "," + b + ")") : "none", a);
  4142. /**
  4143. * The red channel, an integer in [0, 255].
  4144. *
  4145. * @type number
  4146. */
  4147. this.r = r;
  4148. /**
  4149. * The green channel, an integer in [0, 255].
  4150. *
  4151. * @type number
  4152. */
  4153. this.g = g;
  4154. /**
  4155. * The blue channel, an integer in [0, 255].
  4156. *
  4157. * @type number
  4158. */
  4159. this.b = b;
  4160. /**
  4161. * The alpha channel, a float in [0, 1].
  4162. *
  4163. * @type number
  4164. */
  4165. this.a = a;
  4166. };
  4167. pv.Color.Rgb.prototype = pv.extend(pv.Color);
  4168. /**
  4169. * Constructs a new RGB color with the same green, blue and alpha channels as
  4170. * this color, with the specified red channel.
  4171. *
  4172. * @param {number} r the red channel, an integer in [0,255].
  4173. */
  4174. pv.Color.Rgb.prototype.red = function(r) {
  4175. return pv.rgb(r, this.g, this.b, this.a);
  4176. };
  4177. /**
  4178. * Constructs a new RGB color with the same red, blue and alpha channels as this
  4179. * color, with the specified green channel.
  4180. *
  4181. * @param {number} g the green channel, an integer in [0,255].
  4182. */
  4183. pv.Color.Rgb.prototype.green = function(g) {
  4184. return pv.rgb(this.r, g, this.b, this.a);
  4185. };
  4186. /**
  4187. * Constructs a new RGB color with the same red, green and alpha channels as
  4188. * this color, with the specified blue channel.
  4189. *
  4190. * @param {number} b the blue channel, an integer in [0,255].
  4191. */
  4192. pv.Color.Rgb.prototype.blue = function(b) {
  4193. return pv.rgb(this.r, this.g, b, this.a);
  4194. };
  4195. /**
  4196. * Constructs a new RGB color with the same red, green and blue channels as this
  4197. * color, with the specified alpha channel.
  4198. *
  4199. * @param {number} a the alpha channel, a float in [0,1].
  4200. */
  4201. pv.Color.Rgb.prototype.alpha = function(a) {
  4202. return pv.rgb(this.r, this.g, this.b, a);
  4203. };
  4204. /**
  4205. * Returns the RGB color equivalent to this color. This method is abstract and
  4206. * must be implemented by subclasses.
  4207. *
  4208. * @returns {pv.Color.Rgb} an RGB color.
  4209. * @function
  4210. * @name pv.Color.prototype.rgb
  4211. */
  4212. /**
  4213. * Returns this.
  4214. *
  4215. * @returns {pv.Color.Rgb} this.
  4216. */
  4217. pv.Color.Rgb.prototype.rgb = function() { return this; };
  4218. /**
  4219. * Returns a new color that is a brighter version of this color. This method
  4220. * applies an arbitrary scale factor to each of the three RGB components of this
  4221. * color to create a brighter version of this color. Although brighter and
  4222. * darker are inverse operations, the results of a series of invocations of
  4223. * these two methods might be inconsistent because of rounding errors.
  4224. *
  4225. * @param [k] {number} an optional scale factor; defaults to 1.
  4226. * @see #darker
  4227. * @returns {pv.Color.Rgb} a brighter color.
  4228. */
  4229. pv.Color.Rgb.prototype.brighter = function(k) {
  4230. k = Math.pow(0.7, arguments.length ? k : 1);
  4231. var r = this.r, g = this.g, b = this.b, i = 30;
  4232. if (!r && !g && !b) return pv.rgb(i, i, i, this.a);
  4233. if (r && (r < i)) r = i;
  4234. if (g && (g < i)) g = i;
  4235. if (b && (b < i)) b = i;
  4236. return pv.rgb(
  4237. Math.min(255, Math.floor(r / k)),
  4238. Math.min(255, Math.floor(g / k)),
  4239. Math.min(255, Math.floor(b / k)),
  4240. this.a);
  4241. };
  4242. /**
  4243. * Returns a new color that is a darker version of this color. This method
  4244. * applies an arbitrary scale factor to each of the three RGB components of this
  4245. * color to create a darker version of this color. Although brighter and darker
  4246. * are inverse operations, the results of a series of invocations of these two
  4247. * methods might be inconsistent because of rounding errors.
  4248. *
  4249. * @param [k] {number} an optional scale factor; defaults to 1.
  4250. * @see #brighter
  4251. * @returns {pv.Color.Rgb} a darker color.
  4252. */
  4253. pv.Color.Rgb.prototype.darker = function(k) {
  4254. k = Math.pow(0.7, arguments.length ? k : 1);
  4255. return pv.rgb(
  4256. Math.max(0, Math.floor(k * this.r)),
  4257. Math.max(0, Math.floor(k * this.g)),
  4258. Math.max(0, Math.floor(k * this.b)),
  4259. this.a);
  4260. };
  4261. /**
  4262. * Constructs a new HSL color with the specified values.
  4263. *
  4264. * @param {number} h the hue, an integer in [0, 360].
  4265. * @param {number} s the saturation, a float in [0, 1].
  4266. * @param {number} l the lightness, a float in [0, 1].
  4267. * @param {number} [a] the opacity, a float in [0, 1].
  4268. * @returns pv.Color.Hsl
  4269. */
  4270. pv.hsl = function(h, s, l, a) {
  4271. return new pv.Color.Hsl(h, s, l, (arguments.length == 4) ? a : 1);
  4272. };
  4273. /**
  4274. * Constructs a new HSL color with the specified values.
  4275. *
  4276. * @class Represents a color in HSL space.
  4277. *
  4278. * @param {number} h the hue, an integer in [0, 360].
  4279. * @param {number} s the saturation, a float in [0, 1].
  4280. * @param {number} l the lightness, a float in [0, 1].
  4281. * @param {number} a the opacity, a float in [0, 1].
  4282. * @extends pv.Color
  4283. */
  4284. pv.Color.Hsl = function(h, s, l, a) {
  4285. pv.Color.call(this, "hsl(" + h + "," + (s * 100) + "%," + (l * 100) + "%)", a);
  4286. /**
  4287. * The hue, an integer in [0, 360].
  4288. *
  4289. * @type number
  4290. */
  4291. this.h = h;
  4292. /**
  4293. * The saturation, a float in [0, 1].
  4294. *
  4295. * @type number
  4296. */
  4297. this.s = s;
  4298. /**
  4299. * The lightness, a float in [0, 1].
  4300. *
  4301. * @type number
  4302. */
  4303. this.l = l;
  4304. /**
  4305. * The opacity, a float in [0, 1].
  4306. *
  4307. * @type number
  4308. */
  4309. this.a = a;
  4310. };
  4311. pv.Color.Hsl.prototype = pv.extend(pv.Color);
  4312. /**
  4313. * Constructs a new HSL color with the same saturation, lightness and alpha as
  4314. * this color, and the specified hue.
  4315. *
  4316. * @param {number} h the hue, an integer in [0, 360].
  4317. */
  4318. pv.Color.Hsl.prototype.hue = function(h) {
  4319. return pv.hsl(h, this.s, this.l, this.a);
  4320. };
  4321. /**
  4322. * Constructs a new HSL color with the same hue, lightness and alpha as this
  4323. * color, and the specified saturation.
  4324. *
  4325. * @param {number} s the saturation, a float in [0, 1].
  4326. */
  4327. pv.Color.Hsl.prototype.saturation = function(s) {
  4328. return pv.hsl(this.h, s, this.l, this.a);
  4329. };
  4330. /**
  4331. * Constructs a new HSL color with the same hue, saturation and alpha as this
  4332. * color, and the specified lightness.
  4333. *
  4334. * @param {number} l the lightness, a float in [0, 1].
  4335. */
  4336. pv.Color.Hsl.prototype.lightness = function(l) {
  4337. return pv.hsl(this.h, this.s, l, this.a);
  4338. };
  4339. /**
  4340. * Constructs a new HSL color with the same hue, saturation and lightness as
  4341. * this color, and the specified alpha.
  4342. *
  4343. * @param {number} a the opacity, a float in [0, 1].
  4344. */
  4345. pv.Color.Hsl.prototype.alpha = function(a) {
  4346. return pv.hsl(this.h, this.s, this.l, a);
  4347. };
  4348. /**
  4349. * Returns the RGB color equivalent to this HSL color.
  4350. *
  4351. * @returns {pv.Color.Rgb} an RGB color.
  4352. */
  4353. pv.Color.Hsl.prototype.rgb = function() {
  4354. var h = this.h, s = this.s, l = this.l;
  4355. /* Some simple corrections for h, s and l. */
  4356. h = h % 360; if (h < 0) h += 360;
  4357. s = Math.max(0, Math.min(s, 1));
  4358. l = Math.max(0, Math.min(l, 1));
  4359. /* From FvD 13.37, CSS Color Module Level 3 */
  4360. var m2 = (l <= .5) ? (l * (1 + s)) : (l + s - l * s);
  4361. var m1 = 2 * l - m2;
  4362. function v(h) {
  4363. if (h > 360) h -= 360;
  4364. else if (h < 0) h += 360;
  4365. if (h < 60) return m1 + (m2 - m1) * h / 60;
  4366. if (h < 180) return m2;
  4367. if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
  4368. return m1;
  4369. }
  4370. function vv(h) {
  4371. return Math.round(v(h) * 255);
  4372. }
  4373. return pv.rgb(vv(h + 120), vv(h), vv(h - 120), this.a);
  4374. };
  4375. /**
  4376. * @private SVG color keywords, per CSS Color Module Level 3.
  4377. *
  4378. * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color
  4379. * keywords</a>
  4380. */
  4381. pv.Color.names = {
  4382. aliceblue: "#f0f8ff",
  4383. antiquewhite: "#faebd7",
  4384. aqua: "#00ffff",
  4385. aquamarine: "#7fffd4",
  4386. azure: "#f0ffff",
  4387. beige: "#f5f5dc",
  4388. bisque: "#ffe4c4",
  4389. black: "#000000",
  4390. blanchedalmond: "#ffebcd",
  4391. blue: "#0000ff",
  4392. blueviolet: "#8a2be2",
  4393. brown: "#a52a2a",
  4394. burlywood: "#deb887",
  4395. cadetblue: "#5f9ea0",
  4396. chartreuse: "#7fff00",
  4397. chocolate: "#d2691e",
  4398. coral: "#ff7f50",
  4399. cornflowerblue: "#6495ed",
  4400. cornsilk: "#fff8dc",
  4401. crimson: "#dc143c",
  4402. cyan: "#00ffff",
  4403. darkblue: "#00008b",
  4404. darkcyan: "#008b8b",
  4405. darkgoldenrod: "#b8860b",
  4406. darkgray: "#a9a9a9",
  4407. darkgreen: "#006400",
  4408. darkgrey: "#a9a9a9",
  4409. darkkhaki: "#bdb76b",
  4410. darkmagenta: "#8b008b",
  4411. darkolivegreen: "#556b2f",
  4412. darkorange: "#ff8c00",
  4413. darkorchid: "#9932cc",
  4414. darkred: "#8b0000",
  4415. darksalmon: "#e9967a",
  4416. darkseagreen: "#8fbc8f",
  4417. darkslateblue: "#483d8b",
  4418. darkslategray: "#2f4f4f",
  4419. darkslategrey: "#2f4f4f",
  4420. darkturquoise: "#00ced1",
  4421. darkviolet: "#9400d3",
  4422. deeppink: "#ff1493",
  4423. deepskyblue: "#00bfff",
  4424. dimgray: "#696969",
  4425. dimgrey: "#696969",
  4426. dodgerblue: "#1e90ff",
  4427. firebrick: "#b22222",
  4428. floralwhite: "#fffaf0",
  4429. forestgreen: "#228b22",
  4430. fuchsia: "#ff00ff",
  4431. gainsboro: "#dcdcdc",
  4432. ghostwhite: "#f8f8ff",
  4433. gold: "#ffd700",
  4434. goldenrod: "#daa520",
  4435. gray: "#808080",
  4436. green: "#008000",
  4437. greenyellow: "#adff2f",
  4438. grey: "#808080",
  4439. honeydew: "#f0fff0",
  4440. hotpink: "#ff69b4",
  4441. indianred: "#cd5c5c",
  4442. indigo: "#4b0082",
  4443. ivory: "#fffff0",
  4444. khaki: "#f0e68c",
  4445. lavender: "#e6e6fa",
  4446. lavenderblush: "#fff0f5",
  4447. lawngreen: "#7cfc00",
  4448. lemonchiffon: "#fffacd",
  4449. lightblue: "#add8e6",
  4450. lightcoral: "#f08080",
  4451. lightcyan: "#e0ffff",
  4452. lightgoldenrodyellow: "#fafad2",
  4453. lightgray: "#d3d3d3",
  4454. lightgreen: "#90ee90",
  4455. lightgrey: "#d3d3d3",
  4456. lightpink: "#ffb6c1",
  4457. lightsalmon: "#ffa07a",
  4458. lightseagreen: "#20b2aa",
  4459. lightskyblue: "#87cefa",
  4460. lightslategray: "#778899",
  4461. lightslategrey: "#778899",
  4462. lightsteelblue: "#b0c4de",
  4463. lightyellow: "#ffffe0",
  4464. lime: "#00ff00",
  4465. limegreen: "#32cd32",
  4466. linen: "#faf0e6",
  4467. magenta: "#ff00ff",
  4468. maroon: "#800000",
  4469. mediumaquamarine: "#66cdaa",
  4470. mediumblue: "#0000cd",
  4471. mediumorchid: "#ba55d3",
  4472. mediumpurple: "#9370db",
  4473. mediumseagreen: "#3cb371",
  4474. mediumslateblue: "#7b68ee",
  4475. mediumspringgreen: "#00fa9a",
  4476. mediumturquoise: "#48d1cc",
  4477. mediumvioletred: "#c71585",
  4478. midnightblue: "#191970",
  4479. mintcream: "#f5fffa",
  4480. mistyrose: "#ffe4e1",
  4481. moccasin: "#ffe4b5",
  4482. navajowhite: "#ffdead",
  4483. navy: "#000080",
  4484. oldlace: "#fdf5e6",
  4485. olive: "#808000",
  4486. olivedrab: "#6b8e23",
  4487. orange: "#ffa500",
  4488. orangered: "#ff4500",
  4489. orchid: "#da70d6",
  4490. palegoldenrod: "#eee8aa",
  4491. palegreen: "#98fb98",
  4492. paleturquoise: "#afeeee",
  4493. palevioletred: "#db7093",
  4494. papayawhip: "#ffefd5",
  4495. peachpuff: "#ffdab9",
  4496. peru: "#cd853f",
  4497. pink: "#ffc0cb",
  4498. plum: "#dda0dd",
  4499. powderblue: "#b0e0e6",
  4500. purple: "#800080",
  4501. red: "#ff0000",
  4502. rosybrown: "#bc8f8f",
  4503. royalblue: "#4169e1",
  4504. saddlebrown: "#8b4513",
  4505. salmon: "#fa8072",
  4506. sandybrown: "#f4a460",
  4507. seagreen: "#2e8b57",
  4508. seashell: "#fff5ee",
  4509. sienna: "#a0522d",
  4510. silver: "#c0c0c0",
  4511. skyblue: "#87ceeb",
  4512. slateblue: "#6a5acd",
  4513. slategray: "#708090",
  4514. slategrey: "#708090",
  4515. snow: "#fffafa",
  4516. springgreen: "#00ff7f",
  4517. steelblue: "#4682b4",
  4518. tan: "#d2b48c",
  4519. teal: "#008080",
  4520. thistle: "#d8bfd8",
  4521. tomato: "#ff6347",
  4522. turquoise: "#40e0d0",
  4523. violet: "#ee82ee",
  4524. wheat: "#f5deb3",
  4525. white: "#ffffff",
  4526. whitesmoke: "#f5f5f5",
  4527. yellow: "#ffff00",
  4528. yellowgreen: "#9acd32",
  4529. transparent: pv.Color.transparent = pv.rgb(0, 0, 0, 0)
  4530. };
  4531. /* Initialized named colors. */
  4532. (function() {
  4533. var names = pv.Color.names;
  4534. for (var name in names) names[name] = pv.color(names[name]);
  4535. })();
  4536. /**
  4537. * Returns a new categorical color encoding using the specified colors. The
  4538. * arguments to this method are an array of colors; see {@link pv.color}. For
  4539. * example, to create a categorical color encoding using the <tt>species</tt>
  4540. * attribute:
  4541. *
  4542. * <pre>pv.colors("red", "green", "blue").by(function(d) d.species)</pre>
  4543. *
  4544. * The result of this expression can be used as a fill- or stroke-style
  4545. * property. This assumes that the data's <tt>species</tt> attribute is a
  4546. * string.
  4547. *
  4548. * @param {string} colors... categorical colors.
  4549. * @see pv.Scale.ordinal
  4550. * @returns {pv.Scale.ordinal} an ordinal color scale.
  4551. */
  4552. pv.colors = function() {
  4553. var scale = pv.Scale.ordinal();
  4554. scale.range.apply(scale, arguments);
  4555. return scale;
  4556. };
  4557. /**
  4558. * A collection of standard color palettes for categorical encoding.
  4559. *
  4560. * @namespace A collection of standard color palettes for categorical encoding.
  4561. */
  4562. pv.Colors = {};
  4563. /**
  4564. * Returns a new 10-color scheme. The arguments to this constructor are
  4565. * optional, and equivalent to calling {@link pv.Scale.OrdinalScale#domain}. The
  4566. * following colors are used:
  4567. *
  4568. * <div style="background:#1f77b4;">#1f77b4</div>
  4569. * <div style="background:#ff7f0e;">#ff7f0e</div>
  4570. * <div style="background:#2ca02c;">#2ca02c</div>
  4571. * <div style="background:#d62728;">#d62728</div>
  4572. * <div style="background:#9467bd;">#9467bd</div>
  4573. * <div style="background:#8c564b;">#8c564b</div>
  4574. * <div style="background:#e377c2;">#e377c2</div>
  4575. * <div style="background:#7f7f7f;">#7f7f7f</div>
  4576. * <div style="background:#bcbd22;">#bcbd22</div>
  4577. * <div style="background:#17becf;">#17becf</div>
  4578. *
  4579. * @param {number...} domain... domain values.
  4580. * @returns {pv.Scale.ordinal} a new ordinal color scale.
  4581. * @see pv.color
  4582. */
  4583. pv.Colors.category10 = function() {
  4584. var scale = pv.colors(
  4585. "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
  4586. "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf");
  4587. scale.domain.apply(scale, arguments);
  4588. return scale;
  4589. };
  4590. /**
  4591. * Returns a new 20-color scheme. The arguments to this constructor are
  4592. * optional, and equivalent to calling {@link pv.Scale.OrdinalScale#domain}. The
  4593. * following colors are used:
  4594. *
  4595. * <div style="background:#1f77b4;">#1f77b4</div>
  4596. * <div style="background:#aec7e8;">#aec7e8</div>
  4597. * <div style="background:#ff7f0e;">#ff7f0e</div>
  4598. * <div style="background:#ffbb78;">#ffbb78</div>
  4599. * <div style="background:#2ca02c;">#2ca02c</div>
  4600. * <div style="background:#98df8a;">#98df8a</div>
  4601. * <div style="background:#d62728;">#d62728</div>
  4602. * <div style="background:#ff9896;">#ff9896</div>
  4603. * <div style="background:#9467bd;">#9467bd</div>
  4604. * <div style="background:#c5b0d5;">#c5b0d5</div>
  4605. * <div style="background:#8c564b;">#8c564b</div>
  4606. * <div style="background:#c49c94;">#c49c94</div>
  4607. * <div style="background:#e377c2;">#e377c2</div>
  4608. * <div style="background:#f7b6d2;">#f7b6d2</div>
  4609. * <div style="background:#7f7f7f;">#7f7f7f</div>
  4610. * <div style="background:#c7c7c7;">#c7c7c7</div>
  4611. * <div style="background:#bcbd22;">#bcbd22</div>
  4612. * <div style="background:#dbdb8d;">#dbdb8d</div>
  4613. * <div style="background:#17becf;">#17becf</div>
  4614. * <div style="background:#9edae5;">#9edae5</div>
  4615. *
  4616. * @param {number...} domain... domain values.
  4617. * @returns {pv.Scale.ordinal} a new ordinal color scale.
  4618. * @see pv.color
  4619. */
  4620. pv.Colors.category20 = function() {
  4621. var scale = pv.colors(
  4622. "#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c",
  4623. "#98df8a", "#d62728", "#ff9896", "#9467bd", "#c5b0d5",
  4624. "#8c564b", "#c49c94", "#e377c2", "#f7b6d2", "#7f7f7f",
  4625. "#c7c7c7", "#bcbd22", "#dbdb8d", "#17becf", "#9edae5");
  4626. scale.domain.apply(scale, arguments);
  4627. return scale;
  4628. };
  4629. /**
  4630. * Returns a new alternative 19-color scheme. The arguments to this constructor
  4631. * are optional, and equivalent to calling
  4632. * {@link pv.Scale.OrdinalScale#domain}. The following colors are used:
  4633. *
  4634. * <div style="background:#9c9ede;">#9c9ede</div>
  4635. * <div style="background:#7375b5;">#7375b5</div>
  4636. * <div style="background:#4a5584;">#4a5584</div>
  4637. * <div style="background:#cedb9c;">#cedb9c</div>
  4638. * <div style="background:#b5cf6b;">#b5cf6b</div>
  4639. * <div style="background:#8ca252;">#8ca252</div>
  4640. * <div style="background:#637939;">#637939</div>
  4641. * <div style="background:#e7cb94;">#e7cb94</div>
  4642. * <div style="background:#e7ba52;">#e7ba52</div>
  4643. * <div style="background:#bd9e39;">#bd9e39</div>
  4644. * <div style="background:#8c6d31;">#8c6d31</div>
  4645. * <div style="background:#e7969c;">#e7969c</div>
  4646. * <div style="background:#d6616b;">#d6616b</div>
  4647. * <div style="background:#ad494a;">#ad494a</div>
  4648. * <div style="background:#843c39;">#843c39</div>
  4649. * <div style="background:#de9ed6;">#de9ed6</div>
  4650. * <div style="background:#ce6dbd;">#ce6dbd</div>
  4651. * <div style="background:#a55194;">#a55194</div>
  4652. * <div style="background:#7b4173;">#7b4173</div>
  4653. *
  4654. * @param {number...} domain... domain values.
  4655. * @returns {pv.Scale.ordinal} a new ordinal color scale.
  4656. * @see pv.color
  4657. */
  4658. pv.Colors.category19 = function() {
  4659. var scale = pv.colors(
  4660. "#9c9ede", "#7375b5", "#4a5584", "#cedb9c", "#b5cf6b",
  4661. "#8ca252", "#637939", "#e7cb94", "#e7ba52", "#bd9e39",
  4662. "#8c6d31", "#e7969c", "#d6616b", "#ad494a", "#843c39",
  4663. "#de9ed6", "#ce6dbd", "#a55194", "#7b4173");
  4664. scale.domain.apply(scale, arguments);
  4665. return scale;
  4666. };
  4667. /**
  4668. * Returns a linear color ramp from the specified <tt>start</tt> color to the
  4669. * specified <tt>end</tt> color. The color arguments may be specified either as
  4670. * <tt>string</tt>s or as {@link pv.Color}s. This is equivalent to:
  4671. *
  4672. * <pre> pv.Scale.linear().domain(0, 1).range(...)</pre>
  4673. *
  4674. * @param {string} start the start color; may be a <tt>pv.Color</tt>.
  4675. * @param {string} end the end color; may be a <tt>pv.Color</tt>.
  4676. * @returns {Function} a color ramp from <tt>start</tt> to <tt>end</tt>.
  4677. * @see pv.Scale.linear
  4678. */
  4679. pv.ramp = function(start, end) {
  4680. var scale = pv.Scale.linear();
  4681. scale.range.apply(scale, arguments);
  4682. return scale;
  4683. };
  4684. /**
  4685. * @private
  4686. * @namespace
  4687. */
  4688. pv.SvgScene = {
  4689. /* Various namespaces. */
  4690. svg: "http://www.w3.org/2000/svg",
  4691. xmlns: "http://www.w3.org/2000/xmlns",
  4692. xlink: "http://www.w3.org/1999/xlink",
  4693. xhtml: "http://www.w3.org/1999/xhtml",
  4694. /** The pre-multipled scale, based on any enclosing transforms. */
  4695. scale: 1,
  4696. /** The set of supported events. */
  4697. events: [
  4698. "DOMMouseScroll", // for Firefox
  4699. "mousewheel",
  4700. "mousedown",
  4701. "mouseup",
  4702. "mouseover",
  4703. "mouseout",
  4704. "mousemove",
  4705. "click",
  4706. "dblclick"
  4707. ],
  4708. /** Implicit values for SVG and CSS properties. */
  4709. implicit: {
  4710. svg: {
  4711. "shape-rendering": "auto",
  4712. "pointer-events": "painted",
  4713. "x": 0,
  4714. "y": 0,
  4715. "dy": 0,
  4716. "text-anchor": "start",
  4717. "transform": "translate(0,0)",
  4718. "fill": "none",
  4719. "fill-opacity": 1,
  4720. "stroke": "none",
  4721. "stroke-opacity": 1,
  4722. "stroke-width": 1.5,
  4723. "stroke-linejoin": "miter"
  4724. },
  4725. css: {
  4726. "font": "10px sans-serif"
  4727. }
  4728. }
  4729. };
  4730. /**
  4731. * Creates a new SVG element of the specified type.
  4732. *
  4733. * @param type {string} an SVG element type, such as "rect".
  4734. * @returns a new SVG element.
  4735. */
  4736. pv.SvgScene.create = function(type) {
  4737. return document.createElementNS(this.svg, type);
  4738. };
  4739. /**
  4740. * Expects the element <i>e</i> to be the specified type. If the element does
  4741. * not exist, a new one is created. If the element does exist but is the wrong
  4742. * type, it is replaced with the specified element.
  4743. *
  4744. * @param e the current SVG element.
  4745. * @param type {string} an SVG element type, such as "rect".
  4746. * @param attributes an optional attribute map.
  4747. * @param style an optional style map.
  4748. * @returns a new SVG element.
  4749. */
  4750. pv.SvgScene.expect = function(e, type, attributes, style) {
  4751. if (e) {
  4752. if (e.tagName == "a") e = e.firstChild;
  4753. if (e.tagName != type) {
  4754. var n = this.create(type);
  4755. e.parentNode.replaceChild(n, e);
  4756. e = n;
  4757. }
  4758. } else {
  4759. e = this.create(type);
  4760. }
  4761. for (var name in attributes) {
  4762. var value = attributes[name];
  4763. if (value == this.implicit.svg[name]) value = null;
  4764. if (value == null) e.removeAttribute(name);
  4765. else e.setAttribute(name, value);
  4766. }
  4767. for (var name in style) {
  4768. var value = style[name];
  4769. if (value == this.implicit.css[name]) value = null;
  4770. if (value == null) e.style.removeProperty(name);
  4771. else e.style[name] = value;
  4772. }
  4773. return e;
  4774. };
  4775. /** TODO */
  4776. pv.SvgScene.append = function(e, scenes, index) {
  4777. e.$scene = {scenes:scenes, index:index};
  4778. e = this.title(e, scenes[index]);
  4779. if (!e.parentNode) scenes.$g.appendChild(e);
  4780. return e.nextSibling;
  4781. };
  4782. /**
  4783. * Applies a title tooltip to the specified element <tt>e</tt>, using the
  4784. * <tt>title</tt> property of the specified scene node <tt>s</tt>. Note that
  4785. * this implementation does not create an SVG <tt>title</tt> element as a child
  4786. * of <tt>e</tt>; although this is the recommended standard, it is only
  4787. * supported in Opera. Instead, an anchor element is created around the element
  4788. * <tt>e</tt>, and the <tt>xlink:title</tt> attribute is set accordingly.
  4789. *
  4790. * @param e an SVG element.
  4791. * @param s a scene node.
  4792. */
  4793. pv.SvgScene.title = function(e, s) {
  4794. var a = e.parentNode;
  4795. if (a && (a.tagName != "a")) a = null;
  4796. if (s.title) {
  4797. if (!a) {
  4798. a = this.create("a");
  4799. if (e.parentNode) e.parentNode.replaceChild(a, e);
  4800. a.appendChild(e);
  4801. }
  4802. a.setAttributeNS(this.xlink, "title", s.title);
  4803. return a;
  4804. }
  4805. if (a) a.parentNode.replaceChild(e, a);
  4806. return e;
  4807. };
  4808. /** TODO */
  4809. pv.SvgScene.dispatch = pv.listener(function(e) {
  4810. var t = e.target.$scene;
  4811. if (t) {
  4812. var type = e.type;
  4813. /* Fixes for mousewheel support on Firefox & Opera. */
  4814. switch (type) {
  4815. case "DOMMouseScroll": {
  4816. type = "mousewheel";
  4817. e.wheel = -480 * e.detail;
  4818. break;
  4819. }
  4820. case "mousewheel": {
  4821. e.wheel = (window.opera ? 12 : 1) * e.wheelDelta;
  4822. break;
  4823. }
  4824. }
  4825. if (pv.Mark.dispatch(type, t.scenes, t.index)) e.preventDefault();
  4826. }
  4827. });
  4828. /** @private Do nothing when rendering undefined mark types. */
  4829. pv.SvgScene.undefined = function() {};
  4830. /**
  4831. * @private Converts the specified b-spline curve segment to a bezier curve
  4832. * compatible with SVG "C".
  4833. *
  4834. * @param p0 the first control point.
  4835. * @param p1 the second control point.
  4836. * @param p2 the third control point.
  4837. * @param p3 the fourth control point.
  4838. */
  4839. pv.SvgScene.pathBasis = (function() {
  4840. /**
  4841. * Matrix to transform basis (b-spline) control points to bezier control
  4842. * points. Derived from FvD 11.2.8.
  4843. */
  4844. var basis = [
  4845. [ 1/6, 2/3, 1/6, 0 ],
  4846. [ 0, 2/3, 1/3, 0 ],
  4847. [ 0, 1/3, 2/3, 0 ],
  4848. [ 0, 1/6, 2/3, 1/6 ]
  4849. ];
  4850. /**
  4851. * Returns the point that is the weighted sum of the specified control points,
  4852. * using the specified weights. This method requires that there are four
  4853. * weights and four control points.
  4854. */
  4855. function weight(w, p0, p1, p2, p3) {
  4856. return {
  4857. x: w[0] * p0.left + w[1] * p1.left + w[2] * p2.left + w[3] * p3.left,
  4858. y: w[0] * p0.top + w[1] * p1.top + w[2] * p2.top + w[3] * p3.top
  4859. };
  4860. }
  4861. var convert = function(p0, p1, p2, p3) {
  4862. var b1 = weight(basis[1], p0, p1, p2, p3),
  4863. b2 = weight(basis[2], p0, p1, p2, p3),
  4864. b3 = weight(basis[3], p0, p1, p2, p3);
  4865. return "C" + b1.x + "," + b1.y
  4866. + "," + b2.x + "," + b2.y
  4867. + "," + b3.x + "," + b3.y;
  4868. };
  4869. convert.segment = function(p0, p1, p2, p3) {
  4870. var b0 = weight(basis[0], p0, p1, p2, p3),
  4871. b1 = weight(basis[1], p0, p1, p2, p3),
  4872. b2 = weight(basis[2], p0, p1, p2, p3),
  4873. b3 = weight(basis[3], p0, p1, p2, p3);
  4874. return "M" + b0.x + "," + b0.y
  4875. + "C" + b1.x + "," + b1.y
  4876. + "," + b2.x + "," + b2.y
  4877. + "," + b3.x + "," + b3.y;
  4878. };
  4879. return convert;
  4880. })();
  4881. /**
  4882. * @private Interpolates the given points using the basis spline interpolation.
  4883. * Returns an SVG path without the leading M instruction to allow path
  4884. * appending.
  4885. *
  4886. * @param points the array of points.
  4887. */
  4888. pv.SvgScene.curveBasis = function(points) {
  4889. if (points.length <= 2) return "";
  4890. var path = "",
  4891. p0 = points[0],
  4892. p1 = p0,
  4893. p2 = p0,
  4894. p3 = points[1];
  4895. path += this.pathBasis(p0, p1, p2, p3);
  4896. for (var i = 2; i < points.length; i++) {
  4897. p0 = p1;
  4898. p1 = p2;
  4899. p2 = p3;
  4900. p3 = points[i];
  4901. path += this.pathBasis(p0, p1, p2, p3);
  4902. }
  4903. /* Cycle through to get the last point. */
  4904. path += this.pathBasis(p1, p2, p3, p3);
  4905. path += this.pathBasis(p2, p3, p3, p3);
  4906. return path;
  4907. };
  4908. /**
  4909. * @private Interpolates the given points using the basis spline interpolation.
  4910. * If points.length == tangents.length then a regular Hermite interpolation is
  4911. * performed, if points.length == tangents.length + 2 then the first and last
  4912. * segments are filled in with cubic bazier segments. Returns an array of path
  4913. * strings.
  4914. *
  4915. * @param points the array of points.
  4916. */
  4917. pv.SvgScene.curveBasisSegments = function(points) {
  4918. if (points.length <= 2) return "";
  4919. var paths = [],
  4920. p0 = points[0],
  4921. p1 = p0,
  4922. p2 = p0,
  4923. p3 = points[1],
  4924. firstPath = this.pathBasis.segment(p0, p1, p2, p3);
  4925. p0 = p1;
  4926. p1 = p2;
  4927. p2 = p3;
  4928. p3 = points[2];
  4929. paths.push(firstPath + this.pathBasis(p0, p1, p2, p3)); // merge first & second path
  4930. for (var i = 3; i < points.length; i++) {
  4931. p0 = p1;
  4932. p1 = p2;
  4933. p2 = p3;
  4934. p3 = points[i];
  4935. paths.push(this.pathBasis.segment(p0, p1, p2, p3));
  4936. }
  4937. // merge last & second-to-last path
  4938. paths.push(this.pathBasis.segment(p1, p2, p3, p3) + this.pathBasis(p2, p3, p3, p3));
  4939. return paths;
  4940. };
  4941. /**
  4942. * @private Interpolates the given points with respective tangents using the cubic
  4943. * Hermite spline interpolation. If points.length == tangents.length then a regular
  4944. * Hermite interpolation is performed, if points.length == tangents.length + 2 then
  4945. * the first and last segments are filled in with cubic bazier segments.
  4946. * Returns an SVG path without the leading M instruction to allow path appending.
  4947. *
  4948. * @param points the array of points.
  4949. * @param tangents the array of tangent vectors.
  4950. */
  4951. pv.SvgScene.curveHermite = function(points, tangents) {
  4952. if (tangents.length < 1
  4953. || (points.length != tangents.length
  4954. && points.length != tangents.length + 2)) return "";
  4955. var quad = points.length != tangents.length,
  4956. path = "",
  4957. p0 = points[0],
  4958. p = points[1],
  4959. t0 = tangents[0],
  4960. t = t0,
  4961. pi = 1;
  4962. if (quad) {
  4963. path += "Q" + (p.left - t0.x * 2 / 3) + "," + (p.top - t0.y * 2 / 3)
  4964. + "," + p.left + "," + p.top;
  4965. p0 = points[1];
  4966. pi = 2;
  4967. }
  4968. if (tangents.length > 1) {
  4969. t = tangents[1];
  4970. p = points[pi];
  4971. pi++;
  4972. path += "C" + (p0.left + t0.x) + "," + (p0.top + t0.y)
  4973. + "," + (p.left - t.x) + "," + (p.top - t.y)
  4974. + "," + p.left + "," + p.top;
  4975. for (var i = 2; i < tangents.length; i++, pi++) {
  4976. p = points[pi];
  4977. t = tangents[i];
  4978. path += "S" + (p.left - t.x) + "," + (p.top - t.y)
  4979. + "," + p.left + "," + p.top;
  4980. }
  4981. }
  4982. if (quad) {
  4983. var lp = points[pi];
  4984. path += "Q" + (p.left + t.x * 2 / 3) + "," + (p.top + t.y * 2 / 3) + ","
  4985. + lp.left + "," + lp.top;
  4986. }
  4987. return path;
  4988. };
  4989. /**
  4990. * @private Interpolates the given points with respective tangents using the
  4991. * cubic Hermite spline interpolation. Returns an array of path strings.
  4992. *
  4993. * @param points the array of points.
  4994. * @param tangents the array of tangent vectors.
  4995. */
  4996. pv.SvgScene.curveHermiteSegments = function(points, tangents) {
  4997. if (tangents.length < 1
  4998. || (points.length != tangents.length
  4999. && points.length != tangents.length + 2)) return [];
  5000. var quad = points.length != tangents.length,
  5001. paths = [],
  5002. p0 = points[0],
  5003. p = p0,
  5004. t0 = tangents[0],
  5005. t = t0,
  5006. pi = 1;
  5007. if (quad) {
  5008. p = points[1];
  5009. paths.push("M" + p0.left + "," + p0.top
  5010. + "Q" + (p.left - t.x * 2 / 3) + "," + (p.top - t.y * 2 / 3)
  5011. + "," + p.left + "," + p.top);
  5012. pi = 2;
  5013. }
  5014. for (var i = 1; i < tangents.length; i++, pi++) {
  5015. p0 = p;
  5016. t0 = t;
  5017. p = points[pi];
  5018. t = tangents[i];
  5019. paths.push("M" + p0.left + "," + p0.top
  5020. + "C" + (p0.left + t0.x) + "," + (p0.top + t0.y)
  5021. + "," + (p.left - t.x) + "," + (p.top - t.y)
  5022. + "," + p.left + "," + p.top);
  5023. }
  5024. if (quad) {
  5025. var lp = points[pi];
  5026. paths.push("M" + p.left + "," + p.top
  5027. + "Q" + (p.left + t.x * 2 / 3) + "," + (p.top + t.y * 2 / 3) + ","
  5028. + lp.left + "," + lp.top);
  5029. }
  5030. return paths;
  5031. };
  5032. /**
  5033. * @private Computes the tangents for the given points needed for cardinal
  5034. * spline interpolation. Returns an array of tangent vectors. Note: that for n
  5035. * points only the n-2 well defined tangents are returned.
  5036. *
  5037. * @param points the array of points.
  5038. * @param tension the tension of hte cardinal spline.
  5039. */
  5040. pv.SvgScene.cardinalTangents = function(points, tension) {
  5041. var tangents = [],
  5042. a = (1 - tension) / 2,
  5043. p0 = points[0],
  5044. p1 = points[1],
  5045. p2 = points[2];
  5046. for (var i = 3; i < points.length; i++) {
  5047. tangents.push({x: a * (p2.left - p0.left), y: a * (p2.top - p0.top)});
  5048. p0 = p1;
  5049. p1 = p2;
  5050. p2 = points[i];
  5051. }
  5052. tangents.push({x: a * (p2.left - p0.left), y: a * (p2.top - p0.top)});
  5053. return tangents;
  5054. };
  5055. /**
  5056. * @private Interpolates the given points using cardinal spline interpolation.
  5057. * Returns an SVG path without the leading M instruction to allow path
  5058. * appending.
  5059. *
  5060. * @param points the array of points.
  5061. * @param tension the tension of hte cardinal spline.
  5062. */
  5063. pv.SvgScene.curveCardinal = function(points, tension) {
  5064. if (points.length <= 2) return "";
  5065. return this.curveHermite(points, this.cardinalTangents(points, tension));
  5066. };
  5067. /**
  5068. * @private Interpolates the given points using cardinal spline interpolation.
  5069. * Returns an array of path strings.
  5070. *
  5071. * @param points the array of points.
  5072. * @param tension the tension of hte cardinal spline.
  5073. */
  5074. pv.SvgScene.curveCardinalSegments = function(points, tension) {
  5075. if (points.length <= 2) return "";
  5076. return this.curveHermiteSegments(points, this.cardinalTangents(points, tension));
  5077. };
  5078. /**
  5079. * @private Interpolates the given points using Fritsch-Carlson Monotone cubic
  5080. * Hermite interpolation. Returns an array of tangent vectors.
  5081. *
  5082. * @param points the array of points.
  5083. */
  5084. pv.SvgScene.monotoneTangents = function(points) {
  5085. var tangents = [],
  5086. d = [],
  5087. m = [],
  5088. dx = [],
  5089. k = 0;
  5090. /* Compute the slopes of the secant lines between successive points. */
  5091. for (k = 0; k < points.length-1; k++) {
  5092. d[k] = (points[k+1].top - points[k].top)/(points[k+1].left - points[k].left);
  5093. }
  5094. /* Initialize the tangents at every point as the average of the secants. */
  5095. m[0] = d[0];
  5096. dx[0] = points[1].left - points[0].left;
  5097. for (k = 1; k < points.length - 1; k++) {
  5098. m[k] = (d[k-1]+d[k])/2;
  5099. dx[k] = (points[k+1].left - points[k-1].left)/2;
  5100. }
  5101. m[k] = d[k-1];
  5102. dx[k] = (points[k].left - points[k-1].left);
  5103. /* Step 3. Very important, step 3. Yep. Wouldn't miss it. */
  5104. for (k = 0; k < points.length - 1; k++) {
  5105. if (d[k] == 0) {
  5106. m[ k ] = 0;
  5107. m[k+1] = 0;
  5108. }
  5109. }
  5110. /* Step 4 + 5. Out of 5 or more steps. */
  5111. for (k = 0; k < points.length - 1; k++) {
  5112. if ((Math.abs(m[k]) < 1e-5) || (Math.abs(m[k+1]) < 1e-5)) continue;
  5113. var ak = m[k] / d[k],
  5114. bk = m[k + 1] / d[k],
  5115. s = ak * ak + bk * bk; // monotone constant (?)
  5116. if (s > 9) {
  5117. var tk = 3 / Math.sqrt(s);
  5118. m[k] = tk * ak * d[k];
  5119. m[k + 1] = tk * bk * d[k];
  5120. }
  5121. }
  5122. var len;
  5123. for (var i = 0; i < points.length; i++) {
  5124. len = 1 + m[i] * m[i]; // pv.vector(1, m[i]).norm().times(dx[i]/3)
  5125. tangents.push({x: dx[i] / 3 / len, y: m[i] * dx[i] / 3 / len});
  5126. }
  5127. return tangents;
  5128. };
  5129. /**
  5130. * @private Interpolates the given points using Fritsch-Carlson Monotone cubic
  5131. * Hermite interpolation. Returns an SVG path without the leading M instruction
  5132. * to allow path appending.
  5133. *
  5134. * @param points the array of points.
  5135. */
  5136. pv.SvgScene.curveMonotone = function(points) {
  5137. if (points.length <= 2) return "";
  5138. return this.curveHermite(points, this.monotoneTangents(points));
  5139. }
  5140. /**
  5141. * @private Interpolates the given points using Fritsch-Carlson Monotone cubic
  5142. * Hermite interpolation.
  5143. * Returns an array of path strings.
  5144. *
  5145. * @param points the array of points.
  5146. */
  5147. pv.SvgScene.curveMonotoneSegments = function(points) {
  5148. if (points.length <= 2) return "";
  5149. return this.curveHermiteSegments(points, this.monotoneTangents(points));
  5150. };
  5151. pv.SvgScene.area = function(scenes) {
  5152. var e = scenes.$g.firstChild;
  5153. if (!scenes.length) return e;
  5154. var s = scenes[0];
  5155. /* segmented */
  5156. if (s.segmented) return this.areaSegment(scenes);
  5157. /* visible */
  5158. if (!s.visible) return e;
  5159. var fill = s.fillStyle, stroke = s.strokeStyle;
  5160. if (!fill.opacity && !stroke.opacity) return e;
  5161. /** @private Computes the straight path for the range [i, j]. */
  5162. function path(i, j) {
  5163. var p1 = [], p2 = [];
  5164. for (var k = j; i <= k; i++, j--) {
  5165. var si = scenes[i],
  5166. sj = scenes[j],
  5167. pi = si.left + "," + si.top,
  5168. pj = (sj.left + sj.width) + "," + (sj.top + sj.height);
  5169. /* interpolate */
  5170. if (i < k) {
  5171. var sk = scenes[i + 1], sl = scenes[j - 1];
  5172. switch (s.interpolate) {
  5173. case "step-before": {
  5174. pi += "V" + sk.top;
  5175. pj += "H" + (sl.left + sl.width);
  5176. break;
  5177. }
  5178. case "step-after": {
  5179. pi += "H" + sk.left;
  5180. pj += "V" + (sl.top + sl.height);
  5181. break;
  5182. }
  5183. }
  5184. }
  5185. p1.push(pi);
  5186. p2.push(pj);
  5187. }
  5188. return p1.concat(p2).join("L");
  5189. }
  5190. /** @private Computes the curved path for the range [i, j]. */
  5191. function pathCurve(i, j) {
  5192. var pointsT = [], pointsB = [], pathT, pathB;
  5193. for (var k = j; i <= k; i++, j--) {
  5194. var sj = scenes[j];
  5195. pointsT.push(scenes[i]);
  5196. pointsB.push({left: sj.left + sj.width, top: sj.top + sj.height});
  5197. }
  5198. if (s.interpolate == "basis") {
  5199. pathT = pv.SvgScene.curveBasis(pointsT);
  5200. pathB = pv.SvgScene.curveBasis(pointsB);
  5201. } else if (s.interpolate == "cardinal") {
  5202. pathT = pv.SvgScene.curveCardinal(pointsT, s.tension);
  5203. pathB = pv.SvgScene.curveCardinal(pointsB, s.tension);
  5204. } else { // monotone
  5205. pathT = pv.SvgScene.curveMonotone(pointsT);
  5206. pathB = pv.SvgScene.curveMonotone(pointsB);
  5207. }
  5208. return pointsT[0].left + "," + pointsT[0].top + pathT
  5209. + "L" + pointsB[0].left + "," + pointsB[0].top + pathB;
  5210. }
  5211. /* points */
  5212. var d = [], si, sj;
  5213. for (var i = 0; i < scenes.length; i++) {
  5214. si = scenes[i]; if (!si.width && !si.height) continue;
  5215. for (var j = i + 1; j < scenes.length; j++) {
  5216. sj = scenes[j]; if (!sj.width && !sj.height) break;
  5217. }
  5218. if (i && (s.interpolate != "step-after")) i--;
  5219. if ((j < scenes.length) && (s.interpolate != "step-before")) j++;
  5220. d.push(((j - i > 2
  5221. && (s.interpolate == "basis"
  5222. || s.interpolate == "cardinal"
  5223. || s.interpolate == "monotone"))
  5224. ? pathCurve : path)(i, j - 1));
  5225. i = j - 1;
  5226. }
  5227. if (!d.length) return e;
  5228. e = this.expect(e, "path", {
  5229. "shape-rendering": s.antialias ? null : "crispEdges",
  5230. "pointer-events": s.events,
  5231. "cursor": s.cursor,
  5232. "d": "M" + d.join("ZM") + "Z",
  5233. "fill": fill.color,
  5234. "fill-opacity": fill.opacity || null,
  5235. "stroke": stroke.color,
  5236. "stroke-opacity": stroke.opacity || null,
  5237. "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null
  5238. });
  5239. return this.append(e, scenes, 0);
  5240. };
  5241. pv.SvgScene.areaSegment = function(scenes) {
  5242. var e = scenes.$g.firstChild, s = scenes[0], pathsT, pathsB;
  5243. if (s.interpolate == "basis"
  5244. || s.interpolate == "cardinal"
  5245. || s.interpolate == "monotone") {
  5246. var pointsT = [], pointsB = [];
  5247. for (var i = 0, n = scenes.length; i < n; i++) {
  5248. var sj = scenes[n - i - 1];
  5249. pointsT.push(scenes[i]);
  5250. pointsB.push({left: sj.left + sj.width, top: sj.top + sj.height});
  5251. }
  5252. if (s.interpolate == "basis") {
  5253. pathsT = this.curveBasisSegments(pointsT);
  5254. pathsB = this.curveBasisSegments(pointsB);
  5255. } else if (s.interpolate == "cardinal") {
  5256. pathsT = this.curveCardinalSegments(pointsT, s.tension);
  5257. pathsB = this.curveCardinalSegments(pointsB, s.tension);
  5258. } else { // monotone
  5259. pathsT = this.curveMonotoneSegments(pointsT);
  5260. pathsB = this.curveMonotoneSegments(pointsB);
  5261. }
  5262. }
  5263. for (var i = 0, n = scenes.length - 1; i < n; i++) {
  5264. var s1 = scenes[i], s2 = scenes[i + 1];
  5265. /* visible */
  5266. if (!s1.visible || !s2.visible) continue;
  5267. var fill = s1.fillStyle, stroke = s1.strokeStyle;
  5268. if (!fill.opacity && !stroke.opacity) continue;
  5269. var d;
  5270. if (pathsT) {
  5271. var pathT = pathsT[i],
  5272. pathB = "L" + pathsB[n - i - 1].substr(1);
  5273. d = pathT + pathB + "Z";
  5274. } else {
  5275. /* interpolate */
  5276. var si = s1, sj = s2;
  5277. switch (s1.interpolate) {
  5278. case "step-before": si = s2; break;
  5279. case "step-after": sj = s1; break;
  5280. }
  5281. /* path */
  5282. d = "M" + s1.left + "," + si.top
  5283. + "L" + s2.left + "," + sj.top
  5284. + "L" + (s2.left + s2.width) + "," + (sj.top + sj.height)
  5285. + "L" + (s1.left + s1.width) + "," + (si.top + si.height)
  5286. + "Z";
  5287. }
  5288. e = this.expect(e, "path", {
  5289. "shape-rendering": s1.antialias ? null : "crispEdges",
  5290. "pointer-events": s1.events,
  5291. "cursor": s1.cursor,
  5292. "d": d,
  5293. "fill": fill.color,
  5294. "fill-opacity": fill.opacity || null,
  5295. "stroke": stroke.color,
  5296. "stroke-opacity": stroke.opacity || null,
  5297. "stroke-width": stroke.opacity ? s1.lineWidth / this.scale : null
  5298. });
  5299. e = this.append(e, scenes, i);
  5300. }
  5301. return e;
  5302. };
  5303. pv.SvgScene.bar = function(scenes) {
  5304. var e = scenes.$g.firstChild;
  5305. for (var i = 0; i < scenes.length; i++) {
  5306. var s = scenes[i];
  5307. /* visible */
  5308. if (!s.visible) continue;
  5309. var fill = s.fillStyle, stroke = s.strokeStyle;
  5310. if (!fill.opacity && !stroke.opacity) continue;
  5311. e = this.expect(e, "rect", {
  5312. "shape-rendering": s.antialias ? null : "crispEdges",
  5313. "pointer-events": s.events,
  5314. "cursor": s.cursor,
  5315. "x": s.left,
  5316. "y": s.top,
  5317. "width": Math.max(1E-10, s.width),
  5318. "height": Math.max(1E-10, s.height),
  5319. "fill": fill.color,
  5320. "fill-opacity": fill.opacity || null,
  5321. "stroke": stroke.color,
  5322. "stroke-opacity": stroke.opacity || null,
  5323. "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null
  5324. });
  5325. e = this.append(e, scenes, i);
  5326. }
  5327. return e;
  5328. };
  5329. pv.SvgScene.dot = function(scenes) {
  5330. var e = scenes.$g.firstChild;
  5331. for (var i = 0; i < scenes.length; i++) {
  5332. var s = scenes[i];
  5333. /* visible */
  5334. if (!s.visible) continue;
  5335. var fill = s.fillStyle, stroke = s.strokeStyle;
  5336. if (!fill.opacity && !stroke.opacity) continue;
  5337. /* points */
  5338. var radius = s.radius, path = null;
  5339. switch (s.shape) {
  5340. case "cross": {
  5341. path = "M" + -radius + "," + -radius
  5342. + "L" + radius + "," + radius
  5343. + "M" + radius + "," + -radius
  5344. + "L" + -radius + "," + radius;
  5345. break;
  5346. }
  5347. case "triangle": {
  5348. var h = radius, w = radius * 1.1547; // 2 / Math.sqrt(3)
  5349. path = "M0," + h
  5350. + "L" + w +"," + -h
  5351. + " " + -w + "," + -h
  5352. + "Z";
  5353. break;
  5354. }
  5355. case "diamond": {
  5356. radius *= Math.SQRT2;
  5357. path = "M0," + -radius
  5358. + "L" + radius + ",0"
  5359. + " 0," + radius
  5360. + " " + -radius + ",0"
  5361. + "Z";
  5362. break;
  5363. }
  5364. case "square": {
  5365. path = "M" + -radius + "," + -radius
  5366. + "L" + radius + "," + -radius
  5367. + " " + radius + "," + radius
  5368. + " " + -radius + "," + radius
  5369. + "Z";
  5370. break;
  5371. }
  5372. case "tick": {
  5373. path = "M0,0L0," + -s.size;
  5374. break;
  5375. }
  5376. case "bar": {
  5377. path = "M0," + (s.size / 2) + "L0," + -(s.size / 2);
  5378. break;
  5379. }
  5380. }
  5381. /* Use <circle> for circles, <path> for everything else. */
  5382. var svg = {
  5383. "shape-rendering": s.antialias ? null : "crispEdges",
  5384. "pointer-events": s.events,
  5385. "cursor": s.cursor,
  5386. "fill": fill.color,
  5387. "fill-opacity": fill.opacity || null,
  5388. "stroke": stroke.color,
  5389. "stroke-opacity": stroke.opacity || null,
  5390. "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null
  5391. };
  5392. if (path) {
  5393. svg.transform = "translate(" + s.left + "," + s.top + ")";
  5394. if (s.angle) svg.transform += " rotate(" + 180 * s.angle / Math.PI + ")";
  5395. svg.d = path;
  5396. e = this.expect(e, "path", svg);
  5397. } else {
  5398. svg.cx = s.left;
  5399. svg.cy = s.top;
  5400. svg.r = radius;
  5401. e = this.expect(e, "circle", svg);
  5402. }
  5403. e = this.append(e, scenes, i);
  5404. }
  5405. return e;
  5406. };
  5407. pv.SvgScene.image = function(scenes) {
  5408. var e = scenes.$g.firstChild;
  5409. for (var i = 0; i < scenes.length; i++) {
  5410. var s = scenes[i];
  5411. /* visible */
  5412. if (!s.visible) continue;
  5413. /* fill */
  5414. e = this.fill(e, scenes, i);
  5415. /* image */
  5416. if (s.image) {
  5417. e = this.expect(e, "foreignObject", {
  5418. "cursor": s.cursor,
  5419. "x": s.left,
  5420. "y": s.top,
  5421. "width": s.width,
  5422. "height": s.height
  5423. });
  5424. var c = e.firstChild || e.appendChild(document.createElementNS(this.xhtml, "canvas"));
  5425. c.$scene = {scenes:scenes, index:i};
  5426. c.style.width = s.width;
  5427. c.style.height = s.height;
  5428. c.width = s.imageWidth;
  5429. c.height = s.imageHeight;
  5430. c.getContext("2d").putImageData(s.image, 0, 0);
  5431. } else {
  5432. e = this.expect(e, "image", {
  5433. "preserveAspectRatio": "none",
  5434. "cursor": s.cursor,
  5435. "x": s.left,
  5436. "y": s.top,
  5437. "width": s.width,
  5438. "height": s.height
  5439. });
  5440. e.setAttributeNS(this.xlink, "href", s.url);
  5441. }
  5442. e = this.append(e, scenes, i);
  5443. /* stroke */
  5444. e = this.stroke(e, scenes, i);
  5445. }
  5446. return e;
  5447. };
  5448. pv.SvgScene.label = function(scenes) {
  5449. var e = scenes.$g.firstChild;
  5450. for (var i = 0; i < scenes.length; i++) {
  5451. var s = scenes[i];
  5452. /* visible */
  5453. if (!s.visible) continue;
  5454. var fill = s.textStyle;
  5455. if (!fill.opacity || !s.text) continue;
  5456. /* text-baseline, text-align */
  5457. var x = 0, y = 0, dy = 0, anchor = "start";
  5458. switch (s.textBaseline) {
  5459. case "middle": dy = ".35em"; break;
  5460. case "top": dy = ".71em"; y = s.textMargin; break;
  5461. case "bottom": y = "-" + s.textMargin; break;
  5462. }
  5463. switch (s.textAlign) {
  5464. case "right": anchor = "end"; x = "-" + s.textMargin; break;
  5465. case "center": anchor = "middle"; break;
  5466. case "left": x = s.textMargin; break;
  5467. }
  5468. e = this.expect(e, "text", {
  5469. "pointer-events": s.events,
  5470. "cursor": s.cursor,
  5471. "x": x,
  5472. "y": y,
  5473. "dy": dy,
  5474. "transform": "translate(" + s.left + "," + s.top + ")"
  5475. + (s.textAngle ? " rotate(" + 180 * s.textAngle / Math.PI + ")" : "")
  5476. + (this.scale != 1 ? " scale(" + 1 / this.scale + ")" : ""),
  5477. "fill": fill.color,
  5478. "fill-opacity": fill.opacity || null,
  5479. "text-anchor": anchor
  5480. }, {
  5481. "font": s.font,
  5482. "text-shadow": s.textShadow,
  5483. "text-decoration": s.textDecoration
  5484. });
  5485. if (e.firstChild) e.firstChild.nodeValue = s.text;
  5486. else e.appendChild(document.createTextNode(s.text));
  5487. e = this.append(e, scenes, i);
  5488. }
  5489. return e;
  5490. };
  5491. pv.SvgScene.line = function(scenes) {
  5492. var e = scenes.$g.firstChild;
  5493. if (scenes.length < 2) return e;
  5494. var s = scenes[0];
  5495. /* segmented */
  5496. if (s.segmented) return this.lineSegment(scenes);
  5497. /* visible */
  5498. if (!s.visible) return e;
  5499. var fill = s.fillStyle, stroke = s.strokeStyle;
  5500. if (!fill.opacity && !stroke.opacity) return e;
  5501. /* points */
  5502. var d = "M" + s.left + "," + s.top;
  5503. if (scenes.length > 2 && (s.interpolate == "basis" || s.interpolate == "cardinal" || s.interpolate == "monotone")) {
  5504. switch (s.interpolate) {
  5505. case "basis": d += this.curveBasis(scenes); break;
  5506. case "cardinal": d += this.curveCardinal(scenes, s.tension); break;
  5507. case "monotone": d += this.curveMonotone(scenes); break;
  5508. }
  5509. } else {
  5510. for (var i = 1; i < scenes.length; i++) {
  5511. d += this.pathSegment(scenes[i - 1], scenes[i]);
  5512. }
  5513. }
  5514. e = this.expect(e, "path", {
  5515. "shape-rendering": s.antialias ? null : "crispEdges",
  5516. "pointer-events": s.events,
  5517. "cursor": s.cursor,
  5518. "d": d,
  5519. "fill": fill.color,
  5520. "fill-opacity": fill.opacity || null,
  5521. "stroke": stroke.color,
  5522. "stroke-opacity": stroke.opacity || null,
  5523. "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null,
  5524. "stroke-linejoin": s.lineJoin
  5525. });
  5526. return this.append(e, scenes, 0);
  5527. };
  5528. pv.SvgScene.lineSegment = function(scenes) {
  5529. var e = scenes.$g.firstChild;
  5530. var s = scenes[0];
  5531. var paths;
  5532. switch (s.interpolate) {
  5533. case "basis": paths = this.curveBasisSegments(scenes); break;
  5534. case "cardinal": paths = this.curveCardinalSegments(scenes, s.tension); break;
  5535. case "monotone": paths = this.curveMonotoneSegments(scenes); break;
  5536. }
  5537. for (var i = 0, n = scenes.length - 1; i < n; i++) {
  5538. var s1 = scenes[i], s2 = scenes[i + 1];
  5539. /* visible */
  5540. if (!s1.visible || !s2.visible) continue;
  5541. var stroke = s1.strokeStyle, fill = pv.Color.transparent;
  5542. if (!stroke.opacity) continue;
  5543. /* interpolate */
  5544. var d;
  5545. if ((s1.interpolate == "linear") && (s1.lineJoin == "miter")) {
  5546. fill = stroke;
  5547. stroke = pv.Color.transparent;
  5548. d = this.pathJoin(scenes[i - 1], s1, s2, scenes[i + 2]);
  5549. } else if(paths) {
  5550. d = paths[i];
  5551. } else {
  5552. d = "M" + s1.left + "," + s1.top + this.pathSegment(s1, s2);
  5553. }
  5554. e = this.expect(e, "path", {
  5555. "shape-rendering": s1.antialias ? null : "crispEdges",
  5556. "pointer-events": s1.events,
  5557. "cursor": s1.cursor,
  5558. "d": d,
  5559. "fill": fill.color,
  5560. "fill-opacity": fill.opacity || null,
  5561. "stroke": stroke.color,
  5562. "stroke-opacity": stroke.opacity || null,
  5563. "stroke-width": stroke.opacity ? s1.lineWidth / this.scale : null,
  5564. "stroke-linejoin": s1.lineJoin
  5565. });
  5566. e = this.append(e, scenes, i);
  5567. }
  5568. return e;
  5569. };
  5570. /** @private Returns the path segment for the specified points. */
  5571. pv.SvgScene.pathSegment = function(s1, s2) {
  5572. var l = 1; // sweep-flag
  5573. switch (s1.interpolate) {
  5574. case "polar-reverse":
  5575. l = 0;
  5576. case "polar": {
  5577. var dx = s2.left - s1.left,
  5578. dy = s2.top - s1.top,
  5579. e = 1 - s1.eccentricity,
  5580. r = Math.sqrt(dx * dx + dy * dy) / (2 * e);
  5581. if ((e <= 0) || (e > 1)) break; // draw a straight line
  5582. return "A" + r + "," + r + " 0 0," + l + " " + s2.left + "," + s2.top;
  5583. }
  5584. case "step-before": return "V" + s2.top + "H" + s2.left;
  5585. case "step-after": return "H" + s2.left + "V" + s2.top;
  5586. }
  5587. return "L" + s2.left + "," + s2.top;
  5588. };
  5589. /** @private Line-line intersection, per Akenine-Moller 16.16.1. */
  5590. pv.SvgScene.lineIntersect = function(o1, d1, o2, d2) {
  5591. return o1.plus(d1.times(o2.minus(o1).dot(d2.perp()) / d1.dot(d2.perp())));
  5592. }
  5593. /** @private Returns the miter join path for the specified points. */
  5594. pv.SvgScene.pathJoin = function(s0, s1, s2, s3) {
  5595. /*
  5596. * P1-P2 is the current line segment. V is a vector that is perpendicular to
  5597. * the line segment, and has length lineWidth / 2. ABCD forms the initial
  5598. * bounding box of the line segment (i.e., the line segment if we were to do
  5599. * no joins).
  5600. */
  5601. var p1 = pv.vector(s1.left, s1.top),
  5602. p2 = pv.vector(s2.left, s2.top),
  5603. p = p2.minus(p1),
  5604. v = p.perp().norm(),
  5605. w = v.times(s1.lineWidth / (2 * this.scale)),
  5606. a = p1.plus(w),
  5607. b = p2.plus(w),
  5608. c = p2.minus(w),
  5609. d = p1.minus(w);
  5610. /*
  5611. * Start join. P0 is the previous line segment's start point. We define the
  5612. * cutting plane as the average of the vector perpendicular to P0-P1, and
  5613. * the vector perpendicular to P1-P2. This insures that the cross-section of
  5614. * the line on the cutting plane is equal if the line-width is unchanged.
  5615. * Note that we don't implement miter limits, so these can get wild.
  5616. */
  5617. if (s0 && s0.visible) {
  5618. var v1 = p1.minus(s0.left, s0.top).perp().norm().plus(v);
  5619. d = this.lineIntersect(p1, v1, d, p);
  5620. a = this.lineIntersect(p1, v1, a, p);
  5621. }
  5622. /* Similarly, for end join. */
  5623. if (s3 && s3.visible) {
  5624. var v2 = pv.vector(s3.left, s3.top).minus(p2).perp().norm().plus(v);
  5625. c = this.lineIntersect(p2, v2, c, p);
  5626. b = this.lineIntersect(p2, v2, b, p);
  5627. }
  5628. return "M" + a.x + "," + a.y
  5629. + "L" + b.x + "," + b.y
  5630. + " " + c.x + "," + c.y
  5631. + " " + d.x + "," + d.y;
  5632. };
  5633. pv.SvgScene.panel = function(scenes) {
  5634. var g = scenes.$g, e = g && g.firstChild;
  5635. for (var i = 0; i < scenes.length; i++) {
  5636. var s = scenes[i];
  5637. /* visible */
  5638. if (!s.visible) continue;
  5639. /* svg */
  5640. if (!scenes.parent) {
  5641. s.canvas.style.display = "inline-block";
  5642. if (g && (g.parentNode != s.canvas)) {
  5643. g = s.canvas.firstChild;
  5644. e = g && g.firstChild;
  5645. }
  5646. if (!g) {
  5647. g = s.canvas.appendChild(this.create("svg"));
  5648. g.setAttribute("font-size", "10px");
  5649. g.setAttribute("font-family", "sans-serif");
  5650. g.setAttribute("fill", "none");
  5651. g.setAttribute("stroke", "none");
  5652. g.setAttribute("stroke-width", 1.5);
  5653. for (var j = 0; j < this.events.length; j++) {
  5654. g.addEventListener(this.events[j], this.dispatch, false);
  5655. }
  5656. e = g.firstChild;
  5657. }
  5658. scenes.$g = g;
  5659. g.setAttribute("width", s.width + s.left + s.right);
  5660. g.setAttribute("height", s.height + s.top + s.bottom);
  5661. }
  5662. /* clip (nest children) */
  5663. if (s.overflow == "hidden") {
  5664. var id = pv.id().toString(36),
  5665. c = this.expect(e, "g", {"clip-path": "url(#" + id + ")"});
  5666. if (!c.parentNode) g.appendChild(c);
  5667. scenes.$g = g = c;
  5668. e = c.firstChild;
  5669. e = this.expect(e, "clipPath", {"id": id});
  5670. var r = e.firstChild || e.appendChild(this.create("rect"));
  5671. r.setAttribute("x", s.left);
  5672. r.setAttribute("y", s.top);
  5673. r.setAttribute("width", s.width);
  5674. r.setAttribute("height", s.height);
  5675. if (!e.parentNode) g.appendChild(e);
  5676. e = e.nextSibling;
  5677. }
  5678. /* fill */
  5679. e = this.fill(e, scenes, i);
  5680. /* transform (push) */
  5681. var k = this.scale,
  5682. t = s.transform,
  5683. x = s.left + t.x,
  5684. y = s.top + t.y;
  5685. this.scale *= t.k;
  5686. /* children */
  5687. for (var j = 0; j < s.children.length; j++) {
  5688. s.children[j].$g = e = this.expect(e, "g", {
  5689. "transform": "translate(" + x + "," + y + ")"
  5690. + (t.k != 1 ? " scale(" + t.k + ")" : "")
  5691. });
  5692. this.updateAll(s.children[j]);
  5693. if (!e.parentNode) g.appendChild(e);
  5694. e = e.nextSibling;
  5695. }
  5696. /* transform (pop) */
  5697. this.scale = k;
  5698. /* stroke */
  5699. e = this.stroke(e, scenes, i);
  5700. /* clip (restore group) */
  5701. if (s.overflow == "hidden") {
  5702. scenes.$g = g = c.parentNode;
  5703. e = c.nextSibling;
  5704. }
  5705. }
  5706. return e;
  5707. };
  5708. pv.SvgScene.fill = function(e, scenes, i) {
  5709. var s = scenes[i], fill = s.fillStyle;
  5710. if (fill.opacity || s.events == "all") {
  5711. e = this.expect(e, "rect", {
  5712. "shape-rendering": s.antialias ? null : "crispEdges",
  5713. "pointer-events": s.events,
  5714. "cursor": s.cursor,
  5715. "x": s.left,
  5716. "y": s.top,
  5717. "width": s.width,
  5718. "height": s.height,
  5719. "fill": fill.color,
  5720. "fill-opacity": fill.opacity,
  5721. "stroke": null
  5722. });
  5723. e = this.append(e, scenes, i);
  5724. }
  5725. return e;
  5726. };
  5727. pv.SvgScene.stroke = function(e, scenes, i) {
  5728. var s = scenes[i], stroke = s.strokeStyle;
  5729. if (stroke.opacity || s.events == "all") {
  5730. e = this.expect(e, "rect", {
  5731. "shape-rendering": s.antialias ? null : "crispEdges",
  5732. "pointer-events": s.events == "all" ? "stroke" : s.events,
  5733. "cursor": s.cursor,
  5734. "x": s.left,
  5735. "y": s.top,
  5736. "width": Math.max(1E-10, s.width),
  5737. "height": Math.max(1E-10, s.height),
  5738. "fill": null,
  5739. "stroke": stroke.color,
  5740. "stroke-opacity": stroke.opacity,
  5741. "stroke-width": s.lineWidth / this.scale
  5742. });
  5743. e = this.append(e, scenes, i);
  5744. }
  5745. return e;
  5746. };
  5747. pv.SvgScene.rule = function(scenes) {
  5748. var e = scenes.$g.firstChild;
  5749. for (var i = 0; i < scenes.length; i++) {
  5750. var s = scenes[i];
  5751. /* visible */
  5752. if (!s.visible) continue;
  5753. var stroke = s.strokeStyle;
  5754. if (!stroke.opacity) continue;
  5755. e = this.expect(e, "line", {
  5756. "shape-rendering": s.antialias ? null : "crispEdges",
  5757. "pointer-events": s.events,
  5758. "cursor": s.cursor,
  5759. "x1": s.left,
  5760. "y1": s.top,
  5761. "x2": s.left + s.width,
  5762. "y2": s.top + s.height,
  5763. "stroke": stroke.color,
  5764. "stroke-opacity": stroke.opacity,
  5765. "stroke-width": s.lineWidth / this.scale
  5766. });
  5767. e = this.append(e, scenes, i);
  5768. }
  5769. return e;
  5770. };
  5771. pv.SvgScene.wedge = function(scenes) {
  5772. var e = scenes.$g.firstChild;
  5773. for (var i = 0; i < scenes.length; i++) {
  5774. var s = scenes[i];
  5775. /* visible */
  5776. if (!s.visible) continue;
  5777. var fill = s.fillStyle, stroke = s.strokeStyle;
  5778. if (!fill.opacity && !stroke.opacity) continue;
  5779. /* points */
  5780. var r1 = s.innerRadius, r2 = s.outerRadius, a = Math.abs(s.angle), p;
  5781. if (a >= 2 * Math.PI) {
  5782. if (r1) {
  5783. p = "M0," + r2
  5784. + "A" + r2 + "," + r2 + " 0 1,1 0," + (-r2)
  5785. + "A" + r2 + "," + r2 + " 0 1,1 0," + r2
  5786. + "M0," + r1
  5787. + "A" + r1 + "," + r1 + " 0 1,1 0," + (-r1)
  5788. + "A" + r1 + "," + r1 + " 0 1,1 0," + r1
  5789. + "Z";
  5790. } else {
  5791. p = "M0," + r2
  5792. + "A" + r2 + "," + r2 + " 0 1,1 0," + (-r2)
  5793. + "A" + r2 + "," + r2 + " 0 1,1 0," + r2
  5794. + "Z";
  5795. }
  5796. } else {
  5797. var sa = Math.min(s.startAngle, s.endAngle),
  5798. ea = Math.max(s.startAngle, s.endAngle),
  5799. c1 = Math.cos(sa), c2 = Math.cos(ea),
  5800. s1 = Math.sin(sa), s2 = Math.sin(ea);
  5801. if (r1) {
  5802. p = "M" + r2 * c1 + "," + r2 * s1
  5803. + "A" + r2 + "," + r2 + " 0 "
  5804. + ((a < Math.PI) ? "0" : "1") + ",1 "
  5805. + r2 * c2 + "," + r2 * s2
  5806. + "L" + r1 * c2 + "," + r1 * s2
  5807. + "A" + r1 + "," + r1 + " 0 "
  5808. + ((a < Math.PI) ? "0" : "1") + ",0 "
  5809. + r1 * c1 + "," + r1 * s1 + "Z";
  5810. } else {
  5811. p = "M" + r2 * c1 + "," + r2 * s1
  5812. + "A" + r2 + "," + r2 + " 0 "
  5813. + ((a < Math.PI) ? "0" : "1") + ",1 "
  5814. + r2 * c2 + "," + r2 * s2 + "L0,0Z";
  5815. }
  5816. }
  5817. e = this.expect(e, "path", {
  5818. "shape-rendering": s.antialias ? null : "crispEdges",
  5819. "pointer-events": s.events,
  5820. "cursor": s.cursor,
  5821. "transform": "translate(" + s.left + "," + s.top + ")",
  5822. "d": p,
  5823. "fill": fill.color,
  5824. "fill-rule": "evenodd",
  5825. "fill-opacity": fill.opacity || null,
  5826. "stroke": stroke.color,
  5827. "stroke-opacity": stroke.opacity || null,
  5828. "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null
  5829. });
  5830. e = this.append(e, scenes, i);
  5831. }
  5832. return e;
  5833. };
  5834. /**
  5835. *
  5836. */
  5837. pv.VmlScene = {
  5838. events: [
  5839. "onmousedown",
  5840. "onmouseup",
  5841. "onclick",
  5842. "ondblclick",
  5843. "onmousewheel",
  5844. "onmouseover",
  5845. "onmouseout",
  5846. "onmousemove"
  5847. ]
  5848. };
  5849. /**
  5850. *
  5851. */
  5852. pv.VmlScene.init = function() {
  5853. ["group", "shape", "path", "fill", "stroke",
  5854. "polyline", "rect", "image", "textpath", "line"].forEach(function(e) {
  5855. document.createStyleSheet().addRule("v\\:" + e, "behavior:url(#default#VML);display:inline-block");
  5856. });
  5857. document.namespaces.add("v", "urn:schemas-microsoft-com:vml");
  5858. this.init = function() {};
  5859. };
  5860. /**
  5861. *
  5862. */
  5863. pv.VmlScene.create = function(type, parent) {
  5864. this.init();
  5865. var e = document.createElement(type);
  5866. if (parent)
  5867. parent.appendChild(e);
  5868. return e;
  5869. };
  5870. /**
  5871. * Expects the element <i>e</i> to be the specified type. If the element does
  5872. * not exist, a new one is created. If the element does exist but is the wrong
  5873. * type, it is replaced with the specified element.
  5874. *
  5875. * @param type {string} a VML element type, such as "v:group".
  5876. * @return a new VML element.
  5877. */
  5878. pv.VmlScene.expect = function(type, e, parent) {
  5879. if (!e) return this.create(type, parent);
  5880. if ("v:" + e.tagName == type) return e;
  5881. var n = this.create(type);
  5882. e.parentNode.replaceChild(n, e);
  5883. return n;
  5884. };
  5885. /** TODO */
  5886. pv.VmlScene.append = function(e, scenes, index) {
  5887. e.$scene = {scenes:scenes, index:index};
  5888. if (!e.parentNode || e.parentNode.nodeType == 11)
  5889. scenes.$g.appendChild(e);
  5890. return e.nextSibling;
  5891. };
  5892. /** TODO */
  5893. pv.VmlScene.dispatch = pv.listener(function(e) {
  5894. var t = e.srcElement.$scene;
  5895. if (t) {
  5896. var type = e.type;
  5897. /* TODO: mousewheel fixes? */
  5898. if (pv.Mark.dispatch(type, t.scenes, t.index)) e.returnValue = false;
  5899. }
  5900. });
  5901. pv.VmlScene.area = function(scenes) {
  5902. var e = scenes.$g.firstChild;
  5903. if (!scenes.length) return e;
  5904. var s = scenes[0];
  5905. /* segmented */
  5906. if (s.segmented) return this.areaSegment(scenes);
  5907. /* visible */
  5908. if (!s.visible) return e;
  5909. var fill = s.fillStyle, stroke = s.strokeStyle;
  5910. if (!fill.opacity && !stroke.opacity) return e;
  5911. /* points */
  5912. var p = "";
  5913. for (var i = 0; i < scenes.length; i++) {
  5914. var si = scenes[i];
  5915. p += si.left + "," + si.top + " ";
  5916. }
  5917. for (var j = scenes.length - 1; j >= 0; j--) {
  5918. var sj = scenes[j];
  5919. p += (sj.left + sj.width) + "," + (sj.top + sj.height) + " ";
  5920. }
  5921. e = this.expect("v:polyline", e);
  5922. var vml = {root: e};
  5923. vml.fill = this.expect("v:fill", vml.root.firstChild, vml.root);
  5924. vml.stroke = this.expect("v:stroke", vml.fill.nextSibling, vml.root);
  5925. /* polygon */
  5926. vml.root.style.cursor = s.cursor;
  5927. vml.root.style.antialias = s.antialias;
  5928. vml.root.title = s.title || "";
  5929. // Changing the 'points' attribute produces the error "Object doesn't
  5930. // support this property or method" when the node is in the DOM. Remove
  5931. // it; we'll add it back at the end.
  5932. if (vml.root.parentNode)
  5933. vml.root.parentNode.removeChild(vml.root);
  5934. vml.root.points = p;
  5935. vml.fill.color = fill.color;
  5936. vml.fill.opacity = fill.opacity;
  5937. vml.stroke.color = stroke.color;
  5938. vml.stroke.opacity = stroke.opacity * Math.min(s.lineWidth, 1);
  5939. vml.stroke.weight = s.lineWidth + "px";
  5940. return this.append(e, scenes, 0);
  5941. };
  5942. pv.VmlScene.bar = function(scenes) {
  5943. var e = scenes.$g.firstChild;
  5944. for (var i = 0; i < scenes.length; i++) {
  5945. var s = scenes[i];
  5946. /* visible */
  5947. if (!s.visible) continue;
  5948. var fill = s.fillStyle, stroke = s.strokeStyle;
  5949. if (!fill.opacity && !stroke.opacity) continue;
  5950. e = this.expect("v:rect", e);
  5951. var vml = {root: e};
  5952. vml.fill = this.expect("v:fill", vml.root.firstChild, vml.root);
  5953. vml.stroke = this.expect("v:stroke", vml.fill.nextSibling, vml.root);
  5954. vml.root.style.left = s.left;
  5955. vml.root.style.top = s.top;
  5956. vml.root.style.width = s.width;
  5957. vml.root.style.height = s.height;
  5958. vml.root.style.cursor = s.cursor;
  5959. vml.root.style.antialias = s.antialias;
  5960. vml.root.title = s.title || "";
  5961. vml.fill.color = fill.color;
  5962. vml.fill.opacity = fill.opacity;
  5963. vml.stroke.color = stroke.color;
  5964. vml.stroke.opacity = stroke.opacity * Math.min(s.lineWidth, 1);
  5965. vml.stroke.weight = s.lineWidth + "px";
  5966. e = this.append(e, scenes, i);
  5967. }
  5968. return e;
  5969. };
  5970. pv.VmlScene.dot = function(scenes) {
  5971. var e = scenes.$g.firstChild;
  5972. for (var i = 0; i < scenes.length; i++) {
  5973. var s = scenes[i];
  5974. /* visible */
  5975. if (!s.visible) continue;
  5976. var fill = s.fillStyle, stroke = s.strokeStyle;
  5977. if (!fill.opacity && !stroke.opacity) continue;
  5978. /* points */
  5979. var radius = Math.round(s.radius);
  5980. var d;
  5981. switch (s.shape) {
  5982. case "cross": {
  5983. d = "m" + -radius + "," + -radius
  5984. + "l" + radius + "," + radius
  5985. + "m" + radius + "," + -radius
  5986. + "l" + -radius + "," + radius;
  5987. break;
  5988. }
  5989. case "triangle": {
  5990. var h = radius, w = Math.round(radius * 1.1547); // 2 / Math.sqrt(3)
  5991. d = "m0," + h
  5992. + "l" + w +"," + -h
  5993. + " " + -w + "," + -h
  5994. + "x";
  5995. break;
  5996. }
  5997. case "diamond": {
  5998. radius = Math.round(radius * 1.414214); // Math.sqrt(2)
  5999. d = "m0," + -radius
  6000. + "l" + radius + ",0"
  6001. + " 0," + radius
  6002. + " " + -radius + ",0"
  6003. + "x";
  6004. break;
  6005. }
  6006. case "square": {
  6007. d = "m" + -radius + "," + -radius
  6008. + "l" + radius + "," + -radius
  6009. + " " + radius + "," + radius
  6010. + " " + -radius + "," + radius
  6011. + "x";
  6012. break;
  6013. }
  6014. case "tick": {
  6015. d = "m0,0l0," + -Math.round(s.size);
  6016. break;
  6017. }
  6018. default: { // circle
  6019. d = "ar-" + radius + ",-" + radius + "," + radius + "," + radius + ",0,0,0,0x";
  6020. break;
  6021. }
  6022. }
  6023. e = this.expect("v:group", e);
  6024. var vml = {root: e};
  6025. vml.shape = this.expect("v:shape", vml.root.firstChild, vml.root);
  6026. vml.path = this.expect("v:path", vml.shape.firstChild, vml.shape);
  6027. vml.fill = this.expect("v:fill", vml.path.nextSibling, vml.shape);
  6028. vml.stroke = this.expect("v:stroke", vml.fill.nextSibling, vml.shape);
  6029. var parent = scenes.parent[scenes.parentIndex];
  6030. /*
  6031. * This total awkwardness is because VML rotates around what it perceives to
  6032. * be the center axis of the shape. The rotation is applied to a containing
  6033. * group, and then I use offsets to undo the cludge.
  6034. */
  6035. /* root + shape */
  6036. var a = s.angle;
  6037. if (a) {
  6038. var x = s.left, y = s.top;
  6039. vml.shape.style.left = Math.cos(-a) * x - Math.sin(-a) * y;
  6040. vml.shape.style.top = Math.sin(-a) * x + Math.cos(-a) * y;
  6041. vml.root.style.left = -parent.width / 2;
  6042. vml.root.style.top = -parent.height / 2;
  6043. vml.root.style.rotation = 180 * a / Math.PI;
  6044. vml.shape.style.marginLeft = parent.width / 2;
  6045. vml.shape.style.marginTop = parent.height / 2;
  6046. } else {
  6047. vml.root.style.rotation = "";
  6048. vml.shape.style.left = s.left;
  6049. vml.shape.style.top = s.top;
  6050. }
  6051. vml.root.style.width = parent.width;
  6052. vml.root.style.height = parent.height;
  6053. vml.shape.style.width = parent.width;
  6054. vml.shape.style.height = parent.height;
  6055. vml.shape.style.cursor = s.cursor;
  6056. vml.shape.style.antialias = s.antialias;
  6057. vml.shape.title = s.title || "";
  6058. /* path */
  6059. vml.path.v = d;
  6060. vml.fill.color = fill.color;
  6061. vml.fill.opacity = fill.opacity;
  6062. vml.stroke.color = stroke.color;
  6063. vml.stroke.opacity = stroke.opacity * Math.min(s.lineWidth, 1);
  6064. vml.stroke.weight = s.lineWidth + "px";
  6065. e = this.append(e, scenes, i);
  6066. }
  6067. return e;
  6068. };
  6069. pv.VmlScene.image = function(scenes) {
  6070. var e = scenes.$g.firstChild;
  6071. for (var i = 0; i < scenes.length; i++) {
  6072. var s = scenes[i];
  6073. /* visible */
  6074. if (!s.visible) continue;
  6075. e = this.expect("v:image", e);
  6076. var vml = {root: e};
  6077. vml.fill = this.expect("v:fill", vml.root.firstChild, vml.root);
  6078. vml.stroke = this.expect("v:stroke", vml.fill.nextSibling, vml.root);
  6079. vml.root.filled = true;
  6080. vml.root.stroked = true;
  6081. vml.root.style.left = s.left;
  6082. vml.root.style.top = s.top;
  6083. vml.root.style.width = s.width;
  6084. vml.root.style.height = s.height;
  6085. vml.root.style.cursor = s.cursor;
  6086. vml.root.src = s.url;
  6087. vml.root.title = s.title || "";
  6088. var fill = s.fillStyle;
  6089. vml.fill.color = fill.color;
  6090. vml.fill.opacity = fill.opacity;
  6091. var stroke = s.strokeStyle;
  6092. vml.stroke.color = stroke.color;
  6093. vml.stroke.opacity = stroke.opacity * Math.min(s.lineWidth, 1);
  6094. vml.stroke.weight = s.lineWidth + "px";
  6095. e = this.append(e, scenes, i);
  6096. }
  6097. return e;
  6098. };
  6099. /* TODO font-size detection for baseline adjustment */
  6100. pv.VmlScene.label = function(scenes) {
  6101. var e = scenes.$g.firstChild;
  6102. for (var i = 0; i < scenes.length; i++) {
  6103. var s = scenes[i];
  6104. /* visible */
  6105. if (!s.visible) continue;
  6106. var fill = s.textStyle;
  6107. if (!fill.opacity || !s.text) continue;
  6108. /* Create VML elements as needed. */
  6109. e = this.expect("v:shape", e);
  6110. var vml = {root: e};
  6111. vml.path = this.expect("v:path", vml.root.firstChild, vml.root);
  6112. vml.fill = this.expect("v:fill", vml.path.nextSibling, vml.root);
  6113. vml.text = this.expect("v:textpath", vml.fill.nextSibling, vml.root);
  6114. vml.root.filled = true;
  6115. vml.root.stroked = false;
  6116. vml.root.style.width = "100%";
  6117. vml.root.style.height = "100%";
  6118. vml.path.textpathok = true;
  6119. vml.text.on = true;
  6120. var dx = 0, dy = 0, size = 10;
  6121. switch (s.textBaseline) {
  6122. case "top": {
  6123. dx = Math.round(-Math.sin(s.textAngle) * size);
  6124. dy = Math.round(Math.cos(s.textAngle) * size);
  6125. break;
  6126. }
  6127. case "bottom": {
  6128. dx = -Math.round(-Math.sin(s.textAngle) * size);
  6129. dy = -Math.round(Math.cos(s.textAngle) * size);
  6130. break;
  6131. }
  6132. }
  6133. vml.root.style.left = s.left + dx;
  6134. vml.root.style.top = s.top + dy;
  6135. vml.fill.color = fill.color;
  6136. vml.fill.opacity = fill.opacity;
  6137. var x = Math.round(Math.cos(s.textAngle) * 1000),
  6138. y = Math.round(Math.sin(s.textAngle) * 1000),
  6139. mx = Math.round(Math.cos(s.textAngle) * s.textMargin),
  6140. my = Math.round(Math.sin(s.textAngle) * s.textMargin),
  6141. p;
  6142. switch (s.textAlign) {
  6143. case "right": {
  6144. p = "M" + -x + "," + -y + "L" + -mx + "," + -my;
  6145. break;
  6146. }
  6147. case "center": {
  6148. p = "M" + -x + "," + -y + "L" + x + "," + y;
  6149. break;
  6150. }
  6151. default: {
  6152. p = "M" + mx + "," + my + "L" + x + "," + y;
  6153. break;
  6154. }
  6155. }
  6156. vml.path.v = p;
  6157. vml.text.style.font = s.font;
  6158. vml.text.style.color = "black";
  6159. vml.text.style.antialias = s.antialias;
  6160. vml.text.style["alignment-baseline"] = "alphabetic";
  6161. vml.text.style["v-text-align"] = s.textAlign;
  6162. vml.text.string = s.text;
  6163. e = this.append(e, scenes, i);
  6164. }
  6165. return e;
  6166. };
  6167. pv.VmlScene.line = function(scenes) {
  6168. var e = scenes.$g.firstChild;
  6169. if (scenes.length < 2) return e;
  6170. var s = scenes[0];
  6171. /* segmented */
  6172. if (s.segmented) return this.lineSegment(scenes);
  6173. /* visible */
  6174. if (!s.visible) return e;
  6175. var fill = s.fillStyle, stroke = s.strokeStyle;
  6176. if (!fill.opacity && !stroke.opacity) return e;
  6177. /* points */
  6178. var p;
  6179. for (var i = 0; i < scenes.length; i++) {
  6180. var si = scenes[i], x = Math.round(si.left), y = Math.round(si.top);
  6181. if (isNaN(x)) x = 0;
  6182. if (isNaN(y)) y = 0;
  6183. if (!p) p = "m" + x + "," + y + "l";
  6184. else p += x + "," + y + " ";
  6185. }
  6186. e = this.expect("v:shape", e);
  6187. var vml = {root: e};
  6188. vml.path = this.expect("v:path", vml.root.firstChild, vml.root);
  6189. vml.fill = this.expect("v:fill", vml.path.nextSibling, vml.root);
  6190. vml.stroke = this.expect("v:stroke", vml.fill.nextSibling, vml.root);
  6191. /* polygon */
  6192. vml.root.style.width = "100%";
  6193. vml.root.style.height = "100%";
  6194. vml.root.style.cursor = s.cursor;
  6195. vml.root.style.antialias = s.antialias;
  6196. vml.root.title = s.title || "";
  6197. vml.path.v = p;
  6198. vml.fill.color = fill.color;
  6199. vml.fill.opacity = fill.opacity;
  6200. vml.stroke.color = stroke.color;
  6201. vml.stroke.opacity = stroke.opacity * Math.min(s.lineWidth, 1);
  6202. vml.stroke.weight = s.lineWidth + "px";
  6203. return this.append(e, scenes, 0);
  6204. };
  6205. /*
  6206. * TODO
  6207. * - optimize: dirty bit
  6208. * - z-index
  6209. */
  6210. pv.VmlScene.panel = function(scenes) {
  6211. var g = scenes.$g, e = g && g.firstChild;
  6212. for (var i = 0; i < scenes.length; i++) {
  6213. var s = scenes[i];
  6214. /* visible */
  6215. if (!s.visible) continue;
  6216. var width = s.width + s.left + s.right;
  6217. var height = s.height + s.top + s.bottom;
  6218. /* vml */
  6219. if (!scenes.parent) {
  6220. s.canvas.style.position = "relative";
  6221. s.canvas.style.display = "inline-block";
  6222. s.canvas.style.width = width;
  6223. s.canvas.style.height = height;
  6224. s.canvas.style.overflow = "hidden";
  6225. g = s.canvas.firstChild;
  6226. if (!g) {
  6227. s.canvas.appendChild(g = this.create("v:group"));
  6228. /*g.onclick
  6229. = g.onmousedown
  6230. = g.onmouseup
  6231. = g.onmousemove
  6232. = g.onmouseout
  6233. = g.onmouseover
  6234. = pv.VmlScene.dispatch;*/
  6235. for (var j = 0; j < this.events.length; j++) {
  6236. g.attachEvent(this.events[j], this.dispatch);
  6237. }
  6238. }
  6239. scenes.$g = g;
  6240. g.style.position = "relative";
  6241. g.style.width = width;
  6242. g.style.height = height;
  6243. g.coordsize = width + "," + height;
  6244. g.coordorigin = "0 0";
  6245. // Adjust for 1 pixel VML margin that I can find no other way of removing.
  6246. g.style.top = "-1px";
  6247. g.style.left = "-1px";
  6248. if (typeof e == "undefined") e = g.firstChild;
  6249. }
  6250. // v:group doesn't get the full width and height unless it has
  6251. // an inner shape with full width and height.
  6252. e = this.expect("v:rect", e);
  6253. e.style.position = "absolute";
  6254. e.style.width = width;
  6255. e.style.height = height;
  6256. e.stroked = false;
  6257. e.filled = false;
  6258. e = this.append(e, scenes, i);
  6259. /* fill */
  6260. e = this.fill(e, scenes, i);
  6261. /* children */
  6262. for (var j = 0; j < s.children.length; j++) {
  6263. s.children[j].$g = e = this.expect("v:group", e);
  6264. e.style.position = "absolute";
  6265. e.style.width = s.width;
  6266. e.style.height = s.height;
  6267. e.style.left = s.left;
  6268. e.style.top = s.top;
  6269. e.coordsize = s.width + "," + s.height;
  6270. this.updateAll(s.children[j]);
  6271. if (!e.parentNode || e.parentNode.nodeType == 11) g.appendChild(e);
  6272. e = e.nextSibling;
  6273. }
  6274. /* stroke */
  6275. e = this.stroke(e, scenes, i);
  6276. }
  6277. return e;
  6278. };
  6279. pv.VmlScene.fill = function(e, scenes, i) {
  6280. var s = scenes[i], fill = s.fillStyle;
  6281. if (fill.opacity) {
  6282. e = this.expect("v:rect", e);
  6283. e.style.position = "absolute";
  6284. e.style.left = s.left;
  6285. e.style.top = s.top;
  6286. e.style.width = s.width;
  6287. e.style.height = s.height;
  6288. e.style.cursor = s.cursor;
  6289. e.style.antialias = s.antialias;
  6290. e.stroked = false;
  6291. var c = this.expect("v:fill", e.firstChild, e);
  6292. c.color = fill.color;
  6293. c.opacity = fill.opacity;
  6294. e = this.append(e, scenes, i);
  6295. }
  6296. return e;
  6297. };
  6298. pv.VmlScene.stroke = function(e, scenes, i) {
  6299. var s = scenes[i], stroke = s.strokeStyle;
  6300. if (stroke.opacity) {
  6301. e = this.expect("v:rect", e);
  6302. e.style.position = "absolute";
  6303. e.style.left = s.left;
  6304. e.style.top = s.top;
  6305. e.style.width = s.width;
  6306. e.style.height = s.height;
  6307. e.style.cursor = s.cursor;
  6308. e.style.antialias = s.antialias;
  6309. e.filled = false;
  6310. var c = this.expect("v:stroke", e.firstChild, e);
  6311. c.color = stroke.color;
  6312. c.opacity = stroke.opacity * Math.min(s.lineWidth, 1);
  6313. c.weight = s.lineWidth + "px";
  6314. e = this.append(e, scenes, i);
  6315. }
  6316. return e;
  6317. };
  6318. pv.VmlScene.rule = function(scenes) {
  6319. var e = scenes.$g.firstChild;
  6320. for (var i = 0; i < scenes.length; i++) {
  6321. var s = scenes[i];
  6322. /* visible */
  6323. if (!s.visible) continue;
  6324. var stroke = s.strokeStyle;
  6325. if (!stroke.opacity) continue;
  6326. e = this.expect("v:line", e);
  6327. var vml = {root: e};
  6328. vml.stroke = this.expect("v:stroke", vml.root.firstChild, vml.root);
  6329. /* line */
  6330. vml.root.title = s.title;
  6331. vml.root.style.cursor = s.cursor;
  6332. vml.root.style.antialias = s.antialias;
  6333. vml.root.from = s.left + "," + s.top;
  6334. vml.root.to = (s.left + s.width - (s.width == 0 ? 0 : 1)) + "," + (s.top + s.height - (s.height == 0 ? 0 : 1));
  6335. var color = pv.color(s.strokeStyle);
  6336. vml.stroke.color = color.color;
  6337. vml.stroke.opacity = color.opacity * Math.min(s.lineWidth, 1);
  6338. vml.stroke.weight = s.lineWidth + "px";
  6339. e = this.append(e, scenes, i);
  6340. }
  6341. return e;
  6342. };
  6343. pv.VmlScene.wedge = function(scenes) {
  6344. var e = scenes.$g.firstChild;
  6345. for (var i = 0; i < scenes.length; i++) {
  6346. var s = scenes[i];
  6347. /* visible */
  6348. if (!s.visible) continue;
  6349. var fill = s.fillStyle, stroke = s.strokeStyle;
  6350. if (!fill.opacity && !stroke.opacity) continue;
  6351. /* points */
  6352. var r1 = Math.round(s.innerRadius), r2 = Math.round(s.outerRadius), d;
  6353. if (s.angle >= 2 * Math.PI) {
  6354. if (r1) {
  6355. d = "AE0,0 " + r2 + "," + r2 + " 0 23592960"
  6356. + "AL0,0 " + r1 + "," + r1 + " 0 23592960";
  6357. } else {
  6358. d = "AE0,0 " + r2 + "," + r2 + " 0 23592960";
  6359. }
  6360. } else {
  6361. var sa = Math.round(s.startAngle / Math.PI * 11796480),
  6362. a = Math.round(s.angle / Math.PI * 11796480);
  6363. if (r1) {
  6364. d = "AE 0,0 " + r2 + "," + r2 + " " + -sa + " " + -a
  6365. + " 0,0 " + r1 + "," + r1 + " " + -(sa + a) + " " + a
  6366. + "X";
  6367. } else {
  6368. d = "M0,0"
  6369. + "AE0,0 " + r2 + "," + r2 + " " + -sa + " " + -a
  6370. + "X";
  6371. }
  6372. }
  6373. e = this.expect("v:shape", e);
  6374. var vml = {root: e};
  6375. vml.path = this.expect("v:path", vml.root.firstChild, vml.root);
  6376. vml.fill = this.expect("v:fill", vml.path.nextSibling, vml.root);
  6377. vml.stroke = this.expect("v:stroke", vml.fill.nextSibling, vml.root);
  6378. // vml.root.setAttribute("fill-rule", "evenodd");
  6379. /* path */
  6380. vml.root.style.left = s.left;
  6381. vml.root.style.top = s.top;
  6382. vml.root.style.width = "100%";
  6383. vml.root.style.height = "100%";
  6384. vml.root.style.cursor = s.cursor;
  6385. vml.root.style.antialias = s.antialias;
  6386. vml.root.title = s.title || "";
  6387. vml.fill.color = fill.color;
  6388. vml.fill.opacity = fill.opacity;
  6389. vml.stroke.color = stroke.color;
  6390. vml.stroke.opacity = stroke.opacity * Math.min(s.lineWidth, 1);
  6391. vml.stroke.weight = s.lineWidth + "px";
  6392. vml.path.v = d;
  6393. e = this.append(e, scenes, i);
  6394. }
  6395. return e;
  6396. };
  6397. /**
  6398. * @private
  6399. * @namespace
  6400. */
  6401. pv.Scene = document.implementation.hasFeature(
  6402. "http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")
  6403. ? pv.SvgScene : pv.VmlScene;
  6404. /**
  6405. * Updates the display for the specified array of scene nodes.
  6406. *
  6407. * @param scenes {array} an array of scene nodes.
  6408. */
  6409. pv.Scene.updateAll = function(scenes) {
  6410. if (scenes.length
  6411. && scenes[0].reverse
  6412. && (scenes.type != "line")
  6413. && (scenes.type != "area")) {
  6414. var reversed = pv.extend(scenes);
  6415. for (var i = 0, j = scenes.length - 1; j >= 0; i++, j--) {
  6416. reversed[i] = scenes[j];
  6417. }
  6418. scenes = reversed;
  6419. }
  6420. this.removeSiblings(this[scenes.type](scenes));
  6421. };
  6422. /** @private Remove siblings following element <i>e</i>. */
  6423. pv.Scene.removeSiblings = function(e) {
  6424. while (e) {
  6425. var n = e.nextSibling;
  6426. e.parentNode.removeChild(e);
  6427. e = n;
  6428. }
  6429. };
  6430. /**
  6431. * Constructs a new mark with default properties. Marks, with the exception of
  6432. * the root panel, are not typically constructed directly; instead, they are
  6433. * added to a panel or an existing mark via {@link pv.Mark#add}.
  6434. *
  6435. * @class Represents a data-driven graphical mark. The <tt>Mark</tt> class is
  6436. * the base class for all graphical marks in Protovis; it does not provide any
  6437. * specific rendering functionality, but together with {@link Panel} establishes
  6438. * the core framework.
  6439. *
  6440. * <p>Concrete mark types include familiar visual elements such as bars, lines
  6441. * and labels. Although a bar mark may be used to construct a bar chart, marks
  6442. * know nothing about charts; it is only through their specification and
  6443. * composition that charts are produced. These building blocks permit many
  6444. * combinatorial possibilities.
  6445. *
  6446. * <p>Marks are associated with <b>data</b>: a mark is generated once per
  6447. * associated datum, mapping the datum to visual <b>properties</b> such as
  6448. * position and color. Thus, a single mark specification represents a set of
  6449. * visual elements that share the same data and visual encoding. The type of
  6450. * mark defines the names of properties and their meaning. A property may be
  6451. * static, ignoring the associated datum and returning a constant; or, it may be
  6452. * dynamic, derived from the associated datum or index. Such dynamic encodings
  6453. * can be specified succinctly using anonymous functions. Special properties
  6454. * called event handlers can be registered to add interactivity.
  6455. *
  6456. * <p>Protovis uses <b>inheritance</b> to simplify the specification of related
  6457. * marks: a new mark can be derived from an existing mark, inheriting its
  6458. * properties. The new mark can then override properties to specify new
  6459. * behavior, potentially in terms of the old behavior. In this way, the old mark
  6460. * serves as the <b>prototype</b> for the new mark. Most mark types share the
  6461. * same basic properties for consistency and to facilitate inheritance.
  6462. *
  6463. * <p>The prioritization of redundant properties is as follows:<ol>
  6464. *
  6465. * <li>If the <tt>width</tt> property is not specified (i.e., null), its value
  6466. * is the width of the parent panel, minus this mark's left and right margins;
  6467. * the left and right margins are zero if not specified.
  6468. *
  6469. * <li>Otherwise, if the <tt>right</tt> margin is not specified, its value is
  6470. * the width of the parent panel, minus this mark's width and left margin; the
  6471. * left margin is zero if not specified.
  6472. *
  6473. * <li>Otherwise, if the <tt>left</tt> property is not specified, its value is
  6474. * the width of the parent panel, minus this mark's width and the right margin.
  6475. *
  6476. * </ol>This prioritization is then duplicated for the <tt>height</tt>,
  6477. * <tt>bottom</tt> and <tt>top</tt> properties, respectively.
  6478. *
  6479. * <p>While most properties are <i>variable</i>, some mark types, such as lines
  6480. * and areas, generate a single visual element rather than a distinct visual
  6481. * element per datum. With these marks, some properties may be <b>fixed</b>.
  6482. * Fixed properties can vary per mark, but not <i>per datum</i>! These
  6483. * properties are evaluated solely for the first (0-index) datum, and typically
  6484. * are specified as a constant. However, it is valid to use a function if the
  6485. * property varies between panels or is dynamically generated.
  6486. *
  6487. * <p>See also the <a href="../../api/">Protovis guide</a>.
  6488. */
  6489. pv.Mark = function() {
  6490. /*
  6491. * TYPE 0 constant defs
  6492. * TYPE 1 function defs
  6493. * TYPE 2 constant properties
  6494. * TYPE 3 function properties
  6495. * in order of evaluation!
  6496. */
  6497. this.$properties = [];
  6498. this.$handlers = {};
  6499. };
  6500. /** @private Records which properties are defined on this mark type. */
  6501. pv.Mark.prototype.properties = {};
  6502. /** @private Records the cast function for each property. */
  6503. pv.Mark.cast = {};
  6504. /**
  6505. * @private Defines and registers a property method for the property with the
  6506. * given name. This method should be called on a mark class prototype to define
  6507. * each exposed property. (Note this refers to the JavaScript
  6508. * <tt>prototype</tt>, not the Protovis mark prototype, which is the {@link
  6509. * #proto} field.)
  6510. *
  6511. * <p>The created property method supports several modes of invocation: <ol>
  6512. *
  6513. * <li>If invoked with a <tt>Function</tt> argument, this function is evaluated
  6514. * for each associated datum. The return value of the function is used as the
  6515. * computed property value. The context of the function (<tt>this</tt>) is this
  6516. * mark. The arguments to the function are the associated data of this mark and
  6517. * any enclosing panels. For example, a linear encoding of numerical data to
  6518. * height is specified as
  6519. *
  6520. * <pre>m.height(function(d) d * 100);</pre>
  6521. *
  6522. * The expression <tt>d * 100</tt> will be evaluated for the height property of
  6523. * each mark instance. The return value of the property method (e.g.,
  6524. * <tt>m.height</tt>) is this mark (<tt>m</tt>)).<p>
  6525. *
  6526. * <li>If invoked with a non-function argument, the property is treated as a
  6527. * constant. The return value of the property method (e.g., <tt>m.height</tt>)
  6528. * is this mark.<p>
  6529. *
  6530. * <li>If invoked with no arguments, the computed property value for the current
  6531. * mark instance in the scene graph is returned. This facilitates <i>property
  6532. * chaining</i>, where one mark's properties are defined in terms of another's.
  6533. * For example, to offset a mark's location from its prototype, you might say
  6534. *
  6535. * <pre>m.top(function() this.proto.top() + 10);</pre>
  6536. *
  6537. * Note that the index of the mark being evaluated (in the above example,
  6538. * <tt>this.proto</tt>) is inherited from the <tt>Mark</tt> class and set by
  6539. * this mark. So, if the fifth element's top property is being evaluated, the
  6540. * fifth instance of <tt>this.proto</tt> will similarly be queried for the value
  6541. * of its top property. If the mark being evaluated has a different number of
  6542. * instances, or its data is unrelated, the behavior of this method is
  6543. * undefined. In these cases it may be better to index the <tt>scene</tt>
  6544. * explicitly to specify the exact instance.
  6545. *
  6546. * </ol><p>Property names should follow standard JavaScript method naming
  6547. * conventions, using lowerCamel-style capitalization.
  6548. *
  6549. * <p>In addition to creating the property method, every property is registered
  6550. * in the {@link #properties} map on the <tt>prototype</tt>. Although this is an
  6551. * instance field, it is considered immutable and shared by all instances of a
  6552. * given mark type. The <tt>properties</tt> map can be queried to see if a mark
  6553. * type defines a particular property, such as width or height.
  6554. *
  6555. * @param {string} name the property name.
  6556. * @param {function} [cast] the cast function for this property.
  6557. */
  6558. pv.Mark.prototype.property = function(name, cast) {
  6559. if (!this.hasOwnProperty("properties")) {
  6560. this.properties = pv.extend(this.properties);
  6561. }
  6562. this.properties[name] = true;
  6563. /*
  6564. * Define the setter-getter globally, since the default behavior should be the
  6565. * same for all properties, and since the Protovis inheritance chain is
  6566. * independent of the JavaScript inheritance chain. For example, anchors
  6567. * define a "name" property that is evaluated on derived marks, even though
  6568. * those marks don't normally have a name.
  6569. */
  6570. pv.Mark.prototype.propertyMethod(name, false, pv.Mark.cast[name] = cast);
  6571. return this;
  6572. };
  6573. /**
  6574. * @private Defines a setter-getter for the specified property.
  6575. *
  6576. * <p>If a cast function has been assigned to the specified property name, the
  6577. * property function is wrapped by the cast function, or, if a constant is
  6578. * specified, the constant is immediately cast. Note, however, that if the
  6579. * property value is null, the cast function is not invoked.
  6580. *
  6581. * @param {string} name the property name.
  6582. * @param {boolean} [def] whether is a property or a def.
  6583. * @param {function} [cast] the cast function for this property.
  6584. */
  6585. pv.Mark.prototype.propertyMethod = function(name, def, cast) {
  6586. if (!cast) cast = pv.Mark.cast[name];
  6587. this[name] = function(v) {
  6588. /* If this is a def, use it rather than property. */
  6589. if (def && this.scene) {
  6590. var defs = this.scene.defs;
  6591. if (arguments.length) {
  6592. defs[name] = {
  6593. id: (v == null) ? 0 : pv.id(),
  6594. value: ((v != null) && cast) ? cast(v) : v
  6595. };
  6596. return this;
  6597. }
  6598. return defs[name] ? defs[name].value : null;
  6599. }
  6600. /* If arguments are specified, set the property value. */
  6601. if (arguments.length) {
  6602. var type = !def << 1 | (typeof v == "function");
  6603. this.propertyValue(name, (type & 1 && cast) ? function() {
  6604. var x = v.apply(this, arguments);
  6605. return (x != null) ? cast(x) : null;
  6606. } : (((v != null) && cast) ? cast(v) : v)).type = type;
  6607. return this;
  6608. }
  6609. return this.instance()[name];
  6610. };
  6611. };
  6612. /** @private Sets the value of the property <i>name</i> to <i>v</i>. */
  6613. pv.Mark.prototype.propertyValue = function(name, v) {
  6614. var properties = this.$properties, p = {name: name, id: pv.id(), value: v};
  6615. for (var i = 0; i < properties.length; i++) {
  6616. if (properties[i].name == name) {
  6617. properties.splice(i, 1);
  6618. break;
  6619. }
  6620. }
  6621. properties.push(p);
  6622. return p;
  6623. };
  6624. /* Define all global properties. */
  6625. pv.Mark.prototype
  6626. .property("data")
  6627. .property("visible", Boolean)
  6628. .property("left", Number)
  6629. .property("right", Number)
  6630. .property("top", Number)
  6631. .property("bottom", Number)
  6632. .property("cursor", String)
  6633. .property("title", String)
  6634. .property("reverse", Boolean)
  6635. .property("antialias", Boolean)
  6636. .property("events", String);
  6637. /**
  6638. * The mark type; a lower camelCase name. The type name controls rendering
  6639. * behavior, and unless the rendering engine is extended, must be one of the
  6640. * built-in concrete mark types: area, bar, dot, image, label, line, panel,
  6641. * rule, or wedge.
  6642. *
  6643. * @type string
  6644. * @name pv.Mark.prototype.type
  6645. */
  6646. /**
  6647. * The mark prototype, possibly undefined, from which to inherit property
  6648. * functions. The mark prototype is not necessarily of the same type as this
  6649. * mark. Any properties defined on this mark will override properties inherited
  6650. * either from the prototype or from the type-specific defaults.
  6651. *
  6652. * @type pv.Mark
  6653. * @name pv.Mark.prototype.proto
  6654. */
  6655. /**
  6656. * The enclosing parent panel. The parent panel is generally undefined only for
  6657. * the root panel; however, it is possible to create "offscreen" marks that are
  6658. * used only for inheritance purposes.
  6659. *
  6660. * @type pv.Panel
  6661. * @name pv.Mark.prototype.parent
  6662. */
  6663. /**
  6664. * The child index. -1 if the enclosing parent panel is null; otherwise, the
  6665. * zero-based index of this mark into the parent panel's <tt>children</tt> array.
  6666. *
  6667. * @type number
  6668. */
  6669. pv.Mark.prototype.childIndex = -1;
  6670. /**
  6671. * The mark index. The value of this field depends on which instance (i.e.,
  6672. * which element of the data array) is currently being evaluated. During the
  6673. * build phase, the index is incremented over each datum; when handling events,
  6674. * the index is set to the instance that triggered the event.
  6675. *
  6676. * @type number
  6677. */
  6678. pv.Mark.prototype.index = -1;
  6679. /**
  6680. * The current scale factor, based on any enclosing transforms. The current
  6681. * scale can be used to create scale-independent graphics. For example, to
  6682. * define a dot that has a radius of 10 irrespective of any zooming, say:
  6683. *
  6684. * <pre>dot.radius(function() 10 / this.scale)</pre>
  6685. *
  6686. * Note that the stroke width and font size are defined irrespective of scale
  6687. * (i.e., in screen space) already. Also note that when a transform is applied
  6688. * to a panel, the scale affects only the child marks, not the panel itself.
  6689. *
  6690. * @type number
  6691. * @see pv.Panel#transform
  6692. */
  6693. pv.Mark.prototype.scale = 1;
  6694. /**
  6695. * @private The scene graph. The scene graph is an array of objects; each object
  6696. * (or "node") corresponds to an instance of this mark and an element in the
  6697. * data array. The scene graph can be traversed to lookup previously-evaluated
  6698. * properties.
  6699. *
  6700. * @name pv.Mark.prototype.scene
  6701. */
  6702. /**
  6703. * The root parent panel. This may be undefined for "offscreen" marks that are
  6704. * created for inheritance purposes only.
  6705. *
  6706. * @type pv.Panel
  6707. * @name pv.Mark.prototype.root
  6708. */
  6709. /**
  6710. * The data property; an array of objects. The size of the array determines the
  6711. * number of marks that will be instantiated; each element in the array will be
  6712. * passed to property functions to compute the property values. Typically, the
  6713. * data property is specified as a constant array, such as
  6714. *
  6715. * <pre>m.data([1, 2, 3, 4, 5]);</pre>
  6716. *
  6717. * However, it is perfectly acceptable to define the data property as a
  6718. * function. This function might compute the data dynamically, allowing
  6719. * different data to be used per enclosing panel. For instance, in the stacked
  6720. * area graph example (see {@link #scene}), the data function on the area mark
  6721. * dereferences each series.
  6722. *
  6723. * @type array
  6724. * @name pv.Mark.prototype.data
  6725. */
  6726. /**
  6727. * The visible property; a boolean determining whether or not the mark instance
  6728. * is visible. If a mark instance is not visible, its other properties will not
  6729. * be evaluated. Similarly, for panels no child marks will be rendered.
  6730. *
  6731. * @type boolean
  6732. * @name pv.Mark.prototype.visible
  6733. */
  6734. /**
  6735. * The left margin; the distance, in pixels, between the left edge of the
  6736. * enclosing panel and the left edge of this mark. Note that in some cases this
  6737. * property may be redundant with the right property, or with the conjunction of
  6738. * right and width.
  6739. *
  6740. * @type number
  6741. * @name pv.Mark.prototype.left
  6742. */
  6743. /**
  6744. * The right margin; the distance, in pixels, between the right edge of the
  6745. * enclosing panel and the right edge of this mark. Note that in some cases this
  6746. * property may be redundant with the left property, or with the conjunction of
  6747. * left and width.
  6748. *
  6749. * @type number
  6750. * @name pv.Mark.prototype.right
  6751. */
  6752. /**
  6753. * The top margin; the distance, in pixels, between the top edge of the
  6754. * enclosing panel and the top edge of this mark. Note that in some cases this
  6755. * property may be redundant with the bottom property, or with the conjunction
  6756. * of bottom and height.
  6757. *
  6758. * @type number
  6759. * @name pv.Mark.prototype.top
  6760. */
  6761. /**
  6762. * The bottom margin; the distance, in pixels, between the bottom edge of the
  6763. * enclosing panel and the bottom edge of this mark. Note that in some cases
  6764. * this property may be redundant with the top property, or with the conjunction
  6765. * of top and height.
  6766. *
  6767. * @type number
  6768. * @name pv.Mark.prototype.bottom
  6769. */
  6770. /**
  6771. * The cursor property; corresponds to the CSS cursor property. This is
  6772. * typically used in conjunction with event handlers to indicate interactivity.
  6773. *
  6774. * @type string
  6775. * @name pv.Mark.prototype.cursor
  6776. * @see <a href="http://www.w3.org/TR/CSS2/ui.html#propdef-cursor">CSS2 cursor</a>
  6777. */
  6778. /**
  6779. * The title property; corresponds to the HTML/SVG title property, allowing the
  6780. * general of simple plain text tooltips.
  6781. *
  6782. * @type string
  6783. * @name pv.Mark.prototype.title
  6784. */
  6785. /**
  6786. * The events property; corresponds to the SVG pointer-events property,
  6787. * specifying how the mark should participate in mouse events. The default value
  6788. * is "painted". Supported values are:
  6789. *
  6790. * <p>"painted": The given mark may receive events when the mouse is over a
  6791. * "painted" area. The painted areas are the interior (i.e., fill) of the mark
  6792. * if a 'fillStyle' is specified, and the perimeter (i.e., stroke) of the mark
  6793. * if a 'strokeStyle' is specified.
  6794. *
  6795. * <p>"all": The given mark may receive events when the mouse is over either the
  6796. * interior (i.e., fill) or the perimeter (i.e., stroke) of the mark, regardless
  6797. * of the specified fillStyle and strokeStyle.
  6798. *
  6799. * <p>"none": The given mark may not receive events.
  6800. *
  6801. * @type string
  6802. * @name pv.Mark.prototype.events
  6803. */
  6804. /**
  6805. * The reverse property; a boolean determining whether marks are ordered from
  6806. * front-to-back or back-to-front. SVG does not support explicit z-ordering;
  6807. * shapes are rendered in the order they appear. Thus, by default, marks are
  6808. * rendered in data order. Setting the reverse property to false reverses the
  6809. * order in which they are rendered; however, the properties are still evaluated
  6810. * (i.e., built) in forward order.
  6811. *
  6812. * @type boolean
  6813. * @name pv.Mark.prototype.reverse
  6814. */
  6815. /**
  6816. * Default properties for all mark types. By default, the data array is the
  6817. * parent data as a single-element array; if the data property is not specified,
  6818. * this causes each mark to be instantiated as a singleton with the parents
  6819. * datum. The visible property is true by default, and the reverse property is
  6820. * false.
  6821. *
  6822. * @type pv.Mark
  6823. */
  6824. pv.Mark.prototype.defaults = new pv.Mark()
  6825. .data(function(d) { return [d]; })
  6826. .visible(true)
  6827. .antialias(true)
  6828. .events("painted");
  6829. /**
  6830. * Sets the prototype of this mark to the specified mark. Any properties not
  6831. * defined on this mark may be inherited from the specified prototype mark, or
  6832. * its prototype, and so on. The prototype mark need not be the same type of
  6833. * mark as this mark. (Note that for inheritance to be useful, properties with
  6834. * the same name on different mark types should have equivalent meaning.)
  6835. *
  6836. * @param {pv.Mark} proto the new prototype.
  6837. * @returns {pv.Mark} this mark.
  6838. * @see #add
  6839. */
  6840. pv.Mark.prototype.extend = function(proto) {
  6841. this.proto = proto;
  6842. return this;
  6843. };
  6844. /**
  6845. * Adds a new mark of the specified type to the enclosing parent panel, whilst
  6846. * simultaneously setting the prototype of the new mark to be this mark.
  6847. *
  6848. * @param {function} type the type of mark to add; a constructor, such as
  6849. * <tt>pv.Bar</tt>.
  6850. * @returns {pv.Mark} the new mark.
  6851. * @see #extend
  6852. */
  6853. pv.Mark.prototype.add = function(type) {
  6854. return this.parent.add(type).extend(this);
  6855. };
  6856. /**
  6857. * Defines a custom property on this mark. Custom properties are currently
  6858. * fixed, in that they are initialized once per mark set (i.e., per parent panel
  6859. * instance). Custom properties can be used to store local state for the mark,
  6860. * such as data needed by other properties (e.g., a custom scale) or interaction
  6861. * state.
  6862. *
  6863. * <p>WARNING We plan on changing this feature in a future release to define
  6864. * standard properties, as opposed to <i>fixed</i> properties that behave
  6865. * idiosyncratically within event handlers. Furthermore, we recommend storing
  6866. * state in an external data structure, rather than tying it to the
  6867. * visualization specification as with defs.
  6868. *
  6869. * @param {string} name the name of the local variable.
  6870. * @param {function} [v] an optional initializer; may be a constant or a
  6871. * function.
  6872. */
  6873. pv.Mark.prototype.def = function(name, v) {
  6874. this.propertyMethod(name, true);
  6875. return this[name](arguments.length > 1 ? v : null);
  6876. };
  6877. /**
  6878. * Returns an anchor with the specified name. All marks support the five
  6879. * standard anchor names:<ul>
  6880. *
  6881. * <li>top
  6882. * <li>left
  6883. * <li>center
  6884. * <li>bottom
  6885. * <li>right
  6886. *
  6887. * </ul>In addition to positioning properties (left, right, top bottom), the
  6888. * anchors support text rendering properties (text-align, text-baseline). Text is
  6889. * rendered to appear inside the mark by default.
  6890. *
  6891. * <p>To facilitate stacking, anchors are defined in terms of their opposite
  6892. * edge. For example, the top anchor defines the bottom property, such that the
  6893. * mark extends upwards; the bottom anchor instead defines the top property,
  6894. * such that the mark extends downwards. See also {@link pv.Layout.Stack}.
  6895. *
  6896. * <p>While anchor names are typically constants, the anchor name is a true
  6897. * property, which means you can specify a function to compute the anchor name
  6898. * dynamically. See the {@link pv.Anchor#name} property for details.
  6899. *
  6900. * @param {string} name the anchor name; either a string or a property function.
  6901. * @returns {pv.Anchor} the new anchor.
  6902. */
  6903. pv.Mark.prototype.anchor = function(name) {
  6904. var target = this, scene;
  6905. /* Default anchor name. */
  6906. if (!name) name = "center";
  6907. /** @private Find the instances of target that match source. */
  6908. function instances(source) {
  6909. var mark = target, index = [];
  6910. /* Mirrored descent. */
  6911. while (!(scene = mark.scene)) {
  6912. source = source.parent;
  6913. index.push({index: source.index, childIndex: mark.childIndex});
  6914. mark = mark.parent;
  6915. }
  6916. while (index.length) {
  6917. var i = index.pop();
  6918. scene = scene[i.index].children[i.childIndex];
  6919. }
  6920. /*
  6921. * When the anchor target is also an ancestor, as in the case of adding
  6922. * to a panel anchor, only generate one instance per panel. Also, set
  6923. * the margins to zero, since they are offset by the enclosing panel.
  6924. */
  6925. if (target.hasOwnProperty("index")) {
  6926. var s = pv.extend(scene[target.index]);
  6927. s.right = s.top = s.left = s.bottom = 0;
  6928. return [s];
  6929. }
  6930. return scene;
  6931. }
  6932. return new pv.Anchor(this)
  6933. .name(name)
  6934. .def("$mark.anchor", function() {
  6935. scene = this.scene.target = instances(this);
  6936. })
  6937. .data(function() {
  6938. return scene.map(function(s) { return s.data; });
  6939. })
  6940. .visible(function() {
  6941. return scene[this.index].visible;
  6942. })
  6943. .left(function() {
  6944. var s = scene[this.index], w = s.width || 0;
  6945. switch (this.name()) {
  6946. case "bottom":
  6947. case "top":
  6948. case "center": return s.left + w / 2;
  6949. case "left": return null;
  6950. }
  6951. return s.left + w;
  6952. })
  6953. .top(function() {
  6954. var s = scene[this.index], h = s.height || 0;
  6955. switch (this.name()) {
  6956. case "left":
  6957. case "right":
  6958. case "center": return s.top + h / 2;
  6959. case "top": return null;
  6960. }
  6961. return s.top + h;
  6962. })
  6963. .right(function() {
  6964. var s = scene[this.index];
  6965. return this.name() == "left" ? s.right + (s.width || 0) : null;
  6966. })
  6967. .bottom(function() {
  6968. var s = scene[this.index];
  6969. return this.name() == "top" ? s.bottom + (s.height || 0) : null;
  6970. })
  6971. .textAlign(function() {
  6972. switch (this.name()) {
  6973. case "bottom":
  6974. case "top":
  6975. case "center": return "center";
  6976. case "right": return "right";
  6977. }
  6978. return "left";
  6979. })
  6980. .textBaseline(function() {
  6981. switch (this.name()) {
  6982. case "right":
  6983. case "left":
  6984. case "center": return "middle";
  6985. case "top": return "top";
  6986. }
  6987. return "bottom";
  6988. });
  6989. };
  6990. /**
  6991. * Returns the anchor target of this mark, if it is derived from an anchor;
  6992. * otherwise returns null. For example, if a label is derived from a bar anchor,
  6993. *
  6994. * <pre>bar.anchor("top").add(pv.Label);</pre>
  6995. *
  6996. * then property functions on the label can refer to the bar via the
  6997. * <tt>anchorTarget</tt> method. This method is also useful for mark types
  6998. * defining properties on custom anchors.
  6999. *
  7000. * @returns {pv.Mark} the anchor target of this mark; possibly null.
  7001. */
  7002. pv.Mark.prototype.anchorTarget = function() {
  7003. return this.proto.anchorTarget();
  7004. };
  7005. /**
  7006. * Alias for setting the left, right, top and bottom properties simultaneously.
  7007. *
  7008. * @see #left
  7009. * @see #right
  7010. * @see #top
  7011. * @see #bottom
  7012. * @returns {pv.Mark} this.
  7013. */
  7014. pv.Mark.prototype.margin = function(n) {
  7015. return this.left(n).right(n).top(n).bottom(n);
  7016. };
  7017. /**
  7018. * @private Returns the current instance of this mark in the scene graph. This
  7019. * is typically equivalent to <tt>this.scene[this.index]</tt>, however if the
  7020. * scene or index is unset, the default instance of the mark is returned. If no
  7021. * default is set, the default is the last instance. Similarly, if the scene or
  7022. * index of the parent panel is unset, the default instance of this mark in the
  7023. * last instance of the enclosing panel is returned, and so on.
  7024. *
  7025. * @returns a node in the scene graph.
  7026. */
  7027. pv.Mark.prototype.instance = function(defaultIndex) {
  7028. var scene = this.scene || this.parent.instance(-1).children[this.childIndex],
  7029. index = !arguments.length || this.hasOwnProperty("index") ? this.index : defaultIndex;
  7030. return scene[index < 0 ? scene.length - 1 : index];
  7031. };
  7032. /**
  7033. * @private Returns the first instance of this mark in the scene graph. This
  7034. * method can only be called when the mark is bound to the scene graph (for
  7035. * example, from an event handler, or within a property function).
  7036. *
  7037. * @returns a node in the scene graph.
  7038. */
  7039. pv.Mark.prototype.first = function() {
  7040. return this.scene[0];
  7041. };
  7042. /**
  7043. * @private Returns the last instance of this mark in the scene graph. This
  7044. * method can only be called when the mark is bound to the scene graph (for
  7045. * example, from an event handler, or within a property function). In addition,
  7046. * note that mark instances are built sequentially, so the last instance of this
  7047. * mark may not yet be constructed.
  7048. *
  7049. * @returns a node in the scene graph.
  7050. */
  7051. pv.Mark.prototype.last = function() {
  7052. return this.scene[this.scene.length - 1];
  7053. };
  7054. /**
  7055. * @private Returns the previous instance of this mark in the scene graph, or
  7056. * null if this is the first instance.
  7057. *
  7058. * @returns a node in the scene graph, or null.
  7059. */
  7060. pv.Mark.prototype.sibling = function() {
  7061. return (this.index == 0) ? null : this.scene[this.index - 1];
  7062. };
  7063. /**
  7064. * @private Returns the current instance in the scene graph of this mark, in the
  7065. * previous instance of the enclosing parent panel. May return null if this
  7066. * instance could not be found.
  7067. *
  7068. * @returns a node in the scene graph, or null.
  7069. */
  7070. pv.Mark.prototype.cousin = function() {
  7071. var p = this.parent, s = p && p.sibling();
  7072. return (s && s.children) ? s.children[this.childIndex][this.index] : null;
  7073. };
  7074. /**
  7075. * Renders this mark, including recursively rendering all child marks if this is
  7076. * a panel. This method finds all instances of this mark and renders them. This
  7077. * method descends recursively to the level of the mark to be rendered, finding
  7078. * all visible instances of the mark. After the marks are rendered, the scene
  7079. * and index attributes are removed from the mark to restore them to a clean
  7080. * state.
  7081. *
  7082. * <p>If an enclosing panel has an index property set (as is the case inside in
  7083. * an event handler), then only instances of this mark inside the given instance
  7084. * of the panel will be rendered; otherwise, all visible instances of the mark
  7085. * will be rendered.
  7086. */
  7087. pv.Mark.prototype.render = function() {
  7088. var parent = this.parent,
  7089. stack = pv.Mark.stack;
  7090. /* For the first render, take it from the top. */
  7091. if (parent && !this.root.scene) {
  7092. this.root.render();
  7093. return;
  7094. }
  7095. /* Record the path to this mark. */
  7096. var indexes = [];
  7097. for (var mark = this; mark.parent; mark = mark.parent) {
  7098. indexes.unshift(mark.childIndex);
  7099. }
  7100. /** @private */
  7101. function render(mark, depth, scale) {
  7102. mark.scale = scale;
  7103. if (depth < indexes.length) {
  7104. stack.unshift(null);
  7105. if (mark.hasOwnProperty("index")) {
  7106. renderInstance(mark, depth, scale);
  7107. } else {
  7108. for (var i = 0, n = mark.scene.length; i < n; i++) {
  7109. mark.index = i;
  7110. renderInstance(mark, depth, scale);
  7111. }
  7112. delete mark.index;
  7113. }
  7114. stack.shift();
  7115. } else {
  7116. mark.build();
  7117. /*
  7118. * In the update phase, the scene is rendered by creating and updating
  7119. * elements and attributes in the SVG image. No properties are evaluated
  7120. * during the update phase; instead the values computed previously in the
  7121. * build phase are simply translated into SVG. The update phase is
  7122. * decoupled (see pv.Scene) to allow different rendering engines.
  7123. */
  7124. pv.Scene.scale = scale;
  7125. pv.Scene.updateAll(mark.scene);
  7126. }
  7127. delete mark.scale;
  7128. }
  7129. /**
  7130. * @private Recursively renders the current instance of the specified mark.
  7131. * This is slightly tricky because `index` and `scene` properties may or may
  7132. * not already be set; if they are set, it means we are rendering only a
  7133. * specific instance; if they are unset, we are rendering all instances.
  7134. * Furthermore, we must preserve the original context of these properties when
  7135. * rendering completes.
  7136. *
  7137. * <p>Another tricky aspect is that the `scene` attribute should be set for
  7138. * any preceding children, so as to allow property chaining. This is
  7139. * consistent with first-pass rendering.
  7140. */
  7141. function renderInstance(mark, depth, scale) {
  7142. var s = mark.scene[mark.index], i;
  7143. if (s.visible) {
  7144. var childIndex = indexes[depth],
  7145. child = mark.children[childIndex];
  7146. /* Set preceding child scenes. */
  7147. for (i = 0; i < childIndex; i++) {
  7148. mark.children[i].scene = s.children[i];
  7149. }
  7150. /* Set current child scene, if necessary. */
  7151. stack[0] = s.data;
  7152. if (child.scene) {
  7153. render(child, depth + 1, scale * s.transform.k);
  7154. } else {
  7155. child.scene = s.children[childIndex];
  7156. render(child, depth + 1, scale * s.transform.k);
  7157. delete child.scene;
  7158. }
  7159. /* Clear preceding child scenes. */
  7160. for (i = 0; i < childIndex; i++) {
  7161. delete mark.children[i].scene;
  7162. }
  7163. }
  7164. }
  7165. /* Bind this mark's property definitions. */
  7166. this.bind();
  7167. /* The render context is the first ancestor with an explicit index. */
  7168. while (parent && !parent.hasOwnProperty("index")) parent = parent.parent;
  7169. /* Recursively render all instances of this mark. */
  7170. this.context(
  7171. parent ? parent.scene : undefined,
  7172. parent ? parent.index : -1,
  7173. function() { render(this.root, 0, 1); });
  7174. };
  7175. /** @private Stores the current data stack. */
  7176. pv.Mark.stack = [];
  7177. /**
  7178. * @private In the bind phase, inherited property definitions are cached so they
  7179. * do not need to be queried during build.
  7180. */
  7181. pv.Mark.prototype.bind = function() {
  7182. var seen = {}, types = [[], [], [], []], data, visible;
  7183. /** Scans the proto chain for the specified mark. */
  7184. function bind(mark) {
  7185. do {
  7186. var properties = mark.$properties;
  7187. for (var i = properties.length - 1; i >= 0 ; i--) {
  7188. var p = properties[i];
  7189. if (!(p.name in seen)) {
  7190. seen[p.name] = p;
  7191. switch (p.name) {
  7192. case "data": data = p; break;
  7193. case "visible": visible = p; break;
  7194. default: types[p.type].push(p); break;
  7195. }
  7196. }
  7197. }
  7198. } while (mark = mark.proto);
  7199. }
  7200. /* Scan the proto chain for all defined properties. */
  7201. bind(this);
  7202. bind(this.defaults);
  7203. types[1].reverse();
  7204. types[3].reverse();
  7205. /* Any undefined properties are null. */
  7206. var mark = this;
  7207. do for (var name in mark.properties) {
  7208. if (!(name in seen)) {
  7209. types[2].push(seen[name] = {name: name, type: 2, value: null});
  7210. }
  7211. } while (mark = mark.proto);
  7212. /* Define setter-getter for inherited defs. */
  7213. var defs = types[0].concat(types[1]);
  7214. for (var i = 0; i < defs.length; i++) {
  7215. this.propertyMethod(defs[i].name, true);
  7216. }
  7217. /* Setup binds to evaluate constants before functions. */
  7218. this.binds = {
  7219. properties: seen,
  7220. data: data,
  7221. defs: defs,
  7222. required: [visible],
  7223. optional: pv.blend(types)
  7224. };
  7225. };
  7226. /**
  7227. * @private Evaluates properties and computes implied properties. Properties are
  7228. * stored in the {@link #scene} array for each instance of this mark.
  7229. *
  7230. * <p>As marks are built recursively, the {@link #index} property is updated to
  7231. * match the current index into the data array for each mark. Note that the
  7232. * index property is only set for the mark currently being built and its
  7233. * enclosing parent panels. The index property for other marks is unset, but is
  7234. * inherited from the global <tt>Mark</tt> class prototype. This allows mark
  7235. * properties to refer to properties on other marks <i>in the same panel</i>
  7236. * conveniently; however, in general it is better to reference mark instances
  7237. * specifically through the scene graph rather than depending on the magical
  7238. * behavior of {@link #index}.
  7239. *
  7240. * <p>The root scene array has a special property, <tt>data</tt>, which stores
  7241. * the current data stack. The first element in this stack is the current datum,
  7242. * followed by the datum of the enclosing parent panel, and so on. The data
  7243. * stack should not be accessed directly; instead, property functions are passed
  7244. * the current data stack as arguments.
  7245. *
  7246. * <p>The evaluation of the <tt>data</tt> and <tt>visible</tt> properties is
  7247. * special. The <tt>data</tt> property is evaluated first; unlike the other
  7248. * properties, the data stack is from the parent panel, rather than the current
  7249. * mark, since the data is not defined until the data property is evaluated.
  7250. * The <tt>visisble</tt> property is subsequently evaluated for each instance;
  7251. * only if true will the {@link #buildInstance} method be called, evaluating
  7252. * other properties and recursively building the scene graph.
  7253. *
  7254. * <p>If this mark is being re-built, any old instances of this mark that no
  7255. * longer exist (because the new data array contains fewer elements) will be
  7256. * cleared using {@link #clearInstance}.
  7257. *
  7258. * @param parent the instance of the parent panel from the scene graph.
  7259. */
  7260. pv.Mark.prototype.build = function() {
  7261. var scene = this.scene, stack = pv.Mark.stack;
  7262. if (!scene) {
  7263. scene = this.scene = [];
  7264. scene.mark = this;
  7265. scene.type = this.type;
  7266. scene.childIndex = this.childIndex;
  7267. if (this.parent) {
  7268. scene.parent = this.parent.scene;
  7269. scene.parentIndex = this.parent.index;
  7270. }
  7271. }
  7272. /* Evaluate defs. */
  7273. if (this.binds.defs.length) {
  7274. var defs = scene.defs;
  7275. if (!defs) scene.defs = defs = {};
  7276. for (var i = 0; i < this.binds.defs.length; i++) {
  7277. var p = this.binds.defs[i], d = defs[p.name];
  7278. if (!d || (p.id > d.id)) {
  7279. defs[p.name] = {
  7280. id: 0, // this def will be re-evaluated on next build
  7281. value: (p.type & 1) ? p.value.apply(this, stack) : p.value
  7282. };
  7283. }
  7284. }
  7285. }
  7286. /* Evaluate special data property. */
  7287. var data = this.binds.data;
  7288. data = data.type & 1 ? data.value.apply(this, stack) : data.value;
  7289. /* Create, update and delete scene nodes. */
  7290. stack.unshift(null);
  7291. scene.length = data.length;
  7292. for (var i = 0; i < data.length; i++) {
  7293. pv.Mark.prototype.index = this.index = i;
  7294. var s = scene[i];
  7295. if (!s) scene[i] = s = {};
  7296. s.data = stack[0] = data[i];
  7297. this.buildInstance(s);
  7298. }
  7299. pv.Mark.prototype.index = -1;
  7300. delete this.index;
  7301. stack.shift();
  7302. return this;
  7303. };
  7304. /**
  7305. * @private Evaluates the specified array of properties for the specified
  7306. * instance <tt>s</tt> in the scene graph.
  7307. *
  7308. * @param s a node in the scene graph; the instance of the mark to build.
  7309. * @param properties an array of properties.
  7310. */
  7311. pv.Mark.prototype.buildProperties = function(s, properties) {
  7312. for (var i = 0, n = properties.length; i < n; i++) {
  7313. var p = properties[i], v = p.value; // assume case 2 (constant)
  7314. switch (p.type) {
  7315. case 0:
  7316. case 1: v = this.scene.defs[p.name].value; break;
  7317. case 3: v = v.apply(this, pv.Mark.stack); break;
  7318. }
  7319. s[p.name] = v;
  7320. }
  7321. };
  7322. /**
  7323. * @private Evaluates all of the properties for this mark for the specified
  7324. * instance <tt>s</tt> in the scene graph. The set of properties to evaluate is
  7325. * retrieved from the {@link #properties} array for this mark type (see {@link
  7326. * #type}). After these properties are evaluated, any <b>implied</b> properties
  7327. * may be computed by the mark and set on the scene graph; see
  7328. * {@link #buildImplied}.
  7329. *
  7330. * <p>For panels, this method recursively builds the scene graph for all child
  7331. * marks as well. In general, this method should not need to be overridden by
  7332. * concrete mark types.
  7333. *
  7334. * @param s a node in the scene graph; the instance of the mark to build.
  7335. */
  7336. pv.Mark.prototype.buildInstance = function(s) {
  7337. this.buildProperties(s, this.binds.required);
  7338. if (s.visible) {
  7339. this.buildProperties(s, this.binds.optional);
  7340. this.buildImplied(s);
  7341. }
  7342. };
  7343. /**
  7344. * @private Computes the implied properties for this mark for the specified
  7345. * instance <tt>s</tt> in the scene graph. Implied properties are those with
  7346. * dependencies on multiple other properties; for example, the width property
  7347. * may be implied if the left and right properties are set. This method can be
  7348. * overridden by concrete mark types to define new implied properties, if
  7349. * necessary.
  7350. *
  7351. * @param s a node in the scene graph; the instance of the mark to build.
  7352. */
  7353. pv.Mark.prototype.buildImplied = function(s) {
  7354. var l = s.left;
  7355. var r = s.right;
  7356. var t = s.top;
  7357. var b = s.bottom;
  7358. /* Assume width and height are zero if not supported by this mark type. */
  7359. var p = this.properties;
  7360. var w = p.width ? s.width : 0;
  7361. var h = p.height ? s.height : 0;
  7362. /* Compute implied width, right and left. */
  7363. var width = this.parent ? this.parent.width() : (w + l + r);
  7364. if (w == null) {
  7365. w = width - (r = r || 0) - (l = l || 0);
  7366. } else if (r == null) {
  7367. r = width - w - (l = l || 0);
  7368. } else if (l == null) {
  7369. l = width - w - (r = r || 0);
  7370. }
  7371. /* Compute implied height, bottom and top. */
  7372. var height = this.parent ? this.parent.height() : (h + t + b);
  7373. if (h == null) {
  7374. h = height - (t = t || 0) - (b = b || 0);
  7375. } else if (b == null) {
  7376. b = height - h - (t = t || 0);
  7377. } else if (t == null) {
  7378. t = height - h - (b = b || 0);
  7379. }
  7380. s.left = l;
  7381. s.right = r;
  7382. s.top = t;
  7383. s.bottom = b;
  7384. /* Only set width and height if they are supported by this mark type. */
  7385. if (p.width) s.width = w;
  7386. if (p.height) s.height = h;
  7387. /* Set any null colors to pv.Color.transparent. */
  7388. if (p.textStyle && !s.textStyle) s.textStyle = pv.Color.transparent;
  7389. if (p.fillStyle && !s.fillStyle) s.fillStyle = pv.Color.transparent;
  7390. if (p.strokeStyle && !s.strokeStyle) s.strokeStyle = pv.Color.transparent;
  7391. };
  7392. /**
  7393. * Returns the current location of the mouse (cursor) relative to this mark's
  7394. * parent. The <i>x</i> coordinate corresponds to the left margin, while the
  7395. * <i>y</i> coordinate corresponds to the top margin.
  7396. *
  7397. * @returns {pv.Vector} the mouse location.
  7398. */
  7399. pv.Mark.prototype.mouse = function() {
  7400. /* Compute xy-coordinates relative to the panel. */
  7401. var x = pv.event.pageX || 0,
  7402. y = pv.event.pageY || 0,
  7403. n = this.root.canvas();
  7404. // Calculate pageX/Y if missing and clientX/Y available
  7405. /*
  7406. CLOUDERA CHANGE:
  7407. Changed x == undefined to !x, which encompasses the x = 0 which is currently the VML value.
  7408. */
  7409. if ( !x && pv.event.clientX != undefined ) {
  7410. var doc = document.documentElement, body = document.body;
  7411. x = pv.event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
  7412. y = pv.event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
  7413. }
  7414. do {
  7415. /* CLOUDERA CHANGE */
  7416. //Accomodate for the possibility that the current offset parent (n) is in a scrolled object.
  7417. x -= (n.offsetLeft - n.scrollLeft);
  7418. y -= (n.offsetTop - n.scrollTop);
  7419. } while (n = n.offsetParent);
  7420. /* Compute the inverse transform of all enclosing panels. */
  7421. var t = pv.Transform.identity,
  7422. p = this.properties.transform ? this : this.parent,
  7423. pz = [];
  7424. do { pz.push(p); } while (p = p.parent);
  7425. while (p = pz.pop()) t = t.translate(p.left(), p.top()).times(p.transform());
  7426. t = t.invert();
  7427. return pv.vector(x * t.k + t.x, y * t.k + t.y);
  7428. };
  7429. /**
  7430. * Registers an event handler for the specified event type with this mark. When
  7431. * an event of the specified type is triggered, the specified handler will be
  7432. * invoked. The handler is invoked in a similar method to property functions:
  7433. * the context is <tt>this</tt> mark instance, and the arguments are the full
  7434. * data stack. Event handlers can use property methods to manipulate the display
  7435. * properties of the mark:
  7436. *
  7437. * <pre>m.event("click", function() this.fillStyle("red"));</pre>
  7438. *
  7439. * Alternatively, the external data can be manipulated and the visualization
  7440. * redrawn:
  7441. *
  7442. * <pre>m.event("click", function(d) {
  7443. * data = all.filter(function(k) k.name == d);
  7444. * vis.render();
  7445. * });</pre>
  7446. *
  7447. * The return value of the event handler determines which mark gets re-rendered.
  7448. * Use defs ({@link #def}) to set temporary state from event handlers.
  7449. *
  7450. * <p>The complete set of event types is defined by SVG; see the reference
  7451. * below. The set of supported event types is:<ul>
  7452. *
  7453. * <li>click
  7454. * <li>mousedown
  7455. * <li>mouseup
  7456. * <li>mouseover
  7457. * <li>mousemove
  7458. * <li>mouseout
  7459. *
  7460. * </ul>Since Protovis does not specify any concept of focus, it does not
  7461. * support key events; these should be handled outside the visualization using
  7462. * standard JavaScript. In the future, support for interaction may be extended
  7463. * to support additional event types, particularly those most relevant to
  7464. * interactive visualization, such as selection.
  7465. *
  7466. * <p>TODO In the current implementation, event handlers are not inherited from
  7467. * prototype marks. They must be defined explicitly on each interactive mark. In
  7468. * addition, only one event handler for a given event type can be defined; when
  7469. * specifying multiple event handlers for the same type, only the last one will
  7470. * be used.
  7471. *
  7472. * @see <a href="http://www.w3.org/TR/SVGTiny12/interact.html#SVGEvents">SVG events</a>
  7473. * @param {string} type the event type.
  7474. * @param {function} handler the event handler.
  7475. * @returns {pv.Mark} this.
  7476. */
  7477. pv.Mark.prototype.event = function(type, handler) {
  7478. this.$handlers[type] = pv.functor(handler);
  7479. return this;
  7480. };
  7481. /** @private Evaluates the function <i>f</i> with the specified context. */
  7482. pv.Mark.prototype.context = function(scene, index, f) {
  7483. var proto = pv.Mark.prototype,
  7484. stack = pv.Mark.stack,
  7485. oscene = pv.Mark.scene,
  7486. oindex = proto.index;
  7487. /** @private Sets the context. */
  7488. function apply(scene, index) {
  7489. pv.Mark.scene = scene;
  7490. proto.index = index;
  7491. if (!scene) return;
  7492. var that = scene.mark,
  7493. mark = that,
  7494. ancestors = [];
  7495. /* Set ancestors' scene and index; populate data stack. */
  7496. do {
  7497. ancestors.push(mark);
  7498. stack.push(scene[index].data);
  7499. mark.index = index;
  7500. mark.scene = scene;
  7501. index = scene.parentIndex;
  7502. scene = scene.parent;
  7503. } while (mark = mark.parent);
  7504. /* Set ancestors' scale; requires top-down. */
  7505. for (var i = ancestors.length - 1, k = 1; i > 0; i--) {
  7506. mark = ancestors[i];
  7507. mark.scale = k;
  7508. k *= mark.scene[mark.index].transform.k;
  7509. }
  7510. /* Set children's scene and scale. */
  7511. if (that.children) for (var i = 0, n = that.children.length; i < n; i++) {
  7512. mark = that.children[i];
  7513. mark.scene = that.scene[that.index].children[i];
  7514. mark.scale = k;
  7515. }
  7516. }
  7517. /** @private Clears the context. */
  7518. function clear(scene, index) {
  7519. if (!scene) return;
  7520. var that = scene.mark,
  7521. mark;
  7522. /* Reset children. */
  7523. if (that.children) for (var i = 0, n = that.children.length; i < n; i++) {
  7524. mark = that.children[i];
  7525. delete mark.scene;
  7526. delete mark.scale;
  7527. }
  7528. /* Reset ancestors. */
  7529. mark = that;
  7530. do {
  7531. stack.pop();
  7532. if (mark.parent) {
  7533. delete mark.scene;
  7534. delete mark.scale;
  7535. }
  7536. delete mark.index;
  7537. } while (mark = mark.parent);
  7538. }
  7539. /* Context switch, invoke the function, then switch back. */
  7540. clear(oscene, oindex);
  7541. apply(scene, index);
  7542. try {
  7543. f.apply(this, stack);
  7544. } finally {
  7545. clear(scene, index);
  7546. apply(oscene, oindex);
  7547. }
  7548. };
  7549. /** @private Execute the event listener, then re-render. */
  7550. pv.Mark.dispatch = function(type, scene, index) {
  7551. var m = scene.mark, p = scene.parent, l = m.$handlers[type];
  7552. if (!l) return p && pv.Mark.dispatch(type, p, scene.parentIndex);
  7553. m.context(scene, index, function() {
  7554. m = l.apply(m, pv.Mark.stack);
  7555. if (m && m.render) m.render();
  7556. });
  7557. return true;
  7558. };
  7559. /**
  7560. * Constructs a new mark anchor with default properties.
  7561. *
  7562. * @class Represents an anchor on a given mark. An anchor is itself a mark, but
  7563. * without a visual representation. It serves only to provide useful default
  7564. * properties that can be inherited by other marks. Each type of mark can define
  7565. * any number of named anchors for convenience. If the concrete mark type does
  7566. * not define an anchor implementation specifically, one will be inherited from
  7567. * the mark's parent class.
  7568. *
  7569. * <p>For example, the bar mark provides anchors for its four sides: left,
  7570. * right, top and bottom. Adding a label to the top anchor of a bar,
  7571. *
  7572. * <pre>bar.anchor("top").add(pv.Label);</pre>
  7573. *
  7574. * will render a text label on the top edge of the bar; the top anchor defines
  7575. * the appropriate position properties (top and left), as well as text-rendering
  7576. * properties for convenience (textAlign and textBaseline).
  7577. *
  7578. * <p>Note that anchors do not <i>inherit</i> from their targets; the positional
  7579. * properties are copied from the scene graph, which guarantees that the anchors
  7580. * are positioned correctly, even if the positional properties are not defined
  7581. * deterministically. (In addition, it also improves performance by avoiding
  7582. * re-evaluating expensive properties.) If you want the anchor to inherit from
  7583. * the target, use {@link pv.Mark#extend} before adding. For example:
  7584. *
  7585. * <pre>bar.anchor("top").extend(bar).add(pv.Label);</pre>
  7586. *
  7587. * The anchor defines it's own positional properties, but other properties (such
  7588. * as the title property, say) can be inherited using the above idiom. Also note
  7589. * that you can override positional properties in the anchor for custom
  7590. * behavior.
  7591. *
  7592. * @extends pv.Mark
  7593. * @param {pv.Mark} target the anchor target.
  7594. */
  7595. pv.Anchor = function(target) {
  7596. pv.Mark.call(this);
  7597. this.target = target;
  7598. this.parent = target.parent;
  7599. };
  7600. pv.Anchor.prototype = pv.extend(pv.Mark)
  7601. .property("name", String);
  7602. /**
  7603. * The anchor name. The set of supported anchor names is dependent on the
  7604. * concrete mark type; see the mark type for details. For example, bars support
  7605. * left, right, top and bottom anchors.
  7606. *
  7607. * <p>While anchor names are typically constants, the anchor name is a true
  7608. * property, which means you can specify a function to compute the anchor name
  7609. * dynamically. For instance, if you wanted to alternate top and bottom anchors,
  7610. * saying
  7611. *
  7612. * <pre>m.anchor(function() (this.index % 2) ? "top" : "bottom").add(pv.Dot);</pre>
  7613. *
  7614. * would have the desired effect.
  7615. *
  7616. * @type string
  7617. * @name pv.Anchor.prototype.name
  7618. */
  7619. /**
  7620. * Returns the anchor target of this mark, if it is derived from an anchor;
  7621. * otherwise returns null. For example, if a label is derived from a bar anchor,
  7622. *
  7623. * <pre>bar.anchor("top").add(pv.Label);</pre>
  7624. *
  7625. * then property functions on the label can refer to the bar via the
  7626. * <tt>anchorTarget</tt> method. This method is also useful for mark types
  7627. * defining properties on custom anchors.
  7628. *
  7629. * @returns {pv.Mark} the anchor target of this mark; possibly null.
  7630. */
  7631. pv.Anchor.prototype.anchorTarget = function() {
  7632. return this.target;
  7633. };
  7634. /**
  7635. * Constructs a new area mark with default properties. Areas are not typically
  7636. * constructed directly, but by adding to a panel or an existing mark via
  7637. * {@link pv.Mark#add}.
  7638. *
  7639. * @class Represents an area mark: the solid area between two series of
  7640. * connected line segments. Unsurprisingly, areas are used most frequently for
  7641. * area charts.
  7642. *
  7643. * <p>Just as a line represents a polyline, the <tt>Area</tt> mark type
  7644. * represents a <i>polygon</i>. However, an area is not an arbitrary polygon;
  7645. * vertices are paired either horizontally or vertically into parallel
  7646. * <i>spans</i>, and each span corresponds to an associated datum. Either the
  7647. * width or the height must be specified, but not both; this determines whether
  7648. * the area is horizontally-oriented or vertically-oriented. Like lines, areas
  7649. * can be stroked and filled with arbitrary colors.
  7650. *
  7651. * <p>See also the <a href="../../api/Area.html">Area guide</a>.
  7652. *
  7653. * @extends pv.Mark
  7654. */
  7655. pv.Area = function() {
  7656. pv.Mark.call(this);
  7657. };
  7658. pv.Area.prototype = pv.extend(pv.Mark)
  7659. .property("width", Number)
  7660. .property("height", Number)
  7661. .property("lineWidth", Number)
  7662. .property("strokeStyle", pv.color)
  7663. .property("fillStyle", pv.color)
  7664. .property("segmented", Boolean)
  7665. .property("interpolate", String)
  7666. .property("tension", Number);
  7667. pv.Area.prototype.type = "area";
  7668. /**
  7669. * The width of a given span, in pixels; used for horizontal spans. If the width
  7670. * is specified, the height property should be 0 (the default). Either the top
  7671. * or bottom property should be used to space the spans vertically, typically as
  7672. * a multiple of the index.
  7673. *
  7674. * @type number
  7675. * @name pv.Area.prototype.width
  7676. */
  7677. /**
  7678. * The height of a given span, in pixels; used for vertical spans. If the height
  7679. * is specified, the width property should be 0 (the default). Either the left
  7680. * or right property should be used to space the spans horizontally, typically
  7681. * as a multiple of the index.
  7682. *
  7683. * @type number
  7684. * @name pv.Area.prototype.height
  7685. */
  7686. /**
  7687. * The width of stroked lines, in pixels; used in conjunction with
  7688. * <tt>strokeStyle</tt> to stroke the perimeter of the area. Unlike the
  7689. * {@link Line} mark type, the entire perimeter is stroked, rather than just one
  7690. * edge. The default value of this property is 1.5, but since the default stroke
  7691. * style is null, area marks are not stroked by default.
  7692. *
  7693. * <p>This property is <i>fixed</i> for non-segmented areas. See
  7694. * {@link pv.Mark}.
  7695. *
  7696. * @type number
  7697. * @name pv.Area.prototype.lineWidth
  7698. */
  7699. /**
  7700. * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
  7701. * stroke the perimeter of the area. Unlike the {@link Line} mark type, the
  7702. * entire perimeter is stroked, rather than just one edge. The default value of
  7703. * this property is null, meaning areas are not stroked by default.
  7704. *
  7705. * <p>This property is <i>fixed</i> for non-segmented areas. See
  7706. * {@link pv.Mark}.
  7707. *
  7708. * @type string
  7709. * @name pv.Area.prototype.strokeStyle
  7710. * @see pv.color
  7711. */
  7712. /**
  7713. * The area fill style; if non-null, the interior of the polygon forming the
  7714. * area is filled with the specified color. The default value of this property
  7715. * is a categorical color.
  7716. *
  7717. * <p>This property is <i>fixed</i> for non-segmented areas. See
  7718. * {@link pv.Mark}.
  7719. *
  7720. * @type string
  7721. * @name pv.Area.prototype.fillStyle
  7722. * @see pv.color
  7723. */
  7724. /**
  7725. * Whether the area is segmented; whether variations in fill style, stroke
  7726. * style, and the other properties are treated as fixed. Rendering segmented
  7727. * areas is noticeably slower than non-segmented areas.
  7728. *
  7729. * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
  7730. *
  7731. * @type boolean
  7732. * @name pv.Area.prototype.segmented
  7733. */
  7734. /**
  7735. * How to interpolate between values. Linear interpolation ("linear") is the
  7736. * default, producing a straight line between points. For piecewise constant
  7737. * functions (i.e., step functions), either "step-before" or "step-after" can be
  7738. * specified. To draw open uniform b-splines, specify "basis". To draw cardinal
  7739. * splines, specify "cardinal"; see also {@link #tension}.
  7740. *
  7741. * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
  7742. *
  7743. * @type string
  7744. * @name pv.Area.prototype.interpolate
  7745. */
  7746. /**
  7747. * The tension of cardinal splines; used in conjunction with
  7748. * interpolate("cardinal"). A value between 0 and 1 draws cardinal splines with
  7749. * the given tension. In some sense, the tension can be interpreted as the
  7750. * "length" of the tangent; a tension of 1 will yield all zero tangents (i.e.,
  7751. * linear interpolation), and a tension of 0 yields a Catmull-Rom spline. The
  7752. * default value is 0.7.
  7753. *
  7754. * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
  7755. *
  7756. * @type number
  7757. * @name pv.Area.prototype.tension
  7758. */
  7759. /**
  7760. * Default properties for areas. By default, there is no stroke and the fill
  7761. * style is a categorical color.
  7762. *
  7763. * @type pv.Area
  7764. */
  7765. pv.Area.prototype.defaults = new pv.Area()
  7766. .extend(pv.Mark.prototype.defaults)
  7767. .lineWidth(1.5)
  7768. .fillStyle(pv.Colors.category20().by(pv.parent))
  7769. .interpolate("linear")
  7770. .tension(.7);
  7771. /** @private Sets width and height to zero if null. */
  7772. pv.Area.prototype.buildImplied = function(s) {
  7773. if (s.height == null) s.height = 0;
  7774. if (s.width == null) s.width = 0;
  7775. pv.Mark.prototype.buildImplied.call(this, s);
  7776. };
  7777. /** @private Records which properties may be fixed. */
  7778. pv.Area.fixed = {
  7779. lineWidth: 1,
  7780. lineJoin: 1,
  7781. strokeStyle: 1,
  7782. fillStyle: 1,
  7783. segmented: 1,
  7784. interpolate: 1,
  7785. tension: 1
  7786. };
  7787. /**
  7788. * @private Make segmented required, such that this fixed property is always
  7789. * evaluated, even if the first segment is not visible. Also cache which
  7790. * properties are normally fixed.
  7791. */
  7792. pv.Area.prototype.bind = function() {
  7793. pv.Mark.prototype.bind.call(this);
  7794. var binds = this.binds,
  7795. required = binds.required,
  7796. optional = binds.optional;
  7797. for (var i = 0, n = optional.length; i < n; i++) {
  7798. var p = optional[i];
  7799. p.fixed = p.name in pv.Area.fixed;
  7800. if (p.name == "segmented") {
  7801. required.push(p);
  7802. optional.splice(i, 1);
  7803. i--;
  7804. n--;
  7805. }
  7806. }
  7807. /* Cache the original arrays so they can be restored on build. */
  7808. this.binds.$required = required;
  7809. this.binds.$optional = optional;
  7810. };
  7811. /**
  7812. * @private Override the default build behavior such that fixed properties are
  7813. * determined dynamically, based on the value of the (always) fixed segmented
  7814. * property. Any fixed properties are only evaluated on the first instance,
  7815. * although their values are propagated to subsequent instances, so that they
  7816. * are available for property chaining and the like.
  7817. */
  7818. pv.Area.prototype.buildInstance = function(s) {
  7819. var binds = this.binds;
  7820. /* Handle fixed properties on secondary instances. */
  7821. if (this.index) {
  7822. var fixed = binds.fixed;
  7823. /* Determine which properties are fixed. */
  7824. if (!fixed) {
  7825. fixed = binds.fixed = [];
  7826. function f(p) { return !p.fixed || (fixed.push(p), false); }
  7827. binds.required = binds.required.filter(f);
  7828. if (!this.scene[0].segmented) binds.optional = binds.optional.filter(f);
  7829. }
  7830. /* Copy fixed property values from the first instance. */
  7831. for (var i = 0, n = fixed.length; i < n; i++) {
  7832. var p = fixed[i].name;
  7833. s[p] = this.scene[0][p];
  7834. }
  7835. }
  7836. /* Evaluate all properties on the first instance. */
  7837. else {
  7838. binds.required = binds.$required;
  7839. binds.optional = binds.$optional;
  7840. binds.fixed = null;
  7841. }
  7842. pv.Mark.prototype.buildInstance.call(this, s);
  7843. };
  7844. /**
  7845. * Constructs a new area anchor with default properties. Areas support five
  7846. * different anchors:<ul>
  7847. *
  7848. * <li>top
  7849. * <li>left
  7850. * <li>center
  7851. * <li>bottom
  7852. * <li>right
  7853. *
  7854. * </ul>In addition to positioning properties (left, right, top bottom), the
  7855. * anchors support text rendering properties (text-align, text-baseline). Text
  7856. * is rendered to appear inside the area. The area anchor also propagates the
  7857. * interpolate, eccentricity, and tension properties such that an anchored area
  7858. * or line will match positions between control points.
  7859. *
  7860. * <p>For consistency with the other mark types, the anchor positions are
  7861. * defined in terms of their opposite edge. For example, the top anchor defines
  7862. * the bottom property, such that an area added to the top anchor grows upward.
  7863. *
  7864. * @param {string} name the anchor name; either a string or a property function.
  7865. * @returns {pv.Anchor}
  7866. */
  7867. pv.Area.prototype.anchor = function(name) {
  7868. var scene;
  7869. return pv.Mark.prototype.anchor.call(this, name)
  7870. .def("$area.anchor", function() {
  7871. scene = this.scene.target;
  7872. })
  7873. .interpolate(function() {
  7874. return scene[this.index].interpolate;
  7875. })
  7876. .eccentricity(function() {
  7877. return scene[this.index].eccentricity;
  7878. })
  7879. .tension(function() {
  7880. return scene[this.index].tension;
  7881. });
  7882. };
  7883. /**
  7884. * Constructs a new bar mark with default properties. Bars are not typically
  7885. * constructed directly, but by adding to a panel or an existing mark via
  7886. * {@link pv.Mark#add}.
  7887. *
  7888. * @class Represents a bar: an axis-aligned rectangle that can be stroked and
  7889. * filled. Bars are used for many chart types, including bar charts, histograms
  7890. * and Gantt charts. Bars can also be used as decorations, for example to draw a
  7891. * frame border around a panel; in fact, a panel is a special type (a subclass)
  7892. * of bar.
  7893. *
  7894. * <p>Bars can be positioned in several ways. Most commonly, one of the four
  7895. * corners is fixed using two margins, and then the width and height properties
  7896. * determine the extent of the bar relative to this fixed location. For example,
  7897. * using the bottom and left properties fixes the bottom-left corner; the width
  7898. * then extends to the right, while the height extends to the top. As an
  7899. * alternative to the four corners, a bar can be positioned exclusively using
  7900. * margins; this is convenient as an inset from the containing panel, for
  7901. * example. See {@link pv.Mark} for details on the prioritization of redundant
  7902. * positioning properties.
  7903. *
  7904. * <p>See also the <a href="../../api/Bar.html">Bar guide</a>.
  7905. *
  7906. * @extends pv.Mark
  7907. */
  7908. pv.Bar = function() {
  7909. pv.Mark.call(this);
  7910. };
  7911. pv.Bar.prototype = pv.extend(pv.Mark)
  7912. .property("width", Number)
  7913. .property("height", Number)
  7914. .property("lineWidth", Number)
  7915. .property("strokeStyle", pv.color)
  7916. .property("fillStyle", pv.color);
  7917. pv.Bar.prototype.type = "bar";
  7918. /**
  7919. * The width of the bar, in pixels. If the left position is specified, the bar
  7920. * extends rightward from the left edge; if the right position is specified, the
  7921. * bar extends leftward from the right edge.
  7922. *
  7923. * @type number
  7924. * @name pv.Bar.prototype.width
  7925. */
  7926. /**
  7927. * The height of the bar, in pixels. If the bottom position is specified, the
  7928. * bar extends upward from the bottom edge; if the top position is specified,
  7929. * the bar extends downward from the top edge.
  7930. *
  7931. * @type number
  7932. * @name pv.Bar.prototype.height
  7933. */
  7934. /**
  7935. * The width of stroked lines, in pixels; used in conjunction with
  7936. * <tt>strokeStyle</tt> to stroke the bar's border.
  7937. *
  7938. * @type number
  7939. * @name pv.Bar.prototype.lineWidth
  7940. */
  7941. /**
  7942. * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
  7943. * stroke the bar's border. The default value of this property is null, meaning
  7944. * bars are not stroked by default.
  7945. *
  7946. * @type string
  7947. * @name pv.Bar.prototype.strokeStyle
  7948. * @see pv.color
  7949. */
  7950. /**
  7951. * The bar fill style; if non-null, the interior of the bar is filled with the
  7952. * specified color. The default value of this property is a categorical color.
  7953. *
  7954. * @type string
  7955. * @name pv.Bar.prototype.fillStyle
  7956. * @see pv.color
  7957. */
  7958. /**
  7959. * Default properties for bars. By default, there is no stroke and the fill
  7960. * style is a categorical color.
  7961. *
  7962. * @type pv.Bar
  7963. */
  7964. pv.Bar.prototype.defaults = new pv.Bar()
  7965. .extend(pv.Mark.prototype.defaults)
  7966. .lineWidth(1.5)
  7967. .fillStyle(pv.Colors.category20().by(pv.parent));
  7968. /**
  7969. * Constructs a new dot mark with default properties. Dots are not typically
  7970. * constructed directly, but by adding to a panel or an existing mark via
  7971. * {@link pv.Mark#add}.
  7972. *
  7973. * @class Represents a dot; a dot is simply a sized glyph centered at a given
  7974. * point that can also be stroked and filled. The <tt>size</tt> property is
  7975. * proportional to the area of the rendered glyph to encourage meaningful visual
  7976. * encodings. Dots can visually encode up to eight dimensions of data, though
  7977. * this may be unwise due to integrality. See {@link pv.Mark} for details on the
  7978. * prioritization of redundant positioning properties.
  7979. *
  7980. * <p>See also the <a href="../../api/Dot.html">Dot guide</a>.
  7981. *
  7982. * @extends pv.Mark
  7983. */
  7984. pv.Dot = function() {
  7985. pv.Mark.call(this);
  7986. };
  7987. pv.Dot.prototype = pv.extend(pv.Mark)
  7988. .property("size", Number)
  7989. .property("radius", Number)
  7990. .property("shape", String)
  7991. .property("angle", Number)
  7992. .property("lineWidth", Number)
  7993. .property("strokeStyle", pv.color)
  7994. .property("fillStyle", pv.color);
  7995. pv.Dot.prototype.type = "dot";
  7996. /**
  7997. * The size of the dot, in square pixels. Square pixels are used such that the
  7998. * area of the dot is linearly proportional to the value of the size property,
  7999. * facilitating representative encodings.
  8000. *
  8001. * @see #radius
  8002. * @type number
  8003. * @name pv.Dot.prototype.size
  8004. */
  8005. /**
  8006. * The radius of the dot, in pixels. This is an alternative to using
  8007. * {@link #size}.
  8008. *
  8009. * @see #size
  8010. * @type number
  8011. * @name pv.Dot.prototype.radius
  8012. */
  8013. /**
  8014. * The shape name. Several shapes are supported:<ul>
  8015. *
  8016. * <li>cross
  8017. * <li>triangle
  8018. * <li>diamond
  8019. * <li>square
  8020. * <li>circle
  8021. * <li>tick
  8022. * <li>bar
  8023. *
  8024. * </ul>These shapes can be further changed using the {@link #angle} property;
  8025. * for instance, a cross can be turned into a plus by rotating. Similarly, the
  8026. * tick, which is vertical by default, can be rotated horizontally. Note that
  8027. * some shapes (cross and tick) do not have interior areas, and thus do not
  8028. * support fill style meaningfully.
  8029. *
  8030. * <p>Note: it may be more natural to use the {@link pv.Rule} mark for
  8031. * horizontal and vertical ticks. The tick shape is only necessary if angled
  8032. * ticks are needed.
  8033. *
  8034. * @type string
  8035. * @name pv.Dot.prototype.shape
  8036. */
  8037. /**
  8038. * The rotation angle, in radians. Used to rotate shapes, such as to turn a
  8039. * cross into a plus.
  8040. *
  8041. * @type number
  8042. * @name pv.Dot.prototype.angle
  8043. */
  8044. /**
  8045. * The width of stroked lines, in pixels; used in conjunction with
  8046. * <tt>strokeStyle</tt> to stroke the dot's shape.
  8047. *
  8048. * @type number
  8049. * @name pv.Dot.prototype.lineWidth
  8050. */
  8051. /**
  8052. * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
  8053. * stroke the dot's shape. The default value of this property is a categorical
  8054. * color.
  8055. *
  8056. * @type string
  8057. * @name pv.Dot.prototype.strokeStyle
  8058. * @see pv.color
  8059. */
  8060. /**
  8061. * The fill style; if non-null, the interior of the dot is filled with the
  8062. * specified color. The default value of this property is null, meaning dots are
  8063. * not filled by default.
  8064. *
  8065. * @type string
  8066. * @name pv.Dot.prototype.fillStyle
  8067. * @see pv.color
  8068. */
  8069. /**
  8070. * Default properties for dots. By default, there is no fill and the stroke
  8071. * style is a categorical color. The default shape is "circle" with size 20.
  8072. *
  8073. * @type pv.Dot
  8074. */
  8075. pv.Dot.prototype.defaults = new pv.Dot()
  8076. .extend(pv.Mark.prototype.defaults)
  8077. .size(20)
  8078. .shape("circle")
  8079. .lineWidth(1.5)
  8080. .strokeStyle(pv.Colors.category10().by(pv.parent));
  8081. /**
  8082. * Constructs a new dot anchor with default properties. Dots support five
  8083. * different anchors:<ul>
  8084. *
  8085. * <li>top
  8086. * <li>left
  8087. * <li>center
  8088. * <li>bottom
  8089. * <li>right
  8090. *
  8091. * </ul>In addition to positioning properties (left, right, top bottom), the
  8092. * anchors support text rendering properties (text-align, text-baseline). Text is
  8093. * rendered to appear outside the dot. Note that this behavior is different from
  8094. * other mark anchors, which default to rendering text <i>inside</i> the mark.
  8095. *
  8096. * <p>For consistency with the other mark types, the anchor positions are
  8097. * defined in terms of their opposite edge. For example, the top anchor defines
  8098. * the bottom property, such that a bar added to the top anchor grows upward.
  8099. *
  8100. * @param {string} name the anchor name; either a string or a property function.
  8101. * @returns {pv.Anchor}
  8102. */
  8103. pv.Dot.prototype.anchor = function(name) {
  8104. var scene;
  8105. return pv.Mark.prototype.anchor.call(this, name)
  8106. .def("$wedge.anchor", function() {
  8107. scene = this.scene.target;
  8108. })
  8109. .left(function() {
  8110. var s = scene[this.index];
  8111. switch (this.name()) {
  8112. case "bottom":
  8113. case "top":
  8114. case "center": return s.left;
  8115. case "left": return null;
  8116. }
  8117. return s.left + s.radius;
  8118. })
  8119. .right(function() {
  8120. var s = scene[this.index];
  8121. return this.name() == "left" ? s.right + s.radius : null;
  8122. })
  8123. .top(function() {
  8124. var s = scene[this.index];
  8125. switch (this.name()) {
  8126. case "left":
  8127. case "right":
  8128. case "center": return s.top;
  8129. case "top": return null;
  8130. }
  8131. return s.top + s.radius;
  8132. })
  8133. .bottom(function() {
  8134. var s = scene[this.index];
  8135. return this.name() == "top" ? s.bottom + s.radius : null;
  8136. })
  8137. .textAlign(function() {
  8138. switch (this.name()) {
  8139. case "left": return "right";
  8140. case "bottom":
  8141. case "top":
  8142. case "center": return "center";
  8143. }
  8144. return "left";
  8145. })
  8146. .textBaseline(function() {
  8147. switch (this.name()) {
  8148. case "right":
  8149. case "left":
  8150. case "center": return "middle";
  8151. case "bottom": return "top";
  8152. }
  8153. return "bottom";
  8154. });
  8155. };
  8156. /** @private Sets radius based on size or vice versa. */
  8157. pv.Dot.prototype.buildImplied = function(s) {
  8158. if (s.radius == null) s.radius = Math.sqrt(s.size);
  8159. else if (s.size == null) s.size = s.radius * s.radius;
  8160. pv.Mark.prototype.buildImplied.call(this, s);
  8161. };
  8162. /**
  8163. * Constructs a new label mark with default properties. Labels are not typically
  8164. * constructed directly, but by adding to a panel or an existing mark via
  8165. * {@link pv.Mark#add}.
  8166. *
  8167. * @class Represents a text label, allowing textual annotation of other marks or
  8168. * arbitrary text within the visualization. The character data must be plain
  8169. * text (unicode), though the text can be styled using the {@link #font}
  8170. * property. If rich text is needed, external HTML elements can be overlaid on
  8171. * the canvas by hand.
  8172. *
  8173. * <p>Labels are positioned using the box model, similarly to {@link Dot}. Thus,
  8174. * a label has no width or height, but merely a text anchor location. The text
  8175. * is positioned relative to this anchor location based on the
  8176. * {@link #textAlign}, {@link #textBaseline} and {@link #textMargin} properties.
  8177. * Furthermore, the text may be rotated using {@link #textAngle}.
  8178. *
  8179. * <p>Labels ignore events, so as to not interfere with event handlers on
  8180. * underlying marks, such as bars. In the future, we may support event handlers
  8181. * on labels.
  8182. *
  8183. * <p>See also the <a href="../../api/Label.html">Label guide</a>.
  8184. *
  8185. * @extends pv.Mark
  8186. */
  8187. pv.Label = function() {
  8188. pv.Mark.call(this);
  8189. };
  8190. pv.Label.prototype = pv.extend(pv.Mark)
  8191. .property("text", String)
  8192. .property("font", String)
  8193. .property("textAngle", Number)
  8194. .property("textStyle", pv.color)
  8195. .property("textAlign", String)
  8196. .property("textBaseline", String)
  8197. .property("textMargin", Number)
  8198. .property("textDecoration", String)
  8199. .property("textShadow", String);
  8200. pv.Label.prototype.type = "label";
  8201. /**
  8202. * The character data to render; a string. The default value of the text
  8203. * property is the identity function, meaning the label's associated datum will
  8204. * be rendered using its <tt>toString</tt>.
  8205. *
  8206. * @type string
  8207. * @name pv.Label.prototype.text
  8208. */
  8209. /**
  8210. * The font format, per the CSS Level 2 specification. The default font is "10px
  8211. * sans-serif", for consistency with the HTML 5 canvas element specification.
  8212. * Note that since text is not wrapped, any line-height property will be
  8213. * ignored. The other font-style, font-variant, font-weight, font-size and
  8214. * font-family properties are supported.
  8215. *
  8216. * @see <a href="http://www.w3.org/TR/CSS2/fonts.html#font-shorthand">CSS2 fonts</a>
  8217. * @type string
  8218. * @name pv.Label.prototype.font
  8219. */
  8220. /**
  8221. * The rotation angle, in radians. Text is rotated clockwise relative to the
  8222. * anchor location. For example, with the default left alignment, an angle of
  8223. * Math.PI / 2 causes text to proceed downwards. The default angle is zero.
  8224. *
  8225. * @type number
  8226. * @name pv.Label.prototype.textAngle
  8227. */
  8228. /**
  8229. * The text color. The name "textStyle" is used for consistency with "fillStyle"
  8230. * and "strokeStyle", although it might be better to rename this property (and
  8231. * perhaps use the same name as "strokeStyle"). The default color is black.
  8232. *
  8233. * @type string
  8234. * @name pv.Label.prototype.textStyle
  8235. * @see pv.color
  8236. */
  8237. /**
  8238. * The horizontal text alignment. One of:<ul>
  8239. *
  8240. * <li>left
  8241. * <li>center
  8242. * <li>right
  8243. *
  8244. * </ul>The default horizontal alignment is left.
  8245. *
  8246. * @type string
  8247. * @name pv.Label.prototype.textAlign
  8248. */
  8249. /**
  8250. * The vertical text alignment. One of:<ul>
  8251. *
  8252. * <li>top
  8253. * <li>middle
  8254. * <li>bottom
  8255. *
  8256. * </ul>The default vertical alignment is bottom.
  8257. *
  8258. * @type string
  8259. * @name pv.Label.prototype.textBaseline
  8260. */
  8261. /**
  8262. * The text margin; may be specified in pixels, or in font-dependent units (such
  8263. * as ".1ex"). The margin can be used to pad text away from its anchor location,
  8264. * in a direction dependent on the horizontal and vertical alignment
  8265. * properties. For example, if the text is left- and middle-aligned, the margin
  8266. * shifts the text to the right. The default margin is 3 pixels.
  8267. *
  8268. * @type number
  8269. * @name pv.Label.prototype.textMargin
  8270. */
  8271. /**
  8272. * A list of shadow effects to be applied to text, per the CSS Text Level 3
  8273. * text-shadow property. An example specification is "0.1em 0.1em 0.1em
  8274. * rgba(0,0,0,.5)"; the first length is the horizontal offset, the second the
  8275. * vertical offset, and the third the blur radius.
  8276. *
  8277. * @see <a href="http://www.w3.org/TR/css3-text/#text-shadow">CSS3 text</a>
  8278. * @type string
  8279. * @name pv.Label.prototype.textShadow
  8280. */
  8281. /**
  8282. * A list of decoration to be applied to text, per the CSS Text Level 3
  8283. * text-decoration property. An example specification is "underline".
  8284. *
  8285. * @see <a href="http://www.w3.org/TR/css3-text/#text-decoration">CSS3 text</a>
  8286. * @type string
  8287. * @name pv.Label.prototype.textDecoration
  8288. */
  8289. /**
  8290. * Default properties for labels. See the individual properties for the default
  8291. * values.
  8292. *
  8293. * @type pv.Label
  8294. */
  8295. pv.Label.prototype.defaults = new pv.Label()
  8296. .extend(pv.Mark.prototype.defaults)
  8297. .events("none")
  8298. .text(pv.identity)
  8299. .font("10px sans-serif")
  8300. .textAngle(0)
  8301. .textStyle("black")
  8302. .textAlign("left")
  8303. .textBaseline("bottom")
  8304. .textMargin(3);
  8305. /**
  8306. * Constructs a new line mark with default properties. Lines are not typically
  8307. * constructed directly, but by adding to a panel or an existing mark via
  8308. * {@link pv.Mark#add}.
  8309. *
  8310. * @class Represents a series of connected line segments, or <i>polyline</i>,
  8311. * that can be stroked with a configurable color and thickness. Each
  8312. * articulation point in the line corresponds to a datum; for <i>n</i> points,
  8313. * <i>n</i>-1 connected line segments are drawn. The point is positioned using
  8314. * the box model. Arbitrary paths are also possible, allowing radar plots and
  8315. * other custom visualizations.
  8316. *
  8317. * <p>Like areas, lines can be stroked and filled with arbitrary colors. In most
  8318. * cases, lines are only stroked, but the fill style can be used to construct
  8319. * arbitrary polygons.
  8320. *
  8321. * <p>See also the <a href="../../api/Line.html">Line guide</a>.
  8322. *
  8323. * @extends pv.Mark
  8324. */
  8325. pv.Line = function() {
  8326. pv.Mark.call(this);
  8327. };
  8328. pv.Line.prototype = pv.extend(pv.Mark)
  8329. .property("lineWidth", Number)
  8330. .property("lineJoin", String)
  8331. .property("strokeStyle", pv.color)
  8332. .property("fillStyle", pv.color)
  8333. .property("segmented", Boolean)
  8334. .property("interpolate", String)
  8335. .property("eccentricity", Number)
  8336. .property("tension", Number);
  8337. pv.Line.prototype.type = "line";
  8338. /**
  8339. * The width of stroked lines, in pixels; used in conjunction with
  8340. * <tt>strokeStyle</tt> to stroke the line.
  8341. *
  8342. * @type number
  8343. * @name pv.Line.prototype.lineWidth
  8344. */
  8345. /**
  8346. * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
  8347. * stroke the line. The default value of this property is a categorical color.
  8348. *
  8349. * @type string
  8350. * @name pv.Line.prototype.strokeStyle
  8351. * @see pv.color
  8352. */
  8353. /**
  8354. * The type of corners where two lines meet. Accepted values are "bevel",
  8355. * "round" and "miter". The default value is "miter".
  8356. *
  8357. * <p>For segmented lines, only "miter" joins and "linear" interpolation are
  8358. * currently supported. Any other value, including null, will disable joins,
  8359. * producing disjoint line segments. Note that the miter joins must be computed
  8360. * manually (at least in the current SVG renderer); since this calculation may
  8361. * be expensive and unnecessary for small lines, specifying null can improve
  8362. * performance significantly.
  8363. *
  8364. * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
  8365. *
  8366. * @type string
  8367. * @name pv.Line.prototype.lineJoin
  8368. */
  8369. /**
  8370. * The line fill style; if non-null, the interior of the line is closed and
  8371. * filled with the specified color. The default value of this property is a
  8372. * null, meaning that lines are not filled by default.
  8373. *
  8374. * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
  8375. *
  8376. * @type string
  8377. * @name pv.Line.prototype.fillStyle
  8378. * @see pv.color
  8379. */
  8380. /**
  8381. * Whether the line is segmented; whether variations in stroke style, line width
  8382. * and the other properties are treated as fixed. Rendering segmented lines is
  8383. * noticeably slower than non-segmented lines.
  8384. *
  8385. * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
  8386. *
  8387. * @type boolean
  8388. * @name pv.Line.prototype.segmented
  8389. */
  8390. /**
  8391. * How to interpolate between values. Linear interpolation ("linear") is the
  8392. * default, producing a straight line between points. For piecewise constant
  8393. * functions (i.e., step functions), either "step-before" or "step-after" can be
  8394. * specified. To draw a clockwise circular arc between points, specify "polar";
  8395. * to draw a counterclockwise circular arc between points, specify
  8396. * "polar-reverse". To draw open uniform b-splines, specify "basis". To draw
  8397. * cardinal splines, specify "cardinal"; see also {@link #tension}.
  8398. *
  8399. * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
  8400. *
  8401. * @type string
  8402. * @name pv.Line.prototype.interpolate
  8403. */
  8404. /**
  8405. * The eccentricity of polar line segments; used in conjunction with
  8406. * interpolate("polar"). The default value of 0 means that line segments are
  8407. * drawn as circular arcs. A value of 1 draws a straight line. A value between 0
  8408. * and 1 draws an elliptical arc with the given eccentricity.
  8409. *
  8410. * @type number
  8411. * @name pv.Line.prototype.eccentricity
  8412. */
  8413. /**
  8414. * The tension of cardinal splines; used in conjunction with
  8415. * interpolate("cardinal"). A value between 0 and 1 draws cardinal splines with
  8416. * the given tension. In some sense, the tension can be interpreted as the
  8417. * "length" of the tangent; a tension of 1 will yield all zero tangents (i.e.,
  8418. * linear interpolation), and a tension of 0 yields a Catmull-Rom spline. The
  8419. * default value is 0.7.
  8420. *
  8421. * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
  8422. *
  8423. * @type number
  8424. * @name pv.Line.prototype.tension
  8425. */
  8426. /**
  8427. * Default properties for lines. By default, there is no fill and the stroke
  8428. * style is a categorical color. The default interpolation is linear.
  8429. *
  8430. * @type pv.Line
  8431. */
  8432. pv.Line.prototype.defaults = new pv.Line()
  8433. .extend(pv.Mark.prototype.defaults)
  8434. .lineJoin("miter")
  8435. .lineWidth(1.5)
  8436. .strokeStyle(pv.Colors.category10().by(pv.parent))
  8437. .interpolate("linear")
  8438. .eccentricity(0)
  8439. .tension(.7);
  8440. /** @private Reuse Area's implementation for segmented bind & build. */
  8441. pv.Line.prototype.bind = pv.Area.prototype.bind;
  8442. pv.Line.prototype.buildInstance = pv.Area.prototype.buildInstance;
  8443. /**
  8444. * Constructs a new line anchor with default properties. Lines support five
  8445. * different anchors:<ul>
  8446. *
  8447. * <li>top
  8448. * <li>left
  8449. * <li>center
  8450. * <li>bottom
  8451. * <li>right
  8452. *
  8453. * </ul>In addition to positioning properties (left, right, top bottom), the
  8454. * anchors support text rendering properties (text-align, text-baseline). Text is
  8455. * rendered to appear outside the line. Note that this behavior is different
  8456. * from other mark anchors, which default to rendering text <i>inside</i> the
  8457. * mark.
  8458. *
  8459. * <p>For consistency with the other mark types, the anchor positions are
  8460. * defined in terms of their opposite edge. For example, the top anchor defines
  8461. * the bottom property, such that a bar added to the top anchor grows upward.
  8462. *
  8463. * @param {string} name the anchor name; either a string or a property function.
  8464. * @returns {pv.Anchor}
  8465. */
  8466. pv.Line.prototype.anchor = function(name) {
  8467. return pv.Area.prototype.anchor.call(this, name)
  8468. .textAlign(function(d) {
  8469. switch (this.name()) {
  8470. case "left": return "right";
  8471. case "bottom":
  8472. case "top":
  8473. case "center": return "center";
  8474. case "right": return "left";
  8475. }
  8476. })
  8477. .textBaseline(function(d) {
  8478. switch (this.name()) {
  8479. case "right":
  8480. case "left":
  8481. case "center": return "middle";
  8482. case "top": return "bottom";
  8483. case "bottom": return "top";
  8484. }
  8485. });
  8486. };
  8487. /**
  8488. * Constructs a new rule with default properties. Rules are not typically
  8489. * constructed directly, but by adding to a panel or an existing mark via
  8490. * {@link pv.Mark#add}.
  8491. *
  8492. * @class Represents a horizontal or vertical rule. Rules are frequently used
  8493. * for axes and grid lines. For example, specifying only the bottom property
  8494. * draws horizontal rules, while specifying only the left draws vertical
  8495. * rules. Rules can also be used as thin bars. The visual style is controlled in
  8496. * the same manner as lines.
  8497. *
  8498. * <p>Rules are positioned exclusively the standard box model properties. The
  8499. * following combinations of properties are supported:
  8500. *
  8501. * <table>
  8502. * <thead><th style="width:12em;">Properties</th><th>Orientation</th></thead>
  8503. * <tbody>
  8504. * <tr><td>left</td><td>vertical</td></tr>
  8505. * <tr><td>right</td><td>vertical</td></tr>
  8506. * <tr><td>left, bottom, top</td><td>vertical</td></tr>
  8507. * <tr><td>right, bottom, top</td><td>vertical</td></tr>
  8508. * <tr><td>top</td><td>horizontal</td></tr>
  8509. * <tr><td>bottom</td><td>horizontal</td></tr>
  8510. * <tr><td>top, left, right</td><td>horizontal</td></tr>
  8511. * <tr><td>bottom, left, right</td><td>horizontal</td></tr>
  8512. * <tr><td>left, top, height</td><td>vertical</td></tr>
  8513. * <tr><td>left, bottom, height</td><td>vertical</td></tr>
  8514. * <tr><td>right, top, height</td><td>vertical</td></tr>
  8515. * <tr><td>right, bottom, height</td><td>vertical</td></tr>
  8516. * <tr><td>left, top, width</td><td>horizontal</td></tr>
  8517. * <tr><td>left, bottom, width</td><td>horizontal</td></tr>
  8518. * <tr><td>right, top, width</td><td>horizontal</td></tr>
  8519. * <tr><td>right, bottom, width</td><td>horizontal</td></tr>
  8520. * </tbody>
  8521. * </table>
  8522. *
  8523. * <p>Small rules can be used as tick marks; alternatively, a {@link Dot} with
  8524. * the "tick" shape can be used.
  8525. *
  8526. * <p>See also the <a href="../../api/Rule.html">Rule guide</a>.
  8527. *
  8528. * @see pv.Line
  8529. * @extends pv.Mark
  8530. */
  8531. pv.Rule = function() {
  8532. pv.Mark.call(this);
  8533. };
  8534. pv.Rule.prototype = pv.extend(pv.Mark)
  8535. .property("width", Number)
  8536. .property("height", Number)
  8537. .property("lineWidth", Number)
  8538. .property("strokeStyle", pv.color);
  8539. pv.Rule.prototype.type = "rule";
  8540. /**
  8541. * The width of the rule, in pixels. If the left position is specified, the rule
  8542. * extends rightward from the left edge; if the right position is specified, the
  8543. * rule extends leftward from the right edge.
  8544. *
  8545. * @type number
  8546. * @name pv.Rule.prototype.width
  8547. */
  8548. /**
  8549. * The height of the rule, in pixels. If the bottom position is specified, the
  8550. * rule extends upward from the bottom edge; if the top position is specified,
  8551. * the rule extends downward from the top edge.
  8552. *
  8553. * @type number
  8554. * @name pv.Rule.prototype.height
  8555. */
  8556. /**
  8557. * The width of stroked lines, in pixels; used in conjunction with
  8558. * <tt>strokeStyle</tt> to stroke the rule. The default value is 1 pixel.
  8559. *
  8560. * @type number
  8561. * @name pv.Rule.prototype.lineWidth
  8562. */
  8563. /**
  8564. * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
  8565. * stroke the rule. The default value of this property is black.
  8566. *
  8567. * @type string
  8568. * @name pv.Rule.prototype.strokeStyle
  8569. * @see pv.color
  8570. */
  8571. /**
  8572. * Default properties for rules. By default, a single-pixel black line is
  8573. * stroked.
  8574. *
  8575. * @type pv.Rule
  8576. */
  8577. pv.Rule.prototype.defaults = new pv.Rule()
  8578. .extend(pv.Mark.prototype.defaults)
  8579. .lineWidth(1)
  8580. .strokeStyle("black")
  8581. .antialias(false);
  8582. /**
  8583. * Constructs a new rule anchor with default properties. Rules support five
  8584. * different anchors:<ul>
  8585. *
  8586. * <li>top
  8587. * <li>left
  8588. * <li>center
  8589. * <li>bottom
  8590. * <li>right
  8591. *
  8592. * </ul>In addition to positioning properties (left, right, top bottom), the
  8593. * anchors support text rendering properties (text-align, text-baseline). Text is
  8594. * rendered to appear outside the rule. Note that this behavior is different
  8595. * from other mark anchors, which default to rendering text <i>inside</i> the
  8596. * mark.
  8597. *
  8598. * <p>For consistency with the other mark types, the anchor positions are
  8599. * defined in terms of their opposite edge. For example, the top anchor defines
  8600. * the bottom property, such that a bar added to the top anchor grows upward.
  8601. *
  8602. * @param {string} name the anchor name; either a string or a property function.
  8603. * @returns {pv.Anchor}
  8604. */
  8605. pv.Rule.prototype.anchor = pv.Line.prototype.anchor;
  8606. /** @private Sets width or height based on orientation. */
  8607. pv.Rule.prototype.buildImplied = function(s) {
  8608. var l = s.left, r = s.right, t = s.top, b = s.bottom;
  8609. /* Determine horizontal or vertical orientation. */
  8610. if ((s.width != null)
  8611. || ((l == null) && (r == null))
  8612. || ((r != null) && (l != null))) {
  8613. s.height = 0;
  8614. } else {
  8615. s.width = 0;
  8616. }
  8617. pv.Mark.prototype.buildImplied.call(this, s);
  8618. };
  8619. /**
  8620. * Constructs a new, empty panel with default properties. Panels, with the
  8621. * exception of the root panel, are not typically constructed directly; instead,
  8622. * they are added to an existing panel or mark via {@link pv.Mark#add}.
  8623. *
  8624. * @class Represents a container mark. Panels allow repeated or nested
  8625. * structures, commonly used in small multiple displays where a small
  8626. * visualization is tiled to facilitate comparison across one or more
  8627. * dimensions. Other types of visualizations may benefit from repeated and
  8628. * possibly overlapping structure as well, such as stacked area charts. Panels
  8629. * can also offset the position of marks to provide padding from surrounding
  8630. * content.
  8631. *
  8632. * <p>All Protovis displays have at least one panel; this is the root panel to
  8633. * which marks are rendered. The box model properties (four margins, width and
  8634. * height) are used to offset the positions of contained marks. The data
  8635. * property determines the panel count: a panel is generated once per associated
  8636. * datum. When nested panels are used, property functions can declare additional
  8637. * arguments to access the data associated with enclosing panels.
  8638. *
  8639. * <p>Panels can be rendered inline, facilitating the creation of sparklines.
  8640. * This allows designers to reuse browser layout features, such as text flow and
  8641. * tables; designers can also overlay HTML elements such as rich text and
  8642. * images.
  8643. *
  8644. * <p>All panels have a <tt>children</tt> array (possibly empty) containing the
  8645. * child marks in the order they were added. Panels also have a <tt>root</tt>
  8646. * field which points to the root (outermost) panel; the root panel's root field
  8647. * points to itself.
  8648. *
  8649. * <p>See also the <a href="../../api/">Protovis guide</a>.
  8650. *
  8651. * @extends pv.Bar
  8652. */
  8653. pv.Panel = function() {
  8654. pv.Bar.call(this);
  8655. /**
  8656. * The child marks; zero or more {@link pv.Mark}s in the order they were
  8657. * added.
  8658. *
  8659. * @see #add
  8660. * @type pv.Mark[]
  8661. */
  8662. this.children = [];
  8663. this.root = this;
  8664. /**
  8665. * The internal $dom field is set by the Protovis loader; see lang/init.js. It
  8666. * refers to the script element that contains the Protovis specification, so
  8667. * that the panel knows where in the DOM to insert the generated SVG element.
  8668. *
  8669. * @private
  8670. */
  8671. this.$dom = pv.$ && pv.$.s;
  8672. };
  8673. pv.Panel.prototype = pv.extend(pv.Bar)
  8674. .property("transform")
  8675. .property("overflow", String)
  8676. .property("canvas", function(c) {
  8677. return (typeof c == "string")
  8678. ? document.getElementById(c)
  8679. : c; // assume that c is the passed-in element
  8680. });
  8681. pv.Panel.prototype.type = "panel";
  8682. /**
  8683. * The canvas element; either the string ID of the canvas element in the current
  8684. * document, or a reference to the canvas element itself. If null, a canvas
  8685. * element will be created and inserted into the document at the location of the
  8686. * script element containing the current Protovis specification. This property
  8687. * only applies to root panels and is ignored on nested panels.
  8688. *
  8689. * <p>Note: the "canvas" element here refers to a <tt>div</tt> (or other suitable
  8690. * HTML container element), <i>not</i> a <tt>canvas</tt> element. The name of
  8691. * this property is a historical anachronism from the first implementation that
  8692. * used HTML 5 canvas, rather than SVG.
  8693. *
  8694. * @type string
  8695. * @name pv.Panel.prototype.canvas
  8696. */
  8697. /**
  8698. * Specifies whether child marks are clipped when they overflow this panel.
  8699. * This affects the clipping of all this panel's descendant marks.
  8700. *
  8701. * @type string
  8702. * @name pv.Panel.prototype.overflow
  8703. * @see <a href="http://www.w3.org/TR/CSS2/visufx.html#overflow">CSS2</a>
  8704. */
  8705. /**
  8706. * The transform to be applied to child marks. The default transform is
  8707. * identity, which has no effect. Note that the panel's own fill and stroke are
  8708. * not affected by the transform, and panel's transform only affects the
  8709. * <tt>scale</tt> of child marks, not the panel itself.
  8710. *
  8711. * @type pv.Transform
  8712. * @name pv.Panel.prototype.transform
  8713. * @see pv.Mark#scale
  8714. */
  8715. /**
  8716. * Default properties for panels. By default, the margins are zero, the fill
  8717. * style is transparent.
  8718. *
  8719. * @type pv.Panel
  8720. */
  8721. pv.Panel.prototype.defaults = new pv.Panel()
  8722. .extend(pv.Bar.prototype.defaults)
  8723. .fillStyle(null) // override Bar default
  8724. .overflow("visible");
  8725. /**
  8726. * Returns an anchor with the specified name. This method is overridden such
  8727. * that adding to a panel's anchor adds to the panel, rather than to the panel's
  8728. * parent.
  8729. *
  8730. * @param {string} name the anchor name; either a string or a property function.
  8731. * @returns {pv.Anchor} the new anchor.
  8732. */
  8733. pv.Panel.prototype.anchor = function(name) {
  8734. var anchor = pv.Bar.prototype.anchor.call(this, name);
  8735. anchor.parent = this;
  8736. return anchor;
  8737. };
  8738. /**
  8739. * Adds a new mark of the specified type to this panel. Unlike the normal
  8740. * {@link Mark#add} behavior, adding a mark to a panel does not cause the mark
  8741. * to inherit from the panel. Since the contained marks are offset by the panel
  8742. * margins already, inheriting properties is generally undesirable; of course,
  8743. * it is always possible to change this behavior by calling {@link Mark#extend}
  8744. * explicitly.
  8745. *
  8746. * @param {function} type the type of the new mark to add.
  8747. * @returns {pv.Mark} the new mark.
  8748. */
  8749. pv.Panel.prototype.add = function(type) {
  8750. var child = new type();
  8751. child.parent = this;
  8752. child.root = this.root;
  8753. child.childIndex = this.children.length;
  8754. this.children.push(child);
  8755. return child;
  8756. };
  8757. /** @private Bind this panel, then any child marks recursively. */
  8758. pv.Panel.prototype.bind = function() {
  8759. pv.Mark.prototype.bind.call(this);
  8760. for (var i = 0; i < this.children.length; i++) {
  8761. this.children[i].bind();
  8762. }
  8763. };
  8764. /**
  8765. * @private Evaluates all of the properties for this panel for the specified
  8766. * instance <tt>s</tt> in the scene graph, including recursively building the
  8767. * scene graph for child marks.
  8768. *
  8769. * @param s a node in the scene graph; the instance of the panel to build.
  8770. * @see Mark#scene
  8771. */
  8772. pv.Panel.prototype.buildInstance = function(s) {
  8773. pv.Bar.prototype.buildInstance.call(this, s);
  8774. if (!s.visible) return;
  8775. if (!s.children) s.children = [];
  8776. /*
  8777. * Multiply the current scale factor by this panel's transform. Also clear the
  8778. * default index as we recurse into child marks; it will be reset to the
  8779. * current index when the next panel instance is built.
  8780. */
  8781. var scale = this.scale * s.transform.k, child, n = this.children.length;
  8782. pv.Mark.prototype.index = -1;
  8783. /*
  8784. * Build each child, passing in the parent (this panel) scene graph node. The
  8785. * child mark's scene is initialized from the corresponding entry in the
  8786. * existing scene graph, such that properties from the previous build can be
  8787. * reused; this is largely to facilitate the recycling of SVG elements.
  8788. */
  8789. for (var i = 0; i < n; i++) {
  8790. child = this.children[i];
  8791. child.scene = s.children[i]; // possibly undefined
  8792. child.scale = scale;
  8793. child.build();
  8794. }
  8795. /*
  8796. * Once the child marks have been built, the new scene graph nodes are removed
  8797. * from the child marks and placed into the scene graph. The nodes cannot
  8798. * remain on the child nodes because this panel (or a parent panel) may be
  8799. * instantiated multiple times!
  8800. */
  8801. for (var i = 0; i < n; i++) {
  8802. child = this.children[i];
  8803. s.children[i] = child.scene;
  8804. delete child.scene;
  8805. delete child.scale;
  8806. }
  8807. /* Delete any expired child scenes. */
  8808. s.children.length = n;
  8809. };
  8810. /**
  8811. * @private Computes the implied properties for this panel for the specified
  8812. * instance <tt>s</tt> in the scene graph. Panels have two implied
  8813. * properties:<ul>
  8814. *
  8815. * <li>The <tt>canvas</tt> property references the DOM element, typically a DIV,
  8816. * that contains the SVG element that is used to display the visualization. This
  8817. * property may be specified as a string, referring to the unique ID of the
  8818. * element in the DOM. The string is converted to a reference to the DOM
  8819. * element. The width and height of the SVG element is inferred from this DOM
  8820. * element. If no canvas property is specified, a new SVG element is created and
  8821. * inserted into the document, using the panel dimensions; see
  8822. * {@link #createCanvas}.
  8823. *
  8824. * <li>The <tt>children</tt> array, while not a property per se, contains the
  8825. * scene graph for each child mark. This array is initialized to be empty, and
  8826. * is populated above in {@link #buildInstance}.
  8827. *
  8828. * </ul>The current implementation creates the SVG element, if necessary, during
  8829. * the build phase; in the future, it may be preferrable to move this to the
  8830. * update phase, although then the canvas property would be undefined. In
  8831. * addition, DOM inspection is necessary to define the implied width and height
  8832. * properties that may be inferred from the DOM.
  8833. *
  8834. * @param s a node in the scene graph; the instance of the panel to build.
  8835. */
  8836. pv.Panel.prototype.buildImplied = function(s) {
  8837. if (!this.parent) {
  8838. var c = s.canvas;
  8839. if (c) {
  8840. /* Clear the container if it's not associated with this panel. */
  8841. if (c.$panel != this) {
  8842. c.$panel = this;
  8843. while (c.lastChild) c.removeChild(c.lastChild);
  8844. }
  8845. /* If width and height weren't specified, inspect the container. */
  8846. var w, h;
  8847. if (s.width == null) {
  8848. w = parseFloat(pv.css(c, "width"));
  8849. s.width = w - s.left - s.right;
  8850. }
  8851. if (s.height == null) {
  8852. h = parseFloat(pv.css(c, "height"));
  8853. s.height = h - s.top - s.bottom;
  8854. }
  8855. } else {
  8856. var cache = this.$canvas || (this.$canvas = []);
  8857. if (!(c = cache[this.index])) {
  8858. c = cache[this.index] = document.createElement("span");
  8859. if (this.$dom) { // script element for text/javascript+protovis
  8860. /*
  8861. * this.$dom.parentNode.insertBefore(c, this.$dom);
  8862. * Changing this line to allow the direct definiton of the element where the graph should be placed
  8863. */
  8864. this.$dom.appendChild(c);
  8865. } else { // find the last element in the body
  8866. var n = document.body;
  8867. while (n.lastChild && n.lastChild.tagName) n = n.lastChild;
  8868. if (n != document.body) n = n.parentNode;
  8869. n.appendChild(c);
  8870. }
  8871. }
  8872. }
  8873. s.canvas = c;
  8874. }
  8875. if (!s.transform) s.transform = pv.Transform.identity;
  8876. pv.Mark.prototype.buildImplied.call(this, s);
  8877. };
  8878. /**
  8879. * Constructs a new image with default properties. Images are not typically
  8880. * constructed directly, but by adding to a panel or an existing mark via
  8881. * {@link pv.Mark#add}.
  8882. *
  8883. * @class Represents an image, either a static resource or a dynamically-
  8884. * generated pixel buffer. Images share the same layout and style properties as
  8885. * bars. The external image resource is specified via the {@link #url}
  8886. * property. The optional fill, if specified, appears beneath the image, while
  8887. * the optional stroke appears above the image.
  8888. *
  8889. * <p>Dynamic images such as heatmaps are supported using the {@link #image}
  8890. * psuedo-property. This function is passed the <i>x</i> and <i>y</i> index, in
  8891. * addition to the current data stack. The return value is a {@link pv.Color},
  8892. * or null for transparent. A string can also be returned, which will be parsed
  8893. * into a color; however, it is typically much faster to return an object with
  8894. * <tt>r</tt>, <tt>g</tt>, <tt>b</tt> and <tt>a</tt> attributes, to avoid the
  8895. * cost of parsing and object instantiation.
  8896. *
  8897. * <p>See {@link pv.Bar} for details on positioning properties.
  8898. *
  8899. * @extends pv.Bar
  8900. */
  8901. pv.Image = function() {
  8902. pv.Bar.call(this);
  8903. };
  8904. pv.Image.prototype = pv.extend(pv.Bar)
  8905. .property("url", String)
  8906. .property("imageWidth", Number)
  8907. .property("imageHeight", Number);
  8908. pv.Image.prototype.type = "image";
  8909. /**
  8910. * The URL of the image to display. The set of supported image types is
  8911. * browser-dependent; PNG and JPEG are recommended.
  8912. *
  8913. * @type string
  8914. * @name pv.Image.prototype.url
  8915. */
  8916. /**
  8917. * The width of the image in pixels. For static images, this property is
  8918. * computed implicitly from the loaded image resources. For dynamic images, this
  8919. * property can be used to specify the width of the pixel buffer; otherwise, the
  8920. * value is derived from the <tt>width</tt> property.
  8921. *
  8922. * @type number
  8923. * @name pv.Image.prototype.imageWidth
  8924. */
  8925. /**
  8926. * The height of the image in pixels. For static images, this property is
  8927. * computed implicitly from the loaded image resources. For dynamic images, this
  8928. * property can be used to specify the height of the pixel buffer; otherwise, the
  8929. * value is derived from the <tt>height</tt> property.
  8930. *
  8931. * @type number
  8932. * @name pv.Image.prototype.imageHeight
  8933. */
  8934. /**
  8935. * Default properties for images. By default, there is no stroke or fill style.
  8936. *
  8937. * @type pv.Image
  8938. */
  8939. pv.Image.prototype.defaults = new pv.Image()
  8940. .extend(pv.Bar.prototype.defaults)
  8941. .fillStyle(null);
  8942. /**
  8943. * Specifies the dynamic image function. By default, no image function is
  8944. * specified and the <tt>url</tt> property is used to load a static image
  8945. * resource. If an image function is specified, it will be invoked for each
  8946. * pixel in the image, based on the related <tt>imageWidth</tt> and
  8947. * <tt>imageHeight</tt> properties.
  8948. *
  8949. * <p>For example, given a two-dimensional array <tt>heatmap</tt>, containing
  8950. * numbers in the range [0, 1] in row-major order, a simple monochrome heatmap
  8951. * image can be specified as:
  8952. *
  8953. * <pre>vis.add(pv.Image)
  8954. * .imageWidth(heatmap[0].length)
  8955. * .imageHeight(heatmap.length)
  8956. * .image(pv.ramp("white", "black").by(function(x, y) heatmap[y][x]));</pre>
  8957. *
  8958. * For fastest performance, use an ordinal scale which caches the fixed color
  8959. * palette, or return an object literal with <tt>r</tt>, <tt>g</tt>, <tt>b</tt>
  8960. * and <tt>a</tt> attributes. A {@link pv.Color} or string can also be returned,
  8961. * though this typically results in slower performance.
  8962. *
  8963. * @param {function} f the new sizing function.
  8964. * @returns {pv.Layout.Pack} this.
  8965. */
  8966. pv.Image.prototype.image = function(f) {
  8967. /** @private */
  8968. this.$image = function() {
  8969. var c = f.apply(this, arguments);
  8970. return c == null ? pv.Color.transparent
  8971. : typeof c == "string" ? pv.color(c)
  8972. : c;
  8973. };
  8974. return this;
  8975. };
  8976. /** @private Scan the proto chain for an image function. */
  8977. pv.Image.prototype.bind = function() {
  8978. pv.Bar.prototype.bind.call(this);
  8979. var binds = this.binds, mark = this;
  8980. do {
  8981. binds.image = mark.$image;
  8982. } while (!binds.image && (mark = mark.proto));
  8983. };
  8984. /** @private */
  8985. pv.Image.prototype.buildImplied = function(s) {
  8986. pv.Bar.prototype.buildImplied.call(this, s);
  8987. if (!s.visible) return;
  8988. /* Compute the implied image dimensions. */
  8989. if (s.imageWidth == null) s.imageWidth = s.width;
  8990. if (s.imageHeight == null) s.imageHeight = s.height;
  8991. /* Compute the pixel values. */
  8992. if ((s.url == null) && this.binds.image) {
  8993. /* Cache the canvas element to reuse across renders. */
  8994. var canvas = this.$canvas || (this.$canvas = document.createElement("canvas")),
  8995. context = canvas.getContext("2d"),
  8996. w = s.imageWidth,
  8997. h = s.imageHeight,
  8998. stack = pv.Mark.stack,
  8999. data;
  9000. /* Evaluate the image function, storing into a CanvasPixelArray. */
  9001. canvas.width = w;
  9002. canvas.height = h;
  9003. data = (s.image = context.createImageData(w, h)).data;
  9004. stack.unshift(null, null);
  9005. for (var y = 0, p = 0; y < h; y++) {
  9006. stack[1] = y;
  9007. for (var x = 0; x < w; x++) {
  9008. stack[0] = x;
  9009. var color = this.binds.image.apply(this, stack);
  9010. data[p++] = color.r;
  9011. data[p++] = color.g;
  9012. data[p++] = color.b;
  9013. data[p++] = 255 * color.a;
  9014. }
  9015. }
  9016. stack.splice(0, 2);
  9017. }
  9018. };
  9019. /**
  9020. * Constructs a new wedge with default properties. Wedges are not typically
  9021. * constructed directly, but by adding to a panel or an existing mark via
  9022. * {@link pv.Mark#add}.
  9023. *
  9024. * @class Represents a wedge, or pie slice. Specified in terms of start and end
  9025. * angle, inner and outer radius, wedges can be used to construct donut charts
  9026. * and polar bar charts as well. If the {@link #angle} property is used, the end
  9027. * angle is implied by adding this value to start angle. By default, the start
  9028. * angle is the previously-generated wedge's end angle. This design allows
  9029. * explicit control over the wedge placement if desired, while offering
  9030. * convenient defaults for the construction of radial graphs.
  9031. *
  9032. * <p>The center point of the circle is positioned using the standard box model.
  9033. * The wedge can be stroked and filled, similar to {@link pv.Bar}.
  9034. *
  9035. * <p>See also the <a href="../../api/Wedge.html">Wedge guide</a>.
  9036. *
  9037. * @extends pv.Mark
  9038. */
  9039. pv.Wedge = function() {
  9040. pv.Mark.call(this);
  9041. };
  9042. pv.Wedge.prototype = pv.extend(pv.Mark)
  9043. .property("startAngle", Number)
  9044. .property("endAngle", Number)
  9045. .property("angle", Number)
  9046. .property("innerRadius", Number)
  9047. .property("outerRadius", Number)
  9048. .property("lineWidth", Number)
  9049. .property("strokeStyle", pv.color)
  9050. .property("fillStyle", pv.color);
  9051. pv.Wedge.prototype.type = "wedge";
  9052. /**
  9053. * The start angle of the wedge, in radians. The start angle is measured
  9054. * clockwise from the 3 o'clock position. The default value of this property is
  9055. * the end angle of the previous instance (the {@link Mark#sibling}), or -PI / 2
  9056. * for the first wedge; for pie and donut charts, typically only the
  9057. * {@link #angle} property needs to be specified.
  9058. *
  9059. * @type number
  9060. * @name pv.Wedge.prototype.startAngle
  9061. */
  9062. /**
  9063. * The end angle of the wedge, in radians. If not specified, the end angle is
  9064. * implied as the start angle plus the {@link #angle}.
  9065. *
  9066. * @type number
  9067. * @name pv.Wedge.prototype.endAngle
  9068. */
  9069. /**
  9070. * The angular span of the wedge, in radians. This property is used if end angle
  9071. * is not specified.
  9072. *
  9073. * @type number
  9074. * @name pv.Wedge.prototype.angle
  9075. */
  9076. /**
  9077. * The inner radius of the wedge, in pixels. The default value of this property
  9078. * is zero; a positive value will produce a donut slice rather than a pie slice.
  9079. * The inner radius can vary per-wedge.
  9080. *
  9081. * @type number
  9082. * @name pv.Wedge.prototype.innerRadius
  9083. */
  9084. /**
  9085. * The outer radius of the wedge, in pixels. This property is required. For
  9086. * pies, only this radius is required; for donuts, the inner radius must be
  9087. * specified as well. The outer radius can vary per-wedge.
  9088. *
  9089. * @type number
  9090. * @name pv.Wedge.prototype.outerRadius
  9091. */
  9092. /**
  9093. * The width of stroked lines, in pixels; used in conjunction with
  9094. * <tt>strokeStyle</tt> to stroke the wedge's border.
  9095. *
  9096. * @type number
  9097. * @name pv.Wedge.prototype.lineWidth
  9098. */
  9099. /**
  9100. * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
  9101. * stroke the wedge's border. The default value of this property is null,
  9102. * meaning wedges are not stroked by default.
  9103. *
  9104. * @type string
  9105. * @name pv.Wedge.prototype.strokeStyle
  9106. * @see pv.color
  9107. */
  9108. /**
  9109. * The wedge fill style; if non-null, the interior of the wedge is filled with
  9110. * the specified color. The default value of this property is a categorical
  9111. * color.
  9112. *
  9113. * @type string
  9114. * @name pv.Wedge.prototype.fillStyle
  9115. * @see pv.color
  9116. */
  9117. /**
  9118. * Default properties for wedges. By default, there is no stroke and the fill
  9119. * style is a categorical color.
  9120. *
  9121. * @type pv.Wedge
  9122. */
  9123. pv.Wedge.prototype.defaults = new pv.Wedge()
  9124. .extend(pv.Mark.prototype.defaults)
  9125. .startAngle(function() {
  9126. var s = this.sibling();
  9127. return s ? s.endAngle : -Math.PI / 2;
  9128. })
  9129. .innerRadius(0)
  9130. .lineWidth(1.5)
  9131. .strokeStyle(null)
  9132. .fillStyle(pv.Colors.category20().by(pv.index));
  9133. /**
  9134. * Returns the mid-radius of the wedge, which is defined as half-way between the
  9135. * inner and outer radii.
  9136. *
  9137. * @see #innerRadius
  9138. * @see #outerRadius
  9139. * @returns {number} the mid-radius, in pixels.
  9140. */
  9141. pv.Wedge.prototype.midRadius = function() {
  9142. return (this.innerRadius() + this.outerRadius()) / 2;
  9143. };
  9144. /**
  9145. * Returns the mid-angle of the wedge, which is defined as half-way between the
  9146. * start and end angles.
  9147. *
  9148. * @see #startAngle
  9149. * @see #endAngle
  9150. * @returns {number} the mid-angle, in radians.
  9151. */
  9152. pv.Wedge.prototype.midAngle = function() {
  9153. return (this.startAngle() + this.endAngle()) / 2;
  9154. };
  9155. /**
  9156. * Constructs a new wedge anchor with default properties. Wedges support five
  9157. * different anchors:<ul>
  9158. *
  9159. * <li>outer
  9160. * <li>inner
  9161. * <li>center
  9162. * <li>start
  9163. * <li>end
  9164. *
  9165. * </ul>In addition to positioning properties (left, right, top bottom), the
  9166. * anchors support text rendering properties (text-align, text-baseline,
  9167. * textAngle). Text is rendered to appear inside the wedge.
  9168. *
  9169. * @param {string} name the anchor name; either a string or a property function.
  9170. * @returns {pv.Anchor}
  9171. */
  9172. pv.Wedge.prototype.anchor = function(name) {
  9173. function partial(s) { return s.innerRadius || s.angle < 2 * Math.PI; }
  9174. function midRadius(s) { return (s.innerRadius + s.outerRadius) / 2; }
  9175. function midAngle(s) { return (s.startAngle + s.endAngle) / 2; }
  9176. var scene;
  9177. return pv.Mark.prototype.anchor.call(this, name)
  9178. .def("$wedge.anchor", function() {
  9179. scene = this.scene.target;
  9180. })
  9181. .left(function() {
  9182. var s = scene[this.index];
  9183. if (partial(s)) switch (this.name()) {
  9184. case "outer": return s.left + s.outerRadius * Math.cos(midAngle(s));
  9185. case "inner": return s.left + s.innerRadius * Math.cos(midAngle(s));
  9186. case "start": return s.left + midRadius(s) * Math.cos(s.startAngle);
  9187. case "center": return s.left + midRadius(s) * Math.cos(midAngle(s));
  9188. case "end": return s.left + midRadius(s) * Math.cos(s.endAngle);
  9189. }
  9190. return s.left;
  9191. })
  9192. .top(function() {
  9193. var s = scene[this.index];
  9194. if (partial(s)) switch (this.name()) {
  9195. case "outer": return s.top + s.outerRadius * Math.sin(midAngle(s));
  9196. case "inner": return s.top + s.innerRadius * Math.sin(midAngle(s));
  9197. case "start": return s.top + midRadius(s) * Math.sin(s.startAngle);
  9198. case "center": return s.top + midRadius(s) * Math.sin(midAngle(s));
  9199. case "end": return s.top + midRadius(s) * Math.sin(s.endAngle);
  9200. }
  9201. return s.top;
  9202. })
  9203. .textAlign(function() {
  9204. var s = scene[this.index];
  9205. if (partial(s)) switch (this.name()) {
  9206. case "outer": return pv.Wedge.upright(midAngle(s)) ? "right" : "left";
  9207. case "inner": return pv.Wedge.upright(midAngle(s)) ? "left" : "right";
  9208. }
  9209. return "center";
  9210. })
  9211. .textBaseline(function() {
  9212. var s = scene[this.index];
  9213. if (partial(s)) switch (this.name()) {
  9214. case "start": return pv.Wedge.upright(s.startAngle) ? "top" : "bottom";
  9215. case "end": return pv.Wedge.upright(s.endAngle) ? "bottom" : "top";
  9216. }
  9217. return "middle";
  9218. })
  9219. .textAngle(function() {
  9220. var s = scene[this.index], a = 0;
  9221. if (partial(s)) switch (this.name()) {
  9222. case "center":
  9223. case "inner":
  9224. case "outer": a = midAngle(s); break;
  9225. case "start": a = s.startAngle; break;
  9226. case "end": a = s.endAngle; break;
  9227. }
  9228. return pv.Wedge.upright(a) ? a : (a + Math.PI);
  9229. });
  9230. };
  9231. /**
  9232. * Returns true if the specified angle is considered "upright", as in, text
  9233. * rendered at that angle would appear upright. If the angle is not upright,
  9234. * text is rotated 180 degrees to be upright, and the text alignment properties
  9235. * are correspondingly changed.
  9236. *
  9237. * @param {number} angle an angle, in radius.
  9238. * @returns {boolean} true if the specified angle is upright.
  9239. */
  9240. pv.Wedge.upright = function(angle) {
  9241. angle = angle % (2 * Math.PI);
  9242. angle = (angle < 0) ? (2 * Math.PI + angle) : angle;
  9243. return (angle < Math.PI / 2) || (angle >= 3 * Math.PI / 2);
  9244. };
  9245. /** @private Sets angle based on endAngle or vice versa. */
  9246. pv.Wedge.prototype.buildImplied = function(s) {
  9247. if (s.angle == null) s.angle = s.endAngle - s.startAngle;
  9248. else if (s.endAngle == null) s.endAngle = s.startAngle + s.angle;
  9249. pv.Mark.prototype.buildImplied.call(this, s);
  9250. };
  9251. /**
  9252. * Abstract; not implemented. There is no explicit constructor; this class
  9253. * merely serves to document the attributes that are used on particles in
  9254. * physics simulations.
  9255. *
  9256. * @class A weighted particle that can participate in a force simulation.
  9257. *
  9258. * @name pv.Particle
  9259. */
  9260. /**
  9261. * The next particle in the simulation. Particles form a singly-linked list.
  9262. *
  9263. * @field
  9264. * @type pv.Particle
  9265. * @name pv.Particle.prototype.next
  9266. */
  9267. /**
  9268. * The <i>x</i>-position of the particle.
  9269. *
  9270. * @field
  9271. * @type number
  9272. * @name pv.Particle.prototype.x
  9273. */
  9274. /**
  9275. * The <i>y</i>-position of the particle.
  9276. *
  9277. * @field
  9278. * @type number
  9279. * @name pv.Particle.prototype.y
  9280. */
  9281. /**
  9282. * The <i>x</i>-velocity of the particle.
  9283. *
  9284. * @field
  9285. * @type number
  9286. * @name pv.Particle.prototype.vx
  9287. */
  9288. /**
  9289. * The <i>y</i>-velocity of the particle.
  9290. *
  9291. * @field
  9292. * @type number
  9293. * @name pv.Particle.prototype.vy
  9294. */
  9295. /**
  9296. * The <i>x</i>-position of the particle at -dt.
  9297. *
  9298. * @field
  9299. * @type number
  9300. * @name pv.Particle.prototype.px
  9301. */
  9302. /**
  9303. * The <i>y</i>-position of the particle at -dt.
  9304. *
  9305. * @field
  9306. * @type number
  9307. * @name pv.Particle.prototype.py
  9308. */
  9309. /**
  9310. * The <i>x</i>-force on the particle.
  9311. *
  9312. * @field
  9313. * @type number
  9314. * @name pv.Particle.prototype.fx
  9315. */
  9316. /**
  9317. * The <i>y</i>-force on the particle.
  9318. *
  9319. * @field
  9320. * @type number
  9321. * @name pv.Particle.prototype.fy
  9322. */
  9323. /**
  9324. * Constructs a new empty simulation.
  9325. *
  9326. * @param {array} particles
  9327. * @returns {pv.Simulation} a new simulation for the specified particles.
  9328. * @see pv.Simulation
  9329. */
  9330. pv.simulation = function(particles) {
  9331. return new pv.Simulation(particles);
  9332. };
  9333. /**
  9334. * Constructs a new simulation for the specified particles.
  9335. *
  9336. * @class Represents a particle simulation. Particles are massive points in
  9337. * two-dimensional space. Forces can be applied to these particles, causing them
  9338. * to move. Constraints can also be applied to restrict particle movement, for
  9339. * example, constraining particles to a fixed position, or simulating collision
  9340. * between circular particles with area.
  9341. *
  9342. * <p>The simulation uses <a
  9343. * href="http://en.wikipedia.org/wiki/Verlet_integration">Position Verlet</a>
  9344. * integration, due to the ease with which <a
  9345. * href="http://www.teknikus.dk/tj/gdc2001.htm">geometric constraints</a> can be
  9346. * implemented. For each time step, Verlet integration is performed, new forces
  9347. * are accumulated, and then constraints are applied.
  9348. *
  9349. * <p>The simulation makes two simplifying assumptions: all particles are
  9350. * equal-mass, and the time step of the simulation is fixed. It would be easy to
  9351. * incorporate variable-mass particles as a future enhancement. Variable time
  9352. * steps are also possible, but are likely to introduce instability in the
  9353. * simulation.
  9354. *
  9355. * <p>This class can be used directly to simulate particle interaction.
  9356. * Alternatively, for network diagrams, see {@link pv.Layout.Force}.
  9357. *
  9358. * @param {array} particles an array of {@link pv.Particle}s to simulate.
  9359. * @see pv.Layout.Force
  9360. * @see pv.Force
  9361. * @see pv.Constraint
  9362. */
  9363. pv.Simulation = function(particles) {
  9364. for (var i = 0; i < particles.length; i++) this.particle(particles[i]);
  9365. };
  9366. /**
  9367. * The particles in the simulation. Particles are stored as a linked list; this
  9368. * field represents the first particle in the simulation.
  9369. *
  9370. * @field
  9371. * @type pv.Particle
  9372. * @name pv.Simulation.prototype.particles
  9373. */
  9374. /**
  9375. * The forces in the simulation. Forces are stored as a linked list; this field
  9376. * represents the first force in the simulation.
  9377. *
  9378. * @field
  9379. * @type pv.Force
  9380. * @name pv.Simulation.prototype.forces
  9381. */
  9382. /**
  9383. * The constraints in the simulation. Constraints are stored as a linked list;
  9384. * this field represents the first constraint in the simulation.
  9385. *
  9386. * @field
  9387. * @type pv.Constraint
  9388. * @name pv.Simulation.prototype.constraints
  9389. */
  9390. /**
  9391. * Adds the specified particle to the simulation.
  9392. *
  9393. * @param {pv.Particle} p the new particle.
  9394. * @returns {pv.Simulation} this.
  9395. */
  9396. pv.Simulation.prototype.particle = function(p) {
  9397. p.next = this.particles;
  9398. /* Default velocities and forces to zero if unset. */
  9399. if (isNaN(p.px)) p.px = p.x;
  9400. if (isNaN(p.py)) p.py = p.y;
  9401. if (isNaN(p.fx)) p.fx = 0;
  9402. if (isNaN(p.fy)) p.fy = 0;
  9403. this.particles = p;
  9404. return this;
  9405. };
  9406. /**
  9407. * Adds the specified force to the simulation.
  9408. *
  9409. * @param {pv.Force} f the new force.
  9410. * @returns {pv.Simulation} this.
  9411. */
  9412. pv.Simulation.prototype.force = function(f) {
  9413. f.next = this.forces;
  9414. this.forces = f;
  9415. return this;
  9416. };
  9417. /**
  9418. * Adds the specified constraint to the simulation.
  9419. *
  9420. * @param {pv.Constraint} c the new constraint.
  9421. * @returns {pv.Simulation} this.
  9422. */
  9423. pv.Simulation.prototype.constraint = function(c) {
  9424. c.next = this.constraints;
  9425. this.constraints = c;
  9426. return this;
  9427. };
  9428. /**
  9429. * Apply constraints, and then set the velocities to zero.
  9430. *
  9431. * @returns {pv.Simulation} this.
  9432. */
  9433. pv.Simulation.prototype.stabilize = function(n) {
  9434. var c;
  9435. if (!arguments.length) n = 3; // TODO use cooling schedule
  9436. for (var i = 0; i < n; i++) {
  9437. var q = new pv.Quadtree(this.particles);
  9438. for (c = this.constraints; c; c = c.next) c.apply(this.particles, q);
  9439. }
  9440. for (var p = this.particles; p; p = p.next) {
  9441. p.px = p.x;
  9442. p.py = p.y;
  9443. }
  9444. return this;
  9445. };
  9446. /**
  9447. * Advances the simulation one time-step.
  9448. */
  9449. pv.Simulation.prototype.step = function() {
  9450. var p, f, c;
  9451. /*
  9452. * Assumptions:
  9453. * - The mass (m) of every particles is 1.
  9454. * - The time step (dt) is 1.
  9455. */
  9456. /* Position Verlet integration. */
  9457. for (p = this.particles; p; p = p.next) {
  9458. var px = p.px, py = p.py;
  9459. p.px = p.x;
  9460. p.py = p.y;
  9461. p.x += p.vx = ((p.x - px) + p.fx);
  9462. p.y += p.vy = ((p.y - py) + p.fy);
  9463. }
  9464. /* Apply constraints, then accumulate new forces. */
  9465. var q = new pv.Quadtree(this.particles);
  9466. for (c = this.constraints; c; c = c.next) c.apply(this.particles, q);
  9467. for (p = this.particles; p; p = p.next) p.fx = p.fy = 0;
  9468. for (f = this.forces; f; f = f.next) f.apply(this.particles, q);
  9469. };
  9470. /**
  9471. * Constructs a new quadtree for the specified array of particles.
  9472. *
  9473. * @class Represents a quadtree: a two-dimensional recursive spatial
  9474. * subdivision. This particular implementation uses square partitions, dividing
  9475. * each square into four equally-sized squares. Each particle exists in a unique
  9476. * node; if multiple particles are in the same position, some particles may be
  9477. * stored on internal nodes rather than leaf nodes.
  9478. *
  9479. * <p>This quadtree can be used to accelerate various spatial operations, such
  9480. * as the Barnes-Hut approximation for computing n-body forces, or collision
  9481. * detection.
  9482. *
  9483. * @see pv.Force.charge
  9484. * @see pv.Constraint.collision
  9485. * @param {pv.Particle} particles the linked list of particles.
  9486. */
  9487. pv.Quadtree = function(particles) {
  9488. var p;
  9489. /* Compute bounds. */
  9490. var x1 = Number.POSITIVE_INFINITY, y1 = x1,
  9491. x2 = Number.NEGATIVE_INFINITY, y2 = x2;
  9492. for (p = particles; p; p = p.next) {
  9493. if (p.x < x1) x1 = p.x;
  9494. if (p.y < y1) y1 = p.y;
  9495. if (p.x > x2) x2 = p.x;
  9496. if (p.y > y2) y2 = p.y;
  9497. }
  9498. /* Squarify the bounds. */
  9499. var dx = x2 - x1, dy = y2 - y1;
  9500. if (dx > dy) y2 = y1 + dx;
  9501. else x2 = x1 + dy;
  9502. this.xMin = x1;
  9503. this.yMin = y1;
  9504. this.xMax = x2;
  9505. this.yMax = y2;
  9506. /**
  9507. * @ignore Recursively inserts the specified particle <i>p</i> at the node
  9508. * <i>n</i> or one of its descendants. The bounds are defined by [<i>x1</i>,
  9509. * <i>x2</i>] and [<i>y1</i>, <i>y2</i>].
  9510. */
  9511. function insert(n, p, x1, y1, x2, y2) {
  9512. if (isNaN(p.x) || isNaN(p.y)) return; // ignore invalid particles
  9513. if (n.leaf) {
  9514. if (n.p) {
  9515. /*
  9516. * If the particle at this leaf node is at the same position as the new
  9517. * particle we are adding, we leave the particle associated with the
  9518. * internal node while adding the new particle to a child node. This
  9519. * avoids infinite recursion.
  9520. */
  9521. if ((Math.abs(n.p.x - p.x) + Math.abs(n.p.y - p.y)) < .01) {
  9522. insertChild(n, p, x1, y1, x2, y2);
  9523. } else {
  9524. var v = n.p;
  9525. n.p = null;
  9526. insertChild(n, v, x1, y1, x2, y2);
  9527. insertChild(n, p, x1, y1, x2, y2);
  9528. }
  9529. } else {
  9530. n.p = p;
  9531. }
  9532. } else {
  9533. insertChild(n, p, x1, y1, x2, y2);
  9534. }
  9535. }
  9536. /**
  9537. * @ignore Recursively inserts the specified particle <i>p</i> into a
  9538. * descendant of node <i>n</i>. The bounds are defined by [<i>x1</i>,
  9539. * <i>x2</i>] and [<i>y1</i>, <i>y2</i>].
  9540. */
  9541. function insertChild(n, p, x1, y1, x2, y2) {
  9542. /* Compute the split point, and the quadrant in which to insert p. */
  9543. var sx = (x1 + x2) * .5,
  9544. sy = (y1 + y2) * .5,
  9545. right = p.x >= sx,
  9546. bottom = p.y >= sy;
  9547. /* Recursively insert into the child node. */
  9548. n.leaf = false;
  9549. switch ((bottom << 1) + right) {
  9550. case 0: n = n.c1 || (n.c1 = new pv.Quadtree.Node()); break;
  9551. case 1: n = n.c2 || (n.c2 = new pv.Quadtree.Node()); break;
  9552. case 2: n = n.c3 || (n.c3 = new pv.Quadtree.Node()); break;
  9553. case 3: n = n.c4 || (n.c4 = new pv.Quadtree.Node()); break;
  9554. }
  9555. /* Update the bounds as we recurse. */
  9556. if (right) x1 = sx; else x2 = sx;
  9557. if (bottom) y1 = sy; else y2 = sy;
  9558. insert(n, p, x1, y1, x2, y2);
  9559. }
  9560. /* Insert all particles. */
  9561. this.root = new pv.Quadtree.Node();
  9562. for (p = particles; p; p = p.next) insert(this.root, p, x1, y1, x2, y2);
  9563. };
  9564. /**
  9565. * The root node of the quadtree.
  9566. *
  9567. * @type pv.Quadtree.Node
  9568. * @name pv.Quadtree.prototype.root
  9569. */
  9570. /**
  9571. * The minimum x-coordinate value of all contained particles.
  9572. *
  9573. * @type number
  9574. * @name pv.Quadtree.prototype.xMin
  9575. */
  9576. /**
  9577. * The maximum x-coordinate value of all contained particles.
  9578. *
  9579. * @type number
  9580. * @name pv.Quadtree.prototype.xMax
  9581. */
  9582. /**
  9583. * The minimum y-coordinate value of all contained particles.
  9584. *
  9585. * @type number
  9586. * @name pv.Quadtree.prototype.yMin
  9587. */
  9588. /**
  9589. * The maximum y-coordinate value of all contained particles.
  9590. *
  9591. * @type number
  9592. * @name pv.Quadtree.prototype.yMax
  9593. */
  9594. /**
  9595. * Constructs a new node.
  9596. *
  9597. * @class A node in a quadtree.
  9598. *
  9599. * @see pv.Quadtree
  9600. */
  9601. pv.Quadtree.Node = function() {
  9602. /*
  9603. * Prepopulating all attributes significantly increases performance! Also,
  9604. * letting the language interpreter manage garbage collection was moderately
  9605. * faster than creating a cache pool.
  9606. */
  9607. this.leaf = true;
  9608. this.c1 = null;
  9609. this.c2 = null;
  9610. this.c3 = null;
  9611. this.c4 = null;
  9612. this.p = null;
  9613. };
  9614. /**
  9615. * True if this node is a leaf node; i.e., it has no children. Note that both
  9616. * leaf nodes and non-leaf (internal) nodes may have associated particles. If
  9617. * this is a non-leaf node, then at least one of {@link #c1}, {@link #c2},
  9618. * {@link #c3} or {@link #c4} is guaranteed to be non-null.
  9619. *
  9620. * @type boolean
  9621. * @name pv.Quadtree.Node.prototype.leaf
  9622. */
  9623. /**
  9624. * The particle associated with this node, if any.
  9625. *
  9626. * @type pv.Particle
  9627. * @name pv.Quadtree.Node.prototype.p
  9628. */
  9629. /**
  9630. * The child node for the second quadrant, if any.
  9631. *
  9632. * @type pv.Quadtree.Node
  9633. * @name pv.Quadtree.Node.prototype.c2
  9634. */
  9635. /**
  9636. * The child node for the third quadrant, if any.
  9637. *
  9638. * @type pv.Quadtree.Node
  9639. * @name pv.Quadtree.Node.prototype.c3
  9640. */
  9641. /**
  9642. * The child node for the fourth quadrant, if any.
  9643. *
  9644. * @type pv.Quadtree.Node
  9645. * @name pv.Quadtree.Node.prototype.c4
  9646. */
  9647. /**
  9648. * Abstract; see an implementing class.
  9649. *
  9650. * @class Represents a force that acts on particles. Note that this interface
  9651. * does not specify how to bind a force to specific particles; in general,
  9652. * forces are applied globally to all particles. However, some forces may be
  9653. * applied to specific particles or between particles, such as spring forces,
  9654. * through additional specialization.
  9655. *
  9656. * @see pv.Simulation
  9657. * @see pv.Particle
  9658. * @see pv.Force.charge
  9659. * @see pv.Force.drag
  9660. * @see pv.Force.spring
  9661. */
  9662. pv.Force = {};
  9663. /**
  9664. * Applies this force to the specified particles.
  9665. *
  9666. * @function
  9667. * @name pv.Force.prototype.apply
  9668. * @param {pv.Particle} particles particles to which to apply this force.
  9669. * @param {pv.Quadtree} q a quadtree for spatial acceleration.
  9670. */
  9671. /**
  9672. * Constructs a new charge force, with an optional charge constant. The charge
  9673. * constant can be negative for repulsion (e.g., particles with electrical
  9674. * charge of equal sign), or positive for attraction (e.g., massive particles
  9675. * with mutual gravity). The default charge constant is -40.
  9676. *
  9677. * @class An n-body force, as defined by Coulomb's law or Newton's law of
  9678. * gravitation, inversely proportional to the square of the distance between
  9679. * particles. Note that the force is independent of the <i>mass</i> of the
  9680. * associated particles, and that the particles do not have charges of varying
  9681. * magnitude; instead, the attraction or repulsion of all particles is globally
  9682. * specified as the charge {@link #constant}.
  9683. *
  9684. * <p>This particular implementation uses the Barnes-Hut algorithm. For details,
  9685. * see <a
  9686. * href="http://www.nature.com/nature/journal/v324/n6096/abs/324446a0.html">"A
  9687. * hierarchical O(N log N) force-calculation algorithm"</a>, J. Barnes &amp;
  9688. * P. Hut, <i>Nature</i> 1986.
  9689. *
  9690. * @name pv.Force.charge
  9691. * @param {number} [k] the charge constant.
  9692. */
  9693. pv.Force.charge = function(k) {
  9694. var min = 2, // minimum distance at which to observe forces
  9695. min1 = 1 / min,
  9696. max = 500, // maximum distance at which to observe forces
  9697. max1 = 1 / max,
  9698. theta = .9, // Barnes-Hut theta approximation constant
  9699. force = {};
  9700. if (!arguments.length) k = -40; // default charge constant (repulsion)
  9701. /**
  9702. * Sets or gets the charge constant. If an argument is specified, it is the
  9703. * new charge constant. The charge constant can be negative for repulsion
  9704. * (e.g., particles with electrical charge of equal sign), or positive for
  9705. * attraction (e.g., massive particles with mutual gravity). The default
  9706. * charge constant is -40.
  9707. *
  9708. * @function
  9709. * @name pv.Force.charge.prototype.constant
  9710. * @param {number} x the charge constant.
  9711. * @returns {pv.Force.charge} this.
  9712. */
  9713. force.constant = function(x) {
  9714. if (arguments.length) {
  9715. k = Number(x);
  9716. return force;
  9717. }
  9718. return k;
  9719. };
  9720. /**
  9721. * Sets or gets the domain; specifies the minimum and maximum domain within
  9722. * which charge forces are applied. A minimum distance threshold avoids
  9723. * applying forces that are two strong (due to granularity of the simulation's
  9724. * numeric integration). A maximum distance threshold improves performance by
  9725. * skipping force calculations for particles that are far apart.
  9726. *
  9727. * <p>The default domain is [2, 500].
  9728. *
  9729. * @function
  9730. * @name pv.Force.charge.prototype.domain
  9731. * @param {number} a
  9732. * @param {number} b
  9733. * @returns {pv.Force.charge} this.
  9734. */
  9735. force.domain = function(a, b) {
  9736. if (arguments.length) {
  9737. min = Number(a);
  9738. min1 = 1 / min;
  9739. max = Number(b);
  9740. max1 = 1 / max;
  9741. return force;
  9742. }
  9743. return [min, max];
  9744. };
  9745. /**
  9746. * Sets or gets the Barnes-Hut approximation factor. The Barnes-Hut
  9747. * approximation criterion is the ratio of the size of the quadtree node to
  9748. * the distance from the point to the node's center of mass is beneath some
  9749. * threshold.
  9750. *
  9751. * @function
  9752. * @name pv.Force.charge.prototype.theta
  9753. * @param {number} x the new Barnes-Hut approximation factor.
  9754. * @returns {pv.Force.charge} this.
  9755. */
  9756. force.theta = function(x) {
  9757. if (arguments.length) {
  9758. theta = Number(x);
  9759. return force;
  9760. }
  9761. return theta;
  9762. };
  9763. /**
  9764. * @ignore Recursively computes the center of charge for each node in the
  9765. * quadtree. This is equivalent to the center of mass, assuming that all
  9766. * particles have unit weight.
  9767. */
  9768. function accumulate(n) {
  9769. var cx = 0, cy = 0;
  9770. n.cn = 0;
  9771. function accumulateChild(c) {
  9772. accumulate(c);
  9773. n.cn += c.cn;
  9774. cx += c.cn * c.cx;
  9775. cy += c.cn * c.cy;
  9776. }
  9777. if (!n.leaf) {
  9778. if (n.c1) accumulateChild(n.c1);
  9779. if (n.c2) accumulateChild(n.c2);
  9780. if (n.c3) accumulateChild(n.c3);
  9781. if (n.c4) accumulateChild(n.c4);
  9782. }
  9783. if (n.p) {
  9784. n.cn += k;
  9785. cx += k * n.p.x;
  9786. cy += k * n.p.y;
  9787. }
  9788. n.cx = cx / n.cn;
  9789. n.cy = cy / n.cn;
  9790. }
  9791. /**
  9792. * @ignore Recursively computes forces on the given particle using the given
  9793. * quadtree node. The Barnes-Hut approximation criterion is the ratio of the
  9794. * size of the quadtree node to the distance from the point to the node's
  9795. * center of mass is beneath some threshold.
  9796. */
  9797. function forces(n, p, x1, y1, x2, y2) {
  9798. var dx = n.cx - p.x,
  9799. dy = n.cy - p.y,
  9800. dn = 1 / Math.sqrt(dx * dx + dy * dy);
  9801. /* Barnes-Hut criterion. */
  9802. if ((n.leaf && (n.p != p)) || ((x2 - x1) * dn < theta)) {
  9803. if (dn < max1) return;
  9804. if (dn > min1) dn = min1;
  9805. var kc = n.cn * dn * dn * dn,
  9806. fx = dx * kc,
  9807. fy = dy * kc;
  9808. p.fx += fx;
  9809. p.fy += fy;
  9810. } else if (!n.leaf) {
  9811. var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5;
  9812. if (n.c1) forces(n.c1, p, x1, y1, sx, sy);
  9813. if (n.c2) forces(n.c2, p, sx, y1, x2, sy);
  9814. if (n.c3) forces(n.c3, p, x1, sy, sx, y2);
  9815. if (n.c4) forces(n.c4, p, sx, sy, x2, y2);
  9816. if (dn < max1) return;
  9817. if (dn > min1) dn = min1;
  9818. if (n.p && (n.p != p)) {
  9819. var kc = k * dn * dn * dn,
  9820. fx = dx * kc,
  9821. fy = dy * kc;
  9822. p.fx += fx;
  9823. p.fy += fy;
  9824. }
  9825. }
  9826. }
  9827. /**
  9828. * Applies this force to the specified particles. The force is applied between
  9829. * all pairs of particles within the domain, using the specified quadtree to
  9830. * accelerate n-body force calculation using the Barnes-Hut approximation
  9831. * criterion.
  9832. *
  9833. * @function
  9834. * @name pv.Force.charge.prototype.apply
  9835. * @param {pv.Particle} particles particles to which to apply this force.
  9836. * @param {pv.Quadtree} q a quadtree for spatial acceleration.
  9837. */
  9838. force.apply = function(particles, q) {
  9839. accumulate(q.root);
  9840. for (var p = particles; p; p = p.next) {
  9841. forces(q.root, p, q.xMin, q.yMin, q.xMax, q.yMax);
  9842. }
  9843. };
  9844. return force;
  9845. };
  9846. /**
  9847. * Constructs a new drag force with the specified constant.
  9848. *
  9849. * @class Implements a drag force, simulating friction. The drag force is
  9850. * applied in the opposite direction of the particle's velocity. Since Position
  9851. * Verlet integration does not track velocities explicitly, the error term with
  9852. * this estimate of velocity is fairly high, so the drag force may be
  9853. * inaccurate.
  9854. *
  9855. * @extends pv.Force
  9856. * @param {number} k the drag constant.
  9857. * @see #constant
  9858. */
  9859. pv.Force.drag = function(k) {
  9860. var force = {};
  9861. if (!arguments.length) k = .1; // default drag constant
  9862. /**
  9863. * Sets or gets the drag constant, in the range [0,1]. The default drag
  9864. * constant is 0.1. The drag forces scales linearly with the particle's
  9865. * velocity based on the given drag constant.
  9866. *
  9867. * @function
  9868. * @name pv.Force.drag.prototype.constant
  9869. * @param {number} x the new drag constant.
  9870. * @returns {pv.Force.drag} this, or the current drag constant.
  9871. */
  9872. force.constant = function(x) {
  9873. if (arguments.length) { k = x; return force; }
  9874. return k;
  9875. };
  9876. /**
  9877. * Applies this force to the specified particles.
  9878. *
  9879. * @function
  9880. * @name pv.Force.drag.prototype.apply
  9881. * @param {pv.Particle} particles particles to which to apply this force.
  9882. */
  9883. force.apply = function(particles) {
  9884. if (k) for (var p = particles; p; p = p.next) {
  9885. p.fx -= k * p.vx;
  9886. p.fy -= k * p.vy;
  9887. }
  9888. };
  9889. return force;
  9890. };
  9891. /**
  9892. * Constructs a new spring force with the specified constant. The links
  9893. * associated with this spring force must be specified before the spring force
  9894. * can be applied.
  9895. *
  9896. * @class Implements a spring force, per Hooke's law. The spring force can be
  9897. * configured with a tension constant, rest length, and damping factor. The
  9898. * tension and damping will automatically be normalized using the inverse square
  9899. * root of the maximum link degree of attached nodes; this makes springs weaker
  9900. * between nodes of high link degree.
  9901. *
  9902. * <p>Unlike other forces (such as charge and drag forces) which may be applied
  9903. * globally, spring forces are only applied between linked particles. Therefore,
  9904. * an array of links must be specified before this force can be applied; the
  9905. * links should be an array of {@link pv.Layout.Network.Link}s. See also
  9906. * {@link pv.Layout.Force} for an example of using spring and charge forces for
  9907. * network layout.
  9908. *
  9909. * @extends pv.Force
  9910. * @param {number} k the spring constant.
  9911. * @see #constant
  9912. * @see #links
  9913. */
  9914. pv.Force.spring = function(k) {
  9915. var d = .1, // default damping factor
  9916. l = 20, // default rest length
  9917. links, // links on which to apply spring forces
  9918. kl, // per-spring normalization
  9919. force = {};
  9920. if (!arguments.length) k = .1; // default spring constant (tension)
  9921. /**
  9922. * Sets or gets the links associated with this spring force. Unlike other
  9923. * forces (such as charge and drag forces) which may be applied globally,
  9924. * spring forces are only applied between linked particles. Therefore, an
  9925. * array of links must be specified before this force can be applied; the
  9926. * links should be an array of {@link pv.Layout.Network.Link}s.
  9927. *
  9928. * @function
  9929. * @name pv.Force.spring.prototype.links
  9930. * @param {array} x the new array of links.
  9931. * @returns {pv.Force.spring} this, or the current array of links.
  9932. */
  9933. force.links = function(x) {
  9934. if (arguments.length) {
  9935. links = x;
  9936. kl = x.map(function(l) {
  9937. return 1 / Math.sqrt(Math.max(
  9938. l.sourceNode.linkDegree,
  9939. l.targetNode.linkDegree));
  9940. });
  9941. return force;
  9942. }
  9943. return links;
  9944. };
  9945. /**
  9946. * Sets or gets the spring constant. The default value is 0.1; greater values
  9947. * will result in stronger tension. The spring tension is automatically
  9948. * normalized using the inverse square root of the maximum link degree of
  9949. * attached nodes.
  9950. *
  9951. * @function
  9952. * @name pv.Force.spring.prototype.constant
  9953. * @param {number} x the new spring constant.
  9954. * @returns {pv.Force.spring} this, or the current spring constant.
  9955. */
  9956. force.constant = function(x) {
  9957. if (arguments.length) {
  9958. k = Number(x);
  9959. return force;
  9960. }
  9961. return k;
  9962. };
  9963. /**
  9964. * The spring damping factor, in the range [0,1]. Damping functions
  9965. * identically to drag forces, damping spring bounciness by applying a force
  9966. * in the opposite direction of attached nodes' velocities. The default value
  9967. * is 0.1. The spring damping is automatically normalized using the inverse
  9968. * square root of the maximum link degree of attached nodes.
  9969. *
  9970. * @function
  9971. * @name pv.Force.spring.prototype.damping
  9972. * @param {number} x the new spring damping factor.
  9973. * @returns {pv.Force.spring} this, or the current spring damping factor.
  9974. */
  9975. force.damping = function(x) {
  9976. if (arguments.length) {
  9977. d = Number(x);
  9978. return force;
  9979. }
  9980. return d;
  9981. };
  9982. /**
  9983. * The spring rest length. The default value is 20 pixels.
  9984. *
  9985. * @function
  9986. * @name pv.Force.spring.prototype.length
  9987. * @param {number} x the new spring rest length.
  9988. * @returns {pv.Force.spring} this, or the current spring rest length.
  9989. */
  9990. force.length = function(x) {
  9991. if (arguments.length) {
  9992. l = Number(x);
  9993. return force;
  9994. }
  9995. return l;
  9996. };
  9997. /**
  9998. * Applies this force to the specified particles.
  9999. *
  10000. * @function
  10001. * @name pv.Force.spring.prototype.apply
  10002. * @param {pv.Particle} particles particles to which to apply this force.
  10003. */
  10004. force.apply = function(particles) {
  10005. for (var i = 0; i < links.length; i++) {
  10006. var a = links[i].sourceNode,
  10007. b = links[i].targetNode,
  10008. dx = a.x - b.x,
  10009. dy = a.y - b.y,
  10010. dn = Math.sqrt(dx * dx + dy * dy),
  10011. dd = dn ? (1 / dn) : 1,
  10012. ks = k * kl[i], // normalized tension
  10013. kd = d * kl[i], // normalized damping
  10014. kk = (ks * (dn - l) + kd * (dx * (a.vx - b.vx) + dy * (a.vy - b.vy)) * dd) * dd,
  10015. fx = -kk * (dn ? dx : (.01 * (.5 - Math.random()))),
  10016. fy = -kk * (dn ? dy : (.01 * (.5 - Math.random())));
  10017. a.fx += fx;
  10018. a.fy += fy;
  10019. b.fx -= fx;
  10020. b.fy -= fy;
  10021. }
  10022. };
  10023. return force;
  10024. };
  10025. /**
  10026. * Abstract; see an implementing class.
  10027. *
  10028. * @class Represents a constraint that acts on particles. Note that this
  10029. * interface does not specify how to bind a constraint to specific particles; in
  10030. * general, constraints are applied globally to all particles. However, some
  10031. * constraints may be applied to specific particles or between particles, such
  10032. * as position constraints, through additional specialization.
  10033. *
  10034. * @see pv.Simulation
  10035. * @see pv.Particle
  10036. * @see pv.Constraint.bound
  10037. * @see pv.Constraint.collision
  10038. * @see pv.Constraint.position
  10039. */
  10040. pv.Constraint = {};
  10041. /**
  10042. * Applies this constraint to the specified particles.
  10043. *
  10044. * @function
  10045. * @name pv.Constraint.prototype.apply
  10046. * @param {pv.Particle} particles particles to which to apply this constraint.
  10047. * @param {pv.Quadtree} q a quadtree for spatial acceleration.
  10048. * @returns {pv.Constraint} this.
  10049. */
  10050. /**
  10051. * Constructs a new collision constraint. The default search radius is 10, and
  10052. * the default repeat count is 1. A radius function must be specified to compute
  10053. * the radius of particles.
  10054. *
  10055. * @class Constraints circles to avoid overlap. Each particle is treated as a
  10056. * circle, with the radius of the particle computed using a specified function.
  10057. * For example, if the particle has an <tt>r</tt> attribute storing the radius,
  10058. * the radius <tt>function(d) d.r</tt> specifies a collision constraint using
  10059. * this radius. The radius function is passed each {@link pv.Particle} as the
  10060. * first argument.
  10061. *
  10062. * <p>To accelerate collision detection, this implementation uses a quadtree and
  10063. * a search radius. The search radius is computed as the maximum radius of all
  10064. * particles in the simulation.
  10065. *
  10066. * @see pv.Constraint
  10067. * @param {function} radius the radius function.
  10068. */
  10069. pv.Constraint.collision = function(radius) {
  10070. var n = 1, // number of times to repeat the constraint
  10071. r1,
  10072. px1,
  10073. py1,
  10074. px2,
  10075. py2,
  10076. constraint = {};
  10077. if (!arguments.length) r1 = 10; // default search radius
  10078. /**
  10079. * Sets or gets the repeat count. If the repeat count is greater than 1, the
  10080. * constraint will be applied repeatedly; this is a form of the Gauss-Seidel
  10081. * method for constraints relaxation. Repeating the collision constraint makes
  10082. * the constraint have more of an effect when there is a potential for many
  10083. * co-occurring collisions.
  10084. *
  10085. * @function
  10086. * @name pv.Constraint.collision.prototype.repeat
  10087. * @param {number} x the number of times to repeat this constraint.
  10088. * @returns {pv.Constraint.collision} this.
  10089. */
  10090. constraint.repeat = function(x) {
  10091. if (arguments.length) {
  10092. n = Number(x);
  10093. return constraint;
  10094. }
  10095. return n;
  10096. };
  10097. /** @private */
  10098. function constrain(n, p, x1, y1, x2, y2) {
  10099. if (!n.leaf) {
  10100. var sx = (x1 + x2) * .5,
  10101. sy = (y1 + y2) * .5,
  10102. top = sy > py1,
  10103. bottom = sy < py2,
  10104. left = sx > px1,
  10105. right = sx < px2;
  10106. if (top) {
  10107. if (n.c1 && left) constrain(n.c1, p, x1, y1, sx, sy);
  10108. if (n.c2 && right) constrain(n.c2, p, sx, y1, x2, sy);
  10109. }
  10110. if (bottom) {
  10111. if (n.c3 && left) constrain(n.c3, p, x1, sy, sx, y2);
  10112. if (n.c4 && right) constrain(n.c4, p, sx, sy, x2, y2);
  10113. }
  10114. }
  10115. if (n.p && (n.p != p)) {
  10116. var dx = p.x - n.p.x,
  10117. dy = p.y - n.p.y,
  10118. l = Math.sqrt(dx * dx + dy * dy),
  10119. d = r1 + radius(n.p);
  10120. if (l < d) {
  10121. var k = (l - d) / l * .5;
  10122. dx *= k;
  10123. dy *= k;
  10124. p.x -= dx;
  10125. p.y -= dy;
  10126. n.p.x += dx;
  10127. n.p.y += dy;
  10128. }
  10129. }
  10130. }
  10131. /**
  10132. * Applies this constraint to the specified particles.
  10133. *
  10134. * @function
  10135. * @name pv.Constraint.collision.prototype.apply
  10136. * @param {pv.Particle} particles particles to which to apply this constraint.
  10137. * @param {pv.Quadtree} q a quadtree for spatial acceleration.
  10138. */
  10139. constraint.apply = function(particles, q) {
  10140. var p, r, max = -Infinity;
  10141. for (p = particles; p; p = p.next) {
  10142. r = radius(p);
  10143. if (r > max) max = r;
  10144. }
  10145. for (var i = 0; i < n; i++) {
  10146. for (p = particles; p; p = p.next) {
  10147. r = (r1 = radius(p)) + max;
  10148. px1 = p.x - r;
  10149. px2 = p.x + r;
  10150. py1 = p.y - r;
  10151. py2 = p.y + r;
  10152. constrain(q.root, p, q.xMin, q.yMin, q.xMax, q.yMax);
  10153. }
  10154. }
  10155. };
  10156. return constraint;
  10157. };
  10158. /**
  10159. * Constructs a default position constraint using the <tt>fix</tt> attribute.
  10160. * An optional position function can be specified to determine how the fixed
  10161. * position per-particle is determined.
  10162. *
  10163. * @class Constraints particles to a fixed position. The fixed position per
  10164. * particle is determined using a given position function, which defaults to
  10165. * <tt>function(d) d.fix</tt>.
  10166. *
  10167. * <p>If the position function returns null, then no position constraint is
  10168. * applied to the given particle. Otherwise, the particle's position is set to
  10169. * the returned position, as expressed by a {@link pv.Vector}. (Note: the
  10170. * position does not need to be an instance of <tt>pv.Vector</tt>, but simply an
  10171. * object with <tt>x</tt> and <tt>y</tt> attributes.)
  10172. *
  10173. * <p>This constraint also supports a configurable alpha parameter, which
  10174. * defaults to 1. If the alpha parameter is in the range [0,1], then rather than
  10175. * setting the particle's new position directly to the position returned by the
  10176. * supplied position function, the particle's position is interpolated towards
  10177. * the fixed position. This results is a smooth (exponential) drift towards the
  10178. * fixed position, which can increase the stability of the physics simulation.
  10179. * In addition, the alpha parameter can be decayed over time, relaxing the
  10180. * position constraint, which helps to stabilize on an optimal solution.
  10181. *
  10182. * @param {function} [f] the position function.
  10183. */
  10184. pv.Constraint.position = function(f) {
  10185. var a = 1, // default alpha
  10186. constraint = {};
  10187. if (!arguments.length) /** @ignore */ f = function(p) { return p.fix; };
  10188. /**
  10189. * Sets or gets the alpha parameter for position interpolation. If the alpha
  10190. * parameter is in the range [0,1], then rather than setting the particle's
  10191. * new position directly to the position returned by the supplied position
  10192. * function, the particle's position is interpolated towards the fixed
  10193. * position.
  10194. *
  10195. * @function
  10196. * @name pv.Constraint.position.prototype.alpha
  10197. * @param {number} x the new alpha parameter, in the range [0,1].
  10198. * @returns {pv.Constraint.position} this.
  10199. */
  10200. constraint.alpha = function(x) {
  10201. if (arguments.length) {
  10202. a = Number(x);
  10203. return constraint;
  10204. }
  10205. return a;
  10206. };
  10207. /**
  10208. * Applies this constraint to the specified particles.
  10209. *
  10210. * @function
  10211. * @name pv.Constraint.position.prototype.apply
  10212. * @param {pv.Particle} particles particles to which to apply this constraint.
  10213. */
  10214. constraint.apply = function(particles) {
  10215. for (var p = particles; p; p = p.next) {
  10216. var v = f(p);
  10217. if (v) {
  10218. p.x += (v.x - p.x) * a;
  10219. p.y += (v.y - p.y) * a;
  10220. p.fx = p.fy = p.vx = p.vy = 0;
  10221. }
  10222. }
  10223. };
  10224. return constraint;
  10225. };
  10226. /**
  10227. * Constructs a new bound constraint. Before the constraint can be used, the
  10228. * {@link #x} and {@link #y} methods must be call to specify the bounds.
  10229. *
  10230. * @class Constrains particles to within fixed rectangular bounds. For example,
  10231. * this constraint can be used to constrain particles in a physics simulation
  10232. * within the bounds of an enclosing panel.
  10233. *
  10234. * <p>Note that the current implementation treats particles as points, with no
  10235. * area. If the particles are rendered as dots, be sure to include some
  10236. * additional padding to inset the bounds such that the edges of the dots do not
  10237. * get clipped by the panel bounds. If the particles have different radii, this
  10238. * constraint would need to be extended using a radius function, similar to
  10239. * {@link pv.Constraint.collision}.
  10240. *
  10241. * @see pv.Layout.Force
  10242. * @extends pv.Constraint
  10243. */
  10244. pv.Constraint.bound = function() {
  10245. var constraint = {},
  10246. x,
  10247. y;
  10248. /**
  10249. * Sets or gets the bounds on the x-coordinate.
  10250. *
  10251. * @function
  10252. * @name pv.Constraint.bound.prototype.x
  10253. * @param {number} min the minimum allowed x-coordinate.
  10254. * @param {number} max the maximum allowed x-coordinate.
  10255. * @returns {pv.Constraint.bound} this.
  10256. */
  10257. constraint.x = function(min, max) {
  10258. if (arguments.length) {
  10259. x = {min: Math.min(min, max), max: Math.max(min, max)};
  10260. return this;
  10261. }
  10262. return x;
  10263. };
  10264. /**
  10265. * Sets or gets the bounds on the y-coordinate.
  10266. *
  10267. * @function
  10268. * @name pv.Constraint.bound.prototype.y
  10269. * @param {number} min the minimum allowed y-coordinate.
  10270. * @param {number} max the maximum allowed y-coordinate.
  10271. * @returns {pv.Constraint.bound} this.
  10272. */
  10273. constraint.y = function(min, max) {
  10274. if (arguments.length) {
  10275. y = {min: Math.min(min, max), max: Math.max(min, max)};
  10276. return this;
  10277. }
  10278. return y;
  10279. };
  10280. /**
  10281. * Applies this constraint to the specified particles.
  10282. *
  10283. * @function
  10284. * @name pv.Constraint.bound.prototype.apply
  10285. * @param {pv.Particle} particles particles to which to apply this constraint.
  10286. */
  10287. constraint.apply = function(particles) {
  10288. if (x) for (var p = particles; p; p = p.next) {
  10289. p.x = p.x < x.min ? x.min : (p.x > x.max ? x.max : p.x);
  10290. }
  10291. if (y) for (var p = particles; p; p = p.next) {
  10292. p.y = p.y < y.min ? y.min : (p.y > y.max ? y.max : p.y);
  10293. }
  10294. };
  10295. return constraint;
  10296. };
  10297. /**
  10298. * Constructs a new, empty layout with default properties. Layouts are not
  10299. * typically constructed directly; instead, a concrete subclass is added to an
  10300. * existing panel via {@link pv.Mark#add}.
  10301. *
  10302. * @class Represents an abstract layout, encapsulating a visualization technique
  10303. * such as a streamgraph or treemap. Layouts are themselves containers,
  10304. * extending from {@link pv.Panel}, and defining a set of mark prototypes as
  10305. * children. These mark prototypes provide default properties that together
  10306. * implement the given visualization technique.
  10307. *
  10308. * <p>Layouts do not initially contain any marks; any exported marks (such as a
  10309. * network layout's <tt>link</tt> and <tt>node</tt>) are intended to be used as
  10310. * prototypes. By adding a concrete mark, such as a {@link pv.Bar}, to the
  10311. * appropriate mark prototype, the mark is added to the layout and inherits the
  10312. * given properties. This approach allows further customization of the layout,
  10313. * either by choosing a different mark type to add, or more simply by overriding
  10314. * some of the layout's defined properties.
  10315. *
  10316. * <p>Each concrete layout, such as treemap or circle-packing, has different
  10317. * behavior and may export different mark prototypes, depending on what marks
  10318. * are typically needed to render the desired visualization. Therefore it is
  10319. * important to understand how each layout is structured, such that the provided
  10320. * mark prototypes are used appropriately.
  10321. *
  10322. * <p>In addition to the mark prototypes, layouts may define custom properties
  10323. * that affect the overall behavior of the layout. For example, a treemap layout
  10324. * might use a property to specify which layout algorithm to use. These
  10325. * properties are just like other mark properties, and can be defined as
  10326. * constants or as functions. As with panels, the data property can be used to
  10327. * replicate layouts, and properties can be defined to in terms of layout data.
  10328. *
  10329. * @extends pv.Panel
  10330. */
  10331. pv.Layout = function() {
  10332. pv.Panel.call(this);
  10333. };
  10334. pv.Layout.prototype = pv.extend(pv.Panel);
  10335. /**
  10336. * @private Defines a local property with the specified name and cast. Note that
  10337. * although the property method is only defined locally, the cast function is
  10338. * global, which is necessary since properties are inherited!
  10339. *
  10340. * @param {string} name the property name.
  10341. * @param {function} [cast] the cast function for this property.
  10342. */
  10343. pv.Layout.prototype.property = function(name, cast) {
  10344. if (!this.hasOwnProperty("properties")) {
  10345. this.properties = pv.extend(this.properties);
  10346. }
  10347. this.properties[name] = true;
  10348. this.propertyMethod(name, false, pv.Mark.cast[name] = cast);
  10349. return this;
  10350. };
  10351. /**
  10352. * Constructs a new, empty network layout. Layouts are not typically constructed
  10353. * directly; instead, they are added to an existing panel via
  10354. * {@link pv.Mark#add}.
  10355. *
  10356. * @class Represents an abstract layout for network diagrams. This class
  10357. * provides the basic structure for both node-link diagrams (such as
  10358. * force-directed graph layout) and space-filling network diagrams (such as
  10359. * sunbursts and treemaps). Note that "network" here is a general term that
  10360. * includes hierarchical structures; a tree is represented using links from
  10361. * child to parent.
  10362. *
  10363. * <p>Network layouts require the graph data structure to be defined using two
  10364. * properties:<ul>
  10365. *
  10366. * <li><tt>nodes</tt> - an array of objects representing nodes. Objects in this
  10367. * array must conform to the {@link pv.Layout.Network.Node} interface; which is
  10368. * to say, be careful to avoid naming collisions with automatic attributes such
  10369. * as <tt>index</tt> and <tt>linkDegree</tt>. If the nodes property is defined
  10370. * as an array of primitives, such as numbers or strings, these primitives are
  10371. * automatically wrapped in an object; the resulting object's <tt>nodeValue</tt>
  10372. * attribute points to the original primitive value.
  10373. *
  10374. * <p><li><tt>links</tt> - an array of objects representing links. Objects in
  10375. * this array must conform to the {@link pv.Layout.Network.Link} interface; at a
  10376. * minimum, either <tt>source</tt> and <tt>target</tt> indexes or
  10377. * <tt>sourceNode</tt> and <tt>targetNode</tt> references must be set. Note that
  10378. * if the links property is defined after the nodes property, the links can be
  10379. * defined in terms of <tt>this.nodes()</tt>.
  10380. *
  10381. * </ul>
  10382. *
  10383. * <p>Three standard mark prototypes are provided:<ul>
  10384. *
  10385. * <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Dot}. The node
  10386. * mark is added directly to the layout, with the data property defined via the
  10387. * layout's <tt>nodes</tt> property. Properties such as <tt>strokeStyle</tt> and
  10388. * <tt>fillStyle</tt> can be overridden to compute properties from node data
  10389. * dynamically.
  10390. *
  10391. * <p><li><tt>link</tt> - for rendering links; typically a {@link pv.Line}. The
  10392. * link mark is added to a child panel, whose data property is defined as
  10393. * layout's <tt>links</tt> property. The link's data property is then a
  10394. * two-element array of the source node and target node. Thus, poperties such as
  10395. * <tt>strokeStyle</tt> and <tt>fillStyle</tt> can be overridden to compute
  10396. * properties from either the node data (the first argument) or the link data
  10397. * (the second argument; the parent panel data) dynamically.
  10398. *
  10399. * <p><li><tt>label</tt> - for rendering node labels; typically a
  10400. * {@link pv.Label}. The label mark is added directly to the layout, with the
  10401. * data property defined via the layout's <tt>nodes</tt> property. Properties
  10402. * such as <tt>strokeStyle</tt> and <tt>fillStyle</tt> can be overridden to
  10403. * compute properties from node data dynamically.
  10404. *
  10405. * </ul>Note that some network implementations may not support all three
  10406. * standard mark prototypes; for example, space-filling hierarchical layouts
  10407. * typically do not use a <tt>link</tt> prototype, as the parent-child links are
  10408. * implied by the structure of the space-filling <tt>node</tt> marks. Check the
  10409. * specific network layout for implementation details.
  10410. *
  10411. * <p>Network layout properties, including <tt>nodes</tt> and <tt>links</tt>,
  10412. * are typically cached rather than re-evaluated with every call to render. This
  10413. * is a performance optimization, as network layout algorithms can be
  10414. * expensive. If the network structure changes, call {@link #reset} to clear the
  10415. * cache before rendering. Note that although the network layout properties are
  10416. * cached, child mark properties, such as the marks used to render the nodes and
  10417. * links, <i>are not</i>. Therefore, non-structural changes to the network
  10418. * layout, such as changing the color of a mark on mouseover, do not need to
  10419. * reset the layout.
  10420. *
  10421. * @see pv.Layout.Hierarchy
  10422. * @see pv.Layout.Force
  10423. * @see pv.Layout.Matrix
  10424. * @see pv.Layout.Arc
  10425. * @see pv.Layout.Rollup
  10426. * @extends pv.Layout
  10427. */
  10428. pv.Layout.Network = function() {
  10429. pv.Layout.call(this);
  10430. var that = this;
  10431. /* @private Version tracking to cache layout state, improving performance. */
  10432. this.$id = pv.id();
  10433. /**
  10434. * The node prototype. This prototype is intended to be used with a Dot mark
  10435. * in conjunction with the link prototype.
  10436. *
  10437. * @type pv.Mark
  10438. * @name pv.Layout.Network.prototype.node
  10439. */
  10440. (this.node = new pv.Mark()
  10441. .data(function() { return that.nodes(); })
  10442. .strokeStyle("#1f77b4")
  10443. .fillStyle("#fff")
  10444. .left(function(n) { return n.x; })
  10445. .top(function(n) { return n.y; })).parent = this;
  10446. /**
  10447. * The link prototype, which renders edges between source nodes and target
  10448. * nodes. This prototype is intended to be used with a Line mark in
  10449. * conjunction with the node prototype.
  10450. *
  10451. * @type pv.Mark
  10452. * @name pv.Layout.Network.prototype.link
  10453. */
  10454. this.link = new pv.Mark()
  10455. .extend(this.node)
  10456. .data(function(p) { return [p.sourceNode, p.targetNode]; })
  10457. .fillStyle(null)
  10458. .lineWidth(function(d, p) { return p.linkValue * 1.5; })
  10459. .strokeStyle("rgba(0,0,0,.2)");
  10460. this.link.add = function(type) {
  10461. return that.add(pv.Panel)
  10462. .data(function() { return that.links(); })
  10463. .add(type)
  10464. .extend(this);
  10465. };
  10466. /**
  10467. * The node label prototype, which renders the node name adjacent to the node.
  10468. * This prototype is provided as an alternative to using the anchor on the
  10469. * node mark; it is primarily intended to be used with radial node-link
  10470. * layouts, since it provides a convenient mechanism to set the text angle.
  10471. *
  10472. * @type pv.Mark
  10473. * @name pv.Layout.Network.prototype.label
  10474. */
  10475. (this.label = new pv.Mark()
  10476. .extend(this.node)
  10477. .textMargin(7)
  10478. .textBaseline("middle")
  10479. .text(function(n) { return n.nodeName || n.nodeValue; })
  10480. .textAngle(function(n) {
  10481. var a = n.midAngle;
  10482. return pv.Wedge.upright(a) ? a : (a + Math.PI);
  10483. })
  10484. .textAlign(function(n) {
  10485. return pv.Wedge.upright(n.midAngle) ? "left" : "right";
  10486. })).parent = this;
  10487. };
  10488. /**
  10489. * @class Represents a node in a network layout. There is no explicit
  10490. * constructor; this class merely serves to document the attributes that are
  10491. * used on nodes in network layouts. (Note that hierarchical nodes place
  10492. * additional requirements on node representation, vis {@link pv.Dom.Node}.)
  10493. *
  10494. * @see pv.Layout.Network
  10495. * @name pv.Layout.Network.Node
  10496. */
  10497. /**
  10498. * The node index, zero-based. This attribute is populated automatically based
  10499. * on the index in the array returned by the <tt>nodes</tt> property.
  10500. *
  10501. * @type number
  10502. * @name pv.Layout.Network.Node.prototype.index
  10503. */
  10504. /**
  10505. * The link degree; the sum of link values for all incoming and outgoing links.
  10506. * This attribute is populated automatically.
  10507. *
  10508. * @type number
  10509. * @name pv.Layout.Network.Node.prototype.linkDegree
  10510. */
  10511. /**
  10512. * The node name; optional. If present, this attribute will be used to provide
  10513. * the text for node labels. If not present, the label text will fallback to the
  10514. * <tt>nodeValue</tt> attribute.
  10515. *
  10516. * @type string
  10517. * @name pv.Layout.Network.Node.prototype.nodeName
  10518. */
  10519. /**
  10520. * The node value; optional. If present, and no <tt>nodeName</tt> attribute is
  10521. * present, the node value will be used as the label text. This attribute is
  10522. * also automatically populated if the nodes are specified as an array of
  10523. * primitives, such as strings or numbers.
  10524. *
  10525. * @type object
  10526. * @name pv.Layout.Network.Node.prototype.nodeValue
  10527. */
  10528. /**
  10529. * @class Represents a link in a network layout. There is no explicit
  10530. * constructor; this class merely serves to document the attributes that are
  10531. * used on links in network layouts. For hierarchical layouts, this class is
  10532. * used to represent the parent-child links.
  10533. *
  10534. * @see pv.Layout.Network
  10535. * @name pv.Layout.Network.Link
  10536. */
  10537. /**
  10538. * The link value, or weight; optional. If not specified (or not a number), the
  10539. * default value of 1 is used.
  10540. *
  10541. * @type number
  10542. * @name pv.Layout.Network.Link.prototype.linkValue
  10543. */
  10544. /**
  10545. * The link's source node. If not set, this value will be derived from the
  10546. * <tt>source</tt> attribute index.
  10547. *
  10548. * @type pv.Layout.Network.Node
  10549. * @name pv.Layout.Network.Link.prototype.sourceNode
  10550. */
  10551. /**
  10552. * The link's target node. If not set, this value will be derived from the
  10553. * <tt>target</tt> attribute index.
  10554. *
  10555. * @type pv.Layout.Network.Node
  10556. * @name pv.Layout.Network.Link.prototype.targetNode
  10557. */
  10558. /**
  10559. * Alias for <tt>sourceNode</tt>, as expressed by the index of the source node.
  10560. * This attribute is not populated automatically, but may be used as a more
  10561. * convenient identification of the link's source, for example in a static JSON
  10562. * representation.
  10563. *
  10564. * @type number
  10565. * @name pv.Layout.Network.Link.prototype.source
  10566. */
  10567. /**
  10568. * Alias for <tt>targetNode</tt>, as expressed by the index of the target node.
  10569. * This attribute is not populated automatically, but may be used as a more
  10570. * convenient identification of the link's target, for example in a static JSON
  10571. * representation.
  10572. *
  10573. * @type number
  10574. * @name pv.Layout.Network.Link.prototype.target
  10575. */
  10576. /**
  10577. * Alias for <tt>linkValue</tt>. This attribute is not populated automatically,
  10578. * but may be used instead of the <tt>linkValue</tt> attribute when specifying
  10579. * links.
  10580. *
  10581. * @type number
  10582. * @name pv.Layout.Network.Link.prototype.value
  10583. */
  10584. /** @private Transform nodes and links on cast. */
  10585. pv.Layout.Network.prototype = pv.extend(pv.Layout)
  10586. .property("nodes", function(v) {
  10587. return v.map(function(d, i) {
  10588. if (typeof d != "object") d = {nodeValue: d};
  10589. d.index = i;
  10590. d.linkDegree = 0;
  10591. return d;
  10592. });
  10593. })
  10594. .property("links", function(v) {
  10595. return v.map(function(d) {
  10596. if (isNaN(d.linkValue)) d.linkValue = isNaN(d.value) ? 1 : d.value;
  10597. return d;
  10598. });
  10599. });
  10600. /**
  10601. * Resets the cache, such that changes to layout property definitions will be
  10602. * visible on subsequent render. Unlike normal marks (and normal layouts),
  10603. * properties associated with network layouts are not automatically re-evaluated
  10604. * on render; the properties are cached, and any expensive layout algorithms are
  10605. * only run after the layout is explicitly reset.
  10606. *
  10607. * @returns {pv.Layout.Network} this.
  10608. */
  10609. pv.Layout.Network.prototype.reset = function() {
  10610. this.$id = pv.id();
  10611. return this;
  10612. };
  10613. /** @private Skip evaluating properties if cached. */
  10614. pv.Layout.Network.prototype.buildProperties = function(s, properties) {
  10615. if ((s.$id || 0) < this.$id) {
  10616. pv.Layout.prototype.buildProperties.call(this, s, properties);
  10617. }
  10618. };
  10619. /** @private Compute link degrees; map source and target indexes to nodes. */
  10620. pv.Layout.Network.prototype.buildImplied = function(s) {
  10621. pv.Layout.prototype.buildImplied.call(this, s);
  10622. if (s.$id >= this.$id) return true;
  10623. s.$id = this.$id;
  10624. s.links.forEach(function(d) {
  10625. var v = d.linkValue;
  10626. (d.sourceNode || (d.sourceNode = s.nodes[d.source])).linkDegree += v;
  10627. (d.targetNode || (d.targetNode = s.nodes[d.target])).linkDegree += v;
  10628. });
  10629. };
  10630. /**
  10631. * Constructs a new, empty hierarchy layout. Layouts are not typically
  10632. * constructed directly; instead, they are added to an existing panel via
  10633. * {@link pv.Mark#add}.
  10634. *
  10635. * @class Represents an abstract layout for hierarchy diagrams. This class is a
  10636. * specialization of {@link pv.Layout.Network}, providing the basic structure
  10637. * for both hierarchical node-link diagrams (such as Reingold-Tilford trees) and
  10638. * space-filling hierarchy diagrams (such as sunbursts and treemaps).
  10639. *
  10640. * <p>Unlike general network layouts, the <tt>links</tt> property need not be
  10641. * defined explicitly. Instead, the links are computed implicitly from the
  10642. * <tt>parentNode</tt> attribute of the node objects, as defined by the
  10643. * <tt>nodes</tt> property. This implementation is also available as
  10644. * {@link #links}, for reuse with non-hierarchical layouts; for example, to
  10645. * render a tree using force-directed layout.
  10646. *
  10647. * <p>Correspondingly, the <tt>nodes</tt> property is represented as a union of
  10648. * {@link pv.Layout.Network.Node} and {@link pv.Dom.Node}. To construct a node
  10649. * hierarchy from a simple JSON map, use the {@link pv.Dom} operator; this
  10650. * operator also provides an easy way to sort nodes before passing them to the
  10651. * layout.
  10652. *
  10653. * <p>For more details on how to use this layout, see
  10654. * {@link pv.Layout.Network}.
  10655. *
  10656. * @see pv.Layout.Cluster
  10657. * @see pv.Layout.Partition
  10658. * @see pv.Layout.Tree
  10659. * @see pv.Layout.Treemap
  10660. * @see pv.Layout.Indent
  10661. * @see pv.Layout.Pack
  10662. * @extends pv.Layout.Network
  10663. */
  10664. pv.Layout.Hierarchy = function() {
  10665. pv.Layout.Network.call(this);
  10666. this.link.strokeStyle("#ccc");
  10667. };
  10668. pv.Layout.Hierarchy.prototype = pv.extend(pv.Layout.Network);
  10669. /** @private Compute the implied links. (Links are null by default.) */
  10670. pv.Layout.Hierarchy.prototype.buildImplied = function(s) {
  10671. if (!s.links) s.links = pv.Layout.Hierarchy.links.call(this);
  10672. pv.Layout.Network.prototype.buildImplied.call(this, s);
  10673. };
  10674. /** The implied links; computes links using the <tt>parentNode</tt> attribute. */
  10675. pv.Layout.Hierarchy.links = function() {
  10676. return this.nodes()
  10677. .filter(function(n) { return n.parentNode; })
  10678. .map(function(n) {
  10679. return {
  10680. sourceNode: n,
  10681. targetNode: n.parentNode,
  10682. linkValue: 1
  10683. };
  10684. });
  10685. };
  10686. /** @private Provides standard node-link layout based on breadth & depth. */
  10687. pv.Layout.Hierarchy.NodeLink = {
  10688. /** @private */
  10689. buildImplied: function(s) {
  10690. var nodes = s.nodes,
  10691. orient = s.orient,
  10692. horizontal = /^(top|bottom)$/.test(orient),
  10693. w = s.width,
  10694. h = s.height;
  10695. /* Compute default inner and outer radius. */
  10696. if (orient == "radial") {
  10697. var ir = s.innerRadius, or = s.outerRadius;
  10698. if (ir == null) ir = 0;
  10699. if (or == null) or = Math.min(w, h) / 2;
  10700. }
  10701. /** @private Returns the radius of the given node. */
  10702. function radius(n) {
  10703. return n.parentNode ? (n.depth * (or - ir) + ir) : 0;
  10704. }
  10705. /** @private Returns the angle of the given node. */
  10706. function midAngle(n) {
  10707. return (n.parentNode ? (n.breadth - .25) * 2 * Math.PI : 0);
  10708. }
  10709. /** @private */
  10710. function x(n) {
  10711. switch (orient) {
  10712. case "left": return n.depth * w;
  10713. case "right": return w - n.depth * w;
  10714. case "top": return n.breadth * w;
  10715. case "bottom": return w - n.breadth * w;
  10716. case "radial": return w / 2 + radius(n) * Math.cos(n.midAngle);
  10717. }
  10718. }
  10719. /** @private */
  10720. function y(n) {
  10721. switch (orient) {
  10722. case "left": return n.breadth * h;
  10723. case "right": return h - n.breadth * h;
  10724. case "top": return n.depth * h;
  10725. case "bottom": return h - n.depth * h;
  10726. case "radial": return h / 2 + radius(n) * Math.sin(n.midAngle);
  10727. }
  10728. }
  10729. for (var i = 0; i < nodes.length; i++) {
  10730. var n = nodes[i];
  10731. n.midAngle = orient == "radial" ? midAngle(n)
  10732. : horizontal ? Math.PI / 2 : 0;
  10733. n.x = x(n);
  10734. n.y = y(n);
  10735. if (n.firstChild) n.midAngle += Math.PI;
  10736. }
  10737. }
  10738. };
  10739. /** @private Provides standard space-filling layout based on breadth & depth. */
  10740. pv.Layout.Hierarchy.Fill = {
  10741. /** @private */
  10742. constructor: function() {
  10743. this.node
  10744. .strokeStyle("#fff")
  10745. .fillStyle("#ccc")
  10746. .width(function(n) { return n.dx; })
  10747. .height(function(n) { return n.dy; })
  10748. .innerRadius(function(n) { return n.innerRadius; })
  10749. .outerRadius(function(n) { return n.outerRadius; })
  10750. .startAngle(function(n) { return n.startAngle; })
  10751. .angle(function(n) { return n.angle; });
  10752. this.label
  10753. .textAlign("center")
  10754. .left(function(n) { return n.x + (n.dx / 2); })
  10755. .top(function(n) { return n.y + (n.dy / 2); });
  10756. /* Hide unsupported link. */
  10757. delete this.link;
  10758. },
  10759. /** @private */
  10760. buildImplied: function(s) {
  10761. var nodes = s.nodes,
  10762. orient = s.orient,
  10763. horizontal = /^(top|bottom)$/.test(orient),
  10764. w = s.width,
  10765. h = s.height,
  10766. depth = -nodes[0].minDepth;
  10767. /* Compute default inner and outer radius. */
  10768. if (orient == "radial") {
  10769. var ir = s.innerRadius, or = s.outerRadius;
  10770. if (ir == null) ir = 0;
  10771. if (ir) depth *= 2; // use full depth step for root
  10772. if (or == null) or = Math.min(w, h) / 2;
  10773. }
  10774. /** @private Scales the specified depth for a space-filling layout. */
  10775. function scale(d, depth) {
  10776. return (d + depth) / (1 + depth);
  10777. }
  10778. /** @private */
  10779. function x(n) {
  10780. switch (orient) {
  10781. case "left": return scale(n.minDepth, depth) * w;
  10782. case "right": return (1 - scale(n.maxDepth, depth)) * w;
  10783. case "top": return n.minBreadth * w;
  10784. case "bottom": return (1 - n.maxBreadth) * w;
  10785. case "radial": return w / 2;
  10786. }
  10787. }
  10788. /** @private */
  10789. function y(n) {
  10790. switch (orient) {
  10791. case "left": return n.minBreadth * h;
  10792. case "right": return (1 - n.maxBreadth) * h;
  10793. case "top": return scale(n.minDepth, depth) * h;
  10794. case "bottom": return (1 - scale(n.maxDepth, depth)) * h;
  10795. case "radial": return h / 2;
  10796. }
  10797. }
  10798. /** @private */
  10799. function dx(n) {
  10800. switch (orient) {
  10801. case "left":
  10802. case "right": return (n.maxDepth - n.minDepth) / (1 + depth) * w;
  10803. case "top":
  10804. case "bottom": return (n.maxBreadth - n.minBreadth) * w;
  10805. case "radial": return n.parentNode ? (n.innerRadius + n.outerRadius) * Math.cos(n.midAngle) : 0;
  10806. }
  10807. }
  10808. /** @private */
  10809. function dy(n) {
  10810. switch (orient) {
  10811. case "left":
  10812. case "right": return (n.maxBreadth - n.minBreadth) * h;
  10813. case "top":
  10814. case "bottom": return (n.maxDepth - n.minDepth) / (1 + depth) * h;
  10815. case "radial": return n.parentNode ? (n.innerRadius + n.outerRadius) * Math.sin(n.midAngle) : 0;
  10816. }
  10817. }
  10818. /** @private */
  10819. function innerRadius(n) {
  10820. return Math.max(0, scale(n.minDepth, depth / 2)) * (or - ir) + ir;
  10821. }
  10822. /** @private */
  10823. function outerRadius(n) {
  10824. return scale(n.maxDepth, depth / 2) * (or - ir) + ir;
  10825. }
  10826. /** @private */
  10827. function startAngle(n) {
  10828. return (n.parentNode ? n.minBreadth - .25 : 0) * 2 * Math.PI;
  10829. }
  10830. /** @private */
  10831. function angle(n) {
  10832. return (n.parentNode ? n.maxBreadth - n.minBreadth : 1) * 2 * Math.PI;
  10833. }
  10834. for (var i = 0; i < nodes.length; i++) {
  10835. var n = nodes[i];
  10836. n.x = x(n);
  10837. n.y = y(n);
  10838. if (orient == "radial") {
  10839. n.innerRadius = innerRadius(n);
  10840. n.outerRadius = outerRadius(n);
  10841. n.startAngle = startAngle(n);
  10842. n.angle = angle(n);
  10843. n.midAngle = n.startAngle + n.angle / 2;
  10844. } else {
  10845. n.midAngle = horizontal ? -Math.PI / 2 : 0;
  10846. }
  10847. n.dx = dx(n);
  10848. n.dy = dy(n);
  10849. }
  10850. }
  10851. };
  10852. /**
  10853. * Constructs a new, empty grid layout. Layouts are not typically constructed
  10854. * directly; instead, they are added to an existing panel via
  10855. * {@link pv.Mark#add}.
  10856. *
  10857. * @class Implements a grid layout with regularly-sized rows and columns. The
  10858. * number of rows and columns are determined from their respective
  10859. * properties. For example, the 2&times;3 array:
  10860. *
  10861. * <pre>1 2 3
  10862. * 4 5 6</pre>
  10863. *
  10864. * can be represented using the <tt>rows</tt> property as:
  10865. *
  10866. * <pre>[[1, 2, 3], [4, 5, 6]]</pre>
  10867. *
  10868. * If your data is in column-major order, you can equivalently use the
  10869. * <tt>columns</tt> property. If the <tt>rows</tt> property is an array, it
  10870. * takes priority over the <tt>columns</tt> property. The data is implicitly
  10871. * transposed, as if the {@link pv.transpose} operator were applied.
  10872. *
  10873. * <p>This layout exports a single <tt>cell</tt> mark prototype, which is
  10874. * intended to be used with a bar, panel, layout, or subclass thereof. The data
  10875. * property of the cell prototype is defined as the elements in the array. For
  10876. * example, if the array is a two-dimensional array of values in the range
  10877. * [0,1], a simple heatmap can be generated as:
  10878. *
  10879. * <pre>vis.add(pv.Layout.Grid)
  10880. * .rows(arrays)
  10881. * .cell.add(pv.Bar)
  10882. * .fillStyle(pv.ramp("white", "black"))</pre>
  10883. *
  10884. * The grid subdivides the full width and height of the parent panel into equal
  10885. * rectangles. Note, however, that for large, interactive, or animated heatmaps,
  10886. * you may see significantly better performance through dynamic {@link pv.Image}
  10887. * generation.
  10888. *
  10889. * <p>For irregular grids using value-based spatial partitioning, see {@link
  10890. * pv.Layout.Treemap}.
  10891. *
  10892. * @extends pv.Layout
  10893. */
  10894. pv.Layout.Grid = function() {
  10895. pv.Layout.call(this);
  10896. var that = this;
  10897. /**
  10898. * The cell prototype. This prototype is intended to be used with a bar,
  10899. * panel, or layout (or subclass thereof) to render the grid cells.
  10900. *
  10901. * @type pv.Mark
  10902. * @name pv.Layout.Grid.prototype.cell
  10903. */
  10904. (this.cell = new pv.Mark()
  10905. .data(function() {
  10906. return that.scene[that.index].$grid;
  10907. })
  10908. .width(function() {
  10909. return that.width() / that.cols();
  10910. })
  10911. .height(function() {
  10912. return that.height() / that.rows();
  10913. })
  10914. .left(function() {
  10915. return this.width() * (this.index % that.cols());
  10916. })
  10917. .top(function() {
  10918. return this.height() * Math.floor(this.index / that.cols());
  10919. })).parent = this;
  10920. };
  10921. pv.Layout.Grid.prototype = pv.extend(pv.Layout)
  10922. .property("rows")
  10923. .property("cols");
  10924. /**
  10925. * Default properties for grid layouts. By default, there is one row and one
  10926. * column, and the data is the propagated to the child cell.
  10927. *
  10928. * @type pv.Layout.Grid
  10929. */
  10930. pv.Layout.Grid.prototype.defaults = new pv.Layout.Grid()
  10931. .extend(pv.Layout.prototype.defaults)
  10932. .rows(1)
  10933. .cols(1);
  10934. /** @private */
  10935. pv.Layout.Grid.prototype.buildImplied = function(s) {
  10936. pv.Layout.prototype.buildImplied.call(this, s);
  10937. var r = s.rows, c = s.cols;
  10938. if (typeof c == "object") r = pv.transpose(c);
  10939. if (typeof r == "object") {
  10940. s.$grid = pv.blend(r);
  10941. s.rows = r.length;
  10942. s.cols = r[0] ? r[0].length : 0;
  10943. } else {
  10944. s.$grid = pv.repeat([s.data], r * c);
  10945. }
  10946. };
  10947. /**
  10948. * The number of rows. This property can also be specified as the data in
  10949. * row-major order; in this case, the rows property is implicitly set to the
  10950. * length of the array, and the cols property is set to the length of the first
  10951. * element in the array.
  10952. *
  10953. * @type number
  10954. * @name pv.Layout.Grid.prototype.rows
  10955. */
  10956. /**
  10957. * The number of columns. This property can also be specified as the data in
  10958. * column-major order; in this case, the cols property is implicitly set to the
  10959. * length of the array, and the rows property is set to the length of the first
  10960. * element in the array.
  10961. *
  10962. * @type number
  10963. * @name pv.Layout.Grid.prototype.cols
  10964. */
  10965. /**
  10966. * Constructs a new, empty stack layout. Layouts are not typically constructed
  10967. * directly; instead, they are added to an existing panel via
  10968. * {@link pv.Mark#add}.
  10969. *
  10970. * @class Implements a layout for stacked visualizations, ranging from simple
  10971. * stacked bar charts to more elaborate "streamgraphs" composed of stacked
  10972. * areas. Stack layouts uses length as a visual encoding, as opposed to
  10973. * position, as the layers do not share an aligned axis.
  10974. *
  10975. * <p>Marks can be stacked vertically or horizontally. For example,
  10976. *
  10977. * <pre>vis.add(pv.Layout.Stack)
  10978. * .layers([[1, 1.2, 1.7, 1.5, 1.7],
  10979. * [.5, 1, .8, 1.1, 1.3],
  10980. * [.2, .5, .8, .9, 1]])
  10981. * .x(function() this.index * 35)
  10982. * .y(function(d) d * 40)
  10983. * .layer.add(pv.Area);</pre>
  10984. *
  10985. * specifies a vertically-stacked area chart, using the default "bottom-left"
  10986. * orientation with "zero" offset. This visualization can be easily changed into
  10987. * a streamgraph using the "wiggle" offset, which attempts to minimize change in
  10988. * slope weighted by layer thickness. See the {@link #offset} property for more
  10989. * supported streamgraph algorithms.
  10990. *
  10991. * <p>In the simplest case, the layer data can be specified as a two-dimensional
  10992. * array of numbers. The <tt>x</tt> and <tt>y</tt> psuedo-properties are used to
  10993. * define the thickness of each layer at the given position, respectively; in
  10994. * the above example of the "bottom-left" orientation, the <tt>x</tt> and
  10995. * <tt>y</tt> psuedo-properties are equivalent to the <tt>left</tt> and
  10996. * <tt>height</tt> properties that you might use if you implemented a stacked
  10997. * area by hand.
  10998. *
  10999. * <p>The advantage of using the stack layout is that the baseline, i.e., the
  11000. * <tt>bottom</tt> property is computed automatically using the specified offset
  11001. * algorithm. In addition, the order of layers can be computed using a built-in
  11002. * algorithm via the <tt>order</tt> property.
  11003. *
  11004. * <p>With the exception of the "expand" <tt>offset</tt>, the stack layout does
  11005. * not perform any automatic scaling of data; the values returned from
  11006. * <tt>x</tt> and <tt>y</tt> specify pixel sizes. To simplify scaling math, use
  11007. * this layout in conjunction with {@link pv.Scale.linear} or similar.
  11008. *
  11009. * <p>In other cases, the <tt>values</tt> psuedo-property can be used to define
  11010. * the data more flexibly. As with a typical panel &amp; area, the
  11011. * <tt>layers</tt> property corresponds to the data in the enclosing panel,
  11012. * while the <tt>values</tt> psuedo-property corresponds to the data for the
  11013. * area within the panel. For example, given an array of data values:
  11014. *
  11015. * <pre>var crimea = [
  11016. * { date: "4/1854", wounds: 0, other: 110, disease: 110 },
  11017. * { date: "5/1854", wounds: 0, other: 95, disease: 105 },
  11018. * { date: "6/1854", wounds: 0, other: 40, disease: 95 },
  11019. * ...</pre>
  11020. *
  11021. * and a corresponding array of series names:
  11022. *
  11023. * <pre>var causes = ["wounds", "other", "disease"];</pre>
  11024. *
  11025. * Separate layers can be defined for each cause like so:
  11026. *
  11027. * <pre>vis.add(pv.Layout.Stack)
  11028. * .layers(causes)
  11029. * .values(crimea)
  11030. * .x(function(d) x(d.date))
  11031. * .y(function(d, p) y(d[p]))
  11032. * .layer.add(pv.Area)
  11033. * ...</pre>
  11034. *
  11035. * As with the panel &amp; area case, the datum that is passed to the
  11036. * psuedo-properties <tt>x</tt> and <tt>y</tt> are the values (an element in
  11037. * <tt>crimea</tt>); the second argument is the layer data (a string in
  11038. * <tt>causes</tt>). Additional arguments specify the data of enclosing panels,
  11039. * if any.
  11040. *
  11041. * @extends pv.Layout
  11042. */
  11043. pv.Layout.Stack = function() {
  11044. pv.Layout.call(this);
  11045. var that = this,
  11046. /** @ignore */ none = function() { return null; },
  11047. prop = {t: none, l: none, r: none, b: none, w: none, h: none},
  11048. values,
  11049. buildImplied = that.buildImplied;
  11050. /** @private Proxy the given property on the layer. */
  11051. function proxy(name) {
  11052. return function() {
  11053. return prop[name](this.parent.index, this.index);
  11054. };
  11055. }
  11056. /** @private Compute the layout! */
  11057. this.buildImplied = function(s) {
  11058. buildImplied.call(this, s);
  11059. var data = s.layers,
  11060. n = data.length,
  11061. m,
  11062. orient = s.orient,
  11063. horizontal = /^(top|bottom)\b/.test(orient),
  11064. h = this.parent[horizontal ? "height" : "width"](),
  11065. x = [],
  11066. y = [],
  11067. dy = [];
  11068. /*
  11069. * Iterate over the data, evaluating the values, x and y functions. The
  11070. * context in which the x and y psuedo-properties are evaluated is a
  11071. * pseudo-mark that is a grandchild of this layout.
  11072. */
  11073. var stack = pv.Mark.stack, o = {parent: {parent: this}};
  11074. stack.unshift(null);
  11075. values = [];
  11076. for (var i = 0; i < n; i++) {
  11077. dy[i] = [];
  11078. y[i] = [];
  11079. o.parent.index = i;
  11080. stack[0] = data[i];
  11081. values[i] = this.$values.apply(o.parent, stack);
  11082. if (!i) m = values[i].length;
  11083. stack.unshift(null);
  11084. for (var j = 0; j < m; j++) {
  11085. stack[0] = values[i][j];
  11086. o.index = j;
  11087. if (!i) x[j] = this.$x.apply(o, stack);
  11088. dy[i][j] = this.$y.apply(o, stack);
  11089. }
  11090. stack.shift();
  11091. }
  11092. stack.shift();
  11093. /* order */
  11094. var index;
  11095. switch (s.order) {
  11096. case "inside-out": {
  11097. var max = dy.map(function(v) { return pv.max.index(v); }),
  11098. map = pv.range(n).sort(function(a, b) { return max[a] - max[b]; }),
  11099. sums = dy.map(function(v) { return pv.sum(v); }),
  11100. top = 0,
  11101. bottom = 0,
  11102. tops = [],
  11103. bottoms = [];
  11104. for (var i = 0; i < n; i++) {
  11105. var j = map[i];
  11106. if (top < bottom) {
  11107. top += sums[j];
  11108. tops.push(j);
  11109. } else {
  11110. bottom += sums[j];
  11111. bottoms.push(j);
  11112. }
  11113. }
  11114. index = bottoms.reverse().concat(tops);
  11115. break;
  11116. }
  11117. case "reverse": index = pv.range(n - 1, -1, -1); break;
  11118. default: index = pv.range(n); break;
  11119. }
  11120. /* offset */
  11121. switch (s.offset) {
  11122. case "silohouette": {
  11123. for (var j = 0; j < m; j++) {
  11124. var o = 0;
  11125. for (var i = 0; i < n; i++) o += dy[i][j];
  11126. y[index[0]][j] = (h - o) / 2;
  11127. }
  11128. break;
  11129. }
  11130. case "wiggle": {
  11131. var o = 0;
  11132. for (var i = 0; i < n; i++) o += dy[i][0];
  11133. y[index[0]][0] = o = (h - o) / 2;
  11134. for (var j = 1; j < m; j++) {
  11135. var s1 = 0, s2 = 0, dx = x[j] - x[j - 1];
  11136. for (var i = 0; i < n; i++) s1 += dy[i][j];
  11137. for (var i = 0; i < n; i++) {
  11138. var s3 = (dy[index[i]][j] - dy[index[i]][j - 1]) / (2 * dx);
  11139. for (var k = 0; k < i; k++) {
  11140. s3 += (dy[index[k]][j] - dy[index[k]][j - 1]) / dx;
  11141. }
  11142. s2 += s3 * dy[index[i]][j];
  11143. }
  11144. y[index[0]][j] = o -= s1 ? s2 / s1 * dx : 0;
  11145. }
  11146. break;
  11147. }
  11148. case "expand": {
  11149. for (var j = 0; j < m; j++) {
  11150. y[index[0]][j] = 0;
  11151. var k = 0;
  11152. for (var i = 0; i < n; i++) k += dy[i][j];
  11153. if (k) {
  11154. k = h / k;
  11155. for (var i = 0; i < n; i++) dy[i][j] *= k;
  11156. } else {
  11157. k = h / n;
  11158. for (var i = 0; i < n; i++) dy[i][j] = k;
  11159. }
  11160. }
  11161. break;
  11162. }
  11163. default: {
  11164. for (var j = 0; j < m; j++) y[index[0]][j] = 0;
  11165. break;
  11166. }
  11167. }
  11168. /* Propagate the offset to the other series. */
  11169. for (var j = 0; j < m; j++) {
  11170. var o = y[index[0]][j];
  11171. for (var i = 1; i < n; i++) {
  11172. o += dy[index[i - 1]][j];
  11173. y[index[i]][j] = o;
  11174. }
  11175. }
  11176. /* Find the property definitions for dynamic substitution. */
  11177. var i = orient.indexOf("-"),
  11178. pdy = horizontal ? "h" : "w",
  11179. px = i < 0 ? (horizontal ? "l" : "b") : orient.charAt(i + 1),
  11180. py = orient.charAt(0);
  11181. for (var p in prop) prop[p] = none;
  11182. prop[px] = function(i, j) { return x[j]; };
  11183. prop[py] = function(i, j) { return y[i][j]; };
  11184. prop[pdy] = function(i, j) { return dy[i][j]; };
  11185. };
  11186. /**
  11187. * The layer prototype. This prototype is intended to be used with an area,
  11188. * bar or panel mark (or subclass thereof). Other mark types may be possible,
  11189. * though note that the stack layout is not currently designed to support
  11190. * radial stacked visualizations using wedges.
  11191. *
  11192. * <p>The layer is not a direct child of the stack layout; a hidden panel is
  11193. * used to replicate layers.
  11194. *
  11195. * @type pv.Mark
  11196. * @name pv.Layout.Stack.prototype.layer
  11197. */
  11198. this.layer = new pv.Mark()
  11199. .data(function() { return values[this.parent.index]; })
  11200. .top(proxy("t"))
  11201. .left(proxy("l"))
  11202. .right(proxy("r"))
  11203. .bottom(proxy("b"))
  11204. .width(proxy("w"))
  11205. .height(proxy("h"));
  11206. this.layer.add = function(type) {
  11207. return that.add(pv.Panel)
  11208. .data(function() { return that.layers(); })
  11209. .add(type)
  11210. .extend(this);
  11211. };
  11212. };
  11213. pv.Layout.Stack.prototype = pv.extend(pv.Layout)
  11214. .property("orient", String)
  11215. .property("offset", String)
  11216. .property("order", String)
  11217. .property("layers");
  11218. /**
  11219. * Default properties for stack layouts. The default orientation is
  11220. * "bottom-left", the default offset is "zero", and the default layers is
  11221. * <tt>[[]]</tt>.
  11222. *
  11223. * @type pv.Layout.Stack
  11224. */
  11225. pv.Layout.Stack.prototype.defaults = new pv.Layout.Stack()
  11226. .extend(pv.Layout.prototype.defaults)
  11227. .orient("bottom-left")
  11228. .offset("zero")
  11229. .layers([[]]);
  11230. /** @private */
  11231. pv.Layout.Stack.prototype.$x
  11232. = /** @private */ pv.Layout.Stack.prototype.$y
  11233. = function() { return 0; };
  11234. /**
  11235. * The x psuedo-property; determines the position of the value within the layer.
  11236. * This typically corresponds to the independent variable. For example, with the
  11237. * default "bottom-left" orientation, this function defines the "left" property.
  11238. *
  11239. * @param {function} f the x function.
  11240. * @returns {pv.Layout.Stack} this.
  11241. */
  11242. pv.Layout.Stack.prototype.x = function(f) {
  11243. /** @private */ this.$x = pv.functor(f);
  11244. return this;
  11245. };
  11246. /**
  11247. * The y psuedo-property; determines the thickness of the layer at the given
  11248. * value. This typically corresponds to the dependent variable. For example,
  11249. * with the default "bottom-left" orientation, this function defines the
  11250. * "height" property.
  11251. *
  11252. * @param {function} f the y function.
  11253. * @returns {pv.Layout.Stack} this.
  11254. */
  11255. pv.Layout.Stack.prototype.y = function(f) {
  11256. /** @private */ this.$y = pv.functor(f);
  11257. return this;
  11258. };
  11259. /** @private The default value function; identity. */
  11260. pv.Layout.Stack.prototype.$values = pv.identity;
  11261. /**
  11262. * The values function; determines the values for a given layer. The default
  11263. * value is the identity function, which assumes that the layers property is
  11264. * specified as a two-dimensional (i.e., nested) array.
  11265. *
  11266. * @param {function} f the values function.
  11267. * @returns {pv.Layout.Stack} this.
  11268. */
  11269. pv.Layout.Stack.prototype.values = function(f) {
  11270. this.$values = pv.functor(f);
  11271. return this;
  11272. };
  11273. /**
  11274. * The layer data in row-major order. The value of this property is typically a
  11275. * two-dimensional (i.e., nested) array, but any array can be used, provided the
  11276. * values psuedo-property is defined accordingly.
  11277. *
  11278. * @type array[]
  11279. * @name pv.Layout.Stack.prototype.layers
  11280. */
  11281. /**
  11282. * The layer orientation. The following values are supported:<ul>
  11283. *
  11284. * <li>bottom-left == bottom
  11285. * <li>bottom-right
  11286. * <li>top-left == top
  11287. * <li>top-right
  11288. * <li>left-top
  11289. * <li>left-bottom == left
  11290. * <li>right-top
  11291. * <li>right-bottom == right
  11292. *
  11293. * </ul>. The default value is "bottom-left", which means that the layers will
  11294. * be built from the bottom-up, and the values within layers will be laid out
  11295. * from left-to-right.
  11296. *
  11297. * <p>Note that with non-zero baselines, some orientations may give similar
  11298. * results. For example, offset("silohouette") centers the layers, resulting in
  11299. * a streamgraph. Thus, the orientations "bottom-left" and "top-left" will
  11300. * produce similar results, differing only in the layer order.
  11301. *
  11302. * @type string
  11303. * @name pv.Layout.Stack.prototype.orient
  11304. */
  11305. /**
  11306. * The layer order. The following values are supported:<ul>
  11307. *
  11308. * <li><i>null</i> - use given layer order.
  11309. * <li>inside-out - sort by maximum value, with balanced order.
  11310. * <li>reverse - use reverse of given layer order.
  11311. *
  11312. * </ul>For details on the inside-out order algorithm, refer to "Stacked Graphs
  11313. * -- Geometry &amp; Aesthetics" by L. Byron and M. Wattenberg, IEEE TVCG
  11314. * November/December 2008.
  11315. *
  11316. * @type string
  11317. * @name pv.Layout.Stack.prototype.order
  11318. */
  11319. /**
  11320. * The layer offset; the y-position of the bottom of the lowest layer. The
  11321. * following values are supported:<ul>
  11322. *
  11323. * <li>zero - use a zero baseline, i.e., the y-axis.
  11324. * <li>silohouette - center the stream, i.e., ThemeRiver.
  11325. * <li>wiggle - minimize weighted change in slope.
  11326. * <li>expand - expand layers to fill the enclosing layout dimensions.
  11327. *
  11328. * </ul>For details on these offset algorithms, refer to "Stacked Graphs --
  11329. * Geometry &amp; Aesthetics" by L. Byron and M. Wattenberg, IEEE TVCG
  11330. * November/December 2008.
  11331. *
  11332. * @type string
  11333. * @name pv.Layout.Stack.prototype.offset
  11334. */
  11335. /**
  11336. * Constructs a new, empty treemap layout. Layouts are not typically
  11337. * constructed directly; instead, they are added to an existing panel via
  11338. * {@link pv.Mark#add}.
  11339. *
  11340. * @class Implements a space-filling rectangular layout, with the hierarchy
  11341. * represented via containment. Treemaps represent nodes as boxes, with child
  11342. * nodes placed within parent boxes. The size of each box is proportional to the
  11343. * size of the node in the tree. This particular algorithm is taken from Bruls,
  11344. * D.M., C. Huizing, and J.J. van Wijk, <a
  11345. * href="http://www.win.tue.nl/~vanwijk/stm.pdf">"Squarified Treemaps"</a> in
  11346. * <i>Data Visualization 2000, Proceedings of the Joint Eurographics and IEEE
  11347. * TCVG Sumposium on Visualization</i>, 2000, pp. 33-42.
  11348. *
  11349. * <p>The meaning of the exported mark prototypes changes slightly in the
  11350. * space-filling implementation:<ul>
  11351. *
  11352. * <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Bar}. The node
  11353. * data is populated with <tt>dx</tt> and <tt>dy</tt> attributes, in addition to
  11354. * the standard <tt>x</tt> and <tt>y</tt> position attributes.
  11355. *
  11356. * <p><li><tt>leaf</tt> - for rendering leaf nodes only, with no fill or stroke
  11357. * style by default; typically a {@link pv.Panel} or another layout!
  11358. *
  11359. * <p><li><tt>link</tt> - unsupported; undefined. Links are encoded implicitly
  11360. * in the arrangement of the space-filling nodes.
  11361. *
  11362. * <p><li><tt>label</tt> - for rendering node labels; typically a
  11363. * {@link pv.Label}.
  11364. *
  11365. * </ul>For more details on how to use this layout, see
  11366. * {@link pv.Layout.Hierarchy}.
  11367. *
  11368. * @extends pv.Layout.Hierarchy
  11369. */
  11370. pv.Layout.Treemap = function() {
  11371. pv.Layout.Hierarchy.call(this);
  11372. this.node
  11373. .strokeStyle("#fff")
  11374. .fillStyle("rgba(31, 119, 180, .25)")
  11375. .width(function(n) { return n.dx; })
  11376. .height(function(n) { return n.dy; });
  11377. this.label
  11378. .visible(function(n) { return !n.firstChild; })
  11379. .left(function(n) { return n.x + (n.dx / 2); })
  11380. .top(function(n) { return n.y + (n.dy / 2); })
  11381. .textAlign("center")
  11382. .textAngle(function(n) { return n.dx > n.dy ? 0 : -Math.PI / 2; });
  11383. (this.leaf = new pv.Mark()
  11384. .extend(this.node)
  11385. .fillStyle(null)
  11386. .strokeStyle(null)
  11387. .visible(function(n) { return !n.firstChild; })).parent = this;
  11388. /* Hide unsupported link. */
  11389. delete this.link;
  11390. };
  11391. pv.Layout.Treemap.prototype = pv.extend(pv.Layout.Hierarchy)
  11392. .property("round", Boolean)
  11393. .property("paddingLeft", Number)
  11394. .property("paddingRight", Number)
  11395. .property("paddingTop", Number)
  11396. .property("paddingBottom", Number)
  11397. .property("mode", String)
  11398. .property("order", String);
  11399. /**
  11400. * Default propertiess for treemap layouts. The default mode is "squarify" and
  11401. * the default order is "ascending".
  11402. *
  11403. * @type pv.Layout.Treemap
  11404. */
  11405. pv.Layout.Treemap.prototype.defaults = new pv.Layout.Treemap()
  11406. .extend(pv.Layout.Hierarchy.prototype.defaults)
  11407. .mode("squarify") // squarify, slice-and-dice, slice, dice
  11408. .order("ascending"); // ascending, descending, reverse, null
  11409. /**
  11410. * Whether node sizes should be rounded to integer values. This has a similar
  11411. * effect to setting <tt>antialias(false)</tt> for node values, but allows the
  11412. * treemap algorithm to accumulate error related to pixel rounding.
  11413. *
  11414. * @type boolean
  11415. * @name pv.Layout.Treemap.prototype.round
  11416. */
  11417. /**
  11418. * The left inset between parent add child in pixels. Defaults to 0.
  11419. *
  11420. * @type number
  11421. * @name pv.Layout.Treemap.prototype.paddingLeft
  11422. * @see #padding
  11423. */
  11424. /**
  11425. * The right inset between parent add child in pixels. Defaults to 0.
  11426. *
  11427. * @type number
  11428. * @name pv.Layout.Treemap.prototype.paddingRight
  11429. * @see #padding
  11430. */
  11431. /**
  11432. * The top inset between parent and child in pixels. Defaults to 0.
  11433. *
  11434. * @type number
  11435. * @name pv.Layout.Treemap.prototype.paddingTop
  11436. * @see #padding
  11437. */
  11438. /**
  11439. * The bottom inset between parent and child in pixels. Defaults to 0.
  11440. *
  11441. * @type number
  11442. * @name pv.Layout.Treemap.prototype.paddingBottom
  11443. * @see #padding
  11444. */
  11445. /**
  11446. * The treemap algorithm. The default value is "squarify". The "slice-and-dice"
  11447. * algorithm may also be used, which alternates between horizontal and vertical
  11448. * slices for different depths. In addition, the "slice" and "dice" algorithms
  11449. * may be specified explicitly to control whether horizontal or vertical slices
  11450. * are used, which may be useful for nested treemap layouts.
  11451. *
  11452. * @type string
  11453. * @name pv.Layout.Treemap.prototype.mode
  11454. * @see <a
  11455. * href="ftp://ftp.cs.umd.edu/pub/hcil/Reports-Abstracts-Bibliography/2001-06html/2001-06.pdf"
  11456. * >"Ordered Treemap Layouts"</a> by B. Shneiderman &amp; M. Wattenberg, IEEE
  11457. * InfoVis 2001.
  11458. */
  11459. /**
  11460. * The sibling node order. A <tt>null</tt> value means to use the sibling order
  11461. * specified by the nodes property as-is; "reverse" will reverse the given
  11462. * order. The default value "ascending" will sort siblings in ascending order of
  11463. * size, while "descending" will do the reverse. For sorting based on data
  11464. * attributes other than size, use the default <tt>null</tt> for the order
  11465. * property, and sort the nodes beforehand using the {@link pv.Dom} operator.
  11466. *
  11467. * @type string
  11468. * @name pv.Layout.Treemap.prototype.order
  11469. */
  11470. /**
  11471. * Alias for setting the left, right, top and bottom padding properties
  11472. * simultaneously.
  11473. *
  11474. * @see #paddingLeft
  11475. * @see #paddingRight
  11476. * @see #paddingTop
  11477. * @see #paddingBottom
  11478. * @returns {pv.Layout.Treemap} this.
  11479. */
  11480. pv.Layout.Treemap.prototype.padding = function(n) {
  11481. return this.paddingLeft(n).paddingRight(n).paddingTop(n).paddingBottom(n);
  11482. };
  11483. /** @private The default size function. */
  11484. pv.Layout.Treemap.prototype.$size = function(d) {
  11485. return Number(d.nodeValue);
  11486. };
  11487. /**
  11488. * Specifies the sizing function. By default, the size function uses the
  11489. * <tt>nodeValue</tt> attribute of nodes as a numeric value: <tt>function(d)
  11490. * Number(d.nodeValue)</tt>.
  11491. *
  11492. * <p>The sizing function is invoked for each leaf node in the tree, per the
  11493. * <tt>nodes</tt> property. For example, if the tree data structure represents a
  11494. * file system, with files as leaf nodes, and each file has a <tt>bytes</tt>
  11495. * attribute, you can specify a size function as:
  11496. *
  11497. * <pre> .size(function(d) d.bytes)</pre>
  11498. *
  11499. * @param {function} f the new sizing function.
  11500. * @returns {pv.Layout.Treemap} this.
  11501. */
  11502. pv.Layout.Treemap.prototype.size = function(f) {
  11503. this.$size = pv.functor(f);
  11504. return this;
  11505. };
  11506. /** @private */
  11507. pv.Layout.Treemap.prototype.buildImplied = function(s) {
  11508. if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return;
  11509. var that = this,
  11510. nodes = s.nodes,
  11511. root = nodes[0],
  11512. stack = pv.Mark.stack,
  11513. left = s.paddingLeft,
  11514. right = s.paddingRight,
  11515. top = s.paddingTop,
  11516. bottom = s.paddingBottom,
  11517. /** @ignore */ size = function(n) { return n.size; },
  11518. round = s.round ? Math.round : Number,
  11519. mode = s.mode;
  11520. /** @private */
  11521. function slice(row, sum, horizontal, x, y, w, h) {
  11522. for (var i = 0, d = 0; i < row.length; i++) {
  11523. var n = row[i];
  11524. if (horizontal) {
  11525. n.x = x + d;
  11526. n.y = y;
  11527. d += n.dx = round(w * n.size / sum);
  11528. n.dy = h;
  11529. } else {
  11530. n.x = x;
  11531. n.y = y + d;
  11532. n.dx = w;
  11533. d += n.dy = round(h * n.size / sum);
  11534. }
  11535. }
  11536. if (n) { // correct on-axis rounding error
  11537. if (horizontal) {
  11538. n.dx += w - d;
  11539. } else {
  11540. n.dy += h - d;
  11541. }
  11542. }
  11543. }
  11544. /** @private */
  11545. function ratio(row, l) {
  11546. var rmax = -Infinity, rmin = Infinity, s = 0;
  11547. for (var i = 0; i < row.length; i++) {
  11548. var r = row[i].size;
  11549. if (r < rmin) rmin = r;
  11550. if (r > rmax) rmax = r;
  11551. s += r;
  11552. }
  11553. s = s * s;
  11554. l = l * l;
  11555. return Math.max(l * rmax / s, s / (l * rmin));
  11556. }
  11557. /** @private */
  11558. function layout(n, i) {
  11559. var x = n.x + left,
  11560. y = n.y + top,
  11561. w = n.dx - left - right,
  11562. h = n.dy - top - bottom;
  11563. /* Assume squarify by default. */
  11564. if (mode != "squarify") {
  11565. slice(n.childNodes, n.size,
  11566. mode == "slice" ? true
  11567. : mode == "dice" ? false
  11568. : i & 1, x, y, w, h);
  11569. return;
  11570. }
  11571. var row = [],
  11572. mink = Infinity,
  11573. l = Math.min(w, h),
  11574. k = w * h / n.size;
  11575. /* Abort if the size is nonpositive. */
  11576. if (n.size <= 0) return;
  11577. /* Scale the sizes to fill the current subregion. */
  11578. n.visitBefore(function(n) { n.size *= k; });
  11579. /** @private Position the specified nodes along one dimension. */
  11580. function position(row) {
  11581. var horizontal = w == l,
  11582. sum = pv.sum(row, size),
  11583. r = l ? round(sum / l) : 0;
  11584. slice(row, sum, horizontal, x, y, horizontal ? w : r, horizontal ? r : h);
  11585. if (horizontal) {
  11586. y += r;
  11587. h -= r;
  11588. } else {
  11589. x += r;
  11590. w -= r;
  11591. }
  11592. l = Math.min(w, h);
  11593. return horizontal;
  11594. }
  11595. var children = n.childNodes.slice(); // copy
  11596. while (children.length) {
  11597. var child = children[children.length - 1];
  11598. if (!child.size) {
  11599. children.pop();
  11600. continue;
  11601. }
  11602. row.push(child);
  11603. var k = ratio(row, l);
  11604. if (k <= mink) {
  11605. children.pop();
  11606. mink = k;
  11607. } else {
  11608. row.pop();
  11609. position(row);
  11610. row.length = 0;
  11611. mink = Infinity;
  11612. }
  11613. }
  11614. /* correct off-axis rounding error */
  11615. if (position(row)) for (var i = 0; i < row.length; i++) {
  11616. row[i].dy += h;
  11617. } else for (var i = 0; i < row.length; i++) {
  11618. row[i].dx += w;
  11619. }
  11620. }
  11621. /* Recursively compute the node depth and size. */
  11622. stack.unshift(null);
  11623. root.visitAfter(function(n, i) {
  11624. n.depth = i;
  11625. n.x = n.y = n.dx = n.dy = 0;
  11626. n.size = n.firstChild
  11627. ? pv.sum(n.childNodes, function(n) { return n.size; })
  11628. : that.$size.apply(that, (stack[0] = n, stack));
  11629. });
  11630. stack.shift();
  11631. /* Sort. */
  11632. switch (s.order) {
  11633. case "ascending": {
  11634. root.sort(function(a, b) { return a.size - b.size; });
  11635. break;
  11636. }
  11637. case "descending": {
  11638. root.sort(function(a, b) { return b.size - a.size; });
  11639. break;
  11640. }
  11641. case "reverse": root.reverse(); break;
  11642. }
  11643. /* Recursively compute the layout. */
  11644. root.x = 0;
  11645. root.y = 0;
  11646. root.dx = s.width;
  11647. root.dy = s.height;
  11648. root.visitBefore(layout);
  11649. };
  11650. /**
  11651. * Constructs a new, empty tree layout. Layouts are not typically constructed
  11652. * directly; instead, they are added to an existing panel via
  11653. * {@link pv.Mark#add}.
  11654. *
  11655. * @class Implements a node-link tree diagram using the Reingold-Tilford "tidy"
  11656. * tree layout algorithm. The specific algorithm used by this layout is based on
  11657. * <a href="http://citeseer.ist.psu.edu/buchheim02improving.html">"Improving
  11658. * Walker's Algorithm to Run in Linear Time"</A> by C. Buchheim, M. J&uuml;nger
  11659. * &amp; S. Leipert, Graph Drawing 2002. This layout supports both cartesian and
  11660. * radial orientations orientations for node-link diagrams.
  11661. *
  11662. * <p>The tree layout supports a "group" property, which if true causes siblings
  11663. * to be positioned closer together than unrelated nodes at the same depth. The
  11664. * layout can be configured using the <tt>depth</tt> and <tt>breadth</tt>
  11665. * properties, which control the increments in pixel space between nodes in both
  11666. * dimensions, similar to the indent layout.
  11667. *
  11668. * <p>For more details on how to use this layout, see
  11669. * {@link pv.Layout.Hierarchy}.
  11670. *
  11671. * @extends pv.Layout.Hierarchy
  11672. */
  11673. pv.Layout.Tree = function() {
  11674. pv.Layout.Hierarchy.call(this);
  11675. };
  11676. pv.Layout.Tree.prototype = pv.extend(pv.Layout.Hierarchy)
  11677. .property("group", Number)
  11678. .property("breadth", Number)
  11679. .property("depth", Number)
  11680. .property("orient", String);
  11681. /**
  11682. * Default properties for tree layouts. The default orientation is "top", the
  11683. * default group parameter is 1, and the default breadth and depth offsets are
  11684. * 15 and 60 respectively.
  11685. *
  11686. * @type pv.Layout.Tree
  11687. */
  11688. pv.Layout.Tree.prototype.defaults = new pv.Layout.Tree()
  11689. .extend(pv.Layout.Hierarchy.prototype.defaults)
  11690. .group(1)
  11691. .breadth(15)
  11692. .depth(60)
  11693. .orient("top");
  11694. /** @private */
  11695. pv.Layout.Tree.prototype.buildImplied = function(s) {
  11696. if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return;
  11697. var nodes = s.nodes,
  11698. orient = s.orient,
  11699. depth = s.depth,
  11700. breadth = s.breadth,
  11701. group = s.group,
  11702. w = s.width,
  11703. h = s.height;
  11704. /** @private */
  11705. function firstWalk(v) {
  11706. var l, r, a;
  11707. if (!v.firstChild) {
  11708. if (l = v.previousSibling) {
  11709. v.prelim = l.prelim + distance(v.depth, true);
  11710. }
  11711. } else {
  11712. l = v.firstChild;
  11713. r = v.lastChild;
  11714. a = l; // default ancestor
  11715. for (var c = l; c; c = c.nextSibling) {
  11716. firstWalk(c);
  11717. a = apportion(c, a);
  11718. }
  11719. executeShifts(v);
  11720. var midpoint = .5 * (l.prelim + r.prelim);
  11721. if (l = v.previousSibling) {
  11722. v.prelim = l.prelim + distance(v.depth, true);
  11723. v.mod = v.prelim - midpoint;
  11724. } else {
  11725. v.prelim = midpoint;
  11726. }
  11727. }
  11728. }
  11729. /** @private */
  11730. function secondWalk(v, m, depth) {
  11731. v.breadth = v.prelim + m;
  11732. m += v.mod;
  11733. for (var c = v.firstChild; c; c = c.nextSibling) {
  11734. secondWalk(c, m, depth);
  11735. }
  11736. }
  11737. /** @private */
  11738. function apportion(v, a) {
  11739. var w = v.previousSibling;
  11740. if (w) {
  11741. var vip = v,
  11742. vop = v,
  11743. vim = w,
  11744. vom = v.parentNode.firstChild,
  11745. sip = vip.mod,
  11746. sop = vop.mod,
  11747. sim = vim.mod,
  11748. som = vom.mod,
  11749. nr = nextRight(vim),
  11750. nl = nextLeft(vip);
  11751. while (nr && nl) {
  11752. vim = nr;
  11753. vip = nl;
  11754. vom = nextLeft(vom);
  11755. vop = nextRight(vop);
  11756. vop.ancestor = v;
  11757. var shift = (vim.prelim + sim) - (vip.prelim + sip) + distance(vim.depth, false);
  11758. if (shift > 0) {
  11759. moveSubtree(ancestor(vim, v, a), v, shift);
  11760. sip += shift;
  11761. sop += shift;
  11762. }
  11763. sim += vim.mod;
  11764. sip += vip.mod;
  11765. som += vom.mod;
  11766. sop += vop.mod;
  11767. nr = nextRight(vim);
  11768. nl = nextLeft(vip);
  11769. }
  11770. if (nr && !nextRight(vop)) {
  11771. vop.thread = nr;
  11772. vop.mod += sim - sop;
  11773. }
  11774. if (nl && !nextLeft(vom)) {
  11775. vom.thread = nl;
  11776. vom.mod += sip - som;
  11777. a = v;
  11778. }
  11779. }
  11780. return a;
  11781. }
  11782. /** @private */
  11783. function nextLeft(v) {
  11784. return v.firstChild || v.thread;
  11785. }
  11786. /** @private */
  11787. function nextRight(v) {
  11788. return v.lastChild || v.thread;
  11789. }
  11790. /** @private */
  11791. function moveSubtree(wm, wp, shift) {
  11792. var subtrees = wp.number - wm.number;
  11793. wp.change -= shift / subtrees;
  11794. wp.shift += shift;
  11795. wm.change += shift / subtrees;
  11796. wp.prelim += shift;
  11797. wp.mod += shift;
  11798. }
  11799. /** @private */
  11800. function executeShifts(v) {
  11801. var shift = 0, change = 0;
  11802. for (var c = v.lastChild; c; c = c.previousSibling) {
  11803. c.prelim += shift;
  11804. c.mod += shift;
  11805. change += c.change;
  11806. shift += c.shift + change;
  11807. }
  11808. }
  11809. /** @private */
  11810. function ancestor(vim, v, a) {
  11811. return (vim.ancestor.parentNode == v.parentNode) ? vim.ancestor : a;
  11812. }
  11813. /** @private */
  11814. function distance(depth, siblings) {
  11815. return (siblings ? 1 : (group + 1)) / ((orient == "radial") ? depth : 1);
  11816. }
  11817. /* Initialize temporary layout variables. TODO: store separately. */
  11818. var root = nodes[0];
  11819. root.visitAfter(function(v, i) {
  11820. v.ancestor = v;
  11821. v.prelim = 0;
  11822. v.mod = 0;
  11823. v.change = 0;
  11824. v.shift = 0;
  11825. v.number = v.previousSibling ? (v.previousSibling.number + 1) : 0;
  11826. v.depth = i;
  11827. });
  11828. /* Compute the layout using Buchheim et al.'s algorithm. */
  11829. firstWalk(root);
  11830. secondWalk(root, -root.prelim, 0);
  11831. /** @private Returns the angle of the given node. */
  11832. function midAngle(n) {
  11833. return (orient == "radial") ? n.breadth / depth : 0;
  11834. }
  11835. /** @private */
  11836. function x(n) {
  11837. switch (orient) {
  11838. case "left": return n.depth;
  11839. case "right": return w - n.depth;
  11840. case "top":
  11841. case "bottom": return n.breadth + w / 2;
  11842. case "radial": return w / 2 + n.depth * Math.cos(midAngle(n));
  11843. }
  11844. }
  11845. /** @private */
  11846. function y(n) {
  11847. switch (orient) {
  11848. case "left":
  11849. case "right": return n.breadth + h / 2;
  11850. case "top": return n.depth;
  11851. case "bottom": return h - n.depth;
  11852. case "radial": return h / 2 + n.depth * Math.sin(midAngle(n));
  11853. }
  11854. }
  11855. /* Clear temporary layout variables; transform depth and breadth. */
  11856. root.visitAfter(function(v) {
  11857. v.breadth *= breadth;
  11858. v.depth *= depth;
  11859. v.midAngle = midAngle(v);
  11860. v.x = x(v);
  11861. v.y = y(v);
  11862. if (v.firstChild) v.midAngle += Math.PI;
  11863. delete v.breadth;
  11864. delete v.depth;
  11865. delete v.ancestor;
  11866. delete v.prelim;
  11867. delete v.mod;
  11868. delete v.change;
  11869. delete v.shift;
  11870. delete v.number;
  11871. delete v.thread;
  11872. });
  11873. };
  11874. /**
  11875. * The offset between siblings nodes; defaults to 15.
  11876. *
  11877. * @type number
  11878. * @name pv.Layout.Tree.prototype.breadth
  11879. */
  11880. /**
  11881. * The offset between parent and child nodes; defaults to 60.
  11882. *
  11883. * @type number
  11884. * @name pv.Layout.Tree.prototype.depth
  11885. */
  11886. /**
  11887. * The orientation. The default orientation is "top", which means that the root
  11888. * node is placed on the top edge, leaf nodes appear at the bottom, and internal
  11889. * nodes are in-between. The following orientations are supported:<ul>
  11890. *
  11891. * <li>left - left-to-right.
  11892. * <li>right - right-to-left.
  11893. * <li>top - top-to-bottom.
  11894. * <li>bottom - bottom-to-top.
  11895. * <li>radial - radially, with the root at the center.</ul>
  11896. *
  11897. * @type string
  11898. * @name pv.Layout.Tree.prototype.orient
  11899. */
  11900. /**
  11901. * The sibling grouping, i.e., whether differentiating space is placed between
  11902. * sibling groups. The default is 1 (or true), causing sibling leaves to be
  11903. * separated by one breadth offset. Setting this to false (or 0) causes
  11904. * non-siblings to be adjacent.
  11905. *
  11906. * @type number
  11907. * @name pv.Layout.Tree.prototype.group
  11908. */
  11909. /**
  11910. * Constructs a new, empty indent layout. Layouts are not typically constructed
  11911. * directly; instead, they are added to an existing panel via
  11912. * {@link pv.Mark#add}.
  11913. *
  11914. * @class Implements a hierarchical layout using the indent algorithm. This
  11915. * layout implements a node-link diagram where the nodes are presented in
  11916. * preorder traversal, and nodes are indented based on their depth from the
  11917. * root. This technique is used ubiquitously by operating systems to represent
  11918. * file directories; although it requires much vertical space, indented trees
  11919. * allow efficient <i>interactive</i> exploration of trees to find a specific
  11920. * node. In addition they allow rapid scanning of node labels, and multivariate
  11921. * data such as file sizes can be displayed adjacent to the hierarchy.
  11922. *
  11923. * <p>The indent layout can be configured using the <tt>depth</tt> and
  11924. * <tt>breadth</tt> properties, which control the increments in pixel space for
  11925. * each indent and row in the layout. This layout does not support multiple
  11926. * orientations; the root node is rendered in the top-left, while
  11927. * <tt>breadth</tt> is a vertical offset from the top, and <tt>depth</tt> is a
  11928. * horizontal offset from the left.
  11929. *
  11930. * <p>For more details on how to use this layout, see
  11931. * {@link pv.Layout.Hierarchy}.
  11932. *
  11933. * @extends pv.Layout.Hierarchy
  11934. */
  11935. pv.Layout.Indent = function() {
  11936. pv.Layout.Hierarchy.call(this);
  11937. this.link.interpolate("step-after");
  11938. };
  11939. pv.Layout.Indent.prototype = pv.extend(pv.Layout.Hierarchy)
  11940. .property("depth", Number)
  11941. .property("breadth", Number);
  11942. /**
  11943. * The horizontal offset between different levels of the tree; defaults to 15.
  11944. *
  11945. * @type number
  11946. * @name pv.Layout.Indent.prototype.depth
  11947. */
  11948. /**
  11949. * The vertical offset between nodes; defaults to 15.
  11950. *
  11951. * @type number
  11952. * @name pv.Layout.Indent.prototype.breadth
  11953. */
  11954. /**
  11955. * Default properties for indent layouts. By default the depth and breadth
  11956. * offsets are 15 pixels.
  11957. *
  11958. * @type pv.Layout.Indent
  11959. */
  11960. pv.Layout.Indent.prototype.defaults = new pv.Layout.Indent()
  11961. .extend(pv.Layout.Hierarchy.prototype.defaults)
  11962. .depth(15)
  11963. .breadth(15);
  11964. /** @private */
  11965. pv.Layout.Indent.prototype.buildImplied = function(s) {
  11966. if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return;
  11967. var nodes = s.nodes,
  11968. bspace = s.breadth,
  11969. dspace = s.depth,
  11970. ax = 0,
  11971. ay = 0;
  11972. /** @private */
  11973. function position(n, breadth, depth) {
  11974. n.x = ax + depth++ * dspace;
  11975. n.y = ay + breadth++ * bspace;
  11976. n.midAngle = 0;
  11977. for (var c = n.firstChild; c; c = c.nextSibling) {
  11978. breadth = position(c, breadth, depth);
  11979. }
  11980. return breadth;
  11981. }
  11982. position(nodes[0], 1, 1);
  11983. };
  11984. /**
  11985. * Constructs a new, empty circle-packing layout. Layouts are not typically
  11986. * constructed directly; instead, they are added to an existing panel via
  11987. * {@link pv.Mark#add}.
  11988. *
  11989. * @class Implements a hierarchical layout using circle-packing. The meaning of
  11990. * the exported mark prototypes changes slightly in the space-filling
  11991. * implementation:<ul>
  11992. *
  11993. * <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Dot}.
  11994. *
  11995. * <p><li><tt>link</tt> - unsupported; undefined. Links are encoded implicitly
  11996. * in the arrangement of the space-filling nodes.
  11997. *
  11998. * <p><li><tt>label</tt> - for rendering node labels; typically a
  11999. * {@link pv.Label}.
  12000. *
  12001. * </ul>The pack layout support dynamic sizing for leaf nodes, if a
  12002. * {@link #size} psuedo-property is specified. The default size function returns
  12003. * 1, causing all leaf nodes to be sized equally, and all internal nodes to be
  12004. * sized by the number of leaf nodes they have as descendants.
  12005. *
  12006. * <p>The size function can be used in conjunction with the order property,
  12007. * which allows the nodes to the sorted by the computed size. Note: for sorting
  12008. * based on other data attributes, simply use the default <tt>null</tt> for the
  12009. * order property, and sort the nodes beforehand using the {@link pv.Dom}
  12010. * operator.
  12011. *
  12012. * <p>For more details on how to use this layout, see
  12013. * {@link pv.Layout.Hierarchy}.
  12014. *
  12015. * @extends pv.Layout.Hierarchy
  12016. * @see <a href="http://portal.acm.org/citation.cfm?id=1124772.1124851"
  12017. * >"Visualization of large hierarchical data by circle packing"</a> by W. Wang,
  12018. * H. Wang, G. Dai, and H. Wang, ACM CHI 2006.
  12019. */
  12020. pv.Layout.Pack = function() {
  12021. pv.Layout.Hierarchy.call(this);
  12022. this.node
  12023. .radius(function(n) { return n.radius; })
  12024. .strokeStyle("rgb(31, 119, 180)")
  12025. .fillStyle("rgba(31, 119, 180, .25)");
  12026. this.label
  12027. .textAlign("center");
  12028. /* Hide unsupported link. */
  12029. delete this.link;
  12030. };
  12031. pv.Layout.Pack.prototype = pv.extend(pv.Layout.Hierarchy)
  12032. .property("spacing", Number)
  12033. .property("order", String); // ascending, descending, reverse, null
  12034. /**
  12035. * Default properties for circle-packing layouts. The default spacing parameter
  12036. * is 1 and the default order is "ascending".
  12037. *
  12038. * @type pv.Layout.Pack
  12039. */
  12040. pv.Layout.Pack.prototype.defaults = new pv.Layout.Pack()
  12041. .extend(pv.Layout.Hierarchy.prototype.defaults)
  12042. .spacing(1)
  12043. .order("ascending");
  12044. /**
  12045. * The spacing parameter; defaults to 1, which provides a little bit of padding
  12046. * between sibling nodes and the enclosing circle. Larger values increase the
  12047. * spacing, by making the sibling nodes smaller; a value of zero makes the leaf
  12048. * nodes as large as possible, with no padding on enclosing circles.
  12049. *
  12050. * @type number
  12051. * @name pv.Layout.Pack.prototype.spacing
  12052. */
  12053. /**
  12054. * The sibling node order. The default order is <tt>null</tt>, which means to
  12055. * use the sibling order specified by the nodes property as-is. A value of
  12056. * "ascending" will sort siblings in ascending order of size, while "descending"
  12057. * will do the reverse. For sorting based on data attributes other than size,
  12058. * use the default <tt>null</tt> for the order property, and sort the nodes
  12059. * beforehand using the {@link pv.Dom} operator.
  12060. *
  12061. * @see pv.Dom.Node#sort
  12062. * @type string
  12063. * @name pv.Layout.Pack.prototype.order
  12064. */
  12065. /** @private The default size function. */
  12066. pv.Layout.Pack.prototype.$radius = function() { return 1; };
  12067. // TODO is it possible for spacing to operate in pixel space?
  12068. // Right now it appears to be multiples of the smallest radius.
  12069. /**
  12070. * Specifies the sizing function. By default, a sizing function is disabled and
  12071. * all nodes are given constant size. The sizing function is invoked for each
  12072. * leaf node in the tree (passed to the constructor).
  12073. *
  12074. * <p>For example, if the tree data structure represents a file system, with
  12075. * files as leaf nodes, and each file has a <tt>bytes</tt> attribute, you can
  12076. * specify a size function as:
  12077. *
  12078. * <pre> .size(function(d) d.bytes)</pre>
  12079. *
  12080. * As with other properties, a size function may specify additional arguments to
  12081. * access the data associated with the layout and any enclosing panels.
  12082. *
  12083. * @param {function} f the new sizing function.
  12084. * @returns {pv.Layout.Pack} this.
  12085. */
  12086. pv.Layout.Pack.prototype.size = function(f) {
  12087. this.$radius = typeof f == "function"
  12088. ? function() { return Math.sqrt(f.apply(this, arguments)); }
  12089. : (f = Math.sqrt(f), function() { return f; });
  12090. return this;
  12091. };
  12092. /** @private */
  12093. pv.Layout.Pack.prototype.buildImplied = function(s) {
  12094. if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return;
  12095. var that = this,
  12096. nodes = s.nodes,
  12097. root = nodes[0];
  12098. /** @private Compute the radii of the leaf nodes. */
  12099. function radii(nodes) {
  12100. var stack = pv.Mark.stack;
  12101. stack.unshift(null);
  12102. for (var i = 0, n = nodes.length; i < n; i++) {
  12103. var c = nodes[i];
  12104. if (!c.firstChild) {
  12105. c.radius = that.$radius.apply(that, (stack[0] = c, stack));
  12106. }
  12107. }
  12108. stack.shift();
  12109. }
  12110. /** @private */
  12111. function packTree(n) {
  12112. var nodes = [];
  12113. for (var c = n.firstChild; c; c = c.nextSibling) {
  12114. if (c.firstChild) c.radius = packTree(c);
  12115. c.n = c.p = c;
  12116. nodes.push(c);
  12117. }
  12118. /* Sort. */
  12119. switch (s.order) {
  12120. case "ascending": {
  12121. nodes.sort(function(a, b) { return a.radius - b.radius; });
  12122. break;
  12123. }
  12124. case "descending": {
  12125. nodes.sort(function(a, b) { return b.radius - a.radius; });
  12126. break;
  12127. }
  12128. case "reverse": nodes.reverse(); break;
  12129. }
  12130. return packCircle(nodes);
  12131. }
  12132. /** @private */
  12133. function packCircle(nodes) {
  12134. var xMin = Infinity,
  12135. xMax = -Infinity,
  12136. yMin = Infinity,
  12137. yMax = -Infinity,
  12138. a, b, c, j, k;
  12139. /** @private */
  12140. function bound(n) {
  12141. xMin = Math.min(n.x - n.radius, xMin);
  12142. xMax = Math.max(n.x + n.radius, xMax);
  12143. yMin = Math.min(n.y - n.radius, yMin);
  12144. yMax = Math.max(n.y + n.radius, yMax);
  12145. }
  12146. /** @private */
  12147. function insert(a, b) {
  12148. var c = a.n;
  12149. a.n = b;
  12150. b.p = a;
  12151. b.n = c;
  12152. c.p = b;
  12153. }
  12154. /** @private */
  12155. function splice(a, b) {
  12156. a.n = b;
  12157. b.p = a;
  12158. }
  12159. /** @private */
  12160. function intersects(a, b) {
  12161. var dx = b.x - a.x,
  12162. dy = b.y - a.y,
  12163. dr = a.radius + b.radius;
  12164. return (dr * dr - dx * dx - dy * dy) > .001; // within epsilon
  12165. }
  12166. /* Create first node. */
  12167. a = nodes[0];
  12168. a.x = -a.radius;
  12169. a.y = 0;
  12170. bound(a);
  12171. /* Create second node. */
  12172. if (nodes.length > 1) {
  12173. b = nodes[1];
  12174. b.x = b.radius;
  12175. b.y = 0;
  12176. bound(b);
  12177. /* Create third node and build chain. */
  12178. if (nodes.length > 2) {
  12179. c = nodes[2];
  12180. place(a, b, c);
  12181. bound(c);
  12182. insert(a, c);
  12183. a.p = c;
  12184. insert(c, b);
  12185. b = a.n;
  12186. /* Now iterate through the rest. */
  12187. for (var i = 3; i < nodes.length; i++) {
  12188. place(a, b, c = nodes[i]);
  12189. /* Search for the closest intersection. */
  12190. var isect = 0, s1 = 1, s2 = 1;
  12191. for (j = b.n; j != b; j = j.n, s1++) {
  12192. if (intersects(j, c)) {
  12193. isect = 1;
  12194. break;
  12195. }
  12196. }
  12197. if (isect == 1) {
  12198. for (k = a.p; k != j.p; k = k.p, s2++) {
  12199. if (intersects(k, c)) {
  12200. if (s2 < s1) {
  12201. isect = -1;
  12202. j = k;
  12203. }
  12204. break;
  12205. }
  12206. }
  12207. }
  12208. /* Update node chain. */
  12209. if (isect == 0) {
  12210. insert(a, c);
  12211. b = c;
  12212. bound(c);
  12213. } else if (isect > 0) {
  12214. splice(a, j);
  12215. b = j;
  12216. i--;
  12217. } else if (isect < 0) {
  12218. splice(j, b);
  12219. a = j;
  12220. i--;
  12221. }
  12222. }
  12223. }
  12224. }
  12225. /* Re-center the circles and return the encompassing radius. */
  12226. var cx = (xMin + xMax) / 2,
  12227. cy = (yMin + yMax) / 2,
  12228. cr = 0;
  12229. for (var i = 0; i < nodes.length; i++) {
  12230. var n = nodes[i];
  12231. n.x -= cx;
  12232. n.y -= cy;
  12233. cr = Math.max(cr, n.radius + Math.sqrt(n.x * n.x + n.y * n.y));
  12234. }
  12235. return cr + s.spacing;
  12236. }
  12237. /** @private */
  12238. function place(a, b, c) {
  12239. var da = b.radius + c.radius,
  12240. db = a.radius + c.radius,
  12241. dx = b.x - a.x,
  12242. dy = b.y - a.y,
  12243. dc = Math.sqrt(dx * dx + dy * dy),
  12244. cos = (db * db + dc * dc - da * da) / (2 * db * dc),
  12245. theta = Math.acos(cos),
  12246. x = cos * db,
  12247. h = Math.sin(theta) * db;
  12248. dx /= dc;
  12249. dy /= dc;
  12250. c.x = a.x + x * dx + h * dy;
  12251. c.y = a.y + x * dy - h * dx;
  12252. }
  12253. /** @private */
  12254. function transform(n, x, y, k) {
  12255. for (var c = n.firstChild; c; c = c.nextSibling) {
  12256. c.x += n.x;
  12257. c.y += n.y;
  12258. transform(c, x, y, k);
  12259. }
  12260. n.x = x + k * n.x;
  12261. n.y = y + k * n.y;
  12262. n.radius *= k;
  12263. }
  12264. radii(nodes);
  12265. /* Recursively compute the layout. */
  12266. root.x = 0;
  12267. root.y = 0;
  12268. root.radius = packTree(root);
  12269. var w = this.width(),
  12270. h = this.height(),
  12271. k = 1 / Math.max(2 * root.radius / w, 2 * root.radius / h);
  12272. transform(root, w / 2, h / 2, k);
  12273. };
  12274. /**
  12275. * Constructs a new, empty force-directed layout. Layouts are not typically
  12276. * constructed directly; instead, they are added to an existing panel via
  12277. * {@link pv.Mark#add}.
  12278. *
  12279. * @class Implements force-directed network layout as a node-link diagram. This
  12280. * layout uses the Fruchterman-Reingold algorithm, which applies an attractive
  12281. * spring force between neighboring nodes, and a repulsive electrical charge
  12282. * force between all nodes. An additional drag force improves stability of the
  12283. * simulation. See {@link pv.Force.spring}, {@link pv.Force.drag} and {@link
  12284. * pv.Force.charge} for more details; note that the n-body charge force is
  12285. * approximated using the Barnes-Hut algorithm.
  12286. *
  12287. * <p>This layout is implemented on top of {@link pv.Simulation}, which can be
  12288. * used directly for more control over simulation parameters. The simulation
  12289. * uses Position Verlet integration, which does not compute velocities
  12290. * explicitly, but allows for easy geometric constraints, such as bounding the
  12291. * nodes within the layout panel. Many of the configuration properties supported
  12292. * by this layout are simply passed through to the underlying forces and
  12293. * constraints of the simulation.
  12294. *
  12295. * <p>Force layouts are typically interactive. The gradual movement of the nodes
  12296. * as they stabilize to a local stress minimum can help reveal the structure of
  12297. * the network, as can {@link pv.Behavior.drag}, which allows the user to pick
  12298. * up nodes and reposition them while the physics simulation continues. This
  12299. * layout can also be used with pan &amp; zoom behaviors for interaction.
  12300. *
  12301. * <p>To facilitate interaction, this layout by default automatically re-renders
  12302. * using a <tt>setInterval</tt> every 42 milliseconds. This can be disabled via
  12303. * the <tt>iterations</tt> property, which if non-null specifies the number of
  12304. * simulation iterations to run before the force-directed layout is finalized.
  12305. * Be careful not to use too high an iteration count, as this can lead to an
  12306. * annoying delay on page load.
  12307. *
  12308. * <p>As with other network layouts, the network data can be updated
  12309. * dynamically, provided the property cache is reset. See
  12310. * {@link pv.Layout.Network} for details. New nodes are initialized with random
  12311. * positions near the center. Alternatively, positions can be specified manually
  12312. * by setting the <tt>x</tt> and <tt>y</tt> attributes on nodes.
  12313. *
  12314. * @extends pv.Layout.Network
  12315. * @see <a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.13.8444&rep=rep1&type=pdf"
  12316. * >"Graph Drawing by Force-directed Placement"</a> by T. Fruchterman &amp;
  12317. * E. Reingold, Software--Practice &amp; Experience, November 1991.
  12318. */
  12319. pv.Layout.Force = function() {
  12320. pv.Layout.Network.call(this);
  12321. /* Force-directed graphs can be messy, so reduce the link width. */
  12322. this.link.lineWidth(function(d, p) { return Math.sqrt(p.linkValue) * 1.5; });
  12323. this.label.textAlign("center");
  12324. };
  12325. pv.Layout.Force.prototype = pv.extend(pv.Layout.Network)
  12326. .property("bound", Boolean)
  12327. .property("iterations", Number)
  12328. .property("dragConstant", Number)
  12329. .property("chargeConstant", Number)
  12330. .property("chargeMinDistance", Number)
  12331. .property("chargeMaxDistance", Number)
  12332. .property("chargeTheta", Number)
  12333. .property("springConstant", Number)
  12334. .property("springDamping", Number)
  12335. .property("springLength", Number);
  12336. /**
  12337. * The bound parameter; true if nodes should be constrained within the layout
  12338. * panel. Bounding is disabled by default. Currently the layout does not observe
  12339. * the radius of the nodes; strictly speaking, only the center of the node is
  12340. * constrained to be within the panel, with an additional 6-pixel offset for
  12341. * padding. A future enhancement could extend the bound constraint to observe
  12342. * the node's radius, which would also support bounding for variable-size nodes.
  12343. *
  12344. * <p>Note that if this layout is used in conjunction with pan &amp; zoom
  12345. * behaviors, those behaviors should have their bound parameter set to the same
  12346. * value.
  12347. *
  12348. * @type boolean
  12349. * @name pv.Layout.Force.prototype.bound
  12350. */
  12351. /**
  12352. * The number of simulation iterations to run, or null if this layout is
  12353. * interactive. Force-directed layouts are interactive by default, using a
  12354. * <tt>setInterval</tt> to advance the physics simulation and re-render
  12355. * automatically.
  12356. *
  12357. * @type number
  12358. * @name pv.Layout.Force.prototype.iterations
  12359. */
  12360. /**
  12361. * The drag constant, in the range [0,1]. A value of 0 means no drag (a
  12362. * perfectly frictionless environment), while a value of 1 means friction
  12363. * immediately cancels all momentum. The default value is 0.1, which provides a
  12364. * minimum amount of drag that helps stabilize bouncy springs; lower values may
  12365. * result in excessive bounciness, while higher values cause the simulation to
  12366. * take longer to converge.
  12367. *
  12368. * @type number
  12369. * @name pv.Layout.Force.prototype.dragConstant
  12370. * @see pv.Force.drag#constant
  12371. */
  12372. /**
  12373. * The charge constant, which should be a negative number. The default value is
  12374. * -40; more negative values will result in a stronger repulsive force, which
  12375. * may lead to faster convergence at the risk of instability. Too strong
  12376. * repulsive charge forces can cause comparatively weak springs to be stretched
  12377. * well beyond their rest length, emphasizing global structure over local
  12378. * structure. A nonnegative value will break the Fruchterman-Reingold algorithm,
  12379. * and is for entertainment purposes only.
  12380. *
  12381. * @type number
  12382. * @name pv.Layout.Force.prototype.chargeConstant
  12383. * @see pv.Force.charge#constant
  12384. */
  12385. /**
  12386. * The minimum distance at which charge forces are applied. The default minimum
  12387. * distance of 2 avoids applying forces that are two strong; because the physics
  12388. * simulation is run at discrete time intervals, it is possible for two same-
  12389. * charged particles to become very close or even a singularity! Since the
  12390. * charge force is inversely proportional to the square of the distance, very
  12391. * small distances can break the simulation.
  12392. *
  12393. * <p>In rare cases, two particles can become stuck on top of each other, as a
  12394. * minimum distance threshold will prevent the charge force from repelling them.
  12395. * However, this occurs very rarely because other forces and momentum typically
  12396. * cause the particles to become separated again, at which point the repulsive
  12397. * charge force kicks in.
  12398. *
  12399. * @type number
  12400. * @name pv.Layout.Force.prototype.chargeMinDistance
  12401. * @see pv.Force.charge#domain
  12402. */
  12403. /**
  12404. * The maximum distance at which charge forces are applied. This improves
  12405. * performance by ignoring weak charge forces at great distances. Note that this
  12406. * parameter is partly redundant, as the Barnes-Hut algorithm for n-body forces
  12407. * already improves performance for far-away particles through approximation.
  12408. *
  12409. * @type number
  12410. * @name pv.Layout.Force.prototype.chargeMaxDistance
  12411. * @see pv.Force.charge#domain
  12412. */
  12413. /**
  12414. * The Barnes-Hut approximation factor. The Barnes-Hut approximation criterion
  12415. * is the ratio of the size of the quadtree node to the distance from the point
  12416. * to the node's center of mass is beneath some threshold. The default value is
  12417. * 0.9.
  12418. *
  12419. * @type number
  12420. * @name pv.Layout.Force.prototype.chargeTheta
  12421. * @see pv.Force.charge#theta
  12422. */
  12423. /**
  12424. * The spring constant, which should be a positive number. The default value is
  12425. * 0.1; greater values will result in a stronger attractive force, which may
  12426. * lead to faster convergence at the risk of instability. Too strong spring
  12427. * forces can cause comparatively weak charge forces to be ignored, emphasizing
  12428. * local structure over global structure. A nonpositive value will break the
  12429. * Fruchterman-Reingold algorithm, and is for entertainment purposes only.
  12430. *
  12431. * <p>The spring tension is automatically normalized using the inverse square
  12432. * root of the maximum link degree of attached nodes.
  12433. *
  12434. * @type number
  12435. * @name pv.Layout.Force.prototype.springConstant
  12436. * @see pv.Force.spring#constant
  12437. */
  12438. /**
  12439. * The spring damping factor, in the range [0,1]. Damping functions identically
  12440. * to drag forces, damping spring bounciness by applying a force in the opposite
  12441. * direction of attached nodes' velocities. The default value is 0.3.
  12442. *
  12443. * <p>The spring damping is automatically normalized using the inverse square
  12444. * root of the maximum link degree of attached nodes.
  12445. *
  12446. * @type number
  12447. * @name pv.Layout.Force.prototype.springDamping
  12448. * @see pv.Force.spring#damping
  12449. */
  12450. /**
  12451. * The spring rest length. The default value is 20 pixels. Larger values may be
  12452. * appropriate if the layout panel is larger, or if the nodes are rendered
  12453. * larger than the default dot size of 20.
  12454. *
  12455. * @type number
  12456. * @name pv.Layout.Force.prototype.springLength
  12457. * @see pv.Force.spring#length
  12458. */
  12459. /**
  12460. * Default properties for force-directed layouts. The default drag constant is
  12461. * 0.1, the default charge constant is -40 (with a domain of [2, 500] and theta
  12462. * of 0.9), and the default spring constant is 0.1 (with a damping of 0.3 and a
  12463. * rest length of 20).
  12464. *
  12465. * @type pv.Layout.Force
  12466. */
  12467. pv.Layout.Force.prototype.defaults = new pv.Layout.Force()
  12468. .extend(pv.Layout.Network.prototype.defaults)
  12469. .dragConstant(.1)
  12470. .chargeConstant(-40)
  12471. .chargeMinDistance(2)
  12472. .chargeMaxDistance(500)
  12473. .chargeTheta(.9)
  12474. .springConstant(.1)
  12475. .springDamping(.3)
  12476. .springLength(20);
  12477. /** @private Initialize the physics simulation. */
  12478. pv.Layout.Force.prototype.buildImplied = function(s) {
  12479. /* Any cached interactive layouts need to be rebound for the timer. */
  12480. if (pv.Layout.Network.prototype.buildImplied.call(this, s)) {
  12481. var f = s.$force;
  12482. if (f) {
  12483. f.next = this.binds.$force;
  12484. this.binds.$force = f;
  12485. }
  12486. return;
  12487. }
  12488. var that = this,
  12489. nodes = s.nodes,
  12490. links = s.links,
  12491. k = s.iterations,
  12492. w = s.width,
  12493. h = s.height;
  12494. /* Initialize positions randomly near the center. */
  12495. for (var i = 0, n; i < nodes.length; i++) {
  12496. n = nodes[i];
  12497. if (isNaN(n.x)) n.x = w / 2 + 40 * Math.random() - 20;
  12498. if (isNaN(n.y)) n.y = h / 2 + 40 * Math.random() - 20;
  12499. }
  12500. /* Initialize the simulation. */
  12501. var sim = pv.simulation(nodes);
  12502. /* Drag force. */
  12503. sim.force(pv.Force.drag(s.dragConstant));
  12504. /* Charge (repelling) force. */
  12505. sim.force(pv.Force.charge(s.chargeConstant)
  12506. .domain(s.chargeMinDistance, s.chargeMaxDistance)
  12507. .theta(s.chargeTheta));
  12508. /* Spring (attracting) force. */
  12509. sim.force(pv.Force.spring(s.springConstant)
  12510. .damping(s.springDamping)
  12511. .length(s.springLength)
  12512. .links(links));
  12513. /* Position constraint (for interactive dragging). */
  12514. sim.constraint(pv.Constraint.position());
  12515. /* Optionally add bound constraint. TODO: better padding. */
  12516. if (s.bound) {
  12517. sim.constraint(pv.Constraint.bound().x(6, w - 6).y(6, h - 6));
  12518. }
  12519. /** @private Returns the speed of the given node, to determine cooling. */
  12520. function speed(n) {
  12521. return n.fix ? 1 : n.vx * n.vx + n.vy * n.vy;
  12522. }
  12523. /*
  12524. * If the iterations property is null (the default), the layout is
  12525. * interactive. The simulation is run until the fastest particle drops below
  12526. * an arbitrary minimum speed. Although the timer keeps firing, this speed
  12527. * calculation is fast so there is minimal CPU overhead. Note: if a particle
  12528. * is fixed for interactivity, treat this as a high speed and resume
  12529. * simulation.
  12530. */
  12531. if (k == null) {
  12532. sim.step(); // compute initial previous velocities
  12533. sim.step(); // compute initial velocities
  12534. /* Add the simulation state to the bound list. */
  12535. var force = s.$force = this.binds.$force = {
  12536. next: this.binds.$force,
  12537. nodes: nodes,
  12538. min: 1e-4 * (links.length + 1),
  12539. sim: sim
  12540. };
  12541. /* Start the timer, if not already started. */
  12542. if (!this.$timer) this.$timer = setInterval(function() {
  12543. var render = false;
  12544. for (var f = that.binds.$force; f; f = f.next) {
  12545. if (pv.max(f.nodes, speed) > f.min) {
  12546. f.sim.step();
  12547. render = true;
  12548. }
  12549. }
  12550. if (render) that.render();
  12551. }, 42);
  12552. } else for (var i = 0; i < k; i++) {
  12553. sim.step();
  12554. }
  12555. };
  12556. /**
  12557. * Constructs a new, empty cluster layout. Layouts are not typically
  12558. * constructed directly; instead, they are added to an existing panel via
  12559. * {@link pv.Mark#add}.
  12560. *
  12561. * @class Implements a hierarchical layout using the cluster (or dendrogram)
  12562. * algorithm. This layout provides both node-link and space-filling
  12563. * implementations of cluster diagrams. In many ways it is similar to
  12564. * {@link pv.Layout.Partition}, except that leaf nodes are positioned at maximum
  12565. * depth, and the depth of internal nodes is based on their distance from their
  12566. * deepest descendant, rather than their distance from the root.
  12567. *
  12568. * <p>The cluster layout supports a "group" property, which if true causes
  12569. * siblings to be positioned closer together than unrelated nodes at the same
  12570. * depth. Unlike the partition layout, this layout does not support dynamic
  12571. * sizing for leaf nodes; all leaf nodes are the same size.
  12572. *
  12573. * <p>For more details on how to use this layout, see
  12574. * {@link pv.Layout.Hierarchy}.
  12575. *
  12576. * @see pv.Layout.Cluster.Fill
  12577. * @extends pv.Layout.Hierarchy
  12578. */
  12579. pv.Layout.Cluster = function() {
  12580. pv.Layout.Hierarchy.call(this);
  12581. var interpolate, // cached interpolate
  12582. buildImplied = this.buildImplied;
  12583. /** @private Cache layout state to optimize properties. */
  12584. this.buildImplied = function(s) {
  12585. buildImplied.call(this, s);
  12586. interpolate
  12587. = /^(top|bottom)$/.test(s.orient) ? "step-before"
  12588. : /^(left|right)$/.test(s.orient) ? "step-after"
  12589. : "linear";
  12590. };
  12591. this.link.interpolate(function() { return interpolate; });
  12592. };
  12593. pv.Layout.Cluster.prototype = pv.extend(pv.Layout.Hierarchy)
  12594. .property("group", Number)
  12595. .property("orient", String)
  12596. .property("innerRadius", Number)
  12597. .property("outerRadius", Number);
  12598. /**
  12599. * The group parameter; defaults to 0, disabling grouping of siblings. If this
  12600. * parameter is set to a positive number (or true, which is equivalent to 1),
  12601. * then additional space will be allotted between sibling groups. In other
  12602. * words, siblings (nodes that share the same parent) will be positioned more
  12603. * closely than nodes at the same depth that do not share a parent.
  12604. *
  12605. * @type number
  12606. * @name pv.Layout.Cluster.prototype.group
  12607. */
  12608. /**
  12609. * The orientation. The default orientation is "top", which means that the root
  12610. * node is placed on the top edge, leaf nodes appear on the bottom edge, and
  12611. * internal nodes are in-between. The following orientations are supported:<ul>
  12612. *
  12613. * <li>left - left-to-right.
  12614. * <li>right - right-to-left.
  12615. * <li>top - top-to-bottom.
  12616. * <li>bottom - bottom-to-top.
  12617. * <li>radial - radially, with the root at the center.</ul>
  12618. *
  12619. * @type string
  12620. * @name pv.Layout.Cluster.prototype.orient
  12621. */
  12622. /**
  12623. * The inner radius; defaults to 0. This property applies only to radial
  12624. * orientations, and can be used to compress the layout radially. Note that for
  12625. * the node-link implementation, the root node is always at the center,
  12626. * regardless of the value of this property; this property only affects internal
  12627. * and leaf nodes. For the space-filling implementation, a non-zero value of
  12628. * this property will result in the root node represented as a ring rather than
  12629. * a circle.
  12630. *
  12631. * @type number
  12632. * @name pv.Layout.Cluster.prototype.innerRadius
  12633. */
  12634. /**
  12635. * The outer radius; defaults to fill the containing panel, based on the height
  12636. * and width of the layout. If the layout has no height and width specified, it
  12637. * will extend to fill the enclosing panel.
  12638. *
  12639. * @type number
  12640. * @name pv.Layout.Cluster.prototype.outerRadius
  12641. */
  12642. /**
  12643. * Defaults for cluster layouts. The default group parameter is 0 and the
  12644. * default orientation is "top".
  12645. *
  12646. * @type pv.Layout.Cluster
  12647. */
  12648. pv.Layout.Cluster.prototype.defaults = new pv.Layout.Cluster()
  12649. .extend(pv.Layout.Hierarchy.prototype.defaults)
  12650. .group(0)
  12651. .orient("top");
  12652. /** @private */
  12653. pv.Layout.Cluster.prototype.buildImplied = function(s) {
  12654. if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return;
  12655. var root = s.nodes[0],
  12656. group = s.group,
  12657. breadth,
  12658. depth,
  12659. leafCount = 0,
  12660. leafIndex = .5 - group / 2;
  12661. /* Count the leaf nodes and compute the depth of descendants. */
  12662. var p = undefined;
  12663. root.visitAfter(function(n) {
  12664. if (n.firstChild) {
  12665. n.depth = 1 + pv.max(n.childNodes, function(n) { return n.depth; });
  12666. } else {
  12667. if (group && (p != n.parentNode)) {
  12668. p = n.parentNode;
  12669. leafCount += group;
  12670. }
  12671. leafCount++;
  12672. n.depth = 0;
  12673. }
  12674. });
  12675. breadth = 1 / leafCount;
  12676. depth = 1 / root.depth;
  12677. /* Compute the unit breadth and depth of each node. */
  12678. var p = undefined;
  12679. root.visitAfter(function(n) {
  12680. if (n.firstChild) {
  12681. n.breadth = pv.mean(n.childNodes, function(n) { return n.breadth; });
  12682. } else {
  12683. if (group && (p != n.parentNode)) {
  12684. p = n.parentNode;
  12685. leafIndex += group;
  12686. }
  12687. n.breadth = breadth * leafIndex++;
  12688. }
  12689. n.depth = 1 - n.depth * depth;
  12690. });
  12691. /* Compute breadth and depth ranges for space-filling layouts. */
  12692. root.visitAfter(function(n) {
  12693. n.minBreadth = n.firstChild
  12694. ? n.firstChild.minBreadth
  12695. : (n.breadth - breadth / 2);
  12696. n.maxBreadth = n.firstChild
  12697. ? n.lastChild.maxBreadth
  12698. : (n.breadth + breadth / 2);
  12699. });
  12700. root.visitBefore(function(n) {
  12701. n.minDepth = n.parentNode
  12702. ? n.parentNode.maxDepth
  12703. : 0;
  12704. n.maxDepth = n.parentNode
  12705. ? (n.depth + root.depth)
  12706. : (n.minDepth + 2 * root.depth);
  12707. });
  12708. root.minDepth = -depth;
  12709. pv.Layout.Hierarchy.NodeLink.buildImplied.call(this, s);
  12710. };
  12711. /**
  12712. * Constructs a new, empty space-filling cluster layout. Layouts are not
  12713. * typically constructed directly; instead, they are added to an existing panel
  12714. * via {@link pv.Mark#add}.
  12715. *
  12716. * @class A variant of cluster layout that is space-filling. The meaning of the
  12717. * exported mark prototypes changes slightly in the space-filling
  12718. * implementation:<ul>
  12719. *
  12720. * <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Bar} for
  12721. * non-radial orientations, and a {@link pv.Wedge} for radial orientations.
  12722. *
  12723. * <p><li><tt>link</tt> - unsupported; undefined. Links are encoded implicitly
  12724. * in the arrangement of the space-filling nodes.
  12725. *
  12726. * <p><li><tt>label</tt> - for rendering node labels; typically a
  12727. * {@link pv.Label}.
  12728. *
  12729. * </ul>For more details on how to use this layout, see
  12730. * {@link pv.Layout.Cluster}.
  12731. *
  12732. * @extends pv.Layout.Cluster
  12733. */
  12734. pv.Layout.Cluster.Fill = function() {
  12735. pv.Layout.Cluster.call(this);
  12736. pv.Layout.Hierarchy.Fill.constructor.call(this);
  12737. };
  12738. pv.Layout.Cluster.Fill.prototype = pv.extend(pv.Layout.Cluster);
  12739. /** @private */
  12740. pv.Layout.Cluster.Fill.prototype.buildImplied = function(s) {
  12741. if (pv.Layout.Cluster.prototype.buildImplied.call(this, s)) return;
  12742. pv.Layout.Hierarchy.Fill.buildImplied.call(this, s);
  12743. };
  12744. /**
  12745. * Constructs a new, empty partition layout. Layouts are not typically
  12746. * constructed directly; instead, they are added to an existing panel via
  12747. * {@link pv.Mark#add}.
  12748. *
  12749. * @class Implemeents a hierarchical layout using the partition (or sunburst,
  12750. * icicle) algorithm. This layout provides both node-link and space-filling
  12751. * implementations of partition diagrams. In many ways it is similar to
  12752. * {@link pv.Layout.Cluster}, except that leaf nodes are positioned based on
  12753. * their distance from the root.
  12754. *
  12755. * <p>The partition layout support dynamic sizing for leaf nodes, if a
  12756. * {@link #size} psuedo-property is specified. The default size function returns
  12757. * 1, causing all leaf nodes to be sized equally, and all internal nodes to be
  12758. * sized by the number of leaf nodes they have as descendants.
  12759. *
  12760. * <p>The size function can be used in conjunction with the order property,
  12761. * which allows the nodes to the sorted by the computed size. Note: for sorting
  12762. * based on other data attributes, simply use the default <tt>null</tt> for the
  12763. * order property, and sort the nodes beforehand using the {@link pv.Dom}
  12764. * operator.
  12765. *
  12766. * <p>For more details on how to use this layout, see
  12767. * {@link pv.Layout.Hierarchy}.
  12768. *
  12769. * @see pv.Layout.Partition.Fill
  12770. * @extends pv.Layout.Hierarchy
  12771. */
  12772. pv.Layout.Partition = function() {
  12773. pv.Layout.Hierarchy.call(this);
  12774. };
  12775. pv.Layout.Partition.prototype = pv.extend(pv.Layout.Hierarchy)
  12776. .property("order", String) // null, ascending, descending?
  12777. .property("orient", String) // top, left, right, bottom, radial
  12778. .property("innerRadius", Number)
  12779. .property("outerRadius", Number);
  12780. /**
  12781. * The sibling node order. The default order is <tt>null</tt>, which means to
  12782. * use the sibling order specified by the nodes property as-is. A value of
  12783. * "ascending" will sort siblings in ascending order of size, while "descending"
  12784. * will do the reverse. For sorting based on data attributes other than size,
  12785. * use the default <tt>null</tt> for the order property, and sort the nodes
  12786. * beforehand using the {@link pv.Dom} operator.
  12787. *
  12788. * @see pv.Dom.Node#sort
  12789. * @type string
  12790. * @name pv.Layout.Partition.prototype.order
  12791. */
  12792. /**
  12793. * The orientation. The default orientation is "top", which means that the root
  12794. * node is placed on the top edge, leaf nodes appear at the bottom, and internal
  12795. * nodes are in-between. The following orientations are supported:<ul>
  12796. *
  12797. * <li>left - left-to-right.
  12798. * <li>right - right-to-left.
  12799. * <li>top - top-to-bottom.
  12800. * <li>bottom - bottom-to-top.
  12801. * <li>radial - radially, with the root at the center.</ul>
  12802. *
  12803. * @type string
  12804. * @name pv.Layout.Partition.prototype.orient
  12805. */
  12806. /**
  12807. * The inner radius; defaults to 0. This property applies only to radial
  12808. * orientations, and can be used to compress the layout radially. Note that for
  12809. * the node-link implementation, the root node is always at the center,
  12810. * regardless of the value of this property; this property only affects internal
  12811. * and leaf nodes. For the space-filling implementation, a non-zero value of
  12812. * this property will result in the root node represented as a ring rather than
  12813. * a circle.
  12814. *
  12815. * @type number
  12816. * @name pv.Layout.Partition.prototype.innerRadius
  12817. */
  12818. /**
  12819. * The outer radius; defaults to fill the containing panel, based on the height
  12820. * and width of the layout. If the layout has no height and width specified, it
  12821. * will extend to fill the enclosing panel.
  12822. *
  12823. * @type number
  12824. * @name pv.Layout.Partition.prototype.outerRadius
  12825. */
  12826. /**
  12827. * Default properties for partition layouts. The default orientation is "top".
  12828. *
  12829. * @type pv.Layout.Partition
  12830. */
  12831. pv.Layout.Partition.prototype.defaults = new pv.Layout.Partition()
  12832. .extend(pv.Layout.Hierarchy.prototype.defaults)
  12833. .orient("top");
  12834. /** @private */
  12835. pv.Layout.Partition.prototype.$size = function() { return 1; };
  12836. /**
  12837. * Specifies the sizing function. By default, a sizing function is disabled and
  12838. * all nodes are given constant size. The sizing function is invoked for each
  12839. * leaf node in the tree (passed to the constructor).
  12840. *
  12841. * <p>For example, if the tree data structure represents a file system, with
  12842. * files as leaf nodes, and each file has a <tt>bytes</tt> attribute, you can
  12843. * specify a size function as:
  12844. *
  12845. * <pre> .size(function(d) d.bytes)</pre>
  12846. *
  12847. * As with other properties, a size function may specify additional arguments to
  12848. * access the data associated with the layout and any enclosing panels.
  12849. *
  12850. * @param {function} f the new sizing function.
  12851. * @returns {pv.Layout.Partition} this.
  12852. */
  12853. pv.Layout.Partition.prototype.size = function(f) {
  12854. this.$size = f;
  12855. return this;
  12856. };
  12857. /** @private */
  12858. pv.Layout.Partition.prototype.buildImplied = function(s) {
  12859. if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return;
  12860. var that = this,
  12861. root = s.nodes[0],
  12862. stack = pv.Mark.stack,
  12863. maxDepth = 0;
  12864. /* Recursively compute the tree depth and node size. */
  12865. stack.unshift(null);
  12866. root.visitAfter(function(n, i) {
  12867. if (i > maxDepth) maxDepth = i;
  12868. n.size = n.firstChild
  12869. ? pv.sum(n.childNodes, function(n) { return n.size; })
  12870. : that.$size.apply(that, (stack[0] = n, stack));
  12871. });
  12872. stack.shift();
  12873. /* Order */
  12874. switch (s.order) {
  12875. case "ascending": root.sort(function(a, b) { return a.size - b.size; }); break;
  12876. case "descending": root.sort(function(b, a) { return a.size - b.size; }); break;
  12877. }
  12878. /* Compute the unit breadth and depth of each node. */
  12879. var ds = 1 / maxDepth;
  12880. root.minBreadth = 0;
  12881. root.breadth = .5;
  12882. root.maxBreadth = 1;
  12883. root.visitBefore(function(n) {
  12884. var b = n.minBreadth, s = n.maxBreadth - b;
  12885. for (var c = n.firstChild; c; c = c.nextSibling) {
  12886. c.minBreadth = b;
  12887. c.maxBreadth = b += (c.size / n.size) * s;
  12888. c.breadth = (b + c.minBreadth) / 2;
  12889. }
  12890. });
  12891. root.visitAfter(function(n, i) {
  12892. n.minDepth = (i - 1) * ds;
  12893. n.maxDepth = n.depth = i * ds;
  12894. });
  12895. pv.Layout.Hierarchy.NodeLink.buildImplied.call(this, s);
  12896. };
  12897. /**
  12898. * Constructs a new, empty space-filling partition layout. Layouts are not
  12899. * typically constructed directly; instead, they are added to an existing panel
  12900. * via {@link pv.Mark#add}.
  12901. *
  12902. * @class A variant of partition layout that is space-filling. The meaning of
  12903. * the exported mark prototypes changes slightly in the space-filling
  12904. * implementation:<ul>
  12905. *
  12906. * <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Bar} for
  12907. * non-radial orientations, and a {@link pv.Wedge} for radial orientations.
  12908. *
  12909. * <p><li><tt>link</tt> - unsupported; undefined. Links are encoded implicitly
  12910. * in the arrangement of the space-filling nodes.
  12911. *
  12912. * <p><li><tt>label</tt> - for rendering node labels; typically a
  12913. * {@link pv.Label}.
  12914. *
  12915. * </ul>For more details on how to use this layout, see
  12916. * {@link pv.Layout.Partition}.
  12917. *
  12918. * @extends pv.Layout.Partition
  12919. */
  12920. pv.Layout.Partition.Fill = function() {
  12921. pv.Layout.Partition.call(this);
  12922. pv.Layout.Hierarchy.Fill.constructor.call(this);
  12923. };
  12924. pv.Layout.Partition.Fill.prototype = pv.extend(pv.Layout.Partition);
  12925. /** @private */
  12926. pv.Layout.Partition.Fill.prototype.buildImplied = function(s) {
  12927. if (pv.Layout.Partition.prototype.buildImplied.call(this, s)) return;
  12928. pv.Layout.Hierarchy.Fill.buildImplied.call(this, s);
  12929. };
  12930. /**
  12931. * Constructs a new, empty arc layout. Layouts are not typically constructed
  12932. * directly; instead, they are added to an existing panel via
  12933. * {@link pv.Mark#add}.
  12934. *
  12935. * @class Implements a layout for arc diagrams. An arc diagram is a network
  12936. * visualization with a one-dimensional layout of nodes, using circular arcs to
  12937. * render links between nodes. For undirected networks, arcs are rendering on a
  12938. * single side; this makes arc diagrams useful as annotations to other
  12939. * two-dimensional network layouts, such as rollup, matrix or table layouts. For
  12940. * directed networks, links in opposite directions can be rendered on opposite
  12941. * sides using <tt>directed(true)</tt>.
  12942. *
  12943. * <p>Arc layouts are particularly sensitive to node ordering; for best results,
  12944. * order the nodes such that related nodes are close to each other. A poor
  12945. * (e.g., random) order may result in large arcs with crossovers that impede
  12946. * visual processing. A future improvement to this layout may include automatic
  12947. * reordering using, e.g., spectral graph layout or simulated annealing.
  12948. *
  12949. * <p>This visualization technique is related to that developed by
  12950. * M. Wattenberg, <a
  12951. * href="http://www.research.ibm.com/visual/papers/arc-diagrams.pdf">"Arc
  12952. * Diagrams: Visualizing Structure in Strings"</a> in <i>IEEE InfoVis</i>, 2002.
  12953. * However, this implementation is limited to simple node-link networks, as
  12954. * opposed to structures with hierarchical self-similarity (such as strings).
  12955. *
  12956. * <p>As with other network layouts, three mark prototypes are provided:<ul>
  12957. *
  12958. * <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Dot}.
  12959. * <li><tt>link</tt> - for rendering links; typically a {@link pv.Line}.
  12960. * <li><tt>label</tt> - for rendering node labels; typically a {@link pv.Label}.
  12961. *
  12962. * </ul>For more details on how this layout is structured and can be customized,
  12963. * see {@link pv.Layout.Network}.
  12964. *
  12965. * @extends pv.Layout.Network
  12966. **/
  12967. pv.Layout.Arc = function() {
  12968. pv.Layout.Network.call(this);
  12969. var interpolate, // cached interpolate
  12970. directed, // cached directed
  12971. reverse, // cached reverse
  12972. buildImplied = this.buildImplied;
  12973. /** @private Cache layout state to optimize properties. */
  12974. this.buildImplied = function(s) {
  12975. buildImplied.call(this, s);
  12976. directed = s.directed;
  12977. interpolate = s.orient == "radial" ? "linear" : "polar";
  12978. reverse = s.orient == "right" || s.orient == "top";
  12979. };
  12980. /* Override link properties to handle directedness and orientation. */
  12981. this.link
  12982. .data(function(p) {
  12983. var s = p.sourceNode, t = p.targetNode;
  12984. return reverse != (directed || (s.breadth < t.breadth)) ? [s, t] : [t, s];
  12985. })
  12986. .interpolate(function() { return interpolate; });
  12987. };
  12988. pv.Layout.Arc.prototype = pv.extend(pv.Layout.Network)
  12989. .property("orient", String)
  12990. .property("directed", Boolean);
  12991. /**
  12992. * Default properties for arc layouts. By default, the orientation is "bottom".
  12993. *
  12994. * @type pv.Layout.Arc
  12995. */
  12996. pv.Layout.Arc.prototype.defaults = new pv.Layout.Arc()
  12997. .extend(pv.Layout.Network.prototype.defaults)
  12998. .orient("bottom");
  12999. /**
  13000. * Specifies an optional sort function. The sort function follows the same
  13001. * comparator contract required by {@link pv.Dom.Node#sort}. Specifying a sort
  13002. * function provides an alternative to sort the nodes as they are specified by
  13003. * the <tt>nodes</tt> property; the main advantage of doing this is that the
  13004. * comparator function can access implicit fields populated by the network
  13005. * layout, such as the <tt>linkDegree</tt>.
  13006. *
  13007. * <p>Note that arc diagrams are particularly sensitive to order. This is
  13008. * referred to as the seriation problem, and many different techniques exist to
  13009. * find good node orders that emphasize clusters, such as spectral layout and
  13010. * simulated annealing.
  13011. *
  13012. * @param {function} f comparator function for nodes.
  13013. * @returns {pv.Layout.Arc} this.
  13014. */
  13015. pv.Layout.Arc.prototype.sort = function(f) {
  13016. this.$sort = f;
  13017. return this;
  13018. };
  13019. /** @private Populates the x, y and angle attributes on the nodes. */
  13020. pv.Layout.Arc.prototype.buildImplied = function(s) {
  13021. if (pv.Layout.Network.prototype.buildImplied.call(this, s)) return;
  13022. var nodes = s.nodes,
  13023. orient = s.orient,
  13024. sort = this.$sort,
  13025. index = pv.range(nodes.length),
  13026. w = s.width,
  13027. h = s.height,
  13028. r = Math.min(w, h) / 2;
  13029. /* Sort the nodes. */
  13030. if (sort) index.sort(function(a, b) { return sort(nodes[a], nodes[b]); });
  13031. /** @private Returns the mid-angle, given the breadth. */
  13032. function midAngle(b) {
  13033. switch (orient) {
  13034. case "top": return -Math.PI / 2;
  13035. case "bottom": return Math.PI / 2;
  13036. case "left": return Math.PI;
  13037. case "right": return 0;
  13038. case "radial": return (b - .25) * 2 * Math.PI;
  13039. }
  13040. }
  13041. /** @private Returns the x-position, given the breadth. */
  13042. function x(b) {
  13043. switch (orient) {
  13044. case "top":
  13045. case "bottom": return b * w;
  13046. case "left": return 0;
  13047. case "right": return w;
  13048. case "radial": return w / 2 + r * Math.cos(midAngle(b));
  13049. }
  13050. }
  13051. /** @private Returns the y-position, given the breadth. */
  13052. function y(b) {
  13053. switch (orient) {
  13054. case "top": return 0;
  13055. case "bottom": return h;
  13056. case "left":
  13057. case "right": return b * h;
  13058. case "radial": return h / 2 + r * Math.sin(midAngle(b));
  13059. }
  13060. }
  13061. /* Populate the x, y and mid-angle attributes. */
  13062. for (var i = 0; i < nodes.length; i++) {
  13063. var n = nodes[index[i]], b = n.breadth = (i + .5) / nodes.length;
  13064. n.x = x(b);
  13065. n.y = y(b);
  13066. n.midAngle = midAngle(b);
  13067. }
  13068. };
  13069. /**
  13070. * The orientation. The default orientation is "left", which means that nodes
  13071. * will be positioned from left-to-right in the order they are specified in the
  13072. * <tt>nodes</tt> property. The following orientations are supported:<ul>
  13073. *
  13074. * <li>left - left-to-right.
  13075. * <li>right - right-to-left.
  13076. * <li>top - top-to-bottom.
  13077. * <li>bottom - bottom-to-top.
  13078. * <li>radial - radially, starting at 12 o'clock and proceeding clockwise.</ul>
  13079. *
  13080. * @type string
  13081. * @name pv.Layout.Arc.prototype.orient
  13082. */
  13083. /**
  13084. * Whether this arc digram is directed (bidirectional); only applies to
  13085. * non-radial orientations. By default, arc digrams are undirected, such that
  13086. * all arcs appear on one side. If the arc digram is directed, then forward
  13087. * links are drawn on the conventional side (the same as as undirected
  13088. * links--right, left, bottom and top for left, right, top and bottom,
  13089. * respectively), while reverse links are drawn on the opposite side.
  13090. *
  13091. * @type boolean
  13092. * @name pv.Layout.Arc.prototype.directed
  13093. */
  13094. /**
  13095. * Constructs a new, empty horizon layout. Layouts are not typically constructed
  13096. * directly; instead, they are added to an existing panel via
  13097. * {@link pv.Mark#add}.
  13098. *
  13099. * @class Implements a horizon layout, which is a variation of a single-series
  13100. * area chart where the area is folded into multiple bands. Color is used to
  13101. * encode band, allowing the size of the chart to be reduced significantly
  13102. * without impeding readability. This layout algorithm is based on the work of
  13103. * J. Heer, N. Kong and M. Agrawala in <a
  13104. * href="http://hci.stanford.edu/publications/2009/heer-horizon-chi09.pdf">"Sizing
  13105. * the Horizon: The Effects of Chart Size and Layering on the Graphical
  13106. * Perception of Time Series Visualizations"</a>, CHI 2009.
  13107. *
  13108. * <p>This layout exports a single <tt>band</tt> mark prototype, which is
  13109. * intended to be used with an area mark. The band mark is contained in a panel
  13110. * which is replicated per band (and for negative/positive bands). For example,
  13111. * to create a simple horizon graph given an array of numbers:
  13112. *
  13113. * <pre>vis.add(pv.Layout.Horizon)
  13114. * .bands(n)
  13115. * .band.add(pv.Area)
  13116. * .data(data)
  13117. * .left(function() this.index * 35)
  13118. * .height(function(d) d * 40);</pre>
  13119. *
  13120. * The layout can be further customized by changing the number of bands, and
  13121. * toggling whether the negative bands are mirrored or offset. (See the
  13122. * above-referenced paper for guidance.)
  13123. *
  13124. * <p>The <tt>fillStyle</tt> of the area can be overridden, though typically it
  13125. * is easier to customize the layout's behavior through the custom
  13126. * <tt>backgroundStyle</tt>, <tt>positiveStyle</tt> and <tt>negativeStyle</tt>
  13127. * properties. By default, the background is white, positive bands are blue, and
  13128. * negative bands are red. For the most accurate presentation, use fully-opaque
  13129. * colors of equal intensity for the negative and positive bands.
  13130. *
  13131. * @extends pv.Layout
  13132. */
  13133. pv.Layout.Horizon = function() {
  13134. pv.Layout.call(this);
  13135. var that = this,
  13136. bands, // cached bands
  13137. mode, // cached mode
  13138. size, // cached height
  13139. fill, // cached background style
  13140. red, // cached negative color (ramp)
  13141. blue, // cached positive color (ramp)
  13142. buildImplied = this.buildImplied;
  13143. /** @private Cache the layout state to optimize properties. */
  13144. this.buildImplied = function(s) {
  13145. buildImplied.call(this, s);
  13146. bands = s.bands;
  13147. mode = s.mode;
  13148. size = Math.round((mode == "color" ? .5 : 1) * s.height);
  13149. fill = s.backgroundStyle;
  13150. red = pv.ramp(fill, s.negativeStyle).domain(0, bands);
  13151. blue = pv.ramp(fill, s.positiveStyle).domain(0, bands);
  13152. };
  13153. var bands = new pv.Panel()
  13154. .data(function() { return pv.range(bands * 2); })
  13155. .overflow("hidden")
  13156. .height(function() { return size; })
  13157. .top(function(i) { return mode == "color" ? (i & 1) * size : 0; })
  13158. .fillStyle(function(i) { return i ? null : fill; });
  13159. /**
  13160. * The band prototype. This prototype is intended to be used with an Area
  13161. * mark to render the horizon bands.
  13162. *
  13163. * @type pv.Mark
  13164. * @name pv.Layout.Horizon.prototype.band
  13165. */
  13166. this.band = new pv.Mark()
  13167. .top(function(d, i) {
  13168. return mode == "mirror" && i & 1
  13169. ? (i + 1 >> 1) * size
  13170. : null;
  13171. })
  13172. .bottom(function(d, i) {
  13173. return mode == "mirror"
  13174. ? (i & 1 ? null : (i + 1 >> 1) * -size)
  13175. : ((i & 1 || -1) * (i + 1 >> 1) * size);
  13176. })
  13177. .fillStyle(function(d, i) {
  13178. return (i & 1 ? red : blue)((i >> 1) + 1);
  13179. });
  13180. this.band.add = function(type) {
  13181. return that.add(pv.Panel).extend(bands).add(type).extend(this);
  13182. };
  13183. };
  13184. pv.Layout.Horizon.prototype = pv.extend(pv.Layout)
  13185. .property("bands", Number)
  13186. .property("mode", String)
  13187. .property("backgroundStyle", pv.color)
  13188. .property("positiveStyle", pv.color)
  13189. .property("negativeStyle", pv.color);
  13190. /**
  13191. * Default properties for horizon layouts. By default, there are two bands, the
  13192. * mode is "offset", the background style is "white", the positive style is
  13193. * blue, negative style is red.
  13194. *
  13195. * @type pv.Layout.Horizon
  13196. */
  13197. pv.Layout.Horizon.prototype.defaults = new pv.Layout.Horizon()
  13198. .extend(pv.Layout.prototype.defaults)
  13199. .bands(2)
  13200. .mode("offset")
  13201. .backgroundStyle("white")
  13202. .positiveStyle("#1f77b4")
  13203. .negativeStyle("#d62728");
  13204. /**
  13205. * The horizon mode: offset, mirror, or color. The default is "offset".
  13206. *
  13207. * @type string
  13208. * @name pv.Layout.Horizon.prototype.mode
  13209. */
  13210. /**
  13211. * The number of bands. Must be at least one. The default value is two.
  13212. *
  13213. * @type number
  13214. * @name pv.Layout.Horizon.prototype.bands
  13215. */
  13216. /**
  13217. * The positive band color; if non-null, the interior of positive bands are
  13218. * filled with the specified color. The default value of this property is blue.
  13219. * For accurate blending, this color should be fully opaque.
  13220. *
  13221. * @type pv.Color
  13222. * @name pv.Layout.Horizon.prototype.positiveStyle
  13223. */
  13224. /**
  13225. * The negative band color; if non-null, the interior of negative bands are
  13226. * filled with the specified color. The default value of this property is red.
  13227. * For accurate blending, this color should be fully opaque.
  13228. *
  13229. * @type pv.Color
  13230. * @name pv.Layout.Horizon.prototype.negativeStyle
  13231. */
  13232. /**
  13233. * The background color. The panel background is filled with the specified
  13234. * color, and the negative and positive bands are filled with an interpolated
  13235. * color between this color and the respective band color. The default value of
  13236. * this property is white. For accurate blending, this color should be fully
  13237. * opaque.
  13238. *
  13239. * @type pv.Color
  13240. * @name pv.Layout.Horizon.prototype.backgroundStyle
  13241. */
  13242. /**
  13243. * Constructs a new, empty rollup network layout. Layouts are not typically
  13244. * constructed directly; instead, they are added to an existing panel via
  13245. * {@link pv.Mark#add}.
  13246. *
  13247. * @class Implements a network visualization using a node-link diagram where
  13248. * nodes are rolled up along two dimensions. This implementation is based on the
  13249. * "PivotGraph" designed by Martin Wattenberg:
  13250. *
  13251. * <blockquote>The method is designed for graphs that are "multivariate", i.e.,
  13252. * where each node is associated with several attributes. Unlike visualizations
  13253. * which emphasize global graph topology, PivotGraph uses a simple grid-based
  13254. * approach to focus on the relationship between node attributes &amp;
  13255. * connections.</blockquote>
  13256. *
  13257. * This layout requires two psuedo-properties to be specified, which assign node
  13258. * positions along the two dimensions {@link #x} and {@link #y}, corresponding
  13259. * to the left and top properties, respectively. Typically, these functions are
  13260. * specified using an {@link pv.Scale.ordinal}. Nodes that share the same
  13261. * position in <i>x</i> and <i>y</i> are "rolled up" into a meta-node, and
  13262. * similarly links are aggregated between meta-nodes. For example, to construct
  13263. * a rollup to analyze links by gender and affiliation, first define two ordinal
  13264. * scales:
  13265. *
  13266. * <pre>var x = pv.Scale.ordinal(nodes, function(d) d.gender).split(0, w),
  13267. * y = pv.Scale.ordinal(nodes, function(d) d.aff).split(0, h);</pre>
  13268. *
  13269. * Next, define the position psuedo-properties:
  13270. *
  13271. * <pre> .x(function(d) x(d.gender))
  13272. * .y(function(d) y(d.aff))</pre>
  13273. *
  13274. * Linear and other quantitative scales can alternatively be used to position
  13275. * the nodes along either dimension. Note, however, that the rollup requires
  13276. * that the positions match exactly, and thus ordinal scales are recommended to
  13277. * avoid precision errors.
  13278. *
  13279. * <p>Note that because this layout provides a visualization of the rolled up
  13280. * graph, the data properties for the mark prototypes (<tt>node</tt>,
  13281. * <tt>link</tt> and <tt>label</tt>) are different from most other network
  13282. * layouts: they reference the rolled-up nodes and links, rather than the nodes
  13283. * and links of the full network. The underlying nodes and links for each
  13284. * rolled-up node and link can be accessed via the <tt>nodes</tt> and
  13285. * <tt>links</tt> attributes, respectively. The aggregated link values for
  13286. * rolled-up links can similarly be accessed via the <tt>linkValue</tt>
  13287. * attribute.
  13288. *
  13289. * <p>For undirected networks, links are duplicated in both directions. For
  13290. * directed networks, use <tt>directed(true)</tt>. The graph is assumed to be
  13291. * undirected by default.
  13292. *
  13293. * @extends pv.Layout.Network
  13294. * @see <a href="http://www.research.ibm.com/visual/papers/pivotgraph.pdf"
  13295. * >"Visual Exploration of Multivariate Graphs"</a> by M. Wattenberg, CHI 2006.
  13296. */
  13297. pv.Layout.Rollup = function() {
  13298. pv.Layout.Network.call(this);
  13299. var that = this,
  13300. nodes, // cached rollup nodes
  13301. links, // cached rollup links
  13302. buildImplied = that.buildImplied;
  13303. /** @private Cache layout state to optimize properties. */
  13304. this.buildImplied = function(s) {
  13305. buildImplied.call(this, s);
  13306. nodes = s.$rollup.nodes;
  13307. links = s.$rollup.links;
  13308. };
  13309. /* Render rollup nodes. */
  13310. this.node
  13311. .data(function() { return nodes; })
  13312. .size(function(d) { return d.nodes.length * 20; });
  13313. /* Render rollup links. */
  13314. this.link
  13315. .interpolate("polar")
  13316. .eccentricity(.8);
  13317. this.link.add = function(type) {
  13318. return that.add(pv.Panel)
  13319. .data(function() { return links; })
  13320. .add(type)
  13321. .extend(this);
  13322. };
  13323. };
  13324. pv.Layout.Rollup.prototype = pv.extend(pv.Layout.Network)
  13325. .property("directed", Boolean);
  13326. /**
  13327. * Whether the underlying network is directed. By default, the graph is assumed
  13328. * to be undirected, and links are rendered in both directions. If the network
  13329. * is directed, then forward links are drawn above the diagonal, while reverse
  13330. * links are drawn below.
  13331. *
  13332. * @type boolean
  13333. * @name pv.Layout.Rollup.prototype.directed
  13334. */
  13335. /**
  13336. * Specifies the <i>x</i>-position function used to rollup nodes. The rolled up
  13337. * nodes are positioned horizontally using the return values from the given
  13338. * function. Typically the function is specified as an ordinal scale. For
  13339. * single-dimension rollups, a constant value can be specified.
  13340. *
  13341. * @param {function} f the <i>x</i>-position function.
  13342. * @returns {pv.Layout.Rollup} this.
  13343. * @see pv.Scale.ordinal
  13344. */
  13345. pv.Layout.Rollup.prototype.x = function(f) {
  13346. this.$x = pv.functor(f);
  13347. return this;
  13348. };
  13349. /**
  13350. * Specifies the <i>y</i>-position function used to rollup nodes. The rolled up
  13351. * nodes are positioned vertically using the return values from the given
  13352. * function. Typically the function is specified as an ordinal scale. For
  13353. * single-dimension rollups, a constant value can be specified.
  13354. *
  13355. * @param {function} f the <i>y</i>-position function.
  13356. * @returns {pv.Layout.Rollup} this.
  13357. * @see pv.Scale.ordinal
  13358. */
  13359. pv.Layout.Rollup.prototype.y = function(f) {
  13360. this.$y = pv.functor(f);
  13361. return this;
  13362. };
  13363. /** @private */
  13364. pv.Layout.Rollup.prototype.buildImplied = function(s) {
  13365. if (pv.Layout.Network.prototype.buildImplied.call(this, s)) return;
  13366. var nodes = s.nodes,
  13367. links = s.links,
  13368. directed = s.directed,
  13369. n = nodes.length,
  13370. x = [],
  13371. y = [],
  13372. rnindex = 0,
  13373. rnodes = {},
  13374. rlinks = {};
  13375. /** @private */
  13376. function id(i) {
  13377. return x[i] + "," + y[i];
  13378. }
  13379. /* Iterate over the data, evaluating the x and y functions. */
  13380. var stack = pv.Mark.stack, o = {parent: this};
  13381. stack.unshift(null);
  13382. for (var i = 0; i < n; i++) {
  13383. o.index = i;
  13384. stack[0] = nodes[i];
  13385. x[i] = this.$x.apply(o, stack);
  13386. y[i] = this.$y.apply(o, stack);
  13387. }
  13388. stack.shift();
  13389. /* Compute rollup nodes. */
  13390. for (var i = 0; i < nodes.length; i++) {
  13391. var nodeId = id(i),
  13392. rn = rnodes[nodeId];
  13393. if (!rn) {
  13394. rn = rnodes[nodeId] = pv.extend(nodes[i]);
  13395. rn.index = rnindex++;
  13396. rn.x = x[i];
  13397. rn.y = y[i];
  13398. rn.nodes = [];
  13399. }
  13400. rn.nodes.push(nodes[i]);
  13401. }
  13402. /* Compute rollup links. */
  13403. for (var i = 0; i < links.length; i++) {
  13404. var source = links[i].sourceNode,
  13405. target = links[i].targetNode,
  13406. rsource = rnodes[id(source.index)],
  13407. rtarget = rnodes[id(target.index)],
  13408. reverse = !directed && rsource.index > rtarget.index,
  13409. linkId = reverse
  13410. ? rtarget.index + "," + rsource.index
  13411. : rsource.index + "," + rtarget.index,
  13412. rl = rlinks[linkId];
  13413. if (!rl) {
  13414. rl = rlinks[linkId] = {
  13415. sourceNode: rsource,
  13416. targetNode: rtarget,
  13417. linkValue: 0,
  13418. links: []
  13419. };
  13420. }
  13421. rl.links.push(links[i]);
  13422. rl.linkValue += links[i].linkValue;
  13423. }
  13424. /* Export the rolled up nodes and links to the scene. */
  13425. s.$rollup = {
  13426. nodes: pv.values(rnodes),
  13427. links: pv.values(rlinks)
  13428. };
  13429. };
  13430. /**
  13431. * Constructs a new, empty matrix network layout. Layouts are not typically
  13432. * constructed directly; instead, they are added to an existing panel via
  13433. * {@link pv.Mark#add}.
  13434. *
  13435. * @class Implements a network visualization using a matrix view. This is, in
  13436. * effect, a visualization of the graph's <i>adjacency matrix</i>: the cell at
  13437. * row <i>i</i>, column <i>j</i>, corresponds to the link from node <i>i</i> to
  13438. * node <i>j</i>. The fill color of each cell is binary by default, and
  13439. * corresponds to whether a link exists between the two nodes. If the underlying
  13440. * graph has links with variable values, the <tt>fillStyle</tt> property can be
  13441. * substited to use an appropriate color function, such as {@link pv.ramp}.
  13442. *
  13443. * <p>For undirected networks, the matrix is symmetric around the diagonal. For
  13444. * directed networks, links in opposite directions can be rendered on opposite
  13445. * sides of the diagonal using <tt>directed(true)</tt>. The graph is assumed to
  13446. * be undirected by default.
  13447. *
  13448. * <p>The mark prototypes for this network layout are slightly different than
  13449. * other implementations:<ul>
  13450. *
  13451. * <li><tt>node</tt> - unsupported; undefined. No mark is needed to visualize
  13452. * nodes directly, as the nodes are implicit in the location (rows and columns)
  13453. * of the links.
  13454. *
  13455. * <p><li><tt>link</tt> - for rendering links; typically a {@link pv.Bar}. The
  13456. * link mark is added directly to the layout, with the data property defined as
  13457. * all possible pairs of nodes. Each pair is represented as a
  13458. * {@link pv.Network.Layout.Link}, though the <tt>linkValue</tt> attribute may
  13459. * be 0 if no link exists in the graph.
  13460. *
  13461. * <p><li><tt>label</tt> - for rendering node labels; typically a
  13462. * {@link pv.Label}. The label mark is added directly to the layout, with the
  13463. * data property defined via the layout's <tt>nodes</tt> property; note,
  13464. * however, that the nodes are duplicated so as to provide a label across the
  13465. * top and down the side. Properties such as <tt>strokeStyle</tt> and
  13466. * <tt>fillStyle</tt> can be overridden to compute properties from node data
  13467. * dynamically.
  13468. *
  13469. * </ul>For more details on how to use this layout, see
  13470. * {@link pv.Layout.Network}.
  13471. *
  13472. * @extends pv.Layout.Network
  13473. */
  13474. pv.Layout.Matrix = function() {
  13475. pv.Layout.Network.call(this);
  13476. var that = this,
  13477. n, // cached matrix size
  13478. dx, // cached cell width
  13479. dy, // cached cell height
  13480. labels, // cached labels (array of strings)
  13481. pairs, // cached pairs (array of links)
  13482. buildImplied = that.buildImplied;
  13483. /** @private Cache layout state to optimize properties. */
  13484. this.buildImplied = function(s) {
  13485. buildImplied.call(this, s);
  13486. n = s.nodes.length;
  13487. dx = s.width / n;
  13488. dy = s.height / n;
  13489. labels = s.$matrix.labels;
  13490. pairs = s.$matrix.pairs;
  13491. };
  13492. /* Links are all pairs of nodes. */
  13493. this.link
  13494. .data(function() { return pairs; })
  13495. .left(function() { return dx * (this.index % n); })
  13496. .top(function() { return dy * Math.floor(this.index / n); })
  13497. .width(function() { return dx; })
  13498. .height(function() { return dy; })
  13499. .lineWidth(1.5)
  13500. .strokeStyle("#fff")
  13501. .fillStyle(function(l) { return l.linkValue ? "#555" : "#eee"; })
  13502. .parent = this;
  13503. /* No special add for links! */
  13504. delete this.link.add;
  13505. /* Labels are duplicated for top & left. */
  13506. this.label
  13507. .data(function() { return labels; })
  13508. .left(function() { return this.index & 1 ? dx * ((this.index >> 1) + .5) : null; })
  13509. .top(function() { return this.index & 1 ? null : dy * ((this.index >> 1) + .5); })
  13510. .textMargin(4)
  13511. .textAlign(function() { return this.index & 1 ? "left" : "right"; })
  13512. .textAngle(function() { return this.index & 1 ? -Math.PI / 2 : 0; });
  13513. /* The node mark is unused. */
  13514. delete this.node;
  13515. };
  13516. pv.Layout.Matrix.prototype = pv.extend(pv.Layout.Network)
  13517. .property("directed", Boolean);
  13518. /**
  13519. * Whether this matrix visualization is directed (bidirectional). By default,
  13520. * the graph is assumed to be undirected, such that the visualization is
  13521. * symmetric across the matrix diagonal. If the network is directed, then
  13522. * forward links are drawn above the diagonal, while reverse links are drawn
  13523. * below.
  13524. *
  13525. * @type boolean
  13526. * @name pv.Layout.Matrix.prototype.directed
  13527. */
  13528. /**
  13529. * Specifies an optional sort function. The sort function follows the same
  13530. * comparator contract required by {@link pv.Dom.Node#sort}. Specifying a sort
  13531. * function provides an alternative to sort the nodes as they are specified by
  13532. * the <tt>nodes</tt> property; the main advantage of doing this is that the
  13533. * comparator function can access implicit fields populated by the network
  13534. * layout, such as the <tt>linkDegree</tt>.
  13535. *
  13536. * <p>Note that matrix visualizations are particularly sensitive to order. This
  13537. * is referred to as the seriation problem, and many different techniques exist
  13538. * to find good node orders that emphasize clusters, such as spectral layout and
  13539. * simulated annealing.
  13540. *
  13541. * @param {function} f comparator function for nodes.
  13542. * @returns {pv.Layout.Matrix} this.
  13543. */
  13544. pv.Layout.Matrix.prototype.sort = function(f) {
  13545. this.$sort = f;
  13546. return this;
  13547. };
  13548. /** @private */
  13549. pv.Layout.Matrix.prototype.buildImplied = function(s) {
  13550. if (pv.Layout.Network.prototype.buildImplied.call(this, s)) return;
  13551. var nodes = s.nodes,
  13552. links = s.links,
  13553. sort = this.$sort,
  13554. n = nodes.length,
  13555. index = pv.range(n),
  13556. labels = [],
  13557. pairs = [],
  13558. map = {};
  13559. s.$matrix = {labels: labels, pairs: pairs};
  13560. /* Sort the nodes. */
  13561. if (sort) index.sort(function(a, b) { return sort(nodes[a], nodes[b]); });
  13562. /* Create pairs. */
  13563. for (var i = 0; i < n; i++) {
  13564. for (var j = 0; j < n; j++) {
  13565. var a = index[i],
  13566. b = index[j],
  13567. p = {
  13568. row: i,
  13569. col: j,
  13570. sourceNode: nodes[a],
  13571. targetNode: nodes[b],
  13572. linkValue: 0
  13573. };
  13574. pairs.push(map[a + "." + b] = p);
  13575. }
  13576. }
  13577. /* Create labels. */
  13578. for (var i = 0; i < n; i++) {
  13579. var a = index[i];
  13580. labels.push(nodes[a], nodes[a]);
  13581. }
  13582. /* Accumulate link values. */
  13583. for (var i = 0; i < links.length; i++) {
  13584. var l = links[i],
  13585. source = l.sourceNode.index,
  13586. target = l.targetNode.index,
  13587. value = l.linkValue;
  13588. map[source + "." + target].linkValue += value;
  13589. if (!s.directed) map[target + "." + source].linkValue += value;
  13590. }
  13591. };
  13592. // ranges (bad, satisfactory, good)
  13593. // measures (actual, forecast)
  13594. // markers (previous, goal)
  13595. /*
  13596. * Chart design based on the recommendations of Stephen Few. Implementation
  13597. * based on the work of Clint Ivy, Jamie Love, and Jason Davies.
  13598. * http://projects.instantcognition.com/protovis/bulletchart/
  13599. */
  13600. pv.Layout.Bullet = function() {
  13601. pv.Layout.call(this);
  13602. var that = this,
  13603. buildImplied = that.buildImplied,
  13604. scale = that.x = pv.Scale.linear(),
  13605. orient,
  13606. horizontal,
  13607. rangeColor,
  13608. measureColor,
  13609. x;
  13610. /** @private Cache layout state to optimize properties. */
  13611. this.buildImplied = function(s) {
  13612. buildImplied.call(this, x = s);
  13613. orient = s.orient;
  13614. horizontal = /^left|right$/.test(orient);
  13615. rangeColor = pv.ramp("#bbb", "#eee")
  13616. .domain(0, Math.max(1, x.ranges.length - 1));
  13617. measureColor = pv.ramp("steelblue", "lightsteelblue")
  13618. .domain(0, Math.max(1, x.measures.length - 1));
  13619. };
  13620. (this.range = new pv.Mark())
  13621. .data(function() { return x.ranges; })
  13622. .reverse(true)
  13623. .left(function() { return orient == "left" ? 0 : null; })
  13624. .top(function() { return orient == "top" ? 0 : null; })
  13625. .right(function() { return orient == "right" ? 0 : null; })
  13626. .bottom(function() { return orient == "bottom" ? 0 : null; })
  13627. .width(function(d) { return horizontal ? scale(d) : null; })
  13628. .height(function(d) { return horizontal ? null : scale(d); })
  13629. .fillStyle(function() { return rangeColor(this.index); })
  13630. .antialias(false)
  13631. .parent = that;
  13632. (this.measure = new pv.Mark())
  13633. .extend(this.range)
  13634. .data(function() { return x.measures; })
  13635. .left(function() { return orient == "left" ? 0 : horizontal ? null : this.parent.width() / 3.25; })
  13636. .top(function() { return orient == "top" ? 0 : horizontal ? this.parent.height() / 3.25 : null; })
  13637. .right(function() { return orient == "right" ? 0 : horizontal ? null : this.parent.width() / 3.25; })
  13638. .bottom(function() { return orient == "bottom" ? 0 : horizontal ? this.parent.height() / 3.25 : null; })
  13639. .fillStyle(function() { return measureColor(this.index); })
  13640. .parent = that;
  13641. (this.marker = new pv.Mark())
  13642. .data(function() { return x.markers; })
  13643. .left(function(d) { return orient == "left" ? scale(d) : horizontal ? null : this.parent.width() / 2; })
  13644. .top(function(d) { return orient == "top" ? scale(d) : horizontal ? this.parent.height() / 2 : null; })
  13645. .right(function(d) { return orient == "right" ? scale(d) : null; })
  13646. .bottom(function(d) { return orient == "bottom" ? scale(d) : null; })
  13647. .strokeStyle("black")
  13648. .shape("bar")
  13649. .angle(function() { return horizontal ? 0 : Math.PI / 2; })
  13650. .parent = that;
  13651. (this.tick = new pv.Mark())
  13652. .data(function() { return scale.ticks(7); })
  13653. .left(function(d) { return orient == "left" ? scale(d) : null; })
  13654. .top(function(d) { return orient == "top" ? scale(d) : null; })
  13655. .right(function(d) { return orient == "right" ? scale(d) : horizontal ? null : -6; })
  13656. .bottom(function(d) { return orient == "bottom" ? scale(d) : horizontal ? -8 : null; })
  13657. .height(function() { return horizontal ? 6 : null; })
  13658. .width(function() { return horizontal ? null : 6; })
  13659. .parent = that;
  13660. };
  13661. pv.Layout.Bullet.prototype = pv.extend(pv.Layout)
  13662. .property("orient", String) // left, right, top, bottom
  13663. .property("ranges")
  13664. .property("markers")
  13665. .property("measures")
  13666. .property("maximum", Number);
  13667. pv.Layout.Bullet.prototype.defaults = new pv.Layout.Bullet()
  13668. .extend(pv.Layout.prototype.defaults)
  13669. .orient("left")
  13670. .ranges([])
  13671. .markers([])
  13672. .measures([]);
  13673. /** @private */
  13674. pv.Layout.Bullet.prototype.buildImplied = function(s) {
  13675. pv.Layout.prototype.buildImplied.call(this, s);
  13676. var size = this.parent[/^left|right$/.test(s.orient) ? "width" : "height"]();
  13677. s.maximum = s.maximum || pv.max([].concat(s.ranges, s.markers, s.measures));
  13678. this.x.domain(0, s.maximum).range(0, size);
  13679. };
  13680. /**
  13681. * Abstract; see an implementing class for details.
  13682. *
  13683. * @class Represents a reusable interaction; applies an interactive behavior to
  13684. * a given mark. Behaviors are themselves functions designed to be used as event
  13685. * handlers. For example, to add pan and zoom support to any panel, say:
  13686. *
  13687. * <pre> .event("mousedown", pv.Behavior.pan())
  13688. * .event("mousewheel", pv.Behavior.zoom())</pre>
  13689. *
  13690. * The behavior should be registered on the event that triggers the start of the
  13691. * behavior. Typically, the behavior will take care of registering for any
  13692. * additional events that are necessary. For example, dragging starts on
  13693. * mousedown, while the drag behavior automatically listens for mousemove and
  13694. * mouseup events on the window. By listening to the window, the behavior can
  13695. * continue to receive mouse events even if the mouse briefly leaves the mark
  13696. * being dragged, or even the root panel.
  13697. *
  13698. * <p>Each behavior implementation has specific requirements as to which events
  13699. * it supports, and how it should be used. For example, the drag behavior
  13700. * requires that the data associated with the mark be an object with <tt>x</tt>
  13701. * and <tt>y</tt> attributes, such as a {@link pv.Vector}, storing the mark's
  13702. * position. See an implementing class for details.
  13703. *
  13704. * @see pv.Behavior.drag
  13705. * @see pv.Behavior.pan
  13706. * @see pv.Behavior.point
  13707. * @see pv.Behavior.select
  13708. * @see pv.Behavior.zoom
  13709. * @extends function
  13710. */
  13711. pv.Behavior = {};
  13712. /**
  13713. * Returns a new drag behavior to be registered on mousedown events.
  13714. *
  13715. * @class Implements interactive dragging starting with mousedown events.
  13716. * Register this behavior on marks that should be draggable by the user, such as
  13717. * the selected region for brushing and linking. This behavior can be used in
  13718. * tandom with {@link pv.Behavior.select} to allow the selected region to be
  13719. * dragged interactively.
  13720. *
  13721. * <p>After the initial mousedown event is triggered, this behavior listens for
  13722. * mousemove and mouseup events on the window. This allows dragging to continue
  13723. * even if the mouse temporarily leaves the mark that is being dragged, or even
  13724. * the root panel.
  13725. *
  13726. * <p>This behavior requires that the data associated with the mark being
  13727. * dragged have <tt>x</tt> and <tt>y</tt> attributes that correspond to the
  13728. * mark's location in pixels. The mark's positional properties are not set
  13729. * directly by this behavior; instead, the positional properties should be
  13730. * defined as:
  13731. *
  13732. * <pre> .left(function(d) d.x)
  13733. * .top(function(d) d.y)</pre>
  13734. *
  13735. * Thus, the behavior does not move the mark directly, but instead updates the
  13736. * mark position by updating the underlying data. Note that if the positional
  13737. * properties are defined with bottom and right (rather than top and left), the
  13738. * drag behavior will be inverted, which will confuse users!
  13739. *
  13740. * <p>The drag behavior is bounded by the parent panel; the <tt>x</tt> and
  13741. * <tt>y</tt> attributes are clamped such that the mark being dragged does not
  13742. * extend outside the enclosing panel's bounds. To facilitate this, the drag
  13743. * behavior also queries for <tt>dx</tt> and <tt>dy</tt> attributes on the
  13744. * underlying data, to determine the dimensions of the bar being dragged. For
  13745. * non-rectangular marks, the drag behavior simply treats the mark as a point,
  13746. * which means that only the mark's center is bounded.
  13747. *
  13748. * <p>The mark being dragged is automatically re-rendered for each mouse event
  13749. * as part of the drag operation. In addition, a <tt>fix</tt> attribute is
  13750. * populated on the mark, which allows visual feedback for dragging. For
  13751. * example, to change the mark fill color while dragging:
  13752. *
  13753. * <pre> .fillStyle(function(d) d.fix ? "#ff7f0e" : "#aec7e8")</pre>
  13754. *
  13755. * In some cases, such as with network layouts, dragging the mark may cause
  13756. * related marks to change, in which case additional marks may also need to be
  13757. * rendered. This can be accomplished by listening for the drag
  13758. * psuedo-events:<ul>
  13759. *
  13760. * <li>dragstart (on mousedown)
  13761. * <li>drag (on mousemove)
  13762. * <li>dragend (on mouseup)
  13763. *
  13764. * </ul>For example, to render the parent panel while dragging, thus
  13765. * re-rendering all sibling marks:
  13766. *
  13767. * <pre> .event("mousedown", pv.Behavior.drag())
  13768. * .event("drag", function() this.parent)</pre>
  13769. *
  13770. * This behavior may be enhanced in the future to allow more flexible
  13771. * configuration of drag behavior.
  13772. *
  13773. * @extends pv.Behavior
  13774. * @see pv.Behavior
  13775. * @see pv.Behavior.select
  13776. * @see pv.Layout.force
  13777. */
  13778. pv.Behavior.drag = function() {
  13779. var scene, // scene context
  13780. index, // scene context
  13781. p, // particle being dragged
  13782. v1, // initial mouse-particle offset
  13783. max;
  13784. /** @private */
  13785. function mousedown(d) {
  13786. index = this.index;
  13787. scene = this.scene;
  13788. var m = this.mouse();
  13789. v1 = ((p = d).fix = pv.vector(d.x, d.y)).minus(m);
  13790. max = {
  13791. x: this.parent.width() - (d.dx || 0),
  13792. y: this.parent.height() - (d.dy || 0)
  13793. };
  13794. scene.mark.context(scene, index, function() { this.render(); });
  13795. pv.Mark.dispatch("dragstart", scene, index);
  13796. }
  13797. /** @private */
  13798. function mousemove() {
  13799. if (!scene) return;
  13800. scene.mark.context(scene, index, function() {
  13801. var m = this.mouse();
  13802. p.x = p.fix.x = Math.max(0, Math.min(v1.x + m.x, max.x));
  13803. p.y = p.fix.y = Math.max(0, Math.min(v1.y + m.y, max.y));
  13804. this.render();
  13805. });
  13806. pv.Mark.dispatch("drag", scene, index);
  13807. }
  13808. /** @private */
  13809. function mouseup() {
  13810. if (!scene) return;
  13811. p.fix = null;
  13812. scene.mark.context(scene, index, function() { this.render(); });
  13813. pv.Mark.dispatch("dragend", scene, index);
  13814. scene = null;
  13815. }
  13816. pv.listen(window.document.documentElement, "mousemove", mousemove);
  13817. pv.listen(window.document.documentElement, "mouseup", mouseup);
  13818. return mousedown;
  13819. };
  13820. /**
  13821. * Returns a new point behavior to be registered on mousemove events.
  13822. *
  13823. * @class Implements interactive fuzzy pointing, identifying marks that are in
  13824. * close proximity to the mouse cursor. This behavior is an alternative to the
  13825. * native mouseover and mouseout events, improving usability. Rather than
  13826. * requiring the user to mouseover a mark exactly, the mouse simply needs to
  13827. * move near the given mark and a "point" event is triggered. In addition, if
  13828. * multiple marks overlap, the point behavior can be used to identify the mark
  13829. * instance closest to the cursor, as opposed to the one that is rendered on
  13830. * top.
  13831. *
  13832. * <p>The point behavior can also identify the closest mark instance for marks
  13833. * that produce a continuous graphic primitive. The point behavior can thus be
  13834. * used to provide details-on-demand for both discrete marks (such as dots and
  13835. * bars), as well as continuous marks (such as lines and areas).
  13836. *
  13837. * <p>This behavior is implemented by finding the closest mark instance to the
  13838. * mouse cursor on every mousemove event. If this closest mark is within the
  13839. * given radius threshold, which defaults to 30 pixels, a "point" psuedo-event
  13840. * is dispatched to the given mark instance. If any mark were previously
  13841. * pointed, it would receive a corresponding "unpoint" event. These two
  13842. * psuedo-event types correspond to the native "mouseover" and "mouseout"
  13843. * events, respectively. To increase the radius at which the point behavior can
  13844. * be applied, specify an appropriate threshold to the constructor, up to
  13845. * <tt>Infinity</tt>.
  13846. *
  13847. * <p>By default, the standard Cartesian distance is computed. However, with
  13848. * some visualizations it is desirable to consider only a single dimension, such
  13849. * as the <i>x</i>-dimension for an independent variable. In this case, the
  13850. * collapse parameter can be set to collapse the <i>y</i> dimension:
  13851. *
  13852. * <pre> .event("mousemove", pv.Behavior.point(Infinity).collapse("y"))</pre>
  13853. *
  13854. * <p>This behavior only listens to mousemove events on the assigned panel,
  13855. * which is typically the root panel. The behavior will search recursively for
  13856. * descendant marks to point. If the mouse leaves the assigned panel, the
  13857. * behavior no longer receives mousemove events; an unpoint psuedo-event is
  13858. * automatically dispatched to unpoint any pointed mark. Marks may be re-pointed
  13859. * when the mouse reenters the panel.
  13860. *
  13861. * <p>Panels have transparent fill styles by default; this means that panels may
  13862. * not receive the initial mousemove event to start pointing. To fix this
  13863. * problem, either given the panel a visible fill style (such as "white"), or
  13864. * set the <tt>events</tt> property to "all" such that the panel receives events
  13865. * despite its transparent fill.
  13866. *
  13867. * <p>Note: this behavior does not currently wedge marks.
  13868. *
  13869. * @extends pv.Behavior
  13870. *
  13871. * @param {number} [r] the fuzzy radius threshold in pixels
  13872. * @see <a href="http://www.tovigrossman.com/papers/chi2005bubblecursor.pdf"
  13873. * >"The Bubble Cursor: Enhancing Target Acquisition by Dynamic Resizing of the
  13874. * Cursor's Activation Area"</a> by T. Grossman &amp; R. Balakrishnan, CHI 2005.
  13875. */
  13876. pv.Behavior.point = function(r) {
  13877. var unpoint, // the current pointer target
  13878. collapse = null, // dimensions to collapse
  13879. kx = 1, // x-dimension cost scale
  13880. ky = 1, // y-dimension cost scale
  13881. r2 = arguments.length ? r * r : 900; // fuzzy radius
  13882. /** @private Search for the mark closest to the mouse. */
  13883. function search(scene, index) {
  13884. var s = scene[index],
  13885. point = {cost: Infinity};
  13886. for (var i = 0, n = s.visible && s.children.length; i < n; i++) {
  13887. var child = s.children[i], mark = child.mark, p;
  13888. if (mark.type == "panel") {
  13889. mark.scene = child;
  13890. for (var j = 0, m = child.length; j < m; j++) {
  13891. mark.index = j;
  13892. p = search(child, j);
  13893. if (p.cost < point.cost) point = p;
  13894. }
  13895. delete mark.scene;
  13896. delete mark.index;
  13897. } else if (mark.$handlers.point) {
  13898. var v = mark.mouse();
  13899. for (var j = 0, m = child.length; j < m; j++) {
  13900. var c = child[j],
  13901. dx = v.x - c.left - (c.width || 0) / 2,
  13902. dy = v.y - c.top - (c.height || 0) / 2,
  13903. dd = kx * dx * dx + ky * dy * dy;
  13904. if (dd < point.cost) {
  13905. point.distance = dx * dx + dy * dy;
  13906. point.cost = dd;
  13907. point.scene = child;
  13908. point.index = j;
  13909. }
  13910. }
  13911. }
  13912. }
  13913. return point;
  13914. }
  13915. /** @private */
  13916. function mousemove() {
  13917. /* If the closest mark is far away, clear the current target. */
  13918. var point = search(this.scene, this.index);
  13919. if ((point.cost == Infinity) || (point.distance > r2)) point = null;
  13920. /* Unpoint the old target, if it's not the new target. */
  13921. if (unpoint) {
  13922. if (point
  13923. && (unpoint.scene == point.scene)
  13924. && (unpoint.index == point.index)) return;
  13925. pv.Mark.dispatch("unpoint", unpoint.scene, unpoint.index);
  13926. }
  13927. /* Point the new target, if there is one. */
  13928. if (unpoint = point) {
  13929. pv.Mark.dispatch("point", point.scene, point.index);
  13930. /* Unpoint when the mouse leaves the root panel. */
  13931. pv.listen(this.root.canvas(), "mouseout", mouseout);
  13932. }
  13933. }
  13934. /** @private */
  13935. function mouseout(e) {
  13936. if (unpoint && !pv.ancestor(this, e.relatedTarget)) {
  13937. pv.Mark.dispatch("unpoint", unpoint.scene, unpoint.index);
  13938. unpoint = null;
  13939. }
  13940. }
  13941. /**
  13942. * Sets or gets the collapse parameter. By default, the standard Cartesian
  13943. * distance is computed. However, with some visualizations it is desirable to
  13944. * consider only a single dimension, such as the <i>x</i>-dimension for an
  13945. * independent variable. In this case, the collapse parameter can be set to
  13946. * collapse the <i>y</i> dimension:
  13947. *
  13948. * <pre> .event("mousemove", pv.Behavior.point(Infinity).collapse("y"))</pre>
  13949. *
  13950. * @function
  13951. * @returns {pv.Behavior.point} this, or the current collapse parameter.
  13952. * @name pv.Behavior.point.prototype.collapse
  13953. * @param {string} [x] the new collapse parameter
  13954. */
  13955. mousemove.collapse = function(x) {
  13956. if (arguments.length) {
  13957. collapse = String(x);
  13958. switch (collapse) {
  13959. case "y": kx = 1; ky = 0; break;
  13960. case "x": kx = 0; ky = 1; break;
  13961. default: kx = 1; ky = 1; break;
  13962. }
  13963. return mousemove;
  13964. }
  13965. return collapse;
  13966. };
  13967. return mousemove;
  13968. };
  13969. /**
  13970. * Returns a new select behavior to be registered on mousedown events.
  13971. *
  13972. * @class Implements interactive selecting starting with mousedown events.
  13973. * Register this behavior on panels that should be selectable by the user, such
  13974. * for brushing and linking. This behavior can be used in tandom with
  13975. * {@link pv.Behavior.drag} to allow the selected region to be dragged
  13976. * interactively.
  13977. *
  13978. * <p>After the initial mousedown event is triggered, this behavior listens for
  13979. * mousemove and mouseup events on the window. This allows selecting to continue
  13980. * even if the mouse temporarily leaves the assigned panel, or even the root
  13981. * panel.
  13982. *
  13983. * <p>This behavior requires that the data associated with the mark being
  13984. * dragged have <tt>x</tt>, <tt>y</tt>, <tt>dx</tt> and <tt>dy</tt> attributes
  13985. * that correspond to the mark's location and dimensions in pixels. The mark's
  13986. * positional properties are not set directly by this behavior; instead, the
  13987. * positional properties should be defined as:
  13988. *
  13989. * <pre> .left(function(d) d.x)
  13990. * .top(function(d) d.y)
  13991. * .width(function(d) d.dx)
  13992. * .height(function(d) d.dy)</pre>
  13993. *
  13994. * Thus, the behavior does not resize the mark directly, but instead updates the
  13995. * selection by updating the assigned panel's underlying data. Note that if the
  13996. * positional properties are defined with bottom and right (rather than top and
  13997. * left), the drag behavior will be inverted, which will confuse users!
  13998. *
  13999. * <p>The select behavior is bounded by the assigned panel; the positional
  14000. * attributes are clamped such that the selection does not extend outside the
  14001. * panel's bounds.
  14002. *
  14003. * <p>The panel being selected is automatically re-rendered for each mouse event
  14004. * as part of the drag operation. This behavior may be enhanced in the future to
  14005. * allow more flexible configuration of select behavior. In some cases, such as
  14006. * with parallel coordinates, making a selection may cause related marks to
  14007. * change, in which case additional marks may also need to be rendered. This can
  14008. * be accomplished by listening for the select psuedo-events:<ul>
  14009. *
  14010. * <li>selectstart (on mousedown)
  14011. * <li>select (on mousemove)
  14012. * <li>selectend (on mouseup)
  14013. *
  14014. * </ul>For example, to render the parent panel while selecting, thus
  14015. * re-rendering all sibling marks:
  14016. *
  14017. * <pre> .event("mousedown", pv.Behavior.drag())
  14018. * .event("select", function() this.parent)</pre>
  14019. *
  14020. * This behavior may be enhanced in the future to allow more flexible
  14021. * configuration of the selection behavior.
  14022. *
  14023. * @extends pv.Behavior
  14024. * @see pv.Behavior.drag
  14025. */
  14026. pv.Behavior.select = function() {
  14027. var scene, // scene context
  14028. index, // scene context
  14029. r, // region being selected
  14030. m1; // initial mouse position
  14031. /** @private */
  14032. function mousedown(d) {
  14033. index = this.index;
  14034. scene = this.scene;
  14035. m1 = this.mouse();
  14036. r = d;
  14037. r.x = m1.x;
  14038. r.y = m1.y;
  14039. r.dx = r.dy = 0;
  14040. pv.Mark.dispatch("selectstart", scene, index);
  14041. }
  14042. /** @private */
  14043. function mousemove() {
  14044. if (!scene) return;
  14045. scene.mark.context(scene, index, function() {
  14046. var m2 = this.mouse();
  14047. r.x = Math.max(0, Math.min(m1.x, m2.x));
  14048. r.y = Math.max(0, Math.min(m1.y, m2.y));
  14049. r.dx = Math.min(this.width(), Math.max(m2.x, m1.x)) - r.x;
  14050. r.dy = Math.min(this.height(), Math.max(m2.y, m1.y)) - r.y;
  14051. this.render();
  14052. });
  14053. pv.Mark.dispatch("select", scene, index);
  14054. }
  14055. /** @private */
  14056. function mouseup() {
  14057. if (!scene) return;
  14058. pv.Mark.dispatch("selectend", scene, index);
  14059. scene = null;
  14060. }
  14061. pv.listen(window.document.documentElement, "mousemove", mousemove);
  14062. pv.listen(window.document.documentElement, "mouseup", mouseup);
  14063. return mousedown;
  14064. };
  14065. /**
  14066. * Returns a new resize behavior to be registered on mousedown events.
  14067. *
  14068. * @class Implements interactive resizing of a selection starting with mousedown
  14069. * events. Register this behavior on selection handles that should be resizeable
  14070. * by the user, such for brushing and linking. This behavior can be used in
  14071. * tandom with {@link pv.Behavior.select} and {@link pv.Behavior.drag} to allow
  14072. * the selected region to be selected and dragged interactively.
  14073. *
  14074. * <p>After the initial mousedown event is triggered, this behavior listens for
  14075. * mousemove and mouseup events on the window. This allows resizing to continue
  14076. * even if the mouse temporarily leaves the assigned panel, or even the root
  14077. * panel.
  14078. *
  14079. * <p>This behavior requires that the data associated with the mark being
  14080. * resized have <tt>x</tt>, <tt>y</tt>, <tt>dx</tt> and <tt>dy</tt> attributes
  14081. * that correspond to the mark's location and dimensions in pixels. The mark's
  14082. * positional properties are not set directly by this behavior; instead, the
  14083. * positional properties should be defined as:
  14084. *
  14085. * <pre> .left(function(d) d.x)
  14086. * .top(function(d) d.y)
  14087. * .width(function(d) d.dx)
  14088. * .height(function(d) d.dy)</pre>
  14089. *
  14090. * Thus, the behavior does not resize the mark directly, but instead updates the
  14091. * size by updating the assigned panel's underlying data. Note that if the
  14092. * positional properties are defined with bottom and right (rather than top and
  14093. * left), the resize behavior will be inverted, which will confuse users!
  14094. *
  14095. * <p>The resize behavior is bounded by the assigned mark's enclosing panel; the
  14096. * positional attributes are clamped such that the selection does not extend
  14097. * outside the panel's bounds.
  14098. *
  14099. * <p>The mark being resized is automatically re-rendered for each mouse event
  14100. * as part of the resize operation. This behavior may be enhanced in the future
  14101. * to allow more flexible configuration. In some cases, such as with parallel
  14102. * coordinates, resizing the selection may cause related marks to change, in
  14103. * which case additional marks may also need to be rendered. This can be
  14104. * accomplished by listening for the select psuedo-events:<ul>
  14105. *
  14106. * <li>resizestart (on mousedown)
  14107. * <li>resize (on mousemove)
  14108. * <li>resizeend (on mouseup)
  14109. *
  14110. * </ul>For example, to render the parent panel while resizing, thus
  14111. * re-rendering all sibling marks:
  14112. *
  14113. * <pre> .event("mousedown", pv.Behavior.resize("left"))
  14114. * .event("resize", function() this.parent)</pre>
  14115. *
  14116. * This behavior may be enhanced in the future to allow more flexible
  14117. * configuration of the selection behavior.
  14118. *
  14119. * @extends pv.Behavior
  14120. * @see pv.Behavior.select
  14121. * @see pv.Behavior.drag
  14122. */
  14123. pv.Behavior.resize = function(side) {
  14124. var scene, // scene context
  14125. index, // scene context
  14126. r, // region being selected
  14127. m1; // initial mouse position
  14128. /** @private */
  14129. function mousedown(d) {
  14130. index = this.index;
  14131. scene = this.scene;
  14132. m1 = this.mouse();
  14133. r = d;
  14134. switch (side) {
  14135. case "left": m1.x = r.x + r.dx; break;
  14136. case "right": m1.x = r.x; break;
  14137. case "top": m1.y = r.y + r.dy; break;
  14138. case "bottom": m1.y = r.y; break;
  14139. }
  14140. pv.Mark.dispatch("resizestart", scene, index);
  14141. }
  14142. /** @private */
  14143. function mousemove() {
  14144. if (!scene) return;
  14145. scene.mark.context(scene, index, function() {
  14146. var m2 = this.mouse();
  14147. r.x = Math.max(0, Math.min(m1.x, m2.x));
  14148. r.y = Math.max(0, Math.min(m1.y, m2.y));
  14149. r.dx = Math.min(this.parent.width(), Math.max(m2.x, m1.x)) - r.x;
  14150. r.dy = Math.min(this.parent.height(), Math.max(m2.y, m1.y)) - r.y;
  14151. this.render();
  14152. });
  14153. pv.Mark.dispatch("resize", scene, index);
  14154. }
  14155. /** @private */
  14156. function mouseup() {
  14157. if (!scene) return;
  14158. pv.Mark.dispatch("resizeend", scene, index);
  14159. scene = null;
  14160. }
  14161. pv.listen(window.document.documentElement, "mousemove", mousemove);
  14162. pv.listen(window.document.documentElement, "mouseup", mouseup);
  14163. return mousedown;
  14164. };
  14165. /**
  14166. * Returns a new pan behavior to be registered on mousedown events.
  14167. *
  14168. * @class Implements interactive panning starting with mousedown events.
  14169. * Register this behavior on panels to allow panning. This behavior can be used
  14170. * in tandem with {@link pv.Behavior.zoom} to allow both panning and zooming:
  14171. *
  14172. * <pre> .event("mousedown", pv.Behavior.pan())
  14173. * .event("mousewheel", pv.Behavior.zoom())</pre>
  14174. *
  14175. * The pan behavior currently supports only mouse events; support for keyboard
  14176. * shortcuts to improve accessibility may be added in the future.
  14177. *
  14178. * <p>After the initial mousedown event is triggered, this behavior listens for
  14179. * mousemove and mouseup events on the window. This allows panning to continue
  14180. * even if the mouse temporarily leaves the panel that is being panned, or even
  14181. * the root panel.
  14182. *
  14183. * <p>The implementation of this behavior relies on the panel's
  14184. * <tt>transform</tt> property, which specifies a matrix transformation that is
  14185. * applied to child marks. Note that the transform property only affects the
  14186. * panel's children, but not the panel itself; therefore the panel's fill and
  14187. * stroke will not change when the contents are panned.
  14188. *
  14189. * <p>Panels have transparent fill styles by default; this means that panels may
  14190. * not receive the initial mousedown event to start panning. To fix this
  14191. * problem, either given the panel a visible fill style (such as "white"), or
  14192. * set the <tt>events</tt> property to "all" such that the panel receives events
  14193. * despite its transparent fill.
  14194. *
  14195. * <p>The pan behavior has optional support for bounding. If enabled, the user
  14196. * will not be able to pan the panel outside of the initial bounds. This feature
  14197. * is designed to work in conjunction with the zoom behavior; otherwise,
  14198. * bounding the panel effectively disables all panning.
  14199. *
  14200. * @extends pv.Behavior
  14201. * @see pv.Behavior.zoom
  14202. * @see pv.Panel#transform
  14203. */
  14204. pv.Behavior.pan = function() {
  14205. var scene, // scene context
  14206. index, // scene context
  14207. m1, // transformation matrix at the start of panning
  14208. v1, // mouse location at the start of panning
  14209. k, // inverse scale
  14210. bound; // whether to bound to the panel
  14211. /** @private */
  14212. function mousedown() {
  14213. index = this.index;
  14214. scene = this.scene;
  14215. v1 = pv.vector(pv.event.pageX, pv.event.pageY);
  14216. m1 = this.transform();
  14217. k = 1 / (m1.k * this.scale);
  14218. if (bound) {
  14219. bound = {
  14220. x: (1 - m1.k) * this.width(),
  14221. y: (1 - m1.k) * this.height()
  14222. };
  14223. }
  14224. }
  14225. /** @private */
  14226. function mousemove() {
  14227. if (!scene) return;
  14228. scene.mark.context(scene, index, function() {
  14229. var x = (pv.event.pageX - v1.x) * k,
  14230. y = (pv.event.pageY - v1.y) * k,
  14231. m = m1.translate(x, y);
  14232. if (bound) {
  14233. m.x = Math.max(bound.x, Math.min(0, m.x));
  14234. m.y = Math.max(bound.y, Math.min(0, m.y));
  14235. }
  14236. this.transform(m).render();
  14237. });
  14238. pv.Mark.dispatch("pan", scene, index);
  14239. }
  14240. /** @private */
  14241. function mouseup() {
  14242. scene = null;
  14243. }
  14244. /**
  14245. * Sets or gets the bound parameter. If bounding is enabled, the user will not
  14246. * be able to pan outside the initial panel bounds; this typically applies
  14247. * only when the pan behavior is used in tandem with the zoom behavior.
  14248. * Bounding is not enabled by default.
  14249. *
  14250. * <p>Note: enabling bounding after panning has already occurred will not
  14251. * immediately reset the transform. Bounding should be enabled before the
  14252. * panning behavior is applied.
  14253. *
  14254. * @function
  14255. * @returns {pv.Behavior.pan} this, or the current bound parameter.
  14256. * @name pv.Behavior.pan.prototype.bound
  14257. * @param {boolean} [x] the new bound parameter.
  14258. */
  14259. mousedown.bound = function(x) {
  14260. if (arguments.length) {
  14261. bound = Boolean(x);
  14262. return this;
  14263. }
  14264. return Boolean(bound);
  14265. };
  14266. pv.listen(window, "mousemove", mousemove);
  14267. pv.listen(window, "mouseup", mouseup);
  14268. return mousedown;
  14269. };
  14270. /**
  14271. * Returns a new zoom behavior to be registered on mousewheel events.
  14272. *
  14273. * @class Implements interactive zooming using mousewheel events. Register this
  14274. * behavior on panels to allow zooming. This behavior can be used in tandem with
  14275. * {@link pv.Behavior.pan} to allow both panning and zooming:
  14276. *
  14277. * <pre> .event("mousedown", pv.Behavior.pan())
  14278. * .event("mousewheel", pv.Behavior.zoom())</pre>
  14279. *
  14280. * The zoom behavior currently supports only mousewheel events; support for
  14281. * keyboard shortcuts and gesture events to improve accessibility may be added
  14282. * in the future.
  14283. *
  14284. * <p>The implementation of this behavior relies on the panel's
  14285. * <tt>transform</tt> property, which specifies a matrix transformation that is
  14286. * applied to child marks. Note that the transform property only affects the
  14287. * panel's children, but not the panel itself; therefore the panel's fill and
  14288. * stroke will not change when the contents are zoomed. The built-in support for
  14289. * transforms only supports uniform scaling and translates, which is sufficient
  14290. * for panning and zooming. Note that this is not a strict geometric
  14291. * transformation, as the <tt>lineWidth</tt> property is scale-aware: strokes
  14292. * are drawn at constant size independent of scale.
  14293. *
  14294. * <p>Panels have transparent fill styles by default; this means that panels may
  14295. * not receive mousewheel events to zoom. To fix this problem, either given the
  14296. * panel a visible fill style (such as "white"), or set the <tt>events</tt>
  14297. * property to "all" such that the panel receives events despite its transparent
  14298. * fill.
  14299. *
  14300. * <p>The zoom behavior has optional support for bounding. If enabled, the user
  14301. * will not be able to zoom out farther than the initial bounds. This feature is
  14302. * designed to work in conjunction with the pan behavior.
  14303. *
  14304. * @extends pv.Behavior
  14305. * @see pv.Panel#transform
  14306. * @see pv.Mark#scale
  14307. * @param {number} speed
  14308. */
  14309. pv.Behavior.zoom = function(speed) {
  14310. var bound; // whether to bound to the panel
  14311. if (!arguments.length) speed = 1 / 48;
  14312. /** @private */
  14313. function mousewheel() {
  14314. var v = this.mouse(),
  14315. k = pv.event.wheel * speed,
  14316. m = this.transform().translate(v.x, v.y)
  14317. .scale((k < 0) ? (1e3 / (1e3 - k)) : ((1e3 + k) / 1e3))
  14318. .translate(-v.x, -v.y);
  14319. if (bound) {
  14320. m.k = Math.max(1, m.k);
  14321. m.x = Math.max((1 - m.k) * this.width(), Math.min(0, m.x));
  14322. m.y = Math.max((1 - m.k) * this.height(), Math.min(0, m.y));
  14323. }
  14324. this.transform(m).render();
  14325. pv.Mark.dispatch("zoom", this.scene, this.index);
  14326. }
  14327. /**
  14328. * Sets or gets the bound parameter. If bounding is enabled, the user will not
  14329. * be able to zoom out farther than the initial panel bounds. Bounding is not
  14330. * enabled by default. If this behavior is used in tandem with the pan
  14331. * behavior, both should use the same bound parameter.
  14332. *
  14333. * <p>Note: enabling bounding after zooming has already occurred will not
  14334. * immediately reset the transform. Bounding should be enabled before the zoom
  14335. * behavior is applied.
  14336. *
  14337. * @function
  14338. * @returns {pv.Behavior.zoom} this, or the current bound parameter.
  14339. * @name pv.Behavior.zoom.prototype.bound
  14340. * @param {boolean} [x] the new bound parameter.
  14341. */
  14342. mousewheel.bound = function(x) {
  14343. if (arguments.length) {
  14344. bound = Boolean(x);
  14345. return this;
  14346. }
  14347. return Boolean(bound);
  14348. };
  14349. return mousewheel;
  14350. };
  14351. /**
  14352. * @ignore
  14353. * @namespace
  14354. */
  14355. pv.Geo = function() {};
  14356. /**
  14357. * Abstract; not implemented. There is no explicit constructor; this class
  14358. * merely serves to document the representation used by {@link pv.Geo.scale}.
  14359. *
  14360. * @class Represents a pair of geographic coordinates.
  14361. *
  14362. * @name pv.Geo.LatLng
  14363. * @see pv.Geo.scale
  14364. */
  14365. /**
  14366. * The <i>latitude</i> coordinate in degrees; positive is North.
  14367. *
  14368. * @type number
  14369. * @name pv.Geo.LatLng.prototype.lat
  14370. */
  14371. /**
  14372. * The <i>longitude</i> coordinate in degrees; positive is East.
  14373. *
  14374. * @type number
  14375. * @name pv.Geo.LatLng.prototype.lng
  14376. */
  14377. /**
  14378. * Abstract; not implemented. There is no explicit constructor; this class
  14379. * merely serves to document the representation used by {@link pv.Geo.scale}.
  14380. *
  14381. * @class Represents a geographic projection. This class provides the core
  14382. * implementation for {@link pv.Geo.scale}s, mapping between geographic
  14383. * coordinates (latitude and longitude) and normalized screen space in the range
  14384. * [-1,1]. The remaining mapping between normalized screen space and actual
  14385. * pixels is performed by <tt>pv.Geo.scale</tt>.
  14386. *
  14387. * <p>Many geographic projections have a point around which the projection is
  14388. * centered. Rather than have each implementation add support for a
  14389. * user-specified center point, the <tt>pv.Geo.scale</tt> translates the
  14390. * geographic coordinates relative to the center point for both the forward and
  14391. * inverse projection.
  14392. *
  14393. * <p>In general, this class should not be used directly, unless the desire is
  14394. * to implement a new geographic projection. Instead, use <tt>pv.Geo.scale</tt>.
  14395. * Implementations are not required to implement inverse projections, but are
  14396. * needed for some forms of interactivity. Also note that some inverse
  14397. * projections are ambiguous, such as the connecting points in Dymaxian maps.
  14398. *
  14399. * @name pv.Geo.Projection
  14400. * @see pv.Geo.scale
  14401. */
  14402. /**
  14403. * The <i>forward</i> projection.
  14404. *
  14405. * @function
  14406. * @name pv.Geo.Projection.prototype.project
  14407. * @param {pv.Geo.LatLng} latlng the latitude and longitude to project.
  14408. * @returns {pv.Vector} the xy-coordinates of the given point.
  14409. */
  14410. /**
  14411. * The <i>inverse</i> projection; optional.
  14412. *
  14413. * @function
  14414. * @name pv.Geo.Projection.prototype.invert
  14415. * @param {pv.Vector} xy the x- and y-coordinates to invert.
  14416. * @returns {pv.Geo.LatLng} the latitude and longitude of the given point.
  14417. */
  14418. /**
  14419. * The built-in projections.
  14420. *
  14421. * @see pv.Geo.Projection
  14422. * @namespace
  14423. */
  14424. pv.Geo.projections = {
  14425. /** @see http://en.wikipedia.org/wiki/Mercator_projection */
  14426. mercator: {
  14427. project: function(latlng) {
  14428. return {
  14429. x: latlng.lng / 180,
  14430. y: latlng.lat > 85 ? 1 : latlng.lat < -85 ? -1
  14431. : Math.log(Math.tan(Math.PI / 4
  14432. + pv.radians(latlng.lat) / 2)) / Math.PI
  14433. };
  14434. },
  14435. invert: function(xy) {
  14436. return {
  14437. lng: xy.x * 180,
  14438. lat: pv.degrees(2 * Math.atan(Math.exp(xy.y * Math.PI)) - Math.PI / 2)
  14439. };
  14440. }
  14441. },
  14442. /** @see http://en.wikipedia.org/wiki/Gall-Peters_projection */
  14443. "gall-peters": {
  14444. project: function(latlng) {
  14445. return {
  14446. x: latlng.lng / 180,
  14447. y: Math.sin(pv.radians(latlng.lat))
  14448. };
  14449. },
  14450. invert: function(xy) {
  14451. return {
  14452. lng: xy.x * 180,
  14453. lat: pv.degrees(Math.asin(xy.y))
  14454. };
  14455. }
  14456. },
  14457. /** @see http://en.wikipedia.org/wiki/Sinusoidal_projection */
  14458. sinusoidal: {
  14459. project: function(latlng) {
  14460. return {
  14461. x: pv.radians(latlng.lng) * Math.cos(pv.radians(latlng.lat)) / Math.PI,
  14462. y: latlng.lat / 90
  14463. };
  14464. },
  14465. invert: function(xy) {
  14466. return {
  14467. lng: pv.degrees((xy.x * Math.PI) / Math.cos(xy.y * Math.PI / 2)),
  14468. lat: xy.y * 90
  14469. };
  14470. }
  14471. },
  14472. /** @see http://en.wikipedia.org/wiki/Aitoff_projection */
  14473. aitoff: {
  14474. project: function(latlng) {
  14475. var l = pv.radians(latlng.lng),
  14476. f = pv.radians(latlng.lat),
  14477. a = Math.acos(Math.cos(f) * Math.cos(l / 2));
  14478. return {
  14479. x: 2 * (a ? (Math.cos(f) * Math.sin(l / 2) * a / Math.sin(a)) : 0) / Math.PI,
  14480. y: 2 * (a ? (Math.sin(f) * a / Math.sin(a)) : 0) / Math.PI
  14481. };
  14482. },
  14483. invert: function(xy) {
  14484. var x = xy.x * Math.PI / 2,
  14485. y = xy.y * Math.PI / 2;
  14486. return {
  14487. lng: pv.degrees(x / Math.cos(y)),
  14488. lat: pv.degrees(y)
  14489. };
  14490. }
  14491. },
  14492. /** @see http://en.wikipedia.org/wiki/Hammer_projection */
  14493. hammer: {
  14494. project: function(latlng) {
  14495. var l = pv.radians(latlng.lng),
  14496. f = pv.radians(latlng.lat),
  14497. c = Math.sqrt(1 + Math.cos(f) * Math.cos(l / 2));
  14498. return {
  14499. x: 2 * Math.SQRT2 * Math.cos(f) * Math.sin(l / 2) / c / 3,
  14500. y: Math.SQRT2 * Math.sin(f) / c / 1.5
  14501. };
  14502. },
  14503. invert: function(xy) {
  14504. var x = xy.x * 3,
  14505. y = xy.y * 1.5,
  14506. z = Math.sqrt(1 - x * x / 16 - y * y / 4);
  14507. return {
  14508. lng: pv.degrees(2 * Math.atan2(z * x, 2 * (2 * z * z - 1))),
  14509. lat: pv.degrees(Math.asin(z * y))
  14510. };
  14511. }
  14512. },
  14513. /** The identity or "none" projection. */
  14514. identity: {
  14515. project: function(latlng) {
  14516. return {
  14517. x: latlng.lng / 180,
  14518. y: latlng.lat / 90
  14519. };
  14520. },
  14521. invert: function(xy) {
  14522. return {
  14523. lng: xy.x * 180,
  14524. lat: xy.y * 90
  14525. };
  14526. }
  14527. }
  14528. };
  14529. /**
  14530. * Returns a geographic scale. The arguments to this constructor are optional,
  14531. * and equivalent to calling {@link #projection}.
  14532. *
  14533. * @class Represents a geographic scale; a mapping between latitude-longitude
  14534. * coordinates and screen pixel coordinates. By default, the domain is inferred
  14535. * from the geographic coordinates, so that the domain fills the output range.
  14536. *
  14537. * <p>Note that geographic scales are two-dimensional transformations, rather
  14538. * than the one-dimensional bidrectional mapping typical of other scales.
  14539. * Rather than mapping (for example) between a numeric domain and a numeric
  14540. * range, geographic scales map between two coordinate objects: {@link
  14541. * pv.Geo.LatLng} and {@link pv.Vector}.
  14542. *
  14543. * @param {pv.Geo.Projection} [p] optional projection.
  14544. * @see pv.Geo.scale#ticks
  14545. */
  14546. pv.Geo.scale = function(p) {
  14547. var rmin = {x: 0, y: 0}, // default range minimum
  14548. rmax = {x: 1, y: 1}, // default range maximum
  14549. d = [], // default domain
  14550. j = pv.Geo.projections.identity, // domain <-> normalized range
  14551. x = pv.Scale.linear(-1, 1).range(0, 1), // normalized <-> range
  14552. y = pv.Scale.linear(-1, 1).range(1, 0), // normalized <-> range
  14553. c = {lng: 0, lat: 0}, // Center Point
  14554. lastLatLng, // cached latlng
  14555. lastPoint; // cached point
  14556. /** @private */
  14557. function scale(latlng) {
  14558. if (!lastLatLng
  14559. || (latlng.lng != lastLatLng.lng)
  14560. || (latlng.lat != lastLatLng.lat)) {
  14561. lastLatLng = latlng;
  14562. var p = project(latlng);
  14563. lastPoint = {x: x(p.x), y: y(p.y)};
  14564. }
  14565. return lastPoint;
  14566. }
  14567. /** @private */
  14568. function project(latlng) {
  14569. var offset = {lng: latlng.lng - c.lng, lat: latlng.lat};
  14570. return j.project(offset);
  14571. }
  14572. /** @private */
  14573. function invert(xy) {
  14574. var latlng = j.invert(xy);
  14575. latlng.lng += c.lng;
  14576. return latlng;
  14577. }
  14578. /** Returns the projected x-coordinate. */
  14579. scale.x = function(latlng) {
  14580. return scale(latlng).x;
  14581. };
  14582. /** Returns the projected y-coordinate. */
  14583. scale.y = function(latlng) {
  14584. return scale(latlng).y;
  14585. };
  14586. /**
  14587. * Abstract; this is a local namespace on a given geographic scale.
  14588. *
  14589. * @namespace Tick functions for geographic scales. Because geographic scales
  14590. * represent two-dimensional transformations (as opposed to one-dimensional
  14591. * transformations typical of other scales), the tick values are similarly
  14592. * represented as two-dimensional coordinates in the input domain, i.e.,
  14593. * {@link pv.Geo.LatLng} objects.
  14594. *
  14595. * <p>Also, note that non-rectilinear projections, such as sinsuoidal and
  14596. * aitoff, may not produce straight lines for constant longitude or constant
  14597. * latitude. Therefore the returned array of ticks is a two-dimensional array,
  14598. * sampling various latitudes as constant longitude, and vice versa.
  14599. *
  14600. * <p>The tick lines can therefore be approximated as polylines, either with
  14601. * "linear" or "cardinal" interpolation. This is not as accurate as drawing
  14602. * the true curve through the projection space, but is usually sufficient.
  14603. *
  14604. * @name pv.Geo.scale.prototype.ticks
  14605. * @see pv.Geo.scale
  14606. * @see pv.Geo.LatLng
  14607. * @see pv.Line#interpolate
  14608. */
  14609. scale.ticks = {
  14610. /**
  14611. * Returns longitude ticks.
  14612. *
  14613. * @function
  14614. * @param {number} [m] the desired number of ticks.
  14615. * @returns {array} a nested array of <tt>pv.Geo.LatLng</tt> ticks.
  14616. * @name pv.Geo.scale.prototype.ticks.prototype.lng
  14617. */
  14618. lng: function(m) {
  14619. var lat, lng;
  14620. if (d.length > 1) {
  14621. var s = pv.Scale.linear();
  14622. if (m == undefined) m = 10;
  14623. lat = s.domain(d, function(d) { return d.lat; }).ticks(m);
  14624. lng = s.domain(d, function(d) { return d.lng; }).ticks(m);
  14625. } else {
  14626. lat = pv.range(-80, 81, 10);
  14627. lng = pv.range(-180, 181, 10);
  14628. }
  14629. return lng.map(function(lng) {
  14630. return lat.map(function(lat) {
  14631. return {lat: lat, lng: lng};
  14632. });
  14633. });
  14634. },
  14635. /**
  14636. * Returns latitude ticks.
  14637. *
  14638. * @function
  14639. * @param {number} [m] the desired number of ticks.
  14640. * @returns {array} a nested array of <tt>pv.Geo.LatLng</tt> ticks.
  14641. * @name pv.Geo.scale.prototype.ticks.prototype.lat
  14642. */
  14643. lat: function(m) {
  14644. return pv.transpose(scale.ticks.lng(m));
  14645. }
  14646. };
  14647. /**
  14648. * Inverts the specified value in the output range, returning the
  14649. * corresponding value in the input domain. This is frequently used to convert
  14650. * the mouse location (see {@link pv.Mark#mouse}) to a value in the input
  14651. * domain. Inversion is only supported for numeric ranges, and not colors.
  14652. *
  14653. * <p>Note that this method does not do any rounding or bounds checking. If
  14654. * the input domain is discrete (e.g., an array index), the returned value
  14655. * should be rounded. If the specified <tt>y</tt> value is outside the range,
  14656. * the returned value may be equivalently outside the input domain.
  14657. *
  14658. * @function
  14659. * @name pv.Geo.scale.prototype.invert
  14660. * @param {number} y a value in the output range (a pixel location).
  14661. * @returns {number} a value in the input domain.
  14662. */
  14663. scale.invert = function(p) {
  14664. return invert({x: x.invert(p.x), y: y.invert(p.y)});
  14665. };
  14666. /**
  14667. * Sets or gets the input domain. Note that unlike quantitative scales, the
  14668. * domain cannot be reduced to a simple rectangle (i.e., minimum and maximum
  14669. * values for latitude and longitude). Instead, the domain values must be
  14670. * projected to normalized space, effectively finding the domain in normalized
  14671. * space rather than in terms of latitude and longitude. Thus, changing the
  14672. * projection requires recomputing the normalized domain.
  14673. *
  14674. * <p>This method can be invoked several ways:
  14675. *
  14676. * <p>1. <tt>domain(values...)</tt>
  14677. *
  14678. * <p>Specifying the domain as a series of {@link pv.Geo.LatLng}s is the most
  14679. * explicit and recommended approach. However, if the domain values are
  14680. * derived from data, you may find the second method more appropriate.
  14681. *
  14682. * <p>2. <tt>domain(array, f)</tt>
  14683. *
  14684. * <p>Rather than enumerating the domain explicitly, you can specify a single
  14685. * argument of an array. In addition, you can specify an optional accessor
  14686. * function to extract the domain values (as {@link pv.Geo.LatLng}s) from the
  14687. * array. If the specified array has fewer than two elements, this scale will
  14688. * default to the full normalized domain.
  14689. *
  14690. * <p>2. <tt>domain()</tt>
  14691. *
  14692. * <p>Invoking the <tt>domain</tt> method with no arguments returns the
  14693. * current domain as an array.
  14694. *
  14695. * @function
  14696. * @name pv.Geo.scale.prototype.domain
  14697. * @param {...} domain... domain values.
  14698. * @returns {pv.Geo.scale} <tt>this</tt>, or the current domain.
  14699. */
  14700. scale.domain = function(array, f) {
  14701. if (arguments.length) {
  14702. d = (array instanceof Array)
  14703. ? ((arguments.length > 1) ? pv.map(array, f) : array)
  14704. : Array.prototype.slice.call(arguments);
  14705. if (d.length > 1) {
  14706. var lngs = d.map(function(c) { return c.lng; });
  14707. var lats = d.map(function(c) { return c.lat; });
  14708. c = {
  14709. lng: (pv.max(lngs) + pv.min(lngs)) / 2,
  14710. lat: (pv.max(lats) + pv.min(lats)) / 2
  14711. };
  14712. var n = d.map(project); // normalized domain
  14713. x.domain(n, function(p) { return p.x; });
  14714. y.domain(n, function(p) { return p.y; });
  14715. } else {
  14716. c = {lng: 0, lat: 0};
  14717. x.domain(-1, 1);
  14718. y.domain(-1, 1);
  14719. }
  14720. lastLatLng = null; // invalidate the cache
  14721. return this;
  14722. }
  14723. return d;
  14724. };
  14725. /**
  14726. * Sets or gets the output range. This method can be invoked several ways:
  14727. *
  14728. * <p>1. <tt>range(min, max)</tt>
  14729. *
  14730. * <p>If two objects are specified, the arguments should be {@link pv.Vector}s
  14731. * which specify the minimum and maximum values of the x- and y-coordinates
  14732. * explicitly.
  14733. *
  14734. * <p>2. <tt>range(width, height)</tt>
  14735. *
  14736. * <p>If two numbers are specified, the arguments specify the maximum values
  14737. * of the x- and y-coordinates explicitly; the minimum values are implicitly
  14738. * zero.
  14739. *
  14740. * <p>3. <tt>range()</tt>
  14741. *
  14742. * <p>Invoking the <tt>range</tt> method with no arguments returns the current
  14743. * range as an array of two {@link pv.Vector}s: the minimum (top-left) and
  14744. * maximum (bottom-right) values.
  14745. *
  14746. * @function
  14747. * @name pv.Geo.scale.prototype.range
  14748. * @param {...} range... range values.
  14749. * @returns {pv.Geo.scale} <tt>this</tt>, or the current range.
  14750. */
  14751. scale.range = function(min, max) {
  14752. if (arguments.length) {
  14753. if (typeof min == "object") {
  14754. rmin = {x: Number(min.x), y: Number(min.y)};
  14755. rmax = {x: Number(max.x), y: Number(max.y)};
  14756. } else {
  14757. rmin = {x: 0, y: 0};
  14758. rmax = {x: Number(min), y: Number(max)};
  14759. }
  14760. x.range(rmin.x, rmax.x);
  14761. y.range(rmax.y, rmin.y); // XXX flipped?
  14762. lastLatLng = null; // invalidate the cache
  14763. return this;
  14764. }
  14765. return [rmin, rmax];
  14766. };
  14767. /**
  14768. * Sets or gets the projection. This method can be invoked several ways:
  14769. *
  14770. * <p>1. <tt>projection(string)</tt>
  14771. *
  14772. * <p>Specifying a string sets the projection to the given named projection in
  14773. * {@link pv.Geo.projections}. If no such projection is found, the identity
  14774. * projection is used.
  14775. *
  14776. * <p>2. <tt>projection(object)</tt>
  14777. *
  14778. * <p>Specifying an object sets the projection to the given custom projection,
  14779. * which must implement the <i>forward</i> and <i>inverse</i> methods per the
  14780. * {@link pv.Geo.Projection} interface.
  14781. *
  14782. * <p>3. <tt>projection()</tt>
  14783. *
  14784. * <p>Invoking the <tt>projection</tt> method with no arguments returns the
  14785. * current object that defined the projection.
  14786. *
  14787. * @function
  14788. * @name pv.Scale.geo.prototype.projection
  14789. * @param {...} range... range values.
  14790. * @returns {pv.Scale.geo} <tt>this</tt>, or the current range.
  14791. */
  14792. scale.projection = function(p) {
  14793. if (arguments.length) {
  14794. j = typeof p == "string"
  14795. ? pv.Geo.projections[p] || pv.Geo.projections.identity
  14796. : p;
  14797. return this.domain(d); // recompute normalized domain
  14798. }
  14799. return p;
  14800. };
  14801. /**
  14802. * Returns a view of this scale by the specified accessor function <tt>f</tt>.
  14803. * Given a scale <tt>g</tt>, <tt>g.by(function(d) d.foo)</tt> is equivalent to
  14804. * <tt>function(d) g(d.foo)</tt>. This method should be used judiciously; it
  14805. * is typically more clear to invoke the scale directly, passing in the value
  14806. * to be scaled.
  14807. *
  14808. * @function
  14809. * @name pv.Geo.scale.prototype.by
  14810. * @param {function} f an accessor function.
  14811. * @returns {pv.Geo.scale} a view of this scale by the specified accessor
  14812. * function.
  14813. */
  14814. scale.by = function(f) {
  14815. function by() { return scale(f.apply(this, arguments)); }
  14816. for (var method in scale) by[method] = scale[method];
  14817. return by;
  14818. };
  14819. if (arguments.length) scale.projection(p);
  14820. return scale;
  14821. };