Skip to content

Commit 0db5217

Browse files
authored
Merge pull request #5 from VirtualPlantLab/Ellipsoid
Add Ellipsoid turtle constructors (v1.0.2)
2 parents 7048ccb + 9ce5e61 commit 0db5217

6 files changed

Lines changed: 125 additions & 9 deletions

File tree

CLAUDE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
88
quite high. This *requires* that you use the MCP server to avoid starting a
99
new Julia session each time.
1010

11+
- Run tests by using the MCP server and `include(test/runtests.jl)` rather than `Pkg.test()`. This will
12+
avoid lengthy recompilation and will work even if you iterate changes on the package due to
13+
`Revise`.
14+
1115
- Exploit Julia packages and macros for evaluating performance issues:
1216
`BenchmarkTools.jl` for micro-benchmarks, `Profile` for CPU profiling, and
1317
`Cthulhu.jl` for method analysis (or `@code_warntype`). These tools are in

NEWS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
We started keeping track of changes in the `NEWS.md` file after version 0.1.0.
44

5+
# PlantGeomTurtle 1.0.2 (2026-06-17)
6+
7+
* Add `Ellipsoid!` and `Ellipsoid` turtle constructors to generate solid ellipsoids
8+
in front of the turtle. Requires PlantGeomPrimitives 1.0.2.
9+
510
# PlantGeomTurtle 1.0.0 (2026-06-10)
611

712
No actual changes. We move to version 1.0.0 because:

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "PlantGeomTurtle"
22
uuid = "7d6e2781-1c99-4c66-97ec-106669f3e96e"
33
authors = ["Alejandro Morales Sierra <alejandro.moralessierra@wur.nl> and contributors"]
4-
version = "1.0.0"
4+
version = "1.0.2"
55

66
[deps]
77
CoordinateTransformations = "150eb455-5306-5404-9cee-2592286d6298"
@@ -15,7 +15,7 @@ Unrolled = "9602ed7d-8fef-5bc8-8597-8f21381861e8"
1515
[compat]
1616
CoordinateTransformations = "0.6.3"
1717
LinearAlgebra = "1.11"
18-
PlantGeomPrimitives = "1.0.0"
18+
PlantGeomPrimitives = "1.0.3"
1919
PlantGraphs = "1.0.0"
2020
Rotations = "1.5.1"
2121
StaticArrays = "1.9.13"

