use std::{
fmt,
marker::PhantomData,
ops,
};
use optional::Optioned as Opt;
use typebool::True;
use crate::{
hsize,
prelude::*,
map::{DenseMap, set::DenseSet},
};
use super::{
Checked, OptionalField, OmitField, TriFaces, SplitEdgeWithFacesResult,
util::FieldStorage,
};
use self::adj::{CwVertexCirculator, CwVertexCirculatorState};
mod adj;
#[cfg(test)]
mod tests;
const NON_MANIFOLD_EDGE_ERR: &str = "new face would add a non-manifold edge";
pub trait Config: 'static {
type NextEdge: OptionalField;
type PrevEdge: OptionalField;
}
#[allow(missing_debug_implementations)]
pub enum DefaultConfig {}
impl Config for DefaultConfig {
type NextEdge = OmitField;
type PrevEdge = OmitField;
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
struct HalfEdgeHandle(hsize);
impl Handle for HalfEdgeHandle {
#[inline(always)]
fn new(id: hsize) -> Self {
HalfEdgeHandle(id)
}
#[inline(always)]
fn idx(&self) -> hsize {
self.0
}
}
impl HalfEdgeHandle {
#[inline(always)]
fn first_around(fh: FaceHandle) -> Self {
Self::new(fh.idx() * 3)
}
#[inline(always)]
fn face(&self) -> FaceHandle {
FaceHandle::new(self.idx() / 3)
}
}
impl fmt::Debug for HalfEdgeHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "HE{}", self.idx())
}
}
#[doc = include_str!("diagram.svg")]
#[derive(Empty)]
pub struct DirectedEdgeMesh<C: Config = DefaultConfig> {
vertices: DenseMap<VertexHandle, Vertex>,
half_edges: DenseMap<HalfEdgeHandle, HalfEdge<C>>,
_config: PhantomData<C>,
}
#[derive(Clone, Copy)]
pub(crate) struct Vertex {
outgoing: Opt<Checked<HalfEdgeHandle>>,
}
#[derive(Clone, Copy, PartialEq)]
struct EncodedTwin(hsize);
const TWIN_MASK: hsize = 1 << (std::mem::size_of::<hsize>() * 8 - 1);
impl EncodedTwin {
#[inline(always)]
unsafe fn dummy() -> Self {
Self(0)
}
#[inline(always)]
fn next_boundary_he(he: Checked<HalfEdgeHandle>) -> Self {
Self(he.idx() | TWIN_MASK)
}
#[inline(always)]
fn twin(he: Checked<HalfEdgeHandle>) -> Self {
Self(he.idx())
}
fn decode(&self) -> Twin {
if self.is_real_twin() {
unsafe { Twin::Twin(Checked::new(HalfEdgeHandle::new(self.0))) }
} else {
unsafe { Twin::NextBoundaryHe(Checked::new(HalfEdgeHandle::new(self.0 & !TWIN_MASK))) }
}
}
fn or_next_boundary_he(&self) -> Checked<HalfEdgeHandle> {
unsafe { Checked::new(HalfEdgeHandle::new(self.0 & !TWIN_MASK)) }
}
fn to_next_boundary_he(&self) -> EncodedTwin {
Self(self.0 | TWIN_MASK)
}
#[inline(always)]
fn is_real_twin(&self) -> bool {
self.0 & TWIN_MASK == 0
}
fn as_real_twin(&self) -> Option<Checked<HalfEdgeHandle>> {
if self.is_real_twin() {
unsafe { Some(Checked::new(HalfEdgeHandle::new(self.0))) }
} else {
None
}
}
fn as_next_boundary_he(&self) -> Option<Checked<HalfEdgeHandle>> {
if self.is_real_twin() {
None
} else {
unsafe { Some(Checked::new(HalfEdgeHandle::new(self.0 & !TWIN_MASK))) }
}
}
}
#[derive(Clone, Copy)]
enum Twin {
Twin(Checked<HalfEdgeHandle>),
NextBoundaryHe(Checked<HalfEdgeHandle>),
}
pub(crate) struct HalfEdge<C: Config> {
twin: EncodedTwin,
target: Checked<VertexHandle>,
next: <C::NextEdge as OptionalField>::Storage<Checked<HalfEdgeHandle>>,
prev: <C::PrevEdge as OptionalField>::Storage<Checked<HalfEdgeHandle>>,
}
impl<C: Config> HalfEdge<C> {
unsafe fn dummy_to(target: Checked<VertexHandle>) -> Self {
Self {
twin: EncodedTwin::dummy(),
target,
next: Checked::new(HalfEdgeHandle::new(0)).into(),
prev: Checked::new(HalfEdgeHandle::new(0)).into(),
}
}
fn is_boundary(&self) -> bool {
!self.twin.is_real_twin()
}
}
impl<C: Config> fmt::Debug for DirectedEdgeMesh<C> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("DirectedEdgeMesh")
.field("vertices", &self.vertices)
.field("half_edges", &self.half_edges)
.finish()
}
}
impl<C: Config> Clone for DirectedEdgeMesh<C> {
fn clone(&self) -> Self {
Self {
vertices: self.vertices.clone(),
half_edges: self.half_edges.clone(),
_config: PhantomData,
}
}
}
impl fmt::Debug for Vertex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Vertex {{ outgoing: {:?} }}", self.outgoing)
}
}
impl fmt::Debug for EncodedTwin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.decode().fmt(f)
}
}
impl fmt::Debug for Twin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Twin::Twin(h) => write!(f, "Twin({:?})", h),
Twin::NextBoundaryHe(h) => write!(f, "NextB({:?})", h),
}
}
}
impl<C: Config> Copy for HalfEdge<C> {}
impl<C: Config> Clone for HalfEdge<C> {
fn clone(&self) -> Self {
Self {
twin: self.twin.clone(),
target: self.target.clone(),
next: self.next.clone(),
prev: self.prev.clone(),
}
}
}
impl<C: Config> fmt::Debug for HalfEdge<C> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let next = self.next.into_option()
.map(|next| format!(" next: {:6}", format!("{:?},", next)))
.unwrap_or("".into());
let prev = self.prev.into_option()
.map(|prev| format!(" prev: {:6}", format!("{:?},", prev)))
.unwrap_or("".into());
write!(
f,
"HalfEdge {{ target: {:5}{}{} twin: {:?} }}",
format!("{:?},", self.target),
next,
prev,
self.twin,
)
}
}
impl<C: Config> DirectedEdgeMesh<C> {
fn check_vertex(&self, vh: VertexHandle) -> Checked<VertexHandle> {
if self.vertices.contains_handle(vh) {
unsafe { Checked::new(vh) }
} else {
panic!(
"{:?} was passed to a directed edge mesh, but this vertex does not \
exist in this mesh",
vh,
);
}
}
fn check_face(&self, fh: FaceHandle) {
let heh = HalfEdgeHandle::first_around(fh);
if !self.half_edges.contains_handle(heh) {
panic!(
"{:?} was passed to a directed edge mesh, but this face does not \
exist in this mesh",
fh,
);
}
}
fn checked_half_edges_around(&self, fh: FaceHandle) -> [Checked<HalfEdgeHandle>; 3] {
let heh = HalfEdgeHandle::first_around(fh);
if self.half_edges.contains_handle(heh) {
unsafe { [
Checked::new(heh),
Checked::new(HalfEdgeHandle::new(heh.idx() + 1)),
Checked::new(HalfEdgeHandle::new(heh.idx() + 2)),
] }
} else {
panic!(
"{:?} was passed to a directed edge mesh, but this face does not \
exist in this mesh",
fh,
);
}
}
fn he_between(
&self,
from: Checked<VertexHandle>,
to: Checked<VertexHandle>,
) -> Option<Checked<HalfEdgeHandle>> {
self.circulate_around_vertex(from)
.find(|&outgoing| self[outgoing].target == to)
}
fn circulate_around_vertex(&self, center: Checked<VertexHandle>) -> CwVertexCirculator<'_, C> {
let state = match self[center].outgoing.into_option() {
None => CwVertexCirculatorState::Empty,
Some(start_he) => CwVertexCirculatorState::NonEmpty {
current_he: start_he,
start_he,
}
};
CwVertexCirculator { mesh: self, state }
}
fn next_he(&self, he: Checked<HalfEdgeHandle>) -> Checked<HalfEdgeHandle> {
if let Some(next) = self[he].next.into_option() {
return next;
}
let idx = he.idx() + 1;
let next = if is_divisible_by_3(idx) {
idx - 3
} else {
idx
};
unsafe { Checked::new(HalfEdgeHandle::new(next)) }
}
fn prev_he(&self, he: Checked<HalfEdgeHandle>) -> Checked<HalfEdgeHandle> {
if let Some(prev) = self[he].prev.into_option() {
return prev;
}
let prev = if is_divisible_by_3(he.idx()) {
he.idx() + 2
} else {
he.idx() - 1
};
unsafe { Checked::new(HalfEdgeHandle::new(prev)) }
}
fn set_twins(&mut self, a: Checked<HalfEdgeHandle>, b: Checked<HalfEdgeHandle>) {
self[a].twin = EncodedTwin::twin(b);
self[b].twin = EncodedTwin::twin(a);
}
fn push_half_edge_triple(
&mut self,
[a, b, c]: [HalfEdge<C>; 3],
) -> [Checked<HalfEdgeHandle>; 3] {
let ah = unsafe { Checked::new(self.half_edges.push(a)) };
let bh = unsafe { Checked::new(self.half_edges.push(b)) };
let ch = unsafe { Checked::new(self.half_edges.push(c)) };
self[ah].next = bh.into();
self[bh].next = ch.into();
self[ch].next = ah.into();
self[ah].prev = ch.into();
self[bh].prev = ah.into();
self[ch].prev = bh.into();
[ah, bh, ch]
}
}
#[inline(always)]
fn is_divisible_by_3(idx: hsize) -> bool {
#[cfg(not(feature = "large-handle"))]
#[inline(always)]
fn check(idx: u32) -> bool {
idx.wrapping_mul(0xaaaaaaab) <= 0x55555555
}
#[cfg(feature = "large-handle")]
#[inline(always)]
fn check(idx: u64) -> bool {
idx.wrapping_mul(0xaaaaaaaaaaaaaaab) <= 0x5555555555555555
}
check(idx)
}
macro_rules! impl_index {
($handle:ident, $field:ident, $c:ident, $out:ty) => {
impl<$c: Config> ops::Index<Checked<$handle>> for DirectedEdgeMesh<$c> {
type Output = $out;
#[inline(always)]
fn index(&self, idx: Checked<$handle>) -> &Self::Output {
unsafe { self.$field.get_unchecked(*idx) }
}
}
impl<$c: Config> ops::IndexMut<Checked<$handle>> for DirectedEdgeMesh<$c> {
#[inline(always)]
fn index_mut(&mut self, idx: Checked<$handle>) -> &mut Self::Output {
unsafe { self.$field.get_unchecked_mut(*idx) }
}
}
}
}
impl_index!(VertexHandle, vertices, C, Vertex);
impl_index!(HalfEdgeHandle, half_edges, C, HalfEdge<C>);
impl<C: Config> Mesh for DirectedEdgeMesh<C> {
type FaceKind = TriFaces;
type Orientable = True;
fn num_vertices(&self) -> hsize {
self.vertices.num_elements()
}
fn next_vertex_handle_from(&self, start: VertexHandle) -> Option<VertexHandle> {
(start.idx()..self.vertices.next_push_handle().idx())
.map(VertexHandle::new)
.find(|&vh| self.vertices.contains_handle(vh))
}
fn next_face_handle_from(&self, start: FaceHandle) -> Option<FaceHandle> {
(start.idx()..self.half_edges.next_push_handle().idx() / 3)
.map(|i| HalfEdgeHandle::new(i * 3))
.find(|&heh| self.half_edges.contains_handle(heh))
.map(|he| he.face())
}
fn last_vertex_handle(&self) -> Option<VertexHandle> {
self.vertices.last_handle()
}
fn last_face_handle(&self) -> Option<FaceHandle> {
self.half_edges.last_handle().map(|he| he.face())
}
fn contains_vertex(&self, vertex: VertexHandle) -> bool {
self.vertices.contains_handle(vertex)
}
fn num_faces(&self) -> hsize {
self.half_edges.num_elements() / 3
}
fn contains_face(&self, face: FaceHandle) -> bool {
self.half_edges.contains_handle(HalfEdgeHandle::first_around(face))
}
fn num_edges(&self) -> hsize
where
Self: EdgeMesh
{
unreachable!()
}
fn next_edge_handle_from(&self, _: EdgeHandle) -> Option<EdgeHandle>
where
Self: EdgeMesh
{
unreachable!()
}
fn last_edge_handle(&self) -> Option<EdgeHandle>
where
Self: EdgeMesh
{
unreachable!()
}
fn check_integrity(&self) {
if self.half_edges.num_elements() % 3 != 0 {
panic!("bug: number of half edges not divisible by 3");
}
for (vh, v) in self.vertices.iter() {
if let Some(outgoing) = v.outgoing.into_option() {
if !self.half_edges.contains_handle(*outgoing) {
panic!(
"bug (broken reference): [{:?}].outgoing = Some({:?}), but that \
half edge does not exist!",
vh,
outgoing,
);
}
if let Some(incoming) = self[outgoing].twin.as_real_twin() {
if *self[incoming].target != vh {
panic!(
"bug: [{:?}].outgoing = Some({:?}), but [{:?}].twin = {:?} \
and [{:?}].target = {:?} (should be {:?})",
vh,
*outgoing,
*outgoing,
*incoming,
*incoming,
*self[incoming].target,
vh,
);
}
}
}
}
for fh in self.face_handles() {
let [heh0, heh1, heh2] = self.checked_half_edges_around(fh);
for &heh in &[heh0, heh1, heh2] {
if !self.half_edges.contains_handle(*heh) {
panic!(
"bug: {:?} (returned by `checked_half_edges_around({:?})`) \
does not exist in `self.half_edges`",
heh,
fh,
);
}
let he = self[heh];
if self[he.target].outgoing.is_none() {
panic!(
"bug: [{:?}].target = {:?}, but [{:?}].outgoing = None",
heh,
he.target,
he.target,
);
}
match he.twin.decode() {
Twin::Twin(twin) => {
if self[twin].twin != EncodedTwin::twin(heh) {
panic!(
"bug: [{:?}].twin = {:?}, but [{:?}].twin = {:?}",
heh,
he.twin,
twin,
self[twin].twin,
);
}
}
Twin::NextBoundaryHe(next) => {
if !self.half_edges.contains_handle(*next) {
panic!(
"bug: [{:?}].twin = {:?}, but {:?} does not exist",
heh,
he.twin,
next,
);
}
}
}
}
macro_rules! check_next_prev {
($prev:ident -> $next:ident) => {
if let Some(next) = self[$prev].next.into_option() {
if next != $next {
panic!(
"[{:?}].next = {:?}, but should be {:?}",
$prev,
next,
$next,
);
}
}
if let Some(prev) = self[$next].prev.into_option() {
if prev != $prev {
panic!(
"[{:?}].prev = {:?}, but should be {:?}",
$next,
prev,
$prev,
);
}
}
};
}
check_next_prev!(heh0 -> heh1);
check_next_prev!(heh1 -> heh2);
check_next_prev!(heh2 -> heh0);
}
let mut visited = DenseSet::with_capacity(self.half_edges.num_elements());
for (start, he) in self.half_edges.iter() {
if visited.contains_handle(start) {
continue;
}
if he.is_boundary() {
let mut heh = start;
loop {
if visited.insert(heh) {
panic!(
"bug: encountered {:?} while iterating on boundary starting \
from {:?}, but we already visited it!",
heh,
start,
);
}
heh = match self.half_edges[heh].twin.decode() {
Twin::Twin(_) => {
panic!(
"bug: encountered {:?} while iterating on boundary starting \
from {:?}, but [{:?}].twin = {:?} (should be pointing \
to the next boundary edge)!",
heh,
start,
heh,
self.half_edges[heh].twin,
);
}
Twin::NextBoundaryHe(next) => *next,
};
if heh == start {
break;
}
}
}
}
}
}
impl<C: Config> MeshMut for DirectedEdgeMesh<C> {
fn add_vertex(&mut self) -> VertexHandle {
self.vertices.push(Vertex {
outgoing: Opt::none()
})
}
fn add_triangle(&mut self, [a, b, c]: [VertexHandle; 3]) -> FaceHandle {
assert_ne!(a, b, "vertices of new face are not unique");
assert_ne!(a, c, "vertices of new face are not unique");
let vertices = [self.check_vertex(a), self.check_vertex(b), self.check_vertex(c)];
let outer_hes = [
self.he_between(vertices[1], vertices[0]),
self.he_between(vertices[2], vertices[1]),
self.he_between(vertices[0], vertices[2]),
];
let inner_hes = self.push_half_edge_triple([
unsafe { HalfEdge::dummy_to(vertices[1]) },
unsafe { HalfEdge::dummy_to(vertices[2]) },
unsafe { HalfEdge::dummy_to(vertices[0]) },
]);
for idx in 0..vertices.len() {
let prev_idx = idx.checked_sub(1).unwrap_or(2);
let vh = vertices[idx];
let incoming_inner = inner_hes[prev_idx];
let outgoing_inner = inner_hes[idx];
let incoming_outer = outer_hes[idx];
let outgoing_outer = outer_hes[prev_idx];
let v = &self[vh];
match (incoming_outer, outgoing_outer) {
(None, None) => {
if let Some(outgoing_from_v) = v.outgoing.into_option() {
if let Some(start) = self[outgoing_from_v].twin.as_next_boundary_he() {
self[outgoing_inner].twin
= EncodedTwin::next_boundary_he(start);
self[outgoing_from_v].twin
= EncodedTwin::next_boundary_he(incoming_inner);
} else {
panic!(
"new triangle {:?} would create non-manifold vertex (cycle \
around vertex {:?} already closed)",
[a, b, c],
v,
);
}
} else {
self[outgoing_inner].twin
= EncodedTwin::next_boundary_he(incoming_inner);
self[vh].outgoing = Opt::some(outgoing_inner);
}
}
(Some(incoming_outer), None) => {
let before_new = self.circulate_around_vertex(vh).find(|&outgoing| {
self[outgoing].twin.as_next_boundary_he() == Some(incoming_outer)
}).expect(NON_MANIFOLD_EDGE_ERR);
self[before_new].twin = EncodedTwin::next_boundary_he(incoming_inner);
}
(None, Some(outgoing_outer)) => {
self[outgoing_inner].twin = self[outgoing_outer].twin;
self[vh].outgoing = Opt::some(outgoing_inner);
}
(Some(incoming_outer), Some(outgoing_outer)) => {
let ib_end = {
let start = self.next_he(incoming_outer);
let mut e = start;
while let Some(twin) = self[e].twin.as_real_twin() {
e = self.next_he(twin);
if e == start {
panic!("{}", NON_MANIFOLD_EDGE_ERR);
}
}
e
};
if self[outgoing_outer].twin.as_next_boundary_he() != Some(incoming_outer) {
let bib_end = self.circulate_around_vertex(vh).find(|&outgoing| {
self[outgoing].twin.as_next_boundary_he() == Some(incoming_outer)
}).expect("internal DEM bug: couldn't find `bib_end`");
self[bib_end].twin = self[ib_end].twin;
self[ib_end].twin = self[outgoing_outer].twin;
self[vh].outgoing = Opt::some(bib_end);
} else {
self[vh].outgoing = Opt::some(ib_end);
}
}
}
}
for (&outer, &inner) in outer_hes.iter().zip(&inner_hes) {
if let Some(outer) = outer {
self[outer].twin = EncodedTwin::twin(inner);
self[inner].twin = EncodedTwin::twin(outer);
}
}
inner_hes[0].face()
}
#[inline(never)]
fn reserve_for_vertices(&mut self, count: hsize) {
self.vertices.reserve(count);
}
#[inline(never)]
fn reserve_for_faces(&mut self, count: hsize) {
self.half_edges.reserve(count * 3);
}
fn remove_all_vertices(&mut self) {
assert!(
self.num_faces() == 0,
"call to `remove_all_vertices`, but there are faces in the mesh!",
);
self.vertices.clear();
}
fn remove_all_faces(&mut self) {
self.half_edges.clear();
for v in self.vertices.values_mut() {
v.outgoing = Opt::none();
}
}
fn split_face(&mut self, f: FaceHandle) -> VertexHandle {
let [he_ab_orig, he_bc, he_ca_orig] = self.checked_half_edges_around(f);
let vb = self[he_ab_orig].target;
let vc = self[he_bc].target;
let va = self[he_ca_orig].target;
let has_ab_as_next = self.circulate_around_vertex(vb)
.find(|&outgoing| self[outgoing].twin == EncodedTwin::next_boundary_he(he_ab_orig));
let has_ca_as_next = self.circulate_around_vertex(va)
.find(|&outgoing| self[outgoing].twin == EncodedTwin::next_boundary_he(he_ca_orig));
let has_ab_as_twin = self[he_ab_orig].twin.as_real_twin();
let has_ca_as_twin = self[he_ca_orig].twin.as_real_twin();
let vm = unsafe { Checked::new(self.add_vertex()) };
let [he_am, he_mc, he_ca] = self.push_half_edge_triple([
unsafe { HalfEdge::dummy_to(vm) },
unsafe { HalfEdge::dummy_to(vc) },
self[he_ca_orig],
]);
let [he_bm, he_ma, he_ab] = self.push_half_edge_triple([
unsafe { HalfEdge::dummy_to(vm) },
unsafe { HalfEdge::dummy_to(va) },
self[he_ab_orig],
]);
self[he_ca_orig].target = vm;
self[he_ab_orig].target = vb;
let he_cm = he_ca_orig;
let he_mb = he_ab_orig;
self[vm].outgoing = Opt::some(he_ma);
self.set_twins(he_am, he_ma);
self.set_twins(he_bm, he_mb);
self.set_twins(he_cm, he_mc);
if let Some(he) = has_ab_as_next {
self[he].twin = EncodedTwin::next_boundary_he(he_ab);
}
if let Some(he) = has_ca_as_next {
let he = if he == he_ab_orig { he_ab } else { he };
self[he].twin = EncodedTwin::next_boundary_he(he_ca);
}
if let Some(he) = has_ab_as_twin {
self[he].twin = EncodedTwin::twin(he_ab);
}
if let Some(he) = has_ca_as_twin {
self[he].twin = EncodedTwin::twin(he_ca);
}
if self[va].outgoing == Opt::some(he_ab_orig) {
self[va].outgoing = Opt::some(he_ab);
}
if self[vc].outgoing == Opt::some(he_ca_orig) {
self[vc].outgoing = Opt::some(he_ca);
}
*vm
}
fn remove_isolated_vertex(&mut self, v: VertexHandle) {
assert!(
self.vertices[v].outgoing.is_none(),
"{:?} is not isolated but was passed to `remove_isolated_vertex",
v,
);
self.vertices.remove(v);
}
fn remove_face(&mut self, f: FaceHandle) {
let [he0, he1, he2] = self.checked_half_edges_around(f);
let corners = [[he0, he1, he2], [he1, he2, he0], [he2, he0, he1]];
for &[incoming, outgoing, opposite_of_v] in &corners {
let v = self[incoming].target;
if let Twin::Twin(incoming_outer) = self[outgoing].twin.decode() {
self[incoming_outer].twin = self[opposite_of_v].twin.to_next_boundary_he();
}
match (self[outgoing].twin.decode(), self[incoming].twin.decode()) {
(Twin::NextBoundaryHe(next), Twin::NextBoundaryHe(_)) if next == incoming => {
self[v].outgoing = Opt::none();
}
(_, Twin::NextBoundaryHe(_)) => {
let end = CwVertexCirculator::new(self, outgoing).find(|&outgoing| {
self[outgoing].twin.as_next_boundary_he() == Some(incoming)
}).expect("DEM bug: invalid cycle around vertex");
self[end].twin = self[outgoing].twin.to_next_boundary_he();
if self[v].outgoing == Opt::some(outgoing) {
self[v].outgoing = Opt::some(end);
}
}
(_, Twin::Twin(outgoing_outer)) => {
self[outgoing_outer].twin = self[outgoing].twin.to_next_boundary_he();
self[v].outgoing = Opt::some(outgoing_outer);
}
}
}
self.half_edges.remove(*he0);
self.half_edges.remove(*he1);
self.half_edges.remove(*he2);
}
fn add_face(&mut self, _: &[VertexHandle]) -> FaceHandle
where
Self: PolyMesh
{
unreachable!()
}
fn flip_edge(&mut self, _: EdgeHandle)
where
Self: EdgeMesh + TriMesh
{
unreachable!()
}
fn split_edge_with_faces(&mut self, _: EdgeHandle) -> SplitEdgeWithFacesResult
where
Self: EdgeMesh + TriMesh
{
unreachable!()
}
}
impl<C: Config> SupportsMultiBlade for DirectedEdgeMesh<C> {}