Search code examples
cstructure

Function in C using structures to calculate area and perimeter of a trapezoid (one test not passing)?


The structures are given:

struct Point {
    double x, y;
};
struct Trapezoid {
    struct Point A,B,C,D;
};

I need to write a trapezoids function that receives an array of type struct Trapezoid, the size of the array n, and two empty arrays of type double that we assume are large enough to hold all the required elements. The perimeter of the received trapezoids should be entered in the array given as the third parameter, and their area as the fourth parameter. I have written code that works, but one case doesn't.

  #include <stdio.h>
#include <math.h>

struct Point {
    double x;
    double y;
};

struct Trapezoid {
    struct Point A, B, C, D;
};

double distance(struct Point p1, struct Point p2) {
    double dx = p2.x - p1.x;
    double dy = p2.y - p1.y;
    return sqrt(dx * dx + dy * dy);
}

void trapezoids(struct Trapezoid trapezoids[], int n, double perimeters[], double areas[]) {
    for (int i = 0; i < n; i++) {
        double side1 = distance(trapezoids[i].A, trapezoids[i].B);
        double side2 = distance(trapezoids[i].B, trapezoids[i].C);
        double side3 = distance(trapezoids[i].C, trapezoids[i].D);
        double side4 = distance(trapezoids[i].D, trapezoids[i].A);

        perimeters[i] = side1 + side2 + side3 + side4;

        double baseAvg = (side1 + side3) / 2;
        double height = distance(trapezoids[i].B, trapezoids[i].D);

        double rotation_angle_rad = atan2(trapezoids[i].B.y - trapezoids[i].D.y, trapezoids[i].B.x - trapezoids[i].D.x);
        double rotation_angle_deg = rotation_angle_rad * 180 / M_PI;

        // Corrected formula for area with rotation
        areas[i] = 0.5 * (side1 + side3) * height * fabs(sin(rotation_angle_rad));
    }
}

int main() {
    struct Trapezoid array[4];
    double areas[4];
    double perimeters[4];

    for (int i = 0; i < 4; i++) {
        printf("Enter coordinates for trapezoid %d (A B C D): ", i + 1);
        scanf("%lf %lf %lf %lf %lf %lf %lf %lf",
              &array[i].A.x, &array[i].A.y,
              &array[i].B.x, &array[i].B.y,
              &array[i].C.x, &array[i].C.y,
              &array[i].D.x, &array[i].D.y);
    }

    // If input doesn't match, calculate the actual output
    trapezoids(array, 4, perimeters, areas);

    // Print the results
    for (int i = 0; i < 4; i++) {
        printf("%.2f ", perimeters[i]);
    }

    return 0;
}

The case that doesn't work:

Result: Wrong result
Test Code:

struct Trapezoid array[] = {
  { {-4,2}, {0.950, 6.950}, {-2.586, 7.657}, {-5.414, 4.828} }, // 45 degrees
  { {-4,2}, {-7.5, 8.062}, {-9.098, 4.830}, {-7.098, 1.366} }, // 120 degrees
  { {-4,2}, {-11, 2}, {-9, -1}, {-5, -1} }, // 180 degrees
  { {-4,2}, {-4, -5}, {-1, -3}, {-1, 1} } // 270 degrees
};
double volumes[4], surfaces[4];
int i;

trapezoids(array, 4, perimeters, areas);
for (i=0; i<4; i++)
  printf("%.2f ", surfaces[i]);

Expected output(s): 16.50 16.50 16.50 16.50
 
Your program printed: 11.67 36.83 16.50 33.00

Solution

  • First of all, the OP test code has some confusion of variable names. I assume it should be something like this:

    int main() {
        struct Trapezoid array[] = {
          { {-4,2}, {0.950, 6.950}, {-2.586, 7.657}, {-5.414, 4.828} }, // 45 degrees
          { {-4,2}, {-7.5, 8.062}, {-9.098, 4.830}, {-7.098, 1.366} }, // 120 degrees
          { {-4,2}, {-11, 2}, {-9, -1}, {-5, -1} }, // 180 degrees
          { {-4,2}, {-4, -5}, {-1, -3}, {-1, 1} } // 270 degrees
        };
    
        double areas[4];
        double perimeters[4];
        trapezoids(array, 4, perimeters, areas);
    
        printf("Areas: ");
        for (int i = 0; i < 4; i++) {
            printf("%.2f ", areas[i]);
        }
        printf("\n");
    
        printf("Perimeters: ");
        for (int i = 0; i < 4; i++) {
            printf("%.2f ", perimeters[i]);
        }
        printf("\n");
    }
    

    Secondly, the method for calculating the trapezoid area in the OP code is unnecessarily complicated and gives the wrong result. I have not analyzed the root cause of this since a much simpler approach exists which I recommend to use instead.

    The area calculation can be done using the so-called shoelace formula (250 years old math is helping us here!).

    With the "determinant form" of the formula, the following helper function can be used:

    double determinant(struct Point p1, struct Point p2)
    {
        return (p1.x * p2.y - p2.x * p1.y);
    }
    

    With this, the areas can be calculated as follows:

            double det1 = determinant(trapezoids[i].A, trapezoids[i].B);
            double det2 = determinant(trapezoids[i].B, trapezoids[i].C);
            double det3 = determinant(trapezoids[i].C, trapezoids[i].D);
            double det4 = determinant(trapezoids[i].D, trapezoids[i].A);
            areas[i] = 0.5 * fabs(det1 + det2 + det3 + det4);
    

    Note: calling fabs() can be omitted if it can be assumed that points are provided in counter-clockwise order.

    With the test case of the OP, this gives the correct result:

    Areas: 16.50 16.50 16.50 16.50 
    Perimeters: 17.77 17.77 17.77 17.77