Calculate surface area of a 3D mesh - c

I have a 3D mesh defined by verteces and triangles. I have also normals of the mesh. I'd like to calculate the area of the mesh, assuming it's always closed. I found an interesting implementation of calculation of the 3D volume in this question, and I applied it in a C code to build a function called by R. This is the code:
double SignedVolumeOfTriangle(double p1X, double p1Y, double p1Z,
double p2X, double p2Y, double p2Z, double p3X, double p3Y, double p3Z) {
double v321 = p3X*p2Y*p1Z;
double v231 = p2X*p3Y*p1Z;
double v312 = p3X*p1Y*p2Z;
double v132 = p1X*p3Y*p2Z;
double v213 = p2X*p1Y*p3Z;
double v123 = p1X*p2Y*p3Z;
return (double)(1.0/6.0)*(-v321 + v231 + v312 - v132 - v213 + v123);
}
void MeshVolume(double *X, double *Y, double *Z, int *numT, int *V1, int *V2, int *V3, double *Volume) {
int n;
*Volume=0;
for (n=0; n<*numT; n++) {
*Volume = *Volume + SignedVolumeOfTriangle(X[V1[n]], Y[V1[n]], Z[V1[n]], X[V2[n]], Y[V2[n]], Z[V2[n]], X[V3[n]], Y[V3[n]], Z[V3[n]]);
}
*Volume = fabs(*Volume);
}
Neither in the question nor in the article linked I found the algorithm for calculating the Area of the mesh. Is there anybody can help me please?

You have a closed volume whose surface is made up by triangles. And all triangles contribute to the outer surface. right?
The surface of a triangle between points P, Q and R can be obtained by:
A = 0.5 * |PQ × PR|
= 0.5 * |PQ| * |PR| * sin(Ɵ)
where
PQ = Q - P
PR = R - P
and × denotes the cross product and Ɵ is the angle between the vectors. (The magnitude of the resulting vector of a cross product is the area of a parallelogramme between the two original vectors. Half of that is the area of a triangle.)
Sum the aeras of all triangles. There's no need to take the absolute value, because the area can only be zero or positive. So:
double AreaOfTriangle(double p1X, double p1Y, double p1Z,
double p2X, double p2Y, double p2Z,
double p3X, double p3Y, double p3Z)
{
double ax = p2x - p1x;
double ay = p2y - p1y;
double az = p2z - p1z;
double bx = p3x - p1x;
double by = p3y - p1y;
double bz = p3z - p1z;
double cx = ay*bz - az*by;
double cy = az*bx - ax*bz;
double cz = ax*by - ay*bx;
return 0.5 * sqrt(cx*cx + cy*cy + cz*cz);
}
void MeshSurface(double *X, double *Y, double *Z,
int *numT, int *V1, int *V2, int *V3, double *Area)
{
int n;
*Area = 0.0;
for (n=0; n<*numT; n++) {
*Area += AreaOfTriangle(X[V1[n]], Y[V1[n]], Z[V1[n]],
X[V2[n]], Y[V2[n]], Z[V2[n]],
X[V3[n]], Y[V3[n]], Z[V3[n]]);
}
}

Related

How to insert a value to a variable while also trying to check if that value is zero

