Skip to content

Commit 47870fa

Browse files
committed
prep to edit curves
- preserve first element of points in polygons for path command e.g. M, L, etc.
1 parent 1040786 commit 47870fa

File tree

2 files changed

+106
-78
lines changed

2 files changed

+106
-78
lines changed

src/plots/cartesian/new_shape.js

Lines changed: 87 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,19 @@ var handleOutline = require('./handle_outline');
3939
var clearOutlineControllers = handleOutline.clearOutlineControllers;
4040
var clearSelect = handleOutline.clearSelect;
4141

42-
function recordPositions(polygonsOut, polygonsIn) {
42+
function recordPositions(polygonsOut, polygonsIn) { // copy & clean (i.e. skip duplicates)
4343
for(var i = 0; i < polygonsIn.length; i++) {
4444
polygonsOut[i] = [];
4545
var len = polygonsIn[i].length;
46-
for(var j = 0; j < len; j++) {
46+
for(var newJ = 0, j = 0; j < len; j++) {
4747
// skip close points
48-
if(dist(polygonsIn[i][j], polygonsIn[i][(j + 1) % len]) < 1) continue;
48+
if(j > 0 && dist(polygonsIn[i][j], polygonsIn[i][(j + 1) % len]) < 1) continue;
4949

50-
polygonsOut[i].push([
51-
polygonsIn[i][j][0],
52-
polygonsIn[i][j][1]
53-
]);
50+
polygonsOut[i][newJ] = [];
51+
for(var k = 0; k < polygonsIn[i][newJ].length; k++) {
52+
polygonsOut[i][newJ][k] = polygonsIn[i][j][k];
53+
}
54+
newJ++;
5455
}
5556
}
5657
return polygonsOut;
@@ -90,16 +91,8 @@ function displayOutlines(polygonsIn, outlines, dragOptions, nCalls) {
9091

9192
if(isDrawMode) gd._fullLayout._drawing = true;
9293

93-
var paths = [];
94-
for(var k = 0; k < polygons.length; k++) {
95-
// create outline path
96-
paths.push(
97-
providePath(polygons[k], isOpenMode)
98-
);
99-
}
100-
10194
// make outline
102-
outlines.attr('d', writePaths(paths, isOpenMode));
95+
outlines.attr('d', writePaths(polygons, isOpenMode));
10396

10497
// add controllers
10598
var rVertexController = MINSELECT * 1.5; // bigger vertex buttons
@@ -127,8 +120,8 @@ function displayOutlines(polygonsIn, outlines, dragOptions, nCalls) {
127120
function moveVertexController(dx, dy) {
128121
if(!polygons.length) return;
129122

130-
var x0 = copyPolygons[indexI][indexJ][0];
131-
var y0 = copyPolygons[indexI][indexJ][1];
123+
var x0 = copyPolygons[indexI][indexJ][1];
124+
var y0 = copyPolygons[indexI][indexJ][2];
132125

133126
var cell = polygons[indexI];
134127
var len = cell.length;
@@ -139,29 +132,29 @@ function displayOutlines(polygonsIn, outlines, dragOptions, nCalls) {
139132
// move other corners of rectangle
140133
var pos = cell[q];
141134

142-
if(pos[0] === cell[indexJ][0]) {
143-
pos[0] = x0 + dx;
135+
if(pos[1] === cell[indexJ][1]) {
136+
pos[1] = x0 + dx;
144137
}
145138

146-
if(pos[1] === cell[indexJ][1]) {
147-
pos[1] = y0 + dy;
139+
if(pos[2] === cell[indexJ][2]) {
140+
pos[2] = y0 + dy;
148141
}
149142
}
150143
// move the corner
151-
cell[indexJ][0] = x0 + dx;
152-
cell[indexJ][1] = y0 + dy;
144+
cell[indexJ][1] = x0 + dx;
145+
cell[indexJ][2] = y0 + dy;
153146

154147
if(!pointsShapeRectangle(cell)) {
155148
// reject result to rectangles with ensure areas
156149
for(var j = 0; j < len; j++) {
157-
for(var k = 0; k < 2; k++) {
150+
for(var k = 0; k < 3; k++) {
158151
cell[j][k] = copyPolygons[indexI][j][k];
159152
}
160153
}
161154
}
162155
} else { // other polylines
163-
cell[indexJ][0] = x0 + dx;
164-
cell[indexJ][1] = y0 + dy;
156+
cell[indexJ][1] = x0 + dx;
157+
cell[indexJ][2] = y0 + dy;
165158
}
166159

167160
redraw();
@@ -211,10 +204,10 @@ function displayOutlines(polygonsIn, outlines, dragOptions, nCalls) {
211204
var maxY;
212205
if(onRect) {
213206
// compute bounding box
214-
minX = calcMin(cell, 0);
215-
minY = calcMin(cell, 1);
216-
maxX = calcMax(cell, 0);
217-
maxY = calcMax(cell, 1);
207+
minX = calcMin(cell, 1);
208+
minY = calcMin(cell, 2);
209+
maxX = calcMax(cell, 1);
210+
maxY = calcMax(cell, 2);
218211
}
219212

220213
vertexDragOptions[i] = [];
@@ -228,8 +221,8 @@ function displayOutlines(polygonsIn, outlines, dragOptions, nCalls) {
228221
continue;
229222
}
230223

231-
var x = cell[j][0];
232-
var y = cell[j][1];
224+
var x = cell[j][1];
225+
var y = cell[j][2];
233226

234227
var rIcon = 3;
235228
var button = g.append(onRect ? 'rect' : 'circle')
@@ -301,11 +294,11 @@ function displayOutlines(polygonsIn, outlines, dragOptions, nCalls) {
301294

302295
for(var i = 0; i < polygons.length; i++) {
303296
for(var j = 0; j < polygons[i].length; j++) {
304-
var x0 = copyPolygons[i][j][0];
305-
var y0 = copyPolygons[i][j][1];
297+
var x0 = copyPolygons[i][j][1];
298+
var y0 = copyPolygons[i][j][2];
306299

307-
polygons[i][j][0] = x0 + dx;
308-
polygons[i][j][1] = y0 + dy;
300+
polygons[i][j][1] = x0 + dx;
301+
polygons[i][j][2] = y0 + dy;
309302
}
310303
}
311304
}
@@ -344,14 +337,25 @@ function displayOutlines(polygonsIn, outlines, dragOptions, nCalls) {
344337
}
345338
}
346339

347-
function providePath(cell, isOpenMode) {
348-
return cell.join('L') + (
349-
isOpenMode ? '' : 'L' + cell[0]
350-
);
351-
}
352-
353-
function writePaths(paths, isOpenMode) {
354-
return paths.length > 0 ? 'M' + paths.join('M') + (isOpenMode ? '' : 'Z') : 'M0,0Z';
340+
function writePaths(polygons, isOpenMode) {
341+
var nI = polygons.length;
342+
if(!nI) return 'M0,0Z';
343+
344+
var str = '';
345+
for(var i = 0; i < nI; i++) {
346+
var nJ = polygons[i].length;
347+
for(var j = 0; j < nJ; j++) {
348+
var nK = polygons[i][j].length;
349+
for(var k = 0; k < nK; k++) {
350+
str += polygons[i][j][k];
351+
if(k > 0 && k < nK - 1) {
352+
str += ',';
353+
}
354+
}
355+
}
356+
if(!isOpenMode) str += 'Z';
357+
}
358+
return str;
355359
}
356360

357361
function readPaths(str, plotinfo, size, isActiveShape) {
@@ -378,6 +382,7 @@ function readPaths(str, plotinfo, size, isActiveShape) {
378382
var newPos = [];
379383

380384
var c = cmd[i][0];
385+
var w = c;
381386
switch(c) {
382387
case 'M':
383388
newPoly();
@@ -396,18 +401,21 @@ function readPaths(str, plotinfo, size, isActiveShape) {
396401
break;
397402

398403
case 'H':
404+
w = 'L'; // convert to line
399405
x = +cmd[i][1];
400406
newPos.push([x, y]);
401407

402408
break;
403409

404410
case 'V':
411+
w = 'L'; // convert to line
405412
y = +cmd[i][1];
406413
newPos.push([x, y]);
407414

408415
break;
409416

410417
case 'A':
418+
w = 'L'; // convert to line (for now)
411419
var rx = +cmd[i][1];
412420
var ry = +cmd[i][2];
413421
if(!+cmd[i][4]) {
@@ -438,21 +446,25 @@ function readPaths(str, plotinfo, size, isActiveShape) {
438446

439447
if(!plotinfo || !(plotinfo.xaxis && plotinfo.yaxis)) {
440448
polys[n].push([
449+
w,
441450
x,
442451
y
443452
]);
444453
} else if(plotinfo.domain) {
445454
polys[n].push([
455+
w,
446456
plotinfo.domain.x[0] + x / size.w,
447457
plotinfo.domain.y[1] - y / size.h
448458
]);
449459
} else if(isActiveShape === false) {
450460
polys[n].push([
461+
w,
451462
p2r(plotinfo.xaxis, x - plotinfo.xaxis._offset),
452463
p2r(plotinfo.yaxis, y - plotinfo.yaxis._offset)
453464
]);
454465
} else {
455466
polys[n].push([
467+
w,
456468
p2r(plotinfo.xaxis, x),
457469
p2r(plotinfo.yaxis, y)
458470
]);
@@ -470,8 +482,8 @@ function fixDatesOnPaths(path, xaxis, yaxis) {
470482
if(!xIsDate && !yIsDate) return path;
471483

472484
for(var i = 0; i < path.length; i++) {
473-
if(xIsDate) path[i][0] = path[i][0].replace(' ', '_');
474-
if(yIsDate) path[i][1] = path[i][1].replace(' ', '_');
485+
if(xIsDate) path[i][1] = path[i][1].replace(' ', '_');
486+
if(yIsDate) path[i][2] = path[i][2].replace(' ', '_');
475487
}
476488

477489
return path;
@@ -482,8 +494,8 @@ function almostEq(a, b) {
482494
}
483495

484496
function dist(a, b) {
485-
var dx = b[0] - a[0];
486-
var dy = b[1] - a[1];
497+
var dx = b[1] - a[1];
498+
var dy = b[2] - a[2];
487499
return Math.sqrt(
488500
dx * dx +
489501
dy * dy
@@ -509,7 +521,7 @@ function calcMax(cell, dim) {
509521
function pointsShapeRectangle(cell, len) {
510522
if(!len) len = cell.length;
511523
if(len !== 4) return false;
512-
for(var j = 0; j < 2; j++) {
524+
for(var j = 1; j < 3; j++) {
513525
var e01 = cell[0][j] - cell[1][j];
514526
var e32 = cell[3][j] - cell[2][j];
515527

@@ -522,8 +534,8 @@ function pointsShapeRectangle(cell, len) {
522534

523535
// N.B. rotated rectangles are not valid rects since rotation is not supported in shapes for now.
524536
if(
525-
!almostEq(cell[0][0], cell[1][0]) &&
526-
!almostEq(cell[0][0], cell[3][0])
537+
!almostEq(cell[0][1], cell[1][1]) &&
538+
!almostEq(cell[0][1], cell[3][1])
527539
) return false;
528540

529541
// reject cases with zero area
@@ -650,23 +662,24 @@ function addNewShapes(outlines, dragOptions) {
650662
}
651663
var isOpenMode = openMode(dragmode);
652664

653-
var newShapes = [];
654665
var polygons = readPaths(d, plotinfo, gd._fullLayout._size, isActiveShape);
655666
if(isOpenMode) {
656667
var last = polygons[0].length - 1;
657668
if( // ensure first and last positions are not the same on an open path
658-
polygons[0][0][0] === polygons[0][last][0] &&
659-
polygons[0][0][1] === polygons[0][last][1]
669+
polygons[0][0][1] === polygons[0][last][1] &&
670+
polygons[0][0][2] === polygons[0][last][2]
660671
) {
661672
polygons[0].pop();
662673
}
663674
}
675+
676+
var newShapes = [];
664677
for(var i = 0; i < polygons.length; i++) {
665678
var cell = polygons[i];
666679
var len = cell.length;
667680
if(
668-
cell[0][0] === cell[len - 1][0] &&
669-
cell[0][1] === cell[len - 1][1]
681+
cell[0][1] === cell[len - 1][1] &&
682+
cell[0][2] === cell[len - 1][2]
670683
) {
671684
len -= 1;
672685
}
@@ -697,29 +710,29 @@ function addNewShapes(outlines, dragOptions) {
697710
pointsShapeRectangle(cell, len) // should pass len here which is equal to cell.length - 1 i.e. because of the closing point
698711
) {
699712
shape.type = 'rect';
700-
shape.x0 = cell[0][0];
701-
shape.y0 = cell[0][1];
702-
shape.x1 = cell[2][0];
703-
shape.y1 = cell[2][1];
713+
shape.x0 = cell[0][1];
714+
shape.y0 = cell[0][2];
715+
shape.x1 = cell[2][1];
716+
shape.y1 = cell[2][2];
704717
} else if(
705718
dragmode === 'linedraw'
706719
) {
707720
shape.type = 'line';
708-
shape.x0 = cell[0][0];
709-
shape.y0 = cell[0][1];
710-
shape.x1 = cell[1][0];
711-
shape.y1 = cell[1][1];
721+
shape.x0 = cell[0][1];
722+
shape.y0 = cell[0][2];
723+
shape.x1 = cell[1][1];
724+
shape.y1 = cell[1][2];
712725
} else if(
713726
dragmode === 'ellipsedraw' &&
714727
(isActiveShape === false || pointsShapeEllipse(cell, len)) // should pass len here which is equal to cell.length - 1 i.e. because of the closing point
715728
) {
716729
shape.type = 'circle'; // an ellipse!
717730
var pos = {};
718731
if(isActiveShape === false) {
719-
var x0 = (cell[i090][0] + cell[i270][0]) / 2;
720-
var y0 = (cell[i000][1] + cell[i180][1]) / 2;
721-
var rx = (cell[i270][0] - cell[i090][0] + cell[i180][0] - cell[i000][0]) / 2;
722-
var ry = (cell[i270][1] - cell[i090][1] + cell[i180][1] - cell[i000][1]) / 2;
732+
var x0 = (cell[i090][1] + cell[i270][1]) / 2;
733+
var y0 = (cell[i000][2] + cell[i180][2]) / 2;
734+
var rx = (cell[i270][1] - cell[i090][1] + cell[i180][1] - cell[i000][1]) / 2;
735+
var ry = (cell[i270][2] - cell[i090][2] + cell[i180][2] - cell[i000][2]) / 2;
723736
pos = ellipseOver({
724737
x0: x0,
725738
y0: y0,
@@ -728,10 +741,10 @@ function addNewShapes(outlines, dragOptions) {
728741
});
729742
} else {
730743
pos = ellipseOver({
731-
x0: (cell[i000][0] + cell[i180][0]) / 2,
732-
y0: (cell[i000][1] + cell[i180][1]) / 2,
733-
x1: cell[i045][0],
734-
y1: cell[i045][1]
744+
x0: (cell[i000][1] + cell[i180][1]) / 2,
745+
y0: (cell[i000][2] + cell[i180][2]) / 2,
746+
x1: cell[i045][1],
747+
y1: cell[i045][2]
735748
});
736749
}
737750

@@ -745,9 +758,7 @@ function addNewShapes(outlines, dragOptions) {
745758
fixDatesOnPaths(cell, xaxis, yaxis);
746759
}
747760

748-
shape.path = writePaths([
749-
providePath(cell, isOpenMode)
750-
], isOpenMode);
761+
shape.path = writePaths([cell], isOpenMode);
751762
}
752763

753764
newShapes.push(shape);

0 commit comments

Comments
 (0)