jueves, 12 de febrero de 2015

Homenaje al gato sin hogar

(es) Homenaje al gato sin hogar

(eo) Omaĝo al senhejmaj gekatoj

Homenaje al gato sin hogar

http://dx.doi.org/10.13140/RG.2.2.34092.90243

Descripción artística:

Un gato sentado viendo a lo lejos en dirección del observador.

Motivación:

Hay una gran cantidad de gatos y gatas que son abandonados y/o maltratados por los humanos. En su mayoría porque no tienen los medios económicos para darles el cuido correcto y en otras ocaciones por falta de humanidad.
Si los humanos no desean lidiar con la procreación de sus mascotas, deben esterilizarlos.

Descripción técnica:

Perfil de un gato delineado con curvas cúbicas de Bézier.
Dados cuatro puntos P 0 , P 1 , P 2 , P 3 (que pueden ser unidimensionales, bidimensionales, tridimensionales, etc.), llamados puntos de control, se define la curva de Bézier de la siguiente manera:
B(t)=(1-t ) 3 P 0 +3t(1-t ) 2 P 1 +3 t 2 ( 1-t ) P 2 + t 3 P 3 ,0t1
Las curvas de Bézier son ampliamente usadas para la modelación por computadora por su simplicidad de cálculo frente a cualquier técnica de interpolación polinomial o racional.

Archivos

Código (Python):

Este es un segmento del código necesario para la construcción. Se incluye aquí sólo con propósitos ilustrativos.
# coding: utf-8

# Copyright 2015 Eduardo Adam Navas López
# Este programa es Software Libre liberado bajo la licencia GNU GPLv3 o su versión más reciente:
# http://www.gnu.org/licenses/gpl.html

class SecuenciaBezier:
    """Esta clase representa una secuencia de segmentos de Bézier"""
    def __init__(self, (x1,y1), (x2,y2), (x3,y3), (x4,y4)):
        """Esta función recibe los cuatro nodos del primer segmento de Bézier"""
        self.__lista = [ [x1,y1], [x2,y2], [x3,y3], [x4,y4] ]
        self.curvaSuave = True
    
    def sustituirLista(self, L):
        '''Para sustituír toda la lista de una sóla vez'''
        self.__lista = L
    
    def segmento(self, n):
        """Devuelve una lista de cuatro puntos que representan un segmento de Bézier"""
        return self.__lista[3*n: 3*(n+1)+1]
    
    def agregarSegmento(self, i, (x2,y2), (x3,y3), (x4,y4)):
        '''Agrega un segmento adicional a partir de la posición 'i' '''
        self.__lista[i+1:i+1] = [ [x2,y2], [x3,y3], [x4,y4] ]
        # Garantizar continuidad C1
        if self.curvaSuave:
            self.continuizar()
    
    def punto(self, n):
        """Devuelve el punto representado como una lista de dos elementos."""
        return self.__lista[n]
    
    def desplazar(self, (dx,dy)):
        '''Desplaza todos los nodos en una misma cantidad'''
        for p in self.__lista:
            p[0] += dx
            p[1] += dy
    
    def actualizarPunto(self, n, (x,y)):
        '''Permite modificar un punto específico'''
        if self.curvaSuave and n > 1 and n < len(self.__lista)-2:
            if n%3 == 0:
                '''Es un nodo que une dos segmentos,
                por lo que hay que desplazar el punto anterior
                y el siguiente'''
                self.__lista[n-1][0] += x - self.__lista[n][0]
                self.__lista[n-1][1] += y - self.__lista[n][1]
                self.__lista[n+1][0] += x - self.__lista[n][0]
                self.__lista[n+1][1] += y - self.__lista[n][1]
            elif (n+1)%3 == 0:
                '''Es un nodo previo a la unión de dos segmentos,
                por lo que hay que desplazar el primer punto
                del segmento siguiente'''
                self.__lista[n+2][0] = self.__lista[n+1][0] - (x-self.__lista[n+1][0])
                self.__lista[n+2][1] = self.__lista[n+1][1] - (y-self.__lista[n+1][1])
            else: #(n-1)%3 == 0:
                '''Es un nodo posterior a la unión de dos segmentos,
                por lo que hay que desplazar el penúltimo punto
                del segmento anterior'''
                self.__lista[n-2][0] = self.__lista[n-1][0] - (x-self.__lista[n-1][0])
                self.__lista[n-2][1] = self.__lista[n-1][1] - (y-self.__lista[n-1][1])
        self.__lista[n] = [x,y]
    
    def eliminarPuntos(self, n):
        '''Se elimina un segmento completo,
        es decir, tres nodos,
        tomando como centro a un punto de unión.
        '''
        if n==0:
            self.__lista[0:3]=[]
        elif n==len(self.__lista)-1:
            self.__lista[-3:-1] = []
            self.__lista.pop() #esto borra el último
        else:
            if n%3==0:
                self.__lista[n-1:n+2] = []
            else:
                pass #ignorarlo por el momento

    def numSegmentos(self):
        """Devuelve el número de segmentos que esta serie contiene"""
        return int(len(self.__lista)//3) #compatibilidad con Python3 ;)
    
    def numPuntos(self):
        """Devuelve el número de puntos que esta serie contiene"""
        return len(self.__lista)
        
    def calcular(self, t):
        '''Para la actual secuencia de Segmentos Bézier,
        calcula el punto (x,y) al cual corresponde el valor 't'
        '''
        #Calcular el índice del segmento de Bézier
        #con el que debe calcularse este 't':
        numS = self.numSegmentos()
        ixSegmento = int(t * numS)
        if ixSegmento == numS: ixSegmento = ixSegmento-1
        
        s = self.segmento(ixSegmento)
        t = numS * t - ixSegmento
        
        _1_t1 = 1-t
        _1_t2 = _1_t1 * _1_t1
        _1_t3 = _1_t2 * _1_t1
        t2 = t*t
        t3 = t2*t

        x = _1_t3 * s[0][0] + 3*t*_1_t2 * s[1][0] + 3*t2*_1_t1 * s[2][0] + t3 * s[3][0]
        y = _1_t3 * s[0][1] + 3*t*_1_t2 * s[1][1] + 3*t2*_1_t1 * s[2][1] + t3 * s[3][1]
        return x,y
    
    def continuizar(self):
        '''Garantiza continuidad C^1 entre los segmentos'''
        nodo = 4 #debe haber al menos dos segmentos
        while len(self.__lista)>nodo:
            self.__lista[nodo][0] = self.__lista[nodo-1][0]-self.__lista[nodo-2][0]+self.__lista[nodo-1][0]
            self.__lista[nodo][1] = self.__lista[nodo-1][1]-self.__lista[nodo-2][1]+self.__lista[nodo-1][1]
            nodo += 3

No hay comentarios:

Publicar un comentario