I'm having an assignment to complete where I need to find the zero body bias threshold voltage,based on the value of Vsb. Though this problem is based on the solution of another exercise, I give to you below some screenshots of what I need to calculate:
Where the Vthreshold formula is:
So I want to implement this:
My problem is that I don't know how to insert the Vsb value to the final formula in a way so that it can affect the final value of the zero body bias.The code that I've written returns only the value of the zero body bias based on the vthreshold only
Here's the code that I've written so far:
#include <stdio.h>
#include <math.h>
double vFB (double fgs, double B);
double vT (double A, double C);
double vA (double D);
double vSP (double J);
double gamwt (double A, double F);
double roulis( double H, double L);
double thres_volt(double vFB, double vSP, double vT, double vA);
double zero_body_bias_threshold_voltage(double thresh_volt, double gamma, double roulis);
int main(){
double eps0 = 8.854E-14; //Dielectric constant, void, [F/cm]
double epsox = 3.9*eps0; //Oxide dielectric constant, [F/cm]
double epsSi = 11.2*eps0; //Si dielectric constant, [F/cm]
double q = 1.6E-19; // expressed in Coulomb
double ni = 1.45E10; // intrincic carrier concentration expressed in cm^-3
double Na = 1.0E15; // expressed in cm^-3
double Ndpoly = 1.0E19; // expressed in cm^-3
double xox = 100.0; //Oxide thickness,[A]
double DI = 2.0E12; // number of implanted ions/cm^2 expressed in cm^-2
double kTq = 0.026; // expressed in Volt
double Qf = q*1.0E11; //expressed in C/cm2
double Vsb;
double Cox = (epsox)/(xox*1.0E-8); //Oxide capacitance per unit area, [F/cm2]
double A = 1.0/Cox;
double G = ((Na*Ndpoly))/((ni*ni));
double fgs = -((kTq)*log(G));
double B = A*Qf;
double T = Na/ni;
double J = 2*kTq*log(T);
double K = 2*q*epsSi*Na*J;
double C = sqrt(K);
double W = (q*DI)*(1.0/(Cox));
double D = W;
double U = 2*q*epsSi*Na;
double F = sqrt(U);
double ginomeno1 = J + Vsb;
double tetragwniki_tou_ginomenou1 = sqrt(ginomeno1);
double H = tetragwniki_tou_ginomenou1;
double ginomeno2 = J;
double tetragwniki_tou_ginomenou2 = sqrt(ginomeno2);
double L = tetragwniki_tou_ginomenou2;
double v1 = vFB(fgs, B);
double v2 = vSP(J);
double v3 = vT(A,C);
double v4 = vA(W);
double v5 = gamwt(A,F);
double v6 = roulis(H,L);
double v7 = thres_volt(v1,v2,v3,v4);
double v8 = zero_body_bias_threshold_voltage (v7,v5,v6);
double FocmtofFoum = 1.0E11; //Conversion factor of F/cm to fF/um
double Coxnew = Cox*FocmtofFoum;
printf ("Cox = %e [F/cm2]\n",Cox);
printf ("Coxnew = %f [fF/um]\n",Coxnew);
printf ("VFB = %f [V]\n", v1);
printf ("Vsp = %f [V]\n", v2);
printf ("Vt =%f [V]\n", v3);
printf ("Va = %f [V]\n", v4);
printf ("gamma = %f [V ^1/2]\n", v5);
printf ("Give me a number for Vsb..");
scanf ("%f", &Vsb);
if (Vsb==0)
ginomeno1 = J;
printf ("V_body_bias = %f [V]\n", v8);
getch();
return 0;
}
double vFB (double fgs, double B)
{
return (fgs-B);
}
double vT (double A, double C){
return (A*C);
}
double vA (double D){
return (+D);
}
double vSP (double J)
{
return (+J);
}
double gamwt (double A, double F)
{
return (A*F);
}
double roulis( double H, double L)
{
return (H-L);
}
double thres_volt(double vFB, double vSP, double vT, double vA)
{
return (vFB+vSP+vT+vA);
}
double zero_body_bias_threshold_voltage(double thresh_volt, double gamwt, double roulis)
{
return (thresh_volt + gamwt*roulis);
}

Initial value problem for a system of ODEs solver C program

