Commit 1fa0c72b authored by Thomas Holder's avatar Thomas Holder

New option to invert y-axis

Replaces all hard coded or implicit desktop coordinate usage with
doc2dt multiplication.

New global preference: Interface > Origin at upper left

https://bugs.launchpad.net/inkscape/+bug/170049
parent 38a3495f
Pipeline #30021913 failed with stages
in 87 minutes and 20 seconds
......@@ -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;
......
......@@ -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);
......
......@@ -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;
......
......@@ -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);
}
......
......@@ -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 );
......
......@@ -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);
}
}
......
......@@ -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);
}
}
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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);
......
......@@ -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
......
......@@ -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 */
......
......@@ -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);
......
......@@ -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 <<