Skip to main content

ZOS-API interface 2025 R1

Example 23 - C++

Last update: 17.07.2025

C++

// CppStandaloneApplication.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <ctime>
#include <functional>
#include <assert.h>
#include <fstream>
#include <iomanip>
#include <vector>
#include <cstdlib>
// Note - .tlh files will be generated from the .tlb files (above) once the project is compiled.
// Visual Studio will incorrectly continue to report IntelliSense error messages however until it is restarted.
#include "zosapi.h"
using namespace std;
using namespace ZOSAPI;
using namespace ZOSAPI_Interfaces;
void handleError(std::string msg);
void logInfo(std::string msg);
void finishStandaloneApplication(IZOSAPI_ApplicationPtr TheApplication);
int RunApplication()
{
CoInitialize(NULL);
// Create the initial connection class
IZOSAPI_ConnectionPtr TheConnection(__uuidof(ZOSAPI_Connection));
// Attempt to create a Standalone connection
IZOSAPI_ApplicationPtr TheApplication = TheConnection->CreateNewApplication();
if (TheApplication == nullptr)
{
handleError("An unknown error occurred!");
return -1;
}
// Check the connection status
if (!TheApplication->IsValidLicenseForAPI)
{
handleError("License check failed!");
return -1;
}
if (TheApplication->Mode != ZOSAPI_Mode::ZOSAPI_Mode_Server)
{
handleError("Standlone application was started in the incorrect mode!");
return -1;
}
// Add your custom code here...
// creates a new API directory
CreateDirectory(_bstr_t(TheApplication->SamplesDir + "\\API"), NULL);
CreateDirectory(_bstr_t(TheApplication->SamplesDir + "\\API\\CPP"), NULL);
// Set up primary optical system
IOpticalSystemPtr TheSystem = TheApplication->CreateNewSystem(SystemType_Sequential);
_bstr_t sampleDir = TheApplication->SamplesDir;
_bstr_t testFile = sampleDir + "\\Sequential\\Objectives\\Double Gauss 28 degree field.zos";
TheSystem->LoadFile(testFile, false);
fstream textfile;
string filepath = sampleDir + "\\API\\CPP\\e23_ray_fan_native_manual_comparison.txt";
textfile.open(filepath, fstream::trunc | ios::out);
int max_rays = 150;
int max_num_field = TheSystem->SystemData->Fields->NumberOfFields;
int max_wave = TheSystem->SystemData->Wavelengths->NumberOfWavelengths;
int max_field = 0;
for (int i = 1; i < max_num_field; i++)
if (TheSystem->SystemData->Fields->GetField(i)->Y > max_field)
max_field = (int)TheSystem->SystemData->Fields->GetField(i)->Y;
// Set up timer.
double duration1, duration2;
std::clock_t start;
start = std::clock();
cout << "Section 1: Batch Ray trace\n\n";
textfile << "Section 1: Batch Ray trace\n\n";
// Set up Batch Ray Trace
IBatchRayTracePtr raytrace = TheSystem->Tools->OpenBatchRayTrace();
int nsur = TheSystem->LDE->NumberOfSurfaces;
IRayTraceNormUnpolDataPtr normUnPolData = raytrace->CreateNormUnpol(max_rays + 1, RaysType_Real, nsur);
// define batch ray trace constants (hx, hy, px, py)
double hx = 0, px = 0;
std::vector<double> py_ary(max_rays + 1);
for (int i = 0; i < max_rays + 1; i++) {
py_ary[i] = (double)i / max_rays * 2 - 1;
}
// image surface number and primary wavelength
//
int pwav = 0;
for (int a = 1; a <= TheSystem->SystemData->Wavelengths->NumberOfWavelengths; a++) {
if (TheSystem->SystemData->Wavelengths->GetWavelength(a)->IsPrimary)
pwav = a;
}
// creates array of Y coordinate chief ray values
double *chief_ary = new double[max_num_field];
for (int field = 1; field <= max_num_field; field++) {
double hy = TheSystem->SystemData->Fields->GetField(field)->Y / max_field;
// gets single value without using MFE(see ZPL OPEV)
chief_ary[field - 1] = TheSystem->MFE->GetOperandValue(MeritOperandType_REAY, nsur, pwav, 0, hy, 0, 0, 0, 0);
}
// initialize x / y image plane arrays
std::vector<vector<vector<double>>> y_ary(max_num_field, vector<vector<double>>(max_wave, std::vector<double>((max_rays + 1) * (max_rays + 1), 0)));
// setup plot
for (int field = 1; field < max_num_field + 1; field++) {
double hy = TheSystem->SystemData->Fields->GetField(field)->Y / max_field;
for (int wave = 1; wave < max_wave + 1; wave++) {
// Adding Rays to Batch, varying normalised object height hy
normUnPolData->ClearData();
for (int i = 0; i < max_rays + 1; i++) {
double py = py_ary[i];
normUnPolData->AddRay(wave, hx, hy, px, py, OPDMode_None);
}
// Run Batch Ray Trace
ISystemToolPtr baseTool = raytrace;
baseTool->RunAndWaitForCompletion();
// Read and display results
normUnPolData->StartReadingResults();
long rayNumber, errCode, vigCode;
double rayX, rayY, rayZ, rayL, rayM, rayN, rayl2, raym2, rayn2, rayopd, rayintensiry;
textfile << "Field [" << field << "] and Wave# [" << wave << "]\n";
textfile << "Ray# Py REAY\n";
VARIANT_BOOL success = normUnPolData->ReadNextResult(&rayNumber, &errCode, &vigCode, &rayX, &rayY, &rayZ, &rayL, &rayM, &rayN, &rayl2, &raym2, &rayn2, &rayopd, &rayintensiry);
while (success) {
if ((errCode == 0) && (vigCode == 0)) {
textfile << setw(7) << rayNumber << setw(15) << left << py_ary[rayNumber - 1] << setw(15) << left << ((rayY - chief_ary[field - 1]) * 1000) << "\n";
y_ary[field - 1][wave - 1][rayNumber - 1] = rayY;
}
success = normUnPolData->ReadNextResult(&rayNumber, &errCode, &vigCode, &rayX, &rayY, &rayZ, &rayL, &rayM, &rayN, &rayl2, &raym2, &rayn2, &rayopd, &rayintensiry);
}
textfile << endl;
}
}
duration1 = (std::clock() - start) / (double)CLOCKS_PER_SEC;
cout << "Elapsed Time(Batch): " << duration1 << "\n\n";
textfile << "Elapsed Time(Batch): " << duration1 << "\n\n";
cout << "Section 2: Ray Fan data" << endl;
textfile << "Section 2: Ray Fan data" << endl;
// Set up Ray Fan analysis
IA_Ptr ray = TheSystem->Analyses->New_Analysis(AnalysisIDM_RayFan);
IAS_Ptr ray_set = ray->GetSettings();
IAS_FanPtr ray_fanset = ray_set;
ray_fanset->NumberOfRays = max_rays / 2;
ray_fanset->Field->UseAllFields();
ray_fanset->Wavelength->UseAllWavelengths();
ray->ApplyAndWaitForCompletion();
IAR_Ptr ray_results = ray->GetResults();
cout << "Number Of Series: [" << ray_results->NumberOfDataSeries << "]\n" << endl;
textfile << "Number Of Series: [" << ray_results->NumberOfDataSeries << "]\n" << endl;
for (int field = 1; field < max_num_field + 1; field++) {
// COM will return a SAFEARRAY, use SafeArrayAccessData() to retrieve data
double *xdata, *ydata;
SAFEARRAY *xData = ray_results->GetDataSeries(field * 2 - 1)->XData->Data;
SAFEARRAY *yData = ray_results->GetDataSeries(field * 2 - 1)->YData->Data;
HRESULT hrx = SafeArrayAccessData(xData, (void**)&xdata);
HRESULT hry = SafeArrayAccessData(yData, (void**)&ydata);
int XData_Length = ray_results->GetDataSeries(field * 2 - 1)->XData->Length;
int YData_Rows = ray_results->GetDataSeries(field * 2 - 1)->YData->Rows;
int YData_Cols = ray_results->GetDataSeries(field * 2 - 1)->YData->Cols;
int YData_TolLen = ray_results->GetDataSeries(field * 2 - 1)->YData->TotalLength;
if (SUCCEEDED(hrx && hry)) {
textfile << "Series [" << (field * 2 - 1) << "]: Tangential Ray Fan of Field# [" << field << "]\n";
textfile << "Each series has properties XData and YData.\n";
textfile << "XData :1D vector, the length is [" << XData_Length << "]\n";
textfile << "YData :2D matrix with [" << YData_Rows << "] rows, [" << YData_Cols << "] columns and total Length of [" << YData_TolLen << "]\n" << endl;
for (int i = 0; i < YData_Cols; i++) {
textfile << "\nData for Field [" << field << "] and Wave# [" << i + 1 << "]\n";
textfile << "Ray# Px Ey\n";
for (int j = 0; j < XData_Length; j++) {
textfile << setw(7) << i * XData_Length + j << setw(15) << left << xdata[j] << setw(15) << left << ydata[i * XData_Length + j] << "\n";
}
}
textfile << endl;
// Cleanup safearray when finished
SafeArrayUnaccessData(xData);
}
}
duration2 = (std::clock() - start) / (double)CLOCKS_PER_SEC - duration1;
cout << "Elapsed Time(RayFan): " << duration2 << "\n\n\n\n";
textfile << "Elapsed Time(RayFan): " << duration2 << "\n\n\n\n";
#if defined(_DEBUG)
// keeps console open when in debug mode
system("pause");
#endif
// Clean up
finishStandaloneApplication(TheApplication);
return 0;
}
void handleError(std::string msg)
{
throw new exception(msg.c_str());
}
void logInfo(std::string msg)
{
printf("%s", msg.c_str());
}
void finishStandaloneApplication(IZOSAPI_ApplicationPtr TheApplication)
{
// Note - TheApplication will close automatically when this application exits, so this isn't strictly necessary in most cases
if (TheApplication != nullptr)
{
TheApplication->CloseApplication();
}
}
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
return RunApplication();
}
int _tmain(int argc, _TCHAR* argv[])
{
return RunApplication();
}
Definition: ZemaxService.cs:198
The ZOSAPI namespace contains classes for initially connecting to zemax. See also ZOSAPI_Connection,...
Definition: IAS_FieldCurvatureAndDistortion.cs:5

Connect with Ansys