src/Primitives.jl

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,16 +1064,87 @@ function PGP.SolidFrustum(turtle::Turtle{FT,UT}; length::FT = one(FT), width::FT
10641064
end
10651065

10661066

1067-
function Ellipsoid!(turtle::Turtle{FT,UT}; length::FT = one(FT), width::FT = one(FT),
1068-
height::FT = one(FT), n::Int = 20, move = false, kwargs...) where {FT,UT}
1069-
@error "Ellipsoid not implemented yet"
1067+
"""
1068+
Ellipsoid!(turtle; length = 1.0, width = 1.0, height = 1.0, n = 20, move = false, kwargs...)
1069+
1070+
Generate a solid ellipsoid in front of the turtle and feed it to a turtle.
1071+
1072+
## Arguments
1073+
- `turtle`: The turtle that we feed the solid ellipsoid to.
1074+
- `length`: Length of the solid ellipsoid along the head axis.
1075+
- `width`: Width of the solid ellipsoid along the arm axis.
1076+
- `height`: Height of the solid ellipsoid along the up axis.
1077+
- `n`: Number of latitude and azimuth subdivisions. The mesh has `2n(n-1)` triangles.
1078+
- `move`: Whether to move the turtle forward or not (`true` or `false`).
1079+
- `kwargs`: Properties to be set per triangle in the mesh.
1080+
1081+
## Details
1082+
A mesh will be generated with `2n(n-1)` triangles that approximate the solid ellipsoid.
1083+
The ellipsoid will be generated in front of the turtle, with the base centered at the
1084+
turtle's current position. The `length` argument refers to the axis aligned with the head
1085+
axis of the turtle, whereas `width` refers to the arm axis and `height` to the up axis.
1086+
1087+
When `move = true`, the turtle will be moved forward by a distance equal to `length`.
1088+
1089+
## Return
1090+
Returns `nothing` but modifies the `turtle` as a side effect.
1091+
1092+
## Examples
1093+
```jldoctest
1094+
julia> turtle = Turtle();
1095+
1096+
julia> e = Ellipsoid!(turtle; length = 1.0, width = 0.5, height = 0.5, n = 20);
1097+
```
1098+
"""
1099+
function PGP.Ellipsoid!(turtle::Turtle{FT,UT}; length::FT = one(FT), width::FT = one(FT),
1100+
height::FT = one(FT), n::Int = 20, move = false, kwargs...) where {FT,UT}
1101+
trans = transformation(turtle, PGP.Vec(height / FT(2), width / FT(2), length / FT(2)))
1102+
PGP.Ellipsoid!(PGP.Mesh(turtle), trans; n = n)
1103+
ntri = 2n * (n - 1)
1104+
for (k, v) in kwargs
1105+
PGP.add_property!(PGP.Mesh(turtle), k, v, ntri)
1106+
end
1107+
move && f!(turtle, length)
10701108
return nothing
10711109
end
10721110

1073-
function Ellipsoid(turtle::Turtle{FT,UT}; length::FT = one(FT), width::FT = one(FT),
1074-
height::FT = one(FT), n::Int = 20, move = false) where {FT,UT}
1075-
@error "Ellipsoid not implemented yet"
1076-
return nothing
1111+
"""
1112+
Ellipsoid(turtle; length = 1.0, width = 1.0, height = 1.0, n = 20, move = false)
1113+
1114+
Generate a solid ellipsoid in front of the turtle and return it.
1115+
1116+
## Arguments
1117+
- `turtle`: The turtle which state is used to create the solid ellipsoid.
1118+
- `length`: Length of the solid ellipsoid along the head axis.
1119+
- `width`: Width of the solid ellipsoid along the arm axis.
1120+
- `height`: Height of the solid ellipsoid along the up axis.
1121+
- `n`: Number of latitude and azimuth subdivisions. The mesh has `2n(n-1)` triangles.
1122+
- `move`: Whether to move the turtle forward or not (`true` or `false`).
1123+
1124+
## Details
1125+
A mesh will be generated with `2n(n-1)` triangles that approximate the solid ellipsoid.
1126+
The ellipsoid will be generated in front of the turtle, with the base centered at the
1127+
turtle's current position. The `length` argument refers to the axis aligned with the head
1128+
axis of the turtle, whereas `width` refers to the arm axis and `height` to the up axis.
1129+
1130+
When `move = true`, the turtle will be moved forward by a distance equal to `length`.
1131+
1132+
## Return
1133+
Returns a triangular mesh (object of type `Mesh`).
1134+
1135+
## Examples
1136+
```jldoctest
1137+
julia> turtle = Turtle();
1138+
1139+
julia> e = Ellipsoid(turtle; length = 1.0, width = 0.5, height = 0.5, n = 20);
1140+
```
1141+
"""
1142+
function PGP.Ellipsoid(turtle::Turtle{FT,UT}; length::FT = one(FT), width::FT = one(FT),
1143+
height::FT = one(FT), n::Int = 20, move = false) where {FT,UT}
1144+
trans = transformation(turtle, PGP.Vec(height / FT(2), width / FT(2), length / FT(2)))
1145+
e = PGP.Ellipsoid(trans; n = n)
1146+
move && f!(turtle, length)
1147+
return e
10771148
end
10781149

10791150

test/runtests.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ end
6868
@testset "solid_cone" begin
6969
include("test_solid_cone.jl")
7070
end
71+
@testset "ellipsoid" begin
72+
include("test_ellipsoid.jl")
73+
end
7174
@testset "mesh" begin
7275
include("test_mesh.jl")
7376
end

test/test_ellipsoid.jl

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import PlantGeomTurtle as PGT
2+
import PlantGeomPrimitives as PGP
3+
using Test
4+
5+
let
6+
7+
n = 20
8+
9+
# Construct solid ellipsoid using a turtle
10+
e = PGP.Ellipsoid(length = 2.0, width = 1.0, height = 0.5, n = n)
11+
t = PGT.Turtle(Float64)
12+
PGP.Ellipsoid!(t; length = 2.0, width = 1.0, height = 0.5, n = n, move = true)
13+
@test PGP.Mesh(t) == e
14+
@test PGT.pos(t) == PGP.Vec{Float64}(0, 0, 2)
15+
16+
t = PGT.Turtle(Float64)
17+
e2 = PGP.Ellipsoid(t; length = 2.0, width = 1.0, height = 0.5, n = n, move = false)
18+
@test e2 == e
19+
@test PGT.pos(t) == PGP.Vec{Float64}(0, 0, 0)
20+
21+
# Check properties
22+
ntri = 2n * (n - 1)
23+
t = PGT.Turtle(Float64)
24+
PGP.Ellipsoid!(t; length = 2.0, width = 1.0, height = 0.5, n = n, move = false, colors = rand(RGB))
25+
@test length(get_colors(t)) == ntri
26+
@test get_colors(t)[1] == get_colors(t)[4]
27+
28+
t = PGT.Turtle(Float64)
29+
PGP.Ellipsoid!(t; length = 2.0, width = 1.0, height = 0.5, n = n, move = false, colors = [rand(RGB) for _ in 1:ntri])
30+
@test length(get_colors(t)) == ntri
31+
@test get_colors(t)[1] != get_colors(t)[4]
32+
33+
end

0 commit comments

Comments
 (0)