LCOV - code coverage report
Current view: top level - src - meshgrid.jl (source / functions) Hit Total Coverage
Test: on branch nothing Lines: 63 63 100.0 %
Date: 2025-02-19 11:29:30 Functions: 0 0 -

          Line data    Source code
       1             : # https://stackoverflow.com/questions/51466537/how-to-plot-a-vector-field-in-julia
       2             : 
       3             : """
       4             :     X, Y = meshgrid(t)
       5             :     X, Y = meshgrid(x, y)
       6             : 
       7             : Replacement for Matlab's `meshgrid`.
       8             : """
       9           6 : meshgrid(x, y = x) =
      10             :     (repeat(x', inner=(length(y), 1)), repeat(y, inner=(1, length(x))))
      11             : 
      12             : struct MeshGrid{T}
      13             :     X::Matrix{T}
      14             :     Y::Matrix{T}
      15             : 
      16           7 :     function MeshGrid{T}(X::Matrix{T}, Y::Matrix{T}) where {T}
      17           7 :         @assert size(X) == size(Y)
      18           7 :         new(X, Y)
      19             :     end
      20             : end
      21             : 
      22           7 : MeshGrid(X::Matrix{T}, Y::Matrix{T}) where {T} = MeshGrid{T}(X, Y)
      23             : 
      24           6 : MeshGrid(g::Tuple{X, Y}) where {X, Y} = MeshGrid(collect(g[1]), collect(g[2]))
      25             : 
      26           6 : MeshGrid(x, y = x) = MeshGrid(meshgrid(x, y))
      27             : 
      28             : """
      29             :     grid = MeshGrid(X, Y)
      30             :     grid = MeshGrid(x, y = x)
      31             :     grid = MeshGrid(meshgrid(x, y))
      32             : 
      33             : Generate data structure that captures contents generated by
      34             : [`meshgrid`](@ref).
      35             : 
      36             : The constructor calls [`meshgrid`](@ref) for non-matrix arguments
      37             : `x, y`. A `MeshGrid` object can be deconstructed by
      38             : 
      39             :     X, Y = grid[]
      40             : 
      41             : You can "enumerate" all vertex positions as `SVector{2, T}` with
      42             : 
      43             :     collect(grid[:])
      44             :     map(grid[:]) do x
      45             :         @SVector[x..., 0]
      46             :     end
      47             : 
      48             : You can pass `grid::MeshGrid` to `Plots.plot`
      49             : 
      50             :     plot(grid, scatter=false, ...)
      51             : 
      52             : See also [`meshgrid`](@ref), [`perturb!`](@ref), [`triangles`](@ref)
      53             : """ MeshGrid
      54             : 
      55             : """
      56             :     X, Y = MeshGrid(X, Y)
      57             : 
      58             : Deconstruct [`MeshGrid`](@ref) to tuple `(X, Y)`.
      59             : """
      60          16 : Base.getindex(g::MeshGrid) = (g.X, g.Y)
      61             : 
      62             : """
      63             :     collect(grid[:])
      64             : 
      65             : Enumerate vertices of [`MeshGrid`](@ref).
      66             : """
      67           1 : function Base.getindex(g::MeshGrid, ::Colon)
      68           1 :     (SA[xy...] for xy in zip(vec(g.X), vec(g.Y)))
      69             : end
      70             : 
      71             : """
      72             :     m, n = size(grid)
      73             : 
      74             : Get dimensions of [`MesgGrid`](@ref), same as
      75             : 
      76             :     m, n = size(first(grid[]))
      77             : """
      78           4 : function Base.size(g::MeshGrid)
      79           4 :     size(first(g[]))
      80             : end
      81             : 
      82             : """
      83             :     hx, hy = spacing(grid)
      84             : 
      85             : Get distances between knots in x- and y-directions, i.e.,
      86             : the cell size of the [`dual`](@ref) grid.
      87             : 
      88             : See also [`MeshGrid`](@ref)
      89             : """
      90           5 : function spacing(g::MeshGrid)
      91           5 :     X, Y = g[]
      92           5 :     hx = X[1, 2] - X[1, 1]
      93           5 :     hy = Y[2, 1] - Y[1, 1]
      94           5 :     hx, hy
      95             : end
      96             : 
      97             : """
      98             :     d = dual(grid)
      99             : 
     100             : Get dual [`MeshGrid`](@ref) such that each knot in `grid`
     101             : refers to a cell in the dual `d`.
     102             : 
     103             : See also [`primal`](@ref)
     104             : """
     105           1 : function dual(grid::MeshGrid)
     106           1 :     hx, hy = spacing(grid)
     107           1 :     X, Y = grid[]
     108           1 :     m, n = size(X)
     109             : 
     110           1 :     x = range(X[1, 1] - hx/2, X[1, end] + hx/2, length=n+1)
     111           1 :     y = range(Y[1, 1] - hy/2, Y[end, 1] + hy/2, length=m+1)
     112             : 
     113           1 :     MeshGrid(x, y)
     114             : end
     115             : 
     116             : """
     117             :     p = primal(grid)
     118             : 
     119             : Get primal [`MeshGrid`](@ref) from [`dual`](@ref) `grid` such that
     120             : every cell in `grid` refers to a knot in `p`.
     121             : """
     122           1 : function primal(grid::MeshGrid)
     123           1 :     hx, hy = spacing(grid)
     124           1 :     X, Y = grid[]
     125           1 :     m, n = size(X)
     126             : 
     127           1 :     x = range(X[1, 1] + hx/2, X[1, end] - hx/2, length=n-1)
     128           1 :     y = range(Y[1, 1] + hy/2, Y[end, 1] - hy/2, length=m-1)
     129             : 
     130           1 :     MeshGrid(x, y)
     131             : end
     132             : 
     133             : """
     134             :     grid = MeshGrid(x, y)
     135             :     x, y = ranges(grid)
     136             : 
     137             : Get ranges `x` and `y` that generate [`MeshGrid`](@ref) `grid`.
     138             : """
     139           1 : function ranges(grid::MeshGrid)
     140           1 :     X, Y = grid[]
     141           1 :     m, n = size(X)
     142             : 
     143           1 :     x = range(X[1, 1], X[1, end], length=n)
     144           1 :     y = range(Y[1, 1], Y[end, 1], length=m)
     145             : 
     146           1 :     x, y
     147             : end
     148             : 
     149             : """
     150             :     perturb!(grid, p=0.05; boundary::Bool = true)
     151             : 
     152             : Perturb vertex positions of grid with uniform random noise.
     153             : `p` is the maximum amplitude relative to the cell size. If
     154             : `boundary = false`, boundary vertices will not be perturbed.
     155             : 
     156             : !!! warning
     157             :     This method does not check whether cells "flip orientation"!
     158             : 
     159             : See also [`MeshGrid`](@ref)
     160             : """
     161           3 : function perturb!(g::MeshGrid, p = 0.05; boundary = true)
     162           1 :     R = CartesianIndices(g.X)
     163           1 :     Ifirst, Ilast = first(R), last(R)
     164           1 :     I1 = oneunit(Ifirst)
     165             : 
     166           1 :     if !boundary
     167           1 :         @assert all(size(g.X) .> 2)
     168           1 :         Ifirst += I1
     169           1 :         Ilast -= I1
     170             :     end
     171             : 
     172           1 :     hx = abs(g.X[1, 2] - g.X[1, 1]) * 2p
     173           1 :     hy = abs(g.Y[2, 1] - g.Y[1, 1]) * 2p
     174             : 
     175           1 :     for i in Ifirst:Ilast
     176           1 :         g.X[i] += (rand() - 0.5) * hx
     177           1 :         g.Y[i] += (rand() - 0.5) * hy
     178           1 :     end
     179             : end
     180             : 
     181             : """
     182             :     triangles(grid) :: Vector
     183             : 
     184             : Collect triangles in `MeshGrid` with each cell split into
     185             : two triangles. Each triangle is represented as a triple of
     186             : linear vertex indices.
     187             : 
     188             : See also [`MeshGrid`](@ref)
     189             : """
     190           1 : triangles(g::MeshGrid) = _triangles(size(g.X)...)
     191             : 
     192           1 : function _triangles(m, n)
     193           1 :     sz = max(0, (m-1) * (n-1) * 2)
     194           2 :     t = Vector{Tuple{Int, Int, Int}}(undef, sz)
     195             : 
     196           1 :     L = LinearIndices((m, n))
     197             : 
     198           1 :     k = 1
     199           1 :     for i in 1:m-1, j in 1:n-1
     200           2 :         t[k  ] = (L[i+1, j], L[i+1, j+1], L[i, j])
     201           2 :         t[k+1] = (L[i, j], L[i+1, j+1], L[i, j+1])
     202           2 :         k += 2
     203           2 :     end
     204             : 
     205           1 :     t
     206             : end

Generated by: LCOV version 1.16