def init_skeleton(dt): """Initialize a data structure that can be used for making the straight skeleton. """ skel = Skeleton() # make skeleton nodes # -> every triangulation vertex becomes a skeleton node nodes = {} avg_x = 0.0 avg_y = 0.0 for v in dt.vertices: if v.is_finite: nodes[v] = SkeletonNode(pos=(v.x, v.y), step=-1, info=v.info) avg_x += v.x / len(dt.vertices) avg_y += v.y / len(dt.vertices) centroid = InfiniteVertex() centroid.origin = (avg_x, avg_y) # make kinetic triangles, so that for every delaunay triangle we have # a kinetic counter part ktriangles = [] # all kinetic triangles # mapping from delaunay triangle to kinetic triangle internal_triangles = set() for _, depth, triangle in RegionatedTriangleIterator(dt): if depth == 1: # FIXME: why not put the depth here as identifier into the triangle # holes can then be separated from others (and possibly calculated in parallel) internal_triangles.add(triangle) triangle2ktriangle = {} for idx, t in enumerate(dt.triangles, start=1): # skip the external triangle # if t is dt.external: # continue k = KineticTriangle() k.info = idx triangle2ktriangle[t] = k ktriangles.append(k) k.internal = t in internal_triangles # whether triangle is internal to a polygon del idx link_around = [] # set up properly the neighbours of all kinetic triangles # blank out the neighbour, if a side is constrained unwanted = [] #it = TriangleIterator(dt) # skip the external triangle (which is the first the iterator gives) # next(it) for t in dt.triangles: k = triangle2ktriangle[t] for i in range(3): edge = Edge(t, i) # k.wavefront_directions[i] = make_unit_vector(edge) k.wavefront_support_lines[i] = make_support_line(edge) for j, n in enumerate(t.neighbours): # set neighbour pointer to None if constrained side if t.constrained[j]: continue # skip linking to non-existing triangle if n is None or n.vertices[2] is None: # if n.external: unwanted.append(k) continue k.neighbours[j] = triangle2ktriangle[n] # FIXME: duplicate <-> each KTriangle has wavefront! # unit_vectors = make_unit_vectors(dt) # make kinetic vertices # and link them to the kinetic triangles # also make sure that every kinetic vertex is related to a skeleton node kvertices = [] # ktri_no_apex = [] # one_ktri_between = {} # with open("/tmp/bisectors.wkt", "w") as bisector_fh: # print >> bisector_fh, "wkt" ct = 0 for v in dt.vertices: assert v.is_finite, "infinite vertex found" groups = split_star(v) if len(groups) == 1: raise NotImplementedError( "not yet dealing with PSLG in initial conversion") for group in groups: first, last = group[0], group[-1] # compute turn type at vertex tail, mid1 = Edge(last.triangle, ccw(last.side)).segment # print(tail.x, tail.y, ";", mid1.x, mid1.y) mid2, head = Edge(first.triangle, cw(first.side)).segment # print(mid2.x, mid2.y, ";", head.x, head.y) assert mid1 is mid2 turn = orient2d((tail.x, tail.y), (mid1.x, mid1.y), (head.x, head.y)) # left : + [ = ccw ] # straight : 0. # right : - [ = cw ] if turn < 0: turn_type = "RIGHT - REFLEX" elif turn > 0: turn_type = "LEFT - CONVEX" else: turn_type = "STRAIGHT" # print(turn_type) # print("--") # FIXME: duplicate <-> each KTriangle has wavefront! right = triangle2ktriangle[first.triangle].wavefront_support_lines[ cw(first.side)] left = triangle2ktriangle[last.triangle].wavefront_support_lines[ ccw(last.side)] assert left is not None assert right is not None intersector = WaveFrontIntersector(left, right) bi = intersector.get_bisector() # print("") # print(v, id(v), v.info) # print(left) # print(right) # print("=-=-=") # print(id(first.triangle)) # print(first_line) # print("") # print(id(last.triangle)) # print(last_line) # print("=-=-=") # print("") # bi = None # left_translated = left.translated(left.w) # right_translated = right.translated(right.w) # intersect = Intersector(left_translated, right_translated) # # # # == 3 possible outcomes in 2D: == # # # # 0. overlapping lines - always intersecting in a line # # 1. crossing - point2 # # 2. parallel - no intersection # # # if intersect.intersection_type() == IntersectionResult.LINE: # bi = tuple(left.w) # elif intersect.intersection_type() == IntersectionResult.POINT: # # velocity # bi = make_vector(end=intersect.result, start=(v.x, v.y)) # elif intersect.intersection_type() == IntersectionResult.NO_INTERSECTION: # # print('no intersection, parallel wavefronts - not overlapping?') # logging.warning('no intersection, parallel wavefronts - not overlapping?') # bi = tuple(left.w) # assert bi is not None ur = right.line # unit_vectors[first.triangle][cw(first.side)] ul = left.line # unit_vectors[last.triangle][ccw(last.side)] # bi = bisector(ul, ur) # print v, ul, ur, bi # for edge in group: # print "", edge.triangle ct += 1 kv = KineticVertex() kv.turn = turn_type kv.info = ct ### "{}-{}".format(ct, turn) kv.origin = (v.x, v.y) kv.velocity = bi kv.start_node = nodes[v] kv.starts_at = 0 # kv.ul = first_line # ul # FIXME: point to original Line2 ? # kv.ur = last_line # ur kv.ul = ul kv.ur = ur kv.wfl = left kv.wfr = right # print(" {}".format(kv.origin) ) # print(" {}".format(kv.wfl) ) # print(" {}".format(kv.wfr) ) for edge in group: ktriangle = triangle2ktriangle[edge.triangle] ktriangle.vertices[edge.side] = kv kv.internal = ktriangle.internal kvertices.append(kv) # link vertices to each other in circular list link_around.append( ((last.triangle, cw(last.side)), kv, (first.triangle, ccw(first.side)))) # # print "" # it = StarEdgeIterator(v) # around = [e for e in it] # # with open("/tmp/vertexit.wkt", "w") as fh: # # output_triangles([e.triangle for e in around], fh) # # constraints = [] # for i, e in enumerate(around): # if e.triangle.constrained[cw(e.side)]: # constraints.append(i) # # # FIXME: # # Check here how many constrained edges we have outgoing of # # this vertex. # # # # In case 0: degenerate case, should not happen # # In case 1: we should make two kvertices vertices # # # # We do not handle this properly at this moment. # # # # In case 2 or more the following is fine. # if len(constraints) == 0: # raise ValueError("Singular point found") # else: # # rotate the list of around triangles, # # so that we start with a triangle that has a constraint # # side # if constraints[0] != 0: # shift = -constraints[0] # how much to rotate # d = deque(around) # d.rotate(shift) # around = list(d) # # also update which triangles have a constraint edge # constraints = [idx + shift for idx in constraints] # # # make two bisectors at a terminal vertex # if len(constraints) == 1: # # assert constraints[0] == 0 # edge = around[0] # start, end = v, edge.triangle.vertices[ccw(edge.side)] # vec = normalize((end.x - start.x, end.y - start.y)) # # FIXME: Replace perp with rotate90cw / rotate90ccw # # # from segment over terminal vertex to this kinetic vertex, # # turns right # # (first bisector when going ccw at end) # p2 = tuple(map(add, start, rotate90ccw(vec))) # p1 = v # p0 = tuple(map(add, start, rotate90ccw(rotate90ccw(vec)))) # bi = bisector(p0, p1, p2) # # print >> bisector_fh, # ##"LINESTRING({0[0]} {0[1]}, {1[0]} {1[1]})".format( # ##p1, map(add, p1, bi)) # # print nodes[v] # # kvA = KineticVertex() # kvA.origin = (p1.x, p1.y) # kvA.velocity = bi # kvA.start_node = nodes[v] # kvA.starts_at = 0 # kvertices.append(kvA) # # # from segment to this vertex, turns left # # second bisector when going ccw at end # p2 = tuple(map(add, start, rotate90ccw(rotate90ccw(vec)))) # p1 = v # p0 = tuple( # map(add, start, rotate90ccw(rotate90ccw(rotate90ccw( # bi = bisector(p0, p1, p2) # # print >> bisector_fh, # "LINESTRING({0[0]} {0[1]}, {1[0]} {1[1]})".format(p1, map(add, p1, bi)) # # FIXME insert additional triangle at this side # # print nodes[v] # # kvB = KineticVertex() # kvB.origin = (p1.x, p1.y) # kvB.velocity = bi # kvB.start_node = nodes[v] # kvB.starts_at = 0 # kvertices.append(kvB) # # groups = [around] # # split_idx = find_overlapping_triangle(around) # # # print split_idx # # print len(around) # # determine which triangles get an incidence with the # # first and which with the second kvertices vertex # # # first go with kvA # # second go with kvB # first, second = around[:split_idx], around[split_idx + 1:] # # print "first ", first # # print "second", second # mid = around[split_idx] # # print "mid ", mid # # # go with kvA # for e in first: # ktriangle = triangle2ktriangle[e.triangle] # ktriangle.vertices[e.side] = kvA # # go with kvB # for e in second: # ktriangle = triangle2ktriangle[e.triangle] # ktriangle.vertices[e.side] = kvB # # # for the mid triangle it depends where it should go # # based on adding an additional kvertices triangle into # # the triangulation here... # # # FIXME: the placement of points A and B should be # # dependent on the distance between A and L or A and F # # to not get False negatives out of the is_quad # # classification # triangle = mid.triangle # # print "PIVOT POINT INDEX", mid.side # first_leg = ccw(mid.side) # last_leg = cw(mid.side) # L = triangle.vertices[last_leg] # F = triangle.vertices[first_leg] # # A = map(add, kvA.origin, kvA.velocity) # B = map(add, kvB.origin, kvB.velocity) # O = triangle.vertices[mid.side] # # print "first", first_leg,"|" , F, "(cw)", "last", last_leg, "|" ,L, # # "(ccw) around", O # # first_quad = [O, A, F, B, O] # last_quad = [O, A, L, B, O] # first_ok = is_quad(first_quad) # last_ok = is_quad(last_quad) # # # if first is True and second False # # assign ktriangles triangle to kvA/kvB and the corner to kvB # # # if both not ok, probably at convex hull overlapping with # # infinite triangle # # only, so take guess and use the first leg # if first_ok or (not first_ok and not last_ok): # ktriangle = triangle2ktriangle[mid.triangle] # ktriangle.vertices[mid.side] = kvB # # knew = KineticTriangle() # knew.vertices[0] = kvB # knew.vertices[1] = kvA # knew.vertices[2] = None # # X, Y = mid.triangle, mid.triangle.neighbours[ # ccw(first_leg)] # sideX = X.neighbours.index(Y) # sideY = Y.neighbours.index(X) # # key = tuple(sorted([X, Y])) # if key not in one_ktri_between: # one_ktri_between[key] = [] # one_ktri_between[key].append( # (knew, # triangle2ktriangle[Y], # sideY, # triangle2ktriangle[X], # sideX)) # # # if first is false and second True # # assign ktriangles triangle to kvA/kvB and the corner to kvA # elif last_ok: # ktriangle = triangle2ktriangle[mid.triangle] # ktriangle.vertices[mid.side] = kvA # # knew = KineticTriangle() # knew.vertices[0] = kvB # knew.vertices[1] = kvA # knew.vertices[2] = None # # ktri_no_apex.append(knew) # # X = mid.triangle # Y = mid.triangle.neighbours[cw(last_leg)] # sideX = X.neighbours.index(Y) # sideY = Y.neighbours.index(X) # # key = tuple(sorted([X, Y])) # if key not in one_ktri_between: # one_ktri_between[key] = [] # one_ktri_between[key].append( # (knew, # triangle2ktriangle[X], # sideX, # triangle2ktriangle[Y], # sideY)) # # # add 2 entries to link_around list # # one for kvA and one for kvB # # link kvA and kvB to point to each other directly # kvA.left = kvB, 0 # link_around.append( # (None, kvA, (first[0].triangle, ccw(first[0].side)))) # kvB.right = kvA, 0 # link_around.append( # ((second[-1].triangle, cw(second[-1].side)), kvB, None)) # # # make bisectors # else: # # assert len(constraints) >= 2 # # group the triangles around the vertex # constraints.append(len(around)) # groups = [] # for lo, hi in zip(constraints[:-1], constraints[1:]): # groups.append(around[lo:hi]) # # # per group make a bisector and KineticVertex # for group in groups: # begin, end = group[0], group[-1] # p2 = begin.triangle.vertices[ # ccw(begin.side)] # the cw vertex # p1 = v # # the ccw vertex # p0 = end.triangle.vertices[cw(end.side)] # bi = bisector(p0, p1, p2) # # print >> bisector_fh, # "LINESTRING({0[0]} {0[1]}, {1[0]} {1[1]})".format(p1, map(add, p1, bi)) # kv = KineticVertex() # kv.origin = (p1.x, p1.y) # kv.velocity = bi # kv.start_node = nodes[v] # kv.starts_at = 0 # for edge in group: # ktriangle = triangle2ktriangle[edge.triangle] # ktriangle.vertices[edge.side] = kv # kvertices.append(kv) # # link vertices to each other in circular list # link_around.append(((end.triangle, cw(end.side)), # kv, (begin.triangle, ccw(begin.side)))) # link vertices in circular list for left, kv, right in link_around: # left is cw, right is ccw assert left is not None assert right is not None # if left is not None: # left cwv = triangle2ktriangle[left[0]].vertices[left[1]] kv.left = cwv, 0 # if right is not None: ccwv = triangle2ktriangle[right[0]].vertices[right[1]] kv.right = ccwv, 0 for left, kv, right in link_around: # left is cw, right is ccw assert kv.left.wfr is kv.wfl, "{} vs\n {}".format(kv.left.wfr, kv.wfl) assert kv.wfr is kv.right.wfl assert kv.is_stopped == False # -- copy infinite vertices into the kinetic triangles # make dico of infinite vertices (lookup by coordinate value) infinites = {} for t in triangle2ktriangle: for i, v in enumerate(t.vertices): # print(t, t.vertices) if v is not None and not v.is_finite: infv = InfiniteVertex() infv.origin = (v[0], v[1]) infinites[(v[0], v[1])] = infv assert len(infinites) == 3 # link infinite triangles to the infinite vertex for (t, kt) in triangle2ktriangle.items(): for i, v in enumerate(t.vertices): if v is not None and not v.is_finite: kt.vertices[i] = infinites[(v[0], v[1])] # # deal with added kinetic triangles at terminal vertices # for val in one_ktri_between.itervalues(): # if len(val) == 1: # knew, x, side_x, y, side_y, = val[0] # knew.neighbours[0] = x # knew.neighbours[1] = y # knew.neighbours[2] = None # x.neighbours[side_x] = knew # y.neighbours[side_y] = knew # knew.vertices[2] = x.vertices[ccw(side_x)] # ktriangles.append(knew) # elif len(val) == 2: # for i, v in enumerate(val): # # the other triangle between these 2 terminal vertices # # is the first value of the other tuple # kother = val[(i + 1) % 2][0] # knew, x, side_x, y, side_y, = v # # link to each other and to neighbour x # knew.neighbours[0] = x # knew.neighbours[1] = kother # knew.neighbours[2] = None # x.neighbours[side_x] = knew # y.neighbours[side_y] = kother # # link to vertex # knew.vertices[2] = x.vertices[ccw(side_x)] # ktriangles.append(knew) # else: # raise ValueError( # "Unexpected # kinetic triangles at terminal vertex") # there are 3 infinite triangles that are supposed to be removed # these triangles were already stored in the unwanted list remove = [] for kt in ktriangles: if [isinstance(v, InfiniteVertex) for v in kt.vertices].count(True) == 2: remove.append(kt) assert len(remove) == 3 assert len(unwanted) == 3 assert remove == unwanted # remove the 3 unwanted triangles and link their neighbours together link = [] for kt in unwanted: v = kt.vertices[kt.neighbours.index(None)] assert isinstance(v, KineticVertex) neighbour_cw = rotate_until_not_in_candidates(kt, v, cw, unwanted) neighbour_ccw = rotate_until_not_in_candidates(kt, v, ccw, unwanted) side_cw = ccw(neighbour_cw.vertices.index(v)) side_ccw = cw(neighbour_ccw.vertices.index(v)) link.append((neighbour_cw, side_cw, neighbour_ccw)) link.append((neighbour_ccw, side_ccw, neighbour_cw)) for item in link: ngb, side, new_ngb, = item ngb.neighbours[side] = new_ngb for kt in unwanted: kt.vertices = [None, None, None] kt.neighbours = [None, None, None] ktriangles.remove(kt) # replace the infinite vertices by one point in the center of the PSLG # (this could be the origin (0,0) if we would scale input to [-1,1] range for kt in ktriangles: for i, v in enumerate(kt.vertices): if isinstance(v, InfiniteVertex): kt.vertices[i] = centroid assert check_ktriangles(ktriangles) ktriangles.sort( key=lambda t: (t.vertices[0].origin[1], t.vertices[0].origin[0])) skel.sk_nodes = list(nodes.values()) skel.triangles = ktriangles skel.vertices = kvertices # INITIALIZATION FINISHES HERE # with open('/tmp/lines.wkt', 'w') as fh: # xaxis = Line2.from_points( (0, 0), (1, 0) ) # yaxis = Line2.from_points( (0, 0), (0, 1) ) # fh.write("wkt") # fh.write("\n") # fh.write(as_wkt(*xaxis.visualize())) # fh.write("\n") # fh.write(as_wkt(*yaxis.visualize())) # fh.write("\n") # for kt in skel.triangles: # for line in filter(lambda x: x is not None, kt.wavefront_support_lines): # fh.write(as_wkt(*line.visualize())) # fh.write("\n") return skel
def compute_new_kvertex(ul, ur, now, sk_node, info, internal, pause=False): """Based on the two wavefront directions and time t=now, compute the velocity and position at t=0 and return a new kinetic vertex Returns: KineticVertex """ kv = KineticVertex() kv.info = info kv.starts_at = now kv.start_node = sk_node kv.internal = internal logging.debug('/=-= New vertex: {} [{}] =-=\\'.format(id(kv), kv.info)) logging.debug('bisector calc') logging.debug(' ul: {}'.format(ul)) logging.debug(' ur: {}'.format(ur)) logging.debug(' sk_node.pos: {}'.format(sk_node.pos)) # FIXME: only in pause mode! from grassfire.vectorops import angle_unit, add import math logging.debug(' >>> {}'.format(angle_unit(ul.w, ur.w))) u1, u2 = ul.w, ur.w direction = add(u1, u2) logging.debug(" direction: {}".format(direction)) d, acos_d = angle_unit(u1, u2) # FIXME: replace with new api: line.at_time(0).visualize() // line.at_time(t).visualize() if pause: with open('/tmpfast/support_lines.wkt', 'w') as fh: from grassfire.inout import interactive_visualize fh.write("wkt\tlr\toriginal") fh.write("\n") # -- bisector line -- # b = ul.bisector(ur) b = ul.translated(mul(ul.w,now)).bisector( ur.translated(mul(ur.w,now)) ) bperp = b.perpendicular(sk_node.pos) for l, lr, original in zip([ul, ur, ul.translated(mul(ul.w,now)), ur.translated(mul(ur.w,now)), b, bperp], ["l", "r", "l", "r", "b", "b"], [True, True, False, False, None, None]): fh.write(l.at_time(0).visualize()) fh.write("\t") fh.write(lr) fh.write("\t") fh.write(str(original)) fh.write("\n") # check for parallel wavefronts if all(map(near_zero, direction)) or near_zero(acos_d - math.pi) or d < math.cos(math.radians(179.999999)): logging.debug(" OVERRULED - vectors cancel each other out / angle ~180° -> parallel wavefront!") bi = (0, 0) else: from grassfire.line2d import LineLineIntersector, make_vector, LineLineIntersectionResult intersect = LineLineIntersector(ul, ur) tp = intersect.intersection_type() if tp == LineLineIntersectionResult.NO_INTERSECTION: bi = (0, 0) elif tp == LineLineIntersectionResult.POINT: pos_at_t0 = intersect.result ul_t = ul.translated(ul.w) ur_t = ur.translated(ur.w) intersect_t = LineLineIntersector(ul_t, ur_t) assert intersect_t.intersection_type() == 1 bi = make_vector(end=intersect_t.result, start=pos_at_t0) elif tp == LineLineIntersectionResult.LINE: # this would mean original overlapping wavefronts... # -> parallel at left / right of a kvertex # FIXME: would it be possible here to get to original position that defined the line? bi = tuple(ul.w[:]) neg_velo = mul(mul(bi, -1.0), now) pos_at_t0 = add(sk_node.pos, neg_velo) kv.velocity = bi #was: bisector(ul, ur) logging.debug(' kv.velocity: {}'.format(kv.velocity)) from grassfire.vectorops import norm magn_v = norm(kv.velocity) logging.debug(' magnitude of velocity: {}'.format(magn_v)) logging.debug('\=-= New vertex =-=/') # if magn_v > 1000000: # logging.debug(" OVERRULED - super fast vertex angle ~180° -> parallel wavefront!") # kv.velocity = (0,0) # compute where this vertex would have been at time t=0 # we set this vertex as infinitely fast, if velocity in one of the # directions is really high, or when the bisectors of adjacent # wavefronts cancel each other out if kv.velocity == (0, 0): ### or abs(kv.velocity[0]) > 100 or abs(kv.velocity[1]) > 100: kv.inf_fast = True kv.origin = sk_node.pos else: # neg_velo = mul(mul(kv.velocity, -1.0), now) # pos_at_t0 = add(sk_node.pos, neg_velo) kv.origin = pos_at_t0 kv.ul = ul kv.ur = ur return kv