def handle_edge_event_3sides(evt, step, skel, queue, immediate): """Handle a collapse of a triangle with 3 sides collapsing. It does not matter whether the 3-triangle has wavefront edges or not. Important: The triangle vertices should collapse to 1 point. The following steps are performed: - stop the 3 kinetic vertices of the triangle - optionally make a new skeleton node - schedule all neighbours, if any, for immediate processing (these also collapse to the same point) """ now = evt.time t = evt.triangle logging.info("* edge 3sides :: tri>> #{} [{}]".format(id(t), t.info)) logging.debug(evt.side) assert len(evt.side) == 3 # we stop the vertices always at the same geometric location # This means that the triangle collapse leads to 1 point sk_node, newly_made = stop_kvertices(t.vertices, step, now) if newly_made: skel.sk_nodes.append(sk_node) # get neighbours around collapsing triangle, if any, and schedule them for n in t.neighbours: if n is not None and n.event is not None and n.stops_at is None: n.neighbours[n.neighbours.index(t)] = None schedule_immediately(n, now, queue, immediate) # we "remove" the triangle itself t.stops_at = now
def handle_edge_event_1side(evt, step, skel, queue, immediate, pause): """Handle a collapse of a triangle with 1 side collapsing. Important: The triangle collapses to a line segment. """ t = evt.triangle logging.info("* edge 1side :: tri>> #{} [{}]".format(id(t), t.info)) logging.debug(evt.side) assert len(evt.side) == 1, len(evt.side) e = evt.side[0] logging.debug( "wavefront edge collapsing? {0}".format(t.neighbours[e] is None)) now = evt.time v0 = t.vertices[e] v1 = t.vertices[ccw(e)] v2 = t.vertices[cw(e)] # stop the two vertices of this edge and make new skeleton node # replace 2 vertices with new kinetic vertex sk_node, newly_made = stop_kvertices([v1, v2], step, now) if newly_made: skel.sk_nodes.append(sk_node) kv = compute_new_kvertex(v1.ul, v2.ur, now, sk_node, len(skel.vertices) + 1, v1.internal or v2.internal, pause) # FIXME: should we update the left and right wavefront line refs here? logging.debug("Computed new kinetic vertex {} [{}]".format( id(kv), kv.info)) logging.debug("v1 := {} [{}]".format(id(v1), v1.info)) logging.debug("v2 := {} [{}]".format(id(v2), v2.info)) logging.debug(kv.position_at(now)) logging.debug(kv.position_at(now + 1)) # append to skeleton structure, new kinetic vertex skel.vertices.append(kv) sk_node, newly_made = stop_kvertices([v0, kv], step, now) if newly_made: skel.sk_nodes.append(sk_node) # we "remove" the triangle itself t.stops_at = now
def handle_parallel_edge_event_even_legs(t, e, pivot, now, step, skel, queue, immediate): logging.info("* parallel|even :: tri>> #{} [{}]".format(id(t), t.info)) logging.debug('At start of handle_parallel_edge_event with same size legs') logging.debug("Edge with inf fast vertex collapsing! {0}".format(t.neighbours[e] is None)) # FIXME: pre-conditions for this handler # there should be 1 edge with zero length, and other 2 edges should have length? # can it also be that this handler deals with collapse to point ??? # does not hold! -> # triangle can also be like: # *-------------------------* # `---------------*''''''' # we are collapsing the edge opposite of the inf fast pivot vertex # this assumes that v1 and v2 need to be on the same location!!! assert t.vertices.index(pivot) == e assert t.vertices[e] is pivot # assert pivot.inf_fast # stop the non-infinite vertices v1 = t.vertices[ccw(e)] v2 = t.vertices[cw(e)] sk_node, newly_made = stop_kvertices([v1,v2], step, now) if newly_made: skel.sk_nodes.append(sk_node) # stop the pivot as well if pivot.stop_node is not None: logging.debug("Infinite fast pivot already stopped, but should not be stopped(?)") # assert pivot.stop_node is None # assert pivot.stops_at is None pivot.stop_node = sk_node pivot.stops_at = now # this is not necessary, is it? ## update_circ(pivot, v1, now) ## update_circ(v2, pivot, now) # we "remove" the triangle itself t.stops_at = now n = t.neighbours[e] msg = "schedule adjacent neighbour for *IMMEDIATE* processing" if n is not None else "no neighbour to collapse simultaneously" logging.debug("*** neighbour n: {} ".format(msg)) if n is not None: n.neighbours[n.neighbours.index(t)] = None if n.event is not None and n.stops_at is None: logging.debug(n.event) schedule_immediately(n, now, queue, immediate)
def handle_split_event(evt, step, skel, queue, immediate, pause): """Handles a split event where a wavefront edge is hit on its interior This splits the wavefront in two pieces """ t = evt.triangle logging.info("* split :: tri>> #{} [{}]".format(id(t), t.info)) logging.debug("{}".format(t.neighbours)) assert len(evt.side) == 1 e = evt.side[0] now = evt.time v = t.vertices[(e) % 3] n = t.neighbours[e] assert n is None v1 = t.vertices[(e + 1) % 3] v2 = t.vertices[(e + 2) % 3] logging.debug("v1 := {} [{}]".format(id(v1), v1.info)) logging.debug("v2 := {} [{}]".format(id(v2), v2.info)) assert v1.wfr is v2.wfl # ---- new use of wavefronts ------------------------------ # # split leads to 2 bisectors a = v.wfr b = v1.wfr assert v2.wfl is b c = v.wfl # TODO: are we having the correct bisectors here? intersector = WaveFrontIntersector(a, b) # bi0 = intersector.get_bisector() # print(bi0) # intersector = WaveFrontIntersector(a, c) # bi1 = intersector.get_bisector() # print(bi1) # the position of the node is witnessed by 3 pairs of wavefronts try: intersector = WaveFrontIntersector(a, b) pos_at_now = intersector.get_intersection_at_t(now) except ValueError: pass # print("POINT({0[0]} {0[1]})".format(pos_at_now)) try: intersector = WaveFrontIntersector(a, c) pos_at_now = intersector.get_intersection_at_t(now) # print("POINT({0[0]} {0[1]})".format(pos_at_now)) except ValueError: pass try: intersector = WaveFrontIntersector(b, c) pos_at_now = intersector.get_intersection_at_t(now) # print("POINT({0[0]} {0[1]})".format(pos_at_now)) # except ValueError: pass # ---- new use of wavefronts ------------------------------ # sk_node, newly_made = stop_kvertices([v], step, now) # add the skeleton node to the skeleton if newly_made: skel.sk_nodes.append(sk_node) # assert v1.right is v2 # assert v2.left is v1 assert v1.ur is v2.ul # pos_vr = v.right.position_at(now) # pos_v1 = v1.position_at(now) # from grassfire.vectorops import make_vector, mul, add, unit, norm # ha = add(v1.position_at(now), mul(make_vector(pos_vr, pos_v1), 0.5)) # pt halfway a # pos_vl = v.left.position_at(now) # pos_v2 = v2.position_at(now) # hb = add(v2.position_at(now), mul(make_vector(pos_vl, pos_v2), 0.5)) # pt halfway b # with open('/tmpfast/split_pts_halfway.wkt', 'w') as fh: # fh.write('wkt') # fh.write('\n') # fh.write("POINT({0[0]} {0[1]})".format(v.position_at(now))) # fh.write('\n') # fh.write("POINT({0[0]} {0[1]})".format(ha)) # fh.write('\n') # fh.write("POINT({0[0]} {0[1]})".format(hb)) # fh.write('\n') # print('written out points') # # the geometric bisector based on the position of the vertices # bi_b = make_vector(hb, v.position_at(now)) # bi_a = make_vector(ha, v.position_at(now)) # def sign(scalar): # if scalar < 0: # return -1 # elif scalar == 0: # return 0 # else: # return 1 # the position of the stop_node can be computed by: # taking the original wavefronts and translate these lines to 'now' # then intersect them -> stop_node position # maybe this is more robust than relying on the direction vector and the velocity of the original point # a bisector based on the original line equations # BI = compute_crossing_bisector(v.ul, v2.ul, now) vb = compute_new_kvertex(v.ul, v2.ul, now, sk_node, len(skel.vertices) + 1, v.internal or v2.internal, pause) # FIXME: new wavefront vb.wfl = v.wfl vb.wfr = v2.wfl # logging.debug(""" # -- BISECTOR BI {} # bi_b {} # vb.v {}""".format(BI, bi_b, vb.velocity) # ) skel.vertices.append(vb) # if not vb.inf_fast: # if sign(vb.velocity[0]) != sign(bi_b[0]) or sign(vb.velocity[1]) != sign(bi_b[1]): # vb.velocity = mul(unit(bi_b), norm(vb.velocity)) if pause: logging.debug('split l.194 -- computed new vertex B') from grassfire.inout import interactive_visualize interactive_visualize(queue, skel, step, now) # BI = compute_crossing_bisector(v1.ur, v.ur, now) va = compute_new_kvertex(v1.ur, v.ur, now, sk_node, len(skel.vertices) + 1, v.internal or v1.internal, pause) va.wfl = v1.wfr va.wfr = v.wfr # logging.debug(""" # -- BISECTOR BI {} # bi_a {} # va.v {}""".format(BI, bi_a, va.velocity) # ) skel.vertices.append(va) if pause: logging.debug('split l.211 -- computed new vertex A') interactive_visualize(queue, skel, step, now) # if not va.inf_fast: # if sign(va.velocity[0]) != sign(bi_a[0]) or sign(va.velocity[1]) != sign(bi_a[1]): # va.velocity = mul(unit(bi_a), norm(va.velocity)) logging.debug("-- update circular list at B-side: {} [{}]".format( id(vb), vb.info)) update_circ(v.left, vb, now) update_circ(vb, v2, now) # FIXME: why do these assertions not hold? assert vb.left.wfr is vb.wfl assert vb.right.wfl is vb.wfr logging.debug("-- update circular list at A-side: {} [{}]".format( id(va), va.info)) update_circ(v1, va, now) update_circ(va, v.right, now) logging.debug("-- [{}]".format(va.info)) logging.debug(" [{}]".format(va.right.info)) logging.debug(" {}".format(va.right.wfl)) logging.debug(" {}".format(va.wfr)) # FIXME: why do these assertions not hold? assert va.left.wfr is va.wfl assert va.right.wfl is va.wfr # updates (triangle fan) at neighbour 1 b = t.neighbours[(e + 1) % 3] assert b is not None b.neighbours[b.neighbours.index(t)] = None fan_b = replace_kvertex(b, v, vb, now, ccw, queue, immediate) if pause: logging.debug('split l.243 -- replaced vertex B') from grassfire.inout import interactive_visualize interactive_visualize(queue, skel, step, now) # updates (triangle fan) at neighbour 2 a = t.neighbours[(e + 2) % 3] assert a is not None a.neighbours[a.neighbours.index(t)] = None fan_a = replace_kvertex(a, v, va, now, cw, queue, immediate) if pause: logging.debug('split l.255 -- replaced vertex A') from grassfire.inout import interactive_visualize interactive_visualize(queue, skel, step, now) t.stops_at = now # handle infinitely fast vertices if va.inf_fast: handle_parallel_fan(fan_a, va, now, cw, step, skel, queue, immediate, pause) if vb.inf_fast: handle_parallel_fan(fan_b, vb, now, ccw, step, skel, queue, immediate, pause) # # we "remove" the triangle itself # def is_infinitely_fast(fan): # times = [tri.event.time if tri.event is not None else -1 for tri in fan] # is_inf_fast = all(map(near_zero, [time - now for time in times])) # if fan and is_inf_fast: # return True # else: # return False # is_inf_fast_b = is_infinitely_fast(get_fan(b, v, ccw)) # is_inf_fast_a = is_infinitely_fast(get_fan(a, v, cw)) # double check: infinitely fast vertices # (might have been missed by adding wavefront vectors cancelling out) # if is_inf_fast_a and not va.inf_fast: # logging.debug("New kinetic vertex vA: ***Upgrading*** to infinitely fast moving vertex!") # va.inf_fast = True # if is_inf_fast_b and not vb.inf_fast: # logging.debug("New kinetic vertex vB: ***Upgrading*** to infinitely fast moving vertex!") # vb.inf_fast = True
def handle_edge_event(evt, step, skel, queue, immediate, pause): """Handles triangle collapse, where exactly 1 edge collapses""" t = evt.triangle logging.info("* edge :: tri>> #{} [{}]".format(id(t), t.info)) logging.debug(evt.side) assert len(evt.side) == 1, len(evt.side) # take edge e e = evt.side[0] logging.debug( "wavefront edge collapsing? {0}".format(t.neighbours[e] is None)) is_wavefront_collapse = t.neighbours[e] is None # if t.neighbours.count(None) == 2: # assert t.neighbours[e] is None now = evt.time v1 = t.vertices[ccw(e)] v2 = t.vertices[cw(e)] # v1. logging.debug("v1 := {} [{}] -- stop_node: {}".format( id(v1), v1.info, v1.stop_node)) logging.debug("v2 := {} [{}] -- stop_node: {}".format( id(v2), v2.info, v2.stop_node)) # FIXME: assertion is not ok when this is triangle from spoke collapse? if is_wavefront_collapse and not v1.is_stopped and not v2.is_stopped: assert v1.right is v2 assert v2.left is v1 # stop the two vertices of this edge and make new skeleton node # replace 2 vertices with new kinetic vertex # +--- new use of wavefronts ------------------------------ # # ⋮ a = v1.wfl b = v1.wfr if is_wavefront_collapse and not v1.is_stopped and not v2.is_stopped: assert v2.wfl is b c = v2.wfr # intersector = WaveFrontIntersector(a, c) bi = intersector.get_bisector() logging.debug(bi) # in general position the new position of the node can be constructed by intersecting 3 pairs of wavefronts # (a,c), (a,b), (b,c) # in case (a,c) are parallel, this is new infinitely fast vertex and triangles are locked between # or (a,c) are parallel due to spoke collapse (then new vertex is on straight line, splitting it in 2 times 90 degree angles) # in case (a,b) are parallel, then v1 is straight -- no turn # in case (b,c) are parallel, then v2 is straight -- no turn pos_at_now = None try: intersector = WaveFrontIntersector(a, c) pos_at_now = intersector.get_intersection_at_t(now) logging.debug("POINT({0[0]} {0[1]});a;c".format(pos_at_now)) except ValueError: pass # iff the wavefronts wfl/wfr are parallel # then only the following 2 pairs of wavefronts can be properly intersected! # try: # intersector = WaveFrontIntersector(a, b) # pos_at_now = intersector.get_intersection_at_t(now) # logging.debug("POINT({0[0]} {0[1]});a;b".format(pos_at_now)) # except ValueError: # pass # # # try: # intersector = WaveFrontIntersector(b, c) # pos_at_now = intersector.get_intersection_at_t(now) # logging.debug("POINT({0[0]} {0[1]});b;c".format(pos_at_now)) # # except ValueError: # pass # ⋮ # +--- new use of wavefronts ------------------------------ # sk_node, newly_made = stop_kvertices([v1, v2], step, now, pos=pos_at_now) if newly_made: skel.sk_nodes.append(sk_node) kv = compute_new_kvertex(v1.ul, v2.ur, now, sk_node, len(skel.vertices) + 1, v1.internal or v2.internal, pause) # ---- new use of wavefronts ---------- # kv.wfl = v1.wfl # kv.wfr = v2.wfr # # ---- new use of wavefronts ---------- # logging.debug("Computed new kinetic vertex {} [{}]".format( id(kv), kv.info)) logging.debug("v1 := {} [{}]".format(id(v1), v1.info)) logging.debug("v2 := {} [{}]".format(id(v2), v2.info)) # logging.debug(kv.position_at(now)) # logging.debug(kv.position_at(now+1)) # logging.debug("||| {} | {} | {} ||| ".format( v1.left.position_at(now), sk_node.pos, v2.right.position_at(now) )) # logging.debug("||| {} ||| ".format(signed_turn( v1.left.position_at(now), sk_node.pos, v2.right.position_at(now) ))) # logging.debug("||| {} ||| ".format(get_bisector( v1.left.position_at(now), sk_node.pos, v2.right.position_at(now) ))) if v1.left: logging.debug(v1.left.position_at(now)) else: logging.warning("no v1.left") if v2.right: logging.debug(v2.right.position_at(now)) else: logging.warning("no v2.right") if kv.inf_fast: logging.debug("New kinetic vertex moves infinitely fast!") # append to skeleton structure, new kinetic vertex skel.vertices.append(kv) # update circular list of kinetic vertices update_circ(v1.left, kv, now) update_circ(kv, v2.right, now) # def sign(val): # if val > 0: # return +1 # elif val < 0: # return -1 # else: # return 0 # bisector_check = get_bisector( v1.left.position_at(now), sk_node.pos, v2.right.position_at(now) ) # if not kv.inf_fast: # logging.debug("{} [{}]".format(v1.left, v1.left.info)) # logging.debug("{} [{}]".format(v2.right, v2.right.info)) # logging.debug("{0} vs {1}".format(bisector_check, kv.velocity)) # if sign(bisector_check[0]) == sign(kv.velocity[0]) and sign(bisector_check[1]) == sign(kv.velocity[1]): # logging.debug('signs agree') # else: # logging.warning(""" # BISECTOR SIGNS DISAGREE # """) # kv.velocity = (sign(bisector_check[0]) * abs(kv.velocity[0]), sign(bisector_check[1]) * abs(kv.velocity[1])) # raise ValueError('bisector signs disagree') # ---- new use of wavefronts ---------- # # post condition assert kv.wfl is kv.left.wfr assert kv.wfr is kv.right.wfl # ---- new use of wavefronts ---------- # # get neighbours around collapsing triangle a = t.neighbours[ccw(e)] b = t.neighbours[cw(e)] n = t.neighbours[e] # second check: is vertex infinitely fast? # is_inf_fast_a = is_infinitely_fast(get_fan(a, v2, cw), now) # is_inf_fast_b = is_infinitely_fast(get_fan(b, v1, ccw), now) # if is_inf_fast_a and is_inf_fast_b: # if not kv.inf_fast: # logging.debug("New kinetic vertex: ***Upgrading*** to infinitely fast moving vertex!") # kv.inf_fast = True # fan_a = [] fan_b = [] if a is not None: logging.debug("replacing vertex for neighbours at side A") a_idx = a.neighbours.index(t) a.neighbours[a_idx] = b fan_a = replace_kvertex(a, v2, kv, now, cw, queue, immediate) if fan_a: e = Edge(fan_a[-1], cw(fan_a[-1].vertices.index(kv))) orig, dest = e.segment import math if (near_zero(math.sqrt(orig.distance2_at(dest, now)))): logging.info( "collapsing neighbouring edge, as it is very tiny -- cw") schedule_immediately(fan_a[-1], now, queue, immediate) if b is not None: logging.debug("replacing vertex for neighbours at side B") b_idx = b.neighbours.index(t) b.neighbours[b_idx] = a fan_b = replace_kvertex(b, v1, kv, now, ccw, queue, immediate) if fan_b: e = Edge(fan_b[-1], ccw(fan_b[-1].vertices.index(kv))) orig, dest = e.segment import math if (near_zero(math.sqrt(orig.distance2_at(dest, now)))): logging.info( "collapsing neighbouring edge, as it is very tiny -- ccw") schedule_immediately(fan_b[-1], now, queue, immediate) if n is not None: logging.debug( "*** neighbour n: schedule adjacent neighbour for *IMMEDIATE* processing" ) n.neighbours[n.neighbours.index(t)] = None if n.event is not None and n.stops_at is None: schedule_immediately(n, now, queue, immediate) # if t.info == 134: # raise NotImplementedError('problem: #134 exists now') # we "remove" the triangle itself t.stops_at = now # process parallel fan if kv.inf_fast: if fan_a and fan_b: # combine both fans into 1 fan_a = list(fan_a) fan_a.reverse() fan_a.extend(fan_b) handle_parallel_fan(fan_a, kv, now, ccw, step, skel, queue, immediate, pause) return elif fan_a: handle_parallel_fan(fan_a, kv, now, cw, step, skel, queue, immediate, pause) return elif fan_b: handle_parallel_fan(fan_b, kv, now, ccw, step, skel, queue, immediate, pause) return
def handle_parallel_edge_event_3tri(t, e, pivot, now, step, skel, queue, immediate): logging.info("* parallel|even#3 :: tri>> #{} [{}]".format(id(t), t.info)) logging.debug('At start of handle_parallel_edge_event for 3 triangle') logging.debug("Edge with inf fast vertex collapsing! {0}".format(t.neighbours[e] is None)) # triangle is like: # *-------------------------* # `---------------*''''''' # we are collapsing the edge opposite of the inf fast pivot vertex # this assumes that v1 and v2 need to be on the same location!!! assert t.vertices.index(pivot) == e assert t.vertices[e] is pivot # assert pivot.inf_fast left_leg_idx = ccw(t.vertices.index(pivot)) left_leg = Edge(t, left_leg_idx) left_dist = dist(*map(lambda x: x.position_at(now), left_leg.segment)) v1 = t.vertices[ccw(e)] right_leg_idx = cw(t.vertices.index(pivot)) right_leg = Edge(t, right_leg_idx) right_dist = dist(*map(lambda x: x.position_at(now), right_leg.segment)) v2 = t.vertices[cw(e)] assert v1 is not pivot assert v2 is not pivot assert pivot in t.vertices assert v1 in t.vertices assert v2 in t.vertices from grassfire.vectorops import dot, norm logging.debug(v1.velocity) logging.debug(v2.velocity) magn_v1 = norm(v1.velocity) magn_v2 = norm(v2.velocity) logging.debug(' velocity magnitude: {}'.format([magn_v1, magn_v2])) dists = [left_dist, right_dist] logging.debug(' distances: {}'.format(dists)) dists_sub_min = [near_zero(_ - min(dists)) for _ in dists] logging.debug(dists_sub_min) # stop the non-infinite vertices at the same location # use the slowest moving vertex to determine the location if magn_v2 < magn_v1: sk_node, newly_made = stop_kvertices([v2], step, now) if newly_made: skel.sk_nodes.append(sk_node) v1.stop_node = sk_node v1.stops_at = now else: sk_node, newly_made = stop_kvertices([v1], step, now) if newly_made: skel.sk_nodes.append(sk_node) v2.stop_node = sk_node v2.stops_at = now # FIXME: # make edge between v1 and v2 # assert pivot.stop_node is None # assert pivot.stops_at is None #FIXME: wrong sk_node for pivot pivot.stop_node = sk_node pivot.stops_at = now # this is not necessary, is it? ## update_circ(pivot, v1, now) ## update_circ(v2, pivot, now) # we "remove" the triangle itself t.stops_at = now for kv in t.vertices: assert kv.stops_at is not None
def handle_parallel_edge_event_shorter_leg(t, e, pivot, now, step, skel, queue, immediate, pause): """Handles triangle collapse, where exactly 1 edge collapses One of the vertices of the triangle moves *infinitely* fast. There are 2 cases handled in this function a. triangle with long left leg, short right leg b. triangle with long right leg, short left leg Arguments: t -- triangle that collapses e -- short side over which pivot moves inf fast """ logging.info("* parallel|short :: tri>> #{} [{}]".format(id(t), t.info)) logging.debug('At start of handle_parallel_edge_event_shorter_leg') logging.debug("Edge with inf fast vertex collapsing! {0}".format(t.neighbours[e] is None)) assert pivot.inf_fast # vertices, that are not inf fast, need to stop # FIXME: this is not necessarily correct ... # where they need to stop depends on the configuration # -- now they are *always* snapped to same location v1 = t.vertices[ccw(e)] v2 = t.vertices[cw(e)] v3 = t.vertices[e] logging.debug("* tri>> #{} [{}]".format(id(t), t.info)) logging.debug("* pivot #{} [{}]".format(id(pivot), pivot.info)) logging.debug("* v1 #{} [{}]".format(id(v1), v1.info)) logging.debug("* v2 #{} [{}]".format(id(v2), v2.info)) logging.debug("* v3 #{} [{}]".format(id(v3), v3.info)) assert pivot is v1 or pivot is v2 to_stop = [] for v in [v1, v2]: if not v.inf_fast: to_stop.append(v) # stop the non-infinite vertices sk_node, newly_made = stop_kvertices(to_stop, step, now) if newly_made: skel.sk_nodes.append(sk_node) if pivot.stop_node is None: assert pivot.stop_node is None assert pivot.stops_at is None pivot.stop_node = sk_node pivot.stops_at = now # we will update the circular list # at the pivot a little bit later else: logging.debug("Infinite fast pivot already stopped," " but should not be stopped(?)") # we "remove" the triangle itself t.stops_at = now # check that the edge that collapses is not opposite of the pivot # i.e. the edge is one of the two adjacent legs at the pivot assert t.vertices.index(pivot) != e kv = compute_new_kvertex(v1.ul, v2.ur, now, sk_node, len(skel.vertices) + 1, v1.internal or v2.internal, pause) # FIXME new wavefront -- update refs kv.wfl = v1.left.wfr kv.wfr = v2.right.wfl logging.debug("Computed new kinetic vertex {} [{}]".format(id(kv), kv.info)) if kv.inf_fast: logging.debug("New kinetic vertex moves infinitely fast!") # get neighbours around collapsing triangle a = t.neighbours[ccw(e)] b = t.neighbours[cw(e)] n = t.neighbours[e] # second check: # is vertex infinitely fast? # in this case, both sides of the new vertex # should collapse to be infinitely fast! # is_inf_fast_a = is_infinitely_fast(get_fan(a, v2, cw), now) # is_inf_fast_b = is_infinitely_fast(get_fan(b, v1, ccw), now) # if is_inf_fast_a and is_inf_fast_b: # assert kv is not None # if not kv.inf_fast: # logging.debug("New kinetic vertex: ***Not upgrading*** to infinitely fast moving vertex!") # we if the vertex is in a 90.0 degree angle for which both sides are inf-fast # kv.inf_fast = True # append to skeleton structure, new kinetic vertex skel.vertices.append(kv) # update circular list of kinetic vertices logging.debug("-- update circular list for new kinetic vertex kv: {} [{}]".format(id(kv), kv.info)) update_circ(v1.left, kv, now) update_circ(kv, v2.right, now) # update the triangle fans incident fan_a = [] fan_b = [] if a is not None: logging.debug("- replacing vertex for neighbours at side A {} [{}]".format(id(a), a.info)) a_idx = a.neighbours.index(t) a.neighbours[a_idx] = b fan_a = replace_kvertex(a, v2, kv, now, cw, queue, immediate) if pause: logging.debug('replaced neighbour A') interactive_visualize(queue, skel, step, now) if b is not None: logging.debug("- replacing vertex for neighbours at side B {} [{}]".format(id(b), b.info)) b_idx = b.neighbours.index(t) b.neighbours[b_idx] = a fan_b = replace_kvertex(b, v1, kv, now, ccw, queue, immediate) if pause: logging.debug('replaced neighbour B') interactive_visualize(queue, skel, step, now) # logging.debug("*** neighbour n: {} ".format("schedule adjacent neighbour for *IMMEDIATE* processing" if n is not None else "no neighbour to collapse simultaneously")) if n is not None: n.neighbours[n.neighbours.index(t)] = None if n.event is not None and n.stops_at is None: schedule_immediately(n, now, queue, immediate) #visualize(queue, skel, now-1.0e-3) #raw_input('continue after parallel -- one of two legs') # process parallel fan, only if the fan has all un-dealt with triangles if kv and kv.inf_fast: # # fan - cw # if fan_a and all([t.stops_at is None for t in fan_a]): # handle_parallel_fan(fan_a, kv, now, cw, step, skel, queue, immediate, pause) # return # elif fan_a: # # we should have a fan in which all triangles are already stopped # assert all([t.stops_at is not None for t in fan_a]) # # fan - ccw # if fan_b and all([t.stops_at is None for t in fan_b]): # handle_parallel_fan(fan_b, kv, now, ccw, step, skel, queue, immediate, pause) # return # elif fan_b: # # we should have a fan in which all triangles are already stopped # assert all([t.stops_at is not None for t in fan_b]) if fan_a and fan_b: # combine both fans into 1 fan_a = list(fan_a) fan_a.reverse() fan_a.extend(fan_b) handle_parallel_fan(fan_a, kv, now, ccw, step, skel, queue, immediate, pause) return elif fan_a: handle_parallel_fan(fan_a, kv, now, cw, step, skel, queue, immediate, pause) return elif fan_b: handle_parallel_fan(fan_b, kv, now, ccw, step, skel, queue, immediate, pause) return