Skip to content

Instantly share code, notes, and snippets.

@tomgp
Last active August 19, 2016 14:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomgp/05fcbf6c0ebb9822a76831a94f91ace3 to your computer and use it in GitHub Desktop.
Save tomgp/05fcbf6c0ebb9822a76831a94f91ace3 to your computer and use it in GitHub Desktop.
path data polyfill example

A quick example of using this path data polyfill https://github.com/jarek-foksa/path-data-polyfill.js/ to reduce the precision of a path in an SVG document.

If you look at the source html file you'll see the path SVG path instructions defining are like:

M384.6230117358495,287.6713744571799L397.08777969854003,288.53704901891433L399.016198139235,288.65687471889737L405.0775753249624,289.04830888751303L413.0760961999088,289.504479388146L415.02192531223386,289.60024485251165L421.0827733171602,289.9084797753558L429.13443404004863,290.2648097494001L428.83556361439037,298.6295362012246L428.69664162606546,301.9280612693191L428.48932991288234,306.85041295093174L428.33332690011395,310.5545051145947L428.14336832261506,315.0648287669893L428.023390513073,317.91354852087034L427.7976117184192,323.2743774507584L427.69412354673955,326.7725476944737L428.8374432923897,326.556035920319L430.00513510391124,327.89322762504503L432.1727589548982,330.38775811815697L433.9605749955244,329.7296809910117L435.54682365172744,330.18421351335894L436.7127371625205,330.5916451128692L437.08575535040404,329.0589050181393L439.54527993900876,331.43782001784666L439.81345430247575,333.7613704720907L443.4242592788312,333.9053676145802L446.2469024670128,335.42239578633473L448.6405465392132,335.10075973961045L450.49240351789365,336.92407073999505L451.4660574384825,336.57308134920584L452.40958733769946,335.1983999150019L455.38455602850274,336.07626092313376L455.87580805799564,335.5423989169211L456.66529546295556,338.21190858184866L458.7612752077533,338.4908689702986L458.2258241881882,340.3600246858591L459.87838566958897,341.15964728460165L462.7256430612595,338.6171967100563L464.6401250141841,340.28944173696914L465.8318410975988,340.0164309403742L466.1468165975049,341.4373047021678L467.5171254768992,341.84987012724275L468.51344333692316,340.56119870901784L470.40521924762396,340.3444852630158L470.1051931172094,342.2295751168L471.5860077811759,343.75325483342965L471.7844786270004,342.3822771249942L474.13587435323024,339.4918473802152L474.28585107593375,339.39428050804133L475.2841273238143,341.2724555697505L476.76173479010464,341.6773208478088L478.58487417392166,340.19330076962615L479.6041182917555,340.55368968476523L478.99036078636186,341.49870565049923L482.12355750615563,342.79233112655356L482.8111190099523,343.753134 ...

a far greater degree of precision than required. The rendered verion (right-click > inspect element) is much simpler

M385 288 L 397 289 L 399 289 L 405 289 L 413 290 L 415 290 L 421 290 L 429 290 L 429 299 L 429 302 L 428 307 L 428 311 L 428 315 L 428 318 L 428 323 L 428 327 L 429 327 L 430 328 L 432 330 L 434 330 L 436 330 L 437 331 L 437 329 L 440 331 L 440 334 L 443 334 L 446 335 L 449 335 L 450 337 L 451 337 L 452 335 L 455 336 L 456 336 L 457 338 L 459 338 L 458 340 L 460 341 L 463 339 L 465 340 L 466 340 L 466 341 L 468 342 L 469 341 L 470 340 L 470 342 L 472 344 L 472 342 L 474 339 L 474 339 L 475 341 L 477 342 L 479 340 L 480 341 L 479 341 L 482 343 L 483 344 L 486 343 L 487 341 L 490 341 L 491 342 L 492 341 L 495 340 L 496 341 L 499 341 L 501 339 L 502 340 L 505 341 L 506 343 L 508 343 L 508 344 L 512 345 L 514 347 L 517 346 L 519 347 L 519 352 L 519 357 L 519 359 L 519 363 L 519 369 L 519 372 L 520 376 L 522 379 L 524 381 L 523 384 L 525 385 L 525 389 L 527 391 L 528 391 L 528 394 L 528 397 L 527 400 L 525 403 L 526 405 L 525 406 L 525 407 L 526 409 L 525 412 L 523 416 L 522 417 L 524 419 L 521 419 L 515 422 L 515 422 L 509 425 L 512 422 L 514 422 L 512 421 L 509 422 L 510 418 L 508 417 L 507 420 L 506 420 L 505 419 L 504 417 L 503 418 L 505 420 L 505 422 L 506 423 L 506 424 L 507 425 L 507 427 L 506 426 L 504 429 L 503 429 L 501 432 L 498 435 L 497 436 L 494 437 L 492 437 L 490 438 L 489 439 L 494 438 L 483 443 L 488 440 L 489 439 L 486 440 ...

etc.

