From b69c4ccf97025ed9cd18025ca4e4cb4546d660f0 Mon Sep 17 00:00:00 2001 From: abakkk Date: Sun, 7 Jun 2020 18:57:11 +0200 Subject: [PATCH] new polygon shape Mark vertices with `Enter` and finish drawing by releasing clic, like other shapes. --- draw.js | 107 +++++++++++++++--- extension.js | 1 + locale/draw-on-your-screen.pot | 11 ++ prefs.js | 1 + schemas/gschemas.compiled | Bin 3540 -> 3588 bytes ...extensions.draw-on-your-screen.gschema.xml | 5 + 6 files changed, 110 insertions(+), 15 deletions(-) diff --git a/draw.js b/draw.js index 7fbf66b..27f3d6d 100644 --- a/draw.js +++ b/draw.js @@ -56,9 +56,9 @@ const LINECAP_ICON_PATH = Me.dir.get_child('data').get_child('icons').get_child( const DASHED_LINE_ICON_PATH = Me.dir.get_child('data').get_child('icons').get_child('dashed-line-symbolic.svg').get_path(); const FULL_LINE_ICON_PATH = Me.dir.get_child('data').get_child('icons').get_child('full-line-symbolic.svg').get_path(); -var Shapes = { NONE: 0, LINE: 1, ELLIPSE: 2, RECTANGLE: 3, TEXT: 4 }; +var Shapes = { NONE: 0, LINE: 1, ELLIPSE: 2, RECTANGLE: 3, TEXT: 4, POLYGON: 5 }; const TextState = { DRAWING: 0, WRITING: 1 }; -const ShapeNames = { 0: "Free drawing", 1: "Line", 2: "Ellipse", 3: "Rectangle", 4: "Text" }; +const ShapeNames = { 0: "Free drawing", 1: "Line", 2: "Ellipse", 3: "Rectangle", 4: "Text", 5: "Polygon" }; const LineCapNames = { 0: 'Butt', 1: 'Round', 2: 'Square' }; const LineJoinNames = { 0: 'Miter', 1: 'Round', 2: 'Bevel' }; const FontWeightNames = { 0: 'Normal', 1: 'Bold' }; @@ -325,7 +325,16 @@ var DrawingArea = new Lang.Class({ } this._redisplay(); return Clutter.EVENT_STOP; - + + } else if (this.currentElement && this.currentElement.shape == Shapes.POLYGON && + (event.get_key_symbol() == Clutter.KEY_Return || event.get_key_symbol() == 65421)) { + // copy last point + let lastPoint = this.currentElement.points[this.currentElement.points.length - 1]; + let secondToLastPoint = this.currentElement.points[this.currentElement.points.length - 2]; + if (!getNearness(secondToLastPoint, lastPoint, 3)) + this.currentElement.points.push([lastPoint[0], lastPoint[1]]); + return Clutter.EVENT_STOP; + } else if (event.get_key_symbol() == Clutter.KEY_Escape) { if (this.helper.visible) this.helper.hideHelp(); @@ -382,6 +391,11 @@ var DrawingArea = new Lang.Class({ this.currentElement.state = TextState.DRAWING; } + if (this.currentShape == Shapes.POLYGON) { + this.currentElement.points.push([startX, startY]); + this.emit('show-osd', null, _("Press Enter\nto mark vertices"), "", -1); + } + this.motionHandler = this.connect('motion-event', (actor, event) => { if (this.spaceKeyPressed) return; @@ -405,11 +419,19 @@ var DrawingArea = new Lang.Class({ this.buttonReleasedHandler = null; } + // skip when a polygon has not at least 3 points + if (this.currentElement && this.currentElement.shape == Shapes.POLYGON && this.currentElement.points.length < 3) + this.currentElement = null; + // skip when the size is too small to be visible (3px) (except for free drawing) - if (this.currentElement && this.currentElement.points.length >= 2 && - (this.currentShape == Shapes.NONE || - Math.hypot(this.currentElement.points[1][0] - this.currentElement.points[0][0], this.currentElement.points[1][1] - this.currentElement.points[0][1]) > 3)) { - + if (this.currentElement && this.currentElement.shape != Shapes.NONE && this.currentElement.points.length >= 2) { + let lastPoint = this.currentElement.points[this.currentElement.points.length - 1]; + let secondToLastPoint = this.currentElement.points[this.currentElement.points.length - 2]; + if (getNearness(secondToLastPoint, lastPoint, 3)) + this.currentElement.points.pop(); + } + + if (this.currentElement && this.currentElement.points.length >= 2) { if (this.currentElement.shape == Shapes.TEXT && this.currentElement.state == TextState.DRAWING) { // start writing this.currentElement.state = TextState.WRITING; @@ -436,11 +458,15 @@ var DrawingArea = new Lang.Class({ if (this.currentElement.shape == Shapes.NONE) this.currentElement.addPoint(x, y, controlPressed); else if ((this.currentElement.shape == Shapes.RECTANGLE || this.currentElement.shape == Shapes.TEXT) && (controlPressed || this.currentElement.transform.active)) - this.currentElement.transformRectangle(x, y); + this.currentElement.transformPolygon(x, y); else if (this.currentElement.shape == Shapes.ELLIPSE && (controlPressed || this.currentElement.transform.active)) this.currentElement.transformEllipse(x, y); else if (this.currentElement.shape == Shapes.LINE && (controlPressed || this.currentElement.transform.active)) this.currentElement.transformLine(x, y); + else if (this.currentElement.shape == Shapes.POLYGON && (controlPressed || this.currentElement.transform.active)) + this.currentElement.transformPolygon(x, y); + else if (this.currentElement.shape == Shapes.POLYGON) + this.currentElement.points[this.currentElement.points.length - 1] = [x, y]; else this.currentElement.points[1] = [x, y]; @@ -928,6 +954,7 @@ const DrawingElement = new Lang.Class({ if (shape == Shapes.LINE && points.length == 3) { cr.moveTo(points[0][0], points[0][1]); cr.curveTo(points[0][0], points[0][1], points[1][0], points[1][1], points[2][0], points[2][1]); + } else if (shape == Shapes.NONE || shape == Shapes.LINE) { cr.moveTo(points[0][0], points[0][1]); for (let j = 1; j < points.length; j++) { @@ -946,6 +973,16 @@ const DrawingElement = new Lang.Class({ this.rotate(cr, trans.angle, trans.center[0], trans.center[1]); cr.rectangle(points[0][0], points[0][1], points[1][0] - points[0][0], points[1][1] - points[0][1]); this.rotate(cr, - trans.angle, trans.center[0], trans.center[1]); + + } else if (shape == Shapes.POLYGON && points.length >= 2) { + this.rotate(cr, trans.angle, trans.center[0], trans.center[1]); + cr.moveTo(points[0][0], points[0][1]); + for (let j = 1; j < points.length; j++) { + cr.lineTo(points[j][0], points[j][1]); + } + if (shape == Shapes.POLYGON) + cr.closePath(); + this.rotate(cr, - trans.angle, trans.center[0], trans.center[1]); } else if (shape == Shapes.TEXT && points.length == 2) { this.rotate(cr, trans.angle, trans.center[0], trans.center[1]); @@ -982,9 +1019,8 @@ const DrawingElement = new Lang.Class({ } else if (this.shape == Shapes.NONE || this.shape == Shapes.LINE) { row += ``; } else if (this.shape == Shapes.ELLIPSE && points.length == 2 && this.transform.ratio != 1) { @@ -1006,6 +1042,17 @@ const DrawingElement = new Lang.Class({ row += ``; + } else if (this.shape == Shapes.POLYGON && points.length >= 3) { + let transAttribute = ""; + if (this.transform.angle != 0) { + let angle = this.transform.angle * 180 / Math.PI; + transAttribute = ` transform="rotate(${angle}, ${this.transform.center[0]}, ${this.transform.center[1]})"`; + } + row += ``; + } else if (this.shape == Shapes.TEXT && points.length == 2) { let transAttribute = ""; if (this.transform.angle != 0) { @@ -1060,14 +1107,13 @@ const DrawingElement = new Lang.Class({ cr.translate(-x, -y); }, - transformRectangle: function(x, y) { + transformPolygon: function(x, y) { let points = this.points; if (points.length < 2 || points[0][0] == points[1][0] || points[0][1] == points[1][1]) return; - this.transform.center = [points[0][0] + (points[1][0] - points[0][0]) / 2, points[0][1] + (points[1][1] - points[0][1]) / 2]; - - this.transform.angle = getAngle(this.transform.center[0], this.transform.center[1], points[1][0], points[1][1], x, y); + this.transform.center = points.length >= 3 ? getCentroid(points) : getNaiveCenter(points); + this.transform.angle = getAngle(this.transform.center[0], this.transform.center[1], points[points.length - 1][0], points[points.length - 1][1], x, y); this.transform.active = true; }, @@ -1096,9 +1142,36 @@ const DrawingElement = new Lang.Class({ this.points[2] = this.points[1]; this.points[1] = [x, y]; this.transform.active = true; - }, + } }); +const getNearness = function(pointA, pointB, distance) { + return Math.hypot(pointB[0] - pointA[0], pointB[1] - pointA[1]) < distance; +}; + +// mean of the vertices, ok for regular polygons +const getNaiveCenter = function(points) { + return points.reduce((accumulator, point) => accumulator = [accumulator[0] + point[0], accumulator[1] + point[1]]) + .map(coord => coord / points.length); +}; + +// https://en.wikipedia.org/wiki/Centroid#Of_a_polygon +const getCentroid = function(points) { + let n = points.length; + points.push(points[0]); + + let [sA, sX, sY] = [0, 0, 0]; + for (let i = 0; i <= n-1; i++) { + let a = points[i][0]*points[i+1][1] - points[i+1][0]*points[i][1]; + sA += a; + sX += (points[i][0] + points[i+1][0]) * a; + sY += (points[i][1] + points[i+1][1]) * a; + } + + points.pop(); + return [sX / (3 * sA), sY / (3 * sA)]; +}; + const getAngle = function(xO, yO, xA, yA, xB, yB) { // calculate angle of rotation in absolute value // cos(AOB) = (OA.OB)/(||OA||*||OB||) where OA.OB = (xA-xO)*(xB-xO) + (yA-yO)*(yB-yO) @@ -1477,6 +1550,10 @@ const DrawingMenu = new Lang.Class({ }); subItem.label.get_clutter_text().set_use_markup(true); + + // change the display order of shapes + if (obj == ShapeNames && i == Shapes.POLYGON) + item.menu.moveMenuItem(subItem, 4); } return GLib.SOURCE_REMOVE; }); diff --git a/extension.js b/extension.js index 8fa8ad2..bc628a8 100644 --- a/extension.js +++ b/extension.js @@ -191,6 +191,7 @@ var AreaManager = new Lang.Class({ 'select-ellipse-shape': () => this.activeArea.selectShape(Draw.Shapes.ELLIPSE), 'select-rectangle-shape': () => this.activeArea.selectShape(Draw.Shapes.RECTANGLE), 'select-text-shape': () => this.activeArea.selectShape(Draw.Shapes.TEXT), + 'select-polygon-shape': () => this.activeArea.selectShape(Draw.Shapes.POLYGON), 'toggle-font-family': this.activeArea.toggleFontFamily.bind(this.activeArea), 'toggle-font-weight': this.activeArea.toggleFontWeight.bind(this.activeArea), 'toggle-font-style': this.activeArea.toggleFontStyle.bind(this.activeArea), diff --git a/locale/draw-on-your-screen.pot b/locale/draw-on-your-screen.pot index 8452fe8..9863ac1 100644 --- a/locale/draw-on-your-screen.pot +++ b/locale/draw-on-your-screen.pot @@ -58,6 +58,9 @@ msgstr "" msgid "Text" msgstr "" +msgid "Polygon" +msgstr "" + msgid "Fill" msgstr "" @@ -70,6 +73,11 @@ msgstr "" msgid "Full line" msgstr "" +msgid "" +"Press Enter\n" +"to mark vertices" +msgstr "" + msgid "" "Type your text\n" "and press Escape" @@ -153,6 +161,9 @@ msgstr "" msgid "Select rectangle" msgstr "" +msgid "Select polygon" +msgstr "" + msgid "Select text" msgstr "" diff --git a/prefs.js b/prefs.js index 114c7f3..b7ff66e 100644 --- a/prefs.js +++ b/prefs.js @@ -47,6 +47,7 @@ var INTERNAL_KEYBINDINGS = { 'select-line-shape': "Select line", 'select-ellipse-shape': "Select ellipse", 'select-rectangle-shape': "Select rectangle", + 'select-polygon-shape': "Select polygon", 'select-text-shape': "Select text", 'select-none-shape': "Unselect shape (free drawing)", 'toggle-fill': "Toggle fill/stroke", diff --git a/schemas/gschemas.compiled b/schemas/gschemas.compiled index 1cd66c59bb02fdc2bc1e162edaad257b4dc06f6c..ab895c757111c49a03e8bbd59f7659fb51befd82 100644 GIT binary patch literal 3588 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 diff --git a/schemas/org.gnome.shell.extensions.draw-on-your-screen.gschema.xml b/schemas/org.gnome.shell.extensions.draw-on-your-screen.gschema.xml index 1454195..616e3f7 100644 --- a/schemas/org.gnome.shell.extensions.draw-on-your-screen.gschema.xml +++ b/schemas/org.gnome.shell.extensions.draw-on-your-screen.gschema.xml @@ -81,6 +81,11 @@ select rectangle select rectangle + + ["<Primary>y"] + select polygon + select polygon + ["<Primary>l"] select line