From 14eb796a381f304188887b363af70c670ad0b8e8 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 30 Apr 2015 22:06:34 -0400 Subject: [PATCH 01/13] Capture shift and setup cursor for orthogonal drawing --- css/map.css | 6 ++++++ dist/img/cursor-draw-square.png | Bin 0 -> 772 bytes dist/img/cursor-draw-square2x.png | Bin 0 -> 1153 bytes js/id/behavior/draw.js | 29 +++++++++++++++++++++++++++-- 4 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 dist/img/cursor-draw-square.png create mode 100644 dist/img/cursor-draw-square2x.png diff --git a/css/map.css b/css/map.css index c7a166e438..5006cfcec7 100644 --- a/css/map.css +++ b/css/map.css @@ -1451,6 +1451,12 @@ g.turn circle { cursor: url(img/cursor-draw.png) 9 9, crosshair; /* FF */ } +.mode-add-line .behavior-draworthogonal, +.mode-add-area .behavior-draworthogonal { + cursor: crosshair; /* Opera */ + cursor: url(img/cursor-draw-square.png) 9 9, crosshair; /* FF */ +} + .mode-draw-line .way.hover, .mode-draw-area .way.hover, .mode-add-line .way.hover, diff --git a/dist/img/cursor-draw-square.png b/dist/img/cursor-draw-square.png new file mode 100644 index 0000000000000000000000000000000000000000..bbe395157c3971227b96e77d3d4766751e844ccb GIT binary patch literal 772 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>A_ld`MjvE3l4Bq~ z1&ac`YR6@x4-YmwE}ui{k-$LK@^oYa7iKwzugHgQJz1rL1tCx-S-NWFgd zso2h5?l=qg6P~*aSvGN{JfDBZoUPF&&&K!VCboq$FV0>r`I2+C$G=S`zWiP**A~Cy z`jM;ax?cBEl+5vb*FX)P+_iVTD^wXLsaTr%8XpYEyt3_Wp|1Oy0^Ogxx7Htey3#=_ zU7j~r{;~1hPueFPv*&(vReJIJpUknwyNtWJlT(s_u&M=knl z9xXVu#_(A0V~tl6!;jP!SQPlWNbY5`Su4(0wJcR2F5qu_gyh_r`Lfdb=cb4&a#`N2#P)4Ak%|72?WDB&b(LF(uhPb+k;nMw*sF^~t67GGZk231=-}q3 s6*PsHFVdQ&MBb@0JM_~6#xJL literal 0 HcmV?d00001 diff --git a/dist/img/cursor-draw-square2x.png b/dist/img/cursor-draw-square2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9729c7f2217edf51d96606c91f88badb47c18d9f GIT binary patch literal 1153 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$Ysfq^kHGbExU!q>+tIX_n~ zF(p4KRj(qq0H}k3!KT6r$jnVGNmQuF&B-gas<2f8tFQvHLBje<3ScEA*|tg%z5xo( z`9-M;rg|oN21<5Z3JMA~MJZ`kK`w4k?LeNbQbtKhft9{~d3m{Bxv^e;QM$gNrKP35 zfswwEkuFe$ZgFK^Nn(X=Ua>O75STeGsl~}fnFS@8`FRQ;a}$&DOG|8(lt3220mPjp znP~`{@`|C}0(wv%B%^PrXP^%^8>rO=Bx>bfl$i>&8Dzelp$%9iiWt-$8-0-FNREN{ z6f6q#svVb&K0Mg$xO@(&M=~%lE%$VB42j@;8+o?xQh-Pv_X`)bjF=gUn+jODg}1Xz z=2X$E;TL(ty<`5={sf**{)d-ZPd2bP8Ac;$PqExc=7+-+s3`JydnL%l*@8 zYwmB|igd+&m#zg|+UI6D|Gd5Gj;yV}(hVdc{?^q8uD&W3pli7L`V5VYN$CO%@ybbs z7BaKGR@KhG=)U;kqMJGQ4i{HAS_hNb=*t|Y$wYEgSx6`%^al8pCSo5gob&&X}4n6AjfQhR!nU1v%6 z(MQHSLcSCI0?HUaeVe0xUwXz(zJnTtZHH~j`F2g%>vMU@+6gO^U-wrX5tyOQ&)f6T zUt;0)*DrhB9&a;<;&Mt-5NlJ~R%$uXPrda=kYZB=kKKeVuS%@+mOTpA$vxWrOeyG- zJI@iP_9Mqa-aSmLKl+_3aEk7x6MkWD9+XN<`7S=|bu6EZbnoZ0+4p|mQ2%AheBa*Y ze*d+LN4dEl2~YXS{Eqv7bwG}gV(PRHnbD4>A*x2Z9slOOE}fhc-0_Ip>&B;)K)sev zHy9r7Rqari#CgVS(Vw)Lr(UrH@`)(@^^tzSDD^CHXH4G_i}~kGO>Q)AVa#GNpX;a0 z7^WqfYc~5NKifpPd=JG=wVjF?|L>$RJYHsg`oaGRj&kpAr5G938vJ3r9Ian_Z{2ql zuRpB^Z@=AUE!VHx^tx>K^ol=`#o+~8ypv`H{uHcTRWZrJli>+F@4jta|Joizo+($j cd1~%|)?(wd(^G<4c|f_})78&qol`;+0E_*_Gynhq literal 0 HcmV?d00001 diff --git a/js/id/behavior/draw.js b/js/id/behavior/draw.js index 87027fb31c..40f003e6ab 100644 --- a/js/id/behavior/draw.js +++ b/js/id/behavior/draw.js @@ -10,13 +10,27 @@ iD.behavior.Draw = function(context) { closeTolerance = 4, tolerance = 12; + + function keydown() { + if (d3.event && d3.event.shiftKey) { + context.surface() + .classed('behavior-draworthogonal', true); + } + } + + function keyup() { + if (!d3.event || !d3.event.shiftKey) { + context.surface() + .classed('behavior-draworthogonal', false); + } + } + function datum() { if (d3.event.altKey) return {}; else return d3.event.target.__data__ || {}; } function mousedown() { - function point() { var p = context.container().node(); return touchId !== null ? d3.touches(p).filter(function(p) { @@ -112,6 +126,12 @@ iD.behavior.Draw = function(context) { d3.select(document) .call(keybinding); + d3.select(window) + .on('keydown.select', keydown) + .on('keyup.select', keyup); + + keydown(); + return draw; } @@ -129,8 +149,13 @@ iD.behavior.Draw = function(context) { .on('mousedown.draw', null) .on('mousemove.draw', null); + keyup(); + d3.select(window) - .on('mouseup.draw', null); + .on('mouseup.draw', null) + .on('keydown.select', null) + .on('keyup.select', null); + d3.select(document) .call(keybinding.off); From 0f162381c2354c8d507f3cfd341776cf51724566 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 2 May 2015 00:42:25 -0400 Subject: [PATCH 02/13] Initial work on orthonogal drawing * update draw behavior so watch for shift key modifier and send initial click --- js/id/behavior/draw.js | 70 ++++++++++++++++++++++---------------- js/id/behavior/draw_way.js | 2 ++ js/id/modes/add_area.js | 11 +++--- js/id/modes/add_line.js | 11 +++--- js/id/modes/draw_area.js | 5 +-- js/id/modes/draw_line.js | 5 +-- 6 files changed, 61 insertions(+), 43 deletions(-) diff --git a/js/id/behavior/draw.js b/js/id/behavior/draw.js index 40f003e6ab..2e92bc5184 100644 --- a/js/id/behavior/draw.js +++ b/js/id/behavior/draw.js @@ -38,43 +38,55 @@ iD.behavior.Draw = function(context) { })[0] : d3.mouse(p); } - var element = d3.select(this), - touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null, - t1 = +new Date(), - p1 = point(); + if (d3.event.shiftKey) { + context.mode().option = 'orthogonal'; + d3.event.preventDefault(); + d3.event.stopPropagation(); + click(); - element.on('mousemove.draw', null); + } else { + var element = d3.select(this), + touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null, + t1 = +new Date(), + p1 = point(); + + element.on('mousemove.draw', null); - d3.select(window).on('mouseup.draw', function() { - var t2 = +new Date(), - p2 = point(), - dist = iD.geo.euclideanDistance(p1, p2); + d3.select(window).on('mouseup.draw', function () { + var t2 = +new Date(), + p2 = point(), + dist = iD.geo.euclideanDistance(p1, p2); - element.on('mousemove.draw', mousemove); - d3.select(window).on('mouseup.draw', null); + d3.select(window).on('mouseup.draw', null); + element.on('mousemove.draw', mousemove); - if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) { - // Prevent a quick second click - d3.select(window).on('click.draw-block', function() { - d3.event.stopPropagation(); - }, true); + if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) { + // Prevent a quick second click + d3.select(window).on('click.draw-block', function() { + d3.event.stopPropagation(); + }, true); - context.map().dblclickEnable(false); + context.map().dblclickEnable(false); - window.setTimeout(function() { - context.map().dblclickEnable(true); - d3.select(window).on('click.draw-block', null); - }, 500); + window.setTimeout(function() { + context.map().dblclickEnable(true); + d3.select(window).on('click.draw-block', null); + }, 500); - click(); - } - }); + click(); + } + }); + } } function mousemove() { event.move(datum()); } + function mouseup() { + if (context.mode().option === 'orthogonal') click(); + } + function click() { var d = datum(); if (d.type === 'way') { @@ -127,8 +139,9 @@ iD.behavior.Draw = function(context) { .call(keybinding); d3.select(window) - .on('keydown.select', keydown) - .on('keyup.select', keyup); + .on('mouseup.draw', mouseup) + .on('keydown.draw', keydown) + .on('keyup.draw', keyup); keydown(); @@ -153,9 +166,8 @@ iD.behavior.Draw = function(context) { d3.select(window) .on('mouseup.draw', null) - .on('keydown.select', null) - .on('keyup.select', null); - + .on('keydown.draw', null) + .on('keyup.draw', null); d3.select(document) .call(keybinding.off); diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index bc7d1736c1..e09c853f27 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -7,6 +7,7 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { 'operations.continue.annotation.') + context.geometry(wayId)), draw = iD.behavior.Draw(context); + var startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0, start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}), end = iD.Node({loc: context.map().mouseCoordinates()}), @@ -105,6 +106,7 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { // Accept the current position of the temporary node and continue drawing. drawWay.add = function(loc) { +console.log('behavior.DrawWay.add: got click'); // prevent duplicate nodes var last = context.hasEntity(way.nodes[way.nodes.length - (isArea ? 2 : 1)]); diff --git a/js/id/modes/add_area.js b/js/id/modes/add_area.js index 2f529b9bf9..d6954d66c5 100644 --- a/js/id/modes/add_area.js +++ b/js/id/modes/add_area.js @@ -1,10 +1,11 @@ -iD.modes.AddArea = function(context) { +iD.modes.AddArea = function(context, option) { var mode = { id: 'add-area', button: 'area', title: t('modes.add_area.title'), description: t('modes.add_area.description'), - key: '3' + key: '3', + option: option }; var behavior = iD.behavior.AddWay(context) @@ -25,7 +26,7 @@ iD.modes.AddArea = function(context) { iD.actions.AddVertex(way.id, node.id), iD.actions.AddVertex(way.id, node.id)); - context.enter(iD.modes.DrawArea(context, way.id, graph)); + context.enter(iD.modes.DrawArea(context, way.id, graph, mode.option)); } function startFromWay(loc, edge) { @@ -40,7 +41,7 @@ iD.modes.AddArea = function(context) { iD.actions.AddVertex(way.id, node.id), iD.actions.AddMidpoint({ loc: loc, edge: edge }, node)); - context.enter(iD.modes.DrawArea(context, way.id, graph)); + context.enter(iD.modes.DrawArea(context, way.id, graph, mode.option)); } function startFromNode(node) { @@ -52,7 +53,7 @@ iD.modes.AddArea = function(context) { iD.actions.AddVertex(way.id, node.id), iD.actions.AddVertex(way.id, node.id)); - context.enter(iD.modes.DrawArea(context, way.id, graph)); + context.enter(iD.modes.DrawArea(context, way.id, graph, mode.option)); } mode.enter = function() { diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js index 3dc014c728..7453eb1490 100644 --- a/js/id/modes/add_line.js +++ b/js/id/modes/add_line.js @@ -1,10 +1,11 @@ -iD.modes.AddLine = function(context) { +iD.modes.AddLine = function(context, option) { var mode = { id: 'add-line', button: 'line', title: t('modes.add_line.title'), description: t('modes.add_line.description'), - key: '2' + key: '2', + option: option }; var behavior = iD.behavior.AddWay(context) @@ -23,7 +24,7 @@ iD.modes.AddLine = function(context) { iD.actions.AddEntity(way), iD.actions.AddVertex(way.id, node.id)); - context.enter(iD.modes.DrawLine(context, way.id, baseGraph)); + context.enter(iD.modes.DrawLine(context, way.id, baseGraph, mode.option)); } function startFromWay(loc, edge) { @@ -37,7 +38,7 @@ iD.modes.AddLine = function(context) { iD.actions.AddVertex(way.id, node.id), iD.actions.AddMidpoint({ loc: loc, edge: edge }, node)); - context.enter(iD.modes.DrawLine(context, way.id, baseGraph)); + context.enter(iD.modes.DrawLine(context, way.id, baseGraph, mode.option)); } function startFromNode(node) { @@ -48,7 +49,7 @@ iD.modes.AddLine = function(context) { iD.actions.AddEntity(way), iD.actions.AddVertex(way.id, node.id)); - context.enter(iD.modes.DrawLine(context, way.id, baseGraph)); + context.enter(iD.modes.DrawLine(context, way.id, baseGraph, mode.option)); } mode.enter = function() { diff --git a/js/id/modes/draw_area.js b/js/id/modes/draw_area.js index 84382996fe..54644bc7d1 100644 --- a/js/id/modes/draw_area.js +++ b/js/id/modes/draw_area.js @@ -1,7 +1,8 @@ -iD.modes.DrawArea = function(context, wayId, baseGraph) { +iD.modes.DrawArea = function(context, wayId, baseGraph, option) { var mode = { button: 'area', - id: 'draw-area' + id: 'draw-area', + option: option }; var behavior; diff --git a/js/id/modes/draw_line.js b/js/id/modes/draw_line.js index d3e52280ff..ad9df64d54 100644 --- a/js/id/modes/draw_line.js +++ b/js/id/modes/draw_line.js @@ -1,7 +1,8 @@ -iD.modes.DrawLine = function(context, wayId, baseGraph, affix) { +iD.modes.DrawLine = function(context, wayId, baseGraph, affix, option) { var mode = { button: 'line', - id: 'draw-line' + id: 'draw-line', + option: option }; var behavior; From 68c23e71e27a5079c0b0374912cba0a9af815bbc Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 5 May 2015 21:45:23 -0400 Subject: [PATCH 03/13] 2-click shape - drag initial wall, click opposite corner --- js/id/behavior/draw.js | 29 ++++++++++++---- js/id/behavior/draw_way.js | 16 ++++++--- js/id/modes/add_area.js | 1 + js/id/modes/add_line.js | 71 ++++++++++++++++++++++++++------------ 4 files changed, 84 insertions(+), 33 deletions(-) diff --git a/js/id/behavior/draw.js b/js/id/behavior/draw.js index 2e92bc5184..6adfa86291 100644 --- a/js/id/behavior/draw.js +++ b/js/id/behavior/draw.js @@ -7,6 +7,7 @@ iD.behavior.Draw = function(context) { .on('hover', context.ui().sidebar.hover), tail = iD.behavior.Tail(), edit = iD.behavior.Edit(context), + startSegment = [], closeTolerance = 4, tolerance = 12; @@ -25,9 +26,10 @@ iD.behavior.Draw = function(context) { } } - function datum() { + function datum(p) { if (d3.event.altKey) return {}; - else return d3.event.target.__data__ || {}; + var target = p ? document.elementFromPoint(p[0], p[1]) : d3.event.target; + return (target && target.__data__) || {}; } function mousedown() { @@ -38,8 +40,9 @@ iD.behavior.Draw = function(context) { })[0] : d3.mouse(p); } - if (d3.event.shiftKey) { - context.mode().option = 'orthogonal'; + var mode = context.mode(); + if (d3.event.shiftKey && (mode.id === 'add-area' || mode.id === 'add-line')) { + mode.option = 'orthogonal'; d3.event.preventDefault(); d3.event.stopPropagation(); click(); @@ -83,8 +86,12 @@ iD.behavior.Draw = function(context) { event.move(datum()); } + function needsSegment() { + return context.mode().option === 'orthogonal' && startSegment.length < 2; + } + function mouseup() { - if (context.mode().option === 'orthogonal') click(); + if (needsSegment()) click(); } function click() { @@ -92,13 +99,17 @@ iD.behavior.Draw = function(context) { if (d.type === 'way') { var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection), edge = [d.nodes[choice.index - 1], d.nodes[choice.index]]; + if (needsSegment()) startSegment.push(choice.loc); event.clickWay(choice.loc, edge); } else if (d.type === 'node') { + if (needsSegment()) startSegment.push(d.loc); event.clickNode(d); } else { - event.click(context.map().mouseCoordinates()); + var loc = context.map().mouseCoordinates(); + if (needsSegment()) startSegment.push(loc); + event.click(loc); } } @@ -178,6 +189,12 @@ iD.behavior.Draw = function(context) { return draw; }; + draw.startSegment = function(_) { + if (!arguments.length) return startSegment; + startSegment = _ || []; + return draw; + }; + return d3.rebind(draw, event, 'on'); }; diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index e09c853f27..31ef6522f2 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -1,6 +1,6 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { var way = context.entity(wayId), - isArea = context.geometry(wayId) === 'area', + isArea = context.geometry(wayId) === 'area' || mode.option === 'orthogonal', finished = false, annotation = t((way.isDegenerate() ? 'operations.start.annotation.' : @@ -52,7 +52,8 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { } var drawWay = function(surface) { - draw.on('move', move) + draw + .on('move', move) .on('click', drawWay.add) .on('clickWay', drawWay.addWay) .on('clickNode', drawWay.addNode) @@ -60,6 +61,15 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { .on('cancel', drawWay.cancel) .on('finish', drawWay.finish); + if (mode.option === 'orthogonal') { + var seg = [start.loc]; + if (way.nodes.length > 2) { + var next = context.entity(way.nodes[1]); + seg.push(next.loc); + } + draw.startSegment(seg); + } + context.map() .dblclickEnable(false) .on('drawn.draw', setActiveElements); @@ -106,8 +116,6 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { // Accept the current position of the temporary node and continue drawing. drawWay.add = function(loc) { -console.log('behavior.DrawWay.add: got click'); - // prevent duplicate nodes var last = context.hasEntity(way.nodes[way.nodes.length - (isArea ? 2 : 1)]); if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return; diff --git a/js/id/modes/add_area.js b/js/id/modes/add_area.js index d6954d66c5..92f5c58497 100644 --- a/js/id/modes/add_area.js +++ b/js/id/modes/add_area.js @@ -61,6 +61,7 @@ iD.modes.AddArea = function(context, option) { }; mode.exit = function() { + mode.option = option; // reset context.uninstall(behavior); }; diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js index 7453eb1490..87bb49f8a7 100644 --- a/js/id/modes/add_line.js +++ b/js/id/modes/add_line.js @@ -9,22 +9,30 @@ iD.modes.AddLine = function(context, option) { }; var behavior = iD.behavior.AddWay(context) - .tail(t('modes.add_line.tail')) - .on('start', start) - .on('startFromWay', startFromWay) - .on('startFromNode', startFromNode); + .tail(t('modes.add_line.tail')) + .on('start', start) + .on('startFromWay', startFromWay) + .on('startFromNode', startFromNode); function start(loc) { var baseGraph = context.graph(), node = iD.Node({loc: loc}), way = iD.Way(); - context.perform( - iD.actions.AddEntity(node), - iD.actions.AddEntity(way), - iD.actions.AddVertex(way.id, node.id)); - - context.enter(iD.modes.DrawLine(context, way.id, baseGraph, mode.option)); + if (mode.option === 'orthogonal') { + context.perform( + iD.actions.AddEntity(node), + iD.actions.AddEntity(way), + iD.actions.AddVertex(way.id, node.id), + iD.actions.AddVertex(way.id, node.id)); + context.enter(iD.modes.DrawArea(context, way.id, baseGraph, mode.option)); + } else { + context.perform( + iD.actions.AddEntity(node), + iD.actions.AddEntity(way), + iD.actions.AddVertex(way.id, node.id)); + context.enter(iD.modes.DrawLine(context, way.id, baseGraph, undefined, mode.option)); + } } function startFromWay(loc, edge) { @@ -32,24 +40,40 @@ iD.modes.AddLine = function(context, option) { node = iD.Node({loc: loc}), way = iD.Way(); - context.perform( - iD.actions.AddEntity(node), - iD.actions.AddEntity(way), - iD.actions.AddVertex(way.id, node.id), - iD.actions.AddMidpoint({ loc: loc, edge: edge }, node)); - - context.enter(iD.modes.DrawLine(context, way.id, baseGraph, mode.option)); + if (mode.option === 'orthogonal') { + context.perform( + iD.actions.AddEntity(node), + iD.actions.AddEntity(way), + iD.actions.AddVertex(way.id, node.id), + iD.actions.AddVertex(way.id, node.id), + iD.actions.AddMidpoint({ loc: loc, edge: edge }, node)); + context.enter(iD.modes.DrawArea(context, way.id, baseGraph, mode.option)); + } else { + context.perform( + iD.actions.AddEntity(node), + iD.actions.AddEntity(way), + iD.actions.AddVertex(way.id, node.id), + iD.actions.AddMidpoint({ loc: loc, edge: edge }, node)); + context.enter(iD.modes.DrawLine(context, way.id, baseGraph, undefined, mode.option)); + } } function startFromNode(node) { - var baseGraph = context.graph(), + var graph = context.graph(), way = iD.Way(); - context.perform( - iD.actions.AddEntity(way), - iD.actions.AddVertex(way.id, node.id)); - - context.enter(iD.modes.DrawLine(context, way.id, baseGraph, mode.option)); + if (mode.option === 'orthogonal') { + context.perform( + iD.actions.AddEntity(way), + iD.actions.AddVertex(way.id, node.id), + iD.actions.AddVertex(way.id, node.id)); + context.enter(iD.modes.DrawArea(context, way.id, baseGraph, mode.option)); + } else { + context.perform( + iD.actions.AddEntity(way), + iD.actions.AddVertex(way.id, node.id)); + context.enter(iD.modes.DrawLine(context, way.id, baseGraph, undefined, mode.option)); + } } mode.enter = function() { @@ -57,6 +81,7 @@ iD.modes.AddLine = function(context, option) { }; mode.exit = function() { + mode.option = option; // reset context.uninstall(behavior); }; From c27e014ebf5395ca34dee6600b311b6b7caaa07d Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 6 May 2015 22:25:08 -0400 Subject: [PATCH 04/13] WIP: support multiple snap targets, more efficient drawWay --- js/id/behavior/draw.js | 60 +++++++++++++------- js/id/behavior/draw_way.js | 109 +++++++++++++++++++++++++++---------- js/id/modes/add_line.js | 9 +-- js/id/modes/draw_area.js | 4 +- js/id/modes/draw_line.js | 6 +- 5 files changed, 127 insertions(+), 61 deletions(-) diff --git a/js/id/behavior/draw.js b/js/id/behavior/draw.js index 6adfa86291..95d395d17c 100644 --- a/js/id/behavior/draw.js +++ b/js/id/behavior/draw.js @@ -26,10 +26,25 @@ iD.behavior.Draw = function(context) { } } - function datum(p) { - if (d3.event.altKey) return {}; - var target = p ? document.elementFromPoint(p[0], p[1]) : d3.event.target; - return (target && target.__data__) || {}; + // Depending on mode option, return an array of touch targets: + // [{ entity: entity, loc: [lon,lat] }] + // There will normally be a singular touch target at mouseLoc, + // unless we're in a special drawing mode. + function getTargets() { + var mouseLoc = context.map().mouseCoordinates(); + if (d3.event.altKey) return [{ entity: null, loc: mouseLoc }]; + + var points; + if (context.mode().option === 'orthogonal' && startSegment.length === 2) { + points = [[300, 300], [250,300]]; + return _.map(points, function(p) { + var target = document.elementFromPoint(p[0], p[1]); + return { entity: target && target.__data__, loc: context.projection.invert(p) }; + }); + + } else { + return [{ entity: d3.event.target.__data__, loc: mouseLoc }]; + } } function mousedown() { @@ -83,7 +98,7 @@ iD.behavior.Draw = function(context) { } function mousemove() { - event.move(datum()); + event.move(getTargets()); } function needsSegment() { @@ -95,21 +110,26 @@ iD.behavior.Draw = function(context) { } function click() { - var d = datum(); - if (d.type === 'way') { - var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection), - edge = [d.nodes[choice.index - 1], d.nodes[choice.index]]; - if (needsSegment()) startSegment.push(choice.loc); - event.clickWay(choice.loc, edge); - - } else if (d.type === 'node') { - if (needsSegment()) startSegment.push(d.loc); - event.clickNode(d); - - } else { - var loc = context.map().mouseCoordinates(); - if (needsSegment()) startSegment.push(loc); - event.click(loc); + var targets = getTargets(); + for (var i = 0; i < targets.length; i++) { + var more = (i !== targets.length - 1), + d = targets[i], + e = d.entity; + + if (e && e.type === 'way') { + var choice = iD.geo.chooseEdge(context.childNodes(e), context.mouse(), context.projection), + edge = [e.nodes[choice.index - 1], e.nodes[choice.index]]; + if (needsSegment()) startSegment.push(choice.loc); + event.clickWay(choice.loc, edge, more); + + } else if (e && e.type === 'node') { + if (needsSegment()) startSegment.push(e.loc); + event.clickNode(e, more); + + } else { + if (needsSegment()) startSegment.push(d.loc); + event.click(d.loc, more); + } } } diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index 31ef6522f2..23eec8b86a 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -1,23 +1,48 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { var way = context.entity(wayId), - isArea = context.geometry(wayId) === 'area' || mode.option === 'orthogonal', + isClosed = way.isClosed(), + isDegenerate = way.isDegenerate(), + isReverse = typeof index !== 'undefined', finished = false, - annotation = t((way.isDegenerate() ? + annotation = t((isDegenerate ? 'operations.start.annotation.' : 'operations.continue.annotation.') + context.geometry(wayId)), draw = iD.behavior.Draw(context); - var startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0, + // var startIndex = isReverse ? 0 : way.nodes.length - 1, + // endIndex = isReverse ? way.nodes.length - 1 : 0, + // // start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}), + // startNode = context.entity(way.nodes[startIndex]), + // endNode = isClosed ? context.entity(way.nodes[endIndex]) : null, + // addNodes = [ iD.Node({ loc: context.map().mouseCoordinates() }) ]; + // // segment; + + // if (endNode && mode.option === 'orthogonal' && way.nodes.length > 2) { + // addNodes.push(iD.Node({ loc: endNode.loc })); + // } + + // var f = context[isDegenerate ? 'replace' : 'perform'], + // actions = []; + + // _.each(addNodes, function(node) { + // actions.push(iD.actions.AddEntity(node)); + // actions.push(iD.actions.AddVertex(wayId, node.id, index)); + // }); + // actions.push(annotation); + // f.apply(context, actions); + + + var startIndex = isReverse ? 0 : way.nodes.length - 1, start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}), end = iD.Node({loc: context.map().mouseCoordinates()}), segment = iD.Way({ - nodes: typeof index === 'undefined' ? [start.id, end.id] : [end.id, start.id], - tags: _.clone(way.tags) + nodes: isReverse ? [end.id, start.id] : [start.id, end.id] + // tags: _.clone(way.tags) }); var f = context[way.isDegenerate() ? 'replace' : 'perform']; - if (isArea) { + if (isClosed) { f(iD.actions.AddEntity(end), iD.actions.AddVertex(wayId, end.id, index)); } else { @@ -26,15 +51,33 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { iD.actions.AddEntity(segment)); } - function move(datum) { - var loc; - if (datum.type === 'node' && datum.id !== end.id) { - loc = datum.loc; - } else if (datum.type === 'way' && datum.id !== segment.id) { - loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc; - } else { - loc = context.map().mouseCoordinates(); + + function move(targets) { + // // for (var i = 0; i < targets.length && i < addNodes.length; i++) { + // var snapTo = targets[0].entity, + // loc = targets[0].loc; + + // // if (snapTo) { + // // if (datum.type === 'node' && datum.id !== id) { + // // loc = datum.loc; + // // } else if (datum.type === 'way' && datum.id !== segment.id) { + // // loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc; + // // } + // // } + + // context.replace(iD.actions.MoveNode(id, loc)); + // // } + + var datum = targets[0].entity, + loc = targets[0].loc; + + if (datum) { + if (datum.type === 'node' && datum.id !== end.id) { + loc = datum.loc; + } else if (datum.type === 'way' && datum.id !== segment.id) { + loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc; + } } context.replace(iD.actions.MoveNode(end.id, loc)); @@ -42,13 +85,16 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { function undone() { finished = true; + context.pop(); context.enter(iD.modes.Browse(context)); } function setActiveElements() { - var active = isArea ? [wayId, end.id] : [segment.id, start.id, end.id]; + var active = isClosed ? [wayId, end.id] : [segment.id, start.id, end.id]; context.surface().selectAll(iD.util.entitySelector(active)) .classed('active', true); + // context.surface().selectAll(iD.util.entitySelector([way.id])) + // .classed('active', true); } var drawWay = function(surface) { @@ -99,7 +145,7 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { function ReplaceTemporaryNode(newNode) { return function(graph) { - if (isArea) { + if (isClosed) { return graph .replace(way.addNode(newNode.id, index)) .remove(end); @@ -115,9 +161,9 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { } // Accept the current position of the temporary node and continue drawing. - drawWay.add = function(loc) { + drawWay.add = function(loc, more) { // prevent duplicate nodes - var last = context.hasEntity(way.nodes[way.nodes.length - (isArea ? 2 : 1)]); + var last = context.hasEntity(way.nodes[way.nodes.length - (isClosed ? 2 : 1)]); if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return; var newNode = iD.Node({loc: loc}); @@ -127,19 +173,20 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { ReplaceTemporaryNode(newNode), annotation); - finished = true; - context.enter(mode); + if (!more) { + finished = true; + context.enter(mode); + } }; // Connect the way to an existing way. - drawWay.addWay = function(loc, edge) { + drawWay.addWay = function(loc, edge, more) { var previousEdge = startIndex ? [way.nodes[startIndex], way.nodes[startIndex - 1]] : [way.nodes[0], way.nodes[1]]; // Avoid creating duplicate segments - if (!isArea && iD.geo.edgeEqual(edge, previousEdge)) - return; + if (!isClosed && iD.geo.edgeEqual(edge, previousEdge)) return; var newNode = iD.Node({ loc: loc }); @@ -148,12 +195,14 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { ReplaceTemporaryNode(newNode), annotation); - finished = true; - context.enter(mode); + if (!more) { + finished = true; + context.enter(mode); + } }; // Connect the way to an existing node and continue drawing. - drawWay.addNode = function(node) { + drawWay.addNode = function(node, more) { // Avoid creating duplicate segments if (way.areAdjacent(node.id, way.nodes[way.nodes.length - 1])) return; @@ -162,12 +211,12 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { ReplaceTemporaryNode(node), annotation); - finished = true; - context.enter(mode); + if (!more) { + finished = true; + context.enter(mode); + } }; - // Finish the draw operation, removing the temporary node. If the way has enough - // nodes to be valid, it's selected. Otherwise, return to browse mode. drawWay.finish = function() { context.pop(); finished = true; diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js index 87bb49f8a7..599b07ff95 100644 --- a/js/id/modes/add_line.js +++ b/js/id/modes/add_line.js @@ -25,14 +25,13 @@ iD.modes.AddLine = function(context, option) { iD.actions.AddEntity(way), iD.actions.AddVertex(way.id, node.id), iD.actions.AddVertex(way.id, node.id)); - context.enter(iD.modes.DrawArea(context, way.id, baseGraph, mode.option)); } else { context.perform( iD.actions.AddEntity(node), iD.actions.AddEntity(way), iD.actions.AddVertex(way.id, node.id)); - context.enter(iD.modes.DrawLine(context, way.id, baseGraph, undefined, mode.option)); } + context.enter(iD.modes.DrawLine(context, way.id, baseGraph, undefined, mode.option)); } function startFromWay(loc, edge) { @@ -47,15 +46,14 @@ iD.modes.AddLine = function(context, option) { iD.actions.AddVertex(way.id, node.id), iD.actions.AddVertex(way.id, node.id), iD.actions.AddMidpoint({ loc: loc, edge: edge }, node)); - context.enter(iD.modes.DrawArea(context, way.id, baseGraph, mode.option)); } else { context.perform( iD.actions.AddEntity(node), iD.actions.AddEntity(way), iD.actions.AddVertex(way.id, node.id), iD.actions.AddMidpoint({ loc: loc, edge: edge }, node)); - context.enter(iD.modes.DrawLine(context, way.id, baseGraph, undefined, mode.option)); } + context.enter(iD.modes.DrawLine(context, way.id, baseGraph, undefined, mode.option)); } function startFromNode(node) { @@ -67,13 +65,12 @@ iD.modes.AddLine = function(context, option) { iD.actions.AddEntity(way), iD.actions.AddVertex(way.id, node.id), iD.actions.AddVertex(way.id, node.id)); - context.enter(iD.modes.DrawArea(context, way.id, baseGraph, mode.option)); } else { context.perform( iD.actions.AddEntity(way), iD.actions.AddVertex(way.id, node.id)); - context.enter(iD.modes.DrawLine(context, way.id, baseGraph, undefined, mode.option)); } + context.enter(iD.modes.DrawLine(context, way.id, baseGraph, undefined, mode.option)); } mode.enter = function() { diff --git a/js/id/modes/draw_area.js b/js/id/modes/draw_area.js index 54644bc7d1..e1fd2e044f 100644 --- a/js/id/modes/draw_area.js +++ b/js/id/modes/draw_area.js @@ -17,11 +17,11 @@ iD.modes.DrawArea = function(context, wayId, baseGraph, option) { var addNode = behavior.addNode; - behavior.addNode = function(node) { + behavior.addNode = function(node, more) { if (node.id === headId || node.id === tailId) { behavior.finish(); } else { - addNode(node); + addNode(node, more); } }; diff --git a/js/id/modes/draw_line.js b/js/id/modes/draw_line.js index ad9df64d54..b61bbc4c6c 100644 --- a/js/id/modes/draw_line.js +++ b/js/id/modes/draw_line.js @@ -12,16 +12,16 @@ iD.modes.DrawLine = function(context, wayId, baseGraph, affix, option) { index = (affix === 'prefix') ? 0 : undefined, headId = (affix === 'prefix') ? way.first() : way.last(); - behavior = iD.behavior.DrawWay(context, wayId, index, mode, baseGraph) + behavior = iD.behavior.DrawWay(context, wayId, (option === 'orthogonal' ? -1 : index), mode, baseGraph) .tail(t('modes.draw_line.tail')); var addNode = behavior.addNode; - behavior.addNode = function(node) { + behavior.addNode = function(node, more) { if (node.id === headId) { behavior.finish(); } else { - addNode(node); + addNode(node, more); } }; From 5edf7e6d2ac33e903f075cfa5a1815c423307128 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 11 May 2015 23:14:23 -0400 Subject: [PATCH 05/13] WIP: calculate orthogonal-mode click targets --- js/id/behavior/draw.js | 29 ++++++++++++++++++++++++++--- js/id/behavior/draw_way.js | 1 + 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/js/id/behavior/draw.js b/js/id/behavior/draw.js index 95d395d17c..b8c5ccfbf4 100644 --- a/js/id/behavior/draw.js +++ b/js/id/behavior/draw.js @@ -31,12 +31,34 @@ iD.behavior.Draw = function(context) { // There will normally be a singular touch target at mouseLoc, // unless we're in a special drawing mode. function getTargets() { + function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; } + function perpendicular(a, b, dist) { + var len = iD.geo.euclideanDistance(a, b); + return len === 0 ? [0, 0] : [ + ((b[1] - a[1]) / len) * dist, + ((b[0] - a[0]) / len) * dist * -1 + ]; + return [ + [a[0] + pvec[0], a[1] + pvec[1]], + [b[0] + pvec[0], b[1] + pvec[1]] + ]; + } + var mouseLoc = context.map().mouseCoordinates(); if (d3.event.altKey) return [{ entity: null, loc: mouseLoc }]; - var points; if (context.mode().option === 'orthogonal' && startSegment.length === 2) { - points = [[300, 300], [250,300]]; + var p0 = context.projection(startSegment[0]), + p1 = context.projection(startSegment[1]), + pMouse = context.map().mouse(), + theta = Math.atan2(p1[1] - pMouse[1], p1[0] - pMouse[0]) - + Math.atan2(p1[1] - p0[1], p1[0] - p0[0]), + height = iD.geo.euclideanDistance(p1, pMouse) * Math.sin(theta), + perp = perpendicular(p0, p1, height), + q0 = vecAdd(p0, perp), + q1 = vecAdd(p1, perp); + + var points = [q1, q0]; return _.map(points, function(p) { var target = document.elementFromPoint(p[0], p[1]); return { entity: target && target.__data__, loc: context.projection.invert(p) }; @@ -110,7 +132,8 @@ iD.behavior.Draw = function(context) { } function click() { - var targets = getTargets(); + var targets = [getTargets()[0]]; // only one target for now + for (var i = 0; i < targets.length; i++) { var more = (i !== targets.length - 1), d = targets[i], diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index 23eec8b86a..e9323c16ef 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -69,6 +69,7 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { // context.replace(iD.actions.MoveNode(id, loc)); // // } + // only one target for now.. var datum = targets[0].entity, loc = targets[0].loc; From a028e6f709bda531b02cc31f10e4a66a17381748 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 26 May 2015 09:44:32 -0400 Subject: [PATCH 06/13] WIP: more refactoring --- js/id/behavior/add_way.js | 3 +- js/id/behavior/draw.js | 41 ++++++++-------- js/id/behavior/draw_way.js | 96 ++++++++++++++++---------------------- 3 files changed, 62 insertions(+), 78 deletions(-) diff --git a/js/id/behavior/add_way.js b/js/id/behavior/add_way.js index 4a52143ee7..0a8d4f17e5 100644 --- a/js/id/behavior/add_way.js +++ b/js/id/behavior/add_way.js @@ -7,7 +7,8 @@ iD.behavior.AddWay = function(context) { .on('clickWay', event.startFromWay) .on('clickNode', event.startFromNode) .on('cancel', addWay.cancel) - .on('finish', addWay.cancel); + .on('finish', addWay.cancel) + .startSegment(null); context.map() .dblclickEnable(false); diff --git a/js/id/behavior/draw.js b/js/id/behavior/draw.js index b8c5ccfbf4..28b246704c 100644 --- a/js/id/behavior/draw.js +++ b/js/id/behavior/draw.js @@ -27,45 +27,44 @@ iD.behavior.Draw = function(context) { } // Depending on mode option, return an array of touch targets: - // [{ entity: entity, loc: [lon,lat] }] - // There will normally be a singular touch target at mouseLoc, + // [{ entity: entity, point: [x,y], loc: [lon,lat] }] + // There will normally be a singular touch target at mousePoint, // unless we're in a special drawing mode. function getTargets() { function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; } - function perpendicular(a, b, dist) { + function perpendicularVector(a, b, mag) { var len = iD.geo.euclideanDistance(a, b); return len === 0 ? [0, 0] : [ - ((b[1] - a[1]) / len) * dist, - ((b[0] - a[0]) / len) * dist * -1 - ]; - return [ - [a[0] + pvec[0], a[1] + pvec[1]], - [b[0] + pvec[0], b[1] + pvec[1]] + ((b[1] - a[1]) / len) * mag, + ((b[0] - a[0]) / len) * mag * -1 ]; } - var mouseLoc = context.map().mouseCoordinates(); - if (d3.event.altKey) return [{ entity: null, loc: mouseLoc }]; + var altKey = d3.event.altKey, + mousePoint = context.map().mouse(), + mouseLoc = context.map().mouseCoordinates(); if (context.mode().option === 'orthogonal' && startSegment.length === 2) { var p0 = context.projection(startSegment[0]), p1 = context.projection(startSegment[1]), - pMouse = context.map().mouse(), - theta = Math.atan2(p1[1] - pMouse[1], p1[0] - pMouse[0]) - + surface = context.surfaceRect(), + theta = Math.atan2(p1[1] - mousePoint[1], p1[0] - mousePoint[0]) - Math.atan2(p1[1] - p0[1], p1[0] - p0[0]), - height = iD.geo.euclideanDistance(p1, pMouse) * Math.sin(theta), - perp = perpendicular(p0, p1, height), - q0 = vecAdd(p0, perp), - q1 = vecAdd(p1, perp); + height = iD.geo.euclideanDistance(p1, mousePoint) * Math.sin(theta), + perpVec = perpendicularVector(p0, p1, height), + q0 = iD.geo.roundCoords(vecAdd(p0, perpVec)), + q1 = iD.geo.roundCoords(vecAdd(p1, perpVec)), + points = [q0, q1]; - var points = [q1, q0]; return _.map(points, function(p) { - var target = document.elementFromPoint(p[0], p[1]); - return { entity: target && target.__data__, loc: context.projection.invert(p) }; + var target = document.elementFromPoint(p[0] + surface.left, p[1] + surface.top), + data = target && target.__data__, + entity = data instanceof iD.Entity ? data : null; + return { entity: altKey ? null : entity, point: p, loc: context.projection.invert(p) }; }); } else { - return [{ entity: d3.event.target.__data__, loc: mouseLoc }]; + return [{ entity: altKey ? null : d3.event.target.__data__, point: mousePoint, loc: mouseLoc }]; } } diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index e9323c16ef..7c42452593 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -3,46 +3,43 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { isClosed = way.isClosed(), isDegenerate = way.isDegenerate(), isReverse = typeof index !== 'undefined', + isOrthogonal = (mode.option === 'orthogonal' && way.nodes.length > 2), finished = false, annotation = t((isDegenerate ? 'operations.start.annotation.' : 'operations.continue.annotation.') + context.geometry(wayId)), draw = iD.behavior.Draw(context); + var mouseCoord = context.map().mouseCoordinates(), + startIndex = isReverse ? 0 : way.nodes.length - 1, + start, end, ortho1, ortho2, segment; - // var startIndex = isReverse ? 0 : way.nodes.length - 1, - // endIndex = isReverse ? way.nodes.length - 1 : 0, - // // start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}), - // startNode = context.entity(way.nodes[startIndex]), - // endNode = isClosed ? context.entity(way.nodes[endIndex]) : null, - // addNodes = [ iD.Node({ loc: context.map().mouseCoordinates() }) ]; - // // segment; - - // if (endNode && mode.option === 'orthogonal' && way.nodes.length > 2) { - // addNodes.push(iD.Node({ loc: endNode.loc })); - // } - - // var f = context[isDegenerate ? 'replace' : 'perform'], - // actions = []; - - // _.each(addNodes, function(node) { - // actions.push(iD.actions.AddEntity(node)); - // actions.push(iD.actions.AddVertex(wayId, node.id, index)); - // }); - // actions.push(annotation); - // f.apply(context, actions); - - - var startIndex = isReverse ? 0 : way.nodes.length - 1, - start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}), - end = iD.Node({loc: context.map().mouseCoordinates()}), + if (isOrthogonal) { + start = iD.Node({ loc: context.graph().entity(way.nodes[1]).loc }), + ortho1 = iD.Node({ loc: start.loc }); + ortho2 = iD.Node({ loc: context.graph().entity(way.nodes[0]).loc }); + end = iD.Node({ loc: ortho2.loc }); + segment = iD.Way({ + nodes: [start.id, ortho1.id, ortho2.id, end.id], + tags: _.clone(way.tags) + }); + } else { + start = iD.Node({ loc: context.graph().entity(way.nodes[startIndex]).loc }), + end = iD.Node({ loc: mouseCoord }); segment = iD.Way({ - nodes: isReverse ? [end.id, start.id] : [start.id, end.id] - // tags: _.clone(way.tags) + nodes: isReverse ? [end.id, start.id] : [start.id, end.id], + tags: _.clone(way.tags) }); + } var f = context[way.isDegenerate() ? 'replace' : 'perform']; - if (isClosed) { + if (isOrthogonal) { + f(iD.actions.AddEntity(start), + iD.actions.AddEntity(ortho1), + iD.actions.AddEntity(ortho2), + iD.actions.AddEntity(end), + iD.actions.AddEntity(segment)); + } else if (isClosed) { f(iD.actions.AddEntity(end), iD.actions.AddVertex(wayId, end.id, index)); } else { @@ -52,36 +49,23 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { } - function move(targets) { - // // for (var i = 0; i < targets.length && i < addNodes.length; i++) { - // var snapTo = targets[0].entity, - // loc = targets[0].loc; - - // // if (snapTo) { - // // if (datum.type === 'node' && datum.id !== id) { - // // loc = datum.loc; - // // } else if (datum.type === 'way' && datum.id !== segment.id) { - // // loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc; - // // } - // // } - - // context.replace(iD.actions.MoveNode(id, loc)); - // // } - - // only one target for now.. - var datum = targets[0].entity, - loc = targets[0].loc; - - if (datum) { - if (datum.type === 'node' && datum.id !== end.id) { - loc = datum.loc; - } else if (datum.type === 'way' && datum.id !== segment.id) { - loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc; + for (var i = 0; i < targets.length; i++) { + var entity = targets[i].entity, + loc = targets[i].loc, + point = targets[i].point, + which = isOrthogonal ? [ortho1.id, ortho2.id][i] : end.id; + + if (entity) { + if (entity.type === 'node' && entity.id !== which) { + loc = entity.loc; + } else if (entity.type === 'way' && entity.id !== segment.id) { + loc = iD.geo.chooseEdge(context.childNodes(entity), point, context.projection).loc; + } } - } - context.replace(iD.actions.MoveNode(end.id, loc)); + context.replace(iD.actions.MoveNode(which, loc)); + } } function undone() { From e0626a73c0e07a1efa6d6084a24ad1274f142a91 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 12 Jun 2015 15:36:07 -0400 Subject: [PATCH 07/13] 2 click orthogonal drawing mode working --- js/id/behavior/draw.js | 48 +++++++------- js/id/behavior/draw_way.js | 132 ++++++++++++++++++++----------------- js/id/modes/add_line.js | 6 +- 3 files changed, 100 insertions(+), 86 deletions(-) diff --git a/js/id/behavior/draw.js b/js/id/behavior/draw.js index 28b246704c..dc4a961bd5 100644 --- a/js/id/behavior/draw.js +++ b/js/id/behavior/draw.js @@ -1,6 +1,6 @@ iD.behavior.Draw = function(context) { var event = d3.dispatch('move', 'click', 'clickWay', - 'clickNode', 'undo', 'cancel', 'finish'), + 'clickNode', 'clickTargets', 'undo', 'cancel', 'finish'), keybinding = d3.keybinding('draw'), hover = iD.behavior.Hover(context) .altDisables(true) @@ -54,7 +54,7 @@ iD.behavior.Draw = function(context) { perpVec = perpendicularVector(p0, p1, height), q0 = iD.geo.roundCoords(vecAdd(p0, perpVec)), q1 = iD.geo.roundCoords(vecAdd(p1, perpVec)), - points = [q0, q1]; + points = [q1, q0]; return _.map(points, function(p) { var target = document.elementFromPoint(p[0] + surface.left, p[1] + surface.top), @@ -131,27 +131,29 @@ iD.behavior.Draw = function(context) { } function click() { - var targets = [getTargets()[0]]; // only one target for now - - for (var i = 0; i < targets.length; i++) { - var more = (i !== targets.length - 1), - d = targets[i], - e = d.entity; - - if (e && e.type === 'way') { - var choice = iD.geo.chooseEdge(context.childNodes(e), context.mouse(), context.projection), - edge = [e.nodes[choice.index - 1], e.nodes[choice.index]]; - if (needsSegment()) startSegment.push(choice.loc); - event.clickWay(choice.loc, edge, more); - - } else if (e && e.type === 'node') { - if (needsSegment()) startSegment.push(e.loc); - event.clickNode(e, more); - - } else { - if (needsSegment()) startSegment.push(d.loc); - event.click(d.loc, more); - } + var targets = getTargets(); + + if (targets.length > 1) { + event.clickTargets(targets); + return; + } + + var d = targets[0], + e = d.entity; + + if (e && e.type === 'way') { + var choice = iD.geo.chooseEdge(context.childNodes(e), context.mouse(), context.projection), + edge = [e.nodes[choice.index - 1], e.nodes[choice.index]]; + if (needsSegment()) startSegment.push(choice.loc); + event.clickWay(choice.loc, edge); + + } else if (e && e.type === 'node') { + if (needsSegment()) startSegment.push(e.loc); + event.clickNode(e); + + } else { + if (needsSegment()) startSegment.push(d.loc); + event.click(d.loc); } } diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index 7c42452593..0b604a5285 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -15,16 +15,10 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { start, end, ortho1, ortho2, segment; if (isOrthogonal) { - start = iD.Node({ loc: context.graph().entity(way.nodes[1]).loc }), - ortho1 = iD.Node({ loc: start.loc }); + ortho1 = iD.Node({ loc: context.graph().entity(way.nodes[1]).loc }); ortho2 = iD.Node({ loc: context.graph().entity(way.nodes[0]).loc }); - end = iD.Node({ loc: ortho2.loc }); - segment = iD.Way({ - nodes: [start.id, ortho1.id, ortho2.id, end.id], - tags: _.clone(way.tags) - }); } else { - start = iD.Node({ loc: context.graph().entity(way.nodes[startIndex]).loc }), + start = iD.Node({ loc: context.graph().entity(way.nodes[startIndex]).loc }); end = iD.Node({ loc: mouseCoord }); segment = iD.Way({ nodes: isReverse ? [end.id, start.id] : [start.id, end.id], @@ -34,11 +28,10 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { var f = context[way.isDegenerate() ? 'replace' : 'perform']; if (isOrthogonal) { - f(iD.actions.AddEntity(start), - iD.actions.AddEntity(ortho1), + f(iD.actions.AddEntity(ortho1), iD.actions.AddEntity(ortho2), - iD.actions.AddEntity(end), - iD.actions.AddEntity(segment)); + iD.actions.AddVertex(wayId, ortho1.id, -1), + iD.actions.AddVertex(wayId, ortho2.id, -1)); } else if (isClosed) { f(iD.actions.AddEntity(end), iD.actions.AddVertex(wayId, end.id, index)); @@ -49,6 +42,32 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { } + function ReplaceTemporaryNode(newNode, newNode2) { + return function(graph) { + if (isOrthogonal) { // todo: make less hacky + var newWay = way + .addNode(newNode.id, -1) + .addNode(newNode2.id, -1); + return graph + .replace(newWay) + .remove(ortho1) + .remove(ortho2); + + } else if (isClosed) { + return graph + .replace(way.addNode(newNode.id, index)) + .remove(end); + + } else { + return graph + .replace(graph.entity(wayId).addNode(newNode.id, index)) + .remove(end) + .remove(segment) + .remove(start); + } + }; + } + function move(targets) { for (var i = 0; i < targets.length; i++) { var entity = targets[i].entity, @@ -56,13 +75,15 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { point = targets[i].point, which = isOrthogonal ? [ortho1.id, ortho2.id][i] : end.id; - if (entity) { - if (entity.type === 'node' && entity.id !== which) { - loc = entity.loc; - } else if (entity.type === 'way' && entity.id !== segment.id) { - loc = iD.geo.chooseEdge(context.childNodes(entity), point, context.projection).loc; - } - } +console.log('in move(), targets=' + JSON.stringify(_.pluck(targets,'point'))); +// console.log(' temp segment=' + segment.nodes); + // if (entity) { + // if (entity.type === 'node' && entity.id !== which) { + // loc = entity.loc; + // } else if (entity.type === 'way' && entity.id !== segment.id) { + // loc = iD.geo.chooseEdge(context.childNodes(entity), point, context.projection).loc; + // } + // } context.replace(iD.actions.MoveNode(which, loc)); } @@ -75,11 +96,12 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { } function setActiveElements() { - var active = isClosed ? [wayId, end.id] : [segment.id, start.id, end.id]; + var active = isOrthogonal ? [wayId, ortho1.id, ortho2.id] + : isClosed ? [wayId, end.id] + : [segment.id, start.id, end.id]; + context.surface().selectAll(iD.util.entitySelector(active)) .classed('active', true); - // context.surface().selectAll(iD.util.entitySelector([way.id])) - // .classed('active', true); } var drawWay = function(surface) { @@ -88,17 +110,13 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { .on('click', drawWay.add) .on('clickWay', drawWay.addWay) .on('clickNode', drawWay.addNode) + .on('clickTargets', drawWay.addTargets) .on('undo', context.undo) .on('cancel', drawWay.cancel) .on('finish', drawWay.finish); - if (mode.option === 'orthogonal') { - var seg = [start.loc]; - if (way.nodes.length > 2) { - var next = context.entity(way.nodes[1]); - seg.push(next.loc); - } - draw.startSegment(seg); + if (isOrthogonal) { + draw.startSegment([ortho2.loc, ortho1.loc]); } context.map() @@ -128,25 +146,26 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { .on('undone.draw', null); }; - function ReplaceTemporaryNode(newNode) { - return function(graph) { - if (isClosed) { - return graph - .replace(way.addNode(newNode.id, index)) - .remove(end); - } else { - return graph - .replace(graph.entity(wayId).addNode(newNode.id, index)) - .remove(end) - .remove(segment) - .remove(start); - } - }; - } + // For now, orthogonal mode only, assume targets.length === 2 + // todo: make less hacky.. + drawWay.addTargets = function(targets) { + var newNode1 = iD.Node({loc: targets[0].loc}), + newNode2 = iD.Node({loc: targets[1].loc}); + + context.replace( + iD.actions.AddEntity(newNode1), + iD.actions.AddEntity(newNode2), + ReplaceTemporaryNode(newNode1, newNode2), + iD.actions.ChangeTags(wayId, {building:'yes'}), // just for show, remove later.. + annotation); + + finished = true; + context.enter(iD.modes.Browse(context)); + }; // Accept the current position of the temporary node and continue drawing. - drawWay.add = function(loc, more) { + drawWay.add = function(loc) { // prevent duplicate nodes var last = context.hasEntity(way.nodes[way.nodes.length - (isClosed ? 2 : 1)]); if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return; @@ -158,14 +177,12 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { ReplaceTemporaryNode(newNode), annotation); - if (!more) { - finished = true; - context.enter(mode); - } + finished = true; + context.enter(mode); }; // Connect the way to an existing way. - drawWay.addWay = function(loc, edge, more) { + drawWay.addWay = function(loc, edge) { var previousEdge = startIndex ? [way.nodes[startIndex], way.nodes[startIndex - 1]] : [way.nodes[0], way.nodes[1]]; @@ -180,15 +197,12 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { ReplaceTemporaryNode(newNode), annotation); - if (!more) { - finished = true; - context.enter(mode); - } + finished = true; + context.enter(mode); }; // Connect the way to an existing node and continue drawing. - drawWay.addNode = function(node, more) { - + drawWay.addNode = function(node) { // Avoid creating duplicate segments if (way.areAdjacent(node.id, way.nodes[way.nodes.length - 1])) return; @@ -196,10 +210,8 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { ReplaceTemporaryNode(node), annotation); - if (!more) { - finished = true; - context.enter(mode); - } + finished = true; + context.enter(mode); }; drawWay.finish = function() { diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js index 599b07ff95..aa93ab3153 100644 --- a/js/id/modes/add_line.js +++ b/js/id/modes/add_line.js @@ -20,7 +20,7 @@ iD.modes.AddLine = function(context, option) { way = iD.Way(); if (mode.option === 'orthogonal') { - context.perform( + context.replace( iD.actions.AddEntity(node), iD.actions.AddEntity(way), iD.actions.AddVertex(way.id, node.id), @@ -40,7 +40,7 @@ iD.modes.AddLine = function(context, option) { way = iD.Way(); if (mode.option === 'orthogonal') { - context.perform( + context.replace( iD.actions.AddEntity(node), iD.actions.AddEntity(way), iD.actions.AddVertex(way.id, node.id), @@ -61,7 +61,7 @@ iD.modes.AddLine = function(context, option) { way = iD.Way(); if (mode.option === 'orthogonal') { - context.perform( + context.replace( iD.actions.AddEntity(way), iD.actions.AddVertex(way.id, node.id), iD.actions.AddVertex(way.id, node.id)); From 78448060195326847158d4dfefd068fc42fb96b2 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 13 Jun 2015 20:35:24 -0400 Subject: [PATCH 08/13] Don't roundcoords before inverting projection --- js/id/behavior/draw.js | 10 +++++++--- js/id/behavior/draw_way.js | 6 ++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/js/id/behavior/draw.js b/js/id/behavior/draw.js index dc4a961bd5..16e01022cc 100644 --- a/js/id/behavior/draw.js +++ b/js/id/behavior/draw.js @@ -52,15 +52,19 @@ iD.behavior.Draw = function(context) { Math.atan2(p1[1] - p0[1], p1[0] - p0[0]), height = iD.geo.euclideanDistance(p1, mousePoint) * Math.sin(theta), perpVec = perpendicularVector(p0, p1, height), - q0 = iD.geo.roundCoords(vecAdd(p0, perpVec)), - q1 = iD.geo.roundCoords(vecAdd(p1, perpVec)), + q0 = vecAdd(p0, perpVec), + q1 = vecAdd(p1, perpVec), points = [q1, q0]; return _.map(points, function(p) { var target = document.elementFromPoint(p[0] + surface.left, p[1] + surface.top), data = target && target.__data__, entity = data instanceof iD.Entity ? data : null; - return { entity: altKey ? null : entity, point: p, loc: context.projection.invert(p) }; + return { + entity: altKey ? null : entity, + point: iD.geo.roundCoords(p), + loc: context.projection.invert(p) + }; }); } else { diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index 0b604a5285..99f3af34c8 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -1,11 +1,10 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { var way = context.entity(wayId), isClosed = way.isClosed(), - isDegenerate = way.isDegenerate(), isReverse = typeof index !== 'undefined', isOrthogonal = (mode.option === 'orthogonal' && way.nodes.length > 2), finished = false, - annotation = t((isDegenerate ? + annotation = t((way.isDegenerate() ? 'operations.start.annotation.' : 'operations.continue.annotation.') + context.geometry(wayId)), draw = iD.behavior.Draw(context); @@ -44,7 +43,7 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { function ReplaceTemporaryNode(newNode, newNode2) { return function(graph) { - if (isOrthogonal) { // todo: make less hacky + if (isOrthogonal) { var newWay = way .addNode(newNode.id, -1) .addNode(newNode2.id, -1); @@ -148,7 +147,6 @@ console.log('in move(), targets=' + JSON.stringify(_.pluck(targets,'point'))); // For now, orthogonal mode only, assume targets.length === 2 - // todo: make less hacky.. drawWay.addTargets = function(targets) { var newNode1 = iD.Node({loc: targets[0].loc}), newNode2 = iD.Node({loc: targets[1].loc}); From 14b1f1ceddc94c01dd64574361eaf7359c3fd3db Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 13 Jun 2015 20:43:55 -0400 Subject: [PATCH 09/13] Fix replace/perform to put undo states where they belong --- js/id/behavior/draw_way.js | 3 ++- js/id/modes/add_line.js | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index 99f3af34c8..f5b4e7c9c6 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -27,7 +27,8 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { var f = context[way.isDegenerate() ? 'replace' : 'perform']; if (isOrthogonal) { - f(iD.actions.AddEntity(ortho1), + context.replace( + iD.actions.AddEntity(ortho1), iD.actions.AddEntity(ortho2), iD.actions.AddVertex(wayId, ortho1.id, -1), iD.actions.AddVertex(wayId, ortho2.id, -1)); diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js index aa93ab3153..599b07ff95 100644 --- a/js/id/modes/add_line.js +++ b/js/id/modes/add_line.js @@ -20,7 +20,7 @@ iD.modes.AddLine = function(context, option) { way = iD.Way(); if (mode.option === 'orthogonal') { - context.replace( + context.perform( iD.actions.AddEntity(node), iD.actions.AddEntity(way), iD.actions.AddVertex(way.id, node.id), @@ -40,7 +40,7 @@ iD.modes.AddLine = function(context, option) { way = iD.Way(); if (mode.option === 'orthogonal') { - context.replace( + context.perform( iD.actions.AddEntity(node), iD.actions.AddEntity(way), iD.actions.AddVertex(way.id, node.id), @@ -61,7 +61,7 @@ iD.modes.AddLine = function(context, option) { way = iD.Way(); if (mode.option === 'orthogonal') { - context.replace( + context.perform( iD.actions.AddEntity(way), iD.actions.AddVertex(way.id, node.id), iD.actions.AddVertex(way.id, node.id)); From a07fc33f943029990900f659099ed0ab1727df0e Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 13 Jun 2015 21:25:23 -0400 Subject: [PATCH 10/13] Restore snapping on move, don't create ortho shapes with dup nodes --- js/id/behavior/draw_way.js | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index f5b4e7c9c6..590977fd7c 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -73,19 +73,19 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { var entity = targets[i].entity, loc = targets[i].loc, point = targets[i].point, - which = isOrthogonal ? [ortho1.id, ortho2.id][i] : end.id; - -console.log('in move(), targets=' + JSON.stringify(_.pluck(targets,'point'))); -// console.log(' temp segment=' + segment.nodes); - // if (entity) { - // if (entity.type === 'node' && entity.id !== which) { - // loc = entity.loc; - // } else if (entity.type === 'way' && entity.id !== segment.id) { - // loc = iD.geo.chooseEdge(context.childNodes(entity), point, context.projection).loc; - // } - // } - - context.replace(iD.actions.MoveNode(which, loc)); + selfNode = isOrthogonal ? [ortho1.id, ortho2.id][i] : end.id, + selfWay = (isOrthogonal || isClosed) ? wayId : segment.id; + + if (entity) { // snap to target entity unless it's self.. + // if (isOrthogonal) debugger; + if (entity.type === 'node' && entity.id !== selfNode) { + loc = entity.loc; + } else if (entity.type === 'way' && entity.id !== selfWay) { + loc = iD.geo.chooseEdge(context.childNodes(entity), point, context.projection).loc; + } + } + + context.replace(iD.actions.MoveNode(selfNode, loc)); } } @@ -149,6 +149,13 @@ console.log('in move(), targets=' + JSON.stringify(_.pluck(targets,'point'))); // For now, orthogonal mode only, assume targets.length === 2 drawWay.addTargets = function(targets) { + // Avoid creating orthogonal shapes with duplicate nodes.. + for (var i = 0; i < targets.length; i++) { + var t = targets[i]; + if (!t.entity) continue; + if (t.entity.id === way.nodes[0] || t.entity.id === way.nodes[1]) return; + } + var newNode1 = iD.Node({loc: targets[0].loc}), newNode2 = iD.Node({loc: targets[1].loc}); From 6832ad1a2bf5f73fe9f95f8db39d9a21851eceaf Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 13 Jun 2015 22:49:34 -0400 Subject: [PATCH 11/13] Disable troublesome mouseover hover behaviors --- js/id/behavior/draw.js | 16 ++++++++++++++-- js/id/behavior/draw_way.js | 2 +- js/id/renderer/map.js | 12 +++++++++--- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/js/id/behavior/draw.js b/js/id/behavior/draw.js index 16e01022cc..918bfe1d38 100644 --- a/js/id/behavior/draw.js +++ b/js/id/behavior/draw.js @@ -177,7 +177,13 @@ iD.behavior.Draw = function(context) { } function draw(selection) { - context.install(hover); + if (context.mode().option === 'orthogonal' && startSegment.length === 2) { + hover = null; + context.map().vertexHoverEnable(false); + } else { + context.install(hover); + } + context.install(edit); if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) { @@ -209,7 +215,13 @@ iD.behavior.Draw = function(context) { draw.off = function(selection) { context.ui().sidebar.hover.cancel(); - context.uninstall(hover); + + if (hover) { + context.uninstall(hover); + } else { + context.map().vertexHoverEnable(true); + } + context.uninstall(edit); if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) { diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index 590977fd7c..f69ba8dc44 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -167,7 +167,7 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { annotation); finished = true; - context.enter(iD.modes.Browse(context)); + context.enter(iD.modes.Browse(context)); // just for show, replace with Select later.. }; // Accept the current position of the temporary node and continue drawing. diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 63164fca26..41d7590d60 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -9,6 +9,7 @@ iD.Map = function(context) { .on('zoom', zoomPan), dblclickEnabled = true, redrawEnabled = true, + vertexHoverEnabled = true, transformStart, transformed = false, minzoom = 0, @@ -70,14 +71,14 @@ iD.Map = function(context) { mousemove = d3.event; }) .on('mouseover.vertices', function() { - if (map.editable() && !transformed) { + if (vertexHoverEnabled && map.editable() && !transformed) { var hover = d3.event.target.__data__; surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom()); dispatch.drawn({full: false}); } }) .on('mouseout.vertices', function() { - if (map.editable() && !transformed) { + if (vertexHoverEnabled && map.editable() && !transformed) { var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__; surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom()); dispatch.drawn({full: false}); @@ -88,7 +89,6 @@ iD.Map = function(context) { supersurface .call(context.background()); - context.on('enter.map', function() { if (map.editable() && !transformed) { var all = context.intersects(map.extent()), @@ -282,6 +282,12 @@ iD.Map = function(context) { return map; }; + map.vertexHoverEnable = function(_) { + if (!arguments.length) return vertexHoverEnabled; + vertexHoverEnabled = _; + return map; + }; + function interpolateZoom(_) { var k = projection.scale(), t = projection.translate(); From 315e19af21af0cd4f4186ef7bf4a63be0dafa36b Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 13 Jun 2015 23:51:02 -0400 Subject: [PATCH 12/13] Add snapping to target node/way in addTargets click handler (still hacky) --- js/id/behavior/draw_way.js | 41 ++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index f69ba8dc44..afef396797 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -156,12 +156,45 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { if (t.entity.id === way.nodes[0] || t.entity.id === way.nodes[1]) return; } - var newNode1 = iD.Node({loc: targets[0].loc}), + var entity, newNode1, newNode2, choice, edge; + + // targets[0] + entity = targets[0].entity; + if (entity) { + entity = context.entity(entity.id); + if (entity.type === 'node') { + newNode1 = entity; + } else if (entity.type === 'way') { + choice = iD.geo.chooseEdge(context.childNodes(entity), targets[0].point, context.projection); + edge = [entity.nodes[choice.index - 1], entity.nodes[choice.index]]; + newNode1 = iD.Node({loc: choice.loc}); + context.replace(iD.actions.AddMidpoint({ loc: choice.loc, edge: edge}, newNode1)); + } + } else { + newNode1 = iD.Node({loc: targets[0].loc}); + context.replace(iD.actions.AddEntity(newNode1)); + } + + + // targets[1] + entity = targets[1].entity; + if (entity) { + entity = context.entity(entity.id); + if (entity.type === 'node') { + newNode2 = entity; + } else if (entity.type === 'way') { + choice = iD.geo.chooseEdge(context.childNodes(entity), targets[1].point, context.projection); + edge = [entity.nodes[choice.index - 1], entity.nodes[choice.index]]; + newNode2 = iD.Node({loc: choice.loc}); + context.replace(iD.actions.AddMidpoint({ loc: choice.loc, edge: edge}, newNode2)); + } + } else { newNode2 = iD.Node({loc: targets[1].loc}); + context.replace(iD.actions.AddEntity(newNode2)); + } - context.replace( - iD.actions.AddEntity(newNode1), - iD.actions.AddEntity(newNode2), + + context.perform( ReplaceTemporaryNode(newNode1, newNode2), iD.actions.ChangeTags(wayId, {building:'yes'}), // just for show, remove later.. annotation); From a0744d234bf95c6fa06c701b366912bd060b4c59 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 16 Jun 2015 23:10:54 -0400 Subject: [PATCH 13/13] in addTargets, DRY code and support snapping shape to nearby nodes --- js/id/behavior/draw_way.js | 155 +++++++++++++++++++++---------------- js/id/modes/add_line.js | 2 +- 2 files changed, 90 insertions(+), 67 deletions(-) diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index afef396797..485e3a318c 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -42,32 +42,6 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { } - function ReplaceTemporaryNode(newNode, newNode2) { - return function(graph) { - if (isOrthogonal) { - var newWay = way - .addNode(newNode.id, -1) - .addNode(newNode2.id, -1); - return graph - .replace(newWay) - .remove(ortho1) - .remove(ortho2); - - } else if (isClosed) { - return graph - .replace(way.addNode(newNode.id, index)) - .remove(end); - - } else { - return graph - .replace(graph.entity(wayId).addNode(newNode.id, index)) - .remove(end) - .remove(segment) - .remove(start); - } - }; - } - function move(targets) { for (var i = 0; i < targets.length; i++) { var entity = targets[i].entity, @@ -77,7 +51,6 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { selfWay = (isOrthogonal || isClosed) ? wayId : segment.id; if (entity) { // snap to target entity unless it's self.. - // if (isOrthogonal) debugger; if (entity.type === 'node' && entity.id !== selfNode) { loc = entity.loc; } else if (entity.type === 'way' && entity.id !== selfWay) { @@ -147,55 +120,87 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { }; - // For now, orthogonal mode only, assume targets.length === 2 + // Add multiple click targets, snapping to target entities if necessary.. drawWay.addTargets = function(targets) { - // Avoid creating orthogonal shapes with duplicate nodes.. - for (var i = 0; i < targets.length; i++) { - var t = targets[i]; - if (!t.entity) continue; - if (t.entity.id === way.nodes[0] || t.entity.id === way.nodes[1]) return; - } + function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; } - var entity, newNode1, newNode2, choice, edge; - - // targets[0] - entity = targets[0].entity; - if (entity) { - entity = context.entity(entity.id); - if (entity.type === 'node') { - newNode1 = entity; - } else if (entity.type === 'way') { - choice = iD.geo.chooseEdge(context.childNodes(entity), targets[0].point, context.projection); - edge = [entity.nodes[choice.index - 1], entity.nodes[choice.index]]; - newNode1 = iD.Node({loc: choice.loc}); - context.replace(iD.actions.AddMidpoint({ loc: choice.loc, edge: edge}, newNode1)); - } - } else { - newNode1 = iD.Node({loc: targets[0].loc}); - context.replace(iD.actions.AddEntity(newNode1)); + var newIds = [], + entity, target, choice, edge, newNode, i; + + // Avoid creating orthogonal shapes with duplicate nodes (i.e. a line).. + for (i = 0; i < targets.length; i++) { + entity = targets[i].entity; + if (!entity) continue; + // For now, orthogonal mode only.. + if (entity.id === way.nodes[0] || entity.id === way.nodes[1]) return; } + // For each target: create a node, or snap to an existing entity.. + for (i = 0; i < targets.length; i++) { + target = targets[i]; + entity = target.entity; + + if (entity) { + // Get latest.. If multiple nodes snap to the same target entity, + // it may have been modified during this loop.. + entity = context.entity(entity.id); + + if (entity.type === 'node') { + newIds.push(entity.id); + } else if (entity.type === 'way') { + choice = iD.geo.chooseEdge(context.childNodes(entity), target.point, context.projection); + edge = [entity.nodes[choice.index - 1], entity.nodes[choice.index]]; + newNode = iD.Node({loc: choice.loc}); + context.replace(iD.actions.AddMidpoint({ loc: choice.loc, edge: edge}, newNode)); + newIds.push(newNode.id); + } - // targets[1] - entity = targets[1].entity; - if (entity) { - entity = context.entity(entity.id); - if (entity.type === 'node') { - newNode2 = entity; - } else if (entity.type === 'way') { - choice = iD.geo.chooseEdge(context.childNodes(entity), targets[1].point, context.projection); - edge = [entity.nodes[choice.index - 1], entity.nodes[choice.index]]; - newNode2 = iD.Node({loc: choice.loc}); - context.replace(iD.actions.AddMidpoint({ loc: choice.loc, edge: edge}, newNode2)); + } else { + newNode = iD.Node({loc: target.loc}); + context.replace(iD.actions.AddEntity(newNode)); + newIds.push(newNode.id); } - } else { - newNode2 = iD.Node({loc: targets[1].loc}); - context.replace(iD.actions.AddEntity(newNode2)); } + // Replace temporary nodes.. + context.replace(function(graph) { + var newWay = way; + for (var i = 0; i < newIds.length; i++) { + newWay = newWay.addNode(newIds[i], -1); + } + // For now, orthogonal mode only.. + return graph + .replace(newWay) + .remove(ortho1) + .remove(ortho2); + } + ); + + // Try to snap other nearby nodes onto the new shape.. + if (!d3.event.altKey) { + var pad = 3, // pad extent by a few screen pixels + proj = context.projection, + newWay = context.entity(wayId), + extent = newWay.extent(context.graph()), + min = vecAdd( proj(extent[0]), [-pad, pad] ), + max = vecAdd( proj(extent[1]), [ pad, -pad] ), + padExtent = iD.geo.Extent(proj.invert(min), proj.invert(max)); + + var testNodes = context.intersects(padExtent).filter(function (entity) { + return entity.type === 'node' && newWay.nodes.indexOf(entity.id) === -1; + }); + + for (i = 0; i < testNodes.length; i++) { + choice = iD.geo.chooseEdge(context.childNodes(newWay), proj(testNodes[i].loc), proj); + if (choice.distance < pad) { + edge = [newWay.nodes[choice.index - 1], newWay.nodes[choice.index]]; + context.replace(iD.actions.AddMidpoint({ loc: choice.loc, edge: edge}, testNodes[i])); + newWay = context.entity(wayId); + } + } + } context.perform( - ReplaceTemporaryNode(newNode1, newNode2), iD.actions.ChangeTags(wayId, {building:'yes'}), // just for show, remove later.. annotation); @@ -203,6 +208,24 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) { context.enter(iD.modes.Browse(context)); // just for show, replace with Select later.. }; + + function ReplaceTemporaryNode(newNode) { + return function(graph) { + if (isClosed) { + return graph + .replace(way.addNode(newNode.id, index)) + .remove(end); + + } else { + return graph + .replace(graph.entity(wayId).addNode(newNode.id, index)) + .remove(end) + .remove(segment) + .remove(start); + } + }; + } + // Accept the current position of the temporary node and continue drawing. drawWay.add = function(loc) { // prevent duplicate nodes diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js index 599b07ff95..b1e071502e 100644 --- a/js/id/modes/add_line.js +++ b/js/id/modes/add_line.js @@ -57,7 +57,7 @@ iD.modes.AddLine = function(context, option) { } function startFromNode(node) { - var graph = context.graph(), + var baseGraph = context.graph(), way = iD.Way(); if (mode.option === 'orthogonal') {