def trata_ponta_pra_cima(p, L, dcel, diags): viz_esq = p.next viz_dir = p.prev if left(p, viz_dir, viz_esq): viz_esq, viz_dir = viz_dir, viz_esq t = Trapezio(p) removido = (L.busca(t)).elemento if removido == None: t.a_esq = Segment(p, viz_esq) t.a_dir = Segment(p, viz_dir) t.desenha() L.insere(t) else: L.deleta(t) removido.apaga() d = Segment(p, removido.sup) d.plot('firebrick') dcel.add_edge(d.init, d.to) diags.append(d) desenhos.sleep() t1 = Trapezio(p, removido.a_esq, Segment(p, viz_esq)) t2 = Trapezio(p, Segment(p, viz_dir), removido.a_dir) t1.desenha() t2.desenha() L.insere(t1) L.insere(t2) desenhos.sleep()
def forca_bruta(l): "Algoritmo forca bruta para encontrar o par de pontos mais proximo" "Recebe uma lista de pontos l" if len(l) < 2: return None closest = float("inf") a = b = None for i in range(len(l)): for j in range(i + 1, len(l)): l[i].lineto(l[j], 'firebrick') sleep() l[i].remove_lineto(l[j]) dist = dist2(l[i], l[j]) if dist < closest: if a is not None: a.unhilight() b.unhilight() a.remove_lineto(b) closest = dist a = l[i] b = l[j] a.hilight("orange") b.hilight("orange") a.lineto(b, "orange") sleep() ret = Segment(a, b) return ret
def monotonos(d, P, diags): """ Função que recebe um polígono P e particiona P em vários polígonos monótonos Através da inserção de diagonais Coloca as diagonais na DCEL d """ # Ordena os vértices pela Y-coordenada v = P.vertices() v = sorted(v, key=lambda x: (-x.y * 10000 + x.x)) L = Abbb() # os vértices são os pontos eventos da linha de varredura for p in v: p.hilight() h = desenhos.plot_horiz_line(p.y, 'green') desenhos.sleep() viz_cima = p.next viz_baixo = p.prev if viz_cima.y < viz_baixo.y: viz_cima, viz_baixo = viz_baixo, viz_cima if ((viz_cima.y > p.y and p.y > viz_baixo.y) or (viz_cima.y == p.y and viz_cima.x < p.x) or (viz_baixo.y == p.y and viz_baixo.x > p.x)): trata_caso_meio(p, viz_baixo, L, d, diags) elif viz_cima.y <= p.y: trata_ponta_pra_cima(p, L, d, diags) else: trata_ponta_pra_baixo(p, L, d, diags) desenhos.plot_delete(h) p.unhilight()
def trata_caso_meio(p, viz_baixo, L, dcel, diags): # Remove da linha o trapésio que tem o p t = Trapezio(p) removido = (L.busca(t)).elemento removido.apaga() L.deleta(t) if ponta_pra_baixo(removido.sup): d = Segment(removido.sup, p) d.plot('firebrick') dcel.add_edge(d.init, d.to) diags.append(d) desenhos.sleep() # Insere um novo trapésio com o p # Se o removido estava a direita if (p == removido.a_dir.to): t.a_dir = Segment(p, viz_baixo) t.a_esq = removido.a_esq # Se estava a esquerda else: t.a_esq = Segment(p, viz_baixo) t.a_dir = removido.a_dir t.desenha() L.insere(t) desenhos.sleep()
def marca_intersec(no1, no2, pontos, p_x=None): "Testa se há interseção entre o nó1 e o nó2 e adiciona em pontos, se houver" "E só marca as interseções que ocorrem do p_x pra direita" # Despinta de verde e pinta de amarelo no1.seg.hide() no2.seg.hide() no1.seg.plot('yellow') no2.seg.plot('yellow') desenhos.sleep() # despinta de amarelo e pinta de verde denovo no1.seg.hide() no2.seg.hide() no1.seg.plot('green') no2.seg.plot('green') p = no1.seg.intersection(no2.seg) # Só marco se o o ponto esta pra frente do x especificado if (p != None and (p_x == None or p.x > p_x.x or (p.x == p_x.x and p.y > p_x.y))): # Crio o nó p_no = Node_Point(p, ini=[], fim=[], inter=[no1, no2]) # insere o ponto na arvore, ou só atualiza se ele já existir p_no_abb = pontos.busca(p_no) if p_no_abb.elemento == None: pontos.insere(p_no) p_no.ponto.plot('red') else: if no1 not in p_no_abb.elemento.inter: p_no_abb.elemento.inter.append(no1) if no2 not in p_no_abb.elemento.inter: p_no_abb.elemento.inter.append(no2) desenhos.sleep()
def orelhas(poligono): """ Algoritmo que usa a estratégia de encontrar e remover orelhas para triangular o polígono """ # Cria uma cópia pra não zoar o input original novo = [] for p in poligono.vertices(): novo.append(Point(p.x, p.y)) P = Polygon(novo) n = len(P.vertices()) # Dicionario que relaciona os vértices a um booleano que indica se é orelha # Aproveitando que os pontos são 'hashables' orelha = dict() #PreProcessamento dos vértices v = P.pts orelha[v] = is_orelha(v, P) v = v.next while v != P.pts: orelha[v] = is_orelha(v, P) v = v.next while n > 3: # Procura uma orelha while not orelha[v]: v = v.next # Sinaliza qual orelha eu escolhi v.hilight('firebrick') # Desenha a diagonal e desmarca a orelha v.prev.lineto(v.next, 'orange') orelha[v] = False sleep() # Tira v do polígono u = v.prev w = v.next w.prev = u u.next = w # Essa parte é pra lista sempre ficar circular # (P.pts podia ficar inacessivel dai o algoritmo entrava em loop) if v == P.pts: P.pts = P.pts.next # Confere se não criei nenhuma orelha orelha[u] = is_orelha(u, P) orelha[w] = is_orelha(w, P) v.unhilight() n -= 1 # Despinta alguma orelha que tenha sobrado while not orelha[v]: v = v.next v.unhilight()
def deleta_da_linha(L, no, pontos, p_x=None): "Deleta o nó da linha de varredura L e testa a interseção entre os que ficaram consecutivos" "Mas só marca as interseções que ocorrem do x pra frente" pred = L.predecessor(no) suc = L.sucessor(no) L.deleta(no) no.seg.hide() desenhos.sleep() if pred != None and suc != None and pred != suc: marca_intersec(pred, suc, pontos, p_x)
def is_orelha(v, P): " Função que recebe um vértice v do polígono P e retorna se v é uma ponta de orelha " v.hilight('green') resposta = is_diagonal(v.prev, v.next, P) v.unhilight() if resposta: v.hilight() sleep() return resposta
def marca_intersec(no1, no2, pontos, x=None): "Testa se há interseções entre o nó 1 e o nó 2 e adiciona em pontos, se houver" "E só marca as interseções que ocorrem do x pra direita" # Despinta de verde e pinta de amarelo no1.apaga() no2.apaga() no1.desenha("yellow") no2.desenha("yellow") desenhos.sleep() # despinta de amarelo e pinta de verde denovo no1.apaga() no2.apaga() no1.desenha() no2.desenha() inter = no1.circ.intersection(no2.circ) for p in inter: # Só marco se o o ponto esta pra frente do x especificado # e se faz parte das metades correspondentes #(se intersecta a metade de cima e eu sou a de baixo eu não marco) if ((x == None or p.x > x) and ((no1.baixo and p.y <= no1.circ.center.y) or (not no1.baixo and p.y >= no1.circ.center.y)) and ((no2.baixo and p.y <= no2.circ.center.y) or (not no2.baixo and p.y >= no2.circ.center.y))): # Crio o nó # Caso degenerado onde os arcos se intersectam em 1 só ponto, que eu guardo no inter_unico[] if len(inter) == 1: p_no = Node_Point_Circle(p, ini=[], fim=[], inter=[], inter_unico=[no1, no2]) # caso geral onde os arcos se intersectam em 2 pontos else: p_no = Node_Point_Circle(p, ini=[], fim=[], inter=[no1, no2], inter_unico=[]) # insere o nó na linha, ou só atualiza se ele já existir p_no_abb = pontos.busca(p_no) if p_no_abb.elemento == None: pontos.insere(p_no) p_no.ponto.plot('red') else: if len(inter) == 1: p_no_abb.elemento.inter_unico.append(no1) p_no_abb.elemento.inter_unico.append(no2) else: p_no_abb.elemento.inter.append(no1) p_no_abb.elemento.inter.append(no2) desenhos.sleep()
def add_triangs_dcel (d, p, triang): " Adiciona o P na dcel d e uma aresta de p pra cada ponta do triang " d.add_vertex (p) e1 = d.add_edge (p, triang.p1, triang.a.f) e2 = d.add_edge (p, triang.p2, triang.a.f) e3 = d.add_edge (p, triang.p3, e2.f) e1.draw(color_novo) e2.draw(color_novo) e3.draw(color_novo) sleep() return e1, e2, e3
def ilegal(e): " Devolve se a aresta dada pela meia aresta 'e' é ilegal " # As arestas do triangulão infinito não podem ser ilegais global infs if e.init in infs and e.to in infs: return False e.draw(color_legalizaveis) sleep() # O quadrilatero precisa ser convexo if left(e.twin.prox.to, e.to, e.prox.to) == left(e.twin.prox.to, e.init, e.prox.to): return False def angulo(p1, p2, p3): " Devolve algo proporcional ao angulo em p2 de p1-p2-p3 " # Na verdade, devolve o -2*cosseno do angulo com a lei do cosseno a2 = (p3.x - p1.x)**2 + (p3.y - p1.y)**2 b2 = (p3.x - p2.x)**2 + (p3.y - p2.y)**2 c2 = (p1.x - p2.x)**2 + (p1.y - p2.y)**2 ang = ((b2 + c2 - a2) / (2 * ((b2 * c2)**0.5))) return -ang # Como cosseno é descrescente para angulos menores que pi, # Então posso comparar dois angulos a e b pelos seus cossenos # a > b <=> cos(a) < cos(b) # Acha o menor angulo do triangulo com a aresta e min_ang1 = min([ angulo(e.prev.init, e.init, e.to), angulo(e.init, e.to, e.prev.init), angulo(e.to, e.prev.init, e.init) ]) # Acha o menor angulo do triangulo com a aresta e.twin min_ang2 = min([ angulo(e.twin.prev.init, e.init, e.to), angulo(e.init, e.to, e.twin.prev.init), angulo(e.init, e.twin.prev.init, e.to) ]) min_ang_legal = min(min_ang1, min_ang2) # Acha o menor angulo dos triangulos com a outra diagonal min_ang1 = min([ angulo(e.prev.init, e.init, e.twin.prev.init), angulo(e.init, e.prev.init, e.twin.prev.init), angulo(e.prev.init, e.twin.prev.init, e.init) ]) min_ang2 = min([ angulo(e.prev.init, e.to, e.twin.prev.init), angulo(e.to, e.prev.init, e.twin.prev.init), angulo(e.prev.init, e.twin.prev.init, e.to) ]) min_ang_ilegal = min(min_ang1, min_ang2) return min_ang_legal < min_ang_ilegal
def forca_bruta(l): "Algoritmo força bruta para encontrar todos as interseções entre uma lista de círculos" for i in range(len(l)): l[i].hilight(cor_borda='orange', grossura=2) sleep() for j in l[i + 1:]: j.hilight(cor_borda='green') sleep() for p in l[i].intersection(j): p.hilight('yellow') j.unhilight() l[i].unhilight()
def compara (p_min, p1, p2): " Comparação angular para pré-processamento dos pontos " p_min.lineto(p1, 'gray') p_min.lineto(p2, 'gray') p1.lineto(p2, 'gray') sleep() p_min.remove_lineto(p1) p_min.remove_lineto(p2) p1.remove_lineto(p2) if (right(p_min, p1, p2) or (abs(area2(p_min, p1, p2)) < eps and dist2(p_min, p1) < dist2(p_min, p2))): return 1 return 0
def mergehull_rec(P): """ Função que faz a divisão e conquista, principal do algoritmo """ if len(P) == 1: return [P[0]] q = int(len(P) / 2) h_esq = mergehull_rec(P[:q]) draw_hull(h_esq, 'green') h_dir = mergehull_rec(P[q:]) draw_hull(h_dir, 'red') sleep() m = merge(h_esq, h_dir) hide_hull(h_esq) hide_hull(h_dir) return m
def is_diagonal(u, w, P): """ Função que recebe dois vértices u e w do polígono P e retorna se uw é uma diagonal de P """ # colore a candidata a diagonal uw = Segment(u, w) uw.plot('green') sleep() # Como o dentroDoPoligono é O(1) é muito prudente fazer esse teste primeiro result = dentro_do_poligono(u, w, P) and (not intersecta_borda(u, w, P)) uw.hide() return result
def insere_na_linha(L, no, pontos, x=None, trocados=[]): "Insere o nó na linha de varredura L e testa as interseções com consecutivos " "Mas só marca as interseções que ocorrem do x pra frente e que não se repetem nos trocados" L.insere(no) if x == None: no.desenha() desenhos.sleep() pred = L.predecessor(no) suc = L.sucessor(no) if pred != None and (trocados == [] or pred not in trocados): marca_intersec(no, pred, pontos, x) if suc != None and (trocados == [] or suc not in trocados): marca_intersec(no, suc, pontos, x)
def bentley_ottmann_mod(l): L = Abbb() # Linha de varredura resp = [] # Os nós com os pontos de interseção que retornaremos # Pré-processamento - Transforma cada circulo em pontos-eventos # pontos é a ABBB de pontos eventos pontos = eventos(l) desenhos.sleep() while not pontos.vazia(): p = pontos.deleta_min() # desenha a linha id_linha = desenhos.plot_vert_line(p.ponto.x, 'green') id_evento = p.ponto.hilight('green') desenhos.sleep() "------------------------- Pontos da direita --------------------------------" for arco in p.fim: deleta_da_linha(L, arco, pontos, p.ponto.x) "------------------------- Pontos da esquerda --------------------------------" for arco in p.ini: insere_na_linha(L, arco, pontos) "------------------------- Pontos de interseção ------------------------------" if len(p.inter) > 0 or len( p.inter_unico) > 0 or (len(p.ini) + len(p.fim) >= 4): p.ponto.hilight('yellow') resp.append(p) # Troca a ordem dos arcos (do p.inter[]) # (Não troco a ordem do p.inter_unico[] porque os circulos não se "penetram") trocados = [] # Remove todos for arco in p.inter: if (arco not in trocados and arco not in p.fim and p.ponto.x < arco.circ.center.x + arco.circ.r - eps): trocados.append(arco) L.deleta(arco) # Insere denovo com o novo ponto de referencia for arco in trocados: arco.ref = p.ponto insere_na_linha(L, arco, pontos, p.ponto.x, trocados) # apaga a linha desenhos.plot_delete(id_linha) desenhos.plot_delete(id_evento) p.ponto.unplot() return resp
def quickhull(P): if len(P) <= 1: return P # Ponto mais baixo for i in range(len(P)): if P[i].y < P[0].y or (P[i].y == P[0].y and P[i].x > P[0].x): P[0], P[i] = P[i], P[0] # Ponto mais a direita do ponto mais baixo for i in range(len(P)): if (right(P[0], P[-1], P[i]) or (abs( area2(P[0], P[-1], P[i]) < eps and dist2(P[0], P[-1]) < dist2(P[0], P[i])))): P[-1], P[i] = P[i], P[-1] P[0].hilight("green") P[-1].hilight("red") P[0].lineto(P[-1], "orange") sleep() return quickhull_rec(P, 0, len(P) - 1)
def quickhull_rec(P, l, r): """ Função principal do algoritmo """ if r - l == 1: P[l].hilight() P[r].hilight() P[l].lineto(P[r], "orange") sleep() return [P[r], P[l]] # P[l + 1] recebe ponto extremo for i in range(l + 1, r): P[i].lineto(P[l], "gray") P[i].lineto(P[r], "gray") sleep() P[i].remove_lineto(P[l]) P[i].remove_lineto(P[r]) if (abs(area2(P[i], P[l], P[r])) > abs(area2(P[l + 1], P[l], P[r])) or (abs( abs(area2(P[i], P[l], P[r])) - abs(area2(P[l + 1], P[l], P[r]))) < eps and left(P[l], P[l + 1], P[i]))): P[i], P[l + 1] = P[l + 1], P[i] # caso degenerado: pontos colineares if abs(area2(P[l], P[l + 1], P[r])) < eps: P[l].hilight() P[r].hilight() P[l].lineto(P[r], "orange") sleep() return [P[r], P[l]] p, q, linha_esq, linha_dir = particione(P, l, r) P[p].unhilight() P[q].unhilight() P[p].hilight("green") P[q].hilight("red") sleep() fecho_esq = quickhull_rec(P, p, q) fecho_dir = quickhull_rec(P, q, r) plot_delete(linha_esq) plot_delete(linha_dir) sleep() for e in range(1, len(fecho_esq)): fecho_dir.append(fecho_esq[e]) return fecho_dir
def dentro_do_poligono(u, w, P): """ Função que recebe dois vértices u e w do polígono P e retorna se a candidata a diagonal uw está pra dentro do polígono (equivalente a função NoCone dos slides) """ prevU = u.prev nextU = u.next if (left_on(prevU, u, nextU)): resposta = (left(u, w, prevU) and left(w, u, nextU)) else: resposta = not (left_on(u, w, nextU) and left_on(w, u, prevU)) if not resposta: uw = Segment(u, w) uw.plot('red') sleep() uw.hide() return resposta
def ShamosRec(l, i, j): " Função que faz o serviço recursivo " " recebe uma lista de pontos l[i:j] ordenados pela coordenada x " # Base da recursão, 2 ou 1 ponto if j - i < 3: # registra o par mais proximo par_min = Segment(l[i], l[j - 1]) par_min.hilight('green') desenhos.sleep() # Ordena pelo eixo y if (l[i].y > l[j - 1].y): l[i], l[j - 1] = l[j - 1], l[i] else: q = (i + j) // 2 meio = l[q] vert_id = desenhos.plot_vert_line(meio.x, 'firebrick', grossura=1) meio.hilight('firebrick') desenhos.sleep() # Calcula o menor das duas metades par_esq = ShamosRec(l, i, q) par_dir = ShamosRec(l, q, j) desenhos.plot_delete(vert_id) meio.unhilight() par_min = minPar(par_esq, par_dir) par_esq.unhilight() par_dir.unhilight() par_esq.hilight('red') par_dir.hilight('red') desenhos.sleep() par_esq.unhilight() par_dir.unhilight() par_min.hilight('orange') desenhos.sleep() # Intercala do mergeSort escondido intercalaY(l, i, j) # Calcula o menor entre as duas metade par_inter = menorInter(l, i, j, meio, par_min) if par_inter != None: par_min = minPar(par_inter, par_min) par_inter.hide() global d dnovo = math.sqrt(dist(par_min)) d = min(d, dnovo) return par_min
def intersecta_borda(u, w, P): """ Função que recebe dois vértices u e w do polígono P e retorna se o segmento uw intersecta alguma aresta de P (equivalente a função QuaseDiagonal dos slides) """ borda = P.edges() uw = Segment(u, w) for aresta in borda: aresta.plot('green') sleep() if (u not in aresta.endpoints()) and (w not in aresta.endpoints()): if (uw.intersects(aresta)): aresta.hide() aresta.plot('red') sleep() aresta.hide() return True aresta.hide() return False
def trata_ponta_pra_baixo(p, L, dcel, diags): t = Trapezio(p) removido1 = (L.busca(t)).elemento removido1.apaga() L.deleta(t) if ponta_pra_baixo(removido1.sup): d = Segment(removido1.sup, p) d.plot('firebrick') desenhos.sleep() dcel.add_edge(d.init, d.to) diags.append(d) # Se tem outro polígono removido2 = (L.busca(t)).elemento if removido2 != None: L.deleta(t) removido2.apaga() if ponta_pra_baixo(removido2.sup): d = Segment(removido2.sup, p) d.plot('firebrick') desenhos.sleep() dcel.add_edge(d.init, d.to) diags.append(d) if removido2.a_esq.to == p: t = Trapezio(p, removido1.a_esq, removido2.a_dir) else: t = Trapezio(p, removido2.a_esq, removido1.a_dir) L.insere(t) t.desenha() desenhos.sleep()
def embrulho(P): """ Função principal do algoritmo """ p_esq = P[0] for p in P: if p.x < p_esq.x or (p.x == p_esq.x and p.y < p_esq.y): p_esq = p p_esq.hilight('orange') sleep() H = [p_esq] while True: if P[0] == H[-1]: p_i = P[1] else: p_i = P[0] p_i.hilight('firebrick') H[-1].lineto(p_i, 'firebrick') sleep() for p_j in P: if p_j == H[-1] or p_j == p_i: continue H[-1].lineto(p_j, 'gray') sleep() H[-1].remove_lineto(p_j) if (right(H[-1], p_i, p_j) or (collinear(H[-1], p_i, p_j) and dist2(H[-1], p_i) < dist2(H[-1], p_j))): H[-1].remove_lineto(p_i) p_i.unhilight() if p_i in H: p_i.hilight('orange') p_i = p_j H[-1].lineto(p_i, 'firebrick') p_i.hilight('firebrick') H[-1].lineto(p_i, 'orange') p_i.hilight('orange') if p_i == H[0]: break H.append(p_i) return H
def bentley_ottmann(l): L = Abbb() # Linha de varredura resp = [] # Os nós com os pontos de interseção que retornaremos # Pré-processamento - Transforma cada circulo em pontos-eventos # pontos é a ABBB de pontos eventos pontos = eventos(l) desenhos.sleep() while not pontos.vazia(): p = pontos.deleta_min() # desenha a linha id_linha = desenhos.plot_vert_line(p.ponto.x, 'green') id_evento = p.ponto.hilight('green') desenhos.sleep() "------------------------- Pontos da direita --------------------------------" for seg in p.fim: seg.ref = seg.seg.to deleta_da_linha(L, seg, pontos, p.ponto) "------------------------- Pontos da esquerda --------------------------------" for seg in p.ini: seg.seg.plot('green') desenhos.sleep() insere_na_linha(L, seg, pontos) "------------------------- Pontos de interseção ------------------------------" if len(p.inter) > 0 or (len(p.ini) + len(p.fim)) > 1: p.ponto.hilight('yellow') resp.append(p) # Troca a ordem dos segmentos (do p.inter[]) trocados = [] # Remove todos for seg in p.inter: if seg not in p.fim: if seg.seg.to.x != seg.seg.init.x: y_ref = (((seg.seg.to.x * seg.seg.init.y) - (seg.seg.init.x * seg.seg.to.y) - (p.ponto.x - 10 * eps) * (seg.seg.init.y - seg.seg.to.y)) / (seg.seg.to.x - seg.seg.init.x)) seg.ref = Point(p.ponto.x - 10 * eps, y_ref) else: seg.ref = Point(p.ponto.x, p.ponto.y + 10 * eps) trocados.append(seg) L.deleta(seg) # Insere denovo com o novo ponto de referencia for seg in trocados: seg.ref = p.ponto #print("reinserindo " + str(seg)) insere_na_linha(L, seg, pontos, p.ponto, trocados) # apaga a linha desenhos.plot_delete(id_linha) desenhos.plot_delete(id_evento) p.ponto.unplot() return resp
def lee_preparata(poligono): # Cria uma cópia pra não zoar o input original novo = [] for p in poligono.vertices(): novo.append(Point(p.x, p.y)) P = Polygon(novo) diags = [] d = Dcel() d.init_polygon(P) # Atualiza a DCEL colocando as diagonais parar a partição em monótonos monotonos(d, P, diags) n_face_externa = len(P.vertices()) divisoras = len(diags) # Para cada face, constrói um polígono e triangula ele if len(d.f) == 2: return monotono(P) for e in d.f: vertices = [e.init] while e.to != vertices[0]: vertices.append(e.to) e = e.prox if len(vertices) != n_face_externa: new_p = Polygon(vertices) new_p.plot('green') desenhos.sleep() # Triangula o new_p e adiciona as novas diagonais no diags diags.extend(monotono(new_p)) new_p.hide() # despinta as arestas for i in range(divisoras): diags[i].hide() diags[i].plot("orange")
def tangente_superior(h_esq, h_dir): """ Encontra os pontos i, j tais que a linha h_esq[i] - h_dir[j] deixa todos os demais pontos abaixo dela """ n_esq = len(h_esq) n_dir = len(h_dir) i = j = 0 for k in range(len(h_esq)): if h_esq[k].x > h_esq[i].x or (h_esq[k].x == h_esq[i].x and h_esq[k].y > h_esq[i].y): i = k for k in range(len(h_dir)): if h_dir[k].x < h_dir[j].x or (h_dir[k].x == h_dir[j].x and h_dir[k].y < h_dir[j].y): j = k h_dir[j].remove_lineto(h_esq[i]) h_dir[j].lineto(h_esq[i], 'blue') sleep() prox_i = (i + 1) % n_esq prox_j = (j - 1 + n_dir) % n_dir subiu = True while subiu: subiu = False while (left(h_esq[i], h_dir[j], h_esq[prox_i]) or (collinear(h_esq[i], h_dir[j], h_esq[prox_i]) and dist2(h_esq[i], h_dir[j]) < dist2(h_esq[prox_i], h_dir[j]))): subiu = True h_dir[j].remove_lineto(h_esq[i]) i = prox_i prox_i = (i + 1) % n_esq h_dir[j].lineto(h_esq[i], 'blue') sleep() while (left(h_esq[i], h_dir[j], h_dir[prox_j]) or (collinear(h_esq[i], h_dir[j], h_dir[prox_j]) and dist2(h_dir[j], h_esq[i]) < dist2(h_dir[prox_j], h_esq[i]))): subiu = True h_dir[j].remove_lineto(h_esq[i]) j = prox_j prox_j = (j - 1 + n_dir) % n_dir h_dir[j].lineto(h_esq[i], 'blue') sleep() h_dir[j].remove_lineto(h_esq[i]) h_dir[j].lineto(h_esq[i], 'firebrick') sleep() return i, j
def menorInter(l, i, j, meio, par_min): " Retorna o par de pontos mais proximo dentro da faixa dada pelo ponto meio da lista " " e a distancia do par_min " d = math.sqrt(dist(par_min)) # desenha a faixa que eu estou procurando v1 = desenhos.plot_vert_line(meio.x - d, 'orange', grossura=1) v2 = desenhos.plot_vert_line(meio.x + d, 'orange', grossura=1) cand = candidatos(l, i, j, meio) par_inter = None for k in range(len(cand)): cand[k].plot('red') desenhos.sleep() for l in range(k + 1, len(cand)): # Se os pontos já estão distantes, posso parar de olhar if (cand[l].y - cand[k].y > d): break cand_inter = Segment(cand[k], cand[l]) cand_inter.plot('red') desenhos.sleep() cand_inter.hide() dcand = math.sqrt(dist2(cand[k], cand[l])) # Se achei um novo par, apaga o outro e pinta esse if (dcand < d): d = dcand if par_inter is not None: par_inter.unhilight() par_min.unhilight() par_inter = cand_inter par_inter.hilight('orange') desenhos.sleep() cand[k].unplot() desenhos.plot_delete(v1) desenhos.plot_delete(v2) desenhos.sleep() return par_inter
def particione(P, l, r): """ Particiona o conjunto de pontos P em 3 partes: A esquerda do triangulo l-r-x A direita do triangulo l-r-x Dentro do triangulo l-r-x, onde x é o ponto em P que maximiza a área do triangulo """ # Desenha o triangulo if hasattr(P[l + 1], 'hi'): P[l + 1].unhilight() P[l + 1].hilight("firebrick") linha_esq = P[l].lineto(P[l + 1], "green") linha_dir = P[r].lineto(P[l + 1], "red") sleep() p = q = r for k in range(r - 1, l + 1, -1): if hasattr(P[k], 'hi'): P[k].unhilight() P[k].hilight("yellow") sleep() P[k].unhilight() if left(P[l], P[l + 1], P[k]): # ponto da partição esquerda (verde) p -= 1 P[p], P[k] = P[k], P[p] P[p].hilight("green") sleep() elif right(P[r], P[l + 1], P[k]): # ponto da partição direita (vermelho) p -= 1 q -= 1 P[q], P[k] = P[k], P[q] if p != q: P[p], P[k] = P[k], P[p] P[q].hilight("red") sleep() # Ajustes finais no vetor p -= 1 q -= 1 P[q], P[l + 1] = P[l + 1], P[q] if p != q: P[p], P[l + 1] = P[l + 1], P[p] p -= 1 P[l], P[p] = P[p], P[l] return p, q, linha_esq, linha_dir
def graham (P): """ Função principal do algooritmo """ P = pre_processa(P) if len(P) < 3: return P P[1].hilight('orange') P[0].lineto(P[1], 'orange') P[2].hilight('orange') P[1].lineto(P[2], 'orange') sleep() H = P[:3] for p in P[3:]: p.hilight ('orange') H[-1].lineto (p, 'orange') sleep() while (right (H[-2], H[-1], p) or (abs(area2(H[-2], H[-1], p)) < eps and dist2(H[-2], H[-1]) < dist2(H[-2], p))): H[-1].hilight ('firebrick') H[-2].remove_lineto (H[-1]) H[-1].remove_lineto (p) H[-2].lineto (H[-1], 'firebrick') H[-1].lineto (p, 'firebrick') sleep() H[-1].unhilight() H[-2].remove_lineto (H[-1]) H[-1].remove_lineto (p) H.pop() H[-1].lineto (p, 'orange') sleep() H.append(p) if collinear (H[-2], H[-1], H[0]): H[-2].remove_lineto(H[-1]) H[-1].unhilight() H.pop() H[-1].lineto (H[0], 'orange') return H