-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Support adding text labels to lines and shapes #6454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
7ee2e85
932fa65
1d0e471
e46e1bc
60823dc
2f8c810
e405293
e31de9e
0bcfff3
9972dc9
6d5709a
072edf6
8bee979
8528561
b4a4576
97ee8c4
674abd8
3743de4
c3cc5ed
45a0564
b8085c2
8c0476e
95dd9b6
58e39cb
488318f
c8d20f2
dad6ea5
3134869
f17e218
01ef185
d8c4790
5f39a62
1ab9e24
b9ea251
1f366a8
f1087f3
f1570cf
baec66d
d83e0cd
a91d12f
4948d67
a5e19d3
e55953e
44d5f08
5424ac5
8769cff
f192a16
7ca5cf8
822fe0d
d50da33
8cec651
36771a8
3f9727a
9a4a121
e493873
a17a315
d9bef04
f54f102
f642a52
dfb608e
c20370a
feb2f38
11b3421
ea9493e
52463fa
394b48f
1325b7c
63bb5d9
61e3df7
40933d0
8d35f6e
d5d1927
c34d6de
ffea962
ebe85c4
d113bcf
c703c11
468363e
cdf2d37
9eb7c90
3d2c792
394555c
eaf270d
095561f
b0b8ab1
d927b6a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -598,11 +598,12 @@ function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer, editHe | |
} | ||
|
||
function drawLabel(gd, index, options, shapeGroup) { | ||
if(!(options.label && options.x0 && options.x1)) return; | ||
|
||
// Remove existing label | ||
shapeGroup.selectAll('.shape-label').remove(); | ||
|
||
// If no label, return | ||
if(!options.label) return; | ||
|
||
var labelGroupAttrs = { | ||
'data-index': index, | ||
}; | ||
|
@@ -621,33 +622,49 @@ function drawLabel(gd, index, options, shapeGroup) { | |
.classed('shape-label-text', true) | ||
.text(text); | ||
|
||
// Setup conversion functions | ||
var xa = Axes.getFromId(gd, options.xref); | ||
var xRefType = Axes.getRefType(options.xref); | ||
var ya = Axes.getFromId(gd, options.yref); | ||
var yRefType = Axes.getRefType(options.yref); | ||
var x2p = helpers.getDataToPixel(gd, xa, false, xRefType); | ||
var y2p = helpers.getDataToPixel(gd, ya, true, yRefType); | ||
|
||
var shapex0 = x2p(options.x0); | ||
var shapex1 = x2p(options.x1); | ||
var shapey0 = y2p(options.y0); | ||
var shapey1 = y2p(options.y1); | ||
// If x0, x1, y0, y1 are defined explicitly, use those values | ||
// Otherwise, use shape bounding box | ||
var shapex0, shapex1, shapey0, shapey1; | ||
archmoj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if('x0' in options && 'x1' in options && 'y0' in options && 'y1' in options) { | ||
// Setup conversion functions | ||
var xa = Axes.getFromId(gd, options.xref); | ||
var xRefType = Axes.getRefType(options.xref); | ||
var ya = Axes.getFromId(gd, options.yref); | ||
var yRefType = Axes.getRefType(options.yref); | ||
var x2p = helpers.getDataToPixel(gd, xa, false, xRefType); | ||
var y2p = helpers.getDataToPixel(gd, ya, true, yRefType); | ||
|
||
shapex0 = x2p(options.x0); | ||
shapex1 = x2p(options.x1); | ||
shapey0 = y2p(options.y0); | ||
shapey1 = y2p(options.y1); | ||
} else { | ||
// Get shape bounding box | ||
var shapeBBox = shapeGroup.selectAll('path').node().getBoundingClientRect(); | ||
shapex0 = shapeBBox.x; | ||
emilykl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
shapex1 = shapeBBox.right; | ||
shapey0 = shapeBBox.y; | ||
emilykl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
shapey1 = shapeBBox.bottom; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The vertical positioning for paths is not correct. {
"data": [
{
"x": [
0,
50
],
"y": [
0,
50
]
}
],
"layout": {
"width": 800,
"height": 600,
"margin": {
"t": 100,
"b": 50,
"l": 100,
"r": 50
},
"yaxis": {
"autorange": "reversed"
},
"template": {
"layout": {
"shapes": [
{
"label": {"text": "label"},
"name": "myPath",
"editable": true,
"layer": "below",
"line": {
"width": 0
},
"fillcolor": "gray",
"opacity": 0.5,
"xref": "paper",
"yref": "paper",
"path": "M0.5,0.3C0.5,0.9 0.9,0.9 0.9,0.3C0.9,0.1 0.5,0.1 0.5,0.3ZM0.6,0.4C0.6,0.5 0.66,0.5 0.66,0.4ZM0.74,0.4C0.74,0.5 0.8,0.5 0.8,0.4ZM0.6,0.3C0.63,0.2 0.77,0.2 0.8,0.3Z"
}
]
}
},
"shapes": [
{
"label": {"text": "label"},
"editable": true,
"layer": "below",
"type": "rect",
"line": {
"width": 5
},
"fillcolor": "red",
"opacity": 0.5,
"xref": "xaxis",
"yref": "yaxis",
"x0": 25,
"y0": 25,
"x1": 75,
"y1": 75
},
{
"label": {"text": "label"},
"editable": true,
"layer": "top",
"type": "circle",
"line": {
"width": 5
},
"fillcolor": "green",
"opacity": 0.5,
"xref": "xaxis",
"yref": "yaxis",
"x0": 125,
"y0": 25,
"x1": 175,
"y1": 75
},
{
"label": {"text": "label"},
"editable": true,
"line": {
"width": 5
},
"fillcolor": "blue",
"path": "M250,25L225,75L275,75Z"
},
{
"label": {"text": "label"},
"editable": true,
"line": {
"width": 15
},
"path": "M250,225L225,275L275,275"
},
{
"label": {"text": "label"},
"editable": true,
"layer": "below",
"path": "M320,100C390,180 290,180 360,100Z",
"fillcolor": "rgba(0,127,127,0.5)",
"line": {
"width": 5
}
},
{
"label": {"text": "label"},
"editable": true,
"line": {
"width": 5,
"color": "orange"
},
"fillcolor": "rgba(127,255,127,0.5)",
"path": "M0,100V200H50L0,300Q100,300 100,200T150,200C100,300 200,300 200,200S150,200 150,100Z"
},
{
"label": {"text": "label"},
"editable": true,
"line": {
"width": 2
},
"fillcolor": "yellow",
"path": "M300,70C300,10 380,10 380,70C380,90 300,90 300,70ZM320,60C320,50 332,50 332,60ZM348,60C348,50 360,50 360,60ZM320,70C326,80 354,80 360,70Z"
}
]
},
"config": {
"editable": false,
"modeBarButtonsToAdd": [
"drawline",
"drawopenpath",
"drawclosedpath",
"drawcircle",
"drawrect",
"eraseshape"
]
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @archmoj Mock added! I'm a bit stumped on how to fix the vertical position for paths -- it seems getBoundingClientRect() is giving the wrong y-axis bounding box coords. Is there a different function I should be using? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @archmoj Whoops, forgot to push -- should be good now! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Let's rewrite this part as var shapex0, shapex1, shapey0, shapey1;
if(options.path) {
var d = getPathString(gd, options);
var polygons = readPaths(d, gd);
shapex0 = Infinity;
shapey0 = Infinity;
shapex1 = -Infinity;
shapey1 = -Infinity;
for(var i = 0; i < polygons.length; i++) {
var polygon = polygons[i];
for(var j = 0; j < polygon.length; j++) {
var _x = polygon[j][1];
var _y = polygon[j][2];
shapex0 = Math.min(shapex0, _x);
shapex1 = Math.max(shapex1, _x);
shapey0 = Math.min(shapey0, _y);
shapey1 = Math.max(shapey1, _y);
}
}
} else {
// Setup conversion functions
var xa = Axes.getFromId(gd, options.xref);
var xRefType = Axes.getRefType(options.xref);
var ya = Axes.getFromId(gd, options.yref);
var yRefType = Axes.getRefType(options.yref);
var x2p = helpers.getDataToPixel(gd, xa, false, xRefType);
var y2p = helpers.getDataToPixel(gd, ya, true, yRefType);
shapex0 = x2p(options.x0);
shapex1 = x2p(options.x1);
shapey0 = y2p(options.y0);
shapey1 = y2p(options.y1);
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To take into account of extra vertices for curves, the following would be a better option: var shapex0, shapex1, shapey0, shapey1;
if(options.path) {
var d = getPathString(gd, options);
var polygons = readPaths(d, gd);
shapex0 = Infinity;
shapey0 = Infinity;
shapex1 = -Infinity;
shapey1 = -Infinity;
for(var i = 0; i < polygons.length; i++) {
for(var j = 0; j < polygons[i].length; j++) {
var p = polygons[i][j];
for(var k = 1; k < p.length; k += 2) {
var _x = p[k];
var _y = p[k + 1];
shapex0 = Math.min(shapex0, _x);
shapex1 = Math.max(shapex1, _x);
shapey0 = Math.min(shapey0, _y);
shapey1 = Math.max(shapey1, _y);
}
}
}
} else {
// Setup conversion functions
var xa = Axes.getFromId(gd, options.xref);
var xRefType = Axes.getRefType(options.xref);
var ya = Axes.getFromId(gd, options.yref);
var yRefType = Axes.getRefType(options.yref);
var x2p = helpers.getDataToPixel(gd, xa, false, xRefType);
var y2p = helpers.getDataToPixel(gd, ya, true, yRefType);
shapex0 = x2p(options.x0);
shapex1 = x2p(options.x1);
shapey0 = y2p(options.y0);
shapey1 = y2p(options.y1);
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks @archmoj ! Just pushed your suggested code and this works great. The text position still looks a little off for this one shape though: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's skip this part for now & come back to it later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That particular shape is funny, I don't know its path definition but the curve may extend well below its control points just because of the way bezier curves work, so I wouldn't be too worried about where the labels appear there. Not worth jumping through crazy hoops to improve that, it's a pretty esoteric edge case. |
||
|
||
// Handle 'auto' angle for lines | ||
var textangle = options.label.textangle; | ||
if(textangle === 'auto') { | ||
textangle = calcTextAngle(shapex0, shapey0, shapex1, shapey1); | ||
} | ||
|
||
// Do an initial render just so we can get the bounding box height -- | ||
// this is not the final render | ||
labelText.call(function(s) { | ||
// Do a fake render so we can get the text bounding box height | ||
archmoj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
var _labelText = labelGroup.append('text') | ||
archmoj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.attr(labelTextAttrs) | ||
archmoj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.classed('shape-label-text', true) | ||
.text(text); | ||
_labelText.call(function(s) { | ||
s.call(Drawing.font, font).attr({}); | ||
svgTextUtils.convertToTspans(s, gd); | ||
return s; | ||
}); | ||
var textBB = Drawing.bBox(labelText.node()); | ||
var textBB = Drawing.bBox(_labelText.node()); | ||
_labelText.remove(); | ||
|
||
// Calculate correct (x,y) for text | ||
// We also determine true xanchor since xanchor depends on position when set to 'auto' | ||
|
@@ -721,7 +738,6 @@ function calcTextPosition(shapex0, shapey0, shapex1, shapey1, shapeOptions, actu | |
paddingMultiplier = 1; | ||
if(yanchor === 'auto') yanchor = 'bottom'; | ||
} | ||
// var paddingMultiplier = textPosition.indexOf('middle') !== -1 ? 0 : textPosition.indexOf('bottom') !== -1 ? -1 : 1; | ||
|
||
if(textPosition.indexOf('start') !== -1) { | ||
textx = shapex0 + paddingX * paddingMultiplier; | ||
|
@@ -752,7 +768,7 @@ function calcTextPosition(shapex0, shapey0, shapex1, shapey1, shapeOptions, actu | |
} | ||
|
||
// calc vertical position | ||
paddingY = textPadding | ||
paddingY = textPadding; | ||
if(textPosition.indexOf('top') !== -1) { | ||
texty = Math.min(shapey0, shapey1) - paddingY; | ||
if(yanchor === 'auto') yanchor = 'bottom'; | ||
|
@@ -766,11 +782,13 @@ function calcTextPosition(shapex0, shapey0, shapex1, shapey1, shapeOptions, actu | |
} | ||
|
||
// Shift vertical (& horizontal) position according to `yanchor` | ||
// This shiftFraction is only a rough approximation, but maybe good enough? | ||
var shiftFraction = {middle: -0.2, bottom: 0.3, top: -0.7}[yanchor]; | ||
var shiftFraction = { middle: 0, bottom: 0.5, top: -0.5 }[yanchor]; | ||
// To adjust for text being anchored at baseline instead of bottom of descenders | ||
// Probably not the right way of handling | ||
var baselineAdjust = shapeOptions.label.font.size / 4; | ||
var textHeight = textBB.height; | ||
var xshift = textHeight * Math.sin(textAngleRad) * shiftFraction; | ||
var yshift = -textHeight * Math.cos(textAngleRad) * shiftFraction; | ||
var xshift = (textHeight * shiftFraction - baselineAdjust) * Math.sin(textAngleRad); | ||
archmoj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
var yshift = -(textHeight * shiftFraction - baselineAdjust) * Math.cos(textAngleRad); | ||
|
||
return { textx: textx + xshift, texty: texty + yshift, xanchor: xanchor }; | ||
} | ||
|
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.