Boost::Geometry: how to join intersecting polygons in a multi_polygon? - c++

So I want to join all interrelated poligons in a multi_polygon. How to do such thing?
We have such image (of one green multi_polygon) which we want to optimize (we can see yellow doted lines - result of simplification that apparently was performed on each poligon of multi_polygon not on multi_polygon in general):
And here is compilable code to generate such image:
#include <iostream>
#include <fstream>
#include <boost/assign.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/multi/geometries/multi_polygon.hpp>
#include <boost/geometry/extensions/io/svg/svg_mapper.hpp>
template <typename Geometry1, typename Geometry2>
void create_svg(std::string const& filename, Geometry1 const& a, Geometry2 const& b)
{
typedef typename boost::geometry::point_type<Geometry1>::type point_type;
std::ofstream svg(filename.c_str());
boost::geometry::svg_mapper<point_type> mapper(svg, 400, 400);
mapper.add(a);
mapper.add(b);
mapper.map(a, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
mapper.map(b, "opacity:0.8;fill:none;stroke:rgb(255,128,0);stroke-width:4;stroke-dasharray:1,7;stroke-linecap:round");
}
boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > make_point(int x, int y)
{
boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > return_item;
boost::geometry::model::d2::point_xy<double> p1(x, y);
boost::geometry::model::d2::point_xy<double> p2(x-1, y);
boost::geometry::model::d2::point_xy<double> p3(x-1, y-1);
boost::geometry::model::d2::point_xy<double> p4(x, y-1);
boost::geometry::append( return_item, p1);
boost::geometry::append( return_item, p2);
boost::geometry::append( return_item, p3);
boost::geometry::append( return_item, p4);
return return_item;
}
int main()
{
// create a container for joined points structure
boost::geometry::model::multi_polygon< boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > > output, simpl;
// join points one by one (because one day we would have many=))
output.push_back(make_point(1,1));
boost::geometry::correct(output);
output.push_back(make_point(2,1));
boost::geometry::correct(output);
output.push_back(make_point(3,1));
boost::geometry::correct(output);
output.push_back(make_point(4,1));
boost::geometry::correct(output);
output.push_back(make_point(5,1));
boost::geometry::correct(output);
output.push_back(make_point(2,2));
boost::geometry::correct(output);
output.push_back(make_point(3,2));
boost::geometry::correct(output);
output.push_back(make_point(5,2));
boost::geometry::correct(output);
output.push_back(make_point(5,5));
boost::geometry::correct(output);
// simplify joined structure
boost::geometry::simplify(output, simpl, 0.5);
// create an svg image
create_svg("make_envelope.svg", output, simpl );
}
requires at least boost 1.47.0 and 3 files from boost/geometry/extensions/io/svg/
what I need is simple: how to group interrelated poligons? In this case we shall get 2 poligons in our multy_poligon like shown here - red and green:
Update:
So I found this info on dissolve and created sample code that uses rings for cels creation:
#include <iostream>
#include <fstream>
#include <boost/assign.hpp>
//Boost
#include <boost/algorithm/string.hpp>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/multi/geometries/multi_polygon.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>
BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)
#include <boost/foreach.hpp>
//Boost Geometry extensions (from trunk)
#include <boost/geometry/extensions/io/svg/svg_mapper.hpp>
template <typename Geometry1, typename Geometry2>
void create_svg(std::string const& filename, Geometry1 const& a, Geometry2 const& b)
{
typedef typename boost::geometry::point_type<Geometry1>::type point_type;
std::ofstream svg(filename.c_str());
boost::geometry::svg_mapper<point_type> mapper(svg, 400, 400);
mapper.add(a);
mapper.add(b);
mapper.map(a, "fill-rule:nonzero;fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2;");
mapper.map(b, "opacity:0.8;fill:none;stroke:rgb(255,128,0);stroke-width:4;stroke-dasharray:1,7;stroke-linecap:round");
}
void make_point(int x, int y, boost::geometry::model::ring<boost::geometry::model::d2::point_xy<double> > & ring)
{
using namespace boost::assign;
ring +=
boost::geometry::model::d2::point_xy<double>(x-1, y-1),
boost::geometry::model::d2::point_xy<double>(x, y-1),
boost::geometry::model::d2::point_xy<double>(x, y),
boost::geometry::model::d2::point_xy<double>(x-1, y),
boost::geometry::model::d2::point_xy<double>(x-1, y-1);
}
int main()
{
using namespace boost::assign;
boost::geometry::model::ring<boost::geometry::model::d2::point_xy<double> > ring0, ring1,ring;
boost::geometry::model::multi_polygon< boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > > outputw;
make_point(1, 1, ring) ;
make_point(2, 1, ring) ;
make_point(3, 1, ring) ;
make_point(4, 1, ring) ;
make_point(5, 1, ring) ;
make_point(2, 2, ring) ;
make_point(3, 2, ring) ;
make_point(5, 2, ring) ;
boost::geometry::model::ring<boost::geometry::model::d2::point_xy<double> > output;
boost::geometry::simplify(ring, output, 1);
// create an svg image
create_svg("make_envelope.svg", ring, output );
}
It returns such image of ring:
If we could use dissolve to turn it into poligon that would really solve some of my problems. But looks like currently we can not due to this compiler errors problem described here

