Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 11 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ on:
schedule:
- cron: "40 7 * * 1"

permissions:
contents: read
checks: write
pull-requests: write

env:
VCPKG_BINARY_SOURCES: 'clear;x-gha,readwrite'

Expand Down Expand Up @@ -49,11 +54,13 @@ jobs:
- name: Build (Release)
uses: lukka/run-cmake@v10
with:
configurePreset: 'release-preset'
buildPreset: 'release-preset'
configurePreset: 'vcpkg-release'
buildPreset: 'vcpkg-release'
testPreset: 'vcpkg-release'

- name: Build (Debug)
uses: lukka/run-cmake@v10
with:
configurePreset: 'debug-preset'
buildPreset: 'debug-preset'
configurePreset: 'vcpkg-debug'
buildPreset: 'vcpkg-debug'
testPreset: 'vcpkg-debug'
11 changes: 9 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ cmake_minimum_required (VERSION 3.18)

project (STEPToPoints LANGUAGES CXX VERSION 2025.0.0)

option(BUILD_TESTS "Build tests" ON)

if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;RelWithDebInfo;MinSizeRel")
Expand Down Expand Up @@ -97,7 +99,7 @@ else()
message(STATUS "OpenMP not found")
endif()

add_executable(STEPToPoints src/STEPToPoints.cpp)
add_executable(STEPToPoints src/STEPToPoints.cpp src/solid_index_parser.cpp)

# @TODO: check which OpenCASCADE libraries are really needed
target_link_libraries(STEPToPoints ${OpenCASCADE_FoundationClasses_LIBRARIES} ${OpenCASCADE_ModelingData_LIBRARIES} ${OpenCASCADE_ModelingAlgorithms_LIBRARIES} ${OpenCASCADE_Visualization_LIBRARIES} ${OpenCASCADE_ApplicationFramework_LIBRARIES} ${OpenCASCADE_DataExchange_LIBRARIES} ${OpenCASCADE_Draw_LIBRARIES} indicators::indicators) #
Expand All @@ -108,4 +110,9 @@ endif()

target_compile_options(STEPToPoints PRIVATE ${MY_COMPILE_OPTIONS})
target_compile_definitions(STEPToPoints PUBLIC ${MY_COMPILE_DEFINITIONS})
target_compile_features(STEPToPoints PRIVATE ${MY_CXX_FEATURE})
target_compile_features(STEPToPoints PRIVATE ${MY_CXX_FEATURE})

