Skip to content

Commit

Permalink
Slim down GeometryBasics and remove all type complexity (#173)
Browse files Browse the repository at this point in the history
* get rid of StaticArrays

* alias must be fully typed

* more fixes

* fix order

* more fixes

* simplify fixed array

* remove meta types

* try to get mesh working

* remove all the things!!

* more tests

* remove 1.3

* drop 1.3

* add imports and get overload

* add getindex for faces for vec too

* fixes

* bring back connect

* add / for e.g. mean(points)

* small improvements

* introduce TriangleMesh

* fix constructor and print Mesh more nicely

* add zero for value

* fix tests

* polys

* fix some compile errors

* fix Simplex <: AbstractSimplex <: Polytope

* fix decompose test

* fix Rect2 Tesselation

* fix ngeom for LineString

* fix compat bounds

* change getindex index to Integer

* remove triangle_mesh Tesselation deprecation warning

* fix Base.intersect extension

* update union docs and comment out broken diff or rects

* add decompose_x methdos with types

* disable diff test

* fix MatN constructors

* fix type ambiguity

* fix vcat of StaticVector, Mat convert

* add mat[VecOrInt, VecOrInt] and Vec(mat)

* match isapprox implementation with docstring

* add isapprox to Mat

* fix indexing instead of implementing isapprox

* rework broadcasting & add more tests

* fix nested broadcast

* fix Mat getindex, Boundschecks and add tests

* fix broadcast size missmatch

* handle map like StaticArrays

* add isapprox and converts from Matrix for Mat3f-like types

* avoid creating Base arrays on copy

* Geometry Basics refactor (#219)

* update MetaMesh

* add MultiFace type

* update Mesh & MultiFace types

* update Mesh & MetaMesh utils

* add MultiFace remapping code

* prototype MultiFace Rect -> Mesh pipeline

* generalize MultiFace getindex to Integer

* remove add_meta, pop_meta

* update merge for MultiFace and views

* add AbstractVertexFace and AbstractMultiFace

* split up  mesh() for better usability

* minor fixes

* add views to Mesh constructors

* add `mesh()` method for converting facetype of mesh

* switch back to "normals"

* fix missing normals rename

* add back point/normal/uv-type kwargs

* consider face views in face decompose

* add mesh(mesh; attribs...) & improve dispatch safety

* fix normals(), cleanup tests

* add mesh constructor tests + fixes

* deprecate normals for normal as vertex attribute name

* make NormalFace and NormalUVFace types again

* cleanup tests & normals vs normal

* let ci run

* remove views aware face decomposition

* fix rect index order

* fix MultiFace remapping with OffsetIntegers

* add moreMultiFace utils

* restore decompose(FaceType, faces, views)

* allow MultiFace -> LineFace conversion

* define Rect3 faces counterclockwise

* add more convenience types

* filter nothing attributes

* drop Base.Pairs for 1.6 compat

* Add depwarn in hasproperty too

* improve MultiFace show

* update Pyramid

* update Cylinder

* add MultiFace decompose

* fix Cylinder tests

* make OffsetInteger printing copyable

* update tests for Rect3 and normal gen

* fix remaining tests

* fix incorrect vertex index counter

* simplify merge of mixed Face types

* test merge(meshes)

* prototype swapping from MultiFace to FaceViews

* treat views in vertex index remapping + some fixes

* fix face type change

* clean up AbstractVertexFace

* extend FaceView interface

* update Cylinder, Pyramids

* declutter NgonFace prints

* update tests

* cleanup some test_broken

* switch to Dict

* fix tests

* remove shorthands

* export vertex_attributes and FaceView

* add center point to Circle to avoid shallow triangles

* make untesselated rect vertices counter-clockwise

* fix tests

* fix Cylinder face windig direction

* add `face_normals()` helepr function

* cleanup face_normals and normals a bit more

* update tests for Cylinder

* rename connectivity -> faces

* update docs (meshes, primitives, decompose, Point, Vec, Mat)

* add/update docstrings

* add quick test for face_normals()

* fix TetrahedronFace conversions

* restore volume functions

* fix tests

* add some Polygon tests

* test Pyramids

* test TetrahedronFace decomposition

* test and improve Mesh validation

* test Mesh inteface functions

* test decompose with views

* test and fix matrix det, inv, transpose, mat*vec

* fix tests

* cleanup normal gen and export face_normals

* add util for splitting meshes by views

* fix missing dot in range .+ idx

* improve performance of merge

* fix tests

* improve GeoInterface conversion performance

* switch back to NamedTuple for performance

* cleanup merge(meshes)

* test clear_faceviews with mesh.views

* fix missing import in docs examples

* add convert for arrays of meshes

* add function for removing duplicate faces

* update normal gen tests + fixes

- fix normal gen for varying face types
- fix normalization of face_normals

* remove time piracy

* bring back shorthand types

* restrict type in meshes to error earlier

* update precompiles

* autoconvert point dim in merge(meshes) instead of restricitng type

* add compat entry

* ignore unused PrecompileTools in 1.6, 1.7

* bring back old precompiles

* add convert target to orthogonal_vector

* revert triangulation changes of Circle

* revert to using StaticArrays

* avoid some invalidations (and fix get)

* fix stale instances due to AbstractVector

Apparently T[] is an AbstractVector here?

* use string interpolation in error to avoid invalidation from string *

* fix test

* export clear_faceviews & update FaceView docstring

* update type docstring

* add notes about views

* update Mesh docs

* add FaceView ref to Mesh docstring

* add brief section about extending decompose

* derive Point eltype when dimension is given

* reuse docstring

* fix docs?

* fix docs??

* add GLMakie to docs

* fix pkg command

* rename clear_faceviews & update

* fix pkg

* fix another clear_faceviews

* fix docs

* use action cache

* fix typo

---------

Co-authored-by: ffreyer <frederic481994@hotmail.de>
  • Loading branch information
SimonDanisch and ffreyer authored Oct 17, 2024
1 parent 138fd1c commit 83f9abd
Show file tree
Hide file tree
Showing 37 changed files with 2,569 additions and 2,098 deletions.
23 changes: 9 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
pull_request:
branches:
- master
- sd/simple-mesh
jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
Expand All @@ -29,16 +30,7 @@ jobs:
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: actions/cache@v1
env:
cache-name: cache-artifacts
with:
path: ~/.julia/artifacts
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
restore-keys: |
${{ runner.os }}-test-${{ env.cache-name }}-
${{ runner.os }}-test-
${{ runner.os }}-
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
- uses: julia-actions/julia-processcoverage@v1
Expand All @@ -47,20 +39,23 @@ jobs:
file: lcov.info
docs:
name: Documentation
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
env:
JULIA_PKG_SERVER: ""
steps:
- uses: actions/checkout@v2
- run: sudo apt-get update && sudo apt-get install -y xorg-dev mesa-utils xvfb libgl1 freeglut3-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev xsettingsd x11-xserver-utils
- uses: julia-actions/setup-julia@v1
with:
version: "1.7"
version: "1.11"
- uses: julia-actions/cache@v2
- run: |
julia --project=docs -e '
DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia --project=docs -e '
using Pkg
Pkg.develop(PackageSpec(path=pwd()))
pkg"add MeshIO#ff/GeometryBasics_refactor MakieCore#breaking-0.22 Makie#breaking-0.22 GLMakie#breaking-0.22"
Pkg.instantiate()'
- run: julia --project=docs docs/make.jl
- run: DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia --project=docs docs/make.jl
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}
9 changes: 4 additions & 5 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ Extents = "411431e0-e8b7-467b-b5e0-f676ba4f2910"
GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a"
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"

[compat]
Aqua = "0.8"
Expand All @@ -22,10 +22,9 @@ GeoJSON = "0.7, 0.8"
IterTools = "1.3.0"
LinearAlgebra = "<0.0.1,1"
OffsetArrays = "1"
PrecompileTools = "1.0"
Random = "<0.0.1,1"
StaticArrays = "0.12, 1.0"
StructArrays = "0.6"
Tables = "0.2, 1"
StaticArrays = "0.6, 1"
Test = "<0.0.1,1"
julia = "1.6"

Expand Down
3 changes: 1 addition & 2 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ makedocs(format=Documenter.HTML(prettyurls=get(ENV, "CI", "false") == "true"),
pages=[
"index.md",
"primitives.md",
"rectangles.md",
"polygons.md",
"meshes.md",
"decomposition.md",
"metadata.md",
"static_array_types.md",
"api.md"
],
modules=[GeometryBasics])
Expand Down
111 changes: 24 additions & 87 deletions docs/src/decomposition.md
Original file line number Diff line number Diff line change
@@ -1,89 +1,26 @@
# Decomposition


## GeometryBasics Mesh interface

GeometryBasics defines an interface to decompose abstract geometries into
points and triangle meshes.
This can be done for any arbitrary primitive, by overloading the following interface:

```julia

function GeometryBasics.coordinates(rect::Rect2, nvertices=(2,2))
mini, maxi = extrema(rect)
xrange, yrange = LinRange.(mini, maxi, nvertices)
return ivec(((x,y) for x in xrange, y in yrange))
end

function GeometryBasics.faces(rect::Rect2, nvertices=(2, 2))
w, h = nvertices
idx = LinearIndices(nvertices)
quad(i, j) = QuadFace{Int}(idx[i, j], idx[i+1, j], idx[i+1, j+1], idx[i, j+1])
return ivec((quad(i, j) for i=1:(w-1), j=1:(h-1)))
end
```
Those methods, for performance reasons, expect you to return an iterator, to make
materializing them with different element types allocation free. But of course,
can also return any `AbstractArray`.

With these methods defined, this constructor will magically work:

```julia
rect = Rect2(0.0, 0.0, 1.0, 1.0)
m = GeometryBasics.mesh(rect)
```
If you want to set the `nvertices` argument, you need to wrap your primitive in a `Tesselation`
object:
```julia
m = GeometryBasics.mesh(Tesselation(rect, (50, 50)))
length(coordinates(m)) == 50^2
```

As you can see, `coordinates` and `faces` are also defined on a mesh
```julia
coordinates(m)
faces(m)
```
But will actually not be an iterator anymore. Instead, the mesh constructor uses
the `decompose` function, that will collect the result of coordinates and will
convert it to a concrete element type:
```julia
decompose(Point2f, rect) == convert(Vector{Point2f}, collect(coordinates(rect)))
```
The element conversion is handled by `simplex_convert`, which also handles convert
between different face types:
```julia
decompose(QuadFace{Int}, rect) == convert(Vector{QuadFace{Int}}, collect(faces(rect)))
length(decompose(QuadFace{Int}, rect)) == 1
fs = decompose(GLTriangleFace, rect)
fs isa Vector{GLTriangleFace}
length(fs) == 2 # 2 triangles make up one quad ;)
```
`mesh` uses the most natural element type by default, which you can get with the unqualified Point type:
```julia
decompose(Point, rect) isa Vector{Point{2, Float64}}
```
You can also pass the element type to `mesh`:
```julia
m = GeometryBasics.mesh(rect, pointtype=Point2f, facetype=QuadFace{Int})
```
You can also set the uv and normal type for the mesh constructor, which will then
calculate them for you, with the requested element type:
```julia
m = GeometryBasics.mesh(rect, uv=Vec2f, normaltype=Vec3f)
```

As you can see, the normals are automatically calculated,
the same is true for texture coordinates. You can overload this behavior by overloading
`normals` or `texturecoordinates` the same way as coordinates.
`decompose` works a bit different for normals/texturecoordinates, since they dont have their own element type.
Instead, you can use `decompose` like this:
```julia
decompose(UV(Vec2f), rect)
decompose(Normal(Vec3f), rect)
# the short form for the above:
decompose_uv(rect)
decompose_normals(rect)
```
You can also use `triangle_mesh`, `normal_mesh` and `uv_normal_mesh` to call the
`mesh` constructor with predefined element types (Point2/3f, Vec2/3f), and the requested attributes.
## decompose functions

The `decompose` functions allow you to grab certain data from an `AbstractGeometry` like a mesh or primitive and convert it to a requested type, if possible.
They can also be used to convert an array of e.g. faces into a different face type directly.
The default decomposition implemented by GeoemtryBasics are:
- `decompose(::Type{<: Point}, source)` which collects data from `source` using `coordinates(source)` and converts it to the given point type.
- `decompose_normals([::Type{<: Vec},] source) = decompose([::Type{Normals{<: Vec}}},] source)` which collects data with `normals(source)` and converts it to the given Vec type.
- `decompose_uv([::Type{<: Vec},] source) = decompose([::Type{UV{<: Vec}}},] source)` which collects data with `texturecoordinates(source)` and converts it to the given Vec type. This function also exists with `UVW` texture coordinates.
- `decompose(::Type{<: AbstractFace}, source)` which collects data with `faces(source)` and converts it to the given face type.

### Extending decompose

For `decompose` to work there needs to be a conversion from some element type to some target type.
GeometryBasics relies on `GeometryBasics.convert_simplex(TargetType, value)` for this.
If you want to add new types to decompose, e.g. a new face type, you will need to add a method to that function.

## Primitive decomposition

GeometryBasics defines an interface to decompose geometry primitives into vertex attributes and faces.
The interface includes four functions:
- `coordinates(primitive[, nvertices])` which produces the positions associated with the primitive
- `faces(primitive[, nvertices])` which produces the faces which connect the vertex positions to a mesh
- `normals(primitive[, nvertices])` which optionally provide normal vectors of the primitive
- `texturecoordinates(primitive[, nvertices])` which optional provide texture coordinates (uv/uvw) of the primitive
5 changes: 0 additions & 5 deletions docs/src/implementation.md

This file was deleted.

85 changes: 6 additions & 79 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,56 +22,20 @@ p2 = Point(1, 3);
p3 = Point(4, 4);
```

Geometries can carry metadata:

```@repl quickstart
poi = meta(p1, city="Abuja", rainfall=1221.2)
```

Metadata is stored in a NamedTuple and can be retrieved as such:

```@repl quickstart
meta(poi)
```

Specific metadata attributes can be directly retrieved:

```@repl quickstart
poi.rainfall
```

To remove the metadata and keep only the geometry, use `metafree`:

```@repl quickstart
metafree(poi)
```

Geometries have predefined metatypes:

```@repl quickstart
multipoi = MultiPointMeta([p1], city="Abuja", rainfall=1221.2)
```

Connect the points with lines:
Connect pairs of points as line segments:

```@repl quickstart
l1 = Line(p1, p2)
l2 = Line(p2, p3);
```

Connect the lines in a linestring:

```@repl quickstart
LineString([l1, l2])
```

Linestrings can also be constructed directly from points:
Or connect multiple points as a linestring:

```@repl quickstart
LineString([p1, p2, p3])
```

The same goes for polygons:
You can also create polygons from points:

```@repl quickstart
Polygon(Point{2, Int}[(3, 1), (4, 4), (2, 4), (1, 2), (3, 1)])
Expand All @@ -89,57 +53,20 @@ Decompose the rectangle into two triangular faces:
rect_faces = decompose(TriangleFace{Int}, rect)
```

Decompose the rectangle into four vertices:
Decompose the rectangle into four positions:

```@repl quickstart
rect_vertices = decompose(Point{2, Float64}, rect)
rect_positions = decompose(Point{2, Float64}, rect)
```

Combine the vertices and faces into a triangle mesh:

```@repl quickstart
mesh = Mesh(rect_vertices, rect_faces)
mesh = Mesh(rect_positions, rect_faces)
```

Use `GeometryBasics.mesh` to get a mesh directly from a geometry:

```@repl quickstart
mesh = GeometryBasics.mesh(rect)
```


## Aliases

GeometryBasics exports common aliases for Point, Vec, Mat and Rect:

### Vec

| |`T`(eltype) |`Float64` |`Float32` |`Int` |`UInt` |
|--------|------------|----------|----------|----------|----------|
|`N`(dim)|`Vec{N,T}` |`Vecd{N}` |`Vecf{N}` |`Veci{N}` |`Vecui{N}`|
|`2` |`Vec2{T}` |`Vec2d` |`Vec2f` |`Vec2i` |`Vec2ui` |
|`3` |`Vec3{T}` |`Vec3d` |`Vec3f` |`Vec3i` |`Vec3ui` |

### Point

| |`T`(eltype) |`Float64` |`Float32` |`Int` |`UInt` |
|--------|------------|----------|----------|----------|----------|
|`N`(dim)|`Point{N,T}`|`Pointd{N}`|`Pointf{N}`|`Pointi{N}`|`Pointui{N}`|
|`2` |`Point2{T}` |`Point2d` |`Point2f` |`Point2i` |`Point2ui`|
|`3` |`Point3{T}` |`Point3d` |`Point3f` |`Point3i` |`Point3ui`|

### Mat

| |`T`(eltype) |`Float64` |`Float32` |`Int` |`UInt` |
|--------|------------|----------|----------|----------|----------|
|`N`(dim)|`Mat{N,T}` |`Matd{N}` |`Matf{N}` |`Mati{N}` |`Matui{N}`|
|`2` |`Mat2{T}` |`Mat2d` |`Mat2f` |`Mat2i` |`Mat2ui` |
|`3` |`Mat3{T}` |`Mat3d` |`Mat3f` |`Mat3i` |`Mat3ui` |

### Rect

| |`T`(eltype) |`Float64` |`Float32` |`Int` |`UInt` |
|--------|------------|----------|----------|----------|----------|
|`N`(dim)|`Rect{N,T}` |`Rectd{N}`|`Rectf{N}`|`Recti{N}`|`Rectui{N}`|
|`2` |`Rect2{T}` |`Rect2d` |`Rect2f` |`Rect2i` |`Rect2ui` |
|`3` |`Rect3{T}` |`Rect3d` |`Rect3f` |`Rect3i` |`Rect3ui` |
Loading

0 comments on commit 83f9abd

Please sign in to comment.