What about using Qt. If you use a QPolygonF you can call unite which does exactly what you need. After unison you can extract the points and put them back into your boost container.
If qt is not an option take a look at the algorithms proposed here http://www.wykobi.com

You can use this function from boost library:
void union_(Geometry1 const & geometry1, Geometry2 const & geometry2, Collection & output_collection)
It takes two geometries and unites them if they have intersections or puts them into one collection (vector, deque, etc.) if not.

Related

boost rtree of box gives wrong intersection with segment

Boost rtree gives wrong intersection result for some intersection with segment queries.
In this case the bounding box is a y-planar 10x10 square at y=0. I'm querying with a z-aligned line from (2, 1, 0) to (2, 1, 10). What's interesting is that if I use a box for query instead of a segment then it works as expected. This behavior is also present when the box is not planar, just move the min corner to (0, -5, 0) and it still happens.
Am I using this wrong or is it a bug in boost?
Edit: have tried this on Boost 1.56 and 1.59.
#include <vector>
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/geometries/segment.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <vector>
#include <iterator>
#include <memory>
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
typedef bg::model::point<double, 3, bg::cs::cartesian> point_def;
typedef bg::model::box<point_def> box;
typedef bg::model::segment<point_def> segment;
typedef std::pair<box, size_t> tri_box;
typedef bgi::rtree< tri_box, bgi::linear<8>> tree_type;
using namespace std;
TEST(boost_rtree, cant_intersect_box_with_segment) {
vector<tri_box> buff(1);
buff[0].first = box{point_def{0, 0, 0}, point_def{10, 0, 10}};
buff[0].second = 1;
tree_type tree(buff);
segment query{point_def{2, 1, 0}, point_def{2, 1, 10}};
// box query{point_def{2, 1, 0}, point_def{2, 1, 10}};
vector<tri_box> out;
size_t count = tree.query(bgi::intersects(query), back_inserter(out));
ASSERT_EQ(0, count); // fails here
ASSERT_EQ(0, out.size());
}
Edit: issue is being moved to boost mailing list: lists.boost.org/geometry/2015/09/3472.php
As unlikely as it seems, this appears to me to be a bug.
The first version that even compiles this is Boost 1.56. All previous versions fail with
BOOST_MPL_ASSERT_MSG
(
false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE
, (types<Geometry>)
);
But, even though the code is compiled, it does not seem to be correct...: the intersects call that underlies the query predicate itself returns "false positive" it seems.
Much simplified: Live On Coliru
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/geometries/segment.hpp>
namespace bg = boost::geometry;
typedef bg::model::point<int, 3, bg::cs::cartesian> point;
typedef bg::model::box<point> box;
typedef bg::model::segment<point> segment;
int main() {
box y0rect = box{point{0, 0, 0}, point{10, 0, 10}};
segment seg{point{2, 1, 0}, point{2, 1, 10}};
bg::correct(y0rect);
bg::correct(seg);
assert(!bg::intersects(seg, y0rect));
}
UPDATE
Interestingly, it seems to work correctly sometimes for 2d. I'm not sure the outcome isn't simply undefined...
Live On Coliru
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/geometries/segment.hpp>
namespace bg = boost::geometry;
typedef bg::model::point<int, 4, bg::cs::cartesian> point;
typedef bg::model::box<point> box;
typedef bg::model::segment<point> segment;
int main() {
box y0rect = box{point{0, 0}, point{10, 10}};
bg::correct(y0rect);
{
segment seg{point{12, 0}, point{20, 10}};
bg::correct(seg);
assert(!bg::intersects(seg, y0rect));
}
{
segment seg{point{2, 0}, point{8, 6}};
bg::correct(seg);
assert(bg::intersects(seg, y0rect));
}
{
segment seg{point{2, 0}, point{18, 6}};
bg::correct(seg);
assert(bg::intersects(seg, y0rect)); // OOPS BREAKS?
}
}

