Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Port from Rust implementation #2

Draft
wants to merge 29 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d5c44fb
Port the angled and rough equality modules
chances Jan 11, 2021
cb130cd
Disable license year task
chances Jan 11, 2021
3be397e
Fix License heading level
chances Jan 11, 2021
4ab5f1b
Add unit tests to the fuzzy equality comparators
chances Jan 11, 2021
8440a1b
Merge branch 'master' into port-from-rust
chances Jan 11, 2021
5268287
Add unit tests to angles module
chances Jan 11, 2021
223d186
Extract `up`, `down`, `left`, and `right` constants for unit tests
chances Jan 11, 2021
5c1a0d4
Port conversion module
chances Jan 11, 2021
d889b91
Clean junk coverage files
chances Jan 11, 2021
b987d6e
Add codecov badge
chances Jan 11, 2021
fe2b15a
Add Usage section
chances Jan 11, 2021
ffaffc6
Port line and arc segments module
chances Jan 14, 2021
94a1544
Clean the lint 💅️
chances Jan 14, 2021
2a15fc6
Refactor usages of `approxEqual` for `isClose`
chances Mar 27, 2021
3125a1d
Ignore .envrc
chances Mar 27, 2021
9cbd564
Port `ArcOrLineSegment` into an algebraic union
chances Mar 27, 2021
df68a2f
Format document
chances Mar 27, 2021
3a1e8fa
Add minor arc builder functions
chances Mar 27, 2021
28890a9
Add `ArcSegment` unit tests
chances Mar 27, 2021
ddfa759
Fix `norm` function
chances Mar 27, 2021
83de651
Add colinear apex and major arcs unit tests
chances Mar 27, 2021
732bf73
Refactor Makefile
chances Feb 14, 2024
3533fa7
Update copyright year and author contacts
chances Feb 14, 2024
c98daaa
Refactor coverage artifact destination
chances Feb 14, 2024
5a04d16
Refactor lint task
chances Feb 15, 2024
391d45c
Add a static library configuration
chances Feb 15, 2024
60f69c5
Update copyright and public imports
chances Feb 15, 2024
801e96e
Fix tolerance typo, Refactor arc subdivision lambda
chances Sep 18, 2024
e740ea7
Fix typos
chances Sep 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
restore-keys: |
${{ runner.os }}-dub-
- name: Lint
run: dub lint
run: dub run dscanner -- --styleCheck source
- name: Test
run: dub test --coverage
- name: Upload Coverage to Codecov
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.envrc

# Compiled Object files
*.o
*.obj
Expand All @@ -12,6 +14,7 @@
*.lib

# Executables
bin/
*.exe

# DUB
Expand Down
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
CWD := $(shell pwd)
SOURCES := $(shell find source -name '*.d')
EXAMPLES := $(shell find examples -name '*.d')
TARGET_OS := $(shell uname -s)
LIBS_PATH := lib

Expand All @@ -9,6 +8,7 @@ all: docs

test:
dub test
@rm -f $(COVERAGE_TARGETS)
.PHONY: test

cover: $(SOURCES)
Expand All @@ -25,12 +25,13 @@ docs: docs/sitemap.xml
.PHONY: docs

clean: clean-docs
rm -rf bin $(EXAMPLES)
rm -f -- *.lst
rm -rf bin coverage
.PHONY: clean

clean-docs:
ifeq ($(shell test -d docs && echo -n yes),yes)
rm -f docs.json
rm -f docs/sitemap.xml docs/file_hashes.json
rm -rf `find docs -name '*.html'`
endif
.PHONY: clean-docs
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@

