/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// #include "OdaCommon.h" #include "RxDynamicModule.h" #include "RxObjectImpl.h" #include "DbBaseHostAppServices.h" #include "OdStringBuf.h" #include "Gi/GiGeometry.h" #include "IesnaModule.h" // WebLight behavior support namespace Iesna { // IESNA parser is based on original sources of IESNA LM-63 Photometric Data Module. Look for original copyright: /* ************************************************************************* * * IESNA.C - IESNA LM-63 Photometric Data Module * * Version: 1.00D * * History: 95/08/15 - Created. * 95/08/29 - Version 1.00A release. * 95/09/01 - Fixed memory deallocation error in IE_Flush. * 95/09/04 - Version 1.00B release. * 96/01/28 - Added IE_CalcCU_Array, IE_CalcCoeff, * IE_CalcCU, and IE_CalcData functions. * - Added zonal multiplier equation constants. * - Added IE_Cosine array. * - Added CIE luminaire classification type * strings. * - Fixed error messages in IE_GetLine. * 96/01/30 - Version 1.00C release. * 98/03/07 - Fixed file close problem in IE_ReadFile. * 98/03/09 - Version 1.00D release. * * Compilers: Any ANSI C-compliant compiler * * Author: Ian Ashdown, P. Eng. * byHeart Consultants Limited * 620 Ballantree Road * West Vancouver, B.C. * Canada V7S 1W3 * Tel. (604) 922-6148 * Fax. (604) 987-7621 * * Copyright 1995-1998 byHeart Consultants Limited * * Permission: The following source code is copyrighted. However, it may * be freely copied, redistributed, and modified for personal * use or for royalty-free inclusion in commercial programs. * ************************************************************************* */ #define IE_MaxLabel 80 /* Maximum label line width */ #define IE_MaxLine 140 /* Maximum non-label line width */ enum iesIdent /* Format */ { IESNA_86, /* LM-63-1986 */ IESNA_91, /* LM-63-1991 */ IESNA_95 /* LM-63-1995 */ }; typedef struct IE_Data /* IESNA Standard File data */ { struct /* File information */ { OdChar *name; /* Name */ iesIdent format; } file; struct IE_Label /* Label line linked list element */ { OdChar *line; /* Label line pointer */ IE_Label *pnext; /* Next list element pointer */ } *plline; /* Label line linked list pointer */ enum lampType /* Lamp-to-luminaire geometry */ { LampVert = 1, /* Lamp vertical base up or down */ LampHorz = 2, /* Lamp horizontal */ LampTilt = 3 /* Lamp tilted */ }; struct /* Lamp data */ { int num_lamps; /* Number of lamps */ float lumens_lamp; /* Lumens per lamp */ float multiplier; /* Candela multiplying factor */ OdChar *tilt_fname; /* TILT file name pointer (optional) */ struct /* TILT data structure */ { lampType orientation; /* Lamp-to-luminaire geometry */ int num_pairs; /* # of angle-multiplying factor pairs */ float *angles; /* Angles array pointer */ float *mult_factors; /* Multiplying factors array pointer */ } tilt; } lamp; enum /* Measurement units */ { Feet = 1, /* Imperial */ Meters = 2 /* Standard Internationale */ } units; struct /* Luminous cavity dimensions */ { float width; /* Opening width */ float length; /* Opening length */ float height; /* Cavity height */ } dim; struct /* Electrical data */ { float ball_factor; /* Ballast factor */ float blp_factor; /* Ballast-lamp photometric factor */ float input_watts; /* Input watts */ } elec; struct /* Photometric data */ { enum /* Photometric goniometer type */ { Type_A = 3, /* Type A */ Type_B = 2, /* Type B */ Type_C = 1 /* Type C */ } gonio_type; int num_vert_angles; /* Number of vertical angles */ int num_horz_angles; /* Number of horizontal angles */ float *vert_angles; /* Vertical angles array */ float *horz_angles; /* Horizontal angles array */ float **pcandela; /* Candela values array pointers array */ } photo; } IE_DATA; // static OdChar IE_TextBuf[IE_MaxLabel + 1]; /* Input text buffer */ // ODA // static OdChar IE_ValueBuf[IE_MaxLine + 1]; /* Input value buffer */ // ODA struct IE_TextBuffers { OdString m_textBuf; OdString m_valueBuf; }; #define IE_TextBuf textBufs.m_textBuf #define IE_ValueBuf textBufs.m_valueBuf static bool IE_GetArray( OdStringBuf *, IE_TextBuffers &, float *, int ); static bool IE_GetList( OdStringBuf *, IE_TextBuffers &, const OdChar *, ... ); static bool IE_ReadTilt( IE_DATA *, OdStringBuf *, IE_TextBuffers & ); static OdChar *IE_CopyString( const OdChar * ); static OdChar *IE_GetLine( OdString &, int, OdStringBuf * ); static void IE_AllocErr(); bool loadData(OdLightIes* pLightIes, Iesna::IE_DATA PhotoData); void freeArrays(double*& vertAngles, OdInt32& numVertAngles, double*& horzAngles, OdInt32& numHorzAngles, double**& candelaDistribution); /* ************************************************************************* * * IE_AllocErr - Report Memory Allocation Error * * Purpose: To report an out-of-memory error. * * Setup: static void IE_AllocErr() * ************************************************************************* */ static void IE_AllocErr() { throw OdError(eOutOfMemory); } // ODA allocators static void *IE_Malloc(size_t size) { return ::odrxAlloc(size); } static void *IE_Calloc(size_t count, size_t size) { return ::odrxAlloc(count * size); } static void IE_Free(void *source) { ::odrxFree(source); } // ODA file open static OdStringBufPtr IE_Fopen(const OdChar *pFileName, OdDbBaseHostAppServices *pSvcs, OdDbBaseDatabase *pDb) { OdString fileName = (pSvcs) ? pSvcs->findFile(pFileName, pDb, OdDbBaseHostAppServices::kPhotometricWebFile) : OdString(pFileName); if (fileName.isEmpty()) return OdStringBufPtr(); OdStreamBufPtr pSB = ::odrxSystemServices()->createFile(fileName); if (pSB.isNull()) return OdStringBufPtr(); OdStringBufPtr pSB2 = RXIMPL_CONSTR(OdStringBuf); pSB2->init(pSB); return pSB2; } static OdStringBufPtr IE_FopenStream(OdStreamBufPtr pStreamBuf, OdDbBaseHostAppServices* /*pSvcs*/, OdDbBaseDatabase* /*pDb*/) { if (pStreamBuf.isNull()) return OdStringBufPtr(); OdStringBufPtr pSB2 = RXIMPL_CONSTR(OdStringBuf); pSB2->init(pStreamBuf); return pSB2; } void freeArrays(double*& vertAngles, OdInt32& numVertAngles, double*& horzAngles, OdInt32& numHorzAngles, double**& candelaDistribution) { if (vertAngles) { delete[]vertAngles; vertAngles = NULL; } numVertAngles = 0; if (horzAngles) { delete[]horzAngles; horzAngles = NULL; } if (candelaDistribution) { for (OdInt32 i = 0; i < numHorzAngles; i++) delete[]candelaDistribution[i]; delete[]candelaDistribution; candelaDistribution = NULL; } numHorzAngles = 0; } void IE_Flush( IE_DATA *pdata ); /* ************************************************************************* * * IE_ReadFile - Read IESNA-Format Photometric Data File * * Purpose: To read an IESNA-format photometric data file. * * Setup: BOOL IE_ReadFile * ( * char *fname, * IE_DATA *pdata * ) * * Where: fname is the IESNA-format data file name. * pdata is a pointer to the photometric data structure the * file is to be read into. * * Return: TRUE if the file was successfully read; otherwise FALSE. * * Note: The file is parsed in accordance with the following * Illuminating Engineering Society of North America * publications: * * IES Computer Committee: "IES Recommended Standard File * Format for Electronic Transfer of Photometric Data and * Related Information," IES Publication LM-63-1995 (to * be published). * * IES Computer Committee: "IES Recommended Standard File * Format for Electronic Transfer of Photometric Data and * Related Information," IES Publication LM-63-1991. * * IES Computer Committee: "IES Recommended Standard File * Format for Electronic Transfer of Photometric Data," * IES Publication LM-63-1986. * * The latest edition of this publication (currently * LM-63-1991) may be purchased from: * * Illuminating Engineering Society of North America * 120 Wall Street, 17th Floor * New York, NY 10005 * * Tel. (212) 248-5000 * ************************************************************************* */ bool IE_ReadStringBuf(OdStringBufPtr& piesf, bool& status, OdDbBaseHostAppServices *pArg, OdDbBaseDatabase *pDb, IE_DATA *pdata ) { OdChar *tilt_str = NULL; /* TILT line parameter pointer */ int i; /* Loop index */ IE_Data::IE_Label *pnew = NULL; /* Current label line list element ptr */ IE_Data::IE_Label *pold = NULL; /* Previous label line list element ptr */ OdStringBufPtr ptilt; /* TILT data file pointer */ IE_TextBuffers textBufs; // ODA if (status) { /* Initialize the photometric data structure */ pdata->plline = NULL; pdata->lamp.tilt_fname = NULL; pdata->lamp.tilt.angles = NULL; pdata->lamp.tilt.mult_factors = NULL; pdata->lamp.lumens_lamp = 0.; pdata->lamp.multiplier = 0.; pdata->photo.vert_angles = NULL; pdata->photo.horz_angles = NULL; pdata->photo.pcandela = NULL; } if (status) { /* Read the first line */ if (IE_GetLine(IE_TextBuf, IE_MaxLabel + 1, piesf) == NULL) status = false; } if (status) { int fixTrail = (int)odStrLen(IE_TextBuf) - 1; while (fixTrail >= 0 && isspace(IE_TextBuf[fixTrail])) // ODA IE_TextBuf.setAt(fixTrail--, 0); /* Determine file format */ if (odStrCmp(IE_TextBuf, OD_T("IESNA:LM-63-1995")) == 0) { /* File is LM-63-1995 format */ pdata->file.format = IESNA_95; } else if (odStrCmp(IE_TextBuf, OD_T("IESNA91")) == 0) { /* File is LM-63-1991 format */ pdata->file.format = IESNA_91; } else { /* File is presumably LM-63-1986 format */ pdata->file.format = IESNA_86; /* First line is a label line or "TILT=" */ piesf->getIOPtr()->rewind(); } for ( ; ; ) /* Read label lines */ { if (IE_GetLine(IE_TextBuf, IE_MaxLabel + 1, piesf) == NULL) { status = false; break; } /* Check for "TILT" keyword indicating end of label lines */ if (IE_TextBuf.left(5).compare(OD_T("TILT=")) == 0) break; /* Instantiate a new label line linked list element */ if ((pnew = (IE_Data::IE_Label *)IE_Malloc(sizeof(IE_Data::IE_Label))) == NULL) { status = false; IE_AllocErr(); /* Report memory allocation error */ break; } /* Copy buffer to label line */ if ((pnew->line = IE_CopyString(IE_TextBuf)) == NULL) { status = false; IE_AllocErr(); /* Report memory allocation error */ break; } /* Add label line element to linked list */ if (pdata->plline == NULL) /* Empty list? */ pdata->plline = pnew; else /* Add to end of list */ { if (!pold) // ODA throw OdError(eNullPtr); pold->pnext = pnew; } pold = pnew; /* Update previous list element pointer */ pnew->pnext = NULL; /* Terminate list */ } } if (status) /* Check for errors */ { /* Point to TILT line parameter */ tilt_str = IE_TextBuf.getBuffer(0) + 5; // eliminate trail spaces (ODA) OdChar *es = tilt_str; while (*es != 0) { if (isspace(*es)) { *es = 0; break; } es++; } /* Save the TILT data file name */ if ((pdata->lamp.tilt_fname = IE_CopyString(tilt_str)) == NULL) { status = false; IE_AllocErr(); /* Report memory allocation error */ } } if (status) { if (!tilt_str) // ODA throw OdError(eNullPtr); /* Check for TILT data */ if (odStrCmp(tilt_str, OD_T("NONE")) != 0) { if (odStrCmp(tilt_str, OD_T("INCLUDE")) != 0) { /* Open the TILT data file */ ptilt = IE_Fopen(tilt_str, pArg, pDb); if (ptilt.isNull()) status = false; else { /* Read the TILT data from the TILT data file */ status = IE_ReadTilt(pdata, ptilt, textBufs); ptilt.release(); /* Close the TILT data file */ } } else { /* Read the TILT data from the IESNA data file */ status = IE_ReadTilt(pdata, piesf, textBufs); } } } if (status) { /* Read in next two lines */ status = IE_GetList(piesf, textBufs, OD_T("%d%f%f%d%d%d%d%f%f%f"), &(pdata->lamp.num_lamps), &(pdata->lamp.lumens_lamp), &(pdata->lamp.multiplier), &(pdata->photo.num_vert_angles), &(pdata->photo.num_horz_angles), &(pdata->photo.gonio_type), &(pdata->units),&(pdata->dim.width), &(pdata->dim.length), &(pdata->dim.height)); } if (status) { status = IE_GetList(piesf, textBufs, OD_T("%f%f%f"), &(pdata->elec.ball_factor), &(pdata->elec.blp_factor), &(pdata->elec.input_watts)); } if (status) { /* Allocate space for the vertical angles array */ if ((pdata->photo.vert_angles = (float *) IE_Calloc(pdata->photo.num_vert_angles, sizeof(float))) == NULL) { status = false; IE_AllocErr(); /* Report memory allocation error */ } } if (status) { /* Allocate space for the horizontal angles array */ if ((pdata->photo.horz_angles = (float *) IE_Calloc(pdata->photo.num_horz_angles, sizeof(float))) == NULL) { status = false; IE_AllocErr(); /* Report memory allocation error */ } } if (status) { /* Read in vertical angles array */ status = IE_GetArray(piesf, textBufs, pdata->photo.vert_angles, pdata->photo.num_vert_angles); } if (status) { /* Read in horizontal angles array */ status = IE_GetArray(piesf, textBufs, pdata->photo.horz_angles, pdata->photo.num_horz_angles); } if (status) { /* Allocate space for the candela values array pointers */ if ((pdata->photo.pcandela = (float **) IE_Calloc(pdata->photo.num_horz_angles, sizeof(float *))) == NULL) { status = false; IE_AllocErr(); /* Report memory allocation error */ } } if (status) { /* Read in candela values arrays */ for (i = 0; i < pdata->photo.num_horz_angles; i++) { /* Allocate space for the candela values array */ if ((pdata->photo.pcandela[i] = (float *) IE_Calloc(pdata->photo.num_vert_angles, sizeof(float))) == NULL) { status = false; IE_AllocErr(); /* Report memory allocation error */ break; } /* Read in candela values */ if (!IE_GetArray(piesf, textBufs, pdata->photo.pcandela[i], pdata->photo.num_vert_angles)) { status = false; break; } } } if (!piesf.isNull()) piesf.release(); /* Close the IESNA data file */ if (!status) /* Check for errors */ IE_Flush(pdata); return status; } bool IE_ReadFile(const OdChar* fname, OdDbBaseHostAppServices* pArg, OdDbBaseDatabase* pDb, IE_DATA* pdata) { bool status = true; /* Status flag */ /* Save file name */ if ((pdata->file.name = IE_CopyString(fname)) == NULL) { status = false; IE_AllocErr(); /* Report memory allocation error */ } OdStringBufPtr piesf; /* IESNA data file pointer */ if (status) { piesf = IE_Fopen(fname, pArg, pDb); if (piesf.isNull()) { //IE_Func._fopen_error(fname); // ODA status = false; } } if (piesf.isNull()) return false; return IE_ReadStringBuf(piesf, status, pArg, pDb, pdata); } bool IE_ReadFile(OdStreamBufPtr& pStreamBuf, OdDbBaseHostAppServices* pArg, OdDbBaseDatabase* pDb, IE_DATA* pdata) { bool status = true; /* Status flag */ /* Save file name */ if ((pdata->file.name = IE_CopyString(OD_T("StreamBuf"))) == NULL) { status = false; IE_AllocErr(); /* Report memory allocation error */ } OdStringBufPtr piesf; /* IESNA data file pointer */ if (status) { pStreamBuf->rewind(); piesf = IE_FopenStream(pStreamBuf, pArg, pDb); if (piesf.isNull()) { //IE_Func._fopen_error(fname); // ODA status = false; } } if (piesf.isNull()) return false; return IE_ReadStringBuf(piesf, status, pArg, pDb, pdata); } /* ************************************************************************* * * IE_Flush- Release Photometric Data Structure * * Purpose: To release memory allocated to members of a photometric * data structure. * * Setup: void IE_Flush * ( * IE_DATA *pdata * ) * * Where: pdata is a pointer to a photometric data structure. * ************************************************************************* */ void IE_Flush( IE_DATA *pdata ) { int i; /* Loop index */ float **ppcandela; /* Candela values array pointer pointer */ struct IE_Data::IE_Label *pcurr; /* Current linked list element pointer */ struct IE_Data::IE_Label *pnext; /* Next linked list element pointer */ /* Free file name */ if (pdata->file.name != NULL) IE_Free(pdata->file.name); /* Free label line linked list */ if ((pcurr = pdata->plline) != NULL) { pdata->plline = NULL; while (pcurr != NULL) { IE_Free(pcurr->line); /* Free the label line memory */ pnext = pcurr->pnext; /* Get next element pointer */ IE_Free(pcurr); /* Free the list element memory */ pcurr = pnext; /* Make next element current */ } } /* Free TILT data file name (if allocated) */ if (pdata->lamp.tilt_fname != NULL) { IE_Free(pdata->lamp.tilt_fname); pdata->lamp.tilt_fname = NULL; } /* Free angle and multiplying factor arrays (if allocated) */ if (pdata->lamp.tilt.angles != NULL) { IE_Free(pdata->lamp.tilt.angles); pdata->lamp.tilt.angles = NULL; } if (pdata->lamp.tilt.mult_factors != NULL) { IE_Free(pdata->lamp.tilt.mult_factors); pdata->lamp.tilt.mult_factors = NULL; } /* Free vertical and horizontal angles arrays (if allocated) */ if (pdata->photo.vert_angles != NULL) { IE_Free(pdata->photo.vert_angles); pdata->photo.vert_angles = NULL; } if (pdata->photo.horz_angles != NULL) { IE_Free(pdata->photo.horz_angles); pdata->photo.horz_angles = NULL; } /* Free candela arrays (if allocated) */ if ((ppcandela = pdata->photo.pcandela) != NULL) { for (i = 0; i < pdata->photo.num_horz_angles; i++) IE_Free(ppcandela[i]); /* Free candela array pointer array */ IE_Free(pdata->photo.pcandela); pdata->photo.pcandela = NULL; } } /* ************************************************************************* * * IE_ReadTilt - Read TILT Data From File * * Purpose: To read TILT data from an IESNA-format data file into a * photometric data structure. * * Setup: static BOOL IE_ReadTilt * ( * IE_DATA *pdata, * FILE *pfile * ) * * Where: pdata is a pointer to a photometric data structure. * pfile is the IESNA-format data file pointer. * * Return: TRUE if no errors occurred; otherwise FALSE. * * Note: The file can be either part of a full IESNA-format data * file or a separate TILT data file that was specified in * the parent IESNA-format file on the "TILT=" line. * ************************************************************************* */ static bool IE_ReadTilt( IE_DATA *pdata, OdStringBuf *pfile, IE_TextBuffers &textBufs ) { bool status = true; /* Status flag */ /* Read the lamp-to-luminaire geometry line */ if (IE_GetLine(IE_ValueBuf, IE_MaxLine + 1, pfile) == NULL) status = false; if (status) { /* Get the lamp-to-luminaire geometry value */ pdata->lamp.tilt.orientation = (IE_Data::lampType) odStrToInt(IE_ValueBuf); /* Read the number of angle-multiplying factor pairs line */ if (IE_GetLine(IE_ValueBuf, IE_MaxLine + 1, pfile) == NULL) status = false; } if (status) { /* Get the number of angle-multiplying factor pairs value */ pdata->lamp.tilt.num_pairs = odStrToInt(IE_ValueBuf); if (pdata->lamp.tilt.num_pairs > 0) { /* Allocate space for the angle and multiplying factors arrays */ if ((pdata->lamp.tilt.angles = (float *) IE_Calloc(pdata->lamp.tilt.num_pairs, sizeof(float))) == NULL) { status = false; IE_AllocErr(); /* Report memory allocation error */ } } } if (status) { if ((pdata->lamp.tilt.mult_factors = (float *) IE_Calloc(pdata->lamp.tilt.num_pairs, sizeof(float))) == NULL) { status = false; IE_AllocErr(); /* Report memory allocation error */ } } if (status) { /* Read in the angle values */ if (!IE_GetArray(pfile, textBufs, pdata->lamp.tilt.angles, pdata->lamp.tilt.num_pairs)) status = false; } if (status) { /* Read in the multiplying factor values */ if (!IE_GetArray(pfile, textBufs, pdata->lamp.tilt.mult_factors, pdata->lamp.tilt.num_pairs)) status = false; } return status; } /* ************************************************************************* * * IE_GetList - Get List of Values From IESNA-Format Data File * * Purpose: To read in one or more lines from an IESNA-format data * file and convert their substrings to a list of floating * point and/or integer values. * * Setup: static BOOL IE_GetList * ( * FILE *pfile, * char *format, * ... * ) * * Where: pfile is a pointer to an IESNA-format data file. * format is a format string containing the following * specifiers: * * %d - read in integer value * %f - read in floating point value * * A pointer to a variable of the appropriate type * must follow the format parameter in the same order * (similar to "scanf"). * * Return: TRUE if no errors occurred; otherwise FALSE. * ************************************************************************* */ static bool IE_GetList( OdStringBuf *pfile, IE_TextBuffers &textBufs, const OdChar *format, ... ) { OdChar c; /* Scratch variable */ OdChar type; /* Format specifier */ OdChar *pbuf; /* Input buffer pointer */ const OdChar *pfmt = format; /* Format string pointer */ int itemp; /* Temporary integer variable */ float ftemp; /* Temporary floating point variable */ va_list pvla; /* Variable list argument pointer */ va_start(pvla, format); /* Set up for optional arguments */ /* Read in the first line */ if ((pbuf = IE_GetLine(IE_ValueBuf, IE_MaxLine + 1, pfile)) == NULL) return false; for ( ; ; ) /* Skip over leading delimiters */ { c = *pbuf; if (c == '\0') /* End of current line? */ return false; else if (isspace(c) != 0) pbuf++; else break; } for ( ; ; ) { if (*pfmt != '%') /* Check format specifier delimiter */ return false; /* Get and validate format specifier */ switch (type = *(pfmt + 1)) { case 'd': case 'D': odSScanf(pbuf, OD_T("%d"), &itemp); /* Get integer value */ *(va_arg(pvla, int *)) = itemp; for ( ; ; ) /* Advance buffer pointer past the substring */ { c = *pbuf; if ((isdigit(c) != 0) || c == '-') pbuf++; else break; } break; case 'f': case 'F': ftemp = static_cast(odStrToD(pbuf)); /* Get float value */ *(va_arg(pvla, float *)) = ftemp; for ( ; ; ) /* Advance buffer pointer past the substring */ { c = *pbuf; if ((isdigit(c) != 0) || c == '.' || c == '-') pbuf++; else break; } break; default: return false; } /* Point to next format specifier */ pfmt++; /* Skip over format specifier delimiter */ if (*pfmt == '\0') /* End of format string ? */ return false; pfmt++; /* Skip over format specifier parameter */ if (*pfmt == '\0') /* End of format string ? */ break; for ( ; ; ) /* Skip over delimiters */ { c = *pbuf; if (c == '\0') /* End of current line? */ { /* Get next line */ if ((pbuf = IE_GetLine(IE_ValueBuf, IE_MaxLine + 1, pfile)) == NULL) return false; } else if ((isspace(c) != 0) || c == ',') pbuf++; else break; } } return true; } /* ************************************************************************* * * IE_GetArray - Get Array Elements From IESNA-Format Data File * * Purpose: To read in one or more lines from an IESNA-format data * file and convert their substrings to an array of floating * point numbers. * * Setup: static BOOL IE_GetArray * ( * FILE *pfile, * float *array, * int size * ) * * Where: pfile is a pointer to an IESNA-format data file. * array is a pointer to an array of floats. * size is the number of elements in the array. * * Return: TRUE if no errors occurred; otherwise FALSE. * ************************************************************************* */ static bool IE_GetArray ( OdStringBuf *pfile, IE_TextBuffers &textBufs, float *array, int size ) { int i = 0; /* Loop index */ OdChar c; /* Scratch variable */ OdChar *pbuf; /* Input buffer pointer */ float ftemp; /* Temporary floating point variable */ /* Read in the first line */ if ((pbuf = IE_GetLine(IE_ValueBuf, IE_MaxLine + 1, pfile)) == NULL) return false; for ( ; ; ) /* Skip over leading delimiters */ { c = *pbuf; if (c == '\0') /* End of current line? */ return false; else if ((isspace(c) != 0)) pbuf++; else break; } for ( ; ; ) /* Parse the array elements */ { /* Convert the current substring to its floating point value */ //odSScanf(pbuf, OD_T("%f"), &ftemp); ftemp = static_cast(odStrToD(pbuf)); array[i++] = ftemp; if (i == size) /* All substrings converted ? */ break; for ( ; ; ) /* Advance buffer pointer past the substring */ { c = *pbuf; if ((isdigit(c) != 0) || c == '.' || c == '-') pbuf++; else break; } for ( ; ; ) /* Skip over delimiters */ { if (c == '\0') /* End of current line? */ { /* Get next line */ if ((pbuf = IE_GetLine(IE_ValueBuf, IE_MaxLine + 1, pfile)) == NULL) return false; } else if ((isspace(c) != 0) || c == ',') pbuf++; else break; c = *pbuf; /* Get next delimiter */ } } return true; } /* ************************************************************************* * * IE_GetLine - Get Line From File * * Purpose: To read in a line from a file and remove any trailing * newline character. * * Setup: static char *IE_GetLine * ( * char *pbuf, * int size, * FILE *pfile * ) * * Where: pbuf is a pointer to where the line is to be returned. * size is the maximum number of characters to be returned * in the buffer, including the '\0' terminator. * pfile is the file pointer of the file to be read from. * * Return: A pointer to pbuf if successful; otherwise NULL. * * Result: Up to size - 1 characters are read into the buffer, * which is then null-terminated. Any trailing '\n' * character is stripped off. * * Note: IES LM-63 specifies that lines are terminated with a * pair. The "fgets" function as implemented for * MS-DOS C compilers typically converts these characters * into a single (i.e., newline) character. Thus, * this function should work for both MS-DOS and UNIX * environments. * ************************************************************************* */ static OdChar *IE_GetLine( OdString &pbuf, int /*size*/, OdStringBuf *pfile ) { /* Read in the line */ pbuf = pfile->getString(); return pbuf.getBuffer(0); #if 0 // ODA if (NL.getLength() < size) { for (OdUInt32 nS = 0; nS <= (OdUInt32)NL.getLength(); nS++) pbuf[nS] = NL.c_str()[nS]; return pbuf; } else { /* Report error */ //IE_Func._ferror(pfile); // ODA return NULL; } #endif } /* ************************************************************************* * * IE_CopyString - Copy String * * Purpose: To copy a string. * * Setup: static char *IE_CopyString * ( * char *pstr * ) * * Where: pstr is a pointer to the string that is to be copied. * * Return: A pointer to the copied string if successful; otherwise * NULL. * ************************************************************************* */ static OdChar *IE_CopyString( const OdChar *pstr ) { OdChar *pdup; /* Duplicated string pointer */ if ((pdup = (OdChar *) IE_Malloc((odStrLen(pstr) + 1) * sizeof(OdChar))) != NULL) { ::memcpy(pdup, pstr, (odStrLen(pstr) + 1) * sizeof(OdChar)); } return pdup; } // bool loadData(OdLightIes* pLightIes, Iesna::IE_DATA PhotoData) { double* vertAngles; OdInt32 numVertAngles; double* horzAngles; OdInt32 numHorzAngles; double** candelaDistribution; double lumensLamp = 0.; double multiplier = 0.; OdGiWebLightTraits::WebFileType fileType = OdGiWebLightTraits::kTypeA; /* Calculate additional photometric data*/ numVertAngles = PhotoData.photo.num_vert_angles; numHorzAngles = PhotoData.photo.num_horz_angles; vertAngles = new double[numVertAngles]; horzAngles = new double[numHorzAngles]; candelaDistribution = new double* [numHorzAngles]; lumensLamp = PhotoData.lamp.lumens_lamp; multiplier = PhotoData.lamp.multiplier; OdInt32 i; for (i = 0; i < numHorzAngles; i++) { candelaDistribution[i] = new double[numVertAngles]; horzAngles[i] = PhotoData.photo.horz_angles[i]; } for (i = 0; i < numVertAngles; i++) { vertAngles[i] = PhotoData.photo.vert_angles[i]; } for (i = 0; i < numHorzAngles; i++) { for (OdInt32 j = 0; j < numVertAngles; j++) { candelaDistribution[i][j] = PhotoData.photo.pcandela[i][j]; } } fileType = (OdGiWebLightTraits::WebFileType)PhotoData.photo.gonio_type; Iesna::IE_Flush(&PhotoData); /* Release photometric data memory */ pLightIes->buildInternalRepresentation(vertAngles, numVertAngles, horzAngles, numHorzAngles, candelaDistribution, lumensLamp, multiplier, fileType); Iesna::freeArrays(vertAngles, numVertAngles, horzAngles, numHorzAngles, candelaDistribution); return true; } } // namespace Iesna // OdLightIes OdLightIes::OdLightIes() : _vertAngles(NULL) , _numVertAngles(0) , _horzAngles(NULL) , _numHorzAngles(0) , _candelaDistribution(NULL) , _maxCandela(0.0) , _fileType((OdGiWebLightTraits::WebFileType)0) , _symmetryType(OdGiWebLightTraits::kNoSymmetry) , _horzAng90To270(false) , _lumensLamp(0.) , _multiplier(0.) { } bool OdLightIes::load(const OdChar *pFileName, OdDbBaseHostAppServices *pSvcs, OdDbBaseDatabase *pDb) { bool status; Iesna::IE_DATA PhotoData; /* Read IESNA photometric data file */ status = Iesna::IE_ReadFile(pFileName, pSvcs, pDb, &PhotoData); if (status) return Iesna::loadData(this, PhotoData); return false; } bool OdLightIes::load(OdStreamBufPtr& pStreamBuf, OdDbBaseHostAppServices* pSvcs, OdDbBaseDatabase* pDb) { bool status; Iesna::IE_DATA PhotoData; /* Read IESNA photometric data file */ status = Iesna::IE_ReadFile(pStreamBuf, pSvcs, pDb, &PhotoData); if (status) return Iesna::loadData(this, PhotoData); return false; } void OdLightIes::buildInternalRepresentation(double *vertAngles, OdInt32 numVertAngles, double *horzAngles, OdInt32 numHorzAngles, double **candelaDistribution, double lumensLamp, double multiplier, OdGiWebLightTraits::WebFileType fileType) { ODA_ASSERT(numHorzAngles > 0 && numVertAngles > 0); _lumensLamp = lumensLamp; _multiplier = multiplier; _fileType = fileType; double angleHorzMax = horzAngles[0]; double angleHorzMin = horzAngles[0]; OdInt32 i, j; // Free previous data Iesna::freeArrays(_vertAngles, _numVertAngles, _horzAngles, _numHorzAngles, _candelaDistribution); // Classify light type for (i = 1; i < numHorzAngles; i++) { if (angleHorzMin > horzAngles[i]) angleHorzMin = horzAngles[i]; if (angleHorzMax < horzAngles[i]) angleHorzMax = horzAngles[i]; } _horzAng90To270 = false; if (numHorzAngles == 1) _symmetryType = OdGiWebLightTraits::kAxialSymmetry; else { if ((angleHorzMin <= 0.01) && (angleHorzMax > 180.01)) _symmetryType = OdGiWebLightTraits::kNoSymmetry; else { if (angleHorzMin <= 0.01) { if (angleHorzMax <= 90.01) { _symmetryType = OdGiWebLightTraits::kDoubleSymmetry; } else if (angleHorzMax <= 180.01) { _symmetryType = OdGiWebLightTraits::kSingleSymmetry; } else { ODA_ASSERT(false); } } else if (angleHorzMin <= 90.01) { if (angleHorzMax <= 270.01) { _symmetryType = OdGiWebLightTraits::kSingleSymmetry; _horzAng90To270 = true; } else { ODA_ASSERT(false); } } } } _maxCandela = 0.0; // Prepare vertical angles _numVertAngles = numVertAngles; _vertAngles = new double[_numVertAngles]; for (i = 0; i < _numVertAngles; i++) _vertAngles[i] = vertAngles[i]; // Run special processing for each type of symmetry switch (_symmetryType) { case OdGiWebLightTraits::kNoSymmetry: { // Haven't symmetry since specified for all horizontal angles _numHorzAngles = numHorzAngles; _horzAngles = new double[_numHorzAngles]; _candelaDistribution = new double*[_numHorzAngles]; for (i = 0; i < _numHorzAngles; i++) { _horzAngles[i] = horzAngles[i]; _candelaDistribution[i] = new double[_numVertAngles]; for (j = 0; j < _numVertAngles; j++) { if (candelaDistribution[i][j] > _maxCandela) _maxCandela = candelaDistribution[i][j]; _candelaDistribution[i][j] = candelaDistribution[i][j]; } } for (i = 0; i < _numHorzAngles; i++) { for (j = 0; j < _numVertAngles; j++) _candelaDistribution[i][j] /= _maxCandela; } } break; case OdGiWebLightTraits::kSingleSymmetry: // Symmetric about the XZ plane if (!_horzAng90To270) { // (0 -> 180) OdInt32 zeroPass = (horzAngles[0] == 0.0) ? -1 : 0; OdInt32 midPass = (horzAngles[numHorzAngles - 1] == 180.0) ? -1 : 0; _numHorzAngles = numHorzAngles * 2 + zeroPass + midPass; _horzAngles = new double[_numHorzAngles]; _candelaDistribution = new double*[_numHorzAngles]; for (i = 0; i < numHorzAngles; i++) { _horzAngles[i] = horzAngles[i]; _candelaDistribution[i] = new double[_numVertAngles]; for (j = 0; j < _numVertAngles; j++) { if (candelaDistribution[i][j] > _maxCandela) _maxCandela = candelaDistribution[i][j]; _candelaDistribution[i][j] = candelaDistribution[i][j]; } } for (i = 0; i < numHorzAngles; i++) { for (j = 0; j < _numVertAngles; j++) _candelaDistribution[i][j] /= _maxCandela; } // (0 -> 180) -> (180 -> 360) for (i = numHorzAngles; i < _numHorzAngles; i++) { OdInt32 k = numHorzAngles * 2 - 1 + midPass - i; _horzAngles[i] = 360.0 - _horzAngles[k]; _candelaDistribution[i] = new double[_numVertAngles]; for (j = 0; j < _numVertAngles; j++) _candelaDistribution[i][j] = _candelaDistribution[k][j]; } } else { // (90 -> 270) OdInt32 zeroPass = (horzAngles[0] == 90.0) ? -1 : 0; OdInt32 midPass = (horzAngles[numHorzAngles - 1] == 270.0) ? -1 : 0; _numHorzAngles = numHorzAngles * 2 + zeroPass + midPass; _horzAngles = new double[_numHorzAngles]; _candelaDistribution = new double*[_numHorzAngles]; OdInt32 till180 = 0; for (i = 0; i < numHorzAngles; i++, till180++) { if (horzAngles[i] > 180.01) break; } //OdInt32 till270 = numHorzAngles - till180; for (i = 0; i < numHorzAngles; i++) { OdInt32 k = till180 + zeroPass + i; _horzAngles[k] = horzAngles[i]; _candelaDistribution[k] = new double[_numVertAngles]; for (j = 0; j < _numVertAngles; j++) { if (candelaDistribution[i][j] > _maxCandela) _maxCandela = candelaDistribution[i][j]; _candelaDistribution[k][j] = candelaDistribution[i][j]; } } for (i = 0; i < numHorzAngles; i++) { OdInt32 k = till180 + zeroPass + i; for (j = 0; j < _numVertAngles; j++) _candelaDistribution[k][j] /= _maxCandela; } // (90 -> 180) -> (0 -> 90) for (i = 0; i < till180 + zeroPass; i++) { OdInt32 k = till180 * 2 - 1 + zeroPass - i; _horzAngles[i] = 180.0 - _horzAngles[k]; _candelaDistribution[i] = new double[_numVertAngles]; for (j = 0; j < _numVertAngles; j++) _candelaDistribution[i][j] = _candelaDistribution[k][j]; } // (180 -> 270) -> (270 -> 360) for (i = till180 + zeroPass + numHorzAngles; i < _numHorzAngles; i++) { OdInt32 k = till180 + zeroPass + numHorzAngles - 1 + midPass - (i - (till180 + zeroPass + numHorzAngles)); _horzAngles[i] = (360.0 - _horzAngles[k]) + 180.0; _candelaDistribution[i] = new double[_numVertAngles]; for (j = 0; j < _numVertAngles; j++) _candelaDistribution[i][j] = _candelaDistribution[k][j]; } } break; case OdGiWebLightTraits::kDoubleSymmetry: { // Symmetric about the XZ and YZ planes // (0 -> 90) OdInt32 zeroPass = (horzAngles[0] == 0.0) ? -1 : 0; OdInt32 midPass = (horzAngles[numHorzAngles - 1] == 90.0) ? -1 : 0; _numHorzAngles = numHorzAngles * 4 + zeroPass * 2 + midPass * 2; _horzAngles = new double[_numHorzAngles]; _candelaDistribution = new double*[_numHorzAngles]; for (i = 0; i < numHorzAngles; i++) { _horzAngles[i] = horzAngles[i]; _candelaDistribution[i] = new double[_numVertAngles]; for (j = 0; j < _numVertAngles; j++) { if (candelaDistribution[i][j] > _maxCandela) _maxCandela = candelaDistribution[i][j]; _candelaDistribution[i][j] = candelaDistribution[i][j]; } } for (i = 0; i < numHorzAngles; i++) { for (j = 0; j < _numVertAngles; j++) _candelaDistribution[i][j] /= _maxCandela; } // (0 -> 90) -> (90 -> 180) for (i = numHorzAngles; i < numHorzAngles * 2 + midPass; i++) { OdInt32 k = numHorzAngles * 2 - 1 + midPass - i; _horzAngles[i] = 180.0 - _horzAngles[k]; _candelaDistribution[i] = new double[_numVertAngles]; for (j = 0; j < _numVertAngles; j++) _candelaDistribution[i][j] = _candelaDistribution[k][j]; } // (0 -> 180) -> (180 -> 360) for (i = numHorzAngles * 2 + midPass; i < _numHorzAngles; i++) { OdInt32 k = (numHorzAngles * 2 + midPass) * 2 - 1 + zeroPass - i; _horzAngles[i] = 360.0 - _horzAngles[k]; _candelaDistribution[i] = new double[_numVertAngles]; for (j = 0; j < _numVertAngles; j++) _candelaDistribution[i][j] = _candelaDistribution[k][j]; } } break; case OdGiWebLightTraits::kAxialSymmetry: { const double angleHorzInc = 22.5; // Standard architectural increment _numHorzAngles = (OdInt32)(360.0 / angleHorzInc); _horzAngles = new double[_numHorzAngles]; _candelaDistribution = new double*[_numHorzAngles]; for (i = 0; i < _numHorzAngles; i++) { _candelaDistribution[i] = new double[_numVertAngles]; _horzAngles[i] = angleHorzInc * i; if (i == 0) { for (j = 0; j < _numVertAngles; j++) { if (candelaDistribution[0][j] > _maxCandela) _maxCandela = candelaDistribution[0][j]; _candelaDistribution[i][j] = candelaDistribution[0][j]; } for (j = 0; j < _numVertAngles; j++) _candelaDistribution[i][j] /= _maxCandela; } else { for (j = 0; j < _numVertAngles; j++) _candelaDistribution[i][j] = _candelaDistribution[0][j]; } } } break; default: ODA_FAIL(); } } OdLightIes::~OdLightIes() { Iesna::freeArrays(_vertAngles, _numVertAngles, _horzAngles, _numHorzAngles, _candelaDistribution); } void OdLightIes::render(OdGiGeometry *pWD, double scale, OdInt32 interpolation) const { if (!interpolation) drawCandelaDistribution(pWD, scale); else drawCandelaDistribution_ipl(pWD, scale, (int)interpolation); } void OdLightIes::drawCandelaDistribution(OdGiGeometry *pWD, double scale) const { OdGePoint3d p0, p0Prev; OdGePoint3d p1, p1Prev; OdGePoint3d pPoly[2]; for (OdInt32 i = 0; i < _numHorzAngles - 1; i++) { for (OdInt32 j = 0; j < _numVertAngles; j++) { p0[0] = scale * _candelaDistribution[i][j] * cos(OdaToRadian(_horzAngles[i])) * sin(OdaToRadian(_vertAngles[j])); p0[1] = scale * _candelaDistribution[i][j] * sin(OdaToRadian(_horzAngles[i])) * sin(OdaToRadian(_vertAngles[j])); p0[2] = scale * _candelaDistribution[i][j] * cos(OdaToRadian(_vertAngles[j])); p1[0] = scale * _candelaDistribution[i + 1][j] * cos(OdaToRadian(_horzAngles[i + 1])) * sin(OdaToRadian(_vertAngles[j])); p1[1] = scale * _candelaDistribution[i + 1][j] * sin(OdaToRadian(_horzAngles[i + 1])) * sin(OdaToRadian(_vertAngles[j])); p1[2] = scale * _candelaDistribution[i + 1][j] * cos(OdaToRadian(_vertAngles[j])); if ((i != 0) || (i != j)) { pPoly[0] = p0Prev; pPoly[1] = p0; pWD->polyline(2, pPoly); pPoly[0] = p1Prev; pPoly[1] = p1; pWD->polyline(2, pPoly); } pPoly[0] = p0; pPoly[1] = p1; pWD->polyline(2, pPoly); p0Prev = pPoly[0]; p1Prev = pPoly[1]; } } for (OdInt32 j = 0; j < _numVertAngles; j++) { p0[0] = scale * _candelaDistribution[_numHorzAngles - 1][j] * cos(OdaToRadian(_horzAngles[_numHorzAngles - 1])) * sin(OdaToRadian(_vertAngles[j])); p0[1] = scale * _candelaDistribution[_numHorzAngles - 1][j] * sin(OdaToRadian(_horzAngles[_numHorzAngles - 1])) * sin(OdaToRadian(_vertAngles[j])); p0[2] = scale * _candelaDistribution[_numHorzAngles - 1][j] * cos(OdaToRadian(_vertAngles[j])); p1[0] = scale * _candelaDistribution[0][j] * cos(OdaToRadian(_horzAngles[0])) * sin(OdaToRadian(_vertAngles[j])); p1[1] = scale * _candelaDistribution[0][j] * sin(OdaToRadian(_horzAngles[0])) * sin(OdaToRadian(_vertAngles[j])); p1[2] = scale * _candelaDistribution[0][j] * cos(OdaToRadian(_vertAngles[j])); if (j != 0) { pPoly[0] = p0Prev; pPoly[1] = p0; pWD->polyline(2, pPoly); pPoly[0] = p1Prev; pPoly[1] = p1; pWD->polyline(2, pPoly); } pPoly[0] = p0; pPoly[1] = p1; pWD->polyline(2, pPoly); p0Prev = pPoly[0]; p1Prev = pPoly[1]; } } class OdBSplineCurveHelper { const double *m_pParams; const OdGePoint3d *m_pPoints; int m_nPoints; class BSplineBasis : public OdGeMatrix3d { public: inline void set0(int i, double val) { entry[i / 4][i % 4] = val; } inline double get0(int i) { return entry[i / 4][i % 4]; } BSplineBasis() { const short b[16] = { -1, 3, -3, 1, 3, -6, 3, 0, -3, 0, 3, 0, 1, 4, 1, 0 }; for (int i = 0; i < 16; i++) set0(i, (double)b[i]); } void transform(double b1, double b2, double b3, double b4, double &r1, double &r2, double &r3, double &r4) { double vx, vy, vz, vt; vx = b1; vy = b2; vz = b3; vt = b4; r1 = vx * get0(0) + vy * get0(1) + vz * get0(2) + vt * get0(3); r2 = vx * get0(4) + vy * get0(5) + vz * get0(6) + vt * get0(7); r3 = vx * get0(8) + vy * get0(9) + vz * get0(10) + vt * get0(11); r4 = vx * get0(12) + vy * get0(13) + vz * get0(14) + vt * get0(15); } }; int deriv(int i) const { if (i < 0) return 0; if (i >= m_nPoints) return m_nPoints - 1; return i; } public: OdBSplineCurveHelper(const double *pParams, const OdGePoint3d *pPoints, OdUInt32 nPoints) : m_pParams(pParams), m_pPoints(pPoints), m_nPoints((int)nPoints) { } OdGePoint3d compute(double k) const { OdGePoint3d r; if (k < *m_pParams) k = *m_pParams; if (k > m_pParams[m_nPoints - 1]) k = m_pParams[m_nPoints - 1]; // Until GeCubicSplineCurve2d doesn't implemented int l1 = -1, l2 = -1, l3 = -1, l4 = -1; while (l4 < m_nPoints - 1 && m_pParams[deriv(l2)] <= k) { l4 = l3; l3 = l2; l2 = l1; l1++; } BSplineBasis b; double u; if (m_pParams[deriv(l2)] == m_pParams[deriv(l3)]) u = 0.0; else u = (k - m_pParams[deriv(l3)]) / (m_pParams[deriv(l2)] - m_pParams[deriv(l3)]); double px[4] = { m_pPoints[deriv(l4)].x, m_pPoints[deriv(l3)].x, m_pPoints[deriv(l2)].x, m_pPoints[deriv(l1)].x }, rx[4]; double py[4] = { m_pPoints[deriv(l4)].y, m_pPoints[deriv(l3)].y, m_pPoints[deriv(l2)].y, m_pPoints[deriv(l1)].y }, ry[4]; double pz[4] = { m_pPoints[deriv(l4)].z, m_pPoints[deriv(l3)].z, m_pPoints[deriv(l2)].z, m_pPoints[deriv(l1)].z }, rz[4]; double u_vector[4] = { u * u * u, u * u, u, 1.0 }; b.transform(px[0], px[1], px[2], px[3], rx[0], rx[1], rx[2], rx[3]); b.transform(py[0], py[1], py[2], py[3], ry[0], ry[1], ry[2], ry[3]); b.transform(pz[0], pz[1], pz[2], pz[3], rz[0], rz[1], rz[2], rz[3]); for (int i = 0; i < 4; i++) { rx[i] *= 1. / 6; ry[i] *= 1. / 6; rz[i] *= 1. / 6; } r.x = u_vector[0] * rx[0] + u_vector[1] * rx[1] + u_vector[2] * rx[2] + u_vector[3] * rx[3]; r.y = u_vector[0] * ry[0] + u_vector[1] * ry[1] + u_vector[2] * ry[2] + u_vector[3] * ry[3]; r.z = u_vector[0] * rz[0] + u_vector[1] * rz[1] + u_vector[2] * rz[2] + u_vector[3] * rz[3]; return r; } }; void OdLightIes::drawCandelaDistribution_ipl(OdGiGeometry *pWD, double scale, int level) const { #if 0 OdGePoint3d p0, p0Prev; OdGePoint3d p1, p1Prev; OdGePoint3d pPoly[2]; OdInt32 numHorzAngles = (OdInt32)level, numVertAngles = (OdInt32)level; double thetaMin = _horzAngles[0], thetaMax = _horzAngles[_numHorzAngles - 1]; double phiMin = _vertAngles[0], phiMax = _vertAngles[_numVertAngles - 1]; double deltaTheta = (thetaMax - thetaMin) / numHorzAngles; double deltaPhi = (phiMax - phiMin) / numVertAngles; for (OdInt32 i = 0; i < numHorzAngles - 1; i++) { for (OdInt32 j = 0; j < numVertAngles; j++) { double theta = thetaMin + deltaTheta * i; double phi = phiMin + deltaPhi * j; double cand = getValue_ipl(phi, theta); p0[0] = scale * cand * cos(OdaToRadian(theta)) * sin(OdaToRadian(phi)); p0[1] = scale * cand * sin(OdaToRadian(theta)) * sin(OdaToRadian(phi)); p0[2] = scale * cand * cos(OdaToRadian(phi)); theta = thetaMin + deltaTheta * (i + 1); cand = getValue_ipl(phi, theta); p1[0] = scale * cand * cos(OdaToRadian(theta)) * sin(OdaToRadian(phi)); p1[1] = scale * cand * sin(OdaToRadian(theta)) * sin(OdaToRadian(phi)); p1[2] = scale * cand * cos(OdaToRadian(phi)); if ((i != 0) || (i != j)) { pPoly[0] = p0Prev; pPoly[1] = p0; pWD->polyline(2, pPoly); pPoly[0] = p1Prev; pPoly[1] = p1; pWD->polyline(2, pPoly); } pPoly[0] = p0; pPoly[1] = p1; pWD->polyline(2, pPoly); p0Prev = pPoly[0]; p1Prev = pPoly[1]; } } for (OdInt32 j = 0; j < numVertAngles; j++) { double theta = thetaMin + deltaTheta * (numHorzAngles - 1); double phi = phiMin + deltaPhi * j; double cand = getValue_ipl(phi, theta); p0[0] = scale * cand * cos(OdaToRadian(theta)) * sin(OdaToRadian(phi)); p0[1] = scale * cand * sin(OdaToRadian(theta)) * sin(OdaToRadian(phi)); p0[2] = scale * cand * cos(OdaToRadian(phi)); theta = thetaMin; cand = getValue_ipl(phi, theta); p1[0] = scale * cand * cos(OdaToRadian(theta)) * sin(OdaToRadian(phi)); p1[1] = scale * cand * sin(OdaToRadian(theta)) * sin(OdaToRadian(phi)); p1[2] = scale * cand * cos(OdaToRadian(phi)); if (j != 0) { pPoly[0] = p0Prev; pPoly[1] = p0; pWD->polyline(2, pPoly); pPoly[0] = p1Prev; pPoly[1] = p1; pWD->polyline(2, pPoly); } pPoly[0] = p0; pPoly[1] = p1; pWD->polyline(2, pPoly); p0Prev = pPoly[0]; p1Prev = pPoly[1]; } #else OdInt32 i, j; // 1) Prepare initial horizontals OdGePoint3d **pInHorz = new OdGePoint3d*[_numHorzAngles]; double *pInHorzParams = new double[_numVertAngles + 3]; for (i = 0; i < _numHorzAngles; i++) { pInHorz[i] = new OdGePoint3d[_numVertAngles + 3]; for (j = 0; j < _numVertAngles; j++) { pInHorz[i][j + 1].x = scale * _candelaDistribution[i][j] * cos(OdaToRadian(_horzAngles[i])) * sin(OdaToRadian(_vertAngles[j])); pInHorz[i][j + 1].y = scale * _candelaDistribution[i][j] * sin(OdaToRadian(_horzAngles[i])) * sin(OdaToRadian(_vertAngles[j])); pInHorz[i][j + 1].z = scale * _candelaDistribution[i][j] * cos(OdaToRadian(_vertAngles[j])); pInHorzParams[j + 1] = _vertAngles[j]; } pInHorz[i][0] = OdGePoint3d::kOrigin - pInHorz[i][2].asVector(); pInHorzParams[0] = pInHorzParams[1] - (pInHorzParams[2] - pInHorzParams[1]); pInHorz[i][_numVertAngles + 1] = OdGePoint3d::kOrigin - pInHorz[i][_numVertAngles].asVector(); pInHorzParams[_numVertAngles + 1] = pInHorzParams[_numVertAngles] + (pInHorzParams[_numVertAngles] - pInHorzParams[_numVertAngles - 1]); pInHorz[i][_numVertAngles + 2] = OdGePoint3d::kOrigin - pInHorz[i][_numVertAngles - 1].asVector(); pInHorzParams[_numVertAngles + 2] = pInHorzParams[_numVertAngles - 1] + (pInHorzParams[_numVertAngles - 1] - pInHorzParams[_numVertAngles - 2]); } // 2) Prepare verticals from smooth horizontals OdGePoint3d **pOutVert = new OdGePoint3d*[level]; double *pOutVertParams = new double[_numHorzAngles + 3]; double vertDelta = (_vertAngles[_numVertAngles - 1] - _vertAngles[0]) / (level - 1); for (i = 0; i < level; i++) { double vertAngle = _vertAngles[0] + vertDelta * i; pOutVert[i] = new OdGePoint3d[_numHorzAngles + 3]; for (j = 0; j < _numHorzAngles; j++) { OdBSplineCurveHelper horS(pInHorzParams/* + 1*/, pInHorz[j]/* + 1*/, (OdUInt32)_numVertAngles/* + 1*/ + 3); pOutVert[i][j + 1] = horS.compute(vertAngle); pOutVertParams[j + 1] = _horzAngles[j]; } pOutVert[i][0] = pOutVert[i][_numHorzAngles]; pOutVertParams[0] = _horzAngles[_numHorzAngles - 1] - 360.0; pOutVert[i][_numHorzAngles + 1] = pOutVert[i][1]; pOutVertParams[_numHorzAngles + 1] = _horzAngles[0] + 360.0; pOutVert[i][_numHorzAngles + 2] = pOutVert[i][2]; pOutVertParams[_numHorzAngles + 2] = _horzAngles[1] + 360.0; } // 3) Render verticals OdGePoint3d *pRendVertical = new OdGePoint3d[level + 3]; vertDelta = /*(_vertAngles[_numVertAngles - 1] - _vertAngles[0])*/360. / (level + 2); for (i = 0; i < level; i++) { OdBSplineCurveHelper verS(pOutVertParams, pOutVert[i], (OdUInt32)_numHorzAngles + 3); for (j = 0; j < level + 3; j++) pRendVertical[j] = verS.compute(vertDelta * j); pWD->polyline(level + 3, pRendVertical); } delete []pRendVertical; // 4) Render horizontals //OdGePoint3d *pRendHorz = new OdGePoint3d[level + 3]; OdGePoint3d *pOutHorz = new OdGePoint3d[level]; //double *pOutHorzParams = new double[level]; //vertDelta = (_vertAngles[_numVertAngles - 1] - _vertAngles[0]) / (level - 1); double hDelta = 360.0 / level; //double horzDelta = (_vertAngles[_numVertAngles - 1] - _vertAngles[0]) / (level + 2); for (i = 0; i < level; i++) { double hAngle = hDelta * i; for (j = 0; j < level; j++) { //double vertAngle = _vertAngles[0] + vertDelta * j; OdBSplineCurveHelper verS(pOutVertParams, pOutVert[j], (OdUInt32)_numHorzAngles + 3); pOutHorz[j] = verS.compute(/*vertAngle*/hAngle); //pOutHorzParams[j] = vertAngle; } //OdBSplineCurveHelper horS(pOutHorzParams, pOutHorz, (OdUInt32)level); //for (j = 0; j < level + 3; j++) // pRendHorz[j] = horS.compute(_vertAngles[0] + horzDelta * j); //pRendHorz[0] = pOutHorz[0]; //pWD->polyline(level + 3, pRendHorz); pWD->polyline(level, pOutHorz); } //delete []pOutHorzParams; delete []pOutHorz; //delete []pRendHorz; // 5) Free verticals delete []pOutVertParams; for (i = 0; i < level; i++) delete []pOutVert[i]; delete []pOutVert; // 6) Free horizontals for (i = 0; i < _numHorzAngles; i++) delete []pInHorz[i]; delete []pInHorz; delete []pInHorzParams; #endif } double *OdLightIes::vertAngles() const { return _vertAngles; } OdInt32 OdLightIes::numVertAngles() const { return _numVertAngles; } double *OdLightIes::horzAngles() const { return _horzAngles; } OdInt32 OdLightIes::numHorzAngles() const { return _numHorzAngles; } double **OdLightIes::candelaDistribution() const { return _candelaDistribution; } double OdLightIes::maxCandela() const { return _maxCandela; } OdGiWebLightTraits::WebFileType OdLightIes::fileType() const { return _fileType; } OdGiWebLightTraits::WebSymmetry OdLightIes::fileSymmetry() const { return _symmetryType; } bool OdLightIes::isHorzAng90To270() const { return _horzAng90To270; } double OdLightIes::getLumensLamp() const { return _lumensLamp; } double OdLightIes::getMultiplier() const { return _multiplier; } static OdInt32 searchGreater(double *angles, OdInt32 from, OdInt32 to, double val) { if (to - from == 1) { if (angles[from] > val) return from; else return to; } OdInt32 mediane = from + (to - from) / 2; if (angles[mediane] > val) return searchGreater(angles, from, mediane, val); else return searchGreater(angles, mediane, to, val); } double OdLightIes::getValue(double theta, double phi) const { double val = 0.0; double thetamin, thetamax; OdInt32 idthetamin, idthetamax; double phimin, phimax; OdInt32 idphimin, idphimax; double candela_tm_pm, candela_tm_pM, candela_tM_pM, candela_tM_pm; /* search for first angle greater than theta (if exist) */ idthetamax = searchGreater(_vertAngles, 0, _numVertAngles, theta); if ((idthetamax <= 0) || (idthetamax >= _numVertAngles)) { return 0.0; } /* search for first angle greater than theta (if exist) */ idphimax = searchGreater(_horzAngles, 0, _numHorzAngles, phi); /* get values */ thetamax = _vertAngles[idthetamax]; idthetamin = idthetamax - 1; thetamin = _vertAngles[idthetamin]; if (idphimax >= _numHorzAngles) { idphimax = 0; idphimin = _numHorzAngles - 1; } else if (idphimax <= 0) { idphimax = _numHorzAngles - 1; idphimin = 0; } else { idphimin = idphimax - 1; } phimax = _horzAngles[idphimax]; phimin = _vertAngles[idthetamin]; candela_tm_pm = _candelaDistribution[idphimin][idthetamin]; candela_tm_pM = _candelaDistribution[idphimax][idthetamin]; candela_tM_pM = _candelaDistribution[idphimax][idthetamax]; candela_tM_pm = _candelaDistribution[idphimin][idthetamax]; val = (candela_tm_pm + candela_tm_pM + candela_tM_pM + candela_tM_pm) / 4.0; return val; } double OdLightIes::getValue_ipl(double theta, double phi) const { double val = 0.0; double thetamin, thetamax; OdInt32 idthetamin, idthetamax; double phimin, phimax; OdInt32 idphimin, idphimax; double candela_tm_pm, candela_tm_pM, candela_tM_pM, candela_tM_pm; /* search for first angle greater than theta (if exist) */ idthetamax = searchGreater(_vertAngles, 0, _numVertAngles, theta); /* search for first angle greater than theta (if exist) */ idphimax = searchGreater(_horzAngles, 0, _numHorzAngles, phi); /* get values */ thetamax = _vertAngles[idthetamax]; if (idthetamax >= _numVertAngles) { idthetamax = 0; idthetamin = _numVertAngles - 1; } else if (idthetamax <= 0) { idthetamax = _numVertAngles - 1; idthetamin = 0; } else { idthetamin = idthetamax - 1; } thetamin = _vertAngles[idthetamin]; if (idphimax >= _numHorzAngles) { idphimax = 0; idphimin = _numHorzAngles - 1; } else if (idphimax <= 0) { idphimax = _numHorzAngles - 1; idphimin = 0; } else { idphimin = idphimax - 1; } phimax = _horzAngles[idphimax]; phimin = _horzAngles[idphimin]; double phiMinMult = (phi - phimin) / (phimax - phimin); double phiMaxMult = (phimax - phi) / (phimax - phimin); double thetaMinMult = (theta - thetamin) / (thetamax - thetamin); double thetaMaxMult = (thetamax - theta) / (thetamax - thetamin); candela_tm_pm = _candelaDistribution[idphimin][idthetamin] * (phiMinMult * thetaMinMult); candela_tm_pM = _candelaDistribution[idphimax][idthetamin] * (phiMaxMult * thetaMinMult); candela_tM_pM = _candelaDistribution[idphimax][idthetamax] * (phiMaxMult * thetaMaxMult); candela_tM_pm = _candelaDistribution[idphimin][idthetamax] * (phiMinMult * thetaMaxMult); val = (candela_tm_pm + candela_tm_pM + candela_tM_pM + candela_tM_pm) /*/ 4.0*/; return val; } // OdLightIesModule - Dynamic/static .tx module definitions OdLightIesModule::OdLightIesModule() { } OdLightIesModule::~OdLightIesModule() { } OdIesnaFilePtr OdLightIesModule::load(const OdChar *pFileName, OdDbBaseHostAppServices *pSvcs, OdDbBaseDatabase *pDb) { OdSmartPtr pFile = OdRxObjectImpl::createObject(); if (pFile->load(pFileName, pSvcs, pDb)) { pFile->m_pLockMe = this; return pFile; } return OdIesnaFilePtr(); } OdIesnaFilePtr OdLightIesModule::load(OdStreamBufPtr& pStreamBuf, OdDbBaseHostAppServices* pSvcs, OdDbBaseDatabase* pDb) { OdSmartPtr pFile = OdRxObjectImpl::createObject(); if (pFile->load(pStreamBuf, pSvcs, pDb)) { pFile->m_pLockMe = this; return pFile; } return OdIesnaFilePtr(); } void OdLightIesModule::initApp() { } void OdLightIesModule::uninitApp() { } ODRX_DEFINE_DYNAMIC_MODULE(OdLightIesModule); OdRxModule* odrxCreateModuleObject_For_OdIesnaLoader(const OdString& szModuleName) { return OdRxStaticModule::createModule(szModuleName); } #if defined(_TOOLKIT_IN_DLL_) && defined(_MSC_VER) extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID ) { switch ( dwReason ) { case DLL_PROCESS_ATTACH: // remove this if you need per-thread initialization DisableThreadLibraryCalls( (HMODULE)hInstance ); break; } return TRUE; } #endif //_TOOLKIT_IN_DLL_ //