boost::geometry Most efficient way of measuring max/min distance of a point to a polygon ring

I have been using boost::geometry library in a program, mostly for handling polygon objects.
I am now trying to optimize my code to scale better with larger polygons. One my functions checks for a given polygon and a given point (usually inside the polygon) the minimum and maximum distance between the point and polygon outer ring.
I do it by looping on the polygon edges:
polygon pol;
point myPoint;
double min = 9999999, max = 0;
for(auto it1 = boost::begin(bg::exterior_ring(pol)); it1 != boost::end(bg::exterior_ring(pol)); ++it1){
double distance = bg::distance(*it1, myPoint);
if(max < distance)
max = distance;
if(min > distance)
min = distance;
}
I am hoping that there are algorithms faster than this one, linear in the polygon number of edges. Is there such a thing already inside the boost::geometry library?
I'd suggest you can use the builtin strategies for finding the minimum distance between the polygon and the point:
Live On Coliru
#include <boost/geometry.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/io/io.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/algorithms/distance.hpp>
namespace bg = boost::geometry;
using point = bg::model::d2::point_xy<double>;
using polygon = bg::model::polygon<point>;
int main() {
polygon pol;
boost::geometry::read_wkt(
"POLYGON((2 1.3,2.4 1.7,2.8 1.8,3.4 1.2,3.7 1.6,3.4 2,4.1 3,5.3 2.6,5.4 1.2,4.9 0.8,2.9 0.7,2 1.3)"
"(4.0 2.0, 4.2 1.4, 4.8 1.9, 4.4 2.2, 4.0 2.0))", pol);
point myPoint(7, 7);
double min = 9999999, max = 0;
std::cout << "Minimal distance: " << bg::distance(pol, myPoint);
}
Prints
Minimal distance: 4.71699
Further hints:
You should consider ranking the distances first using comparable_distance. As you can see the sample there suggests looping over all the sampled distances... so I don't think the library has a better offering at this time.
More sophisticated algorithms are planned, of which a number may be related to this problem:
http://boost-geometry.203548.n3.nabble.com/distance-between-geometries-td4025549.html
mailing list thread http://lists.boost.org/geometry/2013/08/2446.php
another here http://lists.boost.org/geometry/2013/09/2494.php
Note also that Boost Geometry Index has a related predicate comparable_distance_far but it's not exposed as of yet.
Summary
You can improve at least a bit by using comparable_distance here for now.
Features have been planned and it looks like there is a good chance that requesting them on the mailing list/Boost Trac will help getting them there.
For best performances you should use an RTree with boost::geometry::index. Creating the RTree has a cost, but then computing the ditance of a point to any of the (multi)polygon ring will be much faster. Example code:
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <iostream>
#include <vector>
int main()
{
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
typedef bg::model::point<double, 2, bg::cs::cartesian> point;
typedef bg::model::polygon<point> polygon;
point p{ 0, 0 };
// create some polygon and fill it with data
polygon poly;
double a = 0;
double as = bg::math::two_pi<double>() / 100;
for (int i = 0; i < 100; ++i, a += as)
{
double c = cos(a);
double s = sin(a);
poly.outer().push_back(point{10 * c, 10 * s});
poly.inners().resize(1);
poly.inners()[0].push_back(point{5 * c, 5 * s});
}
// make sure it is valid
bg::correct(poly);
// create rtree containing objects of type bg::model::pointing_segment
typedef bg::segment_iterator<polygon const> segment_iterator;
typedef std::iterator_traits<segment_iterator>::value_type segment_type;
bgi::rtree<segment_type, bgi::rstar<4> > rtree(bg::segments_begin(poly),
bg::segments_end(poly));
// get 1 nearest segment
std::vector<segment_type> result;
rtree.query(bgi::nearest(p, 1), std::back_inserter(result));
BOOST_ASSERT(!result.empty());
std::cout << bg::wkt(result[0]) << ", " << bg::distance(p, result[0]) << std::endl;
return 0;
}

How to calculate distance from point o filled/unfilled rectangle