So I wanted to implement the path of the Moon around the Earth with a C program.
My problem is that you know the Moon's velocity and position at Apogee and Perigee.
So I started to solve it from Apogee, but I cannot figure out how I could add the second velocity and position as "initial value" for it. I tried it with an if but I don't see any difference between the results. Any help is appreciated!
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
typedef void (*ode)(double* p, double t, double* k, double* dk);
void euler(ode f, double *p, double t, double* k, double h, int n, int N)
{
double kn[N];
double dk[N];
double Rp = - 3.633 * pow(10,8); // x position at Perigee
for(int i = 0; i < n; i++)
{
f(p, 0, k, dk);
for (int j = 0; j < N; j++)
{
if (k[0] == Rp) // this is the "if" I mentioned in my comment
// x coordinate at Perigee
{
k[1] = 0; // y coordinate at Perigee
k[2] = 0; // x velocity component at Perigee
k[3] = 1076; // y velocity component at Perigee
}
kn[j] = k[j] + h * dk[j];
printf("%f ", kn[j]);
k[j] = kn[j];
}
printf("\n");
}
}
void gravity_equation(double* p, double t, double* k, double* dk)
{
// Earth is at the (0, 0)
double G = p[0]; // Gravitational constant
double m = p[1]; // Earth mass
double x = k[0]; // x coordinate at Apogee
double y = k[1]; // y coordinate at Apogee
double Vx = k[2]; // x velocity component at Apogee
double Vy = k[3]; // y velocity component at Apogee
dk[0] = Vx;
dk[1] = Vy;
dk[2] = (- G * m * x) / pow(sqrt((x * x)+(y * y)),3);
dk[3] = (- G * m * y) / pow(sqrt((x * x)+(y * y)),3);
}
void run_gravity_equation()
{
int N = 4; // how many equations there are
double initial_values[N];
initial_values[0] = 4.055*pow(10,8); // x position at Apogee
initial_values[1] = 0; // y position at Apogee
initial_values[2] = 0; // x velocity component at Apogee
initial_values[3] = (-1) * 964; //y velocity component at Perigee
int p = 2; // how many parameters there are
double parameters[p];
parameters[0] = 6.67384 * pow(10, -11); // Gravitational constant
parameters[1] = 5.9736 * pow(10, 24); // Earth mass
double h = 3600; // step size
int n = 3000; // the number of steps
euler(&gravity_equation, parameters, 0, initial_values, h, n, N);
}
int main()
{
run_gravity_equation();
return 0;
}
Your interface is
euler(odefun, params, t0, y0, h, n, N)
where
N = dimension of state space
n = number of steps to perform
h = step size
t0, y0 = initial time and value
The intended function of this procedure seems to be that the updated values are returned inside the array y0. There is no reason to insert some hack to force the state to have some initial conditions. The initial condition is passed as argument. As you are doing in void run_gravity_equation(). The integration routine should remain agnostic of the details of the physical model.
It is extremely improbable that you will hit the same value in k[0] == Rp a second time. What you can do is to check for sign changes in Vx, that is, k[1] to find points or segments of extremal x coordinate.
Trying to interpret your description closer, what you want to do is to solve a boundary value problem where x(0)=4.055e8, x'(0)=0, y'(0)=-964 and x(T)=-3.633e8, x'(T)=0. This has the advanced tasks to solve a boundary value problem with single or multiple shooting and additionally, that the upper boundary is variable.
You might want to to use the Kepler laws to get further insights into the parameters of this problem so that you can solve it just with a forward integration. The Kepler ellipse of the first Kepler law has the formula (scaled for Apogee at phi=0, Perigee at phi=pi)
r = R/(1-E*cos(phi))
so that
R/(1-E)=4.055e8 and R/(1+E)=3.633e8,
which gives
R=3.633*(1+E)=4.055*(1-E)
==> E = (4.055-3.633)/(4.055+3.633) = 0.054891,
R = 3.633e8*(1+0.05489) = 3.8324e8
Further, the angular velocity is given by the second Kepler law
phi'*r^2 = const. = sqrt(R*G*m)
which gives tangential velocities at Apogee (r=R/(1-E))
y'(0)=phi'*r = sqrt(R*G*m)*(1-E)/R = 963.9438
and Perigee (r=R/(1+E))
-y'(T)=phi'*r = sqrt(R*G*m)*(1+E)/R = 1075.9130
which indeed reproduces the constants you used in your code.
The area of the Kepler ellipse is pi/4 times the product of smallest and largest diameter. The smallest diameter can be found at cos(phi)=E, the largest is the sum of apogee and perigee radius, so that the area is
pi*R/sqrt(1-E^2)*(R/(1+E)+R/(1-E))/2= pi*R^2/(1-E^2)^1.5
At the same time it is the integral over 0.5*phi*r^2 over the full period 2*T, thus equal to
sqrt(R*G*m)*T
which is the third Kepler law. This allows to compute the half-period as
T = pi/sqrt(G*m)*(R/(1-E^2))^1.5 = 1185821
With h = 3600 the half point should be reached between n=329 and n=330 (n=329.395). Integration with scipy.integrate.odeint vs. Euler steps gives the following table for h=3600:
n [ x[n], y[n] ] for odeint/lsode for Euler
328 [ -4.05469444e+08, 4.83941626e+06] [ -4.28090166e+08, 3.81898023e+07]
329 [ -4.05497554e+08, 1.36933874e+06] [ -4.28507841e+08, 3.48454695e+07]
330 [ -4.05494242e+08, -2.10084488e+06] [ -4.28897657e+08, 3.14986514e+07]
The same for h=36, n=32939..32940
n [ x[n], y[n] ] for odeint/lsode for Euler
32938 [ -4.05499997e+08 5.06668940e+04] [ -4.05754415e+08 3.93845978e+05]
32939 [ -4.05500000e+08 1.59649309e+04] [ -4.05754462e+08 3.59155385e+05]
32940 [ -4.05500000e+08 -1.87370323e+04] [ -4.05754505e+08 3.24464789e+05]
32941 [ -4.05499996e+08 -5.34389954e+04] [ -4.05754545e+08 2.89774191e+05]
which is a little closer for the Euler method, but not much better.

