diff --git a/src/components/selections/select.js b/src/components/selections/select.js index 8e08da59c31..eb20f71973c 100644 --- a/src/components/selections/select.js +++ b/src/components/selections/select.js @@ -25,7 +25,7 @@ var newShapeHelpers = require('../shapes/draw_newshape/helpers'); var handleEllipse = newShapeHelpers.handleEllipse; var readPaths = newShapeHelpers.readPaths; -var newShapes = require('../shapes/draw_newshape/newshapes'); +var newShapes = require('../shapes/draw_newshape/newshapes').newShapes; var newSelections = require('./draw_newselection/newselections'); var activateLastSelection = require('./draw').activateLastSelection; @@ -112,6 +112,10 @@ function prepSelect(evt, startX, startY, dragOptions, mode) { fullLayout.newshape : fullLayout.newselection; + if(isDrawMode) { + dragOptions.hasText = newStyle.label.text || newStyle.label.texttemplate; + } + var fillC = (isDrawMode && !isOpenMode) ? newStyle.fillcolor : 'rgba(0,0,0,0)'; var strokeC = newStyle.line.color || ( @@ -146,6 +150,16 @@ function prepSelect(evt, startX, startY, dragOptions, mode) { .attr('transform', transform) .attr('d', 'M0,0Z'); + // create & style group for text label + if(isDrawMode && dragOptions.hasText) { + var shapeGroup = zoomLayer.select('.label-temp'); + if(shapeGroup.empty()) { + shapeGroup = zoomLayer.append('g') + .classed('label-temp', true) + .classed('select-outline', true) + .style({ opacity: 0.8 }); + } + } var throttleID = fullLayout._uid + constants.SELECTID; var selection = []; diff --git a/src/components/shapes/display_labels.js b/src/components/shapes/display_labels.js new file mode 100644 index 00000000000..82c0856d943 --- /dev/null +++ b/src/components/shapes/display_labels.js @@ -0,0 +1,268 @@ +'use strict'; + +var Lib = require('../../lib'); +var Axes = require('../../plots/cartesian/axes'); +var svgTextUtils = require('../../lib/svg_text_utils'); + +var Drawing = require('../drawing'); + +var readPaths = require('./draw_newshape/helpers').readPaths; +var helpers = require('./helpers'); +var getPathString = helpers.getPathString; +var shapeLabelTexttemplateVars = require('./label_texttemplate'); + +var FROM_TL = require('../../constants/alignment').FROM_TL; + + +module.exports = function drawLabel(gd, index, options, shapeGroup) { + // Remove existing label + shapeGroup.selectAll('.shape-label').remove(); + + // If no label text or texttemplate, return + if(!(options.label.text || options.label.texttemplate)) return; + + // Text template overrides text + var text; + if(options.label.texttemplate) { + var templateValues = {}; + if(options.type !== 'path') { + var _xa = Axes.getFromId(gd, options.xref); + var _ya = Axes.getFromId(gd, options.yref); + for(var key in shapeLabelTexttemplateVars) { + var val = shapeLabelTexttemplateVars[key](options, _xa, _ya); + if(val !== undefined) templateValues[key] = val; + } + } + text = Lib.texttemplateStringForShapes(options.label.texttemplate, + {}, + gd._fullLayout._d3locale, + templateValues); + } else { + text = options.label.text; + } + + var labelGroupAttrs = { + 'data-index': index, + }; + var font = options.label.font; + + var labelTextAttrs = { + 'data-notex': 1 + }; + + var labelGroup = shapeGroup.append('g') + .attr(labelGroupAttrs) + .classed('shape-label', true); + var labelText = labelGroup.append('text') + .attr(labelTextAttrs) + .classed('shape-label-text', true) + .text(text); + + // Get x and y bounds of shape + var shapex0, shapex1, shapey0, shapey1; + if(options.path) { + // If shape is defined as a path, get the + // min and max bounds across all polygons in 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 { + // Otherwise, we use the x and y bounds defined in the shape options + // and convert them to pixel coordinates + // 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); + } + + // Handle `auto` angle + var textangle = options.label.textangle; + if(textangle === 'auto') { + if(options.type === 'line') { + // Auto angle for line is same angle as line + textangle = calcTextAngle(shapex0, shapey0, shapex1, shapey1); + } else { + // Auto angle for all other shapes is 0 + textangle = 0; + } + } + + // Do an initial render so we can get the text bounding box height + labelText.call(function(s) { + s.call(Drawing.font, font).attr({}); + svgTextUtils.convertToTspans(s, gd); + return s; + }); + var textBB = Drawing.bBox(labelText.node()); + + // Calculate correct (x,y) for text + // We also determine true xanchor since xanchor depends on position when set to 'auto' + var textPos = calcTextPosition(shapex0, shapey0, shapex1, shapey1, options, textangle, textBB); + var textx = textPos.textx; + var texty = textPos.texty; + var xanchor = textPos.xanchor; + + // Update (x,y) position, xanchor, and angle + labelText.attr({ + 'text-anchor': { + left: 'start', + center: 'middle', + right: 'end' + }[xanchor], + y: texty, + x: textx, + transform: 'rotate(' + textangle + ',' + textx + ',' + texty + ')' + }).call(svgTextUtils.positionText, textx, texty); +}; + +function calcTextAngle(shapex0, shapey0, shapex1, shapey1) { + var dy, dx; + dx = Math.abs(shapex1 - shapex0); + if(shapex1 >= shapex0) { + dy = shapey0 - shapey1; + } else { + dy = shapey1 - shapey0; + } + return -180 / Math.PI * Math.atan2(dy, dx); +} + +function calcTextPosition(shapex0, shapey0, shapex1, shapey1, shapeOptions, actualTextAngle, textBB) { + var textPosition = shapeOptions.label.textposition; + var textAngle = shapeOptions.label.textangle; + var textPadding = shapeOptions.label.padding; + var shapeType = shapeOptions.type; + var textAngleRad = Math.PI / 180 * actualTextAngle; + var sinA = Math.sin(textAngleRad); + var cosA = Math.cos(textAngleRad); + var xanchor = shapeOptions.label.xanchor; + var yanchor = shapeOptions.label.yanchor; + + var textx, texty, paddingX, paddingY; + + // Text position functions differently for lines vs. other shapes + if(shapeType === 'line') { + // Set base position for start vs. center vs. end of line (default is 'center') + if(textPosition === 'start') { + textx = shapex0; + texty = shapey0; + } else if(textPosition === 'end') { + textx = shapex1; + texty = shapey1; + } else { // Default: center + textx = (shapex0 + shapex1) / 2; + texty = (shapey0 + shapey1) / 2; + } + + // Set xanchor if xanchor is 'auto' + if(xanchor === 'auto') { + if(textPosition === 'start') { + if(textAngle === 'auto') { + if(shapex1 > shapex0) xanchor = 'left'; + else if(shapex1 < shapex0) xanchor = 'right'; + else xanchor = 'center'; + } else { + if(shapex1 > shapex0) xanchor = 'right'; + else if(shapex1 < shapex0) xanchor = 'left'; + else xanchor = 'center'; + } + } else if(textPosition === 'end') { + if(textAngle === 'auto') { + if(shapex1 > shapex0) xanchor = 'right'; + else if(shapex1 < shapex0) xanchor = 'left'; + else xanchor = 'center'; + } else { + if(shapex1 > shapex0) xanchor = 'left'; + else if(shapex1 < shapex0) xanchor = 'right'; + else xanchor = 'center'; + } + } else { + xanchor = 'center'; + } + } + + // Special case for padding when angle is 'auto' for lines + // Padding should be treated as an orthogonal offset in this case + // Otherwise, padding is just a simple x and y offset + var paddingConstantsX = { left: 1, center: 0, right: -1 }; + var paddingConstantsY = { bottom: -1, middle: 0, top: 1 }; + if(textAngle === 'auto') { + // Set direction to apply padding (based on `yanchor` only) + var paddingDirection = paddingConstantsY[yanchor]; + paddingX = -textPadding * sinA * paddingDirection; + paddingY = textPadding * cosA * paddingDirection; + } else { + // Set direction to apply padding (based on `xanchor` and `yanchor`) + var paddingDirectionX = paddingConstantsX[xanchor]; + var paddingDirectionY = paddingConstantsY[yanchor]; + paddingX = textPadding * paddingDirectionX; + paddingY = textPadding * paddingDirectionY; + } + textx = textx + paddingX; + texty = texty + paddingY; + } else { + // Text position for shapes that are not lines + // calc horizontal position + // Horizontal needs a little extra padding to look balanced + paddingX = textPadding + 3; + if(textPosition.indexOf('right') !== -1) { + textx = Math.max(shapex0, shapex1) - paddingX; + if(xanchor === 'auto') xanchor = 'right'; + } else if(textPosition.indexOf('left') !== -1) { + textx = Math.min(shapex0, shapex1) + paddingX; + if(xanchor === 'auto') xanchor = 'left'; + } else { // Default: center + textx = (shapex0 + shapex1) / 2; + if(xanchor === 'auto') xanchor = 'center'; + } + + // calc vertical position + if(textPosition.indexOf('top') !== -1) { + texty = Math.min(shapey0, shapey1); + } else if(textPosition.indexOf('bottom') !== -1) { + texty = Math.max(shapey0, shapey1); + } else { + texty = (shapey0 + shapey1) / 2; + } + // Apply padding + paddingY = textPadding; + if(yanchor === 'bottom') { + texty = texty - paddingY; + } else if(yanchor === 'top') { + texty = texty + paddingY; + } + } + + // Shift vertical (& horizontal) position according to `yanchor` + var shiftFraction = FROM_TL[yanchor]; + // Adjust so that text is anchored at top of first line rather than at baseline of first line + var baselineAdjust = shapeOptions.label.font.size; + var textHeight = textBB.height; + var xshift = (textHeight * shiftFraction - baselineAdjust) * sinA; + var yshift = -(textHeight * shiftFraction - baselineAdjust) * cosA; + + return { textx: textx + xshift, texty: texty + yshift, xanchor: xanchor }; +} diff --git a/src/components/shapes/display_outlines.js b/src/components/shapes/display_outlines.js index 6f19980ed82..f1b9e8d50f9 100644 --- a/src/components/shapes/display_outlines.js +++ b/src/components/shapes/display_outlines.js @@ -24,8 +24,11 @@ var helpers = require('./draw_newshape/helpers'); var pointsOnRectangle = helpers.pointsOnRectangle; var pointsOnEllipse = helpers.pointsOnEllipse; var writePaths = helpers.writePaths; -var newShapes = require('./draw_newshape/newshapes'); +var newShapes = require('./draw_newshape/newshapes').newShapes; +var createShapeObj = require('./draw_newshape/newshapes').createShapeObj; var newSelections = require('../selections/draw_newselection/newselections'); +var drawLabel = require('./display_labels'); + module.exports = function displayOutlines(polygons, outlines, dragOptions, nCalls) { if(!nCalls) nCalls = 0; @@ -95,6 +98,13 @@ module.exports = function displayOutlines(polygons, outlines, dragOptions, nCall addGroupControllers(); } + // draw label + if(isDrawMode && dragOptions.hasText) { + var shapeGroup = zoomLayer.select('.label-temp'); + var shapeOptions = createShapeObj(outlines, dragOptions, dragOptions.dragmode); + drawLabel(gd, 'label-temp', shapeOptions, shapeGroup); + } + function startDragVertex(evt) { indexI = +evt.srcElement.getAttribute('data-i'); indexJ = +evt.srcElement.getAttribute('data-j'); diff --git a/src/components/shapes/draw.js b/src/components/shapes/draw.js index 13f08719820..5f358145f93 100644 --- a/src/components/shapes/draw.js +++ b/src/components/shapes/draw.js @@ -8,6 +8,7 @@ var Axes = require('../../plots/cartesian/axes'); var readPaths = require('./draw_newshape/helpers').readPaths; var displayOutlines = require('./display_outlines'); +var drawLabel = require('./display_labels'); var clearOutlineControllers = require('./handle_outline').clearOutlineControllers; @@ -18,13 +19,9 @@ var arrayEditor = require('../../plot_api/plot_template').arrayEditor; var dragElement = require('../dragelement'); var setCursor = require('../../lib/setcursor'); -var svgTextUtils = require('../../lib/svg_text_utils'); - var constants = require('./constants'); var helpers = require('./helpers'); var getPathString = helpers.getPathString; -var shapeLabelTexttemplateVars = require('./label_texttemplate'); -var FROM_TL = require('../../constants/alignment').FROM_TL; // Shapes are stored in gd.layout.shapes, an array of objects @@ -603,259 +600,6 @@ function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer, editHe } } -function drawLabel(gd, index, options, shapeGroup) { - // Remove existing label - shapeGroup.selectAll('.shape-label').remove(); - - // If no label text or texttemplate, return - if(!(options.label.text || options.label.texttemplate)) return; - - // Text template overrides text - var text; - if(options.label.texttemplate) { - var templateValues = {}; - if(options.type !== 'path') { - var _xa = Axes.getFromId(gd, options.xref); - var _ya = Axes.getFromId(gd, options.yref); - for(var key in shapeLabelTexttemplateVars) { - var val = shapeLabelTexttemplateVars[key](options, _xa, _ya); - if(val !== undefined) templateValues[key] = val; - } - } - text = Lib.texttemplateStringForShapes(options.label.texttemplate, - {}, - gd._fullLayout._d3locale, - templateValues); - } else { - text = options.label.text; - } - - var labelGroupAttrs = { - 'data-index': index, - }; - var font = options.label.font; - - var labelTextAttrs = { - 'data-notex': 1 - }; - - var labelGroup = shapeGroup.append('g') - .attr(labelGroupAttrs) - .classed('shape-label', true); - var labelText = labelGroup.append('text') - .attr(labelTextAttrs) - .classed('shape-label-text', true) - .text(text); - - // Get x and y bounds of shape - var shapex0, shapex1, shapey0, shapey1; - if(options.path) { - // If shape is defined as a path, get the - // min and max bounds across all polygons in 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 { - // Otherwise, we use the x and y bounds defined in the shape options - // and convert them to pixel coordinates - // 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); - } - - // Handle `auto` angle - var textangle = options.label.textangle; - if(textangle === 'auto') { - if(options.type === 'line') { - // Auto angle for line is same angle as line - textangle = calcTextAngle(shapex0, shapey0, shapex1, shapey1); - } else { - // Auto angle for all other shapes is 0 - textangle = 0; - } - } - - // Do an initial render so we can get the text bounding box height - labelText.call(function(s) { - s.call(Drawing.font, font).attr({}); - svgTextUtils.convertToTspans(s, gd); - return s; - }); - var textBB = Drawing.bBox(labelText.node()); - - // Calculate correct (x,y) for text - // We also determine true xanchor since xanchor depends on position when set to 'auto' - var textPos = calcTextPosition(shapex0, shapey0, shapex1, shapey1, options, textangle, textBB); - var textx = textPos.textx; - var texty = textPos.texty; - var xanchor = textPos.xanchor; - - // Update (x,y) position, xanchor, and angle - labelText.attr({ - 'text-anchor': { - left: 'start', - center: 'middle', - right: 'end' - }[xanchor], - y: texty, - x: textx, - transform: 'rotate(' + textangle + ',' + textx + ',' + texty + ')' - }).call(svgTextUtils.positionText, textx, texty); -} - -function calcTextAngle(shapex0, shapey0, shapex1, shapey1) { - var dy, dx; - dx = Math.abs(shapex1 - shapex0); - if(shapex1 >= shapex0) { - dy = shapey0 - shapey1; - } else { - dy = shapey1 - shapey0; - } - return -180 / Math.PI * Math.atan2(dy, dx); -} - -function calcTextPosition(shapex0, shapey0, shapex1, shapey1, shapeOptions, actualTextAngle, textBB) { - var textPosition = shapeOptions.label.textposition; - var textAngle = shapeOptions.label.textangle; - var textPadding = shapeOptions.label.padding; - var shapeType = shapeOptions.type; - var textAngleRad = Math.PI / 180 * actualTextAngle; - var sinA = Math.sin(textAngleRad); - var cosA = Math.cos(textAngleRad); - var xanchor = shapeOptions.label.xanchor; - var yanchor = shapeOptions.label.yanchor; - - var textx, texty, paddingX, paddingY; - - // Text position functions differently for lines vs. other shapes - if(shapeType === 'line') { - // Set base position for start vs. center vs. end of line (default is 'center') - if(textPosition === 'start') { - textx = shapex0; - texty = shapey0; - } else if(textPosition === 'end') { - textx = shapex1; - texty = shapey1; - } else { // Default: center - textx = (shapex0 + shapex1) / 2; - texty = (shapey0 + shapey1) / 2; - } - - // Set xanchor if xanchor is 'auto' - if(xanchor === 'auto') { - if(textPosition === 'start') { - if(textAngle === 'auto') { - if(shapex1 > shapex0) xanchor = 'left'; - else if(shapex1 < shapex0) xanchor = 'right'; - else xanchor = 'center'; - } else { - if(shapex1 > shapex0) xanchor = 'right'; - else if(shapex1 < shapex0) xanchor = 'left'; - else xanchor = 'center'; - } - } else if(textPosition === 'end') { - if(textAngle === 'auto') { - if(shapex1 > shapex0) xanchor = 'right'; - else if(shapex1 < shapex0) xanchor = 'left'; - else xanchor = 'center'; - } else { - if(shapex1 > shapex0) xanchor = 'left'; - else if(shapex1 < shapex0) xanchor = 'right'; - else xanchor = 'center'; - } - } else { - xanchor = 'center'; - } - } - - // Special case for padding when angle is 'auto' for lines - // Padding should be treated as an orthogonal offset in this case - // Otherwise, padding is just a simple x and y offset - var paddingConstantsX = { left: 1, center: 0, right: -1 }; - var paddingConstantsY = { bottom: -1, middle: 0, top: 1 }; - if(textAngle === 'auto') { - // Set direction to apply padding (based on `yanchor` only) - var paddingDirection = paddingConstantsY[yanchor]; - paddingX = -textPadding * sinA * paddingDirection; - paddingY = textPadding * cosA * paddingDirection; - } else { - // Set direction to apply padding (based on `xanchor` and `yanchor`) - var paddingDirectionX = paddingConstantsX[xanchor]; - var paddingDirectionY = paddingConstantsY[yanchor]; - paddingX = textPadding * paddingDirectionX; - paddingY = textPadding * paddingDirectionY; - } - textx = textx + paddingX; - texty = texty + paddingY; - } else { - // Text position for shapes that are not lines - // calc horizontal position - // Horizontal needs a little extra padding to look balanced - paddingX = textPadding + 3; - if(textPosition.indexOf('right') !== -1) { - textx = Math.max(shapex0, shapex1) - paddingX; - if(xanchor === 'auto') xanchor = 'right'; - } else if(textPosition.indexOf('left') !== -1) { - textx = Math.min(shapex0, shapex1) + paddingX; - if(xanchor === 'auto') xanchor = 'left'; - } else { // Default: center - textx = (shapex0 + shapex1) / 2; - if(xanchor === 'auto') xanchor = 'center'; - } - - // calc vertical position - if(textPosition.indexOf('top') !== -1) { - texty = Math.min(shapey0, shapey1); - } else if(textPosition.indexOf('bottom') !== -1) { - texty = Math.max(shapey0, shapey1); - } else { - texty = (shapey0 + shapey1) / 2; - } - // Apply padding - paddingY = textPadding; - if(yanchor === 'bottom') { - texty = texty - paddingY; - } else if(yanchor === 'top') { - texty = texty + paddingY; - } - } - - // Shift vertical (& horizontal) position according to `yanchor` - var shiftFraction = FROM_TL[yanchor]; - // Adjust so that text is anchored at top of first line rather than at baseline of first line - var baselineAdjust = shapeOptions.label.font.size; - var textHeight = textBB.height; - var xshift = (textHeight * shiftFraction - baselineAdjust) * sinA; - var yshift = -(textHeight * shiftFraction - baselineAdjust) * cosA; - - return { textx: textx + xshift, texty: texty + yshift, xanchor: xanchor }; -} - function movePath(pathIn, moveX, moveY) { return pathIn.replace(constants.segmentRE, function(segment) { var paramNumber = 0; diff --git a/src/components/shapes/draw_newshape/newshapes.js b/src/components/shapes/draw_newshape/newshapes.js index 582b307c603..42443374543 100644 --- a/src/components/shapes/draw_newshape/newshapes.js +++ b/src/components/shapes/draw_newshape/newshapes.js @@ -25,20 +25,12 @@ var writePaths = helpers.writePaths; var ellipseOver = helpers.ellipseOver; var fixDatesForPaths = helpers.fixDatesForPaths; -module.exports = function newShapes(outlines, dragOptions) { +function newShapes(outlines, dragOptions) { if(!outlines.length) return; var e = outlines[0][0]; // pick first if(!e) return; - var d = e.getAttribute('d'); var gd = dragOptions.gd; - var newStyle = gd._fullLayout.newshape; - - var plotinfo = dragOptions.plotinfo; - var xaxis = plotinfo.xaxis; - var yaxis = plotinfo.yaxis; - var xPaper = !!plotinfo.domain || !plotinfo.xaxis; - var yPaper = !!plotinfo.domain || !plotinfo.yaxis; var isActiveShape = dragOptions.isActiveShape; var dragmode = dragOptions.dragmode; @@ -70,8 +62,64 @@ module.exports = function newShapes(outlines, dragOptions) { } } - var isOpenMode = openMode(dragmode); + var newShape = createShapeObj(outlines, dragOptions, dragmode); + + clearOutline(gd); + + var editHelpers = dragOptions.editHelpers; + var modifyItem = (editHelpers || {}).modifyItem; + + var allShapes = []; + for(var q = 0; q < shapes.length; q++) { + var beforeEdit = gd._fullLayout.shapes[q]; + allShapes[q] = beforeEdit._input; + + if( + isActiveShape !== undefined && + q === gd._fullLayout._activeShapeIndex + ) { + var afterEdit = newShape; + + switch(beforeEdit.type) { + case 'line': + case 'rect': + case 'circle': + modifyItem('x0', afterEdit.x0); + modifyItem('x1', afterEdit.x1); + modifyItem('y0', afterEdit.y0); + modifyItem('y1', afterEdit.y1); + break; + + case 'path': + modifyItem('path', afterEdit.path); + break; + } + } + } + + if(isActiveShape === undefined) { + allShapes.push(newShape); // add new shape + return allShapes; + } + + return editHelpers ? editHelpers.getUpdateObj() : {}; +} + +function createShapeObj(outlines, dragOptions, dragmode) { + var e = outlines[0][0]; // pick first outline + var gd = dragOptions.gd; + var d = e.getAttribute('d'); + var newStyle = gd._fullLayout.newshape; + var plotinfo = dragOptions.plotinfo; + var isActiveShape = dragOptions.isActiveShape; + + var xaxis = plotinfo.xaxis; + var yaxis = plotinfo.yaxis; + var xPaper = !!plotinfo.domain || !plotinfo.xaxis; + var yPaper = !!plotinfo.domain || !plotinfo.yaxis; + + var isOpenMode = openMode(dragmode); var polygons = readPaths(d, gd, plotinfo, isActiveShape); var newShape = { @@ -191,44 +239,10 @@ module.exports = function newShapes(outlines, dragOptions) { newShape.path = writePaths(polygons); cell = null; } + return newShape; +} - clearOutline(gd); - - var editHelpers = dragOptions.editHelpers; - var modifyItem = (editHelpers || {}).modifyItem; - - var allShapes = []; - for(var q = 0; q < shapes.length; q++) { - var beforeEdit = gd._fullLayout.shapes[q]; - allShapes[q] = beforeEdit._input; - - if( - isActiveShape !== undefined && - q === gd._fullLayout._activeShapeIndex - ) { - var afterEdit = newShape; - - switch(beforeEdit.type) { - case 'line': - case 'rect': - case 'circle': - modifyItem('x0', afterEdit.x0); - modifyItem('x1', afterEdit.x1); - modifyItem('y0', afterEdit.y0); - modifyItem('y1', afterEdit.y1); - break; - - case 'path': - modifyItem('path', afterEdit.path); - break; - } - } - } - - if(isActiveShape === undefined) { - allShapes.push(newShape); // add new shape - return allShapes; - } - - return editHelpers ? editHelpers.getUpdateObj() : {}; +module.exports = { + newShapes: newShapes, + createShapeObj: createShapeObj, };