Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SimpleSets: basic implementation #46

Merged
merged 41 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
94a340e
Add basic patterns: island, bank; and their cover radius
Yvee1 Aug 13, 2024
648b276
Implement basic dilation and partitioning algorithm. Start on drawing…
Yvee1 Aug 15, 2024
0db582a
Merge master into SimpleSets branch
Yvee1 Aug 15, 2024
0a33517
Use absolute stroke width
Yvee1 Aug 15, 2024
9d3362a
SimpleSets drawing: connected components and store data in halfedges
Yvee1 Aug 17, 2024
f2c478e
Merge render-path-arcs into SimpleSets
Yvee1 Aug 17, 2024
a5622bf
SimpleSets drawing: fix face_to_polygon and draw circular arcs
Yvee1 Aug 18, 2024
11a39dd
Start on component boundary circulator
Yvee1 Aug 18, 2024
a2b25a8
SimpleSets drawing: add stacking preference and helper functions
Yvee1 Aug 20, 2024
4ccbec1
SimpleSets drawing: hyperedges and face ordering
Yvee1 Aug 21, 2024
2c8b31c
Basic cutouts and drawing, with todos.
Yvee1 Sep 4, 2024
28e995d
Fix poly_line_gon_intersection & fix duplicates in avoidees.
Yvee1 Sep 6, 2024
fed5322
Fix stroke drawing
Yvee1 Sep 6, 2024
2c8e349
Fix point size, preference & order, stroke drawing & improve GUI
Yvee1 Sep 6, 2024
749fc7d
Draw using total stacking order if possible and fix issues
Yvee1 Sep 8, 2024
41766a0
Small fix and debugging on diseasome data
Yvee1 Sep 8, 2024
20a7682
Small fixes; intersection delay does not crash anymore
Yvee1 Sep 9, 2024
3b47849
Fixes in intersection delay and hyperedges; add cover slider
Yvee1 Sep 11, 2024
f4bf79a
Add NYC dataset for testing purposes
Yvee1 Sep 30, 2024
32bdfab
Fix small bug in poly_line_gon_intersection & clean test
Yvee1 Oct 1, 2024
e622395
Use relative file path
Yvee1 Oct 1, 2024
551286d
Merge branch 'tue-alga:master' into SimpleSets
Yvee1 Oct 1, 2024
fbd5780
Fix missing typename which annoys clang 14
Willem3141 Oct 2, 2024
2d3674a
Satisfy clang++: add constructors and change <=> to ==
Yvee1 Oct 2, 2024
0226c06
Merge branch 'SimpleSets' of https://github.com/Yvee1/cartocrow into …
Yvee1 Oct 2, 2024
2194234
Merge branch 'tue-alga:master' into SimpleSets
Yvee1 Oct 2, 2024
86fd6e9
Deal with collinear points in an island
Yvee1 Oct 2, 2024
0ad729e
Comment crashing spiral tree test case
Yvee1 Oct 2, 2024
f2ab5dd
Initialize stroke opacity; fixes crashing tests
Yvee1 Oct 2, 2024
ee81e19
Work around CGAL bug
Yvee1 Oct 3, 2024
f51d5aa
Make circle convex hull demo
Yvee1 Oct 3, 2024
ea0b9db
Add source information for nyc.txt
Yvee1 Oct 3, 2024
79e2aec
Fix typo
Yvee1 Oct 3, 2024
af5924f
Remove unused ExactWithSqrt type alias
Yvee1 Oct 3, 2024
7c3444e
Fix poly_line_gon_intersection
Yvee1 Oct 4, 2024
696aa65
Add hex color constructor
Yvee1 Oct 4, 2024
51a845f
Add diseasome dataset
Yvee1 Oct 4, 2024
d096ece
Fix various bugs that occur in edge cases
Yvee1 Oct 7, 2024
88f3da9
When hyperedge has cycle, ignore preferences instead of crashing
Yvee1 Oct 7, 2024
a97f3ac
Fix stacking order drawing
Yvee1 Oct 7, 2024
8395d51
Fix admissible check for non-matchings
Yvee1 Oct 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cartocrow/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ add_subdirectory(necklace_map)
add_subdirectory(simplification)
add_subdirectory(flow_map)
add_subdirectory(isoline_simplification)
add_subdirectory(simplesets)

add_library(cartocrow_lib INTERFACE)
target_link_libraries(
Expand Down
1 change: 1 addition & 0 deletions cartocrow/core/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace cartocrow {

Color::Color() : r(0), g(0), b(0) {}
Color::Color(int r, int g, int b) : r(r), g(g), b(b) {}
Color::Color(int rgb) : r((rgb & 0xff0000) >> 16), g((rgb & 0x00ff00) >> 8), b(rgb & 0x0000ff) {}

Number<Inexact> wrapAngle(Number<Inexact> alpha, Number<Inexact> beta) {
return wrap<Inexact>(alpha, beta, beta + M_2xPI);
Expand Down
5 changes: 5 additions & 0 deletions cartocrow/core/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Created by tvl (t.vanlankveld@esciencecenter.nl) on 07-11-2019
#include <CGAL/Bbox_2.h>
#include <CGAL/Circle_2.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel_with_sqrt.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Line_2.h>
#include <CGAL/Point_2.h>
Expand Down Expand Up @@ -60,6 +61,8 @@ template <class K> using Line = CGAL::Line_2<K>;
template <class K> using Segment = CGAL::Segment_2<K>;
/// A ray in the plane. See \ref CGAL::Ray_2.
template <class K> using Ray = CGAL::Ray_2<K>;
/// An axis-aligned rectangle in the plane. See \ref CGAL::Iso_rectangle_2.
template <class K> using Rectangle = CGAL::Iso_rectangle_2<K>;

/// A polygon in the plane. See \ref CGAL::Polygon_2.
template <class K> using Polygon = CGAL::Polygon_2<K>;
Expand Down Expand Up @@ -174,6 +177,8 @@ struct Color {
Color();
/// Constructs a color.
Color(int r, int g, int b);
/// Constructs a color from a single integer (useful combined with hexadecimal literals, e.g. 0xFFFFFF).
Color(int rgb);
};

/// Wraps the given number \f$n\f$ to the interval \f$[a, b)\f$.
Expand Down
10 changes: 5 additions & 5 deletions cartocrow/isoline_simplification/symmetric_difference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ enum Side {
TOP = 3,
};

Side closest_side(const Point<Exact>& point, const Exact::Iso_rectangle_2& bb) {
Side closest_side(const Point<Exact>& point, const Rectangle<Exact>& bb) {
std::vector<double> dist({ to_double(point.x()) - to_double(bb.xmin()), to_double(point.y()) - to_double(bb.ymin()),
to_double(bb.xmax()) - to_double(point.x()), to_double(bb.ymax()) - to_double(point.y()) });
auto it = std::min_element(dist.begin(), dist.end());
Expand All @@ -50,7 +50,7 @@ Vector<Exact> side_direction(const Side& side) {
}
}

Point<Exact> proj_on_side(Point<Exact> p, Side side, const Exact::Iso_rectangle_2& rect) {
Point<Exact> proj_on_side(Point<Exact> p, Side side, const Rectangle<Exact>& rect) {
switch (side) {
case LEFT:
return { rect.xmin(), p.y() };
Expand All @@ -63,7 +63,7 @@ Point<Exact> proj_on_side(Point<Exact> p, Side side, const Exact::Iso_rectangle_
}
}

Point<Exact> corner(const Side& side1, const Side& side2, const Exact::Iso_rectangle_2& rect) {
Point<Exact> corner(const Side& side1, const Side& side2, const Rectangle<Exact>& rect) {
if (side1 > side2) return corner(side2, side1, rect);
auto dist = abs(side1 - side2);
if (dist == 1) {
Expand All @@ -77,7 +77,7 @@ Side next_side(const Side& side) {
return static_cast<Side>((side + 1) % 4);
}

Polygon<Exact> close_isoline(const Isoline<K>& isoline, Exact::Iso_rectangle_2& bb, Side source_side, Side target_side) {
Polygon<Exact> close_isoline(const Isoline<K>& isoline, Rectangle<Exact>& bb, Side source_side, Side target_side) {
std::vector<Point<Exact>> points;
std::transform(isoline.m_points.begin(), isoline.m_points.end(), std::back_inserter(points),
[](Point<Inexact> p) { return Point<Exact>(p.x(), p.y()); });
Expand Down Expand Up @@ -133,7 +133,7 @@ double symmetric_difference(const Isoline<K>& original, const Isoline<K>& simpli
std::transform(simplified.m_points.begin(), simplified.m_points.end(), std::back_inserter(all_points),
[](Point<Inexact> p) { return Point<Exact>(p.x(), p.y()); });

Exact::Iso_rectangle_2 bb = CGAL::bounding_box(all_points.begin(), all_points.end());
Rectangle<Exact> bb = CGAL::bounding_box(all_points.begin(), all_points.end());

auto source_side = closest_side(
Point<Exact>(original.m_points.front().x(), original.m_points.front().y()), bb);
Expand Down
2 changes: 2 additions & 0 deletions cartocrow/renderer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ set(SOURCES
geometry_widget.cpp
ipe_renderer.cpp
painting_renderer.cpp
function_painting.cpp
render_path.cpp
)
set(HEADERS
Expand All @@ -11,6 +12,7 @@ set(HEADERS
geometry_widget.h
ipe_renderer.h
painting_renderer.h
function_painting.h
render_path.h
)

Expand Down
10 changes: 10 additions & 0 deletions cartocrow/renderer/function_painting.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "function_painting.h"

namespace cartocrow::renderer {
FunctionPainting::FunctionPainting(const std::function<void(GeometryRenderer&)>& draw_function)
Copy link
Member

@Willem3141 Willem3141 Oct 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I don't grok the purpose of a FunctionPainting. One can already make an ad-hoc painting by subclassing GeometryPainting, for example:

    class SomePainting : public GeometryPainting {
        void paint(GeometryRenderer& renderer) const override {
            renderer.draw(/* whatever you want to draw */);
        }
    };
    IpeRenderer renderer(std::make_shared<SomePainting>());

I guess with FunctionPainting slightly less code is needed:

    IpeRenderer renderer(std::make_shared<FunctionPainting>([]() {
        renderer.draw(/* whatever you want to draw */);
    }));

but I'm not sure if the added complexity is worth it?

Anyway, did I miss a use case where using FunctionPainting would be significantly easier?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not use FunctionPainting on its own, but use the helper function (or the implicitly converting constructor that you suggest below).
Then you go from:

class SomePainting : public GeometryPainting {
    void paint(GeometryRenderer& renderer) const override {
        renderer.draw(/* whatever you want to draw */);
    }
};
renderer.addPainting(std::make_shared<SomePainting>());

to

renderer.addPainting([](GeometryRenderer& renderer) {
    renderer.draw(/* whatever you want to draw */);
});

I don't have a specific use case in mind that is way simpler this way, I added it only as a more concise way of drawing something simple. For me, having to create a class, derive from a specific other one, and override one specific method, in order to draw something is boiler plate and an obstacle when programming. Generally I would also define this painting class in a header, and have the implementation in the cpp file, though I see your point that you can also define it locally just in the cpp file. Part of the reason that I find this much simpler is probably that I'm more used to functional programming than object oriented programming.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An argument for adding this: a lambda function makes it easy to capture variables that are in scope. When debugging a function, I sometimes use IpeRenderer together with a lambda function to draw some debug info and save it to a file.

: m_draw_function(draw_function) {}

void FunctionPainting::paint(GeometryRenderer& renderer) const {
m_draw_function(renderer);
}
}
18 changes: 18 additions & 0 deletions cartocrow/renderer/function_painting.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef CARTOCROW_FUNCTION_PAINTING_H
#define CARTOCROW_FUNCTION_PAINTING_H

#include "geometry_renderer.h"
#include "geometry_painting.h"

namespace cartocrow::renderer {
class FunctionPainting : public GeometryPainting {
public:
FunctionPainting(const std::function<void(GeometryRenderer&)>& draw_function);
void paint(renderer::GeometryRenderer& renderer) const override;

private:
const std::function<void(GeometryRenderer&)> m_draw_function;
};
}

#endif //CARTOCROW_FUNCTION_PAINTING_H
9 changes: 8 additions & 1 deletion cartocrow/renderer/geometry_widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "geometry_renderer.h"
#include "ipe_renderer.h"

#include "function_painting.h"

#include <QFileDialog>
#include <QGuiApplication>
#include <QPainterPath>
Expand Down Expand Up @@ -541,7 +543,7 @@ void GeometryWidget::draw(const BezierSpline& s) {

void GeometryWidget::draw(const Ray<Inexact>& r) {
Box bounds = inverseConvertBox(rect());
auto result = intersection(r, CGAL::Iso_rectangle_2<Inexact>(Point<Inexact>(bounds.xmin(), bounds.ymin()), Point<Inexact>(bounds.xmax(), bounds.ymax())));
auto result = intersection(r, Rectangle<Inexact>(Point<Inexact>(bounds.xmin(), bounds.ymin()), Point<Inexact>(bounds.xmax(), bounds.ymax())));
if (result) {
if (const Segment<Inexact>* s = boost::get<Segment<Inexact>>(&*result)) {
int oldMode = m_style.m_mode;
Expand Down Expand Up @@ -680,6 +682,11 @@ void GeometryWidget::addPainting(std::shared_ptr<GeometryPainting> painting, con
updateLayerList();
}

void GeometryWidget::addPainting(const std::function<void(renderer::GeometryRenderer&)>& draw_function, const std::string& name) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(see comment about FunctionPainting above) I'm also not a huge fan of having to do this in every renderer 🤔

Copy link
Member

@Willem3141 Willem3141 Oct 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid this while keeping FunctionPainting we may provide an (implicitly converting) constructor from std::function<void(renderer::GeometryRenderer&)> to FunctionPainting?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not think of that.
That does mean you cannot do

renderer.addPainting([](GeometryRenderer& renderer) {
    renderer.draw(/* whatever you want to draw */);
});

because you need to still create a shared pointer, which I would like to avoid having to do.

auto painting = std::make_shared<FunctionPainting>(draw_function);
addPainting(painting, name);
}

void GeometryWidget::clear() {
m_paintings.clear();
updateLayerList();
Expand Down
1 change: 1 addition & 0 deletions cartocrow/renderer/geometry_widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ class GeometryWidget : public QWidget, public GeometryRenderer {

/// Adds a new painting to this widget.
void addPainting(std::shared_ptr<GeometryPainting> painting, const std::string& name);
void addPainting(const std::function<void(renderer::GeometryRenderer&)>& draw_function, const std::string& name);
/// Removes all paintings from this widget.
void clear();

Expand Down
37 changes: 33 additions & 4 deletions cartocrow/renderer/ipe_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.

#include "ipe_renderer.h"

#include "function_painting.h"
#include "geometry_renderer.h"

#include <ipeattributes.h>
Expand Down Expand Up @@ -74,9 +75,18 @@ void IpeRenderer::save(const std::filesystem::path& file) {
m_alphaSheet->setName("alpha-values");
document.cascade()->insert(2, m_alphaSheet);
setFillOpacity(255); // add default alpha to style sheet
setStrokeOpacity(255); // add default alpha to style sheet

m_page = new ipe::Page();
for (auto painting : m_paintings) {
document.push_back(m_page);

int current_page = 0;

for (auto painting : m_paintings) { // Assumes m_paintings are ordered in increasing page_index
while (painting.page_index > current_page) {
m_page = new ipe::Page();
document.push_back(m_page);
}
pushStyle();
if (auto name = painting.name) {
m_page->addLayer(name->c_str());
Expand All @@ -88,7 +98,6 @@ void IpeRenderer::save(const std::filesystem::path& file) {
popStyle();
}

document.push_back(m_page);
document.save(file.string().c_str(), ipe::FileFormat::Xml, 0);
}

Expand Down Expand Up @@ -320,17 +329,37 @@ ipe::AllAttributes IpeRenderer::getAttributesForStyle() const {
attributes.iStroke = ipe::Attribute(ipe::Color(m_style.m_strokeColor));
attributes.iFill = ipe::Attribute(ipe::Color(m_style.m_fillColor));
attributes.iOpacity = m_style.m_fillOpacity;
attributes.iStrokeOpacity = m_style.m_strokeOpacity;
return attributes;
}


void IpeRenderer::addPainting(const std::function<void(renderer::GeometryRenderer&)>& draw_function) {
auto painting = std::make_shared<FunctionPainting>(draw_function);
addPainting(painting);
}

void IpeRenderer::addPainting(const std::function<void(renderer::GeometryRenderer&)>& draw_function, const std::string& name) {
auto painting = std::make_shared<FunctionPainting>(draw_function);
addPainting(painting, name);
}

void IpeRenderer::addPainting(const std::shared_ptr<GeometryPainting>& painting) {
m_paintings.push_back(DrawnPainting{painting});
m_paintings.push_back(DrawnPainting{painting, std::nullopt, m_page_index});
}

void IpeRenderer::addPainting(const std::shared_ptr<GeometryPainting>& painting, const std::string& name) {
std::string spaceless;
std::replace_copy(name.begin(), name.end(), std::back_inserter(spaceless), ' ', '_');
m_paintings.push_back(DrawnPainting{painting, spaceless});
m_paintings.push_back(DrawnPainting{painting, spaceless, m_page_index});
}

void IpeRenderer::nextPage() {
++m_page_index;
}

int IpeRenderer::currentPage() {
return m_page_index;
}

std::string IpeRenderer::escapeForLaTeX(const std::string& text) const {
Expand Down
11 changes: 11 additions & 0 deletions cartocrow/renderer/ipe_renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,16 @@ class IpeRenderer : public GeometryRenderer {
void setFill(Color color) override;
void setFillOpacity(int alpha) override;

void addPainting(const std::function<void(renderer::GeometryRenderer&)>& draw_function);
void addPainting(const std::function<void(renderer::GeometryRenderer&)>& draw_function, const std::string& name);
void addPainting(const std::shared_ptr<GeometryPainting>& painting);
void addPainting(const std::shared_ptr<GeometryPainting>& painting, const std::string& name);

/// Paintings will be added to a new page.
void nextPage();
/// Returns the current page index.
int currentPage();

private:
/// Converts a polygon to an Ipe curve.
ipe::Curve* convertPolygonToCurve(const Polygon<Inexact>& p) const;
Expand All @@ -119,6 +126,8 @@ class IpeRenderer : public GeometryRenderer {
std::shared_ptr<GeometryPainting> m_painting;
/// The name of the painting displayed as a layer name in ipe.
std::optional<std::string> name;
/// The Ipe page the painting will be drawn onto.
int page_index;
};

/// The paintings we're drawing.
Expand All @@ -138,6 +147,8 @@ class IpeRenderer : public GeometryRenderer {
ipe::StyleSheet* m_alphaSheet;
/// The index of the Ipe layer we are currently drawing to.
int m_layer;
/// The index of the Ipe page a painting will get drawn to.
int m_page_index = 0;
};

} // namespace cartocrow::renderer
Expand Down
55 changes: 55 additions & 0 deletions cartocrow/simplesets/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
set(SOURCES
cat_point.cpp
types.cpp
parse_input.cpp
patterns/pattern.cpp
patterns/single_point.cpp
patterns/matching.cpp
patterns/island.cpp
patterns/bank.cpp
dilated/dilated_poly.cpp
helpers/approximate_convex_hull.cpp
helpers/cs_curve_helpers.cpp
helpers/cs_polygon_helpers.cpp
helpers/cs_polyline_helpers.cpp
helpers/poly_line_gon_intersection.cpp
partition_algorithm.cpp
partition_painting.cpp
drawing_algorithm.cpp
grow_circles.cpp
)
set(HEADERS
types.h
settings.h
cat_point.h
parse_input.h
patterns/pattern.h
patterns/poly_pattern.h
patterns/single_point.h
patterns/matching.h
patterns/island.h
patterns/bank.h
dilated/dilated_poly.h
helpers/approximate_convex_hull.h
helpers/arrangement_helpers.h
helpers/point_voronoi_helpers.h
helpers/poly_line_gon_intersection.h
helpers/cropped_voronoi.h
helpers/cs_curve_helpers.h
helpers/cs_polygon_helpers.h
helpers/cs_polyline_helpers.h
partition_algorithm.h
partition_painting.h
partition.h
drawing_algorithm.h
general_polyline.h
grow_circles.h
)

add_library(simplesets ${SOURCES})
target_link_libraries(simplesets
PUBLIC core
)

cartocrow_install_module(simplesets)
install(FILES ${HEADERS} DESTINATION ${CARTOCROW_INSTALL_DIR}/simplesets)
7 changes: 7 additions & 0 deletions cartocrow/simplesets/cat_point.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "cat_point.h"

namespace cartocrow::simplesets {
std::ostream& operator<<(std::ostream& os, CatPoint const& catPoint) {
return os << catPoint.category << " " << catPoint.point;
}
}
18 changes: 18 additions & 0 deletions cartocrow/simplesets/cat_point.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef CARTOCROW_CAT_POINT_H
#define CARTOCROW_CAT_POINT_H

#include "types.h"

namespace cartocrow::simplesets {
/// Categorical point
struct CatPoint {
unsigned int category;
Point<Inexact> point;
CatPoint(unsigned int category, Point<Inexact> point) : category(category), point(point) {};
bool operator==(const CatPoint&) const = default;
};

std::ostream& operator<<(std::ostream& os, CatPoint const& catPoint);
}

#endif //CARTOCROW_CAT_POINT_H
Loading
Loading