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
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
quite high. This *requires* that you use the MCP server to avoid starting a
new Julia session each time.

- Run tests by using the MCP server and `include(test/runtests.jl)` rather than `Pkg.test()`. This will
avoid lengthy recompilation and will work even if you iterate changes on the package due to
`Revise`.

- Exploit Julia packages and macros for evaluating performance issues:
`BenchmarkTools.jl` for micro-benchmarks, `Profile` for CPU profiling, and
`Cthulhu.jl` for method analysis (or `@code_warntype`). These tools are in
Expand Down
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

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

# PlantGeomTurtle 1.0.2 (2026-06-17)

* Add `Ellipsoid!` and `Ellipsoid` turtle constructors to generate solid ellipsoids
in front of the turtle. Requires PlantGeomPrimitives 1.0.2.

# PlantGeomTurtle 1.0.0 (2026-06-10)

No actual changes. We move to version 1.0.0 because:
Expand Down
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "PlantGeomTurtle"
uuid = "7d6e2781-1c99-4c66-97ec-106669f3e96e"
authors = ["Alejandro Morales Sierra <alejandro.moralessierra@wur.nl> and contributors"]
version = "1.0.0"
version = "1.0.2"

[deps]
CoordinateTransformations = "150eb455-5306-5404-9cee-2592286d6298"
Expand All @@ -15,7 +15,7 @@ Unrolled = "9602ed7d-8fef-5bc8-8597-8f21381861e8"
[compat]
CoordinateTransformations = "0.6.3"
LinearAlgebra = "1.11"
PlantGeomPrimitives = "1.0.0"
PlantGeomPrimitives = "1.0.3"
PlantGraphs = "1.0.0"
Rotations = "1.5.1"
StaticArrays = "1.9.13"
Expand Down
85 changes: 78 additions & 7 deletions src/Primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1064,16 +1064,87 @@ function PGP.SolidFrustum(turtle::Turtle{FT,UT}; length::FT = one(FT), width::FT
end


function Ellipsoid!(turtle::Turtle{FT,UT}; length::FT = one(FT), width::FT = one(FT),
height::FT = one(FT), n::Int = 20, move = false, kwargs...) where {FT,UT}
@error "Ellipsoid not implemented yet"
"""
Ellipsoid!(turtle; length = 1.0, width = 1.0, height = 1.0, n = 20, move = false, kwargs...)

Generate a solid ellipsoid in front of the turtle and feed it to a turtle.

## Arguments
- `turtle`: The turtle that we feed the solid ellipsoid to.
- `length`: Length of the solid ellipsoid along the head axis.
- `width`: Width of the solid ellipsoid along the arm axis.
- `height`: Height of the solid ellipsoid along the up axis.
- `n`: Number of latitude and azimuth subdivisions. The mesh has `2n(n-1)` triangles.
- `move`: Whether to move the turtle forward or not (`true` or `false`).
- `kwargs`: Properties to be set per triangle in the mesh.

## Details
A mesh will be generated with `2n(n-1)` triangles that approximate the solid ellipsoid.
The ellipsoid will be generated in front of the turtle, with the base centered at the
turtle's current position. The `length` argument refers to the axis aligned with the head
axis of the turtle, whereas `width` refers to the arm axis and `height` to the up axis.

When `move = true`, the turtle will be moved forward by a distance equal to `length`.

## Return
Returns `nothing` but modifies the `turtle` as a side effect.

## Examples
```jldoctest
julia> turtle = Turtle();

julia> e = Ellipsoid!(turtle; length = 1.0, width = 0.5, height = 0.5, n = 20);
```
"""
function PGP.Ellipsoid!(turtle::Turtle{FT,UT}; length::FT = one(FT), width::FT = one(FT),
height::FT = one(FT), n::Int = 20, move = false, kwargs...) where {FT,UT}
trans = transformation(turtle, PGP.Vec(height / FT(2), width / FT(2), length / FT(2)))
PGP.Ellipsoid!(PGP.Mesh(turtle), trans; n = n)
ntri = 2n * (n - 1)
for (k, v) in kwargs
PGP.add_property!(PGP.Mesh(turtle), k, v, ntri)
end
move && f!(turtle, length)
return nothing
end

function Ellipsoid(turtle::Turtle{FT,UT}; length::FT = one(FT), width::FT = one(FT),
height::FT = one(FT), n::Int = 20, move = false) where {FT,UT}
@error "Ellipsoid not implemented yet"
return nothing
"""
Ellipsoid(turtle; length = 1.0, width = 1.0, height = 1.0, n = 20, move = false)

Generate a solid ellipsoid in front of the turtle and return it.

## Arguments
- `turtle`: The turtle which state is used to create the solid ellipsoid.
- `length`: Length of the solid ellipsoid along the head axis.
- `width`: Width of the solid ellipsoid along the arm axis.
- `height`: Height of the solid ellipsoid along the up axis.
- `n`: Number of latitude and azimuth subdivisions. The mesh has `2n(n-1)` triangles.
- `move`: Whether to move the turtle forward or not (`true` or `false`).

## Details
A mesh will be generated with `2n(n-1)` triangles that approximate the solid ellipsoid.
The ellipsoid will be generated in front of the turtle, with the base centered at the
turtle's current position. The `length` argument refers to the axis aligned with the head
axis of the turtle, whereas `width` refers to the arm axis and `height` to the up axis.

When `move = true`, the turtle will be moved forward by a distance equal to `length`.

## Return
Returns a triangular mesh (object of type `Mesh`).

## Examples
```jldoctest
julia> turtle = Turtle();

julia> e = Ellipsoid(turtle; length = 1.0, width = 0.5, height = 0.5, n = 20);
```
"""
function PGP.Ellipsoid(turtle::Turtle{FT,UT}; length::FT = one(FT), width::FT = one(FT),
height::FT = one(FT), n::Int = 20, move = false) where {FT,UT}
trans = transformation(turtle, PGP.Vec(height / FT(2), width / FT(2), length / FT(2)))
e = PGP.Ellipsoid(trans; n = n)
move && f!(turtle, length)
return e
end


Expand Down
3 changes: 3 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ end
@testset "solid_cone" begin
include("test_solid_cone.jl")
end
@testset "ellipsoid" begin
include("test_ellipsoid.jl")
end
@testset "mesh" begin
include("test_mesh.jl")
end
Expand Down
33 changes: 33 additions & 0 deletions test/test_ellipsoid.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import PlantGeomTurtle as PGT
import PlantGeomPrimitives as PGP
using Test

let

n = 20

# Construct solid ellipsoid using a turtle
e = PGP.Ellipsoid(length = 2.0, width = 1.0, height = 0.5, n = n)
t = PGT.Turtle(Float64)
PGP.Ellipsoid!(t; length = 2.0, width = 1.0, height = 0.5, n = n, move = true)
@test PGP.Mesh(t) == e
@test PGT.pos(t) == PGP.Vec{Float64}(0, 0, 2)

t = PGT.Turtle(Float64)
e2 = PGP.Ellipsoid(t; length = 2.0, width = 1.0, height = 0.5, n = n, move = false)
@test e2 == e
@test PGT.pos(t) == PGP.Vec{Float64}(0, 0, 0)

# Check properties
ntri = 2n * (n - 1)
t = PGT.Turtle(Float64)
PGP.Ellipsoid!(t; length = 2.0, width = 1.0, height = 0.5, n = n, move = false, colors = rand(RGB))
@test length(get_colors(t)) == ntri
@test get_colors(t)[1] == get_colors(t)[4]

t = PGT.Turtle(Float64)
PGP.Ellipsoid!(t; length = 2.0, width = 1.0, height = 0.5, n = n, move = false, colors = [rand(RGB) for _ in 1:ntri])
@test length(get_colors(t)) == ntri
@test get_colors(t)[1] != get_colors(t)[4]

end
Loading