diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index c1f6550f..9704582f 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v2 with: - version: '1.9' + version: '1.10' - uses: julia-actions/cache@v2 - name: Install dependencies run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' diff --git a/docs/Project.toml b/docs/Project.toml index 8a11b5a1..744745e3 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,13 +1,21 @@ [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" +Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" +Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" MeshIntegrals = "dadec2fd-bbe0-4da4-9dbe-476c782c8e47" Meshes = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [compat] BenchmarkTools = "1" +CairoMakie = "0.15" +Colors = "0.13" +Distributions = "0.25" Documenter = "1" +GeometryBasics = "0.5" Meshes = "0.53, 0.54" Unitful = "1.19" julia = "1.9" diff --git a/docs/make.jl b/docs/make.jl index 0cb480e7..fd2311ba 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -16,6 +16,9 @@ makedocs( "Support Status" => "support.md", "Tips" => "tips.md" ], + "Examples" => [ + "Darts Strategy Analysis" => "examples/darts.md" + ], "Developer Notes" => [ "Changelog" => "developer/CHANGELOG.md", "How it Works" => "developer/how_it_works.md", diff --git a/docs/src/examples/darts.md b/docs/src/examples/darts.md new file mode 100644 index 00000000..a221855d --- /dev/null +++ b/docs/src/examples/darts.md @@ -0,0 +1,150 @@ +# Darts (Draft) + +Steps +- Construct a set of geometries representing a dartboard with individual sector scores +- Develop a model of the dart trajectory with probability density distribution +- Use integration over each geometry to determine the probabilities of particular outcomes +- Calculate expected value for the throw, repeat for other distributions to compare strategies + +```@example darts +using CairoMakie +using Colors +using Distributions +using GeometryBasics +using Meshes +using MeshIntegrals +using Unitful +using Unitful.DefaultSymbols: mm, m + +red = colorant"red" +black = colorant"black" +white = colorant"white" +green = colorant"green" + +# For containing any scored landing region on the dart board +struct ScoredRegion{G, C} + geometry::G + points::Int64 + color::C +end + +# For defining an annular region +struct Sector{L, A} + r_inner::L + r_outer::L + phi_a::A + phi_b::A +end +Sector(rs, phis) = Sector(rs..., phis...) + +# Sector -> Ngon +function _Ngon(sector::Sector; N=8) + ϕs = range(sector.phi_a, sector.phi_b, length=N) + arc_o = [point(sector.r_outer, ϕ) for ϕ in ϕs] + arc_i = [point(sector.r_inner, ϕ) for ϕ in reverse(ϕs)] + return Meshes.Ngon(arc_o..., arc_i...) +end + +function _Point2f(p::Meshes.Point) + x, y, z = ustrip.(m, [p.coords.x, p.coords.y, p.coords.z]) + return Point2f(y, z) +end + +function _Point3f(p::Meshes.Point) + x, y, z = ustrip.(m, [p.coords.x, p.coords.y, p.coords.z]) + return Point3f(x, y, z) +end + +_poly(circle::Meshes.Circle; N=32) = [(_Point3f(circle(t)) for t in range(0, 1, length=N))...] +_poly(ngon::Meshes.Ngon) = [(_Point3f(pt) for pt in ngon.vertices)...] + +_poly2d(circle::Meshes.Circle; N=32) = [(_Point2f(circle(t)) for t in range(0, 1, length=N))...] +_poly2d(ngon::Meshes.Ngon) = [(_Point2f(pt) for pt in ngon.vertices)...] +``` + +## Modeling the Dartboard + +Model the dartboard +```@example darts +dartboard_center = Meshes.Point(0m, 0m, 1.5m) +dartboard_plane = Plane(dartboard_center, Meshes.Vec(1, 0, 0)) + +function point(r::Unitful.Length, ϕ) + t = ustrip(m, r) + dartboard_plane(t * sin(ϕ), t * cos(ϕ)) +end + +# Scores on the Board +ring1 = [20, 1, 18, 4, 13, 6, 10, 15, 2, 17, 3, 19, 7, 16, 8, 11, 14, 9, 12, 5] +ring2 = 3 .* ring1 +ring3 = ring1 +ring4 = 2 .* ring1 +board_points = hcat(ring1, ring2, ring3, ring4) + +# Colors on the board +ring1 = repeat([black, white], 10) +ring2 = repeat([red, green], 10) +ring3 = ring1 +ring4 = ring2 +board_colors = hcat(ring1, ring2, ring3, ring4) + +# Sector geometries +sector_width = 2π/20 +sector_centers = [n * sector_width for n in 0:19] +phis_a = sector_centers .- sector_width/2 +phis_b = sector_centers .+ sector_width/2 +phis = Iterators.zip(phis_a, phis_b) +rs = [ (16mm, 99mm), (99mm, 107mm), (107mm, 162mm), (162mm, 170mm) ] +board_coords = Iterators.product(phis, rs) +board_sectors = map(((phis, rs),) -> Sector(rs, phis), board_coords) +board_ngons = _Ngon.(board_sectors) + +# Consolidate the Sectors +sector_data = Iterators.zip(board_ngons, board_points, board_colors) +board_regions = map(args -> ScoredRegion(args...), sector_data) + +# Center region +bullseye_inner = ScoredRegion(Meshes.Circle(dartboard_plane, 6.35mm), 50, red) +bullseye_outer = ScoredRegion(_Ngon(Sector((6.35mm, 16.0mm), (0.0, 2π)); N=32), 25, green) + +# Get set of all regions +all_regions = vcat(vec(board_regions), bullseye_inner, bullseye_outer) + +fig = Figure() +ax = Axis(fig[1, 1], xlabel="y [m]", ylabel="z [m]") +ax.aspect = DataAspect() + +for region in board_regions + pts = _poly2d(region.geometry) + poly!(ax, pts, color=region.color) + + centerPt = centroid(region.geometry) + center = ustrip.(u"m", [centerPt.coords.y, centerPt.coords.z]) + text!(ax, string(region.points), position=Point2f(center...), align=(:center,:center), color=:blue, fontsize=10) +end + +fig +``` + +## Modeling the Dart Trajectory + +Define a probability distribution for where the dart will land +``` +# TODO +dist = MvNormal(μs, σs) +``` + +Integrand function is the distribution's PDF value at any particular point +``` +# TODO +function integrand(p::Point) + v_error = dist_center - p + pdf(dist, v_error) +end +``` + +Example image of trajectory probability distribution on board + +## Strategy Evaluation + +Use these tools to evaluate different aiming/throwing parameters and their impact on expected scores.