Soon, we will launch a new and updated customer portal, which is an important step toward providing our customers with one place to learn, interact, and get help.
Learn more.

How to compile a User-Defined surface

This article explains:
  • What user-defined surfaces are
  • How to compile a user-defined surface using Microsoft Visual Community 2017. Please check for conditions of usage.
  • How to use other compilers
Mark Nicholson
User Defined Features


What is a User-Defined surface?

A surface defines the boundaries between optical materials. Surfaces can be refractive, reflective, diffractive and gradient index. OpticStudio supports more than 65 different surface types, including very general surface shapes like Polynomial surfaces and Biconic Zernikes.

But, it is often the case that a user wants something tailored specifically to his or her own needs, and this is where the User-Defined surface is extremely useful and powerful. You can always raise a feature request for us to add a new surface type for you, but OpticStudio also gives you an interface to do this yourself.

The User-Defined surface is a compiled function (strictly speaking, a Windows DLL) which can implement any surface shape, phase, transmission function, or gradient index, and any combination of these, that you wish. User-Defined surfaces can be parametric, or be based on a datafile, or both.

This article describes the steps required to compile a user-defined surface, using the supplied samples as a starting point. It does not address the specific issues involved in coding the surface, which will be described in a separate article.

Producing such a User-Defined surface is relatively easy, provided you have a clear mathematical description of how the surface shape is to be defined, and a little programming experience. We provide sample files to make getting started easy. The hard part is always in knowing what the mathematical functions needed to define the surface are!

The first step, therefore, is to define the specification. Find the surface in OpticStudio which is closest to what you want and read the description in the manual of this surface. Then write out your specification in the same style. Write a text-based description of the surface, then give the sag equation, phase equation etc as required, and list what parameters in the Lens Data Editor and Extra Data Editor do what. Finally, think what the initial values of these parameters should be when the surface is first used. For example, when you first insert a Standard surface, the radius of curvature is infinite, the thickness is zero, and the conic constant is zero. Think what this "safe data" needs to be for your surface.

Then look in the manual again, in the section on the User-Defined surface. You will see that there are many supplied example files. Find the sample file that is closest to what you want and use this as your starting point. By using a supplied sample as your starting point, you will not need to worry about writing all the code that makes your surface talk to OpticStudio: that is already done. You can concentrate solely on programming your surface.

Once you have found the sample file that is closest to your requirements, copy the source file (the file that ends in .c) and give it a new name. In this example, we are going to say that the sample file us_arrayeven.c is the closest to our requirements. This file is in user data folder <…Zemax\DLL>. This surface defines a rectangular array of even-asphere lenslets. Make a copy of this file and call it myarray.c

Compiling a User-Defined surface with Microsoft Visual Community 2017

First start Microsoft Visual Community 2017. The click File...New...Project and select a Visual C++...General...Empty Project. Give the project a name and choose a directory to hold the project files. In this case, the project name is My_asphere, Click OK, and then choose to create an empty project:
The new Project wizrad in Ms Visual C++ version 6

OpticStudio is offered as 64-bit program. DLLs must be correctly compiled as 64-bit to work properly. To compile 64-bit code, you must do the following:
  • Build…Configuration Manager, Active solution platform: <New>
  • Select x64 from the drop-down list; that’s all!

Create an empty dll project

This means that the project is an empty container waiting for you to add files. In the Explorer tree on the left hand side, select File View, and right-mouse-click on Source Files, and select Add and then Existing Items:

Add Files

In the dialog that opens, navigate to <…Zemax\DLL> and select myarray.c. Remember, this is the copy of us_arrayeven.c that you made when you followed the instructions on the bottom of page 1 of this article.

Then, in the project pane above, right-mouse-click on Header Files, choose Add and then Existing Items, and add the file usersurf.h, which is also in the <…Zemax\DLL> folder. The project pane should now look like this:

After inserting all files
The last thing we must do before we can start editing our file is to set the code generation options in Microsoft Visual Community 2017. Right click “My_asphere” and select Properties:

Project Settings

The first setting to change is under General...Configuration Type. Our solution is a DLL:

Dynamic library

The second setting to change is under C/C++...Code Generation. We recommend compiling Multi-Threaded Code. That setting (/MT) option ensures users without C libraries on their computer can run the DLL.
Note on Multi-threaded (/MT) vs Multi-threaded DLL (/MD):
The Multi-threaded vs Multi-threaded DLL choice is somewhat complex, but it mostly comes down to compatibility vs. ease of use. If you are writing a DLL that doesn’t depend on interfacing with other libraries, then using the Multi-Threaded runtime libraries means you don’t have to worry about installing the C++ redistributable on the machine that will use the DLL. If you use other libraries from within your DLL however, you need to use the same runtime libraries that it does, which is almost always the Multi-Threaded DLL runtime.
For simple DLL’s that don’t use other third-party libraries, the Multi-threaded runtime is probably the best choice.
That's all the configuration that is required!
To test that the compiler works, we are going to build the project: Build...Rebuild Solution. The DLL should compile without error:
It compiles!

Then copy the DLL from your project's /release folder into the <…Zemax\DLL>folder.

Selecting the surface

Now start OpticStudio and select a surface to be User-Defined:

Data file
In the Lens Data Editor, we gave our compiled surface:
User defined

Congratulations! You have compiled a User-Defined surface!

Using other compilers

Any other 64-bit C compiler can be used, providing it can create a multi-threaded Windows DLL project. There are a huge number of available compilers, and we can't provide technical support on them all. If you are using some other compiler, the question to ask that program's technical support staff is:
How do I create an empty multi-threaded Win32 DLL project? The steps on the earlier page will be helpful in working this out.


This article has discussed the mechanics of compiling a User-Defined surface using Microsoft Visual Community 2017. Please check for conditions of usage. It has not described the process of writing a specific surface. This will be discussed in a separate article.