fix transformed center getter

Since reflection and inversion introductions, compute center only with the slides of the traslations is not correct.

* `_getCenter` -> `_getOriginalCenter`
* `_getCenterWithSlide` -> `_getTransformedCenter`
* Use pango matrices to compute the new center position since matrices from Cairo, Graphene, ..., are not available with GJS.
* Stock original center and transformed center to not compute it numerous time.
This commit is contained in:
abakkk 2020-06-18 20:21:56 +02:00
parent a33f7b8324
commit 5ca7a5e4a6
1 changed files with 48 additions and 36 deletions

82
draw.js
View File

@ -29,6 +29,7 @@ const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const PangoMatrix = imports.gi.Pango.Matrix;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const St = imports.gi.St; const St = imports.gi.St;
@ -1182,10 +1183,10 @@ const DrawingElement = new Lang.Class({
if (transformation.type == Transformations.TRANSLATION) { if (transformation.type == Transformations.TRANSLATION) {
cr.translate(transformation.slideX, transformation.slideY); cr.translate(transformation.slideX, transformation.slideY);
} else if (transformation.type == Transformations.ROTATION) { } else if (transformation.type == Transformations.ROTATION) {
let center = this._getCenterWithSlideBefore(transformation); let center = this._getTransformedCenter(transformation);
crRotate(cr, transformation.angle, center[0], center[1]); crRotate(cr, transformation.angle, center[0], center[1]);
} else if (transformation.type == Transformations.SCALE_PRESERVE || transformation.type == Transformations.SCALE_DIRECTIONAL) { } else if (transformation.type == Transformations.SCALE_PRESERVE || transformation.type == Transformations.SCALE_DIRECTIONAL) {
let center = this._getCenterWithSlideBefore(transformation); let center = this._getTransformedCenter(transformation);
crRotate(cr, transformation.angle, center[0], center[1]); crRotate(cr, transformation.angle, center[0], center[1]);
crScale(cr, transformation.scaleX, transformation.scaleY, center[0], center[1]); crScale(cr, transformation.scaleX, transformation.scaleY, center[0], center[1]);
crRotate(cr, -transformation.angle, center[0], center[1]); crRotate(cr, -transformation.angle, center[0], center[1]);
@ -1301,7 +1302,7 @@ const DrawingElement = new Lang.Class({
.reverse() .reverse()
.forEach(transformation => { .forEach(transformation => {
transAttribute += transAttribute ? ' ' : ' transform="'; transAttribute += transAttribute ? ' ' : ' transform="';
let center = this._getCenter(); let center = this._getOriginalCenter();
if (transformation.type == Transformations.ROTATION) { if (transformation.type == Transformations.ROTATION) {
transAttribute += `rotate(${transformation.angle * 180 / Math.PI},${center[0]},${center[1]})`; transAttribute += `rotate(${transformation.angle * 180 / Math.PI},${center[0]},${center[1]})`;
@ -1418,7 +1419,7 @@ const DrawingElement = new Lang.Class({
if (points.length < 2) if (points.length < 2)
return; return;
let center = this._getCenter(); let center = this._getOriginalCenter();
this.transformations[0] = { type: Transformations.ROTATION, angle: getAngle(center[0], center[1], points[points.length - 1][0], points[points.length - 1][1], x, y) }; this.transformations[0] = { type: Transformations.ROTATION, angle: getAngle(center[0], center[1], points[points.length - 1][0], points[points.length - 1][1], x, y) };
} else if (this.shape == Shapes.ELLIPSE && transform) { } else if (this.shape == Shapes.ELLIPSE && transform) {
@ -1426,7 +1427,7 @@ const DrawingElement = new Lang.Class({
return; return;
points[2] = [x, y]; points[2] = [x, y];
let center = this._getCenter(); let center = this._getOriginalCenter();
this.transformations[0] = { type: Transformations.ROTATION, angle: getAngle(center[0], center[1], center[0] + 1, center[1], x, y) }; this.transformations[0] = { type: Transformations.ROTATION, angle: getAngle(center[0], center[1], center[0] + 1, center[1], x, y) };
} else if (this.shape == Shapes.LINE && transform) { } else if (this.shape == Shapes.LINE && transform) {
@ -1482,14 +1483,14 @@ const DrawingElement = new Lang.Class({
transformation.slideX = x - transformation.startX; transformation.slideX = x - transformation.startX;
transformation.slideY = y - transformation.startY; transformation.slideY = y - transformation.startY;
} else if (transformation.type == Transformations.ROTATION) { } else if (transformation.type == Transformations.ROTATION) {
let center = this._getCenterWithSlideBefore(transformation); let center = this._getTransformedCenter(transformation);
transformation.angle = getAngle(center[0], center[1], transformation.startX, transformation.startY, x, y); transformation.angle = getAngle(center[0], center[1], transformation.startX, transformation.startY, x, y);
} else if (transformation.type == Transformations.SCALE_PRESERVE) { } else if (transformation.type == Transformations.SCALE_PRESERVE) {
let center = this._getCenterWithSlideBefore(transformation); let center = this._getTransformedCenter(transformation);
let scale = Math.hypot(x - center[0], y - center[1]) / Math.hypot(transformation.startX - center[0], transformation.startY - center[1]) || 1; let scale = Math.hypot(x - center[0], y - center[1]) / Math.hypot(transformation.startX - center[0], transformation.startY - center[1]) || 1;
[transformation.scaleX, transformation.scaleY] = [scale, scale]; [transformation.scaleX, transformation.scaleY] = [scale, scale];
} else if (transformation.type == Transformations.SCALE_DIRECTIONAL) { } else if (transformation.type == Transformations.SCALE_DIRECTIONAL) {
let center = this._getCenterWithSlideBefore(transformation); let center = this._getTransformedCenter(transformation);
let startAngle = getAngle(center[0], center[1], center[0] + 1, center[1], transformation.startX, transformation.startY); let startAngle = getAngle(center[0], center[1], center[0] + 1, center[1], transformation.startX, transformation.startY);
let vertical = Math.abs(Math.sin(startAngle)) >= Math.sin(3 * Math.PI / 8); let vertical = Math.abs(Math.sin(startAngle)) >= Math.sin(3 * Math.PI / 8);
let horizontal = Math.abs(Math.cos(startAngle)) >= Math.cos(Math.PI / 8); let horizontal = Math.abs(Math.cos(startAngle)) >= Math.cos(Math.PI / 8);
@ -1549,43 +1550,54 @@ const DrawingElement = new Lang.Class({
} }
}, },
_getTotalSlideBefore: function(transformation) {
let totalSlide = [0, 0];
this.transformations.slice(0, this.transformations.indexOf(transformation))
.filter(transformation => transformation.type == Transformations.TRANSLATION)
.forEach(transformation => totalSlide = [totalSlide[0] + transformation.slideX, totalSlide[1] + transformation.slideY]);
return totalSlide;
},
// When rotating grouped lines, lineOffset is used to retrieve the rotation center of the first line. // When rotating grouped lines, lineOffset is used to retrieve the rotation center of the first line.
_getLineOffset: function() { _getLineOffset: function() {
return (this.lineIndex || 0) * Math.abs(this.points[1][1] - this.points[0][1]); return (this.lineIndex || 0) * Math.abs(this.points[1][1] - this.points[0][1]);
}, },
// The figure rotation center before transformations (original). // The figure rotation center before transformations (original).
_getCenter: function() { _getOriginalCenter: function() {
let points = this.points; if (!this._originalCenter) {
let points = this.points;
this._originalCenter = this.shape == Shapes.ELLIPSE ? [points[0][0], points[0][1]] :
this.shape == Shapes.LINE && points.length == 3 ? getCurveCenter(points[0], points[1], points[2]) :
this.shape == Shapes.TEXT && this.textWidth ? [Math.min(points[0][0], points[1][0]),
Math.max(points[0][1], points[1][1]) - this._getLineOffset()] :
points.length >= 3 ? getCentroid(points) :
getNaiveCenter(points);
}
return this.shape == Shapes.ELLIPSE ? [points[0][0], points[0][1]] : return this._originalCenter;
this.shape == Shapes.LINE && points.length == 3 ? getCurveCenter(points[0], points[1], points[2]) :
this.shape == Shapes.TEXT && this.textWidth ? [Math.min(points[0][0], points[1][0]), Math.max(points[0][1], points[1][1]) - this._getLineOffset()] :
points.length >= 3 ? getCentroid(points) :
getNaiveCenter(points);
}, },
// The figure rotation center that takes previous translations into account. // The figure rotation center, whose position is affected by all transformations done before 'transformation'.
_getCenterWithSlideBefore: function(transformation) { _getTransformedCenter: function(transformation) {
let [center, totalSlide] = [this._getCenter(), this._getTotalSlideBefore(transformation)]; if (!transformation.elementTransformedCenter) {
return [center[0] + totalSlide[0], center[1] + totalSlide[1]]; let matrix = new PangoMatrix({ xx: 1, xy: 0, yx: 0, yy: 1, x0: 0, y0: 0 });
},
_getCenterWithSlide: function() { // Apply transformations to the matrice in reverse order
if (this.transformations.length) // because Pango multiply matrices by the left when applying a transformation
return this._getCenterWithSlideBefore(this.lastTransformation); this.transformations.slice(0, this.transformations.indexOf(transformation)).reverse().forEach(transformation => {
else if (transformation.type == Transformations.TRANSLATION) {
return this._getCenter(); matrix.translate(transformation.slideX, transformation.slideY);
} else if (transformation.type == Transformations.ROTATION) {
// nothing, the center position is preserved.
} else if (transformation.type == Transformations.SCALE_PRESERVE || transformation.type == Transformations.SCALE_DIRECTIONAL) {
// nothing, the center position is preserved.
} else if (transformation.type == Transformations.REFLECTION || transformation.type == Transformations.INVERSION) {
matrix.translate(transformation.slideX, transformation.slideY);
matrix.rotate(-transformation.angle * 180 / Math.PI);
matrix.scale(transformation.scaleX, transformation.scaleY);
matrix.rotate(transformation.angle * 180 / Math.PI);
matrix.translate(-transformation.slideX, -transformation.slideY);
}
});
let originalCenter = this._getOriginalCenter();
transformation.elementTransformedCenter = matrix.transform_point(originalCenter[0], originalCenter[1]);
}
return transformation.elementTransformedCenter;
}, },
_addPoint: function(x, y, smoothedStroke) { _addPoint: function(x, y, smoothedStroke) {