56 Commits

Author SHA1 Message Date
eeeck dabb15b8e4 ci: change workflow trigger behaviour 2025-11-20 14:46:21 +01:00
eeeck 4b9e04ff75 test: refactor test cases 2025-11-20 09:58:48 +01:00
eeeck 80707a055d ci: avoid running this workflow when doing unrelated changes to other
workflows
2025-11-14 16:56:33 +01:00
eeeck 050b022ab1 Merge pull request #8 from erickahmed/dependabot/github_actions/actions/checkout-5
build(deps): bump actions/checkout from 4 to 5
2025-11-14 16:56:12 +01:00
eeeck a8bc5d2038 Merge pull request #7 from erickahmed/dependabot/github_actions/actions/stale-10
build(deps): bump actions/stale from 5 to 10
2025-11-14 16:55:30 +01:00
eeeck c224933459 Merge branch 'master' into dependabot/github_actions/actions/checkout-5
Signed-off-by: Erick <engineering@erickahmed.com>
2025-11-14 15:47:41 +01:00
eeeck e769c61892 Merge branch 'master' into dependabot/github_actions/actions/stale-10
Signed-off-by: Erick <engineering@erickahmed.com>
2025-11-14 15:47:07 +01:00
eeeck e1cb61800d general: fix Cargo.toml directory
Signed-off-by: Erick <engineering@erickahmed.com>
2025-11-14 15:40:04 +01:00
eeeck 365a120eba general: add Dependabot settings 2025-11-14 15:24:00 +01:00
eeeck a7166af45b general: add stale.yml workflow for managing inactive issues and PRs
This workflow automatically marks issues and pull requests as stale
after a period of inactivity and can close them if no activity occurs.

