diff --git a/externs/olx.js b/externs/olx.js
index 25c3589c3c..f258393c28 100644
--- a/externs/olx.js
+++ b/externs/olx.js
@@ -2360,7 +2360,8 @@ olx.interaction.DragZoomOptions.prototype.style;
* minPointsPerRing: (number|undefined),
* style: (ol.style.Style|Array.
|ol.style.StyleFunction|undefined),
* geometryName: (string|undefined),
- * condition: (ol.events.ConditionType|undefined)}}
+ * condition: (ol.events.ConditionType|undefined),
+ * freehandCondition: (ol.events.ConditionType|undefined)}}
* @api
*/
olx.interaction.DrawOptions;
@@ -2427,13 +2428,26 @@ olx.interaction.DrawOptions.prototype.geometryName;
/**
* A function that takes an {@link ol.MapBrowserEvent} and returns a boolean
* to indicate whether that event should be handled.
- * By default {@link ol.events.condition.noModifierKeys} adds a vertex.
+ * By default {@link ol.events.condition.noModifierKeys}, i.e. a click, adds a
+ * vertex or deactivates freehand drawing.
* @type {ol.events.ConditionType|undefined}
* @api
*/
olx.interaction.DrawOptions.prototype.condition;
+/**
+ * Condition that activates freehand drawing for lines and polygons. This
+ * function takes an {@link ol.MapBrowserEvent} and returns a boolean to
+ * indicate whether that event should be handled. The default is
+ * {@link ol.events.condition.shiftKeyOnly}, meaning that the Shift key
+ * activates freehand drawing.
+ * @type {ol.events.ConditionType|undefined}
+ * @api
+ */
+olx.interaction.DrawOptions.prototype.freehandCondition;
+
+
/**
* @typedef {{condition: (ol.events.ConditionType|undefined),
* duration: (number|undefined),
diff --git a/src/ol/interaction/drawinteraction.js b/src/ol/interaction/drawinteraction.js
index fd564a4d90..90078b19e7 100644
--- a/src/ol/interaction/drawinteraction.js
+++ b/src/ol/interaction/drawinteraction.js
@@ -98,6 +98,12 @@ ol.interaction.Draw = function(options) {
*/
this.downPx_ = null;
+ /**
+ * @type {boolean}
+ * @private
+ */
+ this.freehand_ = false;
+
/**
* Target source for drawn features.
* @type {ol.source.Vector}
@@ -212,6 +218,13 @@ ol.interaction.Draw = function(options) {
this.condition_ = goog.isDef(options.condition) ?
options.condition : ol.events.condition.noModifierKeys;
+ /**
+ * @private
+ * @type {ol.events.ConditionType}
+ */
+ this.freehandCondition_ = goog.isDef(options.freehandCondition) ?
+ options.freehandCondition : ol.events.condition.shiftKeyOnly;
+
goog.events.listen(this,
ol.Object.getChangeEventType(ol.interaction.InteractionProperty.ACTIVE),
this.updateState_, false, this);
@@ -247,8 +260,13 @@ ol.interaction.Draw.prototype.setMap = function(map) {
* @api
*/
ol.interaction.Draw.handleEvent = function(mapBrowserEvent) {
- var pass = true;
- if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERMOVE) {
+ var pass = !this.freehand_;
+ if (this.freehand_ &&
+ mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERDRAG) {
+ this.addToDrawing_(mapBrowserEvent);
+ pass = false;
+ } else if (mapBrowserEvent.type ===
+ ol.MapBrowserEvent.EventType.POINTERMOVE) {
pass = this.handlePointerMove_(mapBrowserEvent);
} else if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.DBLCLICK) {
pass = false;
@@ -267,6 +285,15 @@ ol.interaction.Draw.handleDownEvent_ = function(event) {
if (this.condition_(event)) {
this.downPx_ = event.pixel;
return true;
+ } else if ((this.mode_ === ol.interaction.DrawMode.LINE_STRING ||
+ this.mode_ === ol.interaction.DrawMode.POLYGON) &&
+ this.freehandCondition_(event)) {
+ this.downPx_ = event.pixel;
+ this.freehand_ = true;
+ if (goog.isNull(this.finishCoordinate_)) {
+ this.startDrawing_(event);
+ }
+ return true;
} else {
return false;
}
@@ -280,6 +307,7 @@ ol.interaction.Draw.handleDownEvent_ = function(event) {
* @private
*/
ol.interaction.Draw.handleUpEvent_ = function(event) {
+ this.freehand_ = false;
var downPx = this.downPx_;
var clickPx = event.pixel;
var dx = downPx[0] - clickPx[0];
@@ -290,8 +318,8 @@ ol.interaction.Draw.handleUpEvent_ = function(event) {
this.handlePointerMove_(event);
if (goog.isNull(this.finishCoordinate_)) {
this.startDrawing_(event);
- } else if (this.mode_ === ol.interaction.DrawMode.POINT ||
- this.mode_ === ol.interaction.DrawMode.CIRCLE &&
+ } else if ((this.mode_ === ol.interaction.DrawMode.POINT ||
+ this.mode_ === ol.interaction.DrawMode.CIRCLE) &&
!goog.isNull(this.finishCoordinate_) ||
this.atFinish_(event)) {
this.finishDrawing();
@@ -355,7 +383,9 @@ ol.interaction.Draw.prototype.atFinish_ = function(event) {
var pixel = event.pixel;
var dx = pixel[0] - finishPixel[0];
var dy = pixel[1] - finishPixel[1];
- at = Math.sqrt(dx * dx + dy * dy) <= this.snapTolerance_;
+ var freehand = this.freehand_ && this.freehandCondition_(event);
+ var snapTolerance = freehand ? 1 : this.snapTolerance_;
+ at = Math.sqrt(dx * dx + dy * dy) <= snapTolerance;
if (at) {
this.finishCoordinate_ = finishCoordinate;
break;
diff --git a/test/spec/ol/interaction/drawinteraction.test.js b/test/spec/ol/interaction/drawinteraction.test.js
index 1d73c68bb4..dda8d2dcdc 100644
--- a/test/spec/ol/interaction/drawinteraction.test.js
+++ b/test/spec/ol/interaction/drawinteraction.test.js
@@ -225,6 +225,29 @@ describe('ol.interaction.Draw', function() {
expect(geometry.getCoordinates()).to.eql([[10, -20], [30, -20]]);
});
+ it('supports freehand drawing for linestrings', function() {
+ // freehand sequence
+ simulateEvent('pointermove', 10, 20);
+ simulateEvent('pointerdown', 10, 20, true);
+ simulateEvent('pointermove', 20, 30, true);
+ simulateEvent('pointerdrag', 20, 30, true);
+ simulateEvent('pointermove', 20, 40, true);
+ simulateEvent('pointerdrag', 20, 40, true);
+ simulateEvent('pointerup', 20, 40, true);
+
+ // finish on third point
+ simulateEvent('pointermove', 20, 40);
+ simulateEvent('pointerdown', 20, 40);
+ simulateEvent('pointerup', 20, 40);
+
+ var features = source.getFeatures();
+ expect(features).to.have.length(1);
+ var geometry = features[0].getGeometry();
+ expect(geometry).to.be.a(ol.geom.LineString);
+ expect(geometry.getCoordinates()).to.eql(
+ [[10, -20], [20, -30], [20, -40]]);
+ });
+
it('does not add a point with a significant drag', function() {
// first point
simulateEvent('pointermove', 10, 20);
@@ -385,6 +408,30 @@ describe('ol.interaction.Draw', function() {
]);
});
+ it('supports freehand drawing for polygons', function() {
+ // freehand sequence
+ simulateEvent('pointermove', 10, 20);
+ simulateEvent('pointerdown', 10, 20, true);
+ simulateEvent('pointermove', 30, 20, true);
+ simulateEvent('pointerdrag', 30, 20, true);
+ simulateEvent('pointermove', 40, 10, true);
+ simulateEvent('pointerdrag', 40, 10, true);
+ simulateEvent('pointerup', 40, 10, true);
+
+ // finish on last point
+ simulateEvent('pointerdown', 40, 10);
+ simulateEvent('pointerup', 40, 10);
+
+ var features = source.getFeatures();
+ expect(features).to.have.length(1);
+ var geometry = features[0].getGeometry();
+ expect(geometry).to.be.a(ol.geom.Polygon);
+
+ expect(geometry.getCoordinates()).to.eql([
+ [[10, -20], [30, -20], [40, -10], [10, -20]]
+ ]);
+ });
+
it('triggers draw events', function() {
var ds = sinon.spy();
var de = sinon.spy();