[![DUB Package](https://img.shields.io/dub/v/descartes.svg)](https://code.dlang.org/packages/descartes)
![Descartes CI](https://github.com/chances/descartes-d/workflows/Descartes%20CI/badge.svg?branch=master)
<!-- [![codecov](https://codecov.io/gh/chances/descartes-d/branch/master/graph/badge.svg?token=5YN3BU7KR3)](https://codecov.io/gh/chances/descartes-d/) -->
[![codecov](https://codecov.io/gh/chances/descartes-d/branch/master/graph/badge.svg?token=bL2FkBtfPK)](https://codecov.io/gh/chances/descartes-d/)

Imprecision-tolerant computational geometry for [D](https://dlang.org).

# License
## Usage

```json
"dependencies": {
"descartes": "0.1.0"
}
```

<!-- TODO: Usage examples -->

## License

A port of https://github.com/aeplay/descartes, under the [MIT License](https://github.com/aeplay/descartes/blob/master/LICENSE):

Expand Down
15 changes: 11 additions & 4 deletions dub.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@
"name": "descartes",
"description": "Imprecision-tolerant computational geometry for D",
"license": "MIT",
"copyright": "Copyright © 2021, Chance Snow;Copyright © 2018, Anselm Eickhoff",
"copyright": "Copyright © 2021-2024, Chance Snow; Copyright © 2018, Anselm Eickhoff",
"authors": [
"Chance Snow",
"Anselm Eickhoff"
"Chance Snow <git@chancesnow.me>",
"Anselm Eickhoff <anselm.eickhoff@gmail.com>"
],
"targetPath": "bin",
"targetType": "sourceLibrary",
"dependencies": {
"gfm": "~>8.0.5"
}
},
"configurations": [
{ "name": "library" },
{
"name": "static",
"targetType": "staticLibrary"
}
]
}
8 changes: 8 additions & 0 deletions scripts/delete-junk-lst-files.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
for lst in `find . -name '-tmp*.lst'`; do
rm -f $lst
done

for lst in `find . -name '..*.lst'`; do
rm -f $lst
done
83 changes: 83 additions & 0 deletions source/descartes/angles.d
Original file line number Diff line number Diff line change
@@ -1,4 +1,87 @@
/// Angle-related linear algebra.
///
/// Authors: Chance Snow
/// Copyright: Copyright © 2021 Chance Snow. All rights reserved.
/// License: MIT License
module descartes.angles;

import descartes : N, norm, V2;
import gfm.math.vector : dot;
import std.math : acos, atan2, fmax, fmin, PI;

pragma(inline, true):

///
N angleTo(V2 a, V2 b) {
const theta = dot(a, b) / (a.norm * b.norm);
return theta.fmin(1.0).fmax(-1.0).acos;
}

///
N angleAlongTo(V2 a, V2 aDirection, V2 b) {
const simpleAngle = a.angleTo(b);
const linearDirection = (b - a).normalized;

if (aDirection.dot(linearDirection) >= 0) return simpleAngle;
return 2.0 * PI - simpleAngle;
}

///
N signedAngleTo(V2 a, V2 b) {
// https://stackoverflow.com/a/2150475
const det = a.x * b.y - a.y * b.x;
const dot = a.x * b.x + a.y * b.y;
return det.atan2(dot);
}

unittest {
import descartes : up, down, left, right;
import std.math : isClose;

assert(V2(3).angleTo(V2(4)) == 0.0);
// FIXME: assert((V2(3, 0).angleTo(V2(0, -4))) == 0.0);
// FIXME: assert((V2(3, 7).angleTo(V2(2, -4))) == 0.0);
assert((V2(3, 0).angleTo(V2(0, 4))).isClose(1.5708));

// FIXME: assert((V2(3, 0).angleAlongTo(up, V2(3, 4))).isClose(6.28319));
// FIXME: assert((V2(3, 0).angleAlongTo(down, V2(3, 4))) == 0.0);
// FIXME: assert((V2(3, 0).angleAlongTo(left, V2(3, 4))) == 0.0);
// FIXME: assert((V2(3, 0).angleAlongTo(right, V2(3, 4))) == 0.0);

assert(V2(3).signedAngleTo(V2(4)) == 0.0);
assert(V2(3, 0).signedAngleTo(V2(0, -4)).isClose(-1.5708));
assert(V2(3, 7).signedAngleTo(V2(2, -4)).isClose(-2.27305));
assert(V2(3, 0).signedAngleTo(V2(0, 4)).isClose(1.5708));
}

///
/// Warning:
/// Descartes assumes a right-hand coordinate system.
///
/// Positive angles are counter-clockwise if z-axis points offscreen.
V2 orthogonalRight(V2 self) {
return V2(self.y, -self.x);
}

///
/// Warning:
/// Descartes assumes a right-hand coordinate system.
///
/// Positive angles are counter-clockwise if z-axis points offscreen.
V2 orthogonalLeft(V2 self) {
return -self.orthogonalRight;
}

unittest {
import descartes : up, down, left, right;

assert(up.orthogonalRight == right);
assert(down.orthogonalRight == left);
assert(left.orthogonalRight == up);
assert(right.orthogonalRight == down);

assert(up.orthogonalLeft == left);
assert(down.orthogonalLeft == right);
assert(left.orthogonalLeft == down);
assert(right.orthogonalLeft == up);
}
61 changes: 61 additions & 0 deletions source/descartes/convert.d
Original file line number Diff line number Diff line change
@@ -1,4 +1,65 @@
/// 2D ⇆ 3D vector conversions.
///
/// Authors: Chance Snow
/// Copyright: Copyright © 2021 Chance Snow. All rights reserved.
/// License: MIT License
module descartes.convert;

import descartes : V2, V3;

pragma(inline, true):

/// Determines how vecotrs are swizzled between 2D and 3D, like in shader languages.
/// See_Also: <a href="https://en.wikipedia.org/wiki/Swizzling_(computer_graphics)">Swizzling (computer graphics)</a> on Wikipedia
enum Swizzle {
///
xy,
///
xz
}

/// Swizzle the given vector from 3D to 2D.
/// Params:
/// vector=
/// swizzle=How the given `vector` should be swizzled to 2D.
/// See_Also: <a href="https://en.wikipedia.org/wiki/Swizzling_(computer_graphics)">Swizzling (computer graphics)</a> on Wikipedia
V2 to2d(V3 vector, Swizzle swizzle = Swizzle.xy) {
if (swizzle == Swizzle.xy) return vector.xy;
if (swizzle == Swizzle.xz) return vector.xz;
assert(0, "Unreachable");
}

/// Swizzle the given vector from 2D to 3D.
/// Params:
/// vector=
/// swizzle=How the given `vector` should be swizzled to 3D.
/// See_Also: <a href="https://en.wikipedia.org/wiki/Swizzling_(computer_graphics)">Swizzling (computer graphics)</a> on Wikipedia
V3 to3d(V2 vector, Swizzle swizzle = Swizzle.xy) {
if (swizzle == Swizzle.xy) return V3(vector.xy.v ~ 0.0);
if (swizzle == Swizzle.xz) return V3(vector.x, 0.0, vector.y);
assert(0, "Unreachable");
}

unittest {
import descartes : up, down, left, right;

assert(up.to3d == V3(0, -1, 0));
assert(down.to3d == V3(0, 1, 0));
assert(left.to3d == V3(1, 0, 0));
assert(right.to3d == V3(-1, 0, 0));

assert(up.to3d(Swizzle.xz) == V3(0, 0, -1));
assert(down.to3d(Swizzle.xz) == V3(0, 0, 1));
assert(left.to3d(Swizzle.xz) == V3(1, 0, 0));
assert(right.to3d(Swizzle.xz) == V3(-1, 0, 0));

assert(up.to3d.to2d == up);
assert(down.to3d.to2d == down);
assert(left.to3d.to2d == left);
assert(right.to3d.to2d == right);

assert(up.to3d(Swizzle.xz).to2d(Swizzle.xz) == up);
assert(down.to3d(Swizzle.xz).to2d(Swizzle.xz) == down);
assert(left.to3d(Swizzle.xz).to2d(Swizzle.xz) == left);
assert(right.to3d(Swizzle.xz).to2d(Swizzle.xz) == right);
}
4 changes: 0 additions & 4 deletions source/descartes/grid/segment.d

This file was deleted.

29 changes: 29 additions & 0 deletions source/descartes/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ module descartes;
// https://gfm.dpldocs.info/gfm.math.html
import gfm.math;

// Emit coverage artifacts to ./coverage
version(D_Coverage) shared static this() {
import core.runtime : dmd_coverDestPath;
import std.file : exists, mkdir;

enum COV_PATH = "coverage";

if(!COV_PATH.exists) COV_PATH.mkdir;
dmd_coverDestPath(COV_PATH);
}

public:

import descartes.angles;
Expand Down Expand Up @@ -42,6 +53,24 @@ alias M4 = mat4!N;
// TODO: Ensure this is stored as a homogeneous 4x4 matrix.
/// A 3D affine transformation. Stored as a homogeneous, row-major 4x4 matrix.
alias Affine3 = mat4!N;
alias Rotation2 = mat2!N;
// TODO: Ensure this is stored as a homogeneous 4x4 matrix.
/// A 3D perspective projection stored as a homogeneous, row-major 4x4 matrix.
alias Perspective3 = mat4!N;

// TODO: Refactor this function upstream to [gfm](https://github.com/d-gamedev-team/gfm)
/// Computes the L2 (Euclidean) norm of a point.
/// See_Also: <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)#Euclidean_norm">Norm (mathematics): Euclidean norm</a> on Wikipedia
N norm(V2 x) {
import std.algorithm : map, sum;
import std.math : sqrt;

return sqrt(x.v[].map!"a * a".sum);
}

version (unittest) {
const up = V2(0, -1);
const down = V2(0, 1);
const left = V2(1, 0);
const right = V2(-1, 0);
}
8 changes: 7 additions & 1 deletion source/descartes/path/package.d
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
/// Authors: Chance Snow
/// Copyright: Copyright © 2021 Chance Snow. All rights reserved.
/// Copyright: Copyright © 2021-2024 Chance Snow. All rights reserved.
/// License: MIT License
module descartes.path;

public {
import descartes.path.arc;
import descartes.path.closed;
import descartes.path.line;
}
45 changes: 45 additions & 0 deletions source/descartes/rough.d
Original file line number Diff line number Diff line change
@@ -1,4 +1,49 @@
/// Fuzzy equality comparators.
///
/// Authors: Chance Snow
/// Copyright: Copyright © 2021 Chance Snow. All rights reserved.
/// License: MIT License
module descartes.rough;

import descartes : N, norm, V2;
import std.math : abs;

/// Thickness radius
enum float thickness = 0.001;
///
enum double roughTolerance = 0.000_000_1;

// TODO: How do these compare to [std.math.isClose](https://dlang.org/library/std/math/is_close.html)?

///
bool roughlyEqualTo(N a, N b, N tolerance = roughTolerance) {
return (a - b).abs <= tolerance;
}

///
bool roughlyEqualTo(V2 a, V2 b, N tolerance = roughTolerance) {
return (a - b).norm <= tolerance;
}

unittest {
import descartes : P2;

assert( P2(10, 20.0).roughlyEqualTo(P2(10, 20.0 + roughTolerance)));
assert(!P2(10, 21.0).roughlyEqualTo(P2(10, 20.0 + roughTolerance)));
assert( V2(10, 20.0).roughlyEqualTo(V2(10, 20.0 + roughTolerance)));
assert(!V2(10, 21.0).roughlyEqualTo(V2(10, 20.0 + roughTolerance)));
assert( P2(10, 20.0).roughlyEqualTo(P2(10, 20.0 + (roughTolerance / 2.0f))));
assert(!P2(10, 21.0).roughlyEqualTo(P2(10, 20.0 + (roughTolerance / 2.0f))));
assert( V2(10, 20.0).roughlyEqualTo(V2(10, 20.0 + (roughTolerance / 2.0f))));
assert(!V2(10, 21.0).roughlyEqualTo(V2(10, 20.0 + (roughTolerance / 2.0f))));
assert( P2(10, 20.0).roughlyEqualTo(P2(10, 20.0 + (roughTolerance * 5.0f))));
assert(!P2(10, 21.0).roughlyEqualTo(P2(10, 20.0 + (roughTolerance * 5.0f))));
assert( V2(10, 20.0).roughlyEqualTo(V2(10, 20.0 + (roughTolerance * 5.0f))));
assert(!V2(10, 21.0).roughlyEqualTo(V2(10, 20.0 + (roughTolerance * 5.0f))));

const lowerTolerance = 0.000_1;
assert( P2(10, 20.0 + lowerTolerance).roughlyEqualTo(P2(10, 20.000_1), lowerTolerance));
assert( V2(10, 20.0 + lowerTolerance).roughlyEqualTo(V2(10, 20.000_1), lowerTolerance));
// FIXME: assert(!P2(10, 20.000_000_1).roughlyEqualTo(P2(10, 20.000_1), lowerTolerance));
// FIXME: assert(!V2(10, 20.000_000_1).roughlyEqualTo(V2(10, 20.000_1), lowerTolerance));
}
Loading
Loading