I am visualising different sorts of geometries using Qt 5.
There I have a QRect that is either visualised as filled or not.
Now I want to calculate the distance of a QPoint to that rectangle using boost::geometry.
A point within the rectangle should have a distance of 0 when filled, and the distance to the next line when not filled.
Since the documentation of Box does not mention that it is a shape I thought I could use it for this case and adapted the Box concept to QRect.
The following example does not work though, since a Box is treated as shape and therefore always "filled".
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <QtCore/QPoint>
#include <QtCore/QRect>
BOOST_GEOMETRY_REGISTER_POINT_2D_GET_SET(QPoint, int, boost::geometry::cs::cartesian, x, y, setX, setY);
namespace boost { namespace geometry {
namespace traits
{
template <> struct tag<QRect> { typedef box_tag type; };
template <> struct point_type<QRect> { typedef QPoint type; };
template <std::size_t Index, std::size_t Dimension>
struct indexed_access<QRect, Index, Dimension>
{
typedef typename geometry::coordinate_type<QRect>::type coordinate_type;
static inline coordinate_type get(const QRect &r)
{
if (Index == boost::geometry::min_corner)
return geometry::get<Dimension>(r.topLeft());
else
return geometry::get<Dimension>(r.bottomRight());
}
};
}
}}
double distance(const QPoint &p, const QRect &r, const bool filled)
{
if (filled && r.contains(p))
return 0.0;
else
return boost::geometry::distance(p, r);
}
int main()
{
QRect r(QPoint(0, 0), QPoint(20, 10));
QPoint p(5, 5); // whithin rect
// 0, instead of 5
std::cout << "not filled: " << distance(p, r, false) << '\n';
// 0, as expected
std::cout << "filled: " << distance(p, r, true) << '\n';
}
Run g++ -Wall -O2 -fPIC main.cpp -I/usr/include/qt -lQtCore to build this on Linux.
I could of course use the LineString for the not filled case, though then there would be dynamic allocations.
Unless I create a manualy adaption which uses an underlying QRect, which would be quite some work.
How do I best tackle this issue?
Indeed you're right line-string is required because Box implies a filled shape. Same thing for polygons, actually, in my quick test.
You could of course create a fake "holey" polygon that has an edge of some small width. But that's cheating and certainly less efficient
Indeed, you can use linestring here:
Live On Coliru
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>
using namespace boost::geometry;
int main()
{
using Point = model::d2::point_xy<double>;
using Rect = model::linestring<Point>;
Rect rect;
rect.insert(rect.end(), {
Point { 0, 0 },
Point { 10, 0 },
Point { 10, 20 },
Point { 0, 20 },
Point { 0, 0 },
});
std::cout << "distance point within: " << distance(rect, Point(5, 5)) << '\n'; // 0
std::cout << "distance point not within: " << distance(rect, Point(15, 5)) << '\n'; // 5
}
Which prints
distance point within: 5
distance point not within: 5
I don't see any reason to believe that the linestring is less efficient than the polygon (it's basically the same as just the outer ring of a polygon).
However, indeed box testing might be faster. I suggest you profile it. If it's faster, just use the box in case the shape is known to be "filled" and a linestring otherwise.
A relative easy way to support non-filled QRect is to use the LineString concept.
To avoid overhead of a allocations std::array could be used.
Based on the intial code, the following parts need to be added:
#include <array>
using RectLineString = std::array<QPoint, 5>;
BOOST_GEOMETRY_REGISTER_LINESTRING(RectLineString)
double distance(const QPoint &p, const QRect &r, const bool filled)
{
if (filled && r.contains(p))
return 0.0;
else
{
RectLineString rls;
fillRectLineString(rls, rect);
return boost::geometry::distance(p, rls);
}
}
What fillrectLineString should look like depends on how you want to handle the issue that QRect::bottomRight() returns QPoint(rect.x() + rect.width() - 1, rect.y() + rect.height() - 1).
So I provide two versions here:
// bottomRight() is QPoint(rect.x() + rect.width() - 1, rect.y() + rect.height() - 1)
void fillRectLineString1(RectLineString &rls, const QRect &rect)
{
rls[0] = rect.topLeft();
rls[1] = rect.topRight();
rls[2] = rect.bottomRight();
rls[3] = rect.bottomLeft();
rls[4] = rect.topLeft();
}
// bottomRight() is QPoint(rect.x() + rect.width(), rect.y() + rect.height())
void fillRectLineString2(RectLineString &rls, const QRect &rect)
{
rls[0] = QPoint(rect.x(), rect.y());
rls[1] = QPoint(rect.x() + rect.width(), rect.y());
rls[2] = QPoint(rect.x() + rect.width(), rect.y() + rect.height());
rls[3] = QPoint(rect.x(), rect.y() + rect.height());
rls[4] = QPoint(rect.x(), rect.y());
}

