summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/desktop-events.cpp11
-rw-r--r--src/desktop.cpp21
-rw-r--r--src/desktop.h11
-rw-r--r--src/display/canvas-grid.cpp5
-rw-r--r--src/display/sodipodi-ctrlrect.cpp35
-rw-r--r--src/document.cpp10
-rw-r--r--src/extension/internal/cairo-renderer.cpp16
-rw-r--r--src/extension/internal/grid.cpp4
-rw-r--r--src/extension/internal/latex-text-renderer.cpp5
-rw-r--r--src/helper/pixbuf-ops.cpp9
-rw-r--r--src/helper/png-write.cpp9
-rw-r--r--src/knot-holder-entity.cpp90
-rw-r--r--src/object/box3d.cpp17
-rw-r--r--src/object/persp3d.cpp75
-rw-r--r--src/object/sp-guide.cpp31
-rw-r--r--src/object/sp-item.cpp3
-rwxr-xr-xsrc/selection-chemistry.cpp48
-rw-r--r--src/seltrans.cpp8
-rw-r--r--src/ui/dialog/align-and-distribute.cpp18
-rw-r--r--src/ui/dialog/inkscape-preferences.cpp5
-rw-r--r--src/ui/dialog/inkscape-preferences.h1
-rw-r--r--src/ui/tool/control-point-selection.cpp8
-rw-r--r--src/ui/tool/multi-path-manipulator.cpp4
-rw-r--r--src/ui/tool/path-manipulator.cpp6
-rw-r--r--src/ui/tool/transform-handle-set.cpp27
-rw-r--r--src/ui/tools/calligraphic-tool.cpp1
-rw-r--r--src/ui/tools/flood-tool.cpp10
-rw-r--r--src/ui/tools/measure-tool.cpp7
-rw-r--r--src/ui/tools/select-tool.cpp11
-rw-r--r--src/ui/tools/spray-tool.cpp6
-rw-r--r--src/ui/tools/text-tool.cpp3
-rw-r--r--src/ui/tools/tweak-tool.cpp7
-rw-r--r--src/ui/widget/page-sizer.cpp2
-rw-r--r--src/widgets/desktop-widget.cpp8
34 files changed, 308 insertions, 224 deletions
diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp
index 6af5a6210..5256d5abb 100644
--- a/src/desktop-events.cpp
+++ b/src/desktop-events.cpp
@@ -128,8 +128,9 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge
Geom::Point const event_dt(desktop->w2d(event_w));
// calculate the normal of the guidelines when dragged from the edges of rulers.
- Geom::Point normal_bl_to_tr(-1.,1.); //bottomleft to topright
- Geom::Point normal_tr_to_bl(1.,1.); //topright to bottomleft
+ auto const y_dir = desktop->yaxisdir();
+ Geom::Point normal_bl_to_tr(1., y_dir); //bottomleft to topright
+ Geom::Point normal_tr_to_bl(-1., y_dir); //topright to bottomleft
normal_bl_to_tr.normalize();
normal_tr_to_bl.normalize();
Inkscape::CanvasGrid * grid = sp_namedview_get_first_enabled_grid(desktop->namedview);
@@ -246,6 +247,12 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge
double newx = event_dt.x();
double newy = event_dt.y();
+ // <sodipodi:guide> stores inverted y-axis coordinates
+ if (desktop->is_yaxisdown()) {
+ newy = desktop->doc()->getHeight().value("px") - newy;
+ normal[Geom::Y] *= -1.0;
+ }
+
SPRoot *root = desktop->doc()->getRoot();
if( root->viewBox_set ) {
newx = newx * root->viewBox.width() / root->width.computed;
diff --git a/src/desktop.cpp b/src/desktop.cpp
index 7b72524cc..bec4bd68f 100644
--- a/src/desktop.cpp
+++ b/src/desktop.cpp
@@ -127,7 +127,7 @@ SPDesktop::SPDesktop() :
_widget( nullptr ),
_guides_message_context( nullptr ),
_active( false ),
- _doc2dt( Geom::Scale(1, -1) ),
+ _doc2dt( Geom::identity() ),
_image_render_observer(this, "/options/rendering/imageinoutlinemode"),
grids_visible( false )
{
@@ -255,7 +255,10 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWid
/* Connect event for page resize */
- _doc2dt[5] = document->getHeight().value("px");
+ if (!prefs->getBool("/options/yaxisdown", false)) {
+ _doc2dt[3] = -1;
+ _doc2dt[5] = document->getHeight().value("px");
+ }
sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
_modified_connection =
@@ -886,7 +889,7 @@ SPDesktop::set_display_area( Geom::Rect const &r, double border, bool log)
} else {
zoom = w.height() / r.height();
}
- _current_affine.setScale( zoom );
+ _current_affine.setScale( Geom::Scale(zoom, _doc2dt[3] * zoom) );
// Zero offset, actual offset calculated later.
_current_affine.setOffset( Geom::Point( 0, 0 ) );
@@ -903,9 +906,7 @@ Geom::Rect SPDesktop::get_display_area() const
Geom::Rect const viewbox = canvas->getViewbox();
double const scale = _current_affine.getZoom();
- /// @fixme hardcoded desktop transform
- return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
- Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
+ return viewbox * Geom::Scale(1. / scale, _doc2dt[3] / scale);
}
@@ -917,7 +918,7 @@ SPDesktop::zoom_absolute_keep_point (Geom::Point const &c, double zoom)
{
zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
Geom::Point w = d2w( c ); // Must be before zoom changed.
- _current_affine.setScale( zoom );
+ _current_affine.setScale( Geom::Scale(zoom, _doc2dt[3] * zoom) );
set_display_area( c, w );
}
@@ -937,7 +938,7 @@ void
SPDesktop::zoom_absolute_center_point (Geom::Point const &c, double zoom)
{
zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
- _current_affine.setScale( zoom );
+ _current_affine.setScale( Geom::Scale(zoom, _doc2dt[3] * zoom) );
Geom::Rect viewbox = canvas->getViewbox();
set_display_area( c, viewbox.midpoint() );
}
@@ -1682,7 +1683,9 @@ SPDesktop::onDocumentURISet (gchar const* uri)
void
SPDesktop::onDocumentResized (gdouble width, gdouble height)
{
- _doc2dt[5] = height;
+ if (!Inkscape::Preferences::get()->getBool("/options/yaxisdown", false)) {
+ _doc2dt[5] = height;
+ }
sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
SP_CTRLRECT(page)->setRectangle(a);
diff --git a/src/desktop.h b/src/desktop.h
index a1281a5b8..9087f8d31 100644
--- a/src/desktop.h
+++ b/src/desktop.h
@@ -426,6 +426,9 @@ public:
Geom::Point doc2dt(Geom::Point const &p) const;
Geom::Point dt2doc(Geom::Point const &p) const;
+ bool is_yaxisdown() const { return _doc2dt[3] > 0; }
+ double yaxisdir() const { return _doc2dt[3]; }
+
void setDocument (SPDocument* doc) override;
bool shutdown() override;
void mouseover() override {}
@@ -455,18 +458,10 @@ private:
_scale = scale;
_update();
}
- void setScale( double scale ) {
- _scale = Geom::Scale(scale, -scale); // Y flip
- _update();
- }
void addScale( Geom::Scale scale) {
_scale *= scale;
_update();
}
- void addScale( double scale ) {
- _scale *= Geom::Scale(scale, -scale); // Y flip?? Check
- _update();
- }
void setRotate( Geom::Rotate rotate ) {
_rotate = rotate;
diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp
index 42769e1b1..db765820f 100644
--- a/src/display/canvas-grid.cpp
+++ b/src/display/canvas-grid.cpp
@@ -389,7 +389,10 @@ void CanvasGrid::align_clicked(int align)
{
Geom::Point dimensions = doc->getDimensions();
dimensions[Geom::X] *= align % 3 * 0.5;
- dimensions[Geom::Y] *= 1 - (align / 3 * 0.5);
+ dimensions[Geom::Y] *= align / 3 * 0.5;
+ if (SP_ACTIVE_DESKTOP) {
+ dimensions = SP_ACTIVE_DESKTOP->doc2dt(dimensions);
+ }
setOrigin(dimensions);
}
diff --git a/src/display/sodipodi-ctrlrect.cpp b/src/display/sodipodi-ctrlrect.cpp
index a35f07c3d..f097203fd 100644
--- a/src/display/sodipodi-ctrlrect.cpp
+++ b/src/display/sodipodi-ctrlrect.cpp
@@ -16,6 +16,7 @@
*
*/
+#include "inkscape.h"
#include "sodipodi-ctrlrect.h"
#include "sp-canvas-util.h"
#include "display/cairo-utils.h"
@@ -122,33 +123,41 @@ void CtrlRect::render(SPCanvasBuf *buf)
// Draw shadow first. Shadow extends under rectangle to reduce aliasing effects.
if (_shadow_width > 0 && !_dashed) {
+ Geom::Point const * corners = rect_transformed;
+ double shadowydir = _affine.det() > 0 ? -1 : 1;
+
+ // is the desktop y-axis downwards?
+ if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->is_yaxisdown()) {
+ ++corners; // need corners 1/2/3 instead of 0/1/2
+ shadowydir *= -1;
+ }
// Offset by half stroke width (_shadow_width is in window coordinates).
// Need to handle change in handedness with flips.
- Geom::Point shadow( _shadow_width/2.0, (_affine.det()>0?-1:1)*_shadow_width/2.0 );
+ Geom::Point shadow( _shadow_width/2.0, shadowydir * _shadow_width/2.0 );
shadow *= Geom::Rotate( rotation );
if (axis_aligned) {
// Snap to pixel grid (add 0.5 to center on pixel).
cairo_move_to( buf->ct,
- floor(rect_transformed[0][X]+shadow[X]+0.5) + 0.5,
- floor(rect_transformed[0][Y]+shadow[Y]+0.5) + 0.5 );
+ floor(corners[0][X] + shadow[X]+0.5) + 0.5,
+ floor(corners[0][Y] + shadow[Y]+0.5) + 0.5 );
cairo_line_to( buf->ct,
- floor(rect_transformed[1][X]+shadow[X]+0.5) + 0.5,
- floor(rect_transformed[1][Y]+shadow[Y]+0.5) + 0.5 );
+ floor(corners[1][X] + shadow[X]+0.5) + 0.5,
+ floor(corners[1][Y] + shadow[Y]+0.5) + 0.5 );
cairo_line_to( buf->ct,
- floor(rect_transformed[2][X]+shadow[X]+0.5) + 0.5,
- floor(rect_transformed[2][Y]+shadow[Y]+0.5) + 0.5 );
+ floor(corners[2][X] + shadow[X]+0.5) + 0.5,
+ floor(corners[2][Y] + shadow[Y]+0.5) + 0.5 );
} else {
cairo_move_to( buf->ct,
- rect_transformed[0][X]+shadow[X],
- rect_transformed[0][Y]+shadow[Y] );
+ corners[0][X] + shadow[X],
+ corners[0][Y] + shadow[Y] );
cairo_line_to( buf->ct,
- rect_transformed[1][X]+shadow[X],
- rect_transformed[1][Y]+shadow[Y] );
+ corners[1][X] + shadow[X],
+ corners[1][Y] + shadow[Y] );
cairo_line_to( buf->ct,
- rect_transformed[2][X]+shadow[X],
- rect_transformed[2][Y]+shadow[Y] );
+ corners[2][X] + shadow[X],
+ corners[2][Y] + shadow[Y] );
}
ink_cairo_set_source_rgba32( buf->ct, _shadow_color );
diff --git a/src/document.cpp b/src/document.cpp
index 658bc93c9..d4094b7e9 100644
--- a/src/document.cpp
+++ b/src/document.cpp
@@ -841,6 +841,12 @@ void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins)
margin_bottom = Inkscape::Util::Quantity::convert(margin_bottom, nv_units, "px");
}
}
+
+ double y_dir = SP_ACTIVE_DESKTOP->yaxisdir();
+
+ if (y_dir > 0) {
+ std::swap(margin_top, margin_bottom);
+ }
Geom::Rect const rect_with_margins(
rect.min() - Geom::Point(margin_left, margin_bottom),
@@ -852,7 +858,7 @@ void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins)
);
Geom::Translate const tr(
- Geom::Point(0, old_height - rect_with_margins.height())
+ Geom::Point(0, (y_dir > 0) ? 0 : old_height - rect_with_margins.height())
- rect_with_margins.min());
root->translateChildItems(tr);
@@ -862,7 +868,7 @@ void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins)
nv->translateGrids(tr2);
// update the viewport so the drawing appears to stay where it was
- nv->scrollAllDesktops(-tr2[0], tr2[1], false);
+ nv->scrollAllDesktops(-tr2[0], -tr2[1] * y_dir, false);
}
}
diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp
index e390c5314..32f42c3ce 100644
--- a/src/extension/internal/cairo-renderer.cpp
+++ b/src/extension/internal/cairo-renderer.cpp
@@ -663,7 +663,7 @@ CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool page
if (pageBoundingBox) {
d = Geom::Rect::from_xywh(Geom::Point(0,0), doc->getDimensions());
} else {
- Geom::OptRect bbox = base->desktopVisualBounds();
+ Geom::OptRect bbox = base->documentVisualBounds();
if (!bbox) {
g_message("CairoRenderer: empty bounding box.");
return false;
@@ -672,13 +672,14 @@ CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool page
}
d.expandBy(bleedmargin_px);
+ double px_to_ctx_units = 1.0;
if (ctx->_vector_based_target) {
// convert from px to pt
- d *= Geom::Scale(Inkscape::Util::Quantity::convert(1, "px", "pt"));
+ px_to_ctx_units = Inkscape::Util::Quantity::convert(1, "px", "pt");
}
- ctx->_width = d.width();
- ctx->_height = d.height();
+ ctx->_width = d.width() * px_to_ctx_units;
+ ctx->_height = d.height() * px_to_ctx_units;
TRACE(("setupDocument: %f x %f\n", ctx->_width, ctx->_height));
@@ -690,13 +691,8 @@ CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool page
Geom::Affine tp( Geom::Translate( bleedmargin_px, bleedmargin_px ) );
ctx->transform(tp);
} else {
- double high = doc->getHeight().value("px");
- if (ctx->_vector_based_target)
- high = Inkscape::Util::Quantity::convert(high, "px", "pt");
-
// this transform translates the export drawing to a virtual page (0,0)-(width,height)
- Geom::Affine tp(Geom::Translate(-d.left() * (ctx->_vector_based_target ? Inkscape::Util::Quantity::convert(1, "pt", "px") : 1.0),
- (d.bottom() - high) * (ctx->_vector_based_target ? Inkscape::Util::Quantity::convert(1, "pt", "px") : 1.0)));
+ Geom::Affine tp(Geom::Translate(-d.min()));
ctx->transform(tp);
}
}
diff --git a/src/extension/internal/grid.cpp b/src/extension/internal/grid.cpp
index 9e05ecd29..82531e009 100644
--- a/src/extension/internal/grid.cpp
+++ b/src/extension/internal/grid.cpp
@@ -100,9 +100,7 @@ Grid::effect (Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc
bounding_area = *bounds;
}
- gdouble doc_height = (document->doc())->getHeight().value("px");
- Geom::Rect temprec = Geom::Rect(Geom::Point(bounding_area.min()[Geom::X], doc_height - bounding_area.min()[Geom::Y]),
- Geom::Point(bounding_area.max()[Geom::X], doc_height - bounding_area.max()[Geom::Y]));
+ Geom::Rect temprec = bounding_area * static_cast<SPDesktop *>(document)->doc2dt();
bounding_area = temprec;
}
diff --git a/src/extension/internal/latex-text-renderer.cpp b/src/extension/internal/latex-text-renderer.cpp
index f346a304a..c78471080 100644
--- a/src/extension/internal/latex-text-renderer.cpp
+++ b/src/extension/internal/latex-text-renderer.cpp
@@ -45,6 +45,7 @@
#include "extension/output.h"
#include "extension/system.h"
+#include "inkscape.h"
#include "inkscape-version.h"
#include "io/sys.h"
#include "document.h"
@@ -680,7 +681,9 @@ LaTeXTextRenderer::setupDocument(SPDocument *doc, bool pageBoundingBox, float bl
}
// flip y-axis
- push_transform( Geom::Scale(1,-1) * Geom::Translate(0, doc->getHeight().value("px")) ); /// @fixme hardcoded desktop transform!
+ if (SP_ACTIVE_DESKTOP) {
+ push_transform( SP_ACTIVE_DESKTOP->doc2dt() );
+ }
// write the info to LaTeX
Inkscape::SVGOStringStream os;
diff --git a/src/helper/pixbuf-ops.cpp b/src/helper/pixbuf-ops.cpp
index a79efdbdd..93ea8bc98 100644
--- a/src/helper/pixbuf-ops.cpp
+++ b/src/helper/pixbuf-ops.cpp
@@ -27,6 +27,7 @@
#include "object/sp-defs.h"
#include "object/sp-use.h"
#include "util/units.h"
+#include "inkscape.h"
#include "helper/pixbuf-ops.h"
@@ -111,13 +112,7 @@ Inkscape::Pixbuf *sp_generate_internal_bitmap(SPDocument *doc, gchar const */*fi
Geom::Rect screen=Geom::Rect(Geom::Point(x0,y0), Geom::Point(x1, y1));
- double padding = 1.0;
-
- Geom::Point origin(screen.min()[Geom::X],
- doc->getHeight().value("px") - screen[Geom::Y].extent() - screen.min()[Geom::Y]);
-
- origin[Geom::X] = origin[Geom::X] + (screen[Geom::X].extent() * ((1 - padding) / 2));
- origin[Geom::Y] = origin[Geom::Y] + (screen[Geom::Y].extent() * ((1 - padding) / 2));
+ Geom::Point origin = screen.min() * SP_ACTIVE_DESKTOP->doc2dt();
Geom::Scale scale(Inkscape::Util::Quantity::convert(xdpi, "px", "in"), Inkscape::Util::Quantity::convert(ydpi, "px", "in"));
Geom::Affine affine = scale * Geom::Translate(-origin * scale);
diff --git a/src/helper/png-write.cpp b/src/helper/png-write.cpp
index 92fdc3648..fe5412c4e 100644
--- a/src/helper/png-write.cpp
+++ b/src/helper/png-write.cpp
@@ -31,6 +31,8 @@
#include "rdf.h"
#include "util/units.h"
+#include "inkscape.h"
+
#include "object/sp-item.h"
#include "object/sp-root.h"
#include "object/sp-defs.h"
@@ -425,8 +427,13 @@ ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename,
doc->ensureUpToDate();
+ Geom::Affine dt2doc;
+ if (SP_ACTIVE_DESKTOP) {
+ dt2doc = SP_ACTIVE_DESKTOP->dt2doc();
+ }
+
/* Calculate translation by transforming to document coordinates (flipping Y)*/
- Geom::Point translation = Geom::Point(-area[Geom::X][0], area[Geom::Y][1] - doc->getHeight().value("px"));
+ Geom::Point translation = -(area * dt2doc).min();
/* This calculation is only valid when assumed that (x0,y0)= area.corner(0) and (x1,y1) = area.corner(2)
* 1) a[0] * x0 + a[2] * y1 + a[4] = 0.0
diff --git a/src/knot-holder-entity.cpp b/src/knot-holder-entity.cpp
index 3d34cdf01..83cc3d772 100644
--- a/src/knot-holder-entity.cpp
+++ b/src/knot-holder-entity.cpp
@@ -142,28 +142,9 @@ KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape:
/* TODO: this pattern manipulation is not able to handle general transformation matrices. Only matrices that are the result of a pure scale times a pure rotation. */
-static gdouble sp_pattern_extract_theta(SPPattern const *pat)
-{
- Geom::Affine transf = pat->getTransform();
- return Geom::atan2(transf.xAxis());
-}
-
-static Geom::Point sp_pattern_extract_scale(SPPattern const *pat)
-{
- Geom::Affine transf = pat->getTransform();
- return Geom::Point( transf.expansionX(), transf.expansionY() );
-}
-
-static Geom::Point sp_pattern_extract_trans(SPPattern const *pat)
-{
- return Geom::Point(pat->getTransform()[4], pat->getTransform()[5]);
-}
-
void
PatternKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state)
{
- SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer());
-
// FIXME: this snapping should be done together with knowing whether control was pressed. If GDK_CONTROL_MASK, then constrained snapping should be used.
Geom::Point p_snapped = snap_knot_position(p, state);
@@ -176,18 +157,23 @@ PatternKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &ori
}
if (state) {
- Geom::Point const q = p_snapped - sp_pattern_extract_trans(pat);
+ Geom::Point const q = p_snapped - knot_get();
item->adjust_pattern(Geom::Translate(q), false, _fill ? TRANSFORM_FILL : TRANSFORM_STROKE);
}
item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}
+static Geom::Point sp_pattern_knot_get(SPPattern const *pat, gdouble x, gdouble y)
+{
+ return Geom::Point(x, y) * pat->getTransform();
+}
+
Geom::Point
PatternKnotHolderEntityXY::knot_get() const
{
SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer());
- return sp_pattern_extract_trans(pat);
+ return sp_pattern_knot_get(pat, 0, 0);
}
Geom::Point
@@ -195,14 +181,7 @@ PatternKnotHolderEntityAngle::knot_get() const
{
SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer());
- gdouble x = pat->width();
- gdouble y = 0;
- Geom::Point delta = Geom::Point(x,y);
- Geom::Point scale = sp_pattern_extract_scale(pat);
- gdouble theta = sp_pattern_extract_theta(pat);
- delta = delta * Geom::Affine(Geom::Scale(scale))*Geom::Affine(Geom::Rotate(theta));
- delta = delta + sp_pattern_extract_trans(pat);
- return delta;
+ return sp_pattern_knot_get(pat, pat->width(), 0);
}
void
@@ -214,53 +193,47 @@ PatternKnotHolderEntityAngle::knot_set(Geom::Point const &p, Geom::Point const &
SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer());
// get the angle from pattern 0,0 to the cursor pos
- Geom::Point delta = p - sp_pattern_extract_trans(pat);
- gdouble theta = atan2(delta);
+ Geom::Point transform_origin = sp_pattern_knot_get(pat, 0, 0);
+ gdouble theta = atan2(p - transform_origin);
+ gdouble theta_old = atan2(knot_get() - transform_origin);
if ( state & GDK_CONTROL_MASK ) {
theta = sp_round(theta, M_PI/snaps);
}
- // get the scale from the current transform so we can keep it.
- Geom::Point scl = sp_pattern_extract_scale(pat);
- Geom::Affine rot = Geom::Affine(Geom::Scale(scl)) * Geom::Affine(Geom::Rotate(theta));
- Geom::Point const t = sp_pattern_extract_trans(pat);
- rot[4] = t[Geom::X];
- rot[5] = t[Geom::Y];
- item->adjust_pattern(rot, true, _fill ? TRANSFORM_FILL : TRANSFORM_STROKE);
+ Geom::Affine rot = Geom::Translate(-transform_origin)
+ * Geom::Rotate(theta - theta_old)
+ * Geom::Translate(transform_origin);
+ item->adjust_pattern(rot, false, _fill ? TRANSFORM_FILL : TRANSFORM_STROKE);
item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}
void
-PatternKnotHolderEntityScale::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
+PatternKnotHolderEntityScale::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state)
{
SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer());
// FIXME: this snapping should be done together with knowing whether control was pressed. If GDK_CONTROL_MASK, then constrained snapping should be used.
Geom::Point p_snapped = snap_knot_position(p, state);
- // get angle from current transform
- gdouble theta = sp_pattern_extract_theta(pat);
-
// Get the new scale from the position of the knotholder
- Geom::Point d = p_snapped - sp_pattern_extract_trans(pat);
+ Geom::Affine transform = pat->getTransform();
+ Geom::Affine transform_inverse = transform.inverse();
+ Geom::Point d = p_snapped * transform_inverse;
+ Geom::Point d_origin = origin * transform_inverse;
+ Geom::Point origin_dt;
gdouble pat_x = pat->width();
gdouble pat_y = pat->height();
- Geom::Scale scl(1);
if ( state & GDK_CONTROL_MASK ) {
// if ctrl is pressed: use 1:1 scaling
- gdouble pat_h = hypot(pat_x, pat_y);
- scl = Geom::Scale(d.length() / pat_h);
- } else {
- d *= Geom::Rotate(-theta);
- scl = Geom::Scale(d[Geom::X] / pat_x, d[Geom::Y] / pat_y);
+ d = d_origin * (d.length() / d_origin.length());
}
- Geom::Affine rot = (Geom::Affine)scl * Geom::Rotate(theta);
+ Geom::Affine rot = Geom::Translate(-origin_dt)
+ * Geom::Scale(d.x() / pat_x, d.y() / pat_y)
+ * Geom::Translate(origin_dt)
+ * transform;
- Geom::Point const t = sp_pattern_extract_trans(pat);
- rot[4] = t[Geom::X];
- rot[5] = t[Geom::Y];
item->adjust_pattern(rot, true, _fill ? TRANSFORM_FILL : TRANSFORM_STROKE);
item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}
@@ -270,16 +243,7 @@ Geom::Point
PatternKnotHolderEntityScale::knot_get() const
{
SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer());
-
- gdouble x = pat->width();
- gdouble y = pat->height();
- Geom::Point delta = Geom::Point(x,y);
- Geom::Affine a = pat->getTransform();
- a[4] = 0;
- a[5] = 0;
- delta = delta * a;
- delta = delta + sp_pattern_extract_trans(pat);
- return delta;
+ return sp_pattern_knot_get(pat, pat->width(), pat->height());
}
/* Filter manipulation */
diff --git a/src/object/box3d.cpp b/src/object/box3d.cpp
index df95f4f84..7be7c10f7 100644
--- a/src/object/box3d.cpp
+++ b/src/object/box3d.cpp
@@ -149,6 +149,9 @@ void SPBox3D::set(unsigned int key, const gchar* value) {
case SP_ATTR_INKSCAPE_BOX3D_CORNER0:
if (value && strcmp(value, "0 : 0 : 0 : 0")) {
box->orig_corner0 = Proj::Pt3(value);
+ if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->is_yaxisdown()) {
+ box->orig_corner0[Proj::Y] *= -1;
+ }
box->save_corner0 = box->orig_corner0;
box3d_position_set(box);
}
@@ -156,6 +159,9 @@ void SPBox3D::set(unsigned int key, const gchar* value) {
case SP_ATTR_INKSCAPE_BOX3D_CORNER7:
if (value && strcmp(value, "0 : 0 : 0 : 0")) {
box->orig_corner7 = Proj::Pt3(value);
+ if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->is_yaxisdown()) {
+ box->orig_corner7[Proj::Y] *= -1;
+ }
box->save_corner7 = box->orig_corner7;
box3d_position_set(box);
}
@@ -227,8 +233,15 @@ Inkscape::XML::Node* SPBox3D::write(Inkscape::XML::Document *xml_doc, Inkscape::
}
}
- gchar *coordstr0 = box->orig_corner0.coord_string();
- gchar *coordstr7 = box->orig_corner7.coord_string();
+ auto corner0 = box->orig_corner0;
+ auto corner7 = box->orig_corner7;
+ if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->is_yaxisdown()) {
+ corner0[Proj::Y] *= -1;
+ corner7[Proj::Y] *= -1;
+ }
+
+ gchar *coordstr0 = corner0.coord_string();
+ gchar *coordstr7 = corner7.coord_string();
repr->setAttribute("inkscape:corner0", coordstr0);
repr->setAttribute("inkscape:corner7", coordstr7);
g_free(coordstr0);
diff --git a/src/object/persp3d.cpp b/src/object/persp3d.cpp
index 2183b6236..05ba5898b 100644
--- a/src/object/persp3d.cpp
+++ b/src/object/persp3d.cpp
@@ -84,6 +84,43 @@ void Persp3D::release() {
this->getRepr()->removeListenerByData(this);
}
+/**
+ * Apply viewBox and legacy desktop transformation to point loaded from SVG
+ */
+static Proj::Pt2 legacy_transform_forward(Proj::Pt2 pt, SPDocument const *doc) {
+ // Read values are in 'user units'.
+ auto root = doc->getRoot();
+ if (root->viewBox_set) {
+ pt[0] *= root->width.computed / root->viewBox.width();
+ pt[1] *= root->height.computed / root->viewBox.height();
+ }
+
+ // <inkscape:perspective> stores inverted y-axis coordinates
+ if (pt[2] && SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->is_yaxisdown()) {
+ pt[1] = doc->getHeight().value("px") - pt[1];
+ }
+
+ return pt;
+}
+
+/**
+ * Apply viewBox and legacy desktop transformation to point to be written to SVG
+ */
+static Proj::Pt2 legacy_transform_backward(Proj::Pt2 pt, SPDocument const *doc) {
+ // <inkscape:perspective> stores inverted y-axis coordinates
+ if (pt[2] && SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->is_yaxisdown()) {
+ pt[1] = doc->getHeight().value("px") - pt[1];
+ }
+
+ // Written values are in 'user units'.
+ auto root = doc->getRoot();
+ if (root->viewBox_set) {
+ pt[0] *= root->viewBox.width() / root->width.computed;
+ pt[1] *= root->viewBox.height() / root->height.computed;
+ }
+
+ return pt;
+}
/**
* Virtual set: set attribute to value.
@@ -92,20 +129,11 @@ void Persp3D::release() {
// should we move VPs into their own repr (as it's done for SPStop, e.g.)?
void Persp3D::set(unsigned key, gchar const *value) {
- // Read values are in 'user units'.
- double scale_x = 1.0;
- double scale_y = 1.0;
- SPRoot *root = document->getRoot();
- if( root->viewBox_set ) {
- scale_x = root->width.computed / root->viewBox.width();
- scale_y = root->height.computed / root->viewBox.height();
- }
-
switch (key) {
case SP_ATTR_INKSCAPE_PERSP3D_VP_X: {
if (value) {
Proj::Pt2 pt (value);
- Proj::Pt2 ptn ( pt[0]*scale_x, pt[1]*scale_y, pt[2] );
+ Proj::Pt2 ptn = legacy_transform_forward(pt, document);
perspective_impl->tmat.set_image_pt( Proj::X, ptn );
}
break;
@@ -113,7 +141,7 @@ void Persp3D::set(unsigned key, gchar const *value) {
case SP_ATTR_INKSCAPE_PERSP3D_VP_Y: {
if (value) {
Proj::Pt2 pt (value);
- Proj::Pt2 ptn ( pt[0]*scale_x, pt[1]*scale_y, pt[2] );
+ Proj::Pt2 ptn = legacy_transform_forward(pt, document);
perspective_impl->tmat.set_image_pt( Proj::Y, ptn );
}
break;
@@ -121,7 +149,7 @@ void Persp3D::set(unsigned key, gchar const *value) {
case SP_ATTR_INKSCAPE_PERSP3D_VP_Z: {
if (value) {
Proj::Pt2 pt (value);
- Proj::Pt2 ptn ( pt[0]*scale_x, pt[1]*scale_y, pt[2] );
+ Proj::Pt2 ptn = legacy_transform_forward(pt, document);
perspective_impl->tmat.set_image_pt( Proj::Z, ptn );
}
break;
@@ -129,7 +157,7 @@ void Persp3D::set(unsigned key, gchar const *value) {
case SP_ATTR_INKSCAPE_PERSP3D_ORIGIN: {
if (value) {
Proj::Pt2 pt (value);
- Proj::Pt2 ptn ( pt[0]*scale_x, pt[1]*scale_y, pt[2] );
+ Proj::Pt2 ptn = legacy_transform_forward(pt, document);
perspective_impl->tmat.set_image_pt( Proj::W, ptn );
}
break;
@@ -236,37 +264,32 @@ Inkscape::XML::Node* Persp3D::write(Inkscape::XML::Document *xml_doc, Inkscape::
}
if (flags & SP_OBJECT_WRITE_EXT) {
-
- // Written values are in 'user units'.
- double scale_x = 1.0;
- double scale_y = 1.0;
- SPRoot *root = document->getRoot();
- if( root->viewBox_set ) {
- scale_x = root->viewBox.width() / root->width.computed;
- scale_y = root->viewBox.height() / root->height.computed;
- }
{
Proj::Pt2 pt = perspective_impl->tmat.column( Proj::X );
Inkscape::SVGOStringStream os;
- os << pt[0] * scale_x << " : " << pt[1] * scale_y << " : " << pt[2];
+ pt = legacy_transform_backward(pt, document);
+ os << pt[0] << " : " << pt[1] << " : " << pt[2];
repr->setAttribute("inkscape:vp_x", os.str().c_str());
}
{
Proj::Pt2 pt = perspective_impl->tmat.column( Proj::Y );
Inkscape::SVGOStringStream os;
- os << pt[0] * scale_x << " : " << pt[1] * scale_y << " : " << pt[2];
+ pt = legacy_transform_backward(pt, document);
+ os << pt[0] << " : " << pt[1] << " : " << pt[2];
repr->setAttribute("inkscape:vp_y", os.str().c_str());
}
{
Proj::Pt2 pt = perspective_impl->tmat.column( Proj::Z );
Inkscape::SVGOStringStream os;
- os << pt[0] * scale_x << " : " << pt[1] * scale_y << " : " << pt[2];
+ pt = legacy_transform_backward(pt, document);
+ os << pt[0] << " : " << pt[1] << " : " << pt[2];
repr->setAttribute("inkscape:vp_z", os.str().c_str());
}
{
Proj::Pt2 pt = perspective_impl->tmat.column( Proj::W );
Inkscape::SVGOStringStream os;
- os << pt[0] * scale_x << " : " << pt[1] * scale_y << " : " << pt[2];
+ pt = legacy_transform_backward(pt, document);
+ os << pt[0] << " : " << pt[1] << " : " << pt[2];
repr->setAttribute("inkscape:persp3d-origin", os.str().c_str());
}
}
diff --git a/src/object/sp-guide.cpp b/src/object/sp-guide.cpp
index 0b8558230..acb0707d0 100644
--- a/src/object/sp-guide.cpp
+++ b/src/object/sp-guide.cpp
@@ -131,6 +131,12 @@ void SPGuide::set(unsigned int key, const gchar *value) {
g_strfreev (strarray);
if (success == 2 && (fabs(newx) > 1e-6 || fabs(newy) > 1e-6)) {
Geom::Point direction(newx, newy);
+
+ // <sodipodi:guide> stores inverted y-axis coordinates
+ if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->is_yaxisdown()) {
+ direction[Geom::Y] *= -1.0;
+ }
+
direction.normalize();
this->normal_to_line = direction;
} else {
@@ -176,6 +182,11 @@ void SPGuide::set(unsigned int key, const gchar *value) {
this->point_on_line = Geom::Point(newx, 0);
}
}
+
+ // <sodipodi:guide> stores inverted y-axis coordinates
+ if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->is_yaxisdown()) {
+ this->point_on_line[Geom::Y] = document->getHeight().value("px") - this->point_on_line[Geom::Y];
+ }
} else {
// default to (0,0) for bad arguments
this->point_on_line = Geom::Point(0,0);
@@ -217,6 +228,12 @@ SPGuide *SPGuide::createSPGuide(SPDocument *doc, Geom::Point const &pt1, Geom::P
}
}
+ // <sodipodi:guide> stores inverted y-axis coordinates
+ if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->is_yaxisdown()) {
+ newy = doc->getHeight().value("px") - newy;
+ n[Geom::Y] *= -1.0;
+ }
+
sp_repr_set_point(repr, "position", Geom::Point( newx, newy ));
sp_repr_set_point(repr, "orientation", n);
@@ -368,6 +385,11 @@ void SPGuide::moveto(Geom::Point const point_on_line, bool const commit)
double newx = point_on_line.x();
double newy = point_on_line.y();
+ // <sodipodi:guide> stores inverted y-axis coordinates
+ if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->is_yaxisdown()) {
+ newy = document->getHeight().value("px") - newy;
+ }
+
SPRoot *root = document->getRoot();
if( root->viewBox_set ) {
// check to see if scaling is uniform
@@ -414,7 +436,14 @@ void SPGuide::set_normal(Geom::Point const normal_to_line, bool const commit)
case, so that the guide's new position is available for sp_item_rm_unsatisfied_cns. */
if (commit) {
//XML Tree being used directly while it shouldn't be
- sp_repr_set_point(getRepr(), "orientation", normal_to_line);
+ auto normal = normal_to_line;
+
+ // <sodipodi:guide> stores inverted y-axis coordinates
+ if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->is_yaxisdown()) {
+ normal[Geom::Y] *= -1.0;
+ }
+
+ sp_repr_set_point(getRepr(), "orientation", normal);
}
/* DISABLED CODE BECAUSE SPGuideAttachment IS NOT USE AT THE MOMENT (johan)
diff --git a/src/object/sp-item.cpp b/src/object/sp-item.cpp
index d7c461f92..474687cc6 100644
--- a/src/object/sp-item.cpp
+++ b/src/object/sp-item.cpp
@@ -918,8 +918,7 @@ Geom::OptRect SPItem::desktopGeometricBounds() const
Geom::OptRect SPItem::desktopVisualBounds() const
{
- /// @fixme hardcoded desktop transform
- Geom::Affine m = Geom::Scale(1, -1) * Geom::Translate(0, document->getHeight().value("px"));
+ Geom::Affine const& m = SP_ACTIVE_DESKTOP->doc2dt();
Geom::OptRect ret = documentVisualBounds();
if (ret) *ret *= m;
return ret;
diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp
index e9b9bb469..bc27d380c 100755
--- a/src/selection-chemistry.cpp
+++ b/src/selection-chemistry.cpp
@@ -2990,17 +2990,7 @@ void ObjectSet::toMarker(bool apply)
return;
}
- // FIXME: Inverted Y coordinate
- Geom::Point doc_height( 0, doc->getHeight().value("px"));
-
- // calculate the transform to be applied to objects to move them to 0,0
- Geom::Point corner( r->min()[Geom::X], r->max()[Geom::Y] ); // FIXME: Inverted Y coordinate
- Geom::Point move_p = doc_height - corner;
- move_p[Geom::Y] = -move_p[Geom::Y];
- Geom::Affine move = Geom::Affine(Geom::Translate(move_p));
-
- Geom::Point center( *c - corner ); // As defined by rotation center
- center[Geom::Y] = -center[Geom::Y];
+ Geom::Point center = desktop()->dt2doc(*c);
std::vector<SPItem*> items_(items().begin(), items().end());
@@ -3042,7 +3032,7 @@ void ObjectSet::toMarker(bool apply)
int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
- gchar const *mark_id = generate_marker(repr_copies, bbox, doc, center, parent_transform * move);
+ gchar const *mark_id = generate_marker(repr_copies, bbox, doc, center, parent_transform);
(void)mark_id;
// restore compensation setting
@@ -3378,11 +3368,6 @@ void ObjectSet::tile(bool apply)
return;
}
- // calculate the transform to be applied to objects to move them to 0,0
- Geom::Point move_p = Geom::Point(0, doc->getHeight().value("px")) - (r->min() + Geom::Point(0, r->dimensions()[Geom::Y]));
- move_p[Geom::Y] = -move_p[Geom::Y];
- Geom::Affine move = Geom::Affine(Geom::Translate(move_p));
-
std::vector<SPItem*> items_(items().begin(), items().end());
sort(items_.begin(),items_.end(),sp_object_compare_position_bool);
@@ -3428,10 +3413,9 @@ void ObjectSet::tile(bool apply)
int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
+ Geom::Affine move = Geom::Translate(- bbox.min());
gchar const *pat_id = SPPattern::produce(repr_copies, bbox, doc,
- ( Geom::Affine(Geom::Translate(desktop()->dt2doc(Geom::Point(r->min()[Geom::X],
- r->max()[Geom::Y]))))
- * parent_transform.inverse() ),
+ move.inverse() /* patternTransform */,
parent_transform * move);
// restore compensation setting
@@ -3443,13 +3427,14 @@ void ObjectSet::tile(bool apply)
rect->setAttribute("style", style_str);
g_free(style_str);
- Geom::Point min = bbox.min() * parent_transform.inverse();
- Geom::Point max = bbox.max() * parent_transform.inverse();
+ gchar *c = sp_svg_transform_write(parent_transform.inverse());
+ rect->setAttribute("transform", c);
+ g_free(c);
- sp_repr_set_svg_double(rect, "width", max[Geom::X] - min[Geom::X]);
- sp_repr_set_svg_double(rect, "height", max[Geom::Y] - min[Geom::Y]);
- sp_repr_set_svg_double(rect, "x", min[Geom::X]);
- sp_repr_set_svg_double(rect, "y", min[Geom::Y]);
+ sp_repr_set_svg_double(rect, "width", bbox.width());
+ sp_repr_set_svg_double(rect, "height", bbox.height());
+ sp_repr_set_svg_double(rect, "x", bbox.left());
+ sp_repr_set_svg_double(rect, "y", bbox.top());
// restore parent and position
parent->getRepr()->appendChild(rect);
@@ -3741,20 +3726,21 @@ void ObjectSet::createBitmapCopy()
{
SPItem *parentItem = dynamic_cast<SPItem *>(parent_object);
if (parentItem) {
- eek = parentItem->i2dt_affine();
+ eek = parentItem->i2doc_affine();
} else {
g_assert_not_reached();
}
}
Geom::Affine t;
- double shift_x = bbox->min()[Geom::X];
- double shift_y = bbox->max()[Geom::Y];
+ auto bbox_doc = (*bbox) * _desktop->dt2doc();
+ double shift_x = bbox_doc.left();
+ double shift_y = bbox_doc.top();
if (res == Inkscape::Util::Quantity::convert(1, "in", "px")) { // for default 96 dpi, snap it to pixel grid
shift_x = round(shift_x);
- shift_y = -round(-shift_y); // this gets correct rounding despite coordinate inversion, remove the negations when the inversion is gone
+ shift_y = round(shift_y);
}
- t = Geom::Scale(1, -1) * Geom::Translate(shift_x, shift_y) * eek.inverse(); /// @fixme hardcoded doc2dt transform?
+ t = Geom::Translate(shift_x, shift_y) * eek.inverse();
// TODO: avoid roundtrip via file
// Do the export
diff --git a/src/seltrans.cpp b/src/seltrans.cpp
index 837f0da0e..ce7c63b04 100644
--- a/src/seltrans.cpp
+++ b/src/seltrans.cpp
@@ -278,6 +278,10 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s
_items_centers.push_back(it->getCenter()); // for content-dragging, we need to remember original centers
}
+ if (y != -1 && _desktop->is_yaxisdown()) {
+ y = 1 - y;
+ }
+
_handle_x = x;
_handle_y = y;
@@ -631,12 +635,14 @@ void Inkscape::SelTrans::_showHandles(SPSelTransType type)
// shouldn't have nullary bbox, but knots
g_assert(_bbox);
+ auto const y_dir = _desktop->yaxisdir();
+
for (int i = 0; i < NUMHANDS; i++) {
if (hands[i].type != type)
continue;
// Position knots to scale the selection bbox
- Geom::Point const bpos(hands[i].x, hands[i].y);
+ Geom::Point const bpos(hands[i].x, (hands[i].y - 0.5) * (-y_dir) + 0.5);
Geom::Point p(_bbox->min() + (_bbox->dimensions() * Geom::Scale(bpos)));
knots[i]->moveto(p);
knots[i]->show();
diff --git a/src/ui/dialog/align-and-distribute.cpp b/src/ui/dialog/align-and-distribute.cpp
index 7ad8a5e18..b21fd2956 100644
--- a/src/ui/dialog/align-and-distribute.cpp
+++ b/src/ui/dialog/align-and-distribute.cpp
@@ -130,7 +130,7 @@ void ActionAlign::do_action(SPDesktop *desktop, int index)
std::vector<SPItem*> selected(selection->items().begin(), selection->items().end());
if (selected.empty()) return;
- const Coeffs &a = _allCoeffs[index];
+ Coeffs a = _allCoeffs[index]; // copy
SPItem *focus = nullptr;
Geom::OptRect b = Geom::OptRect();
Selection::CompareSize horiz = (a.mx0 != 0.0) || (a.mx1 != 0.0)
@@ -168,6 +168,13 @@ void ActionAlign::do_action(SPDesktop *desktop, int index)
b = focus->desktopPreferredBounds();
g_return_if_fail(b);
+ if (horiz == Selection::HORIZONTAL && desktop->is_yaxisdown()) {
+ a.my0 = 1. - a.my0;
+ a.my1 = 1. - a.my1;
+ a.sy0 = 1. - a.sy0;
+ a.sy1 = 1. - a.sy1;
+ }
+
// Generate the move point from the selected bounding box
Geom::Point mp = Geom::Point(a.mx0 * b->min()[Geom::X] + a.mx1 * b->max()[Geom::X],
a.my0 * b->min()[Geom::Y] + a.my1 * b->max()[Geom::Y]);
@@ -303,6 +310,13 @@ private :
++second;
if (second == selected.end()) return;
+ double kBegin = _kBegin;
+ double kEnd = _kEnd;
+ if (_orientation == Geom::Y && desktop->is_yaxisdown()) {
+ kBegin = 1. - kBegin;
+ kEnd = 1. - kEnd;
+ }
+
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
int prefs_bbox = prefs->getBool("/tools/bounding_box");
std::vector< BBoxSort > sorted;
@@ -312,7 +326,7 @@ private :
SPItem *item = *it;
Geom::OptRect bbox = !prefs_bbox ? (item)->desktopVisualBounds() : (item)->desktopGeometricBounds();
if (bbox) {
- sorted.emplace_back(item, *bbox, _orientation, _kBegin, _kEnd);
+ sorted.emplace_back(item, *bbox, _orientation, kBegin, kEnd);
}
}
//sort bbox by anchors
diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp
index ecf470d21..bb15d771d 100644
--- a/src/ui/dialog/inkscape-preferences.cpp
+++ b/src/ui/dialog/inkscape-preferences.cpp
@@ -771,6 +771,11 @@ void InkscapePreferences::initPageUI()
_("Selects whether the dockbar switcher will show text labels, icons, or both"), false);
}
+ _ui_yaxisdown.init( _("Origin at upper left with y-axis pointing down (requires restart)"), "/options/yaxisdown", false);
+ _page_ui.add_line( false, "", _ui_yaxisdown, "",
+ _("When off, origin is at lower left corner and y-axis points up"), true);
+
+
// Theme
_page_theme.add_group_header(_("Theme changes"));
{
diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h
index 55ea35f63..d0b51fa83 100644
--- a/src/ui/dialog/inkscape-preferences.h
+++ b/src/ui/dialog/inkscape-preferences.h
@@ -385,6 +385,7 @@ protected:
UI::Widget::PrefSpinButton _misc_recent;
UI::Widget::PrefCheckButton _ui_partialdynamic;
UI::Widget::ZoomCorrRulerSlider _ui_zoom_correction;
+ UI::Widget::PrefCheckButton _ui_yaxisdown;
//Spellcheck
UI::Widget::PrefCombo _spell_language;
diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp
index 8f7a1761b..27105b8cf 100644
--- a/src/ui/tool/control-point-selection.cpp
+++ b/src/ui/tool/control-point-selection.cpp
@@ -662,11 +662,11 @@ bool ControlPointSelection::event(Inkscape::UI::Tools::ToolBase * /*event_contex
case GDK_KEY_Up:
case GDK_KEY_KP_Up:
case GDK_KEY_KP_8:
- return _keyboardMove(event->key, Geom::Point(0, 1));
+ return _keyboardMove(event->key, Geom::Point(0, -_desktop->yaxisdir()));
case GDK_KEY_Down:
case GDK_KEY_KP_Down:
case GDK_KEY_KP_2:
- return _keyboardMove(event->key, Geom::Point(0, -1));
+ return _keyboardMove(event->key, Geom::Point(0, _desktop->yaxisdir()));
case GDK_KEY_Right:
case GDK_KEY_KP_Right:
case GDK_KEY_KP_6:
@@ -678,9 +678,9 @@ bool ControlPointSelection::event(Inkscape::UI::Tools::ToolBase * /*event_contex
// rotates
case GDK_KEY_bracketleft:
- return _keyboardRotate(event->key, 1);
+ return _keyboardRotate(event->key, -_desktop->yaxisdir());
case GDK_KEY_bracketright:
- return _keyboardRotate(event->key, -1);
+ return _keyboardRotate(event->key, _desktop->yaxisdir());
// scaling
case GDK_KEY_less:
diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp
index b80a1e675..dd700d9b3 100644
--- a/src/ui/tool/multi-path-manipulator.cpp
+++ b/src/ui/tool/multi-path-manipulator.cpp
@@ -595,11 +595,11 @@ bool MultiPathManipulator::event(Inkscape::UI::Tools::ToolBase *event_context, G
// rotation
case GDK_KEY_bracketleft:
case GDK_KEY_braceleft:
- pm.rotateHandle(n, which, 1, one_pixel);
+ pm.rotateHandle(n, which, -_desktop->yaxisdir(), one_pixel);
break;
case GDK_KEY_bracketright:
case GDK_KEY_braceright:
- pm.rotateHandle(n, which, -1, one_pixel);
+ pm.rotateHandle(n, which, _desktop->yaxisdir(), one_pixel);
break;
// adjust length
case GDK_KEY_period:
diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp
index 2369a75d4..a00f065d8 100644
--- a/src/ui/tool/path-manipulator.cpp
+++ b/src/ui/tool/path-manipulator.cpp
@@ -1445,16 +1445,14 @@ void PathManipulator::_updateOutline()
// linear segment that starts at the time value of 0.5 and extends for 10 pixels
// at an angle 150 degrees from the unit tangent. This creates the appearance
// of little 'harpoons' that show the direction of the subpaths.
+ auto rot_scale_w2d = Geom::Rotate(210.0 / 180.0 * M_PI) * Geom::Scale(10.0) * _desktop->w2d();
Geom::PathVector arrows;
for (Geom::PathVector::iterator i = pv.begin(); i != pv.end(); ++i) {
Geom::Path &path = *i;
for (Geom::Path::iterator j = path.begin(); j != path.end_default(); ++j) {
Geom::Point at = j->pointAt(0.5);
Geom::Point ut = j->unitTangentAt(0.5);
- // rotate the point
- ut *= Geom::Rotate(150.0 / 180.0 * M_PI);
- Geom::Point arrow_end = _desktop->w2d(
- _desktop->d2w(at) + Geom::unit_vector(_desktop->d2w(ut)) * 10.0);
+ Geom::Point arrow_end = at + (Geom::unit_vector(_desktop->d2w(ut)) * rot_scale_w2d);
Geom::Path arrow(at);
arrow.appendNew<Geom::LineSegment>(arrow_end);
diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp
index 382654dad..b0da4f026 100644
--- a/src/ui/tool/transform-handle-set.cpp
+++ b/src/ui/tool/transform-handle-set.cpp
@@ -243,8 +243,8 @@ double ScaleHandle::_last_scale_y = 1.0;
class ScaleCornerHandle : public ScaleHandle {
public:
- ScaleCornerHandle(TransformHandleSet &th, unsigned corner) :
- ScaleHandle(th, corner_to_anchor(corner), _corner_to_pixbuf(corner)),
+ ScaleCornerHandle(TransformHandleSet &th, unsigned corner, unsigned d_corner) :
+ ScaleHandle(th, corner_to_anchor(d_corner), _corner_to_pixbuf(d_corner)),
_corner(corner)
{}
@@ -330,8 +330,8 @@ private:
*/
class ScaleSideHandle : public ScaleHandle {
public:
- ScaleSideHandle(TransformHandleSet &th, unsigned side)
- : ScaleHandle(th, side_to_anchor(side), _side_to_pixbuf(side))
+ ScaleSideHandle(TransformHandleSet &th, unsigned side, unsigned d_side)
+ : ScaleHandle(th, side_to_anchor(d_side), _side_to_pixbuf(side))
, _side(side)
{}
protected:
@@ -409,8 +409,8 @@ private:
*/
class RotateHandle : public TransformHandle {
public:
- RotateHandle(TransformHandleSet &th, unsigned corner)
- : TransformHandle(th, corner_to_anchor(corner), _corner_to_pixbuf(corner))
+ RotateHandle(TransformHandleSet &th, unsigned corner, unsigned d_corner)
+ : TransformHandle(th, corner_to_anchor(d_corner), _corner_to_pixbuf(d_corner))
, _corner(corner)
{}
protected:
@@ -491,8 +491,8 @@ double RotateHandle::_last_angle = 0;
class SkewHandle : public TransformHandle {
public:
- SkewHandle(TransformHandleSet &th, unsigned side)
- : TransformHandle(th, side_to_anchor(side), _side_to_pixbuf(side))
+ SkewHandle(TransformHandleSet &th, unsigned side, unsigned d_side)
+ : TransformHandle(th, side_to_anchor(d_side), _side_to_pixbuf(side))
, _side(side)
{}
@@ -707,11 +707,14 @@ TransformHandleSet::TransformHandleSet(SPDesktop *d, SPCanvasGroup *th_group)
sp_canvas_item_hide(_trans_outline);
_trans_outline->setDashed(true);
+ bool y_inverted = !d->is_yaxisdown();
for (unsigned i = 0; i < 4; ++i) {
- _scale_corners[i] = new ScaleCornerHandle(*this, i);
- _scale_sides[i] = new ScaleSideHandle(*this, i);
- _rot_corners[i] = new RotateHandle(*this, i);
- _skew_sides[i] = new SkewHandle(*this, i);
+ unsigned d_c = y_inverted ? i : 3 - i;
+ unsigned d_s = y_inverted ? i : 6 - i;
+ _scale_corners[i] = new ScaleCornerHandle(*this, i, d_c);
+ _scale_sides[i] = new ScaleSideHandle(*this, i, d_s);
+ _rot_corners[i] = new RotateHandle(*this, i, d_c);
+ _skew_sides[i] = new SkewHandle(*this, i, d_s);
}
_center = new RotationCenter(*this);
// when transforming, update rotation center position
diff --git a/src/ui/tools/calligraphic-tool.cpp b/src/ui/tools/calligraphic-tool.cpp
index 50c5f413a..b4017e5e7 100644
--- a/src/ui/tools/calligraphic-tool.cpp
+++ b/src/ui/tools/calligraphic-tool.cpp
@@ -288,6 +288,7 @@ bool CalligraphicTool::apply(Geom::Point p) {
// 1b. fixed dc->angle (absolutely flat nib):
double const radians = ( (this->angle - 90) / 180.0 ) * M_PI;
Geom::Point ang1 = Geom::Point(-sin(radians), cos(radians));
+ ang1.y() *= -this->desktop->yaxisdir();
a1 = atan2(ang1);
}
diff --git a/src/ui/tools/flood-tool.cpp b/src/ui/tools/flood-tool.cpp
index 1d1930a4e..de57d4500 100644
--- a/src/ui/tools/flood-tool.cpp
+++ b/src/ui/tools/flood-tool.cpp
@@ -765,8 +765,8 @@ static void sp_flood_do_flood_fill(ToolBase *event_context, GdkEvent *event, boo
unsigned int width = (int)ceil(screen.width() * zoom_scale * padding);
unsigned int height = (int)ceil(screen.height() * zoom_scale * padding);
- Geom::Point origin(screen.min()[Geom::X],
- document->getHeight().value("px") - screen.height() - screen.min()[Geom::Y]);
+ Geom::Point origin = screen.corner(desktop->is_yaxisdown() ? 0 : 3)
+ * desktop->doc2dt();
origin[Geom::X] += (screen.width() * ((1 - padding) / 2));
origin[Geom::Y] += (screen.height() * ((1 - padding) / 2));
@@ -876,7 +876,11 @@ static void sp_flood_do_flood_fill(ToolBase *event_context, GdkEvent *event, boo
}
for (unsigned int i = 0; i < fill_points.size(); i++) {
- Geom::Point pw = Geom::Point(fill_points[i][Geom::X] / zoom_scale, document->getHeight().value("px") + (fill_points[i][Geom::Y] / zoom_scale)) * affine;
+ Geom::Point pw = fill_points[i]
+ * Geom::Scale(1. / zoom_scale)
+ * desktop->doc2dt().withoutTranslation()
+ * desktop->doc2dt()
+ * affine;
pw[Geom::X] = (int)MIN(width - 1, MAX(0, pw[Geom::X]));
pw[Geom::Y] = (int)MIN(height - 1, MAX(0, pw[Geom::Y]));
diff --git a/src/ui/tools/measure-tool.cpp b/src/ui/tools/measure-tool.cpp
index a305fa350..75335ce1a 100644
--- a/src/ui/tools/measure-tool.cpp
+++ b/src/ui/tools/measure-tool.cpp
@@ -862,6 +862,13 @@ void MeasureTool::setGuide(Geom::Point origin,double angle, const char *label)
if(!namedview) {
return;
}
+
+ // <sodipodi:guide> stores inverted y-axis coordinates
+ if (desktop->is_yaxisdown()) {
+ origin[Geom::Y] = doc->getHeight().value("px") - origin[Geom::Y];
+ angle *= -1.0;
+ }
+
origin *= affine;
//measure angle
Inkscape::XML::Node *guide;
diff --git a/src/ui/tools/select-tool.cpp b/src/ui/tools/select-tool.cpp
index 92ae7558b..6e8d631df 100644
--- a/src/ui/tools/select-tool.cpp
+++ b/src/ui/tools/select-tool.cpp
@@ -906,6 +906,7 @@ bool SelectTool::root_handler(GdkEvent* event) {
gdouble const nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000, "px"); // in px
gdouble const offset = prefs->getDoubleLimited("/options/defaultscale/value", 2, 0, 1000, "px");
int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
+ auto const y_dir = desktop->yaxisdir();
switch (get_latin_keyval (&event->key)) {
case GDK_KEY_Left: // move selection left
@@ -935,6 +936,7 @@ bool SelectTool::root_handler(GdkEvent* event) {
case GDK_KEY_KP_Up:
if (!MOD__CTRL(event)) { // not ctrl
gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask
+ mul *= -y_dir;
if (MOD__ALT(event)) { // alt
if (MOD__SHIFT(event)) {
@@ -981,6 +983,7 @@ bool SelectTool::root_handler(GdkEvent* event) {
case GDK_KEY_KP_Down:
if (!MOD__CTRL(event)) { // not ctrl
gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask
+ mul *= -y_dir;
if (MOD__ALT(event)) { // alt
if (MOD__SHIFT(event)) {
@@ -1038,9 +1041,9 @@ bool SelectTool::root_handler(GdkEvent* event) {
gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask
selection->rotateScreen(mul*1);
} else if (MOD__CTRL(event)) {
- selection->rotate(90);
+ selection->rotate(-90 * y_dir);
} else if (snaps) {
- selection->rotate(180.0/snaps);
+ selection->rotate(-180.0/snaps * y_dir);
}
ret = TRUE;
@@ -1051,9 +1054,9 @@ bool SelectTool::root_handler(GdkEvent* event) {
gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask
selection->rotateScreen(-1*mul);
} else if (MOD__CTRL(event)) {
- selection->rotate(-90);
+ selection->rotate(90 * y_dir);
} else if (snaps) {
- selection->rotate(-180.0/snaps);
+ selection->rotate(180.0/snaps * y_dir);
}
ret = TRUE;
diff --git a/src/ui/tools/spray-tool.cpp b/src/ui/tools/spray-tool.cpp
index c56bceda4..0cad1382e 100644
--- a/src/ui/tools/spray-tool.cpp
+++ b/src/ui/tools/spray-tool.cpp
@@ -969,7 +969,7 @@ static bool sp_spray_recursive(SPDesktop *desktop,
sp_spray_scale_rel(center,desktop, item_copied, Geom::Scale(scale));
sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
// Move the cursor p
- sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
+ sp_item_move_rel(item_copied, Geom::Translate(move * desktop->doc2dt().withoutTranslation()));
Inkscape::GC::release(copy);
if(picker){
sp_desktop_apply_css_recursive(item_copied, css, true);
@@ -1013,7 +1013,7 @@ static bool sp_spray_recursive(SPDesktop *desktop,
sp_spray_scale_rel(center, desktop, item_copied, Geom::Scale(_scale, _scale));
sp_spray_scale_rel(center, desktop, item_copied, Geom::Scale(scale, scale));
sp_spray_rotate_rel(center, desktop, item_copied, Geom::Rotate(angle));
- sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
+ sp_item_move_rel(item_copied, Geom::Translate(move * desktop->doc2dt().withoutTranslation()));
// Union and duplication
set->clear();
@@ -1101,7 +1101,7 @@ static bool sp_spray_recursive(SPDesktop *desktop,
sp_spray_scale_rel(center, desktop, item_copied, Geom::Scale(_scale, _scale));
sp_spray_scale_rel(center, desktop, item_copied, Geom::Scale(scale, scale));
sp_spray_rotate_rel(center, desktop, item_copied, Geom::Rotate(angle));
- sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
+ sp_item_move_rel(item_copied, Geom::Translate(move * desktop->doc2dt().withoutTranslation()));
if(picker){
sp_desktop_apply_css_recursive(item_copied, css, true);
}
diff --git a/src/ui/tools/text-tool.cpp b/src/ui/tools/text-tool.cpp
index 713d72a40..8542b1b6f 100644
--- a/src/ui/tools/text-tool.cpp
+++ b/src/ui/tools/text-tool.cpp
@@ -623,7 +623,8 @@ bool TextTool::root_handler(GdkEvent* event) {
// Cursor height is defined by the new text object's font size; it needs to be set
// artificially here, for the text object does not exist yet:
double cursor_height = sp_desktop_get_font_size_tool(desktop);
- this->cursor->setCoords(p1, p1 + Geom::Point(0, cursor_height));
+ auto const y_dir = desktop->yaxisdir();
+ this->cursor->setCoords(p1, p1 - Geom::Point(0, y_dir * cursor_height));
if (this->imc) {
GdkRectangle im_cursor;
Geom::Point const top_left = SP_EVENT_CONTEXT(this)->desktop->get_display_area().corner(3);
diff --git a/src/ui/tools/tweak-tool.cpp b/src/ui/tools/tweak-tool.cpp
index 060131b49..d0b8209f0 100644
--- a/src/ui/tools/tweak-tool.cpp
+++ b/src/ui/tools/tweak-tool.cpp
@@ -397,7 +397,7 @@ sp_tweak_dilate_recursive (Inkscape::Selection *selection, SPItem *item, Geom::P
if (a->contains(p)) x = 0;
if (x < 1) {
Geom::Point move = force * 0.5 * (cos(M_PI * x) + 1) * vector;
- sp_item_move_rel(item, Geom::Translate(move[Geom::X], -move[Geom::Y]));
+ sp_item_move_rel(item, Geom::Translate(move * selection->desktop()->doc2dt().withoutTranslation()));
did = true;
}
}
@@ -411,7 +411,7 @@ sp_tweak_dilate_recursive (Inkscape::Selection *selection, SPItem *item, Geom::P
if (x < 1) {
Geom::Point move = force * 0.5 * (cos(M_PI * x) + 1) *
(reverse? (a->midpoint() - p) : (p - a->midpoint()));
- sp_item_move_rel(item, Geom::Translate(move[Geom::X], -move[Geom::Y]));
+ sp_item_move_rel(item, Geom::Translate(move * selection->desktop()->doc2dt().withoutTranslation()));
did = true;
}
}
@@ -426,7 +426,7 @@ sp_tweak_dilate_recursive (Inkscape::Selection *selection, SPItem *item, Geom::P
if (a->contains(p)) x = 0;
if (x < 1) {
Geom::Point move = force * 0.5 * (cos(M_PI * x) + 1) * Geom::Point(cos(dp)*dr, sin(dp)*dr);
- sp_item_move_rel(item, Geom::Translate(move[Geom::X], -move[Geom::Y]));
+ sp_item_move_rel(item, Geom::Translate(move * selection->desktop()->doc2dt().withoutTranslation()));
did = true;
}
}
@@ -452,6 +452,7 @@ sp_tweak_dilate_recursive (Inkscape::Selection *selection, SPItem *item, Geom::P
if (a->contains(p)) x = 0;
if (x < 1) {
double angle = (reverse? force : -force) * 0.05 * (cos(M_PI * x) + 1) * M_PI;
+ angle *= -selection->desktop()->yaxisdir();
sp_item_rotate_rel(item, Geom::Rotate(angle));
did = true;
}
diff --git a/src/ui/widget/page-sizer.cpp b/src/ui/widget/page-sizer.cpp
index 58af30fc9..aede866b1 100644
--- a/src/ui/widget/page-sizer.cpp
+++ b/src/ui/widget/page-sizer.cpp
@@ -527,7 +527,7 @@ PageSizer::setDim (Inkscape::Util::Quantity w, Inkscape::Util::Quantity h, bool
doc->setWidthAndHeight (w, h, changeSize);
// The origin for the user is in the lower left corner; this point should remain stationary when
// changing the page size. The SVG's origin however is in the upper left corner, so we must compensate for this
- if (changeSize) {
+ if (changeSize && !SP_ACTIVE_DESKTOP->is_yaxisdown()) {
Geom::Translate const vert_offset(Geom::Point(0, (old_height.value("px") - h.value("px"))));
doc->getRoot()->translateChildItems(vert_offset);
}
diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp
index 1626655ff..fcb114260 100644
--- a/src/widgets/desktop-widget.cpp
+++ b/src/widgets/desktop-widget.cpp
@@ -1712,6 +1712,9 @@ sp_desktop_widget_update_rulers (SPDesktopWidget *dtw)
double lower_y = dtw->dt2r * (viewbox.bottom() - dtw->ruler_origin[Geom::Y]);
double upper_y = dtw->dt2r * (viewbox.top() - dtw->ruler_origin[Geom::Y]);
+ if (dtw->desktop->is_yaxisdown()) {
+ std::swap(lower_y, upper_y);
+ }
sp_ruler_set_range(SP_RULER(dtw->vruler),
lower_y,
upper_y,
@@ -2293,8 +2296,9 @@ sp_desktop_widget_update_scrollbars (SPDesktopWidget *dtw, double scale)
}
/* Canvas region we always show unconditionally */
- Geom::Rect carea( Geom::Point(deskarea->min()[Geom::X] * scale - 64, deskarea->max()[Geom::Y] * -scale - 64),
- Geom::Point(deskarea->max()[Geom::X] * scale + 64, deskarea->min()[Geom::Y] * -scale + 64) );
+ double const y_dir = dtw->desktop->yaxisdir();
+ Geom::Rect carea( Geom::Point(deskarea->left() * scale - 64, (deskarea->top() * scale + 64) * y_dir),
+ Geom::Point(deskarea->right() * scale + 64, (deskarea->bottom() * scale - 64) * y_dir) );
Geom::Rect viewbox = dtw->canvas->getViewbox();