package com.aaaaahhhhhhh.bananapuncher714.mesh; /** * A half-edge class used to represent a DCEL(doubly connected edge list): https://en.wikipedia.org/wiki/Doubly_connected_edge_list * * In this case, it is intended to be used for a 2d PSLG(planar straight-line graph), but can probably be repurposed * for some other nefarious use case. * * An edge is a line segment connecting an origin to a destination. A half-edge is a partial line segment containing only * the origin, but not the destination. * * Each half-edge structure contains: * - A reference to the origin * - A reference to the symmetrical, or opposite half-edge which is attached to the destination. * - A reference to the next clockwise half-edge of the opposite half edge. This can be used to * traverse counter-clockwise through the half-edges of a polygon. * - A reference to the previous edge, which is the next counter-clockwise edge of this half edge. * This can be used to traverse all the edges of a vertex, in counter clockwise order. * * For a given half-edge attached to two points that contain no other edges, the previous edge is * itself, and the next edge is the same as the opposite edge. * * There are 2 main operations that are used when manipulating half-edges to attach/detach them from * a vertex: * - splicing * - splitting * * Splicing is the most important of the 2 operations, by a large margin. Given two half-edges, A and B, * swap the previous edges, and set the next edge for both of the previous edges to the opposite half edge. * This has the desired effect of either detaching two edges if they are connected to the same vertex, or * attaching two separate half-edges together to the same vertex. * * Splitting is adding an edge between the opposite of an edge and attaching it to the destination. It * splits a single edge into two edges with a vertex in the middle. * * @author BananaPuncher714 */ public class HalfEdge { /** * The origin */ protected Vertex origin; /** * The symmetrical half-edge */ protected HalfEdge sym; /** * The half-edge counter-clockwise on the same origin */ protected HalfEdge prev; /** * The half-edge clockwise from the symmetrical half-edge */ protected HalfEdge next; // TODO Consider adding a constructor with a origin and destination public HalfEdge() { init(); // Whenever we create a new half-edge, we need to make // sure that it has a symmetrical half-edge. new HalfEdge( this ); } private HalfEdge( HalfEdge o ) { init(); sym = o; o.sym = this; next = o; o.next = this; } private void init() { origin = new Vertex( this ); prev = this; } public Vertex getOrigin() { return origin; } /** * Set the origin. Does not affect any additional * * @param vert * @return */ public HalfEdge setOrigin( Vertex vert ) { origin = vert; return this; } public HalfEdge getSym() { return sym; } public HalfEdge getPrev() { return prev; } /** * Set the previous half-edge. Does not update additional properties. * * @param edge The half-edge to set as the previous half-edge. * @return This half-edge */ public HalfEdge setPrev( HalfEdge edge ) { this.prev = edge; return this; } public HalfEdge getNext() { return next; } /** * Set the next half-edge. Does not update additional properties. * * @param edge The half-edge to set as the next half-edge. * @return This half-edge */ public HalfEdge setNext( HalfEdge edge ) { this.next = edge; return this; } public Vertex getDest() { return sym.origin; } /** * Get the difference between the destination position and the origin position * * @return A vector representing the destination minus the origin */ public Vector2d toVector2d() { return getDest().getPosition().subtracted( getOrigin().getPosition() ); } /** * Check if the origin and destination are the same * * @return If the origin is equal to the destination */ public boolean isZero() { return getOrigin().equals( getDest() ); } /** * Split this half-edge in half and return the newly created * half-edge, as the next half-edge of this half-edge. * * @return A new half-edge with a default initialized origin. */ public HalfEdge split() { final HalfEdge edge = new HalfEdge(); splice( edge, getNext() ); splice( getSym(), getNext() ); splice( getSym(), edge.getSym() ); edge.setOrigin( getDest() ); edge.getOrigin().setEdge( edge ); getSym().setOrigin( edge.getDest() ); getDest().setEdge( getSym() ); edge.getOrigin().setEdge( edge ); return edge.getSym(); } /** * Splice two half-edges together. A commutative operation. * * This does _not_ modify any vertices. * * @param a The first half-edge * @param b The second half-edge */ public static void splice( HalfEdge a, HalfEdge b ) { final HalfEdge ap = a.prev; final HalfEdge bp = b.prev; a.prev = bp; b.prev = ap; ap.sym.next = b; bp.sym.next = a; } @Override public String toString() { return String.format( "HalfEdge{orig=%1$s,dest=%2$s}", getOrigin(), getDest() ); } }