Instance of CGAL::Voronoi_diagram_2 is always invalid

I'm using CGAL to construct a half-edge based structure of 2D Voronoi diagram. I chose the CGAL::Voronoi_diagram_2<DT,AT,AP> class to do that, and starting with the example provided by CGAL.
What confuses me now is: for the initial sites (which are loaded from file) added by the insert method as follow:
Site_2 t;
while ( ifs >> t ) { vd.insert(t); }
Things seem to be correct.
But when I try to add some extra sites by the insert method again, and use vd.is_valid() to check the validity, the assertion always return false. I enter the the is_valid() function and find that the following line:
valid = valid && ap_.is_valid(dual_);
is false since ap_.is_valid(dual_) return false. Since I'm new to CGAL, I only know that ap_ is some Adaption_policy class and here it is Delaunay_triangulation_caching_degeneracy_removal_policy_2.
I tried to comment this assertion, but the program crashed for some reason, which means apparently this assertion is necessary.
Does anyone know how to make this assertion correct? Thanks in advance.
Edit:
Following is the main part of my code (which uses OpenGL under GLUT, and I left out some unrelated part such as rendering):
#include <iostream>
#include <fstream>
#include <cassert>
#include <windows.h>
#include <gl\glut.h>
#include <cmath>
// includes for defining the Voronoi diagram adaptor
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_2.h>
#include <CGAL/Voronoi_diagram_2.h>
#include <CGAL/Delaunay_triangulation_adaptation_traits_2.h>
#include <CGAL/Delaunay_triangulation_adaptation_policies_2.h>
#include <CGAL/intersections.h>
// typedefs for defining the adaptor
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Delaunay_triangulation_2<K> DT;
typedef CGAL::Delaunay_triangulation_adaptation_traits_2<DT> AT;
typedef CGAL::Delaunay_triangulation_caching_degeneracy_removal_policy_2<DT> AP;
typedef CGAL::Voronoi_diagram_2<DT,AT,AP> VD;
// typedef for the result type of the point location
typedef AT::Site_2 Site_2;
typedef AT::Point_2 Point_2;
typedef VD::Locate_result Locate_result;
typedef VD::Vertex_handle Vertex_handle;
typedef VD::Face_handle Face_handle;
typedef VD::Halfedge_handle Halfedge_handle;
typedef VD::Ccb_halfedge_circulator Ccb_halfedge_circulator;
VD vd;
int Width = 640;
int Height = 480;
void myMouseClick(int button, int state, int x, int y)
{
if (state != GLUT_DOWN) return;
if (button == GLUT_LEFT_BUTTON)
{
double viewPort[4];
glGetDoublev(GL_VIEWPORT, viewPort);
Point_2 p0(x, viewPort[3] - y);
vd.insert(Site_2(p0));
assert(vd.is_valid()); //here the assertion fails
}
glutPostRedisplay();
}
int myInit()
{
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_POINT_SMOOTH);
// load voronoi data
std::ifstream ifs("data/data2.dt.cin");
assert( ifs );
Site_2 t;
while ( ifs >> t ) { vd.insert(t); }
ifs.close();
assert( vd.is_valid() ); //here the assertion passes
return 0;
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(Width, Height);
glutInitWindowPosition(100, 100);
glutCreateWindow("2-D Voronoi Diagram");
glutMouseFunc(myMouseClick);
if(myInit() < 0) return -1;
glutMainLoop();
return 0;
}
Edit:
It seems that the CGAL::Delaunay_triangulation_caching_degeneracy_removal_policy_2<DT> policy's problem. I replace it with CGAL::Identity_policy_2<DT, AT>, the default one, and the assertion passes.
According to the documents, the former policy seems to find degeneracy edge in the voronoi diagram, but I don't see any one visually (I don't have reputation to upload the result, but the input data is the same as what I mentioned in the comment.

Change colour of an edge