if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
91 changes: 83 additions & 8 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,36 @@
"configurePresets": [
{
"name": "default",
"description": "default preset that disable vcpkg, so that the dependencies are not built automatically",
"hidden": true,
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": {
"type": "FILEPATH",
"value": ""
}
}
},
{
"name": "debug",
"description": "Debug build",
"inherits": "default",
"binaryDir": "${sourceDir}/build/debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "release",
"description": "Release build",
"inherits": "default",
"binaryDir": "${sourceDir}/build/release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "vcpkg-default",
"hidden": true,
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
Expand All @@ -22,17 +52,17 @@
}
},
{
"name": "release-preset",
"inherits": "default",
"name": "vcpkg-release",
"inherits": "vcpkg-default",
"displayName": "Release Build",
"description": "Release build configuration",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "debug-preset",
"inherits": "default",
"name": "vcpkg-debug",
"inherits": "vcpkg-default",
"displayName": "Debug Build",
"description": "Debug build configuration",
"cacheVariables": {
Expand All @@ -42,16 +72,61 @@
],
"buildPresets": [
{
"name": "release-preset",
"configurePreset": "release-preset",
"name": "release",
"configurePreset": "release",
"description": "Build in Release mode"
},
{
"name": "debug",
"configurePreset": "debug",
"description": "Build in Debug mode"
},
{
"name": "vcpkg-release",
"configurePreset": "vcpkg-release",
"displayName": "Release Build",
"description": "Build in Release mode"
},
{
"name": "debug-preset",
"configurePreset": "debug-preset",
"name": "vcpkg-debug",
"configurePreset": "vcpkg-debug",
"displayName": "Debug Build",
"description": "Build in Debug mode"
}
],
"testPresets": [
{
"name": "test-default",
"description": "default test",
"hidden": true,
"output": {"outputOnFailure": true},
"execution": {"noTestsAction": "error", "stopOnFailure": true}
},
{
"name": "release",
"description": "Test in Release mode",
"configurePreset": "release",
"inherits": "test-default"
},
{
"name": "debug",
"description": "Test in Debug mode",
"configurePreset": "debug",
"inherits": "test-default"
},
{
"name": "vcpkg-release",
"description": "Test in Release mode with vcpkg",
"configurePreset": "vcpkg-release",
"configuration": "Release",
"inherits": "test-default"
},
{
"name": "vcpkg-debug",
"description": "Test in Debug mode with vcpkg",
"configurePreset": "vcpkg-debug",
"configuration": "Debug",
"inherits": "test-default"
}
]
}
1 change: 1 addition & 0 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ cmake -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake ..
# this will take a while as it will download and build all the necessary dependencies
# now we can build the project
cmake --build . --config Release
```
28 changes: 19 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
# STEPToPoints

[![CI-Build](https://github.com/simogasp/STEPToPoints/actions/workflows/build.yml/badge.svg)](https://github.com/simogasp/STEPToPoints/actions/workflows/build.yml)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fsimogasp%2FSTEPToPoints.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fsimogasp%2FSTEPToPoints?ref=badge_shield)

## Description

The program STEPToPoints is a command line utility to generate point clouds out of solids contained in STEP files.
The supported output file format is xyz (vertex positions and normal vectors).
A popular viewer for the supported file format is MeshLab (https://www.meshlab.net).
STEPToPoints is based on OpenCASCADE (https://www.opencascade.com).
The program uses cxxops (https://github.com/jarro2783/cxxopts) for parsing the command line.
A popular viewer for the supported file format is MeshLab (<https://www.meshlab.net>).
STEPToPoints is based on OpenCASCADE (<https://www.opencascade.com>).
The program uses cxxops (<https://github.com/jarro2783/cxxopts>) for parsing the command line.

## Requirements
* CMake installation (https://cmake.org)
* OpenCASCADE installation (https://old.opencascade.com/content/latest-release, download needs registration)

For OpenCASCADE if it is not found it will be fetched and built via CMake FetchContent.
* CMake installation (<https://cmake.org>)
* OpenCASCADE installation (<https://old.opencascade.com/content/latest-release>, download needs registration)

For OpenCASCADE if it is not found it will be fetched and built via CMake FetchContent.
For that, you need the following additional libs installed:
* tcl-dev
* tk-dev

* tcl-dev
* tk-dev

On Linux you can install these packages via the package manager, e.g. on ubuntu:

Expand All @@ -25,6 +29,7 @@ sudo apt-get install tcl-dev tk-dev
```

## Usage

Listing the contents (solids) of a STEP file:

```bash
Expand Down Expand Up @@ -74,4 +79,9 @@ Examples are from the `examples` directory.
| ![Image Point-Cloud-Basic-Shapes](examples/basic_shapes/point_cloud.png) |

## Remarks
This code has been tested with an OpenCASCADE 7.5.0 prebuilt binary (`opencascade-7.5.0-vc14-64.exe`) on Windows, as well as OpenCASCADE system packages on openSUSE Linux. With changes in the configuration section in the `CMakeLists.txt` file the build should also work with other OpenCASCADE versions.

This code has been tested with an OpenCASCADE 7.5.0 prebuilt binary (`opencascade-7.5.0-vc14-64.exe`) on Windows, as well as OpenCASCADE system packages on openSUSE Linux. With changes in the configuration section in the `CMakeLists.txt` file the build should also work with other OpenCASCADE versions.

## License

[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fsimogasp%2FSTEPToPoints.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fsimogasp%2FSTEPToPoints?ref=badge_large)
9 changes: 5 additions & 4 deletions build_with_presets.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,20 @@ How to build the project using CMake presets and VCPKG for dependency management
```

3. go back to the project directory and configure the project using CMake presets:

```bash
cd ..
cmake --preset release-preset
cmake --preset debug-preset
```

This will create the build directories `build/<preset-name>`.

4. **Build the project**: You can now build the project using the configured presets.

```bash
cmake --build --preset release-preset
cmake --build --preset debug-preset
```
Note that the dependencies will be installed in a common directory `vcpkg_installed` in the project root (check `VCPKG_INSTALLED_DIR` in the presets) so that it will only build the dependencies in release/debug once.

