diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp index 6af5a6210ce7c0cbdaa68b15afb5627264be4a2d..5256d5abb3f99a8ba81f6ed33a18ba68fa40a211 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(); + // 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 7b72524ccc3be851bfa9cc67f26c557dedfbf3a8..bec4bd68f9939d687f20b3e7a87c2a24b4796105 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 a1281a5b826c03d9221e59cfe2638a9cf4c5d8d1..9087f8d317a3edb829bfda254e929275b17854ca 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 42769e1b1e3cdc44191aa8d4a7da5951bb3ac892..db765820f95902eda0486e30744e2f1a17c8a0fd 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 a35f07c3d4794dcbc83be6bb915bde60649d0967..f097203fd7d6b49ae5bcaa57e32afbfcbfea4d2b 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 658bc93c9bced159bfb30f39acb2f2ab4cc0d3e6..d4094b7e9696a72b5e73d1f57ed047e958808807 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 e390c5314672e9c762849d87db48ad6f3716e5eb..32f42c3cec972d03c1ffc65c1892d506648adc58 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 9e05ecd2942174743ad1643fffbed86125783fdb..82531e009fc656e6e1fcaf43d4e28098e0b6bc6e 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(document)->doc2dt(); bounding_area = temprec; } diff --git a/src/extension/internal/latex-text-renderer.cpp b/src/extension/internal/latex-text-renderer.cpp index f346a304ad16f2cb004db2d08a98b93dd0c31ea7..c78471080e780557deb931444358c6c954b706b1 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 a79efdbddfb8ed5216aa09d0e599ad088faea7c5..93ea8bc98caf5eae8d74780bae5f9189e04be9ef 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 92fdc36485829ef4276229be16c75df724301915..fe5412c4e42133339b16581cc6c3fbc07692ed65 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 3d34cdf01f6cce0de0cf1f42a961059f40cce042..83cc3d772db1cf302d705f0724484b7fb4d13114 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 df95f4f84198c4b1c6025d74ccdf5bfb1b808659..7be7c10f73d9925d37790e378069433ce790d68f 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 2183b62369973ff30e77435c11d3399b9f7c93bd..05ba5898bc32db695cc0e9eb124e241acb2d286d 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(); + } + + // 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) { + // 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 0b85582306dfd16f9f2c545d65114ebf7ace40e3..acb0707d0c7bb512a17ae06b405ef80d9116dcd6 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); + + // 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); } } + + // 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 } } + // 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(); + // 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; + + // 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 d7c461f921b7b85852c2d6b7228e2ec74873cd39..474687cc6a18053f9e23706ea1af2895f83040c6 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 e9b9bb4691a8351c4ada18af9558d65b412686d7..bc27d380cf422f0363ce584d57ea7d5087f90c06 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 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 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(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 837f0da0e2c5b9aae8c597646cd008c355ab9713..ce7c63b0429e0fd3a50fcdeb7f399f1064d58ce1 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 7ad8a5e185d95c42da0ced487829620106bfb31f..b21fd295690ed93d06dae32f55e0a148128f6bc7 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 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 ecf470d21c70bf517f8f5bb256d6b81a69635c95..bb15d771d40aea7c70e35b5eeea21677bb155100 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 55ea35f63445932ed9b84df3c688ab0d138dd3bd..d0b51fa836f90f325de49b6f021e34698bc57b64 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 8f7a1761b29c0b6d954c10632242a51da2122932..27105b8cfc0aef3a1fe3035b3e7426a9f8d265bc 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 b80a1e67569120cea1e21c92791d04dd389c97f3..dd700d9b3b9b950299dd79cab58700808785ae0c 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 2369a75d416f86560aab6e835326d0564afd2e5f..a00f065d84c1864bd25e41b2a5e1c91a0186188a 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(arrow_end); diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp index 382654dad110672b6c3f210583a490ea49372e11..b0da4f026382f105bce71d58c196c2cb222ec63f 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 50c5f413ac1d5839c7cd81947327842b80a9bae4..b4017e5e7819caec21e47648f8652c2ec72b1308 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 1d1930a4e2a549e229be4537a60eefbf67cab331..de57d450053e87943add18420a921abf34a4079d 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 a305fa350178816f92df5dc0cf377f6f12ea5307..75335ce1a6c3b0be9e7cf713857b5f1c4b8bdfe3 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; } + + // 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 92ae7558b359b6336516645c73882466ed92122a..6e8d631df1d65eb8bc35fa6fc4e729aa90d42498 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 c56bceda46f40e33301d9a9d8bac9c5e334dd3a7..0cad1382e0dad663e4bce03c0b93fccec0538363 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 713d72a40a10ae2cbbcc28ab4a6d6e53fcdf980c..8542b1b6f350461cc88a53930272339f11a73e6f 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 060131b497a6cd8e672010b89a2171c3b76b33c9..d0b8209f09aaf42dd44b29eb69582002608eff49 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 58af30fc93a219f5d24798f42de471709b098d6e..aede866b1cfe8807a90c95142dbedf9042dcb0a9 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 1626655fff43fe3127700c8852a1ed724ab723b1..fcb1142603e00b72df0774e1b84e8a65705a2541 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();