I have an graph which consists of edges and vertices. When the clicks an edge in the graph, the edge is supposed to change colour. I have included some code samples to demonstrate my problem.
To draw the initial graph;
#include "StdAfx.h"
#include <vtkSmartPointer.h>
#include <vtkCallbackCommand.h>
#include <vtkAnnotationLink.h>
#include <vtkRenderedGraphRepresentation.h>
#include <vtkRenderer.h>
#include <vtkDoubleArray.h>
#include <vtkSelectionNode.h>
#include <vtkIdTypeArray.h>
#include <vtkSelection.h>
#include <vtkRenderWindow.h>
#include <vtkUnsignedCharArray.h>
#include <vtkObjectFactory.h>
#include <vtkGraphLayoutStrategy.h>
#include <vtkGraphLayoutView.h>
#include <vtkGraphWriter.h>
#include <vtkMutableUndirectedGraph.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkIntArray.h>
#include <vtkLookupTable.h>
#include <vtkDataSetAttributes.h>
#include <vtkViewTheme.h>
void SelectionCallbackFunction(vtkObject* caller, long unsigned int eventId, void* clientData, void* callData);
vtkSmartPointer<vtkMutableUndirectedGraph> g;
int main(int, char *[])
{
g =
vtkSmartPointer<vtkMutableUndirectedGraph>::New();
vtkIdType v1 = g->AddVertex();
vtkIdType v2 = g->AddVertex();
g->AddEdge(v1, v2);
g->AddEdge(v1, v2);
vtkSmartPointer<vtkCallbackCommand> selectionCallback =
vtkSmartPointer<vtkCallbackCommand>::New();
selectionCallback->SetCallback (SelectionCallbackFunction);
// Create the color array
vtkSmartPointer<vtkIntArray> edgeColors =
vtkSmartPointer<vtkIntArray>::New();
edgeColors->SetNumberOfComponents(1);
edgeColors->SetName("Color");
vtkSmartPointer<vtkLookupTable> lookupTable =
vtkSmartPointer<vtkLookupTable>::New();
lookupTable->SetNumberOfTableValues(1);
lookupTable->SetTableValue(0, 1.0, 0.0, 0.0); // red
lookupTable->Build();
edgeColors->InsertNextValue(0);
// Add the color array to the graph
g->GetEdgeData()->AddArray(edgeColors);
vtkSmartPointer<vtkGraphLayoutView> view =
vtkSmartPointer<vtkGraphLayoutView>::New();
view->SetEdgeColorArrayName("Color");
view->ColorEdgesOn();
vtkSmartPointer<vtkViewTheme> theme =
vtkSmartPointer<vtkViewTheme>::New();
theme->SetCellLookupTable(lookupTable);
view->ApplyViewTheme(theme);
view->AddRepresentationFromInput(g);
view->SetLayoutStrategy("Simple 2D");
view->GetRepresentation()->GetAnnotationLink()->AddObserver("AnnotationChangedEvent", selectionCallback);
view->ResetCamera();
view->Render();
view->GetInteractor()->Start();
return EXIT_SUCCESS;
}
For the mouse click function I have used the below code;
vtkAnnotationLink* annotationLink =
static_cast<vtkAnnotationLink*>(caller);
vtkSelection* selection = annotationLink->GetCurrentSelection();
vtkSelectionNode* edges;
if(selection->GetNode(0)->GetFieldType() == vtkSelectionNode::EDGE)
{
edges = selection->GetNode(0);
}
if(selection->GetNode(1)->GetFieldType() == vtkSelectionNode::EDGE)
{
edges = selection->GetNode(1);
}
vtkIdTypeArray* edgeList = vtkIdTypeArray::SafeDownCast(edges->GetSelectionList());
for(vtkIdType i = 0; i < edgeList->GetNumberOfTuples(); i++)
{
//Change colour of the edge
}
My problem is that I cannot change the colour of the edge dynamically. I would be very grateful for any help regarding the matter.
The below code worked for me. First when I create the graph I set the colour of each and every edge,
edgeColors = vtkSmartPointer<vtkIntArray>::New();
edgeColors->SetNumberOfComponents(1);
edgeColors->SetName("Color");
vtkSmartPointer<vtkLookupTable> lookupTable =
vtkSmartPointer<vtkLookupTable>::New();
lookupTable->SetNumberOfTableValues(2);
lookupTable->SetTableValue(0, 0.5, 1.0, 0.5); // green
lookupTable->SetTableValue(1, 0.0, 1.0, 0.0); // white
lookupTable->Build();
//For each edge id insert colour
for(int i = 0;i<=graph->GetNumberOfEdges();i++)
edgeColors->InsertValue(i,0);
// Add the color array to the graph
graph->GetEdgeData()->AddArray(edgeColors);
Then in my mouse click function I get the vtkIdType of the clicked edge and set the colour of it.
vtkIdType edge = edgeList->GetValue(0);
edgeColors->InsertValue(edge.Id,1);//set colour of edge
graphLayoutView->GetInteractor()->Render();