Ejercicio obligatorio 3

Fecha de entrega: Domingo 8 de octubre

Nota

Nunca es buena idea empezar por un ejercicio integrador antes de tener practicados los temas que el trabajo integra.

Se sugiere antes de desarrollar este trabajo resolver, al menos, los siguientes ejercicios de la guía de Memoria dinámica:

  • Ejercicio 12 (combinar vectores).

  • Ejercicio 14.a (matriz identidad).

  • Ejercicio 16 con el 1.b y 1.c de la guía de estructuras (dirección y persona).

Introducción

Queremos modelar una curva de Bézier, para ello definimos la estructura:

typedef struct {
    float (*puntos)[2];
    size_t n_puntos;
    size_t grado;
} bezier_t;

que permite almacenar en memoria una lista de puntos según el siguiente esquema:

../_images/20232_ej3.png

Trabajo

Creación y destrucción

Se pide implementar la función bezier_t *bezier_crear(size_t grado); que genere la memoria necesaria para una curva de Bézier. Como la curva se crea sin ningún punto, el puntero puntos debe ser inicializado a NULL. La función debe devolver la curva creada o NULL en caso de falla.

Se pide implementar la función void bezier_destruir(bezier_t *bezier); que libere la memoria asociada a la bezier previamente creada.

Tramos

Se pide implementar la función size_t bezier_cantidad_tramos(const bezier_t *bezier); que en función de la cantidad de puntos que tenga la curva de bezier y su grado diga cuántos tramos pueden generarse.

Nota

La relación entre grado, puntos y tramos está en el enunciado del EJ2.

Manipulación

Se pide implementar la función bool bezier_agregar_punto(bezier_t *bezier, float x, float y); que agregue un punto de coordenadas (x, y) a la curva de bezier recibida. La función debe devolver true de poder realizar la operación correctamente.

Se pide implementar la función bezier_t *bezier_clonar(const bezier_t *bezier); que devuelva una copia de bezier en memoria nueva o NULL en caso de falla.

Evaluación

Implementar una función bool bezier_evaluar(const bezier_t *bezier, double t, double *x, double *y); que evalúe la curva de bezier en un t dado. La función debe devolver el valor de x e y en ese punto por la interfaz y por el nombre debe devolver true si el valor de t era válido.

El valor de t será un número entre 0 y la cantidad de tramos de la curva. Valores de t entre 0 y 1 interpolarán el primer tramo, entre 1 y 2 el segundo tramo, etc.

Aplicación y validación

Se provee el código fuente de la función main() que realiza llamadas a cada una de estas funciones. Dicha función debe ser la que se entregue como parte del trabajo sin efectuarle modificaciones de ningún tipo

#include <stddef.h>
#include <assert.h>

typedef struct {
    float (*puntos)[2];
    size_t n_puntos;
    size_t grado;
} bezier_t;

int main(void) {
    float ps[][2] = {
            {0.17305465, 10.136934},
            {0.19149611, 23.287691},
            {12.230908, 23.62676},
            {15.632344, 15.423703},
            {15.632344, 15.423703},
            {13.223768, 15.479423},
            {13.223768, 15.479423},
            {7.7396987, 22.246678},
            {2.9096797, 16.657932},
            {2.7566421, 10.345005},
            {2.6054406, 4.1079585},
            {8.3739896, -0.49167977},
            {13.273927, 5.6239999},
            {13.273927, 5.6239999},
            {16.023752, 5.5849339},
            {16.023752, 5.5849339},
            {13.505533, -1.4803139},
            {0.15461584, -3.0138257},
            {0.17305465, 10.136934},
    };

    bezier_t *bezier = bezier_crear(3);
    assert(bezier != NULL);

    assert(bezier_cantidad_tramos(bezier) == 0);

    for(int i = 0; i < sizeof(ps) / sizeof(ps[0]); i++)
        assert(bezier_agregar_punto(bezier, ps[i][0], ps[i][1]));

    assert(bezier_cantidad_tramos(bezier) == 6);

    bezier_t *clon = bezier_clonar(bezier);
    assert(clon != NULL);

    bezier_destruir(bezier);

    assert(bezier_cantidad_tramos(clon) == 6);

    for(double t = 0; t <= bezier_cantidad_tramos(clon); t += 0.01) {
        double x, y;
        assert(bezier_evaluar(clon, t, &x, &y));
        printf("%f,%f\n", x, y);
    }

    double aux;
    assert(!bezier_evaluar(clon, -0.1, &aux, &aux));
    assert(!bezier_evaluar(clon, 7.1, &aux, &aux));

    bezier_destruir(clon);

    return 0;
}

El programa al ser invocado deberá pasar los asserts dados. La salida del programa es la misma C del ejercicio 2.

Entrega

Deberá entregarse el código fuente del programa desarrollado.

El programa debe:

  1. Compilar correctamente con los flags:

    -Wall -Werror -std=c99 -pedantic
    
  2. pasar Valgrind correctamente,

  3. validar los asserts.

La entrega se realiza a través del sistema de entregas.

El ejercicio es de entrega individual.