LCOV - code coverage report
Current view: top level - src - off.jl (source / functions) Hit Total Coverage
Test: on branch nothing Lines: 72 79 91.1 %
Date: 2025-07-10 13:12:25 Functions: 0 0 -

          Line data    Source code
       1             : # TODO: split file and move format independent methods
       2             : 
       3             : # TODO: silently ignore uv argument if not supported (change!)
       4             : 
       5             : 
       6             : # TODO: alternatively specify function VHnd -> pos (write)
       7             : #       application "2d height field [xy ; z] -> 3d coordinate"
       8             : #       (note: splitting for read is more difficult)
       9             : 
      10             : # TODO: OFF support uv section ... see pmp
      11             : #       requires map attribute class to attribute
      12             : 
      13           4 : function _read(io::IO,
      14             :                ::Val{:off},
      15             :                template::Mesh{V, F, H, E};
      16             :                position=Val(:x), uv::Nothing=nothing) where {V, F, H, E}
      17           2 :     match(r"^\s*OFF\s*$", readline(io)) !== nothing ||
      18             :         error("invalid header")
      19             : 
      20           8 :     n, m, e = map(w -> parse(Int, w), split(readline(io)))
      21             : 
      22           2 :     (m>=0 && n>=0) || error("negative size")
      23           2 :     e==0 || error("expect 0 edges")
      24             : 
      25           2 :     mesh = similar(template)
      26             : 
      27           2 :     sizehint!(mesh, (n, m))
      28           2 :     ax = vattr(mesh, position)
      29           2 :     isenabled(ax) || error("require positions :x")
      30             : 
      31           2 :     vtyp = eltype(ax)
      32           2 :     typ = eltype(vtyp)
      33             : 
      34           2 :     d = length(vtyp)
      35             : 
      36           2 :     for i = 1:n
      37          48 :         coords = map(w -> parse(typ, w), split(readline(io)))
      38          12 :         length(coords) == 3 || error("invalid coordinate")
      39          12 :         x = vtyp(coords[1:d]...)
      40          12 :         v = addvertex!(mesh)
      41          12 :         ax[v] = x
      42          22 :     end
      43             : 
      44           2 :     for i = 1:m
      45          20 :         tf = map(w -> parse(Int, w), split(readline(io)))
      46           4 :         (length(tf) >= 3) || error("invalid face")
      47             : 
      48           4 :         d = tf[1]
      49           4 :         (length(tf) == 1 + d) || error("inconsistent face")
      50             : 
      51           4 :         vs = VHnd.(tf[2:end] .+ 1)
      52             : 
      53           4 :         all(1 <= Int(v) <= n for v in vs) || error("invalid index")
      54             : 
      55           4 :         f = addface!(mesh, vs)
      56           4 :         f !== nothing || error("cannot add face $(vs)")
      57           6 :     end
      58             : 
      59           2 :     mesh
      60             : end
      61             : 
      62           4 : function Base.read(fmt::Val{S}, filename::AbstractString,
      63             :                    template::Mesh{V, F, H, E};
      64             :                    position=Val(:x), uv=nothing,
      65             :                    exceptions::Bool=false) where {S, V, F, H, E}
      66           2 :     try
      67           2 :         open(filename) do f
      68           2 :             _read(f, fmt, template; position, uv)
      69             :         end
      70             :     catch e
      71           0 :        exceptions && rethrow()
      72           0 :        showerror(stderr, e, Base.catch_backtrace())
      73           0 :        nothing
      74             :     end
      75             : end
      76             : 
      77           2 : Base.read(fmt::Symbol, filename::AbstractString,
      78             :           template::Mesh{V, F, H, E};
      79             :           exceptions::Bool=false, options...) where {V, F, H, E} =
      80             :               read(Val(fmt), filename, template;
      81             :                   exceptions, options...)
      82             : 
      83           2 : _format_from_extension(filename) =
      84             :     splitext(filename)[2][2:end] |> lowercase |> Symbol |> Val
      85             : 
      86           2 : function Base.read(filename::AbstractString, template::Mesh{V, F, H, E};
      87             :                    exceptions::Bool=false,
      88             :                    options...) where {V, F, H, E}
      89           1 :     fmt = _format_from_extension(filename)
      90           1 :     read(fmt, filename, template; exceptions, options...)
      91             : end
      92             : 
      93             : """
      94             :     mesh = read([format,] filename, template::Mesh[;
      95             :                 position=Val(:x), uv=:nothing, exceptions=false])
      96             : 
      97             : Read a polygonal mesh from file `filename` assuming `format`, where
      98             : `template` defines the concrete type of mesh (e.g., which attributes
      99             : exist) and state of attributes (i.e., which attributes are enabled).
     100             : 
     101             : In case `read` fails, the optional argument `exceptions` determines
     102             : whether an exception is thrown or `read` return `nothing`.
     103             : 
     104             : The following formats are currently supported
     105             : 
     106             : - `:off` OFF file, only vertex positions
     107             : - `:obj` OBJ file, vertex positions and texture coordinates (optional)
     108             : 
     109             : See also [`write`](@ref)
     110             : """ read
     111             : 
     112           4 : function _write(io::IO,
     113             :                 ::Val{:off}, mesh::Mesh{V, F, H, E};
     114             :                 position=Val(:x), uv::Nothing=nothing) where {V, F, H, E}
     115           2 :     print(io, "OFF\n$(nv(mesh)) $(nf(mesh)) 0\n")
     116          12 :     vidx = zeros(Int, n_total(vattr(mesh)))
     117             : 
     118           2 :     ax = vattr(mesh, position)
     119           2 :     isenabled(ax) || error("require positions :x")
     120             : 
     121           6 :     x = zeros(3)
     122           2 :     d = min(3, length(zeros(eltype(ax))))
     123             : 
     124           4 :     for (i, v) in enumerate(vertices(mesh))
     125          12 :         vidx[Int(v)] = i
     126             :         # Note: ax is not necessarily a 3-vector
     127          12 :         for j in 1:d
     128          36 :             x[j] = ax[v][j]
     129          60 :         end
     130          12 :         print(io, "$(x[1]) $(x[2]) $(x[3])\n")
     131          22 :     end
     132             : 
     133           4 :     for f in faces(mesh)
     134           4 :         vtx = collect(vertices(mesh, f))
     135           4 :         print(io, length(vtx))
     136           4 :         for v in vtx
     137          12 :             idx = vidx[Int(v)]
     138          12 :             @assert idx > 0
     139          12 :             print(io, " $(idx-1)")
     140          12 :         end
     141           4 :         print(io, "\n")
     142           6 :     end
     143             : 
     144           2 :     io
     145             : end
     146             : 
     147           4 : function Base.write(fmt::Val{S}, filename::AbstractString,
     148             :                     mesh::Mesh{V, F, H, E};
     149             :                     exceptions::Bool=false,
     150             :                     position=Val(:x), uv=nothing) where {S, V, F, H, E}
     151           2 :     try
     152           2 :         open(filename, "w+") do f
     153           2 :             _write(f, fmt, mesh; position, uv)
     154             :         end
     155             :     catch e
     156           0 :         exceptions && rethrow()
     157           0 :         showerror(stderr, e, Base.catch_backtrace())
     158           0 :         false
     159             :     end
     160             : 
     161           0 :     true
     162             : end
     163             : 
     164           2 : Base.write(fmt::Symbol, filename::AbstractString,
     165             :            mesh::Mesh{V, F, H, E};
     166             :            exceptions::Bool=false,
     167             :            position=Val(:x), uv=nothing) where {V, F, H, E} =
     168             :     write(Val(fmt), filename, mesh; exceptions, position, uv)
     169             : 
     170           2 : function Base.write(filename::AbstractString, mesh::Mesh{V, F, H, E};
     171             :                     exceptions::Bool=false,
     172             :                     position=Val(:x), uv=nothing) where {V, F, H, E}
     173           1 :     fmt = _format_from_extension(filename)
     174           1 :     write(fmt, filename, mesh; exceptions, position, uv)
     175             : end
     176             : 
     177             : """
     178             :     success = write([format,] filename, mesh::Mesh[;
     179             :                     position=Val(:x), uv=nothing, exceptions=false])
     180             : 
     181             : Write a polygonal `mesh` to file `filename` using `format`. Returns
     182             : `true` on success.
     183             : 
     184             : In case `write` fails, the optional argument `exceptions` determines
     185             : whether `an exception is thrown or `write` returns `false`.
     186             : 
     187             : The following formats are currently supported
     188             : 
     189             : - `:off` OFF file, only vertex positions
     190             : - `:obj` OBJ file, vertex positions and texture coordinates (optional)
     191             : 
     192             : 
     193             : See also [`read`](@ref)
     194             : """ write

Generated by: LCOV version 1.16