From a344fb9062aad269c98e0357807be2ffcdd07fbc Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Sat, 8 Dec 2012 13:57:50 -0500 Subject: [PATCH 1/7] whitespace fix --- src/display/canvas-grid.cpp | 2 +- src/ui/dialog/inkscape-preferences.cpp | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index 329b621..dd3304c 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -80,7 +80,7 @@ grid_canvasitem_get_type (void) (GInstanceInitFunc) grid_canvasitem_init, NULL }; - + grid_canvasitem_type = g_type_register_static(SPCanvasItem::getType(), "GridCanvasItem", &grid_canvasitem_info, GTypeFlags(0)); } return grid_canvasitem_type; diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 2910599..8506f33 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -492,7 +492,7 @@ void InkscapePreferences::initPageTools() this->AddPage(_page_dropper, _("Dropper"), iter_tools, PREFS_PAGE_TOOLS_DROPPER); this->AddSelcueCheckbox(_page_dropper, "/tools/dropper", true); this->AddGradientCheckbox(_page_dropper, "/tools/dropper", true); - + //Connector this->AddPage(_page_connector, _("Connector"), iter_tools, PREFS_PAGE_TOOLS_CONNECTOR); this->AddSelcueCheckbox(_page_connector, "/tools/connector", true); @@ -614,7 +614,7 @@ void InkscapePreferences::initPageUI() _win_native.init ( _("Native open/save dialogs"), "/options/desktopintegration/value", 1, true, 0); _win_gtk.init ( _("GTK open/save dialogs"), "/options/desktopintegration/value", 0, false, &_win_native); - + _win_hide_task.init ( _("Dialogs are hidden in taskbar"), "/options/dialogsskiptaskbar/value", true); _win_save_viewport.init ( _("Save and restore documents viewport"), "/options/savedocviewport/value", true); _win_zoom_resize.init ( _("Zoom when window is resized"), "/options/stickyzoom/value", false); @@ -1264,28 +1264,28 @@ void InkscapePreferences::initPageBehavior() _mask_mask_remove.init ( _("Remove clippath/mask object after applying"), "/options/maskobject/remove", true); _page_mask.add_line(false, "", _mask_mask_remove, "", _("After applying, remove the object used as the clipping path or mask from the drawing")); - + _page_mask.add_group_header( _("Before applying")); - + _mask_grouping_none.init( _("Do not group clipped/masked objects"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_NONE, true, 0); _mask_grouping_separate.init( _("Enclose every clipped/masked object in its own group"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_SEPARATE, false, &_mask_grouping_none); _mask_grouping_all.init( _("Put all clipped/masked objects into one group"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_ALL, false, &_mask_grouping_none); - + _page_mask.add_line(true, "", _mask_grouping_none, "", _("Apply clippath/mask to every object")); - + _page_mask.add_line(true, "", _mask_grouping_separate, "", _("Apply clippath/mask to groups containing single object")); - + _page_mask.add_line(true, "", _mask_grouping_all, "", _("Apply clippath/mask to group containing all objects")); - + _page_mask.add_group_header( _("After releasing")); - + _mask_ungrouping.init ( _("Ungroup automatically created groups"), "/options/maskobject/ungrouping", true); _page_mask.add_line(true, "", _mask_ungrouping, "", _("Ungroup groups created when setting clip/mask")); - + this->AddPage(_page_mask, _("Clippaths and masks"), iter_behavior, PREFS_PAGE_BEHAVIOR_MASKS); @@ -1736,15 +1736,15 @@ void InkscapePreferences::initPageSpellcheck() /* the returned pointer should _not_ need to be deleted */ AspellDictInfoList *dlist = get_aspell_dict_info_list(config); - + /* config is no longer needed */ delete_aspell_config(config); - + AspellDictInfoEnumeration *dels = aspell_dict_info_list_elements(dlist); - + languages.push_back(Glib::ustring(_("None"))); langValues.push_back(Glib::ustring("")); - + const AspellDictInfo *entry; int en_index = 0; int i = 0; -- 1.7.11.7 From 1a400763903b4bc2a1a9e7cdff05b7b31fb68fb7 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Sat, 8 Dec 2012 15:03:45 -0500 Subject: [PATCH 2/7] add template for polygonal grid Create new 'polygonal' grid type. Right now this is just a copy of the axonometric grid type, to get the necessary bits in place before starting to develop the actual logic. --- src/display/CMakeLists.txt | 1 + src/display/canvas-grid.cpp | 9 +- src/display/canvas-grid.h | 5 +- src/display/canvas-ngongrid.cpp | 771 +++++++++++++++++++++++++++++++++ src/display/canvas-ngongrid.h | 90 ++++ src/ui/dialog/document-properties.cpp | 2 + src/ui/dialog/inkscape-preferences.cpp | 24 + src/ui/dialog/inkscape-preferences.h | 11 + 8 files changed, 909 insertions(+), 4 deletions(-) create mode 100644 src/display/canvas-ngongrid.cpp create mode 100644 src/display/canvas-ngongrid.h diff --git a/src/display/CMakeLists.txt b/src/display/CMakeLists.txt index 20424c8..5779e75 100644 --- a/src/display/CMakeLists.txt +++ b/src/display/CMakeLists.txt @@ -5,6 +5,7 @@ set(display_SRC canvas-axonomgrid.cpp canvas-bpath.cpp canvas-grid.cpp + canvas-ngongrid.cpp canvas-temporary-item-list.cpp canvas-temporary-item.cpp canvas-text.cpp diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index dd3304c..507f30b 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -26,6 +26,7 @@ #include "display/cairo-utils.h" #include "display/canvas-axonomgrid.h" #include "display/canvas-grid.h" +#include "display/canvas-ngongrid.h" #include "display/sp-canvas-group.h" #include "document.h" #include "helper/units.h" @@ -46,11 +47,13 @@ namespace Inkscape { static gchar const *const grid_name[] = { N_("Rectangular grid"), - N_("Axonometric grid") + N_("Axonometric grid"), + N_("Polygonal grid") }; static gchar const *const grid_svgname[] = { "xygrid", - "axonomgrid" + "axonomgrid", + "ngongrid" }; @@ -277,6 +280,8 @@ CanvasGrid::NewGrid(SPNamedView * nv, Inkscape::XML::Node * repr, SPDocument * d return dynamic_cast(new CanvasXYGrid(nv, repr, doc)); case GRID_AXONOMETRIC: return dynamic_cast(new CanvasAxonomGrid(nv, repr, doc)); + case GRID_POLYGONAL: + return dynamic_cast(new CanvasNGonGrid(nv, repr, doc)); } return NULL; diff --git a/src/display/canvas-grid.h b/src/display/canvas-grid.h index bba9b7e..43b4d7e 100644 --- a/src/display/canvas-grid.h +++ b/src/display/canvas-grid.h @@ -30,9 +30,10 @@ class Node; enum GridType { GRID_RECTANGULAR = 0, - GRID_AXONOMETRIC = 1 + GRID_AXONOMETRIC = 1, + GRID_POLYGONAL = 2 }; -#define GRID_MAXTYPENR 1 +#define GRID_MAXTYPENR 2 #define INKSCAPE_TYPE_GRID_CANVASITEM (Inkscape::grid_canvasitem_get_type ()) #define INKSCAPE_GRID_CANVASITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INKSCAPE_TYPE_GRID_CANVASITEM, GridCanvasItem)) diff --git a/src/display/canvas-ngongrid.cpp b/src/display/canvas-ngongrid.cpp new file mode 100644 index 0000000..8dbe273 --- /dev/null +++ b/src/display/canvas-ngongrid.cpp @@ -0,0 +1,771 @@ +/* + * Authors: + * Matthew Woehlke + * Johan Engelen + * + * Copyright (C) 2006-2012 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + /* + * Current limits are: one axis (y-axis) is always vertical. The other two + * axes are bound to a certain range of angles. The z-axis always has an angle + * smaller than 90 degrees (measured from horizontal, 0 degrees being a line extending + * to the right). The x-axis will always have an angle between 0 and 90 degrees. + */ + + +#include +#include +#include +#include + +#include "display/canvas-ngongrid.h" + +#include "ui/widget/registered-widget.h" +#include "desktop.h" +#include "desktop-handles.h" +#include "display/cairo-utils.h" +#include "display/canvas-grid.h" +#include "display/sp-canvas-util.h" +#include "display/sp-canvas.h" +#include "document.h" +#include "inkscape.h" +#include "preferences.h" +#include "sp-namedview.h" +#include "sp-object.h" +#include "svg/svg-color.h" +#include "2geom/line.h" +#include "2geom/angle.h" +#include "util/mathfns.h" +#include "round.h" +#include "helper/units.h" + + +enum Dim3 { X=0, Y, Z }; + +/** + * This function calls Cairo to render a line on a particular canvas buffer. + * Coordinates are interpreted as SCREENcoordinates + */ +static void +sp_cngongrid_drawline (SPCanvasBuf *buf, gint x0, gint y0, gint x1, gint y1, guint32 rgba) +{ + cairo_move_to(buf->ct, 0.5 + x0, 0.5 + y0); + cairo_line_to(buf->ct, 0.5 + x1, 0.5 + y1); + ink_cairo_set_source_rgba32(buf->ct, rgba); + cairo_stroke(buf->ct); +} + +static void +sp_grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba) +{ + if ((x < buf->rect.left()) || (x >= buf->rect.right())) + return; + + cairo_move_to(buf->ct, 0.5 + x, 0.5 + ys); + cairo_line_to(buf->ct, 0.5 + x, 0.5 + ye); + ink_cairo_set_source_rgba32(buf->ct, rgba); + cairo_stroke(buf->ct); +} + +namespace Inkscape { + + +/** +* A DIRECT COPY-PASTE FROM DOCUMENT-PROPERTIES.CPP TO QUICKLY GET RESULTS +* + * Helper function that attachs widgets in a 3xn table. The widgets come in an + * array that has two entries per table row. The two entries code for four + * possible cases: (0,0) means insert space in first column; (0, non-0) means + * widget in columns 2-3; (non-0, 0) means label in columns 1-3; and + * (non-0, non-0) means two widgets in columns 2 and 3. +**/ +#define SPACE_SIZE_X 15 +#define SPACE_SIZE_Y 10 +static inline void +attach_all(Gtk::Table &table, Gtk::Widget const *const arr[], unsigned size, int start = 0) +{ + for (unsigned i=0, r=start; i(*arr[i]), 1, 2, r, r+1, + Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0); + table.attach (const_cast(*arr[i+1]), 2, 3, r, r+1, + Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0); + } else { + if (arr[i+1]) { + table.attach (const_cast(*arr[i+1]), 1, 3, r, r+1, + Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0); + } else if (arr[i]) { + Gtk::Label& label = reinterpret_cast (const_cast(*arr[i])); + label.set_alignment (0.0); + table.attach (label, 0, 3, r, r+1, + Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0); + } else { + Gtk::HBox *space = manage (new Gtk::HBox); + space->set_size_request (SPACE_SIZE_X, SPACE_SIZE_Y); + table.attach (*space, 0, 1, r, r+1, + (Gtk::AttachOptions)0, (Gtk::AttachOptions)0,0,0); + } + } + ++r; + } +} + +CanvasNGonGrid::CanvasNGonGrid (SPNamedView * nv, Inkscape::XML::Node * in_repr, SPDocument * in_doc) + : CanvasGrid(nv, in_repr, in_doc, GRID_POLYGONAL) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gridunit = sp_unit_get_by_abbreviation( prefs->getString("/options/grids/ngon/units").data() ); + if (!gridunit) + gridunit = &sp_unit_get_by_id(SP_UNIT_PX); + origin[Geom::X] = sp_units_get_pixels( prefs->getDouble("/options/grids/ngon/origin_x", 0.0), *gridunit ); + origin[Geom::Y] = sp_units_get_pixels( prefs->getDouble("/options/grids/ngon/origin_y", 0.0), *gridunit ); + color = prefs->getInt("/options/grids/ngon/color", 0x0000ff20); + empcolor = prefs->getInt("/options/grids/ngon/empcolor", 0x0000ff40); + empspacing = prefs->getInt("/options/grids/ngon/empspacing", 5); + lengthy = sp_units_get_pixels( prefs->getDouble("/options/grids/ngon/spacing_y", 1.0), *gridunit ); + angle_deg[X] = prefs->getDouble("/options/grids/ngon/angle_x", 30.0); + angle_deg[Z] = prefs->getDouble("/options/grids/ngon/angle_z", 30.0); + angle_deg[Y] = 0; + + angle_rad[X] = Geom::deg_to_rad(angle_deg[X]); + tan_angle[X] = tan(angle_rad[X]); + angle_rad[Z] = Geom::deg_to_rad(angle_deg[Z]); + tan_angle[Z] = tan(angle_rad[Z]); + + snapper = new CanvasNGonGridSnapper(this, &namedview->snap_manager, 0); + + if (repr) readRepr(); +} + +CanvasNGonGrid::~CanvasNGonGrid () +{ + if (snapper) delete snapper; +} + + +/* fixme: Collect all these length parsing methods and think common sane API */ + +static gboolean sp_nv_read_length(gchar const *str, guint base, gdouble *val, SPUnit const **unit) +{ + if (!str) { + return FALSE; + } + + gchar *u; + gdouble v = g_ascii_strtod(str, &u); + if (!u) { + return FALSE; + } + while (isspace(*u)) { + u += 1; + } + + if (!*u) { + /* No unit specified - keep default */ + *val = v; + return TRUE; + } + + if (base & SP_UNIT_DEVICE) { + if (u[0] && u[1] && !isalnum(u[2]) && !strncmp(u, "px", 2)) { + *unit = &sp_unit_get_by_id(SP_UNIT_PX); + *val = v; + return TRUE; + } + } + + if (base & SP_UNIT_ABSOLUTE) { + if (!strncmp(u, "pt", 2)) { + *unit = &sp_unit_get_by_id(SP_UNIT_PT); + } else if (!strncmp(u, "mm", 2)) { + *unit = &sp_unit_get_by_id(SP_UNIT_MM); + } else if (!strncmp(u, "cm", 2)) { + *unit = &sp_unit_get_by_id(SP_UNIT_CM); + } else if (!strncmp(u, "m", 1)) { + *unit = &sp_unit_get_by_id(SP_UNIT_M); + } else if (!strncmp(u, "in", 2)) { + *unit = &sp_unit_get_by_id(SP_UNIT_IN); + } else if (!strncmp(u, "ft", 2)) { + *unit = &sp_unit_get_by_id(SP_UNIT_FT); + } else if (!strncmp(u, "pc", 2)) { + *unit = &sp_unit_get_by_id(SP_UNIT_PC); + } else { + return FALSE; + } + *val = v; + return TRUE; + } + + return FALSE; +} + +static gboolean sp_nv_read_opacity(gchar const *str, guint32 *color) +{ + if (!str) { + return FALSE; + } + + gchar *u; + gdouble v = g_ascii_strtod(str, &u); + if (!u) { + return FALSE; + } + v = CLAMP(v, 0.0, 1.0); + + *color = (*color & 0xffffff00) | (guint32) floor(v * 255.9999); + + return TRUE; +} + + + +void +CanvasNGonGrid::readRepr() +{ + gchar const *value; + if ( (value = repr->attribute("originx")) ) { + sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[Geom::X], &gridunit); + origin[Geom::X] = sp_units_get_pixels(origin[Geom::X], *(gridunit)); + } + if ( (value = repr->attribute("originy")) ) { + sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[Geom::Y], &gridunit); + origin[Geom::Y] = sp_units_get_pixels(origin[Geom::Y], *(gridunit)); + } + + if ( (value = repr->attribute("spacingy")) ) { + sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &lengthy, &gridunit); + lengthy = sp_units_get_pixels(lengthy, *(gridunit)); + if (lengthy < 0.0500) lengthy = 0.0500; + } + + if ( (value = repr->attribute("gridanglex")) ) { + angle_deg[X] = g_ascii_strtod(value, NULL); + if (angle_deg[X] < 0.) angle_deg[X] = 0.; + if (angle_deg[X] > 89.0) angle_deg[X] = 89.0; + angle_rad[X] = Geom::deg_to_rad(angle_deg[X]); + tan_angle[X] = tan(angle_rad[X]); + } + + if ( (value = repr->attribute("gridanglez")) ) { + angle_deg[Z] = g_ascii_strtod(value, NULL); + if (angle_deg[Z] < 0.) angle_deg[Z] = 0.; + if (angle_deg[Z] > 89.0) angle_deg[Z] = 89.0; + angle_rad[Z] = Geom::deg_to_rad(angle_deg[Z]); + tan_angle[Z] = tan(angle_rad[Z]); + } + + if ( (value = repr->attribute("color")) ) { + color = (color & 0xff) | sp_svg_read_color(value, color); + } + + if ( (value = repr->attribute("empcolor")) ) { + empcolor = (empcolor & 0xff) | sp_svg_read_color(value, empcolor); + } + + if ( (value = repr->attribute("opacity")) ) { + sp_nv_read_opacity(value, &color); + } + if ( (value = repr->attribute("empopacity")) ) { + sp_nv_read_opacity(value, &empcolor); + } + + if ( (value = repr->attribute("empspacing")) ) { + empspacing = atoi(value); + } + + if ( (value = repr->attribute("visible")) ) { + visible = (strcmp(value,"false") != 0 && strcmp(value, "0") != 0); + } + + if ( (value = repr->attribute("enabled")) ) { + g_assert(snapper != NULL); + snapper->setEnabled(strcmp(value,"false") != 0 && strcmp(value, "0") != 0); + } + + if ( (value = repr->attribute("snapvisiblegridlinesonly")) ) { + g_assert(snapper != NULL); + snapper->setSnapVisibleOnly(strcmp(value,"false") != 0 && strcmp(value, "0") != 0); + } + + for (GSList *l = canvasitems; l != NULL; l = l->next) { + sp_canvas_item_request_update ( SP_CANVAS_ITEM(l->data) ); + } + return; +} + +/** + * Called when XML node attribute changed; updates dialog widgets if change was not done by widgets themselves. + */ +void +CanvasNGonGrid::onReprAttrChanged(Inkscape::XML::Node */*repr*/, gchar const */*key*/, gchar const */*oldval*/, gchar const */*newval*/, bool /*is_interactive*/) +{ + readRepr(); + + if ( ! (_wr.isUpdating()) ) + updateWidgets(); +} + + + + +Gtk::Widget * +CanvasNGonGrid::newSpecificWidget() +{ + Gtk::Table * table = Gtk::manage( new Gtk::Table(1,1) ); + table->set_spacings(2); + +_wr.setUpdating (true); + + Inkscape::UI::Widget::RegisteredUnitMenu *_rumg = Gtk::manage( new Inkscape::UI::Widget::RegisteredUnitMenu( + _("Grid _units:"), "units", _wr, repr, doc) ); + Inkscape::UI::Widget::RegisteredScalarUnit *_rsu_ox = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalarUnit( + _("_Origin X:"), _("X coordinate of grid origin"), "originx", *_rumg, _wr, repr, doc) ); + Inkscape::UI::Widget::RegisteredScalarUnit *_rsu_oy = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalarUnit( + _("O_rigin Y:"), _("Y coordinate of grid origin"), "originy", *_rumg, _wr, repr, doc) ); + Inkscape::UI::Widget::RegisteredScalarUnit *_rsu_sy = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalarUnit( + _("Spacing _Y:"), _("Base length of z-axis"), "spacingy", *_rumg, _wr, repr, doc) ); + Inkscape::UI::Widget::RegisteredScalar *_rsu_ax = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalar( + _("Angle X:"), _("Angle of x-axis"), "gridanglex", _wr, repr, doc ) ); + Inkscape::UI::Widget::RegisteredScalar *_rsu_az = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalar( + _("Angle Z:"), _("Angle of z-axis"), "gridanglez", _wr, repr, doc ) ); + + Inkscape::UI::Widget::RegisteredColorPicker *_rcp_gcol = Gtk::manage( + new Inkscape::UI::Widget::RegisteredColorPicker( + _("Minor grid line _color:"), _("Minor grid line color"), _("Color of the minor grid lines"), + "color", "opacity", _wr, repr, doc)); + + Inkscape::UI::Widget::RegisteredColorPicker *_rcp_gmcol = Gtk::manage( + new Inkscape::UI::Widget::RegisteredColorPicker( + _("Ma_jor grid line color:"), _("Major grid line color"), + _("Color of the major (highlighted) grid lines"), + "empcolor", "empopacity", _wr, repr, doc)); + + Inkscape::UI::Widget::RegisteredSuffixedInteger *_rsi = Gtk::manage( new Inkscape::UI::Widget::RegisteredSuffixedInteger( + _("_Major grid line every:"), "", _("lines"), "empspacing", _wr, repr, doc ) ); + + _rsu_ox->setDigits(5); + _rsu_ox->setIncrements(0.1, 1.0); + + _rsu_oy->setDigits(5); + _rsu_oy->setIncrements(0.1, 1.0); + + _rsu_sy->setDigits(5); + _rsu_sy->setIncrements(0.1, 1.0); + +_wr.setUpdating (false); + + Gtk::Widget const *const widget_array[] = { + 0, _rumg, + 0, _rsu_ox, + 0, _rsu_oy, + 0, _rsu_sy, + 0, _rsu_ax, + 0, _rsu_az, + _rcp_gcol->_label, _rcp_gcol, + 0, 0, + _rcp_gmcol->_label, _rcp_gmcol, + 0, _rsi, + }; + + attach_all (*table, widget_array, sizeof(widget_array)); + + // set widget values + _rumg->setUnit (gridunit); + + gdouble val; + val = origin[Geom::X]; + val = sp_pixels_get_units (val, *(gridunit)); + _rsu_ox->setValue (val); + val = origin[Geom::Y]; + val = sp_pixels_get_units (val, *(gridunit)); + _rsu_oy->setValue (val); + val = lengthy; + double gridy = sp_pixels_get_units (val, *(gridunit)); + _rsu_sy->setValue (gridy); + + _rsu_ax->setValue(angle_deg[X]); + _rsu_az->setValue(angle_deg[Z]); + + _rcp_gcol->setRgba32 (color); + _rcp_gmcol->setRgba32 (empcolor); + _rsi->setValue (empspacing); + + return table; +} + + +/** + * Update dialog widgets from object's values. + */ +void +CanvasNGonGrid::updateWidgets() +{ +/* if (_wr.isUpdating()) return; + + _wr.setUpdating (true); + + _rcb_visible.setActive(visible); + if (snapper != NULL) { + _rcb_enabled.setActive(snapper->getEnabled()); + } + + _rumg.setUnit (gridunit); + + gdouble val; + val = origin[Geom::X]; + val = sp_pixels_get_units (val, *(gridunit)); + _rsu_ox.setValue (val); + val = origin[Geom::Y]; + val = sp_pixels_get_units (val, *(gridunit)); + _rsu_oy.setValue (val); + val = lengthy; + double gridy = sp_pixels_get_units (val, *(gridunit)); + _rsu_sy.setValue (gridy); + + _rsu_ax.setValue(angle_deg[X]); + _rsu_az.setValue(angle_deg[Z]); + + _rcp_gcol.setRgba32 (color); + _rcp_gmcol.setRgba32 (empcolor); + _rsi.setValue (empspacing); + + _wr.setUpdating (false); + + return; + */ +} + + + +void +CanvasNGonGrid::Update (Geom::Affine const &affine, unsigned int /*flags*/) +{ + ow = origin * affine; + sw = Geom::Point(fabs(affine[0]),fabs(affine[3])); + sw *= lengthy; + + scaled = false; + + for(int dim = 0; dim < 2; dim++) { + gint scaling_factor = empspacing; + + if (scaling_factor <= 1) + scaling_factor = 5; + + int watchdog = 0; + while ( (sw[dim] < 8.0) & (watchdog < 100) ) { + scaled = true; + sw[dim] *= scaling_factor; + // First pass, go up to the major line spacing, then + // keep increasing by two. + scaling_factor = 2; + watchdog++; + } + + } + + spacing_ylines = sw[Geom::X] /(tan_angle[X] + tan_angle[Z]); + lyw = sw[Geom::Y]; + lxw_x = Geom::are_near(tan_angle[X],0.) ? Geom::infinity() : sw[Geom::X] / tan_angle[X]; + lxw_z = Geom::are_near(tan_angle[Z],0.) ? Geom::infinity() : sw[Geom::X] / tan_angle[Z]; + + if (empspacing == 0) { + scaled = true; + } +} + +void +CanvasNGonGrid::Render (SPCanvasBuf *buf) +{ + //set correct coloring, depending preference (when zoomed out, always major coloring or minor coloring) + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + guint32 _empcolor; + bool preference = prefs->getBool("/options/grids/no_emphasize_when_zoomedout", false); + if( scaled && preference ) { + _empcolor = color; + } else { + _empcolor = empcolor; + } + + cairo_save(buf->ct); + cairo_translate(buf->ct, -buf->rect.left(), -buf->rect.top()); + cairo_set_line_width(buf->ct, 1.0); + cairo_set_line_cap(buf->ct, CAIRO_LINE_CAP_SQUARE); + + // gc = gridcoordinates (the coordinates calculated from the grids origin 'grid->ow'. + // sc = screencoordinates ( for example "buf->rect.left()" is in screencoordinates ) + // bc = buffer patch coordinates (x=0 on left side of page, y=0 on bottom of page) + + // tl = topleft ; br = bottomright + Geom::Point buf_tl_gc; + Geom::Point buf_br_gc; + buf_tl_gc[Geom::X] = buf->rect.left() - ow[Geom::X]; + buf_tl_gc[Geom::Y] = buf->rect.top() - ow[Geom::Y]; + buf_br_gc[Geom::X] = buf->rect.right() - ow[Geom::X]; + buf_br_gc[Geom::Y] = buf->rect.bottom() - ow[Geom::Y]; + + // render the three separate line groups representing the main-axes + + // x-axis always goes from topleft to bottomright. (0,0) - (1,1) + gdouble const xintercept_y_bc = (buf_tl_gc[Geom::X] * tan_angle[X]) - buf_tl_gc[Geom::Y] ; + gdouble const xstart_y_sc = ( xintercept_y_bc - floor(xintercept_y_bc/lyw)*lyw ) + buf->rect.top(); + gint const xlinestart = round( (xstart_y_sc - buf_tl_gc[Geom::X]*tan_angle[X] - ow[Geom::Y]) / lyw ); + gint xlinenum = xlinestart; + // lines starting on left side. + for (gdouble y = xstart_y_sc; y < buf->rect.bottom(); y += lyw, xlinenum++) { + gint const x0 = buf->rect.left(); + gint const y0 = round(y); + gint x1 = x0 + round( (buf->rect.bottom() - y) / tan_angle[X] ); + gint y1 = buf->rect.bottom(); + if ( Geom::are_near(tan_angle[X],0.) ) { + x1 = buf->rect.right(); + y1 = y0; + } + + if (!scaled && (xlinenum % empspacing) != 0) { + sp_cngongrid_drawline (buf, x0, y0, x1, y1, color); + } else { + sp_cngongrid_drawline (buf, x0, y0, x1, y1, _empcolor); + } + } + // lines starting from top side + if (!Geom::are_near(tan_angle[X],0.)) + { + gdouble const xstart_x_sc = buf->rect.left() + (lxw_x - (xstart_y_sc - buf->rect.top()) / tan_angle[X]) ; + xlinenum = xlinestart-1; + for (gdouble x = xstart_x_sc; x < buf->rect.right(); x += lxw_x, xlinenum--) { + gint const y0 = buf->rect.top(); + gint const y1 = buf->rect.bottom(); + gint const x0 = round(x); + gint const x1 = x0 + round( (y1 - y0) / tan_angle[X] ); + + if (!scaled && (xlinenum % empspacing) != 0) { + sp_cngongrid_drawline (buf, x0, y0, x1, y1, color); + } else { + sp_cngongrid_drawline (buf, x0, y0, x1, y1, _empcolor); + } + } + } + + // y-axis lines (vertical) + gdouble const ystart_x_sc = floor (buf_tl_gc[Geom::X] / spacing_ylines) * spacing_ylines + ow[Geom::X]; + gint const ylinestart = round((ystart_x_sc - ow[Geom::X]) / spacing_ylines); + gint ylinenum = ylinestart; + for (gdouble x = ystart_x_sc; x < buf->rect.right(); x += spacing_ylines, ylinenum++) { + gint const x0 = round(x); + + if (!scaled && (ylinenum % empspacing) != 0) { + sp_grid_vline (buf, x0, buf->rect.top(), buf->rect.bottom() - 1, color); + } else { + sp_grid_vline (buf, x0, buf->rect.top(), buf->rect.bottom() - 1, _empcolor); + } + } + + // z-axis always goes from bottomleft to topright. (0,1) - (1,0) + gdouble const zintercept_y_bc = (buf_tl_gc[Geom::X] * -tan_angle[Z]) - buf_tl_gc[Geom::Y] ; + gdouble const zstart_y_sc = ( zintercept_y_bc - floor(zintercept_y_bc/lyw)*lyw ) + buf->rect.top(); + gint const zlinestart = round( (zstart_y_sc + buf_tl_gc[Geom::X]*tan_angle[Z] - ow[Geom::Y]) / lyw ); + gint zlinenum = zlinestart; + // lines starting from left side + gdouble next_y = zstart_y_sc; + for (gdouble y = zstart_y_sc; y < buf->rect.bottom(); y += lyw, zlinenum++, next_y = y) { + gint const x0 = buf->rect.left(); + gint const y0 = round(y); + gint x1 = x0 + round( (y - buf->rect.top() ) / tan_angle[Z] ); + gint y1 = buf->rect.top(); + if ( Geom::are_near(tan_angle[Z],0.) ) { + x1 = buf->rect.right(); + y1 = y0; + } + + if (!scaled && (zlinenum % empspacing) != 0) { + sp_cngongrid_drawline (buf, x0, y0, x1, y1, color); + } else { + sp_cngongrid_drawline (buf, x0, y0, x1, y1, _empcolor); + } + } + // draw lines from bottom-up + if (!Geom::are_near(tan_angle[Z],0.)) + { + gdouble const zstart_x_sc = buf->rect.left() + (next_y - buf->rect.bottom()) / tan_angle[Z] ; + for (gdouble x = zstart_x_sc; x < buf->rect.right(); x += lxw_z, zlinenum++) { + gint const y0 = buf->rect.bottom(); + gint const y1 = buf->rect.top(); + gint const x0 = round(x); + gint const x1 = x0 + round(buf->rect.height() / tan_angle[Z] ); + + if (!scaled && (zlinenum % empspacing) != 0) { + sp_cngongrid_drawline (buf, x0, y0, x1, y1, color); + } else { + sp_cngongrid_drawline (buf, x0, y0, x1, y1, _empcolor); + } + } + } + + cairo_restore(buf->ct); +} + +CanvasNGonGridSnapper::CanvasNGonGridSnapper(CanvasNGonGrid *grid, SnapManager *sm, Geom::Coord const d) : LineSnapper(sm, d) +{ + this->grid = grid; +} + +/** + * \return Snap tolerance (desktop coordinates); depends on current zoom so that it's always the same in screen pixels + */ +Geom::Coord CanvasNGonGridSnapper::getSnapperTolerance() const +{ + SPDesktop const *dt = _snapmanager->getDesktop(); + double const zoom = dt ? dt->current_zoom() : 1; + return _snapmanager->snapprefs.getGridTolerance() / zoom; +} + +bool CanvasNGonGridSnapper::getSnapperAlwaysSnap() const +{ + return _snapmanager->snapprefs.getGridTolerance() == 10000; //TODO: Replace this threshold of 10000 by a constant; see also tolerance-slider.cpp +} + +LineSnapper::LineList +CanvasNGonGridSnapper::_getSnapLines(Geom::Point const &p) const +{ + LineList s; + + if ( grid == NULL ) { + return s; + } + + double spacing_h; + double spacing_v; + + if (getSnapVisibleOnly()) { + // Only snapping to visible grid lines + spacing_h = grid->spacing_ylines; // this is the spacing of the visible grid lines measured in screen pixels + spacing_v = grid->lyw; // vertical + // convert screen pixels to px + // FIXME: after we switch to snapping dist in screen pixels, this will be unnecessary + SPDesktop const *dt = _snapmanager->getDesktop(); + if (dt) { + spacing_h /= dt->current_zoom(); + spacing_v /= dt->current_zoom(); + } + } else { + // Snapping to any grid line, whether it's visible or not + spacing_h = grid->lengthy /(grid->tan_angle[X] + grid->tan_angle[Z]); + spacing_v = grid->lengthy; + + } + + // In an axonometric grid, any point will be surrounded by 6 grid lines: + // - 2 vertical grid lines, one left and one right from the point + // - 2 angled z grid lines, one above and one below the point + // - 2 angled x grid lines, one above and one below the point + + // Calculate the x coordinate of the vertical grid lines + Geom::Coord x_max = Inkscape::Util::round_to_upper_multiple_plus(p[Geom::X], spacing_h, grid->origin[Geom::X]); + Geom::Coord x_min = Inkscape::Util::round_to_lower_multiple_plus(p[Geom::X], spacing_h, grid->origin[Geom::X]); + + // Calculate the y coordinate of the intersection of the angled grid lines with the y-axis + double y_proj_along_z = p[Geom::Y] - grid->tan_angle[Z]*(p[Geom::X] - grid->origin[Geom::X]); + double y_proj_along_x = p[Geom::Y] + grid->tan_angle[X]*(p[Geom::X] - grid->origin[Geom::X]); + double y_proj_along_z_max = Inkscape::Util::round_to_upper_multiple_plus(y_proj_along_z, spacing_v, grid->origin[Geom::Y]); + double y_proj_along_z_min = Inkscape::Util::round_to_lower_multiple_plus(y_proj_along_z, spacing_v, grid->origin[Geom::Y]); + double y_proj_along_x_max = Inkscape::Util::round_to_upper_multiple_plus(y_proj_along_x, spacing_v, grid->origin[Geom::Y]); + double y_proj_along_x_min = Inkscape::Util::round_to_lower_multiple_plus(y_proj_along_x, spacing_v, grid->origin[Geom::Y]); + + // Calculate the versor for the angled grid lines + Geom::Point vers_x = Geom::Point(1, -grid->tan_angle[X]); + Geom::Point vers_z = Geom::Point(1, grid->tan_angle[Z]); + + // Calculate the normal for the angled grid lines + Geom::Point norm_x = Geom::rot90(vers_x); + Geom::Point norm_z = Geom::rot90(vers_z); + + // The four angled grid lines form a parallelogram, enclosing the point + // One of the two vertical grid lines divides this parallelogram in two triangles + // We will now try to find out in which half (i.e. triangle) our point is, and return + // only the three grid lines defining that triangle + + // The vertical grid line is at the intersection of two angled grid lines. + // Now go find that intersection! + Geom::Point p_x(0, y_proj_along_x_max); + Geom::Line line_x(p_x, p_x + vers_x); + Geom::Point p_z(0, y_proj_along_z_max); + Geom::Line line_z(p_z, p_z + vers_z); + + Geom::OptCrossing inters = Geom::OptCrossing(); // empty by default + try + { + inters = Geom::intersection(line_x, line_z); + } + catch (Geom::InfiniteSolutions &e) + { + // We're probably dealing with parallel lines; this is useless! + return s; + } + + // Determine which half of the parallelogram to use + bool use_left_half = true; + bool use_right_half = true; + + if (inters) { + Geom::Point inters_pt = line_x.pointAt((*inters).ta); + use_left_half = (p[Geom::X] - grid->origin[Geom::X]) < inters_pt[Geom::X]; + use_right_half = !use_left_half; + } + + // Return the three grid lines which define the triangle that encloses our point + // If we didn't find an intersection above, all 6 grid lines will be returned + if (use_left_half) { + s.push_back(std::make_pair(norm_z, Geom::Point(grid->origin[Geom::X], y_proj_along_z_max))); + s.push_back(std::make_pair(norm_x, Geom::Point(grid->origin[Geom::X], y_proj_along_x_min))); + s.push_back(std::make_pair(Geom::Point(1, 0), Geom::Point(x_max, 0))); + } + + if (use_right_half) { + s.push_back(std::make_pair(norm_z, Geom::Point(grid->origin[Geom::X], y_proj_along_z_min))); + s.push_back(std::make_pair(norm_x, Geom::Point(grid->origin[Geom::X], y_proj_along_x_max))); + s.push_back(std::make_pair(Geom::Point(1, 0), Geom::Point(x_min, 0))); + } + + return s; +} + +void CanvasNGonGridSnapper::_addSnappedLine(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const +{ + SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, source, source_num, Inkscape::SNAPTARGET_GRID, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line); + isr.grid_lines.push_back(dummy); +} + +void CanvasNGonGridSnapper::_addSnappedPoint(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const +{ + SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true); + isr.points.push_back(dummy); +} + +void CanvasNGonGridSnapper::_addSnappedLinePerpendicularly(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const +{ + SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID_PERPENDICULAR, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true); + isr.points.push_back(dummy); +} + +bool CanvasNGonGridSnapper::ThisSnapperMightSnap() const +{ + return _snap_enabled && _snapmanager->snapprefs.isTargetSnappable(Inkscape::SNAPTARGET_GRID); +} + + +}; // namespace Inkscape + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/canvas-ngongrid.h b/src/display/canvas-ngongrid.h new file mode 100644 index 0000000..ca4bec9 --- /dev/null +++ b/src/display/canvas-ngongrid.h @@ -0,0 +1,90 @@ +#ifndef CANVAS_NGONGRID_H +#define CANVAS_NGONGRID_H + +/* + * Authors: + * Matthew Woehlke + * Johan Engelen + * + * Copyright (C) 2006-2012 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "line-snapper.h" +#include "canvas-grid.h" + +class SPCanvasBuf; +class SPDesktop; +struct SPNamedView; + +namespace Inkscape { +namespace XML { + class Node; +}; + +class CanvasNGonGrid : public CanvasGrid { +public: + CanvasNGonGrid(SPNamedView * nv, Inkscape::XML::Node * in_repr, SPDocument * in_doc); + virtual ~CanvasNGonGrid(); + + void Update (Geom::Affine const &affine, unsigned int flags); + void Render (SPCanvasBuf *buf); + + void readRepr(); + void onReprAttrChanged (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive); + + double lengthy; /**< The lengths of the primary y-axis */ + double angle_deg[3]; /**< Angle of each axis (note that angle[2] == 0) */ + double angle_rad[3]; /**< Angle of each axis (note that angle[2] == 0) */ + double tan_angle[3]; /**< tan(angle[.]) */ + + bool scaled; /**< Whether the grid is in scaled mode */ + +protected: + friend class CanvasNGonGridSnapper; + + Geom::Point ow; /**< Transformed origin by the affine for the zoom */ + double lyw; /**< Transformed length y by the affine for the zoom */ + double lxw_x; + double lxw_z; + double spacing_ylines; + + Geom::Point sw; /**< the scaling factors of the affine transform */ + + virtual Gtk::Widget * newSpecificWidget(); + +private: + CanvasNGonGrid(const CanvasNGonGrid&); + CanvasNGonGrid& operator=(const CanvasNGonGrid&); + + void updateWidgets(); +}; + + + +class CanvasNGonGridSnapper : public LineSnapper +{ +public: + CanvasNGonGridSnapper(CanvasNGonGrid *grid, SnapManager *sm, Geom::Coord const d); + bool ThisSnapperMightSnap() const; + + Geom::Coord getSnapperTolerance() const; //returns the tolerance of the snapper in screen pixels (i.e. independent of zoom) + bool getSnapperAlwaysSnap() const; //if true, then the snapper will always snap, regardless of its tolerance + +private: + LineList _getSnapLines(Geom::Point const &p) const; + void _addSnappedLine(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, const Geom::Point point_on_line) const; + void _addSnappedPoint(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const; + void _addSnappedLinePerpendicularly(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const; + + CanvasNGonGrid *grid; +}; + + +}; //namespace Inkscape + + + +#endif + + diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index ab1e8fb..572b486 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -1120,6 +1120,8 @@ void DocumentProperties::update_gridspage() case GRID_AXONOMETRIC: icon = "grid-axonometric"; break; + case GRID_POLYGONAL: + icon = "grid-polygonal"; default: break; } diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 8506f33..d6afd88 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -698,6 +698,7 @@ void InkscapePreferences::initPageUI() _page_grids.add_line( true, "", _grids_notebook, "", "", false); _grids_notebook.append_page(_grids_xy, CanvasGrid::getName( GRID_RECTANGULAR )); _grids_notebook.append_page(_grids_axonom, CanvasGrid::getName( GRID_AXONOMETRIC )); + _grids_notebook.append_page(_grids_ngon, CanvasGrid::getName( GRID_POLYGONAL )); _grids_xy_units.init("/options/grids/xy/units"); _grids_xy.add_line( false, _("Grid units:"), _grids_xy_units, "", "", false); _grids_xy_origin_x.init("/options/grids/xy/origin_x", -10000.0, 10000.0, 0.1, 1.0, 0.0, false, false); @@ -745,6 +746,29 @@ void InkscapePreferences::initPageUI() _grids_axonom_empspacing.init("/options/grids/axonom/empspacing", 1.0, 1000.0, 1.0, 5.0, 5.0, true, false); _grids_axonom.add_line( false, _("Major grid line every:"), _grids_axonom_empspacing, "", "", false); + // CanvasNGonGrid properties: + _grids_ngon_units.init("/options/grids/ngon/units"); + _grids_ngon.add_line( false, _("Grid units:"), _grids_ngon_units, "", "", false); + _grids_ngon_origin_x.init("/options/grids/ngon/origin_x", -10000.0, 10000.0, 0.1, 1.0, 0.0, false, false); + _grids_ngon_origin_y.init("/options/grids/ngon/origin_y", -10000.0, 10000.0, 0.1, 1.0, 0.0, false, false); + _grids_ngon_origin_x.set_digits(5); + _grids_ngon_origin_y.set_digits(5); + _grids_ngon.add_line( false, _("Origin X:"), _grids_ngon_origin_x, "", _("X coordinate of grid origin"), false); + _grids_ngon.add_line( false, _("Origin Y:"), _grids_ngon_origin_y, "", _("Y coordinate of grid origin"), false); + _grids_ngon_spacing_y.init("/options/grids/ngon/spacing_y", -10000.0, 10000.0, 0.1, 1.0, 1.0, false, false); + _grids_ngon_spacing_y.set_digits(5); + _grids_ngon.add_line( false, _("Spacing Y:"), _grids_ngon_spacing_y, "", _("Base length of z-axis"), false); + _grids_ngon_angle_x.init("/options/grids/ngon/angle_x", -360.0, 360.0, 1.0, 10.0, 30.0, false, false); + _grids_ngon_angle_z.init("/options/grids/ngon/angle_z", -360.0, 360.0, 1.0, 10.0, 30.0, false, false); + _grids_ngon.add_line( false, _("Angle X:"), _grids_ngon_angle_x, "", _("Angle of x-axis"), false); + _grids_ngon.add_line( false, _("Angle Z:"), _grids_ngon_angle_z, "", _("Angle of z-axis"), false); + _grids_ngon_color.init(_("Minor grid line color:"), "/options/grids/ngon/color", 0x0000ff20); + _grids_ngon.add_line( false, _("Minor grid line color:"), _grids_ngon_color, "", _("Color used for normal grid lines"), false); + _grids_ngon_empcolor.init(_("Major grid line color:"), "/options/grids/ngon/empcolor", 0x0000ff40); + _grids_ngon.add_line( false, _("Major grid line color:"), _grids_ngon_empcolor, "", _("Color used for major (highlighted) grid lines"), false); + _grids_ngon_empspacing.init("/options/grids/ngon/empspacing", 1.0, 1000.0, 1.0, 5.0, 5.0, true, false); + _grids_ngon.add_line( false, _("Major grid line every:"), _grids_ngon_empspacing, "", "", false); + this->AddPage(_page_grids, _("Grids"), iter_ui, PREFS_PAGE_UI_GRIDS); initKeyboardShortcuts(iter_ui); diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index 6900165..28aa51a 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -378,6 +378,7 @@ protected: UI::Widget::PrefRadioButton _grids_emphasize_on_zoom; UI::Widget::DialogPage _grids_xy; UI::Widget::DialogPage _grids_axonom; + UI::Widget::DialogPage _grids_ngon; // CanvasXYGrid properties: UI::Widget::PrefUnit _grids_xy_units; UI::Widget::PrefSpinButton _grids_xy_origin_x; @@ -398,6 +399,16 @@ protected: UI::Widget::PrefColorPicker _grids_axonom_color; UI::Widget::PrefColorPicker _grids_axonom_empcolor; UI::Widget::PrefSpinButton _grids_axonom_empspacing; + // CanvasNGonGrid properties: + UI::Widget::PrefUnit _grids_ngon_units; + UI::Widget::PrefSpinButton _grids_ngon_origin_x; + UI::Widget::PrefSpinButton _grids_ngon_origin_y; + UI::Widget::PrefSpinButton _grids_ngon_spacing_y; + UI::Widget::PrefSpinButton _grids_ngon_angle_x; + UI::Widget::PrefSpinButton _grids_ngon_angle_z; + UI::Widget::PrefColorPicker _grids_ngon_color; + UI::Widget::PrefColorPicker _grids_ngon_empcolor; + UI::Widget::PrefSpinButton _grids_ngon_empspacing; // SVG Output page: UI::Widget::PrefCheckButton _svgoutput_usenamedcolors; -- 1.7.11.7 From 8c19d92846c45d69cf097eae2e26f24edc280f36 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Sat, 8 Dec 2012 16:40:22 -0500 Subject: [PATCH 3/7] implement UI for polygonal grid Modify the UI for polygonal grid to have the appropriate parameters, rather than those copied from axonometric grid. Since the grid logic has not been changed yet, we are using the rotation angle as both angles for the axonometric logic (which gives a degenerate grid with the default value 0). --- src/display/canvas-ngongrid.cpp | 150 ++++++++++++++++++--------------- src/display/canvas-ngongrid.h | 10 ++- src/ui/dialog/inkscape-preferences.cpp | 16 ++-- src/ui/dialog/inkscape-preferences.h | 5 +- 4 files changed, 103 insertions(+), 78 deletions(-) diff --git a/src/display/canvas-ngongrid.cpp b/src/display/canvas-ngongrid.cpp index 8dbe273..00effba 100644 --- a/src/display/canvas-ngongrid.cpp +++ b/src/display/canvas-ngongrid.cpp @@ -121,18 +121,15 @@ CanvasNGonGrid::CanvasNGonGrid (SPNamedView * nv, Inkscape::XML::Node * in_repr, gridunit = &sp_unit_get_by_id(SP_UNIT_PX); origin[Geom::X] = sp_units_get_pixels( prefs->getDouble("/options/grids/ngon/origin_x", 0.0), *gridunit ); origin[Geom::Y] = sp_units_get_pixels( prefs->getDouble("/options/grids/ngon/origin_y", 0.0), *gridunit ); + sections = prefs->getInt("/options/grids/ngon/sect_n", 8); + lengthx = sp_units_get_pixels( prefs->getDouble("/options/grids/ngon/spacing_x", 1.0), *gridunit ); + lengthy = sp_units_get_pixels( prefs->getDouble("/options/grids/ngon/spacing_y", 1.0), *gridunit ); + angle_deg = prefs->getDouble("/options/grids/ngon/rotation", 0.0); color = prefs->getInt("/options/grids/ngon/color", 0x0000ff20); empcolor = prefs->getInt("/options/grids/ngon/empcolor", 0x0000ff40); empspacing = prefs->getInt("/options/grids/ngon/empspacing", 5); - lengthy = sp_units_get_pixels( prefs->getDouble("/options/grids/ngon/spacing_y", 1.0), *gridunit ); - angle_deg[X] = prefs->getDouble("/options/grids/ngon/angle_x", 30.0); - angle_deg[Z] = prefs->getDouble("/options/grids/ngon/angle_z", 30.0); - angle_deg[Y] = 0; - - angle_rad[X] = Geom::deg_to_rad(angle_deg[X]); - tan_angle[X] = tan(angle_rad[X]); - angle_rad[Z] = Geom::deg_to_rad(angle_deg[Z]); - tan_angle[Z] = tan(angle_rad[Z]); + angle_rad = Geom::deg_to_rad(angle_deg); + tan_angle = tan(angle_rad); snapper = new CanvasNGonGridSnapper(this, &namedview->snap_manager, 0); @@ -234,26 +231,30 @@ CanvasNGonGrid::readRepr() origin[Geom::Y] = sp_units_get_pixels(origin[Geom::Y], *(gridunit)); } + if ( (value = repr->attribute("sections")) ) { + sections = atoi(value); + if (sections < 3) sections = 3; + if (sections > 360) sections = 360; + } + + if ( (value = repr->attribute("spacingx")) ) { + sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &lengthx, &gridunit); + lengthx = sp_units_get_pixels(lengthx, *(gridunit)); + if (lengthx < 0.0500) lengthx = 0.0500; + } + if ( (value = repr->attribute("spacingy")) ) { sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &lengthy, &gridunit); lengthy = sp_units_get_pixels(lengthy, *(gridunit)); if (lengthy < 0.0500) lengthy = 0.0500; } - if ( (value = repr->attribute("gridanglex")) ) { - angle_deg[X] = g_ascii_strtod(value, NULL); - if (angle_deg[X] < 0.) angle_deg[X] = 0.; - if (angle_deg[X] > 89.0) angle_deg[X] = 89.0; - angle_rad[X] = Geom::deg_to_rad(angle_deg[X]); - tan_angle[X] = tan(angle_rad[X]); - } - - if ( (value = repr->attribute("gridanglez")) ) { - angle_deg[Z] = g_ascii_strtod(value, NULL); - if (angle_deg[Z] < 0.) angle_deg[Z] = 0.; - if (angle_deg[Z] > 89.0) angle_deg[Z] = 89.0; - angle_rad[Z] = Geom::deg_to_rad(angle_deg[Z]); - tan_angle[Z] = tan(angle_rad[Z]); + if ( (value = repr->attribute("rotation")) ) { + angle_deg = g_ascii_strtod(value, NULL); + if (angle_deg < 0.) angle_deg = 0.; + if (angle_deg > 89.0) angle_deg = 89.0; + angle_rad = Geom::deg_to_rad(angle_deg); + tan_angle = tan(angle_rad); } if ( (value = repr->attribute("color")) ) { @@ -324,12 +325,14 @@ _wr.setUpdating (true); _("_Origin X:"), _("X coordinate of grid origin"), "originx", *_rumg, _wr, repr, doc) ); Inkscape::UI::Widget::RegisteredScalarUnit *_rsu_oy = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalarUnit( _("O_rigin Y:"), _("Y coordinate of grid origin"), "originy", *_rumg, _wr, repr, doc) ); + Inkscape::UI::Widget::RegisteredScalar *_rsu_ns = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalar( + _("_Number of sections:"), "", "sections", _wr, repr, doc ) ); + Inkscape::UI::Widget::RegisteredScalarUnit *_rsu_sx = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalarUnit( + _("Spacing _X:"), _("Distance between concentric grid polygons"), "spacingx", *_rumg, _wr, repr, doc) ); Inkscape::UI::Widget::RegisteredScalarUnit *_rsu_sy = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalarUnit( - _("Spacing _Y:"), _("Base length of z-axis"), "spacingy", *_rumg, _wr, repr, doc) ); - Inkscape::UI::Widget::RegisteredScalar *_rsu_ax = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalar( - _("Angle X:"), _("Angle of x-axis"), "gridanglex", _wr, repr, doc ) ); - Inkscape::UI::Widget::RegisteredScalar *_rsu_az = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalar( - _("Angle Z:"), _("Angle of z-axis"), "gridanglez", _wr, repr, doc ) ); + _("Spacing _Y:"), _("Distance between semi-radial grid lines"), "spacingy", *_rumg, _wr, repr, doc) ); + Inkscape::UI::Widget::RegisteredScalar *_rsu_ar = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalar( + _("Ro_tation:"), _("Angle of axis rotation"), "rotation", _wr, repr, doc ) ); Inkscape::UI::Widget::RegisteredColorPicker *_rcp_gcol = Gtk::manage( new Inkscape::UI::Widget::RegisteredColorPicker( @@ -351,22 +354,32 @@ _wr.setUpdating (true); _rsu_oy->setDigits(5); _rsu_oy->setIncrements(0.1, 1.0); + _rsu_ns->setDigits(0); + _rsu_ns->setIncrements(1, 3); + + _rsu_sx->setDigits(5); + _rsu_sx->setIncrements(0.1, 1.0); + _rsu_sy->setDigits(5); _rsu_sy->setIncrements(0.1, 1.0); + _rsu_ar->setDigits(5); + _rsu_ar->setIncrements(0.5, 5.0); + _wr.setUpdating (false); Gtk::Widget const *const widget_array[] = { - 0, _rumg, + 0, _rumg, 0, _rsu_ox, 0, _rsu_oy, + 0, _rsu_ns, + 0, _rsu_sx, 0, _rsu_sy, - 0, _rsu_ax, - 0, _rsu_az, - _rcp_gcol->_label, _rcp_gcol, + 0, _rsu_ar, + _rcp_gcol->_label, _rcp_gcol, 0, 0, - _rcp_gmcol->_label, _rcp_gmcol, - 0, _rsi, + _rcp_gmcol->_label, _rcp_gmcol, + 0, _rsi, }; attach_all (*table, widget_array, sizeof(widget_array)); @@ -381,12 +394,15 @@ _wr.setUpdating (false); val = origin[Geom::Y]; val = sp_pixels_get_units (val, *(gridunit)); _rsu_oy->setValue (val); + _rsu_ns->setValue (sections); + val = lengthx; + double gridx = sp_pixels_get_units (val, *(gridunit)); + _rsu_sx->setValue (gridx); val = lengthy; double gridy = sp_pixels_get_units (val, *(gridunit)); _rsu_sy->setValue (gridy); - _rsu_ax->setValue(angle_deg[X]); - _rsu_az->setValue(angle_deg[Z]); + _rsu_ar->setValue(angle_deg); _rcp_gcol->setRgba32 (color); _rcp_gmcol->setRgba32 (empcolor); @@ -416,20 +432,22 @@ CanvasNGonGrid::updateWidgets() gdouble val; val = origin[Geom::X]; val = sp_pixels_get_units (val, *(gridunit)); - _rsu_ox.setValue (val); + _rsu_ox->setValue (val); val = origin[Geom::Y]; val = sp_pixels_get_units (val, *(gridunit)); - _rsu_oy.setValue (val); + _rsu_oy->setValue (val); + val = lengthx; + double gridx = sp_pixels_get_units (val, *(gridunit)); + _rsu_sx->setValue (gridx); val = lengthy; double gridy = sp_pixels_get_units (val, *(gridunit)); - _rsu_sy.setValue (gridy); + _rsu_sy->setValue (gridy); - _rsu_ax.setValue(angle_deg[X]); - _rsu_az.setValue(angle_deg[Z]); + _rsu_ar->setValue(angle_deg); - _rcp_gcol.setRgba32 (color); - _rcp_gmcol.setRgba32 (empcolor); - _rsi.setValue (empspacing); + _rcp_gcol->setRgba32 (color); + _rcp_gmcol->setRgba32 (empcolor); + _rsi->setValue (empspacing); _wr.setUpdating (false); @@ -466,10 +484,10 @@ CanvasNGonGrid::Update (Geom::Affine const &affine, unsigned int /*flags*/) } - spacing_ylines = sw[Geom::X] /(tan_angle[X] + tan_angle[Z]); + spacing_ylines = sw[Geom::X] /(tan_angle + tan_angle); lyw = sw[Geom::Y]; - lxw_x = Geom::are_near(tan_angle[X],0.) ? Geom::infinity() : sw[Geom::X] / tan_angle[X]; - lxw_z = Geom::are_near(tan_angle[Z],0.) ? Geom::infinity() : sw[Geom::X] / tan_angle[Z]; + lxw_x = Geom::are_near(tan_angle,0.) ? Geom::infinity() : sw[Geom::X] / tan_angle; + lxw_z = Geom::are_near(tan_angle,0.) ? Geom::infinity() : sw[Geom::X] / tan_angle; if (empspacing == 0) { scaled = true; @@ -509,17 +527,17 @@ CanvasNGonGrid::Render (SPCanvasBuf *buf) // render the three separate line groups representing the main-axes // x-axis always goes from topleft to bottomright. (0,0) - (1,1) - gdouble const xintercept_y_bc = (buf_tl_gc[Geom::X] * tan_angle[X]) - buf_tl_gc[Geom::Y] ; + gdouble const xintercept_y_bc = (buf_tl_gc[Geom::X] * tan_angle) - buf_tl_gc[Geom::Y] ; gdouble const xstart_y_sc = ( xintercept_y_bc - floor(xintercept_y_bc/lyw)*lyw ) + buf->rect.top(); - gint const xlinestart = round( (xstart_y_sc - buf_tl_gc[Geom::X]*tan_angle[X] - ow[Geom::Y]) / lyw ); + gint const xlinestart = round( (xstart_y_sc - buf_tl_gc[Geom::X]*tan_angle - ow[Geom::Y]) / lyw ); gint xlinenum = xlinestart; // lines starting on left side. for (gdouble y = xstart_y_sc; y < buf->rect.bottom(); y += lyw, xlinenum++) { gint const x0 = buf->rect.left(); gint const y0 = round(y); - gint x1 = x0 + round( (buf->rect.bottom() - y) / tan_angle[X] ); + gint x1 = x0 + round( (buf->rect.bottom() - y) / tan_angle ); gint y1 = buf->rect.bottom(); - if ( Geom::are_near(tan_angle[X],0.) ) { + if ( Geom::are_near(tan_angle,0.) ) { x1 = buf->rect.right(); y1 = y0; } @@ -531,15 +549,15 @@ CanvasNGonGrid::Render (SPCanvasBuf *buf) } } // lines starting from top side - if (!Geom::are_near(tan_angle[X],0.)) + if (!Geom::are_near(tan_angle,0.)) { - gdouble const xstart_x_sc = buf->rect.left() + (lxw_x - (xstart_y_sc - buf->rect.top()) / tan_angle[X]) ; + gdouble const xstart_x_sc = buf->rect.left() + (lxw_x - (xstart_y_sc - buf->rect.top()) / tan_angle) ; xlinenum = xlinestart-1; for (gdouble x = xstart_x_sc; x < buf->rect.right(); x += lxw_x, xlinenum--) { gint const y0 = buf->rect.top(); gint const y1 = buf->rect.bottom(); gint const x0 = round(x); - gint const x1 = x0 + round( (y1 - y0) / tan_angle[X] ); + gint const x1 = x0 + round( (y1 - y0) / tan_angle ); if (!scaled && (xlinenum % empspacing) != 0) { sp_cngongrid_drawline (buf, x0, y0, x1, y1, color); @@ -564,18 +582,18 @@ CanvasNGonGrid::Render (SPCanvasBuf *buf) } // z-axis always goes from bottomleft to topright. (0,1) - (1,0) - gdouble const zintercept_y_bc = (buf_tl_gc[Geom::X] * -tan_angle[Z]) - buf_tl_gc[Geom::Y] ; + gdouble const zintercept_y_bc = (buf_tl_gc[Geom::X] * -tan_angle) - buf_tl_gc[Geom::Y] ; gdouble const zstart_y_sc = ( zintercept_y_bc - floor(zintercept_y_bc/lyw)*lyw ) + buf->rect.top(); - gint const zlinestart = round( (zstart_y_sc + buf_tl_gc[Geom::X]*tan_angle[Z] - ow[Geom::Y]) / lyw ); + gint const zlinestart = round( (zstart_y_sc + buf_tl_gc[Geom::X]*tan_angle - ow[Geom::Y]) / lyw ); gint zlinenum = zlinestart; // lines starting from left side gdouble next_y = zstart_y_sc; for (gdouble y = zstart_y_sc; y < buf->rect.bottom(); y += lyw, zlinenum++, next_y = y) { gint const x0 = buf->rect.left(); gint const y0 = round(y); - gint x1 = x0 + round( (y - buf->rect.top() ) / tan_angle[Z] ); + gint x1 = x0 + round( (y - buf->rect.top() ) / tan_angle ); gint y1 = buf->rect.top(); - if ( Geom::are_near(tan_angle[Z],0.) ) { + if ( Geom::are_near(tan_angle,0.) ) { x1 = buf->rect.right(); y1 = y0; } @@ -587,14 +605,14 @@ CanvasNGonGrid::Render (SPCanvasBuf *buf) } } // draw lines from bottom-up - if (!Geom::are_near(tan_angle[Z],0.)) + if (!Geom::are_near(tan_angle,0.)) { - gdouble const zstart_x_sc = buf->rect.left() + (next_y - buf->rect.bottom()) / tan_angle[Z] ; + gdouble const zstart_x_sc = buf->rect.left() + (next_y - buf->rect.bottom()) / tan_angle ; for (gdouble x = zstart_x_sc; x < buf->rect.right(); x += lxw_z, zlinenum++) { gint const y0 = buf->rect.bottom(); gint const y1 = buf->rect.top(); gint const x0 = round(x); - gint const x1 = x0 + round(buf->rect.height() / tan_angle[Z] ); + gint const x1 = x0 + round(buf->rect.height() / tan_angle ); if (!scaled && (zlinenum % empspacing) != 0) { sp_cngongrid_drawline (buf, x0, y0, x1, y1, color); @@ -652,7 +670,7 @@ CanvasNGonGridSnapper::_getSnapLines(Geom::Point const &p) const } } else { // Snapping to any grid line, whether it's visible or not - spacing_h = grid->lengthy /(grid->tan_angle[X] + grid->tan_angle[Z]); + spacing_h = grid->lengthy /(grid->tan_angle + grid->tan_angle); spacing_v = grid->lengthy; } @@ -667,16 +685,16 @@ CanvasNGonGridSnapper::_getSnapLines(Geom::Point const &p) const Geom::Coord x_min = Inkscape::Util::round_to_lower_multiple_plus(p[Geom::X], spacing_h, grid->origin[Geom::X]); // Calculate the y coordinate of the intersection of the angled grid lines with the y-axis - double y_proj_along_z = p[Geom::Y] - grid->tan_angle[Z]*(p[Geom::X] - grid->origin[Geom::X]); - double y_proj_along_x = p[Geom::Y] + grid->tan_angle[X]*(p[Geom::X] - grid->origin[Geom::X]); + double y_proj_along_z = p[Geom::Y] - grid->tan_angle*(p[Geom::X] - grid->origin[Geom::X]); + double y_proj_along_x = p[Geom::Y] + grid->tan_angle*(p[Geom::X] - grid->origin[Geom::X]); double y_proj_along_z_max = Inkscape::Util::round_to_upper_multiple_plus(y_proj_along_z, spacing_v, grid->origin[Geom::Y]); double y_proj_along_z_min = Inkscape::Util::round_to_lower_multiple_plus(y_proj_along_z, spacing_v, grid->origin[Geom::Y]); double y_proj_along_x_max = Inkscape::Util::round_to_upper_multiple_plus(y_proj_along_x, spacing_v, grid->origin[Geom::Y]); double y_proj_along_x_min = Inkscape::Util::round_to_lower_multiple_plus(y_proj_along_x, spacing_v, grid->origin[Geom::Y]); // Calculate the versor for the angled grid lines - Geom::Point vers_x = Geom::Point(1, -grid->tan_angle[X]); - Geom::Point vers_z = Geom::Point(1, grid->tan_angle[Z]); + Geom::Point vers_x = Geom::Point(1, -grid->tan_angle); + Geom::Point vers_z = Geom::Point(1, grid->tan_angle); // Calculate the normal for the angled grid lines Geom::Point norm_x = Geom::rot90(vers_x); diff --git a/src/display/canvas-ngongrid.h b/src/display/canvas-ngongrid.h index ca4bec9..2b7d29c 100644 --- a/src/display/canvas-ngongrid.h +++ b/src/display/canvas-ngongrid.h @@ -33,10 +33,12 @@ public: void readRepr(); void onReprAttrChanged (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive); - double lengthy; /**< The lengths of the primary y-axis */ - double angle_deg[3]; /**< Angle of each axis (note that angle[2] == 0) */ - double angle_rad[3]; /**< Angle of each axis (note that angle[2] == 0) */ - double tan_angle[3]; /**< tan(angle[.]) */ + int sections; /**< Number of grid sections */ + double lengthx; /**< Step size along concentric polygons */ + double lengthy; /**< Step size along semi-radius lines */ + double angle_deg; /**< Angle of rotation (degrees) */ + double angle_rad; /**< Angle of rotation (radians) */ + double tan_angle; /**< tan(angle) */ bool scaled; /**< Whether the grid is in scaled mode */ diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index d6afd88..1d07fff 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -755,13 +755,17 @@ void InkscapePreferences::initPageUI() _grids_ngon_origin_y.set_digits(5); _grids_ngon.add_line( false, _("Origin X:"), _grids_ngon_origin_x, "", _("X coordinate of grid origin"), false); _grids_ngon.add_line( false, _("Origin Y:"), _grids_ngon_origin_y, "", _("Y coordinate of grid origin"), false); - _grids_ngon_spacing_y.init("/options/grids/ngon/spacing_y", -10000.0, 10000.0, 0.1, 1.0, 1.0, false, false); + _grids_ngon_sect_n.init("/options/grids/ngon/sect_n", 3.0, 360.0, 1.0, 3.0, 8.0, true, false); + _grids_ngon.add_line( false, _("Number of sections:"), _grids_ngon_empspacing, "", "", false); + _grids_ngon_spacing_x.init("/options/grids/ngon/spacing_x", 0.0, 10000.0, 0.1, 1.0, 1.0, false, false); + _grids_ngon_spacing_y.init("/options/grids/ngon/spacing_y", 0.0, 10000.0, 0.1, 1.0, 1.0, false, false); + _grids_ngon_spacing_x.set_digits(5); _grids_ngon_spacing_y.set_digits(5); - _grids_ngon.add_line( false, _("Spacing Y:"), _grids_ngon_spacing_y, "", _("Base length of z-axis"), false); - _grids_ngon_angle_x.init("/options/grids/ngon/angle_x", -360.0, 360.0, 1.0, 10.0, 30.0, false, false); - _grids_ngon_angle_z.init("/options/grids/ngon/angle_z", -360.0, 360.0, 1.0, 10.0, 30.0, false, false); - _grids_ngon.add_line( false, _("Angle X:"), _grids_ngon_angle_x, "", _("Angle of x-axis"), false); - _grids_ngon.add_line( false, _("Angle Z:"), _grids_ngon_angle_z, "", _("Angle of z-axis"), false); + _grids_ngon.add_line( false, _("Spacing X:"), _grids_ngon_spacing_x, "", _("Distance between concentric grid polygons"), false); + _grids_ngon.add_line( false, _("Spacing Y:"), _grids_ngon_spacing_y, "", _("Distance between semi-radial grid lines"), false); + _grids_ngon_rotation.init("/options/grids/ngon/rotation", -360.0, 360.0, 1.0, 0.0, 0.0, false, false); + _grids_ngon_rotation.set_digits(5); + _grids_ngon.add_line( false, _("Rotation:"), _grids_ngon_rotation, "", _("Angle of axis rotation"), false); _grids_ngon_color.init(_("Minor grid line color:"), "/options/grids/ngon/color", 0x0000ff20); _grids_ngon.add_line( false, _("Minor grid line color:"), _grids_ngon_color, "", _("Color used for normal grid lines"), false); _grids_ngon_empcolor.init(_("Major grid line color:"), "/options/grids/ngon/empcolor", 0x0000ff40); diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index 28aa51a..ee4cecf 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -403,9 +403,10 @@ protected: UI::Widget::PrefUnit _grids_ngon_units; UI::Widget::PrefSpinButton _grids_ngon_origin_x; UI::Widget::PrefSpinButton _grids_ngon_origin_y; + UI::Widget::PrefSpinButton _grids_ngon_sect_n; + UI::Widget::PrefSpinButton _grids_ngon_spacing_x; UI::Widget::PrefSpinButton _grids_ngon_spacing_y; - UI::Widget::PrefSpinButton _grids_ngon_angle_x; - UI::Widget::PrefSpinButton _grids_ngon_angle_z; + UI::Widget::PrefSpinButton _grids_ngon_rotation; UI::Widget::PrefColorPicker _grids_ngon_color; UI::Widget::PrefColorPicker _grids_ngon_empcolor; UI::Widget::PrefSpinButton _grids_ngon_empspacing; -- 1.7.11.7 From 4b940661deda88ed1c85f395c2713a2f649ce010 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Sat, 8 Dec 2012 23:35:48 -0500 Subject: [PATCH 4/7] start implementing polygonal grid renderingg Implement basic rendering for the polygonal grid. For now, this uses an arbitrary max extent (rather than determining max extents per section with respect to the viewport), and doesn't attempt to clip grid lines to the viewport. Also, snapping is not yet implemented (the old code from the axonometric grid has been commented out for now). --- src/display/canvas-ngongrid.cpp | 219 +++++++++++++++++----------------------- src/display/canvas-ngongrid.h | 15 +-- 2 files changed, 103 insertions(+), 131 deletions(-) diff --git a/src/display/canvas-ngongrid.cpp b/src/display/canvas-ngongrid.cpp index 00effba..f2d475a 100644 --- a/src/display/canvas-ngongrid.cpp +++ b/src/display/canvas-ngongrid.cpp @@ -42,33 +42,29 @@ #include "helper/units.h" -enum Dim3 { X=0, Y, Z }; - /** * This function calls Cairo to render a line on a particular canvas buffer. * Coordinates are interpreted as SCREENcoordinates */ static void -sp_cngongrid_drawline (SPCanvasBuf *buf, gint x0, gint y0, gint x1, gint y1, guint32 rgba) +sp_cngongrid_drawline (SPCanvasBuf *buf, gdouble x0, gdouble y0, gdouble x1, gdouble y1, guint32 rgba) { + // Prevent aliasing of horizontal/vertical lines + if (Geom::are_near(x0, x1)) { + x0 = round(x0); + x1 = round(x1); + } + if (Geom::are_near(y0, y1)) { + y0 = round(y0); + y1 = round(y1); + } + // TODO clip to viewport? cairo_move_to(buf->ct, 0.5 + x0, 0.5 + y0); cairo_line_to(buf->ct, 0.5 + x1, 0.5 + y1); ink_cairo_set_source_rgba32(buf->ct, rgba); cairo_stroke(buf->ct); } -static void -sp_grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba) -{ - if ((x < buf->rect.left()) || (x >= buf->rect.right())) - return; - - cairo_move_to(buf->ct, 0.5 + x, 0.5 + ys); - cairo_line_to(buf->ct, 0.5 + x, 0.5 + ye); - ink_cairo_set_source_rgba32(buf->ct, rgba); - cairo_stroke(buf->ct); -} - namespace Inkscape { @@ -128,8 +124,10 @@ CanvasNGonGrid::CanvasNGonGrid (SPNamedView * nv, Inkscape::XML::Node * in_repr, color = prefs->getInt("/options/grids/ngon/color", 0x0000ff20); empcolor = prefs->getInt("/options/grids/ngon/empcolor", 0x0000ff40); empspacing = prefs->getInt("/options/grids/ngon/empspacing", 5); - angle_rad = Geom::deg_to_rad(angle_deg); - tan_angle = tan(angle_rad); + + se_angle_deg = 180.0 / sections; + se_angle_rad = Geom::deg_to_rad(se_angle_deg); + se_tan = tan(se_angle_rad); snapper = new CanvasNGonGridSnapper(this, &namedview->snap_manager, 0); @@ -235,6 +233,9 @@ CanvasNGonGrid::readRepr() sections = atoi(value); if (sections < 3) sections = 3; if (sections > 360) sections = 360; + se_angle_deg = 180.0 / sections; + se_angle_rad = Geom::deg_to_rad(se_angle_deg); + se_tan = tan(se_angle_rad); } if ( (value = repr->attribute("spacingx")) ) { @@ -251,10 +252,8 @@ CanvasNGonGrid::readRepr() if ( (value = repr->attribute("rotation")) ) { angle_deg = g_ascii_strtod(value, NULL); - if (angle_deg < 0.) angle_deg = 0.; - if (angle_deg > 89.0) angle_deg = 89.0; - angle_rad = Geom::deg_to_rad(angle_deg); - tan_angle = tan(angle_rad); + double max_angle = 360.0 / sections; + if (fabs(angle_deg) > max_angle) angle_deg = fmod(angle_deg, max_angle); } if ( (value = repr->attribute("color")) ) { @@ -462,7 +461,8 @@ CanvasNGonGrid::Update (Geom::Affine const &affine, unsigned int /*flags*/) { ow = origin * affine; sw = Geom::Point(fabs(affine[0]),fabs(affine[3])); - sw *= lengthy; + sw[Geom::X] *= lengthx; + sw[Geom::Y] *= lengthy; scaled = false; @@ -484,10 +484,8 @@ CanvasNGonGrid::Update (Geom::Affine const &affine, unsigned int /*flags*/) } - spacing_ylines = sw[Geom::X] /(tan_angle + tan_angle); - lyw = sw[Geom::Y]; - lxw_x = Geom::are_near(tan_angle,0.) ? Geom::infinity() : sw[Geom::X] / tan_angle; - lxw_z = Geom::are_near(tan_angle,0.) ? Geom::infinity() : sw[Geom::X] / tan_angle; + lxw = sw[Geom::X]; + lyw = sw[Geom::Y]; if (empspacing == 0) { scaled = true; @@ -512,9 +510,19 @@ CanvasNGonGrid::Render (SPCanvasBuf *buf) cairo_set_line_width(buf->ct, 1.0); cairo_set_line_cap(buf->ct, CAIRO_LINE_CAP_SQUARE); - // gc = gridcoordinates (the coordinates calculated from the grids origin 'grid->ow'. - // sc = screencoordinates ( for example "buf->rect.left()" is in screencoordinates ) - // bc = buffer patch coordinates (x=0 on left side of page, y=0 on bottom of page) + double angle_step = 360.0 / sections; + for (int s = 0; s < sections; ++s) { + renderSection(buf, (s * angle_step) - angle_deg, _empcolor); + } + + cairo_restore(buf->ct); +} + +void +CanvasNGonGrid::renderSection (SPCanvasBuf *buf, double section_angle_deg, guint32 _empcolor) +{ + // pc = preimagecoordinates (the coordinates of the section before rotation) + // gc = gridcoordinates (the coordinates calculated from the grids origin 'grid->ow') // tl = topleft ; br = bottomright Geom::Point buf_tl_gc; @@ -524,105 +532,66 @@ CanvasNGonGrid::Render (SPCanvasBuf *buf) buf_br_gc[Geom::X] = buf->rect.right() - ow[Geom::X]; buf_br_gc[Geom::Y] = buf->rect.bottom() - ow[Geom::Y]; - // render the three separate line groups representing the main-axes - - // x-axis always goes from topleft to bottomright. (0,0) - (1,1) - gdouble const xintercept_y_bc = (buf_tl_gc[Geom::X] * tan_angle) - buf_tl_gc[Geom::Y] ; - gdouble const xstart_y_sc = ( xintercept_y_bc - floor(xintercept_y_bc/lyw)*lyw ) + buf->rect.top(); - gint const xlinestart = round( (xstart_y_sc - buf_tl_gc[Geom::X]*tan_angle - ow[Geom::Y]) / lyw ); - gint xlinenum = xlinestart; - // lines starting on left side. - for (gdouble y = xstart_y_sc; y < buf->rect.bottom(); y += lyw, xlinenum++) { - gint const x0 = buf->rect.left(); - gint const y0 = round(y); - gint x1 = x0 + round( (buf->rect.bottom() - y) / tan_angle ); - gint y1 = buf->rect.bottom(); - if ( Geom::are_near(tan_angle,0.) ) { - x1 = buf->rect.right(); - y1 = y0; - } + double const section_angle_rad = Geom::deg_to_rad(section_angle_deg); + double const section_sin = sin(section_angle_rad); + double const section_cos = cos(section_angle_rad); - if (!scaled && (xlinenum % empspacing) != 0) { - sp_cngongrid_drawline (buf, x0, y0, x1, y1, color); - } else { - sp_cngongrid_drawline (buf, x0, y0, x1, y1, _empcolor); - } - } - // lines starting from top side - if (!Geom::are_near(tan_angle,0.)) - { - gdouble const xstart_x_sc = buf->rect.left() + (lxw_x - (xstart_y_sc - buf->rect.top()) / tan_angle) ; - xlinenum = xlinestart-1; - for (gdouble x = xstart_x_sc; x < buf->rect.right(); x += lxw_x, xlinenum--) { - gint const y0 = buf->rect.top(); - gint const y1 = buf->rect.bottom(); - gint const x0 = round(x); - gint const x1 = x0 + round( (y1 - y0) / tan_angle ); - - if (!scaled && (xlinenum % empspacing) != 0) { - sp_cngongrid_drawline (buf, x0, y0, x1, y1, color); - } else { - sp_cngongrid_drawline (buf, x0, y0, x1, y1, _empcolor); - } - } - } - - // y-axis lines (vertical) - gdouble const ystart_x_sc = floor (buf_tl_gc[Geom::X] / spacing_ylines) * spacing_ylines + ow[Geom::X]; - gint const ylinestart = round((ystart_x_sc - ow[Geom::X]) / spacing_ylines); - gint ylinenum = ylinestart; - for (gdouble x = ystart_x_sc; x < buf->rect.right(); x += spacing_ylines, ylinenum++) { - gint const x0 = round(x); + gdouble xmax = 25; // TODO compute from viewport intersection - DON'T FLOOR + if (xmax <= 0) return; // Section is entirely out of viewport - if (!scaled && (ylinenum % empspacing) != 0) { - sp_grid_vline (buf, x0, buf->rect.top(), buf->rect.bottom() - 1, color); - } else { - sp_grid_vline (buf, x0, buf->rect.top(), buf->rect.bottom() - 1, _empcolor); - } - } + gdouble ymin = ceil(-10); // TODO compute from viewport intersection + gdouble ymax = floor(10); // TODO compute from viewport intersection - // z-axis always goes from bottomleft to topright. (0,1) - (1,0) - gdouble const zintercept_y_bc = (buf_tl_gc[Geom::X] * -tan_angle) - buf_tl_gc[Geom::Y] ; - gdouble const zstart_y_sc = ( zintercept_y_bc - floor(zintercept_y_bc/lyw)*lyw ) + buf->rect.top(); - gint const zlinestart = round( (zstart_y_sc + buf_tl_gc[Geom::X]*tan_angle - ow[Geom::Y]) / lyw ); - gint zlinenum = zlinestart; - // lines starting from left side - gdouble next_y = zstart_y_sc; - for (gdouble y = zstart_y_sc; y < buf->rect.bottom(); y += lyw, zlinenum++, next_y = y) { - gint const x0 = buf->rect.left(); - gint const y0 = round(y); - gint x1 = x0 + round( (y - buf->rect.top() ) / tan_angle ); - gint y1 = buf->rect.top(); - if ( Geom::are_near(tan_angle,0.) ) { - x1 = buf->rect.right(); - y1 = y0; - } + gdouble const lxmax = xmax * lxw; + gdouble const xbound = (xmax + 0.5) * lyw; + gdouble const ybound = (ymax + 0.5) * lyw; - if (!scaled && (zlinenum % empspacing) != 0) { - sp_cngongrid_drawline (buf, x0, y0, x1, y1, color); - } else { - sp_cngongrid_drawline (buf, x0, y0, x1, y1, _empcolor); - } - } - // draw lines from bottom-up - if (!Geom::are_near(tan_angle,0.)) + // Render section edge line { - gdouble const zstart_x_sc = buf->rect.left() + (next_y - buf->rect.bottom()) / tan_angle ; - for (gdouble x = zstart_x_sc; x < buf->rect.right(); x += lxw_z, zlinenum++) { - gint const y0 = buf->rect.bottom(); - gint const y1 = buf->rect.top(); - gint const x0 = round(x); - gint const x1 = x0 + round(buf->rect.height() / tan_angle ); - - if (!scaled && (zlinenum % empspacing) != 0) { - sp_cngongrid_drawline (buf, x0, y0, x1, y1, color); - } else { - sp_cngongrid_drawline (buf, x0, y0, x1, y1, _empcolor); - } - } + gdouble const pc_x = lxmax; + gdouble const pc_y = lxmax * se_tan; + gdouble const gc_x1 = ( (pc_x * section_cos) - (pc_y * section_sin) ) + ow[Geom::X]; + gdouble const gc_y1 = ( (pc_x * section_sin) + (pc_y * section_cos) ) + ow[Geom::Y]; + sp_cngongrid_drawline (buf, ow[Geom::X], ow[Geom::Y], gc_x1, gc_y1, color); + } + + // Render semi-radius lines + gint xlinenum = ymin; + for (gdouble y = ymin * lyw; y <= ybound; y += lyw, xlinenum++) { + // Compute points in preimage coordinates + gdouble const pc_x0 = fabs(y) / se_tan; + gdouble const pc_x1 = lxmax; + if (pc_x1 <= pc_x0) continue; + // Compute points in grid coordinates (with rotation applied) + gdouble const ys = y * section_sin; + gdouble const yc = y * section_cos; + gdouble const gc_x0 = ( (pc_x0 * section_cos) - ys ) + ow[Geom::X]; + gdouble const gc_x1 = ( (pc_x1 * section_cos) - ys ) + ow[Geom::X]; + gdouble const gc_y0 = ( (pc_x0 * section_sin) + yc ) + ow[Geom::Y]; + gdouble const gc_y1 = ( (pc_x1 * section_sin) + yc ) + ow[Geom::Y]; + // Draw segment + guint32 const _color = ( (!scaled && (xlinenum % empspacing) != 0) ? color : _empcolor); + sp_cngongrid_drawline (buf, gc_x0, gc_y0, gc_x1, gc_y1, _color); + } + + // Render concentric lines + gint ylinenum = 1; + for (gdouble x = lxw; x < xbound; x += lxw, ylinenum++) { + // Compute points in preimage coordinates + gdouble const pc_y = x * se_tan; + // Compute points in grid coordinates (with rotation applied) + gdouble const xs = x * section_sin; + gdouble const xc = x * section_cos; + gdouble const ys = pc_y * section_sin; + gdouble const yc = pc_y * section_cos; + gdouble const gc_x0 = xc + ys + ow[Geom::X]; + gdouble const gc_x1 = xc - ys + ow[Geom::X]; + gdouble const gc_y0 = xs - yc + ow[Geom::Y]; + gdouble const gc_y1 = xs + yc + ow[Geom::Y]; + // Draw segment + guint32 const _color = ( (!scaled && (ylinenum % empspacing) != 0) ? color : _empcolor); + sp_cngongrid_drawline (buf, gc_x0, gc_y0, gc_x1, gc_y1, _color); } - - cairo_restore(buf->ct); } CanvasNGonGridSnapper::CanvasNGonGridSnapper(CanvasNGonGrid *grid, SnapManager *sm, Geom::Coord const d) : LineSnapper(sm, d) @@ -659,7 +628,7 @@ CanvasNGonGridSnapper::_getSnapLines(Geom::Point const &p) const if (getSnapVisibleOnly()) { // Only snapping to visible grid lines - spacing_h = grid->spacing_ylines; // this is the spacing of the visible grid lines measured in screen pixels + spacing_h = grid->lxw; // horizontal spacing_v = grid->lyw; // vertical // convert screen pixels to px // FIXME: after we switch to snapping dist in screen pixels, this will be unnecessary @@ -670,11 +639,12 @@ CanvasNGonGridSnapper::_getSnapLines(Geom::Point const &p) const } } else { // Snapping to any grid line, whether it's visible or not - spacing_h = grid->lengthy /(grid->tan_angle + grid->tan_angle); + spacing_h = grid->lengthx; spacing_v = grid->lengthy; } + /* // In an axonometric grid, any point will be surrounded by 6 grid lines: // - 2 vertical grid lines, one left and one right from the point // - 2 angled z grid lines, one above and one below the point @@ -746,6 +716,7 @@ CanvasNGonGridSnapper::_getSnapLines(Geom::Point const &p) const s.push_back(std::make_pair(norm_x, Geom::Point(grid->origin[Geom::X], y_proj_along_x_max))); s.push_back(std::make_pair(Geom::Point(1, 0), Geom::Point(x_min, 0))); } + */ return s; } diff --git a/src/display/canvas-ngongrid.h b/src/display/canvas-ngongrid.h index 2b7d29c..3c81619 100644 --- a/src/display/canvas-ngongrid.h +++ b/src/display/canvas-ngongrid.h @@ -36,9 +36,10 @@ public: int sections; /**< Number of grid sections */ double lengthx; /**< Step size along concentric polygons */ double lengthy; /**< Step size along semi-radius lines */ - double angle_deg; /**< Angle of rotation (degrees) */ - double angle_rad; /**< Angle of rotation (radians) */ - double tan_angle; /**< tan(angle) */ + double angle_deg; /**< Angle of rotation */ + double se_angle_deg; /**< Half of section arc (degrees) */ + double se_angle_rad; /**< Half of section arc (radians) */ + double se_tan; /**< tan(se_angle) */ bool scaled; /**< Whether the grid is in scaled mode */ @@ -46,10 +47,8 @@ protected: friend class CanvasNGonGridSnapper; Geom::Point ow; /**< Transformed origin by the affine for the zoom */ - double lyw; /**< Transformed length y by the affine for the zoom */ - double lxw_x; - double lxw_z; - double spacing_ylines; + double lxw; /**< Transformed length x by the affine for the zoom */ + double lyw; /**< Transformed length y by the affine for the zoom */ Geom::Point sw; /**< the scaling factors of the affine transform */ @@ -60,6 +59,8 @@ private: CanvasNGonGrid& operator=(const CanvasNGonGrid&); void updateWidgets(); + + void renderSection (SPCanvasBuf *buf, double section_angle_deg, guint32 _empcolor); }; -- 1.7.11.7 From f4ea893b9e7ca5561e5b105e1fa7ce9a0db3075d Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Sun, 9 Dec 2012 19:48:39 -0500 Subject: [PATCH 5/7] fix polygonal grid extents Hook up polygonal grid extents calculation so that we always draw enough grid to fill the viewport, rather than truncating the grid at an arbitrary point. --- src/display/canvas-ngongrid.cpp | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/display/canvas-ngongrid.cpp b/src/display/canvas-ngongrid.cpp index f2d475a..de0a26a 100644 --- a/src/display/canvas-ngongrid.cpp +++ b/src/display/canvas-ngongrid.cpp @@ -65,6 +65,27 @@ sp_cngongrid_drawline (SPCanvasBuf *buf, gdouble x0, gdouble y0, gdouble x1, gdo cairo_stroke(buf->ct); } +static gdouble +distance(gdouble x, gdouble y, double dx, double dy) +{ + return (dy * x) - (dx * y); +} + +static gdouble +find_bound(Geom::Rect const &rect, double dx, double dy, gdouble const & (*bound_func)(gdouble const &, gdouble const &)) +{ + // Note: Y+ is DOWN, not up! + if ( (dx > 0.0) == (dy > 0.0) ) { // Interesting points are bottom-left, top-right + gdouble const a = distance(rect.left(), rect.bottom(), dx, dy); + gdouble const b = distance(rect.right(), rect.top(), dx, dy); + return bound_func(a, b); + } else { // Interesting points are top-left, bottom-right + gdouble const a = distance(rect.left(), rect.top(), dx, dy); + gdouble const b = distance(rect.right(), rect.bottom(), dx, dy); + return bound_func(a, b); + } +} + namespace Inkscape { @@ -524,27 +545,22 @@ CanvasNGonGrid::renderSection (SPCanvasBuf *buf, double section_angle_deg, guint // pc = preimagecoordinates (the coordinates of the section before rotation) // gc = gridcoordinates (the coordinates calculated from the grids origin 'grid->ow') - // tl = topleft ; br = bottomright - Geom::Point buf_tl_gc; - Geom::Point buf_br_gc; - buf_tl_gc[Geom::X] = buf->rect.left() - ow[Geom::X]; - buf_tl_gc[Geom::Y] = buf->rect.top() - ow[Geom::Y]; - buf_br_gc[Geom::X] = buf->rect.right() - ow[Geom::X]; - buf_br_gc[Geom::Y] = buf->rect.bottom() - ow[Geom::Y]; + Geom::Rect buf_gc(buf->rect); + buf_gc -= ow; double const section_angle_rad = Geom::deg_to_rad(section_angle_deg); double const section_sin = sin(section_angle_rad); double const section_cos = cos(section_angle_rad); - gdouble xmax = 25; // TODO compute from viewport intersection - DON'T FLOOR + gdouble xmax = find_bound(buf_gc, -section_sin, section_cos, &std::max) / lxw; if (xmax <= 0) return; // Section is entirely out of viewport - gdouble ymin = ceil(-10); // TODO compute from viewport intersection - gdouble ymax = floor(10); // TODO compute from viewport intersection + gdouble ymin = find_bound(buf_gc, -section_cos, -section_sin, &std::min) / lyw; + gdouble ymax = find_bound(buf_gc, -section_cos, -section_sin, &std::max) / lyw; gdouble const lxmax = xmax * lxw; - gdouble const xbound = (xmax + 0.5) * lyw; - gdouble const ybound = (ymax + 0.5) * lyw; + gdouble const xbound = (floor(xmax) + 0.5) * lxw; + gdouble const ybound = (floor(ymax) + 0.5) * lyw; // Render section edge line { @@ -556,6 +572,7 @@ CanvasNGonGrid::renderSection (SPCanvasBuf *buf, double section_angle_deg, guint } // Render semi-radius lines + ymin = ceil(ymin); gint xlinenum = ymin; for (gdouble y = ymin * lyw; y <= ybound; y += lyw, xlinenum++) { // Compute points in preimage coordinates -- 1.7.11.7 From bb6821ecddd97d942a143e8dcc9fdc5dbb128a3a Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Sun, 9 Dec 2012 20:13:04 -0500 Subject: [PATCH 6/7] auto-extents improvements Compute minimum X extent so we don't render concentric segments that aren't needed. Add note that we could do better skipping sections that won't be visible. --- src/display/canvas-ngongrid.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/display/canvas-ngongrid.cpp b/src/display/canvas-ngongrid.cpp index de0a26a..74d356e 100644 --- a/src/display/canvas-ngongrid.cpp +++ b/src/display/canvas-ngongrid.cpp @@ -58,7 +58,7 @@ sp_cngongrid_drawline (SPCanvasBuf *buf, gdouble x0, gdouble y0, gdouble x1, gdo y0 = round(y0); y1 = round(y1); } - // TODO clip to viewport? + //TODO: clip to viewport? cairo_move_to(buf->ct, 0.5 + x0, 0.5 + y0); cairo_line_to(buf->ct, 0.5 + x1, 0.5 + y1); ink_cairo_set_source_rgba32(buf->ct, rgba); @@ -533,6 +533,7 @@ CanvasNGonGrid::Render (SPCanvasBuf *buf) double angle_step = 360.0 / sections; for (int s = 0; s < sections; ++s) { + //TODO: get angles from origin to viewport corners, use to test if section needs to be rendered renderSection(buf, (s * angle_step) - angle_deg, _empcolor); } @@ -552,6 +553,7 @@ CanvasNGonGrid::renderSection (SPCanvasBuf *buf, double section_angle_deg, guint double const section_sin = sin(section_angle_rad); double const section_cos = cos(section_angle_rad); + gdouble xmin = find_bound(buf_gc, -section_sin, section_cos, &std::min) / lxw; gdouble xmax = find_bound(buf_gc, -section_sin, section_cos, &std::max) / lxw; if (xmax <= 0) return; // Section is entirely out of viewport @@ -592,8 +594,9 @@ CanvasNGonGrid::renderSection (SPCanvasBuf *buf, double section_angle_deg, guint } // Render concentric lines - gint ylinenum = 1; - for (gdouble x = lxw; x < xbound; x += lxw, ylinenum++) { + xmin = std::max(1.0, ceil(xmin)); + gint ylinenum = xmin; + for (gdouble x = xmin * lxw; x < xbound; x += lxw, ylinenum++) { // Compute points in preimage coordinates gdouble const pc_y = x * se_tan; // Compute points in grid coordinates (with rotation applied) -- 1.7.11.7 From 575b967f57532356acbb780513f8689e94533b23 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Mon, 10 Dec 2012 10:05:36 -0500 Subject: [PATCH 7/7] implement polygonal grid snapping Add necessary code to find the nearest relevant lines to which to snap for the polygonal grid. --- src/display/canvas-ngongrid.cpp | 115 ++++++++++++++-------------------------- src/display/canvas-ngongrid.h | 3 +- 2 files changed, 43 insertions(+), 75 deletions(-) diff --git a/src/display/canvas-ngongrid.cpp b/src/display/canvas-ngongrid.cpp index 74d356e..11db39d 100644 --- a/src/display/canvas-ngongrid.cpp +++ b/src/display/canvas-ngongrid.cpp @@ -146,6 +146,7 @@ CanvasNGonGrid::CanvasNGonGrid (SPNamedView * nv, Inkscape::XML::Node * in_repr, empcolor = prefs->getInt("/options/grids/ngon/empcolor", 0x0000ff40); empspacing = prefs->getInt("/options/grids/ngon/empspacing", 5); + angle_rad = Geom::deg_to_rad(angle_deg); se_angle_deg = 180.0 / sections; se_angle_rad = Geom::deg_to_rad(se_angle_deg); se_tan = tan(se_angle_rad); @@ -275,6 +276,7 @@ CanvasNGonGrid::readRepr() angle_deg = g_ascii_strtod(value, NULL); double max_angle = 360.0 / sections; if (fabs(angle_deg) > max_angle) angle_deg = fmod(angle_deg, max_angle); + angle_rad = Geom::deg_to_rad(angle_deg); } if ( (value = repr->attribute("color")) ) { @@ -661,82 +663,47 @@ CanvasNGonGridSnapper::_getSnapLines(Geom::Point const &p) const // Snapping to any grid line, whether it's visible or not spacing_h = grid->lengthx; spacing_v = grid->lengthy; - } - /* - // In an axonometric grid, any point will be surrounded by 6 grid lines: - // - 2 vertical grid lines, one left and one right from the point - // - 2 angled z grid lines, one above and one below the point - // - 2 angled x grid lines, one above and one below the point - - // Calculate the x coordinate of the vertical grid lines - Geom::Coord x_max = Inkscape::Util::round_to_upper_multiple_plus(p[Geom::X], spacing_h, grid->origin[Geom::X]); - Geom::Coord x_min = Inkscape::Util::round_to_lower_multiple_plus(p[Geom::X], spacing_h, grid->origin[Geom::X]); - - // Calculate the y coordinate of the intersection of the angled grid lines with the y-axis - double y_proj_along_z = p[Geom::Y] - grid->tan_angle*(p[Geom::X] - grid->origin[Geom::X]); - double y_proj_along_x = p[Geom::Y] + grid->tan_angle*(p[Geom::X] - grid->origin[Geom::X]); - double y_proj_along_z_max = Inkscape::Util::round_to_upper_multiple_plus(y_proj_along_z, spacing_v, grid->origin[Geom::Y]); - double y_proj_along_z_min = Inkscape::Util::round_to_lower_multiple_plus(y_proj_along_z, spacing_v, grid->origin[Geom::Y]); - double y_proj_along_x_max = Inkscape::Util::round_to_upper_multiple_plus(y_proj_along_x, spacing_v, grid->origin[Geom::Y]); - double y_proj_along_x_min = Inkscape::Util::round_to_lower_multiple_plus(y_proj_along_x, spacing_v, grid->origin[Geom::Y]); - - // Calculate the versor for the angled grid lines - Geom::Point vers_x = Geom::Point(1, -grid->tan_angle); - Geom::Point vers_z = Geom::Point(1, grid->tan_angle); - - // Calculate the normal for the angled grid lines - Geom::Point norm_x = Geom::rot90(vers_x); - Geom::Point norm_z = Geom::rot90(vers_z); - - // The four angled grid lines form a parallelogram, enclosing the point - // One of the two vertical grid lines divides this parallelogram in two triangles - // We will now try to find out in which half (i.e. triangle) our point is, and return - // only the three grid lines defining that triangle - - // The vertical grid line is at the intersection of two angled grid lines. - // Now go find that intersection! - Geom::Point p_x(0, y_proj_along_x_max); - Geom::Line line_x(p_x, p_x + vers_x); - Geom::Point p_z(0, y_proj_along_z_max); - Geom::Line line_z(p_z, p_z + vers_z); - - Geom::OptCrossing inters = Geom::OptCrossing(); // empty by default - try - { - inters = Geom::intersection(line_x, line_z); - } - catch (Geom::InfiniteSolutions &e) - { - // We're probably dealing with parallel lines; this is useless! - return s; - } - - // Determine which half of the parallelogram to use - bool use_left_half = true; - bool use_right_half = true; - - if (inters) { - Geom::Point inters_pt = line_x.pointAt((*inters).ta); - use_left_half = (p[Geom::X] - grid->origin[Geom::X]) < inters_pt[Geom::X]; - use_right_half = !use_left_half; - } - - // Return the three grid lines which define the triangle that encloses our point - // If we didn't find an intersection above, all 6 grid lines will be returned - if (use_left_half) { - s.push_back(std::make_pair(norm_z, Geom::Point(grid->origin[Geom::X], y_proj_along_z_max))); - s.push_back(std::make_pair(norm_x, Geom::Point(grid->origin[Geom::X], y_proj_along_x_min))); - s.push_back(std::make_pair(Geom::Point(1, 0), Geom::Point(x_max, 0))); - } - - if (use_right_half) { - s.push_back(std::make_pair(norm_z, Geom::Point(grid->origin[Geom::X], y_proj_along_z_min))); - s.push_back(std::make_pair(norm_x, Geom::Point(grid->origin[Geom::X], y_proj_along_x_max))); - s.push_back(std::make_pair(Geom::Point(1, 0), Geom::Point(x_min, 0))); - } - */ + // In a polygonal grid, any point will be surrounded by 6 grid lines: + // - 4 lines of a section grid + // - 2 lines of the section edges + // Of these, we use the closer section edge, and the closer each of the grid X and Y lines + + // Calculate what section the point is in + Geom::Point gc_point = p - grid->origin; + double point_angle_rad; + bool const x_is_zero = Geom::are_near(gc_point[Geom::X], 0.); + bool const y_is_zero = Geom::are_near(gc_point[Geom::Y], 0.); + if (Geom::are_near(p, grid->origin)) + point_angle_rad = 0; + else + point_angle_rad = Geom::atan2(gc_point); + double const section_ratio = (point_angle_rad - grid->angle_rad - grid->se_angle_rad) / (2.0 * M_PI); + int const section = floor( grid->sections * section_ratio ) + 1; + + // Compute spacing-unit vectors for section + double const section_angle_rad = (2.0 * section * grid->se_angle_rad) + grid->angle_rad; + double const section_sin = sin(section_angle_rad); + double const section_cos = cos(section_angle_rad); + Geom::Point const gc_nx(-section_sin, section_cos); + Geom::Point const gc_ny( section_cos, section_sin); + Geom::Point const gc_sx = gc_ny * spacing_h; + Geom::Point const gc_sy = gc_nx * spacing_v; + + // Get point in section pre-image space + double const pc_x = ( (gc_point[Geom::Y] * section_sin) + (gc_point[Geom::X] * section_cos) ) / spacing_h; + double const pc_y = ( (gc_point[Geom::Y] * section_cos) - (gc_point[Geom::X] * section_sin) ) / spacing_v; + + // Add the nearer section edge line + double const section_edge_angle_rad = section_angle_rad + ( (pc_y > 0.0 ? 1.0 : -1.0) * grid->se_angle_rad); + Geom::Point const section_edge_norm(-sin(section_edge_angle_rad), cos(section_edge_angle_rad)); + s.push_back( std::make_pair(section_edge_norm, grid->origin) ); + + // Add the two nearer lines of the grid square + Geom::Point const gc_corner = (gc_sx * round(pc_x)) + (gc_sy * round(pc_y)); + s.push_back( std::make_pair(gc_nx, gc_corner) ); + s.push_back( std::make_pair(gc_ny, gc_corner) ); return s; } diff --git a/src/display/canvas-ngongrid.h b/src/display/canvas-ngongrid.h index 3c81619..592ee34 100644 --- a/src/display/canvas-ngongrid.h +++ b/src/display/canvas-ngongrid.h @@ -36,7 +36,8 @@ public: int sections; /**< Number of grid sections */ double lengthx; /**< Step size along concentric polygons */ double lengthy; /**< Step size along semi-radius lines */ - double angle_deg; /**< Angle of rotation */ + double angle_deg; /**< Angle of rotation (degrees) */ + double angle_rad; /**< Angle of rotation (radians) */ double se_angle_deg; /**< Half of section arc (degrees) */ double se_angle_rad; /**< Half of section arc (radians) */ double se_tan; /**< tan(se_angle) */ -- 1.7.11.7