Skip to content
Merged
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
113 changes: 20 additions & 93 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -1,50 +1,45 @@
# This workflow builds and tests the FixedMathSharp .NET project.
# Documentation: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net

name: .NET CI

on:
# Run the workflow on all branch pushes and pull requests
push:
branches-ignore:
- 'dependabot/**' #avoid duplicates: only run the PR, not the push
- 'gh-pages' #github pages do not trigger all tests
- 'dependabot/**'
- 'gh-pages'
tags-ignore:
- 'v*' #avoid rerun existing commit on release
- 'v*'
pull_request:
branches:
- 'main'

jobs:
build-and-test-linux:
if: |
(github.event_name != 'pull_request' && !github.event.pull_request.head.repo.fork)
|| (github.event_name == 'pull_request' && (github.event.pull_request.head.repo.fork
|| startsWith(github.head_ref, 'dependabot/')))
runs-on: ubuntu-latest
build-and-test:
runs-on: ${{ matrix.os }}

strategy:
matrix:
os: [ubuntu-latest, windows-latest]

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false # Ensure credentials aren't retained
persist-credentials: false

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x

- name: Install Mono (required for .NET Framework tests on Linux)
run: |
sudo apt update
sudo apt install -y mono-complete

- name: Install GitVersion

- name: Setup GitVersion
uses: gittools/actions/gitversion/setup@v3.1.1
with:
versionSpec: '6.0.x'

- name: Execute GitVersion
id: gitversion
uses: gittools/actions/gitversion/execute@v3.1.1

- name: Cache NuGet packages
uses: actions/cache@v3
with:
Expand All @@ -53,82 +48,14 @@ jobs:
restore-keys: |
${{ runner.os }}-nuget-