Signed-off-by: Erick <engineering@erickahmed.com>
2025-11-14 15:23:42 +01:00
dependabot[bot] 226b8b3786 build(deps): bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-14 14:20:49 +00:00
dependabot[bot] 1972c6dcf2 build(deps): bump actions/stale from 5 to 10
Bumps [actions/stale](https://github.com/actions/stale) from 5 to 10.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v5...v10)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-version: '10'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-14 14:20:46 +00:00
eeeck a4c727de85 general: add Dependabot settings 2025-11-14 15:20:10 +01:00
eeeck 8ac53966e4 Add stale.yml workflow for managing inactive issues and PRs
This workflow automatically marks issues and pull requests as stale after a period of inactivity and can close them if no activity occurs.

Signed-off-by: Erick <engineering@erickahmed.com>
2025-11-14 14:36:12 +01:00
eeeck a5f882a462 Merge pull request #3 from erickahmed/dev-occt
Implementation of safe Rust API for point handling
2025-11-13 22:09:32 +01:00
eeeck 191da020e4 ci: fix action error with pulling OCCT image 2025-11-13 20:55:41 +01:00
eeeck 4a1ff99e06 ci: improve CI/CD automation pipeline 2025-11-13 13:41:37 +01:00
eeeck 87ade68d73 test: fix undefined behaviour 2025-11-13 11:01:53 +01:00
eeeck 1230b0a8ab test: fix typo 2025-11-13 10:55:53 +01:00
eeeck ada549c4d4 test: fix error: 'p' was not declared in this scope 2025-11-13 10:55:22 +01:00
eeeck 16eabc6d23 general: update licensing information 2025-11-13 10:45:22 +01:00
eeeck 1dfa014e05 ci: unify OCCT and FFI in single workflow 2025-11-13 10:06:15 +01:00
eeeck 33407980b5 ci: remove login steps from CI pipeline 2025-11-13 00:06:32 +01:00
eeeck 6ee055c38b test: separate tests into debug and API level tests 2025-11-13 00:03:19 +01:00
eeeck 2548203b3e test: move basic test to library file 2025-11-12 23:43:17 +01:00
eeeck 3c66a49bb0 ffi: implement explicit point deletion 2025-11-12 23:40:13 +01:00
eeeck 29e7e59ffb ci: add CI pipeline for FFI code 2025-11-12 23:21:20 +01:00
eeeck 5227384e0c general: rename wrappers to ffi for coherency with rest of repo 2025-11-12 23:06:45 +01:00
eeeck 1630915010 ci: add FFI CI pipeline 2025-11-12 23:04:19 +01:00
eeeck 958d7965ce ci: change tag name 2025-11-12 23:03:56 +01:00
eeeck 8f87452691 ci: move to root 2025-11-12 23:03:00 +01:00
eeeck 5b37b34422 general: add comment for FFI and wrapper phases 2025-11-12 17:21:41 +01:00
eeeck 33366ebb51 ci: add build instructions 2025-11-12 16:46:22 +01:00
eeeck 4e8f903b70 general: add header 2025-11-12 16:46:10 +01:00
eeeck 708bb84df7 ffi: add build dependency 2025-11-12 16:45:57 +01:00
eeeck f84157756e general: add lib 2025-11-12 16:19:55 +01:00
eeeck 3192e81443 test: move to tests/ because of issues with cargo test 2025-11-12 16:19:37 +01:00
eeeck ced3c4d22b ffi: create lib.rs 2025-11-12 16:18:42 +01:00
eeeck 13c8dae325 general: add header 2025-11-12 16:17:45 +01:00
eeeck 022bc4e9e2 ffi: export pub API 2025-11-12 16:17:10 +01:00
eeeck 612ee5da6e test: rename to tests/ for Rust integration test compatibility 2025-11-12 15:48:24 +01:00
eeeck f96e85416e ffi: add debug method 2025-11-12 15:43:48 +01:00
eeeck c1488507f2 ffi: wrap unsafe FFI functions in safe wrappers 2025-11-12 15:43:15 +01:00
eeeck 2802f7659f ffi: make C function private 2025-11-12 15:13:48 +01:00
eeeck 422f08835e general: change point variables nomenclature to same standard 2025-11-12 09:58:44 +01:00
eeeck 6aa5c32430 test: improve precision in benchmarking operations 2025-11-12 09:56:45 +01:00
eeeck e9cb80797d ffi: add Rust bindings for point.hpp 2025-11-12 01:12:02 +01:00
eeeck ddc2fb4172 occt: update unit tests with new library 2025-11-12 00:23:20 +01:00
eeeck 29a3ff419d occt: refactor library for Rust FFI compatibility 2025-11-12 00:23:05 +01:00
eeeck 2b42634d88 occt: improve delete point with actual clearing from memory instead of
nullification
2025-11-11 23:32:40 +01:00
eeeck 0092d48a30 general: improve README readability 2025-11-11 11:37:15 +01:00
eeeck 94b6085849 test: add benchmarking for point library 2025-11-11 11:34:36 +01:00
eeeck 60d51d91f5 test: add unit tests for point deletion 2025-11-11 10:53:14 +01:00
eeeck 7a903fecab occt: add point deletion function 2025-11-11 10:45:42 +01:00
eeeck af9f65ab1d ci: trigger action on dev-occt branch patterns 2025-11-10 20:17:13 +01:00
eeeck eaf730dc73 occt: bump OCCT version to 7.9.2 2025-11-10 20:16:43 +01:00
20 changed files with 584 additions and 149 deletions
+23
View File
@@ -0,0 +1,23 @@
version: 2
updates:
- package-ecosystem: "docker"
directory: "/ci/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "docker"
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "rust"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "github-actions"
+103
View File
@@ -0,0 +1,103 @@
name: OCCT FFI Unit Tests
on:
push:
branches: [master, dev**]
paths:
- "src/occt/**"
- "src/ffi/**"
- "tests/occt/**"
- "tests/*.rs"
- "ci/occt.Dockerfile"
- "ci/scripts/occt.sh"
- "ci/ffi.Dockerfile"
- "ci/scripts/ffi.sh"
- ".github/workflows/occt-ffi-unit-test.yml"
- "./CMakeLists.txt"
- "Cargo.toml"
- "Cargo.lock"
- "build.rs"
pull_request:
branches: [master, dev-occt, dev-ffi]
paths:
- "src/occt/**"
- "src/ffi/**"
- "tests/occt/**"
- "tests/*.rs"
- "ci/occt.Dockerfile"
- "ci/scripts/occt.sh"
- "ci/ffi.Dockerfile"
- "ci/scripts/ffi.sh"
- ".github/workflows/occt-ffi-unit-test.yml"
- "./CMakeLists.txt"
- "Cargo.toml"
- "Cargo.lock"
- "build.rs"
jobs:
build-occt:
name: OCCT
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Make scripts executable
run: chmod +x ./ci/scripts/occt.sh
- name: Build OCCT and run tests in Docker
run: ./ci/scripts/occt.sh --unit-testing
- name: Tag and push OCCT image
if: github.event_name != 'pull_request'
run: |
docker tag occt-build:latest ghcr.io/${{ github.repository }}/occt-build:latest
docker push ghcr.io/${{ github.repository }}/occt-build:latest
build-ffi:
name: FFI
runs-on: ubuntu-24.04
needs: build-occt
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull and tag OCCT image
run: |
docker pull ghcr.io/${{ github.repository }}/occt-build:latest
docker tag ghcr.io/${{ github.repository }}/occt-build:latest occt-build:latest
- name: Make scripts executable
run: chmod +x ./ci/scripts/ffi.sh
- name: Build and test FFI in Docker
run: ./ci/scripts/ffi.sh --tests
- name: Tag and push FFI image
if: github.event_name != 'pull_request'
run: |
docker tag ffi-build:latest ghcr.io/${{ github.repository }}/ffi-build:latest
docker push ghcr.io/${{ github.repository }}/ffi-build:latest
-37
View File
@@ -1,37 +0,0 @@
name: OCCT CI
on:
push:
branches: [master, dev-occt]
paths:
- "src/occt/**"
- "src/ffi/**"
- "test/occt/**"
- "test/ffi/**"
- "ci/**"
- ".github/**"
- "./CMakeLists.txt"
pull_request:
branches: [master, dev-occt]
paths:
- "src/occt/**"
- "src/ffi/**"
- "test/occt/**"
- "test/ffi/**"
- "ci/**"
- ".github/**"
- "./CMakeLists.txt"
jobs:
build-wrappers:
name: Build OCCT wrappers
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Make script executable
run: chmod +x ./ci/scripts/occt.sh
- name: Build OCCT and run tests
run: ./ci/scripts/occt.sh --unit-testing
+27
View File
@@ -0,0 +1,27 @@
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
#
# You can adjust the behavior by modifying this file.
# For more information, see:
# https://github.com/actions/stale
name: Mark stale issues and pull requests
on:
schedule:
- cron: '25 23 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v10
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Stale issue message'
stale-pr-message: 'Stale pull request message'
stale-issue-label: 'no-issue-activity'
stale-pr-label: 'no-pr-activity'
+8 -1
View File
@@ -1,7 +1,14 @@
[package]
name = "arc-core"
version = "0.0.1"
version = "0.0.2"
edition = "2024"
license = "AGPL-3.0-or-later"
[lib]
name = "arc_core"
path = "src/lib.rs"
[dependencies]
[build-dependencies]
cc = "1.0"
+4 -6
View File
@@ -13,11 +13,11 @@ by the Free Software Foundation, either version 3 of the License, or
## License
This project is licensed under the GNU Affero General Public License v3.0 - see [LICENSE.txt](LICENSE.txt) file for details.
This project is licensed under the [GNU Affero General Public License v3.0](https://spdx.org/licenses/AGPL-3.0-or-later.html) - see [LICENSE.txt](LICENSE.txt) file for details.
## Third-Party Dependencies
This software uses **Open CASCADE Technology** (OCCT), which is licensed under the GNU LGPL version 2.1 with the following exception:
This software makes use of facilities provided by **Open CASCADE Technology** (OCCT), which is licensed under the [GNU Lesser General Public License v2.1](https://spdx.org/licenses/LGPL-2.1-only.html) with the following exception:
### Open CASCADE Exception (version 1.0)
> The object code (i.e. not a source) form of a "work that uses the Library"
@@ -30,8 +30,6 @@ This software uses **Open CASCADE Technology** (OCCT), which is licensed under t
> of or is based on facilities provided by the Open CASCADE Technology software.
### Source Code Availability
The source code for Open CASCADE version 7.4.0 can be obtained from:
The source code for Open CASCADE version 7.9.2 can be obtained from:
- Repository: https://github.com/Open-Cascade-SAS/OCCT.git
- Commit: `fd47711d682be943f0e0a13d1fb54911b0499c31`
**This software makes use of facilities provided by Open CASCADE Technology.**
- Commit: `c5f20409c52bf8f658314d205a0e5d6f0be0969c`
+13
View File
@@ -0,0 +1,13 @@
/*
SPDX-License-Identifier: AGPL-3.0-or-later
Copyright (C) 2025 Erick Ahmed
*/
fn main() {
println!("cargo:rustc-link-search=/usr/local/lib");
println!("cargo:rustc-link-search=/app/build");
println!("cargo:rustc-link-lib=occt");
println!("cargo:rerun-if-changed=src/wrappers/");
}
+41
View File
@@ -0,0 +1,41 @@
FROM occt-build:latest AS occt-local
FROM ghcr.io/erickahmed/arc-core/occt-build:latest AS occt-ghcr
FROM occt-local AS occt-selected
FROM occt-selected AS ffi-base
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y curl build-essential
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
WORKDIR /app
COPY . .
ENV LD_LIBRARY_PATH=/usr/local/lib:/app/build
ENV RUSTFLAGS="-L /usr/local/lib -L /app/build"
RUN cargo build
FROM ubuntu:24.04 AS ffi-unit-testing
RUN apt-get update && apt-get install -y \
build-essential cmake git curl \
libfreetype-dev \
xvfb \
&& rm -rf /var/lib/apt/lists/*
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
COPY --from=occt-selected /usr/local /usr/local
COPY --from=occt-selected /app/build /app/occt-build
COPY --from=ffi-base /app /app
WORKDIR /app
ENV LD_LIBRARY_PATH=/usr/local/lib:/app/occt-build
ENV RUSTFLAGS="-L /usr/local/lib -L /app/occt-build"
CMD ["cargo", "test", "--verbose", "--", "--nocapture"]
+12 -11
View File
@@ -1,5 +1,9 @@
# Builds Open CASCADE Technology (OCCT) v7.9.2
# Licensed under LGPL-2.1-only with OCCT Exception v1.0
# Source: https://github.com/Open-Cascade-SAS/OCCT/tree/c5f20409c52bf8f658314d205a0e5d6f0be0969c
# Base builder
FROM ubuntu:24.04 AS base
FROM ubuntu:24.04 AS occt-base
ENV DEBIAN_FRONTEND=noninteractive
@@ -15,7 +19,7 @@ RUN apt-get update && apt-get install -y \
RUN git clone https://github.com/Open-Cascade-SAS/OCCT.git opencascade
WORKDIR /opencascade
RUN rm -rf /opencascade/build && mkdir build
RUN git checkout V7_4_0 -b dev-branch
RUN git checkout c5f20409c52bf8f658314d205a0e5d6f0be0969c #Tag V7_9_2
RUN mkdir -p build
WORKDIR /opencascade/build
@@ -84,10 +88,9 @@ COPY src/occt/ ./src
RUN cmake -S src -B build -DCMAKE_PREFIX_PATH=/usr/local
RUN cmake --build build
COPY test/occt/ ./test
COPY tests/occt/ ./test
# Unit test runner stage
FROM ubuntu:24.04 AS unit-testing
FROM ubuntu:24.04 AS occt-unit-testing
ENV DEBIAN_FRONTEND=noninteractive
@@ -98,12 +101,10 @@ RUN apt-get update && apt-get install -y \
xvfb \
&& rm -rf /var/lib/apt/lists/*
COPY --from=base /usr/local /usr/local
COPY --from=base /app/build /app/build
COPY --from=base /app/src /app/src
COPY --from=base /app/test /app/test
COPY --from=base /usr/local/include /usr/local/include
COPY --from=base /usr/local/lib /usr/local/lib
COPY --from=occt-base /usr/local /usr/local
COPY --from=occt-base /app/build /app/build
COPY --from=occt-base /app/src /app/src
COPY --from=occt-base /app/test /app/test
WORKDIR /app
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
set -euo pipefail
IMAGE_NAME="ffi-build:latest"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_PATH="$(cd "$SCRIPT_DIR/../../" && pwd)"
DOCKERFILE_PATH="$SCRIPT_DIR/../ffi.Dockerfile"
RUN_UNIT_TESTS=false
if [[ "${1-}" == "--tests" ]]; then
RUN_UNIT_TESTS=true
fi
# Try to understand where tf are we
if [[ -n "${GITHUB_REPOSITORY-}" ]]; then
BASE_IMAGE="ghcr.io/$GITHUB_REPOSITORY/occt-build:latest"
echo "Using GHCR image: $BASE_IMAGE"
docker pull "$BASE_IMAGE" || true
else
BASE_IMAGE="occt-build:latest"
echo "Using local image: $BASE_IMAGE"
fi
echo "Building base image..."
docker build --target ffi-base -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" \
--build-arg BASE_IMAGE="$BASE_IMAGE" \
"$PROJECT_PATH"
if [ "$RUN_UNIT_TESTS" = true ]; then
echo "Building unit-testing stage..."
docker build --target ffi-unit-testing -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" \
--build-arg BASE_IMAGE="$BASE_IMAGE" \
"$PROJECT_PATH"
echo "Running tests..."
docker run --rm "$IMAGE_NAME" cargo test --verbose
fi
+3 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
IMAGE_NAME="occt-builder:latest"
IMAGE_NAME="occt-build:latest"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_PATH="$(cd "$SCRIPT_DIR/../../" && pwd)"
DOCKERFILE_PATH="$SCRIPT_DIR/../occt.Dockerfile"
@@ -12,11 +12,11 @@ if [[ "${1-}" == "--unit-testing" ]]; then
fi
echo "Building base image..."
docker build --target base -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" "$PROJECT_PATH"
docker build --target occt-base -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" "$PROJECT_PATH"
if [ "$RUN_UNIT_TESTS" = true ]; then
echo "Building unit-testing stage..."
docker build --target unit-testing -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" "$PROJECT_PATH"
docker build --target occt-unit-testing -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" "$PROJECT_PATH"
echo "Running unit tests..."
docker run --rm "$IMAGE_NAME" /app/build/unit-tests/tests
+109
View File
@@ -0,0 +1,109 @@
/*
SPDX-License-Identifier: AGPL-3.0-or-later
Copyright (C) 2025 Erick Ahmed
*/
mod ffi_point {
//FFI
use std::os::raw::c_double;
#[repr(C)]
pub(crate) struct PointShape {
_private: [u8; 0],
}
unsafe extern "C" {
fn make_point(x: c_double, y: c_double, z: c_double) -> *mut PointShape;
fn coord_point(
shape: *const PointShape,
x: *mut c_double,
y: *mut c_double,
z: *mut c_double,
);
fn delete_point(shape: *mut PointShape);
}
// Safe wrapper
pub struct Point {
ptr: *mut PointShape,
}
impl Point {
pub fn new(x: f64, y: f64, z: f64) -> Result<Self, &'static str> {
unsafe {
let ptr = make_point(x, y, z);
if ptr.is_null() {
Err("Error: null pointer returned")
} else {
Ok(Point { ptr })
}
}
}
pub fn coordinates(&self) -> (f64, f64, f64) {
unsafe {
let mut x = 0.0;
let mut y = 0.0;
let mut z = 0.0;
coord_point(self.ptr, &mut x, &mut y, &mut z);
(x, y, z)
}
}
pub fn delete(mut self) -> Result<(), &'static str> {
unsafe {
if self.ptr.is_null() {
return Err("Error: Attempted to delete null pointer");
}
delete_point(self.ptr);
self.ptr = std::ptr::null_mut();
std::mem::forget(self);
}
Ok(())
}
}
impl Drop for Point {
fn drop(&mut self) {
unsafe {
delete_point(self.ptr);
}
}
}
// TODO: check if i can mark as thread-safe
impl std::fmt::Debug for Point {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (x, y, z) = self.coordinates();
write!(f, "Point({}, {}, {})", x, y, z)
}
}
}
pub use ffi_point::Point;
// FFI API tests
#[cfg(test)]
mod tests {
use super::*;
use std::os::raw::c_double;
#[test]
fn types_compatibility() {
use std::mem;
assert_eq!(mem::size_of::<ffi_point::PointShape>(), 0);
assert_eq!(mem::size_of::<c_double>(), mem::size_of::<f64>());
assert_eq!(mem::align_of::<c_double>(), mem::align_of::<f64>());
}
#[test]
fn basic_lifecycle() {
let p = Point::new(1.0, 2.0, 3.0).expect("FFI creation failed");
assert_eq!(p.coordinates(), (1.0, 2.0, 3.0));
}
}
+8
View File
@@ -0,0 +1,8 @@
/*
SPDX-License-Identifier: AGPL-3.0-or-later
Copyright (C) 2025 Erick Ahmed
*/
pub mod ffi {
pub mod point;
}
+6
View File
@@ -0,0 +1,6 @@
/*
SPDX-License-Identifier: AGPL-3.0-or-later
Copyright (C) 2025 Erick Ahmed
*/
fn main() {}
+17 -3
View File
@@ -3,9 +3,23 @@
Copyright (C) 2025 Erick Ahmed
*/
#ifndef POINT_HPP
#define POINT_HPP
#pragma once
#include <gp_Pnt.hxx>
#ifdef __cplusplus
extern "C" {
#endif
TopoDS_Shape make_point(double x, double y, double z);
void coord_point(const TopoDS_Shape& shape, double* x, double* y, double* z);
typedef struct point_shape point_shape_t;
point_shape_t* make_point(double x, double y, double z);
void coord_point(const point_shape_t* shape, double* x, double* y, double* z);
void delete_point(point_shape_t* shape);
#ifdef __cplusplus
}
#endif
#endif
+23 -10
View File
@@ -10,16 +10,29 @@
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <gp_Pnt.hxx>
TopoDS_Shape make_point(double x, double y, double z) {
gp_Pnt point(x, y, z);
return BRepBuilderAPI_MakeVertex(point).Vertex();
}
struct point_shape_t {
TopoDS_Shape shape;
};
void coord_point(const TopoDS_Shape& shape, double* x, double* y, double* z) {
TopoDS_Vertex vertex = TopoDS::Vertex(shape);
gp_Pnt point = BRep_Tool::Pnt(vertex);
extern "C" {
point_shape_t* make_point(double x, double y, double z) {
point_shape_t* result = new point_shape_t();
gp_Pnt point(x, y, z);
result->shape = BRepBuilderAPI_MakeVertex(point).Vertex();
return result;
}
*x = point.X();
*y = point.Y();
*z = point.Z();
void coord_point(const point_shape_t* shape, double* x, double* y, double* z) {
if (!shape || !x || !y || !z) return;
TopoDS_Vertex vertex = TopoDS::Vertex(shape->shape);
gp_Pnt point = BRep_Tool::Pnt(vertex);
*x = point.X();
*y = point.Y();
*z = point.Z();
}
void delete_point(point_shape_t* shape) {
delete shape;
}
}
-78
View File
@@ -1,78 +0,0 @@
/*
SPDX-License-Identifier: AGPL-3.0-or-later
Copyright (C) 2025 Erick Ahmed
*/
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <TopoDS_Shape.hxx>
#include "point.hpp"
TEST_CASE("make_point and coord_point behavior", "[make_point]") {
auto check_coords = [](const TopoDS_Shape& p, const double expected[3]) {
double x, y, z;
coord_point(p, &x, &y, &z);
REQUIRE(x == Catch::Approx(expected[0]));
REQUIRE(y == Catch::Approx(expected[1]));
REQUIRE(z == Catch::Approx(expected[2]));
};
SECTION("Accept integer coordinates") {
double coords[3] = {3, -7, 2};
TopoDS_Shape p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords);
}
SECTION("Accept float coordinates") {
double coords[3] = {3.2352, 7.124662, -2.5};
TopoDS_Shape p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords);
}
SECTION("XYZ Origin") {
double coords[3] = {0, 0, 0};
TopoDS_Shape p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords);
}
SECTION("Large magnitude values") {
double coords[3] = {6.5186415e7, 9.48156654e8, -6.516515e6};
TopoDS_Shape p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords);
}
SECTION("Multiple points") {
const double points[5][3] = {
{3, 7, 2},
{3, 7, 2},
{-5, 3.7, 8},
{-3, -7, -2},
{0, 0, 1}
};
for (const auto& c : points) {
TopoDS_Shape p = make_point(c[0], c[1], c[2]);
check_coords(p, c);
}
}
SECTION("Extreme double limits") {
double coords[3] = {std::numeric_limits<double>::max(),
std::numeric_limits<double>::lowest(),
std::numeric_limits<double>::min()};
TopoDS_Shape p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords);
}
SECTION("NaN and Infinity") {
double coords[3] = {std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::infinity(),
-std::numeric_limits<double>::infinity()};
TopoDS_Shape p = make_point(coords[0], coords[1], coords[2]);
double x, y, z;
coord_point(p, &x, &y, &z);
REQUIRE((std::isnan(x) || std::isinf(x)));
REQUIRE((std::isnan(y) || std::isinf(y)));
REQUIRE((std::isnan(z) || std::isinf(z)));
}
}
+87
View File
@@ -0,0 +1,87 @@
/*
SPDX-License-Identifier: AGPL-3.0-or-later
Copyright (C) 2025 Erick Ahmed
*/
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include "point.hpp"
TEST_CASE("Point lifecycle and coordinate operations", "[point]") {
auto check_coords = [](const point_shape_t* p, const double expected[3]) {
double x, y, z;
coord_point(p, &x, &y, &z);
REQUIRE(x == Catch::Approx(expected[0]));
REQUIRE(y == Catch::Approx(expected[1]));
REQUIRE(z == Catch::Approx(expected[2]));
};
SECTION("Basic creation and coordinate retrieval") {
double coords[3] = {3.2352, 7.124662, -2.5};
point_shape_t* p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords);
delete_point(p);
}
SECTION("Extreme double values") {
double coords[3] = {
std::numeric_limits<double>::max(),
std::numeric_limits<double>::lowest(),
std::numeric_limits<double>::min()
};
point_shape_t* p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords);
delete_point(p);
}
SECTION("Special floating-point values") {
double coords[3] = {
std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::infinity(),
-std::numeric_limits<double>::infinity()
};
point_shape_t* p = make_point(coords[0], coords[1], coords[2]);
double x, y, z;
coord_point(p, &x, &y, &z);
REQUIRE(std::isnan(x));
REQUIRE(std::isinf(y));
REQUIRE(std::isinf(z));
REQUIRE(y > 0);
REQUIRE(z < 0);
delete_point(p);
}
SECTION("Multiple independent instances") {
const double points[3][3] = {
{3, 7, 2},
{-5, 3.7, 8},
{-3, -7, -2}
};
for (const auto& coords : points) {
point_shape_t* p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords);
delete_point(p);
}
}
SECTION("Null pointer safety") {
point_shape_t* null_p = nullptr;
delete_point(null_p);
double x, y, z;
coord_point(null_p, &x, &y, &z);
}
}
TEST_CASE("Point memory management", "[point]") {
SECTION("Rapid allocation/deallocation cycle") {
for (int i = 0; i < 50; ++i) {
point_shape_t* p = make_point(1.0, 2.0, 3.0);
REQUIRE(p != nullptr);
delete_point(p);
}
}
}
+63
View File
@@ -0,0 +1,63 @@
/*
SPDX-License-Identifier: AGPL-3.0-or-later
Copyright (C) 2025 Erick Ahmed
*/
use arc_core::ffi::point::Point;
// Safe wrapper tests
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn creation_and_query() {
let p = Point::new(1.0, 2.0, -3.0).expect("Safe creation failed");
assert_eq!(p.coordinates(), (1.0, 2.0, -3.0));
}
#[test]
fn manual_deletion() {
let p = Point::new(1.0, 2.0, 3.0).expect("Creation failed");
p.delete().expect("Manual deletion failed");
}
#[test]
fn automatic_cleanup() {
let coords = {
let p = Point::new(7.0, 8.0, 9.0).expect("Creation failed");
p.coordinates()
};
assert_eq!(coords, (7.0, 8.0, 9.0));
}
#[test]
fn multiple_instances() {
let p1 = Point::new(1.0, 1.0, 1.0).expect("Creation failed");
let p2 = Point::new(2.0, 2.0, 2.0).expect("Creation failed");
assert_eq!(p1.coordinates(), (1.0, 1.0, 1.0));
assert_eq!(p2.coordinates(), (2.0, 2.0, 2.0));
}
#[test]
fn debug_format() {
let p = Point::new(1.1, 2.2, 3.3).expect("Creation failed");
let debug_output = format!("{:?}", p);
assert!(debug_output.starts_with("Point("));
assert!(debug_output.contains("1.1"));
assert!(debug_output.contains("2.2"));
assert!(debug_output.contains("3.3"));
}
#[test]
fn boundary_values() {
let cases = [(0.0, 0.0, 0.0), (f64::MAX, f64::MIN, 0.0)];
for (x, y, z) in cases {
let p = Point::new(x, y, z).expect("Creation with boundary values failed");
assert_eq!(p.coordinates(), (x, y, z));
}
}
}