📄 us_anasp.c
字号:
#include <windows.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include "usersurf.h"
/*
Written by Kenneth E. Moore
January 27, 1998
*/
int __declspec(dllexport) APIENTRY UserDefinedSurface(USER_DATA *UD, FIXED_DATA *FD);
/* a generic Snells law refraction routine */
int Refract(double thisn, double nextn, double *l, double *m, double *n, double ln, double mn, double nn);
BOOL WINAPI DllMain (HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
return TRUE;
}
/*
This DLL models an annular aspheric surface as described in:
"Annular surfaces in annular field systems"
By Jose M. Sasian
Opt. eng. 36 (12) P 3401-3401 December 1997
This surface is essentially an odd aspheric surface with an offset in the aspheric terms.
The sag is given by:
Z = (c*r*r) / (1+(1-((1+k)*c*c*r*r))^ 1/2 ) + a*(r-q)^2 + b*(r-q)^3 + c*(r-q)^4 + ...
Note the terms a, b, c, ... have units of length to the -1, -2, -3, ... power.
*/
int __declspec(dllexport) APIENTRY UserDefinedSurface(USER_DATA *UD, FIXED_DATA *FD)
{
int i, loop, pmax;
double q, dr, r, r2, tp, alpha, power, rad, t, x, y, z, dz, sag, mm;
switch(FD->type)
{
case 0:
/* ZEMAX is requesting general information about the surface */
switch(FD->numb)
{
case 0:
/* ZEMAX wants to know the name of the surface */
/* do not exceed 12 characters */
strcpy(UD->string,"AnnularAsph");
break;
case 1:
/* ZEMAX wants to know if this surface is rotationally symmetric */
/* it is, so return any character in the string; otherwise, return a null string */
strcpy(UD->string, "1");
break;
case 2:
/* ZEMAX wants to know if this surface is a gradient index media */
/* it is not, so return a null string */
UD->string[0] = '\0';
break;
}
break;
case 1:
/* ZEMAX is requesting the names of the parameter columns */
/* the value FD->numb will indicate which value ZEMAX wants. */
/* Only "q" in parameter 1 is used for this surface type */
/* returning a null string indicates that the parameter is unused. */
switch(FD->numb)
{
case 1:
strcpy(UD->string, "q");
break;
default:
UD->string[0] = '\0';
break;
}
break;
case 2:
/* ZEMAX is requesting the names of the extra data columns */
/* the value FD->numb will indicate which value ZEMAX wants. */
/* returning a null string indicates that the extradata value is unused. */
switch(FD->numb)
{
case 1:
strcpy(UD->string, "# Terms");
break;
default:
if (FD->numb <= FD->xdata[1] + 1)
{
sprintf(UD->string, "Term %i", FD->numb - 1);
}
else
{
UD->string[0] = '\0';
}
break;
}
break;
case 3:
/* ZEMAX wants to know the sag of the surface */
UD->sag1 = 0.0;
UD->sag2 = 0.0;
q = FD->param[1];
r2 = UD->x * UD->x + UD->y * UD->y;
r = sqrt(r2);
alpha = 1 - (1+FD->k)*FD->cv*FD->cv*r2;
if (alpha < 0) return(-1);
/* now the aspheric terms */
pmax = FD->xdata[1];
dr = r - q;
UD->sag1 = FD->xdata[pmax+1];
for (i = pmax - 1; i >= 1; i--)
{
UD->sag1 = UD->sag1*dr + FD->xdata[i+1];
}
UD->sag1 = UD->sag1*dr*dr + (FD->cv*r2)/(1 + sqrt(alpha));
UD->sag2 = UD->sag1;
break;
case 4:
/* ZEMAX wants a paraxial ray trace to this surface */
/* x, y, z, and the optical path are unaffected, at least for this surface type */
/* for paraxial ray tracing, the return z coordinate should always be zero. */
/* paraxial surfaces are always planes with the following normals */
/* we will ignore the aspheric terms, even the quadratic one, since it has a */
/* meaning that is hard to interpret if q != 0.0 */
UD->ln = 0.0;
UD->mn = 0.0;
UD->nn = -1.0;
power = (FD->n2 - FD->n1)*FD->cv;
if ((UD->n) != 0.0)
{
(UD->l) = (UD->l)/(UD->n);
(UD->m) = (UD->m)/(UD->n);
(UD->l) = (FD->n1*(UD->l) - (UD->x)*power)/(FD->n2);
(UD->m) = (FD->n1*(UD->m) - (UD->y)*power)/(FD->n2);
/* normalize */
(UD->n) = sqrt(1/(1 + (UD->l)*(UD->l) + (UD->m)*(UD->m) ) );
/* de-paraxialize */
(UD->l) = (UD->l)*(UD->n);
(UD->m) = (UD->m)*(UD->n);
}
break;
case 5:
/* ZEMAX wants a real ray trace to this surface */
/* okay, not a plane. */
/* do not allow n == 0 */
if (UD->n == 0.0) return -1;
/* Now, we illustrate an iterative method of finding
the intercept for a general surface. */
/* make sure we do at least 1 loop */
t = 100.0;
tp = 0.0;
x = UD->x;
y = UD->y;
z = UD->z;
loop = 0;
q = FD->param[1];
pmax = FD->xdata[1];
while (fabs(t) > 1e-10)
{
/*
First, compute the sag using whatever the surface sag expression is.
This is given the x and y starting points. The following block of code
will change depending upon the surface shape, the rest of this iteration
is typically common to all surface shapes.
*/
r2 = x * x + y * y;
r = sqrt(r2);
alpha = 1.0 - (1.0 + FD->k)*FD->cv*FD->cv*r2;
if (alpha < 0.0) return(-1);
/* now the aspheric terms */
dr = r - q;
sag = FD->xdata[pmax+1];
for (i = pmax - 1; i >= 1; i--)
{
sag = sag*dr + FD->xdata[i+1];
}
sag = sag*dr*dr + (FD->cv*r2)/(1.0 + sqrt(alpha));
/* okay, now with sag in hand, how far are we away in z? */
dz = sag - z;
/* now compute how far along the z axis this is */
/* note this will crash if n == 0!! */
t = dz / (UD->n);
/* propagate the additional "t" distance */
x += UD->l*t;
y += UD->m*t;
z += UD->n*t;
/* add in the optical path */
tp += t;
/* prevent infinte loop if no convergence */
loop++;
if (loop > 1000) return(-1);
}
/* okay, we should be a the intercept coordinates now */
UD->x = x;
UD->y = y;
UD->z = z;
/* don't forget the path! */
UD->path = tp;
r2 = x * x + y * y;
r = sqrt(r2);
dr = r - q;
/* now do the normals */
if (r==0)
{
UD->ln = 0;
UD->mn = 0;
UD->nn = -1;
}
else
{
alpha = 1.0 - (1.0+FD->k)*FD->cv*FD->cv*r2;
if (alpha < 0) return(-1); /* ray misses */
alpha = sqrt(alpha);
if (pmax)
{
mm = FD->xdata[pmax+1]*(pmax+1);
for (i = pmax-1; i >= 1; i--)
{
mm = mm*dr + (i+1)*FD->xdata[i+1];
}
mm = mm*dr;
}
else mm = 0.0;
mm += (FD->cv*r/(1.0+alpha))*(2.0 + (FD->cv*FD->cv*r2*(1.0+FD->k))/(alpha*(1.0+alpha)));
rad = 1.0 + (mm*mm);
rad = mm / sqrt(rad);
UD->ln = (x/r)*rad;
UD->mn = (y/r)*rad;
UD->nn = UD->ln*UD->ln + UD->mn*UD->mn;
if (UD->nn >= 1.0) return(-1);
else UD->nn = -sqrt(1.0 - UD->nn);
}
if (Refract(FD->n1, FD->n2, &UD->l, &UD->m, &UD->n, UD->ln, UD->mn, UD->nn)) return(-FD->surf);
break;
case 6:
/* ZEMAX wants the index, dn/dx, dn/dy, and dn/dz at the given x, y, z. */
/* This is only required for gradient index surfaces, so return dummy values */
UD->index = FD->n2;
UD->dndx = 0.0;
UD->dndy = 0.0;
UD->dndz = 0.0;
break;
case 7:
/* ZEMAX wants the "safe" data. */
/* this is used by ZEMAX to set the initial values for all parameters and extra data */
/* when the user first changes to this surface type. */
/* this is the only time the DLL should modify the data in the FIXED_DATA FD structure */
for (i = 1; i <= 8; i++) FD->param[i] = 0.0;
for (i = 1; i <= 200; i++) FD->xdata[i] = 0.0;
break;
}
return 0;
}
int Refract(double thisn, double nextn, double *l, double *m, double *n, double ln, double mn, double nn)
{
double nr, cosi, cosi2, rad, cosr, gamma;
if (thisn != nextn)
{
nr = thisn / nextn;
cosi = fabs((*l) * ln + (*m) * mn + (*n) * nn);
cosi2 = cosi * cosi;
if (cosi2 > 1) cosi2 = 1;
rad = 1 - ((1 - cosi2) * (nr * nr));
if (rad < 0) return(-1);
cosr = sqrt(rad);
gamma = nr * cosi - cosr;
(*l) = (nr * (*l)) + (gamma * ln);
(*m) = (nr * (*m)) + (gamma * mn);
(*n) = (nr * (*n)) + (gamma * nn);
}
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -