{
"cells": [
{
"cell_type": "markdown",
"id": "b49ea582",
"metadata": {},
"source": [
"# Aula5.Ex - *Scalable Vector Graphics*\n",
"O *Scalable Vector Graphics* é um padrão para representação de gráficos vetoriais. Como tem apenas a descrição geométrica para o conjunto de vértices e as relações entre eles, em geral é menor do que os arquivos que representam a imagem como uma matriz de *bytes*.\n",
"## Exemplos com Primitivas\n",
"Para criar uma linha, usamos a primitiva `line` e definimos as posições `x1`, `y1` do início da linha e as posições `x2`, `y2` do fim da linha. Usamos ainda o atributo `stroke-width` para a largura da linha e `stroke` para a cor."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6426a202",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "4eb62d53",
"metadata": {},
"source": [
"Para a construção de um retângulo, usamos a posição inicial em `x` e `y`, as dimensões `width` e `height` e a cor do preenchimento `fill` (representada como código hexadecimal `#FFFF00`)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3ea26d69",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "d4a77571",
"metadata": {},
"source": [
"Podemos representar um texto com tamanho `font-size`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "919cc1b8",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "234972d5",
"metadata": {},
"source": [
"Para desenhar mais de uma figura, basta passar mais de um comando para a função `svg`, isto é, mais de uma *tag*, como `rect` e `ellipse`, por exemplo. Neste caso, as figuras são desenhadas na ordem em que são descritas, ou seja, a que for definida por último será desenhada em cima da anterior."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a0032685",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "26a8a786",
"metadata": {},
"source": [
"A primitiva `path` permite desenhar uma linha contínua a partir de um conjunto de direções, listado em `d`. Partindo da posição inicial `M` (a vírgula é opcional, cada par de coordenadas é considerado uma posição em `(x, y)`), desenha-se um conjunto de retas, definido por `L`, passando por cada ponto descrito por cada par de coordenadas no caminho. Por padrão, presume-se que o caminho forme um objeto. O preenchimento `none` evita que este seja visível, exibindo apenas as linhas que seriam seu contorno."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "971d02fd",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "97a4315d",
"metadata": {},
"source": [
"É possível combinar as primitivas `line` e `path` para desenhar uma figura utilizando ambos os métodos. Assim, para desenhar duas retas, cada uma com uma primitiva, tem-se:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "72caa7e0",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "cf0da1be",
"metadata": {},
"source": [
"Em geral, `path` é usado para figuras mais complexas, que não podem ser facilmente compostas com as outras primitivas. Neste caso, é desenhada uma curva `C` (indicando uma cúbica), com os pares de coordenadas seguintes sendo seus pontos de controle, seguida de uma linha `L`, partindo do fim desta curva. Como todo o conjunto de direções está dentro de um mesmo `d`, a curva e a reta formam a mesma linha."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "055b5a3a",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "0970c11f",
"metadata": {},
"source": [
"Conforme descrito, a primitiva `line` pode ser usada para desenhar retas. Omitir o atributo `stroke` faz com que, na maioria dos navegadores, a reta não seja desenhada (e tenha um comportamento anormal nos que aparece)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "78b98342",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "8602bc3e",
"metadata": {},
"source": [
"Podemos gerar várias retas com atributos específicos para obter efeitos interessantes. Aqui, são criados dois pares de retas de mesma largura e cada um ocupando uma posição. Para cada par, um atributo `stroke-dasharray` determina a proporção na qual a reta será interrompida, permitindo ver a de trás. A reta mais ao fundo da imagem tem as extremidades arredondadas, determinadas pelo valor do atributo `stroke-linecap`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "59484ca1",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "b377298b",
"metadata": {},
"source": [
"Conforme visto no exemplo anterior, a ordem das declarações das primitivas define como serão desenhadas. Assim, pode-se obter resultados mais complexos simplesmente definindo a ordem na qual as primitivas são desenhada. Tomando, por exemplo, um conjunto de retângulos:}"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "24783893",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "e2bf1943",
"metadata": {},
"source": [
"O atributo `stroke-dasharray`, usado para desenhar de forma espaçada, não é restrito apenas à primitiva `line`. Assim como todas as primitivas têm uma cor definida por `stroke` e largura por `stroke-width`, também pode-se usar `stroke-dasharray` para criar intervalos onde o contorno de uma figura não seja representado. Deste modo, combinando a complexidade e intermitência dos exemplos anteriores, usando a primitiva `circle`, tem-se:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a6dea987",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "ffeda2b5",
"metadata": {},
"source": [
"Expandindo ainda mais a complexidade da figura anterior e aumentando a quantidade dos objetos representados, pode-se obter, usando a primitiva `ellipse`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "96794e30",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "8251b918",
"metadata": {},
"source": [
"Usando a primitiva `path` para formar um objeto, pode-se desenhar sobre todo o caminho percorrido dentro de `d`. Para este exemplo, considera-se a regra de preenchimento `evenodd`, que alterna a cor do objeto de acordo com as intersecções entre suas componentes."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "78f21e3a",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "70fd86d6",
"metadata": {},
"source": [
"Uma característica interessante de `path` é a possibilidade de combinar vários segmentos em uma única definição, isto é, um caminho pode ter vários pontos iniciais, denotados por `M`. Adicionando o modificador `z` ao final do caminho, indica-se que este deve ser fechado, isto é, que a última posição deve ser conectada à primeira, definida por `M`. Neste caso, igualmente, a intersecção dos objetos definidos entre `M` e `z`, no mesmo caminho `d` não é exibida."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b6ffdd7b",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "ceefcc7e",
"metadata": {},
"source": [
"A representação das curvas no SVG segue o algoritmo de Bézier, com uma posição final e uma direção na qual deve-se desviar, para aproximar o traço da forma de uma função não linear. Para expressar uma curva como uma função quadrada, usa-se o modificador `Q` dentro do caminho `d` de um `path`. Assim, tomando o mesmo valor para um conjunto de retas passando por um ponto até a posição de destino (definido por `L`, em vermelho) e uma curva indo na direção deste ponto (chamado de controle) e chegando ao destino (definida por `Q`, em azul), tem-se:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "18613026",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "b3928121",
"metadata": {},
"source": [
"Retornando ao exemplo da modificação da regra de preenchimento, pode-se modificá-lo de modo a incluir curvas quadráticas para obter um efeito similar. Deste modo, considerando os pontos que formam as retas como controle para as curvas e mantendo as origens e destinos dos segmentos, tem-se:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3965967f",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "5e4fe9eb",
"metadata": {},
"source": [
"## Operações com SVG\n",
"Até este momento, os exemplos apresentados apenas trabalharam com as primitivas básicas e seus atributos. Para criar imagens mais elaboradas, pode-se utilizar definições de operações, modelos e transformações que podem ser efetuadas sobre um objeto. A seguir serão apresentadas as transformações geométricas básicas implementadas no SVG com a operação `transform`.\n",
"\n",
"Inicialmente, considerando um exemplo anterior, pode-se fazer uma operação de translação usando o parâmetro `translate`, com os valores do deslocamento. Como os eixos do SVG crescem à direita e para baixo, uma translação com valores positivos moverá o objeto nesta direção:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d9d0434e",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "d6a30dd7",
"metadata": {},
"source": [
"De forma similar, podemos realizar a escala usando a transformação `scale` para realizar a escala de uma figura. Caso esta seja uniforme, precisamos apenas de um valor:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9522d407",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "b2c90543",
"metadata": {},
"source": [
"Caso deseje-se fazer a escala diferencial, deve-se informar os valores das coordenadas para cada eixo. Assim, para uma escala que mantenha o tamanho em `x` e dobre em `y`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "240cb9b5",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "b030ddb6",
"metadata": {},
"source": [
"Para realizar uma rotação, pode-se fazer a operação `transform` com o atributo `rotate`, em graus, no sentido horário. Tomando um exemplo anterior, faz-se uma rotação de uma figura como:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fbe8858d",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "8eccb6e6",
"metadata": {},
"source": [
"Neste caso, também, as operações de escala e rotação alteram as posições do objeto em relação à origem, o que pode ser considerado uma translação implícita. Desta forma, deve-se, igualmente, explicitar esta translação caso deseje-se realizar uma destas operações em relação a um ponto de referência distinto da origem. Assim, para realizar uma escala, por exemplo, faz-se uma translação do ponto de referência em relação à origem, a escala com este ponto sobre a origem, e a translação em relação à posição original do ponto. Novamente, as operações são realizadas da direita para a esquerda. Então, para escalar uma elipse (em preto), dobrando seu raio em $y$ (`ry`), em relação a seu centro (em vermelho):"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d7735b0b",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
},
{
"cell_type": "markdown",
"id": "ccfa7bc8",
"metadata": {},
"source": [
"Do mesmo modo, pode-se fazer uma rotação em relação a um ponto de referência compondo operações de translação e rotação. Assim, um exemplo anterior pode ser representado, fazendo uma rotação de $90°$ em torno do centro da figura, como:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cfa76596",
"metadata": {},
"outputs": [],
"source": [
"%%svg\n",
""
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}