- name: Determine Version
id: version_step
run: |
chown -R $(whoami) $(pwd)
dotnet-gitversion /output json > version.json
echo "FULL_SEM_VER=$(grep -oP '"FullSemVer":\s*"\K[^"]+' version.json)" >> $GITHUB_ENV
echo "ASSEMBLY_VERSION=$(grep -oP '"AssemblySemFileVer":\s*"\K[^"]+' version.json)" >> $GITHUB_ENV

- name: Restore dependencies
run: dotnet restore

- name: Build Solution
run: |
echo "Version: ${{ env.FULL_SEM_VER }}"
echo "Assembly Version: ${{ env.ASSEMBLY_VERSION }}"
echo "Version: ${{ steps.gitversion.outputs.fullSemVer }}"
echo "Assembly Version: ${{ steps.gitversion.outputs.assemblySemFileVer }}"
dotnet build --configuration Debug --no-restore

- name: Test .NET48
run: |
mono ~/.nuget/packages/xunit.runner.console/2.9.3/tools/net48/xunit.console.exe ${{github.workspace}}/tests/FixedMathSharp.Tests/bin/Debug/net48/FixedMathSharp.Tests.dll

- name: Test .NET8
run: |
dotnet test -f net8 --verbosity normal

build-and-test-windows:
if: |
(github.event_name != 'pull_request' && !github.event.pull_request.head.repo.fork)
|| (github.event_name == 'pull_request' && (github.event.pull_request.head.repo.fork
|| startsWith(github.head_ref, 'dependabot/')))
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x

- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v3.1.1
with:
versionSpec: 6.0.x

- name: Cache NuGet packages
uses: actions/cache@v3
with:
path: ~/.nuget/packages
key: '${{ runner.os }}-nuget-${{ hashFiles(''**/*.csproj'', ''**/*.sln'') }}'
restore-keys: |
${{ runner.os }}-nuget-

- name: Determine Version
id: version_step
shell: pwsh
run: |
chown -R $env:USERNAME $(Get-Location)
dotnet-gitversion /output json | Out-File -FilePath version.json
$json = Get-Content version.json | ConvertFrom-Json
echo "FULL_SEM_VER=$($json.FullSemVer)" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "ASSEMBLY_VERSION=$($json.AssemblySemFileVer)" | Out-File -FilePath $env:GITHUB_ENV -Append

- name: Restore dependencies
run: dotnet restore

- name: Build Solution
run: |
echo "Version: ${{ env.FULL_SEM_VER }}"
echo "Assembly Version: ${{ env.ASSEMBLY_VERSION }}"
dotnet build --configuration Debug --no-restore

- name: Test .NET48 & .NET8
run: |
dotnet --info
dotnet test --verbosity normal
- name: Run Tests
run: dotnet test --configuration Debug --no-build --verbosity normal
38 changes: 38 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# FixedMathSharp agent instructions

## Project intent and architecture
- This repository is a deterministic fixed-point math library centered on `Fixed64` (`src/FixedMathSharp/Numerics/Fixed64.cs`) with Q32.32 representation (`SHIFT_AMOUNT_I = 32` in `src/FixedMathSharp/Core/FixedMath.cs`).
- Most API surface lives in value-type numerics (`Fixed64`, `Vector2d`, `Vector3d`, `FixedQuaternion`, `Fixed3x3`, `Fixed4x4`) plus bounds types (`BoundingBox`, `BoundingSphere`, `BoundingArea`) implementing `IBound`.
- Keep operations deterministic and allocation-light; many methods use `m_rawValue` and `[MethodImpl(MethodImplOptions.AggressiveInlining)]` for hot paths.
- `FixedMath` and `FixedTrigonometry` are the shared algorithm backbones; extension classes in `src/FixedMathSharp/Numerics/Extensions/` are thin forwarding wrappers, not alternate implementations.

## Build and test workflows
- Solution: `FixedMathSharp.sln` with library project and test project.
- Target frameworks are configured in the respective `.csproj` files; `net8.0` is the primary TFM.
- Typical local workflow:
- `dotnet restore`
- `dotnet build --configuration Debug --no-restore`
- `dotnet test --configuration Debug`
- CI detail from `.github/workflows/dotnet.yml`:
- Linux and Windows both run `dotnet test` against the supported TFMs (with `net8.0` as the primary test target).
- Refer to the workflow file for the exact matrix of OS/TFM combinations.
- Packaging/versioning comes from `src/FixedMathSharp/FixedMathSharp.csproj`: GitVersion variables are consumed when present, otherwise version falls back to `0.0.0`.

## Code conventions specific to this repo
- Prefer `Fixed64` constants (`Fixed64.Zero`, `Fixed64.One`, `FixedMath.PI`) over primitive literals in math-heavy code.
- Preserve saturating/guarded semantics in operators and math helpers (for example `Fixed64` add/sub overflow behavior).
- When touching bounds logic, maintain cross-type dispatch shape in `Intersects(IBound)` and shared clamping projection via `IBoundExtensions.ProjectPointWithinBounds`.
- Serialization compatibility is intentional and now uses MemoryPack:
- MemoryPack attributes on serializable structs (for example `[MemoryPackable]`, `[MemoryPackInclude]`) are the source of truth for serialized layouts.
- Tests use MemoryPack-based roundtrips (and `System.Text.Json` where appropriate) instead of legacy `MessagePack`/`BinaryFormatter` serializers.
- `ThreadLocalRandom` is marked `[Obsolete]`; new deterministic RNG work should prefer `DeterministicRandom` and `DeterministicRandom.FromWorldFeature(...)` in `src/FixedMathSharp/Utility/DeterministicRandom.cs`.

## Testing patterns to mirror
- Tests are xUnit (`tests/FixedMathSharp.Tests`). Keep one feature area per test file (e.g., `Vector3d.Tests.cs`, `Bounds/BoundingBox.Tests.cs`).
- Use helper assertions from `tests/FixedMathSharp.Tests/Support/FixedMathTestHelper.cs` for tolerance/range checks rather than ad-hoc epsilon logic.
- For deterministic RNG changes, validate same-seed reproducibility and bounds/argument exceptions like in `DeterministicRandom.Tests.cs`.

## Agent editing guidance
- Keep public API shape stable unless the task explicitly requests API changes.
- Match existing style (regions, XML docs, explicit namespaces, no implicit usings).
- Make focused edits in the relevant numeric/bounds module and update corresponding tests in the parallel test file.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Ideal for simulations, games, and physics engines requiring reliable arithmetic
- **Bounding Shapes:** Includes `IBound` structs `BoundingBox`, `BoundingSphere`, and `BoundingArea` for lightweight spatial calculations.
- **Advanced Math Functions:** Includes trigonometry and common math utilities.
- **Framework Agnostic:** Works with **.NET, Unity, and other game engines**.
- **Full Serialization Support:** Out-of-the-box round-trip serialization via BinaryFormatter (for .NET Framework 4.8+), System.Text.Json (for .NET 8+), and MessagePack across all serializable structs.
- **Full Serialization Support:** Out-of-the-box round-trip serialization via `MemoryPack` across all serializable structs, with `System.Text.Json` constructor support on .NET 8+.

---

Expand Down Expand Up @@ -176,8 +176,8 @@ dotnet test --configuration debug

## 🛠️ Compatibility

- **.NET Framework** 4.7.2+
- **.NET Core / .NET** 6+
- **.NET Standard** 2.1
- **.NET** 8
- **Unity 2020+** (via [FixedMathSharp-Unity](https://github.com/mrdav30/FixedMathSharp-Unity))
- **Cross-Platform Support** (Windows, Linux, macOS)

Expand Down
38 changes: 19 additions & 19 deletions src/FixedMathSharp/Bounds/BoundingArea.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using MessagePack;
using MemoryPack;
using System;
using System.Runtime.CompilerServices;

Expand All @@ -18,21 +18,21 @@ namespace FixedMathSharp
/// </remarks>

[Serializable]
[MessagePackObject]
public struct BoundingArea : IBound, IEquatable<BoundingArea>
[MemoryPackable]
public partial struct BoundingArea : IBound, IEquatable<BoundingArea>
{
#region Fields

/// <summary>
/// One of the corner points of the bounding area.
/// </summary>
[Key(0)]
[MemoryPackOrder(0)]
public Vector3d Corner1;

/// <summary>
/// The opposite corner point of the bounding area.
/// </summary>
[Key(1)]
[MemoryPackOrder(1)]
public Vector3d Corner2;

#endregion
Expand Down Expand Up @@ -68,7 +68,7 @@ public BoundingArea(Vector3d corner1, Vector3d corner2)
/// <summary>
/// The minimum corner of the bounding box.
/// </summary>
[IgnoreMember]
[MemoryPackIgnore]
public Vector3d Min
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -78,49 +78,49 @@ public Vector3d Min
/// <summary>
/// The maximum corner of the bounding box.
/// </summary>
[IgnoreMember]
[MemoryPackIgnore]
public Vector3d Max
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(MaxX, MaxY, MaxZ);
}

[IgnoreMember]
[MemoryPackIgnore]
public Fixed64 MinX
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Corner1.x < Corner2.x ? Corner1.x : Corner2.x;
}

[IgnoreMember]
[MemoryPackIgnore]
public Fixed64 MaxX
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Corner1.x > Corner2.x ? Corner1.x : Corner2.x;
}

[IgnoreMember]
[MemoryPackIgnore]
public Fixed64 MinY
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Corner1.y < Corner2.y ? Corner1.y : Corner2.y;
}

[IgnoreMember]
[MemoryPackIgnore]
public Fixed64 MaxY
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Corner1.y > Corner2.y ? Corner1.y : Corner2.y;
}

