diff --git a/area.js b/area.js index f629462..2956c0c 100644 --- a/area.js +++ b/area.js @@ -47,6 +47,7 @@ const pgettext = imports.gettext.domain(Me.metadata['gettext-domain']).pgettext; const CAIRO_DEBUG_EXTENDS = false; const SVG_DEBUG_EXTENDS = false; +const MOTION_TIME = 1; // ms, time accuracy for free drawing, max is about 33 ms. The lower it is, the smoother the drawing is. const TEXT_CURSOR_TIME = 600; // ms const ELEMENT_GRABBER_TIME = 80; // ms, default is about 16 ms const GRID_TILES_HORIZONTAL_NUMBER = 30; @@ -656,6 +657,11 @@ var DrawingArea = new Lang.Class({ } this.motionHandler = this.connect('motion-event', (actor, event) => { + if (this.motionTimeout) { + GLib.source_remove(this.motionTimeout); + this.motionTimeout = null; + } + if (this.spaceKeyPressed) return; @@ -665,6 +671,30 @@ var DrawingArea = new Lang.Class({ return; let controlPressed = event.has_control_modifier(); this._updateDrawing(x, y, controlPressed); + + if (this.currentTool == Shapes.NONE) { + let device = event.get_device(); + let sequence = event.get_event_sequence(); + + // Minimum time between two motion events is about 33 ms. + // Add intermediate points to make quick free drawings smoother. + this.motionTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, MOTION_TIME, () => { + let [success, coords] = device.get_coords(sequence); + if (!success) + return GLib.SOURCE_CONTINUE; + + let [s, x, y] = this.transform_stage_point(coords.x, coords.y); + if (!s) + return GLib.SOURCE_CONTINUE; + + // Important: do not call this._updateDrawing because the area MUST NOT BE REDISPLAYED at this step. + // It would lead to critical issues (bad performances and shell crashes). + // The area will be redisplayed, including the intermediate points, at the next motion event. + this.currentElement.addIntermediatePoint(x, y, controlPressed); + + return GLib.SOURCE_CONTINUE; + }); + } }); }, @@ -679,6 +709,10 @@ var DrawingArea = new Lang.Class({ }, _stopDrawing: function() { + if (this.motionTimeout) { + GLib.source_remove(this.motionTimeout); + this.motionTimeout = null; + } if (this.motionHandler) { this.disconnect(this.motionHandler); this.motionHandler = null; diff --git a/elements.js b/elements.js index 509e219..c750025 100644 --- a/elements.js +++ b/elements.js @@ -62,6 +62,7 @@ const MIN_REFLECTION_LINE_LENGTH = 10; // px const MIN_TRANSLATION_DISTANCE = 1; // px const MIN_ROTATION_ANGLE = Math.PI / 1000; // rad const MIN_DRAWING_SIZE = 3; // px +const MIN_INTERMEDIATE_POINT_DISTANCE = 1; // px, the higher it is, the fewer points there will be var DrawingElement = function(params) { return params.shape == Shapes.TEXT ? new TextElement(params) : @@ -422,6 +423,17 @@ const _DrawingElement = new Lang.Class({ } }, + // For free drawing only. + addIntermediatePoint: function(x, y, transform) { + let points = this.points; + if (getNearness(points[points.length - 1], [x, y], MIN_INTERMEDIATE_POINT_DISTANCE)) + return; + + points.push([x, y]); + if (transform) + this._smooth(points.length - 1); + }, + startDrawing: function(startX, startY) { this.points.push([startX, startY]);