vega-label
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -290,2 +290,7 @@ (function (global, factory) { | ||
} | ||
function _outOfBounds() { | ||
return false; | ||
} | ||
function collision($, x, y, textHeight, textWidth, h, bm0, bm1) { | ||
@@ -300,5 +305,23 @@ const w = textWidth * h / (textHeight * 2), | ||
function placeAreaLabelReducedSearch ($, bitmaps, avoidBaseMark, markIndex) { | ||
function _collision($, x, y, textHeight, textWidth, h, bm0, bm1) { | ||
const w = textWidth * h / (textHeight * 2); | ||
let x1 = $(x - w), | ||
x2 = $(x + w), | ||
y1 = $(y - (h = h / 2)), | ||
y2 = $(y + h); | ||
x1 = x1 > 0 ? x1 : 0; | ||
y1 = y1 > 0 ? y1 : 0; | ||
x2 = x2 < $.width ? x2 : $.width - 1; | ||
y2 = y2 < $.height ? y2 : $.height - 1; | ||
return bm0.getRange(x1, y1, x2, y2) || bm1 && bm1.getRange(x1, y1, x2, y2); | ||
} | ||
function getTests(infPadding) { | ||
return infPadding ? [_collision, _outOfBounds] : [collision, outOfBounds]; | ||
} | ||
function placeAreaLabelReducedSearch ($, bitmaps, avoidBaseMark, markIndex, infPadding) { | ||
const width = $.width, | ||
height = $.height, | ||
[collision, outOfBounds] = getTests(infPadding), | ||
bm0 = bitmaps[0], | ||
@@ -447,5 +470,6 @@ // where labels have been placed | ||
const Y_DIR = [-1, 1, -1, 1]; | ||
function placeAreaLabelFloodFill ($, bitmaps, avoidBaseMark, markIndex) { | ||
function placeAreaLabelFloodFill ($, bitmaps, avoidBaseMark, markIndex, infPadding) { | ||
const width = $.width, | ||
height = $.height, | ||
[collision, outOfBounds] = getTests(infPadding), | ||
bm0 = bitmaps[0], | ||
@@ -571,3 +595,3 @@ // where labels have been placed | ||
Baselines = ['bottom', 'middle', 'top']; | ||
function placeMarkLabel ($, bitmaps, anchors, offsets) { | ||
function placeMarkLabel ($, bitmaps, anchors, offsets, infPadding) { | ||
const width = $.width, | ||
@@ -582,3 +606,3 @@ height = $.height, | ||
if (boundary[2] < 0 || boundary[5] < 0 || boundary[0] > width || boundary[3] > height) { | ||
if (!infPadding && (boundary[2] < 0 || boundary[5] < 0 || boundary[0] > width || boundary[3] > height)) { | ||
return false; | ||
@@ -619,2 +643,8 @@ } | ||
if (infPadding) { | ||
_x1 = _x1 < 0 ? 0 : _x1; | ||
_y1 = _y1 < 0 ? 0 : _y1; | ||
_y2 = _y2 >= $.height ? $.height - 1 : _y2; | ||
} | ||
if (!textWidth) { | ||
@@ -637,2 +667,7 @@ // to avoid finding width of text label, | ||
if (infPadding) { | ||
_x1 = _x1 < 0 ? 0 : _x1; | ||
_x2 = _x2 >= $.width ? $.width - 1 : _x2; | ||
} | ||
if (test(_x1, _x2, _y1, _y2, bm0, bm1, x1, x2, y1, y2, boundary, isInside)) { | ||
@@ -694,3 +729,4 @@ // place label if the position is placeable | ||
boundary = markBoundary(marktype, grouptype, lineAnchor, markIndex), | ||
$ = scaler(size[0], size[1], padding), | ||
infPadding = padding === null || padding === Infinity, | ||
$ = scaler(size[0], size[1], infPadding ? 0 : padding), | ||
isNaiveGroupArea = isGroupArea && method === 'naive'; // prepare text mark data for placing | ||
@@ -735,3 +771,3 @@ | ||
const place = isGroupArea ? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex) : placeMarkLabel($, bitmaps, anchors, offsets); // place all labels | ||
const place = isGroupArea ? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex, infPadding) : placeMarkLabel($, bitmaps, anchors, offsets, infPadding); // place all labels | ||
@@ -812,4 +848,5 @@ data.forEach(d => d.opacity = +place(d)); | ||
* @param {Array<number>} [params.offset] - Label offsets (in pixels) from the base mark bounding box. | ||
* This parameter is parallel to the list of anchor points. | ||
* @param {number} [params.padding=0] - The amount (in pixels) that a label may exceed the layout size. | ||
* This parameter is parallel to the list of anchor points. | ||
* @param {number | null} [params.padding=0] - The amount (in pixels) that a label may exceed the layout size. | ||
* If this parameter is null, a label may exceed the layout size without any boundary. | ||
* @param {string} [params.lineAnchor='end'] - For group line mark labels only, indicates the anchor | ||
@@ -859,3 +896,4 @@ * position for labels. One of 'start' or 'end'. | ||
type: 'number', | ||
default: 0 | ||
default: 0, | ||
null: true | ||
}, { | ||
@@ -907,3 +945,3 @@ name: 'lineAnchor', | ||
labelLayout(pulse.materialize(pulse.SOURCE).source, _.size, _.sort, vegaUtil.array(_.offset || 1), vegaUtil.array(_.anchor || Anchors), _.avoidMarks || [], _.avoidBaseMark === false ? false : true, _.lineAnchor || 'end', _.markIndex || 0, _.padding || 0, _.method || 'naive').forEach(l => { | ||
labelLayout(pulse.materialize(pulse.SOURCE).source || [], _.size, _.sort, vegaUtil.array(_.offset == null ? 1 : _.offset), vegaUtil.array(_.anchor || Anchors), _.avoidMarks || [], _.avoidBaseMark !== false, _.lineAnchor || 'end', _.markIndex || 0, _.padding === undefined ? 0 : _.padding, _.method || 'naive').forEach(l => { | ||
// write layout results to data stream | ||
@@ -910,0 +948,0 @@ const t = l.datum; |
@@ -1,2 +0,2 @@ | ||
this.vega=this.vega||{},this.vega.transforms=function(t,e,n,r,a){"use strict";function i(t,n,r,a){const i=t.width,s=t.height,u=r||a,f=e.canvas(i,s).getContext("2d");n.forEach(t=>o(f,t,u));const l=new Uint32Array(f.getImageData(0,0,i,s).data.buffer),d=t.bitmap(),c=u&&t.bitmap();let m,g,h,y,p;for(g=0;g<s;++g)for(m=0;m<i;++m)p=4278190080&l[g*i+m],p&&(h=t(m),y=t(g),a||d.set(h,y),u&&268435456^p&&c.set(h,y));return[d,c]}function o(t,e,n){if(!e.length)return;const a=e[0].mark.marktype;"group"===a?e.forEach(e=>{e.items.forEach(e=>o(t,e.items,n))}):r.Marks[a].draw(t,{items:n?e.map(s):e})}function s(t){const e=n.rederive(t,{});return e.stroke&&(e.strokeOpacity=1),e.fill&&(e.fillOpacity=.0625,e.stroke="#000",e.strokeOpacity=1,e.strokeWidth=2),e}const u=31,f=new Uint32Array(33),l=new Uint32Array(33);l[0]=0,f[0]=~l[0];for(let t=1;t<=32;++t)l[t]=l[t-1]<<1|1,f[t]=~l[t];function d(t,e,n){const r=Math.max(1,Math.sqrt(t*e/1e6)),a=~~((t+2*n+r)/r),i=~~((e+2*n+r)/r),o=t=>~~((t+n)/r);return o.invert=t=>t*r-n,o.bitmap=()=>function(t,e){const n=new Uint32Array(~~((t*e+32)/32));function r(t,e){n[t]|=e}function a(t,e){n[t]&=e}return{array:n,get:(e,r)=>{const a=r*t+e;return n[a>>>5]&1<<(a&u)},set:(e,n)=>{const a=n*t+e;r(a>>>5,1<<(a&u))},clear:(e,n)=>{const r=n*t+e;a(r>>>5,~(1<<(r&u)))},getRange:(e,r,a,i)=>{let o,s,d,c,m=i;for(;m>=r;--m)if(o=m*t+e,s=m*t+a,d=o>>>5,c=s>>>5,d===c){if(n[d]&f[o&u]&l[1+(s&u)])return!0}else{if(n[d]&f[o&u])return!0;if(n[c]&l[1+(s&u)])return!0;for(let t=d+1;t<c;++t)if(n[t])return!0}return!1},setRange:(e,n,a,i)=>{let o,s,d,c,m;for(;n<=i;++n)if(o=n*t+e,s=n*t+a,d=o>>>5,c=s>>>5,d===c)r(d,f[o&u]&l[1+(s&u)]);else for(r(d,f[o&u]),r(c,l[1+(s&u)]),m=d+1;m<c;++m)r(m,4294967295)},clearRange:(e,n,r,i)=>{let o,s,d,c,m;for(;n<=i;++n)if(o=n*t+e,s=n*t+r,d=o>>>5,c=s>>>5,d===c)a(d,l[o&u]|f[1+(s&u)]);else for(a(d,l[o&u]),a(c,f[1+(s&u)]),m=d+1;m<c;++m)a(m,0)},outOfBounds:(n,r,a,i)=>n<0||r<0||i>=e||a>=t}}(a,i),o.ratio=r,o.padding=n,o.width=t,o.height=e,o}function c(t,e,n,r,a,i){let o=n/2;return t-o<0||t+o>a||e-(o=r/2)<0||e+o>i}function m(t,e,n,r,a,i,o,s){const u=a*i/(2*r),f=t(e-u),l=t(e+u),d=t(n-(i/=2)),c=t(n+i);return o.outOfBounds(f,d,l,c)||o.getRange(f,d,l,c)||s&&s.getRange(f,d,l,c)}const g=[-1,-1,1,1],h=[-1,1,-1,1];const y=["right","center","left"],p=["bottom","middle","top"];function x(t,e,n,r,a,i,o,s,u,f,l,d){return!(a.outOfBounds(t,n,e,r)||(d&&i?i.getRange(t,n,e,r)||!function(t,e,n,r,a){return a[0]<=t&&n<=a[2]&&a[3]<=e&&r<=a[5]}(o,u,s,f,l):a.getRange(t,n,e,r)))}const b={"top-left":0,top:1,"top-right":2,left:4,middle:5,right:6,"bottom-left":8,bottom:9,"bottom-right":10},v={naive:function(t,e,n,a){const i=t.width,o=t.height;return function(t){const e=t.datum.datum.items[a].items,n=e.length,s=t.datum.fontSize,u=r.textMetrics.width(t.datum,t.datum.text);let f,l,d,c,m,g,h,y=0;for(let r=0;r<n;++r)f=e[r].x,d=e[r].y,l=void 0===e[r].x2?f:e[r].x2,c=void 0===e[r].y2?d:e[r].y2,m=(f+l)/2,g=(d+c)/2,h=Math.abs(l-f+c-d),h>=y&&(y=h,t.x=m,t.y=g);return m=u/2,g=s/2,f=t.x-m,l=t.x+m,d=t.y-g,c=t.y+g,t.align="center",f<0&&l<=i?t.align="left":0<=f&&i<l&&(t.align="right"),t.baseline="middle",d<0&&c<=o?t.baseline="top":0<=d&&o<c&&(t.baseline="bottom"),!0}},"reduced-search":function(t,e,n,a){const i=t.width,o=t.height,s=e[0],u=e[1];function f(e,n,r,a,f){const l=t.invert(e),d=t.invert(n);let g,h=r,y=o;if(!c(l,d,a,f,i,o)&&!m(t,l,d,f,a,h,s,u)&&!m(t,l,d,f,a,f,s,null)){for(;y-h>=1;)g=(h+y)/2,m(t,l,d,f,a,g,s,u)?y=g:h=g;if(h>r)return[l,d,h,!0]}}return function(e){const u=e.datum.datum.items[a].items,l=u.length,d=e.datum.fontSize,g=r.textMetrics.width(e.datum,e.datum.text);let h,y,p,x,b,v,w,k,M,R,z,A,E,O,S,B,U,D=n?d:0,I=!1,N=!1,T=0;for(let r=0;r<l;++r){for(h=u[r].x,p=u[r].y,y=void 0===u[r].x2?h:u[r].x2,x=void 0===u[r].y2?p:u[r].y2,h>y&&(U=h,h=y,y=U),p>x&&(U=p,p=x,x=U),M=t(h),z=t(y),R=~~((M+z)/2),A=t(p),O=t(x),E=~~((A+O)/2),w=R;w>=M;--w)for(k=E;k>=A;--k)B=f(w,k,D,g,d),B&&([e.x,e.y,D,I]=B);for(w=R;w<=z;++w)for(k=E;k<=O;++k)B=f(w,k,D,g,d),B&&([e.x,e.y,D,I]=B);I||n||(S=Math.abs(y-h+x-p),b=(h+y)/2,v=(p+x)/2,S>=T&&!c(b,v,g,d,i,o)&&!m(t,b,v,d,g,d,s,null)&&(T=S,e.x=b,e.y=v,N=!0))}return!(!I&&!N)&&(b=g/2,v=d/2,s.setRange(t(e.x-b),t(e.y-v),t(e.x+b),t(e.y+v)),e.align="center",e.baseline="middle",!0)}},floodfill:function(t,e,n,a){const i=t.width,o=t.height,s=e[0],u=e[1],f=t.bitmap();return function(e){const l=e.datum.datum.items[a].items,d=l.length,y=e.datum.fontSize,p=r.textMetrics.width(e.datum,e.datum.text),x=[];let b,v,w,k,M,R,z,A,E,O,S,B,U=n?y:0,D=!1,I=!1,N=0;for(let r=0;r<d;++r){for(b=l[r].x,w=l[r].y,v=void 0===l[r].x2?b:l[r].x2,k=void 0===l[r].y2?w:l[r].y2,x.push([t((b+v)/2),t((w+k)/2)]);x.length;)if([z,A]=x.pop(),!(s.get(z,A)||u.get(z,A)||f.get(z,A))){f.set(z,A);for(let t=0;t<4;++t)M=z+g[t],R=A+h[t],f.outOfBounds(M,R,M,R)||x.push([M,R]);if(M=t.invert(z),R=t.invert(A),E=U,O=o,!c(M,R,p,y,i,o)&&!m(t,M,R,y,p,E,s,u)&&!m(t,M,R,y,p,y,s,null)){for(;O-E>=1;)S=(E+O)/2,m(t,M,R,y,p,S,s,u)?O=S:E=S;E>U&&(e.x=M,e.y=R,U=E,D=!0)}}D||n||(B=Math.abs(v-b+k-w),M=(b+v)/2,R=(w+k)/2,B>=N&&!c(M,R,p,y,i,o)&&!m(t,M,R,y,p,y,s,null)&&(N=B,e.x=M,e.y=R,I=!0))}return!(!D&&!I)&&(M=p/2,R=y/2,s.setRange(t(e.x-M),t(e.y-R),t(e.x+M),t(e.y+R)),e.align="center",e.baseline="middle",!0)}}};function w(t,e,n,a,o,s,u,f,l,c,m){if(!t.length)return t;const g=Math.max(a.length,o.length),h=function(t,e){const n=new Float64Array(e),r=t.length;for(let e=0;e<r;++e)n[e]=t[e]||0;for(let t=r;t<e;++t)n[t]=n[r-1];return n}(a,g),w=function(t,e){const n=new Int8Array(e),r=t.length;for(let e=0;e<r;++e)n[e]|=b[t[e]];for(let t=r;t<e;++t)n[t]=n[r-1];return n}(o,g),k=(O=t[0].datum)&&O.mark&&O.mark.marktype,M="group"===k&&t[0].datum.items[l].marktype,R="area"===M,z=function(t,e,n,r){const a=t=>[t.x,t.x,t.x,t.y,t.y,t.y];return t?"line"===t||"area"===t?t=>a(t.datum):"line"===e?t=>{const e=t.datum.items[r].items;return a(e.length?e["start"===n?0:e.length-1]:{x:NaN,y:NaN})}:t=>{const e=t.datum.bounds;return[e.x1,(e.x1+e.x2)/2,e.x2,e.y1,(e.y1+e.y2)/2,e.y2]}:a}(k,M,f,l),A=d(e[0],e[1],c),E=R&&"naive"===m;var O;const S=t.map(t=>({datum:t,opacity:0,x:void 0,y:void 0,align:void 0,baseline:void 0,boundary:z(t)}));let B;if(!E){n&&S.sort((t,e)=>n(t.datum,e.datum));let e=!1;for(let t=0;t<w.length&&!e;++t)e=5===w[t]||h[t]<0;k&&(u||R)&&(s=[t.map(t=>t.datum)].concat(s)),B=s.length?i(A,s,e,R):function(t,e){const n=t.bitmap();return(e||[]).forEach(e=>n.set(t(e.boundary[0]),t(e.boundary[3]))),[n,void 0]}(A,u&&S)}const U=R?v[m](A,B,u,l):function(t,e,n,a){const i=t.width,o=t.height,s=e[0],u=e[1],f=a.length;return function(e){const l=e.boundary,d=e.datum.fontSize;if(l[2]<0||l[5]<0||l[0]>i||l[3]>o)return!1;let c,m,g,h,b,v,w,k,M,R,z,A,E,O,S,B=0;for(let i=0;i<f;++i){if(c=(3&n[i])-1,m=(n[i]>>>2&3)-1,g=0===c&&0===m||a[i]<0,h=c&&m?Math.SQRT1_2:1,b=a[i]<0?-1:1,v=l[1+c]+a[i]*c*h,z=l[4+m]+b*d*m/2+a[i]*m*h,k=z-d/2,M=z+d/2,A=t(v),O=t(k),S=t(M),!B){if(!x(A,A,O,S,s,u,v,v,k,M,l,g))continue;B=r.textMetrics.width(e.datum,e.datum.text)}if(R=v+b*B*c/2,v=R-B/2,w=R+B/2,A=t(v),E=t(w),x(A,E,O,S,s,u,v,w,k,M,l,g))return e.x=c?c*b<0?w:v:R,e.y=m?m*b<0?M:k:z,e.align=y[c*b+1],e.baseline=p[m*b+1],s.setRange(A,O,E,S),!0}return!1}}(A,B,w,h);return S.forEach(t=>t.opacity=+U(t)),S}const k=["x","y","opacity","align","baseline"],M=["top-left","left","bottom-left","top","bottom","top-right","right","bottom-right"];function R(t){n.Transform.call(this,null,t)}return R.Definition={type:"Label",metadata:{modifies:!0},params:[{name:"size",type:"number",array:!0,length:2,required:!0},{name:"sort",type:"compare"},{name:"anchor",type:"string",array:!0,default:M},{name:"offset",type:"number",array:!0,default:[1]},{name:"padding",type:"number",default:0},{name:"lineAnchor",type:"string",values:["start","end"],default:"end"},{name:"markIndex",type:"number",default:0},{name:"avoidBaseMark",type:"boolean",default:!0},{name:"avoidMarks",type:"data",array:!0},{name:"method",type:"string",default:"naive"},{name:"as",type:"string",array:!0,length:k.length,default:k}]},a.inherits(R,n.Transform,{transform(t,e){const n=t.modified();if(!(n||e.changed(e.ADD_REM)||function(n){const r=t[n];return a.isFunction(r)&&e.modified(r.fields)}("sort")))return;t.size&&2===t.size.length||a.error("Size parameter should be specified as a [width, height] array.");const r=t.as||k;return w(e.materialize(e.SOURCE).source,t.size,t.sort,a.array(t.offset||1),a.array(t.anchor||M),t.avoidMarks||[],!1!==t.avoidBaseMark,t.lineAnchor||"end",t.markIndex||0,t.padding||0,t.method||"naive").forEach(t=>{const e=t.datum;e[r[0]]=t.x,e[r[1]]=t.y,e[r[2]]=t.opacity,e[r[3]]=t.align,e[r[4]]=t.baseline}),e.reflow(n).modifies(r)}}),t.label=R,t}({},vega,vega,vega,vega); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("vega-canvas"),require("vega-dataflow"),require("vega-scenegraph"),require("vega-util")):"function"==typeof define&&define.amd?define(["exports","vega-canvas","vega-dataflow","vega-scenegraph","vega-util"],e):e(((t="undefined"!=typeof globalThis?globalThis:t||self).vega=t.vega||{},t.vega.transforms={}),t.vega,t.vega,t.vega,t.vega)}(this,(function(t,e,n,r,a){"use strict";function i(t,n,r,a){const i=t.width,u=t.height,s=r||a,f=e.canvas(i,u).getContext("2d");n.forEach((t=>o(f,t,s)));const l=new Uint32Array(f.getImageData(0,0,i,u).data.buffer),d=t.bitmap(),c=s&&t.bitmap();let m,g,h,y,p;for(g=0;g<u;++g)for(m=0;m<i;++m)p=4278190080&l[g*i+m],p&&(h=t(m),y=t(g),a||d.set(h,y),s&&268435456^p&&c.set(h,y));return[d,c]}function o(t,e,n){if(!e.length)return;const a=e[0].mark.marktype;"group"===a?e.forEach((e=>{e.items.forEach((e=>o(t,e.items,n)))})):r.Marks[a].draw(t,{items:n?e.map(u):e})}function u(t){const e=n.rederive(t,{});return e.stroke&&(e.strokeOpacity=1),e.fill&&(e.fillOpacity=.0625,e.stroke="#000",e.strokeOpacity=1,e.strokeWidth=2),e}const s=31,f=new Uint32Array(33),l=new Uint32Array(33);l[0]=0,f[0]=~l[0];for(let t=1;t<=32;++t)l[t]=l[t-1]<<1|1,f[t]=~l[t];function d(t,e,n){const r=Math.max(1,Math.sqrt(t*e/1e6)),a=~~((t+2*n+r)/r),i=~~((e+2*n+r)/r),o=t=>~~((t+n)/r);return o.invert=t=>t*r-n,o.bitmap=()=>function(t,e){const n=new Uint32Array(~~((t*e+32)/32));function r(t,e){n[t]|=e}function a(t,e){n[t]&=e}return{array:n,get:(e,r)=>{const a=r*t+e;return n[a>>>5]&1<<(a&s)},set:(e,n)=>{const a=n*t+e;r(a>>>5,1<<(a&s))},clear:(e,n)=>{const r=n*t+e;a(r>>>5,~(1<<(r&s)))},getRange:(e,r,a,i)=>{let o,u,d,c,m=i;for(;m>=r;--m)if(o=m*t+e,u=m*t+a,d=o>>>5,c=u>>>5,d===c){if(n[d]&f[o&s]&l[1+(u&s)])return!0}else{if(n[d]&f[o&s])return!0;if(n[c]&l[1+(u&s)])return!0;for(let t=d+1;t<c;++t)if(n[t])return!0}return!1},setRange:(e,n,a,i)=>{let o,u,d,c,m;for(;n<=i;++n)if(o=n*t+e,u=n*t+a,d=o>>>5,c=u>>>5,d===c)r(d,f[o&s]&l[1+(u&s)]);else for(r(d,f[o&s]),r(c,l[1+(u&s)]),m=d+1;m<c;++m)r(m,4294967295)},clearRange:(e,n,r,i)=>{let o,u,d,c,m;for(;n<=i;++n)if(o=n*t+e,u=n*t+r,d=o>>>5,c=u>>>5,d===c)a(d,l[o&s]|f[1+(u&s)]);else for(a(d,l[o&s]),a(c,f[1+(u&s)]),m=d+1;m<c;++m)a(m,0)},outOfBounds:(n,r,a,i)=>n<0||r<0||i>=e||a>=t}}(a,i),o.ratio=r,o.padding=n,o.width=t,o.height=e,o}function c(t,e,n,r,a,i){let o=n/2;return t-o<0||t+o>a||e-(o=r/2)<0||e+o>i}function m(){return!1}function g(t,e,n,r,a,i,o,u){const s=a*i/(2*r),f=t(e-s),l=t(e+s),d=t(n-(i/=2)),c=t(n+i);return o.outOfBounds(f,d,l,c)||o.getRange(f,d,l,c)||u&&u.getRange(f,d,l,c)}function h(t,e,n,r,a,i,o,u){const s=a*i/(2*r);let f=t(e-s),l=t(e+s),d=t(n-(i/=2)),c=t(n+i);return f=f>0?f:0,d=d>0?d:0,l=l<t.width?l:t.width-1,c=c<t.height?c:t.height-1,o.getRange(f,d,l,c)||u&&u.getRange(f,d,l,c)}function y(t){return t?[h,m]:[g,c]}const p=[-1,-1,1,1],x=[-1,1,-1,1];const v=["right","center","left"],b=["bottom","middle","top"];function w(t,e,n,r,a,i,o,u,s,f,l,d){return!(a.outOfBounds(t,n,e,r)||(d&&i?i.getRange(t,n,e,r)||!function(t,e,n,r,a){return a[0]<=t&&n<=a[2]&&a[3]<=e&&r<=a[5]}(o,s,u,f,l):a.getRange(t,n,e,r)))}const k={"top-left":0,top:1,"top-right":2,left:4,middle:5,right:6,"bottom-left":8,bottom:9,"bottom-right":10},M={naive:function(t,e,n,a){const i=t.width,o=t.height;return function(t){const e=t.datum.datum.items[a].items,n=e.length,u=t.datum.fontSize,s=r.textMetrics.width(t.datum,t.datum.text);let f,l,d,c,m,g,h,y=0;for(let r=0;r<n;++r)f=e[r].x,d=e[r].y,l=void 0===e[r].x2?f:e[r].x2,c=void 0===e[r].y2?d:e[r].y2,m=(f+l)/2,g=(d+c)/2,h=Math.abs(l-f+c-d),h>=y&&(y=h,t.x=m,t.y=g);return m=s/2,g=u/2,f=t.x-m,l=t.x+m,d=t.y-g,c=t.y+g,t.align="center",f<0&&l<=i?t.align="left":0<=f&&i<l&&(t.align="right"),t.baseline="middle",d<0&&c<=o?t.baseline="top":0<=d&&o<c&&(t.baseline="bottom"),!0}},"reduced-search":function(t,e,n,a,i){const o=t.width,u=t.height,[s,f]=y(i),l=e[0],d=e[1];function c(e,n,r,a,i){const c=t.invert(e),m=t.invert(n);let g,h=r,y=u;if(!f(c,m,a,i,o,u)&&!s(t,c,m,i,a,h,l,d)&&!s(t,c,m,i,a,i,l,null)){for(;y-h>=1;)g=(h+y)/2,s(t,c,m,i,a,g,l,d)?y=g:h=g;if(h>r)return[c,m,h,!0]}}return function(e){const i=e.datum.datum.items[a].items,d=i.length,m=e.datum.fontSize,g=r.textMetrics.width(e.datum,e.datum.text);let h,y,p,x,v,b,w,k,M,R,z,A,O,E,S,q,B,T=n?m:0,U=!1,D=!1,I=0;for(let r=0;r<d;++r){for(h=i[r].x,p=i[r].y,y=void 0===i[r].x2?h:i[r].x2,x=void 0===i[r].y2?p:i[r].y2,h>y&&(B=h,h=y,y=B),p>x&&(B=p,p=x,x=B),M=t(h),z=t(y),R=~~((M+z)/2),A=t(p),E=t(x),O=~~((A+E)/2),w=R;w>=M;--w)for(k=O;k>=A;--k)q=c(w,k,T,g,m),q&&([e.x,e.y,T,U]=q);for(w=R;w<=z;++w)for(k=O;k<=E;++k)q=c(w,k,T,g,m),q&&([e.x,e.y,T,U]=q);U||n||(S=Math.abs(y-h+x-p),v=(h+y)/2,b=(p+x)/2,S>=I&&!f(v,b,g,m,o,u)&&!s(t,v,b,m,g,m,l,null)&&(I=S,e.x=v,e.y=b,D=!0))}return!(!U&&!D)&&(v=g/2,b=m/2,l.setRange(t(e.x-v),t(e.y-b),t(e.x+v),t(e.y+b)),e.align="center",e.baseline="middle",!0)}},floodfill:function(t,e,n,a,i){const o=t.width,u=t.height,[s,f]=y(i),l=e[0],d=e[1],c=t.bitmap();return function(e){const i=e.datum.datum.items[a].items,m=i.length,g=e.datum.fontSize,h=r.textMetrics.width(e.datum,e.datum.text),y=[];let v,b,w,k,M,R,z,A,O,E,S,q,B=n?g:0,T=!1,U=!1,D=0;for(let r=0;r<m;++r){for(v=i[r].x,w=i[r].y,b=void 0===i[r].x2?v:i[r].x2,k=void 0===i[r].y2?w:i[r].y2,y.push([t((v+b)/2),t((w+k)/2)]);y.length;)if([z,A]=y.pop(),!(l.get(z,A)||d.get(z,A)||c.get(z,A))){c.set(z,A);for(let t=0;t<4;++t)M=z+p[t],R=A+x[t],c.outOfBounds(M,R,M,R)||y.push([M,R]);if(M=t.invert(z),R=t.invert(A),O=B,E=u,!f(M,R,h,g,o,u)&&!s(t,M,R,g,h,O,l,d)&&!s(t,M,R,g,h,g,l,null)){for(;E-O>=1;)S=(O+E)/2,s(t,M,R,g,h,S,l,d)?E=S:O=S;O>B&&(e.x=M,e.y=R,B=O,T=!0)}}T||n||(q=Math.abs(b-v+k-w),M=(v+b)/2,R=(w+k)/2,q>=D&&!f(M,R,h,g,o,u)&&!s(t,M,R,g,h,g,l,null)&&(D=q,e.x=M,e.y=R,U=!0))}return!(!T&&!U)&&(M=h/2,R=g/2,l.setRange(t(e.x-M),t(e.y-R),t(e.x+M),t(e.y+R)),e.align="center",e.baseline="middle",!0)}}};function R(t,e,n,a,o,u,s,f,l,c,m){if(!t.length)return t;const g=Math.max(a.length,o.length),h=function(t,e){const n=new Float64Array(e),r=t.length;for(let e=0;e<r;++e)n[e]=t[e]||0;for(let t=r;t<e;++t)n[t]=n[r-1];return n}(a,g),y=function(t,e){const n=new Int8Array(e),r=t.length;for(let e=0;e<r;++e)n[e]|=k[t[e]];for(let t=r;t<e;++t)n[t]=n[r-1];return n}(o,g),p=(S=t[0].datum)&&S.mark&&S.mark.marktype,x="group"===p&&t[0].datum.items[l].marktype,R="area"===x,z=function(t,e,n,r){const a=t=>[t.x,t.x,t.x,t.y,t.y,t.y];return t?"line"===t||"area"===t?t=>a(t.datum):"line"===e?t=>{const e=t.datum.items[r].items;return a(e.length?e["start"===n?0:e.length-1]:{x:NaN,y:NaN})}:t=>{const e=t.datum.bounds;return[e.x1,(e.x1+e.x2)/2,e.x2,e.y1,(e.y1+e.y2)/2,e.y2]}:a}(p,x,f,l),A=null===c||c===1/0,O=d(e[0],e[1],A?0:c),E=R&&"naive"===m;var S;const q=t.map((t=>({datum:t,opacity:0,x:void 0,y:void 0,align:void 0,baseline:void 0,boundary:z(t)})));let B;if(!E){n&&q.sort(((t,e)=>n(t.datum,e.datum)));let e=!1;for(let t=0;t<y.length&&!e;++t)e=5===y[t]||h[t]<0;p&&(s||R)&&(u=[t.map((t=>t.datum))].concat(u)),B=u.length?i(O,u,e,R):function(t,e){const n=t.bitmap();return(e||[]).forEach((e=>n.set(t(e.boundary[0]),t(e.boundary[3])))),[n,void 0]}(O,s&&q)}const T=R?M[m](O,B,s,l,A):function(t,e,n,a,i){const o=t.width,u=t.height,s=e[0],f=e[1],l=a.length;return function(e){const d=e.boundary,c=e.datum.fontSize;if(!i&&(d[2]<0||d[5]<0||d[0]>o||d[3]>u))return!1;let m,g,h,y,p,x,k,M,R,z,A,O,E,S,q,B=0;for(let o=0;o<l;++o){if(m=(3&n[o])-1,g=(n[o]>>>2&3)-1,h=0===m&&0===g||a[o]<0,y=m&&g?Math.SQRT1_2:1,p=a[o]<0?-1:1,x=d[1+m]+a[o]*m*y,A=d[4+g]+p*c*g/2+a[o]*g*y,M=A-c/2,R=A+c/2,O=t(x),S=t(M),q=t(R),i&&(O=O<0?0:O,S=S<0?0:S,q=q>=t.height?t.height-1:q),!B){if(!w(O,O,S,q,s,f,x,x,M,R,d,h))continue;B=r.textMetrics.width(e.datum,e.datum.text)}if(z=x+p*B*m/2,x=z-B/2,k=z+B/2,O=t(x),E=t(k),i&&(O=O<0?0:O,E=E>=t.width?t.width-1:E),w(O,E,S,q,s,f,x,k,M,R,d,h))return e.x=m?m*p<0?k:x:z,e.y=g?g*p<0?R:M:A,e.align=v[m*p+1],e.baseline=b[g*p+1],s.setRange(O,S,E,q),!0}return!1}}(O,B,y,h,A);return q.forEach((t=>t.opacity=+T(t))),q}const z=["x","y","opacity","align","baseline"],A=["top-left","left","bottom-left","top","bottom","top-right","right","bottom-right"];function O(t){n.Transform.call(this,null,t)}O.Definition={type:"Label",metadata:{modifies:!0},params:[{name:"size",type:"number",array:!0,length:2,required:!0},{name:"sort",type:"compare"},{name:"anchor",type:"string",array:!0,default:A},{name:"offset",type:"number",array:!0,default:[1]},{name:"padding",type:"number",default:0,null:!0},{name:"lineAnchor",type:"string",values:["start","end"],default:"end"},{name:"markIndex",type:"number",default:0},{name:"avoidBaseMark",type:"boolean",default:!0},{name:"avoidMarks",type:"data",array:!0},{name:"method",type:"string",default:"naive"},{name:"as",type:"string",array:!0,length:z.length,default:z}]},a.inherits(O,n.Transform,{transform(t,e){const n=t.modified();if(!(n||e.changed(e.ADD_REM)||function(n){const r=t[n];return a.isFunction(r)&&e.modified(r.fields)}("sort")))return;t.size&&2===t.size.length||a.error("Size parameter should be specified as a [width, height] array.");const r=t.as||z;return R(e.materialize(e.SOURCE).source||[],t.size,t.sort,a.array(null==t.offset?1:t.offset),a.array(t.anchor||A),t.avoidMarks||[],!1!==t.avoidBaseMark,t.lineAnchor||"end",t.markIndex||0,void 0===t.padding?0:t.padding,t.method||"naive").forEach((t=>{const e=t.datum;e[r[0]]=t.x,e[r[1]]=t.y,e[r[2]]=t.opacity,e[r[3]]=t.align,e[r[4]]=t.baseline})),e.reflow(n).modifies(r)}}),t.label=O,Object.defineProperty(t,"__esModule",{value:!0})})); | ||
//# sourceMappingURL=vega-label.min.js.map |
@@ -289,2 +289,7 @@ import { canvas } from 'vega-canvas'; | ||
} | ||
function _outOfBounds() { | ||
return false; | ||
} | ||
function collision($, x, y, textHeight, textWidth, h, bm0, bm1) { | ||
@@ -299,5 +304,23 @@ const w = textWidth * h / (textHeight * 2), | ||
function placeAreaLabelReducedSearch ($, bitmaps, avoidBaseMark, markIndex) { | ||
function _collision($, x, y, textHeight, textWidth, h, bm0, bm1) { | ||
const w = textWidth * h / (textHeight * 2); | ||
let x1 = $(x - w), | ||
x2 = $(x + w), | ||
y1 = $(y - (h = h / 2)), | ||
y2 = $(y + h); | ||
x1 = x1 > 0 ? x1 : 0; | ||
y1 = y1 > 0 ? y1 : 0; | ||
x2 = x2 < $.width ? x2 : $.width - 1; | ||
y2 = y2 < $.height ? y2 : $.height - 1; | ||
return bm0.getRange(x1, y1, x2, y2) || bm1 && bm1.getRange(x1, y1, x2, y2); | ||
} | ||
function getTests(infPadding) { | ||
return infPadding ? [_collision, _outOfBounds] : [collision, outOfBounds]; | ||
} | ||
function placeAreaLabelReducedSearch ($, bitmaps, avoidBaseMark, markIndex, infPadding) { | ||
const width = $.width, | ||
height = $.height, | ||
[collision, outOfBounds] = getTests(infPadding), | ||
bm0 = bitmaps[0], | ||
@@ -446,5 +469,6 @@ // where labels have been placed | ||
const Y_DIR = [-1, 1, -1, 1]; | ||
function placeAreaLabelFloodFill ($, bitmaps, avoidBaseMark, markIndex) { | ||
function placeAreaLabelFloodFill ($, bitmaps, avoidBaseMark, markIndex, infPadding) { | ||
const width = $.width, | ||
height = $.height, | ||
[collision, outOfBounds] = getTests(infPadding), | ||
bm0 = bitmaps[0], | ||
@@ -570,3 +594,3 @@ // where labels have been placed | ||
Baselines = ['bottom', 'middle', 'top']; | ||
function placeMarkLabel ($, bitmaps, anchors, offsets) { | ||
function placeMarkLabel ($, bitmaps, anchors, offsets, infPadding) { | ||
const width = $.width, | ||
@@ -581,3 +605,3 @@ height = $.height, | ||
if (boundary[2] < 0 || boundary[5] < 0 || boundary[0] > width || boundary[3] > height) { | ||
if (!infPadding && (boundary[2] < 0 || boundary[5] < 0 || boundary[0] > width || boundary[3] > height)) { | ||
return false; | ||
@@ -618,2 +642,8 @@ } | ||
if (infPadding) { | ||
_x1 = _x1 < 0 ? 0 : _x1; | ||
_y1 = _y1 < 0 ? 0 : _y1; | ||
_y2 = _y2 >= $.height ? $.height - 1 : _y2; | ||
} | ||
if (!textWidth) { | ||
@@ -636,2 +666,7 @@ // to avoid finding width of text label, | ||
if (infPadding) { | ||
_x1 = _x1 < 0 ? 0 : _x1; | ||
_x2 = _x2 >= $.width ? $.width - 1 : _x2; | ||
} | ||
if (test(_x1, _x2, _y1, _y2, bm0, bm1, x1, x2, y1, y2, boundary, isInside)) { | ||
@@ -693,3 +728,4 @@ // place label if the position is placeable | ||
boundary = markBoundary(marktype, grouptype, lineAnchor, markIndex), | ||
$ = scaler(size[0], size[1], padding), | ||
infPadding = padding === null || padding === Infinity, | ||
$ = scaler(size[0], size[1], infPadding ? 0 : padding), | ||
isNaiveGroupArea = isGroupArea && method === 'naive'; // prepare text mark data for placing | ||
@@ -734,3 +770,3 @@ | ||
const place = isGroupArea ? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex) : placeMarkLabel($, bitmaps, anchors, offsets); // place all labels | ||
const place = isGroupArea ? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex, infPadding) : placeMarkLabel($, bitmaps, anchors, offsets, infPadding); // place all labels | ||
@@ -811,4 +847,5 @@ data.forEach(d => d.opacity = +place(d)); | ||
* @param {Array<number>} [params.offset] - Label offsets (in pixels) from the base mark bounding box. | ||
* This parameter is parallel to the list of anchor points. | ||
* @param {number} [params.padding=0] - The amount (in pixels) that a label may exceed the layout size. | ||
* This parameter is parallel to the list of anchor points. | ||
* @param {number | null} [params.padding=0] - The amount (in pixels) that a label may exceed the layout size. | ||
* If this parameter is null, a label may exceed the layout size without any boundary. | ||
* @param {string} [params.lineAnchor='end'] - For group line mark labels only, indicates the anchor | ||
@@ -858,3 +895,4 @@ * position for labels. One of 'start' or 'end'. | ||
type: 'number', | ||
default: 0 | ||
default: 0, | ||
null: true | ||
}, { | ||
@@ -906,3 +944,3 @@ name: 'lineAnchor', | ||
labelLayout(pulse.materialize(pulse.SOURCE).source, _.size, _.sort, array(_.offset || 1), array(_.anchor || Anchors), _.avoidMarks || [], _.avoidBaseMark === false ? false : true, _.lineAnchor || 'end', _.markIndex || 0, _.padding || 0, _.method || 'naive').forEach(l => { | ||
labelLayout(pulse.materialize(pulse.SOURCE).source || [], _.size, _.sort, array(_.offset == null ? 1 : _.offset), array(_.anchor || Anchors), _.avoidMarks || [], _.avoidBaseMark !== false, _.lineAnchor || 'end', _.markIndex || 0, _.padding === undefined ? 0 : _.padding, _.method || 'naive').forEach(l => { | ||
// write layout results to data stream | ||
@@ -909,0 +947,0 @@ const t = l.datum; |
{ | ||
"name": "vega-label", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Label layout transform for Vega dataflows.", | ||
@@ -35,3 +35,3 @@ "keywords": [ | ||
}, | ||
"gitHead": "4affcbedb9d14815dbb6d3b250ed231b54fc95c0" | ||
"gitHead": "774165e29850b66ec8b79ba52a7955f1ab936ea6" | ||
} |
@@ -35,4 +35,5 @@ import labelLayout from './LabelLayout'; | ||
* @param {Array<number>} [params.offset] - Label offsets (in pixels) from the base mark bounding box. | ||
* This parameter is parallel to the list of anchor points. | ||
* @param {number} [params.padding=0] - The amount (in pixels) that a label may exceed the layout size. | ||
* This parameter is parallel to the list of anchor points. | ||
* @param {number | null} [params.padding=0] - The amount (in pixels) that a label may exceed the layout size. | ||
* If this parameter is null, a label may exceed the layout size without any boundary. | ||
* @param {string} [params.lineAnchor='end'] - For group line mark labels only, indicates the anchor | ||
@@ -63,3 +64,3 @@ * position for labels. One of 'start' or 'end'. | ||
{ name: 'offset', type: 'number', array: true, default: [1] }, | ||
{ name: 'padding', type: 'number', default: 0 }, | ||
{ name: 'padding', type: 'number', default: 0, null: true }, | ||
{ name: 'lineAnchor', type: 'string', values: ['start', 'end'], default: 'end' }, | ||
@@ -91,12 +92,12 @@ { name: 'markIndex', type: 'number', default: 0 }, | ||
labelLayout( | ||
pulse.materialize(pulse.SOURCE).source, | ||
pulse.materialize(pulse.SOURCE).source || [], | ||
_.size, | ||
_.sort, | ||
array(_.offset || 1), | ||
array(_.offset == null ? 1 : _.offset), | ||
array(_.anchor || Anchors), | ||
_.avoidMarks || [], | ||
_.avoidBaseMark === false ? false : true, | ||
_.avoidBaseMark !== false, | ||
_.lineAnchor || 'end', | ||
_.markIndex || 0, | ||
_.padding || 0, | ||
_.padding === undefined ? 0 : _.padding, | ||
_.method || 'naive' | ||
@@ -103,0 +104,0 @@ ).forEach(l => { |
@@ -48,3 +48,4 @@ import {baseBitmaps, markBitmaps} from './util/markBitmaps'; | ||
boundary = markBoundary(marktype, grouptype, lineAnchor, markIndex), | ||
$ = scaler(size[0], size[1], padding), | ||
infPadding = padding === null || padding === Infinity, | ||
$ = scaler(size[0], size[1], infPadding ? 0 : padding), | ||
isNaiveGroupArea = isGroupArea && method === 'naive'; | ||
@@ -92,4 +93,4 @@ | ||
const place = isGroupArea | ||
? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex) | ||
: placeMarkLabel($, bitmaps, anchors, offsets); | ||
? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex, infPadding) | ||
: placeMarkLabel($, bitmaps, anchors, offsets, infPadding); | ||
@@ -96,0 +97,0 @@ // place all labels |
@@ -1,2 +0,2 @@ | ||
export function outOfBounds(x, y, textWidth, textHeight, width, height) { | ||
function outOfBounds(x, y, textWidth, textHeight, width, height) { | ||
let r = textWidth / 2; | ||
@@ -9,3 +9,7 @@ return x - r < 0 | ||
export function collision($, x, y, textHeight, textWidth, h, bm0, bm1) { | ||
function _outOfBounds() { | ||
return false; | ||
} | ||
function collision($, x, y, textHeight, textWidth, h, bm0, bm1) { | ||
const w = (textWidth * h) / (textHeight * 2), | ||
@@ -20,2 +24,23 @@ x1 = $(x - w), | ||
|| (bm1 && bm1.getRange(x1, y1, x2, y2)); | ||
} | ||
function _collision($, x, y, textHeight, textWidth, h, bm0, bm1) { | ||
const w = (textWidth * h) / (textHeight * 2); | ||
let x1 = $(x - w), | ||
x2 = $(x + w), | ||
y1 = $(y - (h = h/2)), | ||
y2 = $(y + h); | ||
x1 = x1 > 0 ? x1 : 0; | ||
y1 = y1 > 0 ? y1 : 0; | ||
x2 = x2 < $.width ? x2 : $.width - 1; | ||
y2 = y2 < $.height ? y2 : $.height - 1; | ||
return bm0.getRange(x1, y1, x2, y2) || (bm1 && bm1.getRange(x1, y1, x2, y2)); | ||
} | ||
export function getTests(infPadding) { | ||
return infPadding | ||
? [_collision, _outOfBounds] | ||
: [collision, outOfBounds]; | ||
} |
import {textMetrics} from 'vega-scenegraph'; | ||
import {collision, outOfBounds} from './common'; | ||
import {getTests} from './common'; | ||
@@ -8,5 +8,6 @@ // pixel direction offsets for flood fill search | ||
export default function($, bitmaps, avoidBaseMark, markIndex) { | ||
export default function($, bitmaps, avoidBaseMark, markIndex, infPadding) { | ||
const width = $.width, | ||
height = $.height, | ||
[collision, outOfBounds] = getTests(infPadding), | ||
bm0 = bitmaps[0], // where labels have been placed | ||
@@ -13,0 +14,0 @@ bm1 = bitmaps[1], // area outlines |
import {textMetrics} from 'vega-scenegraph'; | ||
import {collision, outOfBounds} from './common'; | ||
import {getTests} from './common'; | ||
export default function($, bitmaps, avoidBaseMark, markIndex) { | ||
export default function($, bitmaps, avoidBaseMark, markIndex, infPadding) { | ||
const width = $.width, | ||
height = $.height, | ||
[collision, outOfBounds] = getTests(infPadding), | ||
bm0 = bitmaps[0], // where labels have been placed | ||
@@ -8,0 +9,0 @@ bm1 = bitmaps[1]; // area outlines |
@@ -6,3 +6,3 @@ import {textMetrics} from 'vega-scenegraph'; | ||
export default function($, bitmaps, anchors, offsets) { | ||
export default function($, bitmaps, anchors, offsets, infPadding) { | ||
const width = $.width, | ||
@@ -19,3 +19,3 @@ height = $.height, | ||
// can not be placed if the mark is not visible in the graph bound | ||
if (boundary[2] < 0 || boundary[5] < 0 || boundary[0] > width || boundary[3] > height) { | ||
if (!infPadding && (boundary[2] < 0 || boundary[5] < 0 || boundary[0] > width || boundary[3] > height)) { | ||
return false; | ||
@@ -47,2 +47,8 @@ } | ||
if (infPadding) { | ||
_x1 = _x1 < 0 ? 0 : _x1; | ||
_y1 = _y1 < 0 ? 0 : _y1; | ||
_y2 = _y2 >= $.height ? ($.height - 1) : _y2; | ||
} | ||
if (!textWidth) { | ||
@@ -66,2 +72,7 @@ // to avoid finding width of text label, | ||
if (infPadding) { | ||
_x1 = _x1 < 0 ? 0 : _x1; | ||
_x2 = _x2 >= $.width ? ($.width - 1) : _x2; | ||
} | ||
if (test(_x1, _x2, _y1, _y2, bm0, bm1, x1, x2, y1, y2, boundary, isInside)) { | ||
@@ -68,0 +79,0 @@ // place label if the position is placeable |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
147811
2440
2