// Last edited on 2000-09-05 05:23:42 by stolfi // Ferramentas para desenhar mapas (na tela ou em papel) import java.awt.Graphics; import java.awt.Color; import Gravura; import Ponto; import Planta; import Trecho; import Servico; import Esquina; import Nodo; public class DesenhadorImpl extends Desenhador { private static Ponto pTmp = new Ponto(); private static Ponto qTmp = new Ponto(); ////////////////////////////////////////////////////////////////////// // CONSTRUTOR public DesenhadorImpl() { } ////////////////////////////////////////////////////////////////////// // DESENHO DE MAPA // Níveis de desenho das vias públicas e servico public static final int NIVEL_CONTORNO = 1; // Contorno/sombra dos elementos public static final int NIVEL_RECHEIO = 2; // Interior dos elementos public void desenhaMapa( Gravura g, Trecho[] T, Esquina[] Q, Servico[] S, Logradouro[] log, Elemento[] sel, Nodo[] cam ) { g.modoSET(); // Desenha contornos das vias da planta e servicos desenhaViasDaPlanta(g, T, Q, S, NIVEL_CONTORNO); desenhaServicos(g, S, NIVEL_CONTORNO); // Desenha o "recheio" das vias e dos servicos. // As vias só precisam ser desenhadas se a escala // for grande o bastante. desenhaViasDaPlanta(g, T, Q, S, NIVEL_RECHEIO); desenhaServicos(g, S, NIVEL_RECHEIO); // Desenha os destaques: destacaLogradouros(g, log); destacaSelecoes(g, sel); destacaCaminhos(g, cam); // Escreve os rótulos de vias e servicos. // rotulaLogradouros(g, log); // rotulaServicos(g, S); } ////////////////////////////////////////////////////////////////////// // MÉTODOS PRIVADOS private static Color corViaFina = new Color(140, 120, 80); private static Color corContornoDeVia = new Color( 32, 16, 0); private static Color corRecheioDeViaPublica = new Color(223, 190, 150); private static Color corRotuloDeVia = new Color( 0, 0, 32); private static Color corSombraDeServico = new Color(140, 120, 80); // As estradas são desenhadas com recheio somente se // o raio de um trecho de pista única é maior ou igual a: private static int RAIO_PISTA_MIN = 2; private static final void desenhaViasDaPlanta( Gravura g, Trecho[] T, Esquina[] Q, Servico[] S, int nivel ) // Desenha as vias trafegáveis da planta: trechos, // conexões em esquinas, e rampas de entrada/saída de // serviços. { // Calcula o raio da pena em pixels para vias de 1 pista, ajusta // o mesmo para o nível especificado, e decide as cores a // usar. Se o raio das vias mais finas for 0 (linha de 1 pixel // de largura), traça todas as vias apenas com cor // "corViaFina". Senão, traça os contornos dos trechos com // "corContornoDeVia", e seu recheio com // "corRecheioDeViaPublica" ou a cor própria do serviço. int rPista = raioDeUmaPista(g); Color corPublica = null; Color corPrivada = null; switch (nivel) { case NIVEL_CONTORNO: if (rPista < RAIO_PISTA_MIN) { corPublica = corViaFina; corPrivada = corViaFina; } else { corPublica = corContornoDeVia; corPrivada = corContornoDeVia; } break; case NIVEL_RECHEIO: if (rPista < RAIO_PISTA_MIN) { return; } else { rPista = (5*(rPista - 1))/6; corPublica = corRecheioDeViaPublica; corPrivada = null; } break; default: throw new Error("nivel invalido"); } // Desenha as vias com as cores e largura estabelecidas. if (corPrivada != null) { g.mudaCorDaPena(corPrivada); } for(int i=0; i < S.length; i++) { if (corPrivada == null) { g.mudaCorDaPena(S[i].tipo().cor()); } desenhaRampas(g, S[i], rPista); } g.mudaCorDaPena(corPublica); for(int i=0; i < T.length; i++) { desenhaTrecho(g, T[i], rPista); } for(int i=0; i < Q.length; i++) { desenhaEsquina(g, Q[i], rPista); } } private static final void desenhaServicos(Gravura g, Servico[] S, int nivel) // Desenha o contorno, recheio, ou rótulo dos servicos indicados. // Note porem que as rampas de acesso são desenhadas alhures. { switch (nivel) { case NIVEL_CONTORNO: int rPista = raioDeUmaPista(g); if (rPista >= RAIO_PISTA_MIN) { // Pinta o contorno de cada serviço g.mudaCorDaPena(corSombraDeServico); g.mudaRaioDaPena(1); g.mudaCorDoPincel(null); g.translada(1,1); for(int i=0; i < S.length; i++) { desenhaServico(g, S[i]); } g.translada(-1,-1); } break; case NIVEL_RECHEIO: // Pinta o interior de cada servico com sua cor própria: g.mudaCorDaPena(null); for(int i=0; i < S.length; i++) { g.mudaCorDoPincel(S[i].tipo().cor()); desenhaServico(g, S[i]); } break; default: throw new Error("nivel invalido"); } } private static Color corLogradouro = new Color(255, 230, 0); public static final void destacaLogradouros(Gravura g, Logradouro[] log) // Destaca os elementos selecionados. // Presentemente, elementos são destacados com bolas no seu centro. { if (log != null) { int rPista = raioDeUmaPista(g); rPista = (5*(rPista - 1))/6; g.mudaCorDaPena(corLogradouro); for (int i= 0; i< log.length; i++) { destacaLogradouro(g, log[i], rPista); } } } private static Color corSelecao = new Color(170, 0, 255); public static final void destacaSelecoes(Gravura g, Elemento[] sel) // Destaca os elementos selecionados. // Presentemente, elementos são destacados com bolas no seu centro. { if (sel != null) { int rPista = raioDeUmaPista(g); int rCaminho = (4*(rPista - 2))/6; if (rCaminho < 1) { rCaminho = 1; } int rBola = 3*rCaminho + 1; g.mudaRaioDaPena(rBola); g.mudaCorDaPena(corSelecao); for (int i= 0; i< sel.length; i++) { destacaSelecao(g, sel[i], rBola); } } } private static Color corCaminho = new Color(255, 64, 0); public static final void destacaCaminhos(Gravura g, Nodo[] cam) // Destaca os caminhos computados correntes "cam[i]". { if (cam != null) { int rPista = raioDeUmaPista(g); int rCaminho = (4*(rPista - 2))/6; if (rCaminho < 1) { rCaminho = 1; } g.mudaCorDaPena(corCaminho); for (int i = 0; i < cam.length; i++) { destacaCaminho(g, cam[i], rCaminho); } } } ////////////////////////////////////////////////////////////////////// // MÉTODOS SUPER-PRIVADOS public static final int raioDeUmaPista(Gravura g) // calcula o raio da pena para desenhar uma rua de uma única // pista. { return (int)(Trecho.LARGURA_NOMINAL/g.tamanhoDoPixel()/2 + 0.5); } // Os métodos abaixo não sabem nada de níveis, e pintam os // elementos relevantes com as cores correntes. O parâmetro "rPista" é // o raio da pena a usar para trechos de 1 pista. Note que a // largura real de uma linha de raio r é 2*r+1. private static final void desenhaTrecho(Gravura g, Trecho tr, int rPista) // Desenha o trecho "tr". { int dPista = 2*rPista + 1; int rTrecho = (tr.numeroDePistas() * dPista)/2; g.mudaRaioDaPena(rTrecho); g.linha(tr.origPos(), tr.destPos()); } private static final void desenhaEsquina(Gravura g, Esquina eq, int rPista) // Desenha as conexões da esquina "eq". { Conexao[] con = eq.conexoes(); for(int i = 0; i < con.length; i++) { Trecho t1 = con[i].t1; Trecho t2 = con[i].t2; desenhaConexao(g, t1, t2, rPista); } } private static final void desenhaConexao(Gravura g, Trecho t1, Trecho t2, int rPista) // Desenha a conexao do fim de t1 para o início de t2. { int dPista = 2*rPista + 1; int nPistas = (t1.numeroDePistas() + t2.numeroDePistas())/2; int rConexao = (nPistas*dPista)/2; g.mudaRaioDaPena(rConexao); g.linha(t1.destPos(), t2.origPos()); } private static final void desenhaRampas(Gravura g, Servico sv, int rPista) // Desenha as rampas de entrada e saida do servico "sv". // Supõe que elas têm uma única pista. { Ponto[] lig = sv.rampas(); int ng = lig.length; g.mudaRaioDaPena(rPista); for (int i = 0; i < ng; i += 2) { g.linha(lig[i], lig[i+1]); } } private static final void desenhaServico(Gravura g, Servico sv) // Desenha o contorno ou recheio do serviço { g.poligono(sv.vertices()); } private static final void destacaLogradouro(Gravura g, Logradouro lg, int rPista) { Trecho[] tr = lg.trechos(); for (int i = 0; i < tr.length; i++) { if ((i > 0) && (tr[i-1].dest() == tr[i].orig())) { desenhaConexao(g, tr[i-1], tr[i], rPista); } desenhaTrecho(g, tr[i], rPista); } } private static final void destacaSelecao(Gravura g, Elemento t, int rBola) { g.mudaRaioDaPena(rBola); g.ponto(t.centro()); } private static final void destacaCaminho(Gravura g, Nodo fin, int rCaminho) { if (fin != null) { int rBola = 3*rCaminho; g.mudaRaioDaPena(rBola); g.ponto(fin.posicao()); g.mudaRaioDaPena(rCaminho); Nodo v = fin; while (v.prev != null) { g.linha(v.posicao(), v.prev.posicao()); v = v.prev; } if (v != fin) { g.mudaRaioDaPena(rBola); g.ponto(v.posicao()); } } } }