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

          Line data    Source code
       1             : """
       2             :     maycollapse(mesh, h::HHnd) :: Bool
       3             : 
       4             : Determine whether half-edge `h` can be collapsed, i.e., its source
       5             : vertex is "moved into" its destination vertex.
       6             : 
       7             : !!! warning "Precondition"
       8             :     This method **requires a triangle mesh**!
       9             : 
      10             : See also [`collapse!`](@ref)
      11             : """ maycollapse
      12             : 
      13          14 : function maycollapse(mesh::Mesh, h::HHnd)
      14          14 :     @massert isused(mesh, h)
      15             : 
      16          14 :     v0v1 = h
      17          14 :     v1v0 = opposite(mesh, h)
      18             : 
      19             :     # the edges v1-vl and vl-v0 must not be both boundary edges,
      20             :     # the edges v0-vr and vr-v1 must not be both boundary edges
      21             : 
      22          14 :     success, vl = _maycollpase_check_boundary(mesh, v0v1)
      23          19 :     success || return false
      24             : 
      25           9 :     success, vr = _maycollpase_check_boundary(mesh, v1v0)
      26          12 :     success || return false
      27             : 
      28           6 :     (vl != vr) || return false # equal and used or both NoV
      29             : 
      30           6 :     v0 = destination(mesh, v1v0)
      31           6 :     v1 = destination(mesh, v0v1)
      32             : 
      33             :     # edge between two boundary vertices should be a boundary edge
      34           6 :     isboundary(mesh, v0) && isboundary(mesh, v1) &&
      35             :         !isboundary(mesh, v0v1) && !isboundary(mesh, v1v0) && return false
      36             : 
      37             :     # test intersection of the one-rings of v0 and v1
      38          12 :     for v in ring(mesh, v0)
      39          21 :         if (v != v1) && (v != vl) && (v != vr)
      40           7 :             (halfedge(mesh, v, v1) == NoH) || return false
      41             :         end
      42          21 :     end
      43             : 
      44           0 :     true
      45             : end
      46             : 
      47          23 : function _maycollpase_check_boundary(mesh::Mesh, h::HHnd)
      48          23 :     isboundary(mesh, h) && return (true, NoV)
      49             : 
      50          18 :     h1 = next(mesh, h)
      51          18 :     h2 = next(mesh, h1)
      52          18 :     @massert next(mesh, h2) == h "require triangulation"
      53             : 
      54          18 :     isboundary(mesh, opposite(mesh, h1)) &&
      55             :         isboundary(mesh, opposite(mesh, h2)) && return (false, NoV)
      56             : 
      57          10 :     (true, destination(mesh, h1))
      58             : end
      59             : 
      60             : """
      61             :     collapse!(mesh, h::HHnd)
      62             : 
      63             : Apply half-edge collapse such that the [`source`](@ref) vertex of `h`
      64             : is "collapsed into" the [`destination`](@ref) vertex. This way, the
      65             : source vertex and one (`h` [`isboundary`](@ref)) or two triangles
      66             : incident to `h` and associated edges are removed from `mesh` (i.e,
      67             : [`isused`](@ref) yields `false`).
      68             : 
      69             : !!! warning "Precondition"
      70             :     This method **requires a triangle mesh**!
      71             : 
      72             : !!! warning "Precondition"
      73             :     Requires `maycollapse(mesh, h) == true` immediately before
      74             :     `collapse!(mesh, h)`!
      75             : 
      76             : See also [`maycollapse`](@ref)
      77             : 
      78             : """
      79           3 : function collapse!(mesh::Mesh, h::HHnd)
      80           3 :     @massert isused(mesh, h)
      81             : 
      82           3 :     h0 = h
      83           3 :     h1 = prev(mesh, h0)
      84           3 :     o0 = opposite(mesh, h0)
      85           3 :     o1 = next(mesh, o0)
      86             : 
      87           3 :     _collapse_remove_edge(mesh, h0)
      88             : 
      89           6 :     (next(mesh, next(mesh, h1)) != h1) || _collapse_remove_loop(mesh, h1)
      90           3 :     (next(mesh, next(mesh, o1)) != o1) || _collapse_remove_loop(mesh, o1)
      91             : end
      92             : 
      93           3 : function _collapse_remove_edge(mesh::Mesh, h::HHnd)
      94           3 :     @massert isused(mesh, h)
      95             : 
      96           3 :     hn = next(mesh, h)
      97           3 :     hp = prev(mesh, h)
      98             : 
      99           3 :     o = opposite(mesh, h)
     100           3 :     on = next(mesh, o)
     101           3 :     op = prev(mesh, o)
     102             : 
     103           3 :     fh = face(mesh, h)
     104           3 :     fo = face(mesh, o)
     105             : 
     106           3 :     vh = destination(mesh, h)
     107           3 :     vo = destination(mesh, o)
     108             : 
     109             :     # half-edge -> vertex
     110           6 :     for hc in star(mesh, vo)
     111          12 :         _setvertex!(mesh, opposite(mesh, hc), vh)
     112          21 :     end
     113             : 
     114             :     # half-edge -> half-edge
     115           3 :     _setnexthalfedge!(mesh, hp, hn)
     116           3 :     _setnexthalfedge!(mesh, op, on)
     117             : 
     118             :     # face -> half-edge
     119           6 :     (fh == NoF) || _sethalfedge!(mesh, fh, hn)
     120           5 :     (fo == NoF) || _sethalfedge!(mesh, fo, on)
     121             : 
     122             :     # vertex -> half-edge
     123           4 :     (halfedge(mesh, vh) != o) || _sethalfedge!(mesh, vh, hn)
     124           3 :     _adjustoutgoinghalfedge!(mesh, vh)
     125           3 :     _sethalfedge!(mesh, vo, NoH)
     126             : 
     127           3 :     _setunused!(mesh, vo)
     128           3 :     _setunused!(mesh, edge(mesh, h))
     129             : end
     130             : 
     131           5 : function _collapse_remove_loop(mesh::Mesh, h::HHnd)
     132           5 :     @massert isused(mesh, h)
     133             : 
     134           5 :     h0 = h
     135           5 :     h1 = next(mesh, h0)
     136             : 
     137           5 :     o0 = opposite(mesh, h0)
     138           5 :     o1 = opposite(mesh, h1)
     139             : 
     140           5 :     v0 = destination(mesh, h0)
     141           5 :     v1 = destination(mesh, h1)
     142             : 
     143           5 :     fh = face(mesh, h0)
     144           5 :     fo = face(mesh, o0)
     145             : 
     146           5 :     @massert (next(mesh, h1) == h0) && (h1!= h0) "loop"
     147             : 
     148             :     # half-edge -> half-edge
     149           5 :     _setnexthalfedge!(mesh, h1, next(mesh, o0))
     150           5 :     _setnexthalfedge!(mesh, prev(mesh, o0), h1)
     151             : 
     152             :     # half-edge -> face
     153           5 :     _setface!(mesh, h1, fo)
     154             : 
     155             :     # vertex -> half-edge
     156           5 :     _sethalfedge!(mesh, v0, h1)
     157           5 :     _adjustoutgoinghalfedge!(mesh, v0)
     158           5 :     _sethalfedge!(mesh, v1, o1)
     159           5 :     _adjustoutgoinghalfedge!(mesh, v1)
     160             : 
     161             :     # face -> half-edge
     162           5 :     (fo != NoF) && (halfedge(mesh, fo) == o0) && _sethalfedge!(mesh, fo, h1)
     163             : 
     164           5 :     (fh != NoF) && _setunused!(mesh, fh)
     165           5 :     _setunused!(mesh, edge(mesh, h))
     166             : end

Generated by: LCOV version 1.16