Note that the dependencies will be installed in a common directory `vcpkg_installed` in the project root (check `VCPKG_INSTALLED_DIR` in the presets) so that it will only build the dependencies in release/debug once.
56 changes: 19 additions & 37 deletions src/STEPToPoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "cxxopts.hpp"
#include "Timer.hpp"
#include "happly.hpp"
#include "solid_index_parser.hpp"
#include <algorithm>
#include <vector>
#include <set>
Expand Down Expand Up @@ -434,34 +435,6 @@ auto sampleShape(const TopoDS_Shape& shape, const double sampling) -> std::vecto
return result;
}

/**
* @brief Attempts to parse a string as a solid index (1-based).
*
* @param sel The string to parse.
* @param maxIndex The maximum valid index (size of the namedSolids vector).
* @return std::optional<size_t> The zero-based index if valid, std::nullopt otherwise.
*/
auto parseSolidIndex(const std::string& sel, std::size_t maxIndex) -> std::optional<std::size_t>
{
try
{
if(const auto index = std::stoul(sel);
index >= 1 && index <= maxIndex)
{
return index - 1; // Convert to zero-based
}
}
catch(const std::invalid_argument&)
{
std::cerr << "Invalid index provided: " << sel << "\n";
}
catch(const std::out_of_range&)
{
std::cerr << "Index out of range: " << sel << "\n";
}
return std::nullopt;
}

/**
* @brief Finds a solid by its full name path.
*
Expand All @@ -486,13 +459,13 @@ auto findSolidByName(const std::vector<NamedSolid>& namedSolids, const std::stri
/**
* @brief Resolves a selection string to a solid.
*
* @param sel The selection string (either a name starting with '/' or a 1-based index).
* @param sel The selection string (either a name starting with '/' or a 1-based index or range).
* @param namedSolids The vector of all available named solids.
* @return const NamedSolid& Reference to the selected solid.
* @return std::vector<std::reference_wrapper<const NamedSolid>> List of references to the selected solid(s).
* @throws std::invalid_argument If the selection cannot be resolved.
*/
auto resolveSolidSelection(const std::string& sel, const std::vector<NamedSolid>& namedSolids)
-> const NamedSolid&
-> std::vector<std::reference_wrapper<const NamedSolid>>
{
if(sel.empty())
{
Expand All @@ -504,15 +477,21 @@ auto resolveSolidSelection(const std::string& sel, const std::vector<NamedSolid>
{
if(const auto found = findSolidByName(namedSolids, sel))
{
return found->get();
return {found->get()};
}
throw std::invalid_argument{std::format("Could not find solid with name '{}'", sel)};
}

// Try to parse as index
if(const auto index = parseSolidIndex(sel, namedSolids.size()); index.has_value())
if(const auto indices = parseSolidIndex(sel, namedSolids.size()); indices.has_value())
{
return namedSolids[index.value()];
std::vector<std::reference_wrapper<const NamedSolid>> result;
result.reserve(indices->size());
for(const auto idx : indices.value())
{
result.emplace_back(std::cref(namedSolids[idx]));
}
return result;
}

throw std::invalid_argument{std::format("Invalid selection: '{}' (not a valid name or index)", sel)};
Expand Down Expand Up @@ -550,8 +529,11 @@ auto buildCompoundFromSelections(const std::vector<NamedSolid>& namedSolids,
if(!sel.empty())
{
const auto& selectedSolid = resolveSolidSelection(sel, namedSolids);
std::cout << "Adding solid: " << selectedSolid.name << "\n";
builder.Add(compound, selectedSolid.solid);
for(const auto& solid : selectedSolid)
{
std::cout << "Adding solid: " << solid.get().name << "\n";
builder.Add(compound, solid.get().solid);
}
}
}
}
Expand Down Expand Up @@ -587,7 +569,7 @@ int main(int argc, char* argv[])
cxxopts::value<std::string>())
("c,content", "List content (solids)")
("s,select",
"Select solids by name or index (comma seperated list, index starts with 1)",
"Select solids by name or index (comma seperated list, index starts with 1) or range index (e.g. 3-7)",
cxxopts::value<std::vector<std::string>>())
("g,sampling", "Sampling distance", cxxopts::value<double>())
("b,binary", "Write binary file (only for .ply files)", cxxopts::value<bool>()->default_value("false"))
Expand Down
Loading