From 77437ad04cbb9288b6e8898e8822a16588d55b4a Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Wed, 17 Jun 2009 16:58:37 +0000 Subject: adding a list of uint32 color references and forcing LCMS debugging (bzr r8050.1.2) --- src/widgets/sp-color-icc-selector.cpp | 52 +++++++++++++++++------------------ 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/widgets/sp-color-icc-selector.cpp b/src/widgets/sp-color-icc-selector.cpp index b18290923..144e42aa2 100644 --- a/src/widgets/sp-color-icc-selector.cpp +++ b/src/widgets/sp-color-icc-selector.cpp @@ -23,14 +23,14 @@ #include "color-profile-fns.h" #include "color-profile.h" //#define DEBUG_LCMS -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS #include "preferences.h" #include #endif // DEBUG_LCMS #endif // ENABLE_LCMS -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS extern guint update_in_progress; #define DEBUG_MESSAGE(key, ...) \ {\ @@ -475,11 +475,11 @@ void ColorICCSelector::_switchToProfile( gchar const* name ) if ( name ) { if ( tmp.icc && tmp.icc->colorProfile == name ) { -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message("Already at name [%s]", name ); #endif // DEBUG_LCMS } else { -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message("Need to switch to profile [%s]", name ); #endif // DEBUG_LCMS if ( tmp.icc ) { @@ -498,12 +498,12 @@ void ColorICCSelector::_switchToProfile( gchar const* name ) SP_RGBA32_G_U(val), SP_RGBA32_B_U(val), 255}; -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message("Shoving in [%02x] [%02x] [%02x]", pre[0], pre[1], pre[2]); #endif // DEBUG_LCMS icUInt16Number post[4] = {0,0,0,0}; cmsDoTransform( trans, pre, post, 1 ); -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message("got on out [%04x] [%04x] [%04x] [%04x]", post[0], post[1], post[2], post[3]); #endif // DEBUG_LCMS guint count = _cmsChannelsOf( newProf->getColorSpace() ); @@ -515,7 +515,7 @@ void ColorICCSelector::_switchToProfile( gchar const* name ) for ( guint i = 0; i < count; i++ ) { gdouble val = (((gdouble)post[i])/65535.0) * (gdouble)scales[i]; -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message(" scaled %d by %d to be %f", i, scales[i], val); #endif // DEBUG_LCMS tmp.icc->colors.push_back(val); @@ -523,7 +523,7 @@ void ColorICCSelector::_switchToProfile( gchar const* name ) cmsHTRANSFORM retrans = newProf->getTransfToSRGB8(); if ( retrans ) { cmsDoTransform( retrans, post, pre, 1 ); -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message(" back out [%02x] [%02x] [%02x]", pre[0], pre[1], pre[2]); #endif // DEBUG_LCMS tmp.set(SP_RGBA32_U_COMPOSE(pre[0], pre[1], pre[2], 0xff)); @@ -533,7 +533,7 @@ void ColorICCSelector::_switchToProfile( gchar const* name ) dirty = true; } } else { -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message("NUKE THE ICC"); #endif // DEBUG_LCMS if ( tmp.icc ) { @@ -542,21 +542,21 @@ void ColorICCSelector::_switchToProfile( gchar const* name ) dirty = true; _fixupHit( 0, this ); } else { -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message("No icc to nuke"); #endif // DEBUG_LCMS } } if ( dirty ) { -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message("+----------------"); g_message("+ new color is [%s]", tmp.toString().c_str()); #endif // DEBUG_LCMS _setProfile( tmp.icc ); //_adjustmentChanged( _fooAdj[0], SP_COLOR_ICC_SELECTOR(_csel) ); setColorAlpha( tmp, _alpha, true ); -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message("+_________________"); #endif // DEBUG_LCMS } @@ -603,15 +603,15 @@ void ColorICCSelector::_profilesChanged( std::string const & name ) void ColorICCSelector::_colorChanged() { _updating = TRUE; -// sp_color_icc_set_color( SP_COLOR_ICC( _icc ), &color ); + //sp_color_icc_set_color( SP_COLOR_ICC( _icc ), &color ); -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message( "/^^^^^^^^^ %p::_colorChanged(%08x:%s)", this, _color.toRGBA32(_alpha), ( (_color.icc) ? _color.icc->colorProfile.c_str(): "" ) ); #endif // DEBUG_LCMS -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message("FLIPPIES!!!! %p '%s'", _color.icc, (_color.icc ? _color.icc->colorProfile.c_str():"")); #endif // DEBUG_LCMS @@ -642,7 +642,7 @@ void ColorICCSelector::_colorChanged() if ( other != _color.toRGBA32(255) ) { _fixupNeeded = other; gtk_widget_set_sensitive( _fixupBtn, TRUE ); -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message("Color needs to change 0x%06x to 0x%06x", _color.toRGBA32(255) >> 8, other >> 8 ); #endif // DEBUG_LCMS } @@ -654,7 +654,7 @@ void ColorICCSelector::_colorChanged() _updating = FALSE; -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message( "\\_________ %p::_colorChanged()", this ); #endif // DEBUG_LCMS } @@ -662,7 +662,7 @@ void ColorICCSelector::_colorChanged() #if ENABLE_LCMS void ColorICCSelector::_setProfile( SVGICCColor* profile ) { -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message( "/^^^^^^^^^ %p::_setProfile(%s)", this, ( (profile) ? profile->colorProfile.c_str() : "") ); @@ -730,7 +730,7 @@ void ColorICCSelector::_setProfile( SVGICCColor* profile ) } } -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message( "\\_________ %p::_setProfile()", this ); #endif // DEBUG_LCMS } @@ -800,7 +800,7 @@ void ColorICCSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorICC // gtk_adjustment_set_value( adjustment, floor ((adjustment->value) * adjustment->upper + 0.5) ); // } -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message( "/^^^^^^^^^ %p::_adjustmentChanged()", cs ); #endif // DEBUG_LCMS @@ -816,7 +816,7 @@ void ColorICCSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorICC SPColor newColor( iccSelector->_color ); gfloat scaled = ColorScales::getScaled( iccSelector->_adj ); if ( iccSelector->_adj == adjustment ) { -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message("ALPHA"); #endif // DEBUG_LCMS } else { @@ -828,7 +828,7 @@ void ColorICCSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorICC } } if ( match >= 0 ) { -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message(" channel %d", match ); #endif // DEBUG_LCMS } @@ -852,7 +852,7 @@ void ColorICCSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorICC guint32 newer = other.toRGBA32(255); if ( prior != newer ) { -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message("Transformed color from 0x%08x to 0x%08x", prior, newer ); g_message(" ~~~~ FLIP"); #endif // DEBUG_LCMS @@ -875,7 +875,7 @@ void ColorICCSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorICC iccSelector->_updateSliders( match ); iccSelector->_updating = FALSE; -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message( "\\_________ %p::_adjustmentChanged()", cs ); #endif // DEBUG_LCMS } @@ -900,13 +900,13 @@ void ColorICCSelector::_sliderReleased( SPColorSlider */*slider*/, SPColorICCSel // } } -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS void ColorICCSelector::_sliderChanged( SPColorSlider *slider, SPColorICCSelector *cs ) #else void ColorICCSelector::_sliderChanged( SPColorSlider */*slider*/, SPColorICCSelector */*cs*/ ) #endif // DEBUG_LCMS { -#ifdef DEBUG_LCMS +#if 1 //DEBUG_LCMS g_message("Changed %p and %p", slider, cs ); #endif // DEBUG_LCMS // ColorICCSelector* iccSelector = (ColorICCSelector*)(SP_COLOR_SELECTOR(cs)->base); -- cgit v1.2.3 From 521b94aa9a5ec58311750f1f014958ef5a0c53d3 Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Sat, 20 Jun 2009 06:33:55 +0000 Subject: bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest ); (bzr r8050.1.3) --- src/svg/svg-color.cpp | 6 ++++++ src/svg/svg-color.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp index 9040d6e43..743d2bbc8 100644 --- a/src/svg/svg-color.cpp +++ b/src/svg/svg-color.cpp @@ -547,6 +547,12 @@ bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor return good; } + +bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest ) +{ + sp_svg_read_icc_color(str, NULL, dest); +} + /* Local Variables: mode:c++ diff --git a/src/svg/svg-color.h b/src/svg/svg-color.h index 692c1dd00..d1238dcab 100644 --- a/src/svg/svg-color.h +++ b/src/svg/svg-color.h @@ -10,6 +10,6 @@ guint32 sp_svg_read_color(gchar const *str, gchar const **end_ptr, guint32 def); void sp_svg_write_color(char *buf, unsigned int buflen, unsigned int rgba32); bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor* dest ); - +bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest ); #endif /* !SVG_SVG_COLOR_H_SEEN */ -- cgit v1.2.3 From 59c1c219f9b89e50bd9fa9acce9bed9860a281e7 Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Sat, 20 Jun 2009 11:49:01 +0000 Subject: parsing and handling of icc-color in feFlood SVG Filters TODO: implement interface to set icc-color to the flood-color parameter (bzr r8050.1.4) --- src/display/nr-filter-flood.cpp | 27 ++++++++++++++---- src/display/nr-filter-flood.h | 3 ++ src/filters/flood.cpp | 25 +++++++++++++++-- src/filters/flood.h | 2 ++ src/svg/svg-color.cpp | 47 +++++++++++++++++++++++++++++-- src/svg/svg-color.h | 1 + src/widgets/sp-color-icc-selector.cpp | 52 +++++++++++++++++------------------ 7 files changed, 122 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/display/nr-filter-flood.cpp b/src/display/nr-filter-flood.cpp index 026cbce16..06025392b 100644 --- a/src/display/nr-filter-flood.cpp +++ b/src/display/nr-filter-flood.cpp @@ -9,8 +9,14 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include "display/nr-filter-flood.h" #include "display/nr-filter-utils.h" +#include "svg/svg-icc-color.h" +#include "svg/svg-color.h" namespace Inkscape { namespace Filters { @@ -26,6 +32,7 @@ FilterFlood::~FilterFlood() {} int FilterFlood::render(FilterSlot &slot, FilterUnits const &/*units*/) { +g_message("rendering feflood"); NRPixBlock *in = slot.get(_input); if (!in) { g_warning("Missing source image for feFlood (in=%d)", _input); @@ -43,12 +50,18 @@ int FilterFlood::render(FilterSlot &slot, FilterUnits const &/*units*/) { true); unsigned char *out_data = NR_PIXBLOCK_PX(out); - unsigned char r,g,b,a; - r = CLAMP_D_TO_U8((color >> 24) % 256); - g = CLAMP_D_TO_U8((color >> 16) % 256); - b = CLAMP_D_TO_U8((color >> 8) % 256); - a = CLAMP_D_TO_U8(opacity*255); + + + r = CLAMP_D_TO_U8((color >> 24) % 256); + g = CLAMP_D_TO_U8((color >> 16) % 256); + b = CLAMP_D_TO_U8((color >> 8) % 256); + a = CLAMP_D_TO_U8(opacity*255); + +#if ENABLE_LCMS + icc_color_to_sRGB(icc, &r, &g, &b); +g_message("result: r:%d g:%d b:%d", r, g, b); +#endif //ENABLE_LCMS for(i=0; i < 4*in_h*in_w; i+=4){ out_data[i]=r; @@ -70,6 +83,10 @@ void FilterFlood::set_opacity(double o) { opacity = o; } +void FilterFlood::set_icc(SVGICCColor *icc_color) { + icc = icc_color; +} + void FilterFlood::area_enlarge(NRRectL &/*area*/, Geom::Matrix const &/*trans*/) { } diff --git a/src/display/nr-filter-flood.h b/src/display/nr-filter-flood.h index 9e6a53abb..d38c0d4f5 100644 --- a/src/display/nr-filter-flood.h +++ b/src/display/nr-filter-flood.h @@ -15,6 +15,7 @@ #include "display/nr-filter-primitive.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" +#include "svg/svg-color.h" namespace Inkscape { namespace Filters { @@ -27,11 +28,13 @@ public: virtual void set_opacity(double o); virtual void set_color(guint32 c); + virtual void set_icc(SVGICCColor *icc_color); virtual int render(FilterSlot &slot, FilterUnits const &units); virtual void area_enlarge(NRRectL &area, Geom::Matrix const &trans); private: double opacity; guint32 color; + SVGICCColor *icc; }; } /* namespace Filters */ diff --git a/src/filters/flood.cpp b/src/filters/flood.cpp index 625e35d42..221b0daf2 100644 --- a/src/filters/flood.cpp +++ b/src/filters/flood.cpp @@ -17,12 +17,13 @@ # include "config.h" #endif +#include "strneq.h" + #include "attributes.h" #include "svg/svg.h" #include "flood.h" #include "xml/repr.h" #include "helper-fns.h" -#include "svg/svg-color.h" /* FeFlood base class */ @@ -79,6 +80,7 @@ static void sp_feFlood_init(SPFeFlood *feFlood) { feFlood->opacity = 1; + feFlood->icc = NULL; } /** @@ -120,16 +122,34 @@ sp_feFlood_set(SPObject *object, unsigned int key, gchar const *value) gchar *end_ptr = NULL; guint32 read_color; double read_num; + bool dirty = false; switch(key) { /*DEAL WITH SETTING ATTRIBUTES HERE*/ case SP_PROP_FLOOD_COLOR: cend_ptr = NULL; read_color = sp_svg_read_color(value, &cend_ptr, 0xffffffff); + if (cend_ptr && read_color != feFlood->color){ feFlood->color = read_color; - object->parent->requestModified(SP_OBJECT_MODIFIED_FLAG); + dirty=true; + } + + if (cend_ptr){ + while (g_ascii_isspace(*cend_ptr)) { + ++cend_ptr; + } + if (strneq(cend_ptr, "icc-color(", 10)) { + if (!feFlood->icc) feFlood->icc = new SVGICCColor(); + if ( ! sp_svg_read_icc_color( cend_ptr, feFlood->icc ) ) { + delete feFlood->icc; + feFlood->icc = NULL; + } + dirty = true; + } } + if (dirty) + object->parent->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_PROP_FLOOD_OPACITY: if (value) { @@ -208,6 +228,7 @@ static void sp_feFlood_build_renderer(SPFilterPrimitive *primitive, Inkscape::Fi nr_flood->set_opacity(sp_flood->opacity); nr_flood->set_color(sp_flood->color); + nr_flood->set_icc(sp_flood->icc); } diff --git a/src/filters/flood.h b/src/filters/flood.h index 046c0e868..f386e2cd4 100644 --- a/src/filters/flood.h +++ b/src/filters/flood.h @@ -15,6 +15,7 @@ #include "sp-filter.h" #include "flood-fns.h" +#include "svg/svg-icc-color.h" #include "display/nr-filter.h" #include "display/nr-filter-flood.h" @@ -25,6 +26,7 @@ class SPFeFloodClass; struct SPFeFlood : public SPFilterPrimitive { /** FLOOD ATTRIBUTES HERE */ guint32 color; + SVGICCColor *icc; double opacity; }; diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp index 743d2bbc8..fcfbbdbd8 100644 --- a/src/svg/svg-color.cpp +++ b/src/svg/svg-color.cpp @@ -36,6 +36,15 @@ #include "svg-color.h" #include "svg-icc-color.h" +#if ENABLE_LCMS +#include +#include "color.h" +#include "color-profile.h" +#include "document.h" +#include "inkscape.h" +#include "profile-manager.h" +#endif // ENABLE_LCMS + using std::sprintf; struct SPSVGColor { @@ -454,6 +463,39 @@ sp_svg_create_color_hash() return colors; } +//helper function borrowed from src/widgets/sp-color-icc-selector.cpp: +void getThings( DWORD space, gchar const**& namers, gchar const**& tippies, guint const*& scalies ); + +void icc_color_to_sRGB(SVGICCColor* icc, guchar* r, guchar* g, guchar* b){ + guchar color_out[4]; + guchar color_in[4]; + if (icc){ +g_message("profile name: %s", icc->colorProfile.c_str()); + Inkscape::ColorProfile* prof = SP_ACTIVE_DOCUMENT->profileManager->find(icc->colorProfile.c_str()); + if ( prof ) { + cmsHTRANSFORM trans = prof->getTransfToSRGB8(); + if ( trans ) { + gchar const** names = 0; + gchar const** tips = 0; + guint const* scales = 0; + getThings( prof->getColorSpace(), names, tips, scales ); + + guint count = _cmsChannelsOf( prof->getColorSpace() ); + if (count>4) count=4; //do we need it? Should we allow an arbitrary number of color values? Or should we limit to a maximum? (max==4?) + for (guint i=0;icolors[i])*256.0) * (gdouble)scales[i]); +g_message("input[%d]: %d",i, color_in[i]); + } + + cmsDoTransform( trans, color_in, color_out, 1 ); +g_message("transform to sRGB done"); + } + *r = color_out[0]; + *g = color_out[1]; + *b = color_out[2]; + } + } +} bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor* dest ) { @@ -508,6 +550,7 @@ bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor if ( !errno ) { if ( dest ) { dest->colors.push_back( dbl ); +g_message("color: %f", dbl); } str = endPtr; } else { @@ -529,7 +572,7 @@ bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor while ( g_ascii_isspace(*str) ) { str++; } - good &= *str == ')'; + good &= (*str == ')'); } } @@ -550,7 +593,7 @@ bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest ) { - sp_svg_read_icc_color(str, NULL, dest); + return sp_svg_read_icc_color(str, NULL, dest); } /* diff --git a/src/svg/svg-color.h b/src/svg/svg-color.h index d1238dcab..a3868c149 100644 --- a/src/svg/svg-color.h +++ b/src/svg/svg-color.h @@ -11,5 +11,6 @@ void sp_svg_write_color(char *buf, unsigned int buflen, unsigned int rgba32); bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor* dest ); bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest ); +void icc_color_to_sRGB(SVGICCColor* dest, guchar* r, guchar* g, guchar* b); #endif /* !SVG_SVG_COLOR_H_SEEN */ diff --git a/src/widgets/sp-color-icc-selector.cpp b/src/widgets/sp-color-icc-selector.cpp index 144e42aa2..ae837117a 100644 --- a/src/widgets/sp-color-icc-selector.cpp +++ b/src/widgets/sp-color-icc-selector.cpp @@ -22,15 +22,15 @@ #if ENABLE_LCMS #include "color-profile-fns.h" #include "color-profile.h" -//#define DEBUG_LCMS -#if 1 //DEBUG_LCMS +#define DEBUG_LCMS +#ifdef DEBUG_LCMS #include "preferences.h" #include #endif // DEBUG_LCMS #endif // ENABLE_LCMS -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS extern guint update_in_progress; #define DEBUG_MESSAGE(key, ...) \ {\ @@ -475,11 +475,11 @@ void ColorICCSelector::_switchToProfile( gchar const* name ) if ( name ) { if ( tmp.icc && tmp.icc->colorProfile == name ) { -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message("Already at name [%s]", name ); #endif // DEBUG_LCMS } else { -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message("Need to switch to profile [%s]", name ); #endif // DEBUG_LCMS if ( tmp.icc ) { @@ -498,12 +498,12 @@ void ColorICCSelector::_switchToProfile( gchar const* name ) SP_RGBA32_G_U(val), SP_RGBA32_B_U(val), 255}; -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message("Shoving in [%02x] [%02x] [%02x]", pre[0], pre[1], pre[2]); #endif // DEBUG_LCMS icUInt16Number post[4] = {0,0,0,0}; cmsDoTransform( trans, pre, post, 1 ); -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message("got on out [%04x] [%04x] [%04x] [%04x]", post[0], post[1], post[2], post[3]); #endif // DEBUG_LCMS guint count = _cmsChannelsOf( newProf->getColorSpace() ); @@ -515,7 +515,7 @@ void ColorICCSelector::_switchToProfile( gchar const* name ) for ( guint i = 0; i < count; i++ ) { gdouble val = (((gdouble)post[i])/65535.0) * (gdouble)scales[i]; -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message(" scaled %d by %d to be %f", i, scales[i], val); #endif // DEBUG_LCMS tmp.icc->colors.push_back(val); @@ -523,7 +523,7 @@ void ColorICCSelector::_switchToProfile( gchar const* name ) cmsHTRANSFORM retrans = newProf->getTransfToSRGB8(); if ( retrans ) { cmsDoTransform( retrans, post, pre, 1 ); -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message(" back out [%02x] [%02x] [%02x]", pre[0], pre[1], pre[2]); #endif // DEBUG_LCMS tmp.set(SP_RGBA32_U_COMPOSE(pre[0], pre[1], pre[2], 0xff)); @@ -533,7 +533,7 @@ void ColorICCSelector::_switchToProfile( gchar const* name ) dirty = true; } } else { -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message("NUKE THE ICC"); #endif // DEBUG_LCMS if ( tmp.icc ) { @@ -542,21 +542,21 @@ void ColorICCSelector::_switchToProfile( gchar const* name ) dirty = true; _fixupHit( 0, this ); } else { -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message("No icc to nuke"); #endif // DEBUG_LCMS } } if ( dirty ) { -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message("+----------------"); g_message("+ new color is [%s]", tmp.toString().c_str()); #endif // DEBUG_LCMS _setProfile( tmp.icc ); //_adjustmentChanged( _fooAdj[0], SP_COLOR_ICC_SELECTOR(_csel) ); setColorAlpha( tmp, _alpha, true ); -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message("+_________________"); #endif // DEBUG_LCMS } @@ -605,13 +605,13 @@ void ColorICCSelector::_colorChanged() _updating = TRUE; //sp_color_icc_set_color( SP_COLOR_ICC( _icc ), &color ); -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message( "/^^^^^^^^^ %p::_colorChanged(%08x:%s)", this, _color.toRGBA32(_alpha), ( (_color.icc) ? _color.icc->colorProfile.c_str(): "" ) ); #endif // DEBUG_LCMS -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message("FLIPPIES!!!! %p '%s'", _color.icc, (_color.icc ? _color.icc->colorProfile.c_str():"")); #endif // DEBUG_LCMS @@ -642,7 +642,7 @@ void ColorICCSelector::_colorChanged() if ( other != _color.toRGBA32(255) ) { _fixupNeeded = other; gtk_widget_set_sensitive( _fixupBtn, TRUE ); -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message("Color needs to change 0x%06x to 0x%06x", _color.toRGBA32(255) >> 8, other >> 8 ); #endif // DEBUG_LCMS } @@ -654,7 +654,7 @@ void ColorICCSelector::_colorChanged() _updating = FALSE; -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message( "\\_________ %p::_colorChanged()", this ); #endif // DEBUG_LCMS } @@ -662,7 +662,7 @@ void ColorICCSelector::_colorChanged() #if ENABLE_LCMS void ColorICCSelector::_setProfile( SVGICCColor* profile ) { -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message( "/^^^^^^^^^ %p::_setProfile(%s)", this, ( (profile) ? profile->colorProfile.c_str() : "") ); @@ -730,7 +730,7 @@ void ColorICCSelector::_setProfile( SVGICCColor* profile ) } } -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message( "\\_________ %p::_setProfile()", this ); #endif // DEBUG_LCMS } @@ -800,7 +800,7 @@ void ColorICCSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorICC // gtk_adjustment_set_value( adjustment, floor ((adjustment->value) * adjustment->upper + 0.5) ); // } -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message( "/^^^^^^^^^ %p::_adjustmentChanged()", cs ); #endif // DEBUG_LCMS @@ -816,7 +816,7 @@ void ColorICCSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorICC SPColor newColor( iccSelector->_color ); gfloat scaled = ColorScales::getScaled( iccSelector->_adj ); if ( iccSelector->_adj == adjustment ) { -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message("ALPHA"); #endif // DEBUG_LCMS } else { @@ -828,7 +828,7 @@ void ColorICCSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorICC } } if ( match >= 0 ) { -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message(" channel %d", match ); #endif // DEBUG_LCMS } @@ -852,7 +852,7 @@ void ColorICCSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorICC guint32 newer = other.toRGBA32(255); if ( prior != newer ) { -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message("Transformed color from 0x%08x to 0x%08x", prior, newer ); g_message(" ~~~~ FLIP"); #endif // DEBUG_LCMS @@ -875,7 +875,7 @@ void ColorICCSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorICC iccSelector->_updateSliders( match ); iccSelector->_updating = FALSE; -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message( "\\_________ %p::_adjustmentChanged()", cs ); #endif // DEBUG_LCMS } @@ -900,13 +900,13 @@ void ColorICCSelector::_sliderReleased( SPColorSlider */*slider*/, SPColorICCSel // } } -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS void ColorICCSelector::_sliderChanged( SPColorSlider *slider, SPColorICCSelector *cs ) #else void ColorICCSelector::_sliderChanged( SPColorSlider */*slider*/, SPColorICCSelector */*cs*/ ) #endif // DEBUG_LCMS { -#if 1 //DEBUG_LCMS +#ifdef DEBUG_LCMS g_message("Changed %p and %p", slider, cs ); #endif // DEBUG_LCMS // ColorICCSelector* iccSelector = (ColorICCSelector*)(SP_COLOR_SELECTOR(cs)->base); -- cgit v1.2.3 From 8ef2626fa8bbdf13da2c9fccd9d7b45e4bb0247a Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Sat, 1 Aug 2009 04:01:00 +0000 Subject: enabling all icc-color related debug messages (bzr r8050.1.5) --- src/color-profile.cpp | 11 +++++++++-- src/dropper-context.cpp | 8 ++++++++ src/sp-image.cpp | 11 +++++++++-- src/widgets/sp-color-icc-selector.cpp | 4 ++-- 4 files changed, 28 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/color-profile.cpp b/src/color-profile.cpp index 20e870242..de9dc6959 100644 --- a/src/color-profile.cpp +++ b/src/color-profile.cpp @@ -1,6 +1,6 @@ -//#define DEBUG_LCMS +#define DEBUG_LCMS #include #include @@ -36,7 +36,7 @@ static cmsHPROFILE colorprofile_get_proof_profile_handle(); #ifdef DEBUG_LCMS extern guint update_in_progress; -#define DEBUG_MESSAGE(key, ...) \ +#define DEBUG_MESSAGE_SCISLAC(key, ...) \ {\ Inkscape::Preferences *prefs = Inkscape::Preferences::get();\ bool dump = prefs->getBool(Glib::ustring("/options/scislac/") + #key);\ @@ -62,6 +62,13 @@ extern guint update_in_progress; gtk_widget_show_all( dialog );\ }\ } + + +#define DEBUG_MESSAGE(key, ...)\ +{\ + g_message( __VA_ARGS__ );\ +} + #endif // DEBUG_LCMS static SPObjectClass *cprof_parent_class; diff --git a/src/dropper-context.cpp b/src/dropper-context.cpp index 985e3ac51..5c66eca46 100644 --- a/src/dropper-context.cpp +++ b/src/dropper-context.cpp @@ -137,6 +137,14 @@ static void sp_dropper_context_finish(SPEventContext *ec) } +/** + * Returns the current dropper context icc-color. + */ +SPColor* sp_dropper_context_get_icc_color(SPEventContext *ec) +{ + //TODO: implement-me! +} + /** * Returns the current dropper context color. */ diff --git a/src/sp-image.cpp b/src/sp-image.cpp index 65aad1e2d..f4ab3d9ef 100644 --- a/src/sp-image.cpp +++ b/src/sp-image.cpp @@ -51,8 +51,15 @@ #if ENABLE_LCMS #include "color-profile-fns.h" #include "color-profile.h" -//#define DEBUG_LCMS +#define DEBUG_LCMS #ifdef DEBUG_LCMS + + +#define DEBUG_MESSAGE(key, ...)\ +{\ + g_message( __VA_ARGS__ );\ +} + #include "preferences.h" #include #endif // DEBUG_LCMS @@ -105,7 +112,7 @@ extern "C" #ifdef DEBUG_LCMS extern guint update_in_progress; -#define DEBUG_MESSAGE(key, ...) \ +#define DEBUG_MESSAGE_SCISLAC(key, ...) \ {\ Inkscape::Preferences *prefs = Inkscape::Preferences::get();\ bool dump = prefs->getBool("/options/scislac/" #key);\ diff --git a/src/widgets/sp-color-icc-selector.cpp b/src/widgets/sp-color-icc-selector.cpp index ae837117a..2872dace0 100644 --- a/src/widgets/sp-color-icc-selector.cpp +++ b/src/widgets/sp-color-icc-selector.cpp @@ -17,12 +17,12 @@ #include "inkscape.h" #include "profile-manager.h" -#define noDEBUG_LCMS +#define DEBUG_LCMS #if ENABLE_LCMS #include "color-profile-fns.h" #include "color-profile.h" -#define DEBUG_LCMS + #ifdef DEBUG_LCMS #include "preferences.h" #include -- cgit v1.2.3 From 21636a2ac8416297222ccb2d11005d4571582d41 Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Tue, 11 Aug 2009 04:22:47 +0000 Subject: adding color management icons to fill&stroke dialog (bzr r8050.1.6) --- src/widgets/sp-color-notebook.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp index 779895de4..58974131e 100644 --- a/src/widgets/sp-color-notebook.cpp +++ b/src/widgets/sp-color-notebook.cpp @@ -324,9 +324,25 @@ void ColorNotebook::init() row++; - /* Create RGBA entry and color preview */ GtkWidget *rgbabox = gtk_hbox_new (FALSE, 0); + /* Create color management icons */ + GtkWidget *box_colormanaged = gtk_event_box_new (); + GtkWidget *colormanaged = gtk_image_new_from_icon_name ("color-management-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_container_add (GTK_CONTAINER (box_colormanaged), colormanaged); + GtkTooltips *tooltips_colormanaged = gtk_tooltips_new (); + gtk_tooltips_set_tip (tooltips_colormanaged, box_colormanaged, _("Color Managed"), ""); + //gtk_widget_hide (GTK_WIDGET (box_colormanaged)); + gtk_box_pack_start(GTK_BOX(rgbabox), box_colormanaged, TRUE, FALSE, 2); + + GtkWidget *box_outofgamut = gtk_event_box_new (); + GtkWidget *outofgamut = gtk_image_new_from_icon_name ("out-of-gamut-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_container_add (GTK_CONTAINER (box_outofgamut), outofgamut); + GtkTooltips *tooltips_outofgamut = gtk_tooltips_new (); + gtk_tooltips_set_tip (tooltips_outofgamut, box_outofgamut, _("Out of gamut!"), ""); + //gtk_widget_hide (GTK_WIDGET (box_outofgamut)); + gtk_box_pack_start(GTK_BOX(rgbabox), box_outofgamut, TRUE, FALSE, 2); + /* Create RGBA entry and color preview */ _rgbal = gtk_label_new_with_mnemonic (_("RGBA_:")); gtk_misc_set_alignment (GTK_MISC (_rgbal), 1.0, 0.5); gtk_box_pack_start(GTK_BOX(rgbabox), _rgbal, TRUE, TRUE, 2); -- cgit v1.2.3 From 6391c5291f744ab260ad3d2f661eab6d101f6b23 Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Tue, 11 Aug 2009 04:58:32 +0000 Subject: setting sensibility of "color management icon" depending on icc-color usage (bzr r8050.1.7) --- src/widgets/sp-color-notebook.cpp | 23 +++++++++++++---------- src/widgets/sp-color-notebook.h | 1 + 2 files changed, 14 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp index 58974131e..44de40115 100644 --- a/src/widgets/sp-color-notebook.cpp +++ b/src/widgets/sp-color-notebook.cpp @@ -326,21 +326,21 @@ void ColorNotebook::init() GtkWidget *rgbabox = gtk_hbox_new (FALSE, 0); /* Create color management icons */ - GtkWidget *box_colormanaged = gtk_event_box_new (); + _box_colormanaged = gtk_event_box_new (); GtkWidget *colormanaged = gtk_image_new_from_icon_name ("color-management-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); - gtk_container_add (GTK_CONTAINER (box_colormanaged), colormanaged); + gtk_container_add (GTK_CONTAINER (_box_colormanaged), colormanaged); GtkTooltips *tooltips_colormanaged = gtk_tooltips_new (); - gtk_tooltips_set_tip (tooltips_colormanaged, box_colormanaged, _("Color Managed"), ""); - //gtk_widget_hide (GTK_WIDGET (box_colormanaged)); - gtk_box_pack_start(GTK_BOX(rgbabox), box_colormanaged, TRUE, FALSE, 2); + gtk_tooltips_set_tip (tooltips_colormanaged, _box_colormanaged, _("Color Managed"), ""); + gtk_widget_set_sensitive (_box_colormanaged, false); + gtk_box_pack_start(GTK_BOX(rgbabox), _box_colormanaged, TRUE, FALSE, 2); - GtkWidget *box_outofgamut = gtk_event_box_new (); + _box_outofgamut = gtk_event_box_new (); GtkWidget *outofgamut = gtk_image_new_from_icon_name ("out-of-gamut-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); - gtk_container_add (GTK_CONTAINER (box_outofgamut), outofgamut); + gtk_container_add (GTK_CONTAINER (_box_outofgamut), outofgamut); GtkTooltips *tooltips_outofgamut = gtk_tooltips_new (); - gtk_tooltips_set_tip (tooltips_outofgamut, box_outofgamut, _("Out of gamut!"), ""); - //gtk_widget_hide (GTK_WIDGET (box_outofgamut)); - gtk_box_pack_start(GTK_BOX(rgbabox), box_outofgamut, TRUE, FALSE, 2); + gtk_tooltips_set_tip (tooltips_outofgamut, _box_outofgamut, _("Out of gamut!"), ""); + gtk_widget_set_sensitive (_box_outofgamut, false); + gtk_box_pack_start(GTK_BOX(rgbabox), _box_outofgamut, TRUE, FALSE, 2); /* Create RGBA entry and color preview */ _rgbal = gtk_label_new_with_mnemonic (_("RGBA_:")); @@ -500,6 +500,9 @@ void ColorNotebook::_rgbaEntryChanged(GtkEntry* entry) void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha ) { g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); + + /* update color management icon*/ + gtk_widget_set_sensitive (_box_colormanaged, color.icc != NULL); if ( !_updatingrgba ) { diff --git a/src/widgets/sp-color-notebook.h b/src/widgets/sp-color-notebook.h index bf6fb1002..c9ab29798 100644 --- a/src/widgets/sp-color-notebook.h +++ b/src/widgets/sp-color-notebook.h @@ -61,6 +61,7 @@ protected: gulong _entryId; GtkWidget *_book; GtkWidget *_rgbal, *_rgbae; /* RGBA entry */ + GtkWidget *_box_outofgamut, *_box_colormanaged; GtkWidget *_p; /* Color preview */ GtkWidget *_btn; GtkWidget *_popup; -- cgit v1.2.3 From b7477cae10caca6d9a7ee8156f5e194a1458d3a2 Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Thu, 13 Aug 2009 04:03:19 +0000 Subject: preserve icc on color wheel selector (bzr r8050.1.8) --- src/widgets/sp-color-wheel-selector.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/widgets/sp-color-wheel-selector.cpp b/src/widgets/sp-color-wheel-selector.cpp index 174b071f9..bc8c4763b 100644 --- a/src/widgets/sp-color-wheel-selector.cpp +++ b/src/widgets/sp-color-wheel-selector.cpp @@ -10,7 +10,8 @@ #include "../dialogs/dialog-events.h" #include "sp-color-wheel-selector.h" #include "sp-color-scales.h" - +#include "sp-color-icc-selector.h" +#include "../svg/svg-icc-color.h" G_BEGIN_DECLS @@ -237,6 +238,10 @@ void ColorWheelSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorW wheelSelector->_updating = TRUE; + /* Preserve ICC color */ + ColorSelector* selector = (ColorSelector*)(SP_COLOR_SELECTOR(cs)->base); + wheelSelector->_color.icc = selector->getColor().icc ? new SVGICCColor(*selector->getColor().icc) : 0; + wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging ); wheelSelector->_updating = FALSE; @@ -249,6 +254,11 @@ void ColorWheelSelector::_sliderGrabbed( SPColorSlider *slider, SPColorWheelSele if (!wheelSelector->_dragging) { wheelSelector->_dragging = TRUE; wheelSelector->_grabbed(); + + /* Preserve ICC color */ + ColorSelector* selector = (ColorSelector*)(SP_COLOR_SELECTOR(cs)->base); + wheelSelector->_color.icc = selector->getColor().icc ? new SVGICCColor(*selector->getColor().icc) : 0; + wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging ); } } @@ -260,6 +270,11 @@ void ColorWheelSelector::_sliderReleased( SPColorSlider *slider, SPColorWheelSel if (wheelSelector->_dragging) { wheelSelector->_dragging = FALSE; wheelSelector->_released(); + + /* Preserve ICC color */ + ColorSelector* selector = (ColorSelector*)(SP_COLOR_SELECTOR(cs)->base); + wheelSelector->_color.icc = selector->getColor().icc ? new SVGICCColor(*selector->getColor().icc) : 0; + wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging ); } } @@ -269,6 +284,10 @@ void ColorWheelSelector::_sliderChanged( SPColorSlider *slider, SPColorWheelSele (void)slider; ColorWheelSelector* wheelSelector = (ColorWheelSelector*)(SP_COLOR_SELECTOR(cs)->base); + /* Preserve ICC color */ + ColorSelector* selector = (ColorSelector*)(SP_COLOR_SELECTOR(cs)->base); + wheelSelector->_color.icc = selector->getColor().icc ? new SVGICCColor(*selector->getColor().icc) : 0; + wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging ); } @@ -285,6 +304,10 @@ void ColorWheelSelector::_wheelChanged( SPColorWheel *wheel, SPColorWheelSelecto sp_color_slider_set_colors (SP_COLOR_SLIDER(wheelSelector->_slider), start, mid, end); + /* Preserve ICC color */ + ColorSelector* selector = (ColorSelector*)(SP_COLOR_SELECTOR(cs)->base); + color.icc = selector->getColor().icc ? new SVGICCColor(*selector->getColor().icc) : 0; + wheelSelector->_updateInternals( color, wheelSelector->_alpha, sp_color_wheel_is_adjusting( wheel ) ); } -- cgit v1.2.3 From c8840f1663769367b52c25af12df2cd7032a69d9 Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Fri, 14 Aug 2009 03:33:33 +0000 Subject: use a function instead of copy-pasting code (bzr r8050.1.9) --- src/widgets/sp-color-wheel-selector.cpp | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/widgets/sp-color-wheel-selector.cpp b/src/widgets/sp-color-wheel-selector.cpp index bc8c4763b..6012f4e20 100644 --- a/src/widgets/sp-color-wheel-selector.cpp +++ b/src/widgets/sp-color-wheel-selector.cpp @@ -206,6 +206,11 @@ sp_color_wheel_selector_new (void) /* Helpers for setting color value */ +static void preserve_icc(SPColor *color, SPColorWheelSelector *cs){ + ColorSelector* selector = (ColorSelector*)(SP_COLOR_SELECTOR(cs)->base); + color->icc = selector->getColor().icc ? new SVGICCColor(*selector->getColor().icc) : 0; +} + void ColorWheelSelector::_colorChanged() { #ifdef DUMP_CHANGE_INFO @@ -238,10 +243,7 @@ void ColorWheelSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorW wheelSelector->_updating = TRUE; - /* Preserve ICC color */ - ColorSelector* selector = (ColorSelector*)(SP_COLOR_SELECTOR(cs)->base); - wheelSelector->_color.icc = selector->getColor().icc ? new SVGICCColor(*selector->getColor().icc) : 0; - + preserve_icc(&wheelSelector->_color, cs); wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging ); wheelSelector->_updating = FALSE; @@ -255,10 +257,7 @@ void ColorWheelSelector::_sliderGrabbed( SPColorSlider *slider, SPColorWheelSele wheelSelector->_dragging = TRUE; wheelSelector->_grabbed(); - /* Preserve ICC color */ - ColorSelector* selector = (ColorSelector*)(SP_COLOR_SELECTOR(cs)->base); - wheelSelector->_color.icc = selector->getColor().icc ? new SVGICCColor(*selector->getColor().icc) : 0; - + preserve_icc(&wheelSelector->_color, cs); wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging ); } } @@ -271,10 +270,7 @@ void ColorWheelSelector::_sliderReleased( SPColorSlider *slider, SPColorWheelSel wheelSelector->_dragging = FALSE; wheelSelector->_released(); - /* Preserve ICC color */ - ColorSelector* selector = (ColorSelector*)(SP_COLOR_SELECTOR(cs)->base); - wheelSelector->_color.icc = selector->getColor().icc ? new SVGICCColor(*selector->getColor().icc) : 0; - + preserve_icc(&wheelSelector->_color, cs); wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging ); } } @@ -284,10 +280,7 @@ void ColorWheelSelector::_sliderChanged( SPColorSlider *slider, SPColorWheelSele (void)slider; ColorWheelSelector* wheelSelector = (ColorWheelSelector*)(SP_COLOR_SELECTOR(cs)->base); - /* Preserve ICC color */ - ColorSelector* selector = (ColorSelector*)(SP_COLOR_SELECTOR(cs)->base); - wheelSelector->_color.icc = selector->getColor().icc ? new SVGICCColor(*selector->getColor().icc) : 0; - + preserve_icc(&wheelSelector->_color, cs); wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging ); } @@ -304,10 +297,7 @@ void ColorWheelSelector::_wheelChanged( SPColorWheel *wheel, SPColorWheelSelecto sp_color_slider_set_colors (SP_COLOR_SLIDER(wheelSelector->_slider), start, mid, end); - /* Preserve ICC color */ - ColorSelector* selector = (ColorSelector*)(SP_COLOR_SELECTOR(cs)->base); - color.icc = selector->getColor().icc ? new SVGICCColor(*selector->getColor().icc) : 0; - + preserve_icc(&color, cs); wheelSelector->_updateInternals( color, wheelSelector->_alpha, sp_color_wheel_is_adjusting( wheel ) ); } -- cgit v1.2.3 From 048b095b6031adf91a6dc2fb300270f78ca44cbf Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Fri, 14 Aug 2009 03:52:51 +0000 Subject: a quick workaround in order to avoid some invalid names for icc profiles. A complete sanitizer should be implemented later. (bzr r8050.1.10) --- src/ui/dialog/document-properties.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index bb9ab4d02..c21f64629 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -381,6 +381,17 @@ DocumentProperties::populate_available_profiles(){ _menu.show_all(); } +//this is a quick workaround: +static gchar* sanitize_name(gchar* name){ + gchar* c=name; + while (*c != '\0'){ + if (*c == ' ') *c = '-'; + if (*c == '_') *c = '-'; + c++; + } + return name; +} + void DocumentProperties::linkSelectedProfile() { @@ -396,7 +407,8 @@ DocumentProperties::linkSelectedProfile() } Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc()); Inkscape::XML::Node *cprofRepr = xml_doc->createElement("svg:color-profile"); - cprofRepr->setAttribute("name", (gchar*) _menu.get_active()->get_data("name")); +// cprofRepr->setAttribute("inkscape:name", (gchar*) _menu.get_active()->get_data("name")); + cprofRepr->setAttribute("name", sanitize_name((gchar*) _menu.get_active()->get_data("name"))); cprofRepr->setAttribute("xlink:href", (gchar*) _menu.get_active()->get_data("filepath")); // Checks whether there is a defs element. Creates it when needed -- cgit v1.2.3 From 61ed908098ec4a07eae3308920111522a555c60a Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Fri, 14 Aug 2009 04:21:17 +0000 Subject: preserving icc colors in all tabs of fill&stroke (bzr r8050.1.11) --- src/widgets/sp-color-scales.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/widgets/sp-color-scales.cpp b/src/widgets/sp-color-scales.cpp index cf06247e7..a09a3a2ed 100644 --- a/src/widgets/sp-color-scales.cpp +++ b/src/widgets/sp-color-scales.cpp @@ -10,6 +10,7 @@ #include #include "../dialogs/dialog-events.h" #include "sp-color-scales.h" +#include "svg/svg-icc-color.h" #define CSC_CHANNEL_R (1 << 0) #define CSC_CHANNEL_G (1 << 1) @@ -241,6 +242,10 @@ void ColorScales::_recalcColor( gboolean changing ) g_warning ("file %s: line %d: Illegal color selector mode %d", __FILE__, __LINE__, _mode); break; } + + /* Preserve ICC */ + color.icc = _color.icc ? new SVGICCColor(*_color.icc) : 0; + _updateInternals( color, alpha, _dragging ); } else -- cgit v1.2.3 From dd9b154edc3ee479a3932ae051498389e331812e Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Fri, 14 Aug 2009 06:34:14 +0000 Subject: Initial draft of the out-of-gamut check. It is not working yet. I will continue work on it tomorrow. (bzr r8050.1.12) --- src/widgets/sp-color-notebook.cpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'src') diff --git a/src/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp index 44de40115..5eb44c2cb 100644 --- a/src/widgets/sp-color-notebook.cpp +++ b/src/widgets/sp-color-notebook.cpp @@ -32,6 +32,10 @@ #include "sp-color-scales.h" #include "sp-color-icc-selector.h" #include "sp-color-wheel-selector.h" +#include "svg/svg-icc-color.h" +#include "../inkscape.h" +#include "../document.h" +#include "../profile-manager.h" struct SPColorNotebookTracker { const gchar* name; @@ -504,6 +508,35 @@ void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha ) /* update color management icon*/ gtk_widget_set_sensitive (_box_colormanaged, color.icc != NULL); + /* update out-of-gamut icon */ + gtk_widget_set_sensitive (_box_outofgamut, false); + if (color.icc){ + BYTE outofgamut; + cmsSetAlarmCodes(255, 255, 255); + static cmsHPROFILE hNULL = cmsCreateNULLProfile(); + static cmsHPROFILE hsRGB = cmsCreate_sRGBProfile(); + + cmsHTRANSFORM trans; + Inkscape::ColorProfile* target_profile = SP_ACTIVE_DOCUMENT->profileManager->find(color.icc->colorProfile.c_str()); + if ( target_profile ) { + trans = cmsCreateProofingTransform(hsRGB, TYPE_RGB_DBL, hNULL, TYPE_GRAY_8, target_profile->profHandle, + INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, (cmsFLAGS_GAMUTCHECK|cmsFLAGS_SOFTPROOFING)); + + if ( trans ) { + guint32 val = color.toRGBA32(0); + guchar check_color[4] = { + SP_RGBA32_R_U(val), + SP_RGBA32_G_U(val), + SP_RGBA32_B_U(val), + 255}; + + cmsDoTransform(trans, &check_color, &outofgamut, 1); + gtk_widget_set_sensitive (_box_outofgamut, outofgamut == 255); + } + } + } + + if ( !_updatingrgba ) { gchar s[32]; -- cgit v1.2.3 From ffbf2cca9e54383050bf9bdfbef82ec4b8ccd0ad Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Sat, 15 Aug 2009 05:25:49 +0000 Subject: refactoring ICC out-of-gamut check (bzr r8050.1.13) --- src/widgets/sp-color-notebook.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp index 5eb44c2cb..52c78ba42 100644 --- a/src/widgets/sp-color-notebook.cpp +++ b/src/widgets/sp-color-notebook.cpp @@ -511,15 +511,13 @@ void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha ) /* update out-of-gamut icon */ gtk_widget_set_sensitive (_box_outofgamut, false); if (color.icc){ - BYTE outofgamut; - cmsSetAlarmCodes(255, 255, 255); + BYTE outofgamut = 0; static cmsHPROFILE hNULL = cmsCreateNULLProfile(); static cmsHPROFILE hsRGB = cmsCreate_sRGBProfile(); - cmsHTRANSFORM trans; Inkscape::ColorProfile* target_profile = SP_ACTIVE_DOCUMENT->profileManager->find(color.icc->colorProfile.c_str()); if ( target_profile ) { - trans = cmsCreateProofingTransform(hsRGB, TYPE_RGB_DBL, hNULL, TYPE_GRAY_8, target_profile->profHandle, + cmsHTRANSFORM trans = cmsCreateProofingTransform(hsRGB, TYPE_RGBA_8, hNULL, TYPE_GRAY_8, target_profile->profHandle, INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, (cmsFLAGS_GAMUTCHECK|cmsFLAGS_SOFTPROOFING)); if ( trans ) { @@ -530,7 +528,11 @@ void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha ) SP_RGBA32_B_U(val), 255}; + int alarm_r, alarm_g, alarm_b; + cmsGetAlarmCodes(&alarm_r, &alarm_g, &alarm_b); + cmsSetAlarmCodes(255, 255, 255); cmsDoTransform(trans, &check_color, &outofgamut, 1); + cmsSetAlarmCodes(alarm_r, alarm_g, alarm_b); gtk_widget_set_sensitive (_box_outofgamut, outofgamut == 255); } } -- cgit v1.2.3 From db87667df0c9068024267a4de6137487904048f2 Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Sat, 15 Aug 2009 05:59:15 +0000 Subject: now gamut check works! (bzr r8050.1.14) --- src/color-profile.cpp | 41 +++++++++++++++++++++++++++++++++++++++ src/color-profile.h | 6 ++++++ src/widgets/sp-color-notebook.cpp | 26 ++----------------------- 3 files changed, 49 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/color-profile.cpp b/src/color-profile.cpp index de9dc6959..5f2364449 100644 --- a/src/color-profile.cpp +++ b/src/color-profile.cpp @@ -13,6 +13,7 @@ #include #include #include "xml/repr.h" +#include "color.h" #include "color-profile.h" #include "color-profile-fns.h" #include "attributes.h" @@ -84,6 +85,15 @@ cmsHPROFILE ColorProfile::getSRGBProfile() { return _sRGBProf; } +cmsHPROFILE ColorProfile::_NullProf = 0; + +cmsHPROFILE ColorProfile::getNULLProfile() { + if ( !_NullProf ) { + _NullProf = cmsCreateNULLProfile(); + } + return _NullProf; +} + #endif // ENABLE_LCMS /** @@ -144,6 +154,7 @@ void ColorProfile::init( ColorProfile *cprof ) cprof->_profileSpace = icSigRgbData; cprof->_transf = 0; cprof->_revTransf = 0; + cprof->_gamutTransf = 0; #endif // ENABLE_LCMS } @@ -197,6 +208,10 @@ void ColorProfile::_clearProfile() cmsDeleteTransform( _revTransf ); _revTransf = 0; } + if ( _gamutTransf ) { + cmsDeleteTransform( _gamutTransf ); + _gamutTransf = 0; + } if ( profHandle ) { cmsCloseProfile( profHandle ); profHandle = 0; @@ -496,6 +511,32 @@ cmsHTRANSFORM ColorProfile::getTransfFromSRGB8() return _revTransf; } +cmsHTRANSFORM ColorProfile::getTransfGamutCheck() +{ + if ( !_gamutTransf ) { + _gamutTransf = cmsCreateProofingTransform(getSRGBProfile(), TYPE_RGBA_8, getNULLProfile(), TYPE_GRAY_8, profHandle, INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, (cmsFLAGS_GAMUTCHECK|cmsFLAGS_SOFTPROOFING)); + } + return _gamutTransf; +} + +bool ColorProfile::GamutCheck(SPColor color){ + BYTE outofgamut = 0; + + guint32 val = color.toRGBA32(0); + guchar check_color[4] = { + SP_RGBA32_R_U(val), + SP_RGBA32_G_U(val), + SP_RGBA32_B_U(val), + 255}; + + int alarm_r, alarm_g, alarm_b; + cmsGetAlarmCodes(&alarm_r, &alarm_g, &alarm_b); + cmsSetAlarmCodes(255, 255, 255); + cmsDoTransform(ColorProfile::getTransfGamutCheck(), &check_color, &outofgamut, 1); + cmsSetAlarmCodes(alarm_r, alarm_g, alarm_b); + return (outofgamut == 255); +} + #include diff --git a/src/color-profile.h b/src/color-profile.h index 2e57e7ef0..40d0d7698 100644 --- a/src/color-profile.h +++ b/src/color-profile.h @@ -36,11 +36,15 @@ struct ColorProfile : public SPObject { static std::list getProfileDirs(); #if ENABLE_LCMS static cmsHPROFILE getSRGBProfile(); + static cmsHPROFILE getNULLProfile(); icColorSpaceSignature getColorSpace() const {return _profileSpace;} icProfileClassSignature getProfileClass() const {return _profileClass;} cmsHTRANSFORM getTransfToSRGB8(); cmsHTRANSFORM getTransfFromSRGB8(); + cmsHTRANSFORM getTransfGamutCheck(); + bool GamutCheck(SPColor color); + #endif // ENABLE_LCMS gchar* href; @@ -64,11 +68,13 @@ private: void _clearProfile(); static cmsHPROFILE _sRGBProf; + static cmsHPROFILE _NullProf; icProfileClassSignature _profileClass; icColorSpaceSignature _profileSpace; cmsHTRANSFORM _transf; cmsHTRANSFORM _revTransf; + cmsHTRANSFORM _gamutTransf; #endif // ENABLE_LCMS }; diff --git a/src/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp index 52c78ba42..04a35e4ad 100644 --- a/src/widgets/sp-color-notebook.cpp +++ b/src/widgets/sp-color-notebook.cpp @@ -511,31 +511,9 @@ void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha ) /* update out-of-gamut icon */ gtk_widget_set_sensitive (_box_outofgamut, false); if (color.icc){ - BYTE outofgamut = 0; - static cmsHPROFILE hNULL = cmsCreateNULLProfile(); - static cmsHPROFILE hsRGB = cmsCreate_sRGBProfile(); - Inkscape::ColorProfile* target_profile = SP_ACTIVE_DOCUMENT->profileManager->find(color.icc->colorProfile.c_str()); - if ( target_profile ) { - cmsHTRANSFORM trans = cmsCreateProofingTransform(hsRGB, TYPE_RGBA_8, hNULL, TYPE_GRAY_8, target_profile->profHandle, - INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, (cmsFLAGS_GAMUTCHECK|cmsFLAGS_SOFTPROOFING)); - - if ( trans ) { - guint32 val = color.toRGBA32(0); - guchar check_color[4] = { - SP_RGBA32_R_U(val), - SP_RGBA32_G_U(val), - SP_RGBA32_B_U(val), - 255}; - - int alarm_r, alarm_g, alarm_b; - cmsGetAlarmCodes(&alarm_r, &alarm_g, &alarm_b); - cmsSetAlarmCodes(255, 255, 255); - cmsDoTransform(trans, &check_color, &outofgamut, 1); - cmsSetAlarmCodes(alarm_r, alarm_g, alarm_b); - gtk_widget_set_sensitive (_box_outofgamut, outofgamut == 255); - } - } + if ( target_profile ) + gtk_widget_set_sensitive (_box_outofgamut, target_profile->GamutCheck(color)); } -- cgit v1.2.3 From d27412532cd40717abd78e0b14e9cfc45dd0a4be Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Mon, 17 Aug 2009 20:56:03 +0000 Subject: adding iffdefs for LCMS and fixing layout of CMS icons (bzr r8050.1.15) --- src/widgets/sp-color-notebook.cpp | 15 ++++++++++----- src/widgets/sp-color-notebook.h | 2 ++ 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp index 04a35e4ad..be41f5f0f 100644 --- a/src/widgets/sp-color-notebook.cpp +++ b/src/widgets/sp-color-notebook.cpp @@ -329,6 +329,8 @@ void ColorNotebook::init() row++; GtkWidget *rgbabox = gtk_hbox_new (FALSE, 0); + +#if ENABLE_LCMS /* Create color management icons */ _box_colormanaged = gtk_event_box_new (); GtkWidget *colormanaged = gtk_image_new_from_icon_name ("color-management-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); @@ -336,7 +338,7 @@ void ColorNotebook::init() GtkTooltips *tooltips_colormanaged = gtk_tooltips_new (); gtk_tooltips_set_tip (tooltips_colormanaged, _box_colormanaged, _("Color Managed"), ""); gtk_widget_set_sensitive (_box_colormanaged, false); - gtk_box_pack_start(GTK_BOX(rgbabox), _box_colormanaged, TRUE, FALSE, 2); + gtk_box_pack_start(GTK_BOX(rgbabox), _box_colormanaged, FALSE, FALSE, 2); _box_outofgamut = gtk_event_box_new (); GtkWidget *outofgamut = gtk_image_new_from_icon_name ("out-of-gamut-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); @@ -344,7 +346,9 @@ void ColorNotebook::init() GtkTooltips *tooltips_outofgamut = gtk_tooltips_new (); gtk_tooltips_set_tip (tooltips_outofgamut, _box_outofgamut, _("Out of gamut!"), ""); gtk_widget_set_sensitive (_box_outofgamut, false); - gtk_box_pack_start(GTK_BOX(rgbabox), _box_outofgamut, TRUE, FALSE, 2); + gtk_box_pack_start(GTK_BOX(rgbabox), _box_outofgamut, FALSE, FALSE, 2); + +#endif //ENABLE_LCMS /* Create RGBA entry and color preview */ _rgbal = gtk_label_new_with_mnemonic (_("RGBA_:")); @@ -361,7 +365,7 @@ void ColorNotebook::init() sp_set_font_size_smaller (rgbabox); gtk_widget_show_all (rgbabox); - gtk_table_attach (GTK_TABLE (table), rgbabox, 1, 2, row, row + 1, GTK_FILL, GTK_SHRINK, XPAD, YPAD); + gtk_table_attach (GTK_TABLE (table), rgbabox, 0, 2, row, row + 1, GTK_FILL, GTK_SHRINK, XPAD, YPAD); #ifdef SPCS_PREVIEW _p = sp_color_preview_new (0xffffffff); @@ -504,7 +508,8 @@ void ColorNotebook::_rgbaEntryChanged(GtkEntry* entry) void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha ) { g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); - + +#if ENABLE_LCMS /* update color management icon*/ gtk_widget_set_sensitive (_box_colormanaged, color.icc != NULL); @@ -515,7 +520,7 @@ void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha ) if ( target_profile ) gtk_widget_set_sensitive (_box_outofgamut, target_profile->GamutCheck(color)); } - +#endif //ENABLE_LCMS if ( !_updatingrgba ) { diff --git a/src/widgets/sp-color-notebook.h b/src/widgets/sp-color-notebook.h index c9ab29798..b7cd8295f 100644 --- a/src/widgets/sp-color-notebook.h +++ b/src/widgets/sp-color-notebook.h @@ -61,7 +61,9 @@ protected: gulong _entryId; GtkWidget *_book; GtkWidget *_rgbal, *_rgbae; /* RGBA entry */ +#if ENABLE_LCMS GtkWidget *_box_outofgamut, *_box_colormanaged; +#endif //ENABLE_LCMS GtkWidget *_p; /* Color preview */ GtkWidget *_btn; GtkWidget *_popup; -- cgit v1.2.3 From db07b2c21c8558866713ff8c81629741955283d5 Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Mon, 16 Nov 2009 20:42:17 +0000 Subject: Marking the tests as failing. (bzr r8833.1.2) --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 63b27398a..30d1e5f42 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -266,7 +266,7 @@ check_PROGRAMS = cxxtests # List of all tests to be run. TESTS = $(check_PROGRAMS) ../share/extensions/test/run-all-extension-tests -# XFAIL_TESTS = $(check_PROGRAMS) ../share/extensions/test/run-all-extension-tests +XFAIL_TESTS = $(check_PROGRAMS) ../share/extensions/test/run-all-extension-tests # including the the testsuites here ensures that they get distributed cxxtests_SOURCES = cxxtests.cpp libnr/nr-compose-reference.cpp $(CXXTEST_TESTSUITES) -- cgit v1.2.3 From 8c9f8317c6c8b86eaee8af1afb05e9dee47479a4 Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Sun, 29 Nov 2009 13:25:55 -0600 Subject: Updated version files that were missed. (bzr r8833.1.9) --- src/inkscape.rc | 10 +++++----- src/inkview.rc | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/inkscape.rc b/src/inkscape.rc index d48b68c43..3b946a067 100644 --- a/src/inkscape.rc +++ b/src/inkscape.rc @@ -3,8 +3,8 @@ APPLICATION_ICON ICON DISCARDABLE "../inkscape.ico" 1 24 DISCARDABLE "./inkscape-manifest.xml" 1 VERSIONINFO - FILEVERSION 0,46,0,0 - PRODUCTVERSION 0,46,0,0 + FILEVERSION 0,47,0,0 + PRODUCTVERSION 0,47,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -13,11 +13,11 @@ BEGIN VALUE "Comments", "Published under the GNU GPL" VALUE "CompanyName", "inkscape.org" VALUE "FileDescription", "Inkscape" - VALUE "FileVersion", "0.46.0" + VALUE "FileVersion", "0.47" VALUE "InternalName", "Inkscape" - VALUE "LegalCopyright", "© 2007 Inkscape" + VALUE "LegalCopyright", "© 2009 Inkscape" VALUE "ProductName", "Inkscape" - VALUE "ProductVersion", "0.46.0" + VALUE "ProductVersion", "0.47" END END BLOCK "VarFileInfo" diff --git a/src/inkview.rc b/src/inkview.rc index 9f643eb4c..7a02bf11a 100644 --- a/src/inkview.rc +++ b/src/inkview.rc @@ -3,8 +3,8 @@ APPLICATION_ICON ICON DISCARDABLE "../inkscape.ico" 1 24 DISCARDABLE "./inkview-manifest.xml" 1 VERSIONINFO - FILEVERSION 0,46,0,0 - PRODUCTVERSION 0,46,0,0 + FILEVERSION 0,47,0,0 + PRODUCTVERSION 0,47,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -13,11 +13,11 @@ BEGIN VALUE "Comments", "Published under the GNU GPL" VALUE "CompanyName", "inkscape.org" VALUE "FileDescription", "Inkview" - VALUE "FileVersion", "0.46.0" + VALUE "FileVersion", "0.47" VALUE "InternalName", "Inkview" - VALUE "LegalCopyright", "© 2007 Inkscape" + VALUE "LegalCopyright", "© 2009 Inkscape" VALUE "ProductName", "Inkview" - VALUE "ProductVersion", "0.46.0" + VALUE "ProductVersion", "0.47" END END BLOCK "VarFileInfo" -- cgit v1.2.3 From ce44c8bbdf0be872a572078874880af6540d3f4d Mon Sep 17 00:00:00 2001 From: Nicolas Dufour Date: Mon, 30 Nov 2009 21:05:43 +0100 Subject: Align and distribute tooltip string fix (Bug #442214 Fixed bugs: - https://launchpad.net/bugs/442214 (bzr r8849) --- src/ui/dialog/align-and-distribute.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ui/dialog/align-and-distribute.cpp b/src/ui/dialog/align-and-distribute.cpp index a54f83758..024d4b2f1 100644 --- a/src/ui/dialog/align-and-distribute.cpp +++ b/src/ui/dialog/align-and-distribute.cpp @@ -807,7 +807,7 @@ AlignAndDistribute::AlignAndDistribute() _("Align left edges"), 0, 1); addAlignButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_CENTER, - _("Center objects horizontally"), + _("Center on vertical axis"), 0, 2); addAlignButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_RIGHT, _("Align right sides"), -- cgit v1.2.3 From c8e6bae8d1b9945a60bfba261ad3a299b0b93e42 Mon Sep 17 00:00:00 2001 From: Josh Andler Date: Mon, 30 Nov 2009 12:53:48 -0800 Subject: Spray Tool by the students at Ecole Centrale de Lyon, Lyon, France (bzr r8851) --- src/Makefile_insert | 1 + src/pixmaps/cursor-spray-move.xpm | 38 + src/pixmaps/cursor-spray.xpm | 38 + src/preferences-skeleton.h | 1 + src/spray-context.cpp | 1199 ++++++++++++++++++++++++++++++++ src/spray-context.h | 125 ++++ src/tools-switch.cpp | 8 + src/tools-switch.h | 1 + src/ui/dialog/Makefile_insert | 2 + src/ui/dialog/dialog-manager.cpp | 6 +- src/ui/dialog/inkscape-preferences.cpp | 6 + src/ui/dialog/inkscape-preferences.h | 3 +- src/ui/dialog/spray-option.cpp | 381 ++++++++++ src/ui/dialog/spray-option.h | 130 ++++ src/ui/icon-names.h | 10 + src/ui/view/edit-widget.cpp | 6 + src/ui/view/edit-widget.h | 1 + src/verbs.cpp | 16 + src/verbs.h | 3 + src/widgets/toolbox.cpp | 214 ++++++ 20 files changed, 2186 insertions(+), 3 deletions(-) create mode 100644 src/pixmaps/cursor-spray-move.xpm create mode 100644 src/pixmaps/cursor-spray.xpm create mode 100644 src/spray-context.cpp create mode 100644 src/spray-context.h create mode 100644 src/ui/dialog/spray-option.cpp create mode 100644 src/ui/dialog/spray-option.h (limited to 'src') diff --git a/src/Makefile_insert b/src/Makefile_insert index de986ca16..18beaec37 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -222,6 +222,7 @@ ink_common_sources += \ sp-tspan.cpp sp-tspan.h \ sp-use.cpp sp-use.h \ sp-use-reference.cpp sp-use-reference.h \ + spray-context.cpp spray-context.h \ star-context.cpp star-context.h \ streq.h \ strneq.h \ diff --git a/src/pixmaps/cursor-spray-move.xpm b/src/pixmaps/cursor-spray-move.xpm new file mode 100644 index 000000000..ad898b3a5 --- /dev/null +++ b/src/pixmaps/cursor-spray-move.xpm @@ -0,0 +1,38 @@ +/* XPM */ +static char const * cursor_spray_move_xpm[] = { +"32 32 3 1", +" c None", +". c #FFFFFF", +"+ c #000000", +" ... ", +" .+. ", +" .+. ", +"....+.... ", +".+++ +++. ", +"....+.... ", +" .+. ", +" .+. ", +" ... ", +" ... ... ... ... ", +" ..+. ..+. ..+. ..+. ", +" .+++ .+++ .+++ .+++ ", +" ..+. .+++ .+++ ..+ ", +" .... .+++ .+++ .... ", +" ..+. ..+ ..+ ..+. ", +" .+++ ..+. ..+. .+++ ", +" ..+ .+++ .+++ ..+ ", +" .... ..+ ..+ .... ", +" ..+. .... .... ..+. ", +" .+++ ..+. ..+. .+++ ", +" ..+ .+++ .+++ ..+ ", +" ..+ ..+ ", +" ... ... ", +" ..+. ..+. ", +" .+++ .+++ ", +" ..+ ..+ ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/src/pixmaps/cursor-spray.xpm b/src/pixmaps/cursor-spray.xpm new file mode 100644 index 000000000..9ccefee4f --- /dev/null +++ b/src/pixmaps/cursor-spray.xpm @@ -0,0 +1,38 @@ +/* XPM */ +static char const * cursor_spray_xpm[] = { +"32 32 3 1", +" c None", +". c #FFFFFF", +"+ c #000000", +" ... ", +" .+. ", +" .+. ", +"....+.... ", +".+++ +++. ", +"....+.... ", +" .+. ", +" .+. ", +" ... .+. +. ", +" .+.+.+.+. ", +" .+...+...+. ", +" +...+.....+. ", +" . . . .+.+.......+. ", +" + + + .+.........+. ", +" . . . .+...........+. ", +" + + .+.............+. ", +" . . . .+.............+. ", +" + + + .+.............+. ", +" . . .+.............+. ", +" .+.............+. ", +" .+.............+. ", +" .+...........+. ", +" .+.........+. ", +" .+.......+. ", +" .+.....+. ", +" .+...+. ", +" .+.+. ", +" .+. ", +" . ", +" ", +" ", +" "}; diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index 6185ff729..80a1bd5ea 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -114,6 +114,7 @@ static char const preferences_skeleton[] = " style=\"fill:black;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;font-style:normal;font-weight:normal;font-size:40px;\" selcue=\"1\"/>\n" " \n" " \n" +" \n" " \n" " \n" " \n" diff --git a/src/spray-context.cpp b/src/spray-context.cpp new file mode 100644 index 000000000..71dc9648a --- /dev/null +++ b/src/spray-context.cpp @@ -0,0 +1,1199 @@ +#define __SP_SPRAY_CONTEXT_C__ + +/* + * Spray Tool + * + * Authors: + * Pierre-Antoine MARC + * Pierre CACLIN + * Aurel-Aimé MARMION + * Julien LERAY + * Benoît LAVORATA + * Vincent MONTAGNE + * Pierre BARBRY-BLOT + * + * Copyright (C) 2009 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "config.h" + +#include +#include +#include + +#include + +#include "svg/svg.h" +#include "display/canvas-bpath.h" + +#include +#include "macros.h" +#include "document.h" +#include "selection.h" +#include "desktop.h" +#include "desktop-events.h" +#include "desktop-handles.h" +#include "unistd.h" +#include "desktop-style.h" +#include "message-context.h" +#include "pixmaps/cursor-spray.xpm" +#include "pixmaps/cursor-spray-move.xpm" +#include "pixmaps/cursor-thin.xpm" +#include "pixmaps/cursor-thicken.xpm" +#include "pixmaps/cursor-attract.xpm" +#include "pixmaps/cursor-repel.xpm" +#include "pixmaps/cursor-push.xpm" +#include "pixmaps/cursor-roughen.xpm" +#include "pixmaps/cursor-color.xpm" +#include +#include "libnr/nr-matrix-ops.h" +#include "libnr/nr-scale-translate-ops.h" +#include "xml/repr.h" +#include "context-fns.h" +#include "sp-item.h" +#include "inkscape.h" +#include "color.h" +#include "svg/svg-color.h" +#include "splivarot.h" +#include "sp-item-group.h" +#include "sp-shape.h" +#include "sp-path.h" +#include "path-chemistry.h" +#include "sp-gradient.h" +#include "sp-stop.h" +#include "sp-stop-fns.h" +#include "sp-gradient-reference.h" +#include "sp-linear-gradient.h" +#include "sp-radial-gradient.h" +#include "gradient-chemistry.h" +#include "sp-text.h" +#include "sp-flowtext.h" +#include "display/canvas-bpath.h" +#include "display/canvas-arena.h" +#include "display/curve.h" +#include "livarot/Shape.h" +#include <2geom/isnan.h> +#include <2geom/transforms.h> +#include "preferences.h" +#include "style.h" +#include "box3d.h" +#include "sp-item-transform.h" +#include "filter-chemistry.h" +#include "sp-gaussian-blur-fns.h" +#include "sp-gaussian-blur.h" + +#include "spray-context.h" +#include "ui/dialog/dialog-manager.h" +#include "helper/action.h" + +#include +using namespace std; + + +#define DDC_RED_RGBA 0xff0000ff + +#define DYNA_MIN_WIDTH 1.0e-6 + +static void sp_spray_context_class_init(SPSprayContextClass *klass); +static void sp_spray_context_init(SPSprayContext *ddc); +static void sp_spray_context_dispose(GObject *object); + +static void sp_spray_context_setup(SPEventContext *ec); +static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val); +static gint sp_spray_context_root_handler(SPEventContext *ec, GdkEvent *event); + +static SPEventContextClass *parent_class; + + + +// The following code implements NormalDistribution wich is used for the density of the spray + + +/* + RAND is a macro which returns a pseudo-random numbers from a uniform + distribution on the interval [0 1] +*/ +#define RAND ((double) rand())/((double) RAND_MAX) + +/* + TWOPI = 2.0*pi +*/ +#define TWOPI 2.0*3.141592653589793238462643383279502884197169399375 + +/* + RANDN is a macro which returns a pseudo-random numbers from a normal + distribution with mean zero and standard deviation one. This macro uses Box + Muller's algorithm +*/ +#define RANDN sqrt(-2.0*log(RAND))*cos(TWOPI*RAND) + + +double NormalDistribution(double mu,double sigma) +{ +/* + This function returns a pseudo-random numbers from a normal distribution with + mean equal at mu and standard deviation equal at sigma > 0 +*/ + + return (mu+sigma*RANDN); + +} + +//Fin de la création de NormalDistribution + +GtkType +sp_spray_context_get_type(void) +{ + static GType type = 0; + if (!type) { + GTypeInfo info = { + sizeof(SPSprayContextClass), + NULL, NULL, + (GClassInitFunc) sp_spray_context_class_init, + NULL, NULL, + sizeof(SPSprayContext), + 4, + (GInstanceInitFunc) sp_spray_context_init, + NULL, /* value_table */ + }; + type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPSprayContext", &info, (GTypeFlags)0); + } + return type; +} + +static void +sp_spray_context_class_init(SPSprayContextClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + SPEventContextClass *event_context_class = (SPEventContextClass *) klass; + + parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass); + + object_class->dispose = sp_spray_context_dispose; + + event_context_class->setup = sp_spray_context_setup; + event_context_class->set = sp_spray_context_set; + event_context_class->root_handler = sp_spray_context_root_handler; +} +/*Method to rotate items*/ +void +sp_spray_rotate_rel(Geom::Point c,SPDesktop *desktop,SPItem *item, Geom::Rotate const &rotation) +{ + + Geom::Point center = c; + Geom::Translate const s(c); + Geom::Matrix affine = Geom::Matrix(s).inverse() * Geom::Matrix(rotation) * Geom::Matrix(s); + + // Rotate item. + sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * (Geom::Matrix)affine); + // Use each item's own transform writer, consistent with sp_selection_apply_affine() + sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform); + + // Restore the center position (it's changed because the bbox center changed) + if (item->isCenterSet()) { + item->setCenter(c); + item->updateRepr(); + } +} +/*Method to scale items*/ +void +sp_spray_scale_rel (Geom::Point c, SPDesktop *desktop, SPItem *item, Geom::Scale const &scale) +{ + Geom::Translate const s(c); + + + sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * s.inverse() * scale * s ); + sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform); + + +} +static void +sp_spray_context_init(SPSprayContext *tc) +{ + SPEventContext *event_context = SP_EVENT_CONTEXT(tc); + + + + event_context->cursor_shape = cursor_spray_xpm; + event_context->hot_x = 4; + event_context->hot_y = 4; + + /* attributes */ + tc->dragging = FALSE; + tc->distrib = 1; + tc->width = 0.2; + tc->force = 0.2; + tc->ratio = 0; + tc->tilt=0; + tc->mean = 0.2; + tc->rot_min=0; + tc->rot_max=0; + tc->standard_deviation=0.2; + tc->scale=1; + tc->scale_min = 1; + tc->scale_max=1; + tc->pressure = TC_DEFAULT_PRESSURE; + + tc->is_dilating = false; + tc->has_dilated = false; + + tc->do_h = true; + tc->do_s = true; + tc->do_l = true; + tc->do_o = false; + + new (&tc->style_set_connection) sigc::connection(); +} + +static void +sp_spray_context_dispose(GObject *object) +{ + SPSprayContext *tc = SP_SPRAY_CONTEXT(object); + + tc->style_set_connection.disconnect(); + tc->style_set_connection.~connection(); + + if (tc->dilate_area) { + gtk_object_destroy(GTK_OBJECT(tc->dilate_area)); + tc->dilate_area = NULL; + } + + if (tc->_message_context) { + delete tc->_message_context; + } + + G_OBJECT_CLASS(parent_class)->dispose(object); +} + +bool is_transform_modes (gint mode) +{ + return (mode == SPRAY_MODE_COPY || + mode == SPRAY_MODE_CLONE || + mode == SPRAY_MODE_SINGLE_PATH || + mode == SPRAY_OPTION); +} + +void +sp_spray_update_cursor (SPSprayContext *tc, bool with_shift) +{ + SPEventContext *event_context = SP_EVENT_CONTEXT(tc); + SPDesktop *desktop = event_context->desktop; + + guint num = 0; + gchar *sel_message = NULL; + if (!desktop->selection->isEmpty()) { + num = g_slist_length((GSList *) desktop->selection->itemList()); + sel_message = g_strdup_printf(ngettext("%i object selected","%i objects selected",num), num); + } else { + sel_message = g_strdup_printf(_("Nothing selected")); + } + + + switch (tc->mode) { + case SPRAY_MODE_COPY: + tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray copies of the initial selection"), sel_message); + break; + case SPRAY_MODE_CLONE: + tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray clones of the initial selection"), sel_message); + break; + case SPRAY_MODE_SINGLE_PATH: + tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray in a single path of the initial selection"), sel_message); + break; + case SPRAY_OPTION: + tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Modify spray options"), sel_message); + break; + } + sp_event_context_update_cursor(event_context); + g_free(sel_message); +} + +static void +sp_spray_context_setup(SPEventContext *ec) +{ + SPSprayContext *tc = SP_SPRAY_CONTEXT(ec); + + if (((SPEventContextClass *) parent_class)->setup) + ((SPEventContextClass *) parent_class)->setup(ec); + + { + /* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */ + SPCurve *c = new SPCurve(); + const double C1 = 0.552; + c->moveto(-1,0); + c->curveto(-1, C1, -C1, 1, 0, 1 ); + c->curveto(C1, 1, 1, C1, 1, 0 ); + c->curveto(1, -C1, C1, -1, 0, -1 ); + c->curveto(-C1, -1, -1, -C1, -1, 0 ); + c->closepath(); + tc->dilate_area = sp_canvas_bpath_new(sp_desktop_controls(ec->desktop), c); + c->unref(); + sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(tc->dilate_area), 0x00000000,(SPWindRule)0); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(tc->dilate_area), 0xff9900ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_item_hide(tc->dilate_area); + } + + tc->is_drawing = false; + + tc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack()); + + sp_event_context_read(ec, "distrib"); + sp_event_context_read(ec, "width"); + sp_event_context_read(ec, "ratio"); + sp_event_context_read(ec, "tilt"); + sp_event_context_read(ec, "rot_min"); + sp_event_context_read(ec, "rot_max"); + sp_event_context_read(ec, "scale_min"); + sp_event_context_read(ec, "scale_max"); + sp_event_context_read(ec, "mode"); + sp_event_context_read(ec, "population"); + sp_event_context_read(ec, "force"); + sp_event_context_read(ec, "mean"); + sp_event_context_read(ec, "standard_deviation"); + sp_event_context_read(ec, "usepressure"); + sp_event_context_read(ec, "Rotation min"); + sp_event_context_read(ec, "Rotation max"); + sp_event_context_read(ec, "Scale"); + sp_event_context_read(ec, "doh"); + sp_event_context_read(ec, "dol"); + sp_event_context_read(ec, "dos"); + sp_event_context_read(ec, "doo"); + + ; + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + if (prefs->getBool("/tools/spray/selcue")) { + ec->enableSelectionCue(); + } + + if (prefs->getBool("/tools/spray/gradientdrag")) { + ec->enableGrDrag(); + } +} + +static void +sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val) +{ + SPSprayContext *tc = SP_SPRAY_CONTEXT(ec); + Glib::ustring path = val->getEntryName(); + + if (path == "width") { + tc->width = CLAMP(val->getDouble(0.1), -1000.0, 1000.0); + } else if (path == "mode") { + tc->mode = val->getInt(); + sp_spray_update_cursor(tc, false); + } else if (path == "distribution") { + tc->distrib = val->getInt(1); + } else if (path == "population") { + tc->population = CLAMP(val->getDouble(), 0.0, 1.0); + } else if (path == "tilt") { + tc->tilt = CLAMP(val->getDouble(0.1), 0, 1000.0); + } else if (path == "ratio") { + tc->ratio = CLAMP(val->getDouble(), 0.0, 0.9); + } else if (path == "force") { + tc->force = CLAMP(val->getDouble(1.0), 0, 1.0); + } else if (path == "rot_min") { + tc->rot_min = CLAMP(val->getDouble(0), 0, 10.0); + } else if (path == "rot_max") { + tc->rot_max = CLAMP(val->getDouble(0), 0, 10.0); + } else if (path == "scale_min") { + tc->scale_min = CLAMP(val->getDouble(1.0), 0, 10.0); + } else if (path == "scale_max") { + tc->scale_max = CLAMP(val->getDouble(1.0), 0, 10.0); + } else if (path == "mean") { + tc->mean = CLAMP(val->getDouble(1.0), 0, 1.0); + } else if (path == "standard_deviation") { + tc->standard_deviation = CLAMP(val->getDouble(1.0), 0, 1.0); + } else if (path == "usepressure") { + tc->usepressure = val->getBool(); + } else if (path == "doh") { + tc->do_h = val->getBool(); + } else if (path == "dos") { + tc->do_s = val->getBool(); + } else if (path == "dol") { + tc->do_l = val->getBool(); + } else if (path == "doo") { + tc->do_o = val->getBool(); + } +} + +static void +sp_spray_extinput(SPSprayContext *tc, GdkEvent *event) +{ + if (gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &tc->pressure)) + tc->pressure = CLAMP (tc->pressure, TC_MIN_PRESSURE, TC_MAX_PRESSURE); + else + tc->pressure = TC_DEFAULT_PRESSURE; +} + +double +get_dilate_radius (SPSprayContext *tc) +{ + + return 250 * tc->width/SP_EVENT_CONTEXT(tc)->desktop->current_zoom(); + + +} + +double +get_path_force (SPSprayContext *tc) +{ + double force = 8 * (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE) + /sqrt(SP_EVENT_CONTEXT(tc)->desktop->current_zoom()); + if (force > 3) { + force += 4 * (force - 3); + } + return force * tc->force; +} + +double +get_path_mean (SPSprayContext *tc) +{ + return tc->mean; +} + +double +get_path_standard_deviation (SPSprayContext *tc) +{ + return tc->standard_deviation; +} + +double +get_move_force (SPSprayContext *tc) +{ + double force = (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE); + return force * tc->force; +} + +double +get_move_mean (SPSprayContext *tc) +{ + return tc->mean; +} + +double +get_move_standard_deviation (SPSprayContext *tc) +{ + return tc->standard_deviation; +} + +/* Method to handle the distribution of the items */ + + +void random_position( double &r, double &p, double &a, double &s, int choix) +{ + if (choix == 0) // Mode 1 : uniform repartition + { + r = (1-pow(g_random_double_range(0, 1),2)); + p = g_random_double_range(0, M_PI*2); + } + if (choix == 1) //Mode 0 : gaussian repartition + { + double r_temp =-1; +while(!((r_temp>=0)&&(r_temp<=1))) +{ + r_temp = NormalDistribution(a,s/4); +} +// generates a number following a normal distribution + p = g_random_double_range(0, M_PI*2); + r=r_temp; + /* if (r_temp<=0) r=0; + else + { + if (r_temp>1) r=1; + else r = r_temp; + }*/ + } +} + + + + + +bool +sp_spray_dilate_recursive (SPDesktop *desktop, Inkscape::Selection *selection, SPItem *item, Geom::Point p, Geom::Point vector, gint mode, double radius, double force, double population, double &scale, double scale_min, double scale_max, bool reverse, double mean, double standard_deviation, double ratio,double tilt, double rot_min, double rot_max, gint _distrib ) +{ + + + + bool did = false; + + if (SP_IS_BOX3D(item) /*&& !is_transform_modes(mode)*/) { + // convert 3D boxes to ordinary groups before spraying their shapes + item = SP_ITEM(box3d_convert_to_group(SP_BOX3D(item))); + selection->add(item); + } + +/*if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) { + GSList *items = g_slist_prepend (NULL, item); + GSList *selected = NULL; + GSList *to_select = NULL; + SPDocument *doc = SP_OBJECT_DOCUMENT(item); + sp_item_list_to_curves (items, &selected, &to_select); + g_slist_free (items); + SPObject* newObj = doc->getObjectByRepr((Inkscape::XML::Node *) to_select->data); + g_slist_free (to_select); + item = (SPItem *) newObj; + // selection->add(item); + } +*/ + /*if (SP_IS_GROUP(item) && !SP_IS_BOX3D(item)) { + for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { + if (SP_IS_ITEM(child)) { + if (sp_spray_dilate_recursive (desktop,selection, SP_ITEM(child), p, vector, mode, radius, force, population, scale, scale_min, scale_max, reverse, mean, standard_deviation,ratio,tilt, rot_min, rot_max,_distrib)) + did = true; + } + } + + } else {*/ + if (mode == SPRAY_MODE_COPY) { + + Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item)); + if (a) { + double dr; double dp; + random_position(dr,dp,mean,standard_deviation,_distrib); + dr=dr*radius; + double _fid = g_random_double_range(0,1); + SPItem *item_copied; + double angle = g_random_double_range(rot_min, rot_max); + double _scale = g_random_double_range(scale_min, scale_max); + if(_fid<=population) + { + // duplicate + SPDocument *doc = SP_OBJECT_DOCUMENT(item); + Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc); + Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item); + Inkscape::XML::Node *parent = old_repr->parent(); + Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc); + parent->appendChild(copy); + + SPObject *new_obj = doc->getObjectByRepr(copy); + item_copied = (SPItem *) new_obj; //convertion object->item + Geom::Point center=item->getCenter(); + sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale)); + sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale)); + + sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle)); + Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint());//Move the cursor p + sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y])); + + + + + + did = true; + } + } + + } else if (mode == SPRAY_MODE_SINGLE_PATH) { + + + SPItem *Pere; //Objet initial + SPItem *item_copied;//Objet projeté + SPItem *Union;//Union précédente + SPItem *fils;//Copie du père + + // GSList *items = g_slist_copy((GSList *) selection->itemList()); //Récupère la liste des objects sélectionnés +//Pere = (SPItem *) items->data;//Le premier objet est le père du spray + + int i=1; + for (GSList *items = g_slist_copy((GSList *) selection->itemList()); + items != NULL; + items = items->next) { + + SPItem *item1 = (SPItem *) items->data; + if (i==1) { + Pere=item1; + } + if (i==2) { + Union=item1; + } + i++; + } + SPDocument *doc = SP_OBJECT_DOCUMENT(Pere); + Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc); + Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(Pere); + //SPObject *old_obj = doc->getObjectByRepr(old_repr); + Inkscape::XML::Node *parent = old_repr->parent(); + + Geom::OptRect a = Pere->getBounds(sp_item_i2doc_affine(Pere)); + if (a) { + double dr; double dp; //initialisation des variables + random_position(dr,dp,mean,standard_deviation,_distrib); + dr=dr*radius; + double _fid = g_random_double_range(0,1); + double angle = (g_random_double_range(rot_min, rot_max)); + double _scale = g_random_double_range(scale_min, scale_max); + if (i==2) { + Inkscape::XML::Node *copy1 = old_repr->duplicate(xml_doc); + parent->appendChild(copy1); + SPObject *new_obj1 = doc->getObjectByRepr(copy1); + fils = (SPItem *) new_obj1; //conversion object->item + Union=fils; + Inkscape::GC::release(copy1); + } + + if (_fid<=population) { //Rules the population of objects sprayed + // duplicates the father + Inkscape::XML::Node *copy2 = old_repr->duplicate(xml_doc); + parent->appendChild(copy2); + SPObject *new_obj2 = doc->getObjectByRepr(copy2); + item_copied = (SPItem *) new_obj2; + + Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint());//Move around the cursor + + Geom::Point center=Pere->getCenter(); + sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale)); + sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale)); + sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle)); + sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y])); + +//UNION et surduplication + selection->clear(); + selection->add(item_copied); + selection->add(Union); + sp_selected_path_union(selection->desktop()); + selection->add(Pere); + Inkscape::GC::release(copy2); + did = true; + } + + } + } else if (mode == SPRAY_MODE_CLONE) { + + Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item)); + if (a) { + double dr; double dp; + random_position(dr,dp,mean,standard_deviation,_distrib); + dr=dr*radius; + double _fid = g_random_double_range(0,1); + double angle = (g_random_double_range(rot_min, rot_max)); + double _scale = g_random_double_range(scale_min, scale_max); + + if(_fid<=population) + { + SPItem *item_copied; + SPDocument *doc = SP_OBJECT_DOCUMENT(item); + Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc); + Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item); + Inkscape::XML::Node *parent = old_repr->parent(); + + //Creation of the clone + Inkscape::XML::Node *clone = xml_doc->createElement("svg:use"); + parent->appendChild(clone); //Ajout du clone à la liste d'enfants du père (selection initiale + clone->setAttribute("xlink:href", g_strdup_printf("#%s", old_repr->attribute("id")), false); //Génère le lien entre les attributs du père et du fils + + SPObject *clone_object = doc->getObjectByRepr(clone); + item_copied = (SPItem *) clone_object;//conversion object->item + Geom::Point center=item->getCenter(); + sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale)); + sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale)); + sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle)); + Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint()); + sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y])); + + Inkscape::GC::release(clone); + + did = true; + } }} + return did; + +} + + +bool +sp_spray_color_recursive (guint mode, SPItem *item, SPItem *item_at_point, + guint32 fill_goal, bool do_fill, + guint32 stroke_goal, bool do_stroke, + float opacity_goal, bool do_opacity, + bool do_blur, bool reverse, + Geom::Point p, double radius, double force, + bool do_h, bool do_s, bool do_l, bool do_o) +{ + bool did = false; + + return did; +} + + +bool +sp_spray_dilate (SPSprayContext *tc, Geom::Point event_p, Geom::Point p, Geom::Point vector, bool reverse) +{ + Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(tc)->desktop); + SPDesktop *desktop = SP_EVENT_CONTEXT(tc)->desktop; + + + if (selection->isEmpty()) { + return false; + } + + bool did = false; + double radius = get_dilate_radius(tc); + + + + bool do_fill = false, do_stroke = false, do_opacity = false; + guint32 fill_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", true, &do_fill); + guint32 stroke_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", false, &do_stroke); + double opacity_goal = sp_desktop_get_master_opacity_tool(desktop, "/tools/spray", &do_opacity); + if (reverse) { +#if 0 + // HSL inversion + float hsv[3]; + float rgb[3]; + sp_color_rgb_to_hsv_floatv (hsv, + SP_RGBA32_R_F(fill_goal), + SP_RGBA32_G_F(fill_goal), + SP_RGBA32_B_F(fill_goal)); + sp_color_hsv_to_rgb_floatv (rgb, hsv[0]<.5? hsv[0]+.5 : hsv[0]-.5, 1 - hsv[1], 1 - hsv[2]); + fill_goal = SP_RGBA32_F_COMPOSE(rgb[0], rgb[1], rgb[2], 1); + sp_color_rgb_to_hsv_floatv (hsv, + SP_RGBA32_R_F(stroke_goal), + SP_RGBA32_G_F(stroke_goal), + SP_RGBA32_B_F(stroke_goal)); + sp_color_hsv_to_rgb_floatv (rgb, hsv[0]<.5? hsv[0]+.5 : hsv[0]-.5, 1 - hsv[1], 1 - hsv[2]); + stroke_goal = SP_RGBA32_F_COMPOSE(rgb[0], rgb[1], rgb[2], 1); +#else + // RGB inversion + fill_goal = SP_RGBA32_U_COMPOSE( + (255 - SP_RGBA32_R_U(fill_goal)), + (255 - SP_RGBA32_G_U(fill_goal)), + (255 - SP_RGBA32_B_U(fill_goal)), + (255 - SP_RGBA32_A_U(fill_goal))); + stroke_goal = SP_RGBA32_U_COMPOSE( + (255 - SP_RGBA32_R_U(stroke_goal)), + (255 - SP_RGBA32_G_U(stroke_goal)), + (255 - SP_RGBA32_B_U(stroke_goal)), + (255 - SP_RGBA32_A_U(stroke_goal))); +#endif + opacity_goal = 1 - opacity_goal; + } + + double path_force = get_path_force(tc); + if (radius == 0 || path_force == 0) { + return false; + } + double path_mean = get_path_mean(tc); + if (radius == 0 || path_mean == 0) { + return false; + } + double path_standard_deviation = get_path_standard_deviation(tc); + if (radius == 0 || path_standard_deviation == 0) { + return false; + } + double move_force = get_move_force(tc); + double move_mean = get_move_mean(tc); + double move_standard_deviation = get_move_standard_deviation(tc); + + + for (GSList *items = g_slist_copy((GSList *) selection->itemList()); + items != NULL; + items = items->next) { + + SPItem *item = (SPItem *) items->data; + + /*if (is_color_modes (tc->mode)) { + if (do_fill || do_stroke || do_opacity) { + if (sp_spray_color_recursive (tc->mode, item, item_at_point, + fill_goal, do_fill, + stroke_goal, do_stroke, + opacity_goal, do_opacity, + tc->mode == SPRAY_MODE_BLUR, reverse, + p, radius, color_force, tc->do_h, tc->do_s, tc->do_l, tc->do_o)) + did = true; + } + }else*/ if (is_transform_modes(tc->mode)) { + if (sp_spray_dilate_recursive (desktop,selection, item, p, vector, tc->mode, radius, move_force, tc->population,tc->scale, tc->scale_min, tc->scale_max, reverse, move_mean, move_standard_deviation,tc->ratio,tc->tilt, tc->rot_min, tc->rot_max, tc->distrib)) + did = true; + } else { + if (sp_spray_dilate_recursive (desktop,selection, item, p, vector, tc->mode, radius, path_force, tc->population,tc->scale, tc->scale_min, tc->scale_max, reverse, path_mean, path_standard_deviation,tc->ratio,tc->tilt, tc->rot_min, tc->rot_max, tc->distrib)) + did = true; + } + } + + return did; +} + +void +sp_spray_update_area (SPSprayContext *tc) +{ + double radius = get_dilate_radius(tc); + Geom::Matrix const sm ( Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) ); + sp_canvas_item_affine_absolute(tc->dilate_area, (sm* Geom::Rotate(tc->tilt))* Geom::Translate(SP_EVENT_CONTEXT(tc)->desktop->point())); + sp_canvas_item_show(tc->dilate_area); +} + +void +sp_spray_switch_mode (SPSprayContext *tc, gint mode, bool with_shift) +{ + SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode); //sélectionne le bouton numéro "mode" + // need to set explicitly, because the prefs may not have changed by the previous + tc->mode = mode; + sp_spray_update_cursor (tc, with_shift); +} + +void +sp_spray_switch_mode_temporarily (SPSprayContext *tc, gint mode, bool with_shift) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + // Juggling about so that prefs have the old value but tc->mode and the button show new mode: + gint now_mode = prefs->getInt("/tools/spray/mode", 0); + SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode); + // button has changed prefs, restore + prefs->setInt("/tools/spray/mode", now_mode); + // changing prefs changed tc->mode, restore back :) + tc->mode = mode; + sp_spray_update_cursor (tc, with_shift); +} + +gint +sp_spray_context_root_handler(SPEventContext *event_context, + GdkEvent *event) +{ + SPSprayContext *tc = SP_SPRAY_CONTEXT(event_context); + SPDesktop *desktop = event_context->desktop; + + gint ret = FALSE; + + switch (event->type) { + case GDK_ENTER_NOTIFY: + sp_canvas_item_show(tc->dilate_area); + break; + case GDK_LEAVE_NOTIFY: + sp_canvas_item_hide(tc->dilate_area); + break; + case GDK_BUTTON_PRESS: + if (event->button.button == 1 && !event_context->space_panning) { + + if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false) { + return TRUE; + } + + Geom::Point const motion_w(event->button.x, + event->button.y); + Geom::Point const motion_dt(desktop->w2d(motion_w)); + tc->last_push = desktop->dt2doc(motion_dt); + + sp_spray_extinput(tc, event); + + sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3); + tc->is_drawing = true; + tc->is_dilating = true; + tc->has_dilated = false; + + + + if(tc->is_dilating && event->button.button == 1 && !event_context->space_panning) + + sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT); + + + + tc->has_dilated=true; + + ret = TRUE; + } + break; + case GDK_MOTION_NOTIFY: + { + Geom::Point const motion_w(event->motion.x, + event->motion.y); + Geom::Point motion_dt(desktop->w2d(motion_w)); + Geom::Point motion_doc(desktop->dt2doc(motion_dt)); + sp_spray_extinput(tc, event); + + // draw the dilating cursor + double radius = get_dilate_radius(tc); + Geom::Matrix const sm (Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) ); + sp_canvas_item_affine_absolute(tc->dilate_area, (sm*Geom::Rotate(tc->tilt))*Geom::Translate(desktop->w2d(motion_w))); + sp_canvas_item_show(tc->dilate_area); + + guint num = 0; + if (!desktop->selection->isEmpty()) { + num = g_slist_length((GSList *) desktop->selection->itemList()); + } + if (num == 0) { + tc->_message_context->flash(Inkscape::ERROR_MESSAGE, _("Nothing selected! Select objects to spray.")); + } + + // dilating: + if (tc->is_drawing && ( event->motion.state & GDK_BUTTON1_MASK )) { + sp_spray_dilate (tc, motion_w, motion_doc, motion_doc - tc->last_push, event->button.state & GDK_SHIFT_MASK? true : false); + //tc->last_push = motion_doc; + tc->has_dilated = true; + + // it's slow, so prevent clogging up with events + gobble_motion_events(GDK_BUTTON1_MASK); + return TRUE; + } + + } + break; +/*Spray with the scroll*/ + case GDK_SCROLL: + { + if (event->scroll.state & GDK_BUTTON1_MASK) + { + double temp ; + temp=tc->population; + tc->population=1.0; + desktop->setToolboxAdjustmentValue ("population", tc->population * 100); + Geom::Point const scroll_w(event->button.x,event->button.y); + Geom::Point const scroll_dt = desktop->point();; + Geom::Point motion_doc(desktop->dt2doc(scroll_dt)); + switch (event->scroll.direction) + { + case GDK_SCROLL_UP: + { + if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false) + { + return TRUE; + } + tc->last_push = desktop->dt2doc(scroll_dt); + sp_spray_extinput(tc, event); + sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3); + tc->is_drawing = true; + tc->is_dilating = true; + tc->has_dilated = false; + if(tc->is_dilating && !event_context->space_panning) + + sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0),false); + + + + tc->has_dilated=true; + tc->population=temp; + + desktop->setToolboxAdjustmentValue ("population", tc->population * 100); + + ret = TRUE; + } + break; + case GDK_SCROLL_DOWN: + { + if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false) + { + return TRUE; + } + tc->last_push = desktop->dt2doc(scroll_dt); + sp_spray_extinput(tc, event); + sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3); + tc->is_drawing = true; + tc->is_dilating = true; + tc->has_dilated = false; + if(tc->is_dilating && !event_context->space_panning) + sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0), false); + + tc->has_dilated=true; + + ret = TRUE; + + + } + break; +case GDK_SCROLL_RIGHT: + {} break; +case GDK_SCROLL_LEFT: + {} break; + } + } + + + break; + + } + case GDK_BUTTON_RELEASE: + { + Geom::Point const motion_w(event->button.x, event->button.y); + Geom::Point const motion_dt(desktop->w2d(motion_w)); + + sp_canvas_end_forced_full_redraws(desktop->canvas); + tc->is_drawing = false; + + if (tc->is_dilating && event->button.button == 1 && !event_context->space_panning) { + if (!tc->has_dilated) { + // if we did not rub, do a light tap + tc->pressure = 0.03; + sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT); + } + tc->is_dilating = false; + tc->has_dilated = false; + switch (tc->mode) { + case SPRAY_MODE_COPY: + sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop), + SP_VERB_CONTEXT_SPRAY, _("Spray with copies")); + break; + case SPRAY_MODE_CLONE: + sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop), + SP_VERB_CONTEXT_SPRAY, _("Spray with clones")); + break; + case SPRAY_MODE_SINGLE_PATH: + sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop), + SP_VERB_CONTEXT_SPRAY, _("Spray in single path")); + break; + } + } + break; + } + + case GDK_KEY_PRESS: + switch (get_group0_keyval (&event->key)) { +case GDK_j: if (MOD__SHIFT_ONLY) { + sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT); + ret = TRUE; + } +case GDK_J: if (MOD__SHIFT_ONLY) { + sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT); + ret = TRUE; + } + +break; + case GDK_m: + case GDK_M: + case GDK_0: + + break; + case GDK_i: + case GDK_I: + case GDK_k: if (MOD__SHIFT_ONLY) { + sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT); + ret = TRUE; + } + case GDK_K:if (MOD__SHIFT_ONLY) { + sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT); + ret = TRUE; + } +break; + + case GDK_l: if (MOD__SHIFT_ONLY) { + sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT); + ret = TRUE; + } + + case GDK_L: + if (MOD__SHIFT_ONLY) { + sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT); + ret = TRUE; + } + break; + case GDK_Up: + case GDK_KP_Up: + if (!MOD__CTRL_ONLY) { + tc->scale += 0.05; + + //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100); + ret = TRUE; + } + break; + case GDK_Down: + case GDK_KP_Down: + if (!MOD__CTRL_ONLY) { + + tc->scale -= 0.05; + if (tc->scale < 0.0) + tc->scale = 0.0; + //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100); + + ret = TRUE; + + } + break; + case GDK_Right: + case GDK_KP_Right: + if (!MOD__CTRL_ONLY) { + tc->width += 0.01; + if (tc->width > 1.0) + tc->width = 1.0; + desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100); // the same spinbutton is for alt+x + sp_spray_update_area(tc); + ret = TRUE; + } + break; + case GDK_Left: + case GDK_KP_Left: + if (!MOD__CTRL_ONLY) { + tc->width -= 0.01; + if (tc->width < 0.01) + tc->width = 0.01; + desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100); + sp_spray_update_area(tc); + ret = TRUE; + } + break; + case GDK_Home: + case GDK_KP_Home: + tc->width = 0.01; + desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100); + sp_spray_update_area(tc); + ret = TRUE; + break; + case GDK_End: + case GDK_KP_End: + tc->width = 1.0; + desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100); + sp_spray_update_area(tc); + ret = TRUE; + break; + case GDK_x: + case GDK_X: + if (MOD__ALT_ONLY) { + desktop->setToolboxFocusTo ("altx-spray"); + ret = TRUE; + } + break; + + case GDK_Shift_L: + case GDK_Shift_R: + sp_spray_update_cursor(tc, true); + break; +/*Set the scale to 1*/ + case GDK_Control_L: + tc->scale=1; + default: + break; + } + break; + + case GDK_KEY_RELEASE: { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + switch (get_group0_keyval(&event->key)) { + case GDK_Shift_L: + case GDK_Shift_R: + sp_spray_update_cursor(tc, false); + break; + case GDK_Control_L: + case GDK_Control_R: + sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT); + tc->_message_context->clear(); + break; + default: + sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT); + break; + } + } + + default: + break; + } + + if (!ret) { + if (((SPEventContextClass *) parent_class)->root_handler) { + ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event); + } + } + + return ret; +} + +/* + 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:encoding=utf-8:textwidth=99 : + diff --git a/src/spray-context.h b/src/spray-context.h new file mode 100644 index 000000000..f8822ce39 --- /dev/null +++ b/src/spray-context.h @@ -0,0 +1,125 @@ +#ifndef __SP_SPRAY_CONTEXT_H__ +#define __SP_SPRAY_CONTEXT_H__ + +/* + * Spray Tool + * + * Authors: + * Pierre-Antoine MARC + * Pierre CACLIN + * Aurel-Aimé MARMION + * Julien LERAY + * Benoît LAVORATA + * Vincent MONTAGNE + * Pierre BARBRY-BLOT + * + * Copyright (C) 2009 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "event-context.h" +#include +#include +//#include "ui/widget/spray-option.h" +#include "ui/dialog/dialog.h" + +#define SP_TYPE_SPRAY_CONTEXT (sp_spray_context_get_type()) +#define SP_SPRAY_CONTEXT(o) (GTK_CHECK_CAST((o), SP_TYPE_SPRAY_CONTEXT, SPSprayContext)) +#define SP_SPRAY_CONTEXT_CLASS(k) (GTK_CHECK_CLASS_CAST((k), SP_TYPE_SPRAY_CONTEXT, SPSprayContextClass)) +#define SP_IS_SPRAY_CONTEXT(o) (GTK_CHECK_TYPE((o), SP_TYPE_SPRAY_CONTEXT)) +#define SP_IS_SPRAY_CONTEXT_CLASS(k) (GTK_CHECK_CLASS_TYPE((k), SP_TYPE_SPRAY_CONTEXT)) + +class SPSprayContext; +class SPSprayContextClass; + +namespace Inkscape { + namespace UI { + namespace Dialog { + class Dialog; + } + } +} + + +#define SAMPLING_SIZE 8 /* fixme: ?? */ + +#define TC_MIN_PRESSURE 0.0 +#define TC_MAX_PRESSURE 1.0 +#define TC_DEFAULT_PRESSURE 0.35 + +enum { + SPRAY_MODE_COPY, + SPRAY_MODE_CLONE, + SPRAY_MODE_SINGLE_PATH, + SPRAY_OPTION, +}; + +struct SPSprayContext +{ + SPEventContext event_context; + //Inkscape::UI::Dialog::Dialog *dialog_option;//Attribut de type SprayOptionClass, localisé dans scr/ui/dialog + /* extended input data */ + gdouble pressure; + + /* attributes */ + guint dragging : 1; /* mouse state: mouse is dragging */ + guint usepressure : 1; + guint usetilt : 1; + bool usetext ; + + double width; + double ratio; + double tilt; + double rot_min; + double rot_max; + double force; + double population; + double scale_min; + double scale_max; + double scale; + double mean; + double standard_deviation; + + gint distrib; + + gint mode; + + Inkscape::MessageContext *_message_context; + + bool is_drawing; + + bool is_dilating; + bool has_dilated; + Geom::Point last_push; + SPCanvasItem *dilate_area; + + bool do_h; + bool do_s; + bool do_l; + bool do_o; + + sigc::connection style_set_connection; +}; + +struct SPSprayContextClass +{ + SPEventContextClass parent_class; +}; + +GtkType sp_spray_context_get_type(void); + + +#endif + +/* + 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:encoding=utf-8:textwidth=99 : + diff --git a/src/tools-switch.cpp b/src/tools-switch.cpp index 7902a7988..6c53ce61c 100644 --- a/src/tools-switch.cpp +++ b/src/tools-switch.cpp @@ -28,6 +28,7 @@ #include "select-context.h" #include "node-context.h" #include "tweak-context.h" +#include "spray-context.h" #include "sp-path.h" #include "rect-context.h" #include "sp-rect.h" @@ -62,6 +63,7 @@ static char const *const tool_names[] = { "/tools/select", "/tools/nodes", "/tools/tweak", + "/tools/spray", "/tools/shapes/rect", "/tools/shapes/3dbox", "/tools/shapes/arc", @@ -135,6 +137,12 @@ tools_switch(SPDesktop *dt, int num) inkscape_eventcontext_set(sp_desktop_event_context(dt)); dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("To tweak a path by pushing, select it and drag over it.")); break; + case TOOLS_SPRAY: + dt->set_event_context(SP_TYPE_SPRAY_CONTEXT, tool_names[num]); + dt->activate_guides(true); + inkscape_eventcontext_set(sp_desktop_event_context(dt)); + dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("To spray a path by pushing, select it and drag over it.")); + break; case TOOLS_SHAPES_RECT: dt->set_event_context(SP_TYPE_RECT_CONTEXT, tool_names[num]); dt->activate_guides(false); diff --git a/src/tools-switch.h b/src/tools-switch.h index 36dd8f80b..4cc9aa93d 100644 --- a/src/tools-switch.h +++ b/src/tools-switch.h @@ -19,6 +19,7 @@ enum { TOOLS_SELECT, TOOLS_NODES, TOOLS_TWEAK, + TOOLS_SPRAY, TOOLS_SHAPES_RECT, TOOLS_SHAPES_3DBOX, TOOLS_SHAPES_ARC, diff --git a/src/ui/dialog/Makefile_insert b/src/ui/dialog/Makefile_insert index 565a24ecc..fd1b07394 100644 --- a/src/ui/dialog/Makefile_insert +++ b/src/ui/dialog/Makefile_insert @@ -73,6 +73,8 @@ ink_common_sources += \ ui/dialog/print.h \ ui/dialog/scriptdialog.cpp \ ui/dialog/scriptdialog.h \ + ui/dialog/spray-option.cpp \ + ui/dialog/spray-option.h \ ui/dialog/svg-fonts-dialog.cpp \ ui/dialog/svg-fonts-dialog.h \ ui/dialog/swatches.cpp \ diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index d1b818d23..7f853bedc 100644 --- a/src/ui/dialog/dialog-manager.cpp +++ b/src/ui/dialog/dialog-manager.cpp @@ -40,6 +40,7 @@ #include "ui/dialog/icon-preview.h" #include "ui/dialog/floating-behavior.h" #include "ui/dialog/dock-behavior.h" +#include "ui/dialog/spray-option.h" #include "preferences.h" #ifdef ENABLE_SVG_FONTS @@ -88,7 +89,6 @@ DialogManager::DialogManager() { int dialogs_type = prefs->getIntLimited("/options/dialogtype/value", DOCK, 0, 1); if (dialogs_type == FLOATING) { - registerFactory("AlignAndDistribute", &create); registerFactory("DocumentMetadata", &create); registerFactory("DocumentProperties", &create); @@ -111,7 +111,8 @@ DialogManager::DialogManager() { registerFactory("Trace", &create); registerFactory("Transformation", &create); registerFactory("UndoHistory", &create); - registerFactory("InputDevices", &create); + registerFactory("InputDevices", &create); + registerFactory("SprayOptionClass", &create); } else { @@ -138,6 +139,7 @@ DialogManager::DialogManager() { registerFactory("Transformation", &create); registerFactory("UndoHistory", &create); registerFactory("InputDevices", &create); + registerFactory("SprayOptionClass", &create); } } diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index c7dc789ca..6e30ef61a 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -449,6 +449,12 @@ void InkscapePreferences::initPageTools() AddSelcueCheckbox(_page_tweak, "/tools/tweak", true); AddGradientCheckbox(_page_tweak, "/tools/tweak", false); + //Spray + this->AddPage(_page_spray, _("Spray"), iter_tools, PREFS_PAGE_TOOLS_SPRAY); + this->AddNewObjectsStyle(_page_spray, "/tools/spray", _("Paint objects with:")); + AddSelcueCheckbox(_page_spray, "/tools/spray", true); + AddGradientCheckbox(_page_spray, "/tools/spray", false); + //Zoom this->AddPage(_page_zoom, _("Zoom"), iter_tools, PREFS_PAGE_TOOLS_ZOOM); AddSelcueCheckbox(_page_zoom, "/tools/zoom", true); diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index 364b0eb1d..705e7a352 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -43,6 +43,7 @@ enum { PREFS_PAGE_TOOLS_SELECTOR, PREFS_PAGE_TOOLS_NODE, PREFS_PAGE_TOOLS_TWEAK, + PREFS_PAGE_TOOLS_SPRAY, PREFS_PAGE_TOOLS_ZOOM, PREFS_PAGE_TOOLS_SHAPES, PREFS_PAGE_TOOLS_SHAPES_RECT, @@ -118,7 +119,7 @@ protected: _page_clones, _page_mask, _page_transforms, _page_filters, _page_select, _page_importexport, _page_cms, _page_grids, _page_svgoutput, _page_misc, _page_ui, _page_save, _page_bitmaps, _page_spellcheck; - DialogPage _page_selector, _page_node, _page_tweak, _page_zoom, _page_shapes, _page_pencil, _page_pen, + DialogPage _page_selector, _page_node, _page_tweak, _page_spray, _page_zoom, _page_shapes, _page_pencil, _page_pen, _page_calligraphy, _page_text, _page_gradient, _page_connector, _page_dropper, _page_lpetool; DialogPage _page_rectangle, _page_3dbox, _page_ellipse, _page_star, _page_spiral, _page_paintbucket, _page_eraser; diff --git a/src/ui/dialog/spray-option.cpp b/src/ui/dialog/spray-option.cpp new file mode 100644 index 000000000..8bfe455fa --- /dev/null +++ b/src/ui/dialog/spray-option.cpp @@ -0,0 +1,381 @@ +/*Julien LERAY (julien.leray@ecl2010.ec-lyon.fr), interface for the spray tool*/ + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "desktop-handles.h" +#include "unclump.h" +#include "document.h" +#include "enums.h" +#include "graphlayout/graphlayout.h" +#include "inkscape.h" +#include "macros.h" +#include "node-context.h" +#include "preferences.h" +#include "removeoverlap/removeoverlap.h" +#include "selection.h" +#include "shape-editor.h" +#include "sp-flowtext.h" +#include "sp-item-transform.h" +#include "sp-text.h" +#include "text-editing.h" +#include "tools-switch.h" +#include "ui/icon-names.h" +#include "util/glib-list-iterators.h" +#include "verbs.h" +#include "widgets/icon.h" + +#include "spray-option.h" + +namespace Inkscape { +namespace UI { +namespace Dialog { + + +//Classes qui permettent de créer les environnements Gaussienne, Witdh... + + + +class Action { +public: + Action(const Glib::ustring &id, + const Glib::ustring &tiptext, + guint row, guint column, + Gtk::Table &parent, + Gtk::Tooltips &tooltips, + SprayOptionClass &dialog): + _dialog(dialog), + _id(id), + _parent(parent) {} + + virtual ~Action(){} + virtual void on_button_click(){} + SprayOptionClass &_dialog; + +private : + + Glib::ustring _id; + Gtk::Table &_parent; +}; + +class ActionE : public Action { +private: + Gtk::Label _Label; + Gtk::SpinButton _Gap; + guint _min, _max; + Glib::ustring _pref_path; + +public: + ActionE(const Glib::ustring &id, + const Glib::ustring &tiptext, + guint row, guint column, + SprayOptionClass &dialog, + guint min, guint max, + Glib::ustring const &pref_path ): + Action(id, tiptext, row, column, + dialog._Table(), dialog.tooltips(), dialog), + _min(min), + _max(max), + _pref_path(pref_path) + { + dialog._Table().set_col_spacings(3); + + double increm = ((double)_max - (double)_min)/10; + double val_ini = ((double)_max + (double)_min)/2; + _Gap.set_digits(1); + _Gap.set_size_request(60, -1); + _Gap.set_increments(increm , 0); + _Gap.set_range(_min, _max); + _Gap.set_value(val_ini); + dialog.tooltips().set_tip(_Gap, + tiptext); + _Gap.signal_changed().connect(sigc::mem_fun(*this, &ActionE::on_button_click)); //rajout douteux + _Label.set_label(id); + + dialog._Table().attach(_Label, column, column+1, row, row+1, Gtk::FILL, Gtk::FILL); + dialog._Table().attach(_Gap, column+1, column+2, row, row+1, Gtk::EXPAND, Gtk::EXPAND); + } + + virtual void on_button_click(){ + if (!_dialog.getDesktop()) return; + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + prefs->setDouble(_pref_path, SP_VERB_CONTEXT_SPRAY); + + double const Gap = _Gap.get_value(); + + + prefs->setDouble(_pref_path, Gap); + + sp_document_done(sp_desktop_document(_dialog.getDesktop()), SP_VERB_CONTEXT_SPRAY, + _("Remove overlaps")); + } + + +}; + +class ActionF : public Action { +private: + Gtk::Label _Label; + Gtk::Label _Label1; + Gtk::Label _Label2; + Gtk::SpinButton _Gap1; + Gtk::SpinButton _Gap2; + Glib::ustring _pref1_path; + Glib::ustring _pref2_path; + +public: + ActionF(const Glib::ustring &id, + const Glib::ustring &tiptext, + guint row, guint column, + SprayOptionClass &dialog, + Glib::ustring const &pref1_path, + Glib::ustring const &pref2_path ): + Action(id, tiptext, row, column, + dialog._Table(), dialog.tooltips(), dialog), + _pref1_path(pref1_path), + _pref2_path(pref2_path) + { + dialog.F_Table().set_col_spacings(3); + + _Label.set_label(id); + + _Gap1.set_digits(1); + _Gap1.set_size_request(60, -1); + _Gap1.set_increments(0.1, 0); + _Gap1.set_range(0, 10); + _Gap1.set_value(1); + dialog.tooltips().set_tip(_Gap1, + _("Minimum")); + + _Label1.set_label(Q_("Min")); + + _Gap2.set_digits(1); + _Gap2.set_size_request(60, -1); + _Gap2.set_increments(0.1, 0); + _Gap2.set_range(0, 10); + _Gap2.set_value(1); + dialog.tooltips().set_tip(_Gap2, + _("Maximum")); + + _Label2.set_label(_("Max:")); + + _Gap1.signal_changed().connect(sigc::mem_fun(*this, &ActionF::on_button_click)); + _Gap2.signal_changed().connect(sigc::mem_fun(*this, &ActionF::on_button_click)); + + dialog.F_Table().attach(_Label, column, column+1, row, row+1, Gtk::FILL, Gtk::FILL); + dialog.F_Table().attach(_Label1, column+1, column+2, row, row+1, Gtk::FILL, Gtk::FILL); + dialog.F_Table().attach(_Gap1, column+2, column+3, row, row+1, Gtk::EXPAND, Gtk::EXPAND); + dialog.F_Table().attach(_Label2, column+3, column+4, row, row+1, Gtk::FILL, Gtk::FILL); + dialog.F_Table().attach(_Gap2, column+4, column+5, row, row+1, Gtk::EXPAND, Gtk::EXPAND); + + } + + virtual void on_button_click(){ + if (!_dialog.getDesktop()) return; + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + prefs->setDouble(_pref1_path, SP_VERB_CONTEXT_SPRAY); + prefs->setDouble(_pref2_path, SP_VERB_CONTEXT_SPRAY); + + double const Gap1 = _Gap1.get_value(); + double const Gap2 = _Gap2.get_value(); + + prefs->setDouble(_pref1_path, Gap1); + prefs->setDouble(_pref2_path, Gap2); + + sp_document_done(sp_desktop_document(_dialog.getDesktop()), SP_VERB_CONTEXT_SPRAY, + _("Remove overlaps")); + } + + +}; + + + +void SprayOptionClass::combo_action() { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + cout<<"combo.get_active_row_number = "<<_combo.get_active_row_number()<setInt("/tools/spray/distribution", distrib); + + + sp_document_done(sp_desktop_document(this->getDesktop()), SP_VERB_CONTEXT_SPRAY, + _("Remove overlaps")); + +} + + + + +void SprayOptionClass::action() { + int r=1; + for (list::iterator it = _actionList.begin(); + it != _actionList.end(); + it ++) + (*it)->on_button_click(); + combo_action(); +} + + + + + + +void on_selection_changed(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, SprayOptionClass *daad) +{ + daad->randomize_bbox = Geom::OptRect(); +} + +///////////////////////////////////////////////////////// +//Construction de l'interface +///////////////////////////////////////////////////////// + + +SprayOptionClass::SprayOptionClass() + : UI::Widget::Panel ("", "/dialogs/spray", SP_VERB_DIALOG_SPRAY_OPTION), + _distributionFrame(_("Distribution")), + _Frame(_("Cursor Options")), + _FFrame(_("Random Options")), + _gaussianTable(1, 5, false), + _ETable(3,2,false), + _FTable(2,5,false), + _unifLabel(_("Uniform")), + _gaussLabel(_("Gaussian ")), + _anchorLabel(_("Distribution : ")) + +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + //ComboBoxText + + _combo.append_text(_("Uniforme")); + _combo.append_text(_("Gaussienne")); + + _combo.set_active(prefs->getInt("/tools/spray/distribution", 1)); + _combo.signal_changed().connect(sigc::mem_fun(*this, &SprayOptionClass::combo_action)); + + _anchorBox.pack_start(_anchorLabel); + _anchorBox.pack_start(_combo); + + _gaussianBox.pack_start(_anchorBox); + + + _distributionBox.pack_start(_gaussianBox); + _distributionFrame.add(_distributionBox); + + + //Hbox Random + addFButton(_("Scale : ") ,_("Applique un facteur d'échelle"), 0, 0, "/tools/spray/scale_min","/tools/spray/scale_max"); + addFButton(_("Rotation : ") ,_("Fait tourner"), 1, 0, "/tools/spray/rot_min","/tools/spray/rot_max"); + _FHBox.pack_start(_FLabel); + _FHBox.pack_start(_FTable); + + //Implementation dans la Vbox Cursor + _FVBox.pack_start(_FHBox); + _FFrame.add(_FVBox); + + //Hbox Cursor + addEButton(_("Ratio : ") ,_("Excentricité de l'ellipse"), 0, 0, 0, 1,"/tools/spray/ratio"); + addEButton(_("Angle : ") ,_("Angle de l'ellipse"), 1, 0, 0, 5,"/tools/spray/tilt"); + addEButton(_("Width : ") ,_("Taille de l'ellipse"), 2, 0, 0, 1,"/tools/spray/width"); + _HBox.pack_start(_Label); + _HBox.pack_start(_ETable); + + //Implementation dans la Vbox Cursor + _VBox.pack_start(_HBox); + _Frame.add(_VBox); + + Gtk::Box *contents = _getContents(); + contents->set_spacing(4); + + + + + + + // Crée dans l'ordre suivant les différentes Frames (cadres de réglages) + + contents->pack_start(_distributionFrame, true, true); + contents->pack_start(_FFrame, true, true); + contents->pack_start(_Frame, true, true); + + + + // Connect to the global selection change, to invalidate cached randomize_bbox + g_signal_connect (G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (on_selection_changed), this); + randomize_bbox = Geom::OptRect(); + + show_all_children(); + + + +} + +SprayOptionClass::~SprayOptionClass() +{ + sp_signal_disconnect_by_data (G_OBJECT (INKSCAPE), this); + + for (std::list::iterator it = _actionList.begin(); + it != _actionList.end(); + it ++) + delete *it; +} + + + + + + + +//Fonctions qui lient la demande d'ajout d'une interface graphique à l'action correspondante + +void SprayOptionClass::addEButton(const Glib::ustring &id, + const Glib::ustring &tiptext, + guint row, guint column, + guint min, guint max, + Glib::ustring const &pref_path) +{ + _actionList.push_back( new ActionE(id, tiptext,row, column,*this,min ,max, pref_path )); +} + +void SprayOptionClass::addFButton(const Glib::ustring &id, + const Glib::ustring &tiptext, + guint row, guint column, + Glib::ustring const &pref1_path, + Glib::ustring const &pref2_path) +{ + _actionList.push_back( new ActionF(id, tiptext,row, column,*this,pref1_path, pref2_path )); +} + + + + + +SprayOptionClass &SprayOptionClass::get_SprayOptionClass() +{ + return *this; +} + +} // namespace Dialog +} // namespace UI +} // 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:encoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/spray-option.h b/src/ui/dialog/spray-option.h new file mode 100644 index 000000000..75dfe1e35 --- /dev/null +++ b/src/ui/dialog/spray-option.h @@ -0,0 +1,130 @@ + +/*Julien LERAY (julien.leray@ecl2010.ec-lyon.fr), interface for the spray tool*/ + +#ifndef INKSCAPE_UI_DIALOG_SPRAY_OPTION_H +#define INKSCAPE_UI_DIALOG_SPRAY_OPTION_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "libnr/nr-dim2.h" +#include "libnr/nr-rect.h" + + +#include "ui/widget/panel.h" +#include "ui/widget/notebook-page.h" + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "desktop-handles.h" +#include "unclump.h" +#include "document.h" +#include "enums.h" +#include "graphlayout/graphlayout.h" +#include "inkscape.h" +#include "macros.h" +#include "node-context.h" +#include "preferences.h" +#include "removeoverlap/removeoverlap.h" +#include "selection.h" +#include "shape-editor.h" +#include "sp-flowtext.h" +#include "sp-item-transform.h" +#include "sp-text.h" +#include "text-editing.h" +#include "tools-switch.h" +#include "ui/icon-names.h" +#include "util/glib-list-iterators.h" +#include "verbs.h" +#include "widgets/icon.h" + +#include "spray-context.h" +#include "verbs.h" + +#include +using namespace std; + +using namespace Inkscape::UI::Widget; + +class SPItem; + + +namespace Inkscape { +namespace UI { +namespace Dialog { + +class Action; + +class SprayOptionClass : public Widget::Panel { + +private: + + SprayOptionClass(SprayOptionClass const &d); + SprayOptionClass& operator=(SprayOptionClass const &d); + +public: + SprayOptionClass(); + virtual ~SprayOptionClass(); + void test() { cout<<"appel de test !!"< _actionList; + Gtk::Frame _distributionFrame, _Frame, _FFrame ; + Gtk::Table _distributionTable, _gaussianTable, _ETable, _FTable; + Gtk::HBox _anchorBox; + Gtk::HBox _unifBox, _gaussianBox, _HBox, _FHBox, _BoutonBox; + Gtk::VBox _distributionBox, _VBox, _FVBox, _ActionBox; + Gtk::Label _anchorLabel; + Gtk::Label _unifLabel, _gaussLabel, _Label, _FLabel; + Gtk::CheckButton _unif, _gauss; + Gtk::ComboBoxText _combo; + Gtk::Tooltips _tooltips; + +}; + + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape + +#endif // INKSCAPE_UI_DIALOG_ALIGN_AND_DISTRIBUTE_H + +/* + 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:encoding=utf-8:textwidth=99 : + diff --git a/src/ui/icon-names.h b/src/ui/icon-names.h index f9a6f2a7d..0388b200f 100644 --- a/src/ui/icon-names.h +++ b/src/ui/icon-names.h @@ -456,6 +456,14 @@ "snap-nodes-smooth" #define INKSCAPE_ICON_SNAP_PAGE \ "snap-page" +#define INKSCAPE_ICON_SPRAY_COPY_MODE \ + "spray-copy-mode" +#define INKSCAPE_ICON_SPRAY_CLONE_MODE \ + "spray-clone-mode" +#define INKSCAPE_ICON_SPRAY_UNION_MODE \ + "spray-union-mode" +#define INKSCAPE_ICON_DIALOG_SPRAY_OPTIONS \ + "dialog-spray-options" #define INKSCAPE_ICON_STROKE_CAP_BUTT \ "stroke-cap-butt" #define INKSCAPE_ICON_STROKE_CAP_ROUND \ @@ -488,6 +496,8 @@ "tool-pointer" #define INKSCAPE_ICON_TOOL_TWEAK \ "tool-tweak" +#define INKSCAPE_ICON_TOOL_SPRAY \ + "tool-spray" #define INKSCAPE_ICON_TRANSFORM_AFFECT_GRADIENT \ "transform-affect-gradient" #define INKSCAPE_ICON_TRANSFORM_AFFECT_PATTERN \ diff --git a/src/ui/view/edit-widget.cpp b/src/ui/view/edit-widget.cpp index 770a9bf87..d34b18771 100644 --- a/src/ui/view/edit-widget.cpp +++ b/src/ui/view/edit-widget.cpp @@ -227,6 +227,12 @@ EditWidget::onDialogAlignAndDistribute() _dlg_mgr.showDialog("AlignAndDistribute"); } +void +EditWidget::onDialogSprayOptionClass() +{ + _dlg_mgr.showDialog("SprayOptionClass"); +} + void EditWidget::onDialogDocumentProperties() { diff --git a/src/ui/view/edit-widget.h b/src/ui/view/edit-widget.h index 2bb708305..452641e80 100644 --- a/src/ui/view/edit-widget.h +++ b/src/ui/view/edit-widget.h @@ -70,6 +70,7 @@ public: void onDialogAbout(); void onDialogAlignAndDistribute(); + void onDialogSprayOptionClass(); void onDialogInkscapePreferences(); void onDialogDialog(); void onDialogDocumentProperties(); diff --git a/src/verbs.cpp b/src/verbs.cpp index 29d24c101..56b63e95e 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1464,6 +1464,9 @@ ContextVerb::perform(SPAction *action, void *data, void */*pdata*/) case SP_VERB_CONTEXT_TWEAK: tools_switch(dt, TOOLS_TWEAK); break; + case SP_VERB_CONTEXT_SPRAY: + tools_switch(dt, TOOLS_SPRAY); + break; case SP_VERB_CONTEXT_RECT: tools_switch(dt, TOOLS_SHAPES_RECT); break; @@ -1525,6 +1528,10 @@ ContextVerb::perform(SPAction *action, void *data, void */*pdata*/) prefs->setInt("/dialogs/preferences/page", PREFS_PAGE_TOOLS_TWEAK); dt->_dlg_mgr->showDialog("InkscapePreferences"); break; + case SP_VERB_CONTEXT_SPRAY_PREFS: + prefs->setInt("/dialogs/preferences/page", PREFS_PAGE_TOOLS_SPRAY); + dt->_dlg_mgr->showDialog("InkscapePreferences"); + break; case SP_VERB_CONTEXT_RECT_PREFS: prefs->setInt("/dialogs/preferences/page", PREFS_PAGE_TOOLS_SHAPES_RECT); dt->_dlg_mgr->showDialog("InkscapePreferences"); @@ -1797,6 +1804,9 @@ DialogVerb::perform(SPAction *action, void *data, void */*pdata*/) case SP_VERB_DIALOG_ALIGN_DISTRIBUTE: dt->_dlg_mgr->showDialog("AlignAndDistribute"); break; + case SP_VERB_DIALOG_SPRAY_OPTION: + dt->_dlg_mgr->showDialog("SprayOptionClass"); + break; case SP_VERB_DIALOG_TEXT: sp_text_edit_dialog(); break; @@ -2503,6 +2513,8 @@ Verb *Verb::_base_verbs[] = { N_("Edit paths by nodes"), INKSCAPE_ICON_TOOL_NODE_EDITOR), new ContextVerb(SP_VERB_CONTEXT_TWEAK, "ToolTweak", N_("Tweak"), N_("Tweak objects by sculpting or painting"), INKSCAPE_ICON_TOOL_TWEAK), + new ContextVerb(SP_VERB_CONTEXT_SPRAY, "ToolSpray", N_("Spray"), + N_("Spray objects by sculpting or painting"), INKSCAPE_ICON_TOOL_SPRAY), new ContextVerb(SP_VERB_CONTEXT_RECT, "ToolRect", N_("Rectangle"), N_("Create rectangles and squares"), INKSCAPE_ICON_DRAW_RECTANGLE), new ContextVerb(SP_VERB_CONTEXT_3DBOX, "Tool3DBox", N_("3D Box"), @@ -2544,6 +2556,8 @@ Verb *Verb::_base_verbs[] = { N_("Open Preferences for the Node tool"), NULL), new ContextVerb(SP_VERB_CONTEXT_TWEAK_PREFS, "TweakPrefs", N_("Tweak Tool Preferences"), N_("Open Preferences for the Tweak tool"), NULL), + new ContextVerb(SP_VERB_CONTEXT_SPRAY_PREFS, "SprayPrefs", N_("Spray Tool Preferences"), + N_("Open Preferences for the Spray tool"), NULL), new ContextVerb(SP_VERB_CONTEXT_RECT_PREFS, "RectPrefs", N_("Rectangle Preferences"), N_("Open Preferences for the Rectangle tool"), NULL), new ContextVerb(SP_VERB_CONTEXT_3DBOX_PREFS, "3DBoxPrefs", N_("3D Box Preferences"), @@ -2645,6 +2659,8 @@ Verb *Verb::_base_verbs[] = { N_("Precisely control objects' transformations"), INKSCAPE_ICON_DIALOG_TRANSFORM), new DialogVerb(SP_VERB_DIALOG_ALIGN_DISTRIBUTE, "DialogAlignDistribute", N_("_Align and Distribute..."), N_("Align and distribute objects"), INKSCAPE_ICON_DIALOG_ALIGN_AND_DISTRIBUTE), + new DialogVerb(SP_VERB_DIALOG_SPRAY_OPTION, "DialogSprayOption", N_("_Spray options..."), + N_("Some options for the spray"), INKSCAPE_ICON_DIALOG_SPRAY_OPTIONS), new DialogVerb(SP_VERB_DIALOG_UNDO_HISTORY, "DialogUndoHistory", N_("Undo _History..."), N_("Undo History"), INKSCAPE_ICON_EDIT_UNDO_HISTORY), new DialogVerb(SP_VERB_DIALOG_TEXT, "DialogText", N_("_Text and Font..."), diff --git a/src/verbs.h b/src/verbs.h index 87fe27075..3ea2fdee8 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -153,6 +153,7 @@ enum { SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_TWEAK, + SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_ARC, @@ -174,6 +175,7 @@ enum { SP_VERB_CONTEXT_SELECT_PREFS, SP_VERB_CONTEXT_NODE_PREFS, SP_VERB_CONTEXT_TWEAK_PREFS, + SP_VERB_CONTEXT_SPRAY_PREFS, SP_VERB_CONTEXT_RECT_PREFS, SP_VERB_CONTEXT_3DBOX_PREFS, SP_VERB_CONTEXT_ARC_PREFS, @@ -227,6 +229,7 @@ enum { SP_VERB_DIALOG_SWATCHES, SP_VERB_DIALOG_TRANSFORM, SP_VERB_DIALOG_ALIGN_DISTRIBUTE, + SP_VERB_DIALOG_SPRAY_OPTION, SP_VERB_DIALOG_UNDO_HISTORY, SP_VERB_DIALOG_TEXT, SP_VERB_DIALOG_XML_EDITOR, diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index e0fe9bfd1..41a70f08a 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -86,6 +86,7 @@ #include "../svg/css-ostringstream.h" #include "../tools-switch.h" #include "../tweak-context.h" +#include "../spray-context.h" #include "../ui/dialog/calligraphic-profile-rename.h" #include "../ui/icon-names.h" #include "../ui/widget/style-swatch.h" @@ -107,6 +108,7 @@ typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder); static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder); +static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder); static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder); static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder); static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder); @@ -146,6 +148,7 @@ static struct { { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS}, { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS }, { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS }, + { "SPSprayContext", "spray_tool", SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS }, { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS }, { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS }, { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS }, @@ -181,6 +184,8 @@ static struct { SP_VERB_INVALID, 0, 0}, { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar", SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")}, + { "SPSprayContext", "spray_toolbox", 0, sp_spray_toolbox_prep, "SprayToolbar", + SP_VERB_CONTEXT_SPRAY_PREFS, "/tools/spray", N_("Color/opacity used for color spraying")}, { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar", SP_VERB_INVALID, 0, 0}, { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar", @@ -299,6 +304,21 @@ static gchar const * ui_descr = " " " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " " " " " @@ -718,6 +738,7 @@ Glib::RefPtr create_or_fetch_actions( SPDesktop* desktop ) //SP_VERB_EDIT_TILE, //SP_VERB_EDIT_UNTILE, SP_VERB_DIALOG_ALIGN_DISTRIBUTE, + SP_VERB_DIALOG_SPRAY_OPTION, SP_VERB_DIALOG_DISPLAY, SP_VERB_DIALOG_FILL_STROKE, SP_VERB_DIALOG_NAMEDVIEW, @@ -1627,6 +1648,7 @@ setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop) " " " " " " + " " " " " " " " @@ -4371,6 +4393,198 @@ static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction } +//######################## +//## Spray ## +//######################## + +static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ ) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setDouble( "/tools/spray/width", adj->value ); +} + +static void sp_spray_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ ) +{ + //Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + //prefs->setDouble( "/tools/spray/force", adj->value * 0.01 ); +} + +static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ ) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setDouble( "/tools/spray/mean", adj->value ); +} + +static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ ) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setDouble( "/tools/spray/standard_deviation", adj->value ); +} + +static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ ) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act)); +} + +static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject *tbl ) +{ + int mode = ege_select_one_action_get_active( act ); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setInt("/tools/spray/mode", mode); +} + +static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ ) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setDouble( "/tools/spray/population", adj->value ); +} + +/*static void spray_toggle_doh (GtkToggleAction *act, gpointer ) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/spray/doh", gtk_toggle_action_get_active(act)); +} +static void spray_toggle_dos (GtkToggleAction *act, gpointer ) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/spray/dos", gtk_toggle_action_get_active(act)); +} +static void spray_toggle_dol (GtkToggleAction *act, gpointer ) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/spray/dol", gtk_toggle_action_get_active(act)); +} +static void spray_toggle_doo (GtkToggleAction *act, gpointer ) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/spray/doo", gtk_toggle_action_get_active(act)); +} +*/ +static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder) +{ + Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + { + /* Width */ + gchar const* labels[] = {_("(pinch spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")}; + gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100}; + EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction", + _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"), + "/tools/spray/width", 15, + GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray", + 1, 100, 1.0, 0.0, + labels, values, G_N_ELEMENTS(labels), + sp_spray_width_value_changed, 0.01, 0, 100 ); + ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT ); + gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); + gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); + } + + { + /* Mean */ + gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")}; + gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100}; + EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction", + _("Mean"), _("Mean:"), _("The mean of the spray action"), + "/tools/spray/mean", 20, + GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean", + 1, 100, 1.0, 0.0, + labels, values, G_N_ELEMENTS(labels), + sp_spray_mean_value_changed, 0.01, 0, 100 ); + ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT ); + gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); + gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); + } + + { + /* Standard_deviation */ + gchar const* labels[] = {_("(minimum standard_deviation)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum standard_deviation)")}; + gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100}; + EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction", + _("SD"), _("SD:"), _("The standard deviation of the spray action"), + "/tools/spray/standard_deviation", 20, + GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation", + 1, 100, 1.0, 0.0, + labels, values, G_N_ELEMENTS(labels), + sp_spray_standard_deviation_value_changed, 0.01, 0, 100 ); + ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT ); + gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); + gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); + } + + /* Mode */ + { + GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING ); + + GtkTreeIter iter; + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("Spray with copies"), + 1, _("Spray copies of the initial selection"), + 2, INKSCAPE_ICON_SPRAY_COPY_MODE, + -1 ); + + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("Spray with clones"), + 1, _("Spray clones of the initial selection"), + 2, INKSCAPE_ICON_SPRAY_CLONE_MODE, + -1 ); + + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("Spray single path"), + 1, _("Spray objects in a single path"), + 2, INKSCAPE_ICON_SPRAY_UNION_MODE, + -1 ); + + EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) ); + g_object_set( act, "short_label", _("Mode:"), NULL ); + gtk_action_group_add_action( mainActions, GTK_ACTION(act) ); + g_object_set_data( holder, "mode_action", act ); + + ege_select_one_action_set_appearance( act, "full" ); + ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE ); + g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL ); + ege_select_one_action_set_icon_column( act, 2 ); + ege_select_one_action_set_icon_size( act, secondarySize ); + ege_select_one_action_set_tooltip_column( act, 1 ); + + gint mode = prefs->getInt("/tools/spray/mode", 0); + ege_select_one_action_set_active( act, mode ); + g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder ); + + g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act); + } + + { /* Population */ + gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")}; + gdouble values[] = {10, 25, 35, 50, 60, 80, 100}; + EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction", + _("Population"), _("Population:"), + _("This setting adjusts the number of items sprayed"), + "/tools/spray/population", 50, + GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population", + 1, 100, 1.0, 10.0, + labels, values, G_N_ELEMENTS(labels), + sp_spray_population_value_changed, 0.01, 0, 100 ); + gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); + gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); + g_object_set_data( holder, "spray_population", eact ); + } + + /* Use Pressure button */ + { + InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction", + _("Pressure"), + _("Use the pressure of the input device to alter the force of spray action"), + "use_pressure", + Inkscape::ICON_SIZE_DECORATION ); + gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) ); + } +} + + //######################## //## Calligraphy ## //######################## -- cgit v1.2.3 From 45db1dc0670f816b778c91fb7f43bd95eb656edf Mon Sep 17 00:00:00 2001 From: buliabyak <> Date: Tue, 1 Dec 2009 18:22:24 -0400 Subject: recent files: show tooltips with uri, hide local files which are missing or unaccessible (bzr r8853) --- src/interface.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/interface.cpp b/src/interface.cpp index cf7072064..415593b78 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -992,6 +992,9 @@ sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI: gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname()); gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter); + gtk_recent_chooser_set_show_tips (GTK_RECENT_CHOOSER(recent_menu), TRUE); + gtk_recent_chooser_set_show_not_found (GTK_RECENT_CHOOSER(recent_menu), FALSE); + GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent")); gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu); -- cgit v1.2.3 From 0cf189f9ab1ac334b55e9059ad3905bed9edffc6 Mon Sep 17 00:00:00 2001 From: "Arcadie M. Cracan" Date: Wed, 2 Dec 2009 22:26:44 +0200 Subject: Merge GSoC2009 Connectors into trunk (bzr r8855) --- src/CMakeLists.txt | 1 + src/Makefile_insert | 1 + src/attributes.cpp | 2 + src/attributes.h | 2 + src/conn-avoid-ref.cpp | 398 ++++++++- src/conn-avoid-ref.h | 16 +- src/connector-context.cpp | 1192 ++++++++++++++++++++------- src/connector-context.h | 30 +- src/document.cpp | 98 ++- src/document.h | 3 + src/graphlayout/graphlayout.cpp | 1 + src/libavoid/CMakeLists.txt | 2 + src/libavoid/Makefile_insert | 14 +- src/libavoid/connector.cpp | 1695 +++++++++++++++++++++++++++++++++++---- src/libavoid/connector.h | 380 ++++++++- src/libavoid/debug.h | 19 +- src/libavoid/geometry.cpp | 241 +++--- src/libavoid/geometry.h | 47 +- src/libavoid/geomtypes.h | 284 ++++++- src/libavoid/graph.cpp | 379 +++++++-- src/libavoid/graph.h | 37 +- src/libavoid/libavoid.h | 25 +- src/libavoid/makepath.cpp | 899 ++++++++++++--------- src/libavoid/makepath.h | 17 +- src/libavoid/polyutil.cpp | 92 --- src/libavoid/polyutil.h | 41 - src/libavoid/region.cpp | 858 -------------------- src/libavoid/region.h | 81 -- src/libavoid/router.cpp | 1541 +++++++++++++++++++++++++++++------ src/libavoid/router.h | 331 +++++++- src/libavoid/shape.cpp | 112 +-- src/libavoid/shape.h | 77 +- src/libavoid/static.cpp | 105 --- src/libavoid/static.h | 42 - src/libavoid/timer.cpp | 53 +- src/libavoid/timer.h | 47 +- src/libavoid/vertices.cpp | 212 +++-- src/libavoid/vertices.h | 59 +- src/libavoid/visibility.cpp | 579 +++++++------ src/libavoid/visibility.h | 21 +- src/sp-conn-end-pair.cpp | 248 ++++-- src/sp-conn-end-pair.h | 22 +- src/sp-conn-end.cpp | 328 +++++--- src/sp-conn-end.h | 18 +- src/sp-item.cpp | 4 + src/svg/stringstream.cpp | 25 + src/svg/stringstream.h | 7 + src/ui/icon-names.h | 8 + src/widgets/toolbox.cpp | 242 +++++- 49 files changed, 7622 insertions(+), 3314 deletions(-) delete mode 100644 src/libavoid/polyutil.cpp delete mode 100644 src/libavoid/polyutil.h delete mode 100644 src/libavoid/region.cpp delete mode 100644 src/libavoid/region.h delete mode 100644 src/libavoid/static.cpp delete mode 100644 src/libavoid/static.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1885f56e5..46c2586d1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,6 +86,7 @@ composite-undo-stack-observer.cpp common-context.cpp conditions.cpp conn-avoid-ref.cpp +connection-points.cpp connector-context.cpp console-output-undo-observer.cpp context-fns.cpp diff --git a/src/Makefile_insert b/src/Makefile_insert index 18beaec37..da49187ba 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -20,6 +20,7 @@ ink_common_sources += \ composite-undo-stack-observer.cpp \ composite-undo-stack-observer.h \ conditions.cpp conditions.h \ + connection-points.cpp connection-points.h \ conn-avoid-ref.cpp conn-avoid-ref.h \ connection-pool.h \ connector-context.cpp connector-context.h \ diff --git a/src/attributes.cpp b/src/attributes.cpp index 69b11fae9..d03c3be3e 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -37,6 +37,7 @@ static SPStyleProp const props[] = { {SP_ATTR_SODIPODI_INSENSITIVE, "sodipodi:insensitive"}, {SP_ATTR_SODIPODI_NONPRINTABLE, "sodipodi:nonprintable"}, {SP_ATTR_CONNECTOR_AVOID, "inkscape:connector-avoid"}, + {SP_ATTR_CONNECTION_POINTS, "inkscape:connection-points"}, {SP_ATTR_STYLE, "style"}, {SP_ATTR_TRANSFORM_CENTER_X, "inkscape:transform-center-x"}, {SP_ATTR_TRANSFORM_CENTER_Y, "inkscape:transform-center-y"}, @@ -124,6 +125,7 @@ static SPStyleProp const props[] = { {SP_ATTR_CONNECTOR_TYPE, "inkscape:connector-type"}, {SP_ATTR_CONNECTION_START, "inkscape:connection-start"}, {SP_ATTR_CONNECTION_END, "inkscape:connection-end"}, + {SP_ATTR_CONNECTOR_CURVATURE, "inkscape:connector-curvature"}, /* SPRect */ {SP_ATTR_RX, "rx"}, {SP_ATTR_RY, "ry"}, diff --git a/src/attributes.h b/src/attributes.h index 52f71b203..af60b75be 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -36,6 +36,7 @@ enum SPAttributeEnum { SP_ATTR_SODIPODI_INSENSITIVE, SP_ATTR_SODIPODI_NONPRINTABLE, SP_ATTR_CONNECTOR_AVOID, + SP_ATTR_CONNECTION_POINTS, SP_ATTR_STYLE, SP_ATTR_TRANSFORM_CENTER_X, SP_ATTR_TRANSFORM_CENTER_Y, @@ -124,6 +125,7 @@ enum SPAttributeEnum { SP_ATTR_CONNECTOR_TYPE, SP_ATTR_CONNECTION_START, SP_ATTR_CONNECTION_END, + SP_ATTR_CONNECTOR_CURVATURE, /* SPRect */ SP_ATTR_RX, SP_ATTR_RY, diff --git a/src/conn-avoid-ref.cpp b/src/conn-avoid-ref.cpp index 43c9c0b66..c04ad9e49 100644 --- a/src/conn-avoid-ref.cpp +++ b/src/conn-avoid-ref.cpp @@ -12,23 +12,34 @@ #include #include +#include #include "sp-item.h" +#include "display/curve.h" +#include "2geom/line.h" +#include "2geom/crossing.h" +#include "2geom/convex-cover.h" +#include "svg/stringstream.h" #include "conn-avoid-ref.h" -#include "libavoid/polyutil.h" +#include "connection-points.h" +#include "sp-conn-end.h" +#include "sp-path.h" #include "libavoid/router.h" #include "libavoid/connector.h" +#include "libavoid/geomtypes.h" #include "xml/node.h" #include "document.h" #include "desktop.h" #include "desktop-handles.h" #include "sp-namedview.h" #include "inkscape.h" +#include + using Avoid::Router; -static Avoid::Polygn avoid_item_poly(SPItem const *item); +static Avoid::Polygon avoid_item_poly(SPItem const *item); SPAvoidRef::SPAvoidRef(SPItem *spitem) @@ -44,13 +55,17 @@ SPAvoidRef::SPAvoidRef(SPItem *spitem) SPAvoidRef::~SPAvoidRef() { _transformed_connection.disconnect(); - if (shapeRef) { + + // If the document is being destroyed then the router instance + // and the ShapeRefs will have been destroyed with it. + const bool routerInstanceExists = (item->document->router != NULL); + + if (shapeRef && routerInstanceExists) { Router *router = shapeRef->router(); - // shapeRef is finalised by delShape, - // so no memory is lost here. - router->delShape(shapeRef); - shapeRef = NULL; + router->removeShape(shapeRef); + delete shapeRef; } + shapeRef = NULL; } @@ -66,6 +81,205 @@ void SPAvoidRef::setAvoid(char const *value) } } +void print_connection_points(std::map& cp) +{ + std::map::iterator i; + for (i=cp.begin(); i!=cp.end(); ++i) + { + const ConnectionPoint& p = i->second; + std::cout< updates; + std::set deletes; + std::set seen; + + if (value) + { + /* Rebuild the connection points list. + Update the connectors for which + the endpoint has changed. + */ + + gchar ** strarray = g_strsplit(value, "|", 0); + gchar ** iter = strarray; + + while (*iter != NULL) { + ConnectionPoint cp; + Inkscape::SVGIStringStream is(*iter); + is>>cp; + cp.type = ConnPointUserDefined; + + /* Mark this connection point as seen, so we can delete + the other ones. + */ + seen.insert(cp.id); + if ( connection_points.find(cp.id) != connection_points.end() ) + { + /* An already existing connection point. + Check to see if changed, and, if it is + the case, trigger connector update for + the connector attached to this connection + point. This is done by adding the + connection point to a list of connection + points to be updated. + */ + if ( connection_points[cp.id] != cp ) + // The connection point got updated. + // Put it in the update list. + updates.insert(cp.id); + } + connection_points[cp.id] = cp; + ++iter; + } + /* Delete the connection points that didn't appear + in the new connection point list. + */ + std::map::iterator it; + + for (it=connection_points.begin(); it!=connection_points.end(); ++it) + if ( seen.find(it->first) == seen.end()) + deletes.insert(it->first); + g_strfreev(strarray); + } + else + { + /* Delete all the user-defined connection points + Actually we do this by adding them to the list + of connection points to be deleted. + */ + std::map::iterator it; + + for (it=connection_points.begin(); it!=connection_points.end(); ++it) + deletes.insert(it->first); + } + /* Act upon updates and deletes. + */ + if (deletes.empty() && updates.empty()) + // Nothing to do, just return. + return; + // Get a list of attached connectors. + GSList* conns = getAttachedConnectors(Avoid::runningToAndFrom); + for (GSList *i = conns; i != NULL; i = i->next) + { + SPPath* path = SP_PATH(i->data); + SPConnEnd** connEnds = path->connEndPair.getConnEnds(); + for (int ix=0; ix<2; ++ix) + if (connEnds[ix]->type == ConnPointUserDefined) + if (updates.find(connEnds[ix]->id) != updates.end()) + if (path->connEndPair.isAutoRoutingConn()) + path->connEndPair.tellLibavoidNewEndpoints(); + else + { + } + else + if (deletes.find(connEnds[ix]->id) != deletes.end()) + sp_conn_end_detach(path, ix); + } + g_slist_free(conns); + // Remove all deleted connection points + if (deletes.size()) + for (std::set::iterator it = deletes.begin(); it != deletes.end(); ++it) + connection_points.erase(*it); +} + +void SPAvoidRef::setConnectionPointsAttrUndoable(const gchar* value, const gchar* action) +{ + SPDocument* doc = SP_OBJECT_DOCUMENT(item); + + sp_object_setAttribute( SP_OBJECT(item), "inkscape:connection-points", value, 0 ); + item->updateRepr(); + sp_document_ensure_up_to_date(doc); + sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR, action); +} + +void SPAvoidRef::addConnectionPoint(ConnectionPoint &cp) +{ + Inkscape::SVGOStringStream ostr; + bool first = true; + int newId = 1; + if ( connection_points.size() ) + { + for (IdConnectionPointMap::iterator it = connection_points.begin(); ; ) + { + if ( first ) + { + first = false; + ostr<second; + } + else + ostr<<'|'<second; + IdConnectionPointMap::iterator prev_it = it; + ++it; + if ( it == connection_points.end() || prev_it->first + 1 != it->first ) + { + newId = prev_it->first + 1; + break; + } + } + } + cp.id = newId; + if ( first ) + { + first = false; + ostr<setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Added a new connection point") ); +} + +void SPAvoidRef::updateConnectionPoint(ConnectionPoint &cp) +{ + Inkscape::SVGOStringStream ostr; + IdConnectionPointMap::iterator cp_pos = connection_points.find( cp.id ); + if ( cp_pos != connection_points.end() ) + { + bool first = true; + for (IdConnectionPointMap::iterator it = connection_points.begin(); it != connection_points.end(); ++it) + { + ConnectionPoint* to_write; + if ( it != cp_pos ) + to_write = &it->second; + else + to_write = &cp; + if ( first ) + { + first = false; + ostr<<*to_write; + } + else + ostr<<'|'<<*to_write; + } + this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Moved a connection point") ); + } +} + +void SPAvoidRef::deleteConnectionPoint(ConnectionPoint &cp) +{ + Inkscape::SVGOStringStream ostr; + IdConnectionPointMap::iterator cp_pos = connection_points.find( cp.id ); + if ( cp_pos != connection_points.end() ) + { + bool first = true; + for (IdConnectionPointMap::iterator it = connection_points.begin(); it != connection_points.end(); ++it) + { + if ( it != cp_pos ) + if ( first ) + { + first = false; + ostr<second; + } + else + ostr<<'|'<second; + } + this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Removed a connection point") ); + } +} void SPAvoidRef::handleSettingChange(void) { @@ -93,8 +307,8 @@ void SPAvoidRef::handleSettingChange(void) _transformed_connection.disconnect(); if (new_setting) { - Avoid::Polygn poly = avoid_item_poly(item); - if (poly.pn > 0) { + Avoid::Polygon poly = avoid_item_poly(item); + if (poly.size() > 0) { _transformed_connection = item->connectTransformed( sigc::ptr_fun(&avoid_item_move)); @@ -104,8 +318,7 @@ void SPAvoidRef::handleSettingChange(void) // Get a unique ID for the item. GQuark itemID = g_quark_from_string(id); - shapeRef = new Avoid::ShapeRef(router, itemID, poly); - Avoid::freePoly(poly); + shapeRef = new Avoid::ShapeRef(router, poly, itemID); router->addShape(shapeRef); } @@ -114,9 +327,8 @@ void SPAvoidRef::handleSettingChange(void) { g_assert(shapeRef); - // shapeRef is finalised by delShape, - // so no memory is lost here. - router->delShape(shapeRef); + router->removeShape(shapeRef); + delete shapeRef; shapeRef = NULL; } } @@ -169,14 +381,53 @@ GSList *SPAvoidRef::getAttachedConnectors(const unsigned int type) return list; } +Geom::Point SPAvoidRef::getConnectionPointPos(const int type, const int id) +{ + g_assert(item); + Geom::Point pos; + const Geom::Matrix& transform = sp_item_i2doc_affine(item); + SPDesktop *desktop = inkscape_active_desktop(); + + if ( type == ConnPointDefault ) + { + // For now, just default to the centre of the item + Geom::OptRect bbox = item->getBounds(sp_item_i2doc_affine(item)); + pos = (bbox) ? bbox->midpoint() : Geom::Point(0, 0); + } + else + { + // Get coordinates from the list of connection points + // that are attached to the item + pos = connection_points[id].pos * transform; + } + + return pos; +} + +bool SPAvoidRef::isValidConnPointId( const int type, const int id ) +{ + if ( type < 0 || type > 1 ) + return false; + else + { + if ( type == ConnPointDefault ) + if ( id < 0 || id > 8 ) + return false; + else + { + } + else + return connection_points.find( id ) != connection_points.end(); + } + + return true; +} -static Avoid::Polygn avoid_item_poly(SPItem const *item) +static Avoid::Polygon avoid_item_poly(SPItem const *item) { SPDesktop *desktop = inkscape_active_desktop(); g_assert(desktop != NULL); - Avoid::Polygn poly; - // TODO: The right way to do this is to return the convex hull of // the object, or an approximation in the case of a rounded // object. Specific SPItems will need to have a new @@ -185,24 +436,112 @@ static Avoid::Polygn avoid_item_poly(SPItem const *item) // some convex hull code, though not NR::ConvexHull as this // only keeps the bounding box of the convex hull currently. - // TODO: SPItem::getBounds gives the wrong result for some objects - // that have internal representations that are updated later - // by the sp_*_update functions, e.g., text. - sp_document_ensure_up_to_date(item->document); + double spacing = desktop->namedview->connector_spacing; + + // [sommer] If item is a shape, use an approximation of its convex hull + { + // MJW: Disable this for the moment. It still has some issues. + const bool convex_hull_approximation_enabled = false; + + if ( convex_hull_approximation_enabled && SP_IS_SHAPE (item) ) { + // The number of points to use for approximation + const unsigned NUM_POINTS = 64; + +// printf("[sommer] is a shape\n"); + SPCurve* curve = sp_shape_get_curve (SP_SHAPE (item)); + if (curve) { +// printf("[sommer] is a curve\n"); + + // apply all transformations + Geom::Matrix itd_mat = sp_item_i2doc_affine(item); + curve->transform(itd_mat); + + // iterate over all paths + const Geom::PathVector& curve_pv = curve->get_pathvector(); + std::vector hull_points; + for (Geom::PathVector::const_iterator i = curve_pv.begin(); i != curve_pv.end(); i++) { + const Geom::Path& curve_pv_path = *i; +// printf("[sommer] tracing sub-path\n"); + + // FIXME: enlarge path by "desktop->namedview->connector_spacing" (using sp_selected_path_do_offset)? + + // use appropriate fraction of points for this path (first one gets any remainder) + unsigned num_points = NUM_POINTS / curve_pv.size(); + if (i == curve_pv.begin()) num_points += NUM_POINTS - (num_points * curve_pv.size()); + printf("[sommer] using %d points for this path\n", num_points); + + // sample points along the path for approximation of convex hull + for (unsigned n = 0; n < num_points; n++) { + double at = curve_pv_path.size() / static_cast(num_points) * n; + Geom::Point pt = curve_pv_path.pointAt(at); + hull_points.push_back(pt); + } + } + + curve->unref(); + + // create convex hull from all sampled points + Geom::ConvexHull hull(hull_points); + + // store expanded convex hull in Avoid::Polygn + unsigned n = 0; + Avoid::Polygon poly; + const Geom::Point& old_pt = *hull.boundary.begin(); + + Geom::Line hull_edge(*hull.boundary.begin(), *(hull.boundary.begin()+1)); + Geom::Line parallel_hull_edge; + parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing); + parallel_hull_edge.versor(hull_edge.versor()); + Geom::Line bisector = Geom::make_angle_bisector_line( *(hull.boundary.end()), *hull.boundary.begin(), + *(hull.boundary.begin()+1)); + Geom::OptCrossing int_pt = Geom::intersection(parallel_hull_edge, bisector); + + if (int_pt) + { + Avoid::Point avoid_pt((parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], + (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]); +// printf("[sommer] %f, %f\n", old_pt[Geom::X], old_pt[Geom::Y]); +/* printf("[sommer] %f, %f\n", (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], + (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);*/ + poly.ps.push_back(avoid_pt); + } + for (std::vector::const_iterator i = hull.boundary.begin() + 1; i != hull.boundary.end(); i++, n++) { + const Geom::Point& old_pt = *i; + Geom::Line hull_edge(*i, *(i+1)); + Geom::Line parallel_hull_edge; + parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing); + parallel_hull_edge.versor(hull_edge.versor()); + Geom::Line bisector = Geom::make_angle_bisector_line( *(i-1), *i, *(i+1)); + Geom::OptCrossing intersect_pt = Geom::intersection(parallel_hull_edge, bisector); + + if (int_pt) + { + Avoid::Point avoid_pt((parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], + (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]); +/* printf("[sommer] %f, %f\n", old_pt[Geom::X], old_pt[Geom::Y]); + printf("[sommer] %f, %f\n", (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], + (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);*/ + poly.ps.push_back(avoid_pt); + } + } + + + return poly; + }// else printf("[sommer] is no curve\n"); + }// else printf("[sommer] is no shape\n"); + } Geom::OptRect rHull = item->getBounds(sp_item_i2doc_affine(item)); if (!rHull) { - return Avoid::newPoly(0); + return Avoid::Polygon(); } - double spacing = desktop->namedview->connector_spacing; - // Add a little buffer around the edge of each object. Geom::Rect rExpandedHull = *rHull; rExpandedHull.expandBy(spacing); - poly = Avoid::newPoly(4); + Avoid::Polygon poly(4); - for (unsigned n = 0; n < 4; ++n) { + for (size_t n = 0; n < 4; ++n) { Geom::Point hullPoint = rExpandedHull.corner(n); poly.ps[n].x = hullPoint[Geom::X]; poly.ps[n].y = hullPoint[Geom::Y]; @@ -242,10 +581,9 @@ void avoid_item_move(Geom::Matrix const */*mp*/, SPItem *moved_item) g_assert(shapeRef); Router *router = moved_item->document->router; - Avoid::Polygn poly = avoid_item_poly(moved_item); - if (poly.pn > 0) { - router->moveShape(shapeRef, &poly); - Avoid::freePoly(poly); + Avoid::Polygon poly = avoid_item_poly(moved_item); + if (!poly.empty()) { + router->moveShape(shapeRef, poly); } } diff --git a/src/conn-avoid-ref.h b/src/conn-avoid-ref.h index d34d8ca2e..5dff8dd38 100644 --- a/src/conn-avoid-ref.h +++ b/src/conn-avoid-ref.h @@ -18,6 +18,8 @@ struct SPDesktop; struct SPItem; +struct ConnectionPoint; +typedef std::map IdConnectionPointMap; namespace Avoid { class ShapeRef; } class SPAvoidRef { @@ -28,9 +30,16 @@ public: // libavoid's internal representation of the item. Avoid::ShapeRef *shapeRef; + // Used for holding connection points for item + IdConnectionPointMap connection_points; + void setAvoid(char const *value); + void setConnectionPoints(gchar const *value); + void addConnectionPoint(ConnectionPoint &cp); + void updateConnectionPoint(ConnectionPoint &cp); + void deleteConnectionPoint(ConnectionPoint &cp); void handleSettingChange(void); - + // Returns a list of SPItems of all connectors/shapes attached to // this object. Pass one of the following for 'type': // Avoid::runningTo @@ -38,6 +47,9 @@ public: // Avoid::runningToAndFrom GSList *getAttachedShapes(const unsigned int type); GSList *getAttachedConnectors(const unsigned int type); + Geom::Point getConnectionPointPos(const int type, const int id); + + bool isValidConnPointId( const int type, const int id ); private: SPItem *item; @@ -48,6 +60,7 @@ private: // A sigc connection for transformed signal. sigc::connection _transformed_connection; + void setConnectionPointsAttrUndoable(const gchar* value, const gchar* action); }; extern GSList *get_avoided_items(GSList *list, SPObject *from, @@ -56,6 +69,7 @@ extern void avoid_item_move(Geom::Matrix const *mp, SPItem *moved_item); extern void init_avoided_shape_geometry(SPDesktop *desktop); static const double defaultConnSpacing = 3.0; +static const double defaultConnCurvature = 3.0; #endif /* !SEEN_CONN_AVOID_REF */ diff --git a/src/connector-context.cpp b/src/connector-context.cpp index 2131bdced..228c81d29 100644 --- a/src/connector-context.cpp +++ b/src/connector-context.cpp @@ -4,31 +4,25 @@ * Authors: * Michael Wybrow * - * Copyright (C) 2005 Michael Wybrow + * Copyright (C) 2005-2008 Michael Wybrow + * Copyright (C) 2009 Monash University * * Released under GNU GPL, read the file 'COPYING' for more information * * TODO: - * o Have shapes avoid convex hulls of objects, rather than their - * bounding box. Possibly implement the unfinished ConvexHull - * class in libnr. - * (HOWEVER, using the convex hull C of a shape S does the wrong thing if a - * connector starts outside of S but inside C, or if the best route around - * an object involves going inside C but without entering S.) - * o Draw connectors to shape edges rather than bounding box. * o Show a visual indicator for objects with the 'avoid' property set. * o Allow user to change a object between a path and connector through * the interface. * o Create an interface for setting markers (arrow heads). * o Better distinguish between paths and connectors to prevent problems - * in the node tool and paths accidently being turned into connectors + * in the node tool and paths accidentally being turned into connectors * in the connector tool. Perhaps have a way to convert between. * o Only call libavoid's updateEndPoint as required. Currently we do it * for both endpoints, even if only one is moving. * o Allow user-placeable connection points. * o Deal sanely with connectors with both endpoints attached to the * same connection point, and drawing of connectors attaching - * overlaping shapes (currently tries to adjust connector to be + * overlapping shapes (currently tries to adjust connector to be * outside both bounding boxes). * o Fix many special cases related to connectors updating, * e.g., copying a couple of shapes and a connector that are @@ -37,16 +31,133 @@ * one of the other contexts. * o Cope with shapes whose ids change when they have attached * connectors. - * o gobble_motion_events(GDK_BUTTON1_MASK)?; + * o During dragging motion, gobble up to and use the final motion event. + * Gobbling away all duplicates after the current can occasionally result + * in the path lagging behind the mouse cursor if it is no longer being + * dragged. + * o Fix up libavoid's representation after undo actions. It doesn't see + * any transform signals and hence doesn't know shapes have moved back to + * there earlier positions. + * o Decide whether drawing/editing mode should be an Inkscape preference + * or the connector tool should always start in drawing mode. + * o Correct the problem with switching to the select tool when pressing + * space bar (there are moments when it refuses to do so). + * + * ---------------------------------------------------------------------------- + * + * mjwybrow's observations on acracan's Summer of Code connector work: + * + * - GUI comments: + * + * - Buttons for adding and removing user-specified connection + * points should probably have "+" and "-" symbols on them so they + * are consistent with the similar buttons for the node tool. + * - Controls on the connector tool be should be reordered logically, + * possibly as follows: + * + * *Connector*: [Polyline-radio-button] [Orthgonal-radio-button] + * [Curvature-control] | *Shape*: [Avoid-button] [Dont-avoid-button] + * [Spacing-control] | *Connection pts*: [Edit-mode] [Add-pt] [Rm-pt] + * + * I think that the network layout controls be moved to the + * Align and Distribute dialog (there is already the layout button + * there, but no options are exposed). + * + * I think that the style change between polyline and orthogonal + * would be much clearer with two buttons (radio behaviour -- just + * one is true). + * + * The other tools show a label change from "New:" to "Change:" + * depending on whether an object is selected. We could consider + * this but there may not be space. + * + * The Add-pt and Rm-pt buttons should be greyed out (inactive) if + * we are not in connection point editing mode. And probably also + * if there is no shape selected, i.e. at the times they have no + * effect when clicked. + * + * Likewise for the avoid/ignore shapes buttons. These should be + * inactive when a shape is not selected in the connector context. + * + * - When creating/editing connection points: + * + * - Strange things can happen if you have connectors selected, or + * try rerouting connectors by dragging their endpoints when in + * connection point editing mode. + * + * - Possibly the selected shape's connection points should always + * be shown (i.e., have knots) when in editing mode. + * + * - It is a little strange to be able to place connection points + * competely outside shapes. Especially when you later can't draw + * connectors to them since the knots are only visible when you + * are over the shape. I think that you should only be able to + * place connection points inside or on the boundary of the shape + * itself. + * + * - The intended ability to place a new point at the current cursor + * position by pressing RETURN does not seem to work. + * + * - The Status bar tooltip should change to reflect editing mode + * and tell the user about RETURN and how to use the tool. + * + * - Connection points general: + * + * - Connection points that were inside the shape can end up outside + * after a rotation is applied to the shape in the select tool. + * It doesn't seem like the correct transform is being applied to + * these, or it is being applied at the wrong time. I'd expect + * connection points to rotate with the shape, and stay at the + * same position "on the shape" + * + * - I was able to make the connectors attached to a shape fall off + * the shape after scaling it. Not sure the exact cause, but may + * require more investigation/debugging. + * + * - The user-defined connection points should be either absolute + * (as the current ones are) or defined as a percentage of the + * shape. These would be based on a toggle setting on the + * toolbar, and they would be placed in exactly the same way by + * the user. The only difference would be that they would be + * store as percentage positions in the SVG connection-points + * property and that they would update/move automatically if the + * object was resized or scaled. + * + * - Thinking more, I think you always want to store and think about + * the positions of connection points to be pre-transform, but + * obviously the shape transform is applied to them. That way, + * they will rotate and scale automatically with the shape, when + * the shape transform is altered. The Percentage version would + * compute their position from the pre-transform dimensions and + * then have the transform applied to them, for example. + * + * - The connection points in the test_connection_points.svg file + * seem to follow the shape when it is moved, but connection + * points I add to new shapes, do not follow the shape, either + * when the shape is just moved or transformed. There is + * something wrong here. What exactly should the behaviour be + * currently? + * + * - I see that connection points are specified at absolute canvas + * positions. I really think that they should be specified in + * shape coordinated relative to the shapes. There may be + * transforms applied to layers and the canvas which would make + * specifying them quite difficult. I'd expect a position of 0, 0 + * to be on the shape in question or very close to it, for example. * */ + + #include #include #include #include "connector-context.h" #include "pixmaps/cursor-connector.xpm" +#include "pixmaps/cursor-node.xpm" +#include "pixmaps/cursor-node-m.xpm" +#include "pixmaps/cursor-node-d.xpm" #include "xml/node-event-vector.h" #include "xml/repr.h" #include "svg/svg.h" @@ -63,11 +174,14 @@ #include "display/canvas-bpath.h" #include "display/sodipodi-ctrl.h" #include +#include #include "snap.h" #include "knot.h" #include "sp-conn-end.h" +#include "sp-conn-end-pair.h" #include "conn-avoid-ref.h" #include "libavoid/vertices.h" +#include "libavoid/router.h" #include "context-fns.h" #include "sp-namedview.h" #include "sp-text.h" @@ -79,6 +193,7 @@ static void sp_connector_context_init(SPConnectorContext *conn_context); static void sp_connector_context_dispose(GObject *object); static void sp_connector_context_setup(SPEventContext *ec); +static void sp_connector_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val); static void sp_connector_context_finish(SPEventContext *ec); static gint sp_connector_context_root_handler(SPEventContext *ec, GdkEvent *event); static gint sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event); @@ -98,11 +213,14 @@ static gint connector_handle_motion_notify(SPConnectorContext *const cc, GdkEven static gint connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton const &revent); static gint connector_handle_key_press(SPConnectorContext *const cc, guint const keyval); +static void cc_active_shape_add_knot(SPDesktop* desktop, SPItem* item, ConnectionPointMap &cphandles, ConnectionPoint& cp); static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item); static void cc_clear_active_shape(SPConnectorContext *cc); static void cc_set_active_conn(SPConnectorContext *cc, SPItem *item); static void cc_clear_active_conn(SPConnectorContext *cc); static gchar *conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& w); +static void cc_select_handle(SPKnot* knot); +static void cc_deselect_handle(SPKnot* knot); static bool cc_item_is_shape(SPItem *item); static void cc_selection_changed(Inkscape::Selection *selection, gpointer data); static void cc_connector_rerouting_finish(SPConnectorContext *const cc, @@ -115,8 +233,8 @@ static void shape_event_attr_changed(Inkscape::XML::Node *repr, gchar const *nam gpointer data); -static Geom::Point connector_drag_origin_w(0, 0); -static bool connector_within_tolerance = false; +/*static Geom::Point connector_drag_origin_w(0, 0); +static bool connector_within_tolerance = false;*/ static SPEventContextClass *parent_class; @@ -171,6 +289,7 @@ sp_connector_context_class_init(SPConnectorContextClass *klass) object_class->dispose = sp_connector_context_dispose; event_context_class->setup = sp_connector_context_setup; + event_context_class->set = sp_connector_context_set; event_context_class->finish = sp_connector_context_finish; event_context_class->root_handler = sp_connector_context_root_handler; event_context_class->item_handler = sp_connector_context_item_handler; @@ -188,10 +307,14 @@ sp_connector_context_init(SPConnectorContext *cc) ec->xp = 0; ec->yp = 0; + cc->mode = SP_CONNECTOR_CONTEXT_DRAWING_MODE; + cc->knot_tip = 0; + cc->red_color = 0xff00007f; cc->newconn = NULL; cc->newConnRef = NULL; + cc->curvature = 0.0; cc->sel_changed_connection = sigc::connection(); @@ -204,10 +327,13 @@ sp_connector_context_init(SPConnectorContext *cc) cc->active_handle = NULL; + cc->selected_handle = NULL; + cc->clickeditem = NULL; cc->clickedhandle = NULL; - cc->connpthandle = NULL; + new (&cc->connpthandles) ConnectionPointMap(); + for (int i = 0; i < 2; ++i) { cc->endpt_handle[i] = NULL; cc->endpt_handler_id[i] = 0; @@ -226,10 +352,14 @@ sp_connector_context_dispose(GObject *object) cc->sel_changed_connection.disconnect(); - if (cc->connpthandle) { - g_object_unref(cc->connpthandle); - cc->connpthandle = NULL; + if (!cc->connpthandles.empty()) { + for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); + it != cc->connpthandles.end(); ++it) { + g_object_unref(it->first); + } + cc->connpthandles.clear(); } + cc->connpthandles.~ConnectionPointMap(); for (int i = 0; i < 2; ++i) { if (cc->endpt_handle[1]) { g_object_unref(cc->endpt_handle[i]); @@ -282,6 +412,12 @@ sp_connector_context_setup(SPEventContext *ec) // Notice the initial selection. cc_selection_changed(cc->selection, (gpointer) cc); + cc->within_tolerance = false; + + sp_event_context_read(ec, "curvature"); + sp_event_context_read(ec, "orthogonal"); + sp_event_context_read(ec, "mode"); + cc->knot_tip = cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE ? cc_knot_tips[0] : cc_knot_tips[1]; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (prefs->getBool("/tools/connector/selcue", 0)) { ec->enableSelectionCue(); @@ -293,6 +429,64 @@ sp_connector_context_setup(SPEventContext *ec) } +static void +sp_connector_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val) +{ + SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec); + + /* fixme: Proper error handling for non-numeric data. Use a locale-independent function like + * g_ascii_strtod (or a thin wrapper that does the right thing for invalid values inf/nan). */ + Glib::ustring name = val->getEntryName(); + if ( name == "curvature" ) { + cc->curvature = val->getDoubleLimited(); // prevents NaN and +/-Inf from messing up + } + else if ( name == "orthogonal" ) { + cc->isOrthogonal = val->getBool(); + } + else if ( name == "mode") + { + sp_connector_context_switch_mode(ec, val->getBool() ? SP_CONNECTOR_CONTEXT_EDITING_MODE : SP_CONNECTOR_CONTEXT_DRAWING_MODE); + } +} + +void sp_connector_context_switch_mode(SPEventContext* ec, unsigned int newMode) +{ + SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec); + + cc->mode = newMode; + if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE ) + { + ec->cursor_shape = cursor_connector_xpm; + cc->knot_tip = cc_knot_tips[0]; + if (cc->selected_handle) + cc_deselect_handle( cc->selected_handle ); + cc->selected_handle = NULL; + // Show all default connection points + + } + else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE ) + { + ec->cursor_shape = cursor_node_xpm; + cc->knot_tip = cc_knot_tips[1]; +/* if (cc->active_shape) + { + cc->selection->set( SP_OBJECT( cc->active_shape ) ); + } + else + { + SPItem* item = cc->selection->singleItem(); + if ( item ) + { + cc_set_active_shape(cc, item); + cc->selection->set( SP_OBJECT( item ) ); + } + }*/ + } + sp_event_context_update_cursor(ec); + +} + + static void sp_connector_context_finish(SPEventContext *ec) { @@ -341,9 +535,12 @@ cc_clear_active_shape(SPConnectorContext *cc) cc->active_shape_layer_repr = NULL; } - // Hide the center connection point if it exists. - if (cc->connpthandle) { - sp_knot_hide(cc->connpthandle); + // Hide the connection points if they exist. + if (cc->connpthandles.size()) { + for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); + it != cc->connpthandles.end(); ++it) { + sp_knot_hide(it->first); + } } } @@ -379,16 +576,35 @@ conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p) // TODO: this will need to change when there are more connection // points available for each shape. - SPKnot *centerpt = cc->connpthandle; - if (cc->active_handle && (cc->active_handle == centerpt)) + if (cc->active_handle && (cc->connpthandles.find(cc->active_handle) != cc->connpthandles.end())) { - p = centerpt->pos; - return g_strdup_printf("#%s", SP_OBJECT_ID(cc->active_shape)); + p = cc->active_handle->pos; + const ConnectionPoint& cp = cc->connpthandles[cc->active_handle]; + return g_strdup_printf("#%s_%c_%d", SP_OBJECT_ID(cc->active_shape), + cp.type == ConnPointDefault ? 'd' : 'u' , cp.id); } return NULL; } +static void +cc_select_handle(SPKnot* knot) +{ + knot->setShape(SP_KNOT_SHAPE_SQUARE); + knot->setSize(10); + knot->setAnchor(GTK_ANCHOR_CENTER); + knot->setFill(0x0000ffff, 0x0000ffff, 0x0000ffff); + sp_knot_update_ctrl(knot); +} +static void +cc_deselect_handle(SPKnot* knot) +{ + knot->setShape(SP_KNOT_SHAPE_SQUARE); + knot->setSize(8); + knot->setAnchor(GTK_ANCHOR_CENTER); + knot->setFill(0xffffff00, 0xff0000ff, 0xff0000ff); + sp_knot_update_ctrl(knot); +} static gint sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event) @@ -400,29 +616,40 @@ sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, G SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(event_context); Geom::Point p(event->button.x, event->button.y); - + switch (event->type) { case GDK_BUTTON_RELEASE: if (event->button.button == 1 && !event_context->space_panning) { if ((cc->state == SP_CONNECTOR_CONTEXT_DRAGGING) && - (connector_within_tolerance)) + (event_context->within_tolerance)) { spcc_reset_colors(cc); cc->state = SP_CONNECTOR_CONTEXT_IDLE; - sp_event_context_discard_delayed_snap_event(event_context); +// sp_event_context_snap_window_closed(event_context); } if (cc->state != SP_CONNECTOR_CONTEXT_IDLE) { - // Doing simething else like rerouting. + // Doing something else like rerouting. break; } - // find out clicked item, disregarding groups, honoring Alt - SPItem *item_ungrouped = sp_event_context_find_item(desktop, - p, event->button.state & GDK_MOD1_MASK, TRUE); + // find out clicked item, honoring Alt + SPItem *item = sp_event_context_find_item(desktop, + p, event->button.state & GDK_MOD1_MASK, FALSE); if (event->button.state & GDK_SHIFT_MASK) { - cc->selection->toggle(item_ungrouped); + cc->selection->toggle(item); } else { - cc->selection->set(item_ungrouped); + cc->selection->set(item); + if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE && cc->selected_handle ) + { + cc_deselect_handle( cc->selected_handle ); + cc->selected_handle = NULL; + } + /* When selecting a new item, + do not allow showing connection points + on connectors. (yet?) + */ + if ( item != cc->active_shape && !cc_item_is_connector( item ) ) + cc_set_active_shape( cc, item ); } ret = TRUE; @@ -430,15 +657,24 @@ sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, G break; case GDK_ENTER_NOTIFY: { - if (cc_item_is_shape(item)) { - // This is a shape, so show connection point(s). - if (!(cc->active_shape) || - // Don't show handle for another handle. - (item != ((SPItem *) cc->connpthandle))) { + if (cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE || (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE && !cc->selected_handle)) + { + if (cc_item_is_shape(item)) { + + // I don't really understand what the above does, + // so I commented it. + // This is a shape, so show connection point(s). + /* if (!(cc->active_shape) + // Don't show handle for another handle. + // || (cc->connpthandles.find((SPKnot*) item) != cc->connpthandles.end()) + ) + { + cc_set_active_shape(cc, item); + }*/ cc_set_active_shape(cc, item); } + ret = TRUE; } - ret = TRUE; break; } default: @@ -462,7 +698,7 @@ sp_connector_context_root_handler(SPEventContext *ec, GdkEvent *event) break; case GDK_MOTION_NOTIFY: - ret = connector_handle_motion_notify(cc, event->motion); + ret = connector_handle_motion_notify(cc, event->motion); break; case GDK_BUTTON_RELEASE: @@ -497,96 +733,166 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const SPEventContext *event_context = SP_EVENT_CONTEXT(cc); gint ret = FALSE; - if ( bevent.button == 1 && !event_context->space_panning ) { + if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE ) + { + if ( bevent.button == 1 && !event_context->space_panning ) { - SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); + SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); - if (Inkscape::have_viable_layer(desktop, cc->_message_context) == false) { - return TRUE; - } + if (Inkscape::have_viable_layer(desktop, cc->_message_context) == false) { + return TRUE; + } - Geom::Point const event_w(bevent.x, - bevent.y); - connector_drag_origin_w = event_w; - connector_within_tolerance = true; + Geom::Point const event_w(bevent.x, + bevent.y); +// connector_drag_origin_w = event_w; + cc->xp = bevent.x; + cc->yp = bevent.y; + cc->within_tolerance = true; - Geom::Point const event_dt = cc->desktop->w2d(event_w); + Geom::Point const event_dt = cc->desktop->w2d(event_w); - SnapManager &m = cc->desktop->namedview->snap_manager; - m.setup(cc->desktop); + SnapManager &m = cc->desktop->namedview->snap_manager; + m.setup(cc->desktop); - switch (cc->state) { - case SP_CONNECTOR_CONTEXT_STOP: - /* This is allowed, if we just cancelled curve */ - case SP_CONNECTOR_CONTEXT_IDLE: - { - if ( cc->npoints == 0 ) { - cc_clear_active_conn(cc); + switch (cc->state) { + case SP_CONNECTOR_CONTEXT_STOP: + /* This is allowed, if we just cancelled curve */ + case SP_CONNECTOR_CONTEXT_IDLE: + { + if ( cc->npoints == 0 ) { + cc_clear_active_conn(cc); - SP_EVENT_CONTEXT_DESKTOP(cc)->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Creating new connector")); + SP_EVENT_CONTEXT_DESKTOP(cc)->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Creating new connector")); - /* Set start anchor */ - /* Create green anchor */ - Geom::Point p = event_dt; + /* Set start anchor */ + /* Create green anchor */ + Geom::Point p = event_dt; - // Test whether we clicked on a connection point - cc->sid = conn_pt_handle_test(cc, p); + // Test whether we clicked on a connection point + cc->sid = conn_pt_handle_test(cc, p); + +// sp_event_context_snap_window_open(event_context); + + if (!cc->sid) { + // This is the first point, so just snap it to the grid + // as there's no other points to go off. + m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + } + spcc_connector_set_initial_point(cc, p); - if (!cc->sid) { - // This is the first point, so just snap it to the grid - // as there's no other points to go off. - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); } - spcc_connector_set_initial_point(cc, p); + cc->state = SP_CONNECTOR_CONTEXT_DRAGGING; + ret = TRUE; + break; + } + case SP_CONNECTOR_CONTEXT_DRAGGING: + { + // This is the second click of a connector creation. + m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + spcc_connector_set_subsequent_point(cc, p); + spcc_connector_finish_segment(cc, p); + // Test whether we clicked on a connection point + cc->eid = conn_pt_handle_test(cc, p); + if (cc->npoints != 0) { + spcc_connector_finish(cc); + } + cc_set_active_conn(cc, cc->newconn); + cc->state = SP_CONNECTOR_CONTEXT_IDLE; +// sp_event_context_snap_window_closed(event_context); + ret = TRUE; + break; } - cc->state = SP_CONNECTOR_CONTEXT_DRAGGING; - ret = TRUE; - break; + case SP_CONNECTOR_CONTEXT_CLOSE: + { + g_warning("Button down in CLOSE state"); + break; + } + default: + break; } - case SP_CONNECTOR_CONTEXT_DRAGGING: - { - // This is the second click of a connector creation. - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + } else if (bevent.button == 3) { + if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) { + // A context menu is going to be triggered here, + // so end the rerouting operation. + cc_connector_rerouting_finish(cc, &p); - spcc_connector_set_subsequent_point(cc, p); - spcc_connector_finish_segment(cc, p); - // Test whether we clicked on a connection point - cc->eid = conn_pt_handle_test(cc, p); - if (cc->npoints != 0) { - spcc_connector_finish(cc); - } - cc_set_active_conn(cc, cc->newconn); cc->state = SP_CONNECTOR_CONTEXT_IDLE; - sp_event_context_discard_delayed_snap_event(event_context); - ret = TRUE; - break; +// sp_event_context_snap_window_closed(event_context); + + // Don't set ret to TRUE, so we drop through to the + // parent handler which will open the context menu. } - case SP_CONNECTOR_CONTEXT_CLOSE: - { - g_warning("Button down in CLOSE state"); - break; + else if (cc->npoints != 0) { + spcc_connector_finish(cc); + cc->state = SP_CONNECTOR_CONTEXT_IDLE; +// sp_event_context_snap_window_closed(event_context); + ret = TRUE; } - default: - break; } - } else if (bevent.button == 3) { - if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) { - // A context menu is going to be triggered here, - // so end the rerouting operation. - cc_connector_rerouting_finish(cc, &p); + } + else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE ) + { + if ( bevent.button == 1 && !event_context->space_panning ) + { + // Initialize variables in case of dragging - cc->state = SP_CONNECTOR_CONTEXT_IDLE; - sp_event_context_discard_delayed_snap_event(event_context); + SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); - // Don't set ret to TRUE, so we drop through to the - // parent handler which will open the context menu. - } - else if (cc->npoints != 0) { - spcc_connector_finish(cc); - cc->state = SP_CONNECTOR_CONTEXT_IDLE; - sp_event_context_discard_delayed_snap_event(event_context); - ret = TRUE; + if (Inkscape::have_viable_layer(desktop, cc->_message_context) == false) { + return TRUE; + } + + cc->xp = bevent.x; + cc->yp = bevent.y; + cc->within_tolerance = true; + + ConnectionPointMap::iterator const& active_knot_it = cc->connpthandles.find( cc->active_handle ); + + switch (cc->state) + { + case SP_CONNECTOR_CONTEXT_IDLE: + if ( active_knot_it != cc->connpthandles.end() ) + { + // We do not allow selecting and, thereby, moving default knots + if ( active_knot_it->second.type != ConnPointDefault) + { + if (cc->selected_handle != cc->active_handle) + { + if ( cc->selected_handle ) + cc_deselect_handle( cc->selected_handle ); + cc->selected_handle = cc->active_handle; + cc_select_handle( cc->selected_handle ); + } + } + else + // Just ignore the default connection point + return FALSE; + } + else + if ( cc->selected_handle ) + { + cc_deselect_handle( cc->selected_handle ); + cc->selected_handle = NULL; + } + + if ( cc->selected_handle ) + { +// sp_event_context_snap_window_open(event_context); + cc->state = SP_CONNECTOR_CONTEXT_DRAGGING; + cc->selection->set( SP_OBJECT( cc->active_shape ) ); + } + + ret = TRUE; + break; + // Dragging valid because of the way we create + // new connection points. + case SP_CONNECTOR_CONTEXT_DRAGGING: + // Do nothing. + ret = TRUE; + break; + } } } return ret; @@ -607,71 +913,94 @@ connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion cons Geom::Point const event_w(mevent.x, mevent.y); - if (connector_within_tolerance) { - gint const tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); - if ( Geom::LInfty( event_w - connector_drag_origin_w ) < tolerance ) { + if (cc->within_tolerance) { + cc->tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); + if ( ( abs( (gint) mevent.x - cc->xp ) < cc->tolerance ) && + ( abs( (gint) mevent.y - cc->yp ) < cc->tolerance ) ) { return FALSE; // Do not drag if we're within tolerance from origin. } } // Once the user has moved farther than tolerance from the original location // (indicating they intend to move the object, not click), then always process // the motion notify coordinates as given (no snapping back to origin) - connector_within_tolerance = false; + cc->within_tolerance = false; SPDesktop *const dt = cc->desktop; /* Find desktop coordinates */ Geom::Point p = dt->w2d(event_w); - SnapManager &m = dt->namedview->snap_manager; - m.setup(dt); - - switch (cc->state) { - case SP_CONNECTOR_CONTEXT_DRAGGING: - { - // This is movement during a connector creation. - if ( cc->npoints > 0 ) { - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); - cc->selection->clear(); - spcc_connector_set_subsequent_point(cc, p); - ret = TRUE; + if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE ) + { + SnapManager &m = dt->namedview->snap_manager; + m.setup(dt); + + switch (cc->state) { + case SP_CONNECTOR_CONTEXT_DRAGGING: + { + gobble_motion_events(mevent.state); + // This is movement during a connector creation. + if ( cc->npoints > 0 ) { + m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, + Inkscape::SNAPSOURCE_HANDLE); + cc->selection->clear(); + spcc_connector_set_subsequent_point(cc, p); + ret = TRUE; + } + break; } - break; - } - case SP_CONNECTOR_CONTEXT_REROUTING: - { - g_assert( SP_IS_PATH(cc->clickeditem)); + case SP_CONNECTOR_CONTEXT_REROUTING: + { + gobble_motion_events(GDK_BUTTON1_MASK); + g_assert( SP_IS_PATH(cc->clickeditem)); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, + Inkscape::SNAPSOURCE_HANDLE); - // Update the hidden path - Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem); - Geom::Matrix d2i = i2d.inverse(); - SPPath *path = SP_PATH(cc->clickeditem); - SPCurve *curve = (SP_SHAPE(path))->curve; - if (cc->clickedhandle == cc->endpt_handle[0]) { - Geom::Point o = cc->endpt_handle[1]->pos; - curve->stretch_endpoints(p * d2i, o * d2i); - } - else { - Geom::Point o = cc->endpt_handle[0]->pos; - curve->stretch_endpoints(o * d2i, p * d2i); - } - sp_conn_adjust_path(path); + // Update the hidden path + Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem); + Geom::Matrix d2i = i2d.inverse(); + SPPath *path = SP_PATH(cc->clickeditem); + SPCurve *curve = path->original_curve ? path->original_curve : path->curve; + if (cc->clickedhandle == cc->endpt_handle[0]) { + Geom::Point o = cc->endpt_handle[1]->pos; + curve->stretch_endpoints(p * d2i, o * d2i); + } + else { + Geom::Point o = cc->endpt_handle[0]->pos; + curve->stretch_endpoints(o * d2i, p * d2i); + } + sp_conn_reroute_path_immediate(path); - // Copy this to the temporary visible path - cc->red_curve = SP_SHAPE(path)->curve->copy(); - cc->red_curve->transform(i2d); + // Copy this to the temporary visible path + cc->red_curve = path->original_curve ? + path->original_curve->copy() : path->curve->copy(); + cc->red_curve->transform(i2d); - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve); - ret = TRUE; - break; + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve); + ret = TRUE; + break; + } + case SP_CONNECTOR_CONTEXT_STOP: + /* This is perfectly valid */ + break; + default: + break; + } + } + else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE ) + { + switch ( cc->state ) + { + case SP_CONNECTOR_CONTEXT_DRAGGING: + sp_knot_set_position(cc->selected_handle, p, 0); + ret = TRUE; + break; + case SP_CONNECTOR_CONTEXT_NEWCONNPOINT: + sp_knot_set_position(cc->selected_handle, p, 0); + ret = TRUE; + break; } - case SP_CONNECTOR_CONTEXT_STOP: - /* This is perfectly valid */ - break; - default: - break; } return ret; @@ -685,60 +1014,111 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con SPEventContext *event_context = SP_EVENT_CONTEXT(cc); if ( revent.button == 1 && !event_context->space_panning ) { - SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); + SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); SPDocument *doc = sp_desktop_document(desktop); SnapManager &m = desktop->namedview->snap_manager; - m.setup(desktop); + m.setup(desktop); Geom::Point const event_w(revent.x, revent.y); /* Find desktop coordinates */ Geom::Point p = cc->desktop->w2d(event_w); - - switch (cc->state) { - //case SP_CONNECTOR_CONTEXT_POINT: - case SP_CONNECTOR_CONTEXT_DRAGGING: - { - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); - - if (connector_within_tolerance) + if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE ) + { + switch (cc->state) { + //case SP_CONNECTOR_CONTEXT_POINT: + case SP_CONNECTOR_CONTEXT_DRAGGING: { + m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + + if (cc->within_tolerance) + { + spcc_connector_finish_segment(cc, p); + return TRUE; + } + // Connector has been created via a drag, end it now. + spcc_connector_set_subsequent_point(cc, p); spcc_connector_finish_segment(cc, p); - return TRUE; + // Test whether we clicked on a connection point + cc->eid = conn_pt_handle_test(cc, p); + if (cc->npoints != 0) { + spcc_connector_finish(cc); + } + cc_set_active_conn(cc, cc->newconn); + cc->state = SP_CONNECTOR_CONTEXT_IDLE; +// sp_event_context_snap_window_closed(event_context); + break; } - // Connector has been created via a drag, end it now. - spcc_connector_set_subsequent_point(cc, p); - spcc_connector_finish_segment(cc, p); - // Test whether we clicked on a connection point - cc->eid = conn_pt_handle_test(cc, p); - if (cc->npoints != 0) { - spcc_connector_finish(cc); + case SP_CONNECTOR_CONTEXT_REROUTING: + { + m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + cc_connector_rerouting_finish(cc, &p); + + sp_document_ensure_up_to_date(doc); + cc->state = SP_CONNECTOR_CONTEXT_IDLE; +// sp_event_context_snap_window_closed(event_context); + return TRUE; + break; } - cc_set_active_conn(cc, cc->newconn); - cc->state = SP_CONNECTOR_CONTEXT_IDLE; - sp_event_context_discard_delayed_snap_event(event_context); - break; + case SP_CONNECTOR_CONTEXT_STOP: + /* This is allowed, if we just cancelled curve */ + break; + default: + break; } - case SP_CONNECTOR_CONTEXT_REROUTING: + ret = TRUE; + } + else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE ) + { + switch ( cc->state ) { - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); - cc_connector_rerouting_finish(cc, &p); + case SP_CONNECTOR_CONTEXT_DRAGGING: + + if (!cc->within_tolerance) + { +// sp_event_context_snap_window_open(event_context); + m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + sp_knot_set_position(cc->selected_handle, p, 0); +// sp_event_context_snap_window_closed(event_context); + ConnectionPoint& cp = cc->connpthandles[cc->selected_handle]; + cp.pos = p * sp_item_dt2i_affine(cc->active_shape); + cc->active_shape->avoidRef->updateConnectionPoint(cp); + } - sp_document_ensure_up_to_date(doc); - cc->state = SP_CONNECTOR_CONTEXT_IDLE; - sp_event_context_discard_delayed_snap_event(event_context); - return TRUE; - break; + cc->state = SP_CONNECTOR_CONTEXT_IDLE; + ret = TRUE; + break; + + + case SP_CONNECTOR_CONTEXT_NEWCONNPOINT: +// sp_event_context_snap_window_open( event_context ); + m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + + sp_knot_set_position(cc->selected_handle, p, 0); +// sp_event_context_snap_window_closed(event_context); + + ConnectionPoint cp; + cp.type = ConnPointUserDefined; + cp.pos = p * sp_item_dt2i_affine(cc->active_shape); + cp.dir = Avoid::ConnDirAll; + g_object_unref(cc->selected_handle); + cc->active_shape->avoidRef->addConnectionPoint(cp); + sp_document_ensure_up_to_date(doc); + for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); it != cc->connpthandles.end(); ++it) + if (it->second.type == ConnPointUserDefined && it->second.id == cp.id) + { + cc->selected_handle = it->first; + break; + } + cc_select_handle( cc->selected_handle ); + cc->state = SP_CONNECTOR_CONTEXT_IDLE; + ret = TRUE; + break; } - case SP_CONNECTOR_CONTEXT_STOP: - /* This is allowed, if we just cancelled curve */ - break; - default: - break; } - ret = TRUE; } + return ret; } @@ -749,43 +1129,145 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) { gint ret = FALSE; /* fixme: */ - switch (keyval) { - case GDK_Return: - case GDK_KP_Enter: - if (cc->npoints != 0) { - spcc_connector_finish(cc); - cc->state = SP_CONNECTOR_CONTEXT_IDLE; - sp_event_context_discard_delayed_snap_event(SP_EVENT_CONTEXT(cc)); - ret = TRUE; - } - break; - case GDK_Escape: - if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) { + if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE ) + { + switch (keyval) { + case GDK_Return: + case GDK_KP_Enter: + if (cc->npoints != 0) { + spcc_connector_finish(cc); + cc->state = SP_CONNECTOR_CONTEXT_IDLE; +// sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc)); + ret = TRUE; + } + break; + case GDK_Escape: + if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) { - SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); - SPDocument *doc = sp_desktop_document(desktop); + SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); + SPDocument *doc = sp_desktop_document(desktop); - cc_connector_rerouting_finish(cc, NULL); + cc_connector_rerouting_finish(cc, NULL); - sp_document_undo(doc); + sp_document_undo(doc); - cc->state = SP_CONNECTOR_CONTEXT_IDLE; - sp_event_context_discard_delayed_snap_event(SP_EVENT_CONTEXT(cc)); - desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE, - _("Connector endpoint drag cancelled.")); - ret = TRUE; - } - else if (cc->npoints != 0) { - // if drawing, cancel, otherwise pass it up for deselecting - cc->state = SP_CONNECTOR_CONTEXT_STOP; - sp_event_context_discard_delayed_snap_event(SP_EVENT_CONTEXT(cc)); - spcc_reset_colors(cc); - ret = TRUE; - } - break; - default: - break; + cc->state = SP_CONNECTOR_CONTEXT_IDLE; +// sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc)); + desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE, + _("Connector endpoint drag cancelled.")); + ret = TRUE; + } + else if (cc->npoints != 0) { + // if drawing, cancel, otherwise pass it up for deselecting + cc->state = SP_CONNECTOR_CONTEXT_STOP; +// sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc)); + spcc_reset_colors(cc); + ret = TRUE; + } + break; + default: + break; + } + } + else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE ) + { + switch ( cc->state ) + { + case SP_CONNECTOR_CONTEXT_DRAGGING: + if ( keyval == GDK_Escape ) + { + // Cancel connection point dragging + + // Obtain original position + ConnectionPoint const& cp = cc->connpthandles[cc->selected_handle]; + SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); + const Geom::Matrix& i2doc = sp_item_i2doc_affine(cc->active_shape); + sp_knot_set_position(cc->selected_handle, cp.pos * i2doc * desktop->doc2dt(), 0); + cc->state = SP_CONNECTOR_CONTEXT_IDLE; + desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE, + _("Connection point drag cancelled.")); + ret = TRUE; + } + else if ( keyval == GDK_Return || keyval == GDK_KP_Enter ) + { + // Put connection point at current position + + SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + Geom::Point p = cc->selected_handle->pos; + SPEventContext* event_context = SP_EVENT_CONTEXT( cc ); + + if (!cc->within_tolerance) + { +// sp_event_context_snap_window_open(event_context); + m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + sp_knot_set_position(cc->selected_handle, p, 0); +// sp_event_context_snap_window_closed(event_context); + ConnectionPoint& cp = cc->connpthandles[cc->selected_handle]; + cp.pos = p * sp_item_dt2i_affine(cc->active_shape); + cc->active_shape->avoidRef->updateConnectionPoint(cp); + } + + cc->state = SP_CONNECTOR_CONTEXT_IDLE; + ret = TRUE; + } + break; + case SP_CONNECTOR_CONTEXT_NEWCONNPOINT: + if ( keyval == GDK_Escape ) + { + // Just destroy the knot + g_object_unref( cc->selected_handle ); + cc->selected_handle = NULL; + cc->state = SP_CONNECTOR_CONTEXT_IDLE; + ret = TRUE; + } + else if ( keyval == GDK_Return || keyval == GDK_KP_Enter ) + { + SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc); + SPDocument *doc = sp_desktop_document(desktop); + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + Geom::Point p = cc->selected_handle->pos; + SPEventContext* event_context = SP_EVENT_CONTEXT( cc ); + +// sp_event_context_snap_window_open( event_context ); + m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + + sp_knot_set_position(cc->selected_handle, p, 0); +// sp_event_context_snap_window_closed(event_context); + + ConnectionPoint cp; + cp.type = ConnPointUserDefined; + cp.pos = p * sp_item_dt2i_affine(cc->active_shape); + cp.dir = Avoid::ConnDirAll; + g_object_unref(cc->selected_handle); + cc->active_shape->avoidRef->addConnectionPoint(cp); + sp_document_ensure_up_to_date(doc); + for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); it != cc->connpthandles.end(); ++it) + if (it->second.type == ConnPointUserDefined && it->second.id == cp.id) + { + cc->selected_handle = it->first; + break; + } + cc_select_handle( cc->selected_handle ); + cc->state = SP_CONNECTOR_CONTEXT_IDLE; + ret = TRUE; + } + + break; + case SP_CONNECTOR_CONTEXT_IDLE: + if ( keyval == GDK_Delete && cc->selected_handle ) + { + cc->active_shape->avoidRef->deleteConnectionPoint(cc->connpthandles[cc->selected_handle]); + cc->selected_handle = NULL; + ret = TRUE; + } + + break; + } } + return ret; } @@ -818,7 +1300,7 @@ cc_connector_rerouting_finish(SPConnectorContext *const cc, Geom::Point *const p } } cc->clickeditem->setHidden(false); - sp_conn_adjust_path(SP_PATH(cc->clickeditem)); + sp_conn_reroute_path_immediate(SP_PATH(cc->clickeditem)); cc->clickeditem->updateRepr(); sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR, _("Reroute connector")); @@ -863,25 +1345,20 @@ spcc_connector_set_subsequent_point(SPConnectorContext *const cc, Geom::Point co if (!cc->newConnRef) { Avoid::Router *router = sp_desktop_document(dt)->router; - cc->newConnRef = new Avoid::ConnRef(router, 0, src, dst); - cc->newConnRef->updateEndPoint(Avoid::VertID::src, src); + cc->newConnRef = new Avoid::ConnRef(router); + cc->newConnRef->setEndpoint(Avoid::VertID::src, src); + if (cc->isOrthogonal) + cc->newConnRef->setRoutingType(Avoid::ConnType_Orthogonal); + else + cc->newConnRef->setRoutingType(Avoid::ConnType_PolyLine); } - cc->newConnRef->updateEndPoint(Avoid::VertID::tar, dst); - + // Set new endpoint. + cc->newConnRef->setEndpoint(Avoid::VertID::tar, dst); + // Immediately generate new routes for connector. cc->newConnRef->makePathInvalid(); - cc->newConnRef->generatePath(src, dst); - - Avoid::PolyLine route = cc->newConnRef->route(); - cc->newConnRef->calcRouteDist(); - - cc->red_curve->reset(); - Geom::Point pt(route.ps[0].x, route.ps[0].y); - cc->red_curve->moveto(pt); - - for (int i = 1; i < route.pn; ++i) { - Geom::Point p(route.ps[i].x, route.ps[i].y); - cc->red_curve->lineto(p); - } + cc->newConnRef->router()->processTransaction(); + // Recreate curve from libavoid route. + recreateCurve( cc->red_curve, cc->newConnRef, cc->curvature ); cc->red_curve->transform(dt->doc2dt()); sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve); } @@ -953,14 +1430,13 @@ spcc_flush_white(SPConnectorContext *cc, SPCurve *gc) /* Attach repr */ cc->newconn = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr)); - cc->selection->set(repr); - Inkscape::GC::release(repr); cc->newconn->transform = sp_item_i2doc_affine(SP_ITEM(desktop->currentLayer())).inverse(); - cc->newconn->updateRepr(); bool connection = false; sp_object_setAttribute(cc->newconn, "inkscape:connector-type", - "polyline", false); + cc->isOrthogonal ? "orthogonal" : "polyline", false); + sp_object_setAttribute(cc->newconn, "inkscape:connector-curvature", + Glib::Ascii::dtostr(cc->curvature).c_str(), false); if (cc->sid) { sp_object_setAttribute(cc->newconn, "inkscape:connection-start", @@ -974,19 +1450,27 @@ spcc_flush_white(SPConnectorContext *cc, SPCurve *gc) cc->eid, false); connection = true; } + // Process pending updates. cc->newconn->updateRepr(); + sp_document_ensure_up_to_date(doc); + if (connection) { // Adjust endpoints to shape edge. - sp_conn_adjust_path(SP_PATH(cc->newconn)); + sp_conn_reroute_path_immediate(SP_PATH(cc->newconn)); + cc->newconn->updateRepr(); } - cc->newconn->updateRepr(); + + // Only set the selection after we are finished with creating the attributes of + // the connector. Otherwise, the selection change may alter the defaults for + // values like curvature in the connector context, preventing subsequent lookup + // of their original values. + cc->selection->set(repr); + Inkscape::GC::release(repr); } c->unref(); - /* Flush pending updates */ sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR, _("Create connector")); - sp_document_ensure_up_to_date(doc); } @@ -1036,16 +1520,16 @@ cc_generic_knot_handler(SPCanvasItem *, GdkEvent *event, SPKnot *knot) gboolean consumed = FALSE; + gchar* knot_tip = knot->tip ? knot->tip : cc->knot_tip; switch (event->type) { case GDK_ENTER_NOTIFY: sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, TRUE); cc->active_handle = knot; - - if (knot->tip) + if (knot_tip) { knot->desktop->event_context->defaultMessageContext()->set( - Inkscape::NORMAL_MESSAGE, knot->tip); + Inkscape::NORMAL_MESSAGE, knot_tip); } consumed = TRUE; @@ -1055,7 +1539,7 @@ cc_generic_knot_handler(SPCanvasItem *, GdkEvent *event, SPKnot *knot) cc->active_handle = NULL; - if (knot->tip) { + if (knot_tip) { knot->desktop->event_context->defaultMessageContext()->clear(); } @@ -1087,6 +1571,7 @@ endpt_handler(SPKnot */*knot*/, GdkEvent *event, SPConnectorContext *cc) cc->clickedhandle = cc->active_handle; cc_clear_active_conn(cc); cc->state = SP_CONNECTOR_CONTEXT_REROUTING; +// sp_event_context_snap_window_open(SP_EVENT_CONTEXT(cc)); // Disconnect from attached shape unsigned ind = (cc->active_handle == cc->endpt_handle[0]) ? 0 : 1; @@ -1101,7 +1586,7 @@ endpt_handler(SPKnot */*knot*/, GdkEvent *event, SPConnectorContext *cc) } // Show the red path for dragging. - cc->red_curve = SP_PATH(cc->clickeditem)->curve->copy(); + cc->red_curve = SP_PATH(cc->clickeditem)->original_curve ? SP_PATH(cc->clickeditem)->original_curve->copy() : SP_PATH(cc->clickeditem)->curve->copy(); Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem); cc->red_curve->transform(i2d); sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve); @@ -1120,38 +1605,9 @@ endpt_handler(SPKnot */*knot*/, GdkEvent *event, SPConnectorContext *cc) return consumed; } - -static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item) +static void cc_active_shape_add_knot(SPDesktop* desktop, SPItem* item, ConnectionPointMap &cphandles, ConnectionPoint& cp) { - g_assert(item != NULL ); - - cc->active_shape = item; - - // Remove existing active shape listeners - if (cc->active_shape_repr) { - sp_repr_remove_listener_by_data(cc->active_shape_repr, cc); - Inkscape::GC::release(cc->active_shape_repr); - - sp_repr_remove_listener_by_data(cc->active_shape_layer_repr, cc); - Inkscape::GC::release(cc->active_shape_layer_repr); - } - - // Listen in case the active shape changes - cc->active_shape_repr = SP_OBJECT_REPR(item); - if (cc->active_shape_repr) { - Inkscape::GC::anchor(cc->active_shape_repr); - sp_repr_add_listener(cc->active_shape_repr, &shape_repr_events, cc); - - cc->active_shape_layer_repr = cc->active_shape_repr->parent(); - Inkscape::GC::anchor(cc->active_shape_layer_repr); - sp_repr_add_listener(cc->active_shape_layer_repr, &layer_repr_events, cc); - } - - - // Set center connection point. - if ( cc->connpthandle == NULL ) { - SPKnot *knot = sp_knot_new(cc->desktop, - _("Connection point: click or drag to create a new connector")); + SPKnot *knot = sp_knot_new(desktop, 0); knot->setShape(SP_KNOT_SHAPE_SQUARE); knot->setSize(8); @@ -1159,26 +1615,127 @@ static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item) knot->setFill(0xffffff00, 0xff0000ff, 0xff0000ff); sp_knot_update_ctrl(knot); - // We don't want to use the standard knot handler, - //since we don't want this knot to be draggable. + // We don't want to use the standard knot handler. g_signal_handler_disconnect(G_OBJECT(knot->item), knot->_event_handler_id); knot->_event_handler_id = 0; gtk_signal_connect(GTK_OBJECT(knot->item), "event", GTK_SIGNAL_FUNC(cc_generic_knot_handler), knot); + sp_knot_set_position(knot, item->avoidRef->getConnectionPointPos(cp.type, cp.id) * desktop->doc2dt(), 0); + sp_knot_show(knot); + cphandles[knot] = cp; +} + +static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item) +{ + g_assert(item != NULL ); + + std::map* connpts = &item->avoidRef->connection_points; + + if (cc->active_shape != item) + { + // The active shape has changed + // Rebuild everything + cc->active_shape = item; + // Remove existing active shape listeners + if (cc->active_shape_repr) { + sp_repr_remove_listener_by_data(cc->active_shape_repr, cc); + Inkscape::GC::release(cc->active_shape_repr); + + sp_repr_remove_listener_by_data(cc->active_shape_layer_repr, cc); + Inkscape::GC::release(cc->active_shape_layer_repr); + } + + // Listen in case the active shape changes + cc->active_shape_repr = SP_OBJECT_REPR(item); + if (cc->active_shape_repr) { + Inkscape::GC::anchor(cc->active_shape_repr); + sp_repr_add_listener(cc->active_shape_repr, &shape_repr_events, cc); + + cc->active_shape_layer_repr = cc->active_shape_repr->parent(); + Inkscape::GC::anchor(cc->active_shape_layer_repr); + sp_repr_add_listener(cc->active_shape_layer_repr, &layer_repr_events, cc); + } - cc->connpthandle = knot; + + // Set the connection points. + if ( cc->connpthandles.size() ) + // destroy the old list + while (! cc->connpthandles.empty() ) + { + g_object_unref(cc->connpthandles.begin()->first); + cc->connpthandles.erase(cc->connpthandles.begin()); + } + // build the new one + if ( connpts->size() ) + for (std::map::iterator it = connpts->begin(); it != connpts->end(); ++it) + cc_active_shape_add_knot(cc->desktop, item, cc->connpthandles, it->second); + + // Also add default connection points + // For now, only centre default connection point will + // be available + ConnectionPoint centre; + centre.type = ConnPointDefault; + centre.id = ConnPointPosCC; + cc_active_shape_add_knot(cc->desktop, item, cc->connpthandles, centre); } + else + { + // The active shape didn't change + // Update only the connection point knots + // Ensure the item's connection_points map + // has been updated + sp_document_ensure_up_to_date(SP_OBJECT_DOCUMENT(item)); - Geom::OptRect bbox = sp_item_bbox_desktop(cc->active_shape); - if (bbox) { - Geom::Point center = bbox->midpoint(); - sp_knot_set_position(cc->connpthandle, center, 0); - sp_knot_show(cc->connpthandle); - } else { - sp_knot_hide(cc->connpthandle); + std::set seen; + for ( ConnectionPointMap::iterator it = cc->connpthandles.begin(); it != cc->connpthandles.end() ;) + { + bool removed = false; + if ( it->second.type == ConnPointUserDefined ) + { + std::map::iterator p = connpts->find(it->second.id); + if (p != connpts->end()) + { + if ( it->second != p->second ) + // Connection point position has changed + // Update knot position + sp_knot_set_position(it->first, + item->avoidRef->getConnectionPointPos(it->second.type, it->second.id) * cc->desktop->doc2dt(), 0); + seen.insert(it->second.id); + sp_knot_show(it->first); + } + else + { + // This connection point does no longer exist, + // remove the knot + ConnectionPointMap::iterator curr = it; + ++it; + g_object_unref( curr->first ); + cc->connpthandles.erase(curr); + removed = true; + } + } + else + { + // It's a default connection point + // Just make sure it's position is correct + sp_knot_set_position(it->first, + item->avoidRef->getConnectionPointPos(it->second.type, it->second.id) * cc->desktop->doc2dt(), 0); + sp_knot_show(it->first); + + } + if ( !removed ) + ++it; + } + // Add knots for new connection points. + if (connpts->size()) + for ( std::map::iterator it = connpts->begin(); it != connpts->end(); ++it ) + if ( seen.find(it->first) == seen.end() ) + // A new connection point has been added + // to the shape. Add a knot for it. + cc_active_shape_add_knot(cc->desktop, item, cc->connpthandles, it->second); } } @@ -1188,7 +1745,7 @@ cc_set_active_conn(SPConnectorContext *cc, SPItem *item) { g_assert( SP_IS_PATH(item) ); - SPCurve *curve = SP_SHAPE(SP_PATH(item))->curve; + SPCurve *curve = SP_PATH(item)->original_curve ? SP_PATH(item)->original_curve : SP_PATH(item)->curve; Geom::Matrix i2d = sp_item_i2d_affine(item); if (cc->active_conn == item) @@ -1234,7 +1791,7 @@ cc_set_active_conn(SPConnectorContext *cc, SPItem *item) sp_knot_update_ctrl(knot); // We don't want to use the standard knot handler, - //since we don't want this knot to be draggable. + // since we don't want this knot to be draggable. g_signal_handler_disconnect(G_OBJECT(knot->item), knot->_event_handler_id); knot->_event_handler_id = 0; @@ -1271,6 +1828,35 @@ cc_set_active_conn(SPConnectorContext *cc, SPItem *item) sp_knot_show(cc->endpt_handle[1]); } +void cc_create_connection_point(SPConnectorContext* cc) +{ + if (cc->active_shape && cc->state == SP_CONNECTOR_CONTEXT_IDLE) + { + if (cc->selected_handle) + { + cc_deselect_handle( cc->selected_handle ); + } + SPKnot *knot = sp_knot_new(cc->desktop, 0); + // We do not process events on this knot. + g_signal_handler_disconnect(G_OBJECT(knot->item), + knot->_event_handler_id); + knot->_event_handler_id = 0; + + cc_select_handle( knot ); + cc->selected_handle = knot; + sp_knot_show(cc->selected_handle); + cc->state = SP_CONNECTOR_CONTEXT_NEWCONNPOINT; + } +} + +void cc_remove_connection_point(SPConnectorContext* cc) +{ + if (cc->selected_handle && cc->state == SP_CONNECTOR_CONTEXT_IDLE ) + { + cc->active_shape->avoidRef->deleteConnectionPoint(cc->connpthandles[cc->selected_handle]); + cc->selected_handle = NULL; + } +} static bool cc_item_is_shape(SPItem *item) { @@ -1296,7 +1882,7 @@ bool cc_item_is_connector(SPItem *item) { if (SP_IS_PATH(item)) { if (SP_PATH(item)->connEndPair.isAutoRoutingConn()) { - g_assert( !(SP_SHAPE(item)->curve->is_closed()) ); + g_assert( SP_PATH(item)->original_curve ? !(SP_PATH(item)->original_curve->is_closed()) : !(SP_PATH(item)->curve->is_closed()) ); return true; } } @@ -1394,7 +1980,7 @@ shape_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name, g_assert(data); SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(data); - // Look for changes than result in onscreen movement. + // Look for changes that result in onscreen movement. if (!strcmp(name, "d") || !strcmp(name, "x") || !strcmp(name, "y") || !strcmp(name, "width") || !strcmp(name, "height") || !strcmp(name, "transform")) @@ -1409,6 +1995,12 @@ shape_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name, cc_set_active_conn(cc, cc->active_conn); } } + else + if ( !strcmp(name, "inkscape:connection-points") ) + if (repr == cc->active_shape_repr) + // The connection points of the active shape + // have changed. Update them. + cc_set_active_shape(cc, cc->active_shape); } diff --git a/src/connector-context.h b/src/connector-context.h index d67e12b81..640a03aae 100644 --- a/src/connector-context.h +++ b/src/connector-context.h @@ -19,7 +19,8 @@ #include #include <2geom/point.h> #include "libavoid/connector.h" - +#include "connection-points.h" +#include #define SP_TYPE_CONNECTOR_CONTEXT (sp_connector_context_get_type()) #define SP_CONNECTOR_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_CAST((o), SP_TYPE_CONNECTOR_CONTEXT, SPConnectorContext)) @@ -38,9 +39,18 @@ enum { SP_CONNECTOR_CONTEXT_DRAGGING, SP_CONNECTOR_CONTEXT_CLOSE, SP_CONNECTOR_CONTEXT_STOP, - SP_CONNECTOR_CONTEXT_REROUTING + SP_CONNECTOR_CONTEXT_REROUTING, + SP_CONNECTOR_CONTEXT_NEWCONNPOINT +}; + +enum { + SP_CONNECTOR_CONTEXT_DRAWING_MODE, + SP_CONNECTOR_CONTEXT_EDITING_MODE }; +static char* cc_knot_tips[] = { _("Connection point: click or drag to create a new connector"), + _("Connection point: click to select, drag to move") }; +typedef std::map ConnectionPointMap; struct SPConnectorContext : public SPEventContext { Inkscape::Selection *selection; @@ -48,10 +58,14 @@ struct SPConnectorContext : public SPEventContext { /** \invar npoints in {0, 2}. */ gint npoints; - + /* The tool mode can be connector drawing or + connection points editing. + */ unsigned int mode : 1; unsigned int state : 4; + gchar* knot_tip; + // Red curve SPCanvasItem *red_bpath; SPCurve *red_curve; @@ -63,6 +77,8 @@ struct SPConnectorContext : public SPEventContext { // The new connector SPItem *newconn; Avoid::ConnRef *newConnRef; + gdouble curvature; + bool isOrthogonal; // The active shape SPItem *active_shape; @@ -78,10 +94,13 @@ struct SPConnectorContext : public SPEventContext { // The activehandle SPKnot *active_handle; + // The selected handle, used in editing mode + SPKnot *selected_handle; + SPItem *clickeditem; SPKnot *clickedhandle; - SPKnot *connpthandle; + ConnectionPointMap connpthandles; SPKnot *endpt_handle[2]; guint endpt_handler_id[2]; gchar *sid; @@ -93,7 +112,10 @@ struct SPConnectorContextClass : public SPEventContextClass { }; GType sp_connector_context_get_type(); +void sp_connector_context_switch_mode(SPEventContext* ec, unsigned int newMode); void cc_selection_set_avoid(bool const set_ignore); +void cc_create_connection_point(SPConnectorContext* cc); +void cc_remove_connection_point(SPConnectorContext* cc); bool cc_item_is_connector(SPItem *item); diff --git a/src/document.cpp b/src/document.cpp index d406f3712..a3ad6f7be 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -63,10 +63,16 @@ #include "xml/repr.h" #include "xml/rebase-hrefs.h" -#define SP_DOCUMENT_UPDATE_PRIORITY (G_PRIORITY_HIGH_IDLE - 1) +// Higher number means lower priority. +#define SP_DOCUMENT_UPDATE_PRIORITY (G_PRIORITY_HIGH_IDLE - 2) + +// Should have a lower priority than SP_DOCUMENT_UPDATE_PRIORITY, +// since we want it to happen when there are no more updates. +#define SP_DOCUMENT_REROUTING_PRIORITY (G_PRIORITY_HIGH_IDLE - 1) static gint sp_document_idle_handler(gpointer data); +static gint sp_document_rerouting_handler(gpointer data); gboolean sp_document_resource_list_free(gpointer key, gpointer value, gpointer data); @@ -88,15 +94,17 @@ SPDocument::SPDocument() : priv(0), // reset in ctor actionkey(0), modified_id(0), + rerouting_handler_id(0), profileManager(0), // deferred until after other initialization - router(new Avoid::Router()), + router(new Avoid::Router(Avoid::PolyLineRouting|Avoid::OrthogonalRouting)), perspectives(0), current_persp3d(0), _collection_queue(0), oldSignalsConnected(false) { - // Don't use the Consolidate moves optimisation. - router->ConsolidateMoves = false; + // Penalise libavoid for choosing paths with needless extra segments. + // This results in much better looking orthogonal connector paths. + router->setRoutingPenalty(Avoid::segmentPenalty); SPDocumentPrivate *p = new SPDocumentPrivate(); @@ -132,6 +140,11 @@ SPDocument::~SPDocument() { profileManager = 0; } + if (router) { + delete router; + router = NULL; + } + if (priv) { if (priv->partial) { sp_repr_free_log(priv->partial); @@ -177,10 +190,15 @@ SPDocument::~SPDocument() { } if (modified_id) { - gtk_idle_remove(modified_id); + g_source_remove(modified_id); modified_id = 0; } + if (rerouting_handler_id) { + g_source_remove(rerouting_handler_id); + rerouting_handler_id = 0; + } + if (oldSignalsConnected) { g_signal_handlers_disconnect_by_func(G_OBJECT(INKSCAPE), reinterpret_cast(sp_document_reset_key), @@ -195,13 +213,7 @@ SPDocument::~SPDocument() { keepalive = FALSE; } - if (router) { - delete router; - router = NULL; - } - //delete this->_whiteboard_session_manager; - } void SPDocument::add_persp3d (Persp3D * const /*persp*/) @@ -852,7 +864,12 @@ void sp_document_request_modified(SPDocument *doc) { if (!doc->modified_id) { - doc->modified_id = gtk_idle_add_priority(SP_DOCUMENT_UPDATE_PRIORITY, sp_document_idle_handler, doc); + doc->modified_id = g_idle_add_full(SP_DOCUMENT_UPDATE_PRIORITY, + sp_document_idle_handler, doc, NULL); + } + if (!doc->rerouting_handler_id) { + doc->rerouting_handler_id = g_idle_add_full(SP_DOCUMENT_REROUTING_PRIORITY, + sp_document_rerouting_handler, doc, NULL); } } @@ -908,26 +925,49 @@ SPDocument::_updateDocument() * Repeatedly works on getting the document updated, since sometimes * it takes more than one pass to get the document updated. But it * usually should not take more than a few loops, and certainly never - * more than 64 iterations. So we bail out if we hit 64 iterations, + * more than 32 iterations. So we bail out if we hit 32 iterations, * since this typically indicates we're stuck in an update loop. */ gint sp_document_ensure_up_to_date(SPDocument *doc) { - int counter = 64; - while (!doc->_updateDocument()) { - if (counter == 0) { - g_warning("More than 64 iteration while updating document '%s'", doc->uri? doc->uri:""); + // Bring the document up-to-date, specifically via the following: + // 1a) Process all document updates. + // 1b) When completed, process connector routing changes. + // 2a) Process any updates resulting from connector reroutings. + int counter = 32; + for (unsigned int pass = 1; pass <= 2; ++pass) { + // Process document updates. + while (!doc->_updateDocument()) { + if (counter == 0) { + g_warning("More than 32 iteration while updating document '%s'", doc->uri); + break; + } + counter--; + } + if (counter == 0) + { break; } - counter--; - } + // After updates on the first pass we get libavoid to process all the + // changed objects and provide new routings. This may cause some objects + // to be modified, hence the second update pass. + if (pass == 1) { + doc->router->processTransaction(); + } + } + if (doc->modified_id) { /* Remove handler */ - gtk_idle_remove(doc->modified_id); + g_source_remove(doc->modified_id); doc->modified_id = 0; } + if (doc->rerouting_handler_id) { + /* Remove handler */ + g_source_remove(doc->rerouting_handler_id); + doc->rerouting_handler_id = 0; + } return counter>0; } @@ -947,6 +987,24 @@ sp_document_idle_handler(gpointer data) } } +/** + * An idle handler to reroute connectors in the document. + */ +static gint +sp_document_rerouting_handler(gpointer data) +{ + // Process any queued movement actions and determine new routings for + // object-avoiding connectors. Callbacks will be used to update and + // redraw affected connectors. + SPDocument *doc = static_cast(data); + doc->router->processTransaction(); + + // We don't need to handle rerouting again until there are further + // diagram updates. + doc->rerouting_handler_id = 0; + return false; +} + static bool is_within(Geom::Rect const &area, Geom::Rect const &box) { return area.contains(box); diff --git a/src/document.h b/src/document.h index 789e3e2ed..06174c265 100644 --- a/src/document.h +++ b/src/document.h @@ -98,6 +98,9 @@ struct SPDocument : public Inkscape::GC::Managed<>, const gchar *actionkey; /// Handler ID guint modified_id; + + /// Connector rerouting handler ID + guint rerouting_handler_id; Inkscape::ProfileManager* profileManager; diff --git a/src/graphlayout/graphlayout.cpp b/src/graphlayout/graphlayout.cpp index cd1683c34..81ea59059 100644 --- a/src/graphlayout/graphlayout.cpp +++ b/src/graphlayout/graphlayout.cpp @@ -31,6 +31,7 @@ #include "style.h" #include "conn-avoid-ref.h" #include "libavoid/connector.h" +#include "libavoid/router.h" #include "libavoid/geomtypes.h" #include "libcola/cola.h" #include "libvpsc/generate-constraints.h" diff --git a/src/libavoid/CMakeLists.txt b/src/libavoid/CMakeLists.txt index df532c564..3f408074c 100644 --- a/src/libavoid/CMakeLists.txt +++ b/src/libavoid/CMakeLists.txt @@ -11,6 +11,8 @@ static.cpp timer.cpp vertices.cpp visibility.cpp +orthogonal.cpp +vpsc.cpp ) ADD_LIBRARY(avoid STATIC ${libavoid_SRC}) TARGET_LINK_LIBRARIES(avoid diff --git a/src/libavoid/Makefile_insert b/src/libavoid/Makefile_insert index f75470e26..77728499c 100644 --- a/src/libavoid/Makefile_insert +++ b/src/libavoid/Makefile_insert @@ -11,24 +11,26 @@ libavoid_libavoid_a_SOURCES = \ libavoid/debug.h \ libavoid/geometry.cpp \ libavoid/geometry.h \ + libavoid/geomtypes.cpp \ libavoid/geomtypes.h \ libavoid/graph.cpp \ libavoid/graph.h \ libavoid/makepath.cpp \ libavoid/makepath.h \ - libavoid/polyutil.cpp \ - libavoid/polyutil.h \ + libavoid/orthogonal.cpp \ + libavoid/orthogonal.h \ + libavoid/vpsc.cpp \ + libavoid/vpsc.h \ libavoid/router.cpp \ libavoid/router.h \ libavoid/shape.cpp \ libavoid/shape.h \ - libavoid/static.cpp \ - libavoid/static.h \ libavoid/timer.cpp \ libavoid/timer.h \ libavoid/vertices.cpp \ libavoid/vertices.h \ libavoid/visibility.cpp \ libavoid/visibility.h \ - libavoid/libavoid.h \ - libavoid/region.h + libavoid/viscluster.cpp \ + libavoid/viscluster.h \ + libavoid/libavoid.h diff --git a/src/libavoid/connector.cpp b/src/libavoid/connector.cpp index 647303371..d9088dfe7 100644 --- a/src/libavoid/connector.cpp +++ b/src/libavoid/connector.cpp @@ -2,95 +2,236 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2009 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * Author(s): Michael Wybrow */ + +#include +#include +#include #include + #include "libavoid/graph.h" #include "libavoid/connector.h" #include "libavoid/makepath.h" #include "libavoid/visibility.h" #include "libavoid/debug.h" #include "libavoid/router.h" +#include "libavoid/assertions.h" namespace Avoid { + +ConnEnd::ConnEnd(const Point& point) + : _point(point), + _directions(ConnDirAll), + _shapeRef(NULL) +{ +} + + +ConnEnd::ConnEnd(const Point& point, const ConnDirFlags visDirs) + : _point(point), + _directions(visDirs), + _shapeRef(NULL) +{ +} + +ConnEnd::ConnEnd(ShapeRef *shapeRef, const double x_pos, const double y_pos, + const double insideOffset, const ConnDirFlags visDirs) + : _directions(visDirs), + _shapeRef(shapeRef), + _xPosition(x_pos), + _yPosition(y_pos), + _insideOffset(insideOffset) +{ +} + +const Point ConnEnd::point(void) const +{ + if (_shapeRef) + { + const Polygon& poly = _shapeRef->polygon(); + + double x_min = DBL_MAX; + double x_max = -DBL_MAX; + double y_min = DBL_MAX; + double y_max = -DBL_MAX; + for (size_t i = 0; i < poly.size(); ++i) + { + x_min = std::min(x_min, poly.ps[i].x); + x_max = std::max(x_max, poly.ps[i].x); + y_min = std::min(y_min, poly.ps[i].y); + y_max = std::max(y_max, poly.ps[i].y); + } + + Point point; + + // We want to place connection points on the edges of shapes, + // or possibly slightly inside them (if _insideOfset is set). + + point.vn = kUnassignedVertexNumber; + if (_xPosition == ATTACH_POS_LEFT) + { + point.x = x_min + _insideOffset; + point.vn = 6; + } + else if (_xPosition == ATTACH_POS_RIGHT) + { + point.x = x_max - _insideOffset; + point.vn = 4; + } + else + { + point.x = x_min + (_xPosition * (x_max - x_min)); + } + + if (_yPosition == ATTACH_POS_TOP) + { + point.y = y_max - _insideOffset; + point.vn = 5; + } + else if (_yPosition == ATTACH_POS_BOTTOM) + { + point.y = y_min + _insideOffset; + point.vn = 7; + } + else + { + point.y = y_min + (_yPosition * (y_max - y_min)); + point.vn = kUnassignedVertexNumber; + } + + return point; + } + else + { + return _point; + } +} + + +ConnDirFlags ConnEnd::directions(void) const +{ + if (_shapeRef) + { + ConnDirFlags visDir = _directions; + if (_directions == ConnDirNone) + { + // None is set, use the defaults: + if (_xPosition == ATTACH_POS_LEFT) + { + visDir = ConnDirLeft; + } + else if (_xPosition == ATTACH_POS_RIGHT) + { + visDir = ConnDirRight; + } + if (_yPosition == ATTACH_POS_TOP) + { + visDir = ConnDirDown; + } + else if (_yPosition == ATTACH_POS_BOTTOM) + { + visDir = ConnDirUp; + } + + if (visDir == ConnDirNone) + { + visDir = ConnDirAll; + } + } + return visDir; + } + else + { + return _directions; + } +} + + ConnRef::ConnRef(Router *router, const unsigned int id) - : _router(router) - , _id(id) - , _type(ConnType_PolyLine) - , _srcId(0) - , _dstId(0) - , _needs_reroute_flag(true) - , _false_path(false) - , _active(false) - , _route_dist(0) - , _srcVert(NULL) - , _dstVert(NULL) - , _initialised(false) - , _callback(NULL) - , _connector(NULL) - , _hateCrossings(false) + : _router(router), + _type(router->validConnType()), + _srcId(0), + _dstId(0), + _needs_reroute_flag(true), + _false_path(false), + _needs_repaint(false), + _active(false), + _route_dist(0), + _srcVert(NULL), + _dstVert(NULL), + _startVert(NULL), + _initialised(false), + _callback(NULL), + _connector(NULL), + _hateCrossings(false) { + _id = router->assignId(id); + // TODO: Store endpoints and details. - _route.pn = 0; - _route.ps = NULL; -} - - -ConnRef::ConnRef(Router *router, const unsigned int id, - const Point& src, const Point& dst) - : _router(router) - , _id(id) - , _type(ConnType_PolyLine) - , _srcId(0) - , _dstId(0) - , _needs_reroute_flag(true) - , _false_path(false) - , _active(false) - , _route_dist(0) - , _srcVert(NULL) - , _dstVert(NULL) - , _initialised(false) - , _callback(NULL) - , _connector(NULL) - , _hateCrossings(false) -{ - _route.pn = 0; - _route.ps = NULL; - - if (_router->IncludeEndpoints) - { - bool isShape = false; - _srcVert = new VertInf(_router, VertID(id, isShape, 1), src); - _dstVert = new VertInf(_router, VertID(id, isShape, 2), dst); - _router->vertices.addVertex(_srcVert); - _router->vertices.addVertex(_dstVert); - makeActive(); - _initialised = true; - } + _route.clear(); +} + + +ConnRef::ConnRef(Router *router, const ConnEnd& src, const ConnEnd& dst, + const unsigned int id) + : _router(router), + _type(router->validConnType()), + _srcId(0), + _dstId(0), + _needs_reroute_flag(true), + _false_path(false), + _needs_repaint(false), + _active(false), + _route_dist(0), + _srcVert(NULL), + _dstVert(NULL), + _initialised(false), + _callback(NULL), + _connector(NULL), + _hateCrossings(false) +{ + _id = router->assignId(id); + _route.clear(); + + bool isShape = false; + _srcVert = new VertInf(_router, VertID(_id, isShape, 1), src.point()); + _srcVert->visDirections = src.directions(); + _dstVert = new VertInf(_router, VertID(_id, isShape, 2), dst.point()); + _dstVert->visDirections = dst.directions(); + makeActive(); + _initialised = true; + + setEndpoints(src, dst); } ConnRef::~ConnRef() { - freeRoute(); + _router->removeQueuedConnectorActions(this); + removeFromGraph(); + + freeRoutes(); if (_srcVert) { @@ -106,27 +247,38 @@ ConnRef::~ConnRef() _dstVert = NULL; } - if (_active) - { - makeInactive(); - } + makeInactive(); } -void ConnRef::setType(unsigned int type) +ConnType ConnRef::routingType(void) const { - _type = type; + return _type; } -void ConnRef::updateEndPoint(const unsigned int type, const Point& point) +void ConnRef::setRoutingType(ConnType type) { - assert((type == (unsigned int) VertID::src) || - (type == (unsigned int) VertID::tar)); - - // XXX: This was commented out. Is there a case where it isn't true? - assert(_router->IncludeEndpoints); + type = _router->validConnType(type); + if (_type != type) + { + _type = type; + + makePathInvalid(); + + _router->modifyConnector(this); + } +} + +void ConnRef::common_updateEndPoint(const unsigned int type, const ConnEnd& connEnd) +{ + const Point& point = connEnd.point(); + //db_printf("common_updateEndPoint(%d,(pid=%d,vn=%d,(%f,%f)))\n", + // type,point.id,point.vn,point.x,point.y); + COLA_ASSERT((type == (unsigned int) VertID::src) || + (type == (unsigned int) VertID::tar)); + if (!_initialised) { makeActive(); @@ -141,28 +293,28 @@ void ConnRef::updateEndPoint(const unsigned int type, const Point& point) { if (_srcVert) { - _srcVert->Reset(point); + _srcVert->Reset(VertID(_id, isShape, type), point); } else { _srcVert = new VertInf(_router, VertID(_id, isShape, type), point); - _router->vertices.addVertex(_srcVert); } + _srcVert->visDirections = connEnd.directions(); altered = _srcVert; partner = _dstVert; } - else // if (type == (unsigned int) VertID::dst) + else // if (type == (unsigned int) VertID::tar) { if (_dstVert) { - _dstVert->Reset(point); + _dstVert->Reset(VertID(_id, isShape, type), point); } else { _dstVert = new VertInf(_router, VertID(_id, isShape, type), point); - _router->vertices.addVertex(_dstVert); } + _dstVert->visDirections = connEnd.directions(); altered = _dstVert; partner = _srcVert; @@ -171,8 +323,85 @@ void ConnRef::updateEndPoint(const unsigned int type, const Point& point) // XXX: Seems to be faster to just remove the edges and recreate bool isConn = true; altered->removeFromGraph(isConn); - bool knownNew = true; - vertexVisibility(altered, partner, knownNew, true); + + makePathInvalid(); + _router->setStaticGraphInvalidated(true); +} + + +void ConnRef::setEndpoints(const ConnEnd& srcPoint, const ConnEnd& dstPoint) +{ + _router->modifyConnector(this, VertID::src, srcPoint); + _router->modifyConnector(this, VertID::tar, dstPoint); +} + + +void ConnRef::setEndpoint(const unsigned int type, const ConnEnd& connEnd) +{ + _router->modifyConnector(this, type, connEnd); +} + + +void ConnRef::setSourceEndpoint(const ConnEnd& srcPoint) +{ + _router->modifyConnector(this, VertID::src, srcPoint); +} + + +void ConnRef::setDestEndpoint(const ConnEnd& dstPoint) +{ + _router->modifyConnector(this, VertID::tar, dstPoint); +} + + +void ConnRef::updateEndPoint(const unsigned int type, const ConnEnd& connEnd) +{ + common_updateEndPoint(type, connEnd); + + if (_router->_polyLineRouting) + { + bool knownNew = true; + bool genContains = true; + if (type == (unsigned int) VertID::src) + { + vertexVisibility(_srcVert, _dstVert, knownNew, genContains); + } + else + { + vertexVisibility(_dstVert, _srcVert, knownNew, genContains); + } + } +} + + +bool ConnRef::setEndpoint(const unsigned int type, const VertID& pointID, + Point *pointSuggestion) +{ + VertInf *vInf = _router->vertices.getVertexByID(pointID); + if (vInf == NULL) + { + return false; + } + Point& point = vInf->point; + if (pointSuggestion) + { + if (euclideanDist(point, *pointSuggestion) > 0.5) + { + return false; + } + } + + common_updateEndPoint(type, point); + + // Give this visibility just to the point it is over. + EdgeInf *edge = new EdgeInf( + (type == VertID::src) ? _srcVert : _dstVert, vInf); + // XXX: We should be able to set this to zero, but can't due to + // assumptions elsewhere in the code. + edge->setDist(0.001); + + _router->processTransaction(); + return true; } @@ -203,7 +432,7 @@ unsigned int ConnRef::getDstShapeId(void) void ConnRef::makeActive(void) { - assert(!_active); + COLA_ASSERT(!_active); // Add to connRefs list. _pos = _router->connRefs.insert(_router->connRefs.begin(), this); @@ -213,7 +442,7 @@ void ConnRef::makeActive(void) void ConnRef::makeInactive(void) { - assert(_active); + COLA_ASSERT(_active); // Remove from connRefs list. _router->connRefs.erase(_pos); @@ -221,54 +450,69 @@ void ConnRef::makeInactive(void) } -void ConnRef::freeRoute(void) +void ConnRef::freeRoutes(void) { - if (_route.ps) - { - _route.pn = 0; - std::free(_route.ps); - _route.ps = NULL; - } + _route.clear(); + _display_route.clear(); } -PolyLine& ConnRef::route(void) +const PolyLine& ConnRef::route(void) const { return _route; } -void ConnRef::calcRouteDist(void) +PolyLine& ConnRef::routeRef(void) { - _route_dist = 0; - for (int i = 1; i < _route.pn; i++) + return _route; +} + + +void ConnRef::set_route(const PolyLine& route) +{ + if (&_display_route == &route) { - _route_dist += dist(_route.ps[i], _route.ps[i - 1]); + db_printf("Error:\tTrying to update libavoid route with itself.\n"); + return; } + _display_route.ps = route.ps; + + //_display_route.clear(); } -bool ConnRef::needsReroute(void) +Polygon& ConnRef::displayRoute(void) { - return (_false_path || _needs_reroute_flag); + if (_display_route.empty()) + { + // No displayRoute is set. Simplify the current route to get it. + _display_route = _route.simplify(); + } + return _display_route; } -void ConnRef::lateSetup(const Point& src, const Point& dst) +void ConnRef::calcRouteDist(void) { - assert(!_initialised); + double (*dist)(const Point& a, const Point& b) = + (_type == ConnType_PolyLine) ? euclideanDist : manhattanDist; - bool isShape = false; - _srcVert = new VertInf(_router, VertID(_id, isShape, 1), src); - _dstVert = new VertInf(_router, VertID(_id, isShape, 2), dst); - _router->vertices.addVertex(_srcVert); - _router->vertices.addVertex(_dstVert); - makeActive(); - _initialised = true; + _route_dist = 0; + for (size_t i = 1; i < _route.size(); ++i) + { + _route_dist += dist(_route.at(i), _route.at(i - 1)); + } } -unsigned int ConnRef::id(void) +bool ConnRef::needsRepaint(void) const +{ + return _needs_repaint; +} + + +unsigned int ConnRef::id(void) const { return _id; } @@ -286,6 +530,12 @@ VertInf *ConnRef::dst(void) } +VertInf *ConnRef::start(void) +{ + return _startVert; +} + + bool ConnRef::isInitialised(void) { return _initialised; @@ -303,29 +553,8 @@ void ConnRef::unInitialise(void) void ConnRef::removeFromGraph(void) { - for (VertInf *iter = _srcVert; iter != NULL; ) - { - VertInf *tmp = iter; - iter = (iter == _srcVert) ? _dstVert : NULL; - - // For each vertex. - EdgeInfList& visList = tmp->visList; - EdgeInfList::iterator finish = visList.end(); - EdgeInfList::iterator edge; - while ((edge = visList.begin()) != finish) - { - // Remove each visibility edge - delete (*edge); - } - - EdgeInfList& invisList = tmp->invisList; - finish = invisList.end(); - while ((edge = invisList.begin()) != finish) - { - // Remove each invisibility edge - delete (*edge); - } - } + _srcVert->removeFromGraph(); + _dstVert->removeFromGraph(); } @@ -336,12 +565,11 @@ void ConnRef::setCallback(void (*cb)(void *), void *ptr) } -void ConnRef::handleInvalid(void) +void ConnRef::performCallback(void) { - if (_false_path || _needs_reroute_flag) { - if (_callback) { - _callback(_connector); - } + if (_callback) + { + _callback(_connector); } } @@ -352,79 +580,279 @@ void ConnRef::makePathInvalid(void) } -Router *ConnRef::router(void) +Router *ConnRef::router(void) const { return _router; } -int ConnRef::generatePath(Point p0, Point p1) +bool ConnRef::generatePath(Point p0, Point p1) +{ + // XXX Code to determine when connectors really need to be rerouted + // does not yet work for orthogonal connectors. + if (_type != ConnType_Orthogonal) + { + if (!_false_path && !_needs_reroute_flag) + { + // This connector is up to date. + return false; + } + } + + bool result = generatePath(); + + return result; +} + + +// Validates a bend point on a path to check it does not form a zigzag corner. +// a, b, c are consecutive points on the path. d and e are b's neighbours, +// forming the shape corner d-b-e. +// +bool validateBendPoint(VertInf *aInf, VertInf *bInf, VertInf *cInf) { - if (!_false_path && !_needs_reroute_flag) { + bool bendOkay = true; + + if ((aInf == NULL) || (cInf == NULL)) + { + // Not a bendpoint, i.e., the end of the connector, so don't test. + return bendOkay; + } + + COLA_ASSERT(bInf != NULL); + VertInf *dInf = bInf->shPrev; + VertInf *eInf = bInf->shNext; + COLA_ASSERT(dInf != NULL); + COLA_ASSERT(eInf != NULL); + + Point& a = aInf->point; + Point& b = bInf->point; + Point& c = cInf->point; + Point& d = dInf->point; + Point& e = eInf->point; + + if ((a == b) || (b == c)) + { + return bendOkay; + } + +#ifdef PATHDEBUG + db_printf("a=(%g, %g)\n", a.x, a.y); + db_printf("b=(%g, %g)\n", b.x, b.y); + db_printf("c=(%g, %g)\n", c.x, c.y); + db_printf("d=(%g, %g)\n", d.x, d.y); + db_printf("e=(%g, %g)\n", e.x, e.y); +#endif + // Check angle: + int abc = vecDir(a, b, c); +#ifdef PATHDEBUG + db_printf("(abc == %d) ", abc); +#endif + + if (abc == 0) + { + // The three consecutive point on the path are in a line. + // Thus, there should always be an equally short path that + // skips this bend point. + bendOkay = false; + } + else // (abc != 0) + { + COLA_ASSERT(vecDir(d, b, e) > 0); + int abe = vecDir(a, b, e); + int abd = vecDir(a, b, d); + int bce = vecDir(b, c, e); + int bcd = vecDir(b, c, d); +#ifdef PATHDEBUG + db_printf("&& (abe == %d) && (abd == %d) &&\n(bce == %d) && (bcd == %d)", + abe, abd, bce, bcd); +#endif + + bendOkay = false; + if (abe > 0) + { + if ((abc > 0) && (abd >= 0) && (bce >= 0)) + { + bendOkay = true; + } + } + else if (abd < 0) + { + if ((abc < 0) && (abe <= 0) && (bcd <= 0)) + { + bendOkay = true; + } + } + } +#ifdef PATHDEBUG + db_printf("\n"); +#endif + return bendOkay; +} + + +bool ConnRef::generatePath(void) +{ + if (!_false_path && !_needs_reroute_flag) + { // This connector is up to date. - return (int) false; + return false; } + if (!_dstVert || !_srcVert) + { + // Connector is not fully initialised.. + return false; + } + + //COLA_ASSERT(_srcVert->point != _dstVert->point); + _false_path = false; _needs_reroute_flag = false; - VertInf *src = _srcVert; VertInf *tar = _dstVert; + _startVert = _srcVert; - if ( !(_router->IncludeEndpoints) ) - { - lateSetup(p0, p1); - - // Update as they have just been set by lateSetup. - src = _srcVert; - tar = _dstVert; + bool *flag = &(_needs_reroute_flag); - bool knownNew = true; - bool genContains = true; - vertexVisibility(src, tar, knownNew, genContains); - vertexVisibility(tar, src, knownNew, genContains); + size_t existingPathStart = 0; + const PolyLine& currRoute = route(); + if (_router->RubberBandRouting) + { + COLA_ASSERT(_router->IgnoreRegions == true); + +#ifdef PATHDEBUG + db_printf("\n"); + _srcVert->id.db_print(); + db_printf(": %g, %g\n", _srcVert->point.x, _srcVert->point.y); + tar->id.db_print(); + db_printf(": %g, %g\n", tar->point.x, tar->point.y); + for (size_t i = 0; i < currRoute.ps.size(); ++i) + { + db_printf("%g, %g ", currRoute.ps[i].x, currRoute.ps[i].y); + } + db_printf("\n"); +#endif + if (currRoute.size() > 2) + { + if (_srcVert->point == currRoute.ps[0]) + { + existingPathStart = currRoute.size() - 2; + COLA_ASSERT(existingPathStart != 0); + const Point& pnt = currRoute.at(existingPathStart); + bool isShape = true; + VertID vID(pnt.id, isShape, pnt.vn); + + _startVert = _router->vertices.getVertexByID(vID); + } + } + } + //db_printf("GO\n"); + //db_printf("src: %X strt: %X dst: %x\n", (int) _srcVert, (int) _startVert, (int) _dstVert); + bool found = false; + while (!found) + { + makePath(this, flag); + for (VertInf *i = tar; i != NULL; i = i->pathNext) + { + if (i == _srcVert) + { + found = true; + break; + } + } + if (!found) + { + if (existingPathStart == 0) + { + break; + } +#ifdef PATHDEBUG + db_printf("BACK\n"); +#endif + existingPathStart--; + const Point& pnt = currRoute.at(existingPathStart); + bool isShape = (existingPathStart > 0); + VertID vID(pnt.id, isShape, pnt.vn); + + _startVert = _router->vertices.getVertexByID(vID); + COLA_ASSERT(_startVert); + } + else if (_router->RubberBandRouting) + { + // found. + bool unwind = false; + +#ifdef PATHDEBUG + db_printf("\n\n\nSTART:\n\n"); +#endif + VertInf *prior = NULL; + for (VertInf *curr = tar; curr != _startVert->pathNext; + curr = curr->pathNext) + { + if (!validateBendPoint(curr->pathNext, curr, prior)) + { + unwind = true; + break; + } + prior = curr; + } + if (unwind) + { +#ifdef PATHDEBUG + db_printf("BACK II\n"); +#endif + if (existingPathStart == 0) + { + break; + } + existingPathStart--; + const Point& pnt = currRoute.at(existingPathStart); + bool isShape = (existingPathStart > 0); + VertID vID(pnt.id, isShape, pnt.vn); + + _startVert = _router->vertices.getVertexByID(vID); + COLA_ASSERT(_startVert); + + found = false; + } + } } - bool *flag = &(_needs_reroute_flag); - - makePath(this, flag); bool result = true; int pathlen = 1; - for (VertInf *i = tar; i != src; i = i->pathNext) + for (VertInf *i = tar; i != _srcVert; i = i->pathNext) { pathlen++; if (i == NULL) { db_printf("Warning: Path not found...\n"); pathlen = 2; - tar->pathNext = src; - if (_router->InvisibilityGrph) + tar->pathNext = _srcVert; + if ((_type == ConnType_PolyLine) && _router->InvisibilityGrph) { // TODO: Could we know this edge already? - EdgeInf *edge = EdgeInf::existingEdge(src, tar); - assert(edge != NULL); + EdgeInf *edge = EdgeInf::existingEdge(_srcVert, tar); + COLA_ASSERT(edge != NULL); edge->addCycleBlocker(); } - result = false; break; } - if (pathlen > 100) - { - fprintf(stderr, "ERROR: Should never be here...\n"); - exit(1); - } + // Check we don't have an apparent infinite connector path. + COLA_ASSERT(pathlen < 200); } - Point *path = (Point *) malloc(pathlen * sizeof(Point)); + std::vector path(pathlen); int j = pathlen - 1; - for (VertInf *i = tar; i != src; i = i->pathNext) + for (VertInf *i = tar; i != _srcVert; i = i->pathNext) { - if (_router->InvisibilityGrph) + if (_router->InvisibilityGrph && (_type == ConnType_PolyLine)) { // TODO: Again, we could know this edge without searching. EdgeInf *edge = EdgeInf::existingEdge(i, i->pathNext); + COLA_ASSERT(edge != NULL); edge->addConn(flag); } else @@ -432,25 +860,53 @@ int ConnRef::generatePath(Point p0, Point p1) _false_path = true; } path[j] = i->point; - path[j].id = i->id.objID; + if (i->id.isShape) + { + path[j].id = i->id.objID; + path[j].vn = i->id.vn; + } + else + { + path[j].id = _id; + path[j].vn = kUnassignedVertexNumber; + } j--; - } - path[0] = src->point; + if (i->pathNext && (i->pathNext->point == i->point)) + { + if (i->pathNext->id.isShape && i->id.isShape) + { + // Check for consecutive points on opposite + // corners of two touching shapes. + COLA_ASSERT(abs(i->pathNext->id.objID - i->id.objID) != 2); + } + } + } + path[0] = _srcVert->point; + // Use topbit to differentiate between start and end point of connector. + // They need unique IDs for nudging. + unsigned int topbit = ((unsigned int) 1) << 31; + path[0].id = _id | topbit; + path[0].vn = kUnassignedVertexNumber; // Would clear visibility for endpoints here if required. - PolyLine& output_route = route(); - output_route.pn = pathlen; + freeRoutes(); + PolyLine& output_route = _route; output_route.ps = path; - if ( !(_router->IncludeEndpoints) ) +#ifdef PATHDEBUG + db_printf("Output route:\n"); + for (size_t i = 0; i < output_route.ps.size(); ++i) { - assert(_initialised); - unInitialise(); + db_printf("[%d,%d] %g, %g ", output_route.ps[i].id, + output_route.ps[i].vn, output_route.ps[i].x, + output_route.ps[i].y); } - - return (int) result; + db_printf("\n\n"); +#endif + + return result; } @@ -466,6 +922,895 @@ bool ConnRef::doesHateCrossings(void) } +PtOrder::~PtOrder() +{ + // Free the PointRep lists. + for (int dim = 0; dim < 2; ++dim) + { + PointRepList::iterator curr = connList[dim].begin(); + while (curr != connList[dim].end()) + { + PointRep *doomed = *curr; + curr = connList[dim].erase(curr); + delete doomed; + } + } +} + +bool PointRep::follow_inner(PointRep *target) +{ + if (this == target) + { + return true; + } + else + { + for (PointRepSet::iterator curr = inner_set.begin(); + curr != inner_set.end(); ++curr) + { + if ((*curr)->follow_inner(target)) + { + return true; + } + } + } + return false; +} + + +int PtOrder::positionFor(const ConnRef *conn, const size_t dim) const +{ + int position = 0; + for (PointRepList::const_iterator curr = connList[dim].begin(); + curr != connList[dim].end(); ++curr) + { + if ((*curr)->conn == conn) + { + return position; + } + ++position; + } + // Not found. + return -1; +} + + +bool PtOrder::addPoints(const int dim, PtConnPtrPair innerArg, + PtConnPtrPair outerArg, bool swapped) +{ + PtConnPtrPair inner = (swapped) ? outerArg : innerArg; + PtConnPtrPair outer = (swapped) ? innerArg : outerArg; + COLA_ASSERT(inner != outer); + + //printf("addPoints(%d, [%g, %g]-%X, [%g, %g]-%X)\n", dim, + // inner->x, inner->y, (int) inner, outer->x, outer->y, (int) outer); + + PointRep *innerPtr = NULL; + PointRep *outerPtr = NULL; + for (PointRepList::iterator curr = connList[dim].begin(); + curr != connList[dim].end(); ++curr) + { + if ((*curr)->point == inner.first) + { + innerPtr = *curr; + } + if ((*curr)->point == outer.first) + { + outerPtr = *curr; + } + } + + if (innerPtr == NULL) + { + innerPtr = new PointRep(inner.first, inner.second); + connList[dim].push_back(innerPtr); + } + + if (outerPtr == NULL) + { + outerPtr = new PointRep(outer.first, outer.second); + connList[dim].push_back(outerPtr); + } + // TODO COLA_ASSERT(innerPtr->inner_set.find(outerPtr) == innerPtr->inner_set.end()); + bool cycle = innerPtr->follow_inner(outerPtr); + if (cycle) + { + // Must reverse to avoid a cycle. + innerPtr->inner_set.insert(outerPtr); + } + else + { + outerPtr->inner_set.insert(innerPtr); + } + return cycle; +} + + +// Assuming that addPoints has been called for each pair of points in the +// shared path at that corner, then the contents of inner_set can be used +// to determine the correct ordering. +static bool pointRepLessThan(PointRep *r1, PointRep *r2) +{ + size_t r1less = r1->inner_set.size(); + size_t r2less = r2->inner_set.size(); + //COLA_ASSERT(r1less != r2less); + + return (r1less > r2less); +} + + +void PtOrder::sort(const int dim) +{ + connList[dim].sort(pointRepLessThan); +} + + +// Returns a vertex number representing a point on the line between +// two shape corners, represented by p0 and p1. +// +static int midVertexNumber(const Point& p0, const Point& p1, const Point& c) +{ + if (c.vn != kUnassignedVertexNumber) + { + // The split point is a shape corner, so doesn't need its + // vertex number adjusting. + return c.vn; + } + if ((p0.vn >= 4) && (p0.vn < kUnassignedVertexNumber)) + { + // The point next to this has the correct nudging direction, + // so use that. + return p0.vn; + } + if ((p1.vn >= 4) && (p1.vn < kUnassignedVertexNumber)) + { + // The point next to this has the correct nudging direction, + // so use that. + return p1.vn; + } + if ((p0.vn < 4) && (p1.vn < 4)) + { + if (p0.vn != p1.vn) + { + return p0.vn; + } + // Splitting between two ordinary shape corners. + int vn_mid = std::min(p0.vn, p1.vn); + if ((std::max(p0.vn, p1.vn) == 3) && (vn_mid == 0)) + { + vn_mid = 3; // Next vn is effectively 4. + } + return vn_mid + 4; + } + COLA_ASSERT((p0.x == p1.x) || (p0.y == p1.y)); + if (p0.vn != kUnassignedVertexNumber) + { + if (p0.x == p1.x) + { + if ((p0.vn == 2) || (p0.vn == 3)) + { + return 6; + } + return 4; + } + else + { + if ((p0.vn == 0) || (p0.vn == 3)) + { + return 7; + } + return 5; + } + } + else if (p1.vn != kUnassignedVertexNumber) + { + if (p0.x == p1.x) + { + if ((p1.vn == 2) || (p1.vn == 3)) + { + return 6; + } + return 4; + } + else + { + if ((p1.vn == 0) || (p1.vn == 3)) + { + return 7; + } + return 5; + } + } + + // Shouldn't both be new (kUnassignedVertexNumber) points. + db_printf("midVertexNumber(): p0.vn and p1.vn both = " + "kUnassignedVertexNumber\n"); + db_printf("p0.vn %d p1.vn %d\n", p0.vn, p1.vn); + return kUnassignedVertexNumber; +} + + +// Break up overlapping parallel segments that are not the same edge in +// the visibility graph, i.e., where one segment is a subsegment of another. +void splitBranchingSegments(Avoid::Polygon& poly, bool polyIsConn, + Avoid::Polygon& conn, const double tolerance) +{ + for (std::vector::iterator i = conn.ps.begin(); + i != conn.ps.end(); ++i) + { + if (i == conn.ps.begin()) + { + // Skip the first point. + // There are points-1 segments in a connector. + continue; + } + + for (std::vector::iterator j = poly.ps.begin(); + j != poly.ps.end(); ) + { + if (polyIsConn && (j == poly.ps.begin())) + { + // Skip the first point. + // There are points-1 segments in a connector. + ++j; + continue; + } + Point& c0 = *(i - 1); + Point& c1 = *i; + + Point& p0 = (j == poly.ps.begin()) ? poly.ps.back() : *(j - 1); + Point& p1 = *j; + + // Check the first point of the first segment. + if (((i - 1) == conn.ps.begin()) && + pointOnLine(p0, p1, c0, tolerance)) + { + //db_printf("add to poly %g %g\n", c0.x, c0.y); + + c0.vn = midVertexNumber(p0, p1, c0); + j = poly.ps.insert(j, c0); + if (j != poly.ps.begin()) + { + --j; + } + continue; + } + // And the second point of every segment. + if (pointOnLine(p0, p1, c1, tolerance)) + { + //db_printf("add to poly %g %g\n", c1.x, c1.y); + + c1.vn = midVertexNumber(p0, p1, c1); + j = poly.ps.insert(j, c1); + if (j != poly.ps.begin()) + { + --j; + } + continue; + } + + // Check the first point of the first segment. + if (polyIsConn && ((j - 1) == poly.ps.begin()) && + pointOnLine(c0, c1, p0, tolerance)) + { + //db_printf("add to conn %g %g\n", p0.x, p0.y); + + p0.vn = midVertexNumber(c0, c1, p0); + i = conn.ps.insert(i, p0); + continue; + } + // And the second point of every segment. + if (pointOnLine(c0, c1, p1, tolerance)) + { + //db_printf("add to conn %g %g\n", p1.x, p1.y); + + p1.vn = midVertexNumber(c0, c1, p1); + i = conn.ps.insert(i, p1); + } + ++j; + } + } +} + + +static int segDir(const Point& p1, const Point& p2) +{ + int result = 1; + if (p1.x == p2.x) + { + if (p2.y > p1.y) + { + result = -1; + } + } + else if (p1.y == p2.y) + { + if (p2.x < p1.x) + { + result = -1; + } + } + return result; +} + + +// Works out if the segment conn[cIndex-1]--conn[cIndex] really crosses poly. +// This does not not count non-crossing shared paths as crossings. +// poly can be either a connector (polyIsConn = true) or a cluster +// boundary (polyIsConn = false). +// +CrossingsInfoPair countRealCrossings(Avoid::Polygon& poly, + bool polyIsConn, Avoid::Polygon& conn, size_t cIndex, + bool checkForBranchingSegments, const bool finalSegment, + PointSet *crossingPoints, PtOrderMap *pointOrders, + ConnRef *polyConnRef, ConnRef *connConnRef) +{ + unsigned int crossingFlags = CROSSING_NONE; + if (checkForBranchingSegments) + { + size_t conn_pn = conn.size(); + // XXX When doing the pointOnLine test we allow the points to be + // slightly non-collinear. This addresses a problem with clustered + // routing where connectors could otherwise route cheaply through + // shape corners that were not quite on the cluster boundary, but + // reported to be on there by the line segment intersection code, + // which I suspect is not numerically accurate enough. This occured + // for points that only differed by about 10^-12 in the y-dimension. + double tolerance = (!polyIsConn) ? 0.00001 : 0.0; + splitBranchingSegments(poly, polyIsConn, conn, tolerance); + // cIndex is going to be the last, so take into account added points. + cIndex += (conn.size() - conn_pn); + } + COLA_ASSERT(cIndex >= 1); + COLA_ASSERT(cIndex < conn.size()); + + bool polyIsOrthogonal = (polyConnRef && + (polyConnRef->routingType() == ConnType_Orthogonal)); + bool connIsOrthogonal = (connConnRef && + (connConnRef->routingType() == ConnType_Orthogonal)); + + size_t poly_size = poly.size(); + int crossingCount = 0; + std::vector c_path; + std::vector p_path; + + Avoid::Point& a1 = conn.ps[cIndex - 1]; + Avoid::Point& a2 = conn.ps[cIndex]; + //db_printf("a1: %g %g\n", a1.x, a1.y); + //db_printf("a2: %g %g\n", a2.x, a2.y); + + for (size_t j = ((polyIsConn) ? 1 : 0); j < poly_size; ++j) + { + Avoid::Point& b1 = poly.ps[(j - 1 + poly_size) % poly_size]; + Avoid::Point& b2 = poly.ps[j]; + //db_printf("b1: %g %g\n", b1.x, b1.y); + //db_printf("b2: %g %g\n", b2.x, b2.y); + + p_path.clear(); + c_path.clear(); + bool converging = false; + + const bool a1_eq_b1 = (a1 == b1); + const bool a2_eq_b1 = (a2 == b1); + const bool a2_eq_b2 = (a2 == b2); + const bool a1_eq_b2 = (a1 == b2); + + if ( (a1_eq_b1 && a2_eq_b2) || + (a2_eq_b1 && a1_eq_b2) ) + { + if (finalSegment) + { + converging = true; + } + else + { + // Route along same segment: no penalty. We detect + // crossovers when we see the segments diverge. + continue; + } + } + else if (a2_eq_b1 || a2_eq_b2 || a1_eq_b2) + { + // Each crossing that is at a vertex in the + // visibility graph gets noticed four times. + // We ignore three of these cases. + // This also catches the case of a shared path, + // but this is one that terminates at a common + // endpoint, so we don't care about it. + continue; + } + + if (a1_eq_b1 || converging) + { + if (!converging) + { + if (polyIsConn && (j == 1)) + { + // Can't be the end of a shared path or crossing path + // since the common point is the first point of the + // connector path. This is not a shared path at all. + continue; + } + + Avoid::Point& b0 = poly.ps[(j - 2 + poly_size) % poly_size]; + // The segments share an endpoint -- a1==b1. + if (a2 == b0) + { + // a2 is not a split, continue. + continue; + } + } + + // If here and not converging, then we know that a2 != b2 + // And a2 and its pair in b are a split. + COLA_ASSERT(converging || !a2_eq_b2); + + bool shared_path = false; + + // Initial values here don't matter. They are only used after + // being set to sensible values, but we set them to stop a MSVC + // warning. + bool p_dir_back; + int p_dir = 0; + int trace_c = 0; + int trace_p = 0; + + if (converging) + { + // Determine direction we have to look through + // the points of connector b. + p_dir_back = a2_eq_b2 ? true : false; + p_dir = p_dir_back ? -1 : 1; + trace_c = (int) cIndex; + trace_p = (int) j; + if (!p_dir_back) + { + if (finalSegment) + { + trace_p--; + } + else + { + trace_c--; + } + } + + shared_path = true; + } + else if (cIndex >= 2) + { + Avoid::Point& b0 = poly.ps[(j - 2 + poly_size) % poly_size]; + Avoid::Point& a0 = conn.ps[cIndex - 2]; + + //db_printf("a0: %g %g\n", a0.x, a0.y); + //db_printf("b0: %g %g\n", b0.x, b0.y); + + if ((a0 == b2) || (a0 == b0)) + { + // Determine direction we have to look through + // the points of connector b. + p_dir_back = (a0 == b0) ? true : false; + p_dir = p_dir_back ? -1 : 1; + trace_c = (int) cIndex; + trace_p = (int) (p_dir_back ? j : j - 2); + + shared_path = true; + } + } + + if (shared_path) + { + crossingFlags |= CROSSING_SHARES_PATH; + // Shouldn't be here if p_dir is still equal to zero. + COLA_ASSERT(p_dir != 0); + + // Build the shared path, including the diverging points at + // each end if the connector does not end at a common point. + while ( (trace_c >= 0) && (!polyIsConn || + ((trace_p >= 0) && (trace_p < (int) poly_size))) ) + { + // If poly is a cluster boundary, then it is a closed + // poly-line and so it wraps arounds. + size_t index_p = (size_t) + ((trace_p + (2 * poly_size)) % poly_size); + size_t index_c = (size_t) trace_c; + c_path.push_back(&conn.ps[index_c]); + p_path.push_back(&poly.ps[index_p]); + if ((c_path.size() > 1) && + (conn.ps[index_c] != poly.ps[index_p])) + { + // Points don't match, so break out of loop. + break; + } + trace_c--; + trace_p += p_dir; + } + + // Are there diverging points at the ends of the shared path. + bool front_same = (*(c_path.front()) == *(p_path.front())); + bool back_same = (*(c_path.back()) == *(p_path.back())); + + size_t size = c_path.size(); + + // Check to see if these share a fixed segment. + if (polyIsOrthogonal && connIsOrthogonal) + { + size_t startPt = (front_same) ? 0 : 1; + if (c_path[startPt]->x == c_path[startPt + 1]->x) + { + // Vertical + double xPos = c_path[startPt]->x; + // See if this is inline with either the start + // or end point of both connectors. + if ( ((xPos == poly.ps[0].x) || + (xPos == poly.ps[poly_size - 1].x)) && + ((xPos == conn.ps[0].x) || + (xPos == conn.ps[cIndex].x)) ) + { + crossingFlags |= CROSSING_SHARES_FIXED_SEGMENT; + } + } + else + { + // Horizontal + double yPos = c_path[startPt]->y; + // See if this is inline with either the start + // or end point of both connectors. + if ( ((yPos == poly.ps[0].y) || + (yPos == poly.ps[poly_size - 1].y)) && + ((yPos == conn.ps[0].y) || + (yPos == conn.ps[cIndex].y)) ) + { + crossingFlags |= CROSSING_SHARES_FIXED_SEGMENT; + } + } + } + + int prevTurnDir = -1; + int startCornerSide = 1; + int endCornerSide = 1; + bool reversed = false; + if (!front_same) + { + // If there is a divergence at the beginning, + // then order the shared path based on this. + prevTurnDir = vecDir(*c_path[0], *c_path[1], *c_path[2]); + startCornerSide = Avoid::cornerSide(*c_path[0], *c_path[1], + *c_path[2], *p_path[0]) + * segDir(*c_path[1], *c_path[2]); + reversed = (startCornerSide != -prevTurnDir); + } + if (!back_same) + { + // If there is a divergence at the end of the path, + // then order the shared path based on this. + prevTurnDir = vecDir(*c_path[size - 3], + *c_path[size - 2], *c_path[size - 1]); + endCornerSide = Avoid::cornerSide(*c_path[size - 3], + *c_path[size - 2], *c_path[size - 1], + *p_path[size - 1]) + * segDir(*c_path[size - 3], *c_path[size - 2]); + reversed = (endCornerSide != -prevTurnDir); + } + else + { + endCornerSide = startCornerSide; + } + if (front_same) + { + startCornerSide = endCornerSide; + } + + if (front_same || back_same) + { + crossingFlags |= CROSSING_SHARES_PATH_AT_END; + } + else if (polyIsOrthogonal && connIsOrthogonal) + { + int cStartDir = vecDir(*c_path[0], *c_path[1], *c_path[2]); + int pStartDir = vecDir(*p_path[0], *p_path[1], *p_path[2]); + if ((cStartDir != 0) && (cStartDir == -pStartDir)) + { + // The start segments diverge at 180 degrees to each + // other. So order based on not introducing overlap + // of the diverging segments when these are nudged + // apart. + startCornerSide = -cStartDir * + segDir(*c_path[1], *c_path[2]); + } + else + { + int cEndDir = vecDir(*c_path[size - 3], + *c_path[size - 2], *c_path[size - 1]); + int pEndDir = vecDir(*p_path[size - 3], + *p_path[size - 2], *p_path[size - 1]); + if ((cEndDir != 0) && (cEndDir == -pEndDir)) + { + // The end segments diverge at 180 degrees to + // each other. So order based on not introducing + // overlap of the diverging segments when these + // are nudged apart. + startCornerSide = -cEndDir * segDir( + *c_path[size - 3], *c_path[size - 2]); + } + } + } + +#if 0 + prevTurnDir = 0; + if (pointOrders) + { + // Return the ordering for the shared path. + COLA_ASSERT(c_path.size() > 0 || back_same); + size_t adj_size = (c_path.size() - ((back_same) ? 0 : 1)); + for (size_t i = (front_same) ? 0 : 1; i < adj_size; ++i) + { + Avoid::Point& an = *(c_path[i]); + Avoid::Point& bn = *(p_path[i]); + int currTurnDir = ((i > 0) && (i < (adj_size - 1))) ? + vecDir(*c_path[i - 1], an, + *c_path[i + 1]) : 0; + VertID vID(an.id, true, an.vn); + if ( (currTurnDir == (-1 * prevTurnDir)) && + (currTurnDir != 0) && (prevTurnDir != 0) ) + { + // The connector turns the opposite way around + // this shape as the previous bend on the path, + // so reverse the order so that the inner path + // become the outer path and vice versa. + reversed = !reversed; + } + bool orderSwapped = (*pointOrders)[an].addPoints( + &bn, &an, reversed); + if (orderSwapped) + { + // Reverse the order for later points. + reversed = !reversed; + } + prevTurnDir = currTurnDir; + } + } +#endif + prevTurnDir = 0; + if (pointOrders) + { + reversed = false; + size_t startPt = (front_same) ? 0 : 1; + + // Orthogonal should always have at least one segment. + COLA_ASSERT(c_path.size() > (startPt + 1)); + + if (startCornerSide > 0) + { + reversed = !reversed; + } + + int prevDir = 0; + // Return the ordering for the shared path. + COLA_ASSERT(c_path.size() > 0 || back_same); + size_t adj_size = (c_path.size() - ((back_same) ? 0 : 1)); + for (size_t i = (front_same) ? 0 : 1; i < adj_size; ++i) + { + Avoid::Point& an = *(c_path[i]); + Avoid::Point& bn = *(p_path[i]); + COLA_ASSERT(an == bn); + + int thisDir = prevDir; + if ((i > 0) && (*(c_path[i - 1]) == *(p_path[i - 1]))) + { + thisDir = segDir(*c_path[i - 1], *c_path[i]); + } + + if (thisDir != prevDir) + { + reversed = !reversed; + } + prevDir = thisDir; + + if (i > startPt) + { + Avoid::Point& ap = *(c_path[i - 1]); + Avoid::Point& bp = *(p_path[i - 1]); + int orientation = (ap.x == an.x) ? 0 : 1; + //printf("prevOri %d\n", prevOrientation); + //printf("1: %X, %X\n", (int) &(bn), (int) &(an)); + bool orderSwapped = (*pointOrders)[an].addPoints( + orientation, + std::make_pair(&bn, polyConnRef), + std::make_pair(&an, connConnRef), + reversed); + if (orderSwapped) + { + // Reverse the order for later points. + reversed = !reversed; + } + COLA_ASSERT(ap == bp); + //printf("2: %X, %X\n", (int) &bp, (int) &ap); + orderSwapped = (*pointOrders)[ap].addPoints( + orientation, + std::make_pair(&bp, polyConnRef), + std::make_pair(&ap, connConnRef), + reversed); + COLA_ASSERT(!orderSwapped); + } + } + } +#if 0 + int ymod = -1; + if ((id.vn == 1) || (id.vn == 2)) + { + // bottom. + ymod = +1; + } + + int xmod = -1; + if ((id.vn == 0) || (id.vn == 1)) + { + // right. + xmod = +1; + } + if(id.vn > 3) + { + xmod = ymod = 0; + if (id.vn == 4) + { + // right. + xmod = +1; + } + else if (id.vn == 5) + { + // bottom. + ymod = +1; + } + else if (id.vn == 6) + { + // left. + xmod = -1; + } + else if (id.vn == 7) + { + // top. + ymod = -1; + } + } +#endif + + if (endCornerSide != startCornerSide) + { + // Mark that the shared path crosses. + //db_printf("shared path crosses.\n"); + crossingCount += 1; + if (crossingPoints) + { + crossingPoints->insert(*c_path[1]); + } + } + crossingFlags |= CROSSING_TOUCHES; + } + else if (cIndex >= 2) + { + // The connectors cross or touch at this point. + //db_printf("Cross or touch at point... \n"); + + // Crossing shouldn't be at an endpoint. + COLA_ASSERT(cIndex >= 2); + COLA_ASSERT(polyIsConn && (j >= 2)); + + Avoid::Point& b0 = poly.ps[(j - 2 + poly_size) % poly_size]; + Avoid::Point& a0 = conn.ps[cIndex - 2]; + + int side1 = Avoid::cornerSide(a0, a1, a2, b0); + int side2 = Avoid::cornerSide(a0, a1, a2, b2); + if (side1 != side2) + { + // The connectors cross at this point. + //db_printf("cross.\n"); + crossingCount += 1; + if (crossingPoints) + { + crossingPoints->insert(a1); + } + } + + crossingFlags |= CROSSING_TOUCHES; + if (pointOrders) + { + if (polyIsOrthogonal && connIsOrthogonal) + { + // Orthogonal case: + // Just order based on which comes from the left and + // top in each dimension because this can only be two + // L-shaped segments touching at the bend. + bool reversedX = ((a0.x < a1.x) || (a2.x < a1.x)); + bool reversedY = ((a0.y < a1.y) || (a2.y < a1.y)); + // XXX: Why do we need to invert the reversed values + // here? Are they wrong for orthogonal points + // in the other places? + (*pointOrders)[b1].addPoints(0, + std::make_pair(&b1, polyConnRef), + std::make_pair(&a1, connConnRef), + !reversedX); + (*pointOrders)[b1].addPoints(1, + std::make_pair(&b1, polyConnRef), + std::make_pair(&a1, connConnRef), + !reversedY); + } + else + { + int turnDirA = vecDir(a0, a1, a2); + int turnDirB = vecDir(b0, b1, b2); + bool reversed = (side1 != -turnDirA); + if (side1 != side2) + { + // Interesting case where a connector routes round + // the edge of a shape and intersects a connector + // which is connected to a port on the edge of the + // shape. + if (turnDirA == 0) + { + // We'll make B the outer by preference, + // because the points of A are collinear. + reversed = false; + } + else if (turnDirB == 0) + { + reversed = true; + } + // TODO COLA_ASSERT((turnDirB != 0) || + // (turnDirA != 0)); + } + VertID vID(b1.id, true, b1.vn); + //(*pointOrders)[b1].addPoints(&b1, &a1, reversed); + } + } + } + } + else + { + if ( polyIsOrthogonal && connIsOrthogonal) + { + // All crossings in orthogonal connectors will be at a + // vertex in the visibility graph, so we need not bother + // doing normal line intersection. + continue; + } + + // No endpoint is shared between these two line segments, + // so just calculate normal segment intersection. + + Point cPt; + int intersectResult = Avoid::segmentIntersectPoint( + a1, a2, b1, b2, &(cPt.x), &(cPt.y)); + + if (intersectResult == Avoid::DO_INTERSECT) + { + if (!polyIsConn && + ((a1 == cPt) || (a2 == cPt) || (b1 == cPt) || (b2 == cPt))) + { + // XXX: This shouldn't actually happen, because these + // points should be added as bends to each line by + // splitBranchingSegments(). Thus, lets ignore them. + COLA_ASSERT(a1 != cPt); + COLA_ASSERT(a2 != cPt); + COLA_ASSERT(b1 != cPt); + COLA_ASSERT(b2 != cPt); + continue; + } + //db_printf("crossing lines:\n"); + //db_printf("cPt: %g %g\n", cPt.x, cPt.y); + crossingCount += 1; + if (crossingPoints) + { + crossingPoints->insert(cPt); + } + } + } + } + //db_printf("crossingcount %d\n", crossingCount); + return std::make_pair(crossingCount, crossingFlags); +} + + //============================================================================ } diff --git a/src/libavoid/connector.h b/src/libavoid/connector.h index 64afb4dda..8f7499a29 100644 --- a/src/libavoid/connector.h +++ b/src/libavoid/connector.h @@ -2,93 +2,336 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2009 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * Author(s): Michael Wybrow */ +//! @file shape.h +//! @brief Contains the interface for the ConnRef class. + + #ifndef AVOID_CONNECTOR_H #define AVOID_CONNECTOR_H -#include "libavoid/router.h" +#include +#include + +#include "libavoid/vertices.h" #include "libavoid/geometry.h" #include "libavoid/shape.h" -#include namespace Avoid { +class Router; +class ConnRef; +typedef std::list ConnRefList; + + +//! @brief Describes the type of routing that is performed for each +//! connector. +enum ConnType { + ConnType_None = 0, + //! @brief The connector path will be a shortest-path poly-line that + //! routes around obstacles. + ConnType_PolyLine = 1, + //! @brief The connector path will be a shortest-path orthogonal + //! poly-line (only vertical and horizontal line segments) that + //! routes around obstacles. + ConnType_Orthogonal = 2 +}; + +//! @brief Flags that can be passed to the ConnEnd constructor to specify +//! which sides of a shape this point should have visibility to if +//! it is located within the shape's area. +//! +//! Like SVG, libavoid considers the Y-axis to point downwards, that is, +//! like screen coordinates the coordinates increase from left-to-right and +//! also from top-to-bottom. +//! +enum ConnDirFlag { + ConnDirNone = 0, + //! @brief This option specifies the point should be given visibility + //! to the top of the shape that it is located within. + ConnDirUp = 1, + //! @brief This option specifies the point should be given visibility + //! to the bottom of the shape that it is located within. + ConnDirDown = 2, + //! @brief This option specifies the point should be given visibility + //! to the left side of the shape that it is located within. + ConnDirLeft = 4, + //! @brief This option specifies the point should be given visibility + //! to the right side of the shape that it is located within. + ConnDirRight = 8, + //! @brief This option, provided for convenience, specifies the point + //! should be given visibility to all four sides of the shape + //! that it is located within. + ConnDirAll = 15 +}; +//! @brief One or more Avoid::ConnDirFlag options. +//! +typedef unsigned int ConnDirFlags; + + +static const double ATTACH_POS_TOP = 0; +static const double ATTACH_POS_CENTER = 0.5; +static const double ATTACH_POS_BOTTOM = 1; +static const double ATTACH_POS_LEFT = ATTACH_POS_TOP; +static const double ATTACH_POS_RIGHT = ATTACH_POS_BOTTOM; + + +//! @brief The ConnEnd class represents different possible endpoints for +//! connectors. +//! +//! Currently this class just allows free-floating endpoints, but in future +//! will be capable of representing attachments to connection points on shapes. +//! +class ConnEnd +{ + public: + //! @brief Constructs a ConnEnd from a free-floating point. + //! + //! @param[in] point The position of the connector endpoint. + //! + ConnEnd(const Point& point); + + //! @brief Constructs a ConnEnd from a free-floating point as well + //! as a set of flags specifying visibility for this point + //! if it is located inside a shape. + //! + //! @param[in] point The position of the connector endpoint. + //! @param[in] visDirs One or more Avoid::ConnDirFlag options + //! specifying the directions that this point + //! should be given visibility if it is inside + //! a shape. + //! + ConnEnd(const Point& point, const ConnDirFlags visDirs); + + ConnEnd(ShapeRef *shapeRef, const double x_pos, const double y_pos, + const double insideOffset = 0.0, + const ConnDirFlags visDirs = ConnDirNone); -static const int ConnType_PolyLine = 1; -static const int ConnType_Orthogonal = 2; + //! @brief Returns the position of this connector endpoint + //! + //! @return The position of this connector endpoint. + const Point point(void) const; + + ConnDirFlags directions(void) const; + private: + Point _point; + ConnDirFlags _directions; + + // For referencing ConnEnds + ShapeRef *_shapeRef; + double _xPosition; + double _yPosition; + double _insideOffset; +}; +//! @brief The ConnRef class represents a connector object. +//! +//! Connectors are a (possible multi-segment) line between two points. +//! They are routed intelligently so as not to overlap any of the shape +//! objects in the Router scene. +//! +//! Routing penalties can be applied, resulting in more aesthetically pleasing +//! connector paths with fewer segments or less severe bend-points. +//! +//! You can set a function to be called when the connector has been rerouted +//! and needs to be redrawn. Alternatively, you can query the connector's +//! needsRepaint() function to determine this manually. +//! +//! Usually, it is expected that you would create a ConnRef for each connector +//! in your diagram and keep that reference in your own connector class. +//! class ConnRef { public: - ConnRef(Router *router, const unsigned int id); - ConnRef(Router *router, const unsigned int id, - const Point& src, const Point& dst); - virtual ~ConnRef(); + //! @brief Constructs a connector with no endpoints specified. + //! + //! @param[in] router The router scene to place the connector into. + //! @param[in] id A unique positive integer ID for the connector. + //! + //! If an ID is not specified, then one will be assigned to the shape. + //! If assigning an ID yourself, note that it should be a unique + //! positive integer. Also, IDs are given to all objects in a scene, + //! so the same ID cannot be given to a shape and a connector for + //! example. + //! + ConnRef(Router *router, const unsigned int id = 0); + //! @brief Constructs a connector with endpoints specified. + //! + //! @param[in] router The router scene to place the connector into. + //! @param[in] id A unique positive integer ID for the connector. + //! @param[in] src The source endpoint of the connector. + //! @param[in] dst The destination endpoint of the connector. + //! + //! If an ID is not specified, then one will be assigned to the shape. + //! If assigning an ID yourself, note that it should be a unique + //! positive integer. Also, IDs are given to all objects in a scene, + //! so the same ID cannot be given to a shape and a connector for + //! example. + //! + ConnRef(Router *router, const ConnEnd& src, const ConnEnd& dst, + const unsigned int id = 0); + //! @brief Destuctor. + ~ConnRef(); - void setType(unsigned int type); - PolyLine& route(void); - bool needsReroute(void); - void freeRoute(void); + //! @brief Sets both new source and destination endpoints for this + //! connector. + //! + //! @param[in] srcPoint New source endpoint for the connector. + //! @param[in] dstPoint New destination endpoint for the connector. + void setEndpoints(const ConnEnd& srcPoint, const ConnEnd& dstPoint); + //! @brief Sets just a new source endpoint for this connector. + //! + //! @param[in] srcPoint New source endpoint for the connector. + void setSourceEndpoint(const ConnEnd& srcPoint); + //! @brief Sets just a new destination endpoint for this connector. + //! + //! @param[in] dstPoint New destination endpoint for the connector. + void setDestEndpoint(const ConnEnd& dstPoint); + //! @brief Returns the ID of this connector. + //! @returns The ID of the connector. + unsigned int id(void) const; + //! @brief Returns a pointer to the router scene this connector is in. + //! @returns A pointer to the router scene for this connector. + Router *router(void) const; + + //! @brief Returns an indication of whether this connector has a + //! new route and thus needs to be repainted. + //! + //! If the connector has been rerouted and need repainting, the + //! route() method can be called to get a reference to the new route. + //! + //! @returns Returns true if the connector requires repainting, or + //! false if it does not. + bool needsRepaint(void) const; + + //! @brief Returns a reference to the current route for the connector. + //! + //! This is a "raw" version of the route, where each line segment in + //! the route may be made up of multiple collinear line segments. It + //! will also not have post-processing (like curved corners) applied + //! to it. The simplified route for display can be obtained by calling + //! displayRoute(). + //! + //! @returns The PolyLine route for the connector. + //! @note You can obtain a modified version of this poly-line + //! route with curved corners added by calling + //! PolyLine::curvedPolyline(). + const PolyLine& route(void) const; + + //! @brief Returns a reference to the current display version of the + //! route for the connector. + //! + //! The display version of a route has been simplified to collapse all + //! collinear line segments into single segments. It may also have + //! post-processing applied to the route, such as curved corners or + //! nudging. + //! + //! @returns The PolyLine display route for the connector. + PolyLine& displayRoute(void); + + //! @brief Sets a callback function that will called to indicate that + //! the connector needs rerouting. + //! + //! The cb function will be called when shapes are added to, removed + //! from or moved about on the page. The pointer ptr will be passed + //! as an argument to the callback function. + //! + //! @param[in] cb A pointer to the callback function. + //! @param[in] ptr A generic pointer that will be passed to the + //! callback function. + void setCallback(void (*cb)(void *), void *ptr); + //! @brief Returns the type of routing performed for this connector. + //! @return The type of routing performed. + //! + ConnType routingType(void) const; + //! @brief Sets the type of routing to be performed for this + //! connector. + //! + //! If a call to this method changes the current type of routing + //! being used for the connector, then it will get rerouted during + //! the next processTransaction() call, or immediately if + //! transactions are not being used. + //! + //! @param type The type of routing to be performed. + //! + void setRoutingType(ConnType type); + + + + // @brief Returns the source endpoint vertex in the visibility graph. + // @returns The source endpoint vertex. + VertInf *src(void); + // @brief Returns the destination endpoint vertex in the + // visibility graph. + // @returns The destination endpoint vertex. + VertInf *dst(void); + + + void set_route(const PolyLine& route); void calcRouteDist(void); - void updateEndPoint(const unsigned int type, const Point& point); void setEndPointId(const unsigned int type, const unsigned int id); unsigned int getSrcShapeId(void); unsigned int getDstShapeId(void); void makeActive(void); void makeInactive(void); - void lateSetup(const Point& src, const Point& dst); - unsigned int id(void); - VertInf *src(void); - VertInf *dst(void); + VertInf *start(void); void removeFromGraph(void); bool isInitialised(void); - void unInitialise(void); - void setCallback(void (*cb)(void *), void *ptr); - void handleInvalid(void); - int generatePath(Point p0, Point p1); void makePathInvalid(void); - Router *router(void); void setHateCrossings(bool value); bool doesHateCrossings(void); - - friend void Router::attachedShapes(IntList &shapes, - const unsigned int shapeId, const unsigned int type); - friend void Router::attachedConns(IntList &conns, - const unsigned int shapeId, const unsigned int type); - friend void Router::markConnectors(ShapeRef *shape); - + void setEndpoint(const unsigned int type, const ConnEnd& connEnd); + bool setEndpoint(const unsigned int type, const VertID& pointID, + Point *pointSuggestion = NULL); + private: + friend class Router; + + PolyLine& routeRef(void); + void freeRoutes(void); + void performCallback(void); + bool generatePath(void); + bool generatePath(Point p0, Point p1); + void unInitialise(void); + void updateEndPoint(const unsigned int type, const ConnEnd& connEnd); + void common_updateEndPoint(const unsigned int type, const ConnEnd& connEnd); Router *_router; unsigned int _id; - unsigned int _type; + ConnType _type; unsigned int _srcId, _dstId; + bool _orthogonal; bool _needs_reroute_flag; bool _false_path; + bool _needs_repaint; bool _active; PolyLine _route; + Polygon _display_route; double _route_dist; ConnRefList::iterator _pos; VertInf *_srcVert; VertInf *_dstVert; + VertInf *_startVert; bool _initialised; void (*_callback)(void *); void *_connector; @@ -96,6 +339,69 @@ class ConnRef }; +class PointRep; +typedef std::set PointRepSet; +typedef std::list PointRepList; + +class PointRep +{ + public: + PointRep(Point *p, const ConnRef *c) + : point(p), + conn(c) + + { + } + bool follow_inner(PointRep *target); + + Point *point; + const ConnRef *conn; + // inner_set: Set of pointers to the PointReps 'inner' of + // this one, at this corner. + PointRepSet inner_set; +}; + + +typedef std::pair PtConnPtrPair; + +class PtOrder +{ + public: + PtOrder() + { + } + ~PtOrder(); + bool addPoints(const int dim, PtConnPtrPair innerArg, + PtConnPtrPair outerArg, bool swapped); + void sort(const int dim); + int positionFor(const ConnRef *conn, const size_t dim) const; + + // One for each dimension. + PointRepList connList[2]; +}; + +typedef std::map PtOrderMap; +typedef std::set PointSet; + + +const unsigned int CROSSING_NONE = 0; +const unsigned int CROSSING_TOUCHES = 1; +const unsigned int CROSSING_SHARES_PATH = 2; +const unsigned int CROSSING_SHARES_PATH_AT_END = 4; +const unsigned int CROSSING_SHARES_FIXED_SEGMENT = 8; + + +typedef std::pair CrossingsInfoPair; + +extern CrossingsInfoPair countRealCrossings( Avoid::Polygon& poly, + bool polyIsConn, Avoid::Polygon& conn, size_t cIndex, + bool checkForBranchingSegments, const bool finalSegment = false, + PointSet *crossingPoints = NULL, PtOrderMap *pointOrders = NULL, + ConnRef *polyConnRef = NULL, ConnRef *connConnRef = NULL); +extern void splitBranchingSegments(Avoid::Polygon& poly, bool polyIsConn, + Avoid::Polygon& conn, const double tolerance = 0); +extern bool validateBendPoint(VertInf *aInf, VertInf *bInf, VertInf *cInf); + } diff --git a/src/libavoid/debug.h b/src/libavoid/debug.h index 20e6f4705..443529ece 100644 --- a/src/libavoid/debug.h +++ b/src/libavoid/debug.h @@ -2,24 +2,27 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2008 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * Author(s): Michael Wybrow */ + #ifndef AVOID_DEBUG_H #define AVOID_DEBUG_H @@ -42,7 +45,7 @@ inline void db_printf(const char *fmt, ...) va_end(ap); } #else -inline void db_printf(const char */*fmt*/, ...) +inline void db_printf(const char *, ...) { } #endif diff --git a/src/libavoid/geometry.cpp b/src/libavoid/geometry.cpp index 15840c381..2523375cf 100644 --- a/src/libavoid/geometry.cpp +++ b/src/libavoid/geometry.cpp @@ -2,7 +2,8 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2009 Monash University * * -------------------------------------------------------------------- * Much of the code in this module is based on code published with @@ -18,70 +19,42 @@ * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * Author(s): Michael Wybrow */ + +#include + #include "libavoid/graph.h" #include "libavoid/geometry.h" -#include "libavoid/polyutil.h" - -#include +#include "libavoid/assertions.h" namespace Avoid { -Point::Point() -{ -} - - -Point::Point(const double xv, const double yv) - : x(xv) - , y(yv) -{ -} - - -bool Point::operator==(const Point& rhs) const -{ - if ((x == rhs.x) && (y == rhs.y)) - { - return true; - } - return false; -} - - -bool Point::operator!=(const Point& rhs) const -{ - if ((x != rhs.x) || (y != rhs.y)) - { - return true; - } - return false; -} - // Returns true iff the point c lies on the closed segment ab. +// To be used when the points are known to be collinear. // // Based on the code of 'Between'. // -static const bool inBetween(const Point& a, const Point& b, const Point& c) +bool inBetween(const Point& a, const Point& b, const Point& c) { // We only call this when we know the points are collinear, // otherwise we should be checking this here. - assert(vecDir(a, b, c) == 0); + COLA_ASSERT(vecDir(a, b, c, 0.0001) == 0); - if (a.x != b.x) + if ((fabs(a.x - b.x) > 1) && (a.x != b.x)) { // not vertical return (((a.x < c.x) && (c.x < b.x)) || @@ -95,6 +68,15 @@ static const bool inBetween(const Point& a, const Point& b, const Point& c) } +// Returns true iff the point c lies on the closed segment ab. +// +bool pointOnLine(const Point& a, const Point& b, const Point& c, + const double tolerance) +{ + return (vecDir(a, b, c, tolerance) == 0) && inBetween(a, b, c); +} + + // Returns true if the segment cd intersects the segment ab, blocking // visibility. // @@ -104,15 +86,15 @@ bool segmentIntersect(const Point& a, const Point& b, const Point& c, const Point& d) { int ab_c = vecDir(a, b, c); - if ((ab_c == 0) && inBetween(a, b, c)) + if (ab_c == 0) { - return true; + return false; } int ab_d = vecDir(a, b, d); - if ((ab_d == 0) && inBetween(a, b, d)) + if (ab_d == 0) { - return true; + return false; } // It's ok for either of the points a or b to be on the line cd, @@ -131,6 +113,37 @@ bool segmentIntersect(const Point& a, const Point& b, const Point& c, } +// Returns true if the segment e1-e2 intersects the shape boundary +// segment s1-s2, blocking visibility. +// +bool segmentShapeIntersect(const Point& e1, const Point& e2, const Point& s1, + const Point& s2, bool& seenIntersectionAtEndpoint) +{ + if (segmentIntersect(e1, e2, s1, s2)) + { + // Basic intersection of segments. + return true; + } + else if ( (((s2 == e1) || pointOnLine(s1, s2, e1)) && + (vecDir(s1, s2, e2) != 0)) + || + (((s2 == e2) || pointOnLine(s1, s2, e2)) && + (vecDir(s1, s2, e1) != 0)) ) + { + // Segments intersect at the endpoint of one of the segments. We + // allow this once, but the second one blocks visibility. Otherwise + // shapes butted up against each other could have visibility through + // shapes. + if (seenIntersectionAtEndpoint) + { + return true; + } + seenIntersectionAtEndpoint = true; + } + return false; +} + + // Returns true iff the point p in a valid region that can contain // shortest paths. a0, a1, a2 are ordered vertices of a shape. // @@ -205,20 +218,9 @@ int cornerSide(const Point &c1, const Point &c2, const Point &c3, int s12p = vecDir(c1, c2, p); int s23p = vecDir(c2, c3, p); - if (s12p == 0) - { - // Case of p being somewhere on c1-c2. - return s23p; - } - if (s23p == 0) - { - // Case of p being somewhere on c2-c3. - return s12p; - } - if (s123 == 1) { - if ((s12p == 1) && (s23p == 1)) + if ((s12p >= 0) && (s23p >= 0)) { return 1; } @@ -226,18 +228,37 @@ int cornerSide(const Point &c1, const Point &c2, const Point &c3, } else if (s123 == -1) { - if ((s12p == -1) && (s23p == -1)) + if ((s12p <= 0) && (s23p <= 0)) { return -1; } return 1; } - // Case of c3 being somewhere on c1-c2. + + // c1-c2-c3 are collinear, so just return vecDir from c1-c2 return s12p; } -// Returns the distance between points a and b. +// Returns the Euclidean distance between points a and b. +// +double euclideanDist(const Point& a, const Point& b) +{ + double xdiff = a.x - b.x; + double ydiff = a.y - b.y; + + return sqrt((xdiff * xdiff) + (ydiff * ydiff)); +} + +// Returns the Manhattan distance between points a and b. +// +double manhattanDist(const Point& a, const Point& b) +{ + return fabs(a.x - b.x) + fabs(a.y - b.y); +} + + +// Returns the Euclidean distance between points a and b. // double dist(const Point& a, const Point& b) { @@ -248,11 +269,12 @@ double dist(const Point& a, const Point& b) } // Returns the total length of all line segments in the polygon -double totalLength(const Polygn& poly) +double totalLength(const Polygon& poly) { double l = 0; - for (int i = 0; i < poly.pn-1; ++i) { - l += dist(poly.ps[i], poly.ps[i+1]); + for (size_t i = 1; i < poly.size(); ++i) + { + l += dist(poly.ps[i-1], poly.ps[i]); } return l; } @@ -277,18 +299,27 @@ double angle(const Point& a, const Point& b, const Point& c) // This is a fast version that only works for convex shapes. The // other version (inPolyGen) is more general. // -bool inPoly(const Polygn& poly, const Point& q) +bool inPoly(const Polygon& poly, const Point& q, bool countBorder) { - int n = poly.pn; - Point *P = poly.ps; - for (int i = 0; i < n; i++) + size_t n = poly.size(); + const std::vector& P = poly.ps; + bool onBorder = false; + for (size_t i = 0; i < n; i++) { // point index; i1 = i-1 mod n - int prev = (i + n - 1) % n; - if (vecDir(P[prev], P[i], q) == -1) + size_t prev = (i + n - 1) % n; + int dir = vecDir(P[prev], P[i], q); + if (dir == -1) { + // Point is outside return false; } + // Record if point was on a boundary. + onBorder |= (dir == 0); + } + if (!countBorder && onBorder) + { + return false; } return true; } @@ -299,37 +330,36 @@ bool inPoly(const Polygn& poly, const Point& q) // // Based on the code of 'InPoly'. // -bool inPolyGen(const Polygn& argpoly, const Point& q) +bool inPolyGen(const PolygonInterface& argpoly, const Point& q) { // Numbers of right and left edge/ray crossings. int Rcross = 0; int Lcross = 0; // Copy the argument polygon - Polygn poly = copyPoly(argpoly); - Point *P = poly.ps; - int n = poly.pn; + Polygon poly = argpoly; + std::vector& P = poly.ps; + size_t n = poly.size(); // Shift so that q is the origin. This is done for pedogical clarity. - for (int i = 0; i < n; ++i) + for (size_t i = 0; i < n; ++i) { P[i].x = P[i].x - q.x; P[i].y = P[i].y - q.y; } // For each edge e=(i-1,i), see if crosses ray. - for (int i = 0; i < n; ++i) + for (size_t i = 0; i < n; ++i) { // First see if q=(0,0) is a vertex. if ((P[i].x == 0) && (P[i].y == 0)) { // We count a vertex as inside. - freePoly(poly); return true; } // point index; i1 = i-1 mod n - int i1 = ( i + n - 1 ) % n; + size_t i1 = ( i + n - 1 ) % n; // if e "straddles" the x-axis... // The commented-out statement is logically equivalent to the one @@ -367,7 +397,6 @@ bool inPolyGen(const Polygn& argpoly, const Point& q) } } } - freePoly(poly); // q on the edge if left and right cross are not the same parity. if ( (Rcross % 2) != (Lcross % 2) ) @@ -400,8 +429,7 @@ bool inPolyGen(const Polygn& argpoly, const Point& q) int segmentIntersectPoint(const Point& a1, const Point& a2, const Point& b1, const Point& b2, double *x, double *y) { - - double Ax,Bx,Cx,Ay,By,Cy,d,e,f,num,offset; + double Ax,Bx,Cx,Ay,By,Cy,d,e,f,num; double x1lo,x1hi,y1lo,y1hi; Ax = a2.x - a1.x; @@ -450,14 +478,13 @@ int segmentIntersectPoint(const Point& a1, const Point& a2, if (y1hi < b1.y || b2.y < y1lo) return DONT_INTERSECT; } - Cx = a1.x - b1.x; Cy = a1.y - b1.y; // alpha numerator: d = By*Cx - Bx*Cy; // Both denominator: f = Ay*Bx - Ax*By; - // aplha tests: + // alpha tests: if (f > 0) { if (d < 0 || d > f) return DONT_INTERSECT; @@ -485,15 +512,49 @@ int segmentIntersectPoint(const Point& a1, const Point& a2, // Numerator: num = d*Ax; - // Round direction: - offset = SAME_SIGNS(num,f) ? f/2 : -f/2; // Intersection X: - *x = a1.x + (num+offset) / f; + *x = a1.x + (num) / f; + + num = d*Ay; + // Intersection Y: + *y = a1.y + (num) / f; + + return DO_INTERSECT; +} + + +// Line Segment Intersection +// Original code by Franklin Antonio +// +int rayIntersectPoint(const Point& a1, const Point& a2, + const Point& b1, const Point& b2, double *x, double *y) +{ + double Ax,Bx,Cx,Ay,By,Cy,d,f,num; + + Ay = a2.y - a1.y; + By = b1.y - b2.y; + Ax = a2.x - a1.x; + Bx = b1.x - b2.x; + + Cx = a1.x - b1.x; + Cy = a1.y - b1.y; + // alpha numerator: + d = By*Cx - Bx*Cy; + // Both denominator: + f = Ay*Bx - Ax*By; + + // compute intersection coordinates: + + if (f == 0) return PARALLEL; + + // Numerator: + num = d*Ax; + // Intersection X: + *x = a1.x + (num) / f; num = d*Ay; - offset = SAME_SIGNS(num,f) ? f/2 : -f/2; // Intersection Y: - *y = a1.y + (num+offset) / f; + *y = a1.y + (num) / f; return DO_INTERSECT; } diff --git a/src/libavoid/geometry.h b/src/libavoid/geometry.h index 1422be050..abd0d60e2 100644 --- a/src/libavoid/geometry.h +++ b/src/libavoid/geometry.h @@ -2,7 +2,8 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2009 Monash University * * -------------------------------------------------------------------- * Much of the code in this module is based on code published with @@ -18,16 +19,17 @@ * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * Author(s): Michael Wybrow */ @@ -35,21 +37,30 @@ #define _GEOMETRY_H #include "libavoid/geomtypes.h" +#include "libavoid/assertions.h" namespace Avoid { -extern double dist(const Point& a, const Point& b); -extern double totalLength(const Polygn& poly); +extern double euclideanDist(const Point& a, const Point& b); +extern double manhattanDist(const Point& a, const Point& b); +extern double totalLength(const Polygon& poly); extern double angle(const Point& a, const Point& b, const Point& c); extern bool segmentIntersect(const Point& a, const Point& b, const Point& c, const Point& d); -extern bool inPoly(const Polygn& poly, const Point& q); -extern bool inPolyGen(const Polygn& poly, const Point& q); +extern bool segmentShapeIntersect(const Point& e1, const Point& e2, + const Point& s1, const Point& s2, bool& seenIntersectionAtEndpoint); +extern bool inPoly(const Polygon& poly, const Point& q, bool countBorder = true); +extern bool inPolyGen(const PolygonInterface& poly, const Point& q); extern bool inValidRegion(bool IgnoreRegions, const Point& a0, const Point& a1, const Point& a2, const Point& b); extern int cornerSide(const Point &c1, const Point &c2, const Point &c3, const Point& p); +extern bool pointOnLine(const Point& a, const Point& b, const Point& c, + const double tolerance = 0.0); + +// To be used only when the points are known to be colinear. +extern bool inBetween(const Point& a, const Point& b, const Point& c); // Direction from vector. @@ -61,16 +72,22 @@ extern int cornerSide(const Point &c1, const Point &c2, const Point &c3, // // Based on the code of 'AreaSign'. // -static inline int vecDir(const Point& a, const Point& b, const Point& c) +// The 'maybeZero' argument can be used to adjust the tolerance of the +// function. It will be most accurate when 'maybeZero' == 0.0, the default. +// +static inline int vecDir(const Point& a, const Point& b, const Point& c, + const double maybeZero = 0.0) { + COLA_ASSERT(maybeZero >= 0); + double area2 = ((b.x - a.x) * (c.y - a.y)) - ((c.x - a.x) * (b.y - a.y)); - if (area2 < -0.001) + if (area2 < (-maybeZero)) { return -1; } - else if (area2 > 0.001) + else if (area2 > maybeZero) { return 1; } @@ -100,6 +117,8 @@ static const int DO_INTERSECT = 1; static const int PARALLEL = 3; extern int segmentIntersectPoint(const Point& a1, const Point& a2, const Point& b1, const Point& b2, double *x, double *y); +extern int rayIntersectPoint(const Point& a1, const Point& a2, + const Point& b1, const Point& b2, double *x, double *y); } diff --git a/src/libavoid/geomtypes.h b/src/libavoid/geomtypes.h index dd9d26f2f..ced53e6b0 100644 --- a/src/libavoid/geomtypes.h +++ b/src/libavoid/geomtypes.h @@ -2,70 +2,314 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2009 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * Author(s): Michael Wybrow */ +//! @file geomtypes.h +//! @brief Contains the interface for various geometry types and classes. + #ifndef AVOID_GEOMTYPES_H #define AVOID_GEOMTYPES_H +#include +#include + namespace Avoid { - + +//! @brief The Point class defines a point in the plane. +//! +//! Points consist of an x and y value. They may also have an ID and vertex +//! number associated with them. +//! class Point { public: + //! @brief Default constructor. + //! Point(); + //! @brief Standard constructor. + //! + //! @param[in] xv The x position of the point. + //! @param[in] yv The y position of the point. + //! Point(const double xv, const double yv); + + //! @brief Comparison operator. Returns true if at same position. + //! + //! @param[in] rhs The point to compare with this one. + //! @return The result of the comparison. + //! bool operator==(const Point& rhs) const; + //! @brief Comparison operator. Returns true if at different positions. + //! + //! @param[in] rhs The point to compare with this one. + //! @return The result of the comparison. + //! bool operator!=(const Point& rhs) const; + //! @brief Comparison operator. Returns true if less-then rhs point. + //! + //! @note This operator is not particularly useful, but is defined + //! to allow std::set. + //! + //! @param[in] rhs The point to compare with this one. + //! @return The result of the comparison. + //! + bool operator<(const Point& rhs) const; + //! @brief Returns the x or y value of the point, given the dimension. + //! + //! @param[in] dimension The dimension: 0 for x, 1 for y. + //! @return The component of the point in that dimension. + double& operator[](const unsigned int dimension); + const double& operator[](const unsigned int dimension) const; + + //! The x position. double x; + //! The y position. double y; - int id; + //! The ID associated with this point. + unsigned int id; + //! The vertex number associated with this point. + unsigned short vn; }; +//! Constant value representing an unassigned vertex number. +//! +static const unsigned short kUnassignedVertexNumber = 8; + + +//! @brief A vector, represented by the Point class. +//! typedef Point Vector; -typedef struct +//! @brief A common interface used by the Polygon classes. +//! +class PolygonInterface { - int id; - Point *ps; - int pn; -} Polygn; - -typedef Polygn PolyLine; + public: + //! @brief Constructor. + PolygonInterface() { } + //! @brief Destructor. + virtual ~PolygonInterface() { } + //! @brief Resets this to the empty polygon. + virtual void clear(void) = 0; + //! @brief Returns true if this polygon is empty. + virtual bool empty(void) const = 0; + //! @brief Returns the number of points in this polygon. + virtual size_t size(void) const = 0; + //! @brief Returns the ID value associated with this polygon. + virtual int id(void) const = 0; + //! @brief Returns a specific point in the polygon. + //! @param[in] index The array index of the point to be returned. + virtual const Point& at(size_t index) const = 0; + //! @brief Returns the bounding rectangle that contains this polygon. + //! + //! If a NULL pointer is passed for any of the arguments, then that + //! value is ignored and not returned. + //! + //! @param[out] minX The left hand side of the bounding box. + //! @param[out] minY The top of the bounding box. + //! @param[out] maxX The right hand side of the bounding box. + //! @param[out] maxY The bottom of the bounding box. + void getBoundingRect(double *minX, double *minY, + double *maxX, double *maxY) const; +}; -typedef struct +//! @brief A line between two points. +//! +class Edge { - Point a; - Point b; -} Edge; + public: + //! The first point. + Point a; + //! The second point. + Point b; +}; + +//! @brief A bounding box, represented with an Edge between top-left and +//! bottom-right corners. +//! typedef Edge BBox; +class Router; +class ReferencingPolygon; + + +//! @brief A dynamic Polygon, to which points can be easily added and removed. +//! +//! @note The Rectangle class can be used as an easy way of constructing a +//! square or rectangular polygon. +//! +class Polygon : public PolygonInterface +{ + public: + //! @brief Constructs an empty polygon (with zero points). + Polygon(); + //! @brief Constructs a new polygon with n points. + //! + //! A rectangle would be comprised of four point. An n segment + //! PolyLine (represented as a Polygon) would be comprised of n+1 + //! points. Whether a particular Polygon is closed or not, depends + //! on whether it is a Polygon or Polyline. Shape polygons are always + //! considered to be closed, meaning the last point joins back to the + //! first point. + //! + //! @param[in] n Number of points in the polygon. + //! + Polygon(const int n); + //! @brief Constructs a new polygon from an existing Polygon. + //! + //! @param[in] poly An existing polygon to copy the new polygon from. + //! + Polygon(const PolygonInterface& poly); + //! @brief Resets this to the empty polygon. + void clear(void); + //! @brief Returns true if this polygon is empty. + bool empty(void) const; + //! @brief Returns the number of points in this polygon. + size_t size(void) const; + //! @brief Returns the ID value associated with this polygon. + int id(void) const; + //! @brief Returns a specific point in the polygon. + //! @param[in] index The array index of the point to be returned. + const Point& at(size_t index) const; + //! @brief Returns a simplified Polyline, where all collinear line + //! segments have been collapsed down into single line + //! segments. + //! + //! @return A new polyline with a simplified representation. + //! + Polygon simplify(void) const; + //! @brief Returns a curved approximation of this multi-segment + //! PolyLine, with the corners replaced by smooth Bezier + //! curves. + //! + //! This function does not do any further obstacle avoidance with the + //! curves produced. Hence, you would usually specify a curve_amount + //! in similar size to the space buffer around obstacles in the scene. + //! This way the curves will cut the corners around shapes but still + //! run within this buffer space. + //! + //! @param curve_amount Describes the distance along the end of each + //! line segment to turn into a curve. + //! @param closed Describes whether the Polygon should be + //! treated as closed. Defaults to false. + //! @return A new polyline (polygon) representing the curved path. + //! Its points represent endpoints of line segments and + //! Bezier spline control points. The Polygon::ts vector for + //! this returned polygon is populated with a character for + //! each point describing its type. + //! @sa ts + Polygon curvedPolyline(const double curve_amount, + const bool closed = false) const; + //! @brief Translates the polygon position by a relative amount. + //! + //! @param[in] xDist Distance to move polygon in the x dimension. + //! @param[in] yDist Distance to move polygon in the y dimension. + void translate(const double xDist, const double yDist); + + //! @brief An ID for the polygon. + int _id; + //! @brief A vector of the points that make up the Polygon. + std::vector ps; + //! @brief If used, denotes whether the corresponding point in ps is + //! a move-to operation or a Bezier curve-to. + //! + //! Each character describes the drawing operation for the + //! corresponding point in the ps vector. Possible values are: + //! - 'M': A moveto operation, marks the first point; + //! - 'L': A lineto operation, is a line from the previous point to + //! the current point; or + //! - 'C': A curveto operation, three consecutive 'C' points + //! (along with the previous point) describe the control points + //! of a Bezier curve. + //! - 'Z': Closes the path (used for cluster boundaries). + //! + //! @note This vector will currently only be populated for polygons + //! returned by curvedPolyline(). + std::vector ts; +}; + + +//! @brief A multi-segment line, represented with the Polygon class. +//! +typedef Polygon PolyLine; + + +//! @brief A Polygon which just references its points from other Polygons. +//! +//! This type of Polygon is used to accurately represent cluster boundaries +//! made up from the corner points of shapes. +//! +class ReferencingPolygon : public PolygonInterface +{ + public: + ReferencingPolygon(); + ReferencingPolygon(const Polygon& poly, const Router *router); + void clear(void); + bool empty(void) const; + size_t size(void) const; + int id(void) const; + const Point& at(size_t index) const; + + int _id; + std::vector > ps; +}; + + +//! @brief A Rectangle, a simpler way to define the polygon for square or +//! rectangular shapes. +//! +class Rectangle : public Polygon +{ + public: + //! @brief Constructs a rectangular polygon given two opposing + //! corner points. + //! + //! @param[in] topLeft The first corner point of the rectangle. + //! @param[in] bottomRight The opposing corner point of the rectangle. + //! + Rectangle(const Point& topLeft, const Point& bottomRight); + + //! @brief Constructs a rectangular polygon given the centre, width + //! and height. + //! + //! @param[in] centre The centre of the rectangle, specified as + //! a point. + //! @param[in] width The width of the rectangle. + //! @param[in] height The height of the rectangle. + //! + Rectangle(const Point& centre, const double width, const double height); +}; + + } #endif diff --git a/src/libavoid/graph.cpp b/src/libavoid/graph.cpp index 1970212df..728f8c085 100644 --- a/src/libavoid/graph.cpp +++ b/src/libavoid/graph.cpp @@ -2,56 +2,61 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2009 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * Author(s): Michael Wybrow */ + +#include + #include "libavoid/debug.h" #include "libavoid/graph.h" #include "libavoid/connector.h" #include "libavoid/geometry.h" -#include "libavoid/polyutil.h" #include "libavoid/timer.h" #include "libavoid/vertices.h" #include "libavoid/router.h" +#include "libavoid/assertions.h" -#include using std::pair; namespace Avoid { -EdgeInf::EdgeInf(VertInf *v1, VertInf *v2) - : lstPrev(NULL) - , lstNext(NULL) - , _blocker(0) - , _router(NULL) - , _added(false) - , _visible(false) - , _v1(v1) - , _v2(v2) - , _dist(-1) +EdgeInf::EdgeInf(VertInf *v1, VertInf *v2, const bool orthogonal) + : lstPrev(NULL), + lstNext(NULL), + _blocker(0), + _router(NULL), + _added(false), + _visible(false), + _orthogonal(orthogonal), + _v1(v1), + _v2(v2), + _dist(-1) { // Not passed NULL values. - assert(v1 && v2); + COLA_ASSERT(v1 && v2); // We are in the same instance - assert(_v1->_router == _v2->_router); + COLA_ASSERT(_v1->_router == _v2->_router); _router = _v1->_router; _conns.clear(); @@ -67,25 +72,142 @@ EdgeInf::~EdgeInf() } +// Gives an order value between 0 and 3 for the point c, given the last +// segment was from a to b. Returns the following value: +// 0 : Point c is directly backwards from point b. +// 1 : Point c is a left-hand 90 degree turn. +// 2 : Point c is a right-hand 90 degree turn. +// 3 : Point c is straight ahead (collinear). +// +static inline int orthogTurnOrder(const Point& a, const Point& b, + const Point& c) +{ + // We should only be calling this with orthogonal points, + COLA_ASSERT((c.x == b.x) || (c.y == b.y)); + COLA_ASSERT((a.x == b.x) || (a.y == b.y)); + + int direction = vecDir(a, b, c); + + if (direction > 0) + { + // Counterclockwise := left + return 1; + } + else if (direction < 0) + { + // Clockwise := right + return 2; + } + + if (b.x == c.x) + { + if ( ((a.y < b.y) && (c.y < b.y)) || + ((a.y > b.y) && (c.y > b.y)) ) + { + // Behind. + return 0; + } + } + else + { + if ( ((a.x < b.x) && (c.x < b.x)) || + ((a.x > b.x) && (c.x > b.x)) ) + { + // Behind. + return 0; + } + } + + // Ahead. + return 3; +} + + +// Returns a less than operation for a set exploration order for orthogonal +// searching. Forward, then left, then right. Or if there is no previous +// point, then the order is north, east, south, then west. +// Note: This method assumes the two Edges that share a common point. +bool EdgeInf::rotationLessThan(const VertInf *lastV, const EdgeInf *rhs) const +{ + if ((_v1 == rhs->_v1) && (_v2 == rhs->_v2)) + { + // Effectively the same visibility edge, so they are equal. + return false; + } + VertInf *lhsV = NULL, *rhsV = NULL, *commonV = NULL; + + // Determine common Point and the comparison point on the left- and + // the right-hand-side. + if (_v1 == rhs->_v1) + { + commonV = _v1; + lhsV = _v2; + rhsV = rhs->_v2; + } + else if (_v1 == rhs->_v2) + { + commonV = _v1; + lhsV = _v2; + rhsV = rhs->_v1; + } + else if (_v2 == rhs->_v1) + { + commonV = _v2; + lhsV = _v1; + rhsV = rhs->_v2; + } + else if (_v2 == rhs->_v2) + { + commonV = _v2; + lhsV = _v1; + rhsV = rhs->_v1; + } + + const Point& lhsPt = lhsV->point; + const Point& rhsPt = rhsV->point; + const Point& commonPt = commonV->point; + + // If no lastPt, use one directly to the left; + Point lastPt = (lastV) ? lastV->point : Point(commonPt.x - 10, commonPt.y); + + int lhsVal = orthogTurnOrder(lastPt, commonPt, lhsPt); + int rhsVal = orthogTurnOrder(lastPt, commonPt, rhsPt); + + return lhsVal < rhsVal; +} + + void EdgeInf::makeActive(void) { - assert(_added == false); + COLA_ASSERT(_added == false); - if (_visible) + if (_orthogonal) { - _router->visGraph.addEdge(this); - _pos1 = _v1->visList.insert(_v1->visList.begin(), this); - _v1->visListSize++; - _pos2 = _v2->visList.insert(_v2->visList.begin(), this); - _v2->visListSize++; + COLA_ASSERT(_visible); + _router->visOrthogGraph.addEdge(this); + _pos1 = _v1->orthogVisList.insert(_v1->orthogVisList.begin(), this); + _v1->orthogVisListSize++; + _pos2 = _v2->orthogVisList.insert(_v2->orthogVisList.begin(), this); + _v2->orthogVisListSize++; } - else // if (invisible) + else { - _router->invisGraph.addEdge(this); - _pos1 = _v1->invisList.insert(_v1->invisList.begin(), this); - _v1->invisListSize++; - _pos2 = _v2->invisList.insert(_v2->invisList.begin(), this); - _v2->invisListSize++; + if (_visible) + { + _router->visGraph.addEdge(this); + _pos1 = _v1->visList.insert(_v1->visList.begin(), this); + _v1->visListSize++; + _pos2 = _v2->visList.insert(_v2->visList.begin(), this); + _v2->visListSize++; + } + else // if (invisible) + { + _router->invisGraph.addEdge(this); + _pos1 = _v1->invisList.insert(_v1->invisList.begin(), this); + _v1->invisListSize++; + _pos2 = _v2->invisList.insert(_v2->invisList.begin(), this); + _v2->invisListSize++; + } } _added = true; } @@ -93,23 +215,35 @@ void EdgeInf::makeActive(void) void EdgeInf::makeInactive(void) { - assert(_added == true); + COLA_ASSERT(_added == true); - if (_visible) + if (_orthogonal) { - _router->visGraph.removeEdge(this); - _v1->visList.erase(_pos1); - _v1->visListSize--; - _v2->visList.erase(_pos2); - _v2->visListSize--; + COLA_ASSERT(_visible); + _router->visOrthogGraph.removeEdge(this); + _v1->orthogVisList.erase(_pos1); + _v1->orthogVisListSize--; + _v2->orthogVisList.erase(_pos2); + _v2->orthogVisListSize--; } - else // if (invisible) + else { - _router->invisGraph.removeEdge(this); - _v1->invisList.erase(_pos1); - _v1->invisListSize--; - _v2->invisList.erase(_pos2); - _v2->invisListSize--; + if (_visible) + { + _router->visGraph.removeEdge(this); + _v1->visList.erase(_pos1); + _v1->visListSize--; + _v2->visList.erase(_pos2); + _v2->visListSize--; + } + else // if (invisible) + { + _router->invisGraph.removeEdge(this); + _v1->invisList.erase(_pos1); + _v1->invisListSize--; + _v2->invisList.erase(_pos2); + _v2->invisListSize--; + } } _blocker = 0; _conns.clear(); @@ -119,11 +253,12 @@ void EdgeInf::makeInactive(void) void EdgeInf::setDist(double dist) { - //assert(dist != 0); + //COLA_ASSERT(dist != 0); if (_added && !_visible) { makeInactive(); + COLA_ASSERT(!_added); } if (!_added) { @@ -135,6 +270,12 @@ void EdgeInf::setDist(double dist) } +bool EdgeInf::added(void) +{ + return _added; +} + + void EdgeInf::alertConns(void) { FlagList::iterator finish = _conns.end(); @@ -161,11 +302,12 @@ void EdgeInf::addCycleBlocker(void) void EdgeInf::addBlocker(int b) { - assert(_router->InvisibilityGrph); + COLA_ASSERT(_router->InvisibilityGrph); if (_added && _visible) { makeInactive(); + COLA_ASSERT(!_added); } if (!_added) { @@ -232,8 +374,11 @@ void EdgeInf::checkVis(void) cone1 = inValidRegion(_router->IgnoreRegions, i->shPrev->point, iPoint, i->shNext->point, jPoint); } - else + else if (_router->IgnoreRegions == false) { + // If Ignoring regions then this case is already caught by + // the invalid regions, so only check it when not ignoring + // regions. ShapeSet& ss = _router->contains[iID]; if ((jID.isShape) && (ss.find(jID.objID) != ss.end())) @@ -253,8 +398,11 @@ void EdgeInf::checkVis(void) cone2 = inValidRegion(_router->IgnoreRegions, j->shPrev->point, jPoint, j->shNext->point, iPoint); } - else + else if (_router->IgnoreRegions == false) { + // If Ignoring regions then this case is already caught by + // the invalid regions, so only check it when not ignoring + // regions. ShapeSet& ss = _router->contains[jID]; if ((iID.isShape) && (ss.find(iID.objID) != ss.end())) @@ -274,7 +422,7 @@ void EdgeInf::checkVis(void) db_printf("\tSetting visibility edge... \n\t\t"); db_print(); - double d = dist(iPoint, jPoint); + double d = euclideanDist(iPoint, jPoint); setDist(d); @@ -316,26 +464,39 @@ int EdgeInf::firstBlocker(void) } VertInf *last = _router->vertices.end(); + unsigned int lastId = 0; + bool seenIntersectionAtEndpoint = false; for (VertInf *k = _router->vertices.shapesBegin(); k != last; ) { VertID kID = k->id; - if ((ss.find(kID.objID) != ss.end())) + if (k->id == dummyOrthogID) + { + // Don't include orthogonal dummy vertices. + k = k->lstNext; + continue; + } + if (kID.objID != lastId) { - unsigned int shapeID = kID.objID; - db_printf("Endpoint is inside shape %u so ignore shape edges.\n", - kID.objID); - // One of the endpoints is inside this shape so ignore it. - while ((k != last) && (k->id.objID == shapeID)) + if ((ss.find(kID.objID) != ss.end())) { - // And skip the other vertices from this shape. - k = k->lstNext; + unsigned int shapeID = kID.objID; + db_printf("Endpoint is inside shape %u so ignore shape " + "edges.\n", kID.objID); + // One of the endpoints is inside this shape so ignore it. + while ((k != last) && (k->id.objID == shapeID)) + { + // And skip the other vertices from this shape. + k = k->lstNext; + } + continue; } - continue; + seenIntersectionAtEndpoint = false; + lastId = kID.objID; } Point& kPoint = k->point; Point& kPrevPoint = k->shPrev->point; - - if (segmentIntersect(pti, ptj, kPrevPoint, kPoint)) + if (segmentShapeIntersect(pti, ptj, kPrevPoint, kPoint, + seenIntersectionAtEndpoint)) { ss.clear(); return kID.objID; @@ -358,9 +519,17 @@ bool EdgeInf::isBetween(VertInf *i, VertInf *j) } + // Returns true if this edge is a vertical or horizontal line segment. +bool EdgeInf::isOrthogonal(void) const +{ + return ((_v1->point.x == _v2->point.x) || + (_v1->point.y == _v2->point.y)); +} + + VertInf *EdgeInf::otherVert(VertInf *vert) { - assert((vert == _v1) || (vert == _v2)); + COLA_ASSERT((vert == _v1) || (vert == _v2)); if (vert == _v1) { @@ -372,12 +541,17 @@ VertInf *EdgeInf::otherVert(VertInf *vert) EdgeInf *EdgeInf::checkEdgeVisibility(VertInf *i, VertInf *j, bool knownNew) { + // This is for polyline routing, so check we're not + // considering orthogonal vertices. + COLA_ASSERT(i->id != dummyOrthogID); + COLA_ASSERT(j->id != dummyOrthogID); + Router *router = i->_router; EdgeInf *edge = NULL; if (knownNew) { - assert(existingEdge(i, j) == NULL); + COLA_ASSERT(existingEdge(i, j) == NULL); edge = new EdgeInf(i, j); } else @@ -399,22 +573,17 @@ EdgeInf *EdgeInf::checkEdgeVisibility(VertInf *i, VertInf *j, bool knownNew) } + // XXX: This function is ineffecient, and shouldn't even really be + // required. EdgeInf *EdgeInf::existingEdge(VertInf *i, VertInf *j) { VertInf *selected = NULL; - if (i->visListSize <= j->visListSize) - { - selected = i; - } - else - { - selected = j; - } - + // Look through poly-line visibility edges. + selected = (i->visListSize <= j->visListSize) ? i : j; EdgeInfList& visList = selected->visList; - EdgeInfList::iterator finish = visList.end(); - for (EdgeInfList::iterator edge = visList.begin(); edge != finish; + EdgeInfList::const_iterator finish = visList.end(); + for (EdgeInfList::const_iterator edge = visList.begin(); edge != finish; ++edge) { if ((*edge)->isBetween(i, j)) @@ -423,18 +592,24 @@ EdgeInf *EdgeInf::existingEdge(VertInf *i, VertInf *j) } } - if (i->invisListSize <= j->invisListSize) - { - selected = i; - } - else + // Look through orthogonal visbility edges. + selected = (i->orthogVisListSize <= j->orthogVisListSize) ? i : j; + EdgeInfList& orthogVisList = selected->orthogVisList; + finish = orthogVisList.end(); + for (EdgeInfList::const_iterator edge = orthogVisList.begin(); + edge != finish; ++edge) { - selected = j; + if ((*edge)->isBetween(i, j)) + { + return (*edge); + } } + // Look through poly-line invisbility edges. + selected = (i->invisListSize <= j->invisListSize) ? i : j; EdgeInfList& invisList = selected->invisList; finish = invisList.end(); - for (EdgeInfList::iterator edge = invisList.begin(); edge != finish; + for (EdgeInfList::const_iterator edge = invisList.begin(); edge != finish; ++edge) { if ((*edge)->isBetween(i, j)) @@ -450,19 +625,45 @@ EdgeInf *EdgeInf::existingEdge(VertInf *i, VertInf *j) //=========================================================================== -EdgeList::EdgeList() - : _firstEdge(NULL) - , _lastEdge(NULL) - , _count(0) +EdgeList::EdgeList(bool orthogonal) + : _orthogonal(orthogonal), + _firstEdge(NULL), + _lastEdge(NULL), + _count(0) +{ +} + + +EdgeList::~EdgeList() +{ + clear(); +} + + +void EdgeList::clear(void) +{ + while (_firstEdge) + { + delete _firstEdge; + } + COLA_ASSERT(_count == 0); + _lastEdge = NULL; +} + + +int EdgeList::size(void) const { + return _count; } void EdgeList::addEdge(EdgeInf *edge) { + COLA_ASSERT(!_orthogonal || edge->isOrthogonal()); + if (_firstEdge == NULL) { - assert(_lastEdge == NULL); + COLA_ASSERT(_lastEdge == NULL); _lastEdge = edge; _firstEdge = edge; @@ -472,7 +673,7 @@ void EdgeList::addEdge(EdgeInf *edge) } else { - assert(_lastEdge != NULL); + COLA_ASSERT(_lastEdge != NULL); _lastEdge->lstNext = edge; edge->lstPrev = _lastEdge; diff --git a/src/libavoid/graph.h b/src/libavoid/graph.h index 05f03a988..db776b80b 100644 --- a/src/libavoid/graph.h +++ b/src/libavoid/graph.h @@ -2,24 +2,27 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2009 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * Author(s): Michael Wybrow */ + #ifndef AVOID_GRAPH_H #define AVOID_GRAPH_H @@ -31,6 +34,7 @@ namespace Avoid { + class ConnRef; class Router; @@ -42,8 +46,8 @@ typedef std::list FlagList; class EdgeInf { public: - EdgeInf(VertInf *v1, VertInf *v2); - virtual ~EdgeInf(); + EdgeInf(VertInf *v1, VertInf *v2, const bool orthogonal = false); + ~EdgeInf(); inline double getDist(void) { return _dist; @@ -53,6 +57,9 @@ class EdgeInf void addConn(bool *flag); void addCycleBlocker(void); void addBlocker(int b); + bool added(void); + bool isOrthogonal(void) const; + bool rotationLessThan(const VertInf* last, const EdgeInf *rhs) const; std::pair ids(void); std::pair points(void); @@ -70,6 +77,7 @@ class EdgeInf Router *_router; bool _added; bool _visible; + bool _orthogonal; VertInf *_v1; VertInf *_v2; EdgeInfList::iterator _pos1; @@ -87,12 +95,17 @@ class EdgeInf class EdgeList { public: - EdgeList(); - void addEdge(EdgeInf *edge); - void removeEdge(EdgeInf *edge); + friend class EdgeInf; + EdgeList(bool orthogonal = false); + ~EdgeList(); + void clear(void); EdgeInf *begin(void); EdgeInf *end(void); + int size(void) const; private: + void addEdge(EdgeInf *edge); + void removeEdge(EdgeInf *edge); + bool _orthogonal; EdgeInf *_firstEdge; EdgeInf *_lastEdge; unsigned int _count; diff --git a/src/libavoid/libavoid.h b/src/libavoid/libavoid.h index d598c6c74..1d4cd1fdc 100644 --- a/src/libavoid/libavoid.h +++ b/src/libavoid/libavoid.h @@ -2,29 +2,37 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2009 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * Author(s): Michael Wybrow */ +//! @file libavoid.h +//! @brief Standard libavoid include file which includes all libavoid +//! header files. + +//! @namespace Avoid +//! @brief The namespace used by code in the libavoid library. + #ifndef AVOID_LIBAVOID_H #define AVOID_LIBAVOID_H #include "libavoid/geomtypes.h" -#include "libavoid/polyutil.h" #include "libavoid/connector.h" #include "libavoid/graph.h" #include "libavoid/debug.h" @@ -32,7 +40,6 @@ #include "libavoid/makepath.h" #include "libavoid/vertices.h" #include "libavoid/visibility.h" -#include "libavoid/static.h" #include "libavoid/router.h" #endif diff --git a/src/libavoid/makepath.cpp b/src/libavoid/makepath.cpp index 3a57f8e4e..4e15dbca9 100644 --- a/src/libavoid/makepath.cpp +++ b/src/libavoid/makepath.cpp @@ -2,43 +2,105 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow * - * -------------------------------------------------------------------- - * The dijkstraPath function is based on code published and described - * in "Algorithms in C" (Second Edition), 1990, by Robert Sedgewick. - * -------------------------------------------------------------------- + * Copyright (C) 2004-2009 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * Author(s): Michael Wybrow */ + +#include +#include +#include +#define _USE_MATH_DEFINES +#include + #include "libavoid/vertices.h" #include "libavoid/makepath.h" #include "libavoid/geometry.h" #include "libavoid/connector.h" #include "libavoid/graph.h" #include "libavoid/router.h" -#include -#include -#include -#include -#include +#include "libavoid/debug.h" +#include "libavoid/assertions.h" +#ifdef ASTAR_DEBUG + #include +#endif namespace Avoid { +class ANode +{ + public: + VertInf* inf; + double g; // Gone + double h; // Heuristic + double f; // Formula f = g + h + + int prevIndex; // Index into DONE for the previous ANode. + int timeStamp; // Timestamp used to determine explaration order of + // seemingly equal paths during orthogonal routing. + + ANode(VertInf *vinf, int time) + : inf(vinf), + g(0), + h(0), + f(0), + prevIndex(-1), + timeStamp(time) + { + } + ANode() + : inf(NULL), + g(0), + h(0), + f(0), + prevIndex(-1), + timeStamp(-1) + { + } +}; + + +// This returns the opposite result (>) so that when used with stl::make_heap, +// the head node of the heap will be the smallest value, rather than the +// largest. This saves us from having to sort the heap (and then reorder +// it back into a heap) when getting the next node to examine. This way we +// get better complexity -- logarithmic pushs and pops to the heap. +// +bool operator<(const ANode &a, const ANode &b) +{ + if (a.f != b.f) + { + return a.f > b.f; + } + if (a.timeStamp != b.timeStamp) + { + // Tiebreaker, if two paths have equal cost, then choose the one with + // the highest timeStamp. This corresponds to the furthest point + // explored along the straight-line path. When exploring we give the + // directions the following timeStamps; left:1, right:2 and forward:3, + // then we always try to explore forward first. + return a.timeStamp < b.timeStamp; + } + COLA_ASSERT(a.prevIndex != b.prevIndex); + return a.prevIndex > b.prevIndex; +} + static double Dot(const Point& l, const Point& r) { @@ -56,6 +118,13 @@ static double CrossLength(const Point& l, const Point& r) // static double angleBetween(const Point& p1, const Point& p2, const Point& p3) { + if ((p1.x == p2.x && p1.y == p2.y) || (p2.x == p3.x && p2.y == p3.y)) + { + // If two of the points are the same, then we can't say anything + // about the angle between. Treat them as being collinear. + return M_PI; + } + Point v1(p1.x - p2.x, p1.y - p2.y); Point v2(p3.x - p2.x, p3.y - p2.y); @@ -63,21 +132,45 @@ static double angleBetween(const Point& p1, const Point& p2, const Point& p3) } +// Construct a temporary Polygon path given several VertInf's for a connector. +// +static void constructPolygonPath(Polygon& connRoute, VertInf *inf2, + VertInf *inf3, std::vector& done, int inf1Index) +{ + int routeSize = 2; + for (int curr = inf1Index; curr >= 0; curr = done[curr].prevIndex) + { + routeSize += 1; + } + connRoute.ps.resize(routeSize); + connRoute.ps[routeSize - 1] = inf3->point; + connRoute.ps[routeSize - 2] = inf2->point; + routeSize -= 3; + for (int curr = inf1Index; curr >= 0; curr = done[curr].prevIndex) + { + connRoute.ps[routeSize] = done[curr].inf->point; + routeSize -= 1; + } +} + + // Given the two points for a new segment of a path (inf2 & inf3) // as well as the distance between these points (dist), as well as // possibly the previous point (inf1) [from inf1--inf2], return a // cost associated with this route. // -double cost(ConnRef *lineRef, const double dist, VertInf *inf1, - VertInf *inf2, VertInf *inf3) +static double cost(ConnRef *lineRef, const double dist, VertInf *inf2, + VertInf *inf3, std::vector& done, int inf1Index) { + VertInf *inf1 = (inf1Index >= 0) ? done[inf1Index].inf : NULL; double result = dist; + Polygon connRoute; Router *router = inf2->_router; - if (inf2->pathNext != NULL) + if (inf1 != NULL) { - double& angle_penalty = router->angle_penalty; - double& segmt_penalty = router->segmt_penalty; + const double angle_penalty = router->routingPenalty(anglePenalty); + const double segmt_penalty = router->routingPenalty(segmentPenalty); // This is not the first segment, so there is a bend // between it and the last one in the existing path. @@ -89,29 +182,83 @@ double cost(ConnRef *lineRef, const double dist, VertInf *inf1, double rad = M_PI - angleBetween(p1, p2, p3); - // Make `xval' between 0--10 then take its log so small - // angles are not penalised as much as large ones. - // - double xval = rad * 10 / M_PI; - double yval = xval * log10(xval + 1) / 10.5; - result += (angle_penalty * yval); - //printf("deg from straight: %g\tpenalty: %g\n", - // rad * 180 / M_PI, (angle_penalty * yval)); - - // Don't penalise as an extra segment if there is no turn. - if (rad > 0.0005) + if (rad > 0) + { + // Make `xval' between 0--10 then take its log so small + // angles are not penalised as much as large ones. + // + double xval = rad * 10 / M_PI; + double yval = xval * log10(xval + 1) / 10.5; + result += (angle_penalty * yval); + //db_printf("deg from straight: %g\tpenalty: %g\n", + // rad * 180 / M_PI, (angle_penalty * yval)); + } + + if (rad == M_PI) { + // Needs to double back + result += (2 * segmt_penalty); + } + else if (rad > 0) + { + // Only penalise as an extra segment if the two + // segments are not collinear. result += segmt_penalty; } } } - if (lineRef->doesHateCrossings() && (router->crossing_penalty > 0)) + if (!router->_inCrossingPenaltyReroutingStage) { - Point& a1 = inf2->point; - Point& a2 = inf3->point; + // Return here if we ar not in the postprocessing stage + return result; + } - ConnRefList::iterator curr, finish = router->connRefs.end(); + const double cluster_crossing_penalty = + router->routingPenalty(clusterCrossingPenalty); + // XXX: Clustered routing doesn't yet work with orthogonal connectors. + if (router->ClusteredRouting && !router->clusterRefs.empty() && + (cluster_crossing_penalty > 0) && + (lineRef->routingType() != ConnType_Orthogonal)) + { + if (connRoute.empty()) + { + constructPolygonPath(connRoute, inf2, inf3, done, inf1Index); + } + // There are clusters so do cluster routing. + for (ClusterRefList::const_iterator cl = router->clusterRefs.begin(); + cl != router->clusterRefs.end(); ++cl) + { + ReferencingPolygon& cBoundary = (*cl)->polygon(); + COLA_ASSERT(cBoundary.ps[0] != cBoundary.ps[cBoundary.size() - 1]); + for (size_t j = 0; j < cBoundary.size(); ++j) + { + // Cluster boundary points should correspond to shape + // vertices and hence already be in the list of vertices. + COLA_ASSERT(router->vertices.getVertexByPos(cBoundary.at(j))!=NULL); + } + + bool isConn = false; + Polygon dynamic_c_boundary(cBoundary); + Polygon dynamic_conn_route(connRoute); + const bool finalSegment = (inf3 == lineRef->dst()); + CrossingsInfoPair crossings = countRealCrossings( + dynamic_c_boundary, isConn, dynamic_conn_route, + connRoute.size() - 1, true, finalSegment); + result += (crossings.first * cluster_crossing_penalty); + } + } + + const double shared_path_penalty = + router->routingPenalty(fixedSharedPathPenalty); + if (shared_path_penalty > 0) + { + // Penalises shared paths, except if the connectors shared an endpoint. + if (connRoute.empty()) + { + constructPolygonPath(connRoute, inf2, inf3, done, inf1Index); + } + ConnRefList::const_iterator curr, finish = router->connRefs.end(); for (curr = router->connRefs.begin(); curr != finish; ++curr) { ConnRef *connRef = *curr; @@ -120,306 +267,237 @@ double cost(ConnRef *lineRef, const double dist, VertInf *inf1, { continue; } - Avoid::PolyLine& route2 = connRef->route(); - for (int j = 1; j < route2.pn; ++j) - { - Avoid::Point& b1 = route2.ps[j - 1]; - Avoid::Point& b2 = route2.ps[j]; + const Avoid::PolyLine& route2 = connRef->route(); - if (((a1 == b1) && (a2 == b2)) || - ((a2 == b1) && (a1 == b2))) - { - // Route along same segment: no penalty. We detect - // crossovers when we see the segments diverge. - continue; - } - - if ((a2 == b2) || (a2 == b1) || (b2 == a1)) - { - // Each crossing that is at a vertex in the - // visibility graph gets noticed four times. - // We ignore three of these cases. - // This also catches the case of a shared path, - // but this is one that terminates at a common - // endpoint, so we don't care about it. - continue; - } - - if (a1 == b1) - { - if (j == 1) - { - // common source point. - continue; - } - Avoid::Point& b0 = route2.ps[j - 2]; - // The segments share an endpoint -- a1==b1. - if (a2 == b0) - { - // a2 is not a split, continue. - continue; - } - - // If here, then we know that a2 != b2 - // And a2 and its pair in b are a split. - assert(a2 != b2); - - if (inf2->pathNext == NULL) - { - continue; - } - Avoid::Point& a0 = inf1->point; - - if ((a0 == b0) || (a0 == b2)) - { - //printf("Shared path... "); - bool normal = (a0 == b0) ? true : false; - // Determine direction we have to look through - // the points of connector b. - int dir = normal ? -1 : 1; - - int traceJ = j - 1 + dir; - - int endCornerSide = Avoid::cornerSide( - a0, a1, a2, normal ? b2 : b0); - - - VertInf *traceInf1 = inf2->pathNext; - VertInf *traceInf2 = inf2; - VertInf *traceInf3 = inf3; - while (traceInf1 && - (traceJ >= 0) && (traceJ < route2.pn) && - (traceInf1->point == route2.ps[traceJ])) - { - traceInf3 = traceInf2; - traceInf2 = traceInf1; - traceInf1 = traceInf1->pathNext; - traceJ += dir; - } - - if (!traceInf1 || - (traceJ < 0) || (traceJ >= route2.pn)) - { - //printf("common source or destination.\n"); - // The connectors have a shared path, but it - // comes from a common source point. - // XXX: There might be a better way to - // check this by asking the connectors - // for the IDs of the attached shapes. - continue; - } - - int startCornerSide = Avoid::cornerSide( - traceInf1->point, traceInf2->point, - traceInf3->point, route2.ps[traceJ]); - - if (endCornerSide != startCornerSide) - { - //printf("crosses.\n"); - result += router->crossing_penalty; - } - else - { - //printf("doesn't cross.\n"); - } - } - else - { - // The connectors cross or touch at this point. - //printf("Cross or touch at point... "); - - int side1 = Avoid::cornerSide(a0, a1, a2, b0); - int side2 = Avoid::cornerSide(a0, a1, a2, b2); - - if (side1 != side2) - { - //printf("cross.\n"); - // The connectors cross at this point. - result += router->crossing_penalty; - } - else - { - //printf("touch.\n"); - // The connectors touch at this point. - } - } - continue; - } + bool isConn = true; + Polygon dynamic_route2(route2); + Polygon dynamic_conn_route(connRoute); + CrossingsInfoPair crossings = countRealCrossings( + dynamic_route2, isConn, dynamic_conn_route, + connRoute.size() - 1, false, false, NULL, NULL, + connRef, lineRef); + + if ((crossings.second & CROSSING_SHARES_PATH) && + (crossings.second & CROSSING_SHARES_FIXED_SEGMENT) && + !(crossings.second & CROSSING_SHARES_PATH_AT_END)) + { + // Penalise unecessary shared paths in the middle of + // connectors. + result += shared_path_penalty; + } + } + } - double xc, yc; - int intersectResult = Avoid::segmentIntersectPoint( - a1, a2, b1, b2, &xc, &yc); + const double crossing_penalty = router->routingPenalty(crossingPenalty); + if (lineRef->doesHateCrossings() && (crossing_penalty > 0)) + { + if (connRoute.empty()) + { + constructPolygonPath(connRoute, inf2, inf3, done, inf1Index); + } + ConnRefList::const_iterator curr, finish = router->connRefs.end(); + for (curr = router->connRefs.begin(); curr != finish; ++curr) + { + ConnRef *connRef = *curr; - if (intersectResult == Avoid::DO_INTERSECT) - { - result += router->crossing_penalty; - } + if (connRef->id() == lineRef->id()) + { + continue; } + const Avoid::PolyLine& route2 = connRef->route(); + + bool isConn = true; + Polygon dynamic_route2(route2); + Polygon dynamic_conn_route(connRoute); + CrossingsInfoPair crossings = countRealCrossings( + dynamic_route2, isConn, dynamic_conn_route, + connRoute.size() - 1, true); + result += (crossings.first * crossing_penalty); } } - + return result; } -// Returns the best path from src to tar using the cost function. -// -// The path is worked out via Dijkstra's algorithm, and is encoded via -// pathNext links in each of the VerInfs along the path. -// -// Based on the code of 'matrixpfs'. -// -static void dijkstraPath(ConnRef *lineRef, VertInf *src, VertInf *tar) +static double estimatedCost(ConnRef *lineRef, const Point *last, + const Point& a, const Point& b) { - Router *router = src->_router; - - double unseen = (double) __INT_MAX__; - - // initialize arrays - VertInf *finish = router->vertices.end(); - for (VertInf *t = router->vertices.connsBegin(); t != finish; t = t->lstNext) + if (lineRef->routingType() == ConnType_PolyLine) { - t->pathNext = NULL; - t->pathDist = -unseen; + return euclideanDist(a, b); } - - VertInf *min = src; - while (min != tar) + else // Orthogonal { - VertInf *k = min; - min = NULL; - - k->pathDist *= -1; - if (k->pathDist == unseen) + // XXX: This currently just takes into account the compulsory + // bend but will have to be updated when port direction + // information is available. + int num_penalties = 0; + double xmove = b.x - a.x; + double ymove = b.y - a.y; + if (!last) { - k->pathDist = 0; - } - - EdgeInfList& visList = k->visList; - EdgeInfList::iterator finish = visList.end(); - for (EdgeInfList::iterator edge = visList.begin(); edge != finish; - ++edge) - { - VertInf *t = (*edge)->otherVert(k); - VertID tID = t->id; - - // Only check shape verticies, or endpoints. - if ((t->pathDist < 0) && - ((tID.objID == src->id.objID) || tID.isShape)) + // Just two points. + if ((xmove != 0) && (ymove != 0)) { - double kt_dist = (*edge)->getDist(); - double priority = k->pathDist + - cost(lineRef, kt_dist, k->pathNext, k, t); - - if ((kt_dist != 0) && (t->pathDist < -priority)) - { - t->pathDist = -priority; - t->pathNext = k; - } - if ((min == NULL) || (t->pathDist > min->pathDist)) - { - min = t; - } + num_penalties += 1; } } - EdgeInfList& invisList = k->invisList; - finish = invisList.end(); - for (EdgeInfList::iterator edge = invisList.begin(); edge != finish; - ++edge) + else { - VertInf *t = (*edge)->otherVert(k); - VertID tID = t->id; - - // Only check shape verticies, or endpoints. - if ((t->pathDist < 0) && - ((tID.objID == src->id.objID) || tID.isShape > 0)) + // We have three points, so we know the direction of the + // previous segment. + double rad = M_PI - angleBetween(*last, a, b); + if (rad > (M_PI / 2)) { - if ((min == NULL) || (t->pathDist > min->pathDist)) - { - min = t; - } + // Target point is back in the direction of the first point, + // so at least two bends are required. + num_penalties += 2; + } + else if (rad > 0) + { + // To the side, so at least one bend. + num_penalties += 1; } } + double penalty = num_penalties * + lineRef->router()->routingPenalty(segmentPenalty); + + return manhattanDist(a, b) + penalty; } } -class ANode +class CmpVisEdgeRotation { public: - VertInf* inf; - double g; // Gone - double h; // Heuristic - double f; // Formula f = g + h - VertInf *pp; - - ANode(VertInf *vinf) - : inf(vinf) - , g(0) - , h(0) - , f(0) - , pp(NULL) + CmpVisEdgeRotation(const VertInf* lastPt) + : _lastPt(lastPt) { } - ANode() - : inf(NULL) - , g(0) - , h(0) - , f(0) - , pp(NULL) + bool operator() (const EdgeInf* u, const EdgeInf* v) const { + return u->rotationLessThan(_lastPt, v); } + private: + const VertInf *_lastPt; }; -bool operator<(const ANode &a, const ANode &b) -{ - return a.f < b.f; -} - - -bool operator>(const ANode &a, const ANode &b) -{ - return a.f > b.f; -} - // Returns the best path from src to tar using the cost function. // // The path is worked out using the aStar algorithm, and is encoded via -// pathNext links in each of the VerInfs along the path. +// prevIndex values for each ANode which point back to the previous ANode's +// position in the DONE vector. At completion, this order is written into +// the pathNext links in each of the VerInfs along the path. // // The aStar STL code is based on public domain code available on the // internet. // -static void aStarPath(ConnRef *lineRef, VertInf *src, VertInf *tar) +static void aStarPath(ConnRef *lineRef, VertInf *src, VertInf *tar, + VertInf *start) { + bool isOrthogonal = (lineRef->routingType() == ConnType_Orthogonal); + + double (*dist)(const Point& a, const Point& b) = + (isOrthogonal) ? manhattanDist : euclideanDist; + std::vector PENDING; // STL Vectors chosen because of rapid std::vector DONE; // insertions/deletions at back, ANode Node, BestNode; // Temporary Node and BestNode bool bNodeFound = false; // Flag if node is found in container + int timestamp = 1; - tar->pathNext = NULL; + if (start == NULL) + { + start = src; + } + + Router *router = lineRef->router(); + if (router->RubberBandRouting && (start != src)) + { + COLA_ASSERT(router->IgnoreRegions == true); + + const PolyLine& currRoute = lineRef->route(); + VertInf *last = NULL; + int rIndx = 0; + while (last != start) + { + const Point& pnt = currRoute.at(rIndx); + bool isShape = (rIndx > 0); + VertID vID(pnt.id, isShape, pnt.vn); + +#ifdef PATHDEBUG + db_printf("/// %d %d %d\n", pnt.id, (int) isShape, pnt.vn); +#endif + VertInf *curr = router->vertices.getVertexByID(vID); + COLA_ASSERT(curr != NULL); + + Node = ANode(curr, timestamp++); + if (!last) + { + Node.g = 0; + Node.h = estimatedCost(lineRef, NULL, Node.inf->point, + tar->point); + Node.f = Node.g + Node.h; + } + else + { + double edgeDist = dist(BestNode.inf->point, curr->point); + + Node.g = BestNode.g + cost(lineRef, edgeDist, BestNode.inf, + Node.inf, DONE, BestNode.prevIndex); + + // Calculate the Heuristic. + Node.h = estimatedCost(lineRef, &(BestNode.inf->point), + Node.inf->point, tar->point); + + // The A* formula + Node.f = Node.g + Node.h; + + // Point parent to last BestNode (pushed onto DONE) + Node.prevIndex = DONE.size() - 1; + } + + if (curr != start) + { + BestNode = Node; + + DONE.push_back(BestNode); + } + else + { + PENDING.push_back(Node); + } - // Create the start node - Node = ANode(src); - Node.g = 0; - Node.h = dist(Node.inf->point, tar->point); - Node.f = Node.g + Node.h; - // Set a null parent, so cost function knows this is the first segment. - Node.pp = NULL; + rIndx++; + last = curr; + } + } + else + { + // Create the start node + Node = ANode(src, timestamp++); + Node.g = 0; + Node.h = estimatedCost(lineRef, NULL, Node.inf->point, tar->point); + Node.f = Node.g + Node.h; + // Set a null parent, so cost function knows this is the first segment. + + // Populate the PENDING container with the first location + PENDING.push_back(Node); + } + + tar->pathNext = NULL; - // Populate the PENDING container with the first location - PENDING.push_back(Node); // Create a heap from PENDING for sorting using std::make_heap; using std::push_heap; using std::pop_heap; make_heap( PENDING.begin(), PENDING.end() ); while (!PENDING.empty()) { - // Ascending sort based on overloaded operators below - sort_heap(PENDING.begin(), PENDING.end()); - - // Set the Node with lowest f value to BESTNODE + // Set the Node with lowest f value to BESTNODE. + // Since the ANode operator< is reversed, the head of the + // heap is the node with the lowest f value. BestNode = PENDING.front(); // Pop off the heap. Actually this moves the @@ -427,38 +505,118 @@ static void aStarPath(ConnRef *lineRef, VertInf *src, VertInf *tar) // is not actually removed since the pop is to // the heap and not the container. pop_heap(PENDING.begin(), PENDING.end()); - - // Remove node from right (the value we pop_heap'd) PENDING.pop_back(); // Push the BestNode onto DONE - BestNode.inf->pathNext = BestNode.pp; DONE.push_back(BestNode); + VertInf *prevInf = (BestNode.prevIndex >= 0) ? + DONE[BestNode.prevIndex].inf : NULL; #if 0 - printf("Considering... "); - BestNode.ID->print(stdout); - printf(" - g: %3.1f h: %3.1f f: %3.1f back: ", BestNode.g, BestNode.h, - BestNode.f); - BestNode.pp.print(stdout); - printf("\n"); + db_printf("Considering... "); + db_printf(" %g %g ", BestNode.inf->point.x, BestNode.inf->point.y); + BestNode.inf->id.db_print(); + db_printf(" - g: %3.1f h: %3.1f back: ", BestNode.g, BestNode.h); + if (prevInf) + { + db_printf(" %g %g", prevInf->point.x, prevInf->point.y); + //prevInf->id.db_print(); + } + db_printf("\n"); +#endif + +#if defined(ASTAR_DEBUG) + if (router->avoid_screen) + { + int canx = 151; + int cany = 55; + int radius = 5; + ANode curr; + for (curr = BestNode; curr.prevIndex >= 0; + curr = DONE[curr.prevIndex]) + { + filledCircleRGBA(router->avoid_screen, + (int) curr.inf->point.x + canx, + (int) curr.inf->point.y + cany, + radius, 0, 0, 255, 128); + } + filledCircleRGBA(router->avoid_screen, + (int) BestNode.inf->point.x + canx, + (int) BestNode.inf->point.y + cany, + radius, 255, 0, 0, 255); + + SDL_Flip(router->avoid_screen); + //SDL_Delay(500); + + filledCircleRGBA(router->avoid_screen, + (int) BestNode.inf->point.x + canx, + (int) BestNode.inf->point.y + cany, + radius, 255, 255, 255, 255); + filledCircleRGBA(router->avoid_screen, + (int) BestNode.inf->point.x + canx, + (int) BestNode.inf->point.y + cany, + radius, 0, 255, 0, 128); + for (curr = BestNode; curr.prevIndex >= 0; + curr = DONE[curr.prevIndex]) + { + filledCircleRGBA(router->avoid_screen, + (int) curr.inf->point.x + canx, + (int) curr.inf->point.y + cany, + radius, 255, 255, 255, 255); + filledCircleRGBA(router->avoid_screen, + (int) curr.inf->point.x + canx, + (int) curr.inf->point.y + cany, + radius, 0, 255, 0, 128); + } + } #endif // If at destination, break and create path below if (BestNode.inf == tar) { +#ifdef PATHDEBUG + db_printf("Cost: %g\n", BestNode.f); +#endif //bPathFound = true; // arrived at destination... + + // Correct all the pathNext pointers. + ANode curr; + int currIndex = DONE.size() - 1; + for (curr = BestNode; curr.prevIndex > 0; + curr = DONE[curr.prevIndex]) + { + COLA_ASSERT(curr.prevIndex < currIndex); + curr.inf->pathNext = DONE[curr.prevIndex].inf; + currIndex = curr.prevIndex; + } + // Check that we've gone through the complete path. + COLA_ASSERT(curr.prevIndex == 0); + // Fill in the final pathNext pointer. + curr.inf->pathNext = DONE[curr.prevIndex].inf; + break; } // Check adjacent points in graph - EdgeInfList& visList = BestNode.inf->visList; - EdgeInfList::iterator finish = visList.end(); - for (EdgeInfList::iterator edge = visList.begin(); edge != finish; - ++edge) + EdgeInfList& visList = (!isOrthogonal) ? + BestNode.inf->visList : BestNode.inf->orthogVisList; + if (isOrthogonal) + { + // We would like to explore in a structured way, + // so sort the points in the visList... + CmpVisEdgeRotation compare(prevInf); + visList.sort(compare); + } + EdgeInfList::const_iterator finish = visList.end(); + for (EdgeInfList::const_iterator edge = visList.begin(); + edge != finish; ++edge) { - Node.inf = (*edge)->otherVert(BestNode.inf); + Node = ANode((*edge)->otherVert(BestNode.inf), timestamp++); + + // Set the index to the previous ANode that we reached + // this ANode through (the last BestNode pushed onto DONE). + Node.prevIndex = DONE.size() - 1; // Only check shape verticies, or the tar endpoint. if (!(Node.inf->id.isShape) && (Node.inf != tar)) @@ -466,6 +624,15 @@ static void aStarPath(ConnRef *lineRef, VertInf *src, VertInf *tar) continue; } + VertInf *prevInf = (BestNode.prevIndex >= 0) ? + DONE[BestNode.prevIndex].inf : NULL; + + // Don't bother looking at the segment we just arrived along. + if (prevInf && (prevInf == Node.inf)) + { + continue; + } + double edgeDist = (*edge)->getDist(); if (edgeDist == 0) @@ -473,31 +640,49 @@ static void aStarPath(ConnRef *lineRef, VertInf *src, VertInf *tar) continue; } - VertInf *prevInf = BestNode.inf->pathNext; + if (!router->_orthogonalRouting && + (!router->RubberBandRouting || (start == src)) && + (validateBendPoint(prevInf, BestNode.inf, Node.inf) == false)) + { + // The bendpoint is not valid, i.e., is a zigzag corner, so... + continue; + // For RubberBand routing we want to allow these routes and + // unwind them later, otherwise instead or unwinding, paths + // can go the *really* long way round. + } - Node.g = BestNode.g + cost(lineRef, edgeDist, prevInf, - BestNode.inf, Node.inf); + Node.g = BestNode.g + cost(lineRef, edgeDist, BestNode.inf, + Node.inf, DONE, BestNode.prevIndex); // Calculate the Heuristic. - Node.h = dist(Node.inf->point, tar->point); + Node.h = estimatedCost(lineRef, &(BestNode.inf->point), + Node.inf->point, tar->point); // The A* formula Node.f = Node.g + Node.h; - // Point parent to last BestNode (pushed onto DONE) - Node.pp = BestNode.inf; + +#if 0 + db_printf("-- Adding: %g %g ", Node.inf->point.x, + Node.inf->point.y); + Node.inf->id.db_print(); + db_printf(" - g: %3.1f h: %3.1f \n", Node.g, Node.h); +#endif bNodeFound = false; // Check to see if already on PENDING for (unsigned int i = 0; i < PENDING.size(); i++) { - if (Node.inf == PENDING.at(i).inf) - { // If already on PENDING - if (Node.g < PENDING.at(i).g) + ANode& ati = PENDING.at(i); + if ((Node.inf == ati.inf) && + (DONE[Node.prevIndex].inf == DONE[ati.prevIndex].inf)) + { + // If already on PENDING + if (Node.g < ati.g) { - PENDING.at(i).g = Node.g; - PENDING.at(i).f = Node.g + PENDING.at(i).h; - PENDING.at(i).pp = Node.pp; + PENDING[i] = Node; + + make_heap( PENDING.begin(), PENDING.end() ); } bNodeFound = true; break; @@ -508,15 +693,14 @@ static void aStarPath(ConnRef *lineRef, VertInf *src, VertInf *tar) // Check to see if already on DONE for (unsigned int i = 0; i < DONE.size(); i++) { - if (Node.inf == DONE.at(i).inf) + ANode& ati = DONE.at(i); + if ((Node.inf == ati.inf) && + (DONE[Node.prevIndex].inf == DONE[ati.prevIndex].inf)) { // If on DONE, Which has lower gone? - if (Node.g < DONE.at(i).g) + if (Node.g < ati.g) { - DONE.at(i).g = Node.g; - DONE.at(i).f = Node.g + DONE.at(i).h; - DONE.at(i).pp = Node.pp; - DONE.at(i).inf->pathNext = Node.pp; + DONE[i] = Node; } bNodeFound = true; break; @@ -530,26 +714,24 @@ static void aStarPath(ConnRef *lineRef, VertInf *src, VertInf *tar) PENDING.push_back(Node); // Push NewNode onto heap push_heap( PENDING.begin(), PENDING.end() ); - // Re-Assert heap, or will be short by one - make_heap( PENDING.begin(), PENDING.end() ); #if 0 + using std::cout; using std::endl; // Display PENDING and DONE containers (For Debugging) cout << "PENDING: "; - for (int i = 0; i < PENDING.size(); i++) + for (unsigned int i = 0; i < PENDING.size(); i++) { - cout << PENDING.at(i).x << "," << PENDING.at(i).y << ","; - cout << PENDING.at(i).g << "," << PENDING.at(i).h << " "; + cout << PENDING.at(i).g << "," << PENDING.at(i).h << ","; + cout << PENDING.at(i).inf << "," << PENDING.at(i).pp << " "; } cout << endl; cout << "DONE: "; - for (int i = 0; i < DONE.size(); i++) + for (unsigned int i = 0; i < DONE.size(); i++) { - cout << DONE.at(i).x << "," << DONE.at(i).y << ","; - cout << DONE.at(i).g << "," << DONE.at(i).h << " "; + cout << DONE.at(i).g << "," << DONE.at(i).h << ","; + cout << DONE.at(i).inf << "," << DONE.at(i).pp << " "; } cout << endl << endl; - int ch = _getch(); #endif } } @@ -559,75 +741,52 @@ static void aStarPath(ConnRef *lineRef, VertInf *src, VertInf *tar) // Returns the best path for the connector referred to by lineRef. // -// The path encoded in the pathNext links in each of the VerInfs +// The path encoded in the pathNext links in each of the VertInfs // backwards along the path, from the tar back to the source. // void makePath(ConnRef *lineRef, bool *flag) { + bool isOrthogonal = (lineRef->routingType() == ConnType_Orthogonal); Router *router = lineRef->router(); VertInf *src = lineRef->src(); VertInf *tar = lineRef->dst(); + VertInf *start = lineRef->start(); - // If the connector hates crossings then we want to examine direct paths: - bool examineDirectPath = lineRef->doesHateCrossings(); - // TODO: Could be more efficient here. - EdgeInf *directEdge = EdgeInf::existingEdge(src, tar); - if (!(router->IncludeEndpoints) && directVis(src, tar)) - { - Point p = src->point; - Point q = tar->point; - - assert(directEdge == NULL); - - directEdge = new EdgeInf(src, tar); - tar->pathNext = src; - directEdge->setDist(dist(p, q)); - directEdge->addConn(flag); - - return; - } - else if (router->IncludeEndpoints && directEdge && - (directEdge->getDist() > 0) && !examineDirectPath) + if (isOrthogonal) { - tar->pathNext = src; - directEdge->addConn(flag); + aStarPath(lineRef, src, tar, start); } - else + else // if (!isOrthogonal) { - // Mark the path endpoints as not being able to see - // each other. This is true if we are here. - if (!(router->IncludeEndpoints) && router->InvisibilityGrph) - { - if (!directEdge) - { - directEdge = new EdgeInf(src, tar); - } - directEdge->addBlocker(0); - } - - if (router->UseAStarSearch) + EdgeInf *directEdge = EdgeInf::existingEdge(src, tar); + // If the connector hates crossings or there are clusters present, + // then we want to examine direct paths: + bool examineDirectPath = lineRef->doesHateCrossings() || + !(router->clusterRefs.empty()); + + if ((start == src) && directEdge && (directEdge->getDist() > 0) && + !examineDirectPath) { - aStarPath(lineRef, src, tar); + tar->pathNext = src; + directEdge->addConn(flag); } else { - dijkstraPath(lineRef, src, tar); + aStarPath(lineRef, src, tar, start); } + } #if 0 - PointMap::iterator t; - for (VertInf *t = vertices.connsBegin(); t != vertices.end(); - t = t->lstNext) - { - - t->id.print(); - printf(" -> "); - t->pathNext->id.print(); - printf("\n"); - } -#endif + for (VertInf *t = vertices.connsBegin(); t != vertices.end(); + t = t->lstNext) + { + t->id.db_print(); + db_printf(" -> "); + t->pathNext->id.db_print(); + db_printf("\n"); } +#endif } diff --git a/src/libavoid/makepath.h b/src/libavoid/makepath.h index 4d68a01e3..b40bfbc3d 100644 --- a/src/libavoid/makepath.h +++ b/src/libavoid/makepath.h @@ -2,24 +2,27 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2008 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * Author(s): Michael Wybrow */ + #ifndef AVOID_MAKEPATH_H #define AVOID_MAKEPATH_H diff --git a/src/libavoid/polyutil.cpp b/src/libavoid/polyutil.cpp deleted file mode 100644 index 1b4b0c619..000000000 --- a/src/libavoid/polyutil.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * vim: ts=4 sw=4 et tw=0 wm=0 - * - * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -*/ - -#include -#include - -#include "libavoid/polyutil.h" -#include "libavoid/geomtypes.h" -#include "libavoid/vertices.h" -#include "libavoid/shape.h" - -namespace Avoid { - - -Polygn newPoly(int size) -{ - Polygn newpoly; - - newpoly.id = 0; - newpoly.pn = size; - newpoly.ps = (Point *) calloc(size, sizeof(Point)); - if (!newpoly.ps) - { - fprintf(stderr, - "Error: Unable to allocate Point array in Avoid::newPoly\n"); - abort(); - } - return newpoly; -} - - -Polygn copyPoly(Polygn poly) -{ - Polygn newpoly = newPoly(poly.pn); - - newpoly.id = poly.id; - for (int i = 0; i < poly.pn; i++) - { - newpoly.ps[i] = poly.ps[i]; - } - return newpoly; -} - - -Polygn copyPoly(ShapeRef *shape) -{ - Polygn poly = shape->poly(); - Polygn newpoly = newPoly(poly.pn); - - newpoly.id = poly.id; - for (int i = 0; i < poly.pn; i++) - { - newpoly.ps[i] = poly.ps[i]; - } - return newpoly; -} - - -void freePoly(Polygn& poly) -{ - std::free(poly.ps); -} - - -void freePtrPoly(Polygn *poly) -{ - std::free(poly->ps); - std::free(poly); -} - - -} - diff --git a/src/libavoid/polyutil.h b/src/libavoid/polyutil.h deleted file mode 100644 index 9340df5f4..000000000 --- a/src/libavoid/polyutil.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * vim: ts=4 sw=4 et tw=0 wm=0 - * - * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -*/ - -#include "libavoid/geometry.h" -#include "libavoid/shape.h" - -#ifndef AVOID_POLYUTIL_H -#define AVOID_POLYUTIL_H - -namespace Avoid { - - -extern Polygn newPoly(int size); -extern Polygn copyPoly(Polygn); -extern Polygn copyPoly(ShapeRef *shape); -extern void freePoly(Polygn&); -extern void freePtrPoly(Polygn *argpoly); - - -} - -#endif diff --git a/src/libavoid/region.cpp b/src/libavoid/region.cpp deleted file mode 100644 index 5a46d7cbb..000000000 --- a/src/libavoid/region.cpp +++ /dev/null @@ -1,858 +0,0 @@ -/* - * vim: ts=4 sw=4 et tw=0 wm=0 - * - * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -*/ - -#include -#include -#include -#include -#include - -#include "libavoid/shape.h" -#include "libavoid/region.h" - - -namespace Avoid { - -Region *centerRegion = NULL; - -static const BBox screenBBox = - {Point(-INFINITY, -INFINITY), Point(INFINITY, INFINITY)}; - - -Region::Region() - : _bbox(screenBBox) - , _left(NULL), _right(NULL), _up(NULL), _down(NULL) -{ - _blocks.clear(); -} - - -Region::Region(double x1, double y1, double x2, double y2) - : _left(NULL), _right(NULL), _up(NULL), _down(NULL) -{ - _bbox.a.x = x1; - _bbox.a.y = y1; - _bbox.b.x = x2; - _bbox.b.y = y2; - - _blocks.clear(); -} - - -static const unsigned int R_INSIDE = 0; -static const unsigned int R_LEFT = 1; -static const unsigned int R_LEDGE = 2; -static const unsigned int R_RIGHT = 4; -static const unsigned int R_REDGE = 8; -static const unsigned int R_ABOVE = 16; -static const unsigned int R_TEDGE = 32; -static const unsigned int R_BELOW = 64; -static const unsigned int R_BEDGE = 128; - -static const unsigned int R_NONE = R_INSIDE; -static const unsigned int R_UP = R_ABOVE; -static const unsigned int R_DOWN = R_BELOW; -static const unsigned int R_HORI = R_LEFT | R_RIGHT; -static const unsigned int R_VERT = R_UP | R_DOWN; - - -static void printBBox(const char *label, const BBox &bbox) -{ - if (label) - { - printf("%s: ", label); - } - printf("(%.2f, %.2f)-(%.2f, %.2f)\n", bbox.a.x, bbox.a.y, - bbox.b.x, bbox.b.y); -} - - -bool Region::overlapCheck(BBox& bbox, unsigned int& p) -{ - p = R_INSIDE; - Point& a = bbox.a; - Point& b = bbox.b; - Point& r = _bbox.a; - Point& s = _bbox.b; - - if (s.x <= a.x) - { - // Wholly right. - p = R_RIGHT; - return false; - } - else if (r.x >= b.x) - { - // Wholly left. - p = R_LEFT; - return false; - } - - if (s.y <= a.y) - { - // Wholly below. - p = R_BELOW; - return false; - } - else if (r.y >= b.y) - { - // Wholly above. - p = R_ABOVE; - return false; - } - - if (a.y == r.y) - { - // Shared top edge. - p |= R_TEDGE; - } - else if (a.y < r.y) - { - // Need to split above. - p |= R_ABOVE; - } - - if (b.y == s.y) - { - // Shared bottom edge. - p |= R_BEDGE; - } - else if (b.y > s.y) - { - // Need to split below. - p |= R_BELOW; - } - - if (a.x == r.x) - { - // Shared left edge. - p |= R_LEDGE; - } - else if (a.x < r.x) - { - // Need to split left. - p |= R_LEFT; - } - - if (b.x == s.x) - { - // Shared right edge. - p |= R_REDGE; - } - else if (b.x > s.x) - { - // Need to split right. - p |= R_RIGHT; - } - - return true; -} - - -void Region::getBBox(BBox& bb) -{ - bb.a = _bbox.a; - bb.b = _bbox.b; -} - - -Region *Region::up(void) -{ - return _up; -} - - -Region *Region::down(void) -{ - return _down; -} - - -Region *Region::left(void) -{ - return _left; -} - - -Region *Region::right(void) -{ - return _right; -} - - -bool Region::isBlock(void) -{ - return !(_blocks.empty()); -} - - -void Region::initialSplit(BBox& bbox, unsigned int pos, unsigned int& shapeId) -{ - Point& n1 = bbox.a; - Point& n2 = bbox.b; - Region *newR = this; - Region *left, *right, *top, *bottom; - - if (pos == R_INSIDE) - { - split(n2.y, R_HORI); - split(n2.x, R_VERT); - newR = split(n1.y, R_HORI); - newR = newR->split(n1.x, R_VERT); - - printf("%p - list %d add %d\n", newR, - (int) newR->_blocks.size(), shapeId); - newR->_blocks.push_back((int) shapeId); - newR->_blocks.sort(); - newR->_blocks.unique(); - } - else - { - Region *tar = NULL; - tar = newR->findRegion(n1.x, R_VERT); - if (pos & R_LEFT) - { - if (n1.x == tar->_bbox.a.x) - { - newR = tar->_right; - } - else if (n1.x == tar->_bbox.b.x) - { - newR = tar; - } - else - { - newR = tar->split(n1.x, R_VERT); - } - } - else if (!(pos & R_LEDGE)) - { - newR = tar->split(n1.x, R_VERT); - } - left = newR; - - tar = left->findRegion(n1.y, R_HORI); - if (pos & R_ABOVE) - { - if (n1.y == tar->_bbox.a.y) - { - newR = tar->_down; - } - else if (n1.y == tar->_bbox.b.y) - { - newR = tar; - } - else - { - newR = tar->split(n1.y, R_HORI); - } - } - else if (!(pos & R_TEDGE)) - { - newR = tar->split(n1.y, R_HORI); - } - top = newR; - - right = newR; - tar = newR->findRegion(n2.x, R_VERT); - if (pos & R_RIGHT) - { - - if (n2.x == tar->_bbox.a.x) - { - right = tar->_left; - } - else if (n2.x == tar->_bbox.b.x) - { - right = tar; - } - else - { - tar->split(n2.x, R_VERT); - right = tar; - } - } - else if (!(pos & R_REDGE)) - { - tar->split(n2.x, R_VERT); - right = tar; - } - - bottom = right; - tar = right->findRegion(n2.y, R_HORI); - if (pos & R_BELOW) - { - if (n2.y == tar->_bbox.a.y) - { - bottom = tar->_up; - } - else if (n2.y == tar->_bbox.b.y) - { - bottom = tar; - } - else - { - tar->split(n2.y, R_HORI); - bottom = tar; - } - } - else if (!(pos & R_BEDGE)) - { - tar->split(n2.y, R_HORI); - bottom = tar; - } - - // top is actually top-left, and bottom is bottom-right. - Region *curr = top, *cptr = NULL; - while (curr->_bbox.b.y <= bottom->_bbox.b.y) - { - cptr = curr; - while (cptr->_bbox.b.x <= bottom->_bbox.b.x) - { - printf("%p - list %d add %d\n", cptr, - (int) cptr->_blocks.size(), shapeId); - cptr->_blocks.push_back((int) shapeId); - cptr->_blocks.sort(); - cptr->_blocks.unique(); - - cptr = cptr->_right; - } - - curr = curr->_down; - } - } -} - - -// Returns the region containing the value 'pos' in the direction 'dir'. -// Thus, if looking for the x value 55, you would pass R_VERT as 'dir'. -// 'forMerge' specifies that the left or top block of a pair of regions -// with the split value of 'pos' should be returned. -Region *Region::findRegion(double pos, unsigned int dir, const bool forMerge) -{ - Region *curr = this; - - if (dir & R_VERT) - { - while (pos > curr->_bbox.b.x) - { - curr = curr->_right; - } - while (pos < curr->_bbox.a.x) - { - curr = curr->_left; - } - if (forMerge) - { - if (pos == curr->_bbox.a.x) - { - curr = curr->_left; - } - if (pos != curr->_bbox.b.x) - { - // 'pos' is not on the boundary. - return NULL; - } - } - } - else if (dir & R_HORI) - { - while (pos > curr->_bbox.b.y) - { - curr = curr->_down; - } - while (pos < curr->_bbox.a.y) - { - curr = curr->_up; - } - if (forMerge) - { - if (pos == curr->_bbox.a.y) - { - curr = curr->_up; - } - if (pos != curr->_bbox.b.y) - { - // 'pos' is not on the boundary. - return NULL; - } - } - } - return curr; -} - - -Region *Region::split(double pos, unsigned int dir) -{ - Region *newR = NULL; - bool first = true; - - if (dir & R_VERT) - { - newR = splitDir(pos, R_UP, first); - if (_down) _down->splitDir(pos, R_DOWN); - } - else if (dir & R_HORI) - { - newR = splitDir(pos, R_RIGHT, first); - if (_left) _left->splitDir(pos, R_LEFT); - } - return newR; -} - - -void Region::merge(unsigned int dir) -{ - bool first = true; - - if (dir & R_VERT) - { - mergeDir(R_UP, first); - if (_down) _down->mergeDir(R_DOWN); - } - else if (dir & R_HORI) - { - mergeDir(R_RIGHT, first); - if (_left) _left->mergeDir(R_LEFT); - } -} - - -void Region::mergeRegion(Region *src) -{ - assert(src != NULL); - - if (src == _left) - { - pairHor(src->_left, this); - _bbox.a.x = src->_bbox.a.x; - } - else if (src == _right) - { - pairHor(this, src->_right); - _bbox.b.x = src->_bbox.b.x; - } - else if (src == _up) - { - pairVer(src->_up, this); - _bbox.a.y = src->_bbox.a.y; - } - else if (src == _down) - { - pairVer(this, src->_down); - _bbox.b.y = src->_bbox.b.y; - } - else - { - fprintf(stderr, "Error: Avoid::Region::merge(): " - "Argument not adjoining region.\n"); - abort(); - } - mergeAttributes(src); - printf("DEL %p\n", src); - delete src; -} - - -Region *Region::splitDir(double pos, unsigned int dir, bool first) -{ - Point& o1 = _bbox.a; - Point& o2 = _bbox.b; - - Region *newR = NULL; - - if (dir & R_VERT) - { - assert(pos > _bbox.a.x); - assert(pos < _bbox.b.x); - - // Vertical recursion: - - // Create new block. - Region *r = new Region(pos, o1.y, o2.x, o2.y); - printf("NEW %p\n", r); - r->copyAttributes(this); - - Region *o_up = _up; - Region *o_down = _down; - - // Resize old block. - o2.x = pos; - - pairHor(r, _right); - pairHor(this, r); - - if (dir & R_UP) - { - if (!first) pairVer(r, _down->_right); - - if (o_up) o_up->splitDir(pos, R_UP); - } - else if (dir & R_DOWN) - { - if (!first) pairVer(_up->_right, r); - - if (o_down) o_down->splitDir(pos, R_DOWN); - } - newR = r; - } - else if (dir & R_HORI) - { - // Vertical recursion: - - // Create new block. - Region *b = new Region(o1.x, pos, o2.x, o2.y); - printf("NEW %p\n", b); - b->copyAttributes(this); - - Region *o_left = _left; - Region *o_right = _right; - - // Resize old block. - o2.y = pos; - - pairVer(b, _down); - pairVer(this, b); - - if (dir & R_LEFT) - { - if (!first) pairHor(b, _right->_down); - - if (o_left) o_left->splitDir(pos, R_LEFT); - } - else if (dir & R_RIGHT) - { - if (!first) pairHor(_left->_down, b); - - if (o_right) o_right->splitDir(pos, R_RIGHT); - } - newR = b; - } - return newR; -} - - -void Region::mergeDir(unsigned int dir, bool first) -{ - if (dir & R_VERT) - { - assert(_right != NULL); - - mergeRegion(_right); - - if (dir & R_UP) - { - if (_up) _up->mergeDir(dir, R_UP); - } - else if (dir & R_DOWN) - { - if (_down) _down->mergeDir(dir, R_DOWN); - } - } - else if (dir & R_HORI) - { - assert(_down != NULL); - - mergeRegion(_down); - - if (dir & R_LEFT) - { - if (_left) _left->mergeDir(dir, R_LEFT); - } - else if (dir & R_RIGHT) - { - if (_right) _right->mergeDir(dir, R_RIGHT); - } - } -} - - -void Region::copyAttributes(Region *src) -{ - _blocks = src->_blocks; -} - - -void Region::mergeAttributes(Region *src) -{ - _blocks.merge(src->_blocks); - _blocks.sort(); - _blocks.unique(); -} - - -//--------------------------- -// Static member functions: - - -void Region::pairHor(Region *l, Region *r) -{ - if (l) l->_right = r; - if (r) r->_left = l; -} - - -void Region::pairVer(Region *a, Region *b) -{ - if (a) a->_down = b; - if (b) b->_up = a; -} - - -void Region::addShape(ShapeRef *shape) -{ - if (!centerRegion) - { - // Add new default region. - centerRegion = new Region(); - printf("NEW %p\n", centerRegion); - } - BBox bbox; - // Get bounding box for added shape. - shape->boundingBox(bbox); - printBBox("Add", bbox); - - unsigned int shapeId = shape->id(); - - // Find starting point for overlap - Region *curr = centerRegion; - unsigned int pos = R_INSIDE; - while (!(curr->overlapCheck(bbox, pos))) - { - if (pos & R_LEFT) - { - curr = curr->_left; - } - else if (pos & R_RIGHT) - { - curr = curr->_right; - } - else if (pos & R_ABOVE) - { - curr = curr->_up; - } - else if (pos & R_BELOW) - { - curr = curr->_down; - } - } - - curr->initialSplit(bbox, pos, shapeId); - centerRegion = curr; -} - - -void Region::removeShape(ShapeRef *shape) -{ - const bool forMerge = true; - - BBox bbox; - // Get bounding box for added shape. - shape->boundingBox(bbox); - printBBox("Remove", bbox); - - unsigned int shapeId = shape->id(); - - Region *aboveTop = centerRegion->findRegion(bbox.a.y, R_HORI, forMerge); - Region *aboveBottom = aboveTop->findRegion(bbox.b.y, R_HORI, forMerge); - Region *leftOfLeft = aboveBottom->findRegion(bbox.a.x, R_VERT, forMerge); - Region *leftOfRight = leftOfLeft->findRegion(bbox.b.x, R_VERT, forMerge); - - assert(aboveTop != NULL); - assert(aboveBottom != NULL); - assert(leftOfLeft != NULL); - assert(leftOfRight != NULL); - - // Find Top Left. - Region *topLeft = leftOfLeft->_right; - while (topLeft->_bbox.a.y != aboveTop->_bbox.b.y) - { - topLeft = topLeft->_up; - } - - // Find Bottom Right. - Region *botRight = leftOfRight; - while (botRight->_bbox.b.y != aboveBottom->_bbox.b.y) - { - botRight = botRight->_down; - } - - // Clear blocking flag. - Region *curr = topLeft, *cptr = NULL; - while (curr->_bbox.b.y <= botRight->_bbox.b.y) - { - cptr = curr; - while (cptr->_bbox.b.x <= botRight->_bbox.b.x) - { - ShapeList& blocks = cptr->_blocks; - - assert(std::find(blocks.begin(), blocks.end(), (int) shapeId) != - blocks.end()); - printf("%p - list %d remove %d\n", cptr, - (int) blocks.size(), shapeId); - cptr->_blocks.remove((int) shapeId); - - cptr = cptr->_right; - } - - curr = curr->_down; - } - - // These two are safe since they don't invalidate the pointers - // to the regions that are inside the shape. - if (aboveBottom->safeToMerge(R_HORI)) - { - aboveBottom->merge(R_HORI); - } - if (leftOfRight->safeToMerge(R_VERT)) - { - leftOfRight->merge(R_VERT); - } - - // Be a bit more careful with these. - double leftX = leftOfLeft->_bbox.b.x; - if (aboveTop->safeToMerge(R_HORI)) - { - aboveTop->merge(R_HORI); - } - // leftOfLeft may have been freed, so look for the new block at - // that position. - leftOfLeft = aboveTop->findRegion(leftX, R_VERT, forMerge); - assert(leftOfLeft); - if (leftOfLeft->safeToMerge(R_VERT)) - { - leftOfLeft->merge(R_VERT); - } -} - - -bool Region::safeToMerge(unsigned int dir) -{ - bool unsafe = false; - - if (dir == R_HORI) - { - printf("safeToMerge? y = %.3f... ", _bbox.b.y); - unsafe |= unsafeToMerge(R_RIGHT); - if (_left) unsafe |= _left->unsafeToMerge(R_LEFT); - } - else if (dir == R_VERT) - { - printf("safeToMerge? x = %.3f... ", _bbox.b.x); - unsafe |= unsafeToMerge(R_DOWN); - if (_up) unsafe |= _up->unsafeToMerge(R_UP); - } - printf("%s.\n", (unsafe) ? "no" : "yes"); - - return !unsafe; -} - - -bool Region::unsafeToMerge(unsigned int dir) -{ - bool unsafe = false; - - if (dir & R_HORI) - { - // If they are not the same on both sides then we can merge. - if (_blocks != _down->_blocks) - { - printf("\n _blocks:\n "); - for (ShapeList::iterator i = _blocks.begin(); i != _blocks.end(); - ++i) - { - printf("%d ", *i); - } - printf("\n _down->_blocks:\n "); - for (ShapeList::iterator i = _down->_blocks.begin(); - i != _down->_blocks.end(); ++i) - { - printf("%d ", *i); - } - unsafe |= true; - printf("\n"); - } - - if ((dir == R_LEFT) && _left) - { - unsafe |= _left->unsafeToMerge(dir); - } - else if ((dir == R_RIGHT) && _right) - { - unsafe |= _right->unsafeToMerge(dir); - } - } - else if (dir & R_VERT) - { - if (_blocks != _right->_blocks) - { - printf("\n _blocks:\n "); - for (ShapeList::iterator i = _blocks.begin(); i != _blocks.end(); - ++i) - { - printf("%d ", *i); - } - printf("\n _right->_blocks:\n "); - for (ShapeList::iterator i = _right->_blocks.begin(); - i != _right->_blocks.end(); ++i) - { - printf("%d ", *i); - } - unsafe |= true; - printf("\n"); - } - - if ((dir == R_UP) && _up) - { - unsafe |= _up->unsafeToMerge(dir); - } - else if ((dir == R_DOWN) && _down) - { - unsafe |= _down->unsafeToMerge(dir); - } - } - return unsafe; - -} - - -Region *Region::getTopLeftRegion(void) -{ - Region *curr = centerRegion; - - while (curr && (curr->_up || curr->_left)) - { - if (curr->_up) - { - curr = curr->_up; - } - else - { - curr = curr->_left; - } - } - return curr; -} - - -} - diff --git a/src/libavoid/region.h b/src/libavoid/region.h deleted file mode 100644 index 6a44ccb08..000000000 --- a/src/libavoid/region.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * vim: ts=4 sw=4 et tw=0 wm=0 - * - * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -*/ - -#ifndef AVOID_REGION_H -#define AVOID_REGION_H - -#include -#include "libavoid/geomtypes.h" - -namespace Avoid { - -class ShapeRef; - -typedef std::list ShapeList; - - -class Region -{ - public: - Region(); - Region(double x1, double y1, double x2, double y2); - - bool overlapCheck(BBox& bbox, unsigned int& p); - void getBBox(BBox& bb); - Region *up(void); - Region *down(void); - Region *left(void); - Region *right(void); - void initialSplit(BBox& bbox, unsigned int pos, unsigned int& shapeId); - bool isBlock(void); - - static void pairHor(Region *l, Region *r); - static void pairVer(Region *a, Region *b); - static void addShape(ShapeRef *shape); - static void removeShape(ShapeRef *shape); - static Region *getTopLeftRegion(void); - - private: - BBox _bbox; - Region *_left; - Region *_right; - Region *_up; - Region *_down; - ShapeList _blocks; - - Region *split(double pos, unsigned int dir); - void merge(unsigned int dir); - void mergeRegion(Region *src); - Region *findRegion(double pos, unsigned int dir, - const bool forMerge = false); - Region *splitDir(double pos, unsigned int dir, bool first = false); - void mergeDir(unsigned int dir, bool first = false); - void copyAttributes(Region *src); - void mergeAttributes(Region *src); - bool safeToMerge(unsigned int dir); - bool unsafeToMerge(unsigned int dir); -}; - - -} - -#endif diff --git a/src/libavoid/router.cpp b/src/libavoid/router.cpp index df0bacd02..754c464b4 100644 --- a/src/libavoid/router.cpp +++ b/src/libavoid/router.cpp @@ -2,302 +2,667 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2009 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * Author(s): Michael Wybrow */ -#include + +#include +#include + #include "libavoid/shape.h" #include "libavoid/router.h" #include "libavoid/visibility.h" #include "libavoid/connector.h" -#include "libavoid/polyutil.h" #include "libavoid/debug.h" -#include "libavoid/region.h" -#include "math.h" - -//#define ORTHOGONAL_ROUTING +#include "libavoid/orthogonal.h" +#include "libavoid/assertions.h" namespace Avoid { -static const unsigned int infoAdd = 1; -static const unsigned int infoDel = 2; -static const unsigned int infoMov = 3; +enum ActionType { + ShapeMove, + ShapeAdd, + ShapeRemove, + ConnChange +}; +typedef std::list > ConnUpdateList; -class MoveInfo { +class ActionInfo { public: - MoveInfo(ShapeRef *s, Polygn *p, bool fM) - : shape(s) - , newPoly(copyPoly(*p)) - , firstMove(fM) - { } - ~MoveInfo() + ActionInfo(ActionType t, ShapeRef *s, const Polygon& p, bool fM) + : type(t), + objPtr(s), + newPoly(p), + firstMove(fM) + { + COLA_ASSERT(type == ShapeMove); + } + ActionInfo(ActionType t, ShapeRef *s) + : type(t), + objPtr(s) + { + COLA_ASSERT(type != ConnChange); + } + ActionInfo(ActionType t, ConnRef *c) + : type(t), + objPtr(c) + { + COLA_ASSERT(type == ConnChange); + } + ~ActionInfo() + { + } + ShapeRef *shape(void) const + { + COLA_ASSERT((type == ShapeMove) || (type == ShapeAdd) || + (type == ShapeRemove)); + return (static_cast (objPtr)); + } + ConnRef *conn(void) const + { + COLA_ASSERT(type == ConnChange); + return (static_cast (objPtr)); + } + bool operator==(const ActionInfo& rhs) const + { + return (type == rhs.type) && (objPtr == rhs.objPtr); + } + bool operator<(const ActionInfo& rhs) const { - freePoly(newPoly); + if (type != rhs.type) + { + return type < rhs.type; + } + return objPtr < rhs.objPtr; } - ShapeRef *shape; - Polygn newPoly; + ActionType type; + void *objPtr; + Polygon newPoly; bool firstMove; + ConnUpdateList conns; }; - -Router::Router() - : PartialTime(false) - , SimpleRouting(false) - , segmt_penalty(0) - , angle_penalty(0) - , crossing_penalty(200) - // Algorithm options: - , UseAStarSearch(true) - , IgnoreRegions(true) - , SelectiveReroute(true) - , IncludeEndpoints(true) - , UseLeesAlgorithm(false) - , InvisibilityGrph(true) - , ConsolidateMoves(true) - , PartialFeedback(false) - // Instrumentation: - , st_checked_edges(0) -#ifdef LINEDEBUG - , avoid_screen(NULL) +Router::Router(const unsigned int flags) + : visOrthogGraph(true), + PartialTime(false), + SimpleRouting(false), + ClusteredRouting(true), + // Poly-line algorithm options: + IgnoreRegions(true), + UseLeesAlgorithm(true), + InvisibilityGrph(true), + // General algorithm options: + SelectiveReroute(true), + PartialFeedback(false), + RubberBandRouting(false), + // Instrumentation: + st_checked_edges(0), +#ifdef LIBAVOID_SDL + avoid_screen(NULL), #endif -{ } + _largestAssignedId(0), + _consolidateActions(true), + _orthogonalNudgeDistance(4.0), + // Mode options: + _polyLineRouting(false), + _orthogonalRouting(false), + _staticGraphInvalidated(true), + _inCrossingPenaltyReroutingStage(false) +{ + // At least one of the Routing modes must be set. + COLA_ASSERT(flags & (PolyLineRouting | OrthogonalRouting)); + if (flags & PolyLineRouting) + { + _polyLineRouting = true; + } + if (flags & OrthogonalRouting) + { + _orthogonalRouting = true; + } + for (size_t p = 0; p < lastPenaltyMarker; ++p) + { + _routingPenalties[p] = 0.0; + } + _routingPenalties[clusterCrossingPenalty] = 4000; +} -void Router::addShape(ShapeRef *shape) +Router::~Router() { - unsigned int pid = shape->id(); - Polygn poly = shape->poly(); + // Delete remaining connectors. + ConnRefList::iterator conn = connRefs.begin(); + while (conn != connRefs.end()) + { + db_printf("Deleting connector %u in ~Router()\n", (*conn)->id()); + delete *conn; + conn = connRefs.begin(); + } - adjustContainsWithAdd(poly, pid); - - // o Check all visibility edges to see if this one shape - // blocks them. - newBlockingShape(&poly, pid); + // Remove remaining shapes. + ShapeRefList::iterator shape = shapeRefs.begin(); + while (shape != shapeRefs.end()) + { + ShapeRef *shapePtr = *shape; + db_printf("Deleting shape %u in ~Router()\n", shapePtr->id()); + if (shapePtr->isActive()) + { + shapePtr->removeFromGraph(); + shapePtr->makeInactive(); + } + delete shapePtr; + shape = shapeRefs.begin(); + } + + // Cleanup orphaned orthogonal graph vertices. + destroyOrthogonalVisGraph(); + + COLA_ASSERT(connRefs.size() == 0); + COLA_ASSERT(shapeRefs.size() == 0); + COLA_ASSERT(visGraph.size() == 0); + COLA_ASSERT(invisGraph.size() == 0); +} -#ifdef ORTHOGONAL_ROUTING - Region::addShape(shape); -#endif - // o Calculate visibility for the new vertices. - if (UseLeesAlgorithm) +void Router::modifyConnector(ConnRef *conn, const unsigned int type, + const ConnEnd& connEnd) +{ + ActionInfo modInfo(ConnChange, conn); + + ActionInfoList::iterator found = + find(actionList.begin(), actionList.end(), modInfo); + if (found == actionList.end()) { - shapeVisSweep(shape); + modInfo.conns.push_back(std::make_pair(type, connEnd)); + actionList.push_back(modInfo); } else { - shapeVis(shape); + found->conns.push_back(std::make_pair(type, connEnd)); + } + + if (!_consolidateActions) + { + processTransaction(); } - callbackAllInvalidConnectors(); } -void Router::delShape(ShapeRef *shape) +void Router::modifyConnector(ConnRef *conn) { - unsigned int pid = shape->id(); + ActionInfo modInfo(ConnChange, conn); + + ActionInfoList::iterator found = + find(actionList.begin(), actionList.end(), modInfo); + if (found == actionList.end()) + { + actionList.push_back(modInfo); + } - // Delete items that are queued in the movList. - for (MoveInfoList::iterator it = moveList.begin(); it != moveList.end(); ) + if (!_consolidateActions) { - if ((*it)->shape->id() == pid) - { - MoveInfoList::iterator doomed = it; - ++it; - moveList.erase(doomed); - } - else - { - ++it; - } + processTransaction(); } +} - // o Remove entries related to this shape's vertices - shape->removeFromGraph(); - - if (SelectiveReroute) + +void Router::removeQueuedConnectorActions(ConnRef *conn) +{ + ActionInfo modInfo(ConnChange, conn); + + ActionInfoList::iterator found = + find(actionList.begin(), actionList.end(), modInfo); + if (found != actionList.end()) { - markConnectors(shape); + actionList.erase(found); } +} - adjustContainsWithDel(pid); - -#ifdef ORTHOGONAL_ROUTING - Region::removeShape(shape); -#endif - delete shape; +void Router::addShape(ShapeRef *shape) +{ + // There shouldn't be remove events or move events for the same shape + // already in the action list. + // XXX: Possibly we could handle this by ordering them intelligently. + COLA_ASSERT(find(actionList.begin(), actionList.end(), + ActionInfo(ShapeRemove, shape)) == actionList.end()); + COLA_ASSERT(find(actionList.begin(), actionList.end(), + ActionInfo(ShapeMove, shape)) == actionList.end()); + + ActionInfo addInfo(ShapeAdd, shape); - // o Check all edges that were blocked by this shape. - if (InvisibilityGrph) + ActionInfoList::iterator found = + find(actionList.begin(), actionList.end(), addInfo); + if (found == actionList.end()) { - checkAllBlockedEdges(pid); + actionList.push_back(addInfo); } - else + + if (!_consolidateActions) { - // check all edges not in graph - checkAllMissingEdges(); + processTransaction(); } - callbackAllInvalidConnectors(); } -void Router::moveShape(ShapeRef *shape, Polygn *newPoly, const bool first_move) +void Router::removeShape(ShapeRef *shape) { + // There shouldn't be add events events for the same shape already + // in the action list. + // XXX: Possibly we could handle this by ordering them intelligently. + COLA_ASSERT(find(actionList.begin(), actionList.end(), + ActionInfo(ShapeAdd, shape)) == actionList.end()); + + // Delete any ShapeMove entries for this shape in the action list. + ActionInfoList::iterator found = find(actionList.begin(), + actionList.end(), ActionInfo(ShapeMove, shape)); + if (found != actionList.end()) + { + actionList.erase(found); + } + + // Add the ShapeRemove entry. + ActionInfo remInfo(ShapeRemove, shape); + found = find(actionList.begin(), actionList.end(), remInfo); + if (found == actionList.end()) + { + actionList.push_back(remInfo); + } + + if (!_consolidateActions) + { + processTransaction(); + } +} + + +void Router::moveShape(ShapeRef *shape, const double xDiff, const double yDiff) +{ + Polygon newPoly = shape->polygon(); + newPoly.translate(xDiff, yDiff); + + moveShape(shape, newPoly); +} + + +void Router::moveShape(ShapeRef *shape, const Polygon& newPoly, + const bool first_move) +{ + // There shouldn't be remove events or add events for the same shape + // already in the action list. + // XXX: Possibly we could handle this by ordering them intelligently. + COLA_ASSERT(find(actionList.begin(), actionList.end(), + ActionInfo(ShapeRemove, shape)) == actionList.end()); + + if (find(actionList.begin(), actionList.end(), + ActionInfo(ShapeAdd, shape)) != actionList.end()) + { + // The Add is enough, no need for the Move action too. + return; + } + + ActionInfo moveInfo(ShapeMove, shape, newPoly, first_move); // Sanely cope with the case where the user requests moving the same // shape multiple times before rerouting connectors. - bool alreadyThere = false; - unsigned int id = shape->id(); - MoveInfoList::iterator finish = moveList.end(); - for (MoveInfoList::iterator it = moveList.begin(); it != finish; ++it) + ActionInfoList::iterator found = + find(actionList.begin(), actionList.end(), moveInfo); + + if (found != actionList.end()) { - if ((*it)->shape->id() == id) + if (!SimpleRouting) { - if (!SimpleRouting) - { - fprintf(stderr, - "warning: multiple moves requested for shape %d.\n", - (int) id); - } - // Just update the MoveInfo with the second polygon, but - // leave the firstMove setting alone. - (*it)->newPoly = copyPoly(*newPoly); - alreadyThere = true; + db_printf("warning: multiple moves requested for shape %d " + "within a single transaction.\n", (int) shape->id()); } + // Just update the ActionInfo with the second polygon, but + // leave the firstMove setting alone. + found->newPoly = newPoly; + } + else + { + actionList.push_back(moveInfo); } - if (!alreadyThere) + if (!_consolidateActions) { - MoveInfo *moveInfo = new MoveInfo(shape, newPoly, first_move); - moveList.push_back(moveInfo); + processTransaction(); } +} + + +void Router::setStaticGraphInvalidated(const bool invalidated) +{ + _staticGraphInvalidated = invalidated; +} + + +void Router::destroyOrthogonalVisGraph(void) +{ + // Remove orthogonal visibility graph edges. + visOrthogGraph.clear(); - if (!ConsolidateMoves) + // Remove the now orphaned vertices. + VertInf *curr = vertices.shapesBegin(); + while (curr) { - processMoves(); + if (curr->orphaned() && (curr->id == dummyOrthogID)) + { + VertInf *following = vertices.removeVertex(curr); + delete curr; + curr = following; + continue; + } + curr = curr->lstNext; } } -void Router::processMoves(void) +void Router::regenerateStaticBuiltGraph(void) { - // If SimpleRouting, then don't update yet. - if (moveList.empty() || SimpleRouting) + // Here we do talks involved in updating the static-built visibility + // graph (if necessary) before we do any routing. + if (_staticGraphInvalidated) { - return; + if (_orthogonalRouting) + { + destroyOrthogonalVisGraph(); + + timers.Register(tmOrthogGraph, timerStart); + // Regenerate a new visibility graph. + generateStaticOrthogonalVisGraph(this); + + timers.Stop(); + } + _staticGraphInvalidated = false; } +} + - MoveInfoList::iterator curr; - MoveInfoList::iterator finish = moveList.end(); - for (curr = moveList.begin(); curr != finish; ++curr) +bool Router::shapeInQueuedActionList(ShapeRef *shape) const +{ + bool foundAdd = find(actionList.begin(), actionList.end(), + ActionInfo(ShapeAdd, shape)) != actionList.end(); + bool foundRem = find(actionList.begin(), actionList.end(), + ActionInfo(ShapeRemove, shape)) != actionList.end(); + bool foundMove = find(actionList.begin(), actionList.end(), + ActionInfo(ShapeMove, shape)) != actionList.end(); + + return (foundAdd || foundRem || foundMove); +} + + +bool Router::transactionUse(void) const +{ + return _consolidateActions; +} + + +void Router::setTransactionUse(const bool transactions) +{ + _consolidateActions = transactions; +} + + +bool Router::processTransaction(void) +{ + bool notPartialTime = !(PartialFeedback && PartialTime); + bool seenShapeMovesOrDeletes = false; + + // If SimpleRouting, then don't update here. + if (actionList.empty() || SimpleRouting) + { + return false; + } + + actionList.sort(); + ActionInfoList::iterator curr; + ActionInfoList::iterator finish = actionList.end(); + for (curr = actionList.begin(); curr != finish; ++curr) { - MoveInfo *moveInf = *curr; - ShapeRef *shape = moveInf->shape; - Polygn *newPoly = &(moveInf->newPoly); - bool first_move = moveInf->firstMove; + ActionInfo& actInf = *curr; + if (!((actInf.type == ShapeRemove) || (actInf.type == ShapeMove))) + { + // Not a move or remove action, so don't do anything. + continue; + } + seenShapeMovesOrDeletes = true; + + ShapeRef *shape = actInf.shape(); + bool isMove = (actInf.type == ShapeMove); + bool first_move = actInf.firstMove; unsigned int pid = shape->id(); - bool notPartialTime = !(PartialFeedback && PartialTime); // o Remove entries related to this shape's vertices shape->removeFromGraph(); - if (SelectiveReroute && (notPartialTime || first_move)) + if (SelectiveReroute && (!isMove || notPartialTime || first_move)) { markConnectors(shape); } adjustContainsWithDel(pid); -#ifdef ORTHOGONAL_ROUTING - Region::removeShape(shape); -#endif - - shape->setNewPoly(*newPoly); - - adjustContainsWithAdd(*newPoly, pid); - // Ignore this shape for visibility. // XXX: We don't really need to do this if we're not using Partial // Feedback. Without this the blocked edges still route // around the shape until it leaves the connector. shape->makeInactive(); - } - if (InvisibilityGrph) + if (seenShapeMovesOrDeletes && _polyLineRouting) { - for (curr = moveList.begin(); curr != finish; ++curr) + if (InvisibilityGrph) { - MoveInfo *moveInf = *curr; - ShapeRef *shape = moveInf->shape; - unsigned int pid = shape->id(); - - // o Check all edges that were blocked by this shape. - checkAllBlockedEdges(pid); + for (curr = actionList.begin(); curr != finish; ++curr) + { + ActionInfo& actInf = *curr; + if (!((actInf.type == ShapeRemove) || + (actInf.type == ShapeMove))) + { + // Not a move or remove action, so don't do anything. + continue; + } + + // o Check all edges that were blocked by this shape. + checkAllBlockedEdges(actInf.shape()->id()); + } + } + else + { + // check all edges not in graph + checkAllMissingEdges(); } - } - else - { - // check all edges not in graph - checkAllMissingEdges(); } - while ( ! moveList.empty() ) + for (curr = actionList.begin(); curr != finish; ++curr) { - MoveInfo *moveInf = moveList.front(); - ShapeRef *shape = moveInf->shape; - Polygn *newPoly = &(moveInf->newPoly); + ActionInfo& actInf = *curr; + if (!((actInf.type == ShapeAdd) || (actInf.type == ShapeMove))) + { + // Not a move or add action, so don't do anything. + continue; + } + + ShapeRef *shape = actInf.shape(); + Polygon& newPoly = actInf.newPoly; + bool isMove = (actInf.type == ShapeMove); unsigned int pid = shape->id(); - bool notPartialTime = !(PartialFeedback && PartialTime); // Restore this shape for visibility. shape->makeActive(); + + if (isMove) + { + shape->setNewPoly(newPoly); + } + const Polygon& shapePoly = shape->polygon(); -#ifdef ORTHOGONAL_ROUTING - Region::addShape(shape); -#endif + adjustContainsWithAdd(shapePoly, pid); - // o Check all visibility edges to see if this one shape - // blocks them. - if (notPartialTime) + if (_polyLineRouting) { - newBlockingShape(newPoly, pid); + // o Check all visibility edges to see if this one shape + // blocks them. + if (!isMove || notPartialTime) + { + newBlockingShape(shapePoly, pid); + } + + // o Calculate visibility for the new vertices. + if (UseLeesAlgorithm) + { + shapeVisSweep(shape); + } + else + { + shapeVis(shape); + } } + } - // o Calculate visibility for the new vertices. - if (UseLeesAlgorithm) + // Update connector endpoints. + for (curr = actionList.begin(); curr != finish; ++curr) + { + ActionInfo& actInf = *curr; + if (actInf.type != ConnChange) { - shapeVisSweep(shape); + continue; } - else + for (ConnUpdateList::iterator conn = actInf.conns.begin(); + conn != actInf.conns.end(); ++conn) { - shapeVis(shape); + actInf.conn()->updateEndPoint(conn->first, conn->second); } - - moveList.pop_front(); - delete moveInf; } - callbackAllInvalidConnectors(); + // Clear the actionList. + actionList.clear(); + + _staticGraphInvalidated = true; + rerouteAndCallbackConnectors(); + + return true; +} + + +void Router::addCluster(ClusterRef *cluster) +{ + cluster->makeActive(); + + unsigned int pid = cluster->id(); + ReferencingPolygon& poly = cluster->polygon(); + + adjustClustersWithAdd(poly, pid); +} + + +void Router::delCluster(ClusterRef *cluster) +{ + cluster->makeInactive(); + + unsigned int pid = cluster->id(); + + adjustClustersWithDel(pid); +} + + +void Router::setOrthogonalNudgeDistance(const double dist) +{ + COLA_ASSERT(dist >= 0); + _orthogonalNudgeDistance = dist; +} + + +double Router::orthogonalNudgeDistance(void) const +{ + return _orthogonalNudgeDistance; +} + + +unsigned int Router::assignId(const unsigned int suggestedId) +{ + // If the suggestedId is zero, then we assign the object the next + // smallest unassigned ID, otherwise we trust the ID given is unique. + unsigned int assignedId = (suggestedId == 0) ? + (_largestAssignedId + 1) : suggestedId; + + // Have the router record if this ID is larger than the _largestAssignedId. + _largestAssignedId = std::max(_largestAssignedId, assignedId); + + // If assertions are enabled, then we check that this ID really is unique. + COLA_ASSERT(idIsUnique(assignedId)); + + return assignedId; +} + + + // Returns whether the given ID is unique among all objects known by the + // router. Outputs a warning if the ID is found ore than once. + // It is expected this is only going to be called from assertions while + // debugging, so efficiency is not an issue and we just iterate over all + // objects. +bool Router::idIsUnique(const unsigned int id) const +{ + unsigned int count = 0; + + // Examine shapes. + for (ShapeRefList::const_iterator i = shapeRefs.begin(); + i != shapeRefs.end(); ++i) + { + if ((*i)->id() == id) + { + count++; + } + } + + // Examine connectors. + for (ConnRefList::const_iterator i = connRefs.begin(); + i != connRefs.end(); ++i) + { + if ((*i)->id() == id) + { + count++; + } + } + + // Examine clusters. + for (ClusterRefList::const_iterator i = clusterRefs.begin(); + i != clusterRefs.end(); ++i) + { + if ((*i)->id() == id) + { + count++; + } + } + + if (count > 1) + { + db_printf("Warning:\tlibavoid object ID %d not unique.\n", id); + return false; + } + return true; } @@ -313,13 +678,15 @@ void Router::processMoves(void) void Router::attachedConns(IntList &conns, const unsigned int shapeId, const unsigned int type) { - ConnRefList::iterator fin = connRefs.end(); - for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i) { - - if ((type & runningTo) && ((*i)->_dstId == shapeId)) { + ConnRefList::const_iterator fin = connRefs.end(); + for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i) + { + if ((type & runningTo) && ((*i)->_dstId == shapeId)) + { conns.push_back((*i)->_id); } - else if ((type & runningFrom) && ((*i)->_srcId == shapeId)) { + else if ((type & runningFrom) && ((*i)->_srcId == shapeId)) + { conns.push_back((*i)->_id); } } @@ -331,16 +698,19 @@ void Router::attachedConns(IntList &conns, const unsigned int shapeId, void Router::attachedShapes(IntList &shapes, const unsigned int shapeId, const unsigned int type) { - ConnRefList::iterator fin = connRefs.end(); - for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i) { - if ((type & runningTo) && ((*i)->_dstId == shapeId)) { + ConnRefList::const_iterator fin = connRefs.end(); + for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i) + { + if ((type & runningTo) && ((*i)->_dstId == shapeId)) + { if ((*i)->_srcId != 0) { // Only if there is a shape attached to the other end. shapes.push_back((*i)->_srcId); } } - else if ((type & runningFrom) && ((*i)->_srcId == shapeId)) { + else if ((type & runningFrom) && ((*i)->_srcId == shapeId)) + { if ((*i)->_dstId != 0) { // Only if there is a shape attached to the other end. @@ -351,18 +721,127 @@ void Router::attachedShapes(IntList &shapes, const unsigned int shapeId, } - // It's intended this function is called after shape movement has - // happened to alert connectors that they need to be rerouted. -void Router::callbackAllInvalidConnectors(void) + // It's intended this function is called after visibility changes + // resulting from shape movement have happened. It will alert + // rerouted connectors (via a callback) that they need to be redrawn. +void Router::rerouteAndCallbackConnectors(void) +{ + std::set reroutedConns; + ConnRefList::const_iterator fin = connRefs.end(); + + // Updating the orthogonal visibility graph if necessary. + regenerateStaticBuiltGraph(); + + timers.Register(tmOrthogRoute, timerStart); + for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i) + { + (*i)->_needs_repaint = false; + bool rerouted = (*i)->generatePath(); + if (rerouted) + { + reroutedConns.insert(*i); + } + } + timers.Stop(); + + // Find and reroute crossing connectors if crossing penalties are set. + improveCrossings(); + + // Perform centring and nudging for othogonal routes. + improveOrthogonalRoutes(this); + + // Alert connectors that they need redrawing. + for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i) + { + (*i)->_needs_repaint = true; + (*i)->performCallback(); + } +} + + +typedef std::set ConnRefSet; + +void Router::improveCrossings(void) { + const double crossing_penalty = routingPenalty(crossingPenalty); + const double shared_path_penalty = routingPenalty(fixedSharedPathPenalty); + if ((crossing_penalty == 0) && (shared_path_penalty == 0)) + { + // No penalties, return. + return; + } + + // Find crossings and reroute connectors. + _inCrossingPenaltyReroutingStage = true; + ConnRefSet crossingConns; ConnRefList::iterator fin = connRefs.end(); - for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i) { - (*i)->handleInvalid(); + for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i) + { + Avoid::Polygon& iRoute = (*i)->routeRef(); + ConnRefList::iterator j = i; + for (++j; j != fin; ++j) + { + if ((crossingConns.find(*i) != crossingConns.end()) && + (crossingConns.find(*j) != crossingConns.end())) + { + // We already know both these have crossings. + continue; + } + // Determine if this pair cross. + Avoid::Polygon& jRoute = (*j)->routeRef(); + CrossingsInfoPair crossingInfo = std::make_pair(0, 0); + bool meetsPenaltyCriteria = false; + for (size_t jInd = 1; jInd < jRoute.size(); ++jInd) + { + const bool finalSegment = ((jInd + 1) == jRoute.size()); + CrossingsInfoPair crossingInfo = countRealCrossings( + iRoute, true, jRoute, jInd, false, + finalSegment, NULL, NULL, *i, *j); + + if ((shared_path_penalty > 0) && + (crossingInfo.second & CROSSING_SHARES_PATH) && + (crossingInfo.second & CROSSING_SHARES_FIXED_SEGMENT) && + !(crossingInfo.second & CROSSING_SHARES_PATH_AT_END)) + { + // We are penalising fixedSharedPaths and there is a + // fixedSharedPath. + meetsPenaltyCriteria = true; + break; + } + else if ((crossing_penalty > 0) && (crossingInfo.first > 0)) + { + // We are penalising crossings and this is a crossing. + meetsPenaltyCriteria = true; + break; + } + } + if (meetsPenaltyCriteria) + { + crossingConns.insert(*i); + crossingConns.insert(*j); + } + } + } + + for (ConnRefSet::iterator i = crossingConns.begin(); + i != crossingConns.end(); ++i) + { + ConnRef *conn = *i; + conn->makePathInvalid(); + // XXX: Could we free these routes here for extra savings? + // conn->freeRoutes(); + } + for (ConnRefSet::iterator i = crossingConns.begin(); + i != crossingConns.end(); ++i) + { + ConnRef *conn = *i; + conn->generatePath(); } + _inCrossingPenaltyReroutingStage = false; } -void Router::newBlockingShape(Polygn *poly, int pid) +void Router::newBlockingShape(const Polygon& poly, int pid) { // o Check all visibility edges to see if this one shape // blocks them. @@ -382,8 +861,11 @@ void Router::newBlockingShape(Polygn *poly, int pid) Point e2 = points.second; bool blocked = false; - bool ep_in_poly1 = !(eID1.isShape) ? inPoly(*poly, e1) : false; - bool ep_in_poly2 = !(eID2.isShape) ? inPoly(*poly, e2) : false; + bool countBorder = false; + bool ep_in_poly1 = !(eID1.isShape) ? + inPoly(poly, e1, countBorder) : false; + bool ep_in_poly2 = !(eID2.isShape) ? + inPoly(poly, e2, countBorder) : false; if (ep_in_poly1 || ep_in_poly2) { // Don't check edges that have a connector endpoint @@ -391,10 +873,14 @@ void Router::newBlockingShape(Polygn *poly, int pid) continue; } - for (int pt_i = 0; pt_i < poly->pn; pt_i++) + bool seenIntersectionAtEndpoint = false; + for (size_t pt_i = 0; pt_i < poly.size(); ++pt_i) { - int pt_n = (pt_i == (poly->pn - 1)) ? 0 : pt_i + 1; - if (segmentIntersect(e1, e2, poly->ps[pt_i], poly->ps[pt_n])) + size_t pt_n = (pt_i == (poly.size() - 1)) ? 0 : pt_i + 1; + const Point& pi = poly.ps[pt_i]; + const Point& pn = poly.ps[pt_n]; + if (segmentShapeIntersect(e1, e2, pi, pn, + seenIntersectionAtEndpoint)) { blocked = true; break; @@ -422,7 +908,7 @@ void Router::newBlockingShape(Polygn *poly, int pid) void Router::checkAllBlockedEdges(int pid) { - assert(InvisibilityGrph); + COLA_ASSERT(InvisibilityGrph); for (EdgeInf *iter = invisGraph.begin(); iter != invisGraph.end() ; ) { @@ -444,18 +930,9 @@ void Router::checkAllBlockedEdges(int pid) void Router::checkAllMissingEdges(void) { - assert(!InvisibilityGrph); + COLA_ASSERT(!InvisibilityGrph); - VertInf *first = NULL; - - if (IncludeEndpoints) - { - first = vertices.connsBegin(); - } - else - { - first = vertices.shapesBegin(); - } + VertInf *first = vertices.connsBegin(); VertInf *pend = vertices.end(); for (VertInf *i = first; i != pend; i = i->lstNext) @@ -489,39 +966,79 @@ void Router::checkAllMissingEdges(void) void Router::generateContains(VertInf *pt) { contains[pt->id].clear(); + enclosingClusters[pt->id].clear(); - ShapeRefList::iterator finish = shapeRefs.end(); - for (ShapeRefList::iterator i = shapeRefs.begin(); i != finish; ++i) + // Don't count points on the border as being inside. + bool countBorder = false; + + // Compute enclosing shapes. + ShapeRefList::const_iterator finish = shapeRefs.end(); + for (ShapeRefList::const_iterator i = shapeRefs.begin(); i != finish; ++i) { - Polygn poly = copyPoly(*i); - if (inPoly(poly, pt->point)) + if (inPoly((*i)->polygon(), pt->point, countBorder)) { contains[pt->id].insert((*i)->id()); } - freePoly(poly); + } + + // Computer enclosing Clusters + ClusterRefList::const_iterator clFinish = clusterRefs.end(); + for (ClusterRefList::const_iterator i = clusterRefs.begin(); + i != clFinish; ++i) + { + if (inPolyGen((*i)->polygon(), pt->point)) + { + enclosingClusters[pt->id].insert((*i)->id()); + } } } -void Router::adjustContainsWithAdd(const Polygn& poly, const int p_shape) +void Router::adjustClustersWithAdd(const PolygonInterface& poly, + const int p_cluster) { for (VertInf *k = vertices.connsBegin(); k != vertices.shapesBegin(); k = k->lstNext) { - if (inPoly(poly, k->point)) + if (inPolyGen(poly, k->point)) { - contains[k->id].insert(p_shape); + enclosingClusters[k->id].insert(p_cluster); } } } -void Router::adjustContainsWithDel(const int p_shape) +void Router::adjustClustersWithDel(const int p_cluster) +{ + for (ContainsMap::iterator k = enclosingClusters.begin(); + k != enclosingClusters.end(); ++k) + { + (*k).second.erase(p_cluster); + } +} + + +void Router::adjustContainsWithAdd(const Polygon& poly, const int p_shape) { + // Don't count points on the border as being inside. + bool countBorder = false; + for (VertInf *k = vertices.connsBegin(); k != vertices.shapesBegin(); k = k->lstNext) { - contains[k->id].erase(p_shape); + if (inPoly(poly, k->point, countBorder)) + { + contains[k->id].insert(p_shape); + } + } +} + + +void Router::adjustContainsWithDel(const int p_shape) +{ + for (ContainsMap::iterator k = contains.begin(); k != contains.end(); ++k) + { + (*k).second.erase(p_shape); } } @@ -537,14 +1054,21 @@ static double AngleAFromThreeSides(const double a, const double b, void Router::markConnectors(ShapeRef *shape) { - assert(SelectiveReroute); + if (RubberBandRouting) + { + // When rubber-band routing, we do no reroute connectors that + // may have a better route, only invalid connectors. + return; + } + + COLA_ASSERT(SelectiveReroute); - ConnRefList::iterator end = connRefs.end(); - for (ConnRefList::iterator it = connRefs.begin(); it != end; ++it) + ConnRefList::const_iterator end = connRefs.end(); + for (ConnRefList::const_iterator it = connRefs.begin(); it != end; ++it) { ConnRef *conn = (*it); - if (conn->_route.pn == 0) + if (conn->_route.empty()) { // Ignore uninitialised connectors. continue; @@ -556,7 +1080,7 @@ void Router::markConnectors(ShapeRef *shape) } Point start = conn->_route.ps[0]; - Point end = conn->_route.ps[conn->_route.pn - 1]; + Point end = conn->_route.ps[conn->_route.size() - 1]; double conndist = conn->_route_dist; @@ -609,12 +1133,12 @@ void Router::markConnectors(ShapeRef *shape) Point n_p2(p2.x - p1.x, p2.y - p1.y); Point n_start(start.x - p1.x, start.y - p1.y); Point n_end(end.x - p1.x, end.y - p1.y); - //printf("n_p2: (%.1f, %.1f)\n", n_p2.x, n_p2.y); - //printf("n_start: (%.1f, %.1f)\n", n_start.x, n_start.y); - //printf("n_end: (%.1f, %.1f)\n", n_end.x, n_end.y); + //db_printf("n_p2: (%.1f, %.1f)\n", n_p2.x, n_p2.y); + //db_printf("n_start: (%.1f, %.1f)\n", n_start.x, n_start.y); + //db_printf("n_end: (%.1f, %.1f)\n", n_end.x, n_end.y); double theta = 0 - atan2(n_p2.y, n_p2.x); - //printf("theta = %.2f\n", theta * (180 / PI)); + //db_printf("theta = %.2f\n", theta * (180 / PI)); Point r_p1(0, 0); Point r_p2 = n_p2; @@ -630,16 +1154,15 @@ void Router::markConnectors(ShapeRef *shape) start.y = cosv * n_start.y + sinv * n_start.x; end.x = cosv * n_end.x - sinv * n_end.y; end.y = cosv * n_end.y + sinv * n_end.x; - //printf("r_p2: (%.1f, %.1f)\n", r_p2.x, r_p2.y); - //printf("r_start: (%.1f, %.1f)\n", start.x, start.y); - //printf("r_end: (%.1f, %.1f)\n", end.x, end.y); + //db_printf("r_p2: (%.1f, %.1f)\n", r_p2.x, r_p2.y); + //db_printf("r_start: (%.1f, %.1f)\n", start.x, start.y); + //db_printf("r_end: (%.1f, %.1f)\n", end.x, end.y); - if (((int) r_p2.y) != 0) + // This might be slightly off. + if (fabs(r_p2.y) > 0.0001) { - printf("r_p2.y: %f != 0\n", r_p2.y); - std::abort(); + db_printf("r_p2.y: %f != 0\n", r_p2.y); } - // This might be slightly off. r_p2.y = 0; offy = r_p1.y; @@ -679,13 +1202,13 @@ void Router::markConnectors(ShapeRef *shape) x = ((b*c) + (a*d)) / (b + d); } - //printf("%.1f, %.1f, %.1f, %.1f\n", a, b, c, d); - //printf("x = %.1f\n", x); + //db_printf("%.1f, %.1f, %.1f, %.1f\n", a, b, c, d); + //db_printf("x = %.1f\n", x); x = std::max(min, x); x = std::min(max, x); - //printf("x = %.1f\n", x); + //db_printf("x = %.1f\n", x); Point xp; if (p1.x == p2.x) @@ -698,20 +1221,20 @@ void Router::markConnectors(ShapeRef *shape) xp.x = x; xp.y = offy; } - //printf("(%.1f, %.1f)\n", xp.x, xp.y); + //db_printf("(%.1f, %.1f)\n", xp.x, xp.y); - e1 = dist(start, xp); - e2 = dist(xp, end); + e1 = euclideanDist(start, xp); + e2 = euclideanDist(xp, end); estdist = e1 + e2; - //printf("is %.1f < %.1f\n", estdist, conndist); + //db_printf("is %.1f < %.1f\n", estdist, conndist); if (estdist < conndist) { #ifdef SELECTIVE_DEBUG //double angle = AngleAFromThreeSides(dist(start, end), // e1, e2); - printf("[%3d] - Possible better path found (%.1f < %.1f)\n", + db_printf("[%3d] - Possible better path found (%.1f < %.1f)\n", conn->_id, estdist, conndist); #endif conn->_needs_reroute_flag = true; @@ -723,6 +1246,81 @@ void Router::markConnectors(ShapeRef *shape) } +ConnType Router::validConnType(const ConnType select) const +{ + if (select != ConnType_None) + { + if ((select == ConnType_Orthogonal) && _orthogonalRouting) + { + return ConnType_Orthogonal; + } + else if ((select == ConnType_PolyLine) && _polyLineRouting) + { + return ConnType_PolyLine; + } + } + + if (_polyLineRouting) + { + return ConnType_PolyLine; + } + else if (_orthogonalRouting) + { + return ConnType_Orthogonal; + } + return ConnType_None; +} + + +void Router::setRoutingPenalty(const PenaltyType penType, const double penVal) +{ + COLA_ASSERT(penType < lastPenaltyMarker); + if (penVal < 0) + { + // Set some sensible penalty. + switch (penType) + { + case segmentPenalty: + _routingPenalties[penType] = 50; + break; + case fixedSharedPathPenalty: + _routingPenalties[penType] = 110; + break; + case anglePenalty: + _routingPenalties[penType] = 50; + break; + case crossingPenalty: + _routingPenalties[penType] = 200; + break; + case clusterCrossingPenalty: + _routingPenalties[penType] = 4000; + break; + default: + _routingPenalties[penType] = 50; + break; + } + } + else + { + _routingPenalties[penType] = penVal; + } +} + + +double Router::routingPenalty(const PenaltyType penType) const +{ + COLA_ASSERT(penType < lastPenaltyMarker); + return _routingPenalties[penType]; +} + + +double& Router::penaltyRef(const PenaltyType penType) +{ + COLA_ASSERT(penType < lastPenaltyMarker); + return _routingPenalties[penType]; +} + + void Router::printInfo(void) { FILE *fp = stdout; @@ -735,6 +1333,7 @@ void Router::printInfo(void) int st_endpoints = 0; int st_valid_shape_visedges = 0; int st_valid_endpt_visedges = 0; + int st_orthogonal_visedges = 0; int st_invalid_visedges = 0; VertInf *finish = vertices.end(); for (VertInf *t = vertices.connsBegin(); t != finish; t = t->lstNext) @@ -775,9 +1374,15 @@ void Router::printInfo(void) { st_invalid_visedges++; } + for (EdgeInf *t = visOrthogGraph.begin(); t != visOrthogGraph.end(); + t = t->lstNext) + { + st_orthogonal_visedges++; + } fprintf(fp, "Number of shapes: %d\n", st_shapes); fprintf(fp, "Number of vertices: %d (%d real, %d endpoints)\n", st_vertices + st_endpoints, st_vertices, st_endpoints); + fprintf(fp, "Number of orhtog_vis_edges: %d\n", st_orthogonal_visedges); fprintf(fp, "Number of vis_edges: %d (%d valid [%d normal, %d endpt], " "%d invalid)\n", st_valid_shape_visedges + st_invalid_visedges + st_valid_endpt_visedges, st_valid_shape_visedges + @@ -787,12 +1392,462 @@ void Router::printInfo(void) fprintf(fp, "checkVisEdge tally: %d\n", st_checked_edges); fprintf(fp, "----------------------\n"); - fprintf(fp, "ADDS: "); timers.Print(tmAdd); - fprintf(fp, "DELS: "); timers.Print(tmDel); - fprintf(fp, "MOVS: "); timers.Print(tmMov); - fprintf(fp, "***S: "); timers.Print(tmSev); - fprintf(fp, "PTHS: "); timers.Print(tmPth); + fprintf(fp, "ADDS: "); timers.Print(tmAdd, fp); + fprintf(fp, "DELS: "); timers.Print(tmDel, fp); + fprintf(fp, "MOVS: "); timers.Print(tmMov, fp); + fprintf(fp, "***S: "); timers.Print(tmSev, fp); + fprintf(fp, "PTHS: "); timers.Print(tmPth, fp); + fprintf(fp, "OrthogGraph: "); timers.Print(tmOrthogGraph, fp); + fprintf(fp, "OrthogRoute: "); timers.Print(tmOrthogRoute, fp); + fprintf(fp, "OrthogCentre: "); timers.Print(tmOrthogCentre, fp); + fprintf(fp, "OrthogNudge: "); timers.Print(tmOrthogNudge, fp); fprintf(fp, "\n"); + timers.Reset(); +} + + +static const double LIMIT = 100000000; + +static void reduceRange(double& val) +{ + val = std::min(val, LIMIT); + val = std::max(val, -LIMIT); +} + + +//============================================================================= +// The following methods are for testing and debugging. + + +bool Router::existsOrthogonalPathOverlap(void) +{ + ConnRefList::iterator fin = connRefs.end(); + for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i) + { + Avoid::Polygon iRoute = (*i)->displayRoute(); + ConnRefList::iterator j = i; + for (++j; j != fin; ++j) + { + // Determine if this pair overlap + Avoid::Polygon jRoute = (*j)->displayRoute(); + CrossingsInfoPair crossingInfo = std::make_pair(0, 0); + for (size_t jInd = 1; jInd < jRoute.size(); ++jInd) + { + const bool finalSegment = ((jInd + 1) == jRoute.size()); + CrossingsInfoPair crossingInfo = countRealCrossings( + iRoute, true, jRoute, jInd, true, + finalSegment, NULL, NULL, *i, *j); + + if ((crossingInfo.second & CROSSING_SHARES_PATH) && + (crossingInfo.second & CROSSING_SHARES_FIXED_SEGMENT) && + !(crossingInfo.second & CROSSING_SHARES_PATH_AT_END)) + { + // We looking for fixedSharedPaths and there is a + // fixedSharedPath. + return true; + } + } + } + } + return false; +} + + +bool Router::existsOrthogonalTouchingCorners(void) +{ + ConnRefList::iterator fin = connRefs.end(); + for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i) + { + Avoid::Polygon iRoute = (*i)->displayRoute(); + ConnRefList::iterator j = i; + for (++j; j != fin; ++j) + { + // Determine if this pair overlap + Avoid::Polygon jRoute = (*j)->displayRoute(); + CrossingsInfoPair crossingInfo = std::make_pair(0, 0); + for (size_t jInd = 1; jInd < jRoute.size(); ++jInd) + { + const bool finalSegment = ((jInd + 1) == jRoute.size()); + CrossingsInfoPair crossingInfo = countRealCrossings( + iRoute, true, jRoute, jInd, true, + finalSegment, NULL, NULL, *i, *j); + + if (crossingInfo.second & CROSSING_TOUCHES) + { + return true; + } + } + } + } + return false; +} + + +void Router::outputInstanceToSVG(std::string instanceName) +{ + std::string filename; + if (!instanceName.empty()) + { + filename = instanceName; + } + else + { + filename = "libavoid-debug"; + } + filename += ".svg"; + FILE *fp = fopen(filename.c_str(), "w"); + + if (fp == NULL) + { + return; + } + + double minX = LIMIT; + double minY = LIMIT; + double maxX = -LIMIT; + double maxY = -LIMIT; + + VertInf *curr = vertices.connsBegin(); + while (curr) + { + Point p = curr->point; + + reduceRange(p.x); + reduceRange(p.y); + + if (p.x > -LIMIT) + { + minX = std::min(minX, p.x); + } + if (p.x < LIMIT) + { + maxX = std::max(maxX, p.x); + } + if (p.y > -LIMIT) + { + minY = std::min(minY, p.y); + } + if (p.y < LIMIT) + { + maxY = std::max(maxY, p.y); + } + curr = curr->lstNext; + } + minX -= 50; + minY -= 50; + maxX += 50; + maxY += 50; + + fprintf(fp, "\n"); + fprintf(fp, "\n", minX, minY, maxX - minX, maxY - minY); + + // Output source code to generate this instance of the router. + fprintf(fp, "\n"); + + + fprintf(fp, "\n"); + shRefIt = shapeRefs.begin(); + while (shRefIt != shapeRefs.end()) + { + ShapeRef *shRef = *shRefIt; + + fprintf(fp, "id()); + for (size_t i = 0; i < shRef->polygon().size(); ++i) + { + fprintf(fp, "%c %g,%g ", ((i == 0) ? 'M' : 'L'), + shRef->polygon().at(i).x, shRef->polygon().at(i).y); + } + fprintf(fp, "Z\" />\n"); + ++shRefIt; + } + fprintf(fp, "\n"); + + fprintf(fp, "\n"); + shRefIt = shapeRefs.begin(); + while (shRefIt != shapeRefs.end()) + { + ShapeRef *shRef = *shRefIt; + double minX, minY, maxX, maxY; + shRef->polygon().getBoundingRect(&minX, &minY, &maxX, &maxY); + + fprintf(fp, "\n", + shRef->id(), minX, minY, maxX - minX, maxY - minY); + ++shRefIt; + } + fprintf(fp, "\n"); + + + fprintf(fp, "\n"); + EdgeInf *finish = NULL; + fprintf(fp, "\n"); + finish = visGraph.end(); + for (EdgeInf *t = visGraph.begin(); t != finish; t = t->lstNext) + { + std::pair ids = t->ids(); + bool isShape = (ids.first.isShape) && (ids.second.isShape); + if (!isShape) + { + continue; + } + std::pair ptpair = t->points(); + Point p1 = ptpair.first; + Point p2 = ptpair.second; + + reduceRange(p1.x); + reduceRange(p1.y); + reduceRange(p2.x); + reduceRange(p2.y); + + fprintf(fp, "\n", + p1.x, p1.y, p2.x, p2.y, + (!(ids.first.isShape) || !(ids.second.isShape)) ? "green" : + "red"); + } + fprintf(fp, "\n"); + + fprintf(fp, "\n"); + finish = visGraph.end(); + for (EdgeInf *t = visGraph.begin(); t != finish; t = t->lstNext) + { + std::pair ids = t->ids(); + bool isShape = (ids.first.isShape) && (ids.second.isShape); + if (isShape) + { + continue; + } + std::pair ptpair = t->points(); + Point p1 = ptpair.first; + Point p2 = ptpair.second; + + reduceRange(p1.x); + reduceRange(p1.y); + reduceRange(p2.x); + reduceRange(p2.y); + + fprintf(fp, "\n", + p1.x, p1.y, p2.x, p2.y, + (!(ids.first.isShape) || !(ids.second.isShape)) ? "green" : + "red"); + } + fprintf(fp, "\n"); + fprintf(fp, "\n"); + + fprintf(fp, "\n"); + finish = visOrthogGraph.end(); + for (EdgeInf *t = visOrthogGraph.begin(); t != finish; t = t->lstNext) + { + std::pair ptpair = t->points(); + Point p1 = ptpair.first; + Point p2 = ptpair.second; + + reduceRange(p1.x); + reduceRange(p1.y); + reduceRange(p2.x); + reduceRange(p2.y); + + std::pair ids = t->ids(); + + fprintf(fp, "\n", + p1.x, p1.y, p2.x, p2.y, + (!(ids.first.isShape) || !(ids.second.isShape)) ? "green" : + "red"); + } + fprintf(fp, "\n"); + + fprintf(fp, "\n"); + ConnRefList::iterator connRefIt = connRefs.begin(); + while (connRefIt != connRefs.end()) + { + ConnRef *connRef = *connRefIt; + + PolyLine route = connRef->route(); + if (!route.empty()) + { + fprintf(fp, "id(), + route.ps[0].x, route.ps[0].y); + for (size_t i = 1; i < route.size(); ++i) + { + fprintf(fp, "L %g,%g ", route.ps[i].x, route.ps[i].y); + } + fprintf(fp, "\" "); + if (connRef->src() && connRef->dst()) + { + fprintf(fp, "debug=\"src: %d dst: %d\" ", + connRef->src()->visDirections, + connRef->dst()->visDirections); + } + fprintf(fp, "style=\"fill: none; stroke: black; " + "stroke-width: 1px;\" />\n"); + } + + ++connRefIt; + } + fprintf(fp, "\n"); + + fprintf(fp, "\n"); + connRefIt = connRefs.begin(); + while (connRefIt != connRefs.end()) + { + ConnRef *connRef = *connRefIt; + + PolyLine route = connRef->displayRoute().curvedPolyline(8); + if (!route.empty()) + { + fprintf(fp, "id(), + route.ps[0].x, route.ps[0].y); + for (size_t i = 1; i < route.size(); ++i) + { + if (route.ts[i] == 'C') + { + fprintf(fp, "%c %g,%g %g,%g %g,%g", route.ts[i], + route.ps[i].x, route.ps[i].y, + route.ps[i+1].x, route.ps[i+1].y, + route.ps[i+2].x, route.ps[i+2].y); + i += 2; + } + else + { + fprintf(fp, "%c %g,%g ", route.ts[i], + route.ps[i].x, route.ps[i].y); + } + } + fprintf(fp, "\" "); + if (connRef->src() && connRef->dst()) + { + fprintf(fp, "debug=\"src: %d dst: %d\" ", + connRef->src()->visDirections, + connRef->dst()->visDirections); + } + fprintf(fp, "style=\"fill: none; stroke: black; " + "stroke-width: 1px;\" />\n"); + } + + ++connRefIt; + } + fprintf(fp, "\n"); + + + fprintf(fp, "\n"); + connRefIt = connRefs.begin(); + while (connRefIt != connRefs.end()) + { + ConnRef *connRef = *connRefIt; + + PolyLine route = connRef->displayRoute(); + if (!route.empty()) + { + fprintf(fp, "id(), + route.ps[0].x, route.ps[0].y); + for (size_t i = 1; i < route.size(); ++i) + { + fprintf(fp, "L %g,%g ", route.ps[i].x, route.ps[i].y); + } + fprintf(fp, "\" "); + if (connRef->src() && connRef->dst()) + { + fprintf(fp, "debug=\"src: %d dst: %d\" ", + connRef->src()->visDirections, + connRef->dst()->visDirections); + } + fprintf(fp, "style=\"fill: none; stroke: black; " + "stroke-width: 1px;\" />\n"); + } + + ++connRefIt; + } + fprintf(fp, "\n"); + + + fprintf(fp, "\n"); + fclose(fp); } diff --git a/src/libavoid/router.h b/src/libavoid/router.h index 597f48c5e..03060b025 100644 --- a/src/libavoid/router.h +++ b/src/libavoid/router.h @@ -2,94 +2,325 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2009 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * Author(s): Michael Wybrow */ +//! @file router.h +//! @brief Contains the interface for the Router class. + #ifndef AVOID_ROUTER_H #define AVOID_ROUTER_H -//#define LINEDEBUG +#include +#include +#include #include "libavoid/shape.h" +#include "libavoid/viscluster.h" #include "libavoid/graph.h" #include "libavoid/timer.h" -#include -#include -#ifdef LINEDEBUG +#include "libavoid/connector.h" + +#if defined(LINEDEBUG) || defined(ASTAR_DEBUG) || defined(LIBAVOID_SDL) #include + #ifndef LIBAVOID_SDL + #define LIBAVOID_SDL + #endif #endif namespace Avoid { -class ConnRef; -typedef std::list ConnRefList; typedef std::list IntList; -class MoveInfo; -typedef std::list MoveInfoList; + +class ActionInfo; +typedef std::list ActionInfoList; +class ShapeRef; + +//! @brief Flags that can be passed to the router during initialisation +//! to specify options. +enum RouterFlag +{ + //! @brief This option specifies that the router should maintain the + //! structures necessary to allow poly-line connector routing. + PolyLineRouting = 1, + //! @brief This option specifies that the router should maintain the + //! structures necessary to allow orthogonal connector routing. + OrthogonalRouting = 2 +}; static const unsigned int runningTo = 1; static const unsigned int runningFrom = 2; static const unsigned int runningToAndFrom = runningTo | runningFrom; +//! @brief Types of penalty cases that can be used to improve the quality +//! of the connector routes produced. +enum PenaltyType +{ + //! @brief This penalty is applied for each segment in the connector + //! path beyond the first. This should always normally be set + //! when doing orthogonal routing to prevent step-like connector + //! paths. + segmentPenalty = 0, + //! @brief This penalty is applied in its full amount to tight acute + //! bends in the connector path. A smaller portion of the penalty + //! is applied for slight bends, i.e., where the bend is close to + //! 180 degrees. This is useful for polyline routing where there + //! is some evidence that tighter corners are worse for + //! readability, but that slight bends might not be so bad, + //! especially when smoothed by curves. + anglePenalty, + //! @brief This penalty is applied whenever a connector path crosses + //! another connector path. It takes shared paths into + //! consideration and the penalty is only applied if there + //! is an actual crossing. + //! @note This penalty is still experimental! It is not recommended + //! for normal use. + crossingPenalty, + //! @brief This penalty is applied whenever a connector path crosses + //! a cluster boundary. + //! @note This penalty is still experimental! It is not recommended + //! for normal use. + clusterCrossingPenalty, + //! @brief This penalty is applied whenever a connector path shares + //! some segments with an immovable portion of an existing + //! connector route (such as the first or last segment of a + //! connector). + //! @note This penalty is still experimental! It is not recommended + //! for normal use. + fixedSharedPathPenalty, + // Used for determining the size of the penalty array. + // This should always we the last value in the enum. + lastPenaltyMarker +}; + + +static const double noPenalty = 0; +static const double chooseSensiblePenalty = -1; + +//! @brief The Router class represents a libavoid router instance. +//! +//! Usually you would keep a separate Router instance for each diagram +//! or layout you have open in your application. +// class Router { public: - Router(); + //! @brief Constructor for router instance. + //! + //! @param[in] flags One or more Avoid::RouterFlag options to + //! control the behaviour of the router. + Router(const unsigned int flags); + + //! @brief Destructor for router instance. + //! + //! @note Destroying a router instance will delete all remaining + //! shapes and connectors, thereby invalidating any existing + //! pointers to them. + ~Router(); ShapeRefList shapeRefs; ConnRefList connRefs; + ClusterRefList clusterRefs; EdgeList visGraph; EdgeList invisGraph; + EdgeList visOrthogGraph; ContainsMap contains; VertInfList vertices; + ContainsMap enclosingClusters; bool PartialTime; bool SimpleRouting; - double segmt_penalty; - double angle_penalty; - double crossing_penalty; - + bool ClusteredRouting; - bool UseAStarSearch; + // Poly-line routing options: bool IgnoreRegions; - bool SelectiveReroute; - bool IncludeEndpoints; bool UseLeesAlgorithm; bool InvisibilityGrph; - bool ConsolidateMoves; + + // General routing options: + bool SelectiveReroute; + bool PartialFeedback; + bool RubberBandRouting; + // Instrumentation: Timer timers; int st_checked_edges; -#ifdef LINEDEBUG +#ifdef LIBAVOID_SDL SDL_Surface *avoid_screen; #endif + //! @brief Allows setting of the behaviour of the router in regard + //! to transactions. This controls whether transactions are + //! used to queue changes and process them effeciently at once + //! or they are instead processed immediately. + //! + //! It is more efficient to perform actions like shape movement, + //! addition or deletion as batch tasks, and reroute the necessary + //! connectors just once after these actions have been performed. + //! For this reason, libavoid allows you to group such actions into + //! "transactions" that are processed efficiently when the + //! processTransaction() method is called. + //! + //! By default, the router will process all actions as tranactions. + //! If transactionUse() is set to false, then all actions will get + //! processed immediately, and cause immediate routing callbacks to + //! all affected connectors after each action. + //! + //! @param[in] transactions A boolean value specifying whether to + //! use transactions. + //! + void setTransactionUse(const bool transactions); + + //! @brief Reports whether the router groups actions into transactions. + //! + //! @return A boolean value describing whether transactions are in use. + //! + //! @sa setTransactionUse + //! @sa processTransaction + //! + bool transactionUse(void) const; + + //! @brief Finishes the current transaction and processes all the + //! queued object changes efficiently. + //! + //! This method will efficiently process all moves, additions and + //! deletions that have occurred since processTransaction() was + //! last called. + //! + //! If transactionUse() is false, then all actions will have been + //! processed immediately and this method will do nothing. + //! + //! @return A boolean value describing whether there were any actions + //! to process. + //! + //! @sa setTransactionUse + //! + bool processTransaction(void); + + //! @brief Add a shape to the router scene. + //! + //! This shape will be considered to be an obstacle. Calling this + //! method will cause connectors intersecting the added shape to + //! be marked as needing to be rerouted. + //! + //! @param[in] shape Pointer reference to the shape being added. + //! void addShape(ShapeRef *shape); - void delShape(ShapeRef *shape); - void moveShape(ShapeRef *shape, Polygn *newPoly, + + //! @brief Remove a shape from the router scene. + //! + //! Connectors that could have a better (usually shorter) path after + //! the removal of this shape will be marked as needing to be rerouted. + //! + //! @param[in] shape Pointer reference to the shape being removed. + //! + void removeShape(ShapeRef *shape); + + //! @brief Move or resize an existing shape within the router scene. + //! + //! A new polygon for the shape can be given to effectively move or + //! resize the shape with the scene. Connectors that intersect the + //! new shape polygon, or that could have a better (usually shorter) + //! path after the change, will be marked as needing to be rerouted. + //! + //! @param[in] shape Pointer reference to the shape being + //! moved/resized. + //! @param[in] newPoly The new polygon boundary for the shape. + //! @param[in] first_move This option is used for some advanced + //! (currently undocumented) behaviour and + //! it should be ignored for the moment. + //! + void moveShape(ShapeRef *shape, const Polygon& newPoly, const bool first_move = false); - void processMoves(void); - + + //! @brief Move an existing shape within the router scene by a relative + //! distance. + //! + //! Connectors that intersect the shape's new position, or that could + //! have a better (usually shorter) path after the change, will be + //! marked as needing to be rerouted. + //! + //! @param[in] shape Pointer reference to the shape being moved. + //! @param[in] xDiff The distance to move the shape in the + //! x dimension. + //! @param[in] yDiff The distance to move the shape in the + //! y dimension. + //! + void moveShape(ShapeRef *shape, const double xDiff, const double yDiff); + + //! @brief Sets a spacing distance for overlapping orthogonal + //! connectors to be nudged apart. + //! + //! By default, this distance is set to a value of 4. + //! + //! This method does not re-trigger post-processing of connectors. + //! The new distance will be used the next time rerouting is performed. + //! + //! @param[in] dist The distance to be used for orthogonal nudging. + //! + void setOrthogonalNudgeDistance(const double dist); + + //! @brief Returns the spacing distance that overlapping orthogonal + //! connecotrs are nudged apart. + //! + //! @return The current spacing distance used for orthogonal nudging. + //! + double orthogonalNudgeDistance(void) const; + + //! @brief Sets or removes penalty values that are applied during + //! connector routing. + //! + //! By default, libavoid will produce shortest path routes between + //! the source and destination points for each connector. There are + //! several penalties that can be applied during this stage to + //! improve the aesthetics of the routes generated. These different + //! penalties are specified and explained by the PenaltyType enum. + //! + //! If a value of zero or Avoid::noPenalty is given then the penalty + //! for this case will be removed. If no penalty argument (or a + //! negative value) is specified when calling this method, then a + //! sensible penalty value will be automatically chosen. + //! + //! @param[in] penType The type of penalty, a PenaltyType. + //! @param[in] penVal The value to be applied for each occurance + //! of the penalty case. + //! + void setRoutingPenalty(const PenaltyType penType, + const double penVal = chooseSensiblePenalty); + + //! @brief Returns the current penalty value for a particular + //! routing penalty case. + //! + //! @param[in] penType The type of penalty, a PenaltyType. + //! @return The penalty value for the specified penalty case. + //! + double routingPenalty(const PenaltyType penType) const; + + void addCluster(ClusterRef *cluster); + void delCluster(ClusterRef *cluster); + void attachedConns(IntList &conns, const unsigned int shapeId, const unsigned int type); void attachedShapes(IntList &shapes, const unsigned int shapeId, @@ -98,15 +329,49 @@ class Router { void markConnectors(ShapeRef *shape); void generateContains(VertInf *pt); void printInfo(void); + void outputInstanceToSVG(std::string filename = std::string()); + unsigned int assignId(const unsigned int suggestedId); + void regenerateStaticBuiltGraph(void); + void destroyOrthogonalVisGraph(void); + void setStaticGraphInvalidated(const bool invalidated); + ConnType validConnType(const ConnType select = ConnType_None) const; + bool shapeInQueuedActionList(ShapeRef *shape) const; + double& penaltyRef(const PenaltyType penType); + bool existsOrthogonalPathOverlap(void); + bool existsOrthogonalTouchingCorners(void); + private: - void newBlockingShape(Polygn *poly, int pid); + friend class ConnRef; + + void modifyConnector(ConnRef *conn); + void modifyConnector(ConnRef *conn, unsigned int type, + const ConnEnd &connEnd); + void removeQueuedConnectorActions(ConnRef *conn); + void newBlockingShape(const Polygon& poly, int pid); void checkAllBlockedEdges(int pid); void checkAllMissingEdges(void); - void adjustContainsWithAdd(const Polygn& poly, const int p_shape); + void adjustContainsWithAdd(const Polygon& poly, const int p_shape); void adjustContainsWithDel(const int p_shape); - void callbackAllInvalidConnectors(void); + void adjustClustersWithAdd(const PolygonInterface& poly, + const int p_cluster); + void adjustClustersWithDel(const int p_cluster); + void rerouteAndCallbackConnectors(void); + bool idIsUnique(const unsigned int id) const; + void improveCrossings(void); + + ActionInfoList actionList; + unsigned int _largestAssignedId; + bool _consolidateActions; + double _orthogonalNudgeDistance; + double _routingPenalties[lastPenaltyMarker]; + + public: + // Overall modes: + bool _polyLineRouting; + bool _orthogonalRouting; - MoveInfoList moveList; + bool _staticGraphInvalidated; + bool _inCrossingPenaltyReroutingStage; }; } diff --git a/src/libavoid/shape.cpp b/src/libavoid/shape.cpp index c0ff2f6e8..ac3f0c193 100644 --- a/src/libavoid/shape.cpp +++ b/src/libavoid/shape.cpp @@ -2,53 +2,57 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2008 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * Author(s): Michael Wybrow */ -#include #include "libavoid/shape.h" #include "libavoid/graph.h" // For alertConns #include "libavoid/vertices.h" -#include "libavoid/polyutil.h" #include "libavoid/router.h" +#include "libavoid/debug.h" +#include "libavoid/assertions.h" namespace Avoid { -ShapeRef::ShapeRef(Router *router, unsigned int id, Polygn& ply) +ShapeRef::ShapeRef(Router *router, Polygon& ply, const unsigned int id) : _router(router) - , _id(id) - , _poly(copyPoly(ply)) + , _poly(ply) , _active(false) , _inMoveList(false) , _firstVert(NULL) , _lastVert(NULL) { + _id = router->assignId(id); + bool isShape = true; - VertID i = VertID(id, isShape, 0); + VertID i = VertID(_id, isShape, 0); + const bool addToRouterNow = false; VertInf *last = NULL; VertInf *node = NULL; - for (int pt_i = 0; pt_i < _poly.pn; pt_i++) + for (size_t pt_i = 0; pt_i < _poly.size(); ++pt_i) { - node = new VertInf(_router, i, _poly.ps[pt_i]); + node = new VertInf(_router, i, _poly.ps[pt_i], addToRouterNow); if (!_firstVert) { @@ -69,17 +73,22 @@ ShapeRef::ShapeRef(Router *router, unsigned int id, Polygn& ply) _lastVert->shNext = _firstVert; _firstVert->shPrev = _lastVert; - - makeActive(); } ShapeRef::~ShapeRef() { - assert(_firstVert != NULL); - - makeInactive(); + COLA_ASSERT(!_router->shapeInQueuedActionList(this)); + if (_active) + { + // Destroying a shape without calling removeShape(), so do it now. + _router->removeShape(this); + _router->processTransaction(); + } + + COLA_ASSERT(_firstVert != NULL); + VertInf *it = _firstVert; do { @@ -90,41 +99,37 @@ ShapeRef::~ShapeRef() } while (it != _firstVert); _firstVert = _lastVert = NULL; - - freePoly(_poly); } -void ShapeRef::setNewPoly(Polygn& poly) +void ShapeRef::setNewPoly(const Polygon& poly) { - assert(_firstVert != NULL); - assert(_poly.pn == poly.pn); + COLA_ASSERT(_firstVert != NULL); + COLA_ASSERT(_poly.size() == poly.size()); VertInf *curr = _firstVert; - for (int pt_i = 0; pt_i < _poly.pn; pt_i++) + for (size_t pt_i = 0; pt_i < _poly.size(); ++pt_i) { - assert(curr->visListSize == 0); - assert(curr->invisListSize == 0); + COLA_ASSERT(curr->visListSize == 0); + COLA_ASSERT(curr->invisListSize == 0); // Reset with the new polygon point. curr->Reset(poly.ps[pt_i]); curr->pathNext = NULL; - curr->pathDist = 0; curr = curr->shNext; } - assert(curr == _firstVert); + COLA_ASSERT(curr == _firstVert); - freePoly(_poly); - _poly = copyPoly(poly); + _poly = poly; } void ShapeRef::makeActive(void) { - assert(!_active); + COLA_ASSERT(!_active); - // Add to connRefs list. + // Add to shapeRefs list. _pos = _router->shapeRefs.insert(_router->shapeRefs.begin(), this); // Add points to vertex list. @@ -144,9 +149,9 @@ void ShapeRef::makeActive(void) void ShapeRef::makeInactive(void) { - assert(_active); + COLA_ASSERT(_active); - // Remove from connRefs list. + // Remove from shapeRefs list. _router->shapeRefs.erase(_pos); // Remove points from vertex list. @@ -162,7 +167,13 @@ void ShapeRef::makeInactive(void) _active = false; } - + + +bool ShapeRef::isActive(void) const +{ + return _active; +} + VertInf *ShapeRef::firstVert(void) { @@ -176,19 +187,19 @@ VertInf *ShapeRef::lastVert(void) } -unsigned int ShapeRef::id(void) +unsigned int ShapeRef::id(void) const { return _id; } -Polygn ShapeRef::poly(void) +const Polygon& ShapeRef::polygon(void) const { return _poly; } -Router *ShapeRef::router(void) +Router *ShapeRef::router(void) const { return _router; } @@ -196,13 +207,13 @@ Router *ShapeRef::router(void) void ShapeRef::boundingBox(BBox& bbox) { - assert(_poly.pn > 0); + COLA_ASSERT(!_poly.empty()); bbox.a = bbox.b = _poly.ps[0]; Point& a = bbox.a; Point& b = bbox.b; - for (int i = 1; i < _poly.pn; ++i) + for (size_t i = 1; i < _poly.size(); ++i) { const Point& p = _poly.ps[i]; @@ -223,8 +234,8 @@ void ShapeRef::removeFromGraph(void) // For each vertex. EdgeInfList& visList = tmp->visList; - EdgeInfList::iterator finish = visList.end(); - EdgeInfList::iterator edge; + EdgeInfList::const_iterator finish = visList.end(); + EdgeInfList::const_iterator edge; while ((edge = visList.begin()) != finish) { // Remove each visibility edge @@ -239,6 +250,15 @@ void ShapeRef::removeFromGraph(void) // Remove each invisibility edge delete (*edge); } + + EdgeInfList& orthogList = tmp->orthogVisList; + finish = orthogList.end(); + while ((edge = orthogList.begin()) != finish) + { + // Remove each orthogonal visibility edge + (*edge)->alertConns(); + delete (*edge); + } } } @@ -251,8 +271,8 @@ void ShapeRef::markForMove(void) } else { - fprintf(stderr, "WARNING: two moves queued for same shape prior to " - "rerouting.\n This is not safe.\n"); + db_printf("WARNING: two moves queued for same shape prior to rerouting." + "\n This is not safe.\n"); } } diff --git a/src/libavoid/shape.h b/src/libavoid/shape.h index b654c6eea..df4c98df1 100644 --- a/src/libavoid/shape.h +++ b/src/libavoid/shape.h @@ -2,24 +2,30 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2008 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * Author(s): Michael Wybrow */ +//! @file shape.h +//! @brief Contains the interface for the ShapeRef class. + + #ifndef AVOID_SHAPE_H #define AVOID_SHAPE_H @@ -35,21 +41,64 @@ class ShapeRef; typedef std::list ShapeRefList; +//! @brief The ShapeRef class represents a shape object. +//! +//! Shapes are obstacles that connectors must be routed around. They can be +//! placed into a Router scene and can be repositioned or resized (via +//! Router::moveShape()). +//! +//! Usually, it is expected that you would create a ShapeRef for each shape +//! in your diagram and keep that reference in your own shape class. +//! class ShapeRef { public: - ShapeRef(Router *router, unsigned int id, Polygn& poly); - virtual ~ShapeRef(); - void setNewPoly(Polygn& poly); + //! @brief Shape reference constructor. + //! + //! Creates a shape obect reference, but does not yet place it into the + //! Router scene. + //! + //! The poly argument will usually be the boundary of the shape in your + //! application with additional buffer of several pixels on each side. + //! Specifying such a buffer results in connectors leaving a small + //! amount of space around shapes, rather than touching them on the + //! corners or edges. + //! + //! If an ID is not specified, then one will be assigned to the shape. + //! If assigning an ID yourself, note that it should be a unique + //! positive integer. Also, IDs are given to all objects in a scene, + //! so the same ID cannot be given to a shape and a connector for + //! example. + //! + //! @param[in] router The router scene to place the shape into. + //! @param[in] poly A Polygon representing the boundary of the + //! shape. + //! @param[in] id A unique positive integer ID for the shape. + ShapeRef(Router *router, Polygon& poly, const unsigned int id = 0); + //! @brief Shape reference destructor. + //! + //! This will call Router::removeShape() for this shape, if this has + //! not already be called. + ~ShapeRef(); + + //! @brief Returns the ID of this shape. + //! @returns The ID of the shape. + unsigned int id(void) const; + //! @brief Returns a reference to the polygon boundary of this shape. + //! @returns A reference to the polygon boundary of the shape. + const Polygon& polygon(void) const; + //! @brief Returns a pointer to the router scene this shape is in. + //! @returns A pointer to the router scene for this shape. + Router *router(void) const; + + void setNewPoly(const Polygon& poly); VertInf *firstVert(void); VertInf *lastVert(void); - unsigned int id(void); - Polygn poly(void); - Router *router(void); void boundingBox(BBox& bbox); void makeActive(void); void makeInactive(void); + bool isActive(void) const; void removeFromGraph(void); void markForMove(void); @@ -60,7 +109,7 @@ class ShapeRef private: Router *_router; unsigned int _id; - Polygn _poly; + Polygon _poly; bool _active; bool _inMoveList; ShapeRefList::iterator _pos; diff --git a/src/libavoid/static.cpp b/src/libavoid/static.cpp deleted file mode 100644 index 740a4f9c0..000000000 --- a/src/libavoid/static.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * vim: ts=4 sw=4 et tw=0 wm=0 - * - * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -*/ - -#include -#include -#include "libavoid/vertices.h" -#include "libavoid/connector.h" -#include "libavoid/graph.h" -#include "libavoid/static.h" -#include "libavoid/shape.h" -#include "libavoid/visibility.h" -#include "libavoid/debug.h" -#include "libavoid/router.h" - -namespace Avoid { - -static void computeCompleteVis(Router *router); - - -// This should only be used for the static algorithm. -// -// XXX: If to set up the vis graph for incremental it would need -// the shapeRef pointers in obs. -// -void CreateVisGraph(Router *router, Polygn **obs, int n_obs) -{ - for (int poly_i = 0; poly_i < n_obs; poly_i++) - { - unsigned int id = obs[poly_i]->id; - - new ShapeRef(router, id, *(obs[poly_i])); - } - computeCompleteVis(router); -} - - -static void computeCompleteVis(Router *router) -{ - VertInf *beginVert = router->vertices.shapesBegin(); - VertInf *endVert = router->vertices.end(); - for (VertInf *i = beginVert; i != endVert; i = i->lstNext) - { - db_printf("-- CONSIDERING --\n"); - i->id.db_print(); - - for (VertInf *j = i->lstPrev ; j != NULL; j = j->lstPrev) - { - bool knownNew = true; - EdgeInf::checkEdgeVisibility(i, j, knownNew); - } - } -} - - -void DestroyVisGraph(Router *router) -{ - ShapeRefList::iterator sFinish = router->shapeRefs.end(); - ShapeRefList::iterator sCurr; - - while ((sCurr = router->shapeRefs.begin()) != sFinish) - { - ShapeRef *shape = (*sCurr); - - shape->removeFromGraph(); - delete shape; - } - - ConnRefList::iterator cFinish = router->connRefs.end(); - ConnRefList::iterator cCurr; - - while ((cCurr = router->connRefs.begin())!= cFinish) - { - ConnRef *conn = (*cCurr); - - conn->removeFromGraph(); - conn->unInitialise(); - } - // Clear contains info. - router->contains.clear(); - - assert(router->vertices.connsBegin() == NULL); -} - - -} - diff --git a/src/libavoid/static.h b/src/libavoid/static.h deleted file mode 100644 index 18e9ac278..000000000 --- a/src/libavoid/static.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * vim: ts=4 sw=4 et tw=0 wm=0 - * - * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -*/ - - -#ifndef AVOID_STATIC_H -#define AVOID_STATIC_H - -#include "libavoid/geomtypes.h" - - -namespace Avoid { - -class Router; - - -extern void CreateVisGraph(Router *router, Polygn **obstacles, - int n_obstacles); -extern void DestroyVisGraph(Router *router); - -} - - -#endif diff --git a/src/libavoid/timer.cpp b/src/libavoid/timer.cpp index e4349bea9..f8600acbe 100644 --- a/src/libavoid/timer.cpp +++ b/src/libavoid/timer.cpp @@ -2,31 +2,34 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2008 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * Author(s): Michael Wybrow */ + #include #include -#include -using std::abort; #include #include "libavoid/timer.h" +#include "libavoid/debug.h" +#include "libavoid/assertions.h" namespace Avoid { @@ -52,9 +55,9 @@ void Timer::Reset(void) } -void Timer::Register(const int t, const bool start) +void Timer::Register(const TimerIndex t, const bool start) { - assert(t != tmNon); + COLA_ASSERT(t != tmNon); if (type == tmNon) { @@ -73,11 +76,7 @@ void Timer::Register(const int t, const bool start) void Timer::Start(void) { - if (running) - { - fprintf(stderr, "ERROR: Timer already running in Timer::Start()\n"); - abort(); - } + COLA_ASSERT(!running); cStart[type] = clock(); // CPU time running = true; } @@ -85,11 +84,7 @@ void Timer::Start(void) void Timer::Stop(void) { - if (!running) - { - fprintf(stderr, "ERROR: Timer not running in Timer::Stop()\n"); - abort(); - } + COLA_ASSERT(running); clock_t cStop = clock(); // CPU time running = false; @@ -106,11 +101,7 @@ void Timer::Stop(void) cDiff = cStop - cStart[type]; } - if (cDiff > LONG_MAX) - { - fprintf(stderr, "Error: cDiff overflow in Timer:Stop()\n"); - abort(); - } + COLA_ASSERT(cDiff < LONG_MAX); if (type == tmPth) { @@ -136,11 +127,11 @@ void Timer::Stop(void) } -void Timer::PrintAll(void) +void Timer::PrintAll(FILE *fp) { - for (int i = 0; i < tmCount; i++) + for (unsigned int i = 0; i < tmCount; i++) { - Print(i); + Print((TimerIndex) i, fp); } } @@ -148,14 +139,14 @@ void Timer::PrintAll(void) #define toMsec(tot) ((bigclock_t) ((tot) / (((double) CLOCKS_PER_SEC) / 1000))) #define toAvg(tot, cnt) ((((cnt) > 0) ? ((long double) (tot)) / (cnt) : 0)) -void Timer::Print(const int t) +void Timer::Print(const TimerIndex t, FILE *fp) { bigclock_t avg = toMsec(toAvg(cTotal[t], cTally[t])); bigclock_t pind = toMsec(toAvg(cPath[t], cPathTally[t])); bigclock_t pavg = toMsec(toAvg(cPath[t], cTally[t])); double max = toMsec(cMax[t]); double pmax = toMsec(cPathMax[t]); - printf("\t%lld %d %lld %.0f %lld %d %lld %.0f %lld\n", + fprintf(fp, "\t%lld %d %lld %.0f %lld %d %lld %.0f %lld\n", cTotal[t], cTally[t], avg, max, cPath[t], cPathTally[t], pavg, pmax, pind); } diff --git a/src/libavoid/timer.h b/src/libavoid/timer.h index a7e6081fa..9cab6d7ff 100644 --- a/src/libavoid/timer.h +++ b/src/libavoid/timer.h @@ -2,24 +2,27 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2008 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * Author(s): Michael Wybrow */ + #ifndef PROFILE_H #define PROFILE_H @@ -46,14 +49,20 @@ namespace Avoid { typedef unsigned long long int bigclock_t; -static const int tmCount = 5; - -static const int tmNon = -1; -static const int tmAdd = 0; -static const int tmDel = 1; -static const int tmMov = 2; -static const int tmPth = 3; -static const int tmSev = 4; +enum TimerIndex +{ + tmNon = 0, + tmAdd, + tmDel, + tmMov, + tmPth, + tmSev, + tmOrthogGraph, + tmOrthogRoute, + tmOrthogCentre, + tmOrthogNudge, + tmCount +}; static const bool timerStart = true; @@ -64,12 +73,12 @@ class Timer { public: Timer(); - void Register(const int t, const bool start = timerDelay); + void Register(const TimerIndex t, const bool start = timerDelay); void Start(void); void Stop(void); void Reset(void); - void Print(const int t); - void PrintAll(void); + void Print(TimerIndex, FILE *fp); + void PrintAll(FILE *fp); private: clock_t cStart[tmCount]; @@ -82,7 +91,7 @@ class Timer bool running; long count; - int type, lasttype; + TimerIndex type, lasttype; }; diff --git a/src/libavoid/vertices.cpp b/src/libavoid/vertices.cpp index c2be955ac..85226498a 100644 --- a/src/libavoid/vertices.cpp +++ b/src/libavoid/vertices.cpp @@ -2,33 +2,36 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2009 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * Author(s): Michael Wybrow */ + +#include +#include + #include "libavoid/vertices.h" #include "libavoid/geometry.h" #include "libavoid/graph.h" // For alertConns #include "libavoid/debug.h" #include "libavoid/router.h" - -#include -#include -#include +#include "libavoid/assertions.h" using std::ostream; @@ -76,7 +79,8 @@ bool VertID::operator==(const VertID& rhs) const { return false; } - assert(isShape == rhs.isShape); + // XXX RubberBand search breaks this: + // COLA_ASSERT(isShape == rhs.isShape); return true; } @@ -87,7 +91,7 @@ bool VertID::operator!=(const VertID& rhs) const { return true; } - assert(isShape == rhs.isShape); + COLA_ASSERT(isShape == rhs.isShape); return false; } @@ -133,8 +137,8 @@ void VertID::db_print(void) const } -const int VertID::src = 1; -const int VertID::tar = 2; +const unsigned short VertID::src = 1; +const unsigned short VertID::tar = 2; ostream& operator<<(ostream& os, const VertID& vID) @@ -144,25 +148,57 @@ ostream& operator<<(ostream& os, const VertID& vID) -VertInf::VertInf(Router *router, const VertID& vid, const Point& vpoint) - : _router(router) - , id(vid) - , point(vpoint) - , lstPrev(NULL) - , lstNext(NULL) - , shPrev(NULL) - , shNext(NULL) - , visListSize(0) - , invisListSize(0) - , pathNext(NULL) - , pathDist(0) +VertInf::VertInf(Router *router, const VertID& vid, const Point& vpoint, + const bool addToRouter) + : _router(router), + id(vid), + point(vpoint), + lstPrev(NULL), + lstNext(NULL), + shPrev(NULL), + shNext(NULL), + visListSize(0), + orthogVisListSize(0), + invisListSize(0), + pathNext(NULL), + visDirections(ConnDirNone) +{ + point.id = vid.objID; + point.vn = vid.vn; + + if (addToRouter) + { + _router->vertices.addVertex(this); + } +} + + +VertInf::~VertInf() +{ +} + + +void VertInf::Reset(const VertID& vid, const Point& vpoint) { + id = vid; + point = vpoint; + point.id = id.objID; + point.vn = id.vn; } void VertInf::Reset(const Point& vpoint) { point = vpoint; + point.id = id.objID; + point.vn = id.vn; +} + + +// Returns true if this vertex is not involved in any (in)visibility graphs. +bool VertInf::orphaned(void) +{ + return (visList.empty() && invisList.empty() && orthogVisList.empty()); } @@ -170,15 +206,12 @@ void VertInf::removeFromGraph(const bool isConnVert) { if (isConnVert) { - assert(!(id.isShape)); + COLA_ASSERT(!(id.isShape)); } - VertInf *tmp = this; - // For each vertex. - EdgeInfList& visList = tmp->visList; - EdgeInfList::iterator finish = visList.end(); - EdgeInfList::iterator edge; + EdgeInfList::const_iterator finish = visList.end(); + EdgeInfList::const_iterator edge; while ((edge = visList.begin()) != finish) { // Remove each visibility edge @@ -186,7 +219,14 @@ void VertInf::removeFromGraph(const bool isConnVert) delete (*edge); } - EdgeInfList& invisList = tmp->invisList; + finish = orthogVisList.end(); + while ((edge = orthogVisList.begin()) != finish) + { + // Remove each orthogonal visibility edge. + (*edge)->alertConns(); + delete (*edge); + } + finish = invisList.end(); while ((edge = invisList.begin()) != finish) { @@ -208,7 +248,7 @@ bool directVis(VertInf *src, VertInf *dst) // We better be part of the same instance of libavoid. Router *router = src->_router; - assert(router == dst->_router); + COLA_ASSERT(router == dst->_router); ContainsMap& contains = router->contains; if (!(pID.isShape)) @@ -239,40 +279,40 @@ bool directVis(VertInf *src, VertInf *dst) VertInfList::VertInfList() - : _firstShapeVert(NULL) - , _firstConnVert(NULL) - , _lastShapeVert(NULL) - , _lastConnVert(NULL) - , _shapeVertices(0) - , _connVertices(0) + : _firstShapeVert(NULL), + _firstConnVert(NULL), + _lastShapeVert(NULL), + _lastConnVert(NULL), + _shapeVertices(0), + _connVertices(0) { } #define checkVertInfListConditions() \ do { \ - assert((!_firstConnVert && (_connVertices == 0)) || \ + COLA_ASSERT((!_firstConnVert && (_connVertices == 0)) || \ ((_firstConnVert->lstPrev == NULL) && (_connVertices > 0))); \ - assert((!_firstShapeVert && (_shapeVertices == 0)) || \ + COLA_ASSERT((!_firstShapeVert && (_shapeVertices == 0)) || \ ((_firstShapeVert->lstPrev == NULL) && (_shapeVertices > 0))); \ - assert(!_lastShapeVert || (_lastShapeVert->lstNext == NULL)); \ - assert(!_lastConnVert || (_lastConnVert->lstNext == _firstShapeVert)); \ - assert((!_firstConnVert && !_lastConnVert) || \ + COLA_ASSERT(!_lastShapeVert || (_lastShapeVert->lstNext == NULL)); \ + COLA_ASSERT(!_lastConnVert || (_lastConnVert->lstNext == _firstShapeVert)); \ + COLA_ASSERT((!_firstConnVert && !_lastConnVert) || \ (_firstConnVert && _lastConnVert) ); \ - assert((!_firstShapeVert && !_lastShapeVert) || \ + COLA_ASSERT((!_firstShapeVert && !_lastShapeVert) || \ (_firstShapeVert && _lastShapeVert) ); \ - assert(!_firstShapeVert || _firstShapeVert->id.isShape); \ - assert(!_lastShapeVert || _lastShapeVert->id.isShape); \ - assert(!_firstConnVert || !(_firstConnVert->id.isShape)); \ - assert(!_lastConnVert || !(_lastConnVert->id.isShape)); \ + COLA_ASSERT(!_firstShapeVert || _firstShapeVert->id.isShape); \ + COLA_ASSERT(!_lastShapeVert || _lastShapeVert->id.isShape); \ + COLA_ASSERT(!_firstConnVert || !(_firstConnVert->id.isShape)); \ + COLA_ASSERT(!_lastConnVert || !(_lastConnVert->id.isShape)); \ } while(0) void VertInfList::addVertex(VertInf *vert) { checkVertInfListConditions(); - assert(vert->lstPrev == NULL); - assert(vert->lstNext == NULL); + COLA_ASSERT(vert->lstPrev == NULL); + COLA_ASSERT(vert->lstNext == NULL); if (!(vert->id.isShape)) { @@ -329,10 +369,18 @@ void VertInfList::addVertex(VertInf *vert) } -void VertInfList::removeVertex(VertInf *vert) +// Removes a vertex from the list and returns a pointer to the vertex +// following the removed one. +VertInf *VertInfList::removeVertex(VertInf *vert) { + if (vert == NULL) + { + return NULL; + } // Conditions for correct data structure checkVertInfListConditions(); + + VertInf *following = vert->lstNext; if (!(vert->id.isShape)) { @@ -421,6 +469,50 @@ void VertInfList::removeVertex(VertInf *vert) vert->lstNext = NULL; checkVertInfListConditions(); + + return following; +} + + +VertInf *VertInfList::getVertexByID(const VertID& id) +{ + VertID searchID = id; + if (searchID.vn == kUnassignedVertexNumber) + { + unsigned int topbit = ((unsigned int) 1) << 31; + if (searchID.objID & topbit) + { + searchID.objID = searchID.objID & ~topbit; + searchID.vn = VertID::src; + } + else + { + searchID.vn = VertID::tar; + } + } + VertInf *last = end(); + for (VertInf *curr = connsBegin(); curr != last; curr = curr->lstNext) + { + if (curr->id == searchID) + { + return curr; + } + } + return NULL; +} + + +VertInf *VertInfList::getVertexByPos(const Point& p) +{ + VertInf *last = end(); + for (VertInf *curr = shapesBegin(); curr != last; curr = curr->lstNext) + { + if (curr->point == p) + { + return curr; + } + } + return NULL; } @@ -447,6 +539,18 @@ VertInf *VertInfList::end(void) } +unsigned int VertInfList::connsSize(void) const +{ + return _connVertices; +} + + +unsigned int VertInfList::shapesSize(void) const +{ + return _shapeVertices; +} + + } diff --git a/src/libavoid/vertices.h b/src/libavoid/vertices.h index 1b0dcf3aa..b07c87f95 100644 --- a/src/libavoid/vertices.h +++ b/src/libavoid/vertices.h @@ -2,24 +2,27 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2009 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * Author(s): Michael Wybrow */ + #ifndef AVOID_VERTICES_H #define AVOID_VERTICES_H @@ -28,6 +31,7 @@ #include #include #include + #include "libavoid/geomtypes.h" namespace Avoid { @@ -37,16 +41,18 @@ class Router; typedef std::list EdgeInfList; +typedef unsigned int ConnDirFlags; + class VertID { public: unsigned int objID; bool isShape; - int vn; + unsigned short vn; - static const int src; - static const int tar; + static const unsigned short src; + static const unsigned short tar; VertID(); VertID(unsigned int id, bool s, int n); @@ -64,12 +70,23 @@ class VertID }; +// An ID given to all dummy vertices inserted to allow creation of the +// orthogonal visibility graph since the vertices in the orthogonal graph +// mostly do not correspond to shape corners or connector endpoints. +// +static const VertID dummyOrthogID(0, true, 0); + + class VertInf { public: - VertInf(Router *router, const VertID& vid, const Point& vpoint); + VertInf(Router *router, const VertID& vid, const Point& vpoint, + const bool addToRouter = true); + ~VertInf(); + void Reset(const VertID& vid, const Point& vpoint); void Reset(const Point& vpoint); void removeFromGraph(const bool isConnVert = true); + bool orphaned(void); Router *_router; VertID id; @@ -80,28 +97,40 @@ class VertInf VertInf *shNext; EdgeInfList visList; unsigned int visListSize; + EdgeInfList orthogVisList; + unsigned int orthogVisListSize; EdgeInfList invisList; unsigned int invisListSize; VertInf *pathNext; - double pathDist; + ConnDirFlags visDirections; }; bool directVis(VertInf *src, VertInf *dst); +// A linked list of all the vertices in the router instance. All the +// connector endpoints are listed first, then all the shape vertices. +// Dunnny vertices inserted for orthogonal routing are classed as shape +// vertices but have VertID(0, 0). +// class VertInfList { public: VertInfList(); void addVertex(VertInf *vert); - void removeVertex(VertInf *vert); + VertInf *removeVertex(VertInf *vert); + VertInf *getVertexByID(const VertID& id); + VertInf *getVertexByPos(const Point& p); VertInf *shapesBegin(void); VertInf *connsBegin(void); VertInf *end(void); - void stats(void) + unsigned int connsSize(void) const; + unsigned int shapesSize(void) const; + void stats(FILE *fp = stderr) { - printf("Conns %d, shapes %d\n", _connVertices, _shapeVertices); + fprintf(fp, "Conns %d, shapes %d\n", _connVertices, + _shapeVertices); } private: VertInf *_firstShapeVert; diff --git a/src/libavoid/visibility.cpp b/src/libavoid/visibility.cpp index d2b057643..089911f35 100644 --- a/src/libavoid/visibility.cpp +++ b/src/libavoid/visibility.cpp @@ -2,26 +2,39 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2009 Monash University + * + * -------------------------------------------------------------------- + * The Visibility Sweep technique is based upon the method described + * in Section 5.2 of: + * Lee, D.-T. (1978). Proximity and reachability in the plane., + * PhD thesis, Department of Electrical Engineering, + * University of Illinois, Urbana, IL. + * -------------------------------------------------------------------- * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * Author(s): Michael Wybrow */ + #include #include +#define _USE_MATH_DEFINES +#include #include "libavoid/shape.h" #include "libavoid/debug.h" @@ -30,8 +43,7 @@ #include "libavoid/graph.h" #include "libavoid/geometry.h" #include "libavoid/router.h" - -#include +#include "libavoid/assertions.h" #ifdef LINEDEBUG #include "SDL_gfxPrimitives.h" @@ -53,16 +65,7 @@ void shapeVis(ShapeRef *shape) VertInf *shapeBegin = shape->firstVert(); VertInf *shapeEnd = shape->lastVert()->lstNext; - VertInf *pointsBegin = NULL; - if (router->IncludeEndpoints) - { - pointsBegin = router->vertices.connsBegin(); - } - else - { - pointsBegin = router->vertices.shapesBegin(); - } - + VertInf *pointsBegin = router->vertices.connsBegin(); for (VertInf *curr = shapeBegin; curr != shapeEnd; curr = curr->lstNext) { bool knownNew = true; @@ -73,6 +76,11 @@ void shapeVis(ShapeRef *shape) db_printf("\tFirst Half:\n"); for (VertInf *j = pointsBegin ; j != curr; j = j->lstNext) { + if (j->id == dummyOrthogID) + { + // Don't include orthogonal dummy vertices. + continue; + } EdgeInf::checkEdgeVisibility(curr, j, knownNew); } @@ -80,6 +88,11 @@ void shapeVis(ShapeRef *shape) VertInf *pointsEnd = router->vertices.end(); for (VertInf *k = shapeEnd; k != pointsEnd; k = k->lstNext) { + if (k->id == dummyOrthogID) + { + // Don't include orthogonal dummy vertices. + continue; + } EdgeInf::checkEdgeVisibility(curr, k, knownNew); } } @@ -113,7 +126,7 @@ void vertexVisibility(VertInf *point, VertInf *partner, bool knownNew, const VertID& pID = point->id; // Make sure we're only doing ptVis for endpoints. - assert(!(pID.isShape)); + COLA_ASSERT(!(pID.isShape)); if ( !(router->InvisibilityGrph) ) { @@ -135,9 +148,14 @@ void vertexVisibility(VertInf *point, VertInf *partner, bool knownNew, for (VertInf *k = router->vertices.shapesBegin(); k != shapesEnd; k = k->lstNext) { + if (k->id == dummyOrthogID) + { + // Don't include orthogonal dummy vertices. + continue; + } EdgeInf::checkEdgeVisibility(point, k, knownNew); } - if (router->IncludeEndpoints && partner) + if (partner) { EdgeInf::checkEdgeVisibility(point, partner, knownNew); } @@ -152,7 +170,6 @@ void vertexVisibility(VertInf *point, VertInf *partner, bool knownNew, static VertInf *centerInf; static Point centerPoint; static VertID centerID; -static double centerAngle; class PointPair @@ -165,19 +182,44 @@ class PointPair double y = vInf->point.y - centerPoint.y; angle = pos_to_angle(x, y); + distance = euclideanDist(centerPoint, vInf->point); } - bool operator==(const PointPair& rhs) const + bool operator<(const PointPair& rhs) const { - if (vInf->id == rhs.vInf->id) + // Firstly order by angle. + if (angle == rhs.angle) { - return true; + // If the points are collinear, then order them in increasing + // distance from the point we are sweeping around. + if (distance == rhs.distance) + { + // XXX: Add this assertion back if we require that + // connector endpoints have unique IDs. For the + // moment it is okay for them to have the same ID. + //COLA_ASSERT(vInf->id != rhs.vInf->id); + + // If comparing two points at the same physical + // position, then order them by their VertIDs. + return vInf->id < rhs.vInf->id; + } + return distance < rhs.distance; } - return false; + return angle < rhs.angle; } static double pos_to_angle(double x, double y) { + if (y == 0) + { + return ((x < 0) ? 180 : 0); + } + else if (x == 0) + { + return ((y < 0) ? 270 : 90); + } + double ang = atan(y / x); ang = (ang * 180) / M_PI; + if (x < 0) { ang += 180; @@ -186,36 +228,48 @@ class PointPair { ang += 360; } + COLA_ASSERT(ang >= 0); + COLA_ASSERT(ang <= 360); return ang; } VertInf *vInf; double angle; + double distance; }; -typedef std::list VertList; +typedef std::set VertSet; class EdgePair { public: - EdgePair(VertInf *v1, VertInf *v2, double d, double a) - : vInf1(v1), vInf2(v2), initdist(d), initangle(a) + EdgePair() : + vInf1(NULL), vInf2(NULL), dist1(0.0), dist2(0.0), angle(0.0), + angleDist(0.0) + { + // The default constuctor should never be called. + // This is defined to appease the MSVC compiler. + COLA_ASSERT(false); + } + EdgePair(const PointPair& p1, VertInf *v) : + vInf1(p1.vInf), + vInf2(v), + dist1(p1.distance), + dist2(euclideanDist(vInf2->point, centerPoint)), + angle(p1.angle), + angleDist(p1.distance) { - currdist = initdist; - currangle = initangle; } bool operator<(const EdgePair& rhs) const { - if (initdist == rhs.initdist) + COLA_ASSERT(angle == rhs.angle); + if (angleDist == rhs.angleDist) { - // TODO: This is a bit of a hack, should be - // set by the call to the constructor. - return dist(centerPoint, vInf2->point) < - dist(centerPoint, rhs.vInf2->point); + return (dist2 < rhs.dist2); } - return (initdist < rhs.initdist); + return (angleDist < rhs.angleDist); } bool operator==(const EdgePair& rhs) const { @@ -239,37 +293,53 @@ class EdgePair } return true; } - void SetObsAng(double a) + void setNegativeAngle(void) { - obsAngle = fmod(initangle - (a - 180), 360); + angle = -1.0; + } + double setCurrAngle(const PointPair& p) + { + if (p.vInf->point == vInf1->point) + { + angleDist = dist1; + angle = p.angle; + } + else if (p.vInf->point == vInf2->point) + { + angleDist = dist2; + angle = p.angle; + } + else if (p.angle != angle) + { + COLA_ASSERT(p.angle > angle); + angle = p.angle; + Point pp; + int result = rayIntersectPoint(vInf1->point, vInf2->point, + centerPoint, p.vInf->point, &(pp.x), &(pp.y)); + if (result != DO_INTERSECT) + { + // This can happen with points that appear to have the + // same angle but at are at slightly different positions + angleDist = std::min(dist1, dist2); + } + else + { + angleDist = euclideanDist(pp, centerPoint); + } + } - //db_printf("SetObsAng: %.2f (from init %.2f, a %.2f)\n", - // obsAngle, initangle, a); + return angleDist; } VertInf *vInf1; VertInf *vInf2; - double initdist; - double initangle; - double currdist; - double currangle; - double obsAngle; + double dist1; + double dist2; + double angle; + double angleDist; }; -typedef std::set EdgeSet; - - -static bool ppCompare(PointPair& pp1, PointPair& pp2) -{ - if (pp1.angle == pp2.angle) - { - // If the points are colinear, then order them in increasing - // distance from the point we are sweeping around. - return dist(centerPoint, pp1.vInf->point) < - dist(centerPoint, pp2.vInf->point); - } - return pp1.angle < pp2.angle; -} +typedef std::list SweepEdgeList; #define AHEAD 1 @@ -278,11 +348,11 @@ static bool ppCompare(PointPair& pp1, PointPair& pp2) class isBoundingShape { public: - // constructor remembers the value provided - isBoundingShape(ShapeSet& set) - : ss(set) + // Class instance remembers the ShapeSet. + isBoundingShape(ShapeSet& set) : + ss(set) { } - // the following is an overloading of the function call operator + // The following is an overloading of the function call operator. bool operator () (const PointPair& pp) { if (pp.vInf->id.isShape && @@ -293,58 +363,111 @@ class isBoundingShape return false; } private: + // MSVC wants to generate the assignment operator and the default + // constructor, but fails. Therefore we declare them private and + // don't implement them. + isBoundingShape & operator=(isBoundingShape const &); + isBoundingShape(); + ShapeSet& ss; }; -static bool sweepVisible(EdgeSet& T, VertInf *currInf, VertInf *lastInf, - bool lastVisible, double lastAngle, int *blocker) +static bool sweepVisible(SweepEdgeList& T, const PointPair& point, + std::set& onBorderIDs, int *blocker) { + if (T.empty()) + { + // No blocking edges. + return true; + } - if (!lastInf || (lastAngle != centerAngle)) + Router *router = point.vInf->_router; + bool visible = true; + + SweepEdgeList::const_iterator closestIt = T.begin(); + SweepEdgeList::const_iterator end = T.end(); + while (closestIt != end) { - // Nothing before it on the current ray - EdgeSet::iterator closestIt = T.begin(); - if (closestIt != T.end()) + if ((point.vInf->point == closestIt->vInf1->point) || + (point.vInf->point == closestIt->vInf2->point)) { + // If the ray intersects just the endpoint of a + // blocking edge then ignore that edge. + ++closestIt; + continue; + } + break; + } + if (closestIt == end) + { + return true; + } - Point &e1 = (*closestIt).vInf1->point; - Point &e2 = (*closestIt).vInf2->point; - - if (segmentIntersect(centerInf->point, currInf->point, e1, e2)) + if (! point.vInf->id.isShape ) + { + // It's a connector endpoint, so we have to ignore + // edges of containing shapes for determining visibility. + ShapeSet& rss = router->contains[point.vInf->id]; + while (closestIt != end) + { + if (rss.find(closestIt->vInf1->id.objID) == rss.end()) { - *blocker = (*closestIt).vInf1->id.objID; - return false; + // This is not a containing edge so do the normal + // test and then stop. + if (point.distance > closestIt->angleDist) + { + visible = false; + } + else if ((point.distance == closestIt->angleDist) && + onBorderIDs.find(closestIt->vInf1->id.objID) != + onBorderIDs.end()) + { + // Touching, but centerPoint is on another edge of + // shape shape, so count as blocking. + visible = false; + } + break; } + // This was a containing edge, so consider the next along. + ++closestIt; } } else { - // There was another point before this on the ray (lastInf) - if (!lastVisible) + // Just test to see if this point is closer than the closest + // edge blocking this ray. + if (point.distance > closestIt->angleDist) { - *blocker = -1; - return false; + visible = false; } - else + else if ((point.distance == closestIt->angleDist) && + onBorderIDs.find(closestIt->vInf1->id.objID) != + onBorderIDs.end()) { - // Check if there is an edge in T that blocks the ray - // between lastInf and currInf. - EdgeSet::iterator tfin = T.end(); - for (EdgeSet::iterator l = T.begin(); l != tfin; ++l) - { - Point &e1 = (*l).vInf1->point; - Point &e2 = (*l).vInf2->point; + // Touching, but centerPoint is on another edge of + // shape shape, so count as blocking. + visible = false; + } + } - if (segmentIntersect(lastInf->point, currInf->point, e1, e2)) - { - *blocker = (*l).vInf1->id.objID; - return false; - } - } + if (!visible) + { + *blocker = (*closestIt).vInf1->id.objID; +#ifdef LINEDEBUG + Point &e1 = (*closestIt).vInf1->point; + Point &e2 = (*closestIt).vInf2->point; + + if (router->avoid_screen) + { + int canx = 151; + int cany = 55; + lineRGBA(router->avoid_screen, e1.x + canx, e1.y + cany, + e2.x + canx, e2.y + cany, 0, 0, 225, 255); } +#endif } - return true; + return visible; } @@ -358,120 +481,128 @@ void vertexSweep(VertInf *vert) centerID = pID; centerPoint = pPoint; Point centerPt = pPoint; - centerAngle = -1; // List of shape (and maybe endpt) vertices, except p // Sort list, around - VertList v; + VertSet v; // Initialise the vertex list + ShapeSet& ss = router->contains[centerID]; VertInf *beginVert = router->vertices.connsBegin(); VertInf *endVert = router->vertices.end(); for (VertInf *inf = beginVert; inf != endVert; inf = inf->lstNext) { - if (inf->id == centerID) + if (inf == centerInf) + { + // Don't include the center point itself. + continue; + } + else if (inf->id == dummyOrthogID) { - // Don't include the center point + // Don't include orthogonal dummy vertices. + continue; + } + + if (!(centerID.isShape) && (ss.find(inf->id.objID) != ss.end())) + { + // Don't include edge points of containing shapes. + unsigned int shapeID = inf->id.objID; + db_printf("Center is inside shape %u so ignore shape edges.\n", + shapeID); continue; } if (inf->id.isShape) { - // Add shape vertex - v.push_back(inf); + // Add shape vertex. + v.insert(inf); } else { - if (router->IncludeEndpoints) + // Add connector endpoint. + if (centerID.isShape) { - if (centerID.isShape) - { - // Add endpoint vertex - v.push_back(inf); - } - else + // Center is a shape vertex, so add all endpoint vertices. + v.insert(inf); + } + else + { + // Center is an endpoint, so only include the other + // endpoint from the matching connector. + VertID partnerID = VertID(centerID.objID, false, + (centerID.vn == 1) ? 2 : 1); + if (inf->id == partnerID) { - // Center is an endpoint, so only include the other - // endpoint from the matching connector. - VertID partnerID = VertID(centerID.objID, false, - (centerID.vn == 1) ? 2 : 1); - if (inf->id == partnerID) - { - v.push_back(inf); - } + v.insert(inf); } } } } - // TODO: This should be done with a sorted data type and insertion sort. - v.sort(ppCompare); - - EdgeSet e; - ShapeSet& ss = router->contains[centerID]; + std::set onBorderIDs; - // And edge to T that intersect the initial ray. - VertInf *last = router->vertices.end(); - for (VertInf *k = router->vertices.shapesBegin(); k != last; ) + // Add edges to T that intersect the initial ray. + SweepEdgeList e; + VertSet::const_iterator vbegin = v.begin(); + VertSet::const_iterator vend = v.end(); + for (VertSet::const_iterator t = vbegin; t != vend; ++t) { - VertID kID = k->id; - if (!(centerID.isShape) && (ss.find(kID.objID) != ss.end())) - { - unsigned int shapeID = kID.objID; - db_printf("Center is inside shape %u so ignore shape edges.\n", - shapeID); - // One of the endpoints is inside this shape so ignore it. - while ((k != last) && (k->id.objID == shapeID)) - { - // And skip the other vertices from this shape. - k = k->lstNext; - } - continue; - } + VertInf *k = t->vInf; - VertInf *kPrev = k->shPrev; - if ((centerInf == k) || (centerInf == kPrev)) - { - k = k->lstNext; - continue; - } + COLA_ASSERT(centerInf != k); + COLA_ASSERT(centerID.isShape || (ss.find(k->id.objID) == ss.end())); Point xaxis(DBL_MAX, centerInf->point.y); - if (segmentIntersect(centerInf->point, xaxis, kPrev->point, k->point)) + VertInf *kPrev = k->shPrev; + VertInf *kNext = k->shNext; + if (kPrev && (kPrev != centerInf) && + (vecDir(centerInf->point, xaxis, kPrev->point) == AHEAD)) { - double distance; - if (vecDir(centerInf->point, xaxis, kPrev->point) == BEHIND) + if (segmentIntersect(centerInf->point, xaxis, kPrev->point, + k->point)) { - distance = dist(centerInf->point, kPrev->point); + EdgePair intPair = EdgePair(*t, kPrev); + e.push_back(intPair); } - else + if ((vecDir(kPrev->point, k->point, centerInf->point) == 0) && + inBetween(kPrev->point, k->point, centerInf->point)) { - distance = dist(centerInf->point, k->point); + // Record that centerPoint is on an obstacle line. + onBorderIDs.insert(k->id.objID); + } + } + else if (kNext && (kNext != centerInf) && + (vecDir(centerInf->point, xaxis, kNext->point) == AHEAD)) + { + if (segmentIntersect(centerInf->point, xaxis, kNext->point, + k->point)) + { + EdgePair intPair = EdgePair(*t, kNext); + e.push_back(intPair); + } + if ((vecDir(kNext->point, k->point, centerInf->point) == 0) && + inBetween(kNext->point, k->point, centerInf->point)) + { + // Record that centerPoint is on an obstacle line. + onBorderIDs.insert(k->id.objID); } - - EdgePair intPair = EdgePair(k, kPrev, distance, 0.0); - e.insert(intPair).first; } - k = k->lstNext; } + for (SweepEdgeList::iterator c = e.begin(); c != e.end(); ++c) + { + (*c).setNegativeAngle(); + } + // Start the actual sweep. db_printf("SWEEP: "); centerID.db_print(); db_printf("\n"); - VertInf *lastInf = NULL; - double lastAngle = 0; - bool lastVisible = false; - int lastBlocker = 0; - - isBoundingShape isBounding(router->contains[centerID]); - VertList::iterator vfst = v.begin(); - VertList::iterator vfin = v.end(); - for (VertList::iterator t = vfst; t != vfin; ++t) + isBoundingShape isBounding(ss); + for (VertSet::const_iterator t = vbegin; t != vend; ++t) { VertInf *currInf = (*t).vInf; VertID& currID = currInf->id; Point& currPt = currInf->point; - centerAngle = (*t).angle; #ifdef LINEDEBUG Sint16 ppx = (int) centerPt.x; @@ -479,29 +610,28 @@ void vertexSweep(VertInf *vert) Sint16 cx = (int) currPt.x; Sint16 cy = (int) currPt.y; + + int canx = 151; + int cany = 55; #endif - double currDist = dist(centerPt, currPt); - db_printf("Dist: %.1f.\n", currDist); + const double& currDist = (*t).distance; EdgeInf *edge = EdgeInf::existingEdge(centerInf, currInf); if (edge == NULL) { edge = new EdgeInf(centerInf, currInf); } - // Ignore vertices from bounding shapes, if sweeping round an endpoint. - if (!(centerID.isShape) && isBounding(*t)) + + for (SweepEdgeList::iterator c = e.begin(); c != e.end(); ++c) { - if (router->InvisibilityGrph) - { - // if p and t can't see each other, add blank edge - db_printf("\tSkipping visibility edge... \n\t\t"); - edge->addBlocker(currInf->id.objID); - edge->db_print(); - } - continue; + (*c).setCurrAngle(*t); } + e.sort(); + // Check visibility. + int blocker = 0; + bool currVisible = sweepVisible(e, *t, onBorderIDs, &blocker); bool cone1 = true, cone2 = true; if (centerID.isShape) @@ -519,7 +649,6 @@ void vertexSweep(VertInf *vert) if (!cone1 || !cone2) { - lastInf = NULL; if (router->InvisibilityGrph) { db_printf("\tSetting invisibility edge... \n\t\t"); @@ -529,18 +658,15 @@ void vertexSweep(VertInf *vert) } else { - int blocker = 0; - // Check visibility. - bool currVisible = sweepVisible(e, currInf, - lastInf, lastVisible, lastAngle, &blocker); - if (blocker == -1) - { - blocker = lastBlocker; - } if (currVisible) { #ifdef LINEDEBUG - lineRGBA(avoid_screen, ppx, ppy, cx, cy, 255, 0, 0, 32); + if (router->avoid_screen) + { + lineRGBA(router->avoid_screen, ppx + canx, ppy + cany, + cx + canx, cy + cany, 255, 0, 0, 75); + SDL_Delay(1000); + } #endif db_printf("\tSetting visibility edge... \n\t\t"); edge->setDist(currDist); @@ -552,72 +678,55 @@ void vertexSweep(VertInf *vert) edge->addBlocker(blocker); edge->db_print(); } - - lastVisible = currVisible; - lastInf = currInf; - lastAngle = centerAngle; - lastBlocker = blocker; + } + + if (!(edge->added()) && !(router->InvisibilityGrph)) + { + delete edge; + edge = NULL; } if (currID.isShape) { // This is a shape edge - Point& prevPt = currInf->shPrev->point; - Point& nextPt = currInf->shNext->point; - - int prevDir = vecDir(centerPt, currPt, prevPt); - EdgePair prevPair = EdgePair(currInf, currInf->shPrev, - currDist, centerAngle); - EdgeSet::iterator ePtr; - if (prevDir == BEHIND) + if (currInf->shPrev != centerInf) { - // XXX: Strangely e.find does not return the correct results. - // ePtr = e.find(prevPair); - ePtr = std::find(e.begin(), e.end(), prevPair); - if (ePtr != e.end()) + Point& prevPt = currInf->shPrev->point; + int prevDir = vecDir(centerPt, currPt, prevPt); + EdgePair prevPair = EdgePair(*t, currInf->shPrev); + + if (prevDir == BEHIND) { - e.erase(ePtr); + e.remove(prevPair); } - } - else if ((prevDir == AHEAD) && (currInf->shPrev != centerInf)) - { - double x = prevPt.x - currPt.x; - double y = prevPt.y - currPt.y; - double angle = PointPair::pos_to_angle(x, y); - prevPair.SetObsAng(angle); - - ePtr = e.insert(prevPair).first; - } - - - int nextDir = vecDir(centerPt, currPt, nextPt); - EdgePair nextPair = EdgePair(currInf, currInf->shNext, - currDist, centerAngle); - - if (nextDir == BEHIND) - { - // XXX: Strangely e.find does not return the correct results. - // ePtr = e.find(nextPair); - ePtr = std::find(e.begin(), e.end(), nextPair); - if (ePtr != e.end()) + else if (prevDir == AHEAD) { - e.erase(ePtr); + e.push_front(prevPair); } } - else if ((nextDir == AHEAD) && (currInf->shNext != centerInf)) + + if (currInf->shNext != centerInf) { - double x = nextPt.x - currPt.x; - double y = nextPt.y - currPt.y; - double angle = PointPair::pos_to_angle(x, y); - nextPair.SetObsAng(angle); + Point& nextPt = currInf->shNext->point; + int nextDir = vecDir(centerPt, currPt, nextPt); + EdgePair nextPair = EdgePair(*t, currInf->shNext); - ePtr = e.insert(nextPair).first; + if (nextDir == BEHIND) + { + e.remove(nextPair); + } + else if (nextDir == AHEAD) + { + e.push_front(nextPair); + } } } - #ifdef LINEDEBUG - SDL_Flip(avoid_screen); + if (router->avoid_screen) + { + SDL_Flip(router->avoid_screen); + } #endif } } diff --git a/src/libavoid/visibility.h b/src/libavoid/visibility.h index dd68ac692..92e56d922 100644 --- a/src/libavoid/visibility.h +++ b/src/libavoid/visibility.h @@ -2,24 +2,27 @@ * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router - * Copyright (C) 2004-2006 Michael Wybrow + * + * Copyright (C) 2004-2008 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * Author(s): Michael Wybrow */ + #ifndef AVOID_VISIBILITY_H #define AVOID_VISIBILITY_H @@ -28,7 +31,9 @@ namespace Avoid { - +class ShapeRef; +class VertInf; + extern void vertexVisibility(VertInf *point, VertInf *partner, bool knownNew, const bool gen_contains = false); extern void vertexSweep(VertInf *point); diff --git a/src/sp-conn-end-pair.cpp b/src/sp-conn-end-pair.cpp index 4dc0230ff..98b2aec26 100644 --- a/src/sp-conn-end-pair.cpp +++ b/src/sp-conn-end-pair.cpp @@ -12,6 +12,8 @@ #include #include +#include +#include #include "attributes.h" #include "sp-conn-end.h" @@ -26,10 +28,10 @@ SPConnEndPair::SPConnEndPair(SPPath *const owner) - : _invalid_path_connection() - , _path(owner) + : _path(owner) , _connRef(NULL) , _connType(SP_CONNECTOR_NOAVOID) + , _connCurvature(0.0) , _transformed_connection() { for (unsigned handle_ix = 0; handle_ix <= 1; ++handle_ix) { @@ -47,14 +49,6 @@ SPConnEndPair::~SPConnEndPair() delete this->_connEnd[handle_ix]; this->_connEnd[handle_ix] = NULL; } - if (_connRef) { - _connRef->removeFromGraph(); - delete _connRef; - _connRef = NULL; - } - - _invalid_path_connection.disconnect(); - _transformed_connection.disconnect(); } void @@ -68,6 +62,18 @@ SPConnEndPair::release() this->_connEnd[handle_ix]->href = NULL; this->_connEnd[handle_ix]->ref.detach(); } + + // If the document is being destroyed then the router instance + // and the ConnRefs will have been destroyed with it. + const bool routerInstanceExists = (_path->document->router != NULL); + + if (_connRef && routerInstanceExists) { + _connRef->removeFromGraph(); + delete _connRef; + } + _connRef = NULL; + + _transformed_connection.disconnect(); } void @@ -76,16 +82,17 @@ sp_conn_end_pair_build(SPObject *object) sp_object_read_attr(object, "inkscape:connector-type"); sp_object_read_attr(object, "inkscape:connection-start"); sp_object_read_attr(object, "inkscape:connection-end"); + sp_object_read_attr(object, "inkscape:connector-curvature"); } static void -avoid_conn_move(Geom::Matrix const */*mp*/, SPItem *moved_item) +avoid_conn_transformed(Geom::Matrix const */*mp*/, SPItem *moved_item) { - // Reroute connector SPPath *path = SP_PATH(moved_item); - path->connEndPair.makePathInvalid(); - sp_conn_adjust_invalid_path(path); + if (path->connEndPair.isAutoRoutingConn()) { + path->connEndPair.tellLibavoidNewEndpoints(); + } } @@ -93,16 +100,40 @@ void SPConnEndPair::setAttr(unsigned const key, gchar const *const value) { if (key == SP_ATTR_CONNECTOR_TYPE) { - if (value && (strcmp(value, "polyline") == 0)) { - _connType = SP_CONNECTOR_POLYLINE; - - Avoid::Router *router = _path->document->router; - GQuark itemID = g_quark_from_string(SP_OBJECT(_path)->id); - _connRef = new Avoid::ConnRef(router, itemID); - _invalid_path_connection = connectInvalidPath( - sigc::ptr_fun(&sp_conn_adjust_invalid_path)); - _transformed_connection = _path->connectTransformed( - sigc::ptr_fun(&avoid_conn_move)); + if (value && (strcmp(value, "polyline") == 0 || strcmp(value, "orthogonal") == 0)) { + int newconnType = strcmp(value, "polyline") ? SP_CONNECTOR_ORTHOGONAL : SP_CONNECTOR_POLYLINE; + + if (!_connRef) + { + _connType = newconnType; + Avoid::Router *router = _path->document->router; + GQuark itemID = g_quark_from_string(SP_OBJECT(_path)->id); + _connRef = new Avoid::ConnRef(router, itemID); + switch (newconnType) + { + case SP_CONNECTOR_POLYLINE: + _connRef->setRoutingType(Avoid::ConnType_PolyLine); + break; + case SP_CONNECTOR_ORTHOGONAL: + _connRef->setRoutingType(Avoid::ConnType_Orthogonal); + } + _transformed_connection = _path->connectTransformed( + sigc::ptr_fun(&avoid_conn_transformed)); + } + else + if (newconnType != _connType) + { + _connType = newconnType; + switch (newconnType) + { + case SP_CONNECTOR_POLYLINE: + _connRef->setRoutingType(Avoid::ConnType_PolyLine); + break; + case SP_CONNECTOR_ORTHOGONAL: + _connRef->setRoutingType(Avoid::ConnType_Orthogonal); + } + sp_conn_reroute_path(_path); + } } else { _connType = SP_CONNECTOR_NOAVOID; @@ -111,17 +142,25 @@ SPConnEndPair::setAttr(unsigned const key, gchar const *const value) _connRef->removeFromGraph(); delete _connRef; _connRef = NULL; - _invalid_path_connection.disconnect(); _transformed_connection.disconnect(); } } return; - + } + else if (key == SP_ATTR_CONNECTOR_CURVATURE) { + if (value) { + _connCurvature = g_strtod(value, NULL); + if (_connRef && _connRef->isInitialised()) { + // Redraw the connector, but only if it has been initialised. + sp_conn_reroute_path(_path); + } + } + return; } unsigned const handle_ix = key - SP_ATTR_CONNECTION_START; g_assert( handle_ix <= 1 ); - this->_connEnd[handle_ix]->setAttacherHref(value); + this->_connEnd[handle_ix]->setAttacherHref(value, _path); } void @@ -131,11 +170,18 @@ SPConnEndPair::writeRepr(Inkscape::XML::Node *const repr) const if (this->_connEnd[handle_ix]->ref.getURI()) { char const * const attr_strs[] = {"inkscape:connection-start", "inkscape:connection-end"}; - gchar *uri_string = this->_connEnd[handle_ix]->ref.getURI()->toString(); - repr->setAttribute(attr_strs[handle_ix], uri_string); - g_free(uri_string); + std::ostringstream ostr; + ostr<_connEnd[handle_ix]->ref.getURI()->toString()<<"_"<< + (this->_connEnd[handle_ix]->type == ConnPointDefault ? "d":"u") << + "_" << this->_connEnd[handle_ix]->id; + + + repr->setAttribute(attr_strs[handle_ix], ostr.str().c_str()); } } + repr->setAttribute("inkscape:connector-curvature", Glib::Ascii::dtostr(_connCurvature).c_str()); + if (_connType == SP_CONNECTOR_POLYLINE || _connType == SP_CONNECTOR_ORTHOGONAL) + repr->setAttribute("inkscape:connector-type", _connType == SP_CONNECTOR_POLYLINE ? "polyline" : "orthogonal" ); } void @@ -161,19 +207,14 @@ SPConnEndPair::getAttachedItems(SPItem *h2attItem[2]) const { void SPConnEndPair::getEndpoints(Geom::Point endPts[]) const { - SPCurve *curve = _path->curve; + SPCurve *curve = _path->original_curve ? _path->original_curve : _path->curve; SPItem *h2attItem[2]; getAttachedItems(h2attItem); for (unsigned h = 0; h < 2; ++h) { if ( h2attItem[h] ) { - Geom::OptRect bbox = h2attItem[h]->getBounds(sp_item_i2doc_affine(h2attItem[h])); - if (bbox) { - endPts[h] = bbox->midpoint(); - } else { - // FIXME - endPts[h] = Geom::Point(0, 0); - } + g_assert(h2attItem[h]->avoidRef); + endPts[h] = h2attItem[h]->avoidRef->getConnectionPointPos(_connEnd[h]->type, _connEnd[h]->id); } else { @@ -187,37 +228,40 @@ SPConnEndPair::getEndpoints(Geom::Point endPts[]) const { } } -sigc::connection -SPConnEndPair::connectInvalidPath(sigc::slot slot) -{ - return _invalid_path_signal.connect(slot); +gdouble +SPConnEndPair::getCurvature(void) const { + return _connCurvature; } -static void emitPathInvalidationNotification(void *ptr) +SPConnEnd** +SPConnEndPair::getConnEnds(void) { - // We emit a signal here rather than just calling the reroute function - // since this allows all the movement action computation to happen, - // then all connectors (that require it) will be rerouted. Otherwise, - // one connector could get rerouted several times as a result of - // dragging a couple of shapes. + return _connEnd; +} - SPPath *path = SP_PATH(ptr); - path->connEndPair._invalid_path_signal.emit(path); +bool +SPConnEndPair::isOrthogonal(void) const { + return _connType == SP_CONNECTOR_ORTHOGONAL; } -void -SPConnEndPair::rerouteFromManipulation(void) + +static void redrawConnectorCallback(void *ptr) { - _connRef->makePathInvalid(); - sp_conn_adjust_path(_path); + SPPath *path = SP_PATH(ptr); + if (path->document == NULL) { + // This can happen when the document is being destroyed. + return; + } + sp_conn_redraw_path(path); } void -SPConnEndPair::reroute(void) +SPConnEndPair::rerouteFromManipulation(void) { - sp_conn_adjust_path(_path); + sp_conn_reroute_path_immediate(_path); } + // Called from sp_path_update to initialise the endpoints. void SPConnEndPair::update(void) @@ -231,8 +275,8 @@ SPConnEndPair::update(void) Avoid::Point src(endPt[0][Geom::X], endPt[0][Geom::Y]); Avoid::Point dst(endPt[1][Geom::X], endPt[1][Geom::Y]); - _connRef->lateSetup(src, dst); - _connRef->setCallback(&emitPathInvalidationNotification, _path); + _connRef->setEndpoints(src, dst); + _connRef->setCallback(&redrawConnectorCallback, _path); } // Store the ID of the objects attached to the connector. storeIds(); @@ -243,19 +287,25 @@ SPConnEndPair::update(void) void SPConnEndPair::storeIds(void) { if (_connEnd[0]->href) { + gchar ** href_strarray = NULL; + href_strarray = g_strsplit(_connEnd[0]->href, "_", 0); // href begins with a '#' which we don't want. - const char *startId = _connEnd[0]->href + 1; + const char *startId = href_strarray[0] + 1; GQuark itemId = g_quark_from_string(startId); _connRef->setEndPointId(Avoid::VertID::src, itemId); + g_strfreev(href_strarray); } else { _connRef->setEndPointId(Avoid::VertID::src, 0); } if (_connEnd[1]->href) { + gchar ** href_strarray = NULL; + href_strarray = g_strsplit(_connEnd[1]->href, "_", 0); // href begins with a '#' which we don't want. - const char *endId = _connEnd[1]->href + 1; + const char *endId = href_strarray[0] + 1; GQuark itemId = g_quark_from_string(endId); _connRef->setEndPointId(Avoid::VertID::tar, itemId); + g_strfreev(href_strarray); } else { _connRef->setEndPointId(Avoid::VertID::tar, 0); @@ -278,15 +328,55 @@ SPConnEndPair::makePathInvalid(void) _connRef->makePathInvalid(); } + +// Redraws the curve along the recalculated route +// Straight or curved +void recreateCurve(SPCurve *curve, Avoid::ConnRef *connRef, const gdouble curvature) +{ + bool straight = curvature<1e-3; + + Avoid::PolyLine route = connRef->displayRoute(); + if (!straight) + route = route.curvedPolyline(curvature); + connRef->calcRouteDist(); + + curve->reset(); + + curve->moveto( Geom::Point(route.ps[0].x, route.ps[0].y) ); + int pn = route.size(); + for (int i = 1; i < pn; ++i) { + Geom::Point p(route.ps[i].x, route.ps[i].y); + if (straight) { + curve->lineto( p ); + } + else { + switch (route.ts[i]) { + case 'M': + curve->moveto( p ); + break; + case 'L': + curve->lineto( p ); + break; + case 'C': + g_assert( i+2curveto( p, Geom::Point(route.ps[i+1].x, route.ps[i+1].y), + Geom::Point(route.ps[i+2].x, route.ps[i+2].y) ); + i+=2; + break; + } + } + } +} + + void -SPConnEndPair::reroutePath(void) +SPConnEndPair::tellLibavoidNewEndpoints(const bool processTransaction) { if (!isAutoRoutingConn()) { // Do nothing return; } - - SPCurve *curve = _path->curve; + makePathInvalid(); Geom::Point endPt[2]; getEndpoints(endPt); @@ -294,26 +384,34 @@ SPConnEndPair::reroutePath(void) Avoid::Point src(endPt[0][Geom::X], endPt[0][Geom::Y]); Avoid::Point dst(endPt[1][Geom::X], endPt[1][Geom::Y]); - _connRef->updateEndPoint(Avoid::VertID::src, src); - _connRef->updateEndPoint(Avoid::VertID::tar, dst); - - _connRef->generatePath(src, dst); - - Avoid::PolyLine route = _connRef->route(); - _connRef->calcRouteDist(); + _connRef->setEndpoints(src, dst); + if (processTransaction) + { + _connRef->router()->processTransaction(); + } + return; +} - curve->reset(); - curve->moveto(endPt[0]); - for (int i = 1; i < route.pn; ++i) { - Geom::Point p(route.ps[i].x, route.ps[i].y); - curve->lineto(p); +bool +SPConnEndPair::reroutePathFromLibavoid(void) +{ + if (!isAutoRoutingConn()) { + // Do nothing + return false; } + SPCurve *curve = _path->original_curve ?_path->original_curve : _path->curve; + + recreateCurve( curve, _connRef, _connCurvature ); + Geom::Matrix doc2item = sp_item_i2doc_affine(SP_ITEM(_path)).inverse(); curve->transform(doc2item); + + return true; } + /* Local Variables: mode:c++ diff --git a/src/sp-conn-end-pair.h b/src/sp-conn-end-pair.h index 9b9e181db..3b011ed17 100644 --- a/src/sp-conn-end-pair.h +++ b/src/sp-conn-end-pair.h @@ -28,6 +28,7 @@ class Node; } } +extern void recreateCurve(SPCurve *curve, Avoid::ConnRef *connRef, gdouble curvature); class SPConnEndPair { public: @@ -38,19 +39,16 @@ public: void writeRepr(Inkscape::XML::Node *const repr) const; void getAttachedItems(SPItem *[2]) const; void getEndpoints(Geom::Point endPts[]) const; - void reroutePath(void); + gdouble getCurvature(void) const; + SPConnEnd** getConnEnds(void); + bool isOrthogonal(void) const; + friend void recreateCurve(SPCurve *curve, Avoid::ConnRef *connRef, gdouble curvature); + void tellLibavoidNewEndpoints(const bool processTransaction = false); + bool reroutePathFromLibavoid(void); void makePathInvalid(void); void update(void); bool isAutoRoutingConn(void); void rerouteFromManipulation(void); - void reroute(void); - sigc::connection connectInvalidPath(sigc::slot slot); - - // A signal emited by a call back from libavoid. Used to let - // connectors know when they need to reroute themselves. - sigc::signal _invalid_path_signal; - // A sigc connection to listen for connector path invalidation. - sigc::connection _invalid_path_connection; private: SPConnEnd *_connEnd[2]; @@ -61,6 +59,7 @@ private: Avoid::ConnRef *_connRef; int _connType; + gdouble _connCurvature; // A sigc connection for transformed signal. sigc::connection _transformed_connection; @@ -74,8 +73,9 @@ void sp_conn_end_pair_build(SPObject *object); // _connType options: enum { - SP_CONNECTOR_NOAVOID, // Basic connector - a straight line. - SP_CONNECTOR_POLYLINE // Object avoiding polyline. + SP_CONNECTOR_NOAVOID, // Basic connector - a straight line. + SP_CONNECTOR_POLYLINE, // Object avoiding polyline. + SP_CONNECTOR_ORTHOGONAL // Object avoiding orthogonal polyline (only horizontal and verical segments). }; diff --git a/src/sp-conn-end.cpp b/src/sp-conn-end.cpp index 0b420a98e..52914ee9e 100644 --- a/src/sp-conn-end.cpp +++ b/src/sp-conn-end.cpp @@ -16,7 +16,7 @@ #include "2geom/path-intersection.h" -static void change_endpts(SPCurve *const curve, Geom::Point const h2endPt[2]); +static void change_endpts(SPCurve *const curve, double const endPos[2]); SPConnEnd::SPConnEnd(SPObject *const owner) : ref(owner), @@ -39,45 +39,37 @@ get_nearest_common_ancestor(SPObject const *const obj, SPItem const *const objs[ } -static bool try_get_intersect_point_with_item_recursive(SPCurve *conn_curve, SPItem& item, - const Geom::Matrix& item_transform, const bool at_start, double* intersect_pos, - unsigned *intersect_index) { - - double initial_pos = (at_start) ? 0.0 : std::numeric_limits::max(); +static bool try_get_intersect_point_with_item_recursive(Geom::PathVector& conn_pv, SPItem* item, + const Geom::Matrix& item_transform, double& intersect_pos) { + double initial_pos = intersect_pos; // if this is a group... - if (SP_IS_GROUP(&item)) { - SPGroup* group = SP_GROUP(&item); + if (SP_IS_GROUP(item)) { + SPGroup* group = SP_GROUP(item); // consider all first-order children - double child_pos = initial_pos; - unsigned child_index; + double child_pos = std::numeric_limits::max(); for (GSList const* i = sp_item_group_item_list(group); i != NULL; i = i->next) { SPItem* child_item = SP_ITEM(i->data); - try_get_intersect_point_with_item_recursive(conn_curve, *child_item, - item_transform * child_item->transform, at_start, &child_pos, &child_index); - if (fabs(initial_pos - child_pos) > fabs(initial_pos - *intersect_pos)) { - // It is further away from the initial point than the current intersection - // point (i.e. the "outermost" intersection), so use this one. - *intersect_pos = child_pos; - *intersect_index = child_index; - } + try_get_intersect_point_with_item_recursive(conn_pv, child_item, + item_transform * child_item->transform, child_pos); + if (intersect_pos > child_pos) + intersect_pos = child_pos; } - return *intersect_pos != initial_pos; + return intersect_pos != initial_pos; } - // if this is a shape... - if (!SP_IS_SHAPE(&item)) return false; + // if this is not a shape, nothing to be done + if (!SP_IS_SHAPE(item)) return false; // make sure it has an associated curve - SPCurve* item_curve = sp_shape_get_curve(SP_SHAPE(&item)); + SPCurve* item_curve = sp_shape_get_curve(SP_SHAPE(item)); if (!item_curve) return false; // apply transformations (up to common ancestor) item_curve->transform(item_transform); const Geom::PathVector& curve_pv = item_curve->get_pathvector(); - const Geom::PathVector& conn_pv = conn_curve->get_pathvector(); Geom::CrossingSet cross = crossings(conn_pv, curve_pv); // iterate over all Crossings for (Geom::CrossingSet::const_iterator i = cross.begin(); i != cross.end(); i++) { @@ -85,18 +77,14 @@ static bool try_get_intersect_point_with_item_recursive(SPCurve *conn_curve, SPI for (Geom::Crossings::const_iterator i = cr.begin(); i != cr.end(); i++) { const Geom::Crossing& cr_pt = *i; - if (fabs(initial_pos - cr_pt.ta) > fabs(initial_pos - *intersect_pos)) { - // It is further away from the initial point than the current intersection - // point (i.e. the "outermost" intersection), so use this one. - *intersect_pos = cr_pt.ta; - *intersect_index = cr_pt.a; - } + if ( intersect_pos > cr_pt.ta) + intersect_pos = cr_pt.ta; } } item_curve->unref(); - return *intersect_pos != initial_pos; + return intersect_pos != initial_pos; } @@ -104,22 +92,36 @@ static bool try_get_intersect_point_with_item_recursive(SPCurve *conn_curve, SPI // and the item given. If the item is a group, then the component items are considered. // The transforms given should be to a common ancestor of both the path and item. // -static bool try_get_intersect_point_with_item(SPPath& conn, SPItem& item, +static bool try_get_intersect_point_with_item(SPPath* conn, SPItem* item, const Geom::Matrix& item_transform, const Geom::Matrix& conn_transform, - const bool at_start, double* intersect_pos, unsigned *intersect_index) { + const bool at_start, double& intersect_pos) { - // We start with the intersection point either at the beginning or end of the - // path, depending on whether we are considering the source or target endpoint. - *intersect_pos = (at_start) ? 0.0 : std::numeric_limits::max(); - // Copy the curve and apply transformations up to common ancestor. - SPCurve* conn_curve = conn.curve->copy(); + SPCurve* conn_curve = conn->curve->copy(); conn_curve->transform(conn_transform); + Geom::PathVector conn_pv = conn_curve->get_pathvector(); + + // If this is not the starting point, use Geom::Path::reverse() to reverse the path + if (!at_start) + { + // connectors are actually a single path, so consider the first element from a Geom::PathVector + conn_pv[0] = conn_pv[0].reverse(); + } + + // We start with the intersection point at the end of the path + intersect_pos = conn_pv[0].size(); + // Find the intersection. - bool result = try_get_intersect_point_with_item_recursive(conn_curve, item, item_transform, - at_start, intersect_pos, intersect_index); - + bool result = try_get_intersect_point_with_item_recursive(conn_pv, item, item_transform, intersect_pos); + + if (!result) + // No intersection point has been found (why?) + // just default to connector end + intersect_pos = 0; + // If not at the starting point, recompute position with respect to original path + if (!at_start) + intersect_pos = conn_pv[0].size() - intersect_pos; // Free the curve copy. conn_curve->unref(); @@ -128,18 +130,15 @@ static bool try_get_intersect_point_with_item(SPPath& conn, SPItem& item, static void -sp_conn_end_move_compensate(Geom::Matrix const */*mp*/, SPItem */*moved_item*/, - SPPath *const path, - bool const updatePathRepr = true) +sp_conn_get_route_and_redraw(SPPath *const path, + const bool updatePathRepr = true) { - // TODO: SPItem::getBounds gives the wrong result for some objects - // that have internal representations that are updated later - // by the sp_*_update functions, e.g., text. - sp_document_ensure_up_to_date(path->document); - // Get the new route around obstacles. - path->connEndPair.reroutePath(); - + bool rerouted = path->connEndPair.reroutePathFromLibavoid(); + if (!rerouted) { + return; + } + SPItem *h2attItem[2]; path->connEndPair.getAttachedItems(h2attItem); @@ -147,72 +146,80 @@ sp_conn_end_move_compensate(Geom::Matrix const */*mp*/, SPItem */*moved_item*/, SPObject const *const ancestor = get_nearest_common_ancestor(path_item, h2attItem); Geom::Matrix const path2anc(i2anc_affine(path_item, ancestor)); - Geom::Point endPts[2] = { *(path->curve->first_point()), *(path->curve->last_point()) }; - + // Set sensible values incase there the connector ends are not + // attached to any shapes. + Geom::PathVector conn_pv = path->curve->get_pathvector(); + double endPos[2] = { 0, conn_pv[0].size() }; + + SPConnEnd** _connEnd = path->connEndPair.getConnEnds(); for (unsigned h = 0; h < 2; ++h) { - if (h2attItem[h]) { - // For each attached object, change the corresponding point to be - // at the outermost intersection with the object's path. - double intersect_pos; - unsigned intersect_index; + if (h2attItem[h] && _connEnd[h]->type == ConnPointDefault && _connEnd[h]->id == ConnPointPosCC) { Geom::Matrix h2i2anc = i2anc_affine(h2attItem[h], ancestor); - if ( try_get_intersect_point_with_item(*path, *h2attItem[h], h2i2anc, path2anc, - (h == 0), &intersect_pos, &intersect_index) ) { - const Geom::PathVector& curve = path->curve->get_pathvector(); - endPts[h] = curve[intersect_index].pointAt(intersect_pos); - } + try_get_intersect_point_with_item(path, h2attItem[h], h2i2anc, path2anc, + (h == 0), endPos[h]); } } - change_endpts(path->curve, endPts); + change_endpts(path->curve, endPos); if (updatePathRepr) { - path->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); path->updateRepr(); + path->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } } -// TODO: This triggering of makeInvalidPath could be cleaned up to be -// another option passed to move_compensate. + static void -sp_conn_end_shape_move_compensate(Geom::Matrix const *mp, SPItem *moved_item, +sp_conn_end_shape_move(Geom::Matrix const */*mp*/, SPItem */*moved_item*/, SPPath *const path) { if (path->connEndPair.isAutoRoutingConn()) { - path->connEndPair.makePathInvalid(); + path->connEndPair.tellLibavoidNewEndpoints(); } - sp_conn_end_move_compensate(mp, moved_item, path); } void -sp_conn_adjust_invalid_path(SPPath *const path) +sp_conn_reroute_path(SPPath *const path) { - sp_conn_end_move_compensate(NULL, NULL, path); + if (path->connEndPair.isAutoRoutingConn()) { + path->connEndPair.tellLibavoidNewEndpoints(); + } } + void -sp_conn_adjust_path(SPPath *const path) +sp_conn_reroute_path_immediate(SPPath *const path) { if (path->connEndPair.isAutoRoutingConn()) { - path->connEndPair.makePathInvalid(); + bool processTransaction = true; + path->connEndPair.tellLibavoidNewEndpoints(processTransaction); } // Don't update the path repr or else connector dragging is slowed by // constant update of values to the xml editor, and each step is also // needlessly remembered by undo/redo. bool const updatePathRepr = false; - sp_conn_end_move_compensate(NULL, NULL, path, updatePathRepr); + sp_conn_get_route_and_redraw(path, updatePathRepr); +} + +void sp_conn_redraw_path(SPPath *const path) +{ + sp_conn_get_route_and_redraw(path); } static void -change_endpts(SPCurve *const curve, Geom::Point const h2endPt[2]) +change_endpts(SPCurve *const curve, double const endPos[2]) { -#if 0 - curve->reset(); - curve->moveto(h2endPt[0]); - curve->lineto(h2endPt[1]); -#else - curve->move_endpoints(h2endPt[0], h2endPt[1]); -#endif + // Use Geom::Path::portion to cut the curve at the end positions + if (endPos[0] > endPos[1]) + { + // Path is "negative", reset the curve and return + curve->reset(); + return; + } + const Geom::Path& old_path = curve->get_pathvector()[0]; + Geom::PathVector new_path_vector; + new_path_vector.push_back(old_path.portion(endPos[0], endPos[1])); + curve->set_pathvector(new_path_vector); } static void @@ -234,29 +241,150 @@ sp_conn_end_detach(SPObject *const owner, unsigned const handle_ix) } void -SPConnEnd::setAttacherHref(gchar const *value) +SPConnEnd::setAttacherHref(gchar const *value, SPPath* path) { if ( value && href && ( strcmp(value, href) == 0 ) ) { /* No change, do nothing. */ } else { - g_free(href); - href = NULL; - if (value) { - // First, set the href field, because sp_conn_end_href_changed will need it. - href = g_strdup(value); - - // Now do the attaching, which emits the changed signal. - try { - ref.attach(Inkscape::URI(value)); - } catch (Inkscape::BadURIException &e) { - /* TODO: Proper error handling as per - * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing. (Also needed for - * sp-use.) */ - g_warning("%s", e.what()); + if (!value) + { + ref.detach(); + g_free(href); + href = NULL; + } + else + { + + /* References to the connection points have the following format + #svguri_t_id, where #svguri is the id of the item the + connector is attached to, t is the type of the point, which + can be either "d" for default or "u" for user-defined, and + id is the local (inside the item) id of the connection point. + In the case of default points id represents the position on the + item (i.e. Top-Left, Centre-Centre, etc.). + */ + + gchar ** href_strarray = NULL; + if (href) + href_strarray = g_strsplit(href, "_", 0); + gchar ** value_strarray = g_strsplit(value, "_", 0); + + g_free(href); + href = NULL; + + bool changed = false; + bool validRef = true; + + if ( !href_strarray || g_strcmp0(href_strarray[0], value_strarray[0]) != 0 ) + { + // The href has changed, so update it. + changed = true; + // Set the href field, because sp_conn_end_href_changed will need it. + href = g_strdup(value); + // Now do the attaching, which emits the changed signal. + try { + ref.attach(Inkscape::URI(value_strarray[0])); + } catch (Inkscape::BadURIException &e) { + /* TODO: Proper error handling as per + * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing. (Also needed for + * sp-use.) */ + g_warning("%s", e.what()); + validRef = false; + } + } + // Check to see if the connection point changed and update it. + // + + if ( !value_strarray[1] ) + { + /* Treat the old references to connection points + as default points that connect to the centre + of the item. + */ + if ( type != ConnPointDefault ) + { + type = ConnPointDefault; + changed = true; + } + if ( id != ConnPointPosCC ) + { + id = ConnPointPosCC; + changed = true; + } + } + else + { + switch (value_strarray[1][0]) + { + case 'd': + if ( type != ConnPointDefault ) + { + type = ConnPointDefault; + changed = true; + } + break; + case 'u': + if ( type != ConnPointUserDefined) + { + type = ConnPointUserDefined; + changed = true; + } + break; + default: + g_warning("Bad reference to a connection point."); + validRef = false; + } + if ( value_strarray[2] ) + { + int newId = (int) g_ascii_strtod( value_strarray[2], 0 ); + if ( id != newId ) + { + id = newId; + changed = true; + } + + } + else + { + // We have a malformed reference to a connection point, + // emit a warning, clear href and detach ref. + changed = true; + g_warning("Bad reference to a connection point.");\ + validRef = false; + } + } + + if ( changed ) + { + // We still have to verify that the reference to the + // connection point is a valid one. + + // Get the item the connector is attached to + SPItem* item = ref.getObject(); + if ( item && !item->avoidRef->isValidConnPointId( type, id ) ) + { + g_warning("Bad reference to a connection point."); + validRef = false; + } +/* else + // Update the connector + if (path->connEndPair.isAutoRoutingConn()) { + path->connEndPair.tellLibavoidNewEndpoints(); + }*/ + } + + if ( !validRef ) + { ref.detach(); + g_free(href); + href = NULL; } - } else { - ref.detach(); + else + if (!href) + href = g_strdup(value); + + g_strfreev(href_strarray); + g_strfreev(value_strarray); } } } @@ -277,7 +405,7 @@ sp_conn_end_href_changed(SPObject */*old_ref*/, SPObject */*ref*/, = SP_OBJECT(refobj)->connectDelete(sigc::bind(sigc::ptr_fun(&sp_conn_end_deleted), SP_OBJECT(path), handle_ix)); connEnd._transformed_connection - = SP_ITEM(refobj)->connectTransformed(sigc::bind(sigc::ptr_fun(&sp_conn_end_shape_move_compensate), + = SP_ITEM(refobj)->connectTransformed(sigc::bind(sigc::ptr_fun(&sp_conn_end_shape_move), path)); } } diff --git a/src/sp-conn-end.h b/src/sp-conn-end.h index a565b6404..5e9dbb9da 100644 --- a/src/sp-conn-end.h +++ b/src/sp-conn-end.h @@ -5,6 +5,8 @@ #include #include "sp-use-reference.h" +#include "connection-points.h" +#include "conn-avoid-ref.h" class SPConnEnd { @@ -14,6 +16,15 @@ public: SPUseReference ref; gchar *href; + /* In the following, type refers to connection point type, + i.e. default (one of the 9 combinations of right, centre, + left, top, bottom) or user-defined. The id serves to identify + the connection point in a list of connection points. + */ + + ConnPointType type; + int id; + /** Change of href string (not a modification of the attributes of the referrent). */ sigc::connection _changed_connection; @@ -23,7 +34,7 @@ public: /** A sigc connection for transformed signal, used to do move compensation. */ sigc::connection _transformed_connection; - void setAttacherHref(gchar const *); + void setAttacherHref(gchar const *, SPPath *); private: SPConnEnd(SPConnEnd const &); @@ -32,8 +43,9 @@ private: void sp_conn_end_href_changed(SPObject *old_ref, SPObject *ref, SPConnEnd *connEnd, SPPath *path, unsigned const handle_ix); -void sp_conn_adjust_invalid_path(SPPath *const path); -void sp_conn_adjust_path(SPPath *const path); +void sp_conn_reroute_path(SPPath *const path); +void sp_conn_reroute_path_immediate(SPPath *const path); +void sp_conn_redraw_path(SPPath *const path); void sp_conn_end_detach(SPObject *const owner, unsigned const handle_ix); diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 395048280..9f7157b99 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -412,6 +412,7 @@ sp_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) sp_object_read_attr(object, "inkscape:transform-center-x"); sp_object_read_attr(object, "inkscape:transform-center-y"); sp_object_read_attr(object, "inkscape:connector-avoid"); + sp_object_read_attr(object, "inkscape:connection-points"); if (((SPObjectClass *) (parent_class))->build) { (* ((SPObjectClass *) (parent_class))->build)(object, document, repr); @@ -514,6 +515,9 @@ sp_item_set(SPObject *object, unsigned key, gchar const *value) case SP_ATTR_CONNECTOR_AVOID: item->avoidRef->setAvoid(value); break; + case SP_ATTR_CONNECTION_POINTS: + item->avoidRef->setConnectionPoints(value); + break; case SP_ATTR_TRANSFORM_CENTER_X: if (value) { item->transform_center_x = g_strtod(value, NULL); diff --git a/src/svg/stringstream.cpp b/src/svg/stringstream.cpp index 6b9e512a1..431d5d97b 100644 --- a/src/svg/stringstream.cpp +++ b/src/svg/stringstream.cpp @@ -66,6 +66,31 @@ operator<<(Inkscape::SVGOStringStream &os, Geom::Point const & p) return os; } +Inkscape::SVGIStringStream::SVGIStringStream():std::istringstream() +{ + this->imbue(std::locale::classic()); + this->setf(std::ios::showpoint); + + /* This one is (currently) needed though, as we currently use ostr.precision as a sort of + variable for storing the desired precision: see our two precision methods and our operator<< + methods for float and double. */ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + this->precision(prefs->getInt("/options/svgoutput/numericprecision", 8)); +} + +Inkscape::SVGIStringStream::SVGIStringStream(const std::string& str):std::istringstream(str) +{ + this->imbue(std::locale::classic()); + this->setf(std::ios::showpoint); + + /* This one is (currently) needed though, as we currently use ostr.precision as a sort of + variable for storing the desired precision: see our two precision methods and our operator<< + methods for float and double. */ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + this->precision(prefs->getInt("/options/svgoutput/numericprecision", 8)); +} + + /* Local Variables: mode:c++ diff --git a/src/svg/stringstream.h b/src/svg/stringstream.h index 5fbf1976c..60ed74ecb 100644 --- a/src/svg/stringstream.h +++ b/src/svg/stringstream.h @@ -74,6 +74,13 @@ public: } }; +class SVGIStringStream:public std::istringstream { + +public: + SVGIStringStream(); + SVGIStringStream(const std::string &str); +}; + } Inkscape::SVGOStringStream &operator<<(Inkscape::SVGOStringStream &os, float d); diff --git a/src/ui/icon-names.h b/src/ui/icon-names.h index 0388b200f..76e76ea34 100644 --- a/src/ui/icon-names.h +++ b/src/ui/icon-names.h @@ -56,10 +56,18 @@ "color-picker" #define INKSCAPE_ICON_COLOR_REMOVE \ "color-remove" +#define INKSCAPE_ICON_CONNECTOR_EDIT \ + "connector-edit" #define INKSCAPE_ICON_CONNECTOR_AVOID \ "connector-avoid" #define INKSCAPE_ICON_CONNECTOR_IGNORE \ "connector-ignore" +#define INKSCAPE_ICON_CONNECTOR_ORTHOGONAL \ + "connector-orthogonal" +#define INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT \ + "connector-new-connpoint" +#define INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT \ + "connector-remove-connpoint" #define INKSCAPE_ICON_DIALOG_ALIGN_AND_DISTRIBUTE \ "dialog-align-and-distribute" #define INKSCAPE_ICON_DIALOG_FILL_AND_STROKE \ diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 5f163d138..b10c60c79 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -470,13 +470,18 @@ static gchar const * ui_descr = " " " " + " " " " " " + " " + " " " " " " " " " " " " + " " + " " " " "" @@ -7076,6 +7081,13 @@ sp_text_toolbox_new (SPDesktop *desktop) //## Connector ## //######################### +static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ ) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/connector/mode", + gtk_toggle_action_get_active( act )); +} + static void sp_connector_path_set_avoid(void) { cc_selection_set_avoid(true); @@ -7087,6 +7099,106 @@ static void sp_connector_path_set_ignore(void) cc_selection_set_avoid(false); } +static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl ) +{ + SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" ); + Inkscape::Selection * selection = sp_desktop_selection(desktop); + SPDocument *doc = sp_desktop_document(desktop); + + if (!sp_document_get_undo_sensitive(doc)) + { + return; + } + + + // quit if run by the _changed callbacks + if (g_object_get_data( tbl, "freeze" )) { + return; + } + + // in turn, prevent callbacks from responding + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); + + bool is_orthog = gtk_toggle_action_get_active( act ); + gchar orthog_str[] = "orthogonal"; + gchar polyline_str[] = "polyline"; + gchar *value = is_orthog ? orthog_str : polyline_str ; + + bool modmade = false; + GSList *l = (GSList *) selection->itemList(); + while (l) { + SPItem *item = (SPItem *) l->data; + + if (cc_item_is_connector(item)) { + sp_object_setAttribute(item, "inkscape:connector-type", + value, false); + item->avoidRef->handleSettingChange(); + modmade = true; + } + l = l->next; + } + + if (!modmade) + { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/connector/orthogonal", is_orthog); + } + + sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR, + is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline")); + + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); +} + +static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl) +{ + SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" ); + Inkscape::Selection * selection = sp_desktop_selection(desktop); + SPDocument *doc = sp_desktop_document(desktop); + + if (!sp_document_get_undo_sensitive(doc)) + { + return; + } + + + // quit if run by the _changed callbacks + if (g_object_get_data( tbl, "freeze" )) { + return; + } + + // in turn, prevent callbacks from responding + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); + + gdouble newValue = gtk_adjustment_get_value(adj); + gchar value[G_ASCII_DTOSTR_BUF_SIZE]; + g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue); + + bool modmade = false; + GSList *l = (GSList *) selection->itemList(); + while (l) { + SPItem *item = (SPItem *) l->data; + + if (cc_item_is_connector(item)) { + sp_object_setAttribute(item, "inkscape:connector-curvature", + value, false); + item->avoidRef->handleSettingChange(); + modmade = true; + } + l = l->next; + } + + if (!modmade) + { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue); + } + + sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR, + _("Change connector curvature")); + + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); +} static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl) @@ -7183,21 +7295,37 @@ static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr, if (g_object_get_data(G_OBJECT(tbl), "freeze")) { return; } - if (strcmp(name, "inkscape:connector-spacing") != 0) { - return; + if (strcmp(name, "inkscape:connector-spacing") == 0) + { + GtkAdjustment *adj = (GtkAdjustment*) + gtk_object_get_data(GTK_OBJECT(tbl), "spacing"); + gdouble spacing = defaultConnSpacing; + sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing); + + gtk_adjustment_set_value(adj, spacing); + gtk_adjustment_value_changed(adj); } - GtkAdjustment *adj = (GtkAdjustment*) - gtk_object_get_data(GTK_OBJECT(tbl), "spacing"); - gdouble spacing = defaultConnSpacing; - sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing); + spinbutton_defocus(GTK_OBJECT(tbl)); +} - gtk_adjustment_set_value(adj, spacing); - gtk_adjustment_value_changed(adj); +static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl) +{ + SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" ); + SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context); - spinbutton_defocus(GTK_OBJECT(tbl)); + if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE) + cc_create_connection_point(cc); } +static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl) +{ + SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" ); + SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context); + + if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE) + cc_remove_connection_point(cc); +} static Inkscape::XML::NodeEventVector connector_tb_repr_events = { NULL, /* child_added */ @@ -7207,12 +7335,42 @@ static Inkscape::XML::NodeEventVector connector_tb_repr_events = { NULL /* order_changed */ }; +static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl) +{ + GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) ); + GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) ); + SPItem *item = selection->singleItem(); + if (SP_IS_PATH(item)) + { + gdouble curvature = SP_PATH(item)->connEndPair.getCurvature(); + bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal(); + gtk_toggle_action_set_active(act, is_orthog); + gtk_adjustment_set_value(adj, curvature); + } + +} static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder ) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + // Editing mode toggle button + { + InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction", + _("EditMode"), + _("Switch between connection point editing and connector drawing mode"), + INKSCAPE_ICON_CONNECTOR_EDIT, + Inkscape::ICON_SIZE_DECORATION ); + gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); + + bool tbuttonstate = prefs->getBool("/tools/connector/mode"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE )); + g_object_set_data( holder, "mode", act ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder ); + } + + { InkAction* inky = ink_action_new( "ConnectorAvoidAction", _("Avoid"), @@ -7233,17 +7391,42 @@ static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainA gtk_action_group_add_action( mainActions, GTK_ACTION(inky) ); } + // Orthogonal connectors toggle button + { + InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction", + _("Orthogonal"), + _("Make connector orthogonal or polyline"), + INKSCAPE_ICON_CONNECTOR_ORTHOGONAL, + Inkscape::ICON_SIZE_DECORATION ); + gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); + + bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE )); + g_object_set_data( holder, "orthogonal", act ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder ); + } + EgeAdjustmentAction* eact = 0; + // Curvature spinbox + eact = create_adjustment_action( "ConnectorCurvatureAction", + _("Connector Curvature"), _("Curvature:"), + _("The amount of connectors curvature"), + "/tools/connector/curvature", defaultConnCurvature, + GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature", + 0, 100, 1.0, 10.0, + 0, 0, 0, + connector_curvature_changed, 1, 0 ); + gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); // Spacing spinbox eact = create_adjustment_action( "ConnectorSpacingAction", - _("Connector Spacing"), _("Spacing:"), - _("The amount of space left around objects by auto-routing connectors"), - "/tools/connector/spacing", defaultConnSpacing, - GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing", - 0, 100, 1.0, 10.0, - 0, 0, 0, - connector_spacing_changed, 1, 0 ); + _("Connector Spacing"), _("Spacing:"), + _("The amount of space left around objects by auto-routing connectors"), + "/tools/connector/spacing", defaultConnSpacing, + GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing", + 0, 100, 1.0, 10.0, + 0, 0, 0, + connector_spacing_changed, 1, 0 ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); // Graph (connector network) layout @@ -7282,6 +7465,8 @@ static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainA gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE )); g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder ); + sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder)) + ); } // Avoid overlaps toggle button @@ -7299,6 +7484,31 @@ static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainA g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder ); } + + // New connection point button + { + InkAction* inky = ink_action_new( "ConnectorNewConnPointAction", + _("New connection point"), + _("Add a new connection point to the currently selected item"), + INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT, + secondarySize ); + g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder ); + gtk_action_group_add_action( mainActions, GTK_ACTION(inky) ); + } + + // Remove selected connection point button + + { + InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction", + _("Remove connection point"), + _("Remove the currently selected connection point"), + INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT, + secondarySize ); + g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder ); + gtk_action_group_add_action( mainActions, GTK_ACTION(inky) ); + } + + // Code to watch for changes to the connector-spacing attribute in // the XML. Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview); -- cgit v1.2.3 From 89a5d9d34b73249c357fffae17c2989b6fdd6846 Mon Sep 17 00:00:00 2001 From: "Arcadie M. Cracan" Date: Wed, 2 Dec 2009 23:26:56 +0200 Subject: Add forgotten libavoid files (bzr r8856) --- src/libavoid/.dirstamp | 0 src/libavoid/assertions.h | 49 + src/libavoid/geomtypes.cpp | 548 ++++++++++ src/libavoid/makefile | 17 + src/libavoid/orthogonal.cpp | 2343 +++++++++++++++++++++++++++++++++++++++++++ src/libavoid/orthogonal.h | 39 + src/libavoid/viscluster.cpp | 96 ++ src/libavoid/viscluster.h | 67 ++ src/libavoid/vpsc.cpp | 1300 ++++++++++++++++++++++++ src/libavoid/vpsc.h | 255 +++++ 10 files changed, 4714 insertions(+) create mode 100644 src/libavoid/.dirstamp create mode 100644 src/libavoid/assertions.h create mode 100644 src/libavoid/geomtypes.cpp create mode 100644 src/libavoid/makefile create mode 100644 src/libavoid/orthogonal.cpp create mode 100644 src/libavoid/orthogonal.h create mode 100644 src/libavoid/viscluster.cpp create mode 100644 src/libavoid/viscluster.h create mode 100644 src/libavoid/vpsc.cpp create mode 100644 src/libavoid/vpsc.h (limited to 'src') diff --git a/src/libavoid/.dirstamp b/src/libavoid/.dirstamp new file mode 100644 index 000000000..e69de29bb diff --git a/src/libavoid/assertions.h b/src/libavoid/assertions.h new file mode 100644 index 000000000..0725c4482 --- /dev/null +++ b/src/libavoid/assertions.h @@ -0,0 +1,49 @@ +/* + * vim: ts=4 sw=4 et tw=0 wm=0 + * + * libavoid - Fast, Incremental, Object-avoiding Line Router + * + * Copyright (C) 2009 Monash University + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Author(s): Michael Wybrow +*/ + +#ifndef AVOID_ASSERTIONS_H +#define AVOID_ASSERTIONS_H + +#ifdef NDEBUG + + #define COLA_ASSERT(expr) static_cast(0) + +#else // Not NDEBUG + + #if defined(USE_ASSERT_EXCEPTIONS) + + #include "libvpsc/assertions.h" + + #else + + #include + #define COLA_ASSERT(expr) assert(expr) + + #endif + +#endif + + +#endif // AVOID_ASSERTIONS_H + diff --git a/src/libavoid/geomtypes.cpp b/src/libavoid/geomtypes.cpp new file mode 100644 index 000000000..10bb95a7a --- /dev/null +++ b/src/libavoid/geomtypes.cpp @@ -0,0 +1,548 @@ +/* + * vim: ts=4 sw=4 et tw=0 wm=0 + * + * libavoid - Fast, Incremental, Object-avoiding Line Router + * + * Copyright (C) 2004-2009 Monash University + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Author(s): Michael Wybrow +*/ + + +#include +#include +#include + +#include "libavoid/geomtypes.h" +#include "libavoid/shape.h" +#include "libavoid/router.h" +#include "libavoid/assertions.h" + + +namespace Avoid +{ + + +Point::Point() : + id(0), + vn(kUnassignedVertexNumber) +{ +} + + +Point::Point(const double xv, const double yv) : + x(xv), + y(yv), + id(0), + vn(kUnassignedVertexNumber) +{ +} + + +bool Point::operator==(const Point& rhs) const +{ + if ((x == rhs.x) && (y == rhs.y)) + { + return true; + } + return false; +} + + +bool Point::operator!=(const Point& rhs) const +{ + if ((x != rhs.x) || (y != rhs.y)) + { + return true; + } + return false; +} + + +// Just defined to allow std::set. Not particularly meaningful! +bool Point::operator<(const Point& rhs) const +{ + if (x == rhs.x) + { + return (y < rhs.y); + } + return (x < rhs.x); +} + + +double& Point::operator[](const unsigned int dimension) +{ + COLA_ASSERT((dimension == 0) || (dimension == 1)); + return ((dimension == 0) ? x : y); +} + + +const double& Point::operator[](const unsigned int dimension) const +{ + COLA_ASSERT((dimension == 0) || (dimension == 1)); + return ((dimension == 0) ? x : y); +} + + +ReferencingPolygon::ReferencingPolygon(const Polygon& poly, const Router *router) + : PolygonInterface(), + _id(poly._id), + ps(poly.size()) +{ + COLA_ASSERT(router != NULL); + for (size_t i = 0; i < poly.size(); ++i) + { + const Polygon *polyPtr = NULL; + for (ShapeRefList::const_iterator sh = router->shapeRefs.begin(); + sh != router->shapeRefs.end(); ++sh) + { + if ((*sh)->id() == poly.ps[i].id) + { + const Polygon& poly = (*sh)->polygon(); + polyPtr = &poly; + break; + } + } + COLA_ASSERT(polyPtr != NULL); + ps[i] = std::make_pair(polyPtr, poly.ps[i].vn); + } +} + + +ReferencingPolygon::ReferencingPolygon() + : PolygonInterface() +{ + clear(); +} + + +void ReferencingPolygon::clear(void) +{ + ps.clear(); +} + + +bool ReferencingPolygon::empty(void) const +{ + return ps.empty(); +} + + +size_t ReferencingPolygon::size(void) const +{ + return ps.size(); +} + + +int ReferencingPolygon::id(void) const +{ + return _id; +} + + +const Point& ReferencingPolygon::at(size_t index) const +{ + COLA_ASSERT(index < size()); + const Polygon& poly = *(ps[index].first); + unsigned short poly_index = ps[index].second; + COLA_ASSERT(poly_index < poly.size()); + + return poly.ps[poly_index]; +} + + +void PolygonInterface::getBoundingRect(double *minX, double *minY, + double *maxX, double *maxY) const +{ + double progressiveMinX = DBL_MAX; + double progressiveMinY = DBL_MAX; + double progressiveMaxX = -DBL_MAX; + double progressiveMaxY = -DBL_MAX; + + for (size_t i = 0; i < size(); ++i) + { + progressiveMinX = std::min(progressiveMinX, at(i).x); + progressiveMinY = std::min(progressiveMinY, at(i).y); + progressiveMaxX = std::max(progressiveMaxX, at(i).x); + progressiveMaxY = std::max(progressiveMaxY, at(i).y); + } + + if (minX) + { + *minX = progressiveMinX; + } + if (maxX) + { + *maxX = progressiveMaxX; + } + if (minY) + { + *minY = progressiveMinY; + } + if (maxY) + { + *maxY = progressiveMaxY; + } +} + + +Polygon::Polygon() + : PolygonInterface() +{ + clear(); +} + + +Polygon::Polygon(const int pn) + : PolygonInterface(), + ps(pn) +{ +} + + +Polygon::Polygon(const PolygonInterface& poly) + : PolygonInterface(), + _id(poly.id()), + ps(poly.size()) +{ + for (size_t i = 0; i < poly.size(); ++i) + { + ps[i] = poly.at(i); + } +} + + +void Polygon::clear(void) +{ + ps.clear(); + ts.clear(); +} + + +bool Polygon::empty(void) const +{ + return ps.empty(); +} + + +size_t Polygon::size(void) const +{ + return ps.size(); +} + + +int Polygon::id(void) const +{ + return _id; +} + + +const Point& Polygon::at(size_t index) const +{ + COLA_ASSERT(index < size()); + + return ps[index]; +} + + +static const unsigned int SHORTEN_NONE = 0; +static const unsigned int SHORTEN_START = 1; +static const unsigned int SHORTEN_END = 2; +static const unsigned int SHORTEN_BOTH = SHORTEN_START | SHORTEN_END; + +// shorten_line(): +// Given the two endpoints of a line segment, this function adjusts the +// endpoints of the line to shorten the line by shorten_length at either +// or both ends. +// +static void shorten_line(double& x1, double& y1, double& x2, double& y2, + const unsigned int mode, const double shorten_length) +{ + if (mode == SHORTEN_NONE) + { + return; + } + + double rise = y1 - y2; + double run = x1 - x2; + double disty = fabs(rise); + double distx = fabs(run); + + // Handle case where shorten length is greater than the length of the + // line segment. + if ((mode == SHORTEN_BOTH) && + (((distx > disty) && ((shorten_length * 2) > distx)) || + ((disty >= distx) && ((shorten_length * 2) > disty)))) + { + x1 = x2 = x1 - (run / 2); + y1 = y2 = y1 - (rise / 2); + return; + } + else if ((mode == SHORTEN_START) && + (((distx > disty) && (shorten_length > distx)) || + ((disty >= distx) && (shorten_length > disty)))) + { + x1 = x2; + y1 = y2; + return; + } + else if ((mode == SHORTEN_END) && + (((distx > disty) && (shorten_length > distx)) || + ((disty >= distx) && (shorten_length > disty)))) + { + x2 = x1; + y2 = y1; + return; + } + + // Handle orthogonal line segments. + if (x1 == x2) + { + // Vertical + int sign = (y1 < y2) ? 1: -1; + + if (mode & SHORTEN_START) + { + y1 += (sign * shorten_length); + } + if (mode & SHORTEN_END) + { + y2 -= (sign * shorten_length); + } + return; + } + else if (y1 == y2) + { + // Horizontal + int sign = (x1 < x2) ? 1: -1; + + if (mode & SHORTEN_START) + { + x1 += (sign * shorten_length); + } + if (mode & SHORTEN_END) + { + x2 -= (sign * shorten_length); + } + return; + } + + int xpos = (x1 < x2) ? -1 : 1; + int ypos = (y1 < y2) ? -1 : 1; + + double tangent = rise / run; + + if (mode & SHORTEN_END) + { + if (disty > distx) + { + y2 += shorten_length * ypos; + x2 += shorten_length * ypos * (1 / tangent); + } + else if (disty < distx) + { + y2 += shorten_length * xpos * tangent; + x2 += shorten_length * xpos; + } + } + + if (mode & SHORTEN_START) + { + if (disty > distx) + { + y1 -= shorten_length * ypos; + x1 -= shorten_length * ypos * (1 / tangent); + } + else if (disty < distx) + { + y1 -= shorten_length * xpos * tangent; + x1 -= shorten_length * xpos; + } + } +} + + +void Polygon::translate(const double xDist, const double yDist) +{ + for (size_t i = 0; i < size(); ++i) + { + ps[i].x += xDist; + ps[i].y += yDist; + } +} + + +Polygon Polygon::simplify(void) const +{ + Polygon simplified = *this; + std::vector::iterator it = simplified.ps.begin(); + if (it != simplified.ps.end()) ++it; + + // Combine collinear line segments into single segments: + for (size_t j = 2; j < simplified.size(); ) + { + if (vecDir(simplified.ps[j - 2], simplified.ps[j - 1], + simplified.ps[j]) == 0) + { + // These three points make up two collinear segments, so just + // compine them into a single segment. + it = simplified.ps.erase(it); + } + else + { + ++j; + ++it; + } + } + + return simplified; +} + + +#define mid(a, b) ((a < b) ? a + ((b - a) / 2) : b + ((a - b) / 2)) + + +// curvedPolyline(): +// Returns a curved approximation of this multi-segment PolyLine, with +// the corners replaced by smooth Bezier curves. curve_amount describes +// how large to make the curves. +// The ts value for each point in the returned Polygon describes the +// drawing operation: 'M' (move) marks the first point, a line segment +// is marked with an 'L' and three 'C's (along with the previous point) +// describe the control points of a Bezier curve. +// +Polygon Polygon::curvedPolyline(const double curve_amount, + const bool closed) const +{ + Polygon simplified = this->simplify(); + + Polygon curved; + size_t num_of_points = size(); + if (num_of_points <= 2) + { + // There is only a single segment, do nothing. + curved = *this; + curved.ts.push_back('M'); + curved.ts.push_back('L'); + return curved; + } + + // Build the curved polyline: + curved._id = _id; + double last_x = 0; + double last_y = 0; + if (closed) + { + double x1 = simplified.ps[0].x; + double y1 = simplified.ps[0].y; + double x2 = simplified.ps[1].x; + double y2 = simplified.ps[1].y; + shorten_line(x1, y1, x2, y2, SHORTEN_START, curve_amount); + curved.ps.push_back(Point(x1, y1)); + curved.ts.push_back('M'); + } + else + { + curved.ps.push_back(ps[0]); + curved.ts.push_back('M'); + } + + size_t simpSize = simplified.size(); + size_t finish = (closed) ? simpSize + 2 : simpSize; + for (size_t j = 1; j < finish; ++j) + { + double x1 = simplified.ps[(simpSize + j - 1) % simpSize].x; + double y1 = simplified.ps[(simpSize + j - 1) % simpSize].y; + double x2 = simplified.ps[j % simpSize].x; + double y2 = simplified.ps[j % simpSize].y; + + double old_x = x1; + double old_y = y1; + + unsigned int mode = SHORTEN_BOTH; + if (!closed) + { + if (j == 1) + { + mode = SHORTEN_END; + } + else if (j == (size() - 1)) + { + mode = SHORTEN_START; + } + } + shorten_line(x1, y1, x2, y2, mode, curve_amount); + + if (j > 1) + { + curved.ts.insert(curved.ts.end(), 3, 'C'); + curved.ps.push_back(Point(mid(last_x, old_x), mid(last_y, old_y))); + curved.ps.push_back(Point(mid(x1, old_x), mid(y1, old_y))); + curved.ps.push_back(Point(x1, y1)); + } + if (closed && (j == (finish - 1))) + { + // Close the path. + curved.ts.push_back('Z'); + curved.ps.push_back(Point(x1, y1)); + break; + } + curved.ts.push_back('L'); + curved.ps.push_back(Point(x2, y2)); + + last_x = x2; + last_y = y2; + } + + return curved; +} + + +Rectangle::Rectangle(const Point& topLeft, const Point& bottomRight) + : Polygon(4) +{ + double xMin = std::min(topLeft.x, bottomRight.x); + double xMax = std::max(topLeft.x, bottomRight.x); + double yMin = std::min(topLeft.y, bottomRight.y); + double yMax = std::max(topLeft.y, bottomRight.y); + + ps[0] = Point(xMax, yMin); + ps[1] = Point(xMax, yMax); + ps[2] = Point(xMin, yMax); + ps[3] = Point(xMin, yMin); +} + + +Rectangle::Rectangle(const Point& centre, const double width, + const double height) +{ + double halfWidth = width / 2.0; + double halfHeight = height / 2.0; + double xMin = centre.x - halfWidth; + double xMax = centre.x + halfWidth; + double yMin = centre.y - halfHeight; + double yMax = centre.y + halfHeight; + + ps[0] = Point(xMax, yMin); + ps[1] = Point(xMax, yMax); + ps[2] = Point(xMin, yMax); + ps[3] = Point(xMin, yMin); +} + + +} + diff --git a/src/libavoid/makefile b/src/libavoid/makefile new file mode 100644 index 000000000..e4f83a52d --- /dev/null +++ b/src/libavoid/makefile @@ -0,0 +1,17 @@ +# Convenience stub makefile to call the real Makefile. + + + +OBJEXT = o + +# Explicit so that it's the default rule. +all: + cd .. && $(MAKE) libavoid/all + +clean %.a %.$(OBJEXT): + cd .. && $(MAKE) libavoid/$@ + +.PHONY: all clean + +.SUFFIXES: +.SUFFIXES: .a .$(OBJEXT) diff --git a/src/libavoid/orthogonal.cpp b/src/libavoid/orthogonal.cpp new file mode 100644 index 000000000..747fd1f86 --- /dev/null +++ b/src/libavoid/orthogonal.cpp @@ -0,0 +1,2343 @@ +/* + * vim: ts=4 sw=4 et tw=0 wm=0 + * + * libavoid - Fast, Incremental, Object-avoiding Line Router + * + * Copyright (C) 2009 Monash University + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Author(s): Michael Wybrow +*/ + + +#include +#include +#include +#include +#include +#include + +#include "libavoid/router.h" +#include "libavoid/geomtypes.h" +#include "libavoid/shape.h" +#include "libavoid/orthogonal.h" +#include "libavoid/connector.h" +#include "libavoid/vpsc.h" +#include "libavoid/assertions.h" + +#ifdef LIBAVOID_SDL + #include +#endif + + +namespace Avoid { + + +static const double CHANNEL_MAX = 100000000; + +static const size_t XDIM = 0; +static const size_t YDIM = 1; + + +class ShiftSegment +{ + public: + // For shiftable segments. + ShiftSegment(ConnRef *conn, const size_t low, const size_t high, + bool isSBend, const size_t dim, double minLim, double maxLim) + : connRef(conn), + indexLow(low), + indexHigh(high), + sBend(isSBend), + fixed(false), + dimension(dim), + variable(NULL), + minSpaceLimit(minLim), + maxSpaceLimit(maxLim) + { + } + // For fixed segments. + ShiftSegment(ConnRef *conn, const size_t low, const size_t high, + const size_t dim) + : connRef(conn), + indexLow(low), + indexHigh(high), + sBend(false), + fixed(true), + dimension(dim), + variable(NULL) + { + // This has no space to shift. + minSpaceLimit = lowPoint()[dim]; + maxSpaceLimit = lowPoint()[dim]; + } + Point& lowPoint(void) + { + return connRef->displayRoute().ps[indexLow]; + } + Point& highPoint(void) + { + return connRef->displayRoute().ps[indexHigh]; + } + const Point& lowPoint(void) const + { + return connRef->displayRoute().ps[indexLow]; + } + const Point& highPoint(void) const + { + return connRef->displayRoute().ps[indexHigh]; + } + const int fixedOrder(bool& isFixed) const + { + if (fixed) + { + isFixed = true; + return 0; + } + if (lowC()) + { + return 1; + } + else if (highC()) + { + return -1; + } + return 0; + } + const int order(void) const + { + if (lowC()) + { + return -1; + } + else if (highC()) + { + return 1; + } + return 0; + } + bool operator<(const ShiftSegment& rhs) const + { + const Point& lowPt = lowPoint(); + const Point& rhsLowPt = rhs.lowPoint(); + + if (lowPt[dimension] != rhsLowPt[dimension]) + { + return lowPt[dimension] < rhsLowPt[dimension]; + } + return this < &rhs; + } + // This counts segments that are colliear and share an endpoint as + // overlapping. This allows them to be nudged apart where possible. + bool overlapsWith(const ShiftSegment& rhs, const size_t dim) const + { + size_t altDim = (dim + 1) % 2; + const Point& lowPt = lowPoint(); + const Point& highPt = highPoint(); + const Point& rhsLowPt = rhs.lowPoint(); + const Point& rhsHighPt = rhs.highPoint(); + if ( (lowPt[altDim] <= rhsHighPt[altDim]) && + (rhsLowPt[altDim] <= highPt[altDim])) + { + if ( (minSpaceLimit <= rhs.maxSpaceLimit) && + (rhs.minSpaceLimit <= maxSpaceLimit)) + { + return true; + } + } + return false; + } + + ConnRef *connRef; + size_t indexLow; + size_t indexHigh; + bool sBend; + bool fixed; + size_t dimension; + Variable *variable; + double minSpaceLimit; + double maxSpaceLimit; + private: + const bool lowC(void) const + { + // This is true if this is a cBend and its adjoining points + // are at lower positions. + if (!sBend && !fixed && (minSpaceLimit == lowPoint()[dimension])) + { + return true; + } + return false; + } + const bool highC(void) const + { + // This is true if this is a cBend and its adjoining points + // are at higher positions. + if (!sBend && !fixed && (maxSpaceLimit == lowPoint()[dimension])) + { + return true; + } + return false; + } +}; +typedef std::list ShiftSegmentList; + +bool cmpShiftSegment(const ShiftSegment& u, const ShiftSegment& v) +{ + return u < v; +} + + +struct Node; +struct CmpNodePos { bool operator()(const Node* u, const Node* v) const; }; + +typedef std::set NodeSet; +struct Node +{ + ShapeRef *v; + VertInf *c; + ShiftSegment *ss; + double pos; + double min[2], max[2]; + Node *firstAbove, *firstBelow; + NodeSet::iterator iter; + + Node(ShapeRef *v, const double p) + : v(v), + c(NULL), + ss(NULL), + pos(p), + firstAbove(NULL), + firstBelow(NULL) + { + //COLA_ASSERT(r->width()<1e40); + v->polygon().getBoundingRect(&min[0], &min[1], &max[0], &max[1]); + } + Node(VertInf *c, const double p) + : v(NULL), + c(c), + ss(NULL), + pos(p), + firstAbove(NULL), + firstBelow(NULL) + { + min[0] = max[0] = c->point.x; + min[1] = max[1] = c->point.y; + } + Node(ShiftSegment *ss, const double p) + : v(NULL), + c(NULL), + ss(ss), + pos(p), + firstAbove(NULL), + firstBelow(NULL) + { + // These values shouldn't ever be used, so they don't matter. + min[0] = max[0] = min[1] = max[1] = 0; + } + ~Node() + { + } + // Find the first Node above in the scanline that is a shape edge, + // and does not have an open or close event at this position (meaning + // it is just about to be removed). + double firstObstacleAbove(size_t dim) + { + Node *curr = firstAbove; + while (curr && (curr->ss || (curr->max[dim] > pos))) + { + curr = curr->firstAbove; + } + + if (curr) + { + return curr->max[dim]; + } + return -DBL_MAX; + } + // Find the first Node below in the scanline that is a shape edge, + // and does not have an open or close event at this position (meaning + // it is just about to be removed). + double firstObstacleBelow(size_t dim) + { + Node *curr = firstBelow; + while (curr && (curr->ss || (curr->min[dim] < pos))) + { + curr = curr->firstBelow; + } + + if (curr) + { + return curr->min[dim]; + } + return DBL_MAX; + } + // Mark all connector segments above in the scanline as being able + // to see to this shape edge. + void markShiftSegmentsAbove(size_t dim) + { + Node *curr = firstAbove; + while (curr && (curr->ss || (curr->pos > min[dim]))) + { + if (curr->ss && (curr->pos <= min[dim])) + { + curr->ss->maxSpaceLimit = + std::min(min[dim], curr->ss->maxSpaceLimit); + } + curr = curr->firstAbove; + } + } + // Mark all connector segments below in the scanline as being able + // to see to this shape edge. + void markShiftSegmentsBelow(size_t dim) + { + Node *curr = firstBelow; + while (curr && (curr->ss || (curr->pos < max[dim]))) + { + if (curr->ss && (curr->pos >= max[dim])) + { + curr->ss->minSpaceLimit = + std::max(max[dim], curr->ss->minSpaceLimit); + } + curr = curr->firstBelow; + } + } + bool findFirstPointAboveAndBelow(const size_t dim, double& firstAbovePos, + double& firstBelowPos, double& lastAbovePos, double& lastBelowPos) + { + bool clearVisibility = true; + firstAbovePos = -DBL_MAX; + firstBelowPos = DBL_MAX; + // We start looking left from the right side of the shape, + // and vice versa. + lastAbovePos = max[dim]; + lastBelowPos = min[dim]; + + // Find the first blocking edge above this point. Don't count the + // edges as we are travelling out of shapes we are inside, but then + // mark clearVisibility as false. + Node *curr = firstAbove; + while (curr && (curr->max[dim] > min[dim])) + { + lastAbovePos = std::min(curr->min[dim], lastAbovePos); + if ((curr->max[dim] >= min[dim]) && (curr->max[dim] <= max[dim])) + { + lastAbovePos = std::min(curr->max[dim], lastAbovePos); + } + lastBelowPos = std::max(curr->max[dim], lastBelowPos); + clearVisibility = false; + curr = curr->firstAbove; + } + if (curr) + { + firstAbovePos = curr->max[dim]; + } + while (curr) + { + // There might be a larger shape after this one in the ordering. + if (curr->max[dim] < min[dim]) + { + firstAbovePos = std::max(curr->max[dim], firstAbovePos); + } + curr = curr->firstAbove; + } + + // Find the first blocking edge below this point. Don't count the + // edges as we are travelling out of shapes we are inside, but then + // mark clearVisibility as false. + curr = firstBelow; + while (curr && (curr->min[dim] < max[dim])) + { + lastBelowPos = std::max(curr->max[dim], lastBelowPos); + if ((curr->min[dim] >= min[dim]) && (curr->min[dim] <= max[dim])) + { + lastBelowPos = std::max(curr->min[dim], lastBelowPos); + } + lastAbovePos = std::min(curr->min[dim], lastAbovePos); + clearVisibility = false; + curr = curr->firstBelow; + } + if (curr) + { + firstBelowPos = curr->min[dim]; + } + while (curr) + { + // There might be a larger shape after this one in the ordering. + if (curr->min[dim] > max[dim]) + { + firstBelowPos = std::min(curr->min[dim], firstBelowPos); + } + curr = curr->firstBelow; + } + + return clearVisibility; + } + double firstPointAbove(size_t dim) + { + Node *curr = firstAbove; + while (curr && (curr->max[dim] >= pos)) + { + curr = curr->firstAbove; + } + + if (curr) + { + return curr->max[dim]; + } + return -DBL_MAX; + } + double firstPointBelow(size_t dim) + { + Node *curr = firstBelow; + while (curr && (curr->min[dim] <= pos)) + { + curr = curr->firstBelow; + } + + if (curr) + { + return curr->min[dim]; + } + return DBL_MAX; + } + // This is a bit inefficient, but we won't need to do it once we have + // connection points. + bool isInsideShape(size_t dimension) + { + for (Node *curr = firstBelow; curr; curr = curr->firstBelow) + { + if ((curr->min[dimension] < pos) && (pos < curr->max[dimension])) + { + return true; + } + } + for (Node *curr = firstAbove; curr; curr = curr->firstAbove) + { + if ((curr->min[dimension] < pos) && (pos < curr->max[dimension])) + { + return true; + } + } + return false; + } +}; + + +bool CmpNodePos::operator() (const Node* u, const Node* v) const +{ + if (u->pos != v->pos) + { + return u->pos < v->pos; + } + + // Use the pointers to the base objects to differentiate them. + void *up = (u->v) ? (void *) u->v : + ((u->c) ? (void *) u->c : (void *) u->ss); + void *vp = (v->v) ? (void *) v->v : + ((v->c) ? (void *) v->c : (void *) v->ss); + return up < vp; +} + + +// Note: Open must come first. +typedef enum { + Open = 1, + SegOpen = 2, + ConnPoint = 3, + SegClose = 4, + Close = 5 +} EventType; + + +struct Event +{ + Event(EventType t, Node *v, double p) + : type(t), + v(v), + pos(p) + {}; + EventType type; + Node *v; + double pos; +}; + +Event **events; + + +// Used for quicksort. Must return <0, 0, or >0. +int compare_events(const void *a, const void *b) +{ + Event *ea = *(Event**) a; + Event *eb = *(Event**) b; + if (ea->pos != eb->pos) + { + return (ea->pos < eb->pos) ? -1 : 1; + } + if (ea->type != eb->type) + { + return ea->type - eb->type; + } + COLA_ASSERT(ea->v != eb->v); + return ea->v - eb->v; +} + + +// Returns a bitfield of the direction of visibility (in this dimension) +// made up of ConnDirDown (for visibility towards lower position values) +// and ConnDirUp (for visibility towards higher position values). +// +static ConnDirFlags getPosVertInfDirection(VertInf *v, size_t dim) +{ + if (dim == XDIM) // X-dimension + { + unsigned int dirs = v->visDirections & (ConnDirLeft | ConnDirRight); + if (dirs == (ConnDirLeft | ConnDirRight)) + { + return (ConnDirDown | ConnDirUp); + } + else if (dirs == ConnDirLeft) + { + return ConnDirDown; + } + else if (dirs == ConnDirRight) + { + return ConnDirUp; + } + } + else if (dim == YDIM) // Y-dimension + { + unsigned int dirs = v->visDirections & (ConnDirDown | ConnDirUp); + if (dirs == (ConnDirDown | ConnDirUp)) + { + return (ConnDirDown | ConnDirUp); + } + else if (dirs == ConnDirDown) + { + // For libavoid the Y-axis points downwards, so in terms of + // smaller or larger position values, Down is Up and vice versa. + return ConnDirUp; + } + else if (dirs == ConnDirUp) + { + // For libavoid the Y-axis points downwards, so in terms of + // smaller or larger position values, Down is Up and vice versa. + return ConnDirDown; + } + } + + // Can occur for ConnDirNone visibility. + return ConnDirNone; +} + + +struct PosVertInf +{ + PosVertInf(double p, VertInf *vI, ConnDirFlags d = ConnDirNone) + : pos(p), + vert(vI), + dir(d) + { + } + + bool operator<(const PosVertInf& rhs) const + { + if (pos != rhs.pos) + { + return pos < rhs.pos; + } + return vert < rhs.vert; + } + + double pos; + VertInf *vert; + + // A bitfield marking the direction of visibility (in this dimension) + // made up of ConnDirDown (for visibility towards lower position values) + // and ConnDirUp (for visibility towards higher position values). + // + ConnDirFlags dir; +}; + + +struct CmpVertInf +{ + bool operator()(const VertInf* u, const VertInf* v) const + { + // Comparator for VertSet, an ordered set of VertInf pointers. + // It is assumed vertical sets of points will all have the same + // x position and horizontal sets all share a y position, so this + // method can be used to sort both these sets. + COLA_ASSERT((u->point.x == v->point.x) || (u->point.y == v->point.y)); + if (u->point.x != v->point.x) + { + return u->point.x < v->point.x; + } + else if (u->point.y != v->point.y) + { + return u->point.y < v->point.y; + } + return u < v; + } +}; + + +typedef std::set VertSet; + +// A set of points to break the line segment, +// along with vertices for these points. +typedef std::set BreakpointSet; + +// Temporary structure used to store the possible horizontal visibility +// lines arising from the vertical sweep. +class LineSegment +{ +public: + LineSegment(const double& b, const double& f, const double& p, + bool ss = false, VertInf *bvi = NULL, VertInf *fvi = NULL) + : begin(b), + finish(f), + pos(p), + shapeSide(false) + { + COLA_ASSERT(begin < finish); + + if (bvi) + { + vertInfs.insert(bvi); + } + if (fvi) + { + vertInfs.insert(fvi); + } + } + LineSegment(const double& bf, const double& p, VertInf *bfvi = NULL) + : begin(bf), + finish(bf), + pos(p), + shapeSide(false) + { + if (bfvi) + { + vertInfs.insert(bfvi); + } + } + + // Order by begin, pos, finish. + bool operator<(const LineSegment& rhs) const + { + if (begin != rhs.begin) + { + return begin < rhs.begin; + } + if (pos != rhs.pos) + { + return pos < rhs.pos; + } + if (finish != rhs.finish) + { + return finish < rhs.finish; + } + COLA_ASSERT(shapeSide == rhs.shapeSide); + return false; + } + + bool overlaps(const LineSegment& rhs) const + { + if ((begin == rhs.begin) && (pos == rhs.pos) && + (finish == rhs.finish)) + { + // Lines are exactly equal. + return true; + } + + if (pos == rhs.pos) + { + if (((begin >= rhs.begin) && (begin <= rhs.finish)) || + ((rhs.begin >= begin) && (rhs.begin <= finish)) ) + { + // They are colinear and overlap by some amount. + return true; + } + } + return false; + } + + void mergeVertInfs(const LineSegment& segment) + { + begin = std::min(begin, segment.begin); + finish = std::max(finish, segment.finish); + vertInfs.insert(segment.vertInfs.begin(), segment.vertInfs.end()); + } + + VertInf *beginVertInf(void) const + { + if (vertInfs.empty()) + { + return NULL; + } + return *vertInfs.begin(); + } + VertInf *finishVertInf(void) const + { + if (vertInfs.empty()) + { + return NULL; + } + return *vertInfs.rbegin(); + } + + VertInf *commitPositionX(Router *router, double posX) + { + VertInf *found = NULL; + for (VertSet::iterator v = vertInfs.begin(); + v != vertInfs.end(); ++v) + { + if ((*v)->point.x == posX) + { + found = *v; + break; + } + } + if (!found) + { + found = new VertInf(router, dummyOrthogID, Point(posX, pos)); + vertInfs.insert(found); + } + return found; + } + // Set begin endpoint vertex if none has been assigned. + void commitBegin(Router *router, VertInf *vert = NULL) + { + if (vert) + { + vertInfs.insert(vert); + } + + if (vertInfs.empty() || + ((*vertInfs.begin())->point.x != begin)) + { + vertInfs.insert(new + VertInf(router, dummyOrthogID, Point(begin, pos))); + } + } + + // Set begin endpoint vertex if none has been assigned. + void commitFinish(Router *router, VertInf *vert = NULL) + { + if (vert) + { + vertInfs.insert(vert); + } + + if (vertInfs.empty() || + ((*vertInfs.rbegin())->point.x != finish)) + { + vertInfs.insert(new + VertInf(router, dummyOrthogID, Point(finish, pos))); + } + } + + // Converts a section of the points list to a set of breakPoints. + // Returns the first of the intersection points occuring at finishPos. + VertSet::iterator addSegmentsUpTo(Router *router, double finishPos) + { + VertSet::iterator firstIntersectionPt = vertInfs.end(); + for (VertSet::iterator vert = vertInfs.begin(); + vert != vertInfs.end(); ++vert) + { + if ((*vert)->point.x > finishPos) + { + // We're done. + break; + } + + breakPoints.insert(PosVertInf((*vert)->point.x, (*vert), + getPosVertInfDirection(*vert, XDIM))); + + if ((firstIntersectionPt == vertInfs.end()) && + ((*vert)->point.x == finishPos)) + { + firstIntersectionPt = vert; + } + } + // Returns the first of the intersection points at finishPos. + return firstIntersectionPt; + } + + // Add visibility edge(s) for this segment. There may be multiple if + // one of the endpoints is shared by multiple connector endpoints. + void addEdgeHorizontal(Router *router) + { + commitBegin(router); + commitFinish(router); + + addSegmentsUpTo(router, finish); + } + + // Add visibility edge(s) for this segment up until an intersection. + // Then, move the segment beginning to the intersection point, so we + // later only consider the remainder of the segment. + // There may be multiple segments added to the graph if the beginning + // endpoint of the segment is shared by multiple connector endpoints. + VertSet addEdgeHorizontalTillIntersection(Router *router, + LineSegment& vertLine) + { + VertSet intersectionSet; + + commitBegin(router); + + // Does a vertex already exist for this point. + commitPositionX(router, vertLine.pos); + + // Generate segments and set end iterator to the first point + // at the intersection position. + VertSet::iterator restBegin = addSegmentsUpTo(router, vertLine.pos); + + // Add the intersections points to intersectionSet. + VertSet::iterator restEnd = restBegin; + while ((restEnd != vertInfs.end()) && + (*restEnd)->point.x == vertLine.pos) + { + ++restEnd; + } + intersectionSet.insert(restBegin, restEnd); + + // Adjust segment to remove processed portion. + begin = vertLine.pos; + vertInfs.erase(vertInfs.begin(), restBegin); + + return intersectionSet; + } + + // Insert vertical breakpoints. + void insertBreakpointsBegin(Router *router, LineSegment& vertLine) + { + VertInf *vert = NULL; + if (pos == vertLine.begin && vertLine.beginVertInf()) + { + vert = vertLine.beginVertInf(); + } + else if (pos == vertLine.finish && vertLine.finishVertInf()) + { + vert = vertLine.finishVertInf(); + } + commitBegin(router, vert); + + for (VertSet::iterator v = vertInfs.begin(); + v != vertInfs.end(); ++v) + { + if ((*v)->point.x == begin) + { + vertLine.breakPoints.insert(PosVertInf(pos, *v, + getPosVertInfDirection(*v, YDIM))); + } + } + } + + // Insert vertical breakpoints. + void insertBreakpointsFinish(Router *router, LineSegment& vertLine) + { + VertInf *vert = NULL; + if (pos == vertLine.begin && vertLine.beginVertInf()) + { + vert = vertLine.beginVertInf(); + } + else if (pos == vertLine.finish && vertLine.finishVertInf()) + { + vert = vertLine.finishVertInf(); + } + commitFinish(router, vert); + + for (VertSet::iterator v = vertInfs.begin(); + v != vertInfs.end(); ++v) + { + if ((*v)->point.x == finish) + { + vertLine.breakPoints.insert(PosVertInf(pos, *v, + getPosVertInfDirection(*v, YDIM))); + } + } + } + void generateVisibilityEdgesFromBreakpointSet(Router *router, size_t dim) + { + if ((breakPoints.begin())->pos != begin) + { + if (!beginVertInf()) + { + Point point(pos, pos); + point[dim] = begin; + // Add begin point if it didn't intersect another line. + VertInf *vert = new VertInf(router, dummyOrthogID, point); + breakPoints.insert(PosVertInf(begin, vert)); + } + } + if ((breakPoints.rbegin())->pos != finish) + { + if (!finishVertInf()) + { + Point point(pos, pos); + point[dim] = finish; + // Add finish point if it didn't intersect another line. + VertInf *vert = new VertInf(router, dummyOrthogID, point); + breakPoints.insert(PosVertInf(finish, vert)); + } + } + + const bool orthogonal = true; + BreakpointSet::iterator vert, last; + for (vert = last = breakPoints.begin(); vert != breakPoints.end();) + { + BreakpointSet::iterator firstPrev = last; + while (last->vert->point[dim] != vert->vert->point[dim]) + { + COLA_ASSERT(vert != last); + // Assert points are not at the same position. + COLA_ASSERT(vert->vert->point != last->vert->point); + + if ( !(vert->vert->id.isShape || last->vert->id.isShape)) + { + // Here we have a pair of two endpoints that are both + // connector endpoints and both are inside a shape. + + // Give vert visibility back to the first non-connector + // endpoint vertex (i.e., the side of the shape). + BreakpointSet::iterator side = last; + while (!side->vert->id.isShape) + { + if (side == breakPoints.begin()) + { + break; + } + --side; + } + bool canSeeDown = (vert->dir & ConnDirDown); + if (canSeeDown && side->vert->id.isShape) + { + EdgeInf *edge = new + EdgeInf(side->vert, vert->vert, orthogonal); + edge->setDist(vert->vert->point[dim] - + side->vert->point[dim]); + } + + // Give last visibility back to the first non-connector + // endpoint vertex (i.e., the side of the shape). + side = vert; + while ((side != breakPoints.end()) && + !side->vert->id.isShape) + { + ++side; + } + bool canSeeUp = (last->dir & ConnDirUp); + if (canSeeUp && (side != breakPoints.end())) + { + EdgeInf *edge = new + EdgeInf(last->vert, side->vert, orthogonal); + edge->setDist(side->vert->point[dim] - + last->vert->point[dim]); + } + } + + // The normal case. + // + // Note: It's okay to give two connector endpoints visbility + // here since we only consider the partner endpoint as a + // candidate while searching if it is the other endpoint of + // the connector in question. + // + bool generateEdge = true; + if (!last->vert->id.isShape && !(last->dir & ConnDirUp)) + { + generateEdge = false; + } + else if (!vert->vert->id.isShape && !(vert->dir & ConnDirDown)) + { + generateEdge = false; + } + if (generateEdge) + { + EdgeInf *edge = + new EdgeInf(last->vert, vert->vert, orthogonal); + edge->setDist(vert->vert->point[dim] - + last->vert->point[dim]); + } + + ++last; + } + + ++vert; + + if ((vert != breakPoints.end()) && + (last->vert->point[dim] == vert->vert->point[dim])) + { + // Still looking at same pair, just reset prev number pointer. + last = firstPrev; + } + else + { + // vert has moved to the beginning of a number number group. + // Last is now in the right place, so do nothing. + } + } + } + + double begin; + double finish; + double pos; + bool shapeSide; + + VertSet vertInfs; + BreakpointSet breakPoints; +private: + // MSVC wants to generate the assignment operator and the default + // constructor, but fails. Therefore we declare them private and + // don't implement them. + LineSegment & operator=(LineSegment const &); + LineSegment(); +}; + +typedef std::list SegmentList; + +class SegmentListWrapper +{ + public: + LineSegment *insert(LineSegment segment) + { + SegmentList::iterator found = _list.end(); + for (SegmentList::iterator curr = _list.begin(); + curr != _list.end(); ++curr) + { + if (curr->overlaps(segment)) + { + if (found != _list.end()) + { + // This is not the first segment that overlaps, + // so we need to merge and then delete an existing + // segment. + curr->mergeVertInfs(*found); + _list.erase(found); + found = curr; + } + else + { + // This is the first overlapping segment, so just + // merge the new segment with this one. + curr->mergeVertInfs(segment); + found = curr; + } + } + } + + if (found == _list.end()) + { + // Add this line. + _list.push_back(segment); + return &(_list.back()); + } + + return &(*found); + } + SegmentList& list(void) + { + return _list; + } + private: + SegmentList _list; +}; + + +// Given a router instance and a set of possible horizontal segments, and a +// possible vertical visibility segment, compute and add edges to the +// orthogonal visibility graph for all the visibility edges. +static void intersectSegments(Router *router, SegmentList& segments, + LineSegment& vertLine) +{ + COLA_ASSERT(vertLine.beginVertInf() == NULL); + COLA_ASSERT(vertLine.finishVertInf() == NULL); + for (SegmentList::iterator it = segments.begin(); it != segments.end(); ) + { + LineSegment& horiLine = *it; + + bool inVertSegRegion = ((vertLine.begin <= horiLine.pos) && + (vertLine.finish >= horiLine.pos)); + + if (horiLine.finish < vertLine.pos) + { + // Add horizontal visibility segment. + horiLine.addEdgeHorizontal(router); + + size_t dim = XDIM; // x-dimension + horiLine.generateVisibilityEdgesFromBreakpointSet(router, dim); + + // We've now swept past this horizontal segment, so delete. + it = segments.erase(it); + continue; + } + else if (horiLine.begin > vertLine.pos) + { + // We've yet to reach this segment in the sweep, so ignore. + ++it; + continue; + } + else if (horiLine.begin == vertLine.pos) + { + if (inVertSegRegion) + { + horiLine.insertBreakpointsBegin(router, vertLine); + } + } + else if (horiLine.finish == vertLine.pos) + { + if (inVertSegRegion) + { + // Add horizontal visibility segment. + horiLine.addEdgeHorizontal(router); + + horiLine.insertBreakpointsFinish(router, vertLine); + + size_t dim = XDIM; // x-dimension + horiLine.generateVisibilityEdgesFromBreakpointSet(router, dim); + + // And we've now finished with the segment, so delete. + it = segments.erase(it); + continue; + } + } + else + { + COLA_ASSERT(horiLine.begin < vertLine.pos); + COLA_ASSERT(horiLine.finish > vertLine.pos); + + if (inVertSegRegion) + { + // Add horizontal visibility segment. + VertSet intersectionVerts = + horiLine.addEdgeHorizontalTillIntersection( + router, vertLine); + + for (VertSet::iterator v = intersectionVerts.begin(); + v != intersectionVerts.end(); ++v) + { + vertLine.breakPoints.insert(PosVertInf(horiLine.pos, *v, + getPosVertInfDirection(*v, YDIM))); + } + } + } + ++it; + } + + // Split breakPoints set into visibility segments. + size_t dimension = YDIM; // y-dimension + vertLine.generateVisibilityEdgesFromBreakpointSet(router, dimension); +} + + +// Processes an event for the vertical sweep used for computing the static +// orthogonal visibility graph. This adds possible visibility sgments to +// the segments list. +// The first pass is adding the event to the scanline, the second is for +// processing the event and the third for removing it from the scanline. +static void processEventVert(Router *router, NodeSet& scanline, + SegmentListWrapper& segments, Event *e, unsigned int pass) +{ + Node *v = e->v; + + if ( ((pass == 1) && (e->type == Open)) || + ((pass == 2) && (e->type == ConnPoint)) ) + { + std::pair result = scanline.insert(v); + v->iter = result.first; + COLA_ASSERT(result.second); + + NodeSet::iterator it = v->iter; + // Work out neighbours + if (it != scanline.begin()) + { + Node *u = *(--it); + v->firstAbove = u; + u->firstBelow = v; + } + it = v->iter; + if (++it != scanline.end()) + { + Node *u = *it; + v->firstBelow = u; + u->firstAbove = v; + } + } + + if (pass == 2) + { + if ((e->type == Open) || (e->type == Close)) + { + // Shape edge positions. + double minShape = v->min[0]; + double maxShape = v->max[0]; + // As far as we can see. + double minLimit, maxLimit; + double minLimitMax, maxLimitMin; + v->findFirstPointAboveAndBelow(0, minLimit, maxLimit, + minLimitMax, maxLimitMin); + + // Only difference between Open and Close is whether the line + // segments are at the top or bottom of the shape. Decide here. + double lineY = (e->type == Open) ? v->min[1] : v->max[1]; + + if (minLimitMax >= maxLimitMin) + { + // Insert possible visibility segments. + VertInf *vI1 = new VertInf(router, dummyOrthogID, + Point(minShape, lineY)); + VertInf *vI2 = new VertInf(router, dummyOrthogID, + Point(maxShape, lineY)); + + // There are no overlapping shapes, so give full visibility. + if (minLimit < minShape) + { + segments.insert(LineSegment(minLimit, minShape, lineY, + true, NULL, vI1)); + } + segments.insert(LineSegment(minShape, maxShape, lineY, + true, vI1, vI2)); + if (maxShape < maxLimit) + { + segments.insert(LineSegment(maxShape, maxLimit, lineY, + true, vI2, NULL)); + } + } + else + { + if ((minLimitMax > minLimit) && (minLimitMax >= minShape)) + { + segments.insert(LineSegment(minLimit, minLimitMax, lineY, + true, NULL, NULL)); + } + if ((maxLimitMin < maxLimit) && (maxLimitMin <= maxShape)) + { + segments.insert(LineSegment(maxLimitMin, maxLimit, lineY, + true, NULL, NULL)); + } + } + } + else if (e->type == ConnPoint) + { + // Connection point. + VertInf *centreVert = e->v->c; + Point& cp = centreVert->point; + + // As far as we can see. + double minLimit = v->firstPointAbove(0); + double maxLimit = v->firstPointBelow(0); + bool inShape = v->isInsideShape(0); + + LineSegment *line1 = NULL, *line2 = NULL; + if (!inShape || (centreVert->visDirections & ConnDirLeft)) + { + line1 = segments.insert(LineSegment(minLimit, cp.x, e->pos, + true, NULL, centreVert)); + } + if (!inShape || (centreVert->visDirections & ConnDirRight)) + { + line2 = segments.insert(LineSegment(cp.x, maxLimit, e->pos, + true, centreVert, NULL)); + } + if (!line1 && !line2) + { + // Add a point segment for the centre point. + segments.insert(LineSegment(cp.x, e->pos, centreVert)); + } + + if (!inShape) + { + // This is not contained within a shape so add a normal + // visibility graph point here too (since paths won't route + // *through* connector endpoint vertices). + if (line1 || line2) + { + VertInf *cent = new VertInf(router, dummyOrthogID, cp); + if (line1) + { + line1->vertInfs.insert(cent); + } + if (line2) + { + line2->vertInfs.insert(cent); + } + } + } + } + } + + if ( ((pass == 3) && (e->type == Close)) || + ((pass == 2) && (e->type == ConnPoint)) ) + { + // Clean up neighbour pointers. + Node *l = v->firstAbove, *r = v->firstBelow; + if (l != NULL) + { + l->firstBelow = v->firstBelow; + } + if (r != NULL) + { + r->firstAbove = v->firstAbove; + } + + if (e->type == ConnPoint) + { + scanline.erase(v->iter); + delete v; + } + else // if (e->type == Close) + { + size_t result; + result = scanline.erase(v); + COLA_ASSERT(result == 1); + delete v; + } + } +} + + +// Processes an event for the vertical sweep used for computing the static +// orthogonal visibility graph. This adds possible visibility sgments to +// the segments list. +// The first pass is adding the event to the scanline, the second is for +// processing the event and the third for removing it from the scanline. +static void processEventHori(Router *router, NodeSet& scanline, + SegmentListWrapper& segments, Event *e, unsigned int pass) +{ + Node *v = e->v; + + if ( ((pass == 1) && (e->type == Open)) || + ((pass == 2) && (e->type == ConnPoint)) ) + { + std::pair result = scanline.insert(v); + v->iter = result.first; + COLA_ASSERT(result.second); + + NodeSet::iterator it = v->iter; + // Work out neighbours + if (it != scanline.begin()) + { + Node *u = *(--it); + v->firstAbove = u; + u->firstBelow = v; + } + it = v->iter; + if (++it != scanline.end()) + { + Node *u = *it; + v->firstBelow = u; + u->firstAbove = v; + } + } + + if (pass == 2) + { + if ((e->type == Open) || (e->type == Close)) + { + // Shape edge positions. + double minShape = v->min[1]; + double maxShape = v->max[1]; + // As far as we can see. + double minLimit, maxLimit; + double minLimitMax, maxLimitMin; + v->findFirstPointAboveAndBelow(1, minLimit, maxLimit, + minLimitMax, maxLimitMin); + + // Only difference between Open and Close is whether the line + // segments are at the left or right of the shape. Decide here. + double lineX = (e->type == Open) ? v->min[0] : v->max[0]; + + if (minLimitMax >= maxLimitMin) + { + LineSegment vertSeg = LineSegment(minLimit, maxLimit, lineX); + segments.insert(vertSeg); + } + else + { + if ((minLimitMax > minLimit) && (minLimitMax >= minShape)) + { + LineSegment vertSeg = + LineSegment(minLimit, minLimitMax, lineX); + segments.insert(vertSeg); + } + if ((maxLimitMin < maxLimit) && (maxLimitMin <= maxShape)) + { + LineSegment vertSeg = + LineSegment(maxLimitMin, maxLimit, lineX); + segments.insert(vertSeg); + } + } + } + else if (e->type == ConnPoint) + { + // Connection point. + VertInf *centreVert = e->v->c; + Point& cp = centreVert->point; + + // As far as we can see. + double minLimit = v->firstPointAbove(1); + double maxLimit = v->firstPointBelow(1); + bool inShape = v->isInsideShape(1); + + if (!inShape || (centreVert->visDirections & ConnDirUp)) + { + segments.insert(LineSegment(minLimit, cp.y, e->pos)); + } + if (!inShape || (centreVert->visDirections & ConnDirDown)) + { + segments.insert(LineSegment(cp.y, maxLimit, e->pos)); + } + } + } + + if ( ((pass == 3) && (e->type == Close)) || + ((pass == 2) && (e->type == ConnPoint)) ) + { + // Clean up neighbour pointers. + Node *l = v->firstAbove, *r = v->firstBelow; + if (l != NULL) + { + l->firstBelow = v->firstBelow; + } + if (r != NULL) + { + r->firstAbove = v->firstAbove; + } + + if (e->type == ConnPoint) + { + scanline.erase(v->iter); + delete v; + } + else // if (e->type == Close) + { + size_t result; + result = scanline.erase(v); + COLA_ASSERT(result == 1); + delete v; + } + } +} + + +extern void generateStaticOrthogonalVisGraph(Router *router) +{ + const size_t n = router->shapeRefs.size(); + const unsigned cpn = router->vertices.connsSize(); + // Set up the events for the vertical sweep. + size_t totalEvents = (2 * n) + cpn; + events = new Event*[totalEvents]; + unsigned ctr = 0; + ShapeRefList::iterator shRefIt = router->shapeRefs.begin(); + for (unsigned i = 0; i < n; i++) + { + ShapeRef *shRef = *shRefIt; + double minX, minY, maxX, maxY; + shRef->polygon().getBoundingRect(&minX, &minY, &maxX, &maxY); + double midX = minX + ((maxX - minX) / 2); + Node *v = new Node(shRef, midX); + events[ctr++] = new Event(Open, v, minY); + events[ctr++] = new Event(Close, v, maxY); + + ++shRefIt; + } + for (VertInf *curr = router->vertices.connsBegin(); + curr && (curr != router->vertices.shapesBegin()); + curr = curr->lstNext) + { + Point& point = curr->point; + + Node *v = new Node(curr, point.x); + events[ctr++] = new Event(ConnPoint, v, point.y); + } + qsort((Event*)events, (size_t) totalEvents, sizeof(Event*), compare_events); + + // Process the vertical sweep. + // We do multiple passes over sections of the list so we can add relevant + // entries to the scanline that might follow, before process them. + SegmentListWrapper segments; + NodeSet scanline; + double thisPos = (totalEvents > 0) ? events[0]->pos : 0; + unsigned int posStartIndex = 0; + unsigned int posFinishIndex = 0; + for (unsigned i = 0; i <= totalEvents; ++i) + { + // If we have finished the current scanline or all events, then we + // process the events on the current scanline in a couple of passes. + if ((i == totalEvents) || (events[i]->pos != thisPos)) + { + posFinishIndex = i; + for (int pass = 2; pass <= 3; ++pass) + { + for (unsigned j = posStartIndex; j < posFinishIndex; ++j) + { + processEventVert(router, scanline, segments, + events[j], pass); + } + } + + if (i == totalEvents) + { + // We have cleaned up, so we can now break out of loop. + break; + } + + thisPos = events[i]->pos; + posStartIndex = i; + } + + // Do the first sweep event handling -- building the correct + // structure of the scanline. + const int pass = 1; + processEventVert(router, scanline, segments, events[i], pass); + } + COLA_ASSERT(scanline.size() == 0); + for (unsigned i = 0; i < totalEvents; ++i) + { + delete events[i]; + } + + segments.list().sort(); + + // Set up the events for the horizontal sweep. + SegmentListWrapper vertSegments; + ctr = 0; + shRefIt = router->shapeRefs.begin(); + for (unsigned i = 0; i < n; i++) + { + ShapeRef *shRef = *shRefIt; + double minX, minY, maxX, maxY; + shRef->polygon().getBoundingRect(&minX, &minY, &maxX, &maxY); + double midY = minY + ((maxY - minY) / 2); + Node *v = new Node(shRef, midY); + events[ctr++] = new Event(Open, v, minX); + events[ctr++] = new Event(Close, v, maxX); + + ++shRefIt; + } + for (VertInf *curr = router->vertices.connsBegin(); + curr && (curr != router->vertices.shapesBegin()); + curr = curr->lstNext) + { + Point& point = curr->point; + + Node *v = new Node(curr, point.y); + events[ctr++] = new Event(ConnPoint, v, point.x); + } + qsort((Event*)events, (size_t) totalEvents, sizeof(Event*), compare_events); + + // Process the horizontal sweep + thisPos = (totalEvents > 0) ? events[0]->pos : 0; + posStartIndex = 0; + posFinishIndex = 0; + for (unsigned i = 0; i <= totalEvents; ++i) + { + // If we have finished the current scanline or all events, then we + // process the events on the current scanline in a couple of passes. + if ((i == totalEvents) || (events[i]->pos != thisPos)) + { + posFinishIndex = i; + for (int pass = 2; pass <= 3; ++pass) + { + for (unsigned j = posStartIndex; j < posFinishIndex; ++j) + { + processEventHori(router, scanline, vertSegments, + events[j], pass); + } + } + + // Process the merged line segments. + vertSegments.list().sort(); + for (SegmentList::iterator curr = vertSegments.list().begin(); + curr != vertSegments.list().end(); ++curr) + { + intersectSegments(router, segments.list(), *curr); + } + vertSegments.list().clear(); + + if (i == totalEvents) + { + // We have cleaned up, so we can now break out of loop. + break; + } + + thisPos = events[i]->pos; + posStartIndex = i; + } + + // Do the first sweep event handling -- building the correct + // structure of the scanline. + const int pass = 1; + processEventHori(router, scanline, vertSegments, events[i], pass); + } + COLA_ASSERT(scanline.size() == 0); + for (unsigned i = 0; i < totalEvents; ++i) + { + delete events[i]; + } + delete [] events; + + // Add portions of the horizontal line that are after the final vertical + // position we considered. + for (SegmentList::iterator it = segments.list().begin(); + it != segments.list().end(); ) + { + LineSegment& horiLine = *it; + + horiLine.addEdgeHorizontal(router); + + size_t dim = XDIM; // x-dimension + horiLine.generateVisibilityEdgesFromBreakpointSet(router, dim); + + it = segments.list().erase(it); + } +} + + +//============================================================================ +// Path Adjustment code +//============================================================================ + + + + +// Processes sweep events used to determine each horizontal and vertical +// line segment in a connector's channel of visibility. +// Four calls to this function are made at each position by the scanline: +// 1) Handle all Close event processing. +// 2) Remove Close event objects from the scanline. +// 3) Add Open event objects to the scanline. +// 4) Handle all Open event processing. +// +static void processShiftEvent(Router *router, NodeSet& scanline, + ShiftSegmentList& segments, Event *e, size_t dim, + unsigned int pass) +{ + Node *v = e->v; + + if ( ((pass == 3) && (e->type == Open)) || + ((pass == 3) && (e->type == SegOpen)) ) + { + std::pair result = scanline.insert(v); + v->iter = result.first; + COLA_ASSERT(result.second); + + NodeSet::iterator it = v->iter; + // Work out neighbours + if (it != scanline.begin()) + { + Node *u = *(--it); + v->firstAbove = u; + u->firstBelow = v; + } + it = v->iter; + if (++it != scanline.end()) + { + Node *u = *it; + v->firstBelow = u; + u->firstAbove = v; + } + } + + if ( ((pass == 4) && (e->type == Open)) || + ((pass == 4) && (e->type == SegOpen)) || + ((pass == 1) && (e->type == SegClose)) || + ((pass == 1) && (e->type == Close)) ) + { + if (v->ss) + { + // As far as we can see. + double minLimit = v->firstObstacleAbove(dim); + double maxLimit = v->firstObstacleBelow(dim); + + v->ss->minSpaceLimit = + std::max(minLimit, v->ss->minSpaceLimit); + v->ss->maxSpaceLimit = + std::min(maxLimit, v->ss->maxSpaceLimit); + } + else + { + v->markShiftSegmentsAbove(dim); + v->markShiftSegmentsBelow(dim); + } + } + + if ( ((pass == 2) && (e->type == SegClose)) || + ((pass == 2) && (e->type == Close)) ) + { + // Clean up neighbour pointers. + Node *l = v->firstAbove, *r = v->firstBelow; + if (l != NULL) + { + l->firstBelow = v->firstBelow; + } + if (r != NULL) + { + r->firstAbove = v->firstAbove; + } + + size_t result; + result = scanline.erase(v); + COLA_ASSERT(result == 1); + delete v; + } +} + + +static void buildOrthogonalChannelInfo(Router *router, + const size_t dim, ShiftSegmentList& segmentList) +{ + if (router->routingPenalty(segmentPenalty) == 0) + { + // This code assumes the routes are pretty optimal, so we don't + // do this adjustment if the routes have no segment penalty. + return; + } + + size_t altDim = (dim + 1) % 2; + // For each connector. + for (ConnRefList::const_iterator curr = router->connRefs.begin(); + curr != router->connRefs.end(); ++curr) + { + if ((*curr)->routingType() != ConnType_Orthogonal) + { + continue; + } + Polygon& displayRoute = (*curr)->displayRoute(); + // Determine all line segments that we are interested in shifting. + // We don't consider the first or last segment of a path. + for (size_t i = 1; i < displayRoute.size(); ++i) + { + if (displayRoute.ps[i - 1][dim] == displayRoute.ps[i][dim]) + { + // It's a segment in the dimension we are processing, + size_t indexLow = i - 1; + size_t indexHigh = i; + if (displayRoute.ps[i - 1][altDim] > displayRoute.ps[i][altDim]) + { + indexLow = i; + indexHigh = i - 1; + } + COLA_ASSERT(displayRoute.at(indexLow)[altDim] < + displayRoute.at(indexHigh)[altDim]); + + if ((i == 1) || ((i + 1) == displayRoute.size())) + { + // The first and last segment of a connector can't be + // shifted. We call them fixed segments. Note: this + // will change if we later allow connection channels. + segmentList.push_back( + ShiftSegment(*curr, indexLow, indexHigh, dim)); + continue; + } + + // The segment probably has space to be shifted. + double minLim = -CHANNEL_MAX; + double maxLim = CHANNEL_MAX; + bool isSBend = false; + + double prevPos = displayRoute.ps[i - 2][dim]; + double nextPos = displayRoute.ps[i + 1][dim]; + if (((prevPos < displayRoute.ps[i][dim]) && + (nextPos > displayRoute.ps[i][dim])) + || + ((prevPos > displayRoute.ps[i][dim]) && + (nextPos < displayRoute.ps[i][dim])) ) + { + isSBend = true; + + // Determine limits if the s-bend is not due to an + // obstacle. In this case we need to limit the channel + // to the span of the adjoining segments to this one. + if ((prevPos < displayRoute.ps[i][dim]) && + (nextPos > displayRoute.ps[i][dim])) + { + minLim = std::max(minLim, prevPos); + maxLim = std::min(maxLim, nextPos); + } + else + { + minLim = std::max(minLim, nextPos); + maxLim = std::min(maxLim, prevPos); + } + } + else + { + // isCBend: Both adjoining segments are in the same + // direction. We indicate this for later by setting + // the maxLim or minLim to the segment position. + if (prevPos < displayRoute.ps[i][dim]) + { + minLim = displayRoute.ps[i][dim]; + } + else + { + maxLim = displayRoute.ps[i][dim]; + } + } + + segmentList.push_back(ShiftSegment(*curr, indexLow, + indexHigh, isSBend, dim, minLim, maxLim)); + } + } + } + if (segmentList.empty()) + { + // There are no segments, so we can just return now. + return; + } + + // Do a sweep and shift these segments. + const size_t n = router->shapeRefs.size(); + const size_t cpn = segmentList.size(); + // Set up the events for the sweep. + size_t totalEvents = 2 * (n + cpn); + events = new Event*[totalEvents]; + unsigned ctr = 0; + ShapeRefList::iterator shRefIt = router->shapeRefs.begin(); + for (unsigned i = 0; i < n; i++) + { + ShapeRef *shRef = *shRefIt; + Point min, max; + shRef->polygon().getBoundingRect(&min.x, &min.y, &max.x, &max.y); + double mid = min[dim] + ((max[dim] - min[dim]) / 2); + Node *v = new Node(shRef, mid); + events[ctr++] = new Event(Open, v, min[altDim]); + events[ctr++] = new Event(Close, v, max[altDim]); + + ++shRefIt; + } + for (ShiftSegmentList::iterator curr = segmentList.begin(); + curr != segmentList.end(); ++curr) + { + const Point& lowPt = curr->lowPoint(); + const Point& highPt = curr->highPoint(); + + COLA_ASSERT(lowPt[dim] == highPt[dim]); + COLA_ASSERT(lowPt[altDim] < highPt[altDim]); + Node *v = new Node(&(*curr), lowPt[dim]); + events[ctr++] = new Event(SegOpen, v, lowPt[altDim]); + events[ctr++] = new Event(SegClose, v, highPt[altDim]); + } + qsort((Event*)events, (size_t) totalEvents, sizeof(Event*), compare_events); + + // Process the sweep. + // We do multiple passes over sections of the list so we can add relevant + // entries to the scanline that might follow, before process them. + NodeSet scanline; + double thisPos = (totalEvents > 0) ? events[0]->pos : 0; + unsigned int posStartIndex = 0; + unsigned int posFinishIndex = 0; + for (unsigned i = 0; i <= totalEvents; ++i) + { + // If we have finished the current scanline or all events, then we + // process the events on the current scanline in a couple of passes. + if ((i == totalEvents) || (events[i]->pos != thisPos)) + { + posFinishIndex = i; + for (int pass = 2; pass <= 4; ++pass) + { + for (unsigned j = posStartIndex; j < posFinishIndex; ++j) + { + processShiftEvent(router, scanline, segmentList, events[j], + dim, pass); + } + } + + if (i == totalEvents) + { + // We have cleaned up, so we can now break out of loop. + break; + } + + thisPos = events[i]->pos; + posStartIndex = i; + } + + // Do the first sweep event handling -- building the correct + // structure of the scanline. + const int pass = 1; + processShiftEvent(router, scanline, segmentList, events[i], + dim, pass); + } + COLA_ASSERT(scanline.size() == 0); + for (unsigned i = 0; i < totalEvents; ++i) + { + delete events[i]; + } + delete [] events; +} + + +static void simplifyOrthogonalRoutes(Router *router) +{ + // Simplify routes. + for (ConnRefList::const_iterator curr = router->connRefs.begin(); + curr != router->connRefs.end(); ++curr) + { + if ((*curr)->routingType() != ConnType_Orthogonal) + { + continue; + } + (*curr)->set_route((*curr)->displayRoute().simplify()); + } +} + + +static void buildOrthogonalNudgingOrderInfo(Router *router, + PtOrderMap& pointOrders) +{ + // Simplify routes. + simplifyOrthogonalRoutes(router); + + int crossingsN = 0; + + // Do segment splitting. + for (ConnRefList::const_iterator curr = router->connRefs.begin(); + curr != router->connRefs.end(); ++curr) + { + if ((*curr)->routingType() != ConnType_Orthogonal) + { + continue; + } + ConnRef *conn = *curr; + + for (ConnRefList::const_iterator curr2 = router->connRefs.begin(); + curr2 != router->connRefs.end(); ++curr2) + { + if ((*curr2)->routingType() != ConnType_Orthogonal) + { + continue; + } + ConnRef *conn2 = *curr2; + + if (conn == conn2) + { + continue; + } + + Avoid::Polygon& route = conn->displayRoute(); + Avoid::Polygon& route2 = conn2->displayRoute(); + splitBranchingSegments(route2, true, route); + } + } + + for (ConnRefList::const_iterator curr = router->connRefs.begin(); + curr != router->connRefs.end(); ++curr) + { + if ((*curr)->routingType() != ConnType_Orthogonal) + { + continue; + } + ConnRef *conn = *curr; + + for (ConnRefList::const_iterator curr2 = curr; + curr2 != router->connRefs.end(); ++curr2) + { + if ((*curr2)->routingType() != ConnType_Orthogonal) + { + continue; + } + ConnRef *conn2 = *curr2; + + if (conn == conn2) + { + continue; + } + + Avoid::Polygon& route = conn->displayRoute(); + Avoid::Polygon& route2 = conn2->displayRoute(); + bool checkForBranchingSegments = false; + int crossings = 0; + for (size_t i = 1; i < route.size(); ++i) + { + const bool finalSegment = ((i + 1) == route.size()); + crossings += countRealCrossings(route2, true, route, i, + checkForBranchingSegments, finalSegment, NULL, + &pointOrders, conn2, conn).first; + } + if (crossings > 0) + { + crossingsN += crossings; + } + } + } + + // Sort the point orders. + PtOrderMap::iterator finish = pointOrders.end(); + for (PtOrderMap::iterator it = pointOrders.begin(); it != finish; ++it) + { + //const VertID& ptID = it->first; + PtOrder& order = it->second; + + for (size_t dim = XDIM; dim <= YDIM; ++dim) + { + order.sort(dim); + } + } +} + + +class CmpLineOrder +{ + public: + CmpLineOrder(PtOrderMap& ord, const size_t dim) + : orders(ord), + dimension(dim) + { + } + bool operator()(const ShiftSegment& lhs, const ShiftSegment& rhs, + bool *comparable = NULL) const + { + if (comparable) + { + *comparable = true; + } + Point lhsLow = lhs.lowPoint(); + Point rhsLow = rhs.lowPoint(); +#ifndef NDEBUG + const Point& lhsHigh = lhs.highPoint(); + const Point& rhsHigh = rhs.highPoint(); +#endif + size_t altDim = (dimension + 1) % 2; + + COLA_ASSERT(lhsLow[dimension] == lhsHigh[dimension]); + COLA_ASSERT(rhsLow[dimension] == rhsHigh[dimension]); + + if (lhsLow[dimension] != rhsLow[dimension]) + { + return lhsLow[dimension] < rhsLow[dimension]; + } + + // If one of these is fixed, then determine order based on + // fixed segment, that is, order so the fixed segment doesn't + // block movement. + bool oneIsFixed = false; + const int lhsFixedOrder = lhs.fixedOrder(oneIsFixed); + const int rhsFixedOrder = rhs.fixedOrder(oneIsFixed); + if (oneIsFixed && (lhsFixedOrder != rhsFixedOrder)) + { + return lhsFixedOrder < rhsFixedOrder; + } + + // C-bends that did not have a clear order with s-bends might + // not have a good ordering here, so compare their order in + // terms of C-bend direction and S-bends and use that if it + // differs for the two segments. + const int lhsOrder = lhs.order(); + const int rhsOrder = rhs.order(); + if (lhsOrder != rhsOrder) + { + return lhsOrder < rhsOrder; + } + + // Need to index using the original point into the map, so find it. + Point& unchanged = (lhsLow[altDim] > rhsLow[altDim]) ? + lhsLow : rhsLow; + + PtOrder& lowOrder = orders[unchanged]; + int lhsPos = lowOrder.positionFor(lhs.connRef, dimension); + int rhsPos = lowOrder.positionFor(rhs.connRef, dimension); + if ((lhsPos == -1) || (rhsPos == -1)) + { + // A value for rhsPos or lhsPos mean the points are not directly + // comparable, meaning they are at the same position but cannot + // overlap (they are just collinear. The relative order for + // these segments is not important since we do not constrain + // them against each other. + //COLA_ASSERT(lhs.overlapsWith(rhs, dimension) == false); + // We do need to be consistent though. + if (comparable) + { + *comparable = false; + } + return lhsLow[altDim] < rhsLow[altDim]; + } + + return lhsPos < rhsPos; + } + + PtOrderMap& orders; + const size_t dimension; +}; + + +// We can use the normaal sort algorithm for lists since it is not possible +// to comapre all elements, but there will be an ordering defined between +// most of the elements. Hence we order these, using insertion sort, and +// the case of them not being able to be compared is handled by not setting +// up any constraints between such segments when doing the nudging. +// +static ShiftSegmentList linesort(ShiftSegmentList origList, + CmpLineOrder& comparison) +{ + ShiftSegmentList resultList; + + while (!origList.empty()) + { + // Get and remove the first element from the origList. + ShiftSegment segment = origList.front(); + origList.pop_front(); + + // Find the insertion point in the resultList. + ShiftSegmentList::iterator curr; + for (curr = resultList.begin(); curr != resultList.end(); ++curr) + { + bool comparable = false; + bool lessThan = comparison(segment, *curr, &comparable); + + if (comparable && lessThan) + { + // If it is comparable and lessThan, then we have found the + // insertion point. + break; + } + } + + // Insert the element into the reultList at the required point. + resultList.insert(curr, segment); + } + + return resultList; +} + + +typedef std::list ShiftSegmentPtrList; + + +static void nudgeOrthogonalRoutes(Router *router, size_t dimension, + PtOrderMap& pointOrders, ShiftSegmentList& segmentList) +{ + // Do the actual nudging. + ShiftSegmentList currentRegion; + while (!segmentList.empty()) + { + // Take a reference segment + ShiftSegment& currentSegment = segmentList.front(); + // Then, find the segments that overlap this one. + currentRegion.clear(); + currentRegion.push_back(currentSegment); + segmentList.erase(segmentList.begin()); + for (ShiftSegmentList::iterator curr = segmentList.begin(); + curr != segmentList.end(); ) + { + bool overlaps = false; + for (ShiftSegmentList::iterator curr2 = currentRegion.begin(); + curr2 != currentRegion.end(); ++curr2) + { + if (curr->overlapsWith(*curr2, dimension)) + { + overlaps = true; + break; + } + } + if (overlaps) + { + currentRegion.push_back(*curr); + segmentList.erase(curr); + // Consider segments from the beginning, since we mave have + // since passed segments that overlap with the new set. + curr = segmentList.begin(); + } + else + { + ++curr; + } + } + CmpLineOrder lineSortComp(pointOrders, dimension); + currentRegion = linesort(currentRegion, lineSortComp); + + if (currentRegion.size() == 1) + { + // Save creating the solver instance if there is just one + // immovable segment. + if (!currentRegion.front().sBend) + { + continue; + } + } + + // Process these segments. + Variables vs; + Constraints cs; + ShiftSegmentPtrList prevVars; + // IDs: + const int freeID = 0; + const int fixedID = 1; + // Weights: + double freeWeight = 0.00001; + double strongWeight = 0.001; + double fixedWeight = 100000; + //printf("-------------------------------------------------------\n"); + //printf("Nudge -- size: %d\n", (int) currentRegion.size()); + for (ShiftSegmentList::iterator currSegment = currentRegion.begin(); + currSegment != currentRegion.end(); ++currSegment) + { + Point& lowPt = currSegment->lowPoint(); + + // Create a solver variable for the position of this segment. + int varID = freeID; + double idealPos = lowPt[dimension]; + double weight = freeWeight; + if (currSegment->sBend) + { + COLA_ASSERT(currSegment->minSpaceLimit > -CHANNEL_MAX); + COLA_ASSERT(currSegment->maxSpaceLimit < CHANNEL_MAX); + + // For s-bends, take the middle as ideal. + idealPos = currSegment->minSpaceLimit + + ((currSegment->maxSpaceLimit - + currSegment->minSpaceLimit) / 2); + } + else if (currSegment->fixed) + { + // Fixed segments shouldn't get moved. + weight = fixedWeight; + varID = fixedID; + } + else + { + // Set a higher weight for c-bends to stop them sometimes + // getting pushed out into channels by more-free connectors + // to the "inner" side of them. + weight = strongWeight; + } + currSegment->variable = new Variable(varID, idealPos, weight); + vs.push_back(currSegment->variable); + size_t index = vs.size() - 1; + //printf("line %.15f pos: %g min: %g max: %g\n", + // lowPt[dimension], idealPos, currSegment->minSpaceLimit, + // currSegment->maxSpaceLimit); + + // Constrain position in relation to previously seen segments, + // if necessary (i.e. when they could overlap). + for (ShiftSegmentPtrList::iterator prevVarIt = prevVars.begin(); + prevVarIt != prevVars.end(); ) + { + ShiftSegment *prevSeg = *prevVarIt; + Variable *prevVar = prevSeg->variable; + + if (currSegment->overlapsWith(*prevSeg, dimension) && + (!(currSegment->fixed) || !(prevSeg->fixed))) + { + // If there is a previous segment to the left that + // could overlap this in the shift direction, then + // constrain the two segments to be separated. + // Though don't add the constraint if both the + // segments are fixed in place. + cs.push_back(new Constraint(prevVar, vs[index], + router->orthogonalNudgeDistance())); + prevVarIt = prevVars.erase(prevVarIt); + } + else + { + ++prevVarIt; + } + } + + if (!currSegment->fixed) + { + // If this segment sees a channel boundary to its left, + // then constrain its placement as such. + if (currSegment->minSpaceLimit > -CHANNEL_MAX) + { + vs.push_back(new Variable(fixedID, + currSegment->minSpaceLimit, fixedWeight)); + cs.push_back(new Constraint(vs[vs.size() - 1], vs[index], + 0.0)); + } + + // If this segment sees a channel boundary to its right, + // then constrain its placement as such. + if (currSegment->maxSpaceLimit < CHANNEL_MAX) + { + vs.push_back(new Variable(fixedID, + currSegment->maxSpaceLimit, fixedWeight)); + cs.push_back(new Constraint(vs[index], vs[vs.size() - 1], + 0.0)); + } + } + prevVars.push_back(&(*currSegment)); + } +#if 0 + for(unsigned i=0;idesiredPosition); + } +#endif + IncSolver f(vs,cs); + f.solve(); + bool satisfied = true; + for (size_t i = 0; i < vs.size(); ++i) + { + if (vs[i]->id == fixedID) + { + if (fabs(vs[i]->finalPosition - vs[i]->desiredPosition) > 0.01) + { + satisfied = false; + break; + } + } + } + if (satisfied) + { + for (ShiftSegmentList::iterator currSegment = currentRegion.begin(); + currSegment != currentRegion.end(); ++currSegment) + { + Point& lowPt = currSegment->lowPoint(); + Point& highPt = currSegment->highPoint(); + double newPos = currSegment->variable->finalPosition; + //printf("Pos: %X, %g\n", (int) currSegment->connRef, newPos); + lowPt[dimension] = newPos; + highPt[dimension] = newPos; + } + } +#if 0 + for(unsigned i=0;ifinalPosition); + } +#endif + for_each(vs.begin(),vs.end(),delete_object()); + for_each(cs.begin(),cs.end(),delete_object()); + } +} + + +extern void improveOrthogonalRoutes(Router *router) +{ + router->timers.Register(tmOrthogNudge, timerStart); + for (size_t dimension = 0; dimension < 2; ++dimension) + { + // Build nudging info. + // XXX: We need to build the point orders separately in each + // dimension since things move. There is probably a more + // efficient way to do this. + PtOrderMap pointOrders; + buildOrthogonalNudgingOrderInfo(router, pointOrders); + + // Simplify routes. + simplifyOrthogonalRoutes(router); + + // Do the centring and nudging. + ShiftSegmentList segLists; + buildOrthogonalChannelInfo(router, dimension, segLists); + nudgeOrthogonalRoutes(router, dimension, pointOrders, segLists); + } + router->timers.Stop(); +} + + +} diff --git a/src/libavoid/orthogonal.h b/src/libavoid/orthogonal.h new file mode 100644 index 000000000..4fb974099 --- /dev/null +++ b/src/libavoid/orthogonal.h @@ -0,0 +1,39 @@ +/* + * vim: ts=4 sw=4 et tw=0 wm=0 + * + * libavoid - Fast, Incremental, Object-avoiding Line Router + * + * Copyright (C) 2009 Monash University + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Author(s): Michael Wybrow +*/ + + +#ifndef AVOID_ORTHOGONAL_H +#define AVOID_ORTHOGONAL_H + +namespace Avoid { + + +extern void generateStaticOrthogonalVisGraph(Router *router); + +extern void improveOrthogonalRoutes(Router *router); + + +} + +#endif diff --git a/src/libavoid/viscluster.cpp b/src/libavoid/viscluster.cpp new file mode 100644 index 000000000..a127c5a17 --- /dev/null +++ b/src/libavoid/viscluster.cpp @@ -0,0 +1,96 @@ +/* + * vim: ts=4 sw=4 et tw=0 wm=0 + * + * libavoid - Fast, Incremental, Object-avoiding Line Router + * + * Copyright (C) 2004-2008 Monash University + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Author(s): Michael Wybrow +*/ + + +#include "libavoid/viscluster.h" +#include "libavoid/router.h" +#include "libavoid/assertions.h" + + +namespace Avoid { + + +ClusterRef::ClusterRef(Router *router, unsigned int id, Polygon& ply) + : _router(router) + , _poly(ply, router) + , _active(false) +{ + _id = router->assignId(id); +} + + +ClusterRef::~ClusterRef() +{ +} + + +void ClusterRef::makeActive(void) +{ + COLA_ASSERT(!_active); + + // Add to connRefs list. + _pos = _router->clusterRefs.insert(_router->clusterRefs.begin(), this); + + _active = true; +} + + +void ClusterRef::makeInactive(void) +{ + COLA_ASSERT(_active); + + // Remove from connRefs list. + _router->clusterRefs.erase(_pos); + + _active = false; +} + + +void ClusterRef::setNewPoly(Polygon& poly) +{ + _poly = ReferencingPolygon(poly, _router); +} + + +unsigned int ClusterRef::id(void) +{ + return _id; +} + + +ReferencingPolygon& ClusterRef::polygon(void) +{ + return _poly; +} + + +Router *ClusterRef::router(void) +{ + return _router; +} + + +} + + diff --git a/src/libavoid/viscluster.h b/src/libavoid/viscluster.h new file mode 100644 index 000000000..5827e5070 --- /dev/null +++ b/src/libavoid/viscluster.h @@ -0,0 +1,67 @@ +/* + * vim: ts=4 sw=4 et tw=0 wm=0 + * + * libavoid - Fast, Incremental, Object-avoiding Line Router + * + * Copyright (C) 2004-2008 Monash University + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Author(s): Michael Wybrow +*/ + + +#ifndef AVOID_CLUSTER_H +#define AVOID_CLUSTER_H + +#include + +#include "libavoid/geometry.h" + + +namespace Avoid { + +class Router; +class ClusterRef; +typedef std::list ClusterRefList; + + +class ClusterRef +{ + public: + ClusterRef(Router *router, unsigned int id, Polygon& poly); + ~ClusterRef(); + void setNewPoly(Polygon& poly); + unsigned int id(void); + ReferencingPolygon& polygon(void); + Router *router(void); + void makeActive(void); + void makeInactive(void); + + private: + Router *_router; + unsigned int _id; + ReferencingPolygon _poly; + bool _active; + ClusterRefList::iterator _pos; +}; + + +} + + +#endif + + diff --git a/src/libavoid/vpsc.cpp b/src/libavoid/vpsc.cpp new file mode 100644 index 000000000..c9a072375 --- /dev/null +++ b/src/libavoid/vpsc.cpp @@ -0,0 +1,1300 @@ +/* + * vim: ts=4 sw=4 et tw=0 wm=0 + * + * libavoid - Fast, Incremental, Object-avoiding Line Router + * + * Copyright (C) 2005-2009 Monash University + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Author(s): Tim Dwyer + * + * -------------- + * + * This file contains a slightly modified version of Solver() from libvpsc: + * A solver for the problem of Variable Placement with Separation Constraints. + * It has the following changes from the Adaptagrams VPSC version: + * - The required VPSC code has been consolidated into a single file. + * - Unnecessary code (like Solver) has been removed. + * - The PairingHeap code has been replaced by a STL priority_queue. + * + * Modifications: Michael Wybrow + * +*/ + +#include +#include +#include +#include +#include +#include + +#include "libavoid/vpsc.h" +#include "libavoid/assertions.h" + + +using namespace std; + +namespace Avoid { + +static const double ZERO_UPPERBOUND=-1e-10; +static const double LAGRANGIAN_TOLERANCE=-1e-4; + +IncSolver::IncSolver(vector const &vs, vector const &cs) + : m(cs.size()), + cs(cs), + n(vs.size()), + vs(vs) +{ + for(unsigned i=0;iin.clear(); + vs[i]->out.clear(); + } + for(unsigned i=0;ileft->out.push_back(c); + c->right->in.push_back(c); + } + bs=new Blocks(vs); +#ifdef LIBVPSC_LOGGING + printBlocks(); + //COLA_ASSERT(!constraintGraphIsCyclic(n,vs)); +#endif + + inactive=cs; + for(Constraints::iterator i=inactive.begin();i!=inactive.end();++i) { + (*i)->active=false; + } +} +IncSolver::~IncSolver() { + delete bs; +} + +// useful in debugging +void IncSolver::printBlocks() { +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + for(set::iterator i=bs->begin();i!=bs->end();++i) { + Block *b=*i; + f<<" "<<*b<finalPosition=v->position(); + COLA_ASSERT(v->finalPosition==v->finalPosition); + } +} + +struct node { + set in; + set out; +}; +// useful in debugging - cycles would be BAD +bool IncSolver::constraintGraphIsCyclic(const unsigned n, Variable* const vs[]) { + map varmap; + vector graph; + for(unsigned i=0;i::iterator c=vs[i]->in.begin();c!=vs[i]->in.end();++c) { + Variable *l=(*c)->left; + varmap[vs[i]]->in.insert(varmap[l]); + } + + for(vector::iterator c=vs[i]->out.begin();c!=vs[i]->out.end();++c) { + Variable *r=(*c)->right; + varmap[vs[i]]->out.insert(varmap[r]); + } + } + while(graph.size()>0) { + node *u=NULL; + vector::iterator i=graph.begin(); + for(;i!=graph.end();++i) { + u=*i; + if(u->in.size()==0) { + break; + } + } + if(i==graph.end() && graph.size()>0) { + //cycle found! + return true; + } else { + graph.erase(i); + for(set::iterator j=u->out.begin();j!=u->out.end();++j) { + node *v=*j; + v->in.erase(u); + } + delete u; + } + } + for(unsigned i=0; i bmap; + vector graph; + for(set::const_iterator i=bs->begin();i!=bs->end();++i) { + Block *b=*i; + node *u=new node; + graph.push_back(u); + bmap[b]=u; + } + for(set::const_iterator i=bs->begin();i!=bs->end();++i) { + Block *b=*i; + b->setUpInConstraints(); + Constraint *c=b->findMinInConstraint(); + while(c!=NULL) { + Block *l=c->left->block; + bmap[b]->in.insert(bmap[l]); + b->deleteMinInConstraint(); + c=b->findMinInConstraint(); + } + + b->setUpOutConstraints(); + c=b->findMinOutConstraint(); + while(c!=NULL) { + Block *r=c->right->block; + bmap[b]->out.insert(bmap[r]); + b->deleteMinOutConstraint(); + c=b->findMinOutConstraint(); + } + } + while(graph.size()>0) { + node *u=NULL; + vector::iterator i=graph.begin(); + for(;i!=graph.end();++i) { + u=*i; + if(u->in.size()==0) { + break; + } + } + if(i==graph.end() && graph.size()>0) { + //cycle found! + return true; + } else { + graph.erase(i); + for(set::iterator j=u->out.begin();j!=u->out.end();++j) { + node *v=*j; + v->in.erase(u); + } + delete u; + } + } + for(unsigned i=0; icost(); + while(fabs(lastcost-cost)>0.0001) { + satisfy(); + lastcost=cost; + cost = bs->cost(); +#ifdef LIBVPSC_LOGGING + f<<" bs->size="<size()<<", cost="<size()!=n; +} +/* + * incremental version of satisfy that allows refinement after blocks are + * moved. + * + * - move blocks to new positions + * - repeatedly merge across most violated constraint until no more + * violated constraints exist + * + * Note: there is a special case to handle when the most violated constraint + * is between two variables in the same block. Then, we must split the block + * over an active constraint between the two variables. We choose the + * constraint with the most negative lagrangian multiplier. + */ +bool IncSolver::satisfy() { +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<"satisfy_inc()..."<equality || v->slack() < ZERO_UPPERBOUND && !v->active)) + { + COLA_ASSERT(!v->active); + Block *lb = v->left->block, *rb = v->right->block; + if(lb != rb) { + lb->merge(rb,v); + } else { + if(lb->isActiveDirectedPathBetween(v->right,v->left)) { + // cycle found, relax the violated, cyclic constraint + v->unsatisfiable=true; + continue; + //UnsatisfiableException e; + //lb->getActiveDirectedPathBetween(e.path,v->right,v->left); + //e.path.push_back(v); + //throw e; + } + //if(splitCtr++>10000) { + //throw "Cycle Error!"; + //} + // constraint is within block, need to split first + try { + Constraint* splitConstraint + =lb->splitBetween(v->left,v->right,lb,rb); + if(splitConstraint!=NULL) { + COLA_ASSERT(!splitConstraint->active); + inactive.push_back(splitConstraint); + } else { + v->unsatisfiable=true; + continue; + } + } catch(UnsatisfiableException e) { + e.path.push_back(v); + std::cerr << "Unsatisfiable:" << std::endl; + for(std::vector::iterator r=e.path.begin(); + r!=e.path.end();++r) + { + std::cerr << **r <unsatisfiable=true; + continue; + } + if(v->slack()>=0) { + COLA_ASSERT(!v->active); + // v was satisfied by the above split! + inactive.push_back(v); + bs->insert(lb); + bs->insert(rb); + } else { + bs->insert(lb->merge(rb,v)); + } + } + bs->cleanup(); +#ifdef LIBVPSC_LOGGING + f<<"...remaining blocks="<size()<<", cost="<cost()<cleanup(); + bool activeConstraints=false; + for(unsigned i=0;iactive) activeConstraints=true; + if(v->slack() < ZERO_UPPERBOUND) { + ostringstream s; + s<<"Unsatisfied constraint: "<<*v; +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<::const_iterator i(bs->begin());i!=bs->end();++i) { + Block *b = *i; + b->updateWeightedPosition(); + //b->posn = b->wposn / b->weight; + } +#ifdef LIBVPSC_LOGGING + f<<" moved blocks."<::const_iterator i(bs->begin());i!=bs->end();++i) { + Block* b = *i; + Constraint* v=b->findMinLM(); + if(v!=NULL && v->lm < LAGRANGIAN_TOLERANCE) { + COLA_ASSERT(!v->equality); +#ifdef LIBVPSC_LOGGING + f<<" found split point: "<<*v<<" lm="<lm<left->block, *l=NULL, *r=NULL; + COLA_ASSERT(v->left->block == v->right->block); + //double pos = b->posn; + b->split(l,r,v); + //l->posn=r->posn=pos; + //l->wposn = l->posn * l->weight; + //r->wposn = r->posn * r->weight; + l->updateWeightedPosition(); + r->updateWeightedPosition(); + bs->insert(l); + bs->insert(r); + b->deleted=true; + COLA_ASSERT(!v->active); + inactive.push_back(v); +#ifdef LIBVPSC_LOGGING + f<<" new blocks: "<<*l<<" and "<<*r<0) { std::cout<<" splits: "<cleanup(); +} + +/* + * Scan constraint list for the most violated constraint, or the first equality + * constraint + */ +Constraint* IncSolver::mostViolated(Constraints &l) { + double minSlack = DBL_MAX; + Constraint* v=NULL; +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<"Looking for most violated..."<slack(); + if(c->equality || slack < minSlack) { + minSlack=slack; + v=c; + deletePoint=i; + if(c->equality) break; + } + } + // Because the constraint list is not order dependent we just + // move the last element over the deletePoint and resize + // downwards. There is always at least 1 element in the + // vector because of search. + if(deletePoint != end && (minSlack < ZERO_UPPERBOUND && !v->active || v->equality)) { + *deletePoint = l[l.size()-1]; + l.resize(l.size()-1); + } +#ifdef LIBVPSC_LOGGING + f<<" most violated is: "<<*v< const &vs) : vs(vs),nvs(vs.size()) { + blockTimeCtr=0; + for(int i=0;i::iterator i=begin();i!=end();++i) { + delete *i; + } + clear(); +} + +/* + * returns a list of variables with total ordering determined by the constraint + * DAG + */ +list *Blocks::totalOrder() { + list *order = new list; + for(int i=0;ivisited=false; + } + for(int i=0;iin.size()==0) { + dfsVisit(vs[i],order); + } + } + return order; +} +// Recursive depth first search giving total order by pushing nodes in the DAG +// onto the front of the list when we finish searching them +void Blocks::dfsVisit(Variable *v, list *order) { + v->visited=true; + vector::iterator it=v->out.begin(); + for(;it!=v->out.end();++it) { + Constraint *c=*it; + if(!c->right->visited) { + dfsVisit(c->right, order); + } + } +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<" order="<<*v<push_front(v); +} +/* + * Processes incoming constraints, most violated to least, merging with the + * neighbouring (left) block until no more violated constraints are found + */ +void Blocks::mergeLeft(Block *r) { +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<"mergeLeft called on "<<*r<timeStamp=++blockTimeCtr; + r->setUpInConstraints(); + Constraint *c=r->findMinInConstraint(); + while (c != NULL && c->slack()<0) { +#ifdef LIBVPSC_LOGGING + f<<"mergeLeft on constraint: "<<*c<deleteMinInConstraint(); + Block *l = c->left->block; + if (l->in==NULL) l->setUpInConstraints(); + double dist = c->right->offset - c->left->offset - c->gap; + if (r->vars->size() < l->vars->size()) { + dist=-dist; + std::swap(l, r); + } + blockTimeCtr++; + r->merge(l, c, dist); + r->mergeIn(l); + r->timeStamp=blockTimeCtr; + removeBlock(l); + c=r->findMinInConstraint(); + } +#ifdef LIBVPSC_LOGGING + f<<"merged "<<*r<setUpOutConstraints(); + Constraint *c = l->findMinOutConstraint(); + while (c != NULL && c->slack()<0) { +#ifdef LIBVPSC_LOGGING + f<<"mergeRight on constraint: "<<*c<deleteMinOutConstraint(); + Block *r = c->right->block; + r->setUpOutConstraints(); + double dist = c->left->offset + c->gap - c->right->offset; + if (l->vars->size() > r->vars->size()) { + dist=-dist; + std::swap(l, r); + } + l->merge(r, c, dist); + l->mergeOut(r); + removeBlock(r); + c=l->findMinOutConstraint(); + } +#ifdef LIBVPSC_LOGGING + f<<"merged "<<*l<deleted=true; + //erase(doomed); +} +void Blocks::cleanup() { + vector bcopy(begin(),end()); + for(vector::iterator i=bcopy.begin();i!=bcopy.end();++i) { + Block *b=*i; + if(b->deleted) { + erase(b); + delete b; + } + } +} +/* + * Splits block b across constraint c into two new blocks, l and r (c's left + * and right sides respectively) + */ +void Blocks::split(Block *b, Block *&l, Block *&r, Constraint *c) { + b->split(l,r,c); +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<"Split left: "<<*l<posn = b->posn; + //COLA_ASSERT(r->weight!=0); + //r->wposn = r->posn * r->weight; + mergeLeft(l); + // r may have been merged! + r = c->right->block; + r->updateWeightedPosition(); + //r->posn = r->wposn / r->weight; + mergeRight(r); + removeBlock(b); + + insert(l); + insert(r); + COLA_ASSERT(__NOTNAN(l->posn)); + COLA_ASSERT(__NOTNAN(r->posn)); +} +/* + * returns the cost total squared distance of variables from their desired + * positions + */ +double Blocks::cost() { + double c = 0; + for(set::iterator i=begin();i!=end();++i) { + c += (*i)->cost(); + } + return c; +} + +void PositionStats::addVariable(Variable* v) { + double ai=scale/v->scale; + double bi=v->offset/v->scale; + double wi=v->weight; + AB+=wi*ai*bi; + AD+=wi*ai*v->desiredPosition; + A2+=wi*ai*ai; + /* +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f << "adding v[" << v->id << "], blockscale=" << scale << ", despos=" + << v->desiredPosition << ", ai=" << ai << ", bi=" << bi + << ", AB=" << AB << ", AD=" << AD << ", A2=" << A2; +#endif +*/ +} +void Block::addVariable(Variable* v) { + v->block=this; + vars->push_back(v); + if(ps.A2==0) ps.scale=v->scale; + //weight+= v->weight; + //wposn += v->weight * (v->desiredPosition - v->offset); + //posn=wposn/weight; + ps.addVariable(v); + posn=(ps.AD - ps.AB) / ps.A2; + COLA_ASSERT(__NOTNAN(posn)); + /* +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f << ", posn=" << posn << endl; +#endif +*/ +} +Block::Block(Variable* const v) + : vars(new vector) + , posn(0) + //, weight(0) + //, wposn(0) + , deleted(false) + , timeStamp(0) + , in(NULL) + , out(NULL) +{ + if(v!=NULL) { + v->offset=0; + addVariable(v); + } +} + +void Block::updateWeightedPosition() { + //wposn=0; + ps.AB=ps.AD=ps.A2=0; + for (Vit v=vars->begin();v!=vars->end();++v) { + //wposn += ((*v)->desiredPosition - (*v)->offset) * (*v)->weight; + ps.addVariable(*v); + } + posn=(ps.AD - ps.AB) / ps.A2; + COLA_ASSERT(__NOTNAN(posn)); +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f << ", posn=" << posn << endl; +#endif +} +Block::~Block(void) +{ + delete vars; + delete in; + delete out; +} +void Block::setUpInConstraints() { + setUpConstraintHeap(in,true); +} +void Block::setUpOutConstraints() { + setUpConstraintHeap(out,false); +} +void Block::setUpConstraintHeap(Heap* &h,bool in) { + delete h; + h = new Heap(); + for (Vit i=vars->begin();i!=vars->end();++i) { + Variable *v=*i; + vector *cs=in?&(v->in):&(v->out); + for (Cit j=cs->begin();j!=cs->end();++j) { + Constraint *c=*j; + c->timeStamp=blockTimeCtr; + if (c->left->block != this && in || c->right->block != this && !in) { + h->push(c); + } + } + } +} +Block* Block::merge(Block* b, Constraint* c) { +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<" merging on: "<<*c<<",c->left->offset="<left->offset<<",c->right->offset="<right->offset<right->offset - c->left->offset - c->gap; + Block *l=c->left->block; + Block *r=c->right->block; + if (l->vars->size() < r->vars->size()) { + r->merge(l,c,dist); + } else { + l->merge(r,c,-dist); + } + Block* mergeBlock=b->deleted?this:b; +#ifdef LIBVPSC_LOGGING + f<<" merged block="<<*mergeBlock<id<<"]: d="<desiredPosition + <<" a="<scale<<" o="<offset + <findMinInConstraint(); + while (!b->in->empty()) + { + in->push(b->in->top()); + b->in->pop(); + } +#ifdef LIBVPSC_LOGGING + f<<" merged heap: "<<*in<findMinOutConstraint(); + while (!b->out->empty()) + { + out->push(b->out->top()); + b->out->pop(); + } +} +Constraint *Block::findMinInConstraint() { + Constraint *v = NULL; + vector outOfDate; + while (!in->empty()) { + v = in->top(); + Block *lb=v->left->block; + Block *rb=v->right->block; + // rb may not be this if called between merge and mergeIn +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<" checking constraint ... "<<*v; + f<<" timestamps: left="<timeStamp<<" right="<timeStamp<<" constraint="<timeStamp<slack()<0) { + f<<" violated internal constraint found! "<<*v<timeStamp < lb->timeStamp) { + // block at other end of constraint has been moved since this + in->pop(); + outOfDate.push_back(v); +#ifdef LIBVPSC_LOGGING + f<<" reinserting out of date (reinsert later)"<timeStamp=blockTimeCtr; + in->push(v); + } + if(in->empty()) { + v=NULL; + } else { + v=in->top(); + } + return v; +} +Constraint *Block::findMinOutConstraint() { + if(out->empty()) return NULL; + Constraint *v = out->top(); + while (v->left->block == v->right->block) { + out->pop(); + if(out->empty()) return NULL; + v = out->top(); + } + return v; +} +void Block::deleteMinInConstraint() { + in->pop(); +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<"deleteMinInConstraint... "<pop(); +} +inline bool Block::canFollowLeft(Constraint const* c, Variable const* last) const { + return c->left->block==this && c->active && last!=c->left; +} +inline bool Block::canFollowRight(Constraint const* c, Variable const* last) const { + return c->right->block==this && c->active && last!=c->right; +} + +// computes the derivative of v and the lagrange multipliers +// of v's out constraints (as the recursive sum of those below. +// Does not backtrack over u. +// also records the constraint with minimum lagrange multiplier +// in min_lm +double Block::compute_dfdv(Variable* const v, Variable* const u, + Constraint *&min_lm) { + double dfdv=v->dfdv(); + for(Cit it=v->out.begin();it!=v->out.end();++it) { + Constraint *c=*it; + if(canFollowRight(c,u)) { + c->lm=compute_dfdv(c->right,v,min_lm); + dfdv+=c->lm*c->left->scale; + if(!c->equality&&(min_lm==NULL||c->lmlm)) min_lm=c; + } + } + for(Cit it=v->in.begin();it!=v->in.end();++it) { + Constraint *c=*it; + if(canFollowLeft(c,u)) { + c->lm=-compute_dfdv(c->left,v,min_lm); + dfdv-=c->lm*c->right->scale; + if(!c->equality&&(min_lm==NULL||c->lmlm)) min_lm=c; + } + } + return dfdv/v->scale; +} +double Block::compute_dfdv(Variable* const v, Variable* const u) { + double dfdv = v->dfdv(); + for(Cit it = v->out.begin(); it != v->out.end(); ++it) { + Constraint *c = *it; + if(canFollowRight(c,u)) { + c->lm = compute_dfdv(c->right,v); + dfdv += c->lm * c->left->scale; + } + } + for(Cit it=v->in.begin();it!=v->in.end();++it) { + Constraint *c = *it; + if(canFollowLeft(c,u)) { + c->lm = - compute_dfdv(c->left,v); + dfdv -= c->lm * c->right->scale; + } + } + return dfdv/v->scale; +} + +// The top level v and r are variables between which we want to find the +// constraint with the smallest lm. +// Similarly, m is initially NULL and is only assigned a value if the next +// variable to be visited is r or if a possible min constraint is returned from +// a nested call (rather than NULL). +// Then, the search for the m with minimum lm occurs as we return from +// the recursion (checking only constraints traversed left-to-right +// in order to avoid creating any new violations). +// We also do not consider equality constraints as potential split points +bool Block::split_path( + Variable* r, + Variable* const v, + Variable* const u, + Constraint* &m, + bool desperation=false + ) +{ + for(Cit it(v->in.begin());it!=v->in.end();++it) { + Constraint *c=*it; + if(canFollowLeft(c,u)) { +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<" left split path: "<<*c<left==r) { + if(desperation&&!c->equality) m=c; + return true; + } else { + if(split_path(r,c->left,v,m)) { + if(desperation && !c->equality && (!m||c->lmlm)) { + m=c; + } + return true; + } + } + } + } + for(Cit it(v->out.begin());it!=v->out.end();++it) { + Constraint *c=*it; + if(canFollowRight(c,u)) { +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<" right split path: "<<*c<right==r) { + if(!c->equality) m=c; + return true; + } else { + if(split_path(r,c->right,v,m)) { + if(!c->equality && (!m||c->lmlm)) + m=c; + return true; + } + } + } + } + return false; +} +/* +Block::Pair Block::compute_dfdv_between( + Variable* r, Variable* const v, Variable* const u, + const Direction dir = NONE, bool changedDirection = false) { + double dfdv=v->weight*(v->position() - v->desiredPosition); + Constraint *m=NULL; + for(Cit it(v->in.begin());it!=v->in.end();++it) { + Constraint *c=*it; + if(canFollowLeft(c,u)) { + if(dir==RIGHT) { + changedDirection = true; + } + if(c->left==r) { + r=NULL; + if(!c->equality) m=c; + } + Pair p=compute_dfdv_between(r,c->left,v, + LEFT,changedDirection); + dfdv -= c->lm = -p.first; + if(r && p.second) + m = p.second; + } + } + for(Cit it(v->out.begin());it!=v->out.end();++it) { + Constraint *c=*it; + if(canFollowRight(c,u)) { + if(dir==LEFT) { + changedDirection = true; + } + if(c->right==r) { + r=NULL; + if(!c->equality) m=c; + } + Pair p=compute_dfdv_between(r,c->right,v, + RIGHT,changedDirection); + dfdv += c->lm = p.first; + if(r && p.second) + m = changedDirection && !c->equality && c->lm < p.second->lm + ? c + : p.second; + } + } + return Pair(dfdv,m); +} +*/ + +// resets LMs for all active constraints to 0 by +// traversing active constraint tree starting from v, +// not back tracking over u +void Block::reset_active_lm(Variable* const v, Variable* const u) { + for(Cit it=v->out.begin();it!=v->out.end();++it) { + Constraint *c=*it; + if(canFollowRight(c,u)) { + c->lm=0; + reset_active_lm(c->right,v); + } + } + for(Cit it=v->in.begin();it!=v->in.end();++it) { + Constraint *c=*it; + if(canFollowLeft(c,u)) { + c->lm=0; + reset_active_lm(c->left,v); + } + } +} +void Block::list_active(Variable* const v, Variable* const u) { + for(Cit it=v->out.begin();it!=v->out.end();++it) { + Constraint *c=*it; + if(canFollowRight(c,u)) { +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<" "<<*c<right,v); + } + } + for(Cit it=v->in.begin();it!=v->in.end();++it) { + Constraint *c=*it; + if(canFollowLeft(c,u)) { +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<" "<<*c<left,v); + } + } +} +/* + * finds the constraint with the minimum lagrange multiplier, that is, the constraint + * that most wants to split + */ +Constraint *Block::findMinLM() { + Constraint *min_lm=NULL; + reset_active_lm(vars->front(),NULL); + compute_dfdv(vars->front(),NULL,min_lm); +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<" langrangians: "<front(),NULL); +#endif + return min_lm; +} +Constraint *Block::findMinLMBetween(Variable* const lv, Variable* const rv) { + reset_active_lm(vars->front(),NULL); + compute_dfdv(vars->front(),NULL); + Constraint *min_lm=NULL; + split_path(rv,lv,NULL,min_lm); +#if 0 + if(min_lm==NULL) { + split_path(rv,lv,NULL,min_lm,true); + } +#else + if(min_lm==NULL) { + fprintf(stderr,"Couldn't find split point!\n"); + UnsatisfiableException e; + getActivePathBetween(e.path,lv,rv,NULL); + throw e; + } + COLA_ASSERT(min_lm!=NULL); +#endif + return min_lm; +} + +// populates block b by traversing the active constraint tree adding variables as they're +// visited. Starts from variable v and does not backtrack over variable u. +void Block::populateSplitBlock(Block *b, Variable* v, Variable const* u) { + b->addVariable(v); + for (Cit c=v->in.begin();c!=v->in.end();++c) { + if (canFollowLeft(*c,u)) + populateSplitBlock(b, (*c)->left, v); + } + for (Cit c=v->out.begin();c!=v->out.end();++c) { + if (canFollowRight(*c,u)) + populateSplitBlock(b, (*c)->right, v); + } +} +/* + * Returns the active path between variables u and v... not back tracking over w + */ +bool Block::getActivePathBetween(Constraints& path, Variable const* u, + Variable const* v, Variable const *w) const { + if(u==v) return true; + for (Cit_const c=u->in.begin();c!=u->in.end();++c) { + if (canFollowLeft(*c,w)) { + if(getActivePathBetween(path, (*c)->left, v, u)) { + path.push_back(*c); + return true; + } + } + } + for (Cit_const c=u->out.begin();c!=u->out.end();++c) { + if (canFollowRight(*c,w)) { + if(getActivePathBetween(path, (*c)->right, v, u)) { + path.push_back(*c); + return true; + } + } + } + return false; +} +// Search active constraint tree from u to see if there is a directed path to v. +// Returns true if path is found with all constraints in path having their visited flag +// set true. +bool Block::isActiveDirectedPathBetween(Variable const* u, Variable const* v) const { + if(u==v) return true; + for (Cit_const c=u->out.begin();c!=u->out.end();++c) { + if(canFollowRight(*c,NULL)) { + if(isActiveDirectedPathBetween((*c)->right,v)) { + return true; + } + } + } + return false; +} +bool Block::getActiveDirectedPathBetween( + Constraints& path, Variable const* u, Variable const* v) const { + if(u==v) return true; + for (Cit_const c=u->out.begin();c!=u->out.end();++c) { + if(canFollowRight(*c,NULL)) { + if(getActiveDirectedPathBetween(path,(*c)->right,v)) { + path.push_back(*c); + return true; + } + } + } + return false; +} +/* + * Block needs to be split because of a violated constraint between vl and vr. + * We need to search the active constraint tree between l and r and find the constraint + * with min lagrangrian multiplier and split at that point. + * Returns the split constraint + */ +Constraint* Block::splitBetween(Variable* const vl, Variable* const vr, + Block* &lb, Block* &rb) { +#ifdef LIBVPSC_LOGGING + ofstream f(LOGFILE,ios::app); + f<<" need to split between: "<<*vl<<" and "<<*vr<active=false; + l=new Block(); + populateSplitBlock(l,c->left,c->right); + //COLA_ASSERT(l->weight>0); + r=new Block(); + populateSplitBlock(r,c->right,c->left); + //COLA_ASSERT(r->weight>0); +} + +/* + * Computes the cost (squared euclidean distance from desired positions) of the + * current positions for variables in this block + */ +double Block::cost() { + double c = 0; + for (Vit v=vars->begin();v!=vars->end();++v) { + double diff = (*v)->position() - (*v)->desiredPosition; + c += (*v)->weight * diff * diff; + } + return c; +} +ostream& operator <<(ostream &os, const Block& b) +{ + os<<"Block(posn="<begin();v!=b.vars->end();++v) { + os<<" "<<**v; + } + if(b.deleted) { + os<<" Deleted!"; + } + return os; +} + +Constraint::Constraint(Variable *left, Variable *right, double gap, bool equality) +: left(left), + right(right), + gap(gap), + timeStamp(0), + active(false), + equality(equality), + unsatisfiable(false) +{ + // In hindsight I think it's probably better to build the constraint DAG + // (by creating variable in/out lists) when needed, rather than in advance + //left->out.push_back(this); + //right->in.push_back(this); +} +Constraint::~Constraint() { + // see constructor: the following is just way too slow. + // Better to create a + // new DAG on demand than maintain the lists dynamically. + //Constraints::iterator i; + //for(i=left->out.begin(); i!=left->out.end(); i++) { + //if(*i==this) break; + //} + //left->out.erase(i); + //for(i=right->in.begin(); i!=right->in.end(); i++) { + //if(*i==this) break; + //} + //right->in.erase(i); +} +double Constraint::slack() const { + return unsatisfiable ? DBL_MAX + : right->scale * right->position() + - gap - left->scale * left->position(); +} +std::ostream& operator <<(std::ostream &os, const Constraint &c) +{ + if(&c==NULL) { + os<<"NULL"; + } else { + const char *type=c.equality?"=":"<="; + std::ostringstream lscale, rscale; + if(c.left->scale!=1) { + lscale << c.left->scale << "*"; + } + if(c.right->scale!=1) { + rscale << c.right->scale << "*"; + } + os<block&&c.right->block) + os<<"("<left->block->timeStamp > l->timeStamp + ||l->left->block==l->right->block + ?-DBL_MAX:l->slack(); + double const sr = + r->left->block->timeStamp > r->timeStamp + ||r->left->block==r->right->block + ?-DBL_MAX:r->slack(); + if(sl==sr) { + // arbitrary choice based on id + if(l->left->id==r->left->id) { + if(l->right->idright->id) return true; + return false; + } + if(l->left->idleft->id) return true; + return false; + } + return sl > sr; +} + +std::ostream& operator <<(std::ostream &os, const Variable &v) { + if(v.block) + os << "(" << v.id << "=" << v.position() << ")"; + else + os << "(" << v.id << "=" << v.desiredPosition << ")"; + return os; +} + +} diff --git a/src/libavoid/vpsc.h b/src/libavoid/vpsc.h new file mode 100644 index 000000000..4d6d8ce61 --- /dev/null +++ b/src/libavoid/vpsc.h @@ -0,0 +1,255 @@ +/* + * vim: ts=4 sw=4 et tw=0 wm=0 + * + * libavoid - Fast, Incremental, Object-avoiding Line Router + * + * Copyright (C) 2005-2009 Monash University + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Author(s): Tim Dwyer + * + * -------------- + * + * This file contains a slightly modified version of Solver() from libvpsc: + * A solver for the problem of Variable Placement with Separation Constraints. + * It has the following changes from the Adaptagrams VPSC version: + * - The required VPSC code has been consolidated into a single file. + * - Unnecessary code (like Solver) has been removed. + * - The PairingHeap code has been replaced by a STL priority_queue. + * + * Modifications: Michael Wybrow + * +*/ + +#ifndef LIBAVOID_VPSC_H +#define LIBAVOID_VPSC_H + +#include +#include +#include +#include + +namespace Avoid { + +class Variable; +class Constraint; +typedef std::vector Variables; +typedef std::vector Constraints; +class CompareConstraints { +public: + bool operator() (Constraint *const &l, Constraint *const &r) const; +}; +struct PositionStats { + PositionStats() : scale(0), AB(0), AD(0), A2(0) {} + void addVariable(Variable* const v); + double scale; + double AB; + double AD; + double A2; +}; + +typedef std::priority_queue, + CompareConstraints> Heap; + +class Block +{ + typedef Variables::iterator Vit; + typedef Constraints::iterator Cit; + typedef Constraints::const_iterator Cit_const; + + friend std::ostream& operator <<(std::ostream &os,const Block &b); +public: + Variables *vars; + double posn; + //double weight; + //double wposn; + PositionStats ps; + Block(Variable* const v=NULL); + ~Block(void); + Constraint* findMinLM(); + Constraint* findMinLMBetween(Variable* const lv, Variable* const rv); + Constraint* findMinInConstraint(); + Constraint* findMinOutConstraint(); + void deleteMinInConstraint(); + void deleteMinOutConstraint(); + void updateWeightedPosition(); + void merge(Block *b, Constraint *c, double dist); + Block* merge(Block *b, Constraint *c); + void mergeIn(Block *b); + void mergeOut(Block *b); + void split(Block *&l, Block *&r, Constraint *c); + Constraint* splitBetween(Variable* vl, Variable* vr, Block* &lb, Block* &rb); + void setUpInConstraints(); + void setUpOutConstraints(); + double cost(); + bool deleted; + long timeStamp; + Heap *in; + Heap *out; + bool getActivePathBetween(Constraints& path, Variable const* u, + Variable const* v, Variable const *w) const; + bool isActiveDirectedPathBetween( + Variable const* u, Variable const* v) const; + bool getActiveDirectedPathBetween(Constraints& path, Variable const * u, Variable const * v) const; +private: + typedef enum {NONE, LEFT, RIGHT} Direction; + typedef std::pair Pair; + void reset_active_lm(Variable* const v, Variable* const u); + void list_active(Variable* const v, Variable* const u); + double compute_dfdv(Variable* const v, Variable* const u); + double compute_dfdv(Variable* const v, Variable* const u, Constraint *&min_lm); + bool split_path(Variable*, Variable* const, Variable* const, + Constraint* &min_lm, bool desperation); + bool canFollowLeft(Constraint const* c, Variable const* last) const; + bool canFollowRight(Constraint const* c, Variable const* last) const; + void populateSplitBlock(Block *b, Variable* v, Variable const* u); + void addVariable(Variable* v); + void setUpConstraintHeap(Heap* &h,bool in); +}; + + +class Constraint; +typedef std::vector Constraints; +class Variable +{ + friend std::ostream& operator <<(std::ostream &os, const Variable &v); + friend class Block; + friend class Constraint; + friend class IncSolver; +public: + int id; // useful in log files + double desiredPosition; + double finalPosition; + double weight; // how much the variable wants to + // be at it's desired position + double scale; // translates variable to another space + double offset; + Block *block; + bool visited; + bool fixedDesiredPosition; + Constraints in; + Constraints out; + char *toString(); + inline Variable(const int id, const double desiredPos=-1.0, + const double weight=1.0, const double scale=1.0) + : id(id) + , desiredPosition(desiredPos) + , weight(weight) + , scale(scale) + , offset(0) + , block(NULL) + , visited(false) + , fixedDesiredPosition(false) + { + } + double dfdv() const { + return 2. * weight * ( position() - desiredPosition ); + } +private: + double position() const { + return (block->ps.scale*block->posn+offset)/scale; + } +}; + + +class Constraint +{ + friend std::ostream& operator <<(std::ostream &os,const Constraint &c); +public: + Variable *left; + Variable *right; + double gap; + double lm; + Constraint(Variable *left, Variable *right, double gap, bool equality=false); + ~Constraint(); + double slack() const; + long timeStamp; + bool active; + const bool equality; + bool unsatisfiable; +}; +/* + * A block structure defined over the variables such that each block contains + * 1 or more variables, with the invariant that all constraints inside a block + * are satisfied by keeping the variables fixed relative to one another + */ +class Blocks : public std::set +{ +public: + Blocks(Variables const &vs); + ~Blocks(void); + void mergeLeft(Block *r); + void mergeRight(Block *l); + void split(Block *b, Block *&l, Block *&r, Constraint *c); + std::list *totalOrder(); + void cleanup(); + double cost(); +private: + void dfsVisit(Variable *v, std::list *order); + void removeBlock(Block *doomed); + Variables const &vs; + int nvs; +}; + +extern long blockTimeCtr; + +struct UnsatisfiableException { + Constraints path; +}; +struct UnsatisfiedConstraint { + UnsatisfiedConstraint(Constraint& c):c(c) {} + Constraint& c; +}; +/* + * Variable Placement with Separation Constraints problem instance + */ +class IncSolver { +public: + unsigned splitCnt; + bool satisfy(); + bool solve(); + void moveBlocks(); + void splitBlocks(); + IncSolver(Variables const &vs, Constraints const &cs); + + ~IncSolver(); + Variables const & getVariables() { return vs; } +protected: + Blocks *bs; + unsigned m; + Constraints const &cs; + unsigned n; + Variables const &vs; + void printBlocks(); + void copyResult(); +private: + bool constraintGraphIsCyclic(const unsigned n, Variable* const vs[]); + bool blockGraphIsCyclic(); + Constraints inactive; + Constraints violated; + Constraint* mostViolated(Constraints &l); +}; + +struct delete_object +{ + template + void operator()(T *ptr){ delete ptr;} +}; + + +} + +#endif // AVOID_VPSC_H -- cgit v1.2.3 From af83df8da26b21a9fa55c29c40c7bf7bb27c6a33 Mon Sep 17 00:00:00 2001 From: "Arcadie M. Cracan" Date: Wed, 2 Dec 2009 23:40:56 +0200 Subject: Add some more forgotten files (bzr r8857) --- src/connection-points.cpp | 37 +++++++++++++++++++++++++ src/connection-points.h | 70 +++++++++++++++++++++++++++++++++++++++++++++++ src/libavoid/.dirstamp | 0 3 files changed, 107 insertions(+) create mode 100644 src/connection-points.cpp create mode 100644 src/connection-points.h delete mode 100644 src/libavoid/.dirstamp (limited to 'src') diff --git a/src/connection-points.cpp b/src/connection-points.cpp new file mode 100644 index 000000000..9ed98d211 --- /dev/null +++ b/src/connection-points.cpp @@ -0,0 +1,37 @@ +#include "connection-points.h" + + +bool ConnectionPoint::operator!=(ConnectionPoint& cp) +{ + return (id!=cp.id || type!=cp.type || dir!=cp.dir || pos!=cp.pos); +} + +bool ConnectionPoint::operator==(ConnectionPoint& cp) +{ + return (id==cp.id && type==cp.type && dir==cp.dir && pos==cp.pos); +} + + +namespace Inkscape{ + +SVGIStringStream& +operator>>(SVGIStringStream& istr, ConnectionPoint& cp) +{ + istr>>cp.id>>cp.dir>>cp.pos[Geom::X]>>cp.pos[Geom::Y]; + + return istr; +} + +SVGOStringStream& +operator<<(SVGOStringStream& ostr, const ConnectionPoint& cp) +{ + ostr< +//#include +#include + +#include "svg/stringstream.h" + + +enum ConnPointType { + ConnPointDefault = 0, + ConnPointUserDefined = 1 +}; +enum ConnPointDefaultPos{ + ConnPointPosTL, // Top Left + ConnPointPosTC, // Top Centre + ConnPointPosTR, // Top Right + ConnPointPosCL, // Centre Left + ConnPointPosCC, // Centre Centre + ConnPointPosCR, // Centre Right + ConnPointPosBL, // Bottom Left + ConnPointPosBC, // Bottom Centre + ConnPointPosBR, // Bottom Right +}; + + +struct ConnectionPoint +{ + ConnectionPoint(): + type(ConnPointDefault), // default to a default connection point + id(ConnPointPosCC), // default to the centre point + pos(), + dir(Avoid::ConnDirAll) // allow any direction + { + } + // type of the connection point + // default or user-defined + int type; + + /* id of the connection point + in the case of default + connection points it specifies + which of the 9 types the + connection point is. + */ + int id; + + /* position related to parent item + in the case of default connection + points, these positions should be + computed by the item's avoidRef + */ + Geom::Point pos; + + // directions from which connections can occur + Avoid::ConnDirFlags dir; + + bool operator!=(ConnectionPoint&); + bool operator==(ConnectionPoint&); +}; + +namespace Inkscape{ + +SVGIStringStream& operator>>(SVGIStringStream& istr, ConnectionPoint& cp); +SVGOStringStream& operator<<(SVGOStringStream& ostr, const ConnectionPoint& cp); + +} + +#endif \ No newline at end of file diff --git a/src/libavoid/.dirstamp b/src/libavoid/.dirstamp deleted file mode 100644 index e69de29bb..000000000 -- cgit v1.2.3 From 157df655ae46b09f2253c4b2d8c4947201ab84ac Mon Sep 17 00:00:00 2001 From: "Arcadie M. Cracan" Date: Fri, 4 Dec 2009 11:33:53 +0200 Subject: Make curvature work again by fixing a minor omission (bzr r8858.1.1) --- src/sp-path.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/sp-path.cpp b/src/sp-path.cpp index 2120ddd64..bbcb25356 100644 --- a/src/sp-path.cpp +++ b/src/sp-path.cpp @@ -295,6 +295,7 @@ sp_path_set(SPObject *object, unsigned int key, gchar const *value) object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_CONNECTOR_TYPE: + case SP_ATTR_CONNECTOR_CURVATURE: case SP_ATTR_CONNECTION_START: case SP_ATTR_CONNECTION_END: path->connEndPair.setAttr(key, value); -- cgit v1.2.3 From 20a99471b3b4aa9df5312c4d485b9835e7d8f345 Mon Sep 17 00:00:00 2001 From: Diederik van Lierop Date: Fri, 4 Dec 2009 23:38:36 +0100 Subject: Fix rounding error in libnr and remove inefficient mmx optimizations Fixed bugs: - https://launchpad.net/bugs/168384 (bzr r8863) --- src/libnr/nr-compose-transform.cpp | 55 +-- ...mx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM.S | 414 --------------------- 2 files changed, 19 insertions(+), 450 deletions(-) delete mode 100644 src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM.S (limited to 'src') diff --git a/src/libnr/nr-compose-transform.cpp b/src/libnr/nr-compose-transform.cpp index afc8fd987..ad1279f30 100644 --- a/src/libnr/nr-compose-transform.cpp +++ b/src/libnr/nr-compose-transform.cpp @@ -16,30 +16,25 @@ #include "nr-pixops.h" #include "nr-matrix.h" - -#ifdef WITH_MMX +/*#ifdef WITH_MMX #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* fixme: */ -int nr_have_mmx (void); -void nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 (unsigned char *px, int w, int h, int rs, - const unsigned char *spx, int sw, int sh, int srs, - const long *FFd2s, unsigned int alpha); -void nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (unsigned char *px, int w, int h, int rs, - const unsigned char *spx, int sw, int sh, int srs, - const long *FFd2s, const long *FF_S, unsigned int alpha, int dbits); +/*int nr_have_mmx (void); #define NR_PIXOPS_MMX (1 && nr_have_mmx ()) #ifdef __cplusplus } #endif //__cplusplus #endif +*/ /* fixme: Implement missing (Lauris) */ /* fixme: PREMUL colors before calculating average (Lauris) */ /* Fixed point precision */ #define FBITS 12 +#define FBITS_HP 18 // In some places we need a higher precision void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int sw, int sh, int srs, @@ -168,10 +163,10 @@ void nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_TRANSFORM (unsigned char *px, int w, in static void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int sw, int sh, int srs, - const long *FFd2s, unsigned int alpha) + const long long *FFd2s, unsigned int alpha) { - unsigned char *d0; - int FFsx0, FFsy0; + unsigned char *d0; + long long FFsx0, FFsy0; int x, y; d0 = px; @@ -180,15 +175,15 @@ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 (unsigned char *px, int w, int h for (y = 0; y < h; y++) { unsigned char *d; - long FFsx, FFsy; + long long FFsx, FFsy; d = d0; FFsx = FFsx0; FFsy = FFsy0; for (x = 0; x < w; x++) { long sx, sy; - sx = FFsx >> FBITS; + sx = long(FFsx >> FBITS_HP); if ((sx >= 0) && (sx < sw)) { - sy = FFsy >> FBITS; + sy = long(FFsy >> FBITS_HP); if ((sy >= 0) && (sy < sh)) { const unsigned char *s; unsigned int a; @@ -224,11 +219,11 @@ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 (unsigned char *px, int w, int h static void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int sw, int sh, int srs, - const long *FFd2s, const long *FF_S, unsigned int alpha, int dbits) + const long long *FFd2s, const long *FF_S, unsigned int alpha, int dbits) { int size; unsigned char *d0; - int FFsx0, FFsy0; + long long FFsx0, FFsy0; int x, y; size = (1 << dbits); @@ -242,7 +237,7 @@ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (unsigned char *px, int w, int h for (y = 0; y < h; y++) { unsigned char *d; - long FFsx, FFsy; + long long FFsx, FFsy; d = d0; FFsx = FFsx0; FFsy = FFsy0; @@ -252,9 +247,9 @@ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (unsigned char *px, int w, int h r = g = b = a = 0; for (i = 0; i < size; i++) { long sx, sy; - sx = (FFsx + FF_S[2 * i]) >> FBITS; + sx = (long (FFsx >> (FBITS_HP - FBITS)) + FF_S[2 * i]) >> FBITS; if ((sx >= 0) && (sx < sw)) { - sy = (FFsy + FF_S[2 * i + 1]) >> FBITS; + sy = (long (FFsy >> (FBITS_HP - FBITS)) + FF_S[2 * i + 1]) >> FBITS; if ((sy >= 0) && (sy < sh)) { const unsigned char *s; unsigned int ca; @@ -302,6 +297,7 @@ void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, in { int dbits; long FFd2s[6]; + long long FFd2s_HP[6]; // with higher precision int i; if (alpha == 0) return; @@ -310,17 +306,11 @@ void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, in for (i = 0; i < 6; i++) { FFd2s[i] = (long) (d2s[i] * (1 << FBITS) + 0.5); + FFd2s_HP[i] = (long long) (d2s[i] * (1 << FBITS_HP) + 0.5);; } if (dbits == 0) { -#ifdef WITH_MMX - if (NR_PIXOPS_MMX) { - /* WARNING: MMX composer REQUIRES w > 0 and h > 0 */ - nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 (px, w, h, rs, spx, sw, sh, srs, FFd2s, alpha); - return; - } -#endif - nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 (px, w, h, rs, spx, sw, sh, srs, FFd2s, alpha); + nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 (px, w, h, rs, spx, sw, sh, srs, FFd2s_HP, alpha); } else { int xsize, ysize; long FFs_x_x_S, FFs_x_y_S, FFs_y_x_S, FFs_y_y_S; @@ -344,14 +334,7 @@ void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, in } } -#ifdef WITH_MMX - if (NR_PIXOPS_MMX) { - /* WARNING: MMX composer REQUIRES w > 0 and h > 0 */ - nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (px, w, h, rs, spx, sw, sh, srs, FFd2s, FF_S, alpha, dbits); - return; - } -#endif - nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (px, w, h, rs, spx, sw, sh, srs, FFd2s, FF_S, alpha, dbits); + nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (px, w, h, rs, spx, sw, sh, srs, FFd2s_HP, FF_S, alpha, dbits); } } diff --git a/src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM.S b/src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM.S deleted file mode 100644 index e30056af2..000000000 --- a/src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM.S +++ /dev/null @@ -1,414 +0,0 @@ - .file "nr-compose-transform.c" - -# Ensure Inkscape is execshield protected - .section .note.GNU-stack - .previous - - .text - .align 2 -.globl nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 - .type nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0,@function - -/* - * This code is in public domain - * - */ - -nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0: - pushl %ebp - movl %esp, %ebp - pushl %ebx - subl $48, %esp - pushl %edi - pushl %esi - -/* Load %mm7 with [0 0 0 0] */ - movl $0, %eax - movd %eax, %mm7 - -/* Load %mm6 with [128 128 128 128] */ - movl $0x80808080, %eax - movd %eax, %mm6 - punpcklbw %mm7, %mm6 - -/* Load %mm5 with [255 255 255 255] */ - movl $0xffffffff, %eax - movd %eax, %mm5 - punpcklbw %mm7, %mm5 - -/* Load %mm0 with [a a a a] */ - movzbl 44(%ebp), %eax - movd %eax, %mm0 - punpcklwd %mm0, %mm0 - punpckldq %mm0, %mm0 - - movl 8(%ebp), %eax - movl %eax, -8(%ebp) - movl 40(%ebp), %eax - addl $16, %eax - movl (%eax), %eax - movl %eax, -12(%ebp) - movl 40(%ebp), %eax - addl $20, %eax - movl (%eax), %eax - movl %eax, -16(%ebp) - movl $0, -24(%ebp) -.L29: - movl -24(%ebp), %eax - cmpl 16(%ebp), %eax - jl .L32 - jmp .L28 -.L32: - movl -8(%ebp), %edi - - movl -12(%ebp), %eax - movl %eax, %esi - movl -16(%ebp), %eax - movl %eax, -36(%ebp) - - movl 12(%ebp), %ebx -.for_x_0: - - movl %esi, %ecx - cmpl $0, %ecx - js .clip_0 - sarl $12, %ecx - cmpl 28(%ebp), %ecx - jge .clip_0 - shll $2, %ecx - - movl -36(%ebp), %eax - cmpl $0, %eax - js .clip_0 - sarl $12, %eax - cmpl 32(%ebp), %eax - jge .clip_0 - imull 36(%ebp), %eax - - addl %ecx, %eax - addl 24(%ebp), %eax - -/* Fg -> %mm1 */ - movl (%eax), %eax - testl $0xff000000, %eax - jz .clip_0 - movd %eax, %mm1 - punpcklbw %mm7, %mm1 - -/* [a a a 255] -> %mm3 */ - shrl $24, %eax - movl $0x10101, %edx - mull %edx - orl $0xff000000, %eax - movd %eax, %mm3 - punpcklbw %mm7, %mm3 - -/* [Fg * a] -> mm1 */ - pmullw %mm3, %mm1 - paddw %mm6, %mm1 - movq %mm1, %mm4 - psrlw $8, %mm4 - paddw %mm4, %mm1 - psrlw $8, %mm1 - -/* Multiply by alpha */ - pmullw %mm0, %mm1 - paddw %mm6, %mm1 - movq %mm1, %mm4 - psrlw $8, %mm4 - paddw %mm4, %mm1 - psrlw $8, %mm1 - -/* [255 - FgA] -> mm2 */ - movq %mm1, %mm2 - punpckhwd %mm2, %mm2 - punpckhdq %mm2, %mm2 - pxor %mm5, %mm2 - -/* Bg -> mm3 */ - movd (%edi), %mm3 - punpcklbw %mm7, %mm3 - -/* Fg + ((255 - FgA) * Bg) / 255 */ - - pmullw %mm2, %mm3 - paddw %mm6, %mm3 - movq %mm3, %mm4 - psrlw $8, %mm4 - paddw %mm4, %mm3 - psrlw $8, %mm3 - paddw %mm1, %mm3 - -/* Store pixel */ - packuswb %mm3, %mm3 - movd %mm3, (%edi) - -.clip_0: -.L37: - movl 40(%ebp), %ecx - movl (%ecx), %edx - addl %edx, %esi - movl 4(%ecx), %edx - addl %edx, -36(%ebp) - - addl $4, %edi - - decl %ebx - jnz .for_x_0 - -.L34: - movl 8(%ecx), %edx - addl %edx, -12(%ebp) - movl 12(%ecx), %edx - addl %edx, -16(%ebp) - - movl 20(%ebp), %edx - leal -8(%ebp), %eax - addl %edx, (%eax) - leal -24(%ebp), %eax - incl (%eax) - jmp .L29 -.L28: - emms - popl %esi - popl %edi - addl $48, %esp - popl %ebx - popl %ebp - ret -.Lfe2: - .size nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0,.Lfe2-nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 - -/* - * - * dbits 52(%ebp) - * alpha 48(%ebp) - * FF_S 44(%ebp) - * - * d -32(%ebp) -> %edi - * i -60(%ebp) -> %esi - * sx -64(%ebp) -> %ebx - * sy -68(%ebp) - * s -72(%ebp) - * - * %mm0 a a a a - * %mm1 FgA - * %mm2 SumFgA - * %mm3 a a a 255 - * %mm4 -*/ - - .align 2 -.globl nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n - .type nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n,@function -nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n: - pushl %ebp - movl %esp, %ebp - pushl %ebx - subl $72, %esp - pushl %edi - pushl %esi - -/* Load %mm7 with [0 0 0 0] */ - movl $0, %eax - movd %eax, %mm7 - -/* Load %mm6 with [128 128 128 128] */ - movl $0x80808080, %eax - movd %eax, %mm6 - punpcklbw %mm7, %mm6 - -/* Load %mm5 with [255 255 255 255] */ - movl $0xffffffff, %eax - movd %eax, %mm5 - punpcklbw %mm7, %mm5 - -/* Load %mm0 with [a a a a] */ - movzbl 48(%ebp), %eax - movd %eax, %mm0 - punpcklwd %mm0, %mm0 - punpckldq %mm0, %mm0 - - movl $1, %eax - movzbl 52(%ebp), %ecx - sall %cl, %eax - movl %eax, -8(%ebp) - movl 8(%ebp), %eax - movl %eax, -12(%ebp) - movl 40(%ebp), %eax - addl $16, %eax - movl (%eax), %eax - movl %eax, -16(%ebp) - movl 40(%ebp), %eax - addl $20, %eax - movl (%eax), %eax - movl %eax, -20(%ebp) - movl $0, -28(%ebp) -.L44: - movl -28(%ebp), %eax - cmpl 16(%ebp), %eax - jl .L47 - jmp .exit_n -.L47: - movl -12(%ebp), %eax - movl %eax, -32(%ebp) - movl -16(%ebp), %eax - movl %eax, -36(%ebp) - movl -20(%ebp), %eax - movl %eax, -40(%ebp) - movl $0, -24(%ebp) -.L48: - movl -24(%ebp), %eax - cmpl 12(%ebp), %eax - jl .L51 - jmp .L49 -.L51: - -/* Zero accumulator */ - movq %mm7, %mm2 - -/* Set i to dptr (size - 1) */ - movl -8(%ebp), %esi - sub $1, %esi - shll $3, %esi - - movl 44(%ebp), %edi - movl -36(%ebp), %ecx - -.for_i_n: - movl (%edi,%esi), %ebx - addl %ecx, %ebx -/* Test negative before shift */ - cmpl $0, %ebx - js .next_i_n - sarl $12, %ebx - cmpl 28(%ebp), %ebx - jge .next_i_n -/* We multiply sx by 4 here */ - shll $2, %ebx - - movl 4(%edi,%esi), %eax - addl -40(%ebp), %eax -/* Test negative before shift */ - cmpl $0, %eax - js .next_i_n - sarl $12, %eax - cmpl 32(%ebp), %eax - jge .next_i_n -/* We multiply sy by srs here */ - imull 36(%ebp), %eax - - addl %ebx, %eax - addl 24(%ebp), %eax - -/* Fg -> %mm1 */ - movl (%eax), %eax - testl $0xff000000, %eax - jz .next_i_n - movd %eax, %mm1 - punpcklbw %mm7, %mm1 - -/* [a a a 255] -> %mm3 */ - shrl $24, %eax - movl $0x10101, %edx - mull %edx - orl $0xff000000, %eax - movd %eax, %mm3 - punpcklbw %mm7, %mm3 - -/* [Fg * a] -> mm1 */ - pmullw %mm3, %mm1 - paddw %mm6, %mm1 - movq %mm1, %mm4 - psrlw $8, %mm4 - paddw %mm4, %mm1 - psrlw $8, %mm1 - -/* Add to accumulator */ - paddw %mm1, %mm2 - -.next_i_n: - subl $8, %esi - jnb .for_i_n - -/* Divide components by sample size */ - movd 52(%ebp), %mm3 - psrlw %mm3, %mm2 - -/* Multiply by alpha */ - pmullw %mm0, %mm2 - paddw %mm6, %mm2 - movq %mm2, %mm4 - psrlw $8, %mm4 - paddw %mm4, %mm2 - psrlw $8, %mm2 - -/* [255 - FgA] -> mm1 */ - movq %mm2, %mm1 - punpckhwd %mm1, %mm1 - punpckhdq %mm1, %mm1 - pxor %mm5, %mm1 - - movl -32(%ebp), %edi -/* Bg -> mm3 */ - movd (%edi), %mm3 - punpcklbw %mm7, %mm3 - -/* Fg + ((255 - FgA) * Bg) / 255 */ - - pmullw %mm1, %mm3 - paddw %mm6, %mm3 - movq %mm3, %mm4 - psrlw $8, %mm4 - paddw %mm4, %mm3 - psrlw $8, %mm3 - paddw %mm2, %mm3 - -/* Store pixel */ - packuswb %mm3, %mm3 - movd %mm3, (%edi) - -.L58: - movl 40(%ebp), %eax - movl (%eax), %edx - leal -36(%ebp), %eax - addl %edx, (%eax) - movl 40(%ebp), %eax - addl $4, %eax - movl (%eax), %edx - leal -40(%ebp), %eax - addl %edx, (%eax) - leal -32(%ebp), %eax - addl $4, (%eax) - leal -24(%ebp), %eax - incl (%eax) - jmp .L48 -.L49: - movl 40(%ebp), %eax - addl $8, %eax - movl (%eax), %edx - leal -16(%ebp), %eax - addl %edx, (%eax) - movl 40(%ebp), %eax - addl $12, %eax - movl (%eax), %edx - leal -20(%ebp), %eax - addl %edx, (%eax) - movl 20(%ebp), %edx - leal -12(%ebp), %eax - addl %edx, (%eax) - leal -28(%ebp), %eax - incl (%eax) - jmp .L44 - -.exit_n: - emms - popl %esi - popl %edi - addl $72, %esp - popl %ebx - popl %ebp - ret -.Lfe3: - .size nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n,.Lfe3-nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n - .ident "GCC: (GNU) 3.2" -- cgit v1.2.3 From c6e3a60e0f469fe298b7e7b73ee5a57293674df9 Mon Sep 17 00:00:00 2001 From: Diederik van Lierop Date: Sat, 5 Dec 2009 13:28:45 +0100 Subject: Fix build again by modifiying makefile (bzr r8866) --- src/libnr/Makefile_insert | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/libnr/Makefile_insert b/src/libnr/Makefile_insert index 5cd2717be..4b19028f9 100644 --- a/src/libnr/Makefile_insert +++ b/src/libnr/Makefile_insert @@ -5,7 +5,6 @@ libnr_mmx_sources = \ libnr/have_mmx.S \ libnr/nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP.S \ libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP.S \ - libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM.S \ libnr/nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P.S endif -- cgit v1.2.3 From c7205e6390eae17c8ddc684f8a5600c15f206fe4 Mon Sep 17 00:00:00 2001 From: Diederik van Lierop Date: Sat, 5 Dec 2009 22:53:52 +0100 Subject: Text alignment is now taken into account when snapping a text object Fixed bugs: - https://launchpad.net/bugs/412746 (bzr r8867) --- src/sp-text.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/sp-text.cpp b/src/sp-text.cpp index 0d3fd791b..87c67c646 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -430,11 +430,32 @@ sp_text_description(SPItem *item) static void sp_text_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const */*snapprefs*/) { - // the baseline anchor of the first char + // Choose a point on the baseline for snapping from or to, with the horizontal position + // of this point depending on the text alignment (left vs. right) Inkscape::Text::Layout const *layout = te_get_layout((SPItem *) item); if(layout != NULL) { int type = target ? int(Inkscape::SNAPTARGET_TEXT_BASELINE) : int(Inkscape::SNAPSOURCE_TEXT_BASELINE); - p.push_back(std::make_pair(layout->characterAnchorPoint(layout->begin()) * sp_item_i2d_affine(item), type)); + + Inkscape::Text::Layout::iterator pos = layout->begin(); + Inkscape::Text::Layout::Alignment text_alignment = layout->paragraphAlignment(pos); + + Geom::Point left_pt = layout->characterAnchorPoint(pos) * sp_item_i2d_affine(item); + pos.thisEndOfLine(); + Geom::Point right_pt = layout->characterAnchorPoint(pos) * sp_item_i2d_affine(item); + Geom::Point mid_pt = (left_pt + right_pt)/2; + + switch (text_alignment) { + case Inkscape::Text::Layout::LEFT: + case Inkscape::Text::Layout::FULL: + p.push_back(std::make_pair(left_pt, type)); + break; + case Inkscape::Text::Layout::CENTER: + p.push_back(std::make_pair(mid_pt, type)); + break; + default: //RIGHT + p.push_back(std::make_pair(right_pt, type)); + break; + } } } -- cgit v1.2.3 From 0b073c684fd15710d06a3824a7b003fdfd780d8d Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sat, 5 Dec 2009 23:30:27 +0100 Subject: add cxxtests for marker_loc enum. code depends on the exact values of the enumeration. (bzr r8868) --- src/Makefile_insert | 1 + src/marker-test.h | 39 +++++++++++++++++++++++++++++++++++++++ src/sp-marker-loc.h | 2 ++ 3 files changed, 42 insertions(+) create mode 100644 src/marker-test.h (limited to 'src') diff --git a/src/Makefile_insert b/src/Makefile_insert index da49187ba..b32889f65 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -271,6 +271,7 @@ CXXTEST_TESTSUITES += \ $(srcdir)/color-profile-test.h \ $(srcdir)/dir-util-test.h \ $(srcdir)/extract-uri-test.h \ + $(srcdir)/marker-test.h \ $(srcdir)/mod360-test.h \ $(srcdir)/round-test.h \ $(srcdir)/preferences-test.h \ diff --git a/src/marker-test.h b/src/marker-test.h new file mode 100644 index 000000000..5b84dcc66 --- /dev/null +++ b/src/marker-test.h @@ -0,0 +1,39 @@ +/** @file + * @brief Unit tests for SVG marker handling + */ +/* Authors: + * Johan Engelen + * + * This file is released into the public domain. + */ + +#include + +#include "sp-marker-loc.h" + +class MarkerTest : public CxxTest::TestSuite +{ +public: + + void testMarkerLoc() + { + // code depends on these *exact* values, so check them here. + TS_ASSERT_EQUALS(SP_MARKER_LOC, 0); + TS_ASSERT_EQUALS(SP_MARKER_LOC_START, 1); + TS_ASSERT_EQUALS(SP_MARKER_LOC_MID, 2); + TS_ASSERT_EQUALS(SP_MARKER_LOC_END, 3); + TS_ASSERT_EQUALS(SP_MARKER_LOC_QTY, 4); + } + +}; + +/* + 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:encoding=utf-8:textwidth=99 : diff --git a/src/sp-marker-loc.h b/src/sp-marker-loc.h index 98cab3746..b6877e5aa 100644 --- a/src/sp-marker-loc.h +++ b/src/sp-marker-loc.h @@ -5,6 +5,8 @@ * These enums are to allow us to have 4-element arrays that represent a set of marker locations * (all, start, mid, and end). This allows us to iterate through the array in places where we need * to do a process across all of the markers, instead of separate code stanzas for each. + * + * IMPORTANT: the code assumes that the locations have the values as written below! so don't change the values!!! */ enum SPMarkerLoc { SP_MARKER_LOC = 0, -- cgit v1.2.3 From 486a20c1cba4361ede1e8542df98eaf0be2b1e83 Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Sun, 6 Dec 2009 01:56:13 -0200 Subject: added an icon to the color picker dialog to alert when there is too much ink in a color composition. (i.e. when the sum of color components is greater than 320%) (bzr r8870) --- src/widgets/sp-color-notebook.cpp | 23 +++++++++++++++++++++++ src/widgets/sp-color-notebook.h | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp index be41f5f0f..1870c2960 100644 --- a/src/widgets/sp-color-notebook.cpp +++ b/src/widgets/sp-color-notebook.cpp @@ -348,6 +348,14 @@ void ColorNotebook::init() gtk_widget_set_sensitive (_box_outofgamut, false); gtk_box_pack_start(GTK_BOX(rgbabox), _box_outofgamut, FALSE, FALSE, 2); + _box_toomuchink = gtk_event_box_new (); + GtkWidget *toomuchink = gtk_image_new_from_icon_name ("too-much-ink-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_container_add (GTK_CONTAINER (_box_toomuchink), toomuchink); + GtkTooltips *tooltips_toomuchink = gtk_tooltips_new (); + gtk_tooltips_set_tip (tooltips_toomuchink, _box_toomuchink, _("Too much ink!"), ""); + gtk_widget_set_sensitive (_box_toomuchink, false); + gtk_box_pack_start(GTK_BOX(rgbabox), _box_toomuchink, FALSE, FALSE, 2); + #endif //ENABLE_LCMS /* Create RGBA entry and color preview */ @@ -520,6 +528,21 @@ void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha ) if ( target_profile ) gtk_widget_set_sensitive (_box_outofgamut, target_profile->GamutCheck(color)); } + + /* update too-much-ink icon */ + gtk_widget_set_sensitive (_box_toomuchink, false); + if (color.icc){ + double ink_sum = 0; + for (unsigned int i=0; icolors.size(); i++){ + ink_sum += color.icc->colors[i]; + } + + /* Some literature states that when the sum of paint values exceed 320%, it is considered to be a satured color, + which means the paper can get too wet due to an excessive ammount of ink. This may lead to several issues + such as misalignment and poor quality of printing in general.*/ + if ( ink_sum > 3.2 ) + gtk_widget_set_sensitive (_box_toomuchink, true); + } #endif //ENABLE_LCMS if ( !_updatingrgba ) diff --git a/src/widgets/sp-color-notebook.h b/src/widgets/sp-color-notebook.h index b7cd8295f..5eb29ac73 100644 --- a/src/widgets/sp-color-notebook.h +++ b/src/widgets/sp-color-notebook.h @@ -62,7 +62,7 @@ protected: GtkWidget *_book; GtkWidget *_rgbal, *_rgbae; /* RGBA entry */ #if ENABLE_LCMS - GtkWidget *_box_outofgamut, *_box_colormanaged; + GtkWidget *_box_outofgamut, *_box_colormanaged, *_box_toomuchink; #endif //ENABLE_LCMS GtkWidget *_p; /* Color preview */ GtkWidget *_btn; -- cgit v1.2.3 From 2e19b78376f047bbde8eda750522d7250b05cdc6 Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Sun, 6 Dec 2009 06:00:44 -0200 Subject: * infrastructure to store device colors as described in http://www.w3.org/TR/2009/WD-SVGColor12-20091001/#device * related to https://bugs.launchpad.net/inkscape/+bug/444021 (bzr r8871) --- src/color.cpp | 58 +++++++++++++++++++++--- src/color.h | 2 + src/style.cpp | 12 +++++ src/svg/svg-color.cpp | 107 +++++++++++++++++++++++++++++++++++++++++++++ src/svg/svg-color.h | 3 ++ src/svg/svg-device-color.h | 26 +++++++++++ 6 files changed, 202 insertions(+), 6 deletions(-) create mode 100644 src/svg/svg-device-color.h (limited to 'src') diff --git a/src/color.cpp b/src/color.cpp index b16d9950f..abaab6310 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -17,6 +17,7 @@ #include #include "color.h" #include "svg/svg-icc-color.h" +#include "svg/svg-device-color.h" #include "svg/svg-color.h" #include "svg/css-ostringstream.h" @@ -29,7 +30,8 @@ static bool profileMatches( SVGICCColor const* first, SVGICCColor const* second #define PROFILE_EPSILON 0.00000001 SPColor::SPColor() : - icc(0) + icc(0), + device(0) { v.c[0] = 0; v.c[1] = 0; @@ -37,19 +39,22 @@ SPColor::SPColor() : } SPColor::SPColor( SPColor const& other ) : - icc(0) + icc(0), + device(0) { *this = other; } SPColor::SPColor( float r, float g, float b ) : - icc(0) + icc(0), + device(0) { set( r, g, b ); } SPColor::SPColor( guint32 value ) : - icc(0) + icc(0), + device(0) { set( value ); } @@ -57,20 +62,29 @@ SPColor::SPColor( guint32 value ) : SPColor::~SPColor() { delete icc; + delete device; icc = 0; + device = 0; } SPColor& SPColor::operator= (SPColor const& other) { - SVGICCColor* tmp = other.icc ? new SVGICCColor(*other.icc) : 0; + SVGICCColor* tmp_icc = other.icc ? new SVGICCColor(*other.icc) : 0; + SVGDeviceColor* tmp_device = other.device ? new SVGDeviceColor(*other.device) : 0; + v.c[0] = other.v.c[0]; v.c[1] = other.v.c[1]; v.c[2] = other.v.c[2]; if ( icc ) { delete icc; } - icc = tmp; + icc = tmp_icc; + + if ( device ) { + delete device; + } + device = tmp_device; return *this; } @@ -86,6 +100,7 @@ bool SPColor::operator == (SPColor const& other) const && (v.c[2] != other.v.c[2]); match &= profileMatches( icc, other.icc ); +//TODO?: match &= devicecolorMatches( device, other.device ); return match; } @@ -204,6 +219,37 @@ std::string SPColor::toString() const css << ')'; } + if ( device && device->type != DEVICE_COLOR_INVALID) { + if ( !css.str().empty() ) { + css << " "; + } + + switch(device->type){ + case DEVICE_GRAY: + css << "device-gray("; + break; + case DEVICE_RGB: + css << "device-rgb("; + break; + case DEVICE_CMYK: + css << "device-cmyk("; + break; + case DEVICE_NCHANNEL: + css << "device-nchannel("; + break; + case DEVICE_COLOR_INVALID: + //should not be reached + break; + } + + for (vector::const_iterator i(device->colors.begin()), + iEnd(device->colors.end()); + i != iEnd; ++i) { + css << ", " << *i; + } + css << ')'; + } + return css.str(); } diff --git a/src/color.h b/src/color.h index bebeaec60..7fd351360 100644 --- a/src/color.h +++ b/src/color.h @@ -34,6 +34,7 @@ #define SP_RGBA32_F_COMPOSE(r,g,b,a) SP_RGBA32_U_COMPOSE (SP_COLOR_F_TO_U (r), SP_COLOR_F_TO_U (g), SP_COLOR_F_TO_U (b), SP_COLOR_F_TO_U (a)) struct SVGICCColor; +struct SVGDeviceColor; /** * An RGB color with optional icc-color part @@ -59,6 +60,7 @@ struct SPColor { std::string toString() const; SVGICCColor* icc; + SVGDeviceColor* device; union { float c[3]; } v; diff --git a/src/style.cpp b/src/style.cpp index 0b946f348..111018c2a 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -28,6 +28,7 @@ #include "svg/svg.h" #include "svg/svg-color.h" #include "svg/svg-icc-color.h" +#include "svg/svg-device-color.h" #include "display/canvas-bpath.h" #include "attributes.h" @@ -3184,6 +3185,17 @@ sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocume } paint->value.color.icc = tmp; } + if (strneq(str, "device-gray(", 12) || + strneq(str, "device-rgb(", 11) || + strneq(str, "device-cmyk(", 12) || + strneq(str, "device-nchannel(", 16)) { + SVGDeviceColor* tmp = new SVGDeviceColor(); + if ( ! sp_svg_read_device_color( str, &str, tmp ) ) { + delete tmp; + tmp = 0; + } + paint->value.color.device = tmp; + } } } } diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp index d6b33402f..0eaa4431a 100644 --- a/src/svg/svg-color.cpp +++ b/src/svg/svg-color.cpp @@ -35,6 +35,7 @@ #include "preferences.h" #include "svg-color.h" #include "svg-icc-color.h" +#include "svg-device-color.h" #if ENABLE_LCMS #include @@ -604,6 +605,112 @@ bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest ) return sp_svg_read_icc_color(str, NULL, dest); } +bool sp_svg_read_device_color( gchar const *str, gchar const **end_ptr, SVGDeviceColor* dest) +{ + bool good = true; + unsigned int max_colors; + + if ( end_ptr ) { + *end_ptr = str; + } + if ( dest ) { + dest->colors.clear(); + } + + if ( !str ) { + // invalid input + good = false; + } else { + while ( g_ascii_isspace(*str) ) { + str++; + } + + dest->type = DEVICE_COLOR_INVALID; + if (strneq( str, "device-gray(", 12 )){ + dest->type = DEVICE_GRAY; + max_colors=1; + str += 12; + } + + if (strneq( str, "device-rgb(", 11 )){ + dest->type = DEVICE_RGB; + max_colors=3; + str += 11; + } + + if (strneq( str, "device-cmyk(", 12 )){ + dest->type = DEVICE_CMYK; + max_colors=4; + str += 12; + } + + if (strneq( str, "device-nchannel(", 16 )){ + dest->type = DEVICE_NCHANNEL; + max_colors=0; + str += 16; + } + + if ( dest->type != DEVICE_COLOR_INVALID ) { + while ( g_ascii_isspace(*str) ) { + str++; + } + + while ( *str && *str != ')' ) { + if ( g_ascii_isdigit(*str) || *str == '.' || *str == '-' || *str == '+') { + gchar* endPtr = 0; + gdouble dbl = g_ascii_strtod( str, &endPtr ); + if ( !errno ) { + if ( dest ) { + dest->colors.push_back( dbl ); +g_message("color: %f", dbl); + } + str = endPtr; + } else { + good = false; + break; + } + + while ( g_ascii_isspace(*str) || *str == ',' ) { + str++; + } + } else { + break; + } + } + } + + // We need to have ended on a closing parenthesis + if ( good ) { + while ( g_ascii_isspace(*str) ) { + str++; + } + good &= (*str == ')'); + } + } + + if ( dest->colors.size() == 0) good=false; + if ( dest->type != DEVICE_NCHANNEL && (dest->colors.size() != max_colors)) good=false; + + if ( good ) { + if ( end_ptr ) { + *end_ptr = str; + } + } else { + if ( dest ) { + dest->type = DEVICE_COLOR_INVALID; + dest->colors.clear(); + } + } + + return good; +} + + +bool sp_svg_read_device_color( gchar const *str, SVGDeviceColor* dest) +{ + return sp_svg_read_device_color(str, NULL, dest); +} + /* Local Variables: mode:c++ diff --git a/src/svg/svg-color.h b/src/svg/svg-color.h index a3868c149..f4e534652 100644 --- a/src/svg/svg-color.h +++ b/src/svg/svg-color.h @@ -4,6 +4,7 @@ #include class SVGICCColor; +class SVGDeviceColor; guint32 sp_svg_read_color(gchar const *str, unsigned int dfl); guint32 sp_svg_read_color(gchar const *str, gchar const **end_ptr, guint32 def); @@ -11,6 +12,8 @@ void sp_svg_write_color(char *buf, unsigned int buflen, unsigned int rgba32); bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor* dest ); bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest ); +bool sp_svg_read_device_color( gchar const *str, gchar const **end_ptr, SVGDeviceColor* dest ); +bool sp_svg_read_device_color( gchar const *str, SVGDeviceColor* dest ); void icc_color_to_sRGB(SVGICCColor* dest, guchar* r, guchar* g, guchar* b); #endif /* !SVG_SVG_COLOR_H_SEEN */ diff --git a/src/svg/svg-device-color.h b/src/svg/svg-device-color.h new file mode 100644 index 000000000..305133ed3 --- /dev/null +++ b/src/svg/svg-device-color.h @@ -0,0 +1,26 @@ +#ifndef SVG_DEVICE_COLOR_H_SEEN +#define SVG_DEVICE_COLOR_H_SEEN + +#include +#include + +typedef enum {DEVICE_COLOR_INVALID, DEVICE_GRAY, DEVICE_RGB, DEVICE_CMYK, DEVICE_NCHANNEL} SVGDeviceColorType; + +struct SVGDeviceColor { + SVGDeviceColorType type; + std::vector colors; +}; + + +#endif /* !SVG_DEVICE_COLOR_H_SEEN */ + +/* + 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:encoding=utf-8:textwidth=99 : -- cgit v1.2.3 From 15e328b2e1c6f08ca02c69096fca5018a577e4ec Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Sun, 6 Dec 2009 19:25:09 -0200 Subject: fix device-color output (bzr r8873) --- src/color.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/color.cpp b/src/color.cpp index abaab6310..07c15ff15 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -245,7 +245,8 @@ std::string SPColor::toString() const for (vector::const_iterator i(device->colors.begin()), iEnd(device->colors.end()); i != iEnd; ++i) { - css << ", " << *i; + if (i!=device->colors.begin()) css << ", "; + css << *i; } css << ')'; } -- cgit v1.2.3 From 10c0e8f5733f0cde622d259ffd70877ec35577c1 Mon Sep 17 00:00:00 2001 From: Josh Andler Date: Sun, 6 Dec 2009 14:19:59 -0800 Subject: Added UI for threading pref in Filter Preferences for Gaussian Blur, default 1 for sanity (bzr r8874) --- src/preferences-skeleton.h | 1 + src/ui/dialog/inkscape-preferences.cpp | 5 +++++ src/ui/dialog/inkscape-preferences.h | 1 + 3 files changed, 7 insertions(+) (limited to 'src') diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index 70d1140ba..4e5291baf 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -340,6 +340,7 @@ static char const preferences_skeleton[] = " \n" " \n" +" \n" " \n" "\n" " " diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index b217f8695..90516063c 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -741,6 +741,11 @@ void InkscapePreferences::initPageFilters() _page_filters.add_line(true, "", _show_filters_info_box, "", _("Show icons and descriptions for the filter primitives available at the filter effects dialog.")); + /* threaded blur */ //related comments/widgets/functions should be renamed and option should be moved elsewhere when inkscape is fully multi-threaded + _filter_multi_threaded.init("/options/threading/numthreads", 1.0, 8.0, 1.0, 2.0, 4.0, true, false); + _page_filters.add_line( false, _("Number of Threads:"), _filter_multi_threaded, _("(requires restart)"), + _("Configure number of processors/threads to use with rendering of gaussian blur."), false); + this->AddPage(_page_filters, _("Filters"), PREFS_PAGE_FILTERS); } diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index 705e7a352..16e62df59 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -171,6 +171,7 @@ protected: PrefRadioButton _blur_quality_best, _blur_quality_better, _blur_quality_normal, _blur_quality_worse, _blur_quality_worst; PrefRadioButton _filter_quality_best, _filter_quality_better, _filter_quality_normal, _filter_quality_worse, _filter_quality_worst; PrefCheckButton _show_filters_info_box; + PrefSpinButton _filter_multi_threaded; PrefCheckButton _trans_scale_stroke, _trans_scale_corner, _trans_gradient,_trans_pattern; PrefRadioButton _trans_optimized, _trans_preserved; -- cgit v1.2.3 From 335460908708e5a039392f6213d303551a0090f9 Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Mon, 7 Dec 2009 01:19:44 -0200 Subject: This commit hooks device-cmyk handling to color pickers. Now we have basic initial support for SVG Color 1.2 uncalibrated device colors: http://www.w3.org/TR/2009/WD-SVGColor12-20091001/#device Fixed bugs: - https://launchpad.net/bugs/444021 (bzr r8875) --- src/desktop-style.cpp | 28 ++++++++++++++++++++++++++++ src/svg/svg-color.cpp | 2 -- src/widgets/sp-color-scales.cpp | 26 +++++++++++++++++++++----- 3 files changed, 49 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index c8782051b..2afcd6109 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -44,6 +44,7 @@ #include "desktop-style.h" #include "svg/svg-icc-color.h" +#include "svg/svg-device-color.h" #include "box3d-side.h" /** @@ -432,6 +433,8 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill paint_res->set = TRUE; SVGICCColor* iccColor = 0; + SVGDeviceColor* devColor = 0; + bool iccSeen = false; gfloat c[4]; c[0] = c[1] = c[2] = c[3] = 0.0; @@ -529,6 +532,23 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill c[1] += d[1]; c[2] += d[2]; c[3] += SP_SCALE24_TO_FLOAT (isfill? style->fill_opacity.value : style->stroke_opacity.value); + + // average device color + unsigned int it; + if (i==objects /*if this is the first object in the GList*/ + && paint->value.color.device){ + devColor = new SVGDeviceColor(*paint->value.color.device); + for (it=0; it < paint->value.color.device->colors.size(); it++){ + devColor->colors[it] = 0; + } + } + + if (devColor && paint->value.color.device && paint->value.color.device->type == devColor->type){ + for (it=0; it < paint->value.color.device->colors.size(); it++){ + devColor->colors[it] += paint->value.color.device->colors[it]; + } + } + num ++; } @@ -568,6 +588,14 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill paint_res->value.color.icc = tmp; } + // divide and store the device-color + if (devColor){ + for (unsigned int it=0; it < devColor->colors.size(); it++){ + devColor->colors[it] /= num; + } + paint_res->value.color.device = devColor; + } + if (num > 1) { if (same_color) return QUERY_STYLE_MULTIPLE_SAME; diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp index 0eaa4431a..46779311e 100644 --- a/src/svg/svg-color.cpp +++ b/src/svg/svg-color.cpp @@ -559,7 +559,6 @@ bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor if ( !errno ) { if ( dest ) { dest->colors.push_back( dbl ); -g_message("color: %f", dbl); } str = endPtr; } else { @@ -662,7 +661,6 @@ bool sp_svg_read_device_color( gchar const *str, gchar const **end_ptr, SVGDevic if ( !errno ) { if ( dest ) { dest->colors.push_back( dbl ); -g_message("color: %f", dbl); } str = endPtr; } else { diff --git a/src/widgets/sp-color-scales.cpp b/src/widgets/sp-color-scales.cpp index a09a3a2ed..e41b81e5c 100644 --- a/src/widgets/sp-color-scales.cpp +++ b/src/widgets/sp-color-scales.cpp @@ -11,6 +11,7 @@ #include "../dialogs/dialog-events.h" #include "sp-color-scales.h" #include "svg/svg-icc-color.h" +#include "svg/svg-device-color.h" #define CSC_CHANNEL_R (1 << 0) #define CSC_CHANNEL_G (1 << 1) @@ -231,6 +232,12 @@ void ColorScales::_recalcColor( gboolean changing ) case SP_COLOR_SCALES_MODE_CMYK: { _getCmykaFloatv( c ); + color.device = new SVGDeviceColor(); + color.device->type=DEVICE_CMYK; + color.device->colors.clear(); + for (int i=0;i<4;i++){ + color.device->colors.push_back(c[i]); + } float rgb[3]; sp_color_cmyk_to_rgb_floatv( rgb, c[0], c[1], c[2], c[3] ); @@ -475,11 +482,20 @@ void ColorScales::setMode(SPColorScalesMode mode) gtk_widget_show (_s[4]); gtk_widget_show (_b[4]); _updating = TRUE; - sp_color_rgb_to_cmyk_floatv (c, rgba[0], rgba[1], rgba[2]); - setScaled( _a[0], c[0] ); - setScaled( _a[1], c[1] ); - setScaled( _a[2], c[2] ); - setScaled( _a[3], c[3] ); + + if (_color.device && _color.device->type == DEVICE_CMYK){ + setScaled( _a[0], _color.device->colors[0] ); + setScaled( _a[1], _color.device->colors[1] ); + setScaled( _a[2], _color.device->colors[2] ); + setScaled( _a[3], _color.device->colors[3] ); + } else { + //If we still dont have a device-color, convert from rbga + sp_color_rgb_to_cmyk_floatv (c, rgba[0], rgba[1], rgba[2]); + setScaled( _a[0], c[0] ); + setScaled( _a[1], c[1] ); + setScaled( _a[2], c[2] ); + setScaled( _a[3], c[3] ); + } setScaled( _a[4], rgba[3] ); _updating = FALSE; _updateSliders( CSC_CHANNELS_ALL ); -- cgit v1.2.3 From 8509d7dcf34dfc834f4a16cc100ec3cbc32357c2 Mon Sep 17 00:00:00 2001 From: Josh Andler Date: Mon, 7 Dec 2009 11:59:14 -0800 Subject: Correctly read and write values for Spray Tool option on Tool Controls Bar. (bzr r8876) --- src/spray-context.cpp | 8 ++++---- src/widgets/toolbox.cpp | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/spray-context.cpp b/src/spray-context.cpp index 71dc9648a..1c050ea06 100644 --- a/src/spray-context.cpp +++ b/src/spray-context.cpp @@ -379,14 +379,14 @@ sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val) Glib::ustring path = val->getEntryName(); if (path == "width") { - tc->width = CLAMP(val->getDouble(0.1), -1000.0, 1000.0); + tc->width = 0.01 * CLAMP(val->getInt(10), 1, 100); } else if (path == "mode") { tc->mode = val->getInt(); sp_spray_update_cursor(tc, false); } else if (path == "distribution") { tc->distrib = val->getInt(1); } else if (path == "population") { - tc->population = CLAMP(val->getDouble(), 0.0, 1.0); + tc->population = 0.01 * CLAMP(val->getInt(10), 1, 100); } else if (path == "tilt") { tc->tilt = CLAMP(val->getDouble(0.1), 0, 1000.0); } else if (path == "ratio") { @@ -402,9 +402,9 @@ sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val) } else if (path == "scale_max") { tc->scale_max = CLAMP(val->getDouble(1.0), 0, 10.0); } else if (path == "mean") { - tc->mean = CLAMP(val->getDouble(1.0), 0, 1.0); + tc->mean = 0.01 * CLAMP(val->getInt(10), 1, 100); } else if (path == "standard_deviation") { - tc->standard_deviation = CLAMP(val->getDouble(1.0), 0, 1.0); + tc->standard_deviation = 0.01 * CLAMP(val->getInt(10), 1, 100); } else if (path == "usepressure") { tc->usepressure = val->getBool(); } else if (path == "doh") { diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index b10c60c79..f5f1094f0 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -4469,15 +4469,15 @@ static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction { /* Width */ - gchar const* labels[] = {_("(pinch spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")}; + gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")}; gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100}; EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction", _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"), "/tools/spray/width", 15, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray", - 1, 100, 1.0, 0.0, + 1, 100, 1.0, 10.0, labels, values, G_N_ELEMENTS(labels), - sp_spray_width_value_changed, 0.01, 0, 100 ); + sp_spray_width_value_changed, 1, 0 ); ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); @@ -4491,9 +4491,9 @@ static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction _("Mean"), _("Mean:"), _("The mean of the spray action"), "/tools/spray/mean", 20, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean", - 1, 100, 1.0, 0.0, + 1, 100, 1.0, 10.0, labels, values, G_N_ELEMENTS(labels), - sp_spray_mean_value_changed, 0.01, 0, 100 ); + sp_spray_mean_value_changed, 1, 0 ); ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); @@ -4507,9 +4507,9 @@ static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction _("SD"), _("SD:"), _("The standard deviation of the spray action"), "/tools/spray/standard_deviation", 20, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation", - 1, 100, 1.0, 0.0, + 1, 100, 1.0, 10.0, labels, values, G_N_ELEMENTS(labels), - sp_spray_standard_deviation_value_changed, 0.01, 0, 100 ); + sp_spray_standard_deviation_value_changed, 1, 0 ); ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); @@ -4561,7 +4561,7 @@ static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction } { /* Population */ - gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")}; + gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")}; gdouble values[] = {10, 25, 35, 50, 60, 80, 100}; EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction", _("Population"), _("Population:"), @@ -4570,7 +4570,7 @@ static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population", 1, 100, 1.0, 10.0, labels, values, G_N_ELEMENTS(labels), - sp_spray_population_value_changed, 0.01, 0, 100 ); + sp_spray_population_value_changed, 1, 0 ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); g_object_set_data( holder, "spray_population", eact ); @@ -4937,7 +4937,7 @@ static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* main GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy", 1, 100, 1.0, 10.0, labels, values, G_N_ELEMENTS(labels), - sp_ddc_width_value_changed, 1, 0); + sp_ddc_width_value_changed, 1, 0 ); ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); -- cgit v1.2.3 From 77b13a8faf051fc4083b8f6e2efc57efe74d012f Mon Sep 17 00:00:00 2001 From: Ivan Mas??r Date: Tue, 8 Dec 2009 00:34:08 +0100 Subject: Spray Tool: translated forgotten strings from French to English (bzr r8877) --- src/ui/dialog/spray-option.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/ui/dialog/spray-option.cpp b/src/ui/dialog/spray-option.cpp index 8bfe455fa..33e9f2033 100644 --- a/src/ui/dialog/spray-option.cpp +++ b/src/ui/dialog/spray-option.cpp @@ -249,7 +249,7 @@ SprayOptionClass::SprayOptionClass() _ETable(3,2,false), _FTable(2,5,false), _unifLabel(_("Uniform")), - _gaussLabel(_("Gaussian ")), + _gaussLabel(_("Gaussian")), _anchorLabel(_("Distribution : ")) { @@ -257,8 +257,8 @@ SprayOptionClass::SprayOptionClass() //ComboBoxText - _combo.append_text(_("Uniforme")); - _combo.append_text(_("Gaussienne")); + _combo.append_text(_("Uniform")); + _combo.append_text(_("Gaussian")); _combo.set_active(prefs->getInt("/tools/spray/distribution", 1)); _combo.signal_changed().connect(sigc::mem_fun(*this, &SprayOptionClass::combo_action)); @@ -274,8 +274,8 @@ SprayOptionClass::SprayOptionClass() //Hbox Random - addFButton(_("Scale : ") ,_("Applique un facteur d'échelle"), 0, 0, "/tools/spray/scale_min","/tools/spray/scale_max"); - addFButton(_("Rotation : ") ,_("Fait tourner"), 1, 0, "/tools/spray/rot_min","/tools/spray/rot_max"); + addFButton(_("Scale : ") ,_("Apply a scale factor"), 0, 0, "/tools/spray/scale_min","/tools/spray/scale_max"); + addFButton(_("Rotation : ") ,_("Apply rotation"), 1, 0, "/tools/spray/rot_min","/tools/spray/rot_max"); _FHBox.pack_start(_FLabel); _FHBox.pack_start(_FTable); @@ -284,9 +284,9 @@ SprayOptionClass::SprayOptionClass() _FFrame.add(_FVBox); //Hbox Cursor - addEButton(_("Ratio : ") ,_("Excentricité de l'ellipse"), 0, 0, 0, 1,"/tools/spray/ratio"); - addEButton(_("Angle : ") ,_("Angle de l'ellipse"), 1, 0, 0, 5,"/tools/spray/tilt"); - addEButton(_("Width : ") ,_("Taille de l'ellipse"), 2, 0, 0, 1,"/tools/spray/width"); + addEButton(_("Ratio : ") ,_("Eccentricity of the ellipse"), 0, 0, 0, 1,"/tools/spray/ratio"); + addEButton(_("Angle : ") ,_("Angle of the ellipse"), 1, 0, 0, 5,"/tools/spray/tilt"); + addEButton(_("Width : ") ,_("Size of the ellipse"), 2, 0, 0, 1,"/tools/spray/width"); _HBox.pack_start(_Label); _HBox.pack_start(_ETable); -- cgit v1.2.3 From e9b65e09d897f4238eae8ea16021470e06da7076 Mon Sep 17 00:00:00 2001 From: Ivan Mas??r Date: Tue, 8 Dec 2009 00:35:12 +0100 Subject: Spray Tool: add context to strings (bzr r8878) --- src/ui/dialog/spray-option.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/ui/dialog/spray-option.cpp b/src/ui/dialog/spray-option.cpp index 33e9f2033..ec83b07cd 100644 --- a/src/ui/dialog/spray-option.cpp +++ b/src/ui/dialog/spray-option.cpp @@ -242,23 +242,23 @@ void on_selection_changed(Inkscape::Application */*inkscape*/, Inkscape::Selecti SprayOptionClass::SprayOptionClass() : UI::Widget::Panel ("", "/dialogs/spray", SP_VERB_DIALOG_SPRAY_OPTION), - _distributionFrame(_("Distribution")), - _Frame(_("Cursor Options")), - _FFrame(_("Random Options")), + _distributionFrame(Q_("sprayOptions|Distribution")), + _Frame(Q_("sprayOptions|Cursor Options")), + _FFrame(Q_("sprayOptions|Random Options")), _gaussianTable(1, 5, false), _ETable(3,2,false), _FTable(2,5,false), - _unifLabel(_("Uniform")), - _gaussLabel(_("Gaussian")), - _anchorLabel(_("Distribution : ")) + _unifLabel(Q_("sprayOptions|Uniform")), + _gaussLabel(Q_("sprayOptions|Gaussian")), + _anchorLabel(Q_("sprayOptions|Distribution:")) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); //ComboBoxText - _combo.append_text(_("Uniform")); - _combo.append_text(_("Gaussian")); + _combo.append_text(Q_("sprayOptions|Uniform")); + _combo.append_text(Q_("sprayOptions|Gaussian")); _combo.set_active(prefs->getInt("/tools/spray/distribution", 1)); _combo.signal_changed().connect(sigc::mem_fun(*this, &SprayOptionClass::combo_action)); @@ -274,8 +274,8 @@ SprayOptionClass::SprayOptionClass() //Hbox Random - addFButton(_("Scale : ") ,_("Apply a scale factor"), 0, 0, "/tools/spray/scale_min","/tools/spray/scale_max"); - addFButton(_("Rotation : ") ,_("Apply rotation"), 1, 0, "/tools/spray/rot_min","/tools/spray/rot_max"); + addFButton(Q_("sprayOptions|Scale:") ,_("Apply a scale factor"), 0, 0, "/tools/spray/scale_min","/tools/spray/scale_max"); + addFButton(Q_("sprayOptions|Rotation:") ,_("Apply rotation"), 1, 0, "/tools/spray/rot_min","/tools/spray/rot_max"); _FHBox.pack_start(_FLabel); _FHBox.pack_start(_FTable); @@ -284,9 +284,9 @@ SprayOptionClass::SprayOptionClass() _FFrame.add(_FVBox); //Hbox Cursor - addEButton(_("Ratio : ") ,_("Eccentricity of the ellipse"), 0, 0, 0, 1,"/tools/spray/ratio"); - addEButton(_("Angle : ") ,_("Angle of the ellipse"), 1, 0, 0, 5,"/tools/spray/tilt"); - addEButton(_("Width : ") ,_("Size of the ellipse"), 2, 0, 0, 1,"/tools/spray/width"); + addEButton(Q_("sprayOptions|Ratio:") ,_("Eccentricity of the ellipse"), 0, 0, 0, 1,"/tools/spray/ratio"); + addEButton(Q_("sprayOptions|Angle:") ,_("Angle of the ellipse"), 1, 0, 0, 5,"/tools/spray/tilt"); + addEButton(Q_("sprayOptions|Width:") ,_("Size of the ellipse"), 2, 0, 0, 1,"/tools/spray/width"); _HBox.pack_start(_Label); _HBox.pack_start(_ETable); -- cgit v1.2.3 From b13df173f8e6471ce720d45370447b9e52cd3925 Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Fri, 11 Dec 2009 06:37:11 -0200 Subject: - new: Print Colors Preview Dialog and rendermode - works with shapes. I still need to make it work with gradients and imported images (bzr r8881) --- src/desktop.cpp | 3 + src/desktop.h | 3 + src/display/nr-arena-item.cpp | 4 +- src/display/nr-arena-shape.cpp | 63 +++++++++++++--- src/display/rendermode.h | 3 +- src/interface.cpp | 2 + src/menus-skeleton.h | 2 + src/ui/dialog/Makefile_insert | 2 + src/ui/dialog/dialog-manager.cpp | 3 + src/ui/dialog/print-colors-preview-dialog.cpp | 100 ++++++++++++++++++++++++++ src/ui/dialog/print-colors-preview-dialog.h | 48 +++++++++++++ src/verbs.cpp | 10 +++ src/verbs.h | 2 + src/widgets/desktop-widget.cpp | 8 +++ 14 files changed, 240 insertions(+), 13 deletions(-) create mode 100644 src/ui/dialog/print-colors-preview-dialog.cpp create mode 100644 src/ui/dialog/print-colors-preview-dialog.h (limited to 'src') diff --git a/src/desktop.cpp b/src/desktop.cpp index 319a0d407..0e4d4caf3 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -458,6 +458,9 @@ void SPDesktop::displayModeToggle() { _setDisplayMode(Inkscape::RENDERMODE_OUTLINE); break; case Inkscape::RENDERMODE_OUTLINE: + _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); + break; + case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW: default: _setDisplayMode(Inkscape::RENDERMODE_NORMAL); } diff --git a/src/desktop.h b/src/desktop.h index cfb977425..a02a31034 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -201,6 +201,9 @@ struct SPDesktop : public Inkscape::UI::View::View void setDisplayModeOutline() { _setDisplayMode(Inkscape::RENDERMODE_OUTLINE); } + void setDisplayModePrintColorsPreview() { + _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); + } void displayModeToggle(); Inkscape::RenderMode _display_mode; Inkscape::RenderMode getMode() const { return _display_mode; } diff --git a/src/display/nr-arena-item.cpp b/src/display/nr-arena-item.cpp index bdab5b479..b80df7273 100644 --- a/src/display/nr-arena-item.cpp +++ b/src/display/nr-arena-item.cpp @@ -312,7 +312,9 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area NRPixBlock *pb, unsigned int flags) { bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); - bool filter = (item->arena->rendermode == Inkscape::RENDERMODE_NORMAL); + bool filter = (item->arena->rendermode != Inkscape::RENDERMODE_OUTLINE && + item->arena->rendermode != Inkscape::RENDERMODE_NO_FILTERS); + bool print_colors = (item->arena->rendermode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID); nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), diff --git a/src/display/nr-arena-shape.cpp b/src/display/nr-arena-shape.cpp index 96ea76cbe..d5a098c39 100644 --- a/src/display/nr-arena-shape.cpp +++ b/src/display/nr-arena-shape.cpp @@ -35,6 +35,8 @@ #include "display/nr-filter.h" #include #include +#include "preferences.h" +#include "svg/svg-device-color.h" #include #include "svg/svg.h" @@ -844,6 +846,7 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock if (!shape->style) return item->state; bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); + bool print_colors = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); if (outline) { // cairo outline rendering @@ -873,7 +876,22 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock } } + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + SPStyle const *style = shape->style; + bool render_cyan = prefs->getBool("/options/printcolorspreview/cyan", true); + bool render_magenta = prefs->getBool("/options/printcolorspreview/magenta", true); + bool render_yellow = prefs->getBool("/options/printcolorspreview/yellow", true); + bool render_black = prefs->getBool("/options/printcolorspreview/black", true); + + float rgb_v[3]; + float cmyk_v[4]; +#define FLOAT_TO_UINT8(f) (int(f*255)) +#define RGBA_R(v) ((v) >> 24) +#define RGBA_G(v) (((v) >> 16) & 0xff) +#define RGBA_B(v) (((v) >> 8) & 0xff) +#define RGBA_A(v) ((v) & 0xff) + if (shape->fill_shp) { NRPixBlock m; guint32 rgba; @@ -893,12 +911,24 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock if (shape->_fill.paint.type() == NRArenaShape::Paint::NONE) { // do not render fill in any way } else if (shape->_fill.paint.type() == NRArenaShape::Paint::COLOR) { + + const SPColor* fill_color = &shape->_fill.paint.color(); if ( item->render_opacity ) { - rgba = shape->_fill.paint.color().toRGBA32( shape->_fill.opacity * - SP_SCALE24_TO_FLOAT(style->opacity.value) ); + rgba = fill_color->toRGBA32( shape->_fill.opacity * + SP_SCALE24_TO_FLOAT(style->opacity.value) ); } else { - rgba = shape->_fill.paint.color().toRGBA32( shape->_fill.opacity ); + rgba = fill_color->toRGBA32( shape->_fill.opacity ); + } + + if (print_colors){ + sp_color_rgb_to_cmyk_floatv (cmyk_v, RGBA_R(rgba)/256.0, RGBA_G(rgba)/256.0, RGBA_B(rgba)/256.0); + sp_color_cmyk_to_rgb_floatv (rgb_v, render_cyan ? cmyk_v[0] : 0, + render_magenta ? cmyk_v[1] : 0, + render_yellow ? cmyk_v[2] : 0, + render_black ? cmyk_v[3] : 0); + rgba = (FLOAT_TO_UINT8(rgb_v[0])<<24) + (FLOAT_TO_UINT8(rgb_v[1])<<16) + (FLOAT_TO_UINT8(rgb_v[2])<<8) + 0xff; } + nr_blit_pixblock_mask_rgba32(pb, &m, rgba); pb->empty = FALSE; } else if (shape->_fill.paint.type() == NRArenaShape::Paint::SERVER) { @@ -929,14 +959,25 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock nr_pixblock_render_shape_mask_or(m, shape->stroke_shp); m.empty = FALSE; - if ( item->render_opacity ) { - rgba = shape->_stroke.paint.color().toRGBA32( shape->_stroke.opacity * - SP_SCALE24_TO_FLOAT(style->opacity.value) ); - } else { - rgba = shape->_stroke.paint.color().toRGBA32( shape->_stroke.opacity ); - } - nr_blit_pixblock_mask_rgba32(pb, &m, rgba); - pb->empty = FALSE; + const SPColor* stroke_color = &shape->_stroke.paint.color(); + if ( item->render_opacity ) { + rgba = stroke_color->toRGBA32( shape->_stroke.opacity * + SP_SCALE24_TO_FLOAT(style->opacity.value) ); + } else { + rgba = stroke_color->toRGBA32( shape->_stroke.opacity ); + } + + if (print_colors){ + sp_color_rgb_to_cmyk_floatv (cmyk_v, RGBA_R(rgba)/256.0, RGBA_G(rgba)/256.0, RGBA_B(rgba)/256.0); + sp_color_cmyk_to_rgb_floatv (rgb_v, render_cyan ? cmyk_v[0] : 0, + render_magenta ? cmyk_v[1] : 0, + render_yellow ? cmyk_v[2] : 0, + render_black ? cmyk_v[3] : 0); + rgba = (FLOAT_TO_UINT8(rgb_v[0])<<24) + (FLOAT_TO_UINT8(rgb_v[1])<<16) + (FLOAT_TO_UINT8(rgb_v[2])<<8) + 0xff; + } + + nr_blit_pixblock_mask_rgba32(pb, &m, rgba); + pb->empty = FALSE; nr_pixblock_release(&m); diff --git a/src/display/rendermode.h b/src/display/rendermode.h index 1b59ae9bb..abcdb3db4 100644 --- a/src/display/rendermode.h +++ b/src/display/rendermode.h @@ -12,7 +12,8 @@ namespace Inkscape { enum RenderMode { RENDERMODE_NORMAL, RENDERMODE_NO_FILTERS, - RENDERMODE_OUTLINE + RENDERMODE_OUTLINE, + RENDERMODE_PRINT_COLORS_PREVIEW }; } diff --git a/src/interface.cpp b/src/interface.cpp index 415593b78..b29b91d18 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -703,6 +703,8 @@ update_view_menu(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_dat new_state = mode == Inkscape::RENDERMODE_NO_FILTERS; } else if (!strcmp(action->id, "ViewModeOutline")) { new_state = mode == Inkscape::RENDERMODE_OUTLINE; + } else if (!strcmp(action->id, "ViewModePrintColorsPreview")) { + new_state = mode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW; } else { g_warning("update_view_menu does not handle this verb"); } diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h index d7a18f211..c664d683a 100644 --- a/src/menus-skeleton.h +++ b/src/menus-skeleton.h @@ -110,6 +110,8 @@ static char const menus_skeleton[] = " \n" " \n" " \n" +" \n" +" \n" " \n" " \n" " \n" diff --git a/src/ui/dialog/Makefile_insert b/src/ui/dialog/Makefile_insert index fd1b07394..fac5bad80 100644 --- a/src/ui/dialog/Makefile_insert +++ b/src/ui/dialog/Makefile_insert @@ -71,6 +71,8 @@ ink_common_sources += \ ui/dialog/panel-dialog.h \ ui/dialog/print.cpp \ ui/dialog/print.h \ + ui/dialog/print-colors-preview-dialog.cpp \ + ui/dialog/print-colors-preview-dialog.h \ ui/dialog/scriptdialog.cpp \ ui/dialog/scriptdialog.h \ ui/dialog/spray-option.cpp \ diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index 7f853bedc..2116d46c3 100644 --- a/src/ui/dialog/dialog-manager.cpp +++ b/src/ui/dialog/dialog-manager.cpp @@ -41,6 +41,7 @@ #include "ui/dialog/floating-behavior.h" #include "ui/dialog/dock-behavior.h" #include "ui/dialog/spray-option.h" +#include "ui/dialog/print-colors-preview-dialog.h" #include "preferences.h" #ifdef ENABLE_SVG_FONTS @@ -102,6 +103,7 @@ DialogManager::DialogManager() { registerFactory("LivePathEffect", &create); registerFactory("Memory", &create); registerFactory("Messages", &create); + registerFactory("PrintColorsPreviewDialog", &create); registerFactory("Script", &create); #ifdef ENABLE_SVG_FONTS registerFactory("SvgFontsDialog", &create); @@ -129,6 +131,7 @@ DialogManager::DialogManager() { registerFactory("LivePathEffect", &create); registerFactory("Memory", &create); registerFactory("Messages", &create); + registerFactory("PrintColorsPreviewDialog", &create); registerFactory("Script", &create); #ifdef ENABLE_SVG_FONTS registerFactory("SvgFontsDialog", &create); diff --git a/src/ui/dialog/print-colors-preview-dialog.cpp b/src/ui/dialog/print-colors-preview-dialog.cpp new file mode 100644 index 000000000..f4d83c271 --- /dev/null +++ b/src/ui/dialog/print-colors-preview-dialog.cpp @@ -0,0 +1,100 @@ +/** @file + * @brief Print Colors Preview dialog - implementation + */ +/* Authors: + * Felipe C. da S. Sanches + * + * Copyright (C) 2009 Authors + * Released under GNU GPLv2 (or later). Read the file 'COPYING' for more information. + */ + +#include "desktop.h" +#include "print-colors-preview-dialog.h" +#include "preferences.h" +#include + +namespace Inkscape { +namespace UI { +namespace Dialog { + +//Yes, I know we shouldn't hardcode CMYK. This class needs to be refactored +// in order to accomodate spot colors and color components defined using +// ICC colors. --Juca + +void PrintColorsPreviewDialog::toggle_cyan(){ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/options/printcolorspreview/cyan", cyan->get_active()); + + SPDesktop *desktop = getDesktop(); + desktop->setDisplayModePrintColorsPreview(); +} + +void PrintColorsPreviewDialog::toggle_magenta(){ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/options/printcolorspreview/magenta", magenta->get_active()); + + SPDesktop *desktop = getDesktop(); + desktop->setDisplayModePrintColorsPreview(); +} + +void PrintColorsPreviewDialog::toggle_yellow(){ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/options/printcolorspreview/yellow", yellow->get_active()); + + SPDesktop *desktop = getDesktop(); + desktop->setDisplayModePrintColorsPreview(); +} + +void PrintColorsPreviewDialog::toggle_black(){ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/options/printcolorspreview/black", black->get_active()); + + SPDesktop *desktop = getDesktop(); + desktop->setDisplayModePrintColorsPreview(); +} + +PrintColorsPreviewDialog::PrintColorsPreviewDialog() + : UI::Widget::Panel("", "/dialogs/printcolorspreview", SP_VERB_DIALOG_PRINT_COLORS_PREVIEW) +{ + Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox()); + + cyan = new Gtk::ToggleButton(_("Cyan")); + vbox->pack_start( *cyan, false, false ); +// tips.set_tip((*cyan), _("Render cyan separation")); + cyan->signal_clicked().connect( sigc::mem_fun(*this, &PrintColorsPreviewDialog::toggle_cyan) ); + + magenta = new Gtk::ToggleButton(_("Magenta")); + vbox->pack_start( *magenta, false, false ); +// tips.set_tip((*magenta), _("Render magenta separation")); + magenta->signal_clicked().connect( sigc::mem_fun(*this, &PrintColorsPreviewDialog::toggle_magenta) ); + + yellow = new Gtk::ToggleButton(_("Yellow")); + vbox->pack_start( *yellow, false, false ); +// tips.set_tip((*yellow), _("Render yellow separation")); + yellow->signal_clicked().connect( sigc::mem_fun(*this, &PrintColorsPreviewDialog::toggle_yellow) ); + + black = new Gtk::ToggleButton(_("Black")); + vbox->pack_start( *black, false, false ); +// tips.set_tip((*black), _("Render black separation")); + black->signal_clicked().connect( sigc::mem_fun(*this, &PrintColorsPreviewDialog::toggle_black) ); + + gint val; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + val = prefs->getBool("/options/printcolorspreview/cyan"); + cyan->set_active( val != 0 ); + val = prefs->getBool("/options/printcolorspreview/magenta"); + magenta->set_active( val != 0 ); + val = prefs->getBool("/options/printcolorspreview/yellow"); + yellow->set_active( val != 0 ); + val = prefs->getBool("/options/printcolorspreview/black"); + black->set_active( val != 0 ); + + _getContents()->add(*vbox); + _getContents()->show_all(); +} + +PrintColorsPreviewDialog::~PrintColorsPreviewDialog(){} + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape diff --git a/src/ui/dialog/print-colors-preview-dialog.h b/src/ui/dialog/print-colors-preview-dialog.h new file mode 100644 index 000000000..246908556 --- /dev/null +++ b/src/ui/dialog/print-colors-preview-dialog.h @@ -0,0 +1,48 @@ +/** @file + * @brief Print Colors Preview dialog + */ +/* Authors: + * Felipe Corrêa da Silva Sanches + * + * Copyright (C) 2009 Authors + * Released under GNU GPLv2 (or later). Read the file 'COPYING' for more information. + */ + +#ifndef INKSCAPE_UI_DIALOG_PRINT_COLORS_PREVIEW_H +#define INKSCAPE_UI_DIALOG_PRINT_COLORS_PREVIEW_H + +#include "ui/widget/panel.h" +#include "verbs.h" + +#include +#include + +namespace Inkscape { +namespace UI { +namespace Dialog { + +class PrintColorsPreviewDialog : public UI::Widget::Panel { +public: + PrintColorsPreviewDialog(); + ~PrintColorsPreviewDialog(); + + static PrintColorsPreviewDialog &getInstance() + { return *new PrintColorsPreviewDialog(); } + +private: + void toggle_cyan(); + void toggle_magenta(); + void toggle_yellow(); + void toggle_black(); + + Gtk::ToggleButton* cyan; + Gtk::ToggleButton* magenta; + Gtk::ToggleButton* yellow; + Gtk::ToggleButton* black; +}; + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape + +#endif //#ifndef INKSCAPE_UI_PRINT_COLORS_PREVIEW_H diff --git a/src/verbs.cpp b/src/verbs.cpp index 56b63e95e..6f86c3cce 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1749,6 +1749,9 @@ ZoomVerb::perform(SPAction *action, void *data, void */*pdata*/) case SP_VERB_VIEW_MODE_OUTLINE: dt->setDisplayModeOutline(); break; + case SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW: + dt->setDisplayModePrintColorsPreview(); + break; case SP_VERB_VIEW_MODE_TOGGLE: dt->displayModeToggle(); break; @@ -1871,6 +1874,9 @@ DialogVerb::perform(SPAction *action, void *data, void */*pdata*/) case SP_VERB_DIALOG_SVG_FONTS: dt->_dlg_mgr->showDialog("SvgFontsDialog"); break; + case SP_VERB_DIALOG_PRINT_COLORS_PREVIEW: + dt->_dlg_mgr->showDialog("PrintColorsPreviewDialog"); + break; default: break; } @@ -2626,6 +2632,8 @@ Verb *Verb::_base_verbs[] = { N_("Switch to normal display without filters"), NULL), new ZoomVerb(SP_VERB_VIEW_MODE_OUTLINE, "ViewModeOutline", N_("_Outline"), N_("Switch to outline (wireframe) display mode"), NULL), + new ZoomVerb(SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW, "ViewModePrintColorsPreview", N_("_Print Colors Preview"), + N_("Switch to print colors preview mode"), NULL), new ZoomVerb(SP_VERB_VIEW_MODE_TOGGLE, "ViewModeToggle", N_("_Toggle"), N_("Toggle between normal and outline display modes"), NULL), @@ -2701,6 +2709,8 @@ Verb *Verb::_base_verbs[] = { N_("Manage, edit, and apply SVG filters"), NULL), new DialogVerb(SP_VERB_DIALOG_SVG_FONTS, "DialogSVGFonts", N_("SVG Font Editor..."), N_("Edit SVG fonts"), NULL), + new DialogVerb(SP_VERB_DIALOG_PRINT_COLORS_PREVIEW, "DialogPrintColorsPreview", N_("Print Colors..."), + N_("Select which color separations to render in Print Colors Preview rendermode"), NULL), /* Help */ new HelpVerb(SP_VERB_HELP_ABOUT_EXTENSIONS, "HelpAboutExtensions", N_("About E_xtensions"), diff --git a/src/verbs.h b/src/verbs.h index 3ea2fdee8..d0abcdca2 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -214,6 +214,7 @@ enum { SP_VERB_VIEW_MODE_NORMAL, SP_VERB_VIEW_MODE_NO_FILTERS, SP_VERB_VIEW_MODE_OUTLINE, + SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW, SP_VERB_VIEW_MODE_TOGGLE, SP_VERB_VIEW_CMS_TOGGLE, SP_VERB_VIEW_ICON_PREVIEW, @@ -251,6 +252,7 @@ enum { SP_VERB_DIALOG_LIVE_PATH_EFFECT, SP_VERB_DIALOG_FILTER_EFFECTS, SP_VERB_DIALOG_SVG_FONTS, + SP_VERB_DIALOG_PRINT_COLORS_PREVIEW, /* Help */ SP_VERB_HELP_ABOUT_EXTENSIONS, SP_VERB_HELP_MEMORY, diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index e3bf1ae9c..b63992afe 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -613,12 +613,20 @@ SPDesktopWidget::updateTitle(gchar const* uri) if (this->desktop->number > 1) { if (this->desktop->getMode() == Inkscape::RENDERMODE_OUTLINE) { g_string_printf (name, _("%s: %d (outline) - Inkscape"), fname, this->desktop->number); + } else if (this->desktop->getMode() == Inkscape::RENDERMODE_NO_FILTERS) { + g_string_printf (name, _("%s: %d (no filters) - Inkscape"), fname, this->desktop->number); + } else if (this->desktop->getMode() == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW) { + g_string_printf (name, _("%s: %d (print colors preview) - Inkscape"), fname, this->desktop->number); } else { g_string_printf (name, _("%s: %d - Inkscape"), fname, this->desktop->number); } } else { if (this->desktop->getMode() == Inkscape::RENDERMODE_OUTLINE) { g_string_printf (name, _("%s (outline) - Inkscape"), fname); + } else if (this->desktop->getMode() == Inkscape::RENDERMODE_NO_FILTERS) { + g_string_printf (name, _("%s (no filters) - Inkscape"), fname); + } else if (this->desktop->getMode() == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW) { + g_string_printf (name, _("%s (print colors preview) - Inkscape"), fname); } else { g_string_printf (name, _("%s - Inkscape"), fname); } -- cgit v1.2.3 From 8dd8377e1203d214c0119b2c044ba4c3312cecb4 Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Fri, 11 Dec 2009 07:16:15 -0200 Subject: refactoring of previous commit + add separation to glyph renderer also (bzr r8882) --- src/display/nr-arena-glyphs.cpp | 9 +++++++++ src/display/nr-arena-shape.cpp | 38 +++++--------------------------------- src/display/nr-arena.cpp | 24 ++++++++++++++++++++++++ src/display/nr-arena.h | 2 ++ 4 files changed, 40 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/display/nr-arena-glyphs.cpp b/src/display/nr-arena-glyphs.cpp index 429f1ed32..db0922915 100644 --- a/src/display/nr-arena-glyphs.cpp +++ b/src/display/nr-arena-glyphs.cpp @@ -444,6 +444,7 @@ nr_arena_glyphs_group_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPi SPStyle const *style = ggroup->style; guint ret = item->state; + bool print_colors_preview = (item->arena->rendermode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); if (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE) { @@ -511,6 +512,10 @@ nr_arena_glyphs_group_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPi } else { rgba = style->fill.value.color.toRGBA32( SP_SCALE24_TO_FLOAT(style->fill_opacity.value) ); } + + if (print_colors_preview) + nr_arena_separate_color_plates(&rgba); + nr_blit_pixblock_mask_rgba32(pb, &m, rgba); pb->empty = FALSE; } @@ -551,6 +556,10 @@ nr_arena_glyphs_group_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPi } else { rgba = style->stroke.value.color.toRGBA32( SP_SCALE24_TO_FLOAT(style->stroke_opacity.value) ); } + + if (print_colors_preview) + nr_arena_separate_color_plates(&rgba); + nr_blit_pixblock_mask_rgba32(pb, &m, rgba); pb->empty = FALSE; } else { diff --git a/src/display/nr-arena-shape.cpp b/src/display/nr-arena-shape.cpp index d5a098c39..e2a9e9580 100644 --- a/src/display/nr-arena-shape.cpp +++ b/src/display/nr-arena-shape.cpp @@ -36,7 +36,6 @@ #include #include #include "preferences.h" -#include "svg/svg-device-color.h" #include #include "svg/svg.h" @@ -833,7 +832,6 @@ cairo_arena_shape_render_stroke(NRArenaItem *item, NRRectL *area, NRPixBlock *pb pb->empty = FALSE; } - /** * Renders the item. Markers are just composed into the parent buffer. */ @@ -846,7 +844,7 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock if (!shape->style) return item->state; bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); - bool print_colors = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); + bool print_colors_preview = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); if (outline) { // cairo outline rendering @@ -876,21 +874,7 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock } } - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - SPStyle const *style = shape->style; - bool render_cyan = prefs->getBool("/options/printcolorspreview/cyan", true); - bool render_magenta = prefs->getBool("/options/printcolorspreview/magenta", true); - bool render_yellow = prefs->getBool("/options/printcolorspreview/yellow", true); - bool render_black = prefs->getBool("/options/printcolorspreview/black", true); - - float rgb_v[3]; - float cmyk_v[4]; -#define FLOAT_TO_UINT8(f) (int(f*255)) -#define RGBA_R(v) ((v) >> 24) -#define RGBA_G(v) (((v) >> 16) & 0xff) -#define RGBA_B(v) (((v) >> 8) & 0xff) -#define RGBA_A(v) ((v) & 0xff) if (shape->fill_shp) { NRPixBlock m; @@ -920,14 +904,8 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock rgba = fill_color->toRGBA32( shape->_fill.opacity ); } - if (print_colors){ - sp_color_rgb_to_cmyk_floatv (cmyk_v, RGBA_R(rgba)/256.0, RGBA_G(rgba)/256.0, RGBA_B(rgba)/256.0); - sp_color_cmyk_to_rgb_floatv (rgb_v, render_cyan ? cmyk_v[0] : 0, - render_magenta ? cmyk_v[1] : 0, - render_yellow ? cmyk_v[2] : 0, - render_black ? cmyk_v[3] : 0); - rgba = (FLOAT_TO_UINT8(rgb_v[0])<<24) + (FLOAT_TO_UINT8(rgb_v[1])<<16) + (FLOAT_TO_UINT8(rgb_v[2])<<8) + 0xff; - } + if (print_colors_preview) + nr_arena_separate_color_plates(&rgba); nr_blit_pixblock_mask_rgba32(pb, &m, rgba); pb->empty = FALSE; @@ -967,14 +945,8 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock rgba = stroke_color->toRGBA32( shape->_stroke.opacity ); } - if (print_colors){ - sp_color_rgb_to_cmyk_floatv (cmyk_v, RGBA_R(rgba)/256.0, RGBA_G(rgba)/256.0, RGBA_B(rgba)/256.0); - sp_color_cmyk_to_rgb_floatv (rgb_v, render_cyan ? cmyk_v[0] : 0, - render_magenta ? cmyk_v[1] : 0, - render_yellow ? cmyk_v[2] : 0, - render_black ? cmyk_v[3] : 0); - rgba = (FLOAT_TO_UINT8(rgb_v[0])<<24) + (FLOAT_TO_UINT8(rgb_v[1])<<16) + (FLOAT_TO_UINT8(rgb_v[2])<<8) + 0xff; - } + if (print_colors_preview) + nr_arena_separate_color_plates(&rgba); nr_blit_pixblock_mask_rgba32(pb, &m, rgba); pb->empty = FALSE; diff --git a/src/display/nr-arena.cpp b/src/display/nr-arena.cpp index 74e0f409c..33870a118 100644 --- a/src/display/nr-arena.cpp +++ b/src/display/nr-arena.cpp @@ -18,6 +18,7 @@ #include "nr-filter-types.h" #include #include "preferences.h" +#include "color.h" static void nr_arena_class_init (NRArenaClass *klass); static void nr_arena_init (NRArena *arena); @@ -181,6 +182,29 @@ nr_arena_set_renderoffscreen (NRArena *arena) } +#define FLOAT_TO_UINT8(f) (int(f*255)) +#define RGBA_R(v) ((v) >> 24) +#define RGBA_G(v) (((v) >> 16) & 0xff) +#define RGBA_B(v) (((v) >> 8) & 0xff) +#define RGBA_A(v) ((v) & 0xff) + +void nr_arena_separate_color_plates(guint32* rgba){ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool render_cyan = prefs->getBool("/options/printcolorspreview/cyan", true); + bool render_magenta = prefs->getBool("/options/printcolorspreview/magenta", true); + bool render_yellow = prefs->getBool("/options/printcolorspreview/yellow", true); + bool render_black = prefs->getBool("/options/printcolorspreview/black", true); + + float rgb_v[3]; + float cmyk_v[4]; + sp_color_rgb_to_cmyk_floatv (cmyk_v, RGBA_R(*rgba)/256.0, RGBA_G(*rgba)/256.0, RGBA_B(*rgba)/256.0); + sp_color_cmyk_to_rgb_floatv (rgb_v, render_cyan ? cmyk_v[0] : 0, + render_magenta ? cmyk_v[1] : 0, + render_yellow ? cmyk_v[2] : 0, + render_black ? cmyk_v[3] : 0); + *rgba = (FLOAT_TO_UINT8(rgb_v[0])<<24) + (FLOAT_TO_UINT8(rgb_v[1])<<16) + (FLOAT_TO_UINT8(rgb_v[2])<<8) + 0xff; +} + /* Local Variables: mode:c++ diff --git a/src/display/nr-arena.h b/src/display/nr-arena.h index 1c091b7c7..d2f9dc246 100644 --- a/src/display/nr-arena.h +++ b/src/display/nr-arena.h @@ -64,4 +64,6 @@ void nr_arena_set_renderoffscreen (NRArena *arena); void nr_arena_render_paintserver_fill (NRPixBlock *pb, NRRectL *area, SPPainter *painter, float opacity, NRPixBlock *mask); +void nr_arena_separate_color_plates(guint32* rgba); + #endif -- cgit v1.2.3 From fe70193ca646803e41a3e03de0b53fa1240662d1 Mon Sep 17 00:00:00 2001 From: "Jon A. Cruz" Date: Sat, 12 Dec 2009 15:49:23 -0800 Subject: Updating ignored file list to get 'bzr status' more helpful (bzr r8884) --- src/libvpsc/pairingheap/.dirstamp | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/libvpsc/pairingheap/.dirstamp (limited to 'src') diff --git a/src/libvpsc/pairingheap/.dirstamp b/src/libvpsc/pairingheap/.dirstamp deleted file mode 100644 index e69de29bb..000000000 -- cgit v1.2.3 From 0daeeb2e989701416cbd6c33cf7e81c73f4944d3 Mon Sep 17 00:00:00 2001 From: "Jon A. Cruz" Date: Sat, 12 Dec 2009 16:01:30 -0800 Subject: reenable per-document palette to allow for continuing development. (bzr r8885) --- src/ui/dialog/swatches.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ui/dialog/swatches.cpp b/src/ui/dialog/swatches.cpp index 1f708e3de..450d4202d 100644 --- a/src/ui/dialog/swatches.cpp +++ b/src/ui/dialog/swatches.cpp @@ -47,7 +47,7 @@ #include "display/nr-plain-stuff.h" #include "sp-gradient-reference.h" -//#define USE_DOCUMENT_PALETTE 1 +#define USE_DOCUMENT_PALETTE 1 namespace Inkscape { namespace UI { -- cgit v1.2.3 From 896f3734b9c37bf43a76ca8155f45e68004f74e2 Mon Sep 17 00:00:00 2001 From: "Jon A. Cruz" Date: Sat, 12 Dec 2009 18:30:34 -0800 Subject: Reenable new input device dialog for further development (bzr r8886) --- src/menus-skeleton.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h index c664d683a..aeb6500bf 100644 --- a/src/menus-skeleton.h +++ b/src/menus-skeleton.h @@ -40,8 +40,7 @@ static char const menus_skeleton[] = " \n" " \n" " \n" -// TODO look at some dynamic option for changing the menu tree: -//" \n" +" \n" " \n" " \n" " \n" -- cgit v1.2.3 From da49b977d3a7e002c88780c24d9c4020cbe80b0e Mon Sep 17 00:00:00 2001 From: Diederik van Lierop Date: Sun, 13 Dec 2009 14:00:22 +0100 Subject: Position of baseline anchor is now dependent of the text alignment Fixed bugs: - https://launchpad.net/bugs/168329 (bzr r8887) --- src/libnrtype/Layout-TNG-OutIter.cpp | 29 +++++++++++++++++++++++++++-- src/libnrtype/Layout-TNG.h | 4 ++++ src/selcue.cpp | 26 ++++++++++++++------------ src/sp-text.cpp | 29 ++++++----------------------- 4 files changed, 51 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/libnrtype/Layout-TNG-OutIter.cpp b/src/libnrtype/Layout-TNG-OutIter.cpp index 0fc061bfc..f4e8e4031 100644 --- a/src/libnrtype/Layout-TNG-OutIter.cpp +++ b/src/libnrtype/Layout-TNG-OutIter.cpp @@ -221,6 +221,31 @@ Geom::Point Layout::characterAnchorPoint(iterator const &it) const } } +boost::optional Layout::baselineAnchorPoint() const +{ + iterator pos = this->begin(); + Geom::Point left_pt = this->characterAnchorPoint(pos); + pos.thisEndOfLine(); + Geom::Point right_pt = this->characterAnchorPoint(pos); + Geom::Point mid_pt = (left_pt + right_pt)/2; + + switch (this->paragraphAlignment(pos)) { + case LEFT: + case FULL: + return left_pt; + break; + case CENTER: + return mid_pt; + break; + case RIGHT: + return right_pt; + break; + default: + return boost::optional(); + break; + } +} + Geom::Point Layout::chunkAnchorPoint(iterator const &it) const { unsigned chunk_index; @@ -705,7 +730,7 @@ bool Layout::iterator::nextLineCursor(int n) unsigned line_index = _parent_layout->_characters[_char_index].chunk(_parent_layout).in_line; if (line_index == _parent_layout->_lines.size() - 1) return false; // nowhere to go - else + else n = MIN (n, static_cast(_parent_layout->_lines.size() - 1 - line_index)); if (_parent_layout->_lines[line_index + n].in_shape != _parent_layout->_lines[line_index].in_shape) { // switching between shapes: adjust the stored x to compensate @@ -728,7 +753,7 @@ bool Layout::iterator::prevLineCursor(int n) line_index = _parent_layout->_characters[_char_index].chunk(_parent_layout).in_line; if (line_index == 0) return false; // nowhere to go - else + else n = MIN (n, static_cast(line_index)); if (_parent_layout->_lines[line_index - n].in_shape != _parent_layout->_lines[line_index].in_shape) { // switching between shapes: adjust the stored x to compensate diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h index 19680b140..8cd26edef 100644 --- a/src/libnrtype/Layout-TNG.h +++ b/src/libnrtype/Layout-TNG.h @@ -480,6 +480,10 @@ public: /** For latin text, the left side of the character, on the baseline */ Geom::Point characterAnchorPoint(iterator const &it) const; + /** For left aligned text, the leftmost end of the baseline + For rightmost text, the rightmost... you probably got it by now ;-)*/ + boost::optional baselineAnchorPoint() const; + /** This is that value to apply to the x,y attributes of tspan role=line elements, and hence it takes alignment into account. */ Geom::Point chunkAnchorPoint(iterator const &it) const; diff --git a/src/selcue.cpp b/src/selcue.cpp index 714daaa7e..8756524dd 100644 --- a/src/selcue.cpp +++ b/src/selcue.cpp @@ -181,18 +181,20 @@ void Inkscape::SelCue::_newTextBaselines() if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) { // visualize baseline Inkscape::Text::Layout const *layout = te_get_layout(item); if (layout != NULL && layout->outputExists()) { - Geom::Point a = layout->characterAnchorPoint(layout->begin()) * sp_item_i2d_affine(item); - baseline_point = sp_canvas_item_new(sp_desktop_controls(_desktop), SP_TYPE_CTRL, - "mode", SP_CTRL_MODE_XOR, - "size", 4.0, - "filled", 0, - "stroked", 1, - "stroke_color", 0x000000ff, - NULL); - - sp_canvas_item_show(baseline_point); - SP_CTRL(baseline_point)->moveto(a); - sp_canvas_item_move_to_z(baseline_point, 0); + boost::optional pt = layout->baselineAnchorPoint(); + if (pt) { + baseline_point = sp_canvas_item_new(sp_desktop_controls(_desktop), SP_TYPE_CTRL, + "mode", SP_CTRL_MODE_XOR, + "size", 4.0, + "filled", 0, + "stroked", 1, + "stroke_color", 0x000000ff, + NULL); + + sp_canvas_item_show(baseline_point); + SP_CTRL(baseline_point)->moveto((*pt) * sp_item_i2d_affine(item)); + sp_canvas_item_move_to_z(baseline_point, 0); + } } } diff --git a/src/sp-text.cpp b/src/sp-text.cpp index 87c67c646..423922b80 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -431,30 +431,13 @@ sp_text_description(SPItem *item) static void sp_text_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const */*snapprefs*/) { // Choose a point on the baseline for snapping from or to, with the horizontal position - // of this point depending on the text alignment (left vs. right) + // of this point depending on the text alignment (left vs. right) Inkscape::Text::Layout const *layout = te_get_layout((SPItem *) item); - if(layout != NULL) { - int type = target ? int(Inkscape::SNAPTARGET_TEXT_BASELINE) : int(Inkscape::SNAPSOURCE_TEXT_BASELINE); - - Inkscape::Text::Layout::iterator pos = layout->begin(); - Inkscape::Text::Layout::Alignment text_alignment = layout->paragraphAlignment(pos); - - Geom::Point left_pt = layout->characterAnchorPoint(pos) * sp_item_i2d_affine(item); - pos.thisEndOfLine(); - Geom::Point right_pt = layout->characterAnchorPoint(pos) * sp_item_i2d_affine(item); - Geom::Point mid_pt = (left_pt + right_pt)/2; - - switch (text_alignment) { - case Inkscape::Text::Layout::LEFT: - case Inkscape::Text::Layout::FULL: - p.push_back(std::make_pair(left_pt, type)); - break; - case Inkscape::Text::Layout::CENTER: - p.push_back(std::make_pair(mid_pt, type)); - break; - default: //RIGHT - p.push_back(std::make_pair(right_pt, type)); - break; + if (layout != NULL && layout->outputExists()) { + boost::optional pt = layout->baselineAnchorPoint(); + if (pt) { + int type = target ? int(Inkscape::SNAPTARGET_TEXT_BASELINE) : int(Inkscape::SNAPSOURCE_TEXT_BASELINE); + p.push_back(std::make_pair((*pt) * sp_item_i2d_affine(item), type)); } } } -- cgit v1.2.3 From eb6da8747669aac87ca34baffc9cec98bfd43cdc Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Sun, 13 Dec 2009 13:32:09 -0200 Subject: "too much ink" icon is only enabled when the current ICC profile colorspace is cmyk (bzr r8888) --- src/widgets/sp-color-notebook.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp index 1870c2960..56b60d120 100644 --- a/src/widgets/sp-color-notebook.cpp +++ b/src/widgets/sp-color-notebook.cpp @@ -373,6 +373,10 @@ void ColorNotebook::init() sp_set_font_size_smaller (rgbabox); gtk_widget_show_all (rgbabox); + + //the "too much ink" icon is initially hidden + gtk_widget_hide(GTK_WIDGET(_box_toomuchink)); + gtk_table_attach (GTK_TABLE (table), rgbabox, 0, 2, row, row + 1, GTK_FILL, GTK_SHRINK, XPAD, YPAD); #ifdef SPCS_PREVIEW @@ -532,16 +536,22 @@ void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha ) /* update too-much-ink icon */ gtk_widget_set_sensitive (_box_toomuchink, false); if (color.icc){ - double ink_sum = 0; - for (unsigned int i=0; icolors.size(); i++){ - ink_sum += color.icc->colors[i]; - } + Inkscape::ColorProfile* prof = SP_ACTIVE_DOCUMENT->profileManager->find(color.icc->colorProfile.c_str()); + if (prof->getColorSpace() == icSigCmykData || prof->getColorSpace() == icSigCmyData){ + gtk_widget_show(GTK_WIDGET(_box_toomuchink)); + double ink_sum = 0; + for (unsigned int i=0; icolors.size(); i++){ + ink_sum += color.icc->colors[i]; + } - /* Some literature states that when the sum of paint values exceed 320%, it is considered to be a satured color, - which means the paper can get too wet due to an excessive ammount of ink. This may lead to several issues - such as misalignment and poor quality of printing in general.*/ - if ( ink_sum > 3.2 ) - gtk_widget_set_sensitive (_box_toomuchink, true); + /* Some literature states that when the sum of paint values exceed 320%, it is considered to be a satured color, + which means the paper can get too wet due to an excessive ammount of ink. This may lead to several issues + such as misalignment and poor quality of printing in general.*/ + if ( ink_sum > 3.2 ) + gtk_widget_set_sensitive (_box_toomuchink, true); + } else { + gtk_widget_hide(GTK_WIDGET(_box_toomuchink)); + } } #endif //ENABLE_LCMS -- cgit v1.2.3 From 79250d25cc035d894158eaf7ea15cf31df62025c Mon Sep 17 00:00:00 2001 From: Felipe Corr??a da Silva Sanches Date: Sun, 13 Dec 2009 21:55:34 -0200 Subject: updating my email address in file headers (bzr r8889) --- src/display/nr-filter-colormatrix.cpp | 2 +- src/display/nr-filter-colormatrix.h | 2 +- src/display/nr-filter-component-transfer.cpp | 2 +- src/display/nr-filter-component-transfer.h | 2 +- src/display/nr-filter-convolve-matrix.cpp | 2 +- src/display/nr-filter-convolve-matrix.h | 2 +- src/display/nr-filter-displacement-map.cpp | 2 +- src/display/nr-filter-displacement-map.h | 2 +- src/display/nr-filter-flood.cpp | 2 +- src/display/nr-filter-flood.h | 2 +- src/display/nr-filter-image.cpp | 2 +- src/display/nr-filter-morphology.cpp | 2 +- src/display/nr-filter-morphology.h | 2 +- src/display/nr-filter-tile.cpp | 2 +- src/display/nr-filter-tile.h | 2 +- src/display/nr-filter-turbulence.cpp | 2 +- src/display/nr-filter-turbulence.h | 2 +- src/display/nr-svgfonts.cpp | 2 +- src/display/nr-svgfonts.h | 2 +- src/filters/colormatrix.cpp | 2 +- src/filters/componenttransfer-funcnode.cpp | 2 +- src/filters/componenttransfer-funcnode.h | 2 +- src/filters/convolvematrix.cpp | 2 +- src/filters/convolvematrix.h | 2 +- src/filters/image.cpp | 2 +- src/filters/image.h | 2 +- src/filters/morphology.cpp | 2 +- src/filters/turbulence.cpp | 2 +- src/filters/turbulence.h | 2 +- src/helper-fns.h | 2 +- src/sp-font-face.cpp | 2 +- src/sp-font-face.h | 2 +- src/sp-font.cpp | 2 +- src/sp-font.h | 2 +- src/sp-glyph-kerning.cpp | 2 +- src/sp-glyph-kerning.h | 2 +- src/sp-glyph.cpp | 2 +- src/sp-glyph.h | 2 +- src/sp-missing-glyph.cpp | 2 +- src/sp-missing-glyph.h | 2 +- src/sp-script.cpp | 2 +- src/sp-script.h | 2 +- src/ui/dialog/filter-effects-dialog.cpp | 2 +- src/ui/dialog/svg-fonts-dialog.cpp | 2 +- src/ui/dialog/svg-fonts-dialog.h | 2 +- src/ui/widget/spin-slider.cpp | 2 +- 46 files changed, 46 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/display/nr-filter-colormatrix.cpp b/src/display/nr-filter-colormatrix.cpp index 66fb196cb..0b24649a9 100644 --- a/src/display/nr-filter-colormatrix.cpp +++ b/src/display/nr-filter-colormatrix.cpp @@ -2,7 +2,7 @@ * feColorMatrix filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * Jasper van de Gronde * * Copyright (C) 2007 authors diff --git a/src/display/nr-filter-colormatrix.h b/src/display/nr-filter-colormatrix.h index 1c331a5b0..47b454c53 100644 --- a/src/display/nr-filter-colormatrix.h +++ b/src/display/nr-filter-colormatrix.h @@ -5,7 +5,7 @@ * feColorMatrix filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * Copyright (C) 2007 authors * diff --git a/src/display/nr-filter-component-transfer.cpp b/src/display/nr-filter-component-transfer.cpp index 87f87c95a..ab9990360 100644 --- a/src/display/nr-filter-component-transfer.cpp +++ b/src/display/nr-filter-component-transfer.cpp @@ -2,7 +2,7 @@ * feComponentTransfer filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * Jasper van de Gronde * * Copyright (C) 2007 authors diff --git a/src/display/nr-filter-component-transfer.h b/src/display/nr-filter-component-transfer.h index 3d8be272e..eb76bd543 100644 --- a/src/display/nr-filter-component-transfer.h +++ b/src/display/nr-filter-component-transfer.h @@ -5,7 +5,7 @@ * feComponentTransfer filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * Copyright (C) 2007 authors * diff --git a/src/display/nr-filter-convolve-matrix.cpp b/src/display/nr-filter-convolve-matrix.cpp index e9f7e7dfe..05d38701f 100644 --- a/src/display/nr-filter-convolve-matrix.cpp +++ b/src/display/nr-filter-convolve-matrix.cpp @@ -2,7 +2,7 @@ * feConvolveMatrix filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * Jasper van de Gronde * * Copyright (C) 2007,2009 authors diff --git a/src/display/nr-filter-convolve-matrix.h b/src/display/nr-filter-convolve-matrix.h index d7a04a766..e7416f9cc 100644 --- a/src/display/nr-filter-convolve-matrix.h +++ b/src/display/nr-filter-convolve-matrix.h @@ -5,7 +5,7 @@ * feConvolveMatrix filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * Copyright (C) 2007 authors * diff --git a/src/display/nr-filter-displacement-map.cpp b/src/display/nr-filter-displacement-map.cpp index 869a184ac..4de5e658c 100644 --- a/src/display/nr-filter-displacement-map.cpp +++ b/src/display/nr-filter-displacement-map.cpp @@ -2,7 +2,7 @@ * feDisplacementMap filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * Copyright (C) 2007 authors * diff --git a/src/display/nr-filter-displacement-map.h b/src/display/nr-filter-displacement-map.h index 180030c85..bb15b77a3 100644 --- a/src/display/nr-filter-displacement-map.h +++ b/src/display/nr-filter-displacement-map.h @@ -5,7 +5,7 @@ * feDisplacementMap filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * Copyright (C) 2007 authors * diff --git a/src/display/nr-filter-flood.cpp b/src/display/nr-filter-flood.cpp index 06025392b..1d804f969 100644 --- a/src/display/nr-filter-flood.cpp +++ b/src/display/nr-filter-flood.cpp @@ -2,7 +2,7 @@ * feFlood filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * Copyright (C) 2007 authors * diff --git a/src/display/nr-filter-flood.h b/src/display/nr-filter-flood.h index d38c0d4f5..98c374bbd 100644 --- a/src/display/nr-filter-flood.h +++ b/src/display/nr-filter-flood.h @@ -5,7 +5,7 @@ * feFlood filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * Copyright (C) 2007 authors * diff --git a/src/display/nr-filter-image.cpp b/src/display/nr-filter-image.cpp index 2b799f8d2..4ad6982f3 100644 --- a/src/display/nr-filter-image.cpp +++ b/src/display/nr-filter-image.cpp @@ -2,7 +2,7 @@ * feImage filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * Tavmjong Bah * * Copyright (C) 2007 authors diff --git a/src/display/nr-filter-morphology.cpp b/src/display/nr-filter-morphology.cpp index 2df3ff807..258298751 100644 --- a/src/display/nr-filter-morphology.cpp +++ b/src/display/nr-filter-morphology.cpp @@ -2,7 +2,7 @@ * feMorphology filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * Copyright (C) 2007 authors * diff --git a/src/display/nr-filter-morphology.h b/src/display/nr-filter-morphology.h index 1d3e16be3..16ccad5e6 100644 --- a/src/display/nr-filter-morphology.h +++ b/src/display/nr-filter-morphology.h @@ -5,7 +5,7 @@ * feMorphology filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * Copyright (C) 2007 authors * diff --git a/src/display/nr-filter-tile.cpp b/src/display/nr-filter-tile.cpp index 53399eba2..898db9f53 100644 --- a/src/display/nr-filter-tile.cpp +++ b/src/display/nr-filter-tile.cpp @@ -2,7 +2,7 @@ * feTile filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * Copyright (C) 2007 authors * diff --git a/src/display/nr-filter-tile.h b/src/display/nr-filter-tile.h index ea826dfd7..5a6a5a78c 100644 --- a/src/display/nr-filter-tile.h +++ b/src/display/nr-filter-tile.h @@ -5,7 +5,7 @@ * feTile filter primitive renderer * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * Copyright (C) 2007 authors * diff --git a/src/display/nr-filter-turbulence.cpp b/src/display/nr-filter-turbulence.cpp index a91db3d56..8d22b180d 100644 --- a/src/display/nr-filter-turbulence.cpp +++ b/src/display/nr-filter-turbulence.cpp @@ -3,7 +3,7 @@ * * Authors: * World Wide Web Consortium - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * This file has a considerable amount of code adapted from * the W3C SVG filter specs, available at: diff --git a/src/display/nr-filter-turbulence.h b/src/display/nr-filter-turbulence.h index b12e6395a..b841cc37f 100644 --- a/src/display/nr-filter-turbulence.h +++ b/src/display/nr-filter-turbulence.h @@ -6,7 +6,7 @@ * * Authors: * World Wide Web Consortium - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * Niko Kiirala * * This file has a considerable amount of code adapted from diff --git a/src/display/nr-svgfonts.cpp b/src/display/nr-svgfonts.cpp index 2d71504d2..7a0db664a 100644 --- a/src/display/nr-svgfonts.cpp +++ b/src/display/nr-svgfonts.cpp @@ -4,7 +4,7 @@ * SVGFonts rendering implementation * * Authors: - * Felipe C. da S. Sanches + * Felipe C. da S. Sanches * * Copyright (C) 2008 Felipe C. da S. Sanches * diff --git a/src/display/nr-svgfonts.h b/src/display/nr-svgfonts.h index ebf5ad08b..ddf4ba327 100644 --- a/src/display/nr-svgfonts.h +++ b/src/display/nr-svgfonts.h @@ -5,7 +5,7 @@ * SVGFonts rendering headear * * Authors: - * Felipe C. da S. Sanches + * Felipe C. da S. Sanches * * Copyright (C) 2008 Felipe C. da S. Sanches * diff --git a/src/filters/colormatrix.cpp b/src/filters/colormatrix.cpp index 55cfcbeb7..3f60ea05c 100644 --- a/src/filters/colormatrix.cpp +++ b/src/filters/colormatrix.cpp @@ -6,7 +6,7 @@ */ /* * Authors: - * Felipe Sanches + * Felipe Sanches * hugo Rodrigues * * Copyright (C) 2007 Felipe C. da S. Sanches diff --git a/src/filters/componenttransfer-funcnode.cpp b/src/filters/componenttransfer-funcnode.cpp index e66f85e70..8edb9cf2d 100644 --- a/src/filters/componenttransfer-funcnode.cpp +++ b/src/filters/componenttransfer-funcnode.cpp @@ -7,7 +7,7 @@ * Authors: * Hugo Rodrigues * Niko Kiirala - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * Copyright (C) 2006, 2007, 2008 Authors * diff --git a/src/filters/componenttransfer-funcnode.h b/src/filters/componenttransfer-funcnode.h index 4db6ab785..52873f6d3 100644 --- a/src/filters/componenttransfer-funcnode.h +++ b/src/filters/componenttransfer-funcnode.h @@ -8,7 +8,7 @@ * Authors: * Hugo Rodrigues * Niko Kiirala - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * Copyright (C) 2006,2007 Authors * diff --git a/src/filters/convolvematrix.cpp b/src/filters/convolvematrix.cpp index 3e1c36f0a..6440f340a 100644 --- a/src/filters/convolvematrix.cpp +++ b/src/filters/convolvematrix.cpp @@ -6,7 +6,7 @@ */ /* * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * hugo Rodrigues * * Copyright (C) 2006 Hugo Rodrigues diff --git a/src/filters/convolvematrix.h b/src/filters/convolvematrix.h index beb5fad75..1e8545040 100644 --- a/src/filters/convolvematrix.h +++ b/src/filters/convolvematrix.h @@ -6,7 +6,7 @@ */ /* * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * Hugo Rodrigues * * Copyright (C) 2006 Hugo Rodrigues diff --git a/src/filters/image.cpp b/src/filters/image.cpp index d8e5a417f..ad516a84e 100644 --- a/src/filters/image.cpp +++ b/src/filters/image.cpp @@ -6,7 +6,7 @@ */ /* * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * hugo Rodrigues * * Copyright (C) 2007 Felipe Sanches diff --git a/src/filters/image.h b/src/filters/image.h index 7207918e1..78e719ac7 100644 --- a/src/filters/image.h +++ b/src/filters/image.h @@ -6,7 +6,7 @@ */ /* * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * Hugo Rodrigues * * Copyright (C) 2006 Hugo Rodrigues diff --git a/src/filters/morphology.cpp b/src/filters/morphology.cpp index 9a34bbccb..1530dae8c 100644 --- a/src/filters/morphology.cpp +++ b/src/filters/morphology.cpp @@ -6,7 +6,7 @@ */ /* * Authors: - * Felipe Sanches + * Felipe Sanches * Hugo Rodrigues * * Copyright (C) 2006 Hugo Rodrigues diff --git a/src/filters/turbulence.cpp b/src/filters/turbulence.cpp index f3c143056..eed056ecc 100644 --- a/src/filters/turbulence.cpp +++ b/src/filters/turbulence.cpp @@ -6,7 +6,7 @@ */ /* * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * hugo Rodrigues * * Copyright (C) 2007 Felipe Sanches diff --git a/src/filters/turbulence.h b/src/filters/turbulence.h index 5edf678a7..792a6181a 100644 --- a/src/filters/turbulence.h +++ b/src/filters/turbulence.h @@ -6,7 +6,7 @@ */ /* * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * Hugo Rodrigues * * Copyright (C) 2006 Hugo Rodrigues diff --git a/src/helper-fns.h b/src/helper-fns.h index d2a6c9b22..2c2db92c4 100644 --- a/src/helper-fns.h +++ b/src/helper-fns.h @@ -5,7 +5,7 @@ * Some helper functions * * Authors: - * Felipe Corrêa da Silva Sanches + * Felipe Corrêa da Silva Sanches * * * Copyright (C) 2006 Hugo Rodrigues diff --git a/src/sp-font-face.cpp b/src/sp-font-face.cpp index 1ec6f4601..704985b8c 100644 --- a/src/sp-font-face.cpp +++ b/src/sp-font-face.cpp @@ -13,7 +13,7 @@ * http://www.w3.org/TR/SVG/fonts.html#FontFaceElement * * Author: - * Felipe C. da S. Sanches + * Felipe C. da S. Sanches * * Copyright (C) 2008, Felipe C. da S. Sanches * diff --git a/src/sp-font-face.h b/src/sp-font-face.h index 8fe1c752f..e492ba091 100644 --- a/src/sp-font-face.h +++ b/src/sp-font-face.h @@ -14,7 +14,7 @@ * http://www.w3.org/TR/SVG/fonts.html#FontFaceElement * * Authors: - * Felipe C. da S. Sanches + * Felipe C. da S. Sanches * * Copyright (C) 2008 Felipe C. da S. Sanches * diff --git a/src/sp-font.cpp b/src/sp-font.cpp index 75fb18638..de272c72f 100644 --- a/src/sp-font.cpp +++ b/src/sp-font.cpp @@ -8,7 +8,7 @@ * SVG element implementation * * Author: - * Felipe C. da S. Sanches + * Felipe C. da S. Sanches * * Copyright (C) 2008, Felipe C. da S. Sanches * diff --git a/src/sp-font.h b/src/sp-font.h index fad7ead42..a0f895a52 100644 --- a/src/sp-font.h +++ b/src/sp-font.h @@ -9,7 +9,7 @@ * SVG element implementation * * Authors: - * Felipe C. da S. Sanches + * Felipe C. da S. Sanches * * Copyright (C) 2008 Felipe C. da S. Sanches * diff --git a/src/sp-glyph-kerning.cpp b/src/sp-glyph-kerning.cpp index 6d08f212c..872efc853 100644 --- a/src/sp-glyph-kerning.cpp +++ b/src/sp-glyph-kerning.cpp @@ -10,7 +10,7 @@ * W3C SVG 1.1 spec, page 476, section 20.7 * * Author: - * Felipe C. da S. Sanches + * Felipe C. da S. Sanches * * Copyright (C) 2008, Felipe C. da S. Sanches * diff --git a/src/sp-glyph-kerning.h b/src/sp-glyph-kerning.h index ec0866c2c..ce9b4bb15 100644 --- a/src/sp-glyph-kerning.h +++ b/src/sp-glyph-kerning.h @@ -10,7 +10,7 @@ * SVG and elements implementation * * Authors: - * Felipe C. da S. Sanches + * Felipe C. da S. Sanches * * Copyright (C) 2008 Felipe C. da S. Sanches * diff --git a/src/sp-glyph.cpp b/src/sp-glyph.cpp index 8af78a0aa..37e266da0 100644 --- a/src/sp-glyph.cpp +++ b/src/sp-glyph.cpp @@ -9,7 +9,7 @@ * SVG element implementation * * Author: - * Felipe C. da S. Sanches + * Felipe C. da S. Sanches * * Copyright (C) 2008, Felipe C. da S. Sanches * diff --git a/src/sp-glyph.h b/src/sp-glyph.h index 8c35a3a83..316204c23 100644 --- a/src/sp-glyph.h +++ b/src/sp-glyph.h @@ -10,7 +10,7 @@ * SVG element implementation * * Authors: - * Felipe C. da S. Sanches + * Felipe C. da S. Sanches * * Copyright (C) 2008 Felipe C. da S. Sanches * diff --git a/src/sp-missing-glyph.cpp b/src/sp-missing-glyph.cpp index ffc29a71e..d25a5f812 100644 --- a/src/sp-missing-glyph.cpp +++ b/src/sp-missing-glyph.cpp @@ -9,7 +9,7 @@ * SVG element implementation * * Author: - * Felipe C. da S. Sanches + * Felipe C. da S. Sanches * * Copyright (C) 2008, Felipe C. da S. Sanches * diff --git a/src/sp-missing-glyph.h b/src/sp-missing-glyph.h index 2a4cfde07..0b3f74360 100644 --- a/src/sp-missing-glyph.h +++ b/src/sp-missing-glyph.h @@ -10,7 +10,7 @@ * SVG element implementation * * Authors: - * Felipe C. da S. Sanches + * Felipe C. da S. Sanches * * Copyright (C) 2008 Felipe C. da S. Sanches * diff --git a/src/sp-script.cpp b/src/sp-script.cpp index a40132450..ad41b8021 100644 --- a/src/sp-script.cpp +++ b/src/sp-script.cpp @@ -4,7 +4,7 @@ * SVG