How to consult GSL ODE with many parameters and harmonic functions

I'm working on non-linear differential equation using GSL. The thing is I'm quite new on C stuffs. I just adapted the sample on GNU site into the equation I'm interested in right now.
This is the equation:
d2x/dt2 + r*dx/dy + cos(x) + v*cos(2*x+0.4) E1*sin(wt) + E2*sin(2*w*t+a) = 0
What I am stuck is I have no idea how to plug in multiple parameters in the codes. Moreover, I don't know how to employ cosine or sine function in this code.
I tried to figure out this problem, by searching on Google all the way. I couldn't find any thing that helps me.
#include <stdio.h>
#include <gsl/gsl_errno.h>
#include <math.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_odeiv2.h>
int func (double t, const double x[], double y[], void *params)
{
double r = *(double *)params;
double v = *(double *)params;
double w = *(double *)params;
double E1 = *(double *)params;
double E2 = *(double *)params;
double a = *(double *)params;
y[0] = x[1];
y[1] = -r*x[1] - cos(x[0]) - v*cos(2*x[0]+0.4) - E1*sin(w*t) - E2*sin(2*w*t+a);
return GSL_SUCCESS;
}
int jac (double t, const double x[], double *dydx, double dydt[], void *params)
{
double r = *(double *)params;
double v = *(double *)params;
double w = *(double *)params;
double E1 = *(double *)params;
double E2 = *(double *)params;
double a = *(double *)params;
gsl_matrix_view dydx_mat = gsl_matrix_view_array (dydx, 2, 2);
gsl_matrix * m = &dydx_mat.matrix;
gsl_matrix_set (m, 0, 0, 0.0);
gsl_matrix_set (m, 0, 1, 1.0);
gsl_matrix_set (m, 1, 0, sin(x[0]) + 2*v*sin(2*x[0]+0.4));
gsl_matrix_set (m, 1, 1, -r);
dydt[0] = 0.0;
dydt[1] = 0.0;
return GSL_SUCCESS;
}
int main (void)
{
double r = 0.0;
double v = 0.0;
double w = 2.4;
double E1 = -2.3;
double E2 = 0;
double a = 0.7;
gsl_odeiv2_system sys = {func, jac, 2, &r, &v, &w, &E1, &E2, &a};
gsl_odeiv2_driver *d = gsl_odeiv2_driver_alloc_x_new (&sys, gsl_odeiv2_step_rk8pd, 1e-6, 1e-6, 0.0);
int i;
double t = 0.0, t1 = 10000;
double x[2] = {0.0, 0.0};
for (i = 1 ; i<=10000; i++)
{
double ti = i*t1/10000;
int status = gsl_odeiv2_driver_apply (d, &t, ti, x);
if (status != GSL_SUCCESS)
{
printf("error, return value%d\n", status);
break;
}
printf("%.5e %.5e %.5e\n", t, x[0], x[1]);
}
gsl_odeiv2_driver_free (d);
return 0;
}
The params argument is a pointer (address / memory location) to some arbitrary data structure. In the example from the GSL documentation, their equation contained only one parameter, which means it's okay to just pass the address of a double-precision number.
However, for your problem, you need to access 6 different parameters. You can't access every parameter with the same address!
/* this doesn't work! */
double r = *(double *)params;
double v = *(double *)params;
double w = *(double *)params;
double E1 = *(double *)params;
double E2 = *(double *)params;
double a = *(double *)params;
Since all the addresses are the same, you are referring to the same number. To remedy this, you can either: store all the parameters in an array of length 6, or store them in a predefined data structure. The latter approach is more readable so I will demonstrate that.
First define a data type to specify what parameters you will store:
struct param_type {
double r;
double v;
double w;
double E1;
double E2;
double a;
};
Now, create a structure of this type in the main function and store the actual values of the parameter:
struct param_type my_params = {r, v, w, E1, E2, a};
When defining the system, you store a pointer to that struct param_type:
gsl_odeiv2_system sys = {func, jac, 2, &my_params};
To use the parameter inside func and jac, you simply cast the params argument from a generic pointer (void *) to a pointer for your specific data type (struct param_type *):
struct param_type *my_params_pointer = params;
(Note that in C++ this must be written with an explicit cast.) Finally, you can access the parameters via:
double r = my_params_pointer->r;
double v = my_params_pointer->v;
double w = my_params_pointer->w;
double E1 = my_params_pointer->E1;
double E2 = my_params_pointer->E2;
double a = my_params_pointer->a;
The arrow -> is used here instead of the dot . because my_params_pointer is a pointer and needs to be dereferenced before use.
If you are working with parameters, most likely they are of the same type (double). In that case this can be solved too using an array and then access the elements from func and/or jac.
Another option could be use a gsl_vector and then "get" the values inside the functions. This will involve use free.