<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="path-data-polyfill.js"></script>
</head>
<body>
<svg width="300" height="300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path transform="translate(-300,-280)" class="state" id="tx" d="M384.6230117358495,287.6713744571799L397.08777969854003,288.53704901891433L399.016198139235,288.65687471889737L405.0775753249624,289.04830888751303L413.0760961999088,289.504479388146L415.02192531223386,289.60024485251165L421.0827733171602,289.9084797753558L429.13443404004863,290.2648097494001L428.83556361439037,298.6295362012246L428.69664162606546,301.9280612693191L428.48932991288234,306.85041295093174L428.33332690011395,310.5545051145947L428.14336832261506,315.0648287669893L428.023390513073,317.91354852087034L427.7976117184192,323.2743774507584L427.69412354673955,326.7725476944737L428.8374432923897,326.556035920319L430.00513510391124,327.89322762504503L432.1727589548982,330.38775811815697L433.9605749955244,329.7296809910117L435.54682365172744,330.18421351335894L436.7127371625205,330.5916451128692L437.08575535040404,329.0589050181393L439.54527993900876,331.43782001784666L439.81345430247575,333.7613704720907L443.4242592788312,333.9053676145802L446.2469024670128,335.42239578633473L448.6405465392132,335.10075973961045L450.49240351789365,336.92407073999505L451.4660574384825,336.57308134920584L452.40958733769946,335.1983999150019L455.38455602850274,336.07626092313376L455.87580805799564,335.5423989169211L456.66529546295556,338.21190858184866L458.7612752077533,338.4908689702986L458.2258241881882,340.3600246858591L459.87838566958897,341.15964728460165L462.7256430612595,338.6171967100563L464.6401250141841,340.28944173696914L465.8318410975988,340.0164309403742L466.1468165975049,341.4373047021678L467.5171254768992,341.84987012724275L468.51344333692316,340.56119870901784L470.40521924762396,340.3444852630158L470.1051931172094,342.2295751168L471.5860077811759,343.75325483342965L471.7844786270004,342.3822771249942L474.13587435323024,339.4918473802152L474.28585107593375,339.39428050804133L475.2841273238143,341.2724555697505L476.76173479010464,341.6773208478088L478.58487417392166,340.19330076962615L479.6041182917555,340.55368968476523L478.99036078636186,341.49870565049923L482.12355750615563,342.79233112655356L482.8111190099523,343.7531342122886L485.9007305679798,343.10442308717904L487.28550101783486,341.2894996029464L489.65550760404983,340.71883334664324L491.02119480831175,341.6146473944244L492.3515032201153,340.97524518032174L494.8210255649317,339.70567306003477L495.5597217094616,340.6806652255783L499.25611072160046,340.8606787066211L500.6626905074971,339.26408475466735L501.6436469089211,339.74184445655396L504.51802766774017,341.1233591299092L506.2075218253686,343.32124732969066L507.50867729259124,342.944331953889L507.9993124764135,344.0686822170193L512.0322285657359,345.2325879014338L513.5909779234618,346.95387898832735L517.1133843512458,346.04517388437193L518.8962704109348,346.7178404729011L519.0106129147188,352.0009836737694L519.1081259744554,356.73630213902993L519.1615766839401,359.3319147911909L519.2343169910115,362.8642469749418L519.3506060385075,368.5113436346343L519.4321117448201,372.195229319743L519.9399510086631,376.24215325336195L522.1521663012882,378.73968416782566L523.5457582010387,381.376718825471L522.9594037031065,383.56163316845334L524.8792412051306,384.8666990923573L525.4769839591941,388.8358648148792L526.8713733623354,391.15652448890717L527.6267569443177,390.9610562181398L528.3949291542592,393.87674672559626L527.6820484248918,396.89545505230376L526.6068007560256,400.49731166628203L524.93747390049,403.1096700869681L525.6493703967188,404.90450347768217L524.964442771361,405.69909522002206L524.6038349052301,406.8093839400135L525.5548644132031,408.6421593023325L525.4447257946155,412.2490525514039L523.3798449053842,415.7843700194296L522.1915105439131,416.97147597424305L523.6971087487683,419.00602550472706L520.8622980231428,419.30074768682005L515.388873031154,421.6182042332455L515.1226589329395,421.73416914680115L508.8425899029044,424.81645573612104L511.9876293167369,422.24526175122026L513.5132395354232,421.726806148042L512.2791490110409,421.4762718007206L509.1430563043859,422.3648745709273L509.80721033201246,418.06180802915503L508.0479489919558,417.3833000730448L506.9185908934831,419.70964956796774L506.06047635569325,419.5118338464482L504.611062658012,418.9624810279108L503.6359041048115,416.826222361702L503.20447642452336,417.9084657163354L505.1647691838818,419.5869177593172L504.6076888430024,422.0160635117645L506.3798514929021,422.993960946366L505.69728260731864,423.53129937681535L506.84304688972355,425.17122310283855L506.50335836047435,426.62326846104804L505.8982021700938,426.37442071464875L504.04831733068505,428.5174814052692L502.53497382573767,428.87360547843775L501.1346604835137,432.448396221013L498.4670757087735,434.82684553278733L496.7366027020399,435.55596102104204L493.71943174028956,437.37933892129L492.128847315749,437.0472278248081L489.7275855513166,437.99297259496154L489.29221719533217,439.3157736022033L493.50794261343685,437.5372214311138L483.16031461417003,442.8042763455036L487.90638646272987,440.07851868067456L489.07392772894406,438.7391925587521L485.97236293745084,439.68598750544606L483.36240010304783,438.9735369699251L482.24490251279,438.38164642637435L481.62899962996215,437.7662169139126L481.5085793909011,439.62487339844114L479.6370349164561,438.98948387333326L479.24343715671057,437.7916870893512L477.89655682657576,437.5096092528505L477.8493911833147,437.5172937661363L477.83194035942563,437.4861307816594L477.8352229963987,437.8616521266648L478.63549322285917,439.82429928805806L482.1207591177481,442.70821639297907L477.9578591143356,444.9154568993265L475.7287408310014,442.5723739590109L474.5062098114249,443.1711965907134L475.6261219663623,444.96481952083144L475.48917595169905,445.7699405371461L475.5049205499931,446.7162742306605L475.302788178658,446.8005783733009L474.7643113662682,447.3979616802616L472.808437828908,448.58041269447205L471.86347788239317,447.09556571852664L468.1204590033649,449.65594451509236L469.4762888988658,449.9360181812439L469.0079462209349,451.30975412846584L469.0545391602484,451.38530201305264L471.7211629163113,448.7530437301959L471.6635077360939,450.245109365379L469.8358900856188,452.63017446656323L469.8177176597572,452.65793945560716L469.72665384460777,452.8127468957206L468.91316761759776,454.13177319105677L467.5529792233926,453.02395415077524L463.94872351264047,453.7439898644852L465.6624752253029,453.76194247138375L466.5646335994797,456.06173447602725L467.8347162087781,456.4665726351093L466.61343590345643,458.97265480380963L465.04848801170436,463.4179830736737L463.49046742689256,464.2441603669981L464.2108666071831,461.8214638857678L461.7492445183454,463.40532389921054L459.7710781482803,461.54506328523263L460.9253232761058,463.9489322824498L459.00732862588063,464.00303485759093L461.1609765129904,464.1775437259882L462.8478548894675,465.0759005340881L464.8948047580081,464.4833308779168L464.43381583322264,467.2085786537093L464.03330093564284,469.04124416025275L462.59647652336,469.81097663562576L462.6834500119364,472.5913178916517L463.94054871438135,473.86480818512086L464.36637517097836,476.7809742973167L464.6567083213461,478.3586609491569L464.6241129016081,479.72503891715695L464.2707314777898,480.2870667198023L465.2145258335111,480.305137700986L466.76962163842575,484.31314053031593L466.47827250655234,485.6428897344812L468.19430917852554,486.77240210624836L467.48427915398923,488.55667125426544L465.3400264146579,489.61442218433547L465.3583652351342,490.8727809713346L462.8750609672361,489.9793724577259L459.78443411373087,487.1306402601361L457.15332257888394,486.46972656443404L451.5524386575479,486.66450075679757L448.31482615774866,484.65195244119576L447.35777096110775,483.3899530632787L444.98615918337094,482.7102135618484L443.4683283851266,482.9896157145484L441.37405328926087,480.57925041821323L436.23015885705036,479.3229047821107L436.58685118972164,478.4063932093853L435.34041623954846,476.598255271054L433.84748289021263,471.51063569946655L430.99442259528774,468.11566542604623L431.422378542059,465.2514590725577L431.0212565963943,463.574021442694L430.233771699313,462.16841258079063L430.75383964923435,459.5799824256308L428.9425944666232,456.5552463514221L427.0079209726015,456.1110840520755L424.35602258517275,453.37908725348296L423.5742632555052,449.9535731079509L422.6684286267845,449.7187244532205L421.1430748554997,446.75605490535435L419.1398234619126,445.7566681406466L417.8639216365758,444.0636451142494L416.35671560214337,438.35316660315794L414.83556491368114,436.9329481642085L414.17222615200654,433.78253280863555L412.57549455017977,431.51789172063474L412.44625705259995,428.8830994876207L410.76811307086484,427.11474683835775L410.5214779946721,425.84362890608054L407.2177918079258,423.36894482259754L406.4060887888181,421.5803862471927L403.229776752647,419.92897216953656L402.7034671681954,417.9532572871017L400.3705483601588,415.10104885828616L399.0041334398802,415.56387152235743L395.39030457832274,414.9154640641725L389.6707245955446,414.34641417363093L386.4717577220131,412.5151290088447L385.22298719225506,414.63782965913947L383.18679338406514,414.06979568984207L380.6940864556053,414.6250602429302L378.10735023310025,418.6532529179989L376.77855994706925,421.6566493855314L376.43124628697126,424.34267489647857L374.7132084714659,424.71456607131654L371.77791853493716,428.43819272941244L367.2945097478879,426.73276891031924L365.4853249279322,424.4755692332974L362.84702654458476,423.817620447123L361.77945827430125,422.2215356453511L358.91625137166966,421.35975855114384L357.2008484090309,420.2756417798329L354.560577693187,416.91121552306436L353.3559686293436,416.69297227373545L350.7690122339677,414.1333049337761L348.51924431053664,408.875962693289L348.81737406617236,403.9061055634162L346.4140925265436,399.7138492641326L346.4277475681273,397.46287968353204L344.91074270942045,395.08525601404074L341.478980785519,391.43495772256074L338.7191400918224,390.1733963007123L336.3841462847398,387.4798787760931L335.7844173444512,385.6148690714067L333.28915392007434,383.8187974762262L332.0078086389459,381.38964881381503L330.17518922859955,379.340782473088L326.8630029880651,377.25141231279395L324.80548260173975,372.24710722810596L322.6154028586251,371.03918249359117L321.0909446480539,369.3153859951142L321.6476753196993,366.8434645837167L325.427881638945,367.24647833179824L331.3751316740046,367.86714236521016L348.30400127079355,369.5917958413038L349.40805952301116,369.694272924805L362.3336885402464,370.85369125850957L363.02714284732997,370.9112047998584L367.06967695579226,371.24390081792467L373.30151018164304,371.7352781064935L377.41963892084226,372.04460793821215L377.54023001179496,370.4251612080865L378.1412725448681,362.277932999154L378.75074288697783,354.09362672752775L379.4720344857969,346.05446769075854L379.7925586541841,342.64259182848696L380.2246237989767,337.8743855821831L380.94502670872305,328.9182585557895L380.9591757028011,328.72769439859246L381.5813623693014,320.5691482585513L381.869613532874,316.68627369328317L382.19441970056664,312.3843477918616L382.8178058866085,304.13250779078726L382.98637241962524,301.93432902338657L383.43240595739996,295.9973621018182L384.0375756872479,287.62702497928376ZM505.2752182338888,419.5716811178803L505.34460342786616,419.5277776821098L505.2752182338888,419.5716811178803ZM509.27763501167624,425.8998848867826L508.30823856312105,426.9022408301528L503.0952238566929,430.54942954645537L507.3750676875492,426.64707175874173ZM474.935229464574,448.8634088329544L475.61186303010703,447.3912373175205L476.9520947273568,447.27680182988325L478.79361632240267,445.6231752330215L481.9891997370985,444.4646200777657L474.7599744567019,449.2278083315432ZM471.11297901780256,453.29348269555663L471.0316479269543,453.6503055378037L471.11297901780256,453.29348269555663ZM474.2167168401295,449.58783163494047L472.2537551566678,452.2904534527322L472.50054603718144,450.83772841807894ZM465.70739095309466,471.2982783427746L465.428428728714,466.50831857680413L465.75234629857675,464.85012523541616L465.94716972049685,464.2193830507444L466.0262621219337,462.45726137685256L467.91389986066434,458.60882274163475L468.33048453883225,458.7080986886744L466.1687721728503,464.2235387899229L465.6300189699992,467.50161843844927ZM469.27152997080015,455.97045723262084L470.7772023210615,454.04911535707413L468.6857780731812,457.9926545264323ZM466.50362938548534,476.8040580136053L466.576061538392,476.80605612920306L466.50362938548534,476.8040580136053ZM467.5252249245608,478.54746321225576L467.38186945474126,478.4181265735462L466.2577491577185,477.5454975728804L466.66661904303885,476.80730456400363L466.24535868440955,475.4150577229027L466.697350855153,475.88947572156536L466.99869081128236,476.8098469129231ZM466.697350855153,475.88947572156536L465.866397052524,474.49124127185337L465.51833646726016,471.4709616811869L465.70739095309466,471.2982783427746ZM467.71967038016646,480.3199329995512L467.9435366624732,480.3228433641351L467.71967038016646,480.3199329995512Z" fill="#FF00FF"/></svg>
</body>
<script type="text/javascript">
d3.selectAll('path.state')
.each(function(){
var simplifiedData = this.getPathData().map(function(d){
d.values = d.values.map(function(v){
return Math.round(v);
})
return d;
});
console.log(simplifiedData);
this.setPathData(simplifiedData);
});
</script>
</html>
// @info
// Polyfill for SVG getPathData() and setPathData() methods. Based on:
// - SVGPathSeg polyfill by Philip Rogers (MIT License)
// https://github.com/progers/pathseg
// - SVGPathNormalizer by Tadahisa Motooka (MIT License)
// https://github.com/motooka/SVGPathNormalizer/tree/master/src
// - arcToCubicCurves() by Dmitry Baranovskiy (MIT License)
// https://github.com/DmitryBaranovskiy/raphael/blob/v2.1.1/raphael.core.js#L1837
// @author
// Jarosław Foksa
// @license
// MIT License
if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathData) {
(function() {
var commandsMap = {
"Z":"Z", "M":"M", "L":"L", "C":"C", "Q":"Q", "A":"A", "H":"H", "V":"V", "S":"S", "T":"T",
"z":"Z", "m":"m", "l":"l", "c":"c", "q":"q", "a":"a", "h":"h", "v":"v", "s":"s", "t":"t"
};
var Source = function(string) {
this._string = string;
this._currentIndex = 0;
this._endIndex = this._string.length;
this._prevCommand = null;
this._skipOptionalSpaces();
};
var isIE = window.navigator.userAgent.indexOf("MSIE ") !== -1;
Source.prototype = {
parseSegment: function() {
var char = this._string[this._currentIndex];
var command = commandsMap[char] ? commandsMap[char] : null;
if (command === null) {
// Possibly an implicit command. Not allowed if this is the first command.
if (this._prevCommand === null) {
return null;
}
// Check for remaining coordinates in the current command.
if (
(char === "+" || char === "-" || char === "." || (char >= "0" && char <= "9")) && this._prevCommand !== "Z"
) {
if (this._prevCommand === "M") {
command = "L";
}
else if (this._prevCommand === "m") {
command = "l";
}
else {
command = this._prevCommand;
}
}
else {
command = null;
}
if (command === null) {
return null;
}
}
else {
this._currentIndex += 1;
}
this._prevCommand = command;
var values = null;
var cmd = command.toUpperCase();
if (cmd === "H" || cmd === "V") {
values = [this._parseNumber()];
}
else if (cmd === "M" || cmd === "L" || cmd === "T") {
values = [this._parseNumber(), this._parseNumber()];
}
else if (cmd === "S" || cmd === "Q") {
values = [this._parseNumber(), this._parseNumber(), this._parseNumber(), this._parseNumber()];
}
else if (cmd === "C") {
values = [
this._parseNumber(),
this._parseNumber(),
this._parseNumber(),
this._parseNumber(),
this._parseNumber(),
this._parseNumber()
];
}
else if (cmd === "A") {
values = [
this._parseNumber(),
this._parseNumber(),
this._parseNumber(),
this._parseArcFlag(),
this._parseArcFlag(),
this._parseNumber(),
this._parseNumber()
];
}
else if (cmd === "Z") {
this._skipOptionalSpaces();
values = [];
}
if (values === null || values.indexOf(null) >= 0) {
// Unknown command or known command with invalid values
return null;
}
else {
return {type: command, values: values};
}
},
hasMoreData: function() {
return this._currentIndex < this._endIndex;
},
peekSegmentType: function() {
var char = this._string[this._currentIndex];
return commandsMap[char] ? commandsMap[char] : null;
},
initialCommandIsMoveTo: function() {
// If the path is empty it is still valid, so return true.
if (!this.hasMoreData()) {
return true;
}
var command = this.peekSegmentType();
// Path must start with moveTo.
return command === "M" || command === "m";
},
_isCurrentSpace: function() {
var char = this._string[this._currentIndex];
return char <= " " && (char === " " || char === "\n" || char === "\t" || char === "\r" || char === "\f");
},
_skipOptionalSpaces: function() {
while (this._currentIndex < this._endIndex && this._isCurrentSpace()) {
this._currentIndex += 1;
}
return this._currentIndex < this._endIndex;
},
_skipOptionalSpacesOrDelimiter: function() {
if (
this._currentIndex < this._endIndex &&
!this._isCurrentSpace() &&
this._string[this._currentIndex] !== ","
) {
return false;
}
if (this._skipOptionalSpaces()) {
if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === ",") {
this._currentIndex += 1;
this._skipOptionalSpaces();
}
}
return this._currentIndex < this._endIndex;
},
// Parse a number from an SVG path. This very closely follows genericParseNumber(...) from
// Source/core/svg/SVGParserUtilities.cpp.
// Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF
_parseNumber: function() {
var exponent = 0;
var integer = 0;
var frac = 1;
var decimal = 0;
var sign = 1;
var expsign = 1;
var startIndex = this._currentIndex;
this._skipOptionalSpaces();
// Read the sign.
if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === "+") {
this._currentIndex += 1;
}
else if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === "-") {
this._currentIndex += 1;
sign = -1;
}
if (
this._currentIndex === this._endIndex ||
(
(this._string[this._currentIndex] < "0" || this._string[this._currentIndex] > "9") &&
this._string[this._currentIndex] !== "."
)
) {
// The first character of a number must be one of [0-9+-.].
return null;
}
// Read the integer part, build right-to-left.
var startIntPartIndex = this._currentIndex;
while (
this._currentIndex < this._endIndex &&
this._string[this._currentIndex] >= "0" &&
this._string[this._currentIndex] <= "9"
) {
this._currentIndex += 1; // Advance to first non-digit.
}
if (this._currentIndex !== startIntPartIndex) {
var scanIntPartIndex = this._currentIndex - 1;
var multiplier = 1;
while (scanIntPartIndex >= startIntPartIndex) {
integer += multiplier * (this._string[scanIntPartIndex] - "0");
scanIntPartIndex -= 1;
multiplier *= 10;
}
}
// Read the decimals.
if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === ".") {
this._currentIndex += 1;
// There must be a least one digit following the .
if (
this._currentIndex >= this._endIndex ||
this._string[this._currentIndex] < "0" ||
this._string[this._currentIndex] > "9"
) {
return null;
}
while (
this._currentIndex < this._endIndex &&
this._string[this._currentIndex] >= "0" &&
this._string[this._currentIndex] <= "9"
) {
frac *= 10;
decimal += (this._string.charAt(this._currentIndex) - "0") / frac;
this._currentIndex += 1;
}
}
// Read the exponent part.
if (
this._currentIndex !== startIndex &&
this._currentIndex + 1 < this._endIndex &&
(this._string[this._currentIndex] === "e" || this._string[this._currentIndex] === "E") &&
(this._string[this._currentIndex + 1] !== "x" && this._string[this._currentIndex + 1] !== "m")
) {
this._currentIndex += 1;
// Read the sign of the exponent.
if (this._string[this._currentIndex] === "+") {
this._currentIndex += 1;
}
else if (this._string[this._currentIndex] === "-") {
this._currentIndex += 1;
expsign = -1;
}
// There must be an exponent.
if (
this._currentIndex >= this._endIndex ||
this._string[this._currentIndex] < "0" ||
this._string[this._currentIndex] > "9"
) {
return null;
}
while (
this._currentIndex < this._endIndex &&
this._string[this._currentIndex] >= "0" &&
this._string[this._currentIndex] <= "9"
) {
exponent *= 10;
exponent += (this._string[this._currentIndex] - "0");
this._currentIndex += 1;
}
}
var number = integer + decimal;
number *= sign;
if (exponent) {
number *= Math.pow(10, expsign * exponent);
}
if (startIndex === this._currentIndex) {
return null;
}
this._skipOptionalSpacesOrDelimiter();
return number;
},
_parseArcFlag: function() {
if (this._currentIndex >= this._endIndex) {
return null;
}
var flag = null;
var flagChar = this._string[this._currentIndex];
this._currentIndex += 1;
if (flagChar === "0") {
flag = 0;
}
else if (flagChar === "1") {
flag = 1;
}
else {
return null;
}
this._skipOptionalSpacesOrDelimiter();
return flag;
}
};
var parsePathDataString = function(string) {
if (!string || string.length === 0) return [];
var source = new Source(string);
var pathData = [];
if (source.initialCommandIsMoveTo()) {
while (source.hasMoreData()) {
var pathSeg = source.parseSegment();
if (pathSeg === null) {
break;
}
else {
pathData.push(pathSeg);
}
}
}
return pathData;
}
var setAttribute = SVGPathElement.prototype.setAttribute;
var removeAttribute = SVGPathElement.prototype.removeAttribute;
var $cachedPathData = window.Symbol ? Symbol() : "__cachedPathData";
var $cachedNormalizedPathData = window.Symbol ? Symbol() : "__cachedNormalizedPathData";
// @info
// Get an array of corresponding cubic bezier curve parameters for given arc curve paramters.
var arcToCubicCurves = function(x1, y1, x2, y2, r1, r2, angle, largeArcFlag, sweepFlag, _recursive) {
var degToRad = function(degrees) {
return (Math.PI * degrees) / 180;
};
var rotate = function(x, y, angleRad) {
var X = x * Math.cos(angleRad) - y * Math.sin(angleRad);
var Y = x * Math.sin(angleRad) + y * Math.cos(angleRad);
return {x: X, y: Y};
};
var angleRad = degToRad(angle);
var params = [];
var f1, f2, cx, cy;
if (_recursive) {
f1 = _recursive[0];
f2 = _recursive[1];
cx = _recursive[2];
cy = _recursive[3];
}
else {
var p1 = rotate(x1, y1, -angleRad);
x1 = p1.x;
y1 = p1.y;
var p2 = rotate(x2, y2, -angleRad);
x2 = p2.x;
y2 = p2.y;
var x = (x1 - x2) / 2;
var y = (y1 - y2) / 2;
var h = (x * x) / (r1 * r1) + (y * y) / (r2 * r2);
if (h > 1) {
h = Math.sqrt(h);
r1 = h * r1;
r2 = h * r2;
}
var sign;
if (largeArcFlag === sweepFlag) {
sign = -1;
}
else {
sign = 1;
}
var r1Pow = r1 * r1;
var r2Pow = r2 * r2;
var left = r1Pow * r2Pow - r1Pow * y * y - r2Pow * x * x;
var right = r1Pow * y * y + r2Pow * x * x;
var k = sign * Math.sqrt(Math.abs(left/right));
cx = k * r1 * y / r2 + (x1 + x2) / 2;
cy = k * -r2 * x / r1 + (y1 + y2) / 2;
f1 = Math.asin(((y1 - cy) / r2).toFixed(9));
f2 = Math.asin(((y2 - cy) / r2).toFixed(9));
if (x1 < cx) {
f1 = Math.PI - f1;
}
if (x2 < cx) {
f2 = Math.PI - f2;
}
if (f1 < 0) {
f1 = Math.PI * 2 + f1;
}
if (f2 < 0) {
f2 = Math.PI * 2 + f2;
}
if (sweepFlag && f1 > f2) {
f1 = f1 - Math.PI * 2;
}
if (!sweepFlag && f2 > f1) {
f2 = f2 - Math.PI * 2;
}
}
var df = f2 - f1;
if (Math.abs(df) > (Math.PI * 120 / 180)) {
var f2old = f2;
var x2old = x2;
var y2old = y2;
if (sweepFlag && f2 > f1) {
f2 = f1 + (Math.PI * 120 / 180) * (1);
}
else {
f2 = f1 + (Math.PI * 120 / 180) * (-1);
}
x2 = cx + r1 * Math.cos(f2);
y2 = cy + r2 * Math.sin(f2);
params = arcToCubicCurves(x2, y2, x2old, y2old, r1, r2, angle, 0, sweepFlag, [f2, f2old, cx, cy]);
}
df = f2 - f1;
var c1 = Math.cos(f1);
var s1 = Math.sin(f1);
var c2 = Math.cos(f2);
var s2 = Math.sin(f2);
var t = Math.tan(df / 4);
var hx = 4 / 3 * r1 * t;
var hy = 4 / 3 * r2 * t;
var m1 = [x1, y1];
var m2 = [x1 + hx * s1, y1 - hy * c1];
var m3 = [x2 + hx * s2, y2 - hy * c2];
var m4 = [x2, y2];
m2[0] = 2 * m1[0] - m2[0];
m2[1] = 2 * m1[1] - m2[1];
if (_recursive) {
return [m2, m3, m4].concat(params);
}
else {
params = [m2, m3, m4].concat(params).join().split(",");
var curves = [];
var curveParams = [];
params.forEach( function(param, i) {
if (i % 2) {
curveParams.push(rotate(params[i - 1], params[i], angleRad).y);
}
else {
curveParams.push(rotate(params[i], params[i + 1], angleRad).x);
}
if (curveParams.length === 6) {
curves.push(curveParams);
curveParams = [];
}
});
return curves;
}
};
var clonePathData = function(pathData) {
return pathData.map( function(seg) {
return {type: seg.type, values: Array.prototype.slice.call(seg.values)}
});
};
// @info
// Takes any path data, returns path data that consists only from absolute commands.
var absolutizePathData = function(pathData) {
var absolutizedPathData = [];
var currentX = null;
var currentY = null;
var subpathX = null;
var subpathY = null;
pathData.forEach( function(seg) {
var type = seg.type;
if (type === "M") {
var x = seg.values[0];
var y = seg.values[1];
absolutizedPathData.push({type: "M", values: [x, y]});
subpathX = x;
subpathY = y;
currentX = x;
currentY = y;
}
else if (type === "m") {
var x = currentX + seg.values[0];
var y = currentY + seg.values[1];
absolutizedPathData.push({type: "M", values: [x, y]});
subpathX = x;
subpathY = y;
currentX = x;
currentY = y;
}
else if (type === "L") {
var x = seg.values[0];
var y = seg.values[1];
absolutizedPathData.push({type: "L", values: [x, y]});
currentX = x;
currentY = y;
}
else if (type === "l") {
var x = currentX + seg.values[0];
var y = currentY + seg.values[1];
absolutizedPathData.push({type: "L", values: [x, y]});
currentX = x;
currentY = y;
}
else if (type === "C") {
var x1 = seg.values[0];
var y1 = seg.values[1];
var x2 = seg.values[2];
var y2 = seg.values[3];
var x = seg.values[4];
var y = seg.values[5];
absolutizedPathData.push({type: "C", values: [x1, y1, x2, y2, x, y]});
currentX = x;
currentY = y;
}
else if (type === "c") {
var x1 = currentX + seg.values[0];
var y1 = currentY + seg.values[1];
var x2 = currentX + seg.values[2];
var y2 = currentY + seg.values[3];
var x = currentX + seg.values[4];
var y = currentY + seg.values[5];
absolutizedPathData.push({type: "C", values: [x1, y1, x2, y2, x, y]});
currentX = x;
currentY = y;
}
else if (type === "Q") {
var x1 = seg.values[0];
var y1 = seg.values[1];
var x = seg.values[2];
var y = seg.values[3];
absolutizedPathData.push({type: "Q", values: [x1, y1, x, y]});
currentX = x;
currentY = y;
}
else if (type === "q") {
var x1 = currentX + seg.values[0];
var y1 = currentY + seg.values[1];
var x = currentX + seg.values[2];
var y = currentY + seg.values[3];
absolutizedPathData.push({type: "Q", values: [x1, y1, x, y]});
currentX = x;
currentY = y;
}
else if (type === "A") {
var x = seg.values[5];
var y = seg.values[6];
absolutizedPathData.push({
type: "A",
values: [seg.values[0], seg.values[1], seg.values[2], seg.values[3], seg.values[4], x, y]
});
currentX = x;
currentY = y;
}
else if (type === "a") {
var x = currentX + seg.values[5];
var y = currentY + seg.values[6];
absolutizedPathData.push({
type: "A",
values: [seg.values[0], seg.values[1], seg.values[2], seg.values[3], seg.values[4], x, y]
});
currentX = x;
currentY = y;
}
else if (type === "H") {
var x = seg.values[0];
absolutizedPathData.push({type: "H", values: [x]});
currentX = x;
}
else if (type === "h") {
var x = currentX + seg.values[0];
absolutizedPathData.push({type: "H", values: [x]});
currentX = x;
}
else if (type === "V") {
var y = seg.values[0];
absolutizedPathData.push({type: "V", values: [y]});
currentY = y;
}
else if (type === "v") {
var y = currentY + seg.values[0];
absolutizedPathData.push({type: "V", values: [y]});
currentY = y;
}
else if (type === "S") {
var x2 = seg.values[0];
var y2 = seg.values[1];
var x = seg.values[2];
var y = seg.values[3];
absolutizedPathData.push({type: "S", values: [x2, y2, x, y]});
currentX = x;
currentY = y;
}
else if (type === "s") {
var x2 = currentX + seg.values[0];
var y2 = currentY + seg.values[1];
var x = currentX + seg.values[2];
var y = currentY + seg.values[3];
absolutizedPathData.push({type: "S", values: [x2, y2, x, y]});
currentX = x;
currentY = y;
}
else if (type === "T") {
var x = seg.values[0];
var y = seg.values[1]
absolutizedPathData.push({type: "T", values: [x, y]});
currentX = x;
currentY = y;
}
else if (type === "t") {
var x = currentX + seg.values[0];
var y = currentY + seg.values[1]
absolutizedPathData.push({type: "T", values: [x, y]});
currentX = x;
currentY = y;
}
else if (type === "Z" || type === "z") {
absolutizedPathData.push({type: "Z", values: []});
currentX = subpathX;
currentY = subpathY;
}
});
return absolutizedPathData;
};
// @info
// Takes path data that consists only from absolute commands, returns path data that consists only from
// "M", "L", "C" and "Z" commands.
var reducePathData = function(pathData) {
var reducedPathData = [];
var lastType = null;
var lastControlX = null;
var lastControlY = null;
var currentX = null;
var currentY = null;
var subpathX = null;
var subpathY = null;
pathData.forEach( function(seg) {
if (seg.type === "M") {
var x = seg.values[0];
var y = seg.values[1];
reducedPathData.push({type: "M", values: [x, y]});
subpathX = x;
subpathY = y;
currentX = x;
currentY = y;
}
else if (seg.type === "C") {
var x1 = seg.values[0];
var y1 = seg.values[1];
var x2 = seg.values[2];
var y2 = seg.values[3];
var x = seg.values[4];
var y = seg.values[5];
reducedPathData.push({type: "C", values: [x1, y1, x2, y2, x, y]});
lastControlX = x2;
lastControlY = y2;
currentX = x;
currentY = y;
}
else if (seg.type === "L") {
var x = seg.values[0];
var y = seg.values[1];
reducedPathData.push({type: "L", values: [x, y]});
currentX = x;
currentY = y;
}
else if (seg.type === "H") {
var x = seg.values[0];
reducedPathData.push({type: "L", values: [x, currentY]});
currentX = x;
}
else if (seg.type === "V") {
var y = seg.values[0];
reducedPathData.push({type: "L", values: [currentX, y]});
currentY = y;
}
else if (seg.type === "S") {
var x2 = seg.values[0];
var y2 = seg.values[1];
var x = seg.values[2];
var y = seg.values[3];
var cx1, cy1;
if (lastType === "C" || lastType === "S") {
cx1 = currentX + (currentX - lastControlX);
cy1 = currentY + (currentY - lastControlY);
}
else {
cx1 = currentX;
cy1 = currentY;
}
reducedPathData.push({type: "C", values: [cx1, cy1, x2, y2, x, y]});
lastControlX = x2;
lastControlY = y2;
currentX = x;
currentY = y;
}
else if (seg.type === "T") {
var x = seg.values[0];
var y = seg.values[1];
var x1, y1;
if (lastType === "Q" || lastType === "T") {
x1 = currentX + (currentX - lastControlX);
y1 = currentY + (currentY - lastControlY);
}
else {
x1 = currentX;
y1 = currentY;
}
var cx1 = currentX + 2 * (x1 - currentX) / 3;
var cy1 = currentY + 2 * (y1 - currentY) / 3;
var cx2 = x + 2 * (x1 - x) / 3;
var cy2 = y + 2 * (y1 - y) / 3;
reducedPathData.push({type: "C", values: [cx1, cy1, cx2, cy2, x, y]});
lastControlX = x1;
lastControlY = y1;
currentX = x;
currentY = y;
}
else if (seg.type === "Q") {
var x1 = seg.values[0];
var y1 = seg.values[1];
var x = seg.values[2];
var y = seg.values[3];
var cx1 = currentX + 2 * (x1 - currentX) / 3;
var cy1 = currentY + 2 * (y1 - currentY) / 3;
var cx2 = x + 2 * (x1 - x) / 3;
var cy2 = y + 2 * (y1 - y) / 3;
reducedPathData.push({type: "C", values: [cx1, cy1, cx2, cy2, x, y]});
lastControlX = x1;
lastControlY = y1;
currentX = x;
currentY = y;
}
else if (seg.type === "A") {
var r1 = seg.values[0];
var r2 = seg.values[1];
var angle = seg.values[2];
var largeArcFlag = seg.values[3];
var sweepFlag = seg.values[4];
var x = seg.values[5];
var y = seg.values[6];
if (r1 === 0 || r2 === 0) {
reducedPathData.push({type: "C", values: [currentX, currentY, x, y, x, y]});
currentX = x;
currentY = y;
}
else {
if (currentX !== x || currentY !== y) {
var curves = arcToCubicCurves(currentX, currentY, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
curves.forEach( function(curve) {
reducedPathData.push({type: "C", values: curve});
currentX = x;
currentY = y;
});
}
}
}
else if (seg.type === "Z") {
reducedPathData.push(seg);
currentX = subpathX;
currentY = subpathY;
}
lastType = seg.type;
});
return reducedPathData;
};
SVGPathElement.prototype.setAttribute = function(name, value) {
if (name === "d") {
this[$cachedPathData] = null;
this[$cachedNormalizedPathData] = null;
}
setAttribute.call(this, name, value);
};
SVGPathElement.prototype.removeAttribute = function(name, value) {
if (name === "d") {
this[$cachedPathData] = null;
this[$cachedNormalizedPathData] = null;
}
removeAttribute.call(this, name);
};
SVGPathElement.prototype.getPathData = function(options) {
if (options && options.normalize) {
if (this[$cachedNormalizedPathData]) {
return clonePathData(this[$cachedNormalizedPathData]);
}
else {
var pathData;
if (this[$cachedPathData]) {
pathData = clonePathData(this[$cachedPathData]);
}
else {
pathData = parsePathDataString(this.getAttribute("d") || "");
this[$cachedPathData] = clonePathData(pathData);
}
var normalizedPathData = reducePathData(absolutizePathData(pathData));
this[$cachedNormalizedPathData] = clonePathData(normalizedPathData);
return normalizedPathData;
}
}
else {
if (this[$cachedPathData]) {
return clonePathData(this[$cachedPathData]);
}
else {
var pathData = parsePathDataString(this.getAttribute("d") || "");
this[$cachedPathData] = clonePathData(pathData);
return pathData;
}
}
};
SVGPathElement.prototype.setPathData = function(pathData) {
if (pathData.length === 0) {
if (isIE) {
// @bugfix https://github.com/mbostock/d3/issues/1737
this.setAttribute("d", "");
}
else {
this.removeAttribute("d");
}
}
else {
var d = "";
for (var i = 0, l = pathData.length; i < l; i += 1) {
var seg = pathData[i];
if (i > 0) {
d += " ";
}
d += seg.type;
if (seg.values && seg.values.length > 0) {
d += " " + seg.values.join(" ");
}
}
this.setAttribute("d", d);
}
};
SVGRectElement.prototype.getPathData = function(options) {
var x = this.x.baseVal.value;
var y = this.y.baseVal.value;
var width = this.width.baseVal.value;
var height = this.height.baseVal.value;
var rx = this.hasAttribute("rx") ? this.rx.baseVal.value : this.ry.baseVal.value;
var ry = this.hasAttribute("ry") ? this.ry.baseVal.value : this.rx.baseVal.value;
if (rx > width / 2) {
rx = width / 2;
}
if (ry > height / 2) {
ry = height / 2;
}
var pathData = [
{type: "M", values: [x+rx, y]},
{type: "H", values: [x+width-rx]},
{type: "A", values: [rx, ry, 0, 0, 1, x+width, y+ry]},
{type: "V", values: [y+height-ry]},
{type: "A", values: [rx, ry, 0, 0, 1, x+width-rx, y+height]},
{type: "H", values: [x+rx]},
{type: "A", values: [rx, ry, 0, 0, 1, x, y+height-ry]},
{type: "V", values: [y+ry]},
{type: "A", values: [rx, ry, 0, 0, 1, x+rx, y]},
{type: "Z", values: []}
];
// Get rid of redundant "A" segs when either rx or ry is 0
pathData = pathData.filter(function(s) {
return s.type === "A" && (s.values[0] === 0 || s.values[1] === 0) ? false : true;
});
if (options && options.normalize === true) {
pathData = reducePathData(pathData);
}
return pathData;
};
SVGCircleElement.prototype.getPathData = function(options) {
var cx = this.cx.baseVal.value;
var cy = this.cy.baseVal.value;
var r = this.r.baseVal.value;
var pathData = [
{ type: "M", values: [cx + r, cy] },
{ type: "A", values: [r, r, 0, 0, 1, cx, cy+r] },
{ type: "A", values: [r, r, 0, 0, 1, cx-r, cy] },
{ type: "A", values: [r, r, 0, 0, 1, cx, cy-r] },
{ type: "A", values: [r, r, 0, 0, 1, cx+r, cy] },
{ type: "Z", values: [] }
];
if (options && options.normalize === true) {
pathData = reducePathData(pathData);
}
return pathData;
};
SVGEllipseElement.prototype.getPathData = function(options) {
var cx = this.cx.baseVal.value;
var cy = this.cy.baseVal.value;
var rx = this.rx.baseVal.value;
var ry = this.ry.baseVal.value;
var pathData = [
{ type: "M", values: [cx + rx, cy] },
{ type: "A", values: [rx, ry, 0, 0, 1, cx, cy+ry] },
{ type: "A", values: [rx, ry, 0, 0, 1, cx-rx, cy] },
{ type: "A", values: [rx, ry, 0, 0, 1, cx, cy-ry] },
{ type: "A", values: [rx, ry, 0, 0, 1, cx+rx, cy] },
{ type: "Z", values: [] }
];
if (options && options.normalize === true) {
pathData = reducePathData(pathData);
}
return pathData;
};
SVGLineElement.prototype.getPathData = function() {
return [
{ type: "M", values: [this.x1.baseVal.value, this.y1.baseVal.value] },
{ type: "L", values: [this.x2.baseVal.value, this.y2.baseVal.value] }
];
};
SVGPolylineElement.prototype.getPathData = function() {
var pathData = [];
for (var i = 0; i < this.points.numberOfItems; i += 1) {
var point = this.points.getItem(i);
pathData.push({
type: (i === 0 ? "M" : "L"),
values: [point.x, point.y]
});
}
return pathData;
};
SVGPolygonElement.prototype.getPathData = function() {
var pathData = [];
for (var i = 0; i < this.points.numberOfItems; i += 1) {
var point = this.points.getItem(i);
pathData.push({
type: (i === 0 ? "M" : "L"),
values: [point.x, point.y]
});
}
pathData.push({
type: "Z",
values: []
});
return pathData;
};
})();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment