init
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
---
|
||||
BasedOnStyle: LLVM
|
||||
---
|
||||
Language: Cpp
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Left
|
||||
ColumnLimit: 120
|
||||
TabWidth: 4
|
||||
IndentWidth: 2
|
||||
...
|
||||
@@ -0,0 +1,25 @@
|
||||
# debug: clangd --check=modules/iue-io/ccsv.h
|
||||
# debug: clangd --check=task1.hpp
|
||||
# debug: clangd --check=task1.test.cpp
|
||||
InlayHints:
|
||||
Enabled: No
|
||||
ParameterNames: Yes
|
||||
DeducedTypes: No
|
||||
---
|
||||
CompileFlags:
|
||||
Add:
|
||||
# - --target=x86_64-w64-windows-gnu
|
||||
# - --target=x86_64-pc-linux-gnu
|
||||
- -Wall
|
||||
- -Wno-unused-function
|
||||
- -Wno-unused-variable
|
||||
---
|
||||
If:
|
||||
PathMatch: [.*\.c, .*\.h]
|
||||
CompileFlags:
|
||||
Add: [-std=c11]
|
||||
---
|
||||
If:
|
||||
PathMatch: [.*\.cpp, .*\.hpp]
|
||||
CompileFlags:
|
||||
Add: [-std=c++20]
|
||||
@@ -0,0 +1,3 @@
|
||||
task1
|
||||
task2
|
||||
task3
|
||||
@@ -0,0 +1,3 @@
|
||||
task1.main.cpp
|
||||
task2.cpp
|
||||
task3.cpp
|
||||
@@ -0,0 +1,16 @@
|
||||
## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings
|
||||
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
# Explicitly declare text files you want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
*.h text
|
||||
*.hpp text
|
||||
*.c text
|
||||
*.cpp text
|
||||
*.py text
|
||||
*.ipynb text
|
||||
*.md text
|
||||
*.txt text
|
||||
*.csv text
|
||||
@@ -0,0 +1,360 @@
|
||||
# custom
|
||||
|
||||
*.csv
|
||||
*.png
|
||||
build
|
||||
doc
|
||||
.cache
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# https://github.com/github/gitignore/blob/main/CMake.gitignore
|
||||
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
|
||||
# ttps://github.com/github/gitignore/blob/main/C.gitignore
|
||||
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore
|
||||
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# source: https://github.com/github/gitignore/blob/main/Python.gitignore
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
@@ -0,0 +1,4 @@
|
||||
[submodule "modules"]
|
||||
path = modules
|
||||
url = https://sgit.iue.tuwien.ac.at/360050/modules
|
||||
branch = main
|
||||
@@ -0,0 +1,47 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
# define project metadata
|
||||
|
||||
project(exercise5 LANGUAGES CXX
|
||||
DESCRIPTION "exercise5"
|
||||
HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/exercise5")
|
||||
|
||||
# setting required language standards
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# misc settings
|
||||
|
||||
# avoid ctest dashboard targets
|
||||
set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1)
|
||||
# generate a compile_commands.json
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
# make all symbols visible on windows (which is default on unix)
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
|
||||
# options
|
||||
|
||||
option(BUILD_TESTING "enable testing with ctest" ON)
|
||||
|
||||
# testing
|
||||
|
||||
include(CTest)
|
||||
|
||||
# get/setup dependencies
|
||||
|
||||
include_directories(modules)
|
||||
|
||||
# include own targets
|
||||
|
||||
add_executable(task1 task1.main.cpp)
|
||||
add_test(NAME task1 COMMAND task1 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
set_property(TEST task1 PROPERTY PASS_REGULAR_EXPRESSION "14")
|
||||
|
||||
add_executable(task2 task2.cpp task2.test.cpp)
|
||||
add_test(NAME task2 COMMAND task2 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
|
||||
add_executable(task3 TriangleMesh.cpp task2.cpp task3.cpp task3.test.cpp)
|
||||
add_test(NAME task3 COMMAND task3 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Hausübung 5 (3 Punkte)
|
||||
|
||||
**Ausgabe**: Donnerstag 18. April 2024, vormittags.
|
||||
|
||||
**Abgabe bis**: Montag 29. April 2024, Ende des Tages.
|
||||
|
||||
**Abgabe via**: git-Repository mit dem Namen **`exercise5`** auf unserem git-Server https://sgit.iue.tuwien.ac.at
|
||||
|
||||
Details zum Abgabeprozess via `git` finden Sie hier: https://sgit.iue.tuwien.ac.at/360050/git
|
||||
|
||||
# Aufgabenstellung
|
||||
|
||||
In dieser Hausübung werden folgende Themen erstmalig einfliessen:
|
||||
|
||||
- Arbeiten mit Kollektionen von eindeutig Werten `std::set<int>`/`std::unordered_set<int>`
|
||||
- Arbeit mit der öffentlichen Schnittstelle einer gegebenen Klasse
|
||||
- Nutzung von selbst implementierter Funktionalität in einem separaten Anwendungskontext
|
||||
|
||||
**Die genaue Beschreibung und Anforderungen finden Sie in [`main.ipynb`](main.ipynb) und im Quellcode.**
|
||||
@@ -0,0 +1,217 @@
|
||||
/// @file
|
||||
/// @brief TriangleMesh: member function implementation/definitions
|
||||
|
||||
#include "TriangleMesh.hpp" // ex5::TriangleMesh
|
||||
|
||||
#include "iue-svg/render.hpp" // iue::svg::render
|
||||
#include "iue-svg/tags.hpp" // iue::svg::Triangle
|
||||
|
||||
#include <algorithm> // std::sort, std::ranges::max, std::max
|
||||
#include <cassert> // assert
|
||||
#include <filesystem> // std::filesystem
|
||||
#include <iostream> // std::cout, std::endl
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <map> // std::map
|
||||
#include <set> // std::set, std::multiset
|
||||
#include <tuple> // std::tuple
|
||||
#include <unordered_set> // std::unordered_set
|
||||
#include <utility> // std::pair
|
||||
#include <vector> // std::vector, std::erase_if
|
||||
|
||||
namespace ex5 {
|
||||
|
||||
TriangleMesh::TriangleMesh(TriangleMesh::BBox box, double h) : vertices(), triangles() {
|
||||
|
||||
auto [min, max] = box;
|
||||
|
||||
assert(max[0] > min[0]);
|
||||
assert(max[1] > min[1]);
|
||||
|
||||
auto width = max[0] - min[0];
|
||||
auto height = max[1] - min[1];
|
||||
|
||||
int nx = (width / h) + 1;
|
||||
int ny = (height / h) + 1;
|
||||
|
||||
assert(nx >= 2);
|
||||
assert(ny >= 2);
|
||||
|
||||
vertices.resize(nx * ny);
|
||||
|
||||
for (size_t iy = 0; iy != ny; ++iy) {
|
||||
for (size_t ix = 0; ix != nx; ++ix) {
|
||||
vertices.at(ix + iy * nx) = {ix * h + min[0], iy * h + min[1]};
|
||||
}
|
||||
}
|
||||
|
||||
triangles.reserve((nx - 1) * (ny - 1) * 2);
|
||||
for (size_t iy = 1; iy != ny; ++iy) {
|
||||
for (size_t ix = 1; ix != nx; ++ix) {
|
||||
auto i = ix + iy * nx;
|
||||
auto ia = i - nx - 1;
|
||||
auto ib = i - nx;
|
||||
auto ic = i;
|
||||
auto id = i - 1;
|
||||
triangles.push_back({ia, ib, id});
|
||||
triangles.push_back({ib, ic, id});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TriangleMesh::BBox TriangleMesh::bbox() const {
|
||||
Vec2d bbmin = {+std::numeric_limits<double>::max(), +std::numeric_limits<double>::max()};
|
||||
Vec2d bbmax = {-std::numeric_limits<double>::max(), -std::numeric_limits<double>::max()};
|
||||
|
||||
// update minmax from single coordinate
|
||||
auto minmax = [&bbmin, &bbmax](const Vec2d& p) {
|
||||
bbmin[0] = bbmin[0] < p[0] ? bbmin[0] : p[0];
|
||||
bbmin[1] = bbmin[1] < p[1] ? bbmin[1] : p[1];
|
||||
bbmax[0] = bbmax[0] > p[0] ? bbmax[0] : p[0];
|
||||
bbmax[1] = bbmax[1] > p[1] ? bbmax[1] : p[1];
|
||||
};
|
||||
|
||||
for (const auto& v : vertices) {
|
||||
minmax(v);
|
||||
}
|
||||
|
||||
return {bbmin, bbmax};
|
||||
}
|
||||
|
||||
const std::vector<TriangleMesh::Vec2d>& TriangleMesh::getVertices() const { return vertices; }
|
||||
|
||||
const std::vector<TriangleMesh::Vec3i>& TriangleMesh::getTriangles() const { return triangles; }
|
||||
|
||||
void TriangleMesh::print() const {
|
||||
std::cout << "Mesh [" << std::endl;
|
||||
std::cout << " vertices [" << std::endl;
|
||||
{ // print vertices
|
||||
size_t index = 0;
|
||||
for (const auto& [x, y] : vertices) {
|
||||
std::cout << " " << index++ << ": (" << x << ", " << y << " )" << std::endl;
|
||||
}
|
||||
std::cout << " ]" << std::endl;
|
||||
}
|
||||
{ // print triangles
|
||||
size_t index = 0;
|
||||
std::cout << " triangles [" << std::endl;
|
||||
for (const auto& [ia, ib, ic] : triangles) {
|
||||
std::cout << " " << index++ << ": (" << ia << ", " << ib << ", " << ic << " )" << std::endl;
|
||||
}
|
||||
std::cout << " ]" << std::endl;
|
||||
}
|
||||
std::cout << "]" << std::endl;
|
||||
}
|
||||
|
||||
TriangleMesh::BBox TriangleMesh::save(std::filesystem::path filepath) const {
|
||||
|
||||
std::vector<iue::svg::Triangle> tris;
|
||||
|
||||
for (const auto& [ia, ib, ic] : triangles) {
|
||||
tris.push_back({vertices[ia], vertices[ib], vertices[ic]});
|
||||
}
|
||||
|
||||
return iue::svg::render(filepath, {}, {}, tris);
|
||||
}
|
||||
|
||||
bool TriangleMesh::check_invariants() const {
|
||||
|
||||
std::set<size_t> vertices_in_use = {};
|
||||
std::multiset<std::pair<size_t, size_t>> edges;
|
||||
for (auto i3 : triangles) {
|
||||
std::sort(i3.begin(), i3.end());
|
||||
edges.insert({i3[0], i3[1]});
|
||||
edges.insert({i3[1], i3[2]});
|
||||
edges.insert({i3[2], i3[0]});
|
||||
|
||||
// check: each triangle references three distinct corners
|
||||
if (std::set<size_t>{i3[0], i3[1], i3[2]}.size() != 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check: each triangle references valid indices
|
||||
if (std::ranges::max(i3) >= vertices.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vertices_in_use.insert(i3[0]);
|
||||
vertices_in_use.insert(i3[1]);
|
||||
vertices_in_use.insert(i3[2]);
|
||||
}
|
||||
|
||||
// check: edges are not occupied more than twice (no hidden edges)
|
||||
for (const auto& edge : edges) {
|
||||
if (edges.count(edge) > 2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check: no unreferenced vertices are present
|
||||
if (vertices_in_use.size() != vertices.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::tuple<size_t, size_t> TriangleMesh::remove_vertices(std::unordered_set<size_t> indices) {
|
||||
|
||||
if (indices.empty())
|
||||
return {0, 0};
|
||||
|
||||
assert(indices.size() != 0);
|
||||
assert(std::ranges::max(indices) <= vertices.size());
|
||||
|
||||
// remove connected triangles
|
||||
auto connected = [&indices](const Vec3i& triangle) -> bool {
|
||||
const auto& [ia, ib, ic] = triangle;
|
||||
return indices.contains(ia) || indices.contains(ib) || indices.contains(ic);
|
||||
};
|
||||
auto num_removed_triangles = std::erase_if(triangles, connected);
|
||||
|
||||
// find additional points which became unused (due to removal of triangles) and ...
|
||||
std::unordered_set<size_t> in_use;
|
||||
for (auto& [ia, ib, ic] : triangles) {
|
||||
in_use.insert(ia);
|
||||
in_use.insert(ib);
|
||||
in_use.insert(ic);
|
||||
}
|
||||
// .. add those to the list of indices to be removed
|
||||
for (size_t i = 0; i != vertices.size(); ++i) {
|
||||
if (in_use.find(i) == in_use.end()) {
|
||||
indices.insert(i);
|
||||
}
|
||||
}
|
||||
|
||||
// remove vertices and record offsets
|
||||
std::map<size_t, ptrdiff_t> offsets;
|
||||
ptrdiff_t offset = 0;
|
||||
size_t index = 0;
|
||||
auto remove = [&index, &indices, &offsets, &offset](const Vec2d&) -> bool {
|
||||
if (indices.contains(index)) {
|
||||
std::pair<size_t, ptrdiff_t> pair = {std::max<ptrdiff_t>(index - 1, 0), offset--};
|
||||
offsets.insert(pair);
|
||||
++index;
|
||||
return true;
|
||||
}
|
||||
++index;
|
||||
return false;
|
||||
};
|
||||
auto num_removed_vertices = std::erase_if(vertices, remove);
|
||||
|
||||
// add final offset
|
||||
std::pair<size_t, ptrdiff_t> pair = {std::max<ptrdiff_t>(index - 1, 0), offset};
|
||||
offsets.insert(pair);
|
||||
|
||||
// update triangle indices using recorded offsets
|
||||
for (auto& triangle : triangles) {
|
||||
for (auto& i : triangle) {
|
||||
auto iter = offsets.lower_bound(i);
|
||||
if (iter != offsets.end())
|
||||
i += iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
return {num_removed_vertices, num_removed_triangles};
|
||||
}
|
||||
|
||||
} // namespace ex5
|
||||
@@ -0,0 +1,74 @@
|
||||
/// @file
|
||||
/// @brief TriangleMesh: class definition and member function declarations
|
||||
|
||||
#pragma once
|
||||
|
||||
// hpp
|
||||
#include <array> // std::array
|
||||
#include <tuple> // std::tuple
|
||||
#include <vector> // std::vector
|
||||
#include <unordered_set> // std::unordered_set
|
||||
#include <filesystem> // std::filesystem
|
||||
|
||||
namespace ex5 {
|
||||
|
||||
///@brief Two-dimensional triangle mesh
|
||||
class TriangleMesh {
|
||||
|
||||
public:
|
||||
///@brief Vertex coordinate
|
||||
using Vec2d = std::array<double, 2>;
|
||||
|
||||
///@brief Index triplet
|
||||
using Vec3i = std::array<size_t, 3>;
|
||||
|
||||
///@brief Axis-aligned bounding box described using the bottom left and upper right coordinate
|
||||
using BBox = std::tuple<Vec2d, Vec2d>;
|
||||
|
||||
private:
|
||||
std::vector<Vec2d> vertices; ///< sequence of vertices
|
||||
std::vector<Vec3i> triangles; ///< sequence of index triplets each indexing the three corner vertices of a triangle
|
||||
|
||||
public:
|
||||
/// @brief Generates a regular triangular mesh pattern inside an axis-aligned bounding box
|
||||
/// @param box Axis-aligned bounding box of the generated mesh
|
||||
/// @param h Lower bound for the edge length of the generate triangles
|
||||
TriangleMesh(BBox box, double h);
|
||||
|
||||
/// @brief Calculates the current axis-aligned bounding box
|
||||
/// @return Bounding box of all triangles/vertices in the mesh
|
||||
BBox bbox() const;
|
||||
|
||||
/// @brief Read-only access to the vertices of the mesh
|
||||
/// @return Reference to the underlying sequence of vertices
|
||||
const std::vector<Vec2d>& getVertices() const;
|
||||
|
||||
/// @brief Read-only access to triangles of the mesh
|
||||
/// @return Reference to the underlying sequence of triangles
|
||||
const std::vector<Vec3i>& getTriangles() const;
|
||||
|
||||
/// @brief Prints the sequences of vertices and triangles to the console
|
||||
void print() const;
|
||||
|
||||
/// @brief Generate SVG image
|
||||
/// @param filepath Filename for the generated output
|
||||
BBox save(std::filesystem::path filepath) const;
|
||||
|
||||
/// @brief Evaluates the invariants of the class
|
||||
/// @return false, if any of the invariants is violated, true otherwise
|
||||
/// Invariants:
|
||||
/// - each triangle references valid indices
|
||||
/// - each triangle references three distinct corners
|
||||
/// - edges are not occupied more than twice (no hidden edges)
|
||||
/// - no unreferenced vertices are present
|
||||
bool check_invariants() const;
|
||||
|
||||
/// @brief Removes a set of vertices from the mesh:
|
||||
/// - all triangles connected to the removed vertices are removed
|
||||
/// - all unused vertices potentially originating from the triangle removal are removed
|
||||
/// @param indices Vertex indices to be removed
|
||||
/// @return Total number of removed vertices and total number of removed triangles
|
||||
std::tuple<size_t, size_t> remove_vertices(std::unordered_set<size_t> indices);
|
||||
};
|
||||
|
||||
} // namespace mesh
|
||||
@@ -0,0 +1 @@
|
||||
-Imodules
|
||||
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 283 KiB |
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 278 KiB |
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 368 KiB |
@@ -0,0 +1,242 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Aufgabe 1: Ein eigenes kleines C++-Programm (*std::set*/*std::unordered_set*) (1 Punkt)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Erstellen Sie in [`task1.main.cpp`](task1.main.cpp) ein lauffähiges Ein-Dateien-Programm das folgende Struktur aufweist:\n",
|
||||
"\n",
|
||||
"- Einbinden benötigter Header-Dateien aus der Standardbibliothek, z.B.:\n",
|
||||
"\t```cpp\n",
|
||||
"\t#include <iostream> // std::cout, std::endl\n",
|
||||
"\t#include <set> // std::set, std::unordered_set\n",
|
||||
"\t#include <vector> // std::vector\n",
|
||||
"\t...\n",
|
||||
"\t```\n",
|
||||
"- Definition/Implementierung einer eigenen Funktion in einem eignene Namensraum, z.B.:\n",
|
||||
"\t```cpp\n",
|
||||
"\tnamespace task1 {\n",
|
||||
"\n",
|
||||
"\t...\n",
|
||||
"\n",
|
||||
"\t}\n",
|
||||
"\t``` \n",
|
||||
"- Definition/Implementierung einer `main`-Funktion, die Ihre selbst geschriebene Funktion verwendet und die berechneten Ergebnisse in der Konsole ausgibt, z.B.:\n",
|
||||
"\t```cpp\n",
|
||||
"\tint main(){\n",
|
||||
"\n",
|
||||
"\t ...\n",
|
||||
"\n",
|
||||
"\t return 0;\n",
|
||||
"\t}\n",
|
||||
"\t``` \n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"- Eine genaue Beschreibung und Anforderungen finden Sie in [`task1.main.cpp`](task1.main.cpp)\n",
|
||||
"- Ihre Implementierung erfolgt ebenfalls in [`task1.main.cpp`](task1.main.cpp)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Aufgabe 2: Freie Funktionen zur Selektion von zweidimensionalen Koordinaten (1 Punkt)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Die von Ihnen zu implementierenden freien Funktionen wählen aus einer Sequenz von Koordinaten diejenigen aus, die bei Übergabe an eine ebenfalls übergebene Funktion `true` zurückgeben.\n",
|
||||
"\n",
|
||||
"Die Rückgabe der ausgewählten Koordinaten erfolgt nicht direkt über eine Liste von Koordinatenwerten, sondern indirekt über eine Liste von Indizes. \n",
|
||||
"\n",
|
||||
"Hinweis: In Aufgabe 3 werden Sie Ihre Funktionen im Zusammenhang mit der Klasse `ex5::TriangleMesh` anwenden.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Implementieren Sie die folgende freie Funktionen:\n",
|
||||
"\n",
|
||||
"```cpp\n",
|
||||
"namespace task2 {\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"using Vec2d = std::array<double, 2>;\n",
|
||||
"using Circle = std::tuple<Vec2d, double>;\n",
|
||||
"using BBox = std::tuple<Vec2d, Vec2d>;\n",
|
||||
"using Region = std::function<bool(Vec2d)>;\n",
|
||||
"\n",
|
||||
"// todo\n",
|
||||
"std::unordered_set<size_t> select(const std::vector<Vec2d>& vertices, \n",
|
||||
" const Region& region, \n",
|
||||
" bool invert = false);\n",
|
||||
"// todo\n",
|
||||
"std::unordered_set<size_t> select_union(const std::vector<Vec2d>& vertices, \n",
|
||||
" const std::vector<Region>& regions,\n",
|
||||
" bool invert = false);\n",
|
||||
"\n",
|
||||
"} \n",
|
||||
"\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"- Die vorgegebenen Deklarationen und eine genaue Beschreibung und Anforderungen finden Sie in [`task2.hpp`](task2.hpp)\n",
|
||||
"- Ihre Implementierung erfolgt in [`task2.cpp`](task2.cpp)\n",
|
||||
"- Die zugeordneten Tests finden Sie in [`task2.test.cpp`](task2.test.cpp)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Aufgabe 3: Erzeugen eines Dreiecksgitters für zwei Kreisringe (1 Punkt)\n",
|
||||
"\n",
|
||||
"Sie implementieren eine Funktion, die mittels der Klasse `ex5::TriangleMesh` zuerst ein rechteckiges Dreiecksgitter erstellt, und dann mittels\n",
|
||||
"\n",
|
||||
"- der von Ihnen implementierten Funktionen `task2::select`/`task2::select_union` und \n",
|
||||
"- der öffentlichen Schnittstellen der Klasse `ex5::TriangleMesh` \n",
|
||||
"\n",
|
||||
"Dreiecke aus dem Gitter entfernt, so dass schlussendlich ein Dreiecksgitter nur innerhalb zweier Kreisringe bestehen bleibt. \n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Implementieren Sie folgende Funktion:\n",
|
||||
"\n",
|
||||
"```cpp\n",
|
||||
"\n",
|
||||
"#include \"TriangleMesh.hpp\" // ex5::TriangleMesh\n",
|
||||
"\n",
|
||||
"namespace task3 {\n",
|
||||
"\n",
|
||||
"// todo\n",
|
||||
"ex5::TriangleMesh generate_double_annulus(task2::Vec2d c1, task2::Vec2d c2, double r, double R, double h);\n",
|
||||
"\n",
|
||||
"}\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "plaintext"
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"Dies sieht dann abhängig von den Koordinaten der Kreismittelpunkte, Radien und Gitterauflösung z.B. so aus:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"<!--  -->\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"- Die vorgegebenen Deklaration und eine genaue Beschreibung und Anforderungen finden Sie in [`task3.hpp`](task3.hpp)\n",
|
||||
"- Ihre Implementierung erfolgt in [`task3.cpp`](task3.cpp)\n",
|
||||
"- Die zugeordneten Tests finden Sie in [`task3.test.cpp`](task3.test.cpp)\n",
|
||||
"- In [`TriangleMesh.hpp`](TriangleMesh.hpp)/[`TriangleMesh.cpp`](TriangleMesh.cpp) finden Sie die vorgegebene Implementierung für `ex5::TriangleMesh`\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Kompilieren/Testen\n",
|
||||
"\n",
|
||||
"So testen Sie Ihre Implementierung (direkter Aufruf von `g++` und `python`):\n",
|
||||
"\n",
|
||||
"```shell\n",
|
||||
"# prepare\n",
|
||||
"mkdir build\n",
|
||||
"# compile\n",
|
||||
"g++ -g -std=c++20 task1.main.cpp -o build/task1\n",
|
||||
"g++ -g -Imodules -std=c++20 task2.cpp task2.test.cpp -o build/task2\n",
|
||||
"g++ -g -Imodules -std=c++20 TriangleMesh.cpp task2.cpp task3.cpp task3.test.cpp -o build/task3\n",
|
||||
"\n",
|
||||
"# run tests\n",
|
||||
"./build/task1\n",
|
||||
"./build/task2\n",
|
||||
"./build/task3\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"Alternativ (mittels CMake-Configuration):\n",
|
||||
"\n",
|
||||
"```shell\n",
|
||||
"# prepare\n",
|
||||
"cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug\n",
|
||||
"# compile\n",
|
||||
"cmake --build build --config Debug --target task1\n",
|
||||
"cmake --build build --config Debug --target task2\n",
|
||||
"cmake --build build --config Debug --target task3\n",
|
||||
"cmake --build build --config Debug # all\n",
|
||||
"# run tests\n",
|
||||
"ctest --test-dir build -C Debug -R task1 --verbose\n",
|
||||
"ctest --test-dir build -C Debug -R task2 --verbose\n",
|
||||
"ctest --test-dir build -C Debug -R task3 --verbose\n",
|
||||
"ctest --test-dir build -C Debug # all\n",
|
||||
"``` \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.6.15"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
Submodule
+1
Submodule exercise5/modules added at d75e4d122c
@@ -0,0 +1,35 @@
|
||||
/// @file
|
||||
/// @brief Task1: "single-file" excutable C++ program
|
||||
|
||||
/// @todo Include standard library headers as needed
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
namespace task1 {
|
||||
|
||||
/// @todo Implement a function 'count_unique' according to the description below:
|
||||
/// - a sequence of integer values is received via a parameter of type std::vector<int>
|
||||
/// - the function finds the unique entries in the sequence by inserting each value into a std::unordered_set<int>
|
||||
/// - the values in a std::unordered_set are unique, so the number of unique entries is the size of the set
|
||||
/// - the function then returns the number of unique entries
|
||||
/// @param sequence a sequence of integer values
|
||||
/// @return the number of unique entries in the sequence
|
||||
int count_unique(const std::vector<int>& sequence) {
|
||||
return (std::set<int> (sequence.begin(), sequence.end())).size();
|
||||
}
|
||||
|
||||
|
||||
} // namespace task1
|
||||
|
||||
/// - fill a std::vector<int> with with this sequence of values
|
||||
/// 1,1,10,2,2,3,4,5,6,7,8,9,10,3,3,11,12,13,14
|
||||
/// - use your function to count the unique values in the sequence above
|
||||
/// - print the number of unique values to the console
|
||||
int main() {
|
||||
std::vector<int> sequence = {1,1,10,2,2,3,4,5,6,7,8,9,10,3,3,11,12,13,14};
|
||||
int unique_Count = task1::count_unique(sequence);
|
||||
std::cout << "Number of unique values: " << unique_Count << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/// @file
|
||||
/// @brief Task2: member function definitions/implementations
|
||||
|
||||
#include "task2.hpp" // task2::select, task2::select_union
|
||||
|
||||
#include <array> // std::array
|
||||
#include <functional> // std::function
|
||||
#include <unordered_set> // std::unordered_set
|
||||
#include <vector> // std::vector
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace task2 {
|
||||
|
||||
|
||||
/// @brief Selects the subset of vertices for which a boolean function evaluates to true
|
||||
/// @param vertices Two-dimensional vertices to select from
|
||||
/// @param region Boolean function to evaluate if a vertex is selected
|
||||
/// @param invert Inverts the selection process if set to true
|
||||
/// @return Indices of the selected vertices
|
||||
std::unordered_set<size_t> select(const std::vector<Vec2d>& vertices, const Region& region, bool invert) {
|
||||
std::unordered_set<size_t> selected_indices;
|
||||
for (size_t i = 0; i < vertices.size(); ++i) {
|
||||
const bool isSelected = region(vertices[i]);
|
||||
if ((isSelected && !invert) || (!isSelected && invert)) {
|
||||
selected_indices.insert(i);
|
||||
}
|
||||
}
|
||||
return selected_indices;
|
||||
}
|
||||
|
||||
|
||||
/// @brief Selects the subset of vertices for which ANY of a sequence of boolean functions evaluates to true
|
||||
/// @param vertices Two-dimensional vertices to select from
|
||||
/// @param regions Sequence of boolean functions to evaluate
|
||||
/// @param invert Inverts the selection process if set to 'true'
|
||||
/// @return Indices of the selected vertices
|
||||
std::unordered_set<size_t> select_union(const std::vector<Vec2d>& vertices, const std::vector<Region>& regions, bool invert) {
|
||||
std::unordered_set<size_t> selected_indices;
|
||||
for (size_t i = 0; i < vertices.size(); ++i) {
|
||||
bool isSelected = false;
|
||||
// Check if any region function evaluates to true
|
||||
for (const Region& region : regions) {
|
||||
isSelected = isSelected || region(vertices[i]);
|
||||
if (isSelected && !invert) {
|
||||
// Exit inner loop if selected and not inverting
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isSelected && invert) {
|
||||
// Add to selected set only if inverting and not already selected
|
||||
} else {
|
||||
// Add to selected set for normal selection or inverted with not yet selected
|
||||
selected_indices.insert(i);
|
||||
}
|
||||
}
|
||||
return selected_indices;
|
||||
}
|
||||
|
||||
} // namespace task2
|
||||
@@ -0,0 +1,42 @@
|
||||
/// @file
|
||||
/// @brief Task2: class definitions with member function declarations
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array> // std::array
|
||||
#include <functional> // std::function
|
||||
#include <unordered_set> // std::unordered_set
|
||||
#include <vector> // std::vector
|
||||
|
||||
namespace task2 {
|
||||
|
||||
/// @brief two-dimensional coordinate
|
||||
using Vec2d = std::array<double, 2>;
|
||||
|
||||
/// @brief Circle with center and radius
|
||||
using Circle = std::tuple<Vec2d, double>;
|
||||
|
||||
/// @brief Axis-aligned bounding box with bottom left and top right coordinate)
|
||||
using BBox = std::tuple<Vec2d, Vec2d>;
|
||||
|
||||
/// @brief Arbitrary two-dimensional region described by a boolean function returning
|
||||
/// - true, if the point/coordinate in question lies inside this region, and
|
||||
/// - false, otherwise.
|
||||
using Region = std::function<bool(Vec2d)>;
|
||||
|
||||
/// @brief Selects the subset of vertices for which a boolean function evaluates to true
|
||||
/// @param vertices Two-dimensional vertices to select from
|
||||
/// @param region Boolean function to evaluate if a vertex is selected
|
||||
/// @param invert Inverts the selection process if set to true
|
||||
/// @return Indices of the selected vertices
|
||||
std::unordered_set<size_t> select(const std::vector<Vec2d>& vertices, const Region& region, bool invert = false);
|
||||
|
||||
/// @brief Selects the subset of vertices for which ANY of a sequence of boolean functions evaluates to true
|
||||
/// @param vertices Two-dimensional vertices to select from
|
||||
/// @param regions Sequence of boolean functions to evaluate
|
||||
/// @param invert Inverts the selection process if set to 'true'
|
||||
/// @return Indices of the selected vertices
|
||||
std::unordered_set<size_t> select_union(const std::vector<Vec2d>& vertices, const std::vector<Region>& regions,
|
||||
bool invert = false);
|
||||
|
||||
} // namespace task2
|
||||
@@ -0,0 +1,86 @@
|
||||
/// @file
|
||||
/// @brief Task2: tests
|
||||
|
||||
#include "task2.hpp" // task2::select, task2::select_union
|
||||
|
||||
#include "iue-rnd/random.hpp" // iue::rnd::UniformValue
|
||||
|
||||
#include <algorithm> // std::sort
|
||||
#include <array> // std::array
|
||||
#include <cassert> // assert
|
||||
#include <cmath> // std::sqrt
|
||||
#include <iostream> // std::cout, std::endl
|
||||
#include <numbers> // std::numbers::pi
|
||||
|
||||
int main() {
|
||||
|
||||
using namespace task2;
|
||||
|
||||
std::vector<Vec2d> vertices;
|
||||
|
||||
// upper diagonal halfspace region
|
||||
auto upper_diagonal_halfspace = [](const Vec2d& coord) -> bool {
|
||||
auto [x, y] = coord;
|
||||
if (y > x && std::abs(x - y) > 1e-9)
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
{ // adding random vertices in upper diagonal halfspace
|
||||
auto gen = iue::rnd::UniformValue(-1.0, 1.0);
|
||||
for (int n = 0; n != 1e2; ++n) {
|
||||
std::array<double, 2> xy{gen(), gen()};
|
||||
std::ranges::sort(xy);
|
||||
vertices.push_back({xy});
|
||||
}
|
||||
}
|
||||
|
||||
// testing task::select
|
||||
|
||||
{
|
||||
auto subset = task2::select(vertices, upper_diagonal_halfspace, false);
|
||||
assert(subset.size() == vertices.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto subset = task2::select(vertices, upper_diagonal_halfspace, true);
|
||||
assert(subset.size() == 0);
|
||||
}
|
||||
|
||||
// testing task::select_union
|
||||
|
||||
// circular region around center(1,1) with radius 1
|
||||
Circle circle = {{1, 1}, 1};
|
||||
auto circle_c11_r1 = [&circle](const Vec2d& coord) -> bool {
|
||||
auto [c, r] = circle;
|
||||
auto [cx, cy] = c;
|
||||
auto [x, y] = coord;
|
||||
return std::sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)) < r ? true : false;
|
||||
};
|
||||
|
||||
{ // adding random vertices inside the circle
|
||||
auto [center, r] = circle;
|
||||
auto gen_angle = iue::rnd::UniformValue(0, std::numbers::pi * 2);
|
||||
auto gen_radius = iue::rnd::UniformValue(0, r - 1e-9);
|
||||
for (int n = 0; n != 1e3; ++n) {
|
||||
double angle = gen_angle();
|
||||
double radius = gen_radius();
|
||||
std::array<double, 2> xy{center[0] + std::sin(angle) * radius, center[1] + std::cos(angle) * radius};
|
||||
vertices.push_back({xy});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto subset = task2::select_union(vertices, {upper_diagonal_halfspace, circle_c11_r1}, false);
|
||||
assert(subset.size() == vertices.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto subset = task2::select_union(vertices, {upper_diagonal_halfspace, circle_c11_r1}, true);
|
||||
assert(subset.size() == 0);
|
||||
}
|
||||
|
||||
std::cout << "task2.test.cpp: all asserts passed" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/// @file
|
||||
/// @brief Task3: implementation
|
||||
|
||||
#include "task3.hpp" // task3::generate_double_annulus
|
||||
#include "task2.hpp" // task2::select, task2::select_union
|
||||
|
||||
#include "TriangleMesh.hpp" // ex5::TriangleMesh
|
||||
|
||||
#include <cmath> // for std::sqrt
|
||||
|
||||
namespace task3 {
|
||||
|
||||
using namespace task2;
|
||||
|
||||
|
||||
/// @todo Implement function 'generate_double_annulus' as declared and specified in task3.hpp
|
||||
/// Implementation Hints:
|
||||
/// - calculate the required bounding box for the annuluses using the centers coordinates +- outer radius
|
||||
/// - create a triangle mesh using the desired bounding box and resolution (h)
|
||||
/// - use the source code of task2.test.cpp for ideas how to create the boolean functions for circular regions
|
||||
/// - select the union of the two inner circles and remove the triangles using the following functions:
|
||||
/// mesh.getVertices
|
||||
/// task2::select_union(...)
|
||||
/// mesh.remove_vertices(...)
|
||||
/// - analoguously, select the union of the two outer circles and remove the INVERSE of this union using
|
||||
/// - mesh.getVertices()
|
||||
/// - task2::select_union(..., true)
|
||||
/// - mesh.remove_vertices(...)
|
||||
|
||||
|
||||
/// @brief Generates a triangle mesh placed of two (potentially merged) annuluses (Kreisringe)
|
||||
/// @param c1 Center coordinate of the first annulus
|
||||
/// @param c2 Center coordinate of the second annulus
|
||||
/// @param r inner radius of both annuluses
|
||||
/// @param R outer radius of both annuluses
|
||||
/// @param h lower bound for the triangle egde length of the resulting mesh
|
||||
/// @return Resulting triangle mesh
|
||||
ex5::TriangleMesh generate_double_annulus(task2::Vec2d c1, task2::Vec2d c2, double r, double R, double h) {
|
||||
// Calculate bounding box based on center coordinates, radii and a small offset
|
||||
double offset = h / 2.0; // Adjust as needed
|
||||
|
||||
auto bb_min = task2::Vec2d{std::min(c1[0], c2[0]) - R - offset,
|
||||
std::min(c1[1], c2[1]) - R - offset};
|
||||
auto bb_max = task2::Vec2d{std::max(c1[0], c2[0]) + R + offset,
|
||||
std::max(c1[1], c2[1]) + R + offset};
|
||||
|
||||
// Create triangle mesh using bounding box and edge length
|
||||
ex5::TriangleMesh mesh(ex5::TriangleMesh::BBox{bb_min, bb_max}, h);
|
||||
|
||||
// Function to select points within circles (assuming select_union is defined in task2)
|
||||
auto in_circle = [&](const task2::Vec2d& p, task2::Vec2d center, double radius) {
|
||||
return std::sqrt(std::pow(p[0] - center[0], 2) + std::pow(p[1] - center[1], 2)) <= radius;
|
||||
};
|
||||
|
||||
// Select and remove vertices inside both inner circles (union)
|
||||
std::unordered_set<size_t> remove_inner;
|
||||
for (size_t i = 0; i < mesh.getVertices().size(); ++i) {
|
||||
const auto& v = mesh.getVertices()[i];
|
||||
if (in_circle(v, c1, r) && in_circle(v, c2, r)) {
|
||||
remove_inner.insert(i);
|
||||
}
|
||||
}
|
||||
mesh.remove_vertices(remove_inner);
|
||||
|
||||
// Select and remove vertices outside both outer circles (union - inverted)
|
||||
std::unordered_set<size_t> remove_outer;
|
||||
for (size_t i = 0; i < mesh.getVertices().size(); ++i) {
|
||||
const auto& v = mesh.getVertices()[i];
|
||||
if (!in_circle(v, c1, R) && !in_circle(v, c2, R)) {
|
||||
remove_outer.insert(i);
|
||||
}
|
||||
}
|
||||
mesh.remove_vertices(remove_outer);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
} // namespace task3
|
||||
@@ -0,0 +1,21 @@
|
||||
/// @file
|
||||
/// @brief Task3: function declarations
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "task2.hpp" // task2::Vec2d
|
||||
|
||||
#include "TriangleMesh.hpp" // ex5::TriangleMesh
|
||||
|
||||
namespace task3 {
|
||||
|
||||
/// @brief Generates a triangle mesh placed of two (potentially merged) annuluses (Kreisringe)
|
||||
/// @param c1 Center coordinate of the first annulus
|
||||
/// @param c2 Center coordinate of the second annulus
|
||||
/// @param r inner radius of both annuluses
|
||||
/// @param R outer radius of both annuluses
|
||||
/// @param h lower bound for the triangle egde length of the resulting mesh
|
||||
/// @return Resulting triangle mesh
|
||||
ex5::TriangleMesh generate_double_annulus(task2::Vec2d c1, task2::Vec2d c2, double r, double R, double h);
|
||||
|
||||
} // namespace task3
|
||||
@@ -0,0 +1,37 @@
|
||||
/// @file
|
||||
/// @brief Task3: tests
|
||||
|
||||
#include "task3.hpp" // task3::generate_double_annulus
|
||||
#include "TriangleMesh.hpp" // ex5::TriangleMesh
|
||||
|
||||
#include <cassert> // assert
|
||||
#include <filesystem> // std::filesystem::exists
|
||||
#include <iostream> // std::cout, sts::endl
|
||||
#include <string> // std::string
|
||||
|
||||
int main() {
|
||||
|
||||
using namespace task2;
|
||||
|
||||
std::string filepath = "task3.test.svg";
|
||||
|
||||
task2::Vec2d c1 = {5, 5};
|
||||
task2::Vec2d c2 = {10, 10};
|
||||
double r = 3;
|
||||
double R = 4;
|
||||
double h = 0.15;
|
||||
auto mesh = task3::generate_double_annulus(c1, c2, r, R, h);
|
||||
std::filesystem::remove(filepath);
|
||||
mesh.save(filepath);
|
||||
assert(std::filesystem::exists(filepath));
|
||||
auto bbox = mesh.bbox();
|
||||
auto [bbmin, bbmax] = bbox;
|
||||
assert(bbmin[0] <= c1[0] - R + 2 * h);
|
||||
assert(bbmax[0] >= c2[0] + R - 2 * h);
|
||||
assert(bbmin[1] <= c1[1] - R + 2 * h);
|
||||
assert(bbmax[1] >= c2[1] + R - 2 * h);
|
||||
|
||||
std::cout << "task3.test.cpp: all asserts passed" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user