newton-raph func pointer in C

I am trying to implement the Newton-Raphson method using a single function pointer. The function must contain both the equation and its derivative. I am having difficulty passing through these two separate functions within the test function.
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
double NR(double, double(*)(double, double*), double);
void test_function( double x, double * f, double * f_prime )
{
*f = (x-2) * (x-2);
*f_prime = 2*x - 4;
}
double NR( double x0, double (*test_function)(double x, double *f, double *f_prime), double precision )
{
int i;
while(!isnan(x0)){
i = x0;
x0 = (x0 - (test_function(x0, f, 0)/test_function(x0, 0, f_prime)));
if(!isnan(x0))
printf("%f\n",x0);
if ( i - x0 < 0 )
printf("NO ROOT FOUND");
return -1;
else if ( i - x0 > 0 && i - x0 < precision )
break;
}
}
int main(void)
{
double x0 = 300;
double precision = .0000001;
double root = NR( x0, test_function, precision);
printf("%f\n",root);
return 0;
}
Thank you
Update declaration of NR to take function pointer that takes 3 parameters as
double NR(double, double(*)(double, double*, double *), double);
instead of
double NR(double, double(*)(double, double*), double);
As you are passing 0 (or NULL) to either prime or f_prime, you can update test function as
void test_function( double x, double * f, double * f_prime )
{
if(f)
*f = (x-2) * (x-2);
if(f_prime)
*f_prime = 2*x - 4;
}
Also you need to update NR as below to define f and f_prime
double NR( double x0, double (*test_function)(double x, double *f, double *f_prime), double precision )
{
int i;
double f, f_prime;
...
//your code
...
//----------------------------v pass address -------------v
x0 = (x0 - (test_function(x0, &f, 0)/test_function(x0, 0, &f_prime)));
....
//your code
}