[IgnoreMember]
[MemoryPackIgnore]
public Fixed64 MinZ
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Corner1.z < Corner2.z ? Corner1.z : Corner2.z;
}

[IgnoreMember]
[MemoryPackIgnore]
public Fixed64 MaxZ
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -130,7 +130,7 @@ public Fixed64 MaxZ
/// <summary>
/// Calculates the width (X-axis) of the bounding area.
/// </summary>
[IgnoreMember]
[MemoryPackIgnore]
public Fixed64 Width
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -140,7 +140,7 @@ public Fixed64 Width
/// <summary>
/// Calculates the height (Y-axis) of the bounding area.
/// </summary>
[IgnoreMember]
[MemoryPackIgnore]
public Fixed64 Height
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -150,7 +150,7 @@ public Fixed64 Height
/// <summary>
/// Calculates the depth (Z-axis) of the bounding area.
/// </summary>
[IgnoreMember]
[MemoryPackIgnore]
public Fixed64 Depth
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -163,8 +163,8 @@ public Fixed64 Depth
public bool Contains(Vector3d point)
{
// Check if the point is within the bounds of the area (including boundaries)
return point.x >= MinX && point.x <= MaxX
&& point.y >= MinY && point.y <= MaxY
return point.x >= MinX && point.x <= MaxX
&& point.y >= MinY && point.y <= MaxY
&& point.z >= MinZ && point.z <= MaxZ;
}

Expand Down Expand Up @@ -208,7 +208,7 @@ public bool Intersects(IBound other)
return Vector3d.SqrDistance(sphere.Center, this.ProjectPointWithinBounds(sphere.Center)) <= sphere.SqrRadius;

default: return false; // Default case for unknown or unsupported types
};
}
}

/// <summary>
Expand Down
Loading
Loading