How to compute Direct and Inverse Conversion between geodetic coordinates (ECEF) to local reference (ENU)

How to convert X,Y,Z from local reference (ENU) to world reference (ECEF) and reverse it ??
Input:
(lat,long) = center of the local reference system.
x,y,z = position of the point,in local system reference.
Output: (lat,long)= poistion of the point in WGS84.
Also: reverse coordinate.
I suppose the step is:
1) convert Xlocal,Ylocal,Zlocal -> Xecef,Yecef,Zecef
2) convert Xecef,Yecef,Zecef -> Lat,Long (WGS84)
And than
1) convert Lat,Long(WGS84) -> Xecef,Yecef,Zecef
2) convert Xecef,Yecef,Zecef -> Xlocal,Ylocal,Zlocal
The Ordnance Survey has published A Guide to Coordinate Systems in Great Britain. It's a PDF document that contains the mathematical algorithms for converting between lat/long and cartesian coordinates. Obviously it is oriented towards British systems, but the parameters needed for WGS84 are given in the guide.
I'm working on a geospatial AR app and have just tackled this myself in objective-C / iOS (with quite a bit of help from this example code from Apple).
The three C functions to convert from lat lon to ecef to ENU and back are below. The mathematical formulas come from this wikipedia article.
#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))
#define DEGREES_TO_RADIANS(degrees)((M_PI * degrees)/180)
#define WGS84_A (6378137.0) // WGS 84 semi-major axis constant in meters
#define WGS84_E (8.1819190842622e-2) // WGS 84 eccentricity
// Converts latitude, longitude to ECEF coordinate system
void latLonToEcef(double lat, double lon, double alt, double *x, double *y, double *z)
{
double clat = cos(DEGREES_TO_RADIANS(lat));
double slat = sin(DEGREES_TO_RADIANS(lat));
double clon = cos(DEGREES_TO_RADIANS(lon));
double slon = sin(DEGREES_TO_RADIANS(lon));
double N = WGS84_A / sqrt(1.0 - WGS84_E * WGS84_E * slat * slat);
*x = (N + alt) * clat * clon;
*y = (N + alt) * clat * slon;
*z = (N * (1.0 - WGS84_E * WGS84_E) + alt) * slat;
}
// Converts ECEF to ENU coordinates centered at given lat, lon
void ecefToEnu(double lat, double lon, double xRef, double yRef, double zRef, double xPOI, double yPOI, double zPOI, double *e, double *n, double *u)
{
double clat = cos(DEGREES_TO_RADIANS(lat));
double slat = sin(DEGREES_TO_RADIANS(lat));
double clon = cos(DEGREES_TO_RADIANS(lon));
double slon = sin(DEGREES_TO_RADIANS(lon));
double dx = xPOI - xRef;
double dy = yPOI - yRef;
double dz = zPOI - zRef;
*e = -slon * dx + clon * dy;
*n = -slat * clon * dx - slat * slon * dy + clat * dz;
*u = clat * clon * dx + clat * slon * dy + slat * dz;
}
// Converts ENU of a POI (in realtion to a reference point) to ECEF coordinates
void enuToEcef(double lat, double lon, double xRef, double yRef, double zRef, double *xPOI, double *yPOI, double *zPOI, double e, double n, double u)
{
double clat = cos(DEGREES_TO_RADIANS(lat));
double slat = sin(DEGREES_TO_RADIANS(lat));
double clon = cos(DEGREES_TO_RADIANS(lon));
double slon = sin(DEGREES_TO_RADIANS(lon));
*xPOI = (-slon * e) + (-slat * clon * n) + (clat * clon * u) + xRef;
*yPOI = (clon * e) + (-slat * slon * n) + (clat * slon * u) + yRef;
*zPOI = (0 * e) + (clat * n) + (slat * u) + zRef;
}

Resources