diff --git a/docs/外部参考.md b/docs/外部参考.md index 1fa63ae..7a94e6a 100644 --- a/docs/外部参考.md +++ b/docs/外部参考.md @@ -1,6 +1,8 @@ # 外部参考 -本项目独立实现 CM93 importer,不复制 OpenCPN GPL 源码。 +本项目独立实现 CM93 importer;为保证 S-52 / CM93 对照审计可在不同机器复现,仓库内另行保存一份只读 OpenCPN GPL 上游源码快照,作为外部参考资料,不与 CM95 自有实现混合。 + +固定参考快照:`docs/外部参考/OpenCPN源码基线_f7211b4e/`,上游 commit 为 `f7211b4e1874c453e37a7f190aa191c55a25c32e`,目录内保留原始版权声明、GPL 许可证和 SHA-256 校验清单。 用于核对行为和 MySQL 兼容性的公开参考: diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游提交.txt b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游提交.txt new file mode 100644 index 0000000..8edef98 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游提交.txt @@ -0,0 +1 @@ +f7211b4e1874c453e37a7f190aa191c55a25c32e diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/gui/src/cm93.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/gui/src/cm93.cpp new file mode 100644 index 0000000..4735ad7 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/gui/src/cm93.cpp @@ -0,0 +1,6527 @@ +/************************************************************************** + * Copyright (C) 2010 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see . * + **************************************************************************/ + +/** + * \file + * + * Implement cm93.h -- CM93 chart state + */ + +#include +#include +#include + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif // precompiled headers + +#include +#include +#include +#include +#include +#include +#include + +#include "model/plugin_comm.h" +#include "model/chartdata_input_stream.h" + +#include "chcanv.h" +#include "cm93.h" +#include "detail_slider.h" +#include "gui_lib.h" +#include "line_clip.h" +#include "model/georef.h" +#include "mygeom.h" +#include "navutil.h" // for LogMessageOnce +#include "ocpndc.h" +#include "ocpn_pixel.h" // for ocpnUSE_DIBSECTION +#include "ocpn_platform.h" +#include "pluginmanager.h" // for PlugInManager +#include "s52plib.h" +#include "s52s57.h" +#include "s57chart.h" +#include "top_frame.h" + +#ifdef ocpnUSE_GL +#include "gl_chart_canvas.h" +#endif + +#ifdef __VISUALC__ +#include +#endif + +extern s52plib *ps52plib; + +CM93OffsetDialog *g_pCM93OffsetDialog; + +#include +WX_DEFINE_OBJARRAY(Array_Of_M_COVR_Desc); + +#include +WX_DEFINE_LIST(List_Of_M_COVR_Desc); + +static int Get_CM93_CellIndex(double lat, double lon, int scale); + +void Get_CM93_Cell_Origin(int cellindex, int scale, double *lat, double *lon); + +void appendOSDirSep(wxString *pString) { + wxChar sep = wxFileName::GetPathSeparator(); + if (pString->Last() != sep) pString->Append(sep); +} + +//---------------------------------------------------------------------------- +// M_COVR_Desc Implementation +//---------------------------------------------------------------------------- + +M_COVR_Desc::M_COVR_Desc() { + pvertices = NULL; + gl_screen_vertices = NULL; + gl_screen_projection_type = PROJECTION_UNKNOWN; + + user_xoff = 0.; + user_yoff = 0.; + m_centerlat_cos = 1.0; + m_buser_offsets = false; + + m_ngl_vertices = 0; + gl_screen_vertices = NULL; +} + +M_COVR_Desc::~M_COVR_Desc() { + delete[] pvertices; + delete[] gl_screen_vertices; +} + +int M_COVR_Desc::GetWKBSize() { + int size = 0; + + size = sizeof(int); // size itself + size += sizeof(int); // m_cell_index; + size += sizeof(int); // m_object_id; + size += sizeof(int); // m_subcell + size += sizeof(int); // m_nvertices; + size += m_nvertices * sizeof(float_2Dpt); // pvertices; + + size += sizeof(int); // m_npub_year; + size += 8 * sizeof(double); // all the rest + + return size; +} + +bool M_COVR_Desc::WriteWKB(void *p) { + if (p) { + int *pr = (int *)p; + *pr++ = GetWKBSize(); + + *pr++ = m_cell_index; + *pr++ = m_object_id; + *pr++ = m_subcell; + + *pr++ = m_nvertices; + + float_2Dpt *pfo = (float_2Dpt *)pr; + float_2Dpt *pfi = pvertices; + for (int i = 0; i < m_nvertices; i++) *pfo++ = *pfi++; + + int *pi = (int *)pfo; + *pi++ = m_npub_year; + + double *pd = (double *)pi; + *pd++ = transform_WGS84_offset_x; + *pd++ = transform_WGS84_offset_y; + *pd++ = m_covr_lat_min; + *pd++ = m_covr_lat_max; + *pd++ = m_covr_lon_min; + *pd++ = m_covr_lon_max; + + double centerlat_cos = + cos(((m_covr_lat_min + m_covr_lat_max) / 2.) * PI / 180.); + + *pd++ = user_xoff * centerlat_cos; + *pd++ = user_yoff * centerlat_cos; + } + + return true; +} + +int M_COVR_Desc::ReadWKB(wxFFileInputStream &ifs) { + // Read the length of the WKB + int length = 0; + if (!ifs.Read(&length, sizeof(int)).Eof()) { + ifs.Read(&m_cell_index, sizeof(int)); + ifs.Read(&m_object_id, sizeof(int)); + ifs.Read(&m_subcell, sizeof(int)); + + ifs.Read(&m_nvertices, sizeof(int)); + + pvertices = new float_2Dpt[m_nvertices]; + + ifs.Read(pvertices, m_nvertices * sizeof(float_2Dpt)); + + ifs.Read(&m_npub_year, sizeof(int)); + + ifs.Read(&transform_WGS84_offset_x, sizeof(double)); + ifs.Read(&transform_WGS84_offset_y, sizeof(double)); + ifs.Read(&m_covr_lat_min, sizeof(double)); + ifs.Read(&m_covr_lat_max, sizeof(double)); + ifs.Read(&m_covr_lon_min, sizeof(double)); + ifs.Read(&m_covr_lon_max, sizeof(double)); + + m_centerlat_cos = cos(((m_covr_lat_min + m_covr_lat_max) / 2.) * PI / 180.); + + ifs.Read(&user_xoff, sizeof(double)); + ifs.Read(&user_yoff, sizeof(double)); + + user_xoff /= m_centerlat_cos; + user_yoff /= m_centerlat_cos; + + if ((fabs(user_xoff) > 1.) || (fabs(user_yoff) > 1.)) + m_buser_offsets = true; + else + m_buser_offsets = false; + + m_covr_bbox.Set(m_covr_lat_min, m_covr_lon_min, m_covr_lat_max, + m_covr_lon_max); + } + return length; +} + +OCPNRegion M_COVR_Desc::GetRegion(const ViewPort &vp, wxPoint *pwp) { + float_2Dpt *p = pvertices; + + for (int ip = 0; ip < m_nvertices; ip++) { + double plon = p->x; + if (fabs(plon - vp.clon) > 180.) { + if (plon > vp.clon) + plon -= 360.; + else + plon += 360.; + } + + double easting, northing, epix, npix; + toSM(p->y, plon + 360., vp.clat, vp.clon + 360, &easting, &northing); + + // easting -= transform_WGS84_offset_x; + easting -= user_xoff; + // northing -= transform_WGS84_offset_y; + northing -= user_yoff; + + epix = easting * vp.view_scale_ppm; + npix = northing * vp.view_scale_ppm; + + pwp[ip].x = (int)round((vp.pix_width / 2) + epix); + pwp[ip].y = (int)round((vp.pix_height / 2) - npix); + + p++; + } + + return OCPNRegion(m_nvertices, pwp); +} + +//---------------------------------------------------------------------------- +// cm93 covr_set object class +// This is a helper class which holds all the known information +// relating to cm93 cell MCOVR objects of a particular scale +//---------------------------------------------------------------------------- + +char sig_version[] = "COVR1002"; + +class covr_set { +public: + covr_set(cm93chart *parent); + ~covr_set(); + + bool Init(wxChar scale_char, wxString &prefix); + unsigned int GetCoverCount() { return m_covr_array_outlines.GetCount(); } + M_COVR_Desc *GetCover(unsigned int im) { return &m_covr_array_outlines[im]; } + void Add_MCD(M_COVR_Desc *pmcd); + bool Add_Update_MCD(M_COVR_Desc *pmcd); + bool IsCovrLoaded(int cell_index); + int Find_MCD(M_COVR_Desc *pmcd); + M_COVR_Desc *Find_MCD(int cell_index, int object_id, int sbcell); + + cm93chart *m_pParent; + wxChar m_scale_char; + int m_scale; + + wxString m_cachefile; + + // array, for chart outline rendering + Array_Of_M_COVR_Desc m_covr_array_outlines; + + // This is a hash, indexed by cell index, elements + // contain the number of M_COVRs found on this particular cell + std::unordered_map m_cell_hash; +}; + +covr_set::covr_set(cm93chart *parent) { m_pParent = parent; } + +covr_set::~covr_set() { + // Create/Update the cache + if (m_cachefile.IsEmpty()) + return; // presumably for Z scale charts + // for which we create no cache + + if (m_covr_array_outlines.GetCount()) { + wxFFileOutputStream ofs(m_cachefile); + if (ofs.IsOk()) { + ofs.Write(sig_version, 8); // write signature + + for (unsigned int i = 0; i < m_covr_array_outlines.GetCount(); i++) { + int wkbsize = m_covr_array_outlines[i].GetWKBSize(); + if (wkbsize) { + char *p = (char *)malloc(wkbsize * sizeof(char)); + m_covr_array_outlines[i].WriteWKB(p); + ofs.Write(p, wkbsize); + free(p); + } + } + ofs.Close(); + } + } +} + +bool covr_set::Init(wxChar scale_char, wxString &prefix) { + m_scale_char = scale_char; + + switch (m_scale_char) { + case 'Z': + m_scale = 20000000; + break; + case 'A': + m_scale = 3000000; + break; + case 'B': + m_scale = 1000000; + break; + case 'C': + m_scale = 200000; + break; + case 'D': + m_scale = 100000; + break; + case 'E': + m_scale = 50000; + break; + case 'F': + m_scale = 20000; + break; + case 'G': + m_scale = 7500; + break; + default: + m_scale = 20000000; + break; + } + + // Create the cache file name + wxString prefix_string = prefix; + wxString sep(wxFileName::GetPathSeparator()); + prefix_string.Replace(sep, "_"); + prefix_string.Replace(":", "_"); // for Windows + + m_cachefile = g_Platform->GetPrivateDataDir(); + appendOSDirSep(&m_cachefile); + + m_cachefile += "cm93"; + appendOSDirSep(&m_cachefile); + + m_cachefile += + prefix_string; // include the cm93 prefix string in the cache file name + m_cachefile += "_"; // to support multiple cm93 data sets + + wxString cache_old_old_name = m_cachefile; + cache_old_old_name += "coverset."; + cache_old_old_name += m_scale_char; + + wxString cache_old_name = m_cachefile; + cache_old_name += "coverset_sig."; + cache_old_name += m_scale_char; + + m_cachefile += "coverset_sigp."; + m_cachefile += m_scale_char; + + wxFileName fn(m_cachefile); + if (!fn.DirExists()) wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL); + + // Preload the cache + if (!wxFileName::FileExists(m_cachefile)) { + // The signed file does not exist + // Check for an old style file, and delete if found. + if (wxFileName::FileExists(cache_old_name)) ::wxRemoveFile(cache_old_name); + if (wxFileName::FileExists(cache_old_old_name)) + ::wxRemoveFile(cache_old_old_name); + return false; + } + + wxFFileInputStream ifs(m_cachefile); + if (ifs.IsOk()) { + char sig_bytes[9]; + // Validate the file signature + if (!ifs.Read(&sig_bytes, 8).Eof()) { + if (strncmp(sig_bytes, sig_version, 8)) { + return false; // bad signature match + } + } else + return false; // short file + + bool b_cont = true; + while (b_cont) { + M_COVR_Desc *pmcd = new M_COVR_Desc; + int length = pmcd->ReadWKB(ifs); + + if (length) { + m_covr_array_outlines.Add(pmcd); + + if (m_cell_hash.find(pmcd->m_cell_index) == m_cell_hash.end()) + m_cell_hash[pmcd->m_cell_index] = 0; // initialize the element + + m_cell_hash[pmcd->m_cell_index]++; // add this M_COVR to the hash map + + } else { + delete pmcd; + b_cont = false; + } + } + } + + return true; +} + +void covr_set::Add_MCD(M_COVR_Desc *pmcd) { + m_covr_array_outlines.Add(pmcd); + + if (m_cell_hash.find(pmcd->m_cell_index) == + m_cell_hash.end()) // not present yet? + m_cell_hash[pmcd->m_cell_index] = 0; // initialize + + m_cell_hash[pmcd->m_cell_index]++; // add this M_COVR to the hash map +} + +bool covr_set::IsCovrLoaded(int cell_index) { + return (m_cell_hash.find(cell_index) != m_cell_hash.end()); +} + +bool covr_set::Add_Update_MCD(M_COVR_Desc *pmcd) { + if (m_cell_hash.find(pmcd->m_cell_index) == + m_cell_hash.end()) // not present yet? + { + m_covr_array_outlines.Add(pmcd); + m_cell_hash[pmcd->m_cell_index] = 1; // initialize + return true; + } + // There is at least one MCD already in place for this cell index + // We need to search the entire table to see if any of those MCD's + // correspond to this MCD's object identifier and subcell, as well as cell + // index + else { + bool b_found = false; + for (unsigned int i = 0; i < m_covr_array_outlines.GetCount(); i++) { + M_COVR_Desc *pmcd_candidate = &m_covr_array_outlines[i]; + if ((pmcd_candidate->m_cell_index == pmcd->m_cell_index) && + (pmcd_candidate->m_object_id == pmcd->m_object_id) && + (pmcd_candidate->m_subcell == pmcd->m_subcell)) + + { + b_found = true; + break; + } + } + + if (!b_found) { + m_covr_array_outlines.Add(pmcd); + m_cell_hash[pmcd->m_cell_index]++; // add this M_COVR to the hash map + return true; + } else + return false; + } +} + +int covr_set::Find_MCD(M_COVR_Desc *pmcd) { + if (m_cell_hash.find(pmcd->m_cell_index) == + m_cell_hash.end()) // not present? + return -1; + else { + // There is at least one MCD already in place for this cell index + // We need to search the entire table to see if any of those MCD's + // correspond to this MCD's object identifier as well as cell index + + for (unsigned int i = 0; i < m_covr_array_outlines.GetCount(); i++) { + M_COVR_Desc *pmcd_candidate = &m_covr_array_outlines[i]; + if ((pmcd_candidate->m_cell_index == pmcd->m_cell_index) && + (pmcd_candidate->m_object_id == pmcd->m_object_id) && + (pmcd_candidate->m_subcell == pmcd->m_subcell)) { + return (int)i; + } + } + } + return -1; +} + +M_COVR_Desc *covr_set::Find_MCD(int cell_index, int object_id, int subcell) { + if (m_cell_hash.find(cell_index) == m_cell_hash.end()) // not present? + return NULL; + + for (unsigned int i = 0; i < m_covr_array_outlines.GetCount(); i++) { + M_COVR_Desc *pmcd_candidate = &m_covr_array_outlines[i]; + if ((pmcd_candidate->m_cell_index == cell_index) && + (pmcd_candidate->m_object_id == object_id) && + (pmcd_candidate->m_subcell == subcell)) + + return pmcd_candidate; + } + + return NULL; +} + +// CM93 Encode/Decode support tables + +static unsigned char Table_0[] = { + 0x0CD, 0x0EA, 0x0DC, 0x048, 0x03E, 0x06D, 0x0CA, 0x07B, 0x052, 0x0E1, 0x0A4, + 0x08E, 0x0AB, 0x005, 0x0A7, 0x097, 0x0B9, 0x060, 0x039, 0x085, 0x07C, 0x056, + 0x07A, 0x0BA, 0x068, 0x06E, 0x0F5, 0x05D, 0x002, 0x04E, 0x00F, 0x0A1, 0x027, + 0x024, 0x041, 0x034, 0x000, 0x05A, 0x0FE, 0x0CB, 0x0D0, 0x0FA, 0x0F8, 0x06C, + 0x074, 0x096, 0x09E, 0x00E, 0x0C2, 0x049, 0x0E3, 0x0E5, 0x0C0, 0x03B, 0x059, + 0x018, 0x0A9, 0x086, 0x08F, 0x030, 0x0C3, 0x0A8, 0x022, 0x00A, 0x014, 0x01A, + 0x0B2, 0x0C9, 0x0C7, 0x0ED, 0x0AA, 0x029, 0x094, 0x075, 0x00D, 0x0AC, 0x00C, + 0x0F4, 0x0BB, 0x0C5, 0x03F, 0x0FD, 0x0D9, 0x09C, 0x04F, 0x0D5, 0x084, 0x01E, + 0x0B1, 0x081, 0x069, 0x0B4, 0x009, 0x0B8, 0x03C, 0x0AF, 0x0A3, 0x008, 0x0BF, + 0x0E0, 0x09A, 0x0D7, 0x0F7, 0x08C, 0x067, 0x066, 0x0AE, 0x0D4, 0x04C, 0x0A5, + 0x0EC, 0x0F9, 0x0B6, 0x064, 0x078, 0x006, 0x05B, 0x09B, 0x0F2, 0x099, 0x0CE, + 0x0DB, 0x053, 0x055, 0x065, 0x08D, 0x007, 0x033, 0x004, 0x037, 0x092, 0x026, + 0x023, 0x0B5, 0x058, 0x0DA, 0x02F, 0x0B3, 0x040, 0x05E, 0x07F, 0x04B, 0x062, + 0x080, 0x0E4, 0x06F, 0x073, 0x01D, 0x0DF, 0x017, 0x0CC, 0x028, 0x025, 0x02D, + 0x0EE, 0x03A, 0x098, 0x0E2, 0x001, 0x0EB, 0x0DD, 0x0BC, 0x090, 0x0B0, 0x0FC, + 0x095, 0x076, 0x093, 0x046, 0x057, 0x02C, 0x02B, 0x050, 0x011, 0x00B, 0x0C1, + 0x0F0, 0x0E7, 0x0D6, 0x021, 0x031, 0x0DE, 0x0FF, 0x0D8, 0x012, 0x0A6, 0x04D, + 0x08A, 0x013, 0x043, 0x045, 0x038, 0x0D2, 0x087, 0x0A0, 0x0EF, 0x082, 0x0F1, + 0x047, 0x089, 0x06A, 0x0C8, 0x054, 0x01B, 0x016, 0x07E, 0x079, 0x0BD, 0x06B, + 0x091, 0x0A2, 0x071, 0x036, 0x0B7, 0x003, 0x03D, 0x072, 0x0C6, 0x044, 0x08B, + 0x0CF, 0x015, 0x09F, 0x032, 0x0C4, 0x077, 0x083, 0x063, 0x020, 0x088, 0x0F6, + 0x0AD, 0x0F3, 0x0E8, 0x04A, 0x0E9, 0x035, 0x01C, 0x05F, 0x019, 0x01F, 0x07D, + 0x070, 0x0FB, 0x0D1, 0x051, 0x010, 0x0D3, 0x02E, 0x061, 0x09D, 0x05C, 0x02A, + 0x042, 0x0BE, 0x0E6}; + +static unsigned char Encode_table[256]; +static unsigned char Decode_table[256]; + +static bool cm93_decode_table_created; + +// Case-insensitive cm93 directory tree depth-first traversal to find the +// dictionary... This could be made simpler, but matches the old code better as +// is +class FindCM93Dictionary : public wxDirTraverser { +public: + FindCM93Dictionary(wxString &path) : m_path(path) {} + + virtual wxDirTraverseResult OnFile(const wxString &filename) { + wxString name = filename.AfterLast(wxFileName::GetPathSeparator()).Lower(); + if (name == "cm93obj.dic") { + m_path = filename; + return wxDIR_STOP; + } + + return wxDIR_CONTINUE; + } + + virtual wxDirTraverseResult OnDir(const wxString &WXUNUSED(dirname)) { + return wxDIR_CONTINUE; + } + +private: + wxString &m_path; +}; + +cm93_dictionary::cm93_dictionary() { + m_S57ClassArray = NULL; + m_AttrArray = NULL; + m_GeomTypeArray = NULL; + ; + m_ValTypeArray = NULL; + m_max_class = 0; + m_ok = false; +} + +bool cm93_dictionary::LoadDictionary(const wxString &dictionary_dir) { + int i, nline; + wxString line; + wxString dir(dictionary_dir); // a copy + bool ret_val = false; + + wxChar sep = wxFileName::GetPathSeparator(); + if (dir.Last() != sep) dir.Append(sep); + + m_dict_dir = dir; + + // Build some array strings for Feature decoding + + wxString sf(dir); + sf.Append("CM93OBJ.DIC"); + + if (!wxFileName::FileExists(sf)) { + sf = dir; + sf.Append("cm93obj.dic"); + if (!wxFileName::FileExists(sf)) return false; + } + + wxTextFile file; + if (!file.Open(sf)) return false; + + nline = file.GetLineCount(); + + if (!nline) return false; + + // Read the file once to get the max class number + int iclass_max = 0; + + for (i = 0; i < nline; i++) { + line = file.GetLine(i); + + wxStringTokenizer tkz(line, "|"); + // while ( tkz.HasMoreTokens() ) + { + // 6 char class name + wxString class_name = tkz.GetNextToken(); + + // class number, ascii + wxString token = tkz.GetNextToken(); + long liclass; + token.ToLong(&liclass); + int iclass = liclass; + if (iclass > iclass_max) iclass_max = iclass; + + // geom type, ascii + wxString geo_type = tkz.GetNextToken(); + } + } + + m_max_class = iclass_max; + + // Create the class name array + m_S57ClassArray = new wxArrayString; + m_S57ClassArray->Add("NULLNM", iclass_max + 1); + + // And an array of ints describing the geometry type per class + m_GeomTypeArray = (int *)malloc((iclass_max + 1) * sizeof(int)); + + // Iterate over the file, filling in the values + for (i = 0; i < nline; i++) { + line = file.GetLine(i); + + wxStringTokenizer tkz(line, "|"); + // while ( tkz.HasMoreTokens() ) + { + // 6 char class name + wxString class_name = tkz.GetNextToken(); + + // class number, ascii + wxString token = tkz.GetNextToken(); + long liclass; + token.ToLong(&liclass); + int iclass = liclass; + + // geom type, ascii + wxString geo_type = tkz.GetNextToken(); + + m_S57ClassArray->Insert(class_name, iclass); + m_S57ClassArray->RemoveAt(iclass + 1); + + int igeom_type = -1; // default unknown + wxChar geo_type_primary = geo_type[0]; + + if (geo_type_primary == 'A') + igeom_type = 3; + else if (geo_type_primary == 'L') + igeom_type = 2; + else if (geo_type_primary == 'P') + igeom_type = 1; + + // Note: there are other types in the file, e.g. 'C'. Dunno what this + // is Also, some object classes want multiple geometries, like PA, PLA, + // etc. Take only primary, ignore the rest + + m_GeomTypeArray[iclass] = igeom_type; + } + } + file.Close(); + + // Build some array strings for Attribute decoding + + wxString sfa(dir); + sfa.Append("ATTRLUT.DIC"); + + if (!wxFileName::FileExists(sfa)) { + sfa = dir; + sfa.Append("attrlut.dic"); + } + + if (wxFileName::FileExists(sfa)) { + wxFFileInputStream filea(sfa); + + if (filea.IsOk()) { + // Read the file once to get the max attr number + int iattr_max = 0; + + while (!filea.Eof()) { + // read a line + line.Empty(); + while (1) { + char a = filea.GetC(); + if (filea.Eof()) break; + line.Append(a); + if (a == 0x0a) break; + } + + if (!line.StartsWith((const wxChar *)";")) { + wxStringTokenizer tkz(line, "|"); + { + // 6 attribute label + wxString class_name = tkz.GetNextToken(); + + // attribute number, ascii + wxString token = tkz.GetNextToken(); + long liattr; + token.ToLong(&liattr); + int iattr = liattr; + if (iattr > iattr_max) iattr_max = iattr; + } + } + } + + m_max_attr = iattr_max; + + filea.SeekI(0); + + // Create the attribute label array + + m_AttrArray = new wxArrayString; + m_AttrArray->Add("NULLNM", iattr_max + 1); + + // And an array of chars describing the attribute value type + m_ValTypeArray = (char *)malloc((iattr_max + 1) * sizeof(char)); + + // Iterate over the file, filling in the values + while (!filea.Eof()) { + // read a line + line.Empty(); + while (1) { + char a = filea.GetC(); + if (filea.Eof()) break; + line.Append(a); + if (a == 0x0a) break; + } + + if (!line.StartsWith((const wxChar *)";")) { + wxStringTokenizer tkz(line, "|"); + { + // 6 char class name + wxString attr_name = tkz.GetNextToken(); + + // class number, ascii + wxString token = tkz.GetNextToken(); + long liattr; + token.ToLong(&liattr); + int iattr = liattr; + + m_AttrArray->Insert(attr_name, iattr); + m_AttrArray->RemoveAt(iattr + 1); + + // Skip some + token = tkz.GetNextToken(); + token = tkz.GetNextToken(); + token = tkz.GetNextToken(); + token = tkz.GetNextToken().Trim(); + + char atype = '?'; + if (token.IsSameAs("aFLOAT")) + atype = 'R'; + else if (token.IsSameAs("aBYTE")) + atype = 'B'; + else if (token.IsSameAs("aSTRING")) + atype = 'S'; + else if (token.IsSameAs("aCMPLX")) + atype = 'C'; + else if (token.IsSameAs("aLIST")) + atype = 'L'; + else if (token.IsSameAs("aWORD10")) + atype = 'W'; + else if (token.IsSameAs("aLONG")) + atype = 'G'; + + m_ValTypeArray[iattr] = atype; + } + } + } + ret_val = true; + } else // stream IsOK + { + ret_val = false; + } + } + + else // Look for alternate file + { + sfa = dir; + sfa.Append("CM93ATTR.DIC"); + + if (!wxFileName::FileExists(sfa)) { + sfa = dir; + sfa.Append("cm93attr.dic"); + } + + if (wxFileName::FileExists(sfa)) { + wxFFileInputStream filea(sfa); + + if (filea.IsOk()) { + // Read the file once to get the max attr number + int iattr_max = 0; + + while (!filea.Eof()) { + // read a line + line.Empty(); + while (1) { + char a = filea.GetC(); + if (filea.Eof()) break; + line.Append(a); + if (a == 0x0a) break; + } + + if (line[0] != ';') { + wxStringTokenizer tkz(line, "|"); + if (tkz.CountTokens()) { + // 6 attribute label + wxString class_name = tkz.GetNextToken(); + + // attribute number, ascii + wxString token = tkz.GetNextToken(); + long liattr; + token.ToLong(&liattr); + int iattr = liattr; + if (iattr > iattr_max) iattr_max = iattr; + } + } + } + + m_max_attr = iattr_max; + + filea.SeekI(0); + + // Create the attribute label array + + m_AttrArray = new wxArrayString; + m_AttrArray->Add("NULLNM", iattr_max + 1); + + // And an array of chars describing the attribute value type + m_ValTypeArray = (char *)malloc((iattr_max + 1) * sizeof(char)); + for (int iat = 0; iat < iattr_max + 1; iat++) m_ValTypeArray[iat] = '?'; + + // Iterate over the file, filling in the values + while (!filea.Eof()) { + // read a line + line.Empty(); + while (1) { + char a = filea.GetC(); + if (filea.Eof()) break; + line.Append(a); + if (a == 0x0a) break; + } + + if (line[0] != ';') { + wxStringTokenizer tkz(line, "|\r\n"); + if (tkz.CountTokens() >= 3) { + // 6 char class name + wxString attr_name = tkz.GetNextToken(); + + // class number, ascii + wxString token = tkz.GetNextToken(); + long liattr; + token.ToLong(&liattr); + int iattr = liattr; + + m_AttrArray->Insert(attr_name, iattr); + m_AttrArray->RemoveAt(iattr + 1); + + token = tkz.GetNextToken().Trim(); + + char atype = '?'; + if (token.IsSameAs("aFLOAT")) + atype = 'R'; + else if (token.IsSameAs("aBYTE")) + atype = 'B'; + else if (token.IsSameAs("aSTRING")) + atype = 'S'; + else if (token.IsSameAs("aCMPLX")) + atype = 'C'; + else if (token.IsSameAs("aLIST")) + atype = 'L'; + else if (token.IsSameAs("aWORD10")) + atype = 'W'; + else if (token.IsSameAs("aLONG")) + atype = 'G'; + + m_ValTypeArray[iattr] = atype; + } + } + } + ret_val = true; + } else // stream IsOK + ret_val = false; + } + } + + if (ret_val) { + m_ok = true; + + wxString msg("Loaded CM93 Dictionary from "); + msg.Append(dir); + wxLogMessage(msg); + } + + return ret_val; +} + +wxString cm93_dictionary::GetClassName(int iclass) { + if ((iclass > m_max_class) || (iclass < 0)) + return ("Unknown"); + else + return (m_S57ClassArray->Item(iclass)); +} + +wxString cm93_dictionary::GetAttrName(int iattr) { + if ((iattr > m_max_attr) || (iattr < 0)) + return ("UnknownAttr"); + else + return (m_AttrArray->Item(iattr)); +} + +// char vtype = m_pDict->m_ValTypeArray[iattr]; +char cm93_dictionary::GetAttrType(int iattr) { + if ((iattr > m_max_attr) || (iattr < 0)) + return ('?'); + else + return (m_ValTypeArray[iattr]); +} + +cm93_dictionary::~cm93_dictionary() { + delete m_S57ClassArray; + free(m_GeomTypeArray); + delete m_AttrArray; + free(m_ValTypeArray); +} + +// CM93 Decode support routines + +void CreateDecodeTable() { + int i; + for (i = 0; i < 256; i++) { + Encode_table[i] = Table_0[i] ^ 8; + } + + for (i = 0; i < 256; i++) { + unsigned char a = Encode_table[i]; + Decode_table[(int)a] = (unsigned char)i; + } +} + +static int read_and_decode_bytes(FILE *stream, void *p, int nbytes) { + if (0 == nbytes) // declare victory if no bytes requested + return 1; + + // read into callers buffer + if (fread(p, nbytes, 1, stream) != 1) return 0; + + // decode inplace + unsigned char *q = (unsigned char *)p; + + for (int i = 0; i < nbytes; i++) { + unsigned char a = *q; + int b = a; + unsigned char c = Decode_table[b]; + *q = c; + + q++; + } + return 1; +} + +static int read_and_decode_double(FILE *stream, double *p) { + double t; + // read into temp buffer + if (fread(&t, sizeof(double), 1, stream) != 1) return 0; + + // decode inplace + unsigned char *q = (unsigned char *)&t; + + for (unsigned int i = 0; i < sizeof(double); i++) { + unsigned char a = *q; + int b = a; + unsigned char c = Decode_table[b]; + *q = c; + + q++; + } + + // copy to target + *p = t; + + return 1; +} + +static int read_and_decode_int(FILE *stream, int *p) { + int t; + // read into temp buffer + if (fread(&t, sizeof(int), 1, stream) != 1) return 0; + + // decode inplace + unsigned char *q = (unsigned char *)&t; + + for (unsigned int i = 0; i < sizeof(int); i++) { + unsigned char a = *q; + int b = a; + unsigned char c = Decode_table[b]; + *q = c; + + q++; + } + + // copy to target + *p = t; + + return 1; +} + +static int read_and_decode_ushort(FILE *stream, unsigned short *p) { + unsigned short t; + // read into temp buffer + if (fread(&t, sizeof(unsigned short), 1, stream) != 1) return 0; + + // decode inplace + unsigned char *q = (unsigned char *)&t; + + for (unsigned int i = 0; i < sizeof(unsigned short); i++) { + unsigned char a = *q; + int b = a; + unsigned char c = Decode_table[b]; + *q = c; + + q++; + } + + // copy to target + *p = t; + + return 1; +} + +// Calculate the CM93 CellIndex integer for a given Lat/Lon, at a given scale + +int Get_CM93_CellIndex(double lat, double lon, int scale) { + int retval = 0; + + int dval; + switch (scale) { + case 20000000: + dval = 120; + break; // Z + case 3000000: + dval = 60; + break; // A + case 1000000: + dval = 30; + break; // B + case 200000: + dval = 12; + break; // C + case 100000: + dval = 3; + break; // D + case 50000: + dval = 1; + break; // E + case 20000: + dval = 1; + break; // F + case 7500: + dval = 1; + break; // G + default: + dval = 1; + break; + } + + // Longitude + double lon1 = (lon + 360.) * 3.; // basic cell size is 20 minutes + while (lon1 >= 1080.0) lon1 -= 1080.0; + unsigned short lon2 = (unsigned short)floor(lon1 / dval); // normalize + unsigned short lon3 = lon2 * dval; + + retval = lon3; + + // Latitude + double lat1 = (lat * 3.) + 270. - 30; + unsigned short lat2 = (unsigned short)floor(lat1 / dval); // normalize + unsigned short lat3 = lat2 * dval; + + retval += (lat3 + 30) * 10000; + + return retval; +} + +// Calculate the Lat/Lon of the lower left corner of a CM93 cell, +// given a CM93 CellIndex and scale +// Returned longitude value is always > 0 +void Get_CM93_Cell_Origin(int cellindex, int scale, double *lat, double *lon) { + // Longitude + double idx1 = cellindex % 10000; + double lont = (idx1 / 3.); + + *lon = lont; + + // Latitude + int idx2 = cellindex / 10000; + double lat1 = idx2 - 270.; + *lat = lat1 / 3.; +} + +// Answer the query: "Is there a cm93 cell at the specified scale which +// contains a given lat/lon?" +bool Is_CM93Cell_Present(wxString &fileprefix, double lat, double lon, + int scale_index) { + int scale; + int dval; + wxChar scale_char; + + switch (scale_index) { + case 0: + scale = 20000000; + dval = 120; + scale_char = 'Z'; + break; // Z + case 1: + scale = 3000000; + dval = 60; + scale_char = 'A'; + break; // A + case 2: + scale = 1000000; + dval = 30; + scale_char = 'B'; + break; // B + case 3: + scale = 200000; + dval = 12; + scale_char = 'C'; + break; // C + case 4: + scale = 100000; + dval = 3; + scale_char = 'D'; + break; // D + case 5: + scale = 50000; + dval = 1; + scale_char = 'E'; + break; // E + case 6: + scale = 20000; + dval = 1; + scale_char = 'F'; + break; // F + case 7: + scale = 7500; + dval = 1; + scale_char = 'G'; + break; // G + default: + scale = 20000000; + dval = 120; + scale_char = ' '; + break; + } + + int cellindex = Get_CM93_CellIndex(lat, lon, scale); + + // Create the file name + wxString file; + + int ilat = cellindex / 10000; + int ilon = cellindex % 10000; + + int jlat = (((ilat - 30) / dval) * dval) + 30; // normalize + int jlon = (ilon / dval) * dval; + + int ilatroot = (((ilat - 30) / 60) * 60) + 30; + int ilonroot = (ilon / 60) * 60; + + wxString fileroot; + fileroot.Printf("%04d%04d", ilatroot, ilonroot); + appendOSDirSep(&fileroot); + + wxString sdir(fileprefix); + sdir += fileroot; + sdir += scale_char; + + wxString tfile; + tfile.Printf("?%03d%04d.", jlat, jlon); + tfile += scale_char; + + // Validate that the directory exists, adjusting case if necessary + if (!::wxDirExists(sdir)) { + wxString old_scalechar(scale_char); + wxString new_scalechar = old_scalechar.Lower(); + + sdir = fileprefix; + sdir += fileroot; + sdir += new_scalechar; + } + + if (::wxDirExists(sdir)) { + wxDir dir(sdir); + + wxArrayString file_array; + int n_files = dir.GetAllFiles(sdir, &file_array, tfile, wxDIR_FILES); + + if (n_files) return true; + + // Try with alternate case of m_scalechar + wxString old_scalechar(scale_char); + wxString new_scalechar = old_scalechar.Lower(); + + wxString tfile1; + tfile1.Printf("?%03d%04d.", jlat, jlon); + tfile1 += new_scalechar; + + int n_files1 = dir.GetAllFiles(sdir, &file_array, tfile1, wxDIR_FILES); + + if (n_files1) return true; + + // try compressed + n_files = dir.GetAllFiles(sdir, &file_array, tfile + ".xz", wxDIR_FILES); + + if (n_files) return true; + } + + return false; +} + +static bool read_header_and_populate_cib(FILE *stream, Cell_Info_Block *pCIB) { + // Read header, populate Cell_Info_Block + + // This 128 byte block is read element-by-element, to allow for + // endian-ness correction by element. + // Unused elements are read and, well, unused. + + header_struct header; + + memset((void *)&header, 0, sizeof(header)); + + read_and_decode_double(stream, &header.lon_min); + read_and_decode_double(stream, &header.lat_min); + read_and_decode_double(stream, &header.lon_max); + read_and_decode_double(stream, &header.lat_max); + + read_and_decode_double(stream, &header.easting_min); + read_and_decode_double(stream, &header.northing_min); + read_and_decode_double(stream, &header.easting_max); + read_and_decode_double(stream, &header.northing_max); + + read_and_decode_ushort(stream, &header.usn_vector_records); + read_and_decode_int(stream, &header.n_vector_record_points); + read_and_decode_int(stream, &header.m_46); + read_and_decode_int(stream, &header.m_4a); + read_and_decode_ushort(stream, &header.usn_point3d_records); + read_and_decode_int(stream, &header.m_50); + read_and_decode_int(stream, &header.m_54); + read_and_decode_ushort(stream, &header.usn_point2d_records); + read_and_decode_ushort(stream, &header.m_5a); + read_and_decode_ushort(stream, &header.m_5c); + read_and_decode_ushort(stream, &header.usn_feature_records); + + read_and_decode_int(stream, &header.m_60); + read_and_decode_int(stream, &header.m_64); + read_and_decode_ushort(stream, &header.m_68); + read_and_decode_ushort(stream, &header.m_6a); + read_and_decode_ushort(stream, &header.m_6c); + read_and_decode_int(stream, &header.m_nrelated_object_pointers); + + read_and_decode_int(stream, &header.m_72); + read_and_decode_ushort(stream, &header.m_76); + + read_and_decode_int(stream, &header.m_78); + read_and_decode_int(stream, &header.m_7c); + + // Calculate and record the cell coordinate transform coefficients + + double delta_x = header.easting_max - header.easting_min; + if (delta_x < 0) + delta_x += CM93_semimajor_axis_meters * 2.0 * PI; // add one trip around + + pCIB->transform_x_rate = delta_x / 65535; + pCIB->transform_y_rate = (header.northing_max - header.northing_min) / 65535; + + pCIB->transform_x_origin = header.easting_min; + pCIB->transform_y_origin = header.northing_min; + + pCIB->min_lat = header.lat_min; + pCIB->min_lon = header.lon_min; + + // pCIB->m_cell_mcovr_array.Empty(); + + // Extract some table sizes from the header, and pre-allocate the tables + // We do it this way to avoid incremental realloc() calls, which are + // expensive + + pCIB->m_nfeature_records = header.usn_feature_records; + pCIB->pobject_block = + (Object *)calloc(pCIB->m_nfeature_records * sizeof(Object), 1); + + pCIB->m_n_point2d_records = header.usn_point2d_records; + pCIB->p2dpoint_array = + (cm93_point *)malloc(pCIB->m_n_point2d_records * sizeof(cm93_point)); + + pCIB->pprelated_object_block = + (Object **)malloc(header.m_nrelated_object_pointers * sizeof(Object *)); + + pCIB->object_vector_record_descriptor_block = + (vector_record_descriptor *)malloc((header.m_4a + header.m_46) * + sizeof(vector_record_descriptor)); + + pCIB->attribute_block_top = (unsigned char *)calloc(header.m_78, 1); + + pCIB->m_nvector_records = header.usn_vector_records; + pCIB->edge_vector_descriptor_block = (geometry_descriptor *)malloc( + header.usn_vector_records * sizeof(geometry_descriptor)); + + pCIB->pvector_record_block_top = + (cm93_point *)malloc(header.n_vector_record_points * sizeof(cm93_point)); + + pCIB->m_n_point3d_records = header.usn_point3d_records; + pCIB->point3d_descriptor_block = (geometry_descriptor *)malloc( + pCIB->m_n_point3d_records * sizeof(geometry_descriptor)); + + pCIB->p3dpoint_array = + (cm93_point_3d *)malloc(header.m_50 * sizeof(cm93_point_3d)); + + return true; +} + +static bool read_vector_record_table(FILE *stream, int count, + Cell_Info_Block *pCIB) { + bool brv; + + geometry_descriptor *p = pCIB->edge_vector_descriptor_block; + cm93_point *q = pCIB->pvector_record_block_top; + + for (int iedge = 0; iedge < count; iedge++) { + p->index = iedge; + + unsigned short npoints; + brv = !(read_and_decode_ushort(stream, &npoints) == 0); + if (!brv) return false; + + p->n_points = npoints; + p->p_points = q; + + // brv = read_and_decode_bytes(stream, q, p->n_points * + // sizeof(cm93_point)); + // if(!brv) + // return false; + + unsigned short x, y; + for (int index = 0; index < p->n_points; index++) { + if (!read_and_decode_ushort(stream, &x)) return false; + if (!read_and_decode_ushort(stream, &y)) return false; + + q[index].x = x; + q[index].y = y; + } + + // Compute and store the min/max of this block of n_points + cm93_point *t = p->p_points; + + p->x_max = t->x; + p->x_min = t->x; + p->y_max = t->y; + p->y_min = t->y; + + t++; + + for (int j = 0; j < p->n_points - 1; j++) { + if (t->x >= p->x_max) p->x_max = t->x; + + if (t->x <= p->x_min) p->x_min = t->x; + + if (t->y >= p->y_max) p->y_max = t->y; + + if (t->y <= p->y_max) p->y_min = t->y; + + t++; + } + + // Advance the block pointer + q += p->n_points; + + // Advance the geometry descriptor pointer + p++; + } + + return true; +} + +static bool read_3dpoint_table(FILE *stream, int count, Cell_Info_Block *pCIB) { + geometry_descriptor *p = pCIB->point3d_descriptor_block; + cm93_point_3d *q = pCIB->p3dpoint_array; + + for (int i = 0; i < count; i++) { + unsigned short npoints; + if (!read_and_decode_ushort(stream, &npoints)) return false; + + p->n_points = npoints; + p->p_points = (cm93_point *)q; // might not be the right cast + + // unsigned short t = p->n_points; + + // if(!read_and_decode_bytes(stream, q, t*6)) + // return false; + + unsigned short x, y, z; + for (int index = 0; index < p->n_points; index++) { + if (!read_and_decode_ushort(stream, &x)) return false; + if (!read_and_decode_ushort(stream, &y)) return false; + if (!read_and_decode_ushort(stream, &z)) return false; + + q[index].x = x; + q[index].y = y; + q[index].z = z; + } + + p++; + q++; + } + + return true; +} + +static bool read_2dpoint_table(FILE *stream, int count, Cell_Info_Block *pCIB) { + // int rv = read_and_decode_bytes(stream, pCIB->p2dpoint_array, count * + // 4); + + unsigned short x, y; + for (int index = 0; index < count; index++) { + if (!read_and_decode_ushort(stream, &x)) return false; + if (!read_and_decode_ushort(stream, &y)) return false; + + pCIB->p2dpoint_array[index].x = x; + pCIB->p2dpoint_array[index].y = y; + } + + return true; +} + +static bool read_feature_record_table(FILE *stream, int n_features, + Cell_Info_Block *pCIB) { + try { + Object *pobj = pCIB->pobject_block; // head of object array + + vector_record_descriptor *pobject_vector_collection = + pCIB->object_vector_record_descriptor_block; + + Object **p_relob = + pCIB->pprelated_object_block; // head of previously allocated related + // object pointer block + + unsigned char *puc_var10 = pCIB->attribute_block_top; // m_3a; + int puc10count = 0; // should be same as header.m_78 + + unsigned char object_type; + unsigned char geom_prim; + unsigned short obj_desc_bytes = 0; + + unsigned int t; + unsigned short index; + unsigned short n_elements; + + for (int iobject = 0; iobject < n_features; iobject++) { + // read the object definition + read_and_decode_bytes(stream, &object_type, 1); // read the object type + read_and_decode_bytes(stream, &geom_prim, + 1); // read the object geometry primitive type + read_and_decode_ushort(stream, + &obj_desc_bytes); // read the object byte count + + pobj->otype = object_type; + pobj->geotype = geom_prim; + + switch (pobj->geotype & 0x0f) { + case 4: // AREA + { + if (!read_and_decode_ushort(stream, &n_elements)) return false; + + pobj->n_geom_elements = n_elements; + t = (pobj->n_geom_elements * 2) + 2; + obj_desc_bytes -= t; + + pobj->pGeometry = + pobject_vector_collection; // save pointer to created + // vector_record_descriptor in the + // object + + for (unsigned short i = 0; i < pobj->n_geom_elements; i++) { + if (!read_and_decode_ushort(stream, &index)) return false; + + if ((index & 0x1fff) > pCIB->m_nvector_records) + return false; // error in this cell, ignore all of it + + geometry_descriptor *u = &pCIB->edge_vector_descriptor_block[( + index & 0x1fff)]; // point to the vector descriptor + + pobject_vector_collection->pGeom_Description = u; + pobject_vector_collection->segment_usage = + (unsigned char)(index >> 13); + + pobject_vector_collection++; + } + + break; + } // AREA geom + + case 2: // LINE geometry + { + if (!read_and_decode_ushort( + stream, &n_elements)) // read geometry element count + return false; + + pobj->n_geom_elements = n_elements; + t = (pobj->n_geom_elements * 2) + 2; + obj_desc_bytes -= t; + + pobj->pGeometry = + pobject_vector_collection; // save pointer to created + // vector_record_descriptor in the + // object + + for (unsigned short i = 0; i < pobj->n_geom_elements; i++) { + unsigned short geometry_index; + + if (!read_and_decode_ushort(stream, &geometry_index)) return false; + + if ((geometry_index & 0x1fff) > pCIB->m_nvector_records) + // *(int *)(0) = 0; // error + return 0; // error, bad pointer + + geometry_descriptor *u = &pCIB->edge_vector_descriptor_block[( + geometry_index & 0x1fff)]; // point to the vector descriptor + + pobject_vector_collection->pGeom_Description = u; + pobject_vector_collection->segment_usage = + (unsigned char)(geometry_index >> 13); + + pobject_vector_collection++; + } + + break; + } + + case 1: { + if (!read_and_decode_ushort(stream, &index)) return false; + + obj_desc_bytes -= 2; + + pobj->n_geom_elements = 1; // one point + + pobj->pGeometry = &pCIB->p2dpoint_array[index]; // cm93_point * + + break; + } + + case 8: { + if (!read_and_decode_ushort(stream, &index)) return false; + obj_desc_bytes -= 2; + + pobj->n_geom_elements = 1; // one point + + pobj->pGeometry = + &pCIB->point3d_descriptor_block[index]; // geometry_descriptor * + + break; + } + + } // switch + + if ((pobj->geotype & 0x10) == 0x10) // children/related + { + unsigned char nrelated; + if (!read_and_decode_bytes(stream, &nrelated, 1)) return false; + + pobj->n_related_objects = nrelated; + t = (pobj->n_related_objects * 2) + 1; + obj_desc_bytes -= t; + + pobj->p_related_object_pointer_array = p_relob; + p_relob += pobj->n_related_objects; + + Object **w = (Object **)pobj->p_related_object_pointer_array; + for (unsigned char j = 0; j < pobj->n_related_objects; j++) { + if (!read_and_decode_ushort(stream, &index)) return false; + + if (index > pCIB->m_nfeature_records) + // *(int *)(0) = 0; // error + return false; + + Object *prelated_object = &pCIB->pobject_block[index]; + *w = prelated_object; // fwd link + + prelated_object->p_related_object_pointer_array = + pobj; // back link, array of 1 element + w++; + } + } + + if ((pobj->geotype & 0x20) == 0x20) { + unsigned short nrelated; + if (!read_and_decode_ushort(stream, &nrelated)) return false; + + pobj->n_related_objects = (unsigned char)(nrelated & 0xFF); + obj_desc_bytes -= 2; + } + + if ((pobj->geotype & 0x40) == 0x40) { + } + + if ((pobj->geotype & 0x80) == 0x80) // attributes + { + unsigned char nattr; + if (!read_and_decode_bytes(stream, &nattr, 1)) return false; // m_od + + pobj->n_attributes = nattr; + obj_desc_bytes -= 5; + + pobj->attributes_block = puc_var10; + puc_var10 += obj_desc_bytes; + + puc10count += obj_desc_bytes; + + if (!read_and_decode_bytes(stream, pobj->attributes_block, + obj_desc_bytes)) + return false; // the attributes.... + + if ((pobj->geotype & 0x0f) == 1) { + } + } + + pobj++; // next object + } + + // wxASSERT(puc10count == pCIB->m_22->m_78); + } + + catch (...) { + printf("catch on read_feature_record_table\n"); + } + + return true; +} + +bool Ingest_CM93_Cell(const char *cell_file_name, Cell_Info_Block *pCIB) { + try { + int file_length; + + // Get the file length + FILE *flstream = fopen(cell_file_name, "rb"); + if (!flstream) return false; + + fseek(flstream, 0, SEEK_END); + file_length = ftell(flstream); + fclose(flstream); + + // Open the file + FILE *stream = fopen(cell_file_name, "rb"); + if (!stream) return false; + + // Validate the integrity of the cell file + + unsigned short word0 = 0; + ; + int int0 = 0; + int int1 = 0; + ; + + read_and_decode_ushort(stream, + &word0); // length of prolog + header (10 + 128) + read_and_decode_int(stream, &int0); // length of table 1 + read_and_decode_int(stream, &int1); // length of table 2 + + int test = word0 + int0 + int1; + if (test != file_length) { + fclose(stream); + return false; // file is corrupt + } + + // Cell is OK, proceed to ingest + + if (!read_header_and_populate_cib(stream, pCIB)) { + fclose(stream); + return false; + } + + if (!read_vector_record_table(stream, pCIB->m_nvector_records, pCIB)) { + fclose(stream); + return false; + } + + if (!read_3dpoint_table(stream, pCIB->m_n_point3d_records, pCIB)) { + fclose(stream); + return false; + } + + if (!read_2dpoint_table(stream, pCIB->m_n_point2d_records, pCIB)) { + fclose(stream); + return false; + } + + if (!read_feature_record_table(stream, pCIB->m_nfeature_records, pCIB)) { + fclose(stream); + return false; + } + + // int file_end = ftell(stream); + + // wxASSERT(file_end == file_length); + + fclose(stream); + + return true; + } + + catch (...) { + return false; + } +} + +//---------------------------------------------------------------------------------- +// cm93chart Implementation +//---------------------------------------------------------------------------------- + +cm93chart::cm93chart() { + m_ChartType = CHART_TYPE_CM93; + + // Create the decode table once, if needed + if (!cm93_decode_table_created) { + CreateDecodeTable(); + cm93_decode_table_created = true; + } + + m_pDict = NULL; + m_pManager = NULL; + + m_current_cell_vearray_offset = 0; + + m_ncontour_alloc = 100; // allocate inital vertex count container array + m_pcontour_array = (int *)malloc(m_ncontour_alloc * sizeof(int)); + + // Establish a common reference point for the cell + ref_lat = 0.; + ref_lon = 0.; + + // Need a covr_set + m_pcovr_set = new covr_set(this); + + // Make initial allocation of shared outline drawing buffer + m_pDrawBuffer = (wxPoint *)malloc(4 * sizeof(wxPoint)); + m_nDrawBufferSize = 1; + + // Set up the chart context + m_this_chart_context = (chart_context *)calloc(sizeof(chart_context), 1); + m_this_chart_context->chart = this; + m_RAZBuilt = true; +} + +cm93chart::~cm93chart() { + free(m_pcontour_array); + + delete m_pcovr_set; + + free(m_pDrawBuffer); +} + +void cm93chart::Unload_CM93_Cell() { + free(m_CIB.pobject_block); + // free(m_CIB.m_2a); + free(m_CIB.p2dpoint_array); + free(m_CIB.pprelated_object_block); + free(m_CIB.object_vector_record_descriptor_block); + free(m_CIB.attribute_block_top); + free(m_CIB.edge_vector_descriptor_block); + free(m_CIB.pvector_record_block_top); + free(m_CIB.point3d_descriptor_block); + free(m_CIB.p3dpoint_array); +} + +// The idea here is to suggest to upper layers the appropriate scale values +// to be used with this chart If max is too large, performance suffers, and +// the charts are very cluttered onscreen. If the min is too small, then the +// chart rendereding will be over-scaled, and accuracy suffers. In some ways, +// this is subjective..... + +double cm93chart::GetNormalScaleMin(double canvas_scale_factor, + bool b_allow_overzoom) { + switch (GetNativeScale()) { + case 20000000: + return 3000000.; // Z + case 3000000: + return 1000000.; // A + case 1000000: + return 200000.; // B + case 200000: + return 40000.; // C + case 100000: + return 20000.; // D + case 50000: + return 10000.; // E + case 20000: + return 5000.; // F + case 7500: + return 3500.; // G + } + + return 1.0; +} + +double cm93chart::GetNormalScaleMax(double canvas_scale_factor, + int canvas_width) { + /* + XXX previous declaration hides overloaded virtual function + and it was calling: + s57chart::GetNormalScaleMax( canvas_scale_factor, canvas_width ) + should we restore this behavior? + */ + switch (GetNativeScale()) { + case 20000000: + return 50000000.; // Z + case 3000000: + return 6000000.; // A + case 1000000: + return 2000000.; // B + case 200000: + return 400000.; // C + case 100000: + return 200000.; // D + case 50000: + return 100000.; // E + case 20000: + return 40000.; // F + case 7500: + return 15000.; // G + } + + return 1.0e7; +} + +void cm93chart::GetPointPix(ObjRazRules *rzRules, float north, float east, + wxPoint *r) { + wxPoint2DDouble en(east, north); + GetPointPix(rzRules, &en, r, 1); +} + +void cm93chart::GetPointPix(ObjRazRules *rzRules, wxPoint2DDouble *en, + wxPoint *r, int nPoints) { + S57Obj *obj = rzRules->obj; + + double xr = obj->x_rate; + double xo = obj->x_origin; + double yr = obj->y_rate; + double yo = obj->y_origin; + + if (m_vp_current.m_projection_type == PROJECTION_MERCATOR) { + if (m_vp_current.GetBBox().GetMaxLon() >= 180. && + rzRules->obj->BBObj.GetMaxLon() < m_vp_current.GetBBox().GetMinLon()) + xo += mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI; + else if ((m_vp_current.GetBBox().GetMinLon() <= -180. && + rzRules->obj->BBObj.GetMinLon() > + m_vp_current.GetBBox().GetMaxLon()) || + (rzRules->obj->BBObj.GetMaxLon() >= 180 && + m_vp_current.GetBBox().GetMinLon() <= 0.)) + xo -= mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI; + + for (int i = 0; i < nPoints; i++) { + double valx = (en[i].m_x * xr) + xo; + double valy = (en[i].m_y * yr) + yo; + + r[i].x = ((valx - m_easting_vp_center) * m_view_scale_ppm) + + m_pixx_vp_center + 0.5; + r[i].y = m_pixy_vp_center - + ((valy - m_northing_vp_center) * m_view_scale_ppm) + 0.5; + } + } else { + for (int i = 0; i < nPoints; i++) { + double valx = (en[i].m_x * xr) + xo; + double valy = (en[i].m_y * yr) + yo; + + double lat, lon; + fromSM(valx - m_easting_vp_center, valy - m_northing_vp_center, + m_vp_current.clat, m_vp_current.clon, &lat, &lon); + + double rotation = m_vp_current.rotation; + m_vp_current.SetRotationAngle(0); + r[i] = m_vp_current.GetPixFromLL(lat, lon); + m_vp_current.SetRotationAngle(rotation); + } + } +} + +void cm93chart::GetPixPoint(int pixx, int pixy, double *plat, double *plon, + ViewPort *vpt) { +#if 1 + vpt->GetLLFromPix(wxPoint(pixx, pixy), plat, plon); + + // if ( *plon < 0. ) + // *plon += 360.; + +#else + // Use Mercator estimator + int dx = pixx - (vpt->pix_width / 2); + int dy = (vpt->pix_height / 2) - pixy; + + double xp = (dx * cos(vpt->skew)) - (dy * sin(vpt->skew)); + double yp = (dy * cos(vpt->skew)) + (dx * sin(vpt->skew)); + + double d_east = xp / vpt->view_scale_ppm; + double d_north = yp / vpt->view_scale_ppm; + + double slat, slon; + fromSM(d_east, d_north, vpt->clat, vpt->clon, &slat, &slon); + + if (slon > 360.) slon -= 360.; + + *plat = slat; + *plon = slon; +#endif +} + +bool cm93chart::AdjustVP(ViewPort &vp_last, ViewPort &vp_proposed) { + if (IsCacheValid()) { + // If this viewpoint is same scale as last... + if (vp_last.view_scale_ppm == vp_proposed.view_scale_ppm) { + // then require this viewport to be exact integral pixel difference from + // last adjusting clat/clat and SM accordingly +#if 1 + wxPoint2DDouble p = vp_proposed.GetDoublePixFromLL(ref_lat, ref_lon) - + vp_last.GetDoublePixFromLL(ref_lat, ref_lon); + + double xlat, xlon; + vp_last.GetLLFromPix(wxPoint(round(p.m_x), round(p.m_y)), &xlat, &xlon); +#else + double prev_easting_c, prev_northing_c; + toSM(vp_last.clat, vp_last.clon, ref_lat, ref_lon, &prev_easting_c, + &prev_northing_c); + + double easting_c, northing_c; + toSM(vp_proposed.clat, vp_proposed.clon, ref_lat, ref_lon, &easting_c, + &northing_c); + + double delta_pix_x = + (easting_c - prev_easting_c) * vp_proposed.view_scale_ppm; + int dpix_x = (int)round(delta_pix_x); + double dpx = dpix_x; + + double delta_pix_y = + (northing_c - prev_northing_c) * vp_proposed.view_scale_ppm; + int dpix_y = (int)round(delta_pix_y); + double dpy = dpix_y; + + double c_east_d = (dpx / vp_proposed.view_scale_ppm) + prev_easting_c; + double c_north_d = (dpy / vp_proposed.view_scale_ppm) + prev_northing_c; + + double xlat, xlon; + fromSM(c_east_d, c_north_d, ref_lat, ref_lon, &xlat, &xlon); +#endif + vp_proposed.clon = xlon; + vp_proposed.clat = xlat; + + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------- +// Calculate and Set ViewPoint Constants +//----------------------------------------------------------------------- + +void cm93chart::SetVPParms(const ViewPort &vpt) { + if (m_vp_current == vpt) { + return; + } + // Save a copy for later reference + m_vp_current = vpt; + + // Set up local SM rendering constants + m_pixx_vp_center = vpt.pix_width / 2; + m_pixy_vp_center = vpt.pix_height / 2; + m_view_scale_ppm = vpt.view_scale_ppm; + + toSM(vpt.clat, vpt.clon, ref_lat, ref_lon, &m_easting_vp_center, + &m_northing_vp_center); + + vp_transform.easting_vp_center = m_easting_vp_center; + vp_transform.northing_vp_center = m_northing_vp_center; + + if (g_bDebugCM93) { + // Fetch the lat/lon of the screen corner points + ViewPort vptl = vpt; + LLBBox box = vptl.GetBBox(); + double ll_lon = box.GetMinLon(); + double ll_lat = box.GetMinLat(); + + double ur_lon = box.GetMaxLon(); + double ur_lat = box.GetMaxLat(); + + printf( + "cm93chart::SetVPParms ll_lon: %g ll_lat: %g ur_lon: %g ur_lat: " + " %g m_dval: %g\n", + ll_lon, ll_lat, ur_lon, ur_lat, m_dval); + } + + // Create an array of CellIndexes covering the current viewport + std::vector vpcells = GetVPCellArray(vpt); + + // Check the member array to see if all these viewport cells have been + // loaded + bool bcell_is_in; + + for (unsigned int i = 0; i < vpcells.size(); i++) { + bcell_is_in = false; + for (unsigned int j = 0; j < m_cells_loaded_array.size(); j++) { + if (vpcells[i] == m_cells_loaded_array[j]) { + bcell_is_in = true; + break; + } + } + + // The cell is not in place, so go load it + if (!bcell_is_in) { +#ifndef __OCPN__ANDROID__ + AbstractPlatform::ShowBusySpinner(); +#endif + int cell_index = vpcells[i]; + + if (loadcell_in_sequence(cell_index, '0')) // Base cell + { + ProcessVectorEdges(); + CreateObjChain(cell_index, (int)'0', vpt.view_scale_ppm); + + ForceEdgePriorityEvaluate(); // need to re-evaluate priorities + + m_cells_loaded_array.push_back(cell_index); + + Unload_CM93_Cell(); + } + + char loadcell_key = 'A'; // starting + + // Load any subcells in sequence + // On successful load, add it to the member list and process the cell + while (loadcell_in_sequence(cell_index, loadcell_key)) { + ProcessVectorEdges(); + CreateObjChain(cell_index, (int)loadcell_key, vpt.view_scale_ppm); + + ForceEdgePriorityEvaluate(); // need to re-evaluate priorities + + if (std::find(m_cells_loaded_array.begin(), m_cells_loaded_array.end(), + cell_index) == m_cells_loaded_array.end()) + m_cells_loaded_array.push_back(cell_index); + + Unload_CM93_Cell(); + + loadcell_key++; + } + + AssembleLineGeometry(); + + ClearDepthContourArray(); + BuildDepthContourArray(); + + // Set up the chart context + m_this_chart_context->m_pvc_hash = &Get_vc_hash(); + m_this_chart_context->m_pve_hash = &Get_ve_hash(); + + m_this_chart_context->pFloatingATONArray = pFloatingATONArray; + m_this_chart_context->pRigidATONArray = pRigidATONArray; + m_this_chart_context->chart = this; + m_this_chart_context->chart_type = GetChartType(); + + m_this_chart_context->safety_contour = m_next_safe_cnt; + m_this_chart_context->vertex_buffer = GetLineVertexBuffer(); + m_this_chart_context->pt2GetAssociatedObjects = + &s57chart::GetAssociatedObjects; + + // Loop and populate all the objects + for (int i = 0; i < PI_PRIO_NUM; ++i) { + for (int j = 0; j < PI_LUPNAME_NUM; j++) { + ObjRazRules *top = razRules[i][j]; + while (top) { + if (top->obj) top->obj->m_chart_context = m_this_chart_context; + top = top->next; + } + } + } + + AbstractPlatform::HideBusySpinner(); + } + } +} + +std::vector cm93chart::GetVPCellArray(const ViewPort &vpt) { + // Fetch the lat/lon of the screen corner points + ViewPort vptl = vpt; + LLBBox box = vptl.GetBBox(); + double ll_lon = box.GetMinLon(); + double ll_lat = box.GetMinLat(); + + double ur_lon = box.GetMaxLon(); + double ur_lat = box.GetMaxLat(); + + // CLip upper latitude to avoid trying to fetch non-existent cells. + ur_lat = wxMin(ur_lat, 79.99999); + + // CLip upper latitude to avoid trying to fetch non-existent cells above N80. + ur_lat = wxMin(ur_lat, 79.99999); + + // Adjust to always positive for easier cell calculations + if (ll_lon < 0) { + ll_lon += 360; + ur_lon += 360; + } + + // Create an array of CellIndexes covering the current viewport + std::vector vpcells; + + int lower_left_cell = Get_CM93_CellIndex(ll_lat, ll_lon, GetNativeScale()); + vpcells.push_back(lower_left_cell); // always add the lower left cell + + if (g_bDebugCM93) + printf("cm93chart::GetVPCellArray Adding %d\n", lower_left_cell); + + double rlat, rlon; + Get_CM93_Cell_Origin(lower_left_cell, GetNativeScale(), &rlat, &rlon); + + // Use exact integer math here + // It is more obtuse, but it removes dependency on FP rounding policy + + int loni_0 = (int)wxRound(rlon * 3); + int loni_20 = loni_0 + (int)m_dval; // already added the lower left cell + int lati_20 = (int)wxRound(rlat * 3); + + while (lati_20 < (ur_lat * 3.)) { + while (loni_20 < (ur_lon * 3.)) { + unsigned int next_lon = loni_20 + 1080; + while (next_lon >= 1080) next_lon -= 1080; + + unsigned int next_cell = next_lon; + + next_cell += (lati_20 + 270) * 10000; + + vpcells.push_back((int)next_cell); + if (g_bDebugCM93) + printf("cm93chart::GetVPCellArray Adding %d\n", next_cell); + + loni_20 += (int)m_dval; + } + lati_20 += (int)m_dval; + loni_20 = loni_0; + } + + return vpcells; +} + +void cm93chart::ProcessVectorEdges() { + // Create the vector(edge) map for this cell, appending to the existing + // member hash map + auto &vehash = Get_ve_hash(); + + m_current_cell_vearray_offset = + vehash.size(); // keys start at the current size + geometry_descriptor *pgd = m_CIB.edge_vector_descriptor_block; + + for (int iedge = 0; iedge < m_CIB.m_nvector_records; iedge++) { + VE_Element *vep = new VE_Element; + vep->index = iedge + m_current_cell_vearray_offset; + vep->nCount = pgd->n_points; + vep->pPoints = NULL; + vep->max_priority = -99; // Default + + if (pgd->n_points) { + float *pPoints = (float *)malloc(pgd->n_points * 2 * sizeof(float)); + vep->pPoints = pPoints; + + cm93_point *ppt = pgd->p_points; + + // Get a bounding box for the edge + double east_max = -1e7; + double east_min = 1e7; + double north_max = -1e7; + double north_min = 1e7; + + for (int ip = 0; ip < pgd->n_points; ip++) { + *pPoints++ = ppt->x; + *pPoints++ = ppt->y; + + east_max = wxMax(east_max, ppt->x); + east_min = wxMin(east_min, ppt->x); + north_max = wxMax(north_max, ppt->y); + north_min = wxMin(north_min, ppt->y); + + ppt++; + } + + cm93_point p; + double lat1, lon1, lat2, lon2; + + // TODO Not precisely correct, transform should account for + // "trans_WGS84_offset_x" + p.x = east_min; + p.y = north_min; + Transform(&p, 0, 0, &lat1, &lon1); + + p.x = east_max; + p.y = north_max; + Transform(&p, 0, 0, &lat2, &lon2); + + // if(lon1 > lon2) + // lon2 += 360; + + vep->edgeBBox.Set(lat1, lon1, lat2, lon2); + } + + vehash[vep->index] = vep; + + pgd++; // next geometry descriptor + } +} + +int cm93chart::CreateObjChain(int cell_index, int subcell, + double view_scale_ppm) { + LUPrec *LUP; + LUPname LUP_Name = PAPER_CHART; + + m_CIB.m_cell_mcovr_list.Clear(); + + // CALLGRIND_START_INSTRUMENTATION + + Object *pobjectDef = m_CIB.pobject_block; // head of object array + m_CIB.b_have_offsets = false; // will be set if any M_COVRs in this cell have + // defined, non-zero WGS84 offsets + m_CIB.b_have_user_offsets = false; // will be set if any M_COVRs in this cell + // have user defined offsets + + int iObj = 0; + S57Obj *obj; + + double scale = top_frame::Get()->GetBestVPScale(this); + int nativescale = GetNativeScale(); + + while (iObj < m_CIB.m_nfeature_records) { + if ((pobjectDef != NULL)) { + Extended_Geometry *xgeom = BuildGeom(pobjectDef, NULL, iObj); + + obj = NULL; + if (NULL != xgeom) + obj = + CreateS57Obj(cell_index, iObj, subcell, pobjectDef, m_pDict, xgeom, + ref_lat, ref_lon, GetNativeScale(), view_scale_ppm); + + if (obj) { + wxString objnam = obj->GetAttrValueAsString("OBJNAM"); + wxString fe_name = wxString(obj->FeatureName, wxConvUTF8); + if (fe_name == "_texto") objnam = obj->GetAttrValueAsString("_texta"); + if (objnam.Len() > 0) { + wxString cellname = wxString::Format("%i_%i", cell_index, subcell); + SendVectorChartObjectInfo(cellname, fe_name, objnam, obj->m_lat, + obj->m_lon, scale, nativescale); + } + // Build/Maintain the ATON floating/rigid arrays + if (GEO_POINT == obj->Primitive_type) { + // set floating platform + if ((!strncmp(obj->FeatureName, "LITFLT", 6)) || + (!strncmp(obj->FeatureName, "LITVES", 6)) || + (!strncmp(obj->FeatureName, "BOY", 3))) { + pFloatingATONArray->Add(obj); + } + + // set rigid platform + if (!strncmp(obj->FeatureName, "BCN", 3)) pRigidATONArray->Add(obj); + + // Mark the object as an ATON + if ((!strncmp(obj->FeatureName, "LIT", 3)) || + (!strncmp(obj->FeatureName, "LIGHTS", 6)) || + (!strncmp(obj->FeatureName, "BCN", 3)) || + (!strncmp(obj->FeatureName, "_slgto", 6)) || + (!strncmp(obj->FeatureName, "_boygn", 6)) || + (!strncmp(obj->FeatureName, "_bcngn", 6)) || + (!strncmp(obj->FeatureName, "_extgn", 6)) || + (!strncmp(obj->FeatureName, "TOWERS", 6)) || + (!strncmp(obj->FeatureName, "BOY", 3))) { + obj->bIsAton = true; + } + } + + // Mark th object as an "associable depth area" + // Flag is used by conditional symbology + if (GEO_AREA == obj->Primitive_type) { + if (!strncmp(obj->FeatureName, "DEPARE", 6) || + !strncmp(obj->FeatureName, "DRGARE", 6)) + obj->bIsAssociable = true; + } + + // This is where Simplified or Paper-Type point features are + // selected In the case where the chart needs alternate LUPS + // loaded, do so. This case is triggered when the UpdateLUP() + // method has been called on a partially loaded chart. + + switch (obj->Primitive_type) { + case GEO_POINT: + case GEO_META: + case GEO_PRIM: + if (PAPER_CHART == ps52plib->m_nSymbolStyle) + LUP_Name = PAPER_CHART; + else + LUP_Name = SIMPLIFIED; + + if (m_b2pointLUPS) { + LUPname LUPO_Name; + if (PAPER_CHART == ps52plib->m_nSymbolStyle) + LUPO_Name = SIMPLIFIED; + else + LUPO_Name = PAPER_CHART; + + // Load the alternate LUP + LUPrec *LUPO = + ps52plib->S52_LUPLookup(LUPO_Name, obj->FeatureName, obj); + if (LUPO) { + ps52plib->_LUP2rules(LUPO, obj); + _insertRules(obj, LUPO, this); + } + } + break; + + case GEO_LINE: + LUP_Name = LINES; + break; + + case GEO_AREA: + if (PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle) + LUP_Name = PLAIN_BOUNDARIES; + else + LUP_Name = SYMBOLIZED_BOUNDARIES; + + if (m_b2lineLUPS) { + LUPname LUPO_Name; + if (PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle) + LUPO_Name = SYMBOLIZED_BOUNDARIES; + else + LUPO_Name = PLAIN_BOUNDARIES; + + // Load the alternate LUP + LUPrec *LUPO = + ps52plib->S52_LUPLookup(LUPO_Name, obj->FeatureName, obj); + if (LUPO) { + ps52plib->_LUP2rules(LUPO, obj); + _insertRules(obj, LUPO, this); + } + } + break; + } + + LUP = ps52plib->S52_LUPLookup(LUP_Name, obj->FeatureName, obj); + + if (NULL == LUP) { + if (g_bDebugCM93) { + wxString msg(obj->FeatureName, wxConvUTF8); + msg.Prepend(" CM93 could not find LUP for "); + LogMessageOnce(msg); + } + if (0 == obj->nRef) delete obj; + } else { + // Convert LUP to rules set + ps52plib->_LUP2rules(LUP, obj); + + // Add linked object/LUP to the working set + _insertRules(obj, LUP, this); + + // Establish Object's Display Category + obj->m_DisplayCat = LUP->DISC; + + // Establish objects base display priority + obj->m_DPRI = LUP->DPRI - '0'; + + // Populate the chart context + obj->m_chart_context = m_this_chart_context; + } + } + + } + + else // objectdef == NULL + break; + + pobjectDef++; + + iObj++; + } + + // CALLGRIND_STOP_INSTRUMENTATION + + return 1; +} + +InitReturn cm93chart::Init(const wxString &name, ChartInitFlag flags) { + m_FullPath = name; + m_Description = m_FullPath; + + wxFileName fn(name); + + if (!m_prefix.Len()) + m_prefix = fn.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR); + + m_scalechar = fn.GetExt(); + + // Figure out the scale from the file name + + int scale; + switch ((m_scalechar.mb_str())[(size_t)0]) { + case 'Z': + scale = 20000000; + break; + case 'A': + scale = 3000000; + break; + case 'B': + scale = 1000000; + break; + case 'C': + scale = 200000; + break; + case 'D': + scale = 100000; + break; + case 'E': + scale = 50000; + break; + case 'F': + scale = 20000; + break; + case 'G': + scale = 7500; + break; + default: + scale = 20000000; + break; + } + + m_Chart_Scale = scale; + + switch (GetNativeScale()) { + case 20000000: + m_dval = 120; + break; // Z + case 3000000: + m_dval = 60; + break; // A + case 1000000: + m_dval = 30; + break; // B + case 200000: + m_dval = 12; + break; // C + case 100000: + m_dval = 3; + break; // D + case 50000: + m_dval = 1; + break; // E + case 20000: + m_dval = 1; + break; // F + case 7500: + m_dval = 1; + break; // G + default: + m_dval = 1; + break; + } + + // Set the nice name + wxString data = "CM93Chart "; + data.Append(m_scalechar); + wxString s; + s.Printf(" 1/%d", m_Chart_Scale); + data.Append(s); + m_Name = data; + + // Initialize the covr_set + if (scale != 20000000) + m_pcovr_set->Init(m_scalechar.mb_str()[(size_t)0], m_prefix); + + if (flags == THUMB_ONLY) { + // SetColorScheme(cs, false); + + return INIT_OK; + } + + if (!m_pManager) m_pManager = new cm93manager; + + if (flags == HEADER_ONLY) return CreateHeaderDataFromCM93Cell(); + + // Load the cm93 dictionary if necessary + if (!m_pDict) { + if (m_pManager) { + if (m_pManager->Loadcm93Dictionary(name)) + m_pDict = m_pManager->m_pcm93Dict; + else { + wxLogMessage(" CM93Chart Init cannot locate CM93 dictionary."); + return INIT_FAIL_REMOVE; + } + } + } + + bReadyToRender = true; + + return INIT_OK; +} + +Extended_Geometry *cm93chart::BuildGeom(Object *pobject, + wxFileOutputStream *postream, + int iobject) + +{ + wxString s; + int geomtype; + + int geom_type_maybe = pobject->geotype; + + switch (geom_type_maybe) { + case 1: + geomtype = 1; + break; + case 2: + geomtype = 2; + break; + case 4: + geomtype = 3; + break; + case 129: + geomtype = 1; + break; + case 130: + geomtype = 2; + break; + case 132: + geomtype = 3; + break; + case 8: + geomtype = 8; + break; + case 16: + geomtype = 16; + break; + case 161: + geomtype = 1; + break; // lighthouse first child + case 33: + geomtype = 1; + break; + default: + geomtype = -1; + break; + } + + int iseg; + + Extended_Geometry *ret_ptr = new Extended_Geometry; + + int lon_max, lat_max, lon_min, lat_min; + lon_max = 0; + lon_min = 65536; + lat_max = 0; + lat_min = 65536; + + switch (geomtype) { + case 3: // Areas + { + vector_record_descriptor *psegs = + (vector_record_descriptor *)pobject->pGeometry; + + int nsegs = pobject->n_geom_elements; + + ret_ptr->n_vector_indices = nsegs; + ret_ptr->pvector_index = (int *)malloc(nsegs * 3 * sizeof(int)); + + // Traverse the object once to get a maximum polygon vertex count + int n_maxvertex = 0; + for (int i = 0; i < nsegs; i++) { + geometry_descriptor *pgd = + (geometry_descriptor *)(psegs[i].pGeom_Description); + n_maxvertex += pgd->n_points; + } + + // TODO May not need this fluff adder.... + n_maxvertex += 1; // fluff + + wxPoint2DDouble *pPoints = + (wxPoint2DDouble *)calloc((n_maxvertex) * sizeof(wxPoint2DDouble), 1); + + int ip = 1; + int n_prev_vertex_index = 1; + bool bnew_ring = true; + int ncontours = 0; + iseg = 0; + + cm93_point start_point; + start_point.x = 0; + start_point.y = 0; + + cm93_point cur_end_point; + cur_end_point.x = 1; + cur_end_point.y = 1; + + int n_max_points = -1; + while (iseg < nsegs) { + int type_seg = psegs[iseg].segment_usage; + + geometry_descriptor *pgd = + (geometry_descriptor *)(psegs[iseg].pGeom_Description); + + int npoints = pgd->n_points; + cm93_point *rseg = pgd->p_points; + + n_max_points = wxMax(n_max_points, npoints); + + // Establish ring starting conditions + if (bnew_ring) { + bnew_ring = false; + + if ((type_seg & 4) == 0) + start_point = rseg[0]; + else + start_point = rseg[npoints - 1]; + } + + if (((type_seg & 4) == 0)) { + cur_end_point = rseg[npoints - 1]; + for (int j = 0; j < npoints; j++) { + // if(ncontours == 0) // outer + // ring describes envelope + { + lon_max = wxMax(lon_max, rseg[j].x); + lon_min = wxMin(lon_min, rseg[j].x); + lat_max = wxMax(lat_max, rseg[j].y); + lat_min = wxMin(lat_min, rseg[j].y); + } + + pPoints[ip].m_x = rseg[j].x; + pPoints[ip].m_y = rseg[j].y; + ip++; + } + } else if ((type_seg & 4) == 4) // backwards + { + cur_end_point = rseg[0]; + for (int j = npoints - 1; j >= 0; j--) { + // if(ncontours == 0) // outer + // ring describes envelope + { + lon_max = wxMax(lon_max, rseg[j].x); + lon_min = wxMin(lon_min, rseg[j].x); + lat_max = wxMax(lat_max, rseg[j].y); + lat_min = wxMin(lat_min, rseg[j].y); + } + + pPoints[ip].m_x = rseg[j].x; + pPoints[ip].m_y = rseg[j].y; + ip++; + } + } + + ip--; // skip the last point in each segment + + ret_ptr->pvector_index[iseg * 3 + 0] = + 0; //-1; // first connected node + ret_ptr->pvector_index[iseg * 3 + 1] = + pgd->index + m_current_cell_vearray_offset; // edge index + ret_ptr->pvector_index[iseg * 3 + 2] = + 0; //-2; // last connected node + + if ((cur_end_point.x == start_point.x) && + (cur_end_point.y == start_point.y)) { + // done with a ring + + ip++; // leave in ring closure point + + int nRingVertex = ip - n_prev_vertex_index; + + // possibly increase contour array size + if (ncontours > m_ncontour_alloc - 1) { + m_ncontour_alloc *= 2; + int *tmp = m_pcontour_array; + m_pcontour_array = (int *)realloc(m_pcontour_array, + m_ncontour_alloc * sizeof(int)); + if (NULL == tmp) { + free(tmp); + tmp = NULL; + } + } + m_pcontour_array[ncontours] = nRingVertex; // store the vertex count + + bnew_ring = true; // set for next ring + n_prev_vertex_index = ip; + ncontours++; + } + iseg++; + } // while iseg + + ret_ptr->n_max_edge_points = n_max_points; + + ret_ptr->n_contours = + ncontours; // parameters passed to trapezoid tesselator + + if (0 == ncontours) ncontours = 1; // avoid 0 alloc + ret_ptr->contour_array = (int *)malloc(ncontours * sizeof(int)); + memcpy(ret_ptr->contour_array, m_pcontour_array, ncontours * sizeof(int)); + + ret_ptr->vertex_array = pPoints; + ret_ptr->n_max_vertex = n_maxvertex; + + ret_ptr->pogrGeom = NULL; + + ret_ptr->xmin = lon_min; + ret_ptr->xmax = lon_max; + ret_ptr->ymin = lat_min; + ret_ptr->ymax = lat_max; + + break; + } // case 3 + + case 1: // single points + { + cm93_point *pt = (cm93_point *)pobject->pGeometry; + ret_ptr->pogrGeom = NULL; // t; + + ret_ptr->pointx = pt->x; + ret_ptr->pointy = pt->y; + break; + } + + case 2: // LINE geometry + { + vector_record_descriptor *psegs = + (vector_record_descriptor *)pobject->pGeometry; + + int nsegs = pobject->n_geom_elements; + + ret_ptr->n_vector_indices = nsegs; + ret_ptr->pvector_index = (int *)malloc(nsegs * 3 * sizeof(int)); + + // Calculate the number of points + int n_maxvertex = 0; + for (int imseg = 0; imseg < nsegs; imseg++) { + geometry_descriptor *pgd = + (geometry_descriptor *)psegs->pGeom_Description; + + n_maxvertex += pgd->n_points; + psegs++; + } + + wxPoint2DDouble *pPoints = + (wxPoint2DDouble *)malloc(n_maxvertex * sizeof(wxPoint2DDouble)); + + psegs = (vector_record_descriptor *)pobject->pGeometry; + + int ip = 0; + int lon_max, lat_max, lon_min, lat_min; + lon_max = 0; + lon_min = 65536; + lat_max = 0; + lat_min = 65536; + int n_max_points = -1; + + for (int iseg = 0; iseg < nsegs; iseg++) { + int type_seg = psegs->segment_usage; + + geometry_descriptor *pgd = + (geometry_descriptor *)psegs->pGeom_Description; + + psegs++; // next segment + + int npoints = pgd->n_points; + cm93_point *rseg = pgd->p_points; + + n_max_points = wxMax(n_max_points, npoints); + + if (((type_seg & 4) != 4)) { + for (int j = 0; j < npoints; j++) { + lon_max = wxMax(lon_max, rseg[j].x); + lon_min = wxMin(lon_min, rseg[j].x); + lat_max = wxMax(lat_max, rseg[j].y); + lat_min = wxMin(lat_min, rseg[j].y); + + pPoints[ip].m_x = rseg[j].x; + pPoints[ip].m_y = rseg[j].y; + ip++; + } + } + + else if ((type_seg & 4) == 4) // backwards + { + for (int j = npoints - 1; j >= 0; j--) { + lon_max = wxMax(lon_max, rseg[j].x); + lon_min = wxMin(lon_min, rseg[j].x); + lat_max = wxMax(lat_max, rseg[j].y); + lat_min = wxMin(lat_min, rseg[j].y); + + pPoints[ip].m_x = rseg[j].x; + pPoints[ip].m_y = rseg[j].y; + ip++; + } + } + + ret_ptr->pvector_index[iseg * 3 + 0] = + 0; //-1; // first connected node + ret_ptr->pvector_index[iseg * 3 + 1] = + pgd->index + m_current_cell_vearray_offset; // edge index + ret_ptr->pvector_index[iseg * 3 + 2] = + 0; //-2; // last connected node + + } // for + + ret_ptr->n_max_edge_points = n_max_points; + + ret_ptr->vertex_array = pPoints; + ret_ptr->n_max_vertex = n_maxvertex; + + ret_ptr->pogrGeom = NULL; + + ret_ptr->xmin = lon_min; + ret_ptr->xmax = lon_max; + ret_ptr->ymin = lat_min; + ret_ptr->ymax = lat_max; + + break; + } // case 2 (lines) + + case 8: { + geometry_descriptor *pgd = (geometry_descriptor *)pobject->pGeometry; + + int npoints = pgd->n_points; + cm93_point_3d *rseg = (cm93_point_3d *)pgd->p_points; + + OGRMultiPoint *pSMP = new OGRMultiPoint; + + int z; + double zp; + for (int ip = 0; ip < npoints; ip++) { + z = rseg[ip].z; + + // This is a magic number if there ever was one..... + if (z >= 12000) + zp = double(z - 12000); + else + zp = z / 10.; + + OGRPoint *ppoint = new OGRPoint(rseg[ip].x, rseg[ip].y, zp); + pSMP->addGeometryDirectly(ppoint); + + lon_max = wxMax(lon_max, rseg[ip].x); + lon_min = wxMin(lon_min, rseg[ip].x); + lat_max = wxMax(lat_max, rseg[ip].y); + lat_min = wxMin(lat_min, rseg[ip].y); + } + + ret_ptr->pogrGeom = pSMP; + + ret_ptr->xmin = lon_min; + ret_ptr->xmax = lon_max; + ret_ptr->ymin = lat_min; + ret_ptr->ymax = lat_max; + + break; + } + + case 16: + break; // this is the case of objects with children + // the parent has no geometry..... + + default: { + wxPrintf("Unexpected geomtype %d for Feature %d\n", geomtype, iobject); + break; + } + + } // switch + + return ret_ptr; +} + +void cm93chart::Transform(cm93_point *s, double trans_x, double trans_y, + double *lat, double *lon) { + // Simple linear transform + double valx = (s->x * m_CIB.transform_x_rate) + m_CIB.transform_x_origin; + double valy = (s->y * m_CIB.transform_y_rate) + m_CIB.transform_y_origin; + + // Add in the WGS84 offset corrections + valx -= trans_x; + valy -= trans_y; + + // Convert to lat/lon + *lat = + (2.0 * atan(exp(valy / CM93_semimajor_axis_meters)) - PI / 2.) / DEGREE; + *lon = (valx / (DEGREE * CM93_semimajor_axis_meters)); +} + +cm93_attr_block::cm93_attr_block(void *block, cm93_dictionary *pdict) { + m_cptr = 0; + m_block = (unsigned char *)block; + m_pDict = pdict; +} + +unsigned char *cm93_attr_block::GetNextAttr() { + // return current pointer + unsigned char *ret_val = m_block + m_cptr; + + // Advance the pointer + + unsigned char iattr = *(m_block + m_cptr); + m_cptr++; + + // char vtype = m_pDict->m_ValTypeArray[iattr]; + char vtype = m_pDict->GetAttrType(iattr); + + switch (vtype) { + case 'I': // never seen? + m_cptr += 2; + break; + case 'B': + m_cptr += 1; + // pb = (unsigned char *)aval; + // sprintf(val, "%d", *pb); + // pvtype = 'I'; // override + break; + case 'S': + while (*(m_block + m_cptr)) m_cptr++; + m_cptr++; // skip terminator + // sprintf(val, "%s", aval); + break; + case 'R': + m_cptr += 4; + // pf = (float *)aval; + // sprintf(val, "%g", *pf); + break; + case 'W': + m_cptr += 2; + break; + case 'G': + m_cptr += 4; + break; + case 'C': + m_cptr += 3; + while (*(m_block + m_cptr)) m_cptr++; + m_cptr++; // skip terminator + // sprintf(val, "%s", &aval[3]); + // pvtype = 'S'; // override + break; + case 'L': { + unsigned char nl = *(m_block + m_cptr); + m_cptr++; + m_cptr += nl; + + // pb = (unsigned char *)aval; + // unsigned char nl = *pb++; + // char vi[20]; + // val[0] = 0; + // for(int i=0 ; ivalue; + int cur_attr = *pcur_attr; + + wxString lstring; + + switch (cur_attr) { + case 1: + lstring = "4"; + break; // green + case 2: + lstring = "2"; + break; // black + case 3: + lstring = "3"; + break; // red + case 4: + lstring = "6"; + break; // yellow + case 5: + lstring = "1"; + break; // white + case 6: + lstring = "11"; + break; // orange + case 7: + lstring = "2,6"; + break; // black/yellow + case 8: + lstring = "2,6,2"; + break; // black/yellow/black + case 9: + lstring = "6,2"; + break; // yellow/black + case 10: + lstring = "6,2,6"; + break; // yellow/black/yellow + case 11: + lstring = "3,1"; + break; // red/white + case 12: + lstring = "4,3,4"; + break; // green/red/green + case 13: + lstring = "3,4,3"; + break; // red/green/red + case 14: + lstring = "2,3,2"; + break; // black/red/black + case 15: + lstring = "6,3,6"; + break; // yellow/red/yellow + case 16: + lstring = "4,3"; + break; // green/red + case 17: + lstring = "3,4"; + break; // red/green + case 18: + lstring = "4,1"; + break; // green/white + default: + break; + } + + if (lstring.Len()) { + free(pattValTmp->value); // free the old int pointer + + pattValTmp->valType = OGR_STR; + pattValTmp->value = strdup(lstring.mb_str()); + } +} + +S57Obj *cm93chart::CreateS57Obj(int cell_index, int iobject, int subcell, + Object *pobject, cm93_dictionary *pDict, + Extended_Geometry *xgeom, double ref_lat, + double ref_lon, double scale, + double view_scale_ppm) { +#define MAX_HDR_LINE 4000 + + // printf("%d\n", iobject); + + int npub_year = 1993; // silly default + + int iclass = pobject->otype; + int geomtype = pobject->geotype & 0x0f; + + double tmp_transform_x = 0.; + double tmp_transform_y = 0.; + + // Per object transfor offsets, + double trans_WGS84_offset_x = 0.; + double trans_WGS84_offset_y = 0.; + + wxString sclass = pDict->GetClassName(iclass); + if (sclass == "Unknown") { + wxString msg; + msg.Printf(" CM93 Error...object type %d not found in CM93OBJ.DIC", + iclass); + wxLogMessage(msg); + delete xgeom; + return NULL; + } + + wxString sclass_sub = sclass; + + // Going to make some substitutions here + if (sclass.IsSameAs("ITDARE")) sclass_sub = "DEPARE"; + + if (sclass.IsSameAs("_m_sor")) sclass_sub = "M_COVR"; + + if (sclass.IsSameAs("SPOGRD")) sclass_sub = "DMPGRD"; + + if (sclass.IsSameAs("FSHHAV")) sclass_sub = "FSHFAC"; + + if (sclass.IsSameAs("OFSPRD")) sclass_sub = "CTNARE"; + + // Create the S57 Object + S57Obj *pobj = new S57Obj(); + + pobj->Index = iobject; + + char u[201]; + strncpy(u, sclass_sub.mb_str(), 199); + u[200] = '\0'; + memcpy(pobj->FeatureName, u, 7); + + pobj->attVal = new wxArrayOfS57attVal(); + + cm93_attr_block pab(pobject->attributes_block, pDict); + + for (int jattr = 0; jattr < pobject->n_attributes; jattr++) { + unsigned char *curr_attr = pab.GetNextAttr(); + + unsigned char iattr = *curr_attr; + + wxString sattr = pDict->GetAttrName(iattr); + + char vtype = pDict->GetAttrType(iattr); + + unsigned char *aval = curr_attr + 1; + + char val[4000]; + int *pi; + float *pf; + unsigned short *pw; + unsigned char *pb; + int *pAVI; + char *pAVS; + double *pAVR; + double dival; + int ival; + + S57attVal *pattValTmp = new S57attVal; + + switch (vtype) { + case 'I': // never seen? + pi = (int *)aval; + pAVI = (int *)malloc(sizeof(int)); // new int; + *pAVI = *pi; + pattValTmp->valType = OGR_INT; + pattValTmp->value = pAVI; + break; + case 'B': + pb = (unsigned char *)aval; + pAVI = (int *)malloc(sizeof(int)); // new int; + *pAVI = (int)(*pb); + pattValTmp->valType = OGR_INT; + pattValTmp->value = pAVI; + break; + case 'W': // aWORD10 + pw = (unsigned short *)aval; + ival = (int)(*pw); + dival = ival; + + pAVR = (double *)malloc(sizeof(double)); // new double; + *pAVR = dival / 10.; + pattValTmp->valType = OGR_REAL; + pattValTmp->value = pAVR; + break; + case 'G': + pi = (int *)aval; + pAVI = (int *)malloc(sizeof(int)); // new int; + *pAVI = (int)(*pi); + pattValTmp->valType = OGR_INT; + pattValTmp->value = pAVI; + break; + + case 'S': + pAVS = strdup((char *)aval); + pattValTmp->valType = OGR_STR; + pattValTmp->value = pAVS; + break; + + case 'C': + pAVS = strdup((const char *)&aval[3]); + pattValTmp->valType = OGR_STR; + pattValTmp->value = pAVS; + break; + case 'L': { + pb = (unsigned char *)aval; + unsigned char nl = *pb++; + char vi[20]; + val[0] = 0; + for (int i = 0; i < nl; i++) { + sprintf(vi, "%d,", *pb++); + strcat(val, vi); + } + if (strlen(val)) val[strlen(val) - 1] = 0; // strip last "," + + pAVS = strdup(val); + pattValTmp->valType = OGR_STR; + pattValTmp->value = pAVS; + break; + } + case 'R': { + pAVR = (double *)malloc(sizeof(double)); // new double; + pf = (float *)aval; +#ifdef __ARM_ARCH + { + float __attribute__((aligned(16))) tf1; + unsigned char *pucf = (unsigned char *)pf; + + memcpy(&tf1, pucf, sizeof(float)); + *pAVR = tf1; + } +#else + *pAVR = *pf; +#endif + pattValTmp->valType = OGR_REAL; + pattValTmp->value = pAVR; + break; + } + default: + sattr.Clear(); // Unknown, TODO track occasional case '?' + break; + } // switch + + if (sattr.IsSameAs("COLMAR")) { + translate_colmar(sclass, pattValTmp); + sattr = "COLOUR"; + } + // XXX should be done from s57 list ans cm93 list for any mismatch + // ie cm93 QUASOU is an enum s57 is a list + if (pattValTmp->valType == OGR_INT && + (sattr.IsSameAs("QUASOU") || sattr.IsSameAs("CATLIT"))) { + int v = *(int *)pattValTmp->value; + free(pattValTmp->value); + sprintf(val, "%d", v); + pAVS = strdup(val); + pattValTmp->valType = OGR_STR; + pattValTmp->value = pAVS; + } + + // Do CM93 $SCODE attribute substitutions + if (sclass.IsSameAs("$AREAS") && (vtype == 'S') && + sattr.IsSameAs("$SCODE")) { + if (!strcmp((char *)pattValTmp->value, "II25")) { + free(pattValTmp->value); + pattValTmp->value = strdup("BACKGROUND"); + } + } + + // Capture some attributes on the fly as needed + if (sattr.IsSameAs("RECDAT") || sattr.IsSameAs("_dgdat")) { + if (sclass_sub.IsSameAs("M_COVR") && (vtype == 'S')) { + wxString pub_date((char *)pattValTmp->value, wxConvUTF8); + + wxDateTime upd; + upd.ParseFormat(pub_date, "%Y%m%d"); + if (!upd.IsValid()) upd.ParseFormat("20000101", "%Y%m%d"); + m_EdDate = upd; + + pub_date.Truncate(4); + + long nyear = 0; + pub_date.ToLong(&nyear); + npub_year = nyear; + } + } + + // Capture the potential WGS84 transform offset for later use + if (sclass_sub.IsSameAs("M_COVR") && (vtype == 'R')) { + if (sattr.IsSameAs("_wgsox")) { + tmp_transform_x = *(double *)pattValTmp->value; + if (fabs(tmp_transform_x) > 1.0) // metres + m_CIB.b_have_offsets = true; + } else if (sattr.IsSameAs("_wgsoy")) { + tmp_transform_y = *(double *)pattValTmp->value; + if (fabs(tmp_transform_y) > 1.0) m_CIB.b_have_offsets = true; + } + } + + if (sattr.Len()) { + wxASSERT(sattr.Len() == 6); + wxCharBuffer dbuffer = sattr.ToUTF8(); + if (dbuffer.data()) { + pobj->att_array = + (char *)realloc(pobj->att_array, 6 * (pobj->n_attr + 1)); + + strncpy(pobj->att_array + (6 * sizeof(char) * pobj->n_attr), + dbuffer.data(), 6); + pobj->n_attr++; + + pobj->attVal->Add(pattValTmp); + } else + delete pattValTmp; + } else + delete pattValTmp; + + } // for + + // ATON label optimization: + // Some CM93 ATON objects do not contain OBJNAM attribute, which means that + // no label is shown for these objects when ATON labals are requested Look + // for these cases, and change the INFORM attribute label to OBJNAM, if + // present. + + if (1 == geomtype) { + if ((!strncmp(pobj->FeatureName, "LIT", 3)) || + (!strncmp(pobj->FeatureName, "LIGHTS", 6)) || + (!strncmp(pobj->FeatureName, "BCN", 3)) || + (!strncmp(pobj->FeatureName, "_slgto", 6)) || + (!strncmp(pobj->FeatureName, "_boygn", 6)) || + (!strncmp(pobj->FeatureName, "_bcngn", 6)) || + (!strncmp(pobj->FeatureName, "_extgn", 6)) || + (!strncmp(pobj->FeatureName, "TOWERS", 6)) || + (!strncmp(pobj->FeatureName, "BOY", 3))) { + bool bfound_OBJNAM = (pobj->GetAttributeIndex("OBJNAM") != -1); + bool bfound_INFORM = (pobj->GetAttributeIndex("INFORM") != -1); + + if ((!bfound_OBJNAM) && (bfound_INFORM)) // can make substitution + { + char *patl = pobj->att_array; + for (int i = 0; i < pobj->n_attr; i++) { // find "INFORM" + if (!strncmp(patl, "INFORM", 6)) { + memcpy(patl, "OBJNAM", 6); // change to "OBJNAM" + break; + } + + patl += 6; + } + } + } + } + + switch (geomtype) { + case 4: { + pobj->Primitive_type = GEO_AREA; + + // Check for and maintain the class array of M_COVR objects + if (sclass_sub.IsSameAs("M_COVR")) { + M_COVR_Desc *pmcd; + + M_COVR_Desc *pmcd_look = + GetCoverSet()->Find_MCD(cell_index, iobject, subcell); + if (NULL == pmcd_look) // not found + { + double lat, lon; + + pmcd = new M_COVR_Desc; + + // Record unique identifiers for this M_COVR object + pmcd->m_cell_index = cell_index; + pmcd->m_object_id = iobject; + pmcd->m_subcell = subcell; + + // User offsets start empty + pmcd->user_xoff = 0; + pmcd->user_yoff = 0; + pmcd->m_buser_offsets = false; + + // Record the Publication Year of this cell + pmcd->m_npub_year = npub_year; + + // Get number of exterior ring points(vertices) + int npta = xgeom->contour_array[0]; + float_2Dpt *geoPt = new float_2Dpt[npta + 2]; // vertex array + float_2Dpt *ppt = geoPt; + + pmcd->m_covr_lon_max = -1000.; + pmcd->m_covr_lon_min = 1000.; + pmcd->m_covr_lat_max = -1000.; + pmcd->m_covr_lat_min = 1000.; + + // Transcribe exterior ring points to vertex array, in Lat/Lon + // coordinates + for (int ip = 0; ip < npta; ip++) { + cm93_point p; + p.x = (int)xgeom->vertex_array[ip + 1].m_x; + p.y = (int)xgeom->vertex_array[ip + 1].m_y; + + Transform(&p, 0, 0, /*tmp_transform_x, tmp_transform_y,*/ &lat, + &lon); + ppt->x = lon; + ppt->y = lat; + + pmcd->m_covr_lon_max = wxMax(pmcd->m_covr_lon_max, lon); + pmcd->m_covr_lon_min = wxMin(pmcd->m_covr_lon_min, lon); + pmcd->m_covr_lat_max = wxMax(pmcd->m_covr_lat_max, lat); + pmcd->m_covr_lat_min = wxMin(pmcd->m_covr_lat_min, lat); + + ppt++; + } + pmcd->m_nvertices = npta; + pmcd->pvertices = geoPt; + + pmcd->m_covr_bbox.Set(pmcd->m_covr_lat_min, pmcd->m_covr_lon_min, + pmcd->m_covr_lat_max, pmcd->m_covr_lon_max); + + // Capture and store the potential WGS transform offsets grabbed + // during attribute decode + pmcd->transform_WGS84_offset_x = tmp_transform_x; + pmcd->transform_WGS84_offset_y = tmp_transform_y; + + pmcd->m_centerlat_cos = cos( + ((pmcd->m_covr_lat_min + pmcd->m_covr_lat_max) / 2.) * PI / 180.); + + // Add this MCD to the persistent class covr_set + GetCoverSet()->Add_Update_MCD(pmcd); + + } else { + // If already in the coverset, are there user offsets applied to this + // MCD? + if (pmcd_look->m_buser_offsets) { + m_CIB.b_have_user_offsets = true; + + m_CIB.user_xoff = pmcd_look->user_xoff; + m_CIB.user_yoff = pmcd_look->user_yoff; + } + + pmcd = pmcd_look; + } + + // Add this geometry to the currently loaded class M_COVR array + m_pcovr_array_loaded.Add(pmcd); + + // Update the covr region + unsigned int n = pmcd->m_nvertices; + double *pts = new double[2 * n]; + + // copy into array of doubles + for (size_t i = 0; i < 2 * n; i++) + pts[i] = ((float *)pmcd->pvertices)[i]; + + // normalize to 0-360 coords for areas that cross 180 (will be adjusted + // in LLRegion) + if (LLRegion::PointsCCW(n, pts)) + for (size_t i = 0; i < n; i++) + if (pts[2 * i + 1] < 0) pts[2 * i + 1] += 360; + + // perform region union logic + LLRegion rgn_covr(n, pts); + m_region.Union(rgn_covr); + delete[] pts; + + // Add the MCD it to the current (temporary) per cell list + // This array is used only to quickly find the M_COVR object + // parameters which apply to other objects loaded from this cell. We + // do this so we don't have to search the entire (worldwide) coverset + // for this chart scale + m_CIB.m_cell_mcovr_list.Append(pmcd); + } + + // Declare x/y of the object to be average of all cm93points + pobj->x = (xgeom->xmin + xgeom->xmax) / 2.; + pobj->y = (xgeom->ymin + xgeom->ymax) / 2.; + + // associate the vector(edge) index table + pobj->m_n_lsindex = xgeom->n_vector_indices; + pobj->m_lsindex_array = + xgeom->pvector_index; // object now owns the array + pobj->m_n_edge_max_points = 0; // xgeom->n_max_edge_points; + + // Find the proper WGS offset for this object + if (m_CIB.b_have_offsets || m_CIB.b_have_user_offsets) { + double latc, lonc; + cm93_point pc; + pc.x = (short unsigned int)pobj->x; + pc.y = (short unsigned int)pobj->y; + Transform(&pc, 0., 0., &latc, &lonc); + + M_COVR_Desc *pmcd = FindM_COVR_InWorkingSet(latc, lonc); + if (pmcd) { + trans_WGS84_offset_x = pmcd->user_xoff; + trans_WGS84_offset_y = pmcd->user_yoff; + } + } + + // Set the s57obj bounding box as lat/lon + double lat1, lon1, lat2, lon2; + cm93_point p; + + p.x = (int)xgeom->xmin; + p.y = (int)xgeom->ymin; + Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1); + xgeom->ref_lat = lat1; + xgeom->ref_lon = lon1; + + p.x = (int)xgeom->xmax; + p.y = (int)xgeom->ymax; + Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat2, &lon2); + pobj->BBObj.Set(lat1, lon1, lat2, lon2); + + // Set the object base point + p.x = (int)pobj->x; + p.y = (int)pobj->y; + Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1); + pobj->m_lon = lon1; + pobj->m_lat = lat1; + + if (1) { + // This will be a deferred tesselation..... + + // Set up the conversion factors for use in the tesselator + xgeom->x_rate = m_CIB.transform_x_rate; + xgeom->x_offset = m_CIB.transform_x_origin - trans_WGS84_offset_x; + xgeom->y_rate = m_CIB.transform_y_rate; + xgeom->y_offset = m_CIB.transform_y_origin - trans_WGS84_offset_y; + + pobj->pPolyTessGeo = new PolyTessGeo(xgeom); + } + + break; + } + + case 1: { + pobj->Primitive_type = GEO_POINT; + pobj->npt = 1; + + pobj->x = xgeom->pointx; + pobj->y = xgeom->pointy; + + double lat, lon; + cm93_point p; + p.x = xgeom->pointx; + p.y = xgeom->pointy; + Transform(&p, 0., 0., &lat, &lon); + + // Find the proper WGS offset for this object + if (m_CIB.b_have_offsets || m_CIB.b_have_user_offsets) { + M_COVR_Desc *pmcd = FindM_COVR_InWorkingSet(lat, lon); + if (pmcd) { + trans_WGS84_offset_x = pmcd->user_xoff; + trans_WGS84_offset_y = pmcd->user_yoff; + } + } + + // Transform again to pick up offsets + Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat, &lon); + + pobj->m_lat = lat; + pobj->m_lon = lon; + + // make initial bounding box large enough for worst possible case + // it's not possible to know unless we knew the font, but this works + // except for huge font sizes + // this is not very good or accurate or efficient and hopefully we can + // replace the current bounding box logic with calculating logic + double llsize = 1e-3 / view_scale_ppm; + + pobj->BBObj.Set(lat, lon, lat, lon); + pobj->BBObj.EnLarge(llsize); + + break; + } + + case 8: // wkbMultiPoint25D: + { + pobj->Primitive_type = GEO_POINT; + + // Set the s57obj bounding box as lat/lon + double lat1, lon1, lat2, lon2; + cm93_point p; + + p.x = (int)xgeom->xmin; + p.y = (int)xgeom->ymin; + Transform(&p, 0., 0., &lat1, &lon1); + + p.x = (int)xgeom->xmax; + p.y = (int)xgeom->ymax; + Transform(&p, 0., 0., &lat2, &lon2); + pobj->BBObj.Set(lat1, lon1, lat2, lon2); + + // and declare x/y of the object to be average of all cm93points + pobj->x = (xgeom->xmin + xgeom->xmax) / 2.; + pobj->y = (xgeom->ymin + xgeom->ymax) / 2.; + + OGRMultiPoint *pGeo = (OGRMultiPoint *)xgeom->pogrGeom; + pobj->npt = pGeo->getNumGeometries(); + + pobj->geoPtz = (double *)malloc(pobj->npt * 3 * sizeof(double)); + pobj->geoPtMulti = (double *)malloc(pobj->npt * 2 * sizeof(double)); + + double *pdd = pobj->geoPtz; + double *pdl = pobj->geoPtMulti; + + for (int ip = 0; ip < pobj->npt; ip++) { + OGRPoint *ppt = (OGRPoint *)(pGeo->getGeometryRef(ip)); + + cm93_point p; + p.x = (int)ppt->getX(); + p.y = (int)ppt->getY(); + double depth = ppt->getZ(); + + double east = p.x; + double north = p.y; + + double snd_trans_x = 0.; + double snd_trans_y = 0.; + + // Find the proper offset for this individual sounding + if (m_CIB.b_have_user_offsets) { + double lats, lons; + Transform(&p, 0., 0., &lats, &lons); + + M_COVR_Desc *pmcd = FindM_COVR_InWorkingSet(lats, lons); + if (pmcd) { + // For lat/lon calculation below + snd_trans_x = pmcd->user_xoff; + snd_trans_y = pmcd->user_yoff; + + // Actual cm93 point of this sounding, back-converted from metres + // e/n + east -= pmcd->user_xoff / m_CIB.transform_x_rate; + north -= pmcd->user_yoff / m_CIB.transform_y_rate; + } + } + + *pdd++ = east; + *pdd++ = north; + *pdd++ = depth; + + // Save offset lat/lon of point in obj->geoPtMulti for later use in + // decomposed bboxes + Transform(&p, snd_trans_x, snd_trans_y, &lat1, &lon1); + *pdl++ = lon1; + *pdl++ = lat1; + } + + // Set the object base point + p.x = (int)pobj->x; + p.y = (int)pobj->y; + Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1); + pobj->m_lon = lon1; + pobj->m_lat = lat1; + + delete pGeo; + + break; + } // case 8 + + case 2: { + pobj->Primitive_type = GEO_LINE; + + pobj->npt = xgeom->n_max_vertex; + pobj->geoPt = (pt *)xgeom->vertex_array; + xgeom->vertex_array = NULL; // object now owns the array + + // Declare x/y of the object to be average of all cm93points + pobj->x = (xgeom->xmin + xgeom->xmax) / 2.; + pobj->y = (xgeom->ymin + xgeom->ymax) / 2.; + + // associate the vector(edge) index table + pobj->m_n_lsindex = xgeom->n_vector_indices; + pobj->m_lsindex_array = + xgeom->pvector_index; // object now owns the array + pobj->m_n_edge_max_points = 0; // xgeom->n_max_edge_points; + + // Find the proper WGS offset for this object + if (m_CIB.b_have_offsets || m_CIB.b_have_user_offsets) { + double latc, lonc; + cm93_point pc; + pc.x = (short unsigned int)pobj->x; + pc.y = (short unsigned int)pobj->y; + Transform(&pc, 0., 0., &latc, &lonc); + + M_COVR_Desc *pmcd = FindM_COVR_InWorkingSet(latc, lonc); + if (pmcd) { + trans_WGS84_offset_x = pmcd->user_xoff; + trans_WGS84_offset_y = pmcd->user_yoff; + } + } + + // Set the s57obj bounding box as lat/lon + double lat1, lon1, lat2, lon2; + cm93_point p; + + p.x = (int)xgeom->xmin; + p.y = (int)xgeom->ymin; + Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1); + + p.x = (int)xgeom->xmax; + p.y = (int)xgeom->ymax; + Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat2, &lon2); + pobj->BBObj.Set(lat1, lon1, lat2, lon2); + + // Set the object base point + p.x = (int)pobj->x; + p.y = (int)pobj->y; + Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1); + pobj->m_lon = lon1; + pobj->m_lat = lat1; + + break; + + } // case 2 + default: { + // TODO GEO_PRIM here is a placeholder. Trace this code.... + pobj->Primitive_type = GEO_PRIM; + break; + } + + } // geomtype switch + + // Is this a catagory-movable object? + if (!strncmp(pobj->FeatureName, "OBSTRN", 6) || + !strncmp(pobj->FeatureName, "WRECKS", 6) || + !strncmp(pobj->FeatureName, "DEPCNT", 6) || + !strncmp(pobj->FeatureName, "UWTROC", 6)) { + pobj->m_bcategory_mutable = true; + } else { + pobj->m_bcategory_mutable = false; + } + + // Build/Maintain a list of found OBJL types for later use + // And back-reference the appropriate list index in S57Obj for Display + // Filtering + + pobj->iOBJL = -1; // deferred, done by OBJL filtering in the PLIB as needed + + // Everything in Xgeom that is needed later has been given to the object + // So, the xgeom object can be deleted + // Except for area features, which will get deferred tesselation, and so need + // the Extended geometry point Those features will own the xgeom... + if (geomtype != 4) delete xgeom; + + // Set the per-object transform coefficients + pobj->x_rate = + m_CIB.transform_x_rate * + (mercator_k0 * WGS84_semimajor_axis_meters / CM93_semimajor_axis_meters); + pobj->y_rate = + m_CIB.transform_y_rate * + (mercator_k0 * WGS84_semimajor_axis_meters / CM93_semimajor_axis_meters); + pobj->x_origin = + m_CIB.transform_x_origin * + (mercator_k0 * WGS84_semimajor_axis_meters / CM93_semimajor_axis_meters); + pobj->y_origin = + m_CIB.transform_y_origin * + (mercator_k0 * WGS84_semimajor_axis_meters / CM93_semimajor_axis_meters); + + // Add in the possible offsets to WGS84 which come from the proper M_COVR + // containing this feature + pobj->x_origin -= trans_WGS84_offset_x; + pobj->y_origin -= trans_WGS84_offset_y; + + // Mark the object chart type, for the convenience of S52PLIB + pobj->auxParm3 = CHART_TYPE_CM93; + + return pobj; +} + +// Find the proper M_COVR record within this current cell for this lat/lon +M_COVR_Desc *cm93chart::FindM_COVR_InWorkingSet(double lat, double lon) { + M_COVR_Desc *ret = NULL; + // Default is to use the first M_COVR, the usual case + if (m_CIB.m_cell_mcovr_list.GetCount() == 1) { + wxList_Of_M_COVR_DescNode *node0 = m_CIB.m_cell_mcovr_list.GetFirst(); + if (node0) ret = node0->GetData(); + } else { + wxList_Of_M_COVR_DescNode *node = m_CIB.m_cell_mcovr_list.GetFirst(); + while (node) { + M_COVR_Desc *pmcd = node->GetData(); + + if (G_PtInPolygon_FL(pmcd->pvertices, pmcd->m_nvertices, lon, lat)) { + ret = pmcd; + break; + } + + node = node->GetNext(); + } + } + return ret; +} + +// Find the proper M_COVR record within this current cell for this lat/lon +// And return the WGS84 offsets contained within +wxPoint2DDouble cm93chart::FindM_COVROffset(double lat, double lon) { + wxPoint2DDouble ret(0., 0.); + + // Default is to use the first M_COVR, the usual case + wxList_Of_M_COVR_DescNode *node0 = m_CIB.m_cell_mcovr_list.GetFirst(); + if (node0) { + M_COVR_Desc *pmcd0 = node0->GetData(); + ret.m_x = pmcd0->transform_WGS84_offset_x; + ret.m_y = pmcd0->transform_WGS84_offset_y; + } + + // If there are more than one M_COVR in this cell, need to search + if (m_CIB.m_cell_mcovr_list.GetCount() > 1) { + wxList_Of_M_COVR_DescNode *node = m_CIB.m_cell_mcovr_list.GetFirst(); + while (node) { + M_COVR_Desc *pmcd = node->GetData(); + + if (G_PtInPolygon_FL(pmcd->pvertices, pmcd->m_nvertices, lon, lat)) { + ret.m_x = pmcd->transform_WGS84_offset_x; + ret.m_y = pmcd->transform_WGS84_offset_y; + break; + } + + node = node->GetNext(); + } + } + return ret; +} + +// Read the cm93 cell file header and create required Chartbase data +// structures +InitReturn cm93chart::CreateHeaderDataFromCM93Cell() { + // Figure out the scale from the file name + wxFileName fn(m_FullPath); + wxString ext = fn.GetExt(); + + int scale; + switch ((ext.mb_str())[(size_t)0]) { + case 'Z': + scale = 20000000; + break; + case 'A': + scale = 3000000; + break; + case 'B': + scale = 1000000; + break; + case 'C': + scale = 200000; + break; + case 'D': + scale = 100000; + break; + case 'E': + scale = 50000; + break; + case 'F': + scale = 20000; + break; + case 'G': + scale = 7500; + break; + default: + scale = 20000000; + break; + } + + m_Chart_Scale = scale; + + // Check with the manager to see if a chart of this scale has been + // processed If there is no manager, punt and open the chart + if (m_pManager) { + bool bproc = false; + switch (m_Chart_Scale) { + case 20000000: + bproc = m_pManager->m_bfoundZ; + break; + case 3000000: + bproc = m_pManager->m_bfoundA; + break; + case 1000000: + bproc = m_pManager->m_bfoundB; + break; + case 200000: + bproc = m_pManager->m_bfoundC; + break; + case 100000: + bproc = m_pManager->m_bfoundD; + break; + case 50000: + bproc = m_pManager->m_bfoundE; + break; + case 20000: + bproc = m_pManager->m_bfoundF; + break; + case 7500: + bproc = m_pManager->m_bfoundG; + break; + } + + if (bproc) return INIT_FAIL_NOERROR; + + // Inform the manager that a chart of this scale has been processed + switch (m_Chart_Scale) { + case 20000000: + m_pManager->m_bfoundZ = true; + break; + case 3000000: + m_pManager->m_bfoundA = true; + break; + case 1000000: + m_pManager->m_bfoundB = true; + break; + case 200000: + m_pManager->m_bfoundC = true; + break; + case 100000: + m_pManager->m_bfoundD = true; + break; + case 50000: + m_pManager->m_bfoundE = true; + break; + case 20000: + m_pManager->m_bfoundF = true; + break; + case 7500: + m_pManager->m_bfoundG = true; + break; + } + } + + // Specify the whole world as chart coverage + m_FullExtent.ELON = 179.0; + m_FullExtent.WLON = -179.0; + m_FullExtent.NLAT = 80.0; + m_FullExtent.SLAT = -80.0; + m_bExtentSet = true; + + // Populate one (huge) M_COVR Entry + m_nCOVREntries = 1; + m_pCOVRTablePoints = (int *)malloc(sizeof(int)); + *m_pCOVRTablePoints = 4; + m_pCOVRTable = (float **)malloc(sizeof(float *)); + float *pf = (float *)malloc(2 * 4 * sizeof(float)); + *m_pCOVRTable = pf; + float *pfe = pf; + + *pfe++ = m_FullExtent.NLAT; // LatMax; + *pfe++ = m_FullExtent.WLON; // LonMin; + + *pfe++ = m_FullExtent.NLAT; // LatMax; + *pfe++ = m_FullExtent.ELON; // LonMax; + + *pfe++ = m_FullExtent.SLAT; // LatMin; + *pfe++ = m_FullExtent.ELON; // LonMax; + + *pfe++ = m_FullExtent.SLAT; // LatMin; + *pfe++ = m_FullExtent.WLON; // LonMin; + + return INIT_OK; +} + +void cm93chart::ProcessMCOVRObjects(int cell_index, char subcell) { + // Extract the m_covr structures inline + + Object *pobject = m_CIB.pobject_block; // head of object array + + int iObj = 0; + while (iObj < m_CIB.m_nfeature_records) { + if ((pobject != NULL)) { + // Look for and process m_covr object(s) + int iclass = pobject->otype; + + wxString sclass = m_pDict->GetClassName(iclass); + + if (sclass.IsSameAs("_m_sor")) { + M_COVR_Desc *pmcd = + m_pcovr_set->Find_MCD(cell_index, iObj, (int)subcell); + if (NULL == pmcd) { + Extended_Geometry *xgeom = BuildGeom(pobject, NULL, iObj); + + // Decode the attributes, specifically looking for _wgsox, _wgsoy + + double tmp_transform_x = 0.; + double tmp_transform_y = 0.; + + cm93_attr_block pab(pobject->attributes_block, m_pDict); + for (int jattr = 0; jattr < pobject->n_attributes; jattr++) { + unsigned char *curr_attr = pab.GetNextAttr(); + unsigned char iattr = *curr_attr; + wxString sattr = m_pDict->GetAttrName(iattr); + char vtype = m_pDict->GetAttrType(iattr); + unsigned char *aval = curr_attr + 1; + + if (vtype == 'R') { + float *pf = (float *)aval; +#ifdef __ARM_ARCH + float __attribute__((aligned(16))) tf1; + unsigned char *pucf = (unsigned char *)pf; + memcpy(&tf1, pucf, sizeof(float)); + if (sattr.IsSameAs("_wgsox")) + tmp_transform_x = tf1; + else if (sattr.IsSameAs("_wgsoy")) + tmp_transform_y = tf1; +#else + if (sattr.IsSameAs("_wgsox")) + tmp_transform_x = *pf; + else if (sattr.IsSameAs("_wgsoy")) + tmp_transform_y = *pf; +#endif + } + + } // for all attributes + + if (NULL != xgeom) { + double lat, lon; + + pmcd = new M_COVR_Desc; + + // Record unique identifiers for this M_COVR object + pmcd->m_cell_index = cell_index; + pmcd->m_object_id = iObj; + pmcd->m_subcell = (int)subcell; + + // Get number of exterior ring points(vertices) + int npta = xgeom->contour_array[0]; + float_2Dpt *geoPt = new float_2Dpt[npta + 2]; // vertex array + float_2Dpt *ppt = geoPt; + + // Transcribe exterior ring points to vertex array, in Lat/Lon + // coordinates + pmcd->m_covr_lon_max = -1000.; + pmcd->m_covr_lon_min = 1000.; + pmcd->m_covr_lat_max = -1000.; + pmcd->m_covr_lat_min = 1000.; + + for (int ip = 0; ip < npta; ip++) { + cm93_point p; + p.x = (int)xgeom->vertex_array[ip + 1].m_x; + p.y = (int)xgeom->vertex_array[ip + 1].m_y; + + Transform(&p, 0., 0., &lat, &lon); + ppt->x = lon; + ppt->y = lat; + + pmcd->m_covr_lon_max = wxMax(pmcd->m_covr_lon_max, lon); + pmcd->m_covr_lon_min = wxMin(pmcd->m_covr_lon_min, lon); + pmcd->m_covr_lat_max = wxMax(pmcd->m_covr_lat_max, lat); + pmcd->m_covr_lat_min = wxMin(pmcd->m_covr_lat_min, lat); + + ppt++; + } + pmcd->m_nvertices = npta; + pmcd->pvertices = geoPt; + + pmcd->m_covr_bbox.Set(pmcd->m_covr_lat_min, pmcd->m_covr_lon_min, + pmcd->m_covr_lat_max, pmcd->m_covr_lon_max); + + // Capture and store the potential WGS transform offsets grabbed + // during attribute decode + pmcd->transform_WGS84_offset_x = tmp_transform_x; + pmcd->transform_WGS84_offset_y = tmp_transform_y; + + pmcd->m_centerlat_cos = + cos(((pmcd->m_covr_lat_min + pmcd->m_covr_lat_max) / 2.) * PI / + 180.); + + // Add this object to the covr_set + m_pcovr_set->Add_Update_MCD(pmcd); + + // Clean up the xgeom + free(xgeom->pvector_index); + + delete xgeom; + } + } + } + } + + else // objectdef == NULL + break; + + pobject++; + iObj++; + } +} + +bool cm93chart::UpdateCovrSet(ViewPort *vpt) { + // Create an array of CellIndexes covering the current viewport + std::vector vpcells = GetVPCellArray(*vpt); + + // Check the member covr_set to see if all these viewport cells have had + // their m_covr loaded + + for (unsigned int i = 0; i < vpcells.size(); i++) { + // If the cell is not already in the master coverset, go load enough of + // it to get the offsets and outlines..... + if (!m_pcovr_set->IsCovrLoaded(vpcells[i])) { + if (loadcell_in_sequence(vpcells[i], '0')) { + ProcessMCOVRObjects(vpcells[i], '0'); + Unload_CM93_Cell(); // all done with this (sub)cell + } + m_pcovr_set->m_cell_hash[vpcells[i]] = 1; + + char loadcell_key = 'A'; // starting subcells + + // Load the subcells in sequence + // On successful load, add it to the covr set and process the cell + while (loadcell_in_sequence(vpcells[i], loadcell_key)) { + // Extract the m_covr structures inline + + ProcessMCOVRObjects(vpcells[i], loadcell_key); + + Unload_CM93_Cell(); // all done with this (sub)cell + + loadcell_key++; + + } // while + } // cell is not in + } // for cellindex array + + return true; +} + +bool cm93chart::IsPointInLoadedM_COVR(double xc, double yc) { + // Provisionally revert to older method pending investigation. +#if 1 + return m_region.Contains(yc, xc); +#else + for (unsigned int im = 0; im < m_pcovr_array_loaded.GetCount(); im++) { + if (G_PtInPolygon_FL(m_pcovr_array_loaded[im]->pvertices, + m_pcovr_array_loaded[im]->m_nvertices, xc, yc)) + return true; + } + return false; +#endif +} + +LLRegion cm93chart::GetValidRegion() { return m_region; } + +int cm93chart::loadcell_in_sequence(int cellindex, char subcell) { + int rv = loadsubcell(cellindex, subcell); + + return rv; +} + +int cm93chart::loadsubcell(int cellindex, wxChar sub_char) { + // Create the file name + + int ilat = cellindex / 10000; + int ilon = cellindex % 10000; + + if (g_bDebugCM93) { + double dlat = m_dval / 3.; + double dlon = m_dval / 3.; + double lat, lon; + Get_CM93_Cell_Origin(cellindex, GetNativeScale(), &lat, &lon); + printf( + "\n Attempting loadcell %d scale %lc, sub_char %lc at lat: %g/%g " + "lon:%g/%g\n", + cellindex, wxChar(m_scalechar[0]), sub_char, lat, lat + dlat, lon, + lon + dlon); + } + + int jlat = (int)(((ilat - 30) / m_dval) * m_dval) + 30; // normalize + int jlon = (int)((ilon / m_dval) * m_dval); + + int ilatroot = (((ilat - 30) / 60) * 60) + 30; + int ilonroot = (ilon / 60) * 60; + + wxString file; + file.Printf("%04d%04d.", jlat, jlon); + file += m_scalechar; + file[0] = sub_char; + + // We prefer to make use of the NoFind array to avoid file system access to + // cells known not to exist. However, when the arra becomes "large", then + // searching the array becomes slower than actually accessing the file system. + // So, detect this case, and skip the NoFind array if the array size is larger + // than nnn items. "nnn" determined by experimentation/intuition. Could also + // be platform dependent. + bool b_useNoFind = true; + if (m_noFindArray.GetCount() > 500) b_useNoFind = false; + + wxString fileroot; + fileroot.Printf("%04d%04d", ilatroot, ilonroot); + appendOSDirSep(&fileroot); + fileroot.append(m_scalechar); + appendOSDirSep(&fileroot); + wxString key = fileroot; + key.append(file); + fileroot.Prepend(m_prefix); + + file.Prepend(fileroot); + + if (g_bDebugCM93) { + char sfile[200]; + strncpy(sfile, file.mb_str(), 199); + sfile[199] = 0; + printf(" filename: %s\n", sfile); + } + + bool bfound = false; + wxString compfile; + if (b_useNoFind) { + if (m_noFindArray.Index(key) == wxNOT_FOUND) { + if (::wxFileExists(file)) + bfound = true; + else + m_noFindArray.Add(key); + } + } else { + if (::wxFileExists(file)) bfound = true; + ; + } + + if (!bfound) { // try compressed version + if (b_useNoFind) { + if (m_noFindArray.Index(key + ".xz") == wxNOT_FOUND) { + if (::wxFileExists(file + ".xz")) { + compfile = file + ".xz"; + } + } else { + m_noFindArray.Add(key + ".xz"); + } + } else { + if (::wxFileExists(file + ".xz")) compfile = file + ".xz"; + } + } + + // Try again with alternate scale character + if (!bfound && !compfile.Length()) { + // Try with alternate case of m_scalechar + wxString new_scalechar = m_scalechar.Lower(); + + wxString file1; + file1.Printf("%04d%04d.", jlat, jlon); + file1 += new_scalechar; + file1[0] = sub_char; + + fileroot.Printf("%04d%04d", ilatroot, ilonroot); + appendOSDirSep(&fileroot); + fileroot.append(new_scalechar); + appendOSDirSep(&fileroot); + key = fileroot; + key.append(file1); + + fileroot.Prepend(m_prefix); + + file1.Prepend(fileroot); + + if (b_useNoFind) { + if (m_noFindArray.Index(key) == wxNOT_FOUND) { + if (::wxFileExists(file1)) { + bfound = true; + file = file1; // found the file as lowercase, substitute the name + } else { + m_noFindArray.Add(key); + } + } + } else { + if (::wxFileExists(file1)) { + bfound = true; + file = file1; // found the file as lowercase, substitute the name + } + } + + if (!bfound) { // try compressed version + if (b_useNoFind) { + if (m_noFindArray.Index(key + ".xz") == wxNOT_FOUND) { + if (::wxFileExists(file1 + ".xz")) + compfile = file1 + ".xz"; + else + m_noFindArray.Add(key + ".xz"); + } + } else { + if (::wxFileExists(file1 + ".xz")) compfile = file1 + ".xz"; + } + } + } + + if (g_bDebugCM93) { + printf("noFind count: %d\n", (int)m_noFindArray.GetCount()); + } + + if (!bfound && !compfile.Length()) return 0; + + // File is known to exist + + wxString msg("Loading CM93 cell "); + msg += file; + wxLogMessage(msg); + + // Set the member variable to be the actual file name for use in single + // chart mode info display + m_LastFileName = file; + + // Decompress if needed + if (compfile.Length()) { + file = wxFileName::CreateTempFileName(wxFileName(compfile).GetFullName()); + if (!DecompressXZFile(compfile, file)) { + wxRemoveFile(file); + return 0; + } + } + + if (g_bDebugCM93) { + char str[256]; + strncpy(str, msg.mb_str(), 255); + str[255] = 0; + printf(" %s\n", str); + } + + // Ingest it + if (!Ingest_CM93_Cell((const char *)file.mb_str(), &m_CIB)) { + wxString msg(" cm93chart Error ingesting "); + msg.Append(file); + wxLogMessage(msg); + + if (compfile.Length()) wxRemoveFile(file); + return 0; + } + + if (compfile.Length()) wxRemoveFile(file); + + return 1; +} + +void cm93chart::SetUserOffsets(int cell_index, int object_id, int subcell, + int xoff, int yoff) { + M_COVR_Desc *pmcd = GetCoverSet()->Find_MCD(cell_index, object_id, subcell); + if (pmcd) { + pmcd->user_xoff = xoff; + pmcd->user_yoff = yoff; + pmcd->m_buser_offsets = true; + } +} + +wxPoint *cm93chart::GetDrawBuffer(int nSize) { + // Reallocate the cm93chart DrawBuffer if it is currently too small + if (nSize > m_nDrawBufferSize) { + wxPoint *tmp = m_pDrawBuffer; + m_pDrawBuffer = + (wxPoint *)realloc(m_pDrawBuffer, sizeof(wxPoint) * (nSize + 1)); + if (NULL == m_pDrawBuffer) { + free(tmp); + tmp = NULL; + } else + m_nDrawBufferSize = nSize + 1; + } + return m_pDrawBuffer; +} + +//----------------------------------------------------------------------------------------------- +// cm93manager Implementation +//----------------------------------------------------------------------------------------------- + +cm93manager::cm93manager() { + m_pcm93Dict = NULL; + + m_bfoundA = false; + m_bfoundB = false; + m_bfoundC = false; + m_bfoundD = false; + m_bfoundE = false; + m_bfoundF = false; + m_bfoundG = false; + m_bfoundZ = false; +} + +cm93manager::~cm93manager() { delete m_pcm93Dict; } + +bool cm93manager::Loadcm93Dictionary(const wxString &name) { + // Find and load cm93_dictionary + if (!m_pcm93Dict) { + m_pcm93Dict = FindAndLoadDict(name); + + if (!m_pcm93Dict) { + wxLogMessage(" Cannot load CM93 Dictionary."); + return false; + } + + if (!m_pcm93Dict->IsOk()) { + wxLogMessage(" Error in loading CM93 Dictionary."); + delete m_pcm93Dict; + m_pcm93Dict = NULL; + return false; + ; + } + } else if (!m_pcm93Dict->IsOk()) { + wxLogMessage(" CM93 Dictionary is not OK."); + return false; + } + + return true; +} + +cm93_dictionary *cm93manager::FindAndLoadDict(const wxString &file) { + cm93_dictionary *retval = NULL; + cm93_dictionary *pdict = new cm93_dictionary(); + + // Search for the dictionary files all along the path of the passed + // parameter filename + + wxFileName fn(file); + wxString path = fn.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME)); + wxString target; + unsigned int i = 0; + + while (i < path.Len()) { + target.Append(path[i]); + if (path[i] == fn.GetPathSeparator()) { + if (pdict->LoadDictionary(target)) { + retval = pdict; + break; + } + if (pdict->LoadDictionary(target + "CM93ATTR")) { + retval = pdict; + break; + } + } + i++; + } + + char t[100]; + strncpy(t, target.mb_str(), 99); + + if (retval == NULL) delete pdict; + + return retval; +} + +//---------------------------------------------------------------------------- +// cm93 Composite Chart object class Implementation +//---------------------------------------------------------------------------- +cm93compchart::cm93compchart() { + m_ChartType = CHART_TYPE_CM93COMP; + m_pDictComposite = NULL; + + // Supply a default name for status bar field + m_FullPath = "CM93"; + + // Set the "Description", so that it paints nice on the screen + m_Description = "CM93Composite"; + + m_SE = ""; + m_datum_str = "WGS84"; + m_SoundingsDatum = "Unknown"; + + for (int i = 0; i < 8; i++) m_pcm93chart_array[i] = NULL; + + m_pcm93chart_current = NULL; + + m_cmscale = -1; + m_Chart_Skew = 0.0; + + m_pDummyBM = NULL; + + SetSpecialOutlineCellIndex(0, 0, 0); + m_last_cell_adjustvp = NULL; + + m_pcm93mgr = new cm93manager(); +} + +cm93compchart::~cm93compchart() { + if (g_pCM93OffsetDialog) { + g_pCM93OffsetDialog->Hide(); + } + + for (int i = 0; i < 8; i++) delete m_pcm93chart_array[i]; + + delete m_pDictComposite; + delete m_pDummyBM; + delete m_pcm93mgr; +} + +InitReturn cm93compchart::Init(const wxString &name, ChartInitFlag flags) { + m_FullPath = name; + + wxFileName fn(name); + + wxString target; + wxString path; + + // Verify that the passed file name exists + if (!fn.FileExists()) { + // It may be a directory + if (wxDir::Exists(name)) { + target = name; + appendOSDirSep(&target); + path = name; + appendOSDirSep(&path); + } else { + wxString msg(" CM93Composite Chart Init cannot find "); + msg.Append(name); + wxLogMessage(msg); + return INIT_FAIL_REMOVE; + } + } else // its a file that exists + { + // Get the cm93 cell database prefix + path = fn.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME)); + + // Remove two subdirectories from the passed file name + // This will give a normal CM93 root + wxFileName file_path(path); + file_path.RemoveLastDir(); + file_path.RemoveLastDir(); + + target = file_path.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR); + } + + m_prefixComposite = target; + + wxString msg("CM93Composite Chart Root is "); + msg.Append(m_prefixComposite); + wxLogMessage(msg); + + if (flags == THUMB_ONLY) { + // SetColorScheme(cs, false); + + return INIT_OK; + } + + if (flags == HEADER_ONLY) return CreateHeaderData(); + + // Load the cm93 dictionary if necessary + if (!m_pDictComposite) { + if (!m_pDictComposite) // second try from the file + m_pDictComposite = FindAndLoadDictFromDir(path); + + if (!m_pDictComposite) { + wxLogMessage( + " CM93Composite Chart Init cannot locate CM93 dictionary."); + return INIT_FAIL_REMOVE; + } + } + + // Set the color scheme + SetColorScheme(m_global_color_scheme, false); + + bReadyToRender = true; + + return INIT_OK; +} + +void cm93compchart::Activate() { + // if ( g_bShowCM93DetailSlider ) + // { + // if ( !pPopupDetailSlider ) + // { + // pPopupDetailSlider = new PopUpDSlide ( gFrame, -1 , 0, + // -CM93_ZOOM_FACTOR_MAX_RANGE, CM93_ZOOM_FACTOR_MAX_RANGE, + // wxPoint ( + // g_cm93detail_dialog_x, + // g_cm93detail_dialog_y + // ), wxDefaultSize, + // wxSIMPLE_BORDER + // , _T ( + // "cm93 + // Detail" + // ) ); + // } + // + // // Here is an ugly piece of code which prevents the slider + // from taking the keyboard focus + // // Only seems to work for Windows..... + // pPopupDetailSlider->Disable(); + // pPopupDetailSlider->Show(); + // pPopupDetailSlider->Enable(); + // } +} + +void cm93compchart::Deactivate() { + if (pPopupDetailSlider) { + pPopupDetailSlider->Destroy(); + pPopupDetailSlider = NULL; + } +} + +double scale_breaks[] = { + 5000., // G + 15000., // F + 40000., // E + 150000., // D + 300000., // C + 1000000., // B + 5000000., // A + 20000000. // Z +}; + +//----------------------------------------------------------------------- +// Calculate and Set ViewPoint Constants +//----------------------------------------------------------------------- + +int cm93compchart::GetCMScaleFromVP(const ViewPort &vpt) { + double scale_mpp = 3000 / vpt.view_scale_ppm; + + double scale_mpp_adj = scale_mpp; + + double scale_breaks_adj[7]; + + for (int i = 0; i < 7; i++) scale_breaks_adj[i] = scale_breaks[i]; + + if (g_cm93_zoom_factor) { +#if 0 + // Completely intuitive exponential curve adjustment + double efactor = ( double ) ( g_cm93_zoom_factor ) * ( .176 / 7. ); + for ( int i=0 ; i < 7 ; i++ ) + { + double efr = efactor * ( 7 - i ); + scale_breaks_adj[i] = scale_breaks[i] * pow ( 10., efr ); + if ( g_bDebugCM93 ) + printf ( "g_cm93_zoom_factor: %2d efactor: %6g efr:%6g, scale_breaks[i]:%6g scale_breaks_adj[i]: %6g\n", + g_cm93_zoom_factor, efactor, efr, scale_breaks[i], scale_breaks_adj[i] ); + } +#else + // improved adjustment for small scales + double efr = (double)g_cm93_zoom_factor * pow(scale_mpp, -.05); + scale_mpp_adj *= pow(.6, efr); +#endif + } + + int cmscale_calc = 7; + int brk_index = 0; + while (cmscale_calc > 0) { + if (scale_mpp_adj < scale_breaks_adj[brk_index]) break; + cmscale_calc--; + brk_index++; + } + + // Check for overzoom at the theoretically calcuolated chart scale + // If overzoomed possible, switch to larger scale chart if available + double zoom_factor = scale_breaks[7 - cmscale_calc] / vpt.chart_scale; + if (zoom_factor > 4.0) { + if (cmscale_calc < 7) cmscale_calc++; + } + + return cmscale_calc; +} + +void cm93compchart::SetVPParms(const ViewPort &vpt) { + m_vpt = vpt; // save a copy + + int cmscale = GetCMScaleFromVP(vpt); // First order calculation of cmscale + m_cmscale = PrepareChartScale(vpt, cmscale, false); + + // Continuoesly update the composite chart edition date to the latest cell + // decoded + if (m_pcm93chart_array[cmscale]) { + if (!m_EdDate.IsValid() || + !m_pcm93chart_array[cmscale]->GetEditionDate().IsValid() || + m_pcm93chart_array[cmscale]->GetEditionDate().IsLaterThan(m_EdDate)) + m_EdDate = m_pcm93chart_array[cmscale]->GetEditionDate(); + } +} + +int cm93compchart::PrepareChartScale(const ViewPort &vpt, int cmscale, + bool bOZ_protect) { + if (g_bDebugCM93) + printf("\non SetVPParms, cmscale:%d, %c\n", cmscale, + (char)('A' + cmscale - 1)); + + wxChar ext; + bool cellscale_is_useable = false; + bool b_nochart = false; + + while (!cellscale_is_useable) { + // Open the proper scale chart, if not already open + while (NULL == m_pcm93chart_array[cmscale]) { + if (Is_CM93Cell_Present(m_prefixComposite, vpt.clat, vpt.clon, cmscale)) { + if (g_bDebugCM93) + printf(" chart %c at VP clat/clon is present\n", + (char)('A' + cmscale - 1)); + + m_pcm93chart_array[cmscale] = new cm93chart(); + + ext = (wxChar)('A' + cmscale - 1); + if (cmscale == 0) ext = 'Z'; + + wxString file_dummy = "CM93."; + file_dummy << ext; + + m_pcm93chart_array[cmscale]->SetCM93Dict(m_pDictComposite); + m_pcm93chart_array[cmscale]->SetCM93Prefix(m_prefixComposite); + m_pcm93chart_array[cmscale]->SetCM93Manager(m_pcm93mgr); + + m_pcm93chart_array[cmscale]->SetColorScheme(m_global_color_scheme); + m_pcm93chart_array[cmscale]->Init(file_dummy, FULL_INIT); + } else if (cmscale == 0) { + // wxString msg; + // msg.Printf ( _T ( " CM93 finds no chart of + // any scale present at Lat/Lon %g %g" ), + // vpt.clat, vpt.clon ); wxLogMessage ( msg ); + if (g_bDebugCM93) + printf( + " CM93 finds no chart of any scale present at Lat/Lon %g %g\n", + vpt.clat, vpt.clon); + + b_nochart = true; + break; + } + + else { + cmscale--; // revert to smaller scale if selected is not present + if (g_bDebugCM93) + printf(" no %c scale chart present, adjusting cmscale to %c\n", + (char)('A' + cmscale), (char)('A' + cmscale - 1)); + } + } + + m_pcm93chart_current = m_pcm93chart_array[cmscale]; + + if (b_nochart) { + if (g_bDebugCM93) printf(" b_nochart return\n"); + + m_pcm93chart_current = NULL; + for (int i = 0; i < 8; i++) { + delete m_pcm93chart_array[i]; + m_pcm93chart_array[i] = NULL; + } + + return cmscale; + } + + if (m_pcm93chart_current) { + // Pass the parameters to the proper scale chart + // Which will also load the needed cell(s) + m_pcm93chart_current->SetVPParms(vpt); + + // Check to see if the viewpoint center is actually on the selected + // chart + float yc = vpt.clat; + float xc = vpt.clon; + + if (!m_pcm93chart_current->GetCoverSet()->GetCoverCount()) { + if (g_bDebugCM93) + printf(" chart %c has no M_COVR\n", (char)('A' + cmscale - 1)); + } + + if (m_pcm93chart_current->IsPointInLoadedM_COVR(xc, yc)) { + if (g_bDebugCM93) + printf(" chart %c contains clat/clon\n", (char)('A' + cmscale - 1)); + + cellscale_is_useable = true; + break; + } + + // This commented block assumed that scale 0 coverage is available + // worlwide..... Might not be so with partial CM93 sets + /* + else if(cmscale == 0) + { + cellscale_is_useable = true; + break; + } + */ + + else if (vpt.b_quilt && vpt.b_FullScreenQuilt) { + ViewPort vp = vpt; + + covr_set *pcover = m_pcm93chart_current->GetCoverSet(); + if (pcover) { + bool boverlap = false; + for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) { + M_COVR_Desc *mcd = pcover->GetCover(im); + + if (!(vp.GetBBox().IntersectOut(mcd->m_covr_bbox))) { + boverlap = true; + break; + } + } + if (boverlap) cellscale_is_useable = true; + } + } + + if (!cellscale_is_useable) { + if (cmscale > 0) + cmscale--; // revert to larger scale if the current scale cells do + // not contain VP + else + b_nochart = true; // we have retired to scale 0, and still no chart + // coverage, so stop already... + if (g_bDebugCM93) + printf(" VP is not in M_COVR, adjusting cmscale to %c\n", + (char)('A' + cmscale - 1)); + } + } + } + + // Final check the zoom factor + if (bOZ_protect) { + double zoom_factor = scale_breaks[7 - cmscale] / vpt.chart_scale; + + if (zoom_factor > 4.0) { + // See if there is a larger scale chart present that will avoid overzoom + + float yc = vpt.clat; + float xc = vpt.clon; + + // Find out what the smallest available scale is that is not overzoomed + FillScaleArray(vpt.clat, vpt.clon); + int new_scale = cmscale; + bool b_found = false; + while (new_scale <= 7) { + if (m_bScale_Array[new_scale]) { + double new_zoom_factor = + scale_breaks[7 - new_scale] / vpt.chart_scale; + + // Do not allow excessive "under-zoom", for performance reasons + if (new_zoom_factor < 1.0) { + b_found = true; + new_scale = cmscale; + break; + } + + if (new_zoom_factor < 4.0) { + if (NULL == m_pcm93chart_array[new_scale]) { + m_pcm93chart_array[new_scale] = new cm93chart(); + + ext = (wxChar)('A' + new_scale - 1); + if (new_scale == 0) ext = 'Z'; + + wxString file_dummy = "CM93."; + file_dummy << ext; + + m_pcm93chart_array[new_scale]->SetCM93Dict(m_pDictComposite); + m_pcm93chart_array[new_scale]->SetCM93Prefix(m_prefixComposite); + m_pcm93chart_array[new_scale]->SetCM93Manager(m_pcm93mgr); + + m_pcm93chart_array[new_scale]->SetColorScheme( + m_global_color_scheme); + m_pcm93chart_array[new_scale]->Init(file_dummy, FULL_INIT); + } + + m_pcm93chart_array[new_scale]->SetVPParms(vpt); + if (m_pcm93chart_array[new_scale]->IsPointInLoadedM_COVR(xc, yc)) { + b_found = true; + break; + } + } + } + new_scale++; + } + if (b_found) { + cmscale = new_scale; + m_pcm93chart_current = m_pcm93chart_array[cmscale]; + } + } + } + + return cmscale; +} + +// Populate the member bool array describing which chart scales are available +// at any location +void cm93compchart::FillScaleArray(double lat, double lon) { + for (int cmscale = 0; cmscale < 8; cmscale++) + m_bScale_Array[cmscale] = + Is_CM93Cell_Present(m_prefixComposite, lat, lon, cmscale); +} + +// These methods simply pass the called parameters to the currently active +// cm93chart + +wxString cm93compchart::GetPubDate() { + wxString data; + + if (NULL != m_pcm93chart_current) + + data.Printf("%4d", m_current_cell_pub_date); + else + data = "????"; + return data; +} + +int cm93compchart::GetNativeScale() { + if (m_pcm93chart_current) + return m_pcm93chart_current->GetNativeScale(); + else + return (int)1e8; +} + +double cm93compchart::GetNormalScaleMin(double canvas_scale_factor, + bool b_allow_overzoom) { + double oz_factor; + oz_factor = 40.; + + if (m_pcm93chart_current) { + int cmscale = 0; + if (m_pcm93chart_current->m_last_vp.IsValid()) { + FillScaleArray(m_pcm93chart_current->m_last_vp.clat, + m_pcm93chart_current->m_last_vp.clon); + + // Find out what the smallest available scale is + cmscale = 7; + while (cmscale > 0) { + if (m_bScale_Array[cmscale]) break; + cmscale--; + } + } + + // And return a sensible minimum scale, allowing selected overzoom. + switch (cmscale) { + case 0: + return 20000000. / oz_factor; // Z + case 1: + return 3000000. / oz_factor; // A + case 2: + return 1000000. / oz_factor; // B + case 3: + return 200000. / oz_factor; // C + case 4: + return 100000. / oz_factor; // D + case 5: + return 50000. / oz_factor; // E + case 6: + return 20000. / oz_factor; // F + case 7: + return 500.; // G + default: + return 500. / oz_factor; + } + } else + return 500.; +} + +double cm93compchart::GetNormalScaleMax(double canvas_scale_factor, + int canvas_width) { + return (180. / 360.) * PI * 2 * + (WGS84_semimajor_axis_meters / (canvas_width / canvas_scale_factor)); + // return 1.0e8; +} + +wxPoint GetPixFromLLVP(double lat, double lon, const ViewPort &VPoint) { + // Inline the Simple Mercator Transform for performance reasons + double easting, northing; + + double s, y3, s0, y30; + double z = WGS84_semimajor_axis_meters * mercator_k0; + + double xlon = lon; + + /* Make sure lon and lon0 are same phase */ + if (lon * VPoint.clon < 0.) { + if (lon < 0.) + xlon += 360.; + else + xlon -= 360.; + } + + // And choose the closest direction + if (fabs(xlon - VPoint.clon) > 180.) { + if (xlon > VPoint.clon) + xlon -= 360.; + else + xlon += 360.; + } + easting = (xlon - VPoint.clon) * DEGREE * z; + + s = sin(lat * DEGREE); + y3 = (.5 * log((1 + s) / (1 - s))) * z; + + s0 = sin(VPoint.clat * DEGREE); + y30 = (.5 * log((1 + s0) / (1 - s0))) * z; + northing = y3 - y30; + + wxPoint r; + + double epix = easting * VPoint.view_scale_ppm; + double npix = northing * VPoint.view_scale_ppm; + r.x = (int)round((VPoint.pix_width / 2) + epix); + r.y = (int)round((VPoint.pix_height / 2) - npix); + + return r; +} + +// extern void catch_signals(int signo); + +void cm93compchart::GetValidCanvasRegion(const ViewPort &VPoint, + OCPNRegion *pValidRegion) { + OCPNRegion screen_region(0, 0, VPoint.pix_width, VPoint.pix_height); + OCPNRegion ret = GetValidScreenCanvasRegion( + VPoint, g_bopengl ? VPoint.rv_rect : screen_region); + *pValidRegion = ret; +} + +OCPNRegion cm93compchart::GetValidScreenCanvasRegion( + const ViewPort &VPoint, const OCPNRegion &ScreenRegion) { + OCPNRegion ret_region; + + ViewPort vp = VPoint; + + vp.rotation = 0.; + + if (m_pcm93chart_current) { + int chart_native_scale = m_pcm93chart_current->GetNativeScale(); + + for (unsigned int im = 0; + im < m_pcm93chart_current->m_pcovr_array_loaded.GetCount(); im++) { + M_COVR_Desc *pmcd = (m_pcm93chart_current->m_pcovr_array_loaded[im]); + + // We can make a quick test based on the bbox of the M_COVR and the + // bbox of the ViewPort + + if (vp.GetBBox().IntersectOut(pmcd->m_covr_bbox)) continue; + + wxPoint *DrawBuf = m_pcm93chart_current->GetDrawBuffer(pmcd->m_nvertices); + + OCPNRegion rgn_covr = vp.GetVPRegionIntersect( + ScreenRegion, pmcd->m_nvertices, (float *)pmcd->pvertices, + chart_native_scale, DrawBuf); + + if (rgn_covr.IsOk()) // not empty + ret_region.Union(rgn_covr); + } + + } else + ret_region.Union(OCPNRegion(0, 0, 1, 1)); + + return ret_region; +} + +LLRegion cm93compchart::GetValidRegion() { + if (m_pcm93chart_current) return m_pcm93chart_current->GetValidRegion(); + + return LLRegion(); // empty region +} + +bool cm93compchart::RenderRegionViewOnGL(const wxGLContext &glc, + const ViewPort &VPoint, + const OCPNRegion &RectRegion, + const LLRegion &Region) { + SetVPParms(VPoint); + + if (g_pCM93OffsetDialog && g_pCM93OffsetDialog->IsShown()) + g_pCM93OffsetDialog->UpdateMCOVRList(VPoint); + + return DoRenderRegionViewOnGL(glc, VPoint, RectRegion, Region); +} + +bool cm93compchart::DoRenderRegionViewOnGL(const wxGLContext &glc, + const ViewPort &VPoint, + const OCPNRegion &RectRegion, + const LLRegion &Region) { + // g_bDebugCM93 = true; + + // CALLGRIND_START_INSTRUMENTATION + + ViewPort vp = VPoint; + + bool render_return = false; + if (m_pcm93chart_current == 0) return render_return; + + { + // This will be done later, in s57chart base class render method + /// m_pcm93chart_current->SetVPParms ( vp ); + + // Check the current chart scale to see if it covers the requested region + // totally + if (VPoint.b_quilt) { + LLRegion vpr_empty = Region; + LLRegion chart_region = GetValidRegion(); + + // old method which draws the regions from large to small scale, then + // finishes with the largest scale. This is broken on systems with broken + // clipping regions + + // So we modify the algorithm as follows: + // a. Calculate the region patches from large scale to small scale, + // starting with the Reference scale, and + // ending when the total region requested is full. + // b. Save the calculated patches in an array as they are generated. + // c. Render the regions/scales saved in the array in reverse order, from + // small scale to large scale. d. Finally, render the Reference + // region/scale. + // + // This logic has the advantage that only the minimum necessary Object + // rendering is actually performed, and only within the minimum necessary + // region. + + if (!chart_region.Empty()) vpr_empty.Subtract(chart_region); + + if (!vpr_empty.Empty() && + m_cmscale) // This chart scale does not fully cover the region + { + // Save the current cm93 chart scale for restoration later + int cmscale_save = m_cmscale; + + LLRegion region_vect[8]; + + // Render smaller scale cells the entire requested region is full + + while (!vpr_empty.Empty() && m_cmscale) { + // get the next smaller scale chart + m_cmscale = PrepareChartScale(vp, m_cmscale - 1, false); + + if (m_pcm93chart_current) { + LLRegion sscale_region = GetValidRegion(); + + // Save the calculated per-scale region in the array + region_vect[m_cmscale] = sscale_region; + region_vect[m_cmscale].Intersect(vpr_empty); + // Only need to render that part of the vp that is not yet full + // Update the remaining empty region + vpr_empty.Subtract(sscale_region); + } + + } // while + + // Render all non-empty regions saved in the array, from small to large + // scale. + for (int i = 0; i < 8; i++) { + if (!region_vect[i].Empty()) { + m_cmscale = PrepareChartScale(vp, i, false); + if (m_pcm93chart_current) + render_return |= m_pcm93chart_current->RenderRegionViewOnGL( + glc, vp, RectRegion, region_vect[i]); + } + } + + // restore the base chart pointer + m_cmscale = cmscale_save; + m_pcm93chart_current = m_pcm93chart_array[m_cmscale]; + } + + // Render the on-top Reference region/scale + if (m_pcm93chart_current) { + render_return |= m_pcm93chart_current->RenderRegionViewOnGL( + glc, vp, RectRegion, Region); + m_Name = m_pcm93chart_current->GetName(); + } + + } else // Single chart mode + { + if (m_pcm93chart_current) { + render_return = m_pcm93chart_current->RenderRegionViewOnGL( + glc, vp, RectRegion, Region); + m_Name = m_pcm93chart_current->GetLastFileName(); + } + } + } + + if (VPoint.m_projection_type != PROJECTION_MERCATOR) + return render_return; // TODO: fix below for non-mercator + + if (!m_pcm93chart_current) return render_return; + + // Render the cm93 cell's M_COVR outlines if called for + if (m_cell_index_special_outline) { +#ifdef ocpnUSE_GL + wxGLCanvas *glc = top_frame::Get()->GetWxGlCanvas(); + ocpnDC dc(*glc); +#else + ocpnDC dc; +#endif + dc.SetVP(VPoint); + + covr_set *pcover = m_pcm93chart_current->GetCoverSet(); + + for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) { + M_COVR_Desc *pmcd = pcover->GetCover(im); + if ((pmcd->m_cell_index == m_cell_index_special_outline) && + (pmcd->m_object_id == m_object_id_special_outline) && + (pmcd->m_subcell == m_subcell_special_outline)) + + { + // Draw this MCD's represented outline + + // Case: vpBBox is completely inside the mcd box + // if(!( + // vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)) || + // !( vp.vpBBox.IntersectOut(pmcd->m_covr_bbox))) + { + float_2Dpt *p = pmcd->pvertices; + wxPoint *pwp = m_pcm93chart_current->GetDrawBuffer(pmcd->m_nvertices); + + for (int ip = 0; ip < pmcd->m_nvertices; ip++) { + double plon = p->x; + if (fabs(plon - VPoint.clon) > 180.) { + if (plon > VPoint.clon) + plon -= 360.; + else + plon += 360.; + } + + double easting, northing, epix, npix; + toSM(p->y, plon + 360., VPoint.clat, VPoint.clon + 360, &easting, + &northing); + + // Outlines stored in MCDs are not adjusted for offsets + // easting -= + // pmcd->transform_WGS84_offset_x; + easting -= pmcd->user_xoff; + // northing -= + // pmcd->transform_WGS84_offset_y; + northing -= pmcd->user_yoff; + + epix = easting * VPoint.view_scale_ppm; + npix = northing * VPoint.view_scale_ppm; + + pwp[ip].x = (int)round((VPoint.pix_width / 2) + epix); + pwp[ip].y = (int)round((VPoint.pix_height / 2) - npix); + + p++; + } + + bool btest = true; + if (btest) { + wxPen pen(wxTheColourDatabase->Find("YELLOW"), 3); + wxDash dash1[2]; + dash1[0] = 4; // Long dash + dash1[1] = 4; // Short gap + pen.SetStyle(wxPENSTYLE_USER_DASH); + pen.SetDashes(2, dash1); + + dc.SetPen(pen); + + for (int iseg = 0; iseg < pmcd->m_nvertices - 1; iseg++) { + int x0 = pwp[iseg].x; + int y0 = pwp[iseg].y; + int x1 = pwp[iseg + 1].x; + int y1 = pwp[iseg + 1].y; + + ClipResult res = cohen_sutherland_line_clip_i( + &x0, &y0, &x1, &y1, 0, VPoint.pix_width, 0, + VPoint.pix_height); + + if (res == + Invisible) // Do not bother with segments that are invisible + continue; + + dc.DrawLine(x0, y0, x1, y1); + } + } + } + } + } + } + + return render_return; +} + +bool cm93compchart::RenderRegionViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint, + const OCPNRegion &Region) { + SetVPParms(VPoint); + + if (g_pCM93OffsetDialog && g_pCM93OffsetDialog->IsShown()) + g_pCM93OffsetDialog->UpdateMCOVRList(VPoint); + + return DoRenderRegionViewOnDC(dc, VPoint, Region); +} + +bool cm93compchart::RenderViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint) { + const OCPNRegion vpr(0, 0, VPoint.pix_width, VPoint.pix_height); + + SetVPParms(VPoint); + + return DoRenderRegionViewOnDC(dc, VPoint, vpr); +} + +bool cm93compchart::DoRenderRegionViewOnDC(wxMemoryDC &dc, + const ViewPort &VPoint, + const OCPNRegion &Region) { + // g_bDebugCM93 = true; + + // CALLGRIND_START_INSTRUMENTATION + if (g_bDebugCM93) { + printf("\nOn DoRenderRegionViewOnDC Ref scale is %d, %c\n", m_cmscale, + (char)('A' + m_cmscale - 1)); + OCPNRegionIterator upd(Region); + while (upd.HaveRects()) { + wxRect rect = upd.GetRect(); + printf(" Region Rect: %d %d %d %d\n", rect.x, rect.y, rect.width, + rect.height); + upd.NextRect(); + ; + } + } + + ViewPort vp = VPoint; + + bool render_return = false; + if (m_pcm93chart_current) { + m_pcm93chart_current->SetVPParms(vp); + + // Check the current chart scale to see if it covers the requested region + // totally + if (VPoint.b_quilt) { + OCPNRegion vpr_empty = Region; + + OCPNRegion chart_region = GetValidScreenCanvasRegion(vp, Region); + + if (g_bDebugCM93) { + printf( + "On DoRenderRegionViewOnDC : Intersecting Ref region rectangles\n"); + OCPNRegionIterator upd(chart_region); + while (upd.HaveRects()) { + wxRect rect = upd.GetRect(); + printf(" Region Rect: %d %d %d %d\n", rect.x, rect.y, rect.width, + rect.height); + upd.NextRect(); + } + } + + if (!chart_region.IsEmpty()) vpr_empty.Subtract(chart_region); + + if (!vpr_empty.Empty() && + m_cmscale) // This chart scale does not fully cover the region + { + // Render the target scale chart on a temp dc for safekeeping +#ifdef ocpnUSE_DIBSECTION + ocpnMemDC temp_dc; +#else + wxMemoryDC temp_dc; +#endif + if (!chart_region.IsEmpty()) + render_return = m_pcm93chart_current->RenderRegionViewOnDC( + temp_dc, vp, chart_region); + else + render_return = false; + + // Save the current cm93 chart pointer for restoration later + cm93chart *m_pcm93chart_save = m_pcm93chart_current; + + // Prepare a blank quilt bitmap to build up the quilt upon + // We need to do this in order to avoid polluting any of the + // sub-chart cached bitmaps + if (m_pDummyBM) { + if ((m_pDummyBM->GetWidth() != VPoint.rv_rect.width) || + (m_pDummyBM->GetHeight() != VPoint.rv_rect.height)) { + delete m_pDummyBM; + m_pDummyBM = NULL; + } + } + if (NULL == m_pDummyBM) + m_pDummyBM = + new wxBitmap(VPoint.rv_rect.width, VPoint.rv_rect.height, -1); + +#ifdef ocpnUSE_DIBSECTION // Clear the quilt + ocpnMemDC dumm_dc; +#else + wxMemoryDC dumm_dc; +#endif + dumm_dc.SelectObject(*m_pDummyBM); + dumm_dc.SetBackground(*wxBLACK_BRUSH); + dumm_dc.Clear(); + + int cmscale_next = m_cmscale; + + // Render smaller scale cells onto a temporary DC, blitting the valid + // region onto the quilt dc until the region is full + while (!vpr_empty.Empty() && cmscale_next) { + // get the next smaller scale chart + cmscale_next--; + m_cmscale = PrepareChartScale(vp, cmscale_next, false); +#ifdef ocpnUSE_DIBSECTION + ocpnMemDC build_dc; +#else + wxMemoryDC build_dc; +#endif + + if (m_pcm93chart_current) { + if (g_bDebugCM93) + printf(" In DRRVOD, add quilt patch at %d, %c\n", m_cmscale, + (char)('A' + m_cmscale - 1)); + + m_pcm93chart_current->RenderRegionViewOnDC(build_dc, vp, Region); + + OCPNRegion sscale_region = GetValidScreenCanvasRegion(vp, Region); + + // Only need to render that part of the vp that is not yet full + sscale_region.Intersect(vpr_empty); + + // Blit the smaller scale chart patch onto the target DC + OCPNRegionIterator upd(sscale_region); + while (upd.HaveRects()) { + wxRect rect = upd.GetRect(); + dumm_dc.Blit(rect.x, rect.y, rect.width, rect.height, &build_dc, + rect.x, rect.y); + upd.NextRect(); + } + build_dc.SelectObject(wxNullBitmap); // safely unmap the bmp + + // Update the remaining empty region + if (!sscale_region.IsEmpty()) vpr_empty.Subtract(sscale_region); + } + + } // while + + // Finally, Blit the target scale chart as saved on temp_dc to quilt + // dc + OCPNRegionIterator updt(chart_region); + while (updt.HaveRects()) { + wxRect rect = updt.GetRect(); + dumm_dc.Blit(rect.x, rect.y, rect.width, rect.height, &temp_dc, + rect.x, rect.y); + updt.NextRect(); + } + temp_dc.SelectObject(wxNullBitmap); // safely unmap the base chart bmp + + // restore the base chart pointer + m_pcm93chart_current = m_pcm93chart_save; + + // We can unselect the target from the dummy DC, to avoid having to + // copy it. + dumm_dc.SelectObject(wxNullBitmap); + + // And the return dc is the quilt + dc.SelectObject(*m_pDummyBM); + + render_return = true; + } else { + m_pcm93chart_current->RenderRegionViewOnDC(dc, vp, Region); + render_return = true; + } + m_Name = m_pcm93chart_current->GetName(); + + } else // Single chart mode + { + render_return = + m_pcm93chart_current->RenderRegionViewOnDC(dc, vp, Region); + m_Name = m_pcm93chart_current->GetLastFileName(); + } + + } else { + // one must always return a valid bitmap selected into the specified DC + // Since the CM93 cell is not available at this location, select a dummy + // placeholder + if (m_pDummyBM) { + if ((m_pDummyBM->GetWidth() != VPoint.pix_width) || + (m_pDummyBM->GetHeight() != VPoint.pix_height)) { + delete m_pDummyBM; + m_pDummyBM = NULL; + } + } + + if (NULL == m_pDummyBM) + m_pDummyBM = new wxBitmap(VPoint.pix_width, VPoint.pix_height, -1); + + // Clear the bitmap + wxMemoryDC mdc; + mdc.SelectObject(*m_pDummyBM); + mdc.SetBackground(*wxBLACK_BRUSH); + mdc.Clear(); + mdc.SelectObject(wxNullBitmap); + + dc.SelectObject(*m_pDummyBM); + } + + // CALLGRIND_STOP_INSTRUMENTATION + + // Render the cm93 cell's M_COVR outlines if called for + if (m_cell_index_special_outline && m_pcm93chart_current) { + covr_set *pcover = m_pcm93chart_current->GetCoverSet(); + + for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) { + M_COVR_Desc *pmcd = pcover->GetCover(im); + if ((pmcd->m_cell_index == m_cell_index_special_outline) && + (pmcd->m_object_id == m_object_id_special_outline) && + (pmcd->m_subcell == m_subcell_special_outline)) + + { + // Draw this MCD's represented outline + + // Case: vpBBox is completely inside the mcd box + // if(!( + // vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)) || + // !( vp.vpBBox.IntersectOut(pmcd->m_covr_bbox))) + { + float_2Dpt *p = pmcd->pvertices; + wxPoint *pwp = m_pcm93chart_current->GetDrawBuffer(pmcd->m_nvertices); + + for (int ip = 0; ip < pmcd->m_nvertices; ip++) { + double plon = p->x; + if (fabs(plon - VPoint.clon) > 180.) { + if (plon > VPoint.clon) + plon -= 360.; + else + plon += 360.; + } + + double easting, northing, epix, npix; + toSM(p->y, plon + 360., VPoint.clat, VPoint.clon + 360, &easting, + &northing); + + // Outlines stored in MCDs are not adjusted for offsets + // easting -= + // pmcd->transform_WGS84_offset_x; + easting -= pmcd->user_xoff; + // northing -= + // pmcd->transform_WGS84_offset_y; + northing -= pmcd->user_yoff; + + epix = easting * VPoint.view_scale_ppm; + npix = northing * VPoint.view_scale_ppm; + + pwp[ip].x = (int)round((VPoint.pix_width / 2) + epix); + pwp[ip].y = (int)round((VPoint.pix_height / 2) - npix); + + p++; + } + + // Scrub the points + // looking for segments for which the wrong longitude decision was + // made + // TODO all this mole needs to be rethought, again + bool btest = true; + /* + wxPoint p0 = pwp[0]; + for(int ip = 1 ; ip < pmcd->m_nvertices + ; ip++) + { + // if(((p0.x > VPoint.pix_width) && + (pwp[ip].x < 0)) || ((p0.x < 0) && (pwp[ip].x > VPoint.pix_width))) + // btest = false; + + p0 = pwp[ip]; + } + */ + if (btest) { + dc.SetPen(wxPen(wxTheColourDatabase->Find("YELLOW"), 4, + wxPENSTYLE_LONG_DASH)); + + for (int iseg = 0; iseg < pmcd->m_nvertices - 1; iseg++) { + int x0 = pwp[iseg].x; + int y0 = pwp[iseg].y; + int x1 = pwp[iseg + 1].x; + int y1 = pwp[iseg + 1].y; + + ClipResult res = cohen_sutherland_line_clip_i( + &x0, &y0, &x1, &y1, 0, VPoint.pix_width, 0, + VPoint.pix_height); + + if (res == + Invisible) // Do not bother with segments that are invisible + continue; + + dc.DrawLine(x0, y0, x1, y1); + } + } + } + } + } + } + + return render_return; +} + +void cm93compchart::UpdateRenderRegions(const ViewPort &VPoint) { + OCPNRegion full_screen_region(0, 0, VPoint.rv_rect.width, + VPoint.rv_rect.height); + + ViewPort vp = VPoint; + + SetVPParms(VPoint); + + if (m_pcm93chart_current) { + m_pcm93chart_current->SetVPParms(vp); + + // Check the current chart scale to see if it covers the requested region + // totally + if (VPoint.b_quilt) { + // Clear all the subchart regions + for (int i = 0; i < 8; i++) { + if (m_pcm93chart_array[i]) + m_pcm93chart_array[i]->m_render_region.Clear(); + } + + OCPNRegion vpr_empty = full_screen_region; + + OCPNRegion chart_region = + GetValidScreenCanvasRegion(vp, full_screen_region); + m_pcm93chart_current->m_render_region = chart_region; // update + + if (!chart_region.IsEmpty()) vpr_empty.Subtract(chart_region); + + if (!vpr_empty.Empty() && + m_cmscale) // This chart scale does not fully cover the region + { + // Save the current cm93 chart pointer for restoration later + cm93chart *m_pcm93chart_save = m_pcm93chart_current; + + int cmscale_next = m_cmscale; + + while (!vpr_empty.Empty() && cmscale_next) { + // get the next smaller scale chart + cmscale_next--; + m_cmscale = PrepareChartScale(vp, cmscale_next, false); + + if (m_pcm93chart_current) { + OCPNRegion sscale_region = + GetValidScreenCanvasRegion(vp, full_screen_region); + sscale_region.Intersect(vpr_empty); + m_pcm93chart_current->m_render_region = sscale_region; + + // Update the remaining empty region + if (!sscale_region.IsEmpty()) vpr_empty.Subtract(sscale_region); + } + + } // while + + // restore the base chart pointer + m_pcm93chart_current = m_pcm93chart_save; + } + } + } +} + +void cm93compchart::SetSpecialCellIndexOffset(int cell_index, int object_id, + int subcell, int xoff, int yoff) { + m_special_offset_x = xoff; + m_special_offset_y = yoff; + + if (m_pcm93chart_current) + m_pcm93chart_current->SetUserOffsets(cell_index, object_id, subcell, xoff, + yoff); +} + +bool cm93compchart::RenderNextSmallerCellOutlines(ocpnDC &dc, ViewPort &vp, + ChartCanvas *cc) { + if (m_cmscale >= 7) return false; +#ifdef ocpnUSE_GL + glChartCanvas *glcc = cc->GetglCanvas(); + if (!glcc) return false; +#else + return false; +#endif + + int nss_max; + + int nss = m_cmscale + 1; + + // A little magic here. + // Drawing all larger scale cell outlines is way too expensive. + // So, stop the loop after we have rendered "something" + // But don't stop at all if the viewport scale is less than 3 million. + // This will have the effect of bringing in outlines of isolated large + // scale cells embedded within small scale cells, like isolated islands in + // the Pacific. + bool bdrawn = false; + + nss_max = 7; + +#if 0 /* only if chart outlines are rendered grounded to the charts */ + if(g_bopengl) { /* for opengl: lets keep this simple yet also functioning + unlike the unbounded version (which is interesting) + the small update rectangles normally encountered when panning + can cause too many charts to load */ + if(nss_max > m_cmscale+3) + nss_max = m_cmscale+3; + } +#endif + while (nss <= nss_max && (!bdrawn || (vp.chart_scale < 3e6))) { + cm93chart *psc = m_pcm93chart_array[nss]; + + if (!psc) { + m_pcm93chart_array[nss] = new cm93chart(); + psc = m_pcm93chart_array[nss]; + + wxChar ext = (wxChar)('A' + nss - 1); + if (nss == 0) ext = 'Z'; + + wxString file_dummy = "CM93."; + file_dummy << ext; + + psc->SetCM93Dict(m_pDictComposite); + psc->SetCM93Prefix(m_prefixComposite); + psc->SetCM93Manager(m_pcm93mgr); + + psc->SetColorScheme(m_global_color_scheme); + psc->Init(file_dummy, FULL_INIT); + } + + if (nss != 1) { // skip rendering the A scale outlines + + // Make sure the covr bounding box is complete + psc->UpdateCovrSet(&vp); + + // Render the chart outlines + covr_set *pcover = psc->GetCoverSet(); + + for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) { + M_COVR_Desc *mcd = pcover->GetCover(im); + + if (vp.GetBBox().IntersectOut(mcd->m_covr_bbox)) continue; + + wxPoint *pwp = psc->GetDrawBuffer(mcd->m_nvertices); + bdrawn = RenderCellOutlines(dc, vp, pwp, mcd); + } + } + nss++; + } + +#ifdef ocpnUSE_GL + if (g_bopengl) { + glDisable(GL_LINE_STIPPLE); + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); + } +#endif + return true; +} + +bool cm93compchart::RenderCellOutlines(ocpnDC &dc, ViewPort &vp, wxPoint *pwp, + M_COVR_Desc *mcd) { + float_2Dpt *p = mcd->pvertices; + int np = mcd->m_nvertices; + + for (int ip = 0; ip < np; ip++, p++) { + pwp[ip] = vp.GetPixFromLL(p->y, p->x); + + // Outlines stored in MCDs are not adjusted for offsets + pwp[ip].x -= mcd->user_xoff * vp.view_scale_ppm; + pwp[ip].y -= mcd->user_yoff * vp.view_scale_ppm; + } + // Scrub the points + // looking for segments for which the wrong longitude decision was made + // TODO all this mole needs to be rethought, again + wxPoint p0 = pwp[0]; + for (int ip = 1; ip < np; ip++) { + if (((p0.x > vp.pix_width) && (pwp[ip].x < 0)) || + ((p0.x < 0) && (pwp[ip].x > vp.pix_width))) + return false; + + p0 = pwp[ip]; + } + + dc.DrawLines(mcd->m_nvertices, pwp, 0, 0, false); + return true; +} + +void cm93compchart::GetPointPix(ObjRazRules *rzRules, float rlat, float rlon, + wxPoint *r) { + m_pcm93chart_current->GetPointPix(rzRules, rlat, rlon, r); +} + +void cm93compchart::GetPointPix(ObjRazRules *rzRules, wxPoint2DDouble *en, + wxPoint *r, int nPoints) { + m_pcm93chart_current->GetPointPix(rzRules, en, r, nPoints); +} + +void cm93compchart::GetPixPoint(int pixx, int pixy, double *plat, double *plon, + ViewPort *vpt) { + m_pcm93chart_current->GetPixPoint(pixx, pixy, plat, plon, vpt); +} + +void cm93compchart::UpdateLUPs(s57chart *pOwner) { + for (int i = 0; i < 8; i++) { + if (m_pcm93chart_array[i]) m_pcm93chart_array[i]->UpdateLUPs(pOwner); + } +} + +std::list *cm93compchart::GetAssociatedObjects(S57Obj *obj) { + if (m_pcm93chart_current) + return m_pcm93chart_current->GetAssociatedObjects(obj); + else + return NULL; +} + +void cm93compchart::InvalidateCache() { + for (int i = 0; i < 8; i++) { + if (m_pcm93chart_array[i]) m_pcm93chart_array[i]->InvalidateCache(); + } +} + +void cm93compchart::ForceEdgePriorityEvaluate() { + for (int i = 0; i < 8; i++) { + if (m_pcm93chart_array[i]) + m_pcm93chart_array[i]->ForceEdgePriorityEvaluate(); + } +} + +void cm93compchart::SetColorScheme(ColorScheme cs, bool bApplyImmediate) { + m_global_color_scheme = cs; + + for (int i = 0; i < 8; i++) { + if (m_pcm93chart_array[i]) + m_pcm93chart_array[i]->SetColorScheme(cs, bApplyImmediate); + } +} + +ListOfObjRazRules *cm93compchart::GetObjRuleListAtLatLon(float lat, float lon, + float select_radius, + ViewPort *VPoint, + int selection_mask) { + float alon = lon; + + ViewPort vp; // needs a new ViewPort also for ObjectRenderCheck() + vp = *VPoint; + + if (!VPoint->b_quilt) + if (m_pcm93chart_current) + return m_pcm93chart_current->GetObjRuleListAtLatLon(lat, alon, + select_radius, &vp); + else { + // As default, return an empty list + ListOfObjRazRules *ret_ptr = new ListOfObjRazRules; + return ret_ptr; + } + else { + UpdateRenderRegions(*VPoint); + + // Search all of the subcharts, looking for the one whose render region + // contains the requested point + wxPoint p = VPoint->GetPixFromLL(lat, lon); + + for (int i = 0; i < 8; i++) { + if (m_pcm93chart_array[i]) { + if (!m_pcm93chart_array[i]->m_render_region.IsEmpty()) { + if (wxInRegion == m_pcm93chart_array[i]->m_render_region.Contains(p)) + return m_pcm93chart_array[i]->GetObjRuleListAtLatLon( + lat, alon, select_radius, &vp, selection_mask); + } + } + } + + // As default, return an empty list + ListOfObjRazRules *ret_ptr = new ListOfObjRazRules; + + return ret_ptr; + } +} + +std::unordered_map &cm93compchart::Get_ve_hash() { + return m_pcm93chart_current->Get_ve_hash(); +} + +std::unordered_map &cm93compchart::Get_vc_hash() { + return m_pcm93chart_current->Get_vc_hash(); +} + +bool cm93compchart::AdjustVP(ViewPort &vp_last, ViewPort &vp_proposed) { +#ifdef ocpnUSE_GL + if (g_bopengl) { + /* need a full refresh if not in quilted mode, and the cell changed */ + // TODO re-add this for multicanvas + // if ( !vp_last.b_quilt && m_last_cell_adjustvp != m_pcm93chart_current ) + // glChartCanvas::Invalidate(); + + m_last_cell_adjustvp = m_pcm93chart_current; + } +#endif + + // All the below logic is slow, and really redundant. + // so, declare that cm93 charts do not require adjustment for optimum + // performance. + + if (m_pcm93chart_current) return false; + + // This may be a partial screen render + // If it is, the cmscale value on this render must match the same parameter + // on the last render. + // If it does not, the partial render will not quilt correctly with the + // previous data Detect this case, and indicate that the entire screen must + // be rendered. + + int cmscale = GetCMScaleFromVP( + vp_proposed); // This is the scale that should be used, based on the vp + + int cmscale_actual = PrepareChartScale( + vp_proposed, cmscale, + false); // this is the scale that will be used, based on cell coverage + + if (g_bDebugCM93) + printf(" In AdjustVP, adjustment subchart scale is %c\n", + (char)('A' + cmscale_actual - 1)); + + // We always need to do a VP adjustment, independent of this method's + // return value. so, do an AdjustVP() based on the chart scale that WILL BE + // USED And be sure to return false if that adjust method suggests so. + + bool single_adjust = false; + if (m_pcm93chart_array[cmscale_actual]) + single_adjust = + m_pcm93chart_array[cmscale_actual]->AdjustVP(vp_last, vp_proposed); + + if (m_cmscale != cmscale_actual) return false; + + // In quilt mode, always indicate that the adjusted vp requires a full + // repaint + if (vp_last.b_quilt) return false; + + return single_adjust; +} + +ThumbData *cm93compchart::GetThumbData(int tnx, int tny, float lat, float lon) { + return (ThumbData *)NULL; +} + +InitReturn cm93compchart::CreateHeaderData() { + m_Chart_Scale = 20000000; + + // Read the root directory, getting subdirectories to build a small + // scale coverage region + wxRect extent_rect; + + wxDir dirt(m_prefixComposite); + wxString candidate; + wxRegEx test("[0-9]+"); + + bool b_cont = dirt.GetFirst(&candidate); + + while (b_cont) { + if (test.Matches(candidate) && (candidate.Len() == 8)) { + wxString dir = m_prefixComposite; + dir += candidate; + if (wxDir::Exists(dir)) { + wxFileName name(dir); + wxString num_name = name.GetName(); + long number; + if (num_name.ToLong(&number)) { + int ilat = number / 10000; + int ilon = number % 10000; + + int lat_base = (ilat - 270) / 3.; + int lon_base = ilon / 3.; + extent_rect.Union(wxRect(lon_base, lat_base, 20, 20)); + } + } + } + b_cont = dirt.GetNext(&candidate); + } + + // Specify the chart coverage + m_FullExtent.ELON = ((double)extent_rect.x + (double)extent_rect.width); + m_FullExtent.WLON = ((double)extent_rect.x); + m_FullExtent.NLAT = ((double)extent_rect.y + (double)extent_rect.height); + m_FullExtent.SLAT = ((double)extent_rect.y); + m_bExtentSet = true; + + // Populate one M_COVR Entry + m_nCOVREntries = 1; + m_pCOVRTablePoints = (int *)malloc(sizeof(int)); + *m_pCOVRTablePoints = 4; + m_pCOVRTable = (float **)malloc(sizeof(float *)); + float *pf = (float *)malloc(2 * 4 * sizeof(float)); + *m_pCOVRTable = pf; + float *pfe = pf; + + *pfe++ = m_FullExtent.NLAT; // LatMax; + *pfe++ = m_FullExtent.WLON; // LonMin; + + *pfe++ = m_FullExtent.NLAT; // LatMax; + *pfe++ = m_FullExtent.ELON; // LonMax; + + *pfe++ = m_FullExtent.SLAT; // LatMin; + *pfe++ = m_FullExtent.ELON; // LonMax; + + *pfe++ = m_FullExtent.SLAT; // LatMin; + *pfe++ = m_FullExtent.WLON; // LonMin; + + return INIT_OK; +} + +cm93_dictionary *cm93compchart::FindAndLoadDictFromDir(const wxString &dir) { + cm93_dictionary *retval = NULL; + cm93_dictionary *pdict = new cm93_dictionary(); + + // Quick look at the supplied directory... + if (pdict->LoadDictionary(dir)) return pdict; + + // Otherwise, search for the dictionary files all along the path of the + // passed parameter + + wxString path = dir; + wxString target; + unsigned int i = 0; + + while (i < path.Len()) { + target.Append(path[i]); + if (path[i] == wxFileName::GetPathSeparator()) { + // wxString msg = _T ( " Looking for CM93 dictionary in " + // ); msg.Append ( target ); wxLogMessage ( msg ); + + if (pdict->LoadDictionary(target)) { + retval = pdict; + break; + } + } + i++; + } + + if (NULL != retval) // Found it.... + return retval; + + // Dictionary was not found in linear path of supplied dir. + // Could be on branch, so, look at entire tree the hard way. + + wxFileName fnc(dir); + wxString found_dict_file_name; + + bool bdone = false; + while (!bdone) { + path = fnc.GetPath(wxPATH_GET_VOLUME); // get path without sep + + wxString msg = " Looking harder for CM93 dictionary in "; + msg.Append(path); + wxLogMessage(msg); + + if ((path.Len() == 0) || path.IsSameAs(fnc.GetPathSeparator())) { + bdone = true; + wxLogMessage("Early break1"); + break; + } + + // Abort the search loop if the directory tree does not contain some + // indication of CM93 + if ((wxNOT_FOUND == path.Lower().Find("cm93"))) { + bdone = true; + wxLogMessage("Early break2"); + break; + } + + // Search here + // This takes a while to search a fully populated cm93 tree.... + wxDir dir(path); + + if (dir.IsOpened()) { + // Find the dictionary name, case insensitively + FindCM93Dictionary cm93Dictionary(found_dict_file_name); + dir.Traverse(cm93Dictionary); + bdone = found_dict_file_name.Len() != 0; + } + + fnc.Assign(path); // convert the path to a filename for next loop + } + + if (found_dict_file_name.Len()) { + wxFileName fnd(found_dict_file_name); + wxString dpath = + fnd.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME)); + + if (pdict->LoadDictionary(dpath)) retval = pdict; + } + + if (NULL == retval) delete pdict; + + return retval; +} + +void cm93compchart::CloseandReopenCurrentSubchart() { + delete m_pcm93chart_current; + m_pcm93chart_current = NULL; + m_pcm93chart_array[m_cmscale] = NULL; + + SetVPParms(m_vpt); + InvalidateCache(); +} + +class CM93OffsetDialog; + +enum { + tlCELL = 0, + tlMCOVR, + tlSCALE, + tlXOFF, + tlYOFF, + tlUXOFF, + tlUYOFF, +}; // OCPNOffsetListCtrl Columns; + +//--------------------------------------------------------------------------------------- +// OCPNOffsetListCtrl Definition +//--------------------------------------------------------------------------------------- +class OCPNOffsetListCtrl : public wxListCtrl { +public: + OCPNOffsetListCtrl(CM93OffsetDialog *parent, wxWindowID id, + const wxPoint &pos, const wxSize &size, long style); + ~OCPNOffsetListCtrl(); + + wxString OnGetItemText(long item, long column) const; + int OnGetItemColumnImage(long item, long column) const; + + CM93OffsetDialog *m_parent; +}; + +OCPNOffsetListCtrl::OCPNOffsetListCtrl(CM93OffsetDialog *parent, wxWindowID id, + const wxPoint &pos, const wxSize &size, + long style) + : wxListCtrl(parent, id, pos, size, style) { + m_parent = parent; +} + +OCPNOffsetListCtrl::~OCPNOffsetListCtrl() {} + +wxString OCPNOffsetListCtrl::OnGetItemText(long item, long column) const { + wxString ret; + M_COVR_Desc *pmcd = m_parent->m_pcovr_array[item]; + + switch (column) { + case tlCELL: { + ret.Printf("%d", pmcd->m_cell_index); + if (((int)'0') == pmcd->m_subcell) + ret.Prepend("0"); + else { + char t = (char)pmcd->m_subcell; + wxString p; + p.Printf("%c", t); + ret.Prepend(p); + } + + break; + } + case tlMCOVR: + ret.Printf("%d", pmcd->m_object_id); + break; + + case tlSCALE: + ret = m_parent->m_selected_chart_scale_char; + break; + + case tlXOFF: + ret.Printf("%g", pmcd->transform_WGS84_offset_x); + break; + + case tlYOFF: + ret.Printf("%g", pmcd->transform_WGS84_offset_y); + break; + + case tlUXOFF: + ret.Printf("%6.0f", pmcd->user_xoff * pmcd->m_centerlat_cos); + break; + + case tlUYOFF: + ret.Printf("%6.0f", pmcd->user_yoff * pmcd->m_centerlat_cos); + break; + + default: + break; + } + return ret; +} + +int OCPNOffsetListCtrl::OnGetItemColumnImage(long item, long column) const { + return -1; +} + +//--------------------------------------------------------------------------------------- +// CM93OffsetDialog Implementation +//--------------------------------------------------------------------------------------- + +IMPLEMENT_CLASS(CM93OffsetDialog, wxDialog) + +BEGIN_EVENT_TABLE(CM93OffsetDialog, wxDialog) +EVT_CLOSE(CM93OffsetDialog::OnClose) +END_EVENT_TABLE() + +CM93OffsetDialog::CM93OffsetDialog(wxWindow *parent) { + m_pparent = parent; + m_pcompchart = NULL; + + m_xoff = 0; + m_yoff = 0; + + m_selected_list_index = -1; + m_selected_cell_index = 0; + + long wstyle = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER; + wxDialog::Create(parent, -1, _("OpenCPN CM93 Cell Offset Adjustments"), + wxPoint(0, 0), wxSize(800, 200), wstyle); + + wxFont *qFont = GetOCPNScaledFont(_("Dialog")); + SetFont(*qFont); + + // A top-level sizer + wxBoxSizer *topSizer = new wxBoxSizer(wxHORIZONTAL); + SetSizer(topSizer); + + int width; + + long flags = wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_HRULES | wxLC_VRULES | + wxBORDER_SUNKEN; +#ifndef __WXQT__ + flags |= wxLC_VIRTUAL; +#endif + + m_pListCtrlMCOVRs = + new OCPNOffsetListCtrl(this, -1, wxDefaultPosition, wxDefaultSize, flags); + + m_pListCtrlMCOVRs->Connect( + wxEVT_COMMAND_LIST_ITEM_SELECTED, + wxListEventHandler(CM93OffsetDialog::OnCellSelected), NULL, this); + + int dx = GetCharWidth(); + int dy = GetCharHeight(); + + width = dx * 10; + m_pListCtrlMCOVRs->InsertColumn(tlCELL, _("Cell"), wxLIST_FORMAT_LEFT, width); + + // width = 80; + m_pListCtrlMCOVRs->InsertColumn(tlMCOVR, _("M_COVR ID"), wxLIST_FORMAT_CENTER, + width); + + // width = 80; + m_pListCtrlMCOVRs->InsertColumn(tlSCALE, _("Cell Scale"), + wxLIST_FORMAT_CENTER, width); + + // width = 90; + m_pListCtrlMCOVRs->InsertColumn(tlXOFF, _("wgsox"), wxLIST_FORMAT_CENTER, + width); + + // width = 90; + m_pListCtrlMCOVRs->InsertColumn(tlYOFF, _("wgsoy"), wxLIST_FORMAT_CENTER, + width); + + // width = 90; + m_pListCtrlMCOVRs->InsertColumn(tlUXOFF, _("User X Offset"), + wxLIST_FORMAT_CENTER, width); + + // width = 90; + m_pListCtrlMCOVRs->InsertColumn(tlUYOFF, _("User Y Offset"), + wxLIST_FORMAT_CENTER, width); + + topSizer->Add(m_pListCtrlMCOVRs, 1, wxEXPAND | wxALL, 0); + + wxBoxSizer *boxSizer02 = new wxBoxSizer(wxVERTICAL); + boxSizer02->AddSpacer(22); + + wxStaticText *pStaticTextXoff = new wxStaticText( + this, wxID_ANY, + wxString::Format("%s (%s)", _("User X Offset"), _("meters")), + wxDefaultPosition, wxDefaultSize, 0); + boxSizer02->Add(pStaticTextXoff, 0, wxALL, 0); + + m_pSpinCtrlXoff = + new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, + wxSize(50, -1), wxSP_ARROW_KEYS, -10000, 10000, 0); + m_pSpinCtrlXoff->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, + wxCommandEventHandler(CM93OffsetDialog::OnOffSetSet), + NULL, this); + boxSizer02->Add(m_pSpinCtrlXoff, 0, wxEXPAND | wxALL, 0); + + wxStaticText *pStaticTextYoff = new wxStaticText( + this, wxID_ANY, + wxString::Format("%s (%s)", _("User Y Offset"), _("meters")), + wxDefaultPosition, wxDefaultSize, 0); + boxSizer02->Add(pStaticTextYoff, 0, wxALL, 0); + + m_pSpinCtrlYoff = + new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, + wxSize(50, -1), wxSP_ARROW_KEYS, -10000, 10000, 0); + m_pSpinCtrlYoff->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, + wxCommandEventHandler(CM93OffsetDialog::OnOffSetSet), + NULL, this); + boxSizer02->Add(m_pSpinCtrlYoff, 0, wxEXPAND | wxALL, 0); + + m_OKButton = new wxButton(this, wxID_ANY, _("OK"), wxDefaultPosition, + wxDefaultSize, 0); + m_OKButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, + wxCommandEventHandler(CM93OffsetDialog::OnOK), NULL, + this); + boxSizer02->Add(m_OKButton, 0, wxALL, 5); + m_OKButton->SetDefault(); + + topSizer->Add(boxSizer02, 0, wxEXPAND | wxALL, 2); + + wxSize sz(800, dy * 8); +#ifdef __WXQT__ + sz = wxGetDisplaySize(); + sz.y = dy * 8; +#endif + SetSize(sz); + + topSizer->Layout(); + + // This is silly, but seems to be required for __WXMSW__ build + // If not done, the SECOND invocation of dialog fails to expand the list to + // the full wxSizer size.... + SetSize(GetSize().x, GetSize().y - 1); + + SetColorScheme(); + + // GetSizer()->SetSizeHints(this); + Centre(); + +#ifdef __WXQT__ + Move(-1, 100); +#endif +} + +CM93OffsetDialog::~CM93OffsetDialog() { g_pCM93OffsetDialog = NULL; } + +void CM93OffsetDialog::OnClose(wxCloseEvent &event) { + if (m_pcompchart) { + m_pcompchart->SetSpecialOutlineCellIndex(0, 0, 0); + + m_pcompchart->InvalidateCache(); + + if (m_pparent) { + m_pparent->Refresh(true); + top_frame::Get()->InvalidateAllGL(); + } + } + + if (m_pListCtrlMCOVRs->GetItemCount() > m_selected_list_index) + m_pListCtrlMCOVRs->SetItemState(m_selected_list_index, 0, + wxLIST_STATE_SELECTED); + + Hide(); +} + +void CM93OffsetDialog::OnOK(wxCommandEvent &event) { +#ifdef __WXQT__ + UpdateOffsets(); +#endif + Close(); +} + +void CM93OffsetDialog::SetCM93Chart(cm93compchart *pchart) { + m_pcompchart = pchart; +} + +void CM93OffsetDialog::OnOffSetSet(wxCommandEvent &event) { + m_xoff = m_pSpinCtrlXoff->GetValue() / m_centerlat_cos; + m_yoff = m_pSpinCtrlYoff->GetValue() / m_centerlat_cos; + +#ifndef __WXQT__ + UpdateOffsets(); +#endif +} + +void CM93OffsetDialog::UpdateOffsets() { + if (m_pcompchart && m_selected_cell_index) { + // Set the offsets of the selected cell/object + m_pcompchart->SetSpecialCellIndexOffset(m_selected_cell_index, + m_selected_object_id, + m_selected_subcell, m_xoff, m_yoff); + + // Closing the current cell will record the offsets in the M_COVR cache + // file Re-opening will then refresh the M_COVRs in the cover set + AbstractPlatform::ShowBusySpinner(); + m_pcompchart->CloseandReopenCurrentSubchart(); + AbstractPlatform::HideBusySpinner(); + + if (m_pparent) { + m_pparent->Refresh(true); + top_frame::Get()->InvalidateAllGL(); + } + } +} + +void CM93OffsetDialog::SetColorScheme() { DimeControl(this); } + +void CM93OffsetDialog::OnCellSelected(wxListEvent &event) { + if (m_pcompchart) { + m_selected_list_index = event.GetIndex(); + + M_COVR_Desc *mcd = m_pcovr_array.Item(event.GetIndex()); + + if (m_selected_list_index > m_pListCtrlMCOVRs->GetItemCount()) + return; // error + + cm93chart *pchart = m_pcompchart->GetCurrentSingleScaleChart(); + if (pchart) { + M_COVR_Desc *cached_mcd = pchart->GetCoverSet()->Find_MCD( + mcd->m_cell_index, mcd->m_object_id, mcd->m_subcell); + if (cached_mcd) { + m_pSpinCtrlXoff->SetValue( + wxRound(cached_mcd->user_xoff * cached_mcd->m_centerlat_cos)); + m_pSpinCtrlYoff->SetValue( + wxRound(cached_mcd->user_yoff * cached_mcd->m_centerlat_cos)); + } + } + + m_pcompchart->SetSpecialOutlineCellIndex(mcd->m_cell_index, + mcd->m_object_id, mcd->m_subcell); + + m_selected_cell_index = mcd->m_cell_index; + m_selected_object_id = mcd->m_object_id; + m_selected_subcell = mcd->m_subcell; + m_centerlat_cos = mcd->m_centerlat_cos; + + m_pcompchart->InvalidateCache(); + + if (m_pparent) { + m_pparent->Refresh(true); + top_frame::Get()->InvalidateAllGL(); + } + } +} + +void CM93OffsetDialog::UpdateMCOVRList(const ViewPort &vpt) { + if (m_pcompchart) { + // In single chart mode, there is but one cm93chart (i.e. one "scale + // value") shown at any one time + cm93chart *pchart = m_pcompchart->GetCurrentSingleScaleChart(); + + if (pchart) { + m_selected_chart_scale_char = pchart->GetScaleChar(); + + m_pcovr_array.Clear(); + + // Get an array of cell indicies at the current viewport + std::vector cell_array = pchart->GetVPCellArray(vpt); + + ViewPort vp; + vp = vpt; + + // Get the cover set for the cm93chart + // and walk the set looking for matches to the viewport referenced cell + // array This will give us the covr descriptors of interest + covr_set *pcover = pchart->GetCoverSet(); + + for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) { + M_COVR_Desc *mcd = pcover->GetCover(im); + + for (unsigned int icell = 0; icell < cell_array.size(); icell++) { + if (cell_array[icell] == mcd->m_cell_index) { + wxPoint *pwp = pchart->GetDrawBuffer(mcd->m_nvertices); + OCPNRegion rgn = mcd->GetRegion(vp, pwp); + + // if( + // !vp.GetBBox().IntersectOut(mcd->m_covr_bbox)) + if (rgn.Contains(0, 0, vpt.pix_width, vpt.pix_height) != + wxOutRegion) + m_pcovr_array.Add(mcd); + } + } + } + + // Try to find and maintain the correct list selection, even though the + // list contents may have changed + int sel_index = -1; + for (unsigned int im = 0; im < m_pcovr_array.size(); im++) { + M_COVR_Desc *mcd = m_pcovr_array[im]; + if ((m_selected_cell_index == mcd->m_cell_index) && + (m_selected_object_id == mcd->m_object_id) && + (m_selected_subcell == mcd->m_subcell)) { + sel_index = im; + break; + } + } + + if (!m_pListCtrlMCOVRs->IsVirtual()) { + if (m_pListCtrlMCOVRs->GetItemCount()) + m_pListCtrlMCOVRs->DeleteAllItems(); + + for (unsigned int i = 0; i < m_pcovr_array.GetCount(); i++) { + wxListItem item; + item.SetId(i); + m_pListCtrlMCOVRs->InsertItem(item); + for (int j = 0; j < tlUYOFF + 1; j++) { + item.SetColumn(j); + item.SetText(m_pListCtrlMCOVRs->OnGetItemText(i, j)); + m_pListCtrlMCOVRs->SetItem(item); + } + } + } else { + m_pListCtrlMCOVRs->SetItemCount(m_pcovr_array.GetCount()); + } + + if (-1 != sel_index) + m_pListCtrlMCOVRs->SetItemState(sel_index, wxLIST_STATE_SELECTED, + wxLIST_STATE_SELECTED); + else + m_pListCtrlMCOVRs->SetItemState(sel_index, 0, + wxLIST_STATE_SELECTED); // deselect all + + m_pListCtrlMCOVRs->Refresh(true); + } +#ifdef __WXMSW__ + m_pListCtrlMCOVRs->Refresh(false); +#endif + } +} diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.cpp new file mode 100644 index 0000000..41574a6 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.cpp @@ -0,0 +1,305 @@ +/*************************************************************************** + * + * Project: OpenCPN + * + *************************************************************************** + * Copyright (C) 2017 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +#include "Cs52_shaders.h" + +#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL) + +#ifdef USE_ANDROID_GLES2 +#include +#include "qdebug.h" +#endif + +#ifdef USE_ANDROID_GLES2 +const GLchar* Cpreamble = +"\n"; +#else +const GLchar* Cpreamble = +"#version 120\n" +"#define precision\n" +"#define lowp\n" +"#define mediump\n" +"#define highp\n"; +#endif + + +// Simple colored triangle shader + +static const GLchar* xcolor_tri_vertex_shader_source = + "attribute vec2 position;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "uniform vec4 color;\n" + "varying vec4 fragColor;\n" + "void main() {\n" + " fragColor = color;\n" + " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n" + "}\n"; + +static const GLchar* xcolor_tri_fragment_shader_source = + "precision lowp float;\n" + "varying vec4 fragColor;\n" + "void main() {\n" + " gl_FragColor = fragColor;\n" + "}\n"; + +// Simple 2D texture shader +static const GLchar* xtexture_2D_vertex_shader_source = + "attribute vec2 position;\n" + "attribute vec2 aUV;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "varying vec2 varCoord;\n" + "void main() {\n" + " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n" + " varCoord = aUV;\n" + "}\n"; + +static const GLchar* xtexture_2D_fragment_shader_source = + "precision lowp float;\n" + "uniform sampler2D uTex;\n" + "varying vec2 varCoord;\n" + "void main() {\n" + " gl_FragColor = texture2D(uTex, varCoord);\n" + "}\n"; + +// 2D texture shader with color modulation, used for colored text +static const GLchar * xtexture_2D_ColorMod_vertex_shader_source = + "precision highp float;\n" + "attribute vec2 position;\n" + "attribute vec2 aUV;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "varying vec2 varCoord;\n" + "void main() {\n" + " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n" + " varCoord = aUV;\n" + "}\n"; + +static const GLchar * xtexture_2D_ColorMod_fragment_shader_source = + "precision highp float;\n" + "uniform sampler2D uTex;\n" + "uniform vec4 color;\n" + "varying vec2 varCoord;\n" + "void main() {\n" + " vec4 col=texture2D(uTex, varCoord);\n" + " gl_FragColor = color;\n" + " gl_FragColor.a = col.a;\n" + "}\n"; + +// Circle shader + +static const GLchar* xcircle_filled_vertex_shader_source = + "precision highp float;\n" + "attribute vec2 aPos;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "void main() {\n" + " gl_Position = MVMatrix * TransformMatrix * vec4(aPos, 0.0, 1.0);\n" + "}\n"; + +static const GLchar* xcircle_filled_fragment_shader_source = + "precision highp float;\n" + "uniform float border_width;\n" + "uniform float circle_radius;\n" + "uniform vec4 circle_color;\n" + "uniform vec4 border_color;\n" + "uniform vec2 circle_center;\n" + "void main(){\n" + "float d = distance(gl_FragCoord.xy, circle_center);\n" + "if (d < (circle_radius - border_width)) { gl_FragColor = circle_color; }\n" + "else if (d < circle_radius) { gl_FragColor = border_color; }\n" + "else { gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); }\n" + "}\n"; + +// Alpha 2D texture shader +static const GLchar* xtexture_2DA_vertex_shader_source = + "attribute vec2 position;\n" + "attribute vec2 aUV;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "varying vec2 varCoord;\n" + "void main() {\n" + " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n" + " varCoord = aUV;\n" + "}\n"; + +static const GLchar* xtexture_2DA_fragment_shader_source = + "precision lowp float;\n" + "uniform sampler2D uTex;\n" + "varying vec2 varCoord;\n" + "uniform vec4 color;\n" + "void main() {\n" + " gl_FragColor = texture2D(uTex, varCoord) + color;\n" + "}\n"; + + +//https://vitaliburkov.wordpress.com/2016/09/17/simple-and-fast-high-quality-antialiased-lines-with-opengl/ +static const GLchar* xAALine_vertex_shader_source = + "uniform vec2 uViewPort; //Width and Height of the viewport\n" + "varying vec2 vLineCenter;\n" + "attribute vec2 position;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "void main()\n" + "{\n" + " vec4 pp = MVMatrix * vec4(position, 0.0, 1.0);\n" + " gl_Position = pp;\n" + " vec2 vp = uViewPort;\n" + " vLineCenter = 0.5*(pp.xy + vec2(1, 1))*vp;\n" + "}\n"; + + +static const GLchar* xAALine_fragment_shader_source = + "precision mediump float;\n" + "uniform float uLineWidth;\n" + "uniform vec4 color;\n" + "uniform float uBlendFactor; //1.5..2.5\n" + "varying vec2 vLineCenter;\n" + "void main()\n" + "{\n" + " vec4 col = color;\n" + " float d = length(vLineCenter-gl_FragCoord.xy);\n" + " float w = uLineWidth;\n" + " if (d>w)\n" + " col.w = 0.0;\n" + " else{\n" + " if(float((w/2-d)/(w/2)) < .5){\n" + " //col.w *= pow(float((w-d)/w), uBlendFactor);\n" + " col.w *= pow(float((w/2-d)/(w/2)), uBlendFactor);\n" + " }\n" + " }\n" + " gl_FragColor = col;\n" + "}\n"; + + +CGLShaderProgram *pCAALine_shader_program[2]; +CGLShaderProgram *pCcolor_tri_shader_program[2]; +CGLShaderProgram *pCtexture_2D_shader_program[2]; +CGLShaderProgram *pCcircle_filled_shader_program[2]; +CGLShaderProgram *pCtexture_2DA_shader_program[2]; +CGLShaderProgram *pCtexture_2D_Color_shader_program[2]; + +bool bCShadersLoaded[2]; + +bool loadCShaders(int index) { + // Are the shaders ready? + if (bCShadersLoaded[index]) { + reConfigureCShaders(index); + return true; + } + + bool ret_val = true; + + // Simple colored triangle shader + if (!pCcolor_tri_shader_program[index]) { + CGLShaderProgram *shaderProgram = new CGLShaderProgram; + shaderProgram->addShaderFromSource(xcolor_tri_vertex_shader_source, GL_VERTEX_SHADER); + shaderProgram->addShaderFromSource(xcolor_tri_fragment_shader_source, GL_FRAGMENT_SHADER); + shaderProgram->linkProgram(); + + if (shaderProgram->isOK()) + pCcolor_tri_shader_program[index] = shaderProgram; + } + + if (!pCtexture_2D_shader_program[index]) { + CGLShaderProgram *shaderProgram = new CGLShaderProgram; + shaderProgram->addShaderFromSource(xtexture_2D_vertex_shader_source, GL_VERTEX_SHADER); + shaderProgram->addShaderFromSource(xtexture_2D_fragment_shader_source, GL_FRAGMENT_SHADER); + shaderProgram->linkProgram(); + + if (shaderProgram->isOK()) + pCtexture_2D_shader_program[index] = shaderProgram; + } + + if (!pCtexture_2D_Color_shader_program[index]) { + CGLShaderProgram *shaderProgram = new CGLShaderProgram; + shaderProgram->addShaderFromSource(xtexture_2D_ColorMod_vertex_shader_source, GL_VERTEX_SHADER); + shaderProgram->addShaderFromSource(xtexture_2D_ColorMod_fragment_shader_source, GL_FRAGMENT_SHADER); + shaderProgram->linkProgram(); + + if (shaderProgram->isOK()) + pCtexture_2D_Color_shader_program[index] = shaderProgram; + } + +#if 0 + + if (!pcircle_filled_shader_program[index]) { + GLShaderProgram *shaderProgram = new GLShaderProgram; + shaderProgram->addShaderFromSource(circle_filled_vertex_shader_source, GL_VERTEX_SHADER); + shaderProgram->addShaderFromSource(circle_filled_fragment_shader_source, GL_FRAGMENT_SHADER); + shaderProgram->linkProgram(); + + if (shaderProgram->isOK()) + pcircle_filled_shader_program[index] = shaderProgram; + } + + if (!ptexture_2DA_shader_program[index]) { + GLShaderProgram *shaderProgram = new GLShaderProgram; + shaderProgram->addShaderFromSource(texture_2DA_vertex_shader_source, GL_VERTEX_SHADER); + shaderProgram->addShaderFromSource(texture_2DA_fragment_shader_source, GL_FRAGMENT_SHADER); + shaderProgram->linkProgram(); + + if (shaderProgram->isOK()) + ptexture_2DA_shader_program[index] = shaderProgram; + } + + if (!pAALine_shader_program[index]) { + GLShaderProgram *shaderProgram = new GLShaderProgram; + shaderProgram->addShaderFromSource(AALine_fragment_shader_source, GL_FRAGMENT_SHADER); + shaderProgram->addShaderFromSource(AALine_vertex_shader_source, GL_VERTEX_SHADER); + shaderProgram->linkProgram(); + + if (shaderProgram->isOK()) + pAALine_shader_program[index] = shaderProgram; + } + +#endif + + bCShadersLoaded[index] = true; + reConfigureCShaders(index); + + return ret_val; +} + +void reConfigureCShaders(int index) { +} + +void unloadCShaders() { + bCShadersLoaded[0] = bCShadersLoaded[1] = false; +} + +CGLShaderProgram *CGetStaticTriShader() { + CGLShaderProgram *shaderProgram = new CGLShaderProgram; + shaderProgram->addShaderFromSource(xcolor_tri_vertex_shader_source, GL_VERTEX_SHADER); + shaderProgram->addShaderFromSource(xcolor_tri_fragment_shader_source, GL_FRAGMENT_SHADER); + shaderProgram->linkProgram(); + + if (shaderProgram->isOK()) + return shaderProgram; + else + return NULL; +} + + +#endif diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.h new file mode 100644 index 0000000..5e8bf5b --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.h @@ -0,0 +1,186 @@ +/*************************************************************************** + * + * Project: OpenCPN + * + *************************************************************************** + * Copyright (C) 2017 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +#ifndef __XSHADERS_H__ +#define __XSHADERS_H__ + +#include +#ifndef WX_PRECOMP +#include +#endif // precompiled headers + +#include "s52plibGL.h" + +#ifdef __OCPN__ANDROID__ +#include "qdebug.h" +#endif + +#include +#include +#include +#include + +class CGLShaderProgram; + +extern CGLShaderProgram *pAALine_shader_program[2]; +extern CGLShaderProgram *pCcolor_tri_shader_program[2]; +extern CGLShaderProgram *pCtexture_2D_shader_program[2]; +extern CGLShaderProgram *pcircle_filled_shader_program[2]; +extern CGLShaderProgram *ptexture_2DA_shader_program[2]; +extern CGLShaderProgram *pCtexture_2D_Color_shader_program[2]; + +extern const GLchar* Cpreamble; + +class CGLShaderProgram +{ +public: + CGLShaderProgram() : programId_(0), linked_(false) { + programId_ = glCreateProgram(); + } + ~CGLShaderProgram() { } + + bool addShaderFromSource(std::string const &shaderSource, GLenum shaderType) { + char const *shaderCStr = shaderSource.c_str(); + GLuint shaderId = glCreateShader(shaderType); + + GLchar const* files[] = { Cpreamble, shaderCStr }; + GLint lengths[] = { (GLint)strlen(Cpreamble), (GLint)strlen(shaderCStr) }; + + glShaderSource(shaderId, 2, files, lengths); + + glCompileShader(shaderId); + glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success); + if (!success) { + GLint logLength = 0; + glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &logLength); + if (logLength > 0) { + auto log = std::unique_ptr(new char[logLength]); + glGetShaderInfoLog(shaderId, logLength, &logLength, log.get()); + printf("ERROR::SHADER::COMPILATION_FAILED\n%s\n", log.get()); +#ifdef USE_ANDROID_GLES2 + qDebug() << "SHADER COMPILE ERROR " << log.get(); +#endif + } + return false; + } + + glAttachShader(programId_, shaderId); + return true; + } + + bool linkProgram() { + glLinkProgram(programId_); + glGetProgramiv( programId_, GL_LINK_STATUS, &linkSuccess); //requesting the status + if (linkSuccess == GL_FALSE) { + GLint logLength = 0; + glGetShaderiv(programId_, GL_INFO_LOG_LENGTH, &logLength); + if (logLength > 0) { + auto log = std::unique_ptr(new char[logLength]); + glGetShaderInfoLog(programId_, logLength, &logLength, log.get()); + printf("ERROR::SHADER::LINK_FAILED\n%s\n", log.get()); + } + return false; + } + linked_ = true; + return true; + } + + + void Bind() { glUseProgram(programId_); } + void UnBind() { + glDisableVertexAttribArray(0); + glUseProgram(0); + } + + void SetUniform1f( const std::string &name, float value) { + GLint loc = getUniformLocation(name); + glUniform1f( loc, value); + } + void SetUniform2fv( const std::string &name, float *value) { + GLint loc = getUniformLocation(name); + glUniform2fv( loc, 1, value); + } + void SetUniform4fv( const std::string &name, float *value) { + GLint loc = getUniformLocation(name); + glUniform4fv( loc, 1, value); + } + void SetUniform1i( const std::string &name, GLint value) { + GLint loc = getUniformLocation(name); + glUniform1i( loc, value); + } + void SetUniformMatrix4fv( const std::string &name, float *value) { + GLint matloc = getUniformLocation(name); + glUniformMatrix4fv(matloc, 1, GL_FALSE, value); + } + + void SetAttributePointerf( const std::string &name, float *value ){ + GLint aloc = getAttributeLocation( name); + glVertexAttribPointer(aloc, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), value); + glEnableVertexAttribArray(aloc); + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + } + + GLint getAttributeLocation(const std::string &name) { + if(m_attribLocationCache.find(name) != m_attribLocationCache.end()) + return m_attribLocationCache[name]; + + GLint loc = glGetAttribLocation(programId_, name.c_str()); + m_attribLocationCache[name] = loc; + return loc; + } + + + GLuint programId() const { return programId_; } + bool isOK() const { return linked_; } + + GLint getUniformLocation(const std::string &name) { + if(m_uniformLocationCache.find(name) != m_uniformLocationCache.end()) + return m_uniformLocationCache[name]; + + GLint loc = glGetUniformLocation(programId_, name.c_str()); + m_uniformLocationCache[name] = loc; + return loc; + } + +private: + std::unordered_map m_uniformLocationCache; + std::unordered_map m_attribLocationCache; + GLuint programId_; + bool linked_; + GLint success; + GLint linkSuccess; + + +}; + + +bool loadCShaders(int index = 0); +void reConfigureCShaders(int index = 0); +void unloadCShaders(); + +CGLShaderProgram *CGetStaticTriShader(); +#endif diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.cpp new file mode 100755 index 0000000..a8a2792 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.cpp @@ -0,0 +1,179 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: texture OpenGL text rendering built from wxFont + * Author: Sean D'Epagnier + * + *************************************************************************** + * Copyright (C) 2014 Sean D'Epagnier * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +#include + +#include "s52plibGL.h" + +#include "DepthFont.h" +#include "s52_plib_utils.h" + +DepthFont::DepthFont() { + texobj = 0; + m_built = false; + m_scaleFactor = 0; + m_ContentScaleFactor = 1.0; +} + +DepthFont::~DepthFont() { Delete(); } + +void DepthFont::Build(wxFont *font, double scale, double dip_factor) { + /* avoid rebuilding if the parameters are the same */ + if (m_built && (*font == m_font)) return; + + m_font = *font; + m_scaleFactor = scale; + m_dip_factor = dip_factor; + + m_maxglyphw = 0; + m_maxglyphh = 0; + + double scaler = m_ContentScaleFactor * dip_factor; + + wxFont *scaled_font = GetS52Utils()->GetFont(font, scaler); + wxScreenDC sdc; + sdc.SetFont(*scaled_font); + + for (int i = 0; i < 10; i++) { + wxCoord gw, gh; + wxString text; + text = wxString::Format(_T("%d"), i); + wxCoord descent, exlead; + sdc.GetTextExtent(text, &gw, &gh, &descent, &exlead, + scaled_font); // measure the text + + tgi[i].width = gw; + tgi[i].height = gh; // - descent; + + tgi[i].advance = gw; + tgi[i].advance *= dip_factor; + if (i == 1) + m_width_one = gw; + + m_maxglyphw = wxMax(tgi[i].width + tgi[i].advance, m_maxglyphw); + m_maxglyphh = wxMax(tgi[i].height, m_maxglyphh); + } + + int w = 10 * m_maxglyphw; + int h = m_maxglyphh; + + /* make power of 2 */ + + for (tex_w = 1; tex_w < w; tex_w *= 2) + ; + for (tex_h = 1; tex_h < h; tex_h *= 2) + ; + + wxBitmap tbmp(tex_w, tex_h); + wxMemoryDC dc; + dc.SelectObject(tbmp); + dc.SetFont(*scaled_font); + + /* fill bitmap with black */ + dc.SetBackground(wxBrush(wxColour(0, 0, 0))); + dc.Clear(); + + /* draw the text white */ + dc.SetTextForeground(wxColour(255, 255, 255)); + + /* wxPen pen(wxColour( 255, 255, 255 )); + wxBrush brush(wxColour( 255, 255, 255 ), wxTRANSPARENT); + dc.SetPen(pen); + dc.SetBrush(brush); + */ + int row = 0, col = 0; + for (int i = 0; i < 10; i++) { + tgi[i].x = col * m_maxglyphw; + tgi[i].y = row * m_maxglyphh; + + wxString text; + text = wxString::Format(_T("%d"), i); + + dc.DrawText(text, tgi[i].x, tgi[i].y); + + col++; + } + + dc.SelectObject(wxNullBitmap); + + wxImage image = tbmp.ConvertToImage(); + + GLuint format, internalformat; + int stride; + + format = GL_ALPHA; + internalformat = format; + stride = 1; + + unsigned char *imgdata = image.GetData(); + + if (imgdata) { + unsigned char *teximage = (unsigned char *)malloc(stride * tex_w * tex_h); + + for (int j = 0; j < tex_w * tex_h; j++) + for (int k = 0; k < stride; k++) + teximage[j * stride + k] = imgdata[3 * j]; + + glGenTextures(1, &texobj); + glBindTexture(GL_TEXTURE_2D, texobj); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, internalformat, tex_w, tex_h, 0, format, + GL_UNSIGNED_BYTE, teximage); + + free(teximage); + } + + m_built = true; +} + +void DepthFont::Delete() { + if (texobj) { + glDeleteTextures(1, &texobj); + texobj = 0; + } + m_built = false; + m_scaleFactor = 0; +} + +bool DepthFont::GetGLTextureRect(wxRect &texrect, int symIndex) { + if (symIndex < 10) { + texrect.x = tgi[symIndex].x; + texrect.y = tgi[symIndex].y; + texrect.width = tgi[symIndex].width + (m_width_one / 10); + texrect.height = tgi[symIndex].height; + return true; + } else { + texrect.x = tgi[0].x; + texrect.y = tgi[0].y; + texrect.width = tgi[0].width; + texrect.height = tgi[0].height; + return false; + } +} diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.h new file mode 100644 index 0000000..4d4473e --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.h @@ -0,0 +1,72 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: OpenGL text rendering + * Author: David Register, Sean D'Epagnier + * + *************************************************************************** + * Copyright (C) 2020 David Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +#ifndef __DEPTHFONT_H__ +#define __DEPTHFONT_H__ + +#include + +#define SOUND_MAX_GLYPH 50 + +struct SoundTexGlyphInfo { + int x, y, width, height; + float advance; +}; + +class DepthFont { +public: + DepthFont(); + ~DepthFont(); + + void Build(wxFont *font, double scale, double dip_factor = 1.0); + void Delete(); + + bool IsBuilt() { return m_built; } + unsigned int GetTexture() { return texobj; } + bool GetGLTextureRect(wxRect &texrect, int symIndex); + wxSize GLTextureSize() { return wxSize(tex_w, tex_h); }; + double GetScale() { return m_scaleFactor; } + void SetContentScaleFactor(double s){m_ContentScaleFactor = s;} + +private: + wxFont m_font; + + SoundTexGlyphInfo tgi[SOUND_MAX_GLYPH]; + + unsigned int texobj; + int tex_w, tex_h; + int m_maxglyphw; + int m_maxglyphh; + bool m_built; + + float m_dx; + float m_dy; + double m_scaleFactor; + double m_ContentScaleFactor; + int m_width_one; + double m_dip_factor; +}; + +#endif // guard diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.cpp new file mode 100755 index 0000000..40245b7 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.cpp @@ -0,0 +1,542 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: texture OpenGL text rendering built from wxFont + * Author: Sean D'Epagnier + * + *************************************************************************** + * Copyright (C) 2014 Sean D'Epagnier * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +#include + +#ifdef ocpnUSE_GL +#ifdef __OCPN_USE_GLEW__ + #ifndef __OCPN__ANDROID__ + #if defined(_WIN32) + #include "glew.h" + #elif defined(__WXQT__) || defined(__WXGTK__) + #include + #endif + #endif +#endif + + +#if defined(__OCPN__ANDROID__) + #include + #include // this is a cut-down version of gl.h + #include +#elif defined(_WIN32) + #define GL_GLEXT_PROTOTYPES + #include + #include + //typedef void (__stdcall * _GLUfuncptr)(void); +#elif defined(__WXOSX__) + #include + #include + typedef void (* _GLUfuncptr)(); + #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#elif defined(__WXQT__) || defined(__WXGTK__) + #define GL_GLEXT_PROTOTYPES + #include + #include + #include +#endif +#endif + +#include "TexFont.h" +#include "linmath.h" +#ifdef ocpnUSE_GL + #include "Cs52_shaders.h" + +CGLShaderProgram *m_TexFontShader; +#endif + +#include "s52_plib_utils.h" + +TexFont::TexFont() { + texobj = 0; + m_blur = false; + m_built = false; + m_color = wxColor(0, 0, 0); + m_angle = 0; + m_ContentScaleFactor = 1.0; + + m_shadersLoaded = false; + +} + +TexFont::~TexFont() { Delete(); } + +void TexFont::Build(wxFont &font, double scale_factor, double dpi_factor, bool blur) { + /* avoid rebuilding if the parameters are the same */ + if (m_built && (font == m_font) && (blur == m_blur)) return; + + m_font = font; + m_blur = blur; + + m_maxglyphw = 0; + m_maxglyphh = 0; + + double scaler = scale_factor / dpi_factor; + scaler /= m_ContentScaleFactor; + + wxFont *scaled_font = GetS52Utils()->GetFont(&font, scaler); + wxScreenDC sdc; + sdc.SetFont(*scaled_font); + + for (int i = MIN_GLYPH; i < MAX_GLYPH; i++) { + wxCoord gw, gh; + wxString text; + if (i == DEGREE_GLYPH) + text = wxString::Format(_T("%c"), 0x00B0); //_T("?"); + else + text = wxString::Format(_T("%c"), i); + wxCoord descent, exlead; + sdc.GetTextExtent(text, &gw, &gh, &descent, &exlead, + scaled_font); // measure the text + + tgi[i].width = gw; + tgi[i].height = gh; + + tgi[i].advance = gw; + tgi[i].advance *= dpi_factor; + + + m_maxglyphw = wxMax(tgi[i].width, m_maxglyphw); + m_maxglyphh = wxMax(tgi[i].height, m_maxglyphh); + } + + /* add extra pixel to give a border between rows of characters + without this, in some cases a faint line can be see on the edge + from the character above */ + m_maxglyphh++; + + int w = COLS_GLYPHS * m_maxglyphw; + int h = ROWS_GLYPHS * m_maxglyphh; + + wxASSERT(w < 2048 && h < 2048); + + /* make power of 2 */ + for (tex_w = 1; tex_w < w; tex_w *= 2) + ; + for (tex_h = 1; tex_h < h; tex_h *= 2) + ; + + wxBitmap tbmp(tex_w, tex_h); + wxMemoryDC dc; + dc.SelectObject(tbmp); + dc.SetFont(*scaled_font); + + /* fill bitmap with black */ + dc.SetBackground(wxBrush(wxColour(0, 0, 0))); + dc.Clear(); + + /* draw the text white */ + dc.SetTextForeground(wxColour(255, 255, 255)); + + /* wxPen pen(wxColour( 255, 255, 255 )); + wxBrush brush(wxColour( 255, 255, 255 ), wxTRANSPARENT); + dc.SetPen(pen); + dc.SetBrush(brush); + */ + int row = 0, col = 0; + for (int i = MIN_GLYPH; i < MAX_GLYPH; i++) { + if (col == COLS_GLYPHS) { + col = 0; + row++; + } + + tgi[i].x = col * m_maxglyphw; + tgi[i].y = row * m_maxglyphh; + + wxString text; + if (i == DEGREE_GLYPH) + text = wxString::Format(_T("%c"), 0x00B0); //_T("?"); + else + text = wxString::Format(_T("%c"), i); + + dc.DrawText(text, tgi[i].x, tgi[i].y); + + // dc.DrawRectangle(tgi[i].x, tgi[i].y, tgi[i].advance, + // tgi[i].height); + col++; + } + + dc.SelectObject(wxNullBitmap); + + wxImage image = tbmp.ConvertToImage(); + + unsigned int format = 0, internalformat = 0; + int stride; + +#ifdef ocpnUSE_GL + format = GL_ALPHA; +#endif + + internalformat = format; + stride = 1; + + if (m_blur) image = image.Blur(1); + + unsigned char *imgdata = image.GetData(); + + if (imgdata) { + unsigned char *teximage = (unsigned char *)malloc(stride * tex_w * tex_h); + + for (int j = 0; j < tex_w * tex_h; j++) + for (int k = 0; k < stride; k++) + teximage[j * stride + k] = imgdata[3 * j]; + + Delete(); + +#ifdef ocpnUSE_GL + glGenTextures(1, &texobj); + glBindTexture(GL_TEXTURE_2D, texobj); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + GL_NEAREST /*GL_LINEAR*/); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glTexImage2D(GL_TEXTURE_2D, 0, internalformat, tex_w, tex_h, 0, format, + GL_UNSIGNED_BYTE, teximage); +#endif + free(teximage); + + } + + m_built = true; +} + +void TexFont::Delete() { + if (texobj) { +#ifdef ocpnUSE_GL + glDeleteTextures(1, &texobj); +#endif + texobj = 0; + } + m_built = false; +} + +void TexFont::GetTextExtent(const char *string, int *width, int *height) { + int w = 0, h = 0; + + for (int i = 0; string[i]; i++) { + unsigned char c = string[i]; + if (c == '\n') { + h += tgi[(int)'A'].height; + continue; + } + if (c == 0xc2 && (unsigned char)string[i + 1] == 0xb0) { + c = DEGREE_GLYPH; + i++; + } + if (c < MIN_GLYPH || c >= MAX_GLYPH) continue; + + TexGlyphInfo &tgisi = tgi[c]; + w += tgisi.advance; + if (tgisi.height > h) h = tgisi.height; + } + if (width) *width = w; + if (height) *height = h; +} + +void TexFont::GetTextExtent(const wxString &string, int *width, int *height) { + GetTextExtent((const char *)string.ToUTF8(), width, height); +} + +void TexFont::RenderGlyph(int c) { +#ifdef ocpnUSE_GL + if (c < MIN_GLYPH || c >= MAX_GLYPH) return; + + TexGlyphInfo &tgic = tgi[c]; + + int x = tgic.x, y = tgic.y; + float w = m_maxglyphw, h = m_maxglyphh; + float tx1 = (float)x / (float)tex_w; + float tx2 = (float)(x + w) / (float)tex_w; + float ty1 = (float)y / (float)tex_h; + float ty2 = (float)(y + h) / (float)tex_h; + + if(!m_TexFontShader) + return; + + float uv[8]; + float coords[8]; + + // normal uv + uv[0] = tx1; + uv[1] = ty1; + uv[2] = tx2; + uv[3] = ty1; + uv[4] = tx2; + uv[5] = ty2; + uv[6] = tx1; + uv[7] = ty2; + + // pixels + coords[0] = 0; + coords[1] = 0; + coords[2] = w; + coords[3] = 0; + coords[4] = w; + coords[5] = h; + coords[6] = 0; + coords[7] = h; + + m_TexFontShader->Bind(); + + // Set up the texture sampler to texture unit 0 + m_TexFontShader->SetUniform1i( "uTex", 0); + + float colorv[4]; + colorv[0] = m_color.Red() / float(256); + colorv[1] = m_color.Green() / float(256); + colorv[2] = m_color.Blue() / float(256); + colorv[3] = 0; + + m_TexFontShader->SetUniform4fv( "color", colorv); + + // Rotate + //float angle = 0; + mat4x4 I, Q; + mat4x4_identity(I); + mat4x4_identity(Q); + //mat4x4_rotate_Z(Q, I, m_angle); + + // Translate + Q[3][0] = m_dx; // - m_vpwidth/2; //m_dx; + Q[3][1] = m_dy;// - m_vpheight/2; //m_dy; + + // mat4x4_translate_in_place(I, m_dx, m_dy, 0); + // mat4x4_rotate_Z(Q, I, m_angle); + //mat4x4_translate_in_place(Q, -m_dx, -m_dy, 0); + + m_TexFontShader->SetUniformMatrix4fv( "TransformMatrix", (GLfloat *)Q); + +// For some reason, glDrawElements is busted on Android +// So we do this a hard ugly way, drawing two triangles... +#if 0 + GLushort indices1[] = {0,1,3,2}; + glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1); +#else + + float co1[8]; + co1[0] = coords[0]; + co1[1] = coords[1]; + co1[2] = coords[2]; + co1[3] = coords[3]; + co1[4] = coords[6]; + co1[5] = coords[7]; + co1[6] = coords[4]; + co1[7] = coords[5]; + + float tco1[8]; + tco1[0] = uv[0]; + tco1[1] = uv[1]; + tco1[2] = uv[2]; + tco1[3] = uv[3]; + tco1[4] = uv[6]; + tco1[5] = uv[7]; + tco1[6] = uv[4]; + tco1[7] = uv[5]; + + m_TexFontShader->SetAttributePointerf( "position", co1); + m_TexFontShader->SetAttributePointerf( "aUV", tco1); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + m_TexFontShader->UnBind(); + +#endif + + m_dx += tgic.advance; // * cos(m_angle); // + tgic.advance * sin(m_angle); + //m_dy += tgic.advance * sin(m_angle); // - tgic.advance * cos(m_angle); +#endif +} + +void TexFont::RenderString(const char *string, int x, int y, float angle) { +#ifdef ocpnUSE_GL + +#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL) + + glPushMatrix(); + glTranslatef(x, y, 0); + + glPushMatrix(); + glBindTexture(GL_TEXTURE_2D, texobj); + + for (int i = 0; string[i]; i++) { + if (string[i] == '\n') { + glPopMatrix(); + glTranslatef(0, tgi[(int)'A'].height, 0); + glPushMatrix(); + continue; + } + /* degree symbol */ + if ((unsigned char)string[i] == 0xc2 && + (unsigned char)string[i + 1] == 0xb0) { + RenderGlyph(DEGREE_GLYPH); + i++; + continue; + } + RenderGlyph(string[i]); + } + + glPopMatrix(); + glPopMatrix(); +#else + //FIXME (dave) this is awful code, drawing chars glyph at a time. + //FIXME (dave) Also, need to render string at an angle.... + m_dx = (x - m_vpwidth/2) * cos(angle) + (y - m_vpheight/2) * sin(angle); + m_dx += m_vpwidth/2; + m_dy = -(x - m_vpwidth/2) * sin(angle) + (y - m_vpheight/2) * cos(angle); + m_dy += m_vpheight/2; + + m_dx = x; + m_dy = y; + m_angle = angle; + + glBindTexture(GL_TEXTURE_2D, texobj); + + for (int i = 0; string[i]; i++) { + if (string[i] == '\n') { + m_dy += tgi[(int)'A'].height; + continue; + } + /* degree symbol */ + if ((unsigned char)string[i] == 0xc2 && + (unsigned char)string[i + 1] == 0xb0) { + RenderGlyph(DEGREE_GLYPH); + i++; + continue; + } + RenderGlyph(string[i]); + } + +#endif +#endif +} + +void TexFont::RenderString(const wxString &string, int x, int y, float angle) { +#ifdef ocpnUSE_GL + LoadTexFontShaders(); +#endif + RenderString((const char *)string.ToUTF8(), x, y, angle); +} + +void TexFont::PrepareShader(int width, int height, double rotation){ +#ifdef ocpnUSE_GL + if(!m_TexFontShader) + LoadTexFontShaders(); + + m_vpwidth = width; + m_vpheight = height; + + mat4x4 m; + float vp_transform[16]; + mat4x4_identity(m); + mat4x4_scale_aniso((float(*)[4])vp_transform, m, 2.0 / (float)width, + -2.0 / (float)height, 1.0); + // Rotate + mat4x4 Q; + mat4x4_rotate_Z(Q, (float(*)[4])vp_transform, rotation); + mat4x4_translate_in_place(Q, -width / 2.0, -height / 2.0, 0); + + mat4x4 I; + mat4x4_identity(I); + + m_TexFontShader->Bind(); + m_TexFontShader->SetUniformMatrix4fv( "MVMatrix", (GLfloat *)Q); + m_TexFontShader->SetUniformMatrix4fv( "TransformMatrix", (GLfloat *)I); +#endif +} + +#ifdef ocpnUSE_GL +#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL) + +#ifdef USE_ANDROID_GLES2 +const GLchar* TexFont_preamble = +"\n"; +#else +const GLchar* TexFont_preamble = +"#version 120\n" +"#define precision\n" +"#define lowp\n" +"#define mediump\n" +"#define highp\n"; +#endif + + +// 2D texture shader with color modulation, used for colored text +static const GLchar *TexFont_vertex_shader_source = + "precision highp float;\n" + "attribute vec2 position;\n" + "attribute vec2 aUV;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "varying vec2 varCoord;\n" + "void main() {\n" + " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n" + " //varCoord = aUV.st;\n" + " varCoord = aUV;\n" + "}\n"; + +static const GLchar *TexFont_fragment_shader_source = + "precision highp float;\n" + "uniform sampler2D uTex;\n" + "uniform vec4 color;\n" + "varying vec2 varCoord;\n" + "void main() {\n" + " vec4 col=texture2D(uTex, varCoord);\n" + " gl_FragColor = color;\n" + " gl_FragColor.a = col.a;\n" + "}\n"; + + +#if 1 + +bool TexFont::LoadTexFontShaders() { + bool ret_val = true; + + // Are the shaders ready? + if(m_TexFontShader) + return true; + + // Simple colored triangle shader + if (!m_TexFontShader) { + CGLShaderProgram *shaderProgram = new CGLShaderProgram; + shaderProgram->addShaderFromSource(TexFont_vertex_shader_source, GL_VERTEX_SHADER); + shaderProgram->addShaderFromSource(TexFont_fragment_shader_source, GL_FRAGMENT_SHADER); + shaderProgram->linkProgram(); + + if (shaderProgram->isOK()) + m_TexFontShader = shaderProgram; + } + + m_shadersLoaded = true; + + return ret_val; +} + + +#endif +#endif +#endif diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.h new file mode 100644 index 0000000..4148805 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.h @@ -0,0 +1,93 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: OpenGL text rendering + * Author: Sean D'Epagnier + * + *************************************************************************** + * Copyright (C) 2014 Sean D'Epagnier * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +#ifndef __TEXFONT_H__ +#define __TEXFONT_H__ + +#include +#include + +/* support ascii plus degree symbol for now pack font in a single texture 16x8 + */ +#define DEGREE_GLYPH 127 +#define MIN_GLYPH 32 +#define MAX_GLYPH 128 + +#define NUM_GLYPHS (MAX_GLYPH - MIN_GLYPH) + +#define COLS_GLYPHS 16 +#define ROWS_GLYPHS ((NUM_GLYPHS / COLS_GLYPHS) + 1) + +struct TexGlyphInfo { + int x, y, width, height; + float advance; +}; + +class TexFont { +public: + TexFont(); + ~TexFont(); + + void Build(wxFont &font, double scale_factor, double dpi_factor, bool blur = false); + void Delete(); + + void GetTextExtent(const wxString &string, int *width, int *height); + void RenderString(const char *string, int x=0, int y=0, float angle = 0.0); + void RenderString(const wxString &string, int x=0, int y=0, float angle = 0.0); + bool IsBuilt() { return m_built; } + void SetColor(wxColor &color) { m_color = color; } + void PrepareShader(int width, int height, double rotation); + void SetContentScaleFactor(double s){m_ContentScaleFactor = s;} + +private: + void GetTextExtent(const char *string, int *width, int *height); + void RenderGlyph(int c); + bool LoadTexFontShaders(); + + wxFont m_font; + bool m_blur; + + TexGlyphInfo tgi[MAX_GLYPH]; + + unsigned int texobj; + int tex_w, tex_h; + int m_maxglyphw; + int m_maxglyphh; + bool m_built; + + float m_dx, m_dy; + float m_angle; + int m_vpwidth, m_vpheight; + + wxColor m_color; + + bool m_shadersLoaded; + double m_ContentScaleFactor; + +}; + +TexFont *GetTexFont(wxFont *key); + +#endif // guard diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.cpp new file mode 100644 index 0000000..b13a8b0 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.cpp @@ -0,0 +1,885 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: Chart Symbols + * Author: Jesper Weissglas + * + *************************************************************************** + * Copyright (C) 2010 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ +//#include "config.h" + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +#include +#include + +#if defined(__OCPN__ANDROID__) + #include + #include // this is a cut-down version of gl.h + #include +#elif defined(__MSVC__) + #include "glew.h" +#elif defined(__WXOSX__) + #include + #include + typedef void (* _GLUfuncptr)(); + #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#elif defined(__WXQT__) || defined(__WXGTK__) + #include + #include +#endif + +#include "chartsymbols.h" +#ifdef ocpnUSE_GL +#include +#endif + +#include "s52plib.h" + +//-------------------------------------------------------------------------------------- + +ChartSymbols::ChartSymbols(void) {} + +ChartSymbols::~ChartSymbols(void) {} + +void ChartSymbols::InitializeTables(void) { + rasterSymbolsLoadedColorMapNumber = -1; + ColorTableIndex = 0; + rasterSymbolsTexture = 0; +} + +void ChartSymbols::DeleteGlobals(void) { + m_symbolGraphicLocations.clear(); + + for (unsigned int i = 0; i < m_colorTables.GetCount(); i++) { + colTable *ct = (colTable *)m_colorTables.Item(i); + delete ct->tableName; + ct->colors.clear(); + ct->wxColors.clear(); + delete ct; + } + + m_colorTables.Clear(); +} + +void ChartSymbols::ProcessColorTables(pugi::xml_node &node) { + for (pugi::xml_node child = node.first_child(); child != 0; + child = child.next_sibling()) { + const char *pcn = child.name(); + + if (!strcmp(pcn, "color-table")) { + colTable *colortable = new colTable; + colortable->tableName = + new wxString(child.first_attribute().value(), wxConvUTF8); + + pugi::xml_node colorNode = child.first_child(); + while (colorNode) { + if (!strcmp(colorNode.name(), "graphics-file")) { + colortable->rasterFileName = + wxString(colorNode.first_attribute().value(), wxConvUTF8); + } + + if (!strcmp(colorNode.name(), "color")) { + wxString key; + S52color color; + + for (pugi::xml_attribute attr = colorNode.first_attribute(); attr; + attr = attr.next_attribute()) { + const char *pca = attr.name(); + if (!strcmp(pca, "name")) { + strncpy(color.colName, attr.value(), 5); + color.colName[5] = 0; + key = wxString(attr.value(), wxConvUTF8); + + } else if (!strcmp(pca, "r")) { + color.R = attr.as_int(); + } else if (!strcmp(pca, "g")) { + color.G = attr.as_int(); + } else if (!strcmp(pca, "b")) { + color.B = attr.as_int(); + } + } + + colortable->colors[key] = color; + wxColour wxcolor(color.R, color.G, color.B); + colortable->wxColors[key] = wxcolor; + } + + colorNode = colorNode.next_sibling(); + } + + m_colorTables.Add((void *)colortable); + } + } +} + +#define TGET_INT_PROPERTY_VALUE(node, name, target) \ + propVal = wxString(node->Attribute(name), wxConvUTF8); \ + propVal.ToLong(&numVal, 0); \ + target = numVal; +void ChartSymbols::ProcessLookups(pugi::xml_node &node) { + Lookup lookup; + + for (pugi::xml_node child = node.first_child(); child != 0; + child = child.next_sibling()) { + const char *pcn = child.name(); + + if (!strcmp(pcn, "lookup")) { + for (pugi::xml_attribute attr = child.first_attribute(); attr; + attr = attr.next_attribute()) { + const char *pca = attr.name(); + if (!strcmp(pca, "name")) { + lookup.name = wxString(attr.value(), wxConvUTF8); + } else if (!strcmp(pca, "RCID")) { + lookup.RCID = attr.as_int(); + } else if (!strcmp(pca, "id")) { + lookup.id = attr.as_int(); + } + } + } + + pugi::xml_node lookupNode = child.first_child(); + while (lookupNode) { + const char *nodeText = lookupNode.first_child().value(); + + if (!strcmp(lookupNode.name(), "type")) { + if (!strcmp(nodeText, "Area")) + lookup.type = AREAS_T; + else if (!strcmp(nodeText, "Line")) + lookup.type = LINES_T; + else + lookup.type = POINT_T; + } else if (!strcmp(lookupNode.name(), "disp-prio")) { + if (!strcmp(nodeText, "Group 1")) + lookup.displayPrio = PRIO_GROUP1; + else if (!strcmp(nodeText, "Area 1")) + lookup.displayPrio = PRIO_AREA_1; + else if (!strcmp(nodeText, "Area 2")) + lookup.displayPrio = PRIO_AREA_2; + else if (!strcmp(nodeText, "Point Symbol")) + lookup.displayPrio = PRIO_SYMB_POINT; + else if (!strcmp(nodeText, "Line Symbol")) + lookup.displayPrio = PRIO_SYMB_LINE; + else if (!strcmp(nodeText, "Area Symbol")) + lookup.displayPrio = PRIO_SYMB_AREA; + else if (!strcmp(nodeText, "Routing")) + lookup.displayPrio = PRIO_ROUTEING; + else if (!strcmp(nodeText, "Hazards")) + lookup.displayPrio = PRIO_HAZARDS; + else if (!strcmp(nodeText, "Mariners")) + lookup.displayPrio = PRIO_MARINERS; + else + lookup.displayPrio = PRIO_NODATA; + + } + + else if (!strcmp(lookupNode.name(), "radar-prio")) { + if (!strcmp(nodeText, "On Top")) + lookup.radarPrio = RAD_OVER; + else + lookup.radarPrio = RAD_SUPP; + } + + else if (!strcmp(lookupNode.name(), "table-name")) { + if (!strcmp(nodeText, "Simplified")) + lookup.tableName = SIMPLIFIED; + else if (!strcmp(nodeText, "Lines")) + lookup.tableName = LINES; + else if (!strcmp(nodeText, "Plain")) + lookup.tableName = PLAIN_BOUNDARIES; + else if (!strcmp(nodeText, "Symbolized")) + lookup.tableName = SYMBOLIZED_BOUNDARIES; + else + lookup.tableName = PAPER_CHART; + } + + else if (!strcmp(lookupNode.name(), "display-cat")) { + if (!strcmp(nodeText, "Displaybase")) + lookup.displayCat = DISPLAYBASE; + else if (!strcmp(nodeText, "Standard")) + lookup.displayCat = STANDARD; + else if (!strcmp(nodeText, "Other")) + lookup.displayCat = OTHER; + else if (!strcmp(nodeText, "Mariners")) + lookup.displayCat = MARINERS_STANDARD; + else + lookup.displayCat = OTHER; + } + + else if (!strcmp(lookupNode.name(), "comment")) { + lookup.comment = lookupNode.first_child().text().as_int(); + } + + else if (!strcmp(lookupNode.name(), "instruction")) { + wxString inst(nodeText, wxConvUTF8); + lookup.instruction = inst; + lookup.instruction.Append('\037'); + + } + + else if (!strcmp(lookupNode.name(), "attrib-code")) { + int nc = strlen(nodeText); + if (nc >= 6) { // ignore spurious short fields + std::string attVal = std::string(nodeText); + if(attVal.length() == 6) + attVal += ' '; + lookup.attributeCodeArray.push_back(attVal); + } + } + + lookupNode = lookupNode.next_sibling(); + } + + BuildLookup(lookup); + lookup.attributeCodeArray.clear(); + } +} + +void ChartSymbols::ProcessVectorTag(pugi::xml_node &vectorNode, + SymbolSizeInfo_t &vectorSize) { + vectorSize.size.x = vectorNode.attribute("width").as_int(); + vectorSize.size.y = vectorNode.attribute("height").as_int(); + + for (pugi::xml_node child = vectorNode.first_child(); child != 0; + child = child.next_sibling()) { + const char *nodeType = child.name(); + + if (!strcmp(nodeType, "distance")) { + vectorSize.minDistance = child.attribute("min").as_int(); + vectorSize.maxDistance = child.attribute("max").as_int(); + } + + else if (!strcmp(nodeType, "origin")) { + vectorSize.origin.x = child.attribute("x").as_int(); + vectorSize.origin.y = child.attribute("y").as_int(); + } + + else if (!strcmp(nodeType, "pivot")) { + vectorSize.pivot.x = child.attribute("x").as_int(); + vectorSize.pivot.y = child.attribute("y").as_int(); + } + } +} + +void ChartSymbols::ProcessLinestyles(pugi::xml_node &node) { + LineStyle lineStyle; + + for (pugi::xml_node child = node.first_child(); child != 0; + child = child.next_sibling()) { + lineStyle.RCID = child.attribute("RCID").as_int(); + + pugi::xml_node lineNode = child.first_child(); + while (lineNode) { + const char *nodeText = lineNode.first_child().value(); + const char *nodeType = lineNode.name(); + + if (!strcmp(nodeType, "description")) + lineStyle.description = nodeText; + else if (!strcmp(nodeType, "name")) + lineStyle.name = nodeText; + else if (!strcmp(nodeType, "color-ref")) + lineStyle.colorRef = nodeText; + else if (!strcmp(nodeType, "HPGL")) + lineStyle.HPGL = nodeText; + else if (!strcmp(nodeType, "vector")) + ProcessVectorTag(lineNode, lineStyle.vectorSize); + + lineNode = lineNode.next_sibling(); + } + BuildLineStyle(lineStyle); + } +} + +void ChartSymbols::ProcessPatterns(pugi::xml_node &node) { + OCPNPattern pattern; + + for (pugi::xml_node child = node.first_child(); child != 0; + child = child.next_sibling()) { + pattern.RCID = child.attribute("RCID").as_int(); + + pattern.hasVector = false; + pattern.hasBitmap = false; + pattern.preferBitmap = true; + + pugi::xml_node pattNode = child.first_child(); + while (pattNode) { + const char *nodeText = pattNode.first_child().value(); + const char *nodeType = pattNode.name(); + + if (!strcmp(nodeType, "description")) + pattern.description = nodeText; + else if (!strcmp(nodeType, "name")) + pattern.name = nodeText; + else if (!strcmp(nodeType, "filltype")) + pattern.fillType = nodeText[0]; + else if (!strcmp(nodeType, "spacing")) + pattern.spacing = nodeText[0]; + else if (!strcmp(nodeType, "definition")) + pattern.hasVector = !strcmp(nodeText, "V"); + else if (!strcmp(nodeType, "color-ref")) + pattern.colorRef = nodeText; + else if (!strcmp(nodeType, "HPGL")) { + pattern.HPGL = nodeText; + pattern.hasVector = true; + } + + else if (!strcmp(nodeType, "prefer-bitmap")) { + if (!strcmp(nodeText, "no")) + pattern.preferBitmap = false; + else if (!strcmp(nodeText, "false")) + pattern.preferBitmap = false; + } + + else if (!strcmp(nodeType, "bitmap")) { + pattern.bitmapSize.size.x = pattNode.attribute("width").as_int(); + pattern.bitmapSize.size.y = pattNode.attribute("height").as_int(); + + for (pugi::xml_node child = pattNode.first_child(); child != 0; + child = child.next_sibling()) { + const char *nodeType = child.name(); + + if (!strcmp(nodeType, "distance")) { + pattern.bitmapSize.minDistance = child.attribute("min").as_int(); + pattern.bitmapSize.maxDistance = child.attribute("max").as_int(); + } else if (!strcmp(nodeType, "origin")) { + pattern.bitmapSize.origin.x = child.attribute("x").as_int(); + pattern.bitmapSize.origin.y = child.attribute("y").as_int(); + } else if (!strcmp(nodeType, "pivot")) { + pattern.bitmapSize.pivot.x = child.attribute("x").as_int(); + pattern.bitmapSize.pivot.y = child.attribute("y").as_int(); + } else if (!strcmp(nodeType, "graphics-location")) { + pattern.bitmapSize.graphics.x = child.attribute("x").as_int(); + pattern.bitmapSize.graphics.y = child.attribute("y").as_int(); + } + } + } + + else if (!strcmp(nodeType, "vector")) + ProcessVectorTag(pattNode, pattern.vectorSize); + + pattNode = pattNode.next_sibling(); + } + + BuildPattern(pattern); + } +} + +void ChartSymbols::ProcessSymbols(pugi::xml_node &node) { + ChartSymbol symbol; + + for (pugi::xml_node child = node.first_child(); child != 0; + child = child.next_sibling()) { + symbol.RCID = child.attribute("RCID").as_int(); + + symbol.hasVector = false; + symbol.hasBitmap = false; + symbol.preferBitmap = true; + + pugi::xml_node symbolNode = child.first_child(); + while (symbolNode) { + const char *nodeText = symbolNode.first_child().value(); + const char *nodeType = symbolNode.name(); + + if (!strcmp(nodeType, "description")) + symbol.description = nodeText; + else if (!strcmp(nodeType, "name")) + symbol.name = nodeText; + else if (!strcmp(nodeType, "definition")) + symbol.hasVector = !strcmp(nodeText, "V"); + else if (!strcmp(nodeType, "color-ref")) + symbol.colorRef = nodeText; + + else if (!strcmp(nodeType, "prefer-bitmap")) { + if (!strcmp(nodeText, "no")) + symbol.preferBitmap = false; + else if (!strcmp(nodeText, "false")) + symbol.preferBitmap = false; + } + + else if (!strcmp(nodeType, "bitmap")) { + symbol.bitmapSize.size.x = symbolNode.attribute("width").as_int(); + symbol.bitmapSize.size.y = symbolNode.attribute("height").as_int(); + symbol.hasBitmap = true; + + for (pugi::xml_node child = symbolNode.first_child(); child != 0; + child = child.next_sibling()) { + const char *nodeType = child.name(); + + if (!strcmp(nodeType, "distance")) { + symbol.bitmapSize.minDistance = child.attribute("min").as_int(); + symbol.bitmapSize.maxDistance = child.attribute("max").as_int(); + } else if (!strcmp(nodeType, "origin")) { + symbol.bitmapSize.origin.x = child.attribute("x").as_int(); + symbol.bitmapSize.origin.y = child.attribute("y").as_int(); + } else if (!strcmp(nodeType, "pivot")) { + symbol.bitmapSize.pivot.x = child.attribute("x").as_int(); + symbol.bitmapSize.pivot.y = child.attribute("y").as_int(); + } else if (!strcmp(nodeType, "graphics-location")) { + symbol.bitmapSize.graphics.x = child.attribute("x").as_int(); + symbol.bitmapSize.graphics.y = child.attribute("y").as_int(); + } + } + } + + else if (!strcmp(nodeType, "vector")) { + symbol.vectorSize.size.x = symbolNode.attribute("width").as_int(); + symbol.vectorSize.size.y = symbolNode.attribute("height").as_int(); + symbol.hasVector = true; + + for (pugi::xml_node child = symbolNode.first_child(); child != 0; + child = child.next_sibling()) { + const char *nodeType = child.name(); + + if (!strcmp(nodeType, "distance")) { + symbol.vectorSize.minDistance = child.attribute("min").as_int(); + symbol.vectorSize.maxDistance = child.attribute("max").as_int(); + } else if (!strcmp(nodeType, "origin")) { + symbol.vectorSize.origin.x = child.attribute("x").as_int(); + symbol.vectorSize.origin.y = child.attribute("y").as_int(); + } else if (!strcmp(nodeType, "pivot")) { + symbol.vectorSize.pivot.x = child.attribute("x").as_int(); + symbol.vectorSize.pivot.y = child.attribute("y").as_int(); + } else if (!strcmp(nodeType, "HPGL")) { + symbol.HPGL = wxString(child.first_child().value(), wxConvUTF8); + } + } + } + + symbolNode = symbolNode.next_sibling(); + } + + BuildSymbol(symbol); + } +} + + +void ChartSymbols::BuildLookup(Lookup &lookup) { + auto LUP = new LUPrec(); + + LUP->RCID = lookup.RCID; + LUP->nSequence = lookup.id; + LUP->DISC = lookup.displayCat; + LUP->FTYP = lookup.type; + LUP->DPRI = lookup.displayPrio; + LUP->RPRI = lookup.radarPrio; + LUP->TNAM = lookup.tableName; + LUP->OBCL[6] = 0; + memcpy(LUP->OBCL, lookup.name.mb_str(), 7); + + LUP->ATTArray = lookup.attributeCodeArray; + + LUP->INST = lookup.instruction; + LUP->LUCM = lookup.comment; + + // Add LUP to array + // Search the LUPArray to see if there is already a LUP with this RCID + // If found, replace it with the new LUP + // This provides a facility for updating the LUP tables after loading a basic + // set + + unsigned int index = 0; + wxArrayOfLUPrec *pLUPARRAYtyped = plib->SelectLUPARRAY(LUP->TNAM); + + while (index < pLUPARRAYtyped->GetCount()) { + auto pLUPCandidate = pLUPARRAYtyped->Item(index); + if (LUP->RCID == pLUPCandidate->RCID) { + pLUPARRAYtyped->RemoveAt(index); + plib->DestroyLUP(pLUPCandidate); // empties the LUP + break; + } + index++; + } + pLUPARRAYtyped->Add(LUP); +} + +void ChartSymbols::BuildLineStyle(LineStyle &lineStyle) { + Rule *lnstmp = NULL; + Rule *lnst = (Rule *)calloc(1, sizeof(Rule)); + plib->pAlloc->Add(lnst); + + lnst->RCID = lineStyle.RCID; + memcpy(lnst->name.PANM, lineStyle.name.mb_str(), 8); + lnst->bitmap.PBTM = NULL; + + lnst->vector.LVCT = (char *)malloc(lineStyle.HPGL.Len() + 1); + strcpy(lnst->vector.LVCT, lineStyle.HPGL.mb_str()); + + lnst->colRef.LCRF = (char *)malloc(lineStyle.colorRef.Len() + 1); + strcpy(lnst->colRef.LCRF, lineStyle.colorRef.mb_str()); + + lnst->pos.line.minDist.PAMI = lineStyle.vectorSize.minDistance; + lnst->pos.line.maxDist.PAMA = lineStyle.vectorSize.maxDistance; + + lnst->pos.line.pivot_x.PACL = lineStyle.vectorSize.pivot.x; + lnst->pos.line.pivot_y.PARW = lineStyle.vectorSize.pivot.y; + + lnst->pos.line.bnbox_w.PAHL = lineStyle.vectorSize.size.x; + lnst->pos.line.bnbox_h.PAVL = lineStyle.vectorSize.size.y; + + lnst->pos.line.bnbox_x.SBXC = lineStyle.vectorSize.origin.x; + lnst->pos.line.bnbox_y.SBXR = lineStyle.vectorSize.origin.y; + + lnstmp = (*plib->_line_sym)[lineStyle.name]; + + if (NULL == lnstmp) + (*plib->_line_sym)[lineStyle.name] = lnst; + else if (strncmp(lnst->name.LINM, lnstmp->name.LINM, 8)) + (*plib->_line_sym)[lineStyle.name] = lnst; +} + + +void ChartSymbols::BuildPattern(OCPNPattern &pattern) { + Rule *pattmp = NULL; + + Rule *patt = (Rule *)calloc(1, sizeof(Rule)); + plib->pAlloc->Add(patt); + + patt->RCID = pattern.RCID; + patt->exposition.PXPO = new wxString(pattern.description); + memcpy(patt->name.PANM, pattern.name.mb_str(), 8); + patt->bitmap.PBTM = NULL; + patt->fillType.PATP = pattern.fillType; + patt->spacing.PASP = pattern.spacing; + + patt->vector.PVCT = (char *)malloc(pattern.HPGL.Len() + 1); + strcpy(patt->vector.PVCT, pattern.HPGL.mb_str()); + + patt->colRef.PCRF = (char *)malloc(pattern.colorRef.Len() + 1); + strcpy(patt->colRef.PCRF, pattern.colorRef.mb_str()); + + SymbolSizeInfo_t patternSize; + + if (pattern.hasVector && !(pattern.preferBitmap && pattern.hasBitmap)) { + patt->definition.PADF = 'V'; + patternSize = pattern.vectorSize; + } else { + patt->definition.PADF = 'R'; + patternSize = pattern.bitmapSize; + } + + patt->pos.patt.minDist.PAMI = patternSize.minDistance; + patt->pos.patt.maxDist.PAMA = patternSize.maxDistance; + + patt->pos.patt.pivot_x.PACL = patternSize.pivot.x; + patt->pos.patt.pivot_y.PARW = patternSize.pivot.y; + + patt->pos.patt.bnbox_w.PAHL = patternSize.size.x; + patt->pos.patt.bnbox_h.PAVL = patternSize.size.y; + + patt->pos.patt.bnbox_x.SBXC = patternSize.origin.x; + patt->pos.patt.bnbox_y.SBXR = patternSize.origin.y; + + wxRect graphicsLocation(pattern.bitmapSize.graphics, pattern.bitmapSize.size); + m_symbolGraphicLocations[pattern.name] = graphicsLocation; + + // check if key already there + + pattmp = (*plib->_patt_sym)[pattern.name]; + + if (NULL == pattmp) { + (*plib->_patt_sym)[pattern.name] = patt; // insert in hash table + } else // already something here with same key... + { // if the pattern names are not identical + if (strncmp(patt->name.PANM, pattmp->name.PANM, 8)) { + (*plib->_patt_sym)[pattern.name] = patt; // replace the pattern + plib->DestroyPatternRuleNode( + pattmp); // remember to free to replaced node + // the node itself is destroyed as part of pAlloc + } + } +} + + +void ChartSymbols::BuildSymbol(ChartSymbol &symbol) { + Rule *symb = (Rule *)calloc(1, sizeof(Rule)); + plib->pAlloc->Add(symb); + + wxString SVCT; + wxString SCRF; + + symb->RCID = symbol.RCID; + memcpy(symb->name.SYNM, symbol.name.char_str(), 8); + + symb->exposition.SXPO = new wxString(symbol.description); + + symb->vector.SVCT = (char *)malloc(symbol.HPGL.Len() + 1); + strcpy(symb->vector.SVCT, symbol.HPGL.mb_str()); + + symb->colRef.SCRF = (char *)malloc(symbol.colorRef.Len() + 1); + strcpy(symb->colRef.SCRF, symbol.colorRef.mb_str()); + + symb->bitmap.SBTM = NULL; + + SymbolSizeInfo_t symbolSize; + + if (symbol.hasVector && !(symbol.preferBitmap && symbol.hasBitmap)) { + symb->definition.SYDF = 'V'; + symbolSize = symbol.vectorSize; + } else { + symb->definition.SYDF = 'R'; + symbolSize = symbol.bitmapSize; + } + + symb->pos.symb.minDist.PAMI = symbolSize.minDistance; + symb->pos.symb.maxDist.PAMA = symbolSize.maxDistance; + + symb->pos.symb.pivot_x.SYCL = symbolSize.pivot.x; + symb->pos.symb.pivot_y.SYRW = symbolSize.pivot.y; + + symb->pos.symb.bnbox_w.SYHL = symbolSize.size.x; + symb->pos.symb.bnbox_h.SYVL = symbolSize.size.y; + + symb->pos.symb.bnbox_x.SBXC = symbolSize.origin.x; + symb->pos.symb.bnbox_y.SBXR = symbolSize.origin.y; + + wxRect graphicsLocation(symbol.bitmapSize.graphics, symbol.bitmapSize.size); + m_symbolGraphicLocations[symbol.name] = graphicsLocation; + + // Already something here with same key? Then free its strings, otherwise they + // leak. + Rule *symbtmp = (*plib->_symb_sym)[symbol.name]; + if (symbtmp) { + free(symbtmp->colRef.SCRF); + free(symbtmp->vector.SVCT); + delete symbtmp->exposition.SXPO; + } + + (*plib->_symb_sym)[symbol.name] = symb; +} + +bool ChartSymbols::LoadConfigFile(s52plib *plibArg, + const wxString &s52ilePath) { + plib = plibArg; + + // Expect to find library data XML file in same folder as other S52 data. + // Files in CWD takes precedence. + + wxString name, extension; + wxString xmlFileName = _T("chartsymbols.xml"); + + wxFileName::SplitPath(s52ilePath, &configFileDirectory, &name, &extension); + wxString fullFilePath = + configFileDirectory + wxFileName::GetPathSeparator() + xmlFileName; + + if (wxFileName::FileExists(xmlFileName)) { + fullFilePath = xmlFileName; + configFileDirectory = _T("."); + } + + if (!wxFileName::FileExists(fullFilePath)) { + wxString msg(_T("ChartSymbols ConfigFile not found: ")); + msg += fullFilePath; + wxLogMessage(msg); + return false; + } + + if (m_symbolsDoc.load_file(fullFilePath.fn_str())) { + wxString msg(_T("ChartSymbols loaded from ")); + msg += fullFilePath; + wxLogMessage(msg); + + pugi::xml_node elements = m_symbolsDoc.child("chartsymbols"); + + for (pugi::xml_node element = elements.first_child(); element; + element = element.next_sibling()) { + if (!strcmp(element.name(), "color-tables")) + ProcessColorTables(element); + else if (!strcmp(element.name(), "lookups")) + ProcessLookups(element); + else if (!strcmp(element.name(), "line-styles")) + ProcessLinestyles(element); + else if (!strcmp(element.name(), "patterns")) + ProcessPatterns(element); + else if (!strcmp(element.name(), "symbols")) + ProcessSymbols(element); + } + m_symbolsDoc.reset(); // purge the document to recover memory; + } + + + return true; +} +void ChartSymbols::SetColorTableIndex(int index, bool flush, + const ChartCtx& ctx) { + ColorTableIndex = index; + LoadRasterFileForColorTable(ColorTableIndex, flush, ctx); +} + +int ChartSymbols::LoadRasterFileForColorTable(int tableNo, bool flush, + const ChartCtx& ctx) { + if (tableNo == rasterSymbolsLoadedColorMapNumber && !flush) { + if (ctx.m_use_opengl) { + if (rasterSymbolsTexture) return true; +#ifdef ocpnUSE_GL + if (!ctx.m_texture_rectangle_format && rasterSymbols.IsOk()) + return true; +#endif + } + if (rasterSymbols.IsOk()) return true; + } + + colTable *coltab = (colTable *)m_colorTables.Item(tableNo); + + wxString filename = configFileDirectory + wxFileName::GetPathSeparator() + + coltab->rasterFileName; + + wxImage rasterFileImg; + if (rasterFileImg.LoadFile(filename, wxBITMAP_TYPE_PNG)) { +#ifdef ocpnUSE_GL + /* for opengl mode, load the symbols into a texture */ + if (ctx.m_use_opengl && ctx.m_texture_rectangle_format) { + int w = rasterFileImg.GetWidth(); + int h = rasterFileImg.GetHeight(); + + // Get the glRGBA format data from the wxImage + unsigned char *d = rasterFileImg.GetData(); + unsigned char *a = rasterFileImg.GetAlpha(); + + /* combine rgb with alpha */ + unsigned char *e = (unsigned char *)malloc(w * h * 4); + if (d && a) { + for (int y = 0; y < h; y++) + for (int x = 0; x < w; x++) { + int off = (y * w + x); + + e[off * 4 + 0] = d[off * 3 + 0]; + e[off * 4 + 1] = d[off * 3 + 1]; + e[off * 4 + 2] = d[off * 3 + 2]; + e[off * 4 + 3] = a[off]; + } + } + if (!rasterSymbolsTexture) glGenTextures(1, &rasterSymbolsTexture); + + glBindTexture(ctx.m_texture_rectangle_format, rasterSymbolsTexture); + + /* unfortunately this texture looks terrible with compression */ + GLuint format = GL_RGBA; + glTexImage2D(ctx.m_texture_rectangle_format, 0, format, w, h, 0, GL_RGBA, + GL_UNSIGNED_BYTE, e); + + // glTexParameteri( g_texture_rectangle_format, + // GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameteri( + // g_texture_rectangle_format, GL_TEXTURE_MIN_FILTER, + // GL_NEAREST ); + + glTexParameteri(ctx.m_texture_rectangle_format, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); + glTexParameteri(ctx.m_texture_rectangle_format, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); + + glTexParameteri(ctx.m_texture_rectangle_format, GL_TEXTURE_MAG_FILTER, + GL_LINEAR); // No mipmapping + glTexParameteri(ctx.m_texture_rectangle_format, GL_TEXTURE_MIN_FILTER, + GL_LINEAR); + + rasterSymbolsTextureSize = wxSize(w, h); + + glDisable(GL_TEXTURE_2D); + + free(e); + } +#endif + { rasterSymbols = wxBitmap(rasterFileImg, -1 /*32*/); } + + rasterSymbolsLoadedColorMapNumber = tableNo; + return true; + } + + wxString msg(_T("ChartSymbols...Failed to load raster symbols file ")); + msg += filename; + wxLogMessage(msg); + return false; +} + + +S52color *ChartSymbols::GetColor(const char *colorName, int fromTable) { + colTable *colortable; + wxString key(colorName, wxConvUTF8, 5); + colortable = (colTable *)m_colorTables.Item(fromTable); + return &(colortable->colors[key]); +} + +wxColor ChartSymbols::GetwxColor(const wxString &colorName, int fromTable) { + colTable *colortable; + colortable = (colTable *)m_colorTables.Item(fromTable); + wxColor c = colortable->wxColors[colorName]; + return c; +} + +wxColor ChartSymbols::GetwxColor(const char *colorName, int fromTable) { + wxString key(colorName, wxConvUTF8, 5); + return GetwxColor(key, fromTable); +} + +int ChartSymbols::FindColorTable(const wxString &tableName) { + for (unsigned int i = 0; i < m_colorTables.GetCount(); i++) { + colTable *ct = (colTable *)m_colorTables.Item(i); + if (tableName.IsSameAs(*ct->tableName)) { + return i; + } + } + return 0; +} + +bool ChartSymbols::UpdateTableColor( std::string table_name, std::string color_name, wxColor& new_color){ + int table_index = FindColorTable( wxString(table_name.c_str())); + + colTable *colortable = (colTable *)m_colorTables.Item(table_index); + wxString key(color_name.c_str(), wxConvUTF8, 5); + + colortable->wxColors[key] = new_color; + if (colortable->colors.find(key) != colortable->colors.end()) { + S52color *color = &colortable->colors[key]; + color->R = new_color.Red(); + color->G = new_color.Green(); + color->B = new_color.Blue(); + return true; + } + else + return false; +} + + +wxString ChartSymbols::HashKey(const char *symbolName) { + char key[9]; + key[8] = 0; + strncpy(key, symbolName, 8); + return wxString(key, wxConvUTF8); +} + +wxImage ChartSymbols::GetImage(const char *symbolName) { + wxRect bmArea = m_symbolGraphicLocations[HashKey(symbolName)]; + if (rasterSymbols.IsOk()) { + wxBitmap bitmap = rasterSymbols.GetSubBitmap(bmArea); + return bitmap.ConvertToImage(); + } else + return wxImage(1, 1); +} + +unsigned int ChartSymbols::GetGLTextureRect(wxRect &rect, + const char *symbolName) { + rect = m_symbolGraphicLocations[HashKey(symbolName)]; + return rasterSymbolsTexture; +} + +wxSize ChartSymbols::GLTextureSize() { return rasterSymbolsTextureSize; } diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.h new file mode 100755 index 0000000..f626106 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.h @@ -0,0 +1,204 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: Chart Symbols + * Author: Jesper Weissglas + * + *************************************************************************** + * Copyright (C) 2010 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + + +#ifndef _CHARTSYMBOLS_H_ +#define _CHARTSYMBOLS_H_ + +#ifdef ocpnUSE_GL +#ifdef __OCPN_USE_GLEW__ + #ifndef __OCPN__ANDROID__ + #if defined(_WIN32) + #include "glew.h" + #elif defined(__WXQT__) || defined(__WXGTK__) + #include + #endif + #endif +#endif +#endif + +#if defined(__OCPN__ANDROID__) + //#include + #include + #include // this is a cut-down version of gl.h + #include +#elif defined(_WIN32) + #define GL_GLEXT_PROTOTYPES + #include + #include + //typedef void (__stdcall * _GLUfuncptr)(void); +#elif defined(__WXOSX__) + #include + #include + typedef void (* _GLUfuncptr)(); + #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#elif defined(__WXQT__) || defined(__WXGTK__) + #define GL_GLEXT_PROTOTYPES + #include + #include + #include +#endif + + + +#include "s52s57.h" +#include "pugixml.hpp" +class s52plib; + +WX_DECLARE_STRING_HASH_MAP(wxRect, symbolGraphicsHashMap); + +class Lookup { +public: + int RCID; + int id; + wxString name; + Object_t type; // 'A' Area, 'L' Line, 'P' Point + DisPrio displayPrio; // Display Priority + RadPrio radarPrio; // 'O' or 'S', Radar Priority + LUPname tableName; // FTYP: areas, points, lines + std::vector attributeCodeArray; // ArrayString of LUP Attributes + wxString instruction; // Instruction Field (rules) + DisCat displayCat; // Display Categorie: D/S/O, DisplayBase, Standard, Other + int comment; // Look-Up Comment (PLib3.x put 'groupes' here, +}; + +typedef struct _SymbolSizeInfo { + wxSize size; + wxPoint origin; + wxPoint pivot; + wxPoint graphics; + int minDistance; + int maxDistance; +} SymbolSizeInfo_t; + +class OCPNPattern { +public: + int RCID; + wxString name; + wxString description; + wxString colorRef; + bool hasVector; + bool hasBitmap; + bool preferBitmap; + char fillType; + char spacing; + SymbolSizeInfo_t bitmapSize; + SymbolSizeInfo_t vectorSize; + wxBitmap bitmap; + wxString HPGL; +}; + +class LineStyle { +public: + int RCID; + wxString name; + wxString description; + wxString colorRef; + SymbolSizeInfo_t vectorSize; + wxBitmap bitmap; + wxString HPGL; +}; + +/** Generic method argument wrapping differences using opengl or not */ +struct ChartCtx { +#ifdef ocpnUSE_GL + const bool m_use_opengl; + const GLenum m_texture_rectangle_format; + ChartCtx(bool use_opengl, GLenum rect_format) + : m_use_opengl(use_opengl), m_texture_rectangle_format(rect_format) {} +#else + const bool m_use_opengl; + ChartCtx(bool use_opengl) : m_use_opengl(use_opengl) {} +#endif +}; + + + +class ChartSymbol { +public: + wxString name; + int RCID; + bool hasVector; + bool hasBitmap; + bool preferBitmap; + wxString description; + wxString colorRef; + SymbolSizeInfo_t bitmapSize; + SymbolSizeInfo_t vectorSize; + wxString HPGL; +}; + +class ChartSymbols { +public: + ChartSymbols(void); + ~ChartSymbols(void); + bool LoadConfigFile(s52plib *plibArg, const wxString &path); + + void InitializeTables(void); + void DeleteGlobals(void); + int FindColorTable(const wxString &tableName); + S52color *GetColor(const char *colorName, int fromTable); + wxColor GetwxColor(const wxString &colorName, int fromTable); + wxColor GetwxColor(const char *colorName, int fromTable); + wxString HashKey(const char *symbolName); + wxImage GetImage(const char *symbolName); + unsigned int GetGLTextureRect(wxRect &rect, const char *symbolName); + wxSize GLTextureSize(); + void SetTextureFormat( int format){ m_texture_rectangle_format = format; } + int LoadRasterFileForColorTable(int table_nr, bool flush, + const ChartCtx& ctx); + void SetColorTableIndex(int index, bool flush, const ChartCtx& ctx); + + wxArrayPtrVoid m_colorTables; + unsigned int rasterSymbolsTexture; + wxSize rasterSymbolsTextureSize; + wxBitmap rasterSymbols; + int rasterSymbolsLoadedColorMapNumber; + wxString configFileDirectory; + int ColorTableIndex; + symbolGraphicsHashMap m_symbolGraphicLocations; + bool UpdateTableColor( std::string table_name, std::string color_name, wxColor& new_color); + +private: + void BuildLineStyle(LineStyle &lineStyle); + void BuildLookup(Lookup &lookup); + void BuildPattern(OCPNPattern &pattern); + void BuildSymbol(ChartSymbol &symol); + + void ProcessColorTables(pugi::xml_node &node); + void ProcessLookups(pugi::xml_node &node); + void ProcessLinestyles(pugi::xml_node &node); + void ProcessPatterns(pugi::xml_node &node); + void ProcessSymbols(pugi::xml_node &node); + void ProcessVectorTag(pugi::xml_node &vectorNode, + SymbolSizeInfo_t &vectorSize); + + pugi::xml_document m_symbolsDoc; + GLenum m_texture_rectangle_format; + + s52plib *plib; +}; + +#endif diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/color_types.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/color_types.h new file mode 100644 index 0000000..2f307bd --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/color_types.h @@ -0,0 +1,58 @@ +/****************************************************************************** + * + * Project: OpenCP + * Purpose: Color Types + * Author: David Register + * + *************************************************************************** + * Copyright (C) 2022 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + *************************************************************************** + * + */ + +#ifndef _COLORTYPES_H_ +#define _COLORTYPES_H_ + +typedef struct _S52color { + char colName[20]; + unsigned char R; + unsigned char G; + unsigned char B; +} S52color; + +WX_DECLARE_STRING_HASH_MAP(S52color, colorHashMap); +WX_DECLARE_STRING_HASH_MAP(wxColour, wxColorHashMap); + +typedef enum ColorScheme { + GLOBAL_COLOR_SCHEME_RGB, + GLOBAL_COLOR_SCHEME_DAY, + GLOBAL_COLOR_SCHEME_DUSK, + GLOBAL_COLOR_SCHEME_NIGHT, + N_COLOR_SCHEMES +} _ColorScheme; + +typedef struct _colTable { + wxString *tableName; + wxString rasterFileName; + wxArrayPtrVoid *color; + colorHashMap colors; + wxColorHashMap wxColors; +} colTable; + + +#endif diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.cpp new file mode 100644 index 0000000..47f7cc0 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.cpp @@ -0,0 +1,1514 @@ +/****************************************************************************** + * + * Project: OpenCPN + * Purpose: Tesselated Polygon Object + * Author: David Register + * + *************************************************************************** + * Copyright (C) 2010 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + *************************************************************************** + * + * + */ +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif // precompiled headers + +#if defined(__OCPN__ANDROID__) + #include + #include // this is a cut-down version of gl.h + #include + #define GLU_TESS_MAX_COORD 1.0e150 +#elif defined(__MSVC__) + #include "glew.h" +#elif defined(__WXOSX__) + #include + #include + typedef void (* _GLUfuncptr)(); + #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#elif defined(__WXQT__) || defined(__WXGTK__) + #include + #include +#endif + +#include "wx/tokenzr.h" +#include + +#include "s52plib.h" +#include "gdal/ogr_geometry.h" + +#include "vector2D.h" + +#include "s52s57.h" +#include "s52plib.h" + +#include "mygeom.h" +#include "LOD_reduce.h" + +#include +#include +#include +#include + +#include "tesselator.h" +#include "Striper.h" + +#ifdef __WXMSW__ +#include +#endif + +static constexpr double kMercatorK0 = 0.9996; +static constexpr double kRadiansPerDegree = M_PI / 180.0; + +static const double CM93_semimajor_axis_meters = + 6378388.0; // CM93 semimajor axis + +// Module Internal Prototypes + +#if defined(__UNIX__) && \ + !defined(__WXOSX__) // high resolution stopwatch for profiling +class OCPNStopWatchTess { +public: + OCPNStopWatchTess() { Reset(); } + void Reset() { clock_gettime(CLOCK_REALTIME, &tp); } + + double GetTime() { + timespec tp_end; + clock_gettime(CLOCK_REALTIME, &tp_end); + return (tp_end.tv_sec - tp.tv_sec) * 1.e3 + + (tp_end.tv_nsec - tp.tv_nsec) / 1.e6; + } + +private: + timespec tp; +}; +#endif + +//----------------------------------------------------------- +// +// Static memory allocators for libtess2 +// +//----------------------------------------------------------- +//#define USE_POOL 1 + +void *stdAlloc(void *userData, unsigned int size) { + int *allocated = (int *)userData; + TESS_NOTUSED(userData); + *allocated += (int)size; + return malloc(size); +} + +void stdFree(void *userData, void *ptr) { + TESS_NOTUSED(userData); + free(ptr); +} + +struct MemPool { + unsigned char *buf; + unsigned int cap; + unsigned int size; +}; + +void *poolAlloc(void *userData, unsigned int size) { + struct MemPool *pool = (struct MemPool *)userData; + size = (size + 0x7) & ~0x7; + if (pool->size + size < pool->cap) { + unsigned char *ptr = pool->buf + pool->size; + pool->size += size; + return ptr; + } + printf("out of mem: %d < %d!\n", pool->size + size, pool->cap); + return 0; +} + +void poolFree(void *userData, void *ptr) { + // empty + TESS_NOTUSED(userData); + TESS_NOTUSED(ptr); +} + +/** + * Returns TRUE if the ring has clockwise winding. + * + * @return TRUE if clockwise otherwise FALSE. + */ + +bool isRingClockwise(wxPoint2DDouble *pp, int nPointCount) + +{ + double dfSum = 0.0; + + for (int iVert = 0; iVert < nPointCount - 1; iVert++) { + dfSum += + pp[iVert].m_x * pp[iVert + 1].m_y - pp[iVert].m_y * pp[iVert + 1].m_x; + } + + dfSum += + pp[nPointCount - 1].m_x * pp[0].m_y - pp[nPointCount - 1].m_y * pp[0].m_x; + + return dfSum < 0.0; +} + +//------------------------------------------------------------------------------ +// Extended_Geometry Implementation +//------------------------------------------------------------------------------ +Extended_Geometry::Extended_Geometry() { + vertex_array = NULL; + contour_array = NULL; +} + +Extended_Geometry::~Extended_Geometry() { + free(vertex_array); + free(contour_array); +} + +//------------------------------------------------------------------------------ +// PolyTessGeo Implementation +//------------------------------------------------------------------------------ +PolyTessGeo::PolyTessGeo() { + m_pxgeom = NULL; + m_printStats = false; + m_bstripify = false; + m_LOD_meters = 0; +} + +// Build PolyTessGeo Object from Extended_Geometry +PolyTessGeo::PolyTessGeo(Extended_Geometry *pxGeom) { + m_ppg_head = NULL; + m_bOK = false; + + m_pxgeom = pxGeom; + m_printStats = false; + m_bstripify = false; + m_LOD_meters = 0; +} + +// Build PolyTessGeo Object from OGR Polygon +PolyTessGeo::PolyTessGeo(OGRPolygon *poly, bool bSENC_SM, double ref_lat, + double ref_lon, double LOD_meters) { + m_printStats = false; + + ErrorCode = 0; + m_ppg_head = NULL; + m_pxgeom = NULL; + m_tess_orient = TESS_HORZ; + + m_ref_lat = ref_lat; + m_ref_lon = ref_lon; + m_LOD_meters = LOD_meters; + m_b_senc_sm = bSENC_SM; + m_bmerc_transform = false; + + // PolyGeo BBox as lat/lon + OGREnvelope Envelope; + poly->getEnvelope(&Envelope); + xmin = Envelope.MinX; + ymin = Envelope.MinY; + xmax = Envelope.MaxX; + ymax = Envelope.MaxY; + + m_feature_ref_lat = ymin + (ymax - ymin) / 2; + m_feature_ref_lon = xmin + (xmax - xmin) / 2; + + toSM_plib(m_feature_ref_lat, m_feature_ref_lon, ref_lat, ref_lon, + &m_feature_easting, &m_feature_northing); + + // Build the array of contour point counts + // Get total number of contours + m_ncnt = poly->getNumInteriorRings() + 1; + + // build the contour point countarray + m_cntr = (int *)malloc(m_ncnt * sizeof(int)); + + m_cntr[0] = poly->getExteriorRing()->getNumPoints(); + + for (int i = 1; i < m_ncnt; i++) { + m_cntr[i] = poly->getInteriorRing(i - 1)->getNumPoints(); + } + + // Build the point array index table + + OGRPoint p; + m_vertexPtrArray = (double **)malloc(m_ncnt * sizeof(double *)); + + m_vertexPtrArray[0] = (double *)malloc(m_cntr[0] * sizeof(double) * 2); + double *pp = m_vertexPtrArray[0]; + for (int i = 0; i < m_cntr[0]; i++) { + poly->getExteriorRing()->getPoint(i, &p); + + // Calculate SM from feature reference point + double easting, northing; + toSM_plib(p.getY(), p.getX(), m_feature_ref_lat, m_feature_ref_lon, &easting, + &northing); + *pp++ = easting; // x + *pp++ = northing; // y + } + + for (int i = 1; i < m_ncnt; i++) { + m_vertexPtrArray[i] = (double *)malloc(m_cntr[i] * sizeof(double) * 2); + double *pp = m_vertexPtrArray[i]; + for (int j = 0; j < m_cntr[i]; j++) { + poly->getInteriorRing(i - 1)->getPoint(j, &p); + + double easting, northing; + toSM_plib(p.getY(), p.getX(), m_feature_ref_lat, m_feature_ref_lon, &easting, + &northing); + *pp++ = easting; // x + *pp++ = northing; // y + } + } + + mx_rate = 1.0; + mx_offset = 0.0; + my_rate = 1.0; + my_offset = 0.0; + + m_bstripify = true; + + earthAxis = CM93_semimajor_axis_meters * kMercatorK0; + + m_bcm93 = false; + + BuildTessGLU(); + + // Free the working memory + for (int i = 0; i < m_ncnt; i++) free(m_vertexPtrArray[i]); + + free(m_vertexPtrArray); + m_vertexPtrArray = nullptr; +} + +void PolyTessGeo::SetExtents(double x_left, double y_bot, double x_right, + double y_top) { + xmin = x_left; + ymin = y_bot; + xmax = x_right; + ymax = y_top; +} + +PolyTessGeo::~PolyTessGeo() { + delete m_ppg_head; + delete m_pxgeom; +} + +int PolyTessGeo::BuildDeferredTess(void) { + if (m_pxgeom) { + // For cm93 + m_b_senc_sm = false; + m_bmerc_transform = true; + m_tess_orient = TESS_HORZ; // prefer horizontal tristrips + + // Get total number of contours + m_ncnt = m_pxgeom->n_contours; + + // build the contour point countarray + m_cntr = (int *)malloc(m_ncnt * sizeof(int)); + + for (int i = 0; i < m_ncnt; i++) { + m_cntr[i] = m_pxgeom->contour_array[i]; + } + + // Build the point array index table + + m_vertexPtrArray = (double **)malloc(m_ncnt * sizeof(double *)); + + double *vertex_pointer = (double *)m_pxgeom->vertex_array; + vertex_pointer += 2; // skip 0 + + for (int k = 0; k < m_ncnt; k++) { + m_vertexPtrArray[k] = vertex_pointer; + vertex_pointer += 2 * m_cntr[k]; + } + + mx_rate = m_pxgeom->x_rate; + mx_offset = m_pxgeom->x_offset; + my_rate = m_pxgeom->y_rate; + my_offset = m_pxgeom->y_offset; + + m_feature_easting = 0; + m_feature_northing = 0; + + m_ref_lat = m_pxgeom->ref_lat; + m_ref_lon = m_pxgeom->ref_lon; + + // Note Bene... cm93 assumes no flattening constant + earthAxis = CM93_semimajor_axis_meters; + + m_bcm93 = true; + + // For cm93, we prefer TessGLU, since it produces more TriangleStrips, so + // renders faster with less storage. + int rv = BuildTessGLU(); + + // All done with this geometry + delete m_pxgeom; + m_pxgeom = NULL; + + // Free the working memory + free(m_vertexPtrArray); + m_vertexPtrArray = nullptr; + + return rv; + + } else + return 0; +} + +void beginCallback(GLenum which, void *polyData); +void errorCallback(GLenum errorCode, void *polyData); +void endCallback(void *polyData); +void vertexCallback(GLvoid *vertex, void *polyData); +void combineCallback(GLdouble coords[3], GLdouble *vertex_data[4], + GLfloat weight[4], GLdouble **dataOut, void *polyData); + +// Build PolyTessGeo Object from OGR Polygon +// Using OpenGL/GLU tesselator +int PolyTessGeo::BuildTessGLU() { + unsigned int iir, ip; + GLdouble *geoPt; + +#if 0 + // Make a quick sanity check of the polygon coherence + bool b_ok = true; + OGRLineString *tls = poly->getExteriorRing(); + if(!tls) { + b_ok = false; + } + else { + int tnpta = poly->getExteriorRing()->getNumPoints(); + if(tnpta < 3 ) + b_ok = false; + } + + + for( iir=0 ; iir < poly->getNumInteriorRings() ; iir++) + { + int tnptr = poly->getInteriorRing(iir)->getNumPoints(); + if( tnptr < 3 ) + b_ok = false; + } + + if( !b_ok ) + return ERROR_BAD_OGRPOLY; + +#endif + + // Allocate a work buffer, which will be grown as needed +#define NINIT_BUFFER_LEN 10000 + m_pwork_buf = (GLdouble *)malloc(NINIT_BUFFER_LEN * 2 * sizeof(GLdouble)); + m_buf_len = NINIT_BUFFER_LEN * 2; + m_buf_idx = 0; + + // Create an array to hold pointers to allocated vertices created by + // "combine" callback, so that they may be deleted after tesselation. + m_pCombineVertexArray = new wxArrayPtrVoid; + + // Create tesselator + GLUtessobj = gluNewTess(); + + // Register the callbacks + gluTessCallback(GLUtessobj, GLU_TESS_BEGIN_DATA, + (GLvoid(*)()) & beginCallback); + gluTessCallback(GLUtessobj, GLU_TESS_BEGIN_DATA, + (GLvoid(*)()) & beginCallback); + gluTessCallback(GLUtessobj, GLU_TESS_VERTEX_DATA, + (GLvoid(*)()) & vertexCallback); + gluTessCallback(GLUtessobj, GLU_TESS_END_DATA, (GLvoid(*)()) & endCallback); + gluTessCallback(GLUtessobj, GLU_TESS_COMBINE_DATA, + (GLvoid(*)()) & combineCallback); + gluTessCallback(GLUtessobj, GLU_TESS_ERROR_DATA, + (GLvoid(*)()) & errorCallback); + + gluTessProperty(GLUtessobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE); + gluTessNormal(GLUtessobj, 0, 0, 1); + + // Get total number of points(vertices) to build a buffer + int npta = 0; + + for (int i = 0; i < m_ncnt; i++) npta += m_cntr[i]; + + geoPt = + (GLdouble *)malloc((npta + 6) * 3 * sizeof(GLdouble)); // vertex array + + // Grow the work buffer if necessary + + if ((npta * 4) > m_buf_len) { + m_pwork_buf = (GLdouble *)realloc(m_pwork_buf, npta * 4 * sizeof(GLdouble)); + m_buf_len = npta * 4; + } + + // Define the polygon + gluTessBeginPolygon(GLUtessobj, this); + + // Check and account for winding direction of ring + + double x0, y0, x, y; + // OGRPoint p; + + wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[0]; + bool cw = !isRingClockwise(pp, m_cntr[0]); + + // pp++; // skip 0? + + if (cw) { + x0 = pp->m_x; + y0 = pp->m_y; + } else { + x0 = pp[m_cntr[0] - 1].m_x; + y0 = pp[m_cntr[0] - 1].m_y; + } + + unsigned int ptValid = m_cntr[0]; + + // Transcribe points to vertex array, in proper order with no duplicates + // also, accounting for tess_orient + GLdouble *ppt = geoPt; + + for (ip = 0; ip < (unsigned int)m_cntr[0]; ip++) { + int pidx; + if (cw) + pidx = m_cntr[0] - ip - 1; + + else + pidx = ip; + + x = pp[pidx].m_x; + y = pp[pidx].m_y; + + if ((fabs(x - x0) > EQUAL_EPS) || (fabs(y - y0) > EQUAL_EPS)) { + if (m_tess_orient == TESS_VERT) { + *ppt++ = x; + *ppt++ = y; + *ppt++ = 0; + } else { + *ppt++ = y; + *ppt++ = x; + *ppt++ = 0; + } + } else + ptValid--; + + x0 = x; + y0 = y; + } + + // Apply LOD reduction + + if (ptValid > 20 && (m_LOD_meters > .01)) { + std::vector bool_keep(ptValid, false); + + // Keep a few key points + bool_keep[0] = true; + bool_keep[1] = true; + bool_keep[ptValid - 1] = true; + bool_keep[ptValid - 2] = true; + + DouglasPeuckerDI(geoPt, 1, ptValid - 2, m_LOD_meters, bool_keep); + + // Create a new buffer + double *LOD_result = (double *)malloc((m_cntr[0]) * 3 * sizeof(double)); + double *plod = LOD_result; + int kept_LOD = 0; + + for (unsigned int i = 0; i < ptValid; i++) { + if (bool_keep[i]) { + double x = geoPt[i * 3]; + double y = geoPt[(i * 3) + 1]; + *plod++ = x; + *plod++ = y; + *plod++ = 0; + kept_LOD++; + } + } + + // Copy the lod points back into the vertex buffer + memcpy(geoPt, LOD_result, kept_LOD * 3 * sizeof(double)); + + free(LOD_result); + ptValid = kept_LOD; + } + + // Declare the gluContour and copy the points + gluTessBeginContour(GLUtessobj); + + double *DPrun = geoPt; + for (ip = 0; ip < ptValid; ip++) { + for( int i = 0; i < 3; ++i ) { + x = DPrun[i]; + if (x < -GLU_TESS_MAX_COORD) { + m_bOK = false; + ErrorCode = 1; + return 1; + } + if (x > GLU_TESS_MAX_COORD) { + m_bOK = false; + ErrorCode = 2; + return 1; + } + } + gluTessVertex(GLUtessobj, DPrun, DPrun); + DPrun += 3; + } + + gluTessEndContour(GLUtessobj); + + // Now the interior contours +#if 1 + int gpIndex = m_cntr[0]; + + for (iir = 0; iir < (unsigned int)m_ncnt - 1; iir++) { + wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[iir + 1]; + + int npti = m_cntr[iir + 1]; + ptValid = npti; + + double *ppt = &geoPt[gpIndex * 3]; // next available location in geoPT + double *DPStart = ppt; + + // Check and account for winding direction of ring + bool cw = isRingClockwise(pp, m_cntr[iir + 1]); + + if (cw) { + x0 = pp[0].m_x; + y0 = pp[0].m_y; + } else { + x0 = pp[npti - 1].m_x; + y0 = pp[npti - 1].m_y; + } + + // Transcribe points to vertex array, in proper order with no duplicates + // also, accounting for tess_orient + for (int ip = 0; ip < npti; ip++) { + int pidx; + if (cw) // interior contours must be cw + pidx = npti - ip - 1; + else + pidx = ip; + + x = pp[pidx].m_x; + y = pp[pidx].m_y; + + if ((fabs(x - x0) > EQUAL_EPS) || (fabs(y - y0) > EQUAL_EPS)) { + if (m_tess_orient == TESS_VERT) { + *ppt++ = x; + *ppt++ = y; + *ppt++ = 0; + } else { + *ppt++ = y; + *ppt++ = x; + *ppt++ = 0; + } + } else + ptValid--; + + x0 = x; + y0 = y; + } + + // Declare the gluContour and reference the points + gluTessBeginContour(GLUtessobj); + + double *DPruni = DPStart; + for (ip = 0; ip < ptValid; ip++) { + gluTessVertex(GLUtessobj, DPruni, DPruni); + DPruni += 3; + } + + gluTessEndContour(GLUtessobj); + + gpIndex += m_cntr[iir + 1]; + } +#endif + + m_pTPG_Last = NULL; + m_pTPG_Head = NULL; + m_nvmax = 0; + + // Ready to kick off the tesselator + gluTessEndPolygon(GLUtessobj); // here it goes + + m_nvertex_max = m_nvmax; // record largest vertex count, updates in callback + + // Tesselation all done, so... + + // Create the data structures + + m_ppg_head = new PolyTriGroup; + m_ppg_head->m_bSMSENC = m_b_senc_sm; + + m_ppg_head->nContours = m_ncnt; + + m_ppg_head->pn_vertex = m_cntr; // pointer to array of poly vertex counts + m_ppg_head->data_type = DATA_TYPE_DOUBLE; + + // Transcribe the raw geometry buffer + // Converting to float as we go, and + // allowing for tess_orient + // Also, convert to SM if requested + + // Recalculate the size of the geometry buffer + unsigned int nptfinal = m_cntr[0] + 2; + // for(int i=0 ; i < nint ; i++) + // nptfinal += cntr[i+1] + 2; + + // No longer need the full geometry in the SENC, + nptfinal = 1; + + m_nwkb = (nptfinal + 1) * 2 * sizeof(float); + m_ppg_head->pgroup_geom = (float *)calloc(sizeof(float), (nptfinal + 1) * 2); + float *vro = m_ppg_head->pgroup_geom; + ppt = geoPt; + float tx, ty; + + for (ip = 0; ip < nptfinal; ip++) { + if (TESS_HORZ == m_tess_orient) { + ty = *ppt++; + tx = *ppt++; + } else { + tx = *ppt++; + ty = *ppt++; + } + + if (m_b_senc_sm) { + // Calculate SM from chart common reference point + double easting, northing; + toSM_plib(ty, tx, m_ref_lat, m_ref_lon, &easting, &northing); + *vro++ = easting; // x + *vro++ = northing; // y + } else { + *vro++ = tx; // lon + *vro++ = ty; // lat + } + + ppt++; // skip z + } + + m_ppg_head->tri_prim_head = m_pTPG_Head; // head of linked list of TriPrims + + // Convert the Triangle vertex arrays into a single memory allocation of + // floats to reduce SENC size and enable efficient access later + + // First calculate the total byte size + int total_byte_size = 2 * sizeof(float); + TriPrim *p_tp = m_ppg_head->tri_prim_head; + while (p_tp) { + total_byte_size += p_tp->nVert * 2 * sizeof(float); + p_tp = p_tp->p_next; // pick up the next in chain + } + + float *vbuf = (float *)malloc(total_byte_size); + p_tp = m_ppg_head->tri_prim_head; + float *p_run = vbuf; + while (p_tp) { + float *pfbuf = p_run; + GLdouble *pdouble_buf = (GLdouble *)p_tp->p_vertex; + + for (int i = 0; i < p_tp->nVert * 2; ++i) { + float x = (float)(*((GLdouble *)pdouble_buf)); + pdouble_buf++; + *p_run++ = x; + } + + free(p_tp->p_vertex); + p_tp->p_vertex = (double *)pfbuf; + p_tp = p_tp->p_next; // pick up the next in chain + } + m_ppg_head->bsingle_alloc = true; + m_ppg_head->single_buffer = (unsigned char *)vbuf; + m_ppg_head->single_buffer_size = total_byte_size; + m_ppg_head->data_type = DATA_TYPE_FLOAT; + + gluDeleteTess(GLUtessobj); + + free(m_pwork_buf); + m_pwork_buf = NULL; + + free(geoPt); + + // Free up any "Combine" vertices created + for (unsigned int i = 0; i < m_pCombineVertexArray->GetCount(); i++) + free(m_pCombineVertexArray->Item(i)); + delete m_pCombineVertexArray; + + m_bOK = true; + + return 0; +} + +int PolyTessGeo::BuildTess(void) { + // Setup the tesselator + + TESSalloc ma; + TESStesselator *tess = 0; + const int nvp = 3; +// unsigned char* vflags = 0; +#ifdef USE_POOL + struct MemPool pool; + unsigned char mem[4024 * 1024]; +// int nvflags = 0; +#else + int allocated = 0; +#endif + +#ifdef USE_POOL + + pool.size = 0; + pool.cap = sizeof(mem); + pool.buf = mem; + memset(&ma, 0, sizeof(ma)); + ma.memalloc = poolAlloc; + ma.memfree = poolFree; + ma.userData = (void *)&pool; + ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices. + +#else + + memset(&ma, 0, sizeof(ma)); + ma.memalloc = stdAlloc; + ma.memfree = stdFree; + ma.userData = (void *)&allocated; + ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices. +#endif + + tess = tessNewTess(&ma); + if (!tess) return -1; + + // tessSetOption(tess, TESS_CONSTRAINED_DELAUNAY_TRIANGULATION, 1); + + // Create the contour vertex arrays + + int iir, ip; + + // Get max number number of points(vertices) in any contour + int npta = m_cntr[0]; + npta += 2; // fluff + + for (iir = 0; iir < m_ncnt - 1; iir++) { + int nptr = m_cntr[iir + 1]; + npta = wxMax(npta, nptr); + } + + TESSreal *geoPt = (TESSreal *)malloc( + (npta)*2 * sizeof(TESSreal)); // tess input vertex array + TESSreal *ppt = geoPt; + + // Create input structures + + // Exterior Ring + int npte = m_cntr[0]; + unsigned int ptValid = npte; + + // Check and account for winding direction of ring + + double x0, y0, x, y; + OGRPoint p; + + wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[0]; + bool cw = isRingClockwise(pp, m_cntr[0]); + + // pp++; // skip 0? + + if (cw) { + x0 = pp->m_x; + y0 = pp->m_y; + } else { + x0 = pp[npte - 1].m_x; + y0 = pp[npte - 1].m_y; + } + + // Transcribe points to vertex array, in proper order with no duplicates + // also, accounting for tess_orient + for (ip = 0; ip < npte; ip++) { + int pidx; + if (cw) + pidx = npte - ip - 1; + + else + pidx = ip; + + x = pp[pidx].m_x; + y = pp[pidx].m_y; + + if ((fabs(x - x0) > EQUAL_EPS) || (fabs(y - y0) > EQUAL_EPS)) { + if (m_tess_orient == TESS_VERT) { + *ppt++ = x; + *ppt++ = y; + } else { + *ppt++ = y; + *ppt++ = x; + } + } else + ptValid--; + + x0 = x; + y0 = y; + } + + // Apply LOD reduction + int beforeLOD = ptValid; + int afterLOD = beforeLOD; + + std::vector bool_keep; + if (ptValid > 5 && (m_LOD_meters > .01)) { + for (unsigned int i = 0; i < ptValid; i++) bool_keep.push_back(false); + + // Keep a few key points + bool_keep[0] = true; + bool_keep[1] = true; + bool_keep[ptValid - 1] = true; + bool_keep[ptValid - 2] = true; + + DouglasPeuckerFI(geoPt, 1, ptValid - 2, m_LOD_meters, bool_keep); + + // Create a new buffer + float *LOD_result = (float *)malloc((npte)*2 * sizeof(float)); + float *plod = LOD_result; + int kept_LOD = 0; + + for (unsigned int i = 0; i < ptValid; i++) { + if (bool_keep[i]) { + float x = geoPt[i * 2]; + float y = geoPt[(i * 2) + 1]; + *plod++ = x; + *plod++ = y; + kept_LOD++; + } + } + + beforeLOD = ptValid; + afterLOD = kept_LOD; + + tessAddContour(tess, 2, LOD_result, sizeof(float) * 2, kept_LOD); + + free(LOD_result); + } else { + tessAddContour(tess, 2, geoPt, sizeof(float) * 2, ptValid); + } + +#if 1 + // Now the interior contours + for (iir = 0; iir < m_ncnt - 1; iir++) { + ppt = geoPt; + wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[iir + 1]; + + int npti = m_cntr[iir + 1]; + ptValid = npti; + + // Check and account for winding direction of ring + bool cw = isRingClockwise(pp, m_cntr[iir + 1]); + + if (!cw) { + x0 = pp[0].m_x; + y0 = pp[0].m_y; + } else { + x0 = pp[npti - 1].m_x; + y0 = pp[npti - 1].m_y; + } + + // Transcribe points to vertex array, in proper order with no duplicates + // also, accounting for tess_orient + for (int ip = 0; ip < npti; ip++) { + OGRPoint p; + int pidx; + if (!cw) // interior contours must be cw + pidx = npti - ip - 1; + else + pidx = ip; + + x = pp[pidx].m_x; + y = pp[pidx].m_y; + + if ((fabs(x - x0) > EQUAL_EPS) || (fabs(y - y0) > EQUAL_EPS)) { + if (m_tess_orient == TESS_VERT) { + *ppt++ = x; + *ppt++ = y; + } else { + *ppt++ = y; + *ppt++ = x; + } + } else + ptValid--; + + x0 = x; + y0 = y; + } + + tessAddContour(tess, 2, geoPt, sizeof(float) * 2, ptValid); + } +#endif + + // Ready to kick off the tesselator + + TriPrim *pTPG_Last = NULL; + TriPrim *pTPG_Head = NULL; + + // s_nvmax = 0; + + // OCPNStopWatchTess tt0; + + if (!tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, nvp, 2, 0)) + return -1; + double tessTime = 0; // tt0.GetTime(); + + // Tesselation all done, so... + // Create the data structures + + // Make a linked list of TriPrims from tess output arrays + + const TESSreal *verts = tessGetVertices(tess); + const int *vinds = tessGetVertexIndices(tess); + const int *elems = tessGetElements(tess); + const int nverts = tessGetVertexCount(tess); + int nelems = tessGetElementCount(tess); + + bool skip = false; + // skip = true; + + double stripTime = 0; + // tt0.Reset(); + + int bytes_needed_vbo = 0; + float *vbo = 0; + + if (m_bstripify && nelems && !skip) { + STRIPERCREATE sc; + sc.DFaces = (udword *)elems; + sc.NbFaces = nelems; + sc.AskForWords = false; + sc.ConnectAllStrips = false; + sc.OneSided = false; + sc.SGIAlgorithm = false; + + Striper Strip; + Strip.Init(sc); + + STRIPERRESULT sr; + Strip.Compute(sr); + + stripTime = 0; // tt0.GetTime(); + + /* + fprintf(stdout, "Number of strips: %d\n", sr.NbStrips); + fprintf(stdout, "Number of points: %d\n", nelems); + uword* Refs = (uword*)sr.StripRuns; + for(udword i=0;i= 3) { // this is a valid primitive + + TriPrim *pTPG = new TriPrim; + if (NULL == pTPG_Last) { + pTPG_Head = pTPG; + pTPG_Last = pTPG; + } else { + pTPG_Last->p_next = pTPG; + pTPG_Last = pTPG; + } + + pTPG->p_next = NULL; + pTPG->nVert = NbRefs; + + // pTPG->p_vertex = (double *)malloc(NbRefs * 2 * + // sizeof(double)); GLdouble *pdd = + // (GLdouble*)pTPG->p_vertex; + + pTPG->p_vertex = (double *)vbo_run; + + // Preset LLBox bounding box limits + double sxmax = -1e8; + double sxmin = 1e8; + double symax = -1e8; + double symin = 1e8; + + if (NbRefs > 3) + pTPG->type = GL_TRIANGLE_STRIP; + else + pTPG->type = GL_TRIANGLES; + + // Add the first two points + int vindex[3]; + vindex[0] = *Refs++; + vindex[1] = *Refs++; + unsigned int np = 2; // for the first triangle + + for (unsigned int i = 2; i < NbRefs; i++) { + vindex[np] = *Refs++; + np++; + + for (unsigned int j = 0; j < np; ++j) { + double yd = verts[vindex[j] * 2]; + double xd = verts[vindex[j] * 2 + 1]; + + // Calculate LLBox bounding box for each Tri-prim + if (m_bmerc_transform) { + double valx = (xd * mx_rate) + mx_offset; + double valy = (yd * my_rate) + my_offset; + + // quickly convert to lat/lon + double lat = (2.0 * atan(exp(valy / CM93_semimajor_axis_meters)) - + M_PI / 2.) / + kRadiansPerDegree; + double lon = (valx / (kRadiansPerDegree * CM93_semimajor_axis_meters)); + + sxmax = wxMax(lon, sxmax); + sxmin = wxMin(lon, sxmin); + symax = wxMax(lat, symax); + symin = wxMin(lat, symin); + } else { + sxmax = wxMax(xd, sxmax); + sxmin = wxMin(xd, sxmin); + symax = wxMax(yd, symax); + symin = wxMin(yd, symin); + } + + // Append this point to TriPrim vbo + + *vbo_run++ = + (xd) + m_feature_easting; // adjust to chart ref coordinates + *vbo_run++ = (yd) + m_feature_northing; + } // For + + // Compute the final LLbbox for this TriPrim chain + double minlat, minlon, maxlat, maxlon; + fromSM_plib(sxmin, symin, m_feature_ref_lat, m_feature_ref_lon, &minlat, + &minlon); + fromSM_plib(sxmax, symax, m_feature_ref_lat, m_feature_ref_lon, &maxlat, + &maxlon); + + pTPG->tri_box.Set(minlat, minlon, maxlat, maxlon); + + // set for next single point + np = 0; + } + } else + Refs += sr.StripLengths[i]; + + } // for strips + } else { // not stripified + + m_nvertex_max = nverts; // record largest vertex count + + bytes_needed_vbo = nelems * nvp * 2 * sizeof(float); + vbo = (float *)malloc(bytes_needed_vbo); + float *vbo_run = vbo; + + for (int i = 0; i < nelems; ++i) { + const int *p = &elems[i * nvp]; + + TriPrim *pTPG = new TriPrim; + if (NULL == pTPG_Last) { + pTPG_Head = pTPG; + pTPG_Last = pTPG; + } else { + pTPG_Last->p_next = pTPG; + pTPG_Last = pTPG; + } + + pTPG->p_next = NULL; + pTPG->type = GL_TRIANGLES; + pTPG->nVert = nvp; + + // pTPG->p_vertex = (double *)malloc(nvp * 2 * sizeof(double)); + // GLdouble *pdd = (GLdouble*)pTPG->p_vertex; + + pTPG->p_vertex = (double *)vbo_run; + + // Preset LLBox bounding box limits + double sxmax = -1e8; + double sxmin = 1e8; + double symax = -1e8; + double symin = 1e8; + + for (size_t j = 0; j < nvp && p[j] != TESS_UNDEF; ++j) { + double yd = verts[p[j] * 2]; + double xd = verts[p[j] * 2 + 1]; + + // Calculate LLBox bounding box for each Tri-prim + if (m_bmerc_transform) { + double valx = (xd * mx_rate) + mx_offset; + double valy = (yd * my_rate) + my_offset; + + // quickly convert to lat/lon + double lat = + (2.0 * atan(exp(valy / CM93_semimajor_axis_meters)) - M_PI / 2.) / + kRadiansPerDegree; + double lon = (valx / (kRadiansPerDegree * CM93_semimajor_axis_meters)); + + sxmax = wxMax(lon, sxmax); + sxmin = wxMin(lon, sxmin); + symax = wxMax(lat, symax); + symin = wxMin(lat, symin); + } else { + sxmax = wxMax(xd, sxmax); + sxmin = wxMin(xd, sxmin); + symax = wxMax(yd, symax); + symin = wxMin(yd, symin); + } + + // Append this point to TriPrim, converting to SM if called for + + if (m_b_senc_sm) { + double easting, northing; + toSM_plib(yd, xd, m_ref_lat, m_ref_lon, &easting, &northing); + *vbo_run++ = easting; + *vbo_run++ = northing; + } else { + *vbo_run++ = xd; + *vbo_run++ = yd; + } + } + + pTPG->tri_box.Set(symin, sxmin, symax, sxmax); + } + } // stripify + + if (m_printStats) { + int nTri = 0; + int nStrip = 0; + + TriPrim *p_tp = pTPG_Head; + while (p_tp) { + if (p_tp->type == GL_TRIANGLES) nTri++; + if (p_tp->type == GL_TRIANGLE_STRIP) nStrip++; + + p_tp = p_tp->p_next; // pick up the next in chain + } + + if ((nTri + nStrip) > 10000) { + printf("LOD: %d/%d\n", afterLOD, beforeLOD); + + printf("Tess time(ms): %f\n", tessTime); + printf("Strip time(ms): %f\n", stripTime); + + printf("Primitives: Tri: %5d Strip: %5d Total: %5d\n", nTri, nStrip, + nTri + nStrip); + printf("\n"); + } + } + + m_ppg_head = new PolyTriGroup; + m_ppg_head->m_bSMSENC = m_b_senc_sm; + + m_ppg_head->nContours = m_ncnt; + m_ppg_head->pn_vertex = m_cntr; // pointer to array of poly vertex counts + m_ppg_head->tri_prim_head = pTPG_Head; // head of linked list of TriPrims + // m_ppg_head->data_type = DATA_TYPE_DOUBLE; + + // Transcribe the raw geometry buffer + // Converting to float as we go, and + // allowing for tess_orient + // Also, convert to SM if requested + + int nptfinal = npta; + + // No longer need the full geometry in the SENC, + nptfinal = 1; + + m_nwkb = (nptfinal + 1) * 2 * sizeof(float); + m_ppg_head->pgroup_geom = (float *)malloc(m_nwkb); + float *vro = m_ppg_head->pgroup_geom; + ppt = geoPt; + float tx, ty; + + for (ip = 0; ip < nptfinal; ip++) { + if (TESS_HORZ == m_tess_orient) { + ty = *ppt++; + tx = *ppt++; + } else { + tx = *ppt++; + ty = *ppt++; + } + + if (m_b_senc_sm) { + // Calculate SM from chart common reference point + double easting, northing; + toSM_plib(ty, tx, 0 /*ref_lat*/, 0 /*ref_lon*/, &easting, &northing); + *vro++ = easting; // x + *vro++ = northing; // y + } else { + *vro++ = tx; // lon + *vro++ = ty; // lat + } + + ppt++; // skip z + } + + m_ppg_head->bsingle_alloc = true; + m_ppg_head->single_buffer = (unsigned char *)vbo; + m_ppg_head->single_buffer_size = bytes_needed_vbo; + m_ppg_head->data_type = DATA_TYPE_FLOAT; + + free(geoPt); + + // All allocated buffers are owned now by the m_ppg_head + // And will be freed on dtor of this object + + if (tess) tessDeleteTess(tess); + + m_bOK = true; + + return 0; +} + +// GLU tesselation support functions +void beginCallback(GLenum which, void *polyData) { + PolyTessGeo *pThis = (PolyTessGeo *)polyData; + + pThis->m_buf_idx = 0; + pThis->m_nvcall = 0; + pThis->m_gltri_type = which; +} + +void errorCallback(GLenum errorCode, void *polyData) { + const GLubyte *estring; + + estring = gluErrorString(errorCode); + printf("Tessellation Error: %s\n", estring); + exit(0); +} + +void endCallback(void *polyData) { + PolyTessGeo *pThis = (PolyTessGeo *)polyData; + + // Create a TriPrim + if (pThis->m_nvcall > + pThis->m_nvmax) // keep track of largest number of triangle vertices + pThis->m_nvmax = pThis->m_nvcall; + + switch (pThis->m_gltri_type) { + case GL_TRIANGLE_FAN: + case GL_TRIANGLE_STRIP: + case GL_TRIANGLES: { + TriPrim *pTPG = new TriPrim; + if (NULL == pThis->m_pTPG_Last) { + pThis->m_pTPG_Head = pTPG; + pThis->m_pTPG_Last = pTPG; + } else { + pThis->m_pTPG_Last->p_next = pTPG; + pThis->m_pTPG_Last = pTPG; + } + + pTPG->p_next = NULL; + pTPG->type = pThis->m_gltri_type; + pTPG->nVert = pThis->m_nvcall; + + // Calculate bounding box + double sxmax = -1e8; // this poly BBox + double sxmin = 1e8; + double symax = -1e8; + double symin = 1e8; + + GLdouble *pvr = pThis->m_pwork_buf; + for (int iv = 0; iv < pThis->m_nvcall; iv++) { + GLdouble xd, yd; + xd = *pvr++; + yd = *pvr++; + + if (pThis->m_bcm93) { + // cm93 hits here + double valx = (xd * pThis->mx_rate) + pThis->mx_offset + + pThis->m_feature_easting; + double valy = (yd * pThis->my_rate) + pThis->my_offset + + pThis->m_feature_northing; + + // Convert to lat/lon + double lat = + (2.0 * atan(exp(valy / CM93_semimajor_axis_meters)) - M_PI / 2.) / + kRadiansPerDegree; + double lon = (valx / (kRadiansPerDegree * CM93_semimajor_axis_meters)); + + sxmax = wxMax(lon, sxmax); + sxmin = wxMin(lon, sxmin); + symax = wxMax(lat, symax); + symin = wxMin(lat, symin); + } else { + // ENC hits here, values are SM measures from feature reference point + double valx = (xd * pThis->mx_rate) + pThis->mx_offset + + pThis->m_feature_easting; + double valy = (yd * pThis->my_rate) + pThis->my_offset + + pThis->m_feature_northing; + + sxmax = wxMax(valx, sxmax); + sxmin = wxMin(valx, sxmin); + symax = wxMax(valy, symax); + symin = wxMin(valy, symin); + } + } + + // Compute the final LLbbox for this TriPrim chain + if (pThis->m_bcm93) + pTPG->tri_box.Set(symin, sxmin, symax, sxmax); + else { + double minlat, minlon, maxlat, maxlon; + fromSM_plib(sxmin, symin, pThis->m_ref_lat, pThis->m_ref_lon, &minlat, + &minlon); + fromSM_plib(sxmax, symax, pThis->m_ref_lat, pThis->m_ref_lon, &maxlat, + &maxlon); + pTPG->tri_box.Set(minlat, minlon, maxlat, maxlon); + } + + // Transcribe this geometry to TriPrim, + { + GLdouble *pds = pThis->m_pwork_buf; + pTPG->p_vertex = (double *)malloc(pThis->m_nvcall * 2 * sizeof(double)); + GLdouble *pdd = (GLdouble *)pTPG->p_vertex; + + for (int ip = 0; ip < pThis->m_nvcall; ip++) { + double dlon = *pds++; + double dlat = *pds++; + *pdd++ = + dlon + + pThis->m_feature_easting; // adjust to feature ref coordinates + *pdd++ = dlat + pThis->m_feature_northing; + } + } + + break; + } + default: { + // sprintf(buf, "....begin Callback unknown\n"); + break; + } + } +} + +void vertexCallback(GLvoid *vertex, void *polyData) { + PolyTessGeo *pThis = (PolyTessGeo *)polyData; + + GLdouble *pointer; + + pointer = (GLdouble *)vertex; + + if (pThis->m_buf_idx > pThis->m_buf_len - 4) { + int new_buf_len = pThis->m_buf_len + 100; + GLdouble *tmp = pThis->m_pwork_buf; + + pThis->m_pwork_buf = + (GLdouble *)realloc(pThis->m_pwork_buf, new_buf_len * sizeof(GLdouble)); + if (NULL == pThis->m_pwork_buf) { + free(tmp); + tmp = NULL; + } else + pThis->m_buf_len = new_buf_len; + } + + if (pThis->m_tess_orient == TESS_VERT) { + pThis->m_pwork_buf[pThis->m_buf_idx++] = pointer[0]; + pThis->m_pwork_buf[pThis->m_buf_idx++] = pointer[1]; + } else { + pThis->m_pwork_buf[pThis->m_buf_idx++] = pointer[1]; + pThis->m_pwork_buf[pThis->m_buf_idx++] = pointer[0]; + } + + pThis->m_nvcall++; +} + +/* combineCallback is used to create a new vertex when edges + * intersect. + */ +void combineCallback(GLdouble coords[3], GLdouble *vertex_data[4], + GLfloat weight[4], GLdouble **dataOut, void *polyData) { + PolyTessGeo *pThis = (PolyTessGeo *)polyData; + + GLdouble *vertex = (GLdouble *)malloc(6 * sizeof(GLdouble)); + + vertex[0] = coords[0]; + vertex[1] = coords[1]; + vertex[2] = coords[2]; + vertex[3] = vertex[4] = vertex[5] = 0.; // 01/13/05 bugfix + + *dataOut = vertex; + + pThis->m_pCombineVertexArray->Add(vertex); +} + +wxStopWatch *s_stwt; + +//------------------------------------------------------------------------------ +// PolyTriGroup Implementation +//------------------------------------------------------------------------------ +PolyTriGroup::PolyTriGroup() { + pn_vertex = NULL; // pointer to array of poly vertex counts + pgroup_geom = NULL; // pointer to Raw geometry, used for contour line drawing + tri_prim_head = NULL; // head of linked list of TriPrims + m_bSMSENC = false; + bsingle_alloc = false; + single_buffer = NULL; + single_buffer_size = 0; + data_type = DATA_TYPE_DOUBLE; + sfactor = 1.0; +} + +PolyTriGroup::~PolyTriGroup() { + free(pn_vertex); + free(pgroup_geom); + // Walk the list of TriPrims, deleting as we go + TriPrim *tp_next; + TriPrim *tp = tri_prim_head; + + if (bsingle_alloc) { + free(single_buffer); + while (tp) { + tp_next = tp->p_next; + delete tp; + tp = tp_next; + } + } else { + while (tp) { + tp_next = tp->p_next; + tp->FreeMem(); + delete tp; + tp = tp_next; + } + } +} + +//------------------------------------------------------------------------------ +// TriPrim Implementation +//------------------------------------------------------------------------------ +TriPrim::TriPrim() {} + +TriPrim::~TriPrim() {} + +void TriPrim::FreeMem() { free(p_vertex); } diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.h new file mode 100644 index 0000000..50ae402 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.h @@ -0,0 +1,304 @@ +/****************************************************************************** + * + * Project: OpenCPN + * Purpose: Tesselation of Polygon Object + * Author: David Register + * + *************************************************************************** + * Copyright (C) 2010 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + *************************************************************************** + * + * + */ + + +#ifndef __MYGEOM_H +#define __MYGEOM_H + +// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" +//#include + +#if 0 +#if defined(__OCPN__ANDROID__) + #include + #include // this is a cut-down version of gl.h + #include +#elif defined(__MSVC__) + #include "glew.h" +#elif defined(__WXOSX__) + #include + #include + typedef void (* _GLUfuncptr)(); + #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#elif defined(__WXQT__) || defined(__WXGTK__) + #include + #include +#endif +#endif + +#if defined(__OCPN__ANDROID__) +#include +#include // this is a cut-down version of gl.h +#include +#else +#ifdef ocpnUSE_GL +#if defined(__MSVC__) +#include "glew.h" +#elif defined(__WXOSX__) +#include +#include +typedef void (* _GLUfuncptr)(); +#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#elif defined(__WXQT__) || defined(__WXGTK__) +#include +#include +#endif +#endif +#endif + + +class OGRGeometry; +class OGRPolygon; + +#define TESS_VERT 0 // constants describing preferred tess orientation +#define TESS_HORZ 1 + +#define EQUAL_EPS 1.0e-7 // tolerance value + +// nota bene These definitions are identical to OpenGL prototypes +#define PTG_TRIANGLES 0x0004 +#define PTG_TRIANGLE_STRIP 0x0005 +#define PTG_TRIANGLE_FAN 0x0006 + +// Error Return Codes +#define ERROR_NONE 0 +#define ERROR_NO_DLL 1 +#define ERROR_BAD_OGRPOLY 2 + +// Some external prototypes + +#define DATA_TYPE_FLOAT 0 +#define DATA_TYPE_DOUBLE 1 +#define DATA_TYPE_SHORT 2 + +//-------------------------------------------------------------------------------------------------- +// +// Internal data structures used for describing/rendering tesselated +// polygons +// +//-------------------------------------------------------------------------------------------------- + +class Extended_Geometry { +public: + Extended_Geometry(); + ~Extended_Geometry(); + + OGRGeometry *pogrGeom; + int n_vector_indices; + int *pvector_index; + int n_contours; // parameters passed to trapezoid tesselator + int *contour_array; + int n_max_vertex; + int pointx; + int pointy; + wxPoint2DDouble *vertex_array; + int xmin, xmax, ymin, ymax; + int n_max_edge_points; + + // Conversion parameters + // for (assummed linear) convertions from vertex_array points to + // easting/northing, metres from 0,0 To convert to lat/lon, use simple + // merctor equations + double x_rate; + double x_offset; + double y_rate; + double y_offset; + double ref_lat; // Lower left corner of the cell containing this feature + double ref_lon; +}; + +class TriPrim { +public: + TriPrim(); + ~TriPrim(); + void FreeMem(void); + + unsigned int type; // Type of triangle primitive + // May be PTG_TRIANGLES + // PTG_TRIANGLE_STRIP + // PTG_TRIANGLE_FAN + + int nVert; + double *p_vertex; // Pointer to vertex array, x,y,x,y..... + + LLBBox tri_box; + + TriPrim *p_next; // chain link +}; + +class LegacyTriPrim { +public: + LegacyTriPrim(); + ~LegacyTriPrim(); + void FreeMem(void); + + unsigned int type; // Type of triangle primitive + // May be PTG_TRIANGLES + // PTG_TRIANGLE_STRIP + // PTG_TRIANGLE_FAN + + int nVert; + double *p_vertex; // Pointer to vertex array, x,y,x,y..... + + double minx, miny, maxx, maxy; + + LegacyTriPrim *p_next; // chain link +}; + +class PolyTriGroup { +public: + PolyTriGroup(); + PolyTriGroup(Extended_Geometry *pxGeom); + ~PolyTriGroup(); + + int nContours; + int *pn_vertex; // pointer to array of poly vertex counts + float *pgroup_geom; // pointer to Raw geometry, used for contour line drawing + + TriPrim *tri_prim_head; // head of linked list of TriPrims + bool m_bSMSENC; + bool bsingle_alloc; + unsigned char *single_buffer; + int single_buffer_size; + int data_type; // p_vertex in TriPrim chain is FLOAT or DOUBLE + float sfactor; + float soffset; + +private: + int my_bufgets(char *buf, int buf_len_max); +}; + +typedef struct { + int ilseg; + int irseg; + double loy; + double hiy; + +} trapz_t; + +//-------------------------------------------------------------------------------------------------- +// +// Triangle Tesselator Class +// +//-------------------------------------------------------------------------------------------------- +class OGRPolygon; +class GLUtesselator; + +class PolyTessGeo { +public: + PolyTessGeo(); + ~PolyTessGeo(); + + PolyTessGeo(OGRPolygon *poly, bool bSENC_SM, double ref_lat, double ref_lon, + double LOD_meters); // Build this from OGRPolygon + + PolyTessGeo(Extended_Geometry *pxGeom); + + PolyTessGeo(unsigned char *polybuf, int nrecl, int index, + int senc_file_version); + + bool IsOk() { return m_bOK; } + + int BuildDeferredTess(void); + + double Get_xmin() { return xmin; } + double Get_xmax() { return xmax; } + double Get_ymin() { return ymin; } + double Get_ymax() { return ymax; } + void SetExtents(double x_left, double y_bot, double x_right, double y_top); + + PolyTriGroup *Get_PolyTriGroup_head() { return m_ppg_head; } + int GetnVertexMax() { return m_nvertex_max; } + void SetnVertexMax(int max) { m_nvertex_max = max; } + int GetnContours() { return m_ncnt; } + + int ErrorCode; + void GetRefPos( double *lat, double *lon); + void Set_PolyTriGroup_head(PolyTriGroup *head) { m_ppg_head = head; } + void Set_OK(bool bok) { m_bOK = bok; } + + void SetPPGHead(PolyTriGroup *head) { m_ppg_head = head; } + int my_bufgets(char *buf, int buf_len_max); + + GLUtesselator *GLUtessobj; + wxArrayPtrVoid *m_pCombineVertexArray; + double *m_pwork_buf; + int m_buf_len; + int m_buf_idx; + TriPrim *m_pTPG_Last; + TriPrim *m_pTPG_Head; + int m_gltri_type; + int m_nvcall; + int m_nvmax; + bool m_bmerc_transform; + double mx_rate, mx_offset, my_rate, my_offset; + bool m_b_senc_sm; + double m_ref_lat, m_ref_lon; + int m_tess_orient; + double m_feature_ref_lat, m_feature_ref_lon; + double m_feature_easting, m_feature_northing; + + double earthAxis; + bool m_bcm93; + +private: + int BuildTess(void); + // int BuildTessGL2(void); + // int PolyTessGeoGL(OGRPolygon *poly, bool bSENC_SM, double ref_lat, double + // ref_lon); + int BuildTessGLU(void); + int BuildTessGLFromXG(void); + + // Data + + bool m_bOK; + + Extended_Geometry *m_pxgeom; + + double xmin, xmax, ymin, ymax; + PolyTriGroup *m_ppg_head; // head of a PolyTriGroup chain + int m_nvertex_max; // and computed max vertex count + // used by drawing primitives as + // optimization + + int m_ncnt; + int m_nwkb; + int *m_cntr; + char *m_buf_head; + char *m_buf_ptr; // used to read passed SENC record + int m_nrecl; + + double m_LOD_meters; + + double **m_vertexPtrArray; + bool m_printStats; + bool m_bstripify; +}; + +#endif diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.cpp new file mode 100644 index 0000000..c292546 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.cpp @@ -0,0 +1,20 @@ +#include "s52_plib_utils.h" + +static class _DefaultUtils : public S52PlibUtils { + /** Default implementation: return unscaled font. */ + wxFont* GetFont(wxFont* font, double scale) override { return font; } + wxFont* GetDefaultFont(wxString label, int Ptsize) override { + return nullptr; + } + wxFont* GetScaledFont(int pointSize, wxFontFamily family, wxFontStyle style, + wxFontWeight weight, const wxString faceName, + double scale) override { + return nullptr; + } + +} DefaultUtils; + +static S52PlibUtils* s_the_utils = &DefaultUtils; + +S52PlibUtils* GetS52Utils() { return s_the_utils; } +void SetUtils(S52PlibUtils* utils) { s_the_utils = utils; } diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.h new file mode 100644 index 0000000..e067951 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.h @@ -0,0 +1,20 @@ +#ifndef S52_PLIB_UTILS_H +#define S52_PLIB_UTILS_H + +#include + +/** Interface implemented by calling code */ +class S52PlibUtils { +public: + /** Get a scaled font based on given; created if not already existing. */ + virtual wxFont* GetFont(wxFont* font, double scale) = 0; + virtual wxFont* GetDefaultFont(wxString label, int Ptsize) = 0; + virtual wxFont* GetScaledFont(int pointSize, wxFontFamily family, + wxFontStyle style, wxFontWeight weight, + const wxString faceName, double scale) = 0; +}; + +S52PlibUtils* GetS52Utils(); + +void SetUtils(S52PlibUtils* utils); +#endif // S52_PLIB_UTILS_H diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52cnsy.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52cnsy.cpp new file mode 100644 index 0000000..a117c1b --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52cnsy.cpp @@ -0,0 +1,6261 @@ +/****************************************************************************** + * + * Project: OpenCPN + * Purpose: S52 Conditional Symbology Library + * Author: David Register, Sylvain Duclos + * + *************************************************************************** + * Copyright (C) 2010 by David S. Register * + * + * Copyright (C) 2000-2001 Sylvain Duclos + * sylvain_duclos@yahoo.com + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + *************************************************************************** + * + */ + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif // precompiled headers + +#include "wx/tokenzr.h" + +#include "s52plib.h" +#include "s52utils.h" + +bool GetDoubleAttr(S57Obj *obj, const char *AttrName, double &val); + +#define UNKNOWN 1e6 // HUGE_VAL // INFINITY/NAN + +#ifndef chk_snprintf +#define chk_snprintf(buf, len, fmt, ...) \ + { \ + int r = snprintf(buf, len, fmt, ##__VA_ARGS__); \ + if (r == -1 || r >= len) wxLogWarning("snprint overrun"); \ + } +#endif + +WX_DEFINE_ARRAY_DOUBLE(double, ArrayOfSortedDoubles); + +// size of attributes value list buffer +#define LISTSIZE 32 // list size + +extern s52plib *ps52plib; + +wxString *CSQUAPNT01(S57Obj *obj); +wxString *CSQUALIN01(S57Obj *obj); + +wxArrayPtrVoid *GetChartFloatingATONArray(ObjRazRules *rzRules) { + S57Obj *obj = rzRules->obj; + if (obj->m_chart_context) + return obj->m_chart_context->pFloatingATONArray; + else + return NULL; +} + +wxArrayPtrVoid *GetChartRigidATONArray(ObjRazRules *rzRules) { + S57Obj *obj = rzRules->obj; + if (obj->m_chart_context->chart) + return obj->m_chart_context->pRigidATONArray; + else + return NULL; +} + +static void *CLRLIN01(void *param) { + ObjRazRules *rzRules = (ObjRazRules *)param; + // S57Obj *obj = rzRules->obj; + + printf("s52csny : CLRLIN01 ERROR no conditional symbology for: %s\n", + rzRules->LUP->OBCL); + return NULL; +} + +static void *DATCVR01(void *param) { + // Remarks: This conditional symbology procedure describes procedures for: + // - symbolizing the limit of ENC coverage; + // - symbolizing navigational purpose boundaries ("scale boundarie"); and + // - indicating overscale display. + // + // Note that the mandatory meta object CATQUA is symbolized by the look-up + // table. + // + // Because the methods adopted by an ECDIS to meet the IMO and IHO + // requirements listed on the next page will depend on the manufacturer's + // software, and cannot be described in terms of a flow chart in the same way + // as other conditional procedures, this procedure is in the form of written + // notes. + + // ObjRazRules *rzRules = (ObjRazRules *)param; + // S57Obj *obj = rzRules->obj; + + wxString rule_str; + /////////////////////// + // 1- REQUIREMENT + // (IMO/IHO specs. explenation) + + /////////////////////// + // 2- ENC COVERAGE + // + // 2.1- Limit of ENC coverage + // datcvr01 = g_string_new(";OP(3OD11060);LC(HODATA01)"); + rule_str.Append(_T("LC(HODATA01)")); + // rule_str.Append("AC(DEPDW)"); + // FIXME: get cell extend + + // 2.2- No data areas + // This can be done outside of CS (ie when clearing the screen in Mesa) + // FIXME: ";OP(0---);AC(NODATA)" + // FIXME: set geo to cover earth (!) + + ////////////////////// + // 3- SCALE BOUNDARIES + // + // 3.1- Chart scale boundaties + // FIXME; + // g_string_append(datcvr01, ";LS(SOLD,1,CHGRD)"); + // -OR- LC(SCLBDYnn) (?) + // + // ;OP(3OS21030) + + // 3.2- Graphical index of navigational purpose + // FIXME: draw extent of available SENC in DB + + ////////////////////// + // 4- OVERSCALE + // + // FIXME: get meta date CSCL of DSPM field + // FIXME: get object M_CSCL or CSCALE + // + // 4.1- Overscale indication + // FIXME: compute, scale = [denominator of the compilation scale] / + // [denominator of the display scale] + // FIXME: draw overscale indication (ie TX("X%3.1f",scale)) + // + // 4.2- Ovescale area at a chart scale boundary + // FIXME: test if next chart is over scale (ie going from large scale chart + // to a small scale chart) + // FIXME: draw AP(OVERSC01) on overscale part of display + // g_string(";OP(3OS21030)"); + + // + // 4.3- Larger scale data available + // FIXME: display indication of better scale available (?) + + wxString datcvr01; + datcvr01.Append(rule_str); + datcvr01.Append('\037'); + + char *r = (char *)malloc(datcvr01.Len() + 1); + strcpy(r, datcvr01.mb_str()); + + return r; +} + +bool GetIntAttr(S57Obj *obj, const char *AttrName, int &val) { + int idx = obj->GetAttributeIndex(AttrName); + + if (idx >= 0) { + // using idx to get the attribute value + S57attVal *v = obj->attVal->Item(idx); + + assert(v->valType == OGR_INT); + val = *(int *)(v->value); + + return true; + } else + return false; +} +#if 0 +bool GetIntAttr(S57Obj *obj, const char *AttrName, int &val) +{ + char *attList = (char *)calloc(obj->attList->Len()+1, 1); + strncpy(attList, obj->attList->mb_str(), obj->attList->Len()); + + char *patl = attList; + char *patr; + int idx = 0; + while(*patl) + { + patr = patl; + while(*patr != '\037') + patr++; + + if(!strncmp(patl, AttrName, 6)) + break; + + patl = patr + 1; + idx++; + } + + if(!*patl) // Requested Attribute not found + { + free(attList); + return false; // so don't return a value + } + + // using idx to get the attribute value + wxArrayOfS57attVal *pattrVal = obj->attVal; + + S57attVal *v = pattrVal->Item(idx); + val = *(int*)(v->value); + + free(attList); + return true; +} +#endif +/* +bool GetFloatAttr(S57Obj *obj, char *AttrName, float &val) +{ + char *attList = (char *)(obj->attList->mb_str()); //attList is +wxString + + char *patl = attList; + char *patr; + int idx = 0; + while(*patl) + { + patr = patl; + while(*patr != '\037') + patr++; + + if(!strncmp(patl, AttrName, 6)) + break; + + patl = patr + 1; + idx++; + } + + if(!*patl) // Requested Attribute +not found + { + free(attList); + return false; // so don't return a +value + } + +// using idx to get the attribute value + wxArrayOfS57attVal *pattrVal = obj->attVal; + + S57attVal *v = pattrVal->Item(idx); + val = *(float*)(v->value); + + free(attList); + return true; +} + +*/ +bool GetDoubleAttr(S57Obj *obj, const char *AttrName, double &val) { + int idx = obj->GetAttributeIndex(AttrName); + + if (idx >= 0) { + // using idx to get the attribute value + + S57attVal *v = obj->attVal->Item(idx); + assert(v->valType == OGR_REAL); + val = *(double *)(v->value); + + return true; + } else + return false; +} + +bool GetStringAttr(S57Obj *obj, const char *AttrName, char *pval, int nc) { + if (!obj || !AttrName || !pval || nc <= 0) return false; + + pval[0] = '\0'; + + int idx = obj->GetAttributeIndex(AttrName); + if (idx < 0) return false; + + S57attVal *v = obj->attVal->Item(idx); + assert(v->valType == OGR_STR); + + const char *val = static_cast(v->value); + if (!val) return true; + + strncpy(pval, val, nc - 1); + pval[nc - 1] = '\0'; + return true; +} + +wxString *GetStringAttrWXS(S57Obj *obj, const char *AttrName) { + int idx = obj->GetAttributeIndex(AttrName); + + if (idx >= 0) { + // using idx to get the attribute value + S57attVal *v = obj->attVal->Item(idx); + + assert(v->valType == OGR_STR); + char *val = (char *)(v->value); + + return new wxString(val, wxConvUTF8); + } else + return NULL; +} + +static int _parseList(const char *str_in, char *buf, int buf_size) +// Put a string of comma delimited number in an array (buf). +// Return: the number of value in buf. +// Assume: - number < 256, +// - list size less than buf_size . +// Note: buf is \0 terminated for strpbrk(). +{ + char *str = (char *)str_in; + int i = 0; + + if (NULL != str && *str != '\0') { + do { + if (i >= LISTSIZE - 1) { + printf("OVERFLOW --value in list lost!!\n"); + break; + } + + /* + if (255 < (unsigned char) atoi(str)) { + PRINTF("value overflow (>255)\n"); + exit(0); + } + */ + + buf[i++] = (unsigned char)atoi(str); + + while (isdigit(*str)) str++; // next + + } while (*str++ != '\0'); // skip ',' or exit + } + + buf[i] = '\0'; + + return i; +} + +static int _atPtPos(S57Obj *objNew, wxArrayPtrVoid *curntList, int bSectorCheck) +// return TRUE if there is a light at this position +// or if its an extended arc radius else FALSE +{ + unsigned int i; + + if (NULL == curntList) return false; + + for (i = 0; i < curntList->GetCount(); i++) { + S57Obj *objOld = (S57Obj *)curntList->Item(i); + + if ((objOld->x == objNew->x) && (objOld->y == objNew->y)) { + if (!bSectorCheck) return TRUE; + /* + else { + // check for extend arc radius + GString *Asectr1str = S57_getAttVal(geoOld, "SECTR1"); + GString *Asectr2str = S57_getAttVal(geoOld, "SECTR2"); + GString *Bsectr1str = S57_getAttVal(geoNew, "SECTR1"); + GString *Bsectr2str = S57_getAttVal(geoNew, "SECTR2"); + + // check present + if (NULL == Asectr1str || + NULL == Asectr1str || + NULL == Asectr1str || + NULL == Asectr1str) + return FALSE; + + { + double Asectr1 = atof(Asectr1str->str); + double Asectr2 = atof(Asectr2str->str); + double Bsectr1 = atof(Bsectr1str->str); + double Bsectr2 = atof(Bsectr2str->str); + double Asweep = (Asectr1 > Asectr2) ? + Asectr2-Asectr1+360 : Asectr2-Asectr1; + double Bsweep = (Bsectr1 > Bsectr2) ? + Bsectr2-Bsectr1+360 : Bsectr2-Bsectr1; + + // check sector overlap + if (Asectr2<=Bsectr1 || Asectr1>=Bsectr2) { + if (Asweep == Bsweep) { + g_string_truncate(Bsectr2str, 0); + g_string_sprintf(Bsectr2str, "%f",Bsectr2-1); + S57_setAtt(geoNew, "SECTR2", Bsectr2str->str); + } + + return FALSE; + } + + // check if other sector larger + if (Asweep >= Bsweep) + return TRUE; + } + } + */ + } + } + + return FALSE; +} + +wxString _selSYcol(char *buf, bool bsectr, double valnmr) { + wxString sym; + + if (!bsectr) { + sym = _T(";SY(LITDEF11"); // default + + // max 1 color + if ('\0' == buf[1]) { + if (strpbrk(buf, "\003")) + sym = _T(";SY(LIGHTS11"); + else if (strpbrk(buf, "\004")) + sym = _T(";SY(LIGHTS12"); + else if (strpbrk(buf, "\001\006\011")) + sym = _T(";SY(LIGHTS13"); + } else { + // max 2 color + if ('\0' == buf[2]) { + if (strpbrk(buf, "\001") && strpbrk(buf, "\003")) + sym = _T(";SY(LIGHTS11"); + else if (strpbrk(buf, "\001") && strpbrk(buf, "\004")) + sym = _T(";SY(LIGHTS12"); + } + } + } + + else // all-round fixed light symbolized as a circle, radius depends on color + // This treatment is seen on SeeMyDenc by SevenCs + // This may not be S-52 compliant.... + { + // Another non-standard extension.... + // All round light circle diameter is scaled if the light has a + // reasonable VALNMR attribute + int radius = 3; + if (valnmr > 0) { + if (valnmr < 7.0) + radius = 3; + else if (valnmr < 15.0) + radius = 10; + else if (valnmr < 30.0) + radius = 15; + else + radius = 20; + } + + // max 1 color + if ('\0' == buf[1]) { + if (strpbrk(buf, "\003")) + sym.Printf(_T(",LITRD, 2,0,360,%d,0"), radius + 1); + else if (strpbrk(buf, "\004")) + sym.Printf(_T(",LITGN, 2,0,360,%d,0"), radius); + else if (strpbrk(buf, "\001\006\011")) + sym.Printf(_T(",LITYW, 2,0,360,%d,0"), radius + 2); + else if (strpbrk(buf, "\014")) + sym.Printf(_T(",CHMGD, 2,0,360,%d,0"), radius + 3); + else + sym.Printf(_T(",CHMGD, 2,0,360,%d,0"), radius + 5); // default + + } else if ('\0' == buf[2]) // or 2 color + { + if (strpbrk(buf, "\001") && strpbrk(buf, "\003")) + sym.Printf(_T(",LITRD, 2,0,360,%d,0"), radius + 1); + else if (strpbrk(buf, "\001") && strpbrk(buf, "\004")) + sym.Printf(_T(",LITGN, 2,0,360,%d,0"), radius); + else + sym.Printf(_T(",CHMGD, 2,0,360,%d,0"), radius + 5); // default + + } else + sym.Printf(_T(",CHMGD, 2,0,360,%d,0"), radius + 5); + + if (sym.Len()) sym.Prepend(_T(";CA(OUTLW, 4")); + } + + return sym; +} + +static double _DEPVAL01(S57Obj *obj, double least_depth) +// Remarks: S-57 Appendix B1 Annex A requires in Section 6 that areas of rocks +// be encoded as area obstruction, and that area OBSTRNs and area WRECKS be +// covered by either group 1 object DEPARE or group 1 object UNSARE. If the +// value of the attribute VALSOU for an area OBSTRN or WRECKS is missing, the +// DRVAL1 of an underlying DEPARE is the preferred default for establishing a +// depth value. This procedure either finds the shallowest DRVAL1 of the one or +// more underlying DEPAREs, or returns an "unknown"" depth value to the main +// procedure for the next default procedure. + +// NOTE: UNSARE test is useless since least_depth is already UNKNOWN +{ + least_depth = UNKNOWN; + + /* + S57_geo *geoTmp = geo; + + // NOTE: the geo list is unchange (_UDWHAZ03 will unlink geo) + while (NULL != (geoTmp = S57_nextObj(geoTmp))) { + GString *objlstr = S57_getAttVal(geoTmp, "OBJL"); + int objl = (NULL == objlstr) ? 0 : atoi(objlstr->str); + + // get area DEPARE that intersect this area + if (DEPARE==objl && LINES_T==S57_getObjtype(geo)) { + GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1"); + double drval1 = (NULL == drval1str) ? 9.0 : + atof(drval1str->str); + + if (NULL != drval1str) { + if (UNKNOWN==least_depth || least_depth *pobj_list = NULL; + if (obj->m_chart_context->chart){ + chart_context *pcc = obj->m_chart_context; + s57chart *chart = (s57chart*)obj->m_chart_context->chart; + pobj_list = (chart->*pcc->pt2GetAssociatedObjects)(obj); + } + else{ + danger = false; + } + + if (pobj_list) { + for (S57Obj* ptest_obj: *pobj_list) { + if (GEO_LINE == ptest_obj->Primitive_type) { + double drval2 = 0.0; + GetDoubleAttr(ptest_obj, "DRVAL2", drval2); + + if (drval2 < safety_contour) { + danger = TRUE; + break; + } + } else { + double drval1 = 0.0; + GetDoubleAttr(ptest_obj, "DRVAL1", drval1); + +#if 0 + double drval2 = 0.0; + GetDoubleAttr(ptest_obj, "DRVAL2", drval2); + + if(expsou == 1 || depth_value < drval2 ) + b_promote = true; +#endif + + if (drval1 >= safety_contour && expsou != 1) { + danger = TRUE; + break; + } + } + } + + delete pobj_list; + } + } + + if (TRUE == danger) { + int watlev = 0; // Enum 0 invalid + GetIntAttr(obj, "WATLEV", watlev); + + if ((1 == watlev) || (2 == watlev)) { + // dry + } else { + udwhaz03str = _T(";SY(ISODGR51)"); //_T(";OP(8OD14010);SY(ISODGR51)"); + // S57_setAtt(geo, "SCAMIN", "INFINITE"); + } + + // Move this object to DisplayBase category + rzRules->obj->m_DisplayCat = DISPLAYBASE; + + } + + if (promote_return) *promote_return = b_promote; + + wxString *ret_str = new wxString(udwhaz03str); + return ret_str; +} + +// Remarks: An object of the class "depth area" is coloured and covered with +// fill patterns according to the mariners selections of shallow contour, safety +// contour and deep contour. This requires a decision making process based on +// DRVAL1 and DRVAL2. Objects of the class "dredged area" are handled by this +// routine as well to ensure a consistent symbolization of areas that represent +// the surface of the seabed. + +static void *DEPARE01(void *param) { + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + double drval1, drval2; + bool drval1_found; + + // Determine the color based on mariner selections + + drval1 = -1.0; // default values + drval1_found = GetDoubleAttr(obj, "DRVAL1", drval1); + // drval2 = drval1 + 0.01; + GetDoubleAttr(obj, "DRVAL2", drval2); + //make sure drval2 is higher then drval1, always even in bad charts + if (drval2 <= drval1) drval2 = drval1 + 0.01; + + // Create a string of the proper color reference + + wxString rule_str = _T("AC(DEPIT)"); + + if (drval1 >= 0.0 && drval2 > 0.0) rule_str = _T("AC(DEPVS)"); + + if (TRUE == S52_getMarinerParam(S52_MAR_TWO_SHADES)) { + if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR) && + drval2 > S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) { + rule_str = _T("AC(DEPDW)"); + } + } else { + if (drval1 >= S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR) && + drval2 > S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR)) + rule_str = _T("AC(DEPMS)"); + + if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR) && + drval2 > S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) { + rule_str = _T("AC(DEPMD)"); + } + + if (drval1 >= S52_getMarinerParam(S52_MAR_DEEP_CONTOUR) && + drval2 > S52_getMarinerParam(S52_MAR_DEEP_CONTOUR)) { + rule_str = _T("AC(DEPDW)"); + } + } + + // If object is DRGARE.... + + if (!strncmp(rzRules->LUP->OBCL, "DRGARE", 6)) { + if (!drval1_found) // If DRVAL1 was not defined... + { + rule_str = _T("AC(DEPMD)"); + } + rule_str.Append(_T(";AP(DRGARE01)")); + rule_str.Append(_T(";LS(DASH,1,CHGRF)")); + + // Todo Restrictions + /* + char pval[30]; + if(true == GetStringAttr(obj, "RESTRN", pval, 20)) + { + GString *rescsp01 = _RESCSP01(geo); + if (NULL != rescsp01) + { + g_string_append(depare01, rescsp01->str); + g_string_free(rescsp01, TRUE); + } + } + */ + } + + rule_str.Append('\037'); + + return strdup(rule_str.mb_str()); +} +/* +static void *DEPCNT02A(void *param) +{ + // ObjRazRules *rzRules = (ObjRazRules *)param; + // S57Obj *obj = rzRules->obj; + + // Add another rule onto the ruleList + + Rules *r = NULL; + + r = (Rules*)calloc(1, sizeof(Rules)); + r->ruleType = RUL_SIM_LN; + r->INSTstr = "SOLD,1,DEPCN"; // points into +the plib data space + + return NULL; + +} +*/ + +static void *DEPCNT02(void *param) +// Remarks: An object of the class "depth contour" or "line depth area" is +// highlighted and must be shown under all circumstances if it matches the +// safety contour depth value entered by the mariner (see IMO PS 3.6). But, +// while the mariner is free to enter any safety contour depth value that he +// thinks is suitable for the safety of his ship, the SENC only contains a +// limited choice of depth contours. This symbology procedure determines whether +// a contour matches the selected safety contour. If the selected safety contour +// does not exist in the data, the procedure will default to the next deeper +// contour. The contour selected is highlighted as the safety contour and put in +// DISPLAYBASE. The procedure also identifies any line segment of the spatial +// component of the object that has a "QUAPOS" value indicating unreliable +// positioning, and symbolizes it with a double dashed line. +// +// Note: Depth contours are not normally labeled. The ECDIS may provide labels, +// on demand only as with other text, or provide the depth value on cursor +// picking +{ + // GString *depcnt02 = NULL; + // int safe = FALSE; // initialy not a safety contour + // GString *objlstr = NULL; + // int objl = 0; + // GString *quaposstr = NULL; + // int quapos = 0; + double drval1, drval2; + bool safe = FALSE; + wxString rule_str; + double safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR); + + // objlstr = S57_getAttVal(geo, "OBJL"); + // objl = (NULL == objlstr) ? 0 : atoi(objlstr->str); + + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + // Debug + //if(obj->Index == 5014) + //int tty = 5; + + if ((!strncmp(obj->FeatureName, "DEPARE", 6)) && + GEO_LINE == obj->Primitive_type) { + drval1 = 0.0; // default values + GetDoubleAttr(obj, "DRVAL1", drval1); + drval2 = drval1; + GetDoubleAttr(obj, "DRVAL2", drval2); + + if (drval1 <= safety_contour) { + if (drval2 >= safety_contour) safe = TRUE; + } + + else { + double next_safe_contour = 1e6; + next_safe_contour = obj->m_chart_context->safety_contour; + if (fabs(drval1 - next_safe_contour) < 1e-4) safe = true; + } + + } else { + // continuation A (DEPCNT) + double valdco = 0; + GetDoubleAttr(obj, "VALDCO", valdco); + + if (valdco == safety_contour) + safe = TRUE; // this is useless !?!? + + else { + double next_safe_contour = 1e6; + next_safe_contour = obj->m_chart_context->safety_contour; + if (fabs(valdco - next_safe_contour) < 1e-4) safe = true; + } + + /* + if (valdco > safety_contour) + { + safe = FALSE; + if (1 == S52_state) + return NULL; + else { + S57_geo *geoTmp = geo; + + // get area DEPARE & DRGARE that intersect this line + while (NULL != (geoTmp = + S57_nextObj(geoTmp))){ GString *drval1str = S57_getAttVal(geoTmp, + "DRVAL1"); double drval1 = (NULL == drval1str) ? 0.0 : + atof(drval1str->str); + + if (NULL == drval1str) { + safe = TRUE; + break; + } + + if (drval1 < + S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) { safe = TRUE; break; + } + } + // debug trace + //if (safe) PRINTF("** DEPCN: SAFE FOUND**\n"); + } + + */ + } + + // Continuation B + int quapos = 0; + GetIntAttr(obj, "QUAPOS", + quapos); // QUAPOS is an E (Enumerated) type attribute + + if (0 != quapos) { + if (2 <= quapos && quapos < 10) { + if (safe) { + wxString safeCntr = _T("LS(DASH,2,DEPSC)"); + S57Obj tempObj; + LUPrec *safelup = ps52plib->S52_LUPLookup(PLAIN_BOUNDARIES, "SAFECD", + &tempObj, false); + if (safelup) safeCntr = safelup->INST; + rule_str = _T(";") + safeCntr; + } else + rule_str = _T(";LS(DASH,1,DEPCN)"); + } + } else { + if (safe) { + wxString safeCntr = _T("LS(SOLD,2,DEPSC)"); + S57Obj tempObj; + LUPrec *safelup = + ps52plib->S52_LUPLookup(PLAIN_BOUNDARIES, "SAFECN", &tempObj, false); + if (safelup) safeCntr = safelup->INST; + rule_str = _T(";") + safeCntr; + } else + rule_str = _T(";LS(SOLD,1,DEPCN)"); + } + + if (safe) { + // S57_setAtt(geo, "SCAMIN", "INFINITE"); + // rule_str.Prepend(_T(";OP(8OD13010)")); //depcnt02 = + // g_string_prepend(depcnt02, ";OP(8OD13010)"); + // Move this object to DisplayBase category + rzRules->obj->m_DisplayCat = DISPLAYBASE; + rzRules->obj->Scamin = 1e8+1; // effectively no SCAMIN + // rzRules->LUP->DPRI = PRIO_HAZARDS; + + } else { + // rule_str.Prepend(_T(";OP(---33020)")); //depcnt02 = + // g_string_prepend(depcnt02, ";OP(---33020)"); + } + // facultative in S-52 + // if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) { + // GString *sndfrm02 = _SNDFRM02(geo, depth_value); + // depcnt02 = g_string_append(depcnt02, sndfrm02->str); + // g_string_free(sndfrm02, TRUE); + //} + + // debug + // PRINTF("depth= %f\n", depth_value); + + // S57_unlinkObj(geo); + + rule_str.Append('\037'); + + return strdup(rule_str.mb_str()); + + // return depcnt02; +} + +static void *DEPVAL01(void *param) { + ObjRazRules *rzRules = (ObjRazRules *)param; + // S57Obj *obj = rzRules->obj; + + printf("s52csny : DEPVAL01 ERROR no conditional symbology for: %s\n", + rzRules->LUP->OBCL); + return NULL; +} + +static void *LEGLIN02(void *param) { + ObjRazRules *rzRules = (ObjRazRules *)param; + // S57Obj *obj = rzRules->obj; + + printf("s52csny : LEGLIN02 ERROR no conditional symbology for: %s\n", + rzRules->LUP->OBCL); + return NULL; +} + +/* +static void *LIGHTS04A(void *param) +{ + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + wxString rule_str; + + char col_str[2]; + GetStringAttr(obj, "COLOUR", col_str, 1); + + double height_val = 0; + GetDoubleAttr(obj, "HEIGHT", height_val); + +// if(obj->attList->Contains(wxString("HEIGHT"))) +// int uupr = 5; + +// Different symbology depending upon Paper or Simplified Mariner Selection + + + if(ps52plib->m_nSymbolStyle == PAPER_CHART) + { + if(col_str[0] == '3') + { // red + if(height_val) + rule_str = _T("SY(LIGHTS93)"); // all round + else + rule_str = _T("SY(LIGHTS01)"); // flare + } + + else if(col_str[0] == '4') // green + { + if(height_val) + rule_str = _T("SY(LIGHTS92)"); + else + rule_str = _T("SY(LIGHTS02)"); + } + + else // Generic, +shows as yellow + { + if(height_val) + rule_str = _T("SY(LIGHTS91)"); + else + rule_str = _T("SY(LIGHTS03)"); + } + } + + else // must be +Simplified + { + if(col_str[0] == '3') + { // red + if(height_val) + rule_str = _T("SY(LIGHTS93)"); // all round + else + rule_str = _T("SY(LIGHTS01)"); // flare + } + + else if(col_str[0] == '4') // green + { + if(height_val) + rule_str = _T("SY(LIGHTS92)"); + else + rule_str = _T("SY(LIGHTS02)"); + } + + else // Generic, +shows as yellow + { + if(height_val) + rule_str = _T("SY(LIGHTS91)"); + else + rule_str = _T("SY(LIGHTS03)"); + } + } + + + + rule_str.Append('\037'); + + char *r = (char *)malloc(rule_str.Len() + 1); + strcpy(r, rule_str.mb_str()); + return r; +} + +// A simple placeholder for Conditional Symbology method LIGHTS05 +static void *LIGHTS05A(void *param) +{ + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + wxString rule_str; + + char col_str[2]; + GetStringAttr(obj, "COLOUR", col_str, 1); + + double height_val = 0; + GetDoubleAttr(obj, "HEIGHT", height_val); + + if(col_str[0] == '3') + { // red + rule_str = _T("SY(LIGHTS11)"); // flare + } + + else if(col_str[0] == '4') // green + rule_str = _T("SY(LIGHTS12)"); + + else // Generic, shows as +yellow rule_str = _T("SY(LIGHTS13)"); + + + + rule_str.Append('\037'); + + char *r = (char *)malloc(rule_str.Len() + 1); + strcpy(r, rule_str.mb_str()); + return r; +} +*/ + +static wxString _LITDSN01(S57Obj *obj); +static void *LIGHTS06(void *param); + +static void *LIGHTS05(void *param) +// Remarks: A light is one of the most complex S-57 objects. Its presentation +// depends on whether it is a light on a floating or fixed platform, its range, +// it's colour and so on. This conditional symbology procedure derives the +// correct presentation from these parameters and also generates an area that +// shows the coverage of the light. +// +// Notes on light sectors: +// 1.) The radial leg-lines defining the light sectors are normally drawn to +// only 25mm from the light to avoid clutter (see Part C). However, the mariner +// should be able to select "full light-sector lines" and have the leg-lines +// extended to the nominal range of the light (VALMAR). +// +// 2.) Part C of this procedure symbolizes the sectors at the light itself. In +// addition, it should be possible, upon request, for the mariner to be capable +// of identifying the colour and sector limit lines of the sectors affecting the +// ship even if the light itself is off the display. [ed. last sentence in bold] + +{ + // As transition, we use the PLIB 4.0 LIGHTS06 Procedure + return LIGHTS06(param); + +#define UNKNOWN_DOUBLE -9; + wxString lights05; + + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + double valnmr = UNKNOWN_DOUBLE; + GetDoubleAttr(obj, "VALNMR", valnmr); + + char catlitstr[20] = {'\0'}; + GetStringAttr(obj, "CATLIT", catlitstr, 19); + + char litvisstr[20] = {'\0'}; + ; + GetStringAttr(obj, "LITVIS", litvisstr, 19); + + char catlit[LISTSIZE] = {'\0'}; + char litvis[LISTSIZE] = {'\0'}; + char col_str[20] = {'\0'}; + + bool flare_at_45 = false; + double sectr1 = UNKNOWN_DOUBLE; + double sectr2 = UNKNOWN_DOUBLE; + double sweep = 0.; + char colist[LISTSIZE] = {'\0'}; // colour list + bool b_isflare = false; + + wxString orientstr; + + if (strlen(catlitstr)) { + _parseList(catlitstr, catlit, sizeof(colist)); + + // FIXME: OR vs AND/OR + if (strpbrk(catlit, "\010\013")) { + lights05.Append(_T(";SY(LIGHTS82)")); + goto l05_end; + } + + if (strpbrk(catlit, "\011")) { + lights05.Append(_T(";SY(LIGHTS81)")); + goto l05_end; + } + + /* + if (strpbrk(catlit, "\001\020")) { + orientstr = S57_getAttVal(geo, "ORIENT"); + if (NULL != orientstr) { + // FIXME: create a geo object (!?) LINE of lenght VALNMR + // using ORIENT (from seaward) & POINT_T position + g_string_append(lights05, ";LS(DASH,1,CHBLK)"); + } + } + */ + } + + // Continuation A + + GetStringAttr(obj, "COLOUR", col_str, 19); + + if (strlen(col_str)) + _parseList(col_str, colist, sizeof(colist)); + else { + colist[0] = '\014'; // magenta (12) + colist[1] = '\000'; + } + + GetDoubleAttr(obj, "SECTR1", sectr1); + GetDoubleAttr(obj, "SECTR2", sectr2); + + if ((-9 == sectr1) || (-9 == sectr2)) { + // This is not a sector light + + // What follows is one interpretation of the modern (3_3 +) + // Presentation Library CS flow chart, which I(dsr) have never seen + // verbatim. We will use flare light symbols for floating aids, and all + // round sector lights for fixed aids. + + wxString ssym; + + //FIXME plib +// if (_atPtPos(obj, GetChartFloatingATONArray(rzRules), +// false)) // Is this LIGHTS feature colocated with ...ANY... +// // floating aid? +// { +// flare_at_45 = false; +// +// // TODO create LightArray in s57chart. +// // Then, if another LIGHT object is colocated here, set flare_at_45 +// /* if(_atPtPos(obj, rzRules->chart->pLIGHTSArray, false)) // Is +// this LIGHTS feature colocated with another LIGHTS? +// +// +// // If the light is white, yellow, or orange, make it a +// flare at 45 degrees if(strpbrk(colist, "\001\005\011")) flare_at_45 = +// true; +// */ +// ssym = _selSYcol(colist, 0, valnmr); // flare +// b_isflare = true; +// } else + { + ssym = _selSYcol(colist, 1, valnmr); // all round light + b_isflare = false; + } + + // Is the light a directional or moire? + if (strpbrk(catlit, "\001\016")) { + if (orientstr.Len()) { + lights05.Append(ssym); + lights05.Append(orientstr); + lights05.Append( + _T(";TE('%03.0lf deg','ORIENT',3,3,3,'15110',3,1,CHBLK,23)" )); + } else + lights05.Append(_T(";SY(QUESMRK1)")); + } else { + lights05.Append(ssym); + if (b_isflare) { + if (flare_at_45) + lights05.Append(_T(",45)")); + else + lights05.Append(_T(",135)")); + } + } + + goto l05_end; + } + + // Continuation B --sector light + if (-9 == sectr1) { + sectr1 = 0.0; + sectr2 = 0.0; + } else + sweep = (sectr1 > sectr2) ? sectr2 - sectr1 + 360 : sectr2 - sectr1; + + if (sweep < 1.0 || sweep == 360.0) { + // handle all round light + wxString ssym = _selSYcol(colist, 1, valnmr); // all round light + lights05.Append(ssym); + + /* + if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) { + GString *litdsn01 = _LITDSN01(geo); + if (NULL != litdsn01) { + g_string_append(lights05, ";TX('"); + g_string_append(lights05, litdsn01->str); + g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)" ); + g_string_free(litdsn01, TRUE); + } + + } + */ + goto l05_end; + } + + /* + // scan for other lights with sector overlap at this position + // compute light sector radius according to other sector + if (1 == S52_state) + { + _setPtPos(geo, SECTRLIST); + g_string_free(lights05, TRUE); + return NULL; + } + else + { + extend_arc_radius = _atPtPos(geo, SECTRLIST); + + // passe value via attribs to _renderAC + if (extend_arc_radius) + // FIXME: draw radius 25 mm + S57_setAtt(geo, "extend_arc_radius", "Y"); + else + // FIXME: draw radius 20 mm + S57_setAtt(geo, "extend_arc_radius", "N"); + } + */ + // setup sector + { + // Build the (opencpn private) command string like this: + // e.g. ";CA(OUTLW, 4,LITRD, 2, sectr1, sectr2, radius)" + + double arc_radius = 20.; // mm + double sector_radius = 25.; + + // Another non-standard extension.... + // Sector light arc radius is scaled if the light has a reasonable + // VALNMR attribute + if (valnmr > 0) { + if (valnmr < 15.0) + arc_radius = 10.; + else if (valnmr < 30.0) + arc_radius = 15.; + else + arc_radius = 20.; + } + + char sym[80]; + strcpy(sym, ";CA(OUTLW, 4"); + + // max 1 color + if ('\0' == colist[1]) { + if (strpbrk(colist, "\003")) + strcat(sym, ",LITRD, 2"); + else if (strpbrk(colist, "\004")) + strcat(sym, ",LITGN, 2"); + else if (strpbrk(colist, "\001\006\013")) + strcat(sym, ",LITYW, 2"); + else + strcat(sym, ",CHMGD, 2"); // default is magenta + + } else if ('\0' == colist[2]) { + if (strpbrk(colist, "\001") && strpbrk(colist, "\003")) + strcat(sym, ",LITRD, 2"); + else if (strpbrk(colist, "\001") && strpbrk(colist, "\004")) + strcat(sym, ",LITGN, 2"); + else + strcat(sym, ",CHMGD, 2"); // default is magenta + } else + strcat(sym, ",CHMGD, 2"); // default is magenta + + if (strlen(litvisstr)) // Obscured/faint sector? + { + _parseList(litvisstr, litvis, sizeof(litvis)); + + if (strpbrk(litvis, "\003\007\010")) strcpy(sym, ";CA(CHBLK, 4,CHBRN, 1"); + } + + if (sectr2 <= sectr1) sectr2 += 360; + + // Sectors are defined from seaward + if (sectr1 > 180) + sectr1 -= 180; + else + sectr1 += 180; + + if (sectr2 > 180) + sectr2 -= 180; + else + sectr2 += 180; + + char arc_data[80]; + sprintf(arc_data, ",%5.1f, %5.1f, %5.1f, %5.1f", sectr1, sectr2, arc_radius, + sector_radius); + + strcat(sym, arc_data); + + wxString ssym(sym, wxConvUTF8); + lights05 = ssym; + + goto l05_end; + } + +l05_end: + + // if( ps52plib->m_bShowLdisText ) + { + // Only show Light in certain position once. Otherwise there will be + // clutter. + static double lastLat, lastLon; + static wxString lastDescription; + bool isFirstSector = true; + + if (lastLat == obj->m_lat && lastLon == obj->m_lon) isFirstSector = false; + lastLat = obj->m_lat; + lastLon = obj->m_lon; + + wxString litdsn01 = _LITDSN01(obj); + + if (litdsn01.Len() && isFirstSector) { + lastDescription = litdsn01; + lights05.Append(_T(";TX('")); + lights05.Append(litdsn01); + + if (flare_at_45) + lights05.Append(_T("',3,3,3,'15110',2,-1,CHBLK,23)" )); + else + lights05.Append(_T("',3,2,3,'15110',2,0,CHBLK,23)" )); + } + + if (!isFirstSector && lastDescription != litdsn01) { + lastDescription = litdsn01; + lights05.Append(_T(";TX('")); + lights05.Append(litdsn01); + lights05.Append(_T("',3,2,3,'15110',2,1,CHBLK,23)" )); + } + } + + lights05.Append('\037'); + + char *r = (char *)malloc(lights05.Len() + 1); + strcpy(r, lights05.mb_str()); + + return r; +} + +static void *LIGHTS06(void *param) +// Remarks: A light is one of the most complex S-57 objects. Its presentation +// depends on whether it is a light on a floating or fixed platform, its range, +// it's colour and so on. This conditional symbology procedure derives the +// correct presentation from these parameters and also generates an area that +// shows the coverage of the light. +// +// Notes on light sectors: +// 1.) The radial leg-lines defining the light sectors are normally drawn to +// only 25mm from the light to avoid clutter (see Part C). However, the mariner +// should be able to select "full light-sector lines" and have the leg-lines +// extended to the nominal range of the light (VALMAR). +// +// 2.) Part C of this procedure symbolizes the sectors at the light itself. In +// addition, it should be possible, upon request, for the mariner to be capable +// of identifying the colour and sector limit lines of the sectors affecting the +// ship even if the light itself is off the display. [ed. last sentence in bold] + +{ +#define UNKNOWN_DOUBLE -9; + wxString lights06; + + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + double valnmr = 9.0; + GetDoubleAttr(obj, "VALNMR", valnmr); + + char catlitstr[20] = {'\0'}; + GetStringAttr(obj, "CATLIT", catlitstr, 19); + + char litvisstr[20] = {'\0'}; + ; + GetStringAttr(obj, "LITVIS", litvisstr, 19); + + char catlit[LISTSIZE] = {'\0'}; + char litvis[LISTSIZE] = {'\0'}; + char col_str[20] = {'\0'}; + + bool flare_at_45 = false; + double sectr1 = UNKNOWN_DOUBLE; + double sectr2 = UNKNOWN_DOUBLE; + double sweep = 0.; + char colist[LISTSIZE] = {'\0'}; // colour list + bool b_isflare = false; + + wxString orientstr; + + if (strlen(catlitstr)) { + _parseList(catlitstr, catlit, sizeof(colist)); + + if (strpbrk(catlit, "\010\013")) { + lights06.Append(_T(";SY(LIGHTS82)")); + goto l06_end; + } + + if (strpbrk(catlit, "\011")) { + lights06.Append(_T(";SY(LIGHTS81)")); + goto l06_end; + } + + /* + * if (strpbrk(catlit, "\001\020")) { + * orientstr = S57_getAttVal(geo, "ORIENT"); + * if (NULL != orientstr) { + * // FIXME: create a geo object (!?) LINE of lenght VALNMR + * // using ORIENT (from seaward) & POINT_T position + * g_string_append(lights05, ";LS(DASH,1,CHBLK)"); + } + } + */ + } + + // Continuation A + + GetStringAttr(obj, "COLOUR", col_str, 19); + + if (strlen(col_str)) + _parseList(col_str, colist, sizeof(colist)); + else { + colist[0] = '\014'; // magenta (12) + colist[1] = '\000'; + } + + GetDoubleAttr(obj, "SECTR1", sectr1); + GetDoubleAttr(obj, "SECTR2", sectr2); + + if ((-9 == sectr1) || (-9 == sectr2)) { + // This is not a sector light + + wxString ssym; + + if (valnmr < 10.0) { + // TODO create LightArray in s57chart. + // Then, if another LIGHT object is colocated here, set flare_at_45 + /* if(_atPtPos(obj, rzRules->chart->pLIGHTSArray, false)) // Is + * this LIGHTS feature colocated with another LIGHTS? + * + * + * // If the light is white, yellow, or orange, make it a + * flare at 45 degrees if(strpbrk(colist, "\001\005\011")) flare_at_45 = + * true; + */ + ssym = _selSYcol(colist, 0, valnmr); // flare + b_isflare = true; + flare_at_45 = false; + } else { + ssym = _selSYcol(colist, 1, valnmr); // all round light + b_isflare = false; + } + + // Is the light a directional or moire? + if (strpbrk(catlit, "\001\016")) { + if (orientstr.Len()) { + lights06.Append(ssym); + lights06.Append(orientstr); + lights06.Append( + _T(";TE('%03.0lf deg','ORIENT',3,3,3,'15110',3,1,CHBLK,23)" )); + } else + lights06.Append(_T(";SY(QUESMRK1)")); + } else { + lights06.Append(ssym); + + if (b_isflare) { + if (flare_at_45) + lights06.Append(_T(",45)")); + else + lights06.Append(_T(",135)")); + } + } + + goto l06_end; + } + + // Continuation B --sector light + if (-9 == sectr1) { + sectr1 = 0.0; + sectr2 = 0.0; + } else + sweep = (sectr1 > sectr2) ? sectr2 - sectr1 + 360 : sectr2 - sectr1; + + if (sweep < 1.0 || sweep == 360.0) { + // handle all round light + wxString ssym = _selSYcol(colist, 1, valnmr); // all round light + lights06.Append(ssym); + + /* + * if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) { + * GString *litdsn01 = _LITDSN01(geo); + * if (NULL != litdsn01) { + * g_string_append(lights05, ";TX('"); + * g_string_append(lights05, litdsn01->str); + * g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)" + ); + * g_string_free(litdsn01, TRUE); + } + + } + */ + goto l06_end; + } + + /* + * // scan for other lights with sector overlap at this position + * // compute light sector radius according to other sector + * if (1 == S52_state) + * { + * _setPtPos(geo, SECTRLIST); + * g_string_free(lights05, TRUE); + * return NULL; + } + else + { + extend_arc_radius = _atPtPos(geo, SECTRLIST); + + // passe value via attribs to _renderAC + if (extend_arc_radius) + // FIXME: draw radius 25 mm + S57_setAtt(geo, "extend_arc_radius", "Y"); + else + // FIXME: draw radius 20 mm + S57_setAtt(geo, "extend_arc_radius", "N"); + } + */ + // setup sector + { + // Build the (opencpn private) command string like this: + // e.g. ";CA(OUTLW, 4,LITRD, 2, sectr1, sectr2, radius)" + + double arc_radius = 20.; // mm + double sector_radius = 25.; + + // Another non-standard extension.... + // Sector light arc radius is scaled if the light has a reasonable + // VALNMR attribute + if (valnmr > 0) { + if (valnmr < 15.0) + arc_radius = 10.; + else if (valnmr < 30.0) + arc_radius = 15.; + else + arc_radius = 20.; + } + + char sym[80]; + strcpy(sym, ";CA(OUTLW, 4"); + + // max 1 color + if ('\0' == colist[1]) { + if (strpbrk(colist, "\003")) + strcat(sym, ",LITRD, 2"); + else if (strpbrk(colist, "\004")) + strcat(sym, ",LITGN, 2"); + else if (strpbrk(colist, "\001\006\013")) + strcat(sym, ",LITYW, 2"); + else + strcat(sym, ",CHMGD, 2"); // default is magenta + + } else if ('\0' == colist[2]) { + if (strpbrk(colist, "\001") && strpbrk(colist, "\003")) + strcat(sym, ",LITRD, 2"); + else if (strpbrk(colist, "\001") && strpbrk(colist, "\004")) + strcat(sym, ",LITGN, 2"); + else + strcat(sym, ",CHMGD, 2"); // default is magenta + } else + strcat(sym, ",CHMGD, 2"); // default is magenta + + if (strlen(litvisstr)) // Obscured/faint sector? + { + _parseList(litvisstr, litvis, sizeof(litvis)); + + if (strpbrk(litvis, "\003\007\010")) strcpy(sym, ";CA(CHBLK, 4,CHBRN, 1"); + } + + if (sectr2 <= sectr1) sectr2 += 360; + + // Sectors are defined from seaward + if (sectr1 > 180) + sectr1 -= 180; + else + sectr1 += 180; + + if (sectr2 > 180) + sectr2 -= 180; + else + sectr2 += 180; + + char arc_data[80]; + sprintf(arc_data, ",%5.1f, %5.1f, %5.1f, %5.1f", sectr1, sectr2, arc_radius, + sector_radius); + + strcat(sym, arc_data); + + wxString ssym(sym, wxConvUTF8); + lights06 = ssym; + + goto l06_end; + } + +l06_end: + + // if( ps52plib->m_bShowLdisText ) + { + // Only show Light in certain position once. Otherwise there will be + // clutter. + static double lastLat, lastLon; + static wxString lastDescription; + bool isFirstSector = true; + + if (lastLat == obj->m_lat && lastLon == obj->m_lon) isFirstSector = false; + lastLat = obj->m_lat; + lastLon = obj->m_lon; + + wxString litdsn01 = _LITDSN01(obj); + + if (litdsn01.Len() && isFirstSector) { + lastDescription = litdsn01; + lights06.Append(_T(";TX('")); + lights06.Append(litdsn01); + + if (flare_at_45) + lights06.Append(_T("',3,3,3,'15110',2,-1,CHBLK,23)" )); + else + lights06.Append(_T("',3,2,3,'15110',2,0,CHBLK,23)" )); + } + + if (!isFirstSector && lastDescription != litdsn01) { + lastDescription = litdsn01; + lights06.Append(_T(";TX('")); + lights06.Append(litdsn01); + lights06.Append(_T("',3,2,3,'15110',2,1,CHBLK,23)" )); + } + } + + lights06.Append('\037'); + + char *r = (char *)malloc(lights06.Len() + 1); + strcpy(r, lights06.mb_str()); + + return r; +} + +static void *LITDSN01(void *param) { + ObjRazRules *rzRules = (ObjRazRules *)param; + // S57Obj *obj = rzRules->obj; + + printf("s52csny : LITDSN01 ERROR no conditional symbology for: %s\n", + rzRules->LUP->OBCL); + return NULL; +} + +/* +static void *OBSTRN04a(void *param) +{ + ObjRazRules *rzRules = (ObjRazRules *)param; +// S57Obj *obj = rzRules->obj; + + static int f03; + if(!f03) + printf("s52csny : OBSTRN04 ERROR no conditional symbology for: +%s\n",rzRules->LUP->OBCL); f03++; return NULL; +} +*/ + +wxString SNDFRM02(S57Obj *obj, double depth_value); + +static void *OBSTRN04(void *param) +// Remarks: Obstructions or isolated underwater dangers of depths less than the +// safety contour which lie within the safe waters defined by the safety contour +// are to be presented by a specific isolated danger symbol and put in IMO +// category DISPLAYBASE (see (3), App.2, 1.3). This task is performed +// by the sub-procedure "UDWHAZ03" which is called by this symbology +// procedure. Objects of the class "under water rock" are handled by this +// routine as well to ensure a consistent symbolization of isolated dangers on +// the seabed. +{ + wxString obstrn04str; + // GString *sndfrm02str = NULL; + wxString *udwhaz03str = NULL; + // GString *valsoustr = S57_getAttVal(geo, "VALSOU"); + bool b_promote = false; + + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + // TODO Debug Hook + // if(obj->Index == 534) + // int yyp = 5; + + double valsou = UNKNOWN; + double depth_value = UNKNOWN; + double least_depth = UNKNOWN; + + wxString sndfrm02str; + wxString *quapnt01str = NULL; + + GetDoubleAttr(obj, "VALSOU", valsou); + wxString *objName = GetStringAttrWXS(obj, "OBJNAM"); + + if (valsou != UNKNOWN) { + depth_value = valsou; + sndfrm02str = SNDFRM02(obj, valsou); + } else { + if (GEO_AREA == obj->Primitive_type) + least_depth = _DEPVAL01(obj, least_depth); + + if (UNKNOWN == least_depth) { + int catobs = 0; + GetIntAttr(obj, "CATOBS", catobs); + int watlev = 0; + GetIntAttr(obj, "WATLEV", watlev); + int expsou = 0; + GetIntAttr(obj, "EXPSOU", expsou); + if (expsou != 1) { + if (6 == catobs) + depth_value = 0.01; + else if (0 == watlev) // default + depth_value = -15.0; + else { + switch (watlev) { + case 5: + depth_value = 0.0; + break; + case 3: + depth_value = 0.01; + break; + case 4: + case 1: + case 2: + default: + depth_value = -15.0; + break; + } + } + } + } else + depth_value = least_depth; + } + + udwhaz03str = _UDWHAZ03(obj, depth_value, rzRules, &b_promote); + + if (GEO_POINT == obj->Primitive_type) { + // Continuation A + int sounding = FALSE; + quapnt01str = CSQUAPNT01(obj); + + if (0 != udwhaz03str->Len()) { + obstrn04str.Append(*udwhaz03str); + obstrn04str.Append(*quapnt01str); + + goto end; + } + + if (UNKNOWN != valsou) { + if (valsou <= 20.0) { + int watlev = -9; + GetIntAttr(obj, "WATLEV", watlev); + + if (!strncmp(obj->FeatureName, "UWTROC", 6)) { + if (-9 == watlev) { // default + obstrn04str.Append(_T(";SY(DANGER51)")); + sounding = TRUE; + } else { + switch (watlev) { + case 3: + obstrn04str.Append(_T(";SY(DANGER51)")); + sounding = TRUE; + break; + case 4: + case 5: + obstrn04str.Append(_T(";SY(UWTROC04)")); + sounding = FALSE; + break; + default: + obstrn04str.Append(_T(";SY(DANGER51)")); + sounding = TRUE; + break; + } + } + if (b_promote) { + // Move this UWTROC object to DisplayBase category + rzRules->obj->m_DisplayCat = DISPLAYBASE; + } + } else { // OBSTRN + if (-9 == watlev) { // default + obstrn04str.Append(_T(";SY(DANGER01)")); + sounding = TRUE; + } else { + switch (watlev) { + case 1: + case 2: + obstrn04str.Append(_T(";SY(LNDARE01)")); + sounding = FALSE; + break; + case 3: + obstrn04str.Append(_T(";SY(DANGER52)")); + sounding = TRUE; + break; + case 4: + case 5: + obstrn04str.Append(_T(";SY(DANGER53)")); + sounding = TRUE; + break; + default: + obstrn04str.Append(_T(";SY(DANGER51)")); + sounding = TRUE; + break; + } + } + } + } else { // valsou > 20.0 + obstrn04str.Append(_T(";SY(DANGER52)")); + sounding = TRUE; + } + } else { // NO valsou + // GString *objlstr = S57_getAttVal(geo, "OBJL"); + // int objl = (NULL == objlstr)? 0 : + // atoi(objlstr->str); + int watlev = -9; + GetIntAttr(obj, "WATLEV", watlev); + // GString *watlevstr = S57_getAttVal(geo, "WATLEV"); + + if (!strncmp(obj->FeatureName, "UWTROC", 6)) { + if (watlev == -9) // default + obstrn04str.Append(_T(";SY(UWTROC04)")); + else { + switch (watlev) { + case 2: + obstrn04str.Append(_T(";SY(LNDARE01)")); + break; + case 3: + obstrn04str.Append(_T(";SY(UWTROC03)")); + break; + default: + obstrn04str.Append(_T(";SY(UWTROC04)")); + break; + } + } + + if (b_promote) { + // Move this UWTROC object to DisplayBase category + rzRules->obj->m_DisplayCat = DISPLAYBASE; + } + } else { // OBSTRN + if (-9 == watlev) // default + obstrn04str = _T(";SY(OBSTRN01)"); + else { + switch (watlev) { + case 1: + obstrn04str.Append(_T(";SY(OBSTRN11)")); + break; + case 2: + obstrn04str.Append(_T(";SY(OBSTRN11)")); + break; + case 3: + obstrn04str.Append(_T(";SY(OBSTRN01)")); + break; + case 4: + obstrn04str.Append(_T(";SY(OBSTRN03)")); + break; + case 5: + obstrn04str.Append(_T(";SY(OBSTRN03)")); + break; + default: + obstrn04str.Append(_T(";SY(OBSTRN01)")); + break; + } + } + } + } + + if (sounding) obstrn04str.Append(sndfrm02str); + + obstrn04str.Append(*quapnt01str); + + goto end; + + } // if geopoint + else { + if (GEO_LINE == obj->Primitive_type) { + // Continuation B + + quapnt01str = CSQUAPNT01(obj); + + if (quapnt01str->Len() > 1) { + long quapos; + quapnt01str->ToLong(&quapos); + if (2 <= quapos && quapos < 10) { + if (udwhaz03str->Len()) + obstrn04str.Append(_T(";LC(LOWACC41)")); + else + obstrn04str.Append(_T(";LC(LOWACC31)")); + } + goto end; + } + + if (udwhaz03str->Len()) { + obstrn04str.Append(_T("LS(DOTT,2,CHBLK)")); + goto end; + } + + if (UNKNOWN != valsou) { + if (valsou <= 20.0) + obstrn04str.Append(_T(";LS(DOTT,2,CHBLK)")); + else + obstrn04str.Append(_T(";LS(DASH,2,CHBLK)")); + } else + obstrn04str.Append(_T(";LS(DOTT,2,CHBLK)")); + + if (udwhaz03str->Len()) { + // Show the isolated danger symbol at the midpoint of the line + } else { + if (UNKNOWN != valsou) + if (valsou <= 20.0) obstrn04str.Append(sndfrm02str); + } + } + + else // Area feature + { + quapnt01str = CSQUAPNT01(obj); + + if (0 != udwhaz03str->Len()) { + obstrn04str.Append(_T(";AC(DEPVS);AP(FOULAR01)")); + obstrn04str.Append(_T(";LS(DOTT,2,CHBLK)")); + obstrn04str.Append(*udwhaz03str); + obstrn04str.Append(*quapnt01str); + + goto end; + } + + if (UNKNOWN != valsou) { + // BUG in CA49995B.000 if we get here because there is no color + // beside NODATA (ie there is a hole in group 1 area!) + // g_string_append(obstrn04, ";AC(UINFR)"); + + if (valsou <= 20.0) + obstrn04str.Append(_T(";LS(DOTT,2,CHBLK)")); + else + obstrn04str.Append(_T(";LS(DASH,2,CHBLK)")); + + obstrn04str.Append(sndfrm02str); + + } else { + int watlev = -9; + GetIntAttr(obj, "WATLEV", watlev); + // GString *watlevstr = S57_getAttVal(geo, + // "WATLEV"); + + if (watlev == -9) // default + obstrn04str.Append(_T(";AC(DEPVS);LS(DOTT,2,CHBLK)")); + else { + switch (watlev) { + case 1: + case 2: + obstrn04str.Append(_T(";AC(CHBRN);LS(SOLD,2,CSTLN)")); + break; + case 4: + obstrn04str.Append(_T(";AC(DEPIT);LS(DASH,2,CSTLN)")); + break; + case 5: + case 3: { + int catobs = -9; + GetIntAttr(obj, "CATOBS", catobs); + if (6 == catobs) + obstrn04str.Append( + _T(";AC(DEPVS);AP(FOULAR01);LS(DOTT,2,CHBLK)")); + else + obstrn04str.Append(_T(";AC(DEPVS);LS(DOTT,2,CHBLK)")); + } break; + default: + obstrn04str.Append(_T(";AC(DEPVS);LS(DOTT,2,CHBLK)")); + break; + } + } + } + obstrn04str.Append(*quapnt01str); + goto end; + + /* + // Continuation C (AREAS_T) + GString *quapnt01str = CSQUAPNT01(geo); + if (NULL != udwhaz03str) { + g_string_append(obstrn04str, + ";AC(DEPVS);AP(FOULAR01)"); g_string_append(obstrn04str, + ";LS(DOTT,2,CHBLK)"); g_string_append(obstrn04str, udwhaz03str->str); + if (NULL != quapnt01str) + g_string_append(obstrn04str, + quapnt01str->str); + + return obstrn04str; + } + + if (UNKNOWN != valsou) { + // BUG in CA49995B.000 if we get here because there is no + color + // beside NODATA (ie there is a hole in group 1 area!) + //g_string_append(obstrn04, ";AC(UINFR)"); + + if (valsou <= 20.0) + g_string_append(obstrn04str, + ";LS(DOTT,2,CHBLK)"); else g_string_append(obstrn04str, + ";LS(DASH,2,CHBLK)"); + + g_string_append(obstrn04str, sndfrm02str->str); + + } else { + GString *watlevstr = S57_getAttVal(geo, "WATLEV"); + + if (NULL == watlevstr) // default + g_string_append(obstrn04str, + ";AC(DEPVS);LS(DOTT,2,CHBLK)"); else { if ('3' == *watlevstr->str) { + GString *catobsstr = + S57_getAttVal(geo, "CATOBS"); if (NULL != catobsstr && '6' == + *catobsstr->str) g_string_append(obstrn04str, + ";AC(DEPVS);AP(FOULAR01);LS(DOTT,2,CHBLK)"); } else { switch + (*watlevstr->str) { case '1': case '2': g_string_append(obstrn04str, + ";AC(CHBRN);LS(SOLD,2,CSTLN)"); break; case '4': + g_string_append(obstrn04str, ";AC(DEPIT);LS(DASH,2,CSTLN)"); break; + case '5': + case '3': + default : + g_string_append(obstrn04str, ";AC(DEPVS);LS(DOTT,2,CHBLK)"); break; + } + } + } + } + + g_string_append(obstrn04str, quapnt01str->str); + + return obstrn04str; + */ + } // area + } + +end: + + // This is a specialization, to print OBJNAM for obstructions, if available + // Seen in NZ ENCs, e.g. "Horn Rock" + if (objName) { + obstrn04str.Append(_T(";TX(OBJNAM,1,2,3,'15118',-1,-1,CHBLK,26)")); + delete objName; + } + + obstrn04str.Append('\037'); + + char *r = (char *)malloc(obstrn04str.Len() + 1); + strcpy(r, obstrn04str.mb_str()); + + delete udwhaz03str; + delete quapnt01str; + + return r; +} + +static void *OWNSHP02(void *param) { + ObjRazRules *rzRules = (ObjRazRules *)param; + // S57Obj *obj = rzRules->obj; + + printf("s52csny : OWNSHP02 ERROR no conditional symbology for: %s\n", + rzRules->LUP->OBCL); + return NULL; +} + +static void *PASTRK01(void *param) { + ObjRazRules *rzRules = (ObjRazRules *)param; + // S57Obj *obj = rzRules->obj; + + printf("s52csny : PASTRK01 ERROR no conditional symbology for: %s\n", + rzRules->LUP->OBCL); + return NULL; +} + +static void *QUALIN01(void *param); +static void *QUAPNT01(void *param); + +static void *QUAPOS01(void *param) +// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is +// attached to the spatial object, not the feature object. In OpenCPN +// implementation, QUAPOS of Point Objects has been converted to QUALTY +// attribute of object. +// +// This procedure passes the object to procedure QUALIN01 or QUAPNT01, +// which traces back to the spatial object, retrieves any QUAPOS attributes, +// and returns the appropriate symbolization to QUAPOS01. +{ + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + wxString *q = NULL; + + if (GEO_LINE == obj->Primitive_type) + q = CSQUALIN01(obj); + + else + q = CSQUAPNT01(obj); + + char *r = (char *)malloc(q->Len() + 1); + strcpy(r, q->mb_str()); + + delete q; + + return r; +} + +static void *QUALIN01(void *param) +// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is +// attached only to the spatial component(s) of an object. +// +// A line object may be composed of more than one spatial object. +// +// This procedure looks at each of the spatial +// objects, and symbolizes the line according to the positional accuracy. +{ + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + wxString *q = CSQUALIN01(obj); + char *r = (char *)malloc(q->Len() + 1); + strcpy(r, q->mb_str()); + + delete q; + return r; +} + +wxString *CSQUALIN01(S57Obj *obj) +// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is +// attached only to the spatial component(s) of an object. +// +// A line object may be composed of more than one spatial object. +// +// This procedure looks at each of the spatial +// objects, and symbolizes the line according to the positional accuracy. +{ + wxString qualino1; + int quapos = 0; + bool bquapos = GetIntAttr(obj, "QUAPOS", quapos); + const char *line = NULL; + + if (bquapos) { + if (2 <= quapos && quapos < 10) line = "LC(LOWACC21)"; + } else { + if (!strncmp("COALNE", obj->FeatureName, 6)) { + int conrad; + bool bconrad = GetIntAttr(obj, "CONRAD", conrad); + + if (bconrad) { + if (1 == conrad) + line = "LS(SOLD,3,CHMGF);LS(SOLD,1,CSTLN)"; + else + line = "LS(SOLD,1,CSTLN)"; + } else + line = "LS(SOLD,1,CSTLN)"; + + } else // LNDARE + line = "LS(SOLD,1,CSTLN)"; + } + + if (NULL != line) qualino1.Append(wxString(line, wxConvUTF8)); + + qualino1.Append('\037'); + + wxString *r = new wxString(qualino1); + + /* char *r = (char *)malloc(qualino1.Len() + 1); + strcpy(r, qualino1.mb_str()); + */ + return r; +} + +static void *QUAPNT01(void *param) +// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is +// attached only to the spatial component(s) of an object. +// +// This procedure retrieves any QUALTY (ne QUAPOS) attributes, and returns the +// appropriate symbols to the calling procedure. + +{ + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + wxString *q = CSQUAPNT01(obj); + + char *r = (char *)malloc(q->Len() + 1); + strcpy(r, q->mb_str()); + + return r; +} + +wxString *CSQUAPNT01(S57Obj *obj) +// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is +// attached only to the spatial component(s) of an object. +// +// This procedure retrieves any QUALTY (ne QUAPOS) attributes, and returns the +// appropriate symbols to the calling procedure. + +{ + wxString quapnt01; + int accurate = TRUE; + int qualty = 10; + bool bquapos = GetIntAttr(obj, "QUAPOS", qualty); + + if (bquapos) { + if (2 <= qualty && qualty < 10) accurate = FALSE; + } + + if (!accurate) { + switch (qualty) { + case 4: + quapnt01.Append(_T(";SY(QUAPOS01)")); + break; // "PA" + case 5: + quapnt01.Append(_T(";SY(QUAPOS02)")); + break; // "PD" + case 7: + case 8: + quapnt01.Append(_T(";SY(QUAPOS03)")); + break; // "REP" + default: + quapnt01.Append(_T(";SY(LOWACC03)")); + break; // "?" + } + } + + quapnt01.Append('\037'); + + wxString *r = new wxString; + + *r = quapnt01; + + /* char *r = (char *)malloc(quapnt01.Len() + 1); + strcpy(r, quapnt01.mb_str()); + */ + return r; +} + +static void *SLCONS03(void *param) + +// Remarks: Shoreline construction objects which have a QUAPOS attribute on +// their +// spatial component indicating that their position is unreliable are symbolized +// by a special linestyle in the place of the varied linestyles normally used. +// Otherwise this procedure applies the normal symbolization. +{ + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + wxString slcons03; + + bool bvalstr; + int ival; + + const char *cmdw = NULL; // command word + + int quapos; + bool bquapos = GetIntAttr(obj, "QUAPOS", quapos); + + if (GEO_POINT == obj->Primitive_type) { + if (bquapos) { + if (2 <= quapos && quapos < 10) cmdw = "SY(LOWACC01)"; + } + } else { + // This instruction not found in PLIB 3.4, but seems to appear in later PLIB + // implementations by commercial ECDIS providers, so..... + if (GEO_AREA == obj->Primitive_type) { + slcons03 = _T("AP(CROSSX01);"); + } + + // GEO_LINE and GEO_AREA are the same + if (bquapos) { + if (2 <= quapos && quapos < 10) cmdw = "LC(LOWACC01)"; + } else { + bvalstr = GetIntAttr(obj, "CONDTN", ival); + + if (bvalstr && (1 == ival || 2 == ival)) + cmdw = "LS(DASH,1,CSTLN)"; + else { + ival = 0; + bvalstr = GetIntAttr(obj, "CATSLC", ival); + + if (bvalstr && + (6 == ival || 15 == ival || 16 == ival)) // Some sort of wharf + cmdw = "LS(SOLD,4,CSTLN)"; + else { + bvalstr = GetIntAttr(obj, "WATLEV", ival); + + if (bvalstr && 2 == ival) + cmdw = "LS(SOLD,2,CSTLN)"; + else if (bvalstr && (3 == ival || 4 == ival)) + cmdw = "LS(DASH,2,CSTLN)"; + else + cmdw = "LS(SOLD,2,CSTLN)"; // default + } + } + } + } + + // WARNING: not explicitly specified in S-52 !! + // FIXME:this is to put AC(DEPIT) --intertidal area + // Could this be bug in OGR ? + /* + if (AREAS_T == S57_getObjtype(geo)) { + GString *seabed01 = NULL; + GString *drval1str = S57_getAttVal(geo, "DRVAL1"); + double drval1 = (NULL == drval1str)? -UNKNOWN : + atof(drval1str->str); GString *drval2str = S57_getAttVal(geo, "DRVAL2"); + double drval2 = (NULL == drval2str)? -UNKNOWN : + atof(drval2str->str); + // NOTE: change sign of infinity (minus) to get out of bound in seabed01 + + + PRINTF("***********drval1=%f drval2=%f \n", drval1, drval2); + seabed01 = _SEABED01(drval1, drval2); + slcons03 = g_string_new(seabed01->str); + g_string_free(seabed01, TRUE); + + } + */ + + if (NULL != cmdw) slcons03.Append(wxString(cmdw, wxConvUTF8)); + + // Match CM93 CMAPECS presentation? + /* + if (GEO_AREA == obj->Primitive_type) + slcons03.Append(_T(";AC(LANDA)")); + */ + + slcons03.Append('\037'); + + char *r = (char *)malloc(slcons03.Len() + 1); + strcpy(r, slcons03.mb_str()); + + return r; +} + +static void *RESARE02(void *param) +// Remarks: A list-type attribute is used because an area of the object class +// RESARE may have more than one category (CATREA). For example an inshore +// traffic zone might also have fishing and anchoring prohibition and a +// prohibited area might also be a bird sanctuary or a mine field. +// +// This conditional procedure is set up to ensure that the categories of most +// importance to safe navigation are prominently symbolized, and to pass on +// all given information with minimum clutter. Only the most significant +// restriction is symbolized, and an indication of further limitations is given +// by a subscript "!" or "I". Further details are given under conditional +// symbology procedure RESTRN01 +// +// Other object classes affected by attribute RESTRN are handled by +// conditional symbology procedure RESTRN01. +{ + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + wxString resare02; + + wxString *restrnstr = GetStringAttrWXS(obj, "RESTRN"); + // GString *restrnstr = S57_getAttVal(geo, "RESTRN"); + + char restrn[LISTSIZE] = {'\0'}; + // GString *catreastr = S57_getAttVal(geo, "CATREA"); + wxString *catreastr = GetStringAttrWXS(obj, "CATREA"); + + char catrea[LISTSIZE] = {'\0'}; + wxString symb; + wxString line; + wxString prio; + + if (NULL != catreastr) + _parseList(catreastr->mb_str(), catrea, sizeof(catrea)); + + if (NULL != restrnstr) { + _parseList(restrnstr->mb_str(), restrn, sizeof(restrn)); + + if (strpbrk(restrn, "\007\010\016")) { // entry restrictions + // Continuation A + if (strpbrk(restrn, + "\001\002\003\004\005\006")) // anchoring, fishing, trawling + symb = _T(";SY(ENTRES61)"); + else { + if (NULL != catreastr && + strpbrk(catrea, "\001\010\011\014\016\023\025\031")) + symb = _T(";SY(ENTRES61)"); + else { + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = _T(";SY(ENTRES71)"); + else { + if (NULL != catreastr && + strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030")) + symb = _T(";SY(ENTRES71)"); + else + symb = _T(";SY(ENTRES51)"); + } + } + } + + if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND)) + line = _T(";LC(RESARE51)"); + else + line = _T( ";LS(DASH,2,CHMGD)"); + + prio = _T(";OP(6---)"); // display prio set to 6 + + } else { + if (strpbrk(restrn, "\001\002")) { // anchoring + // Continuation B + if (strpbrk(restrn, "\003\004\005\006")) + symb = _T(";SY(ACHRES61)"); + else { + if (NULL != catreastr && + strpbrk(catrea, "\001\010\011\014\016\023\025\031")) + symb = _T(";SY(ACHRES61)"); + else { + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = _T(";SY(ACHRES71)"); + else { + if (NULL != catreastr && + strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030")) + symb = _T( ";SY(ACHRES71)"); + else + symb = _T(";SY(RESTRN51)"); + } + } + } + + if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND)) + line = _T(";LC(RESARE51)"); // could be ACHRES51 when _drawLC is + // implemented fully + else + line = _T(";LS(DASH,2,CHMGD)"); + + prio = _T(";OP(6---)"); // display prio set to 6 + + } else { + if (strpbrk(restrn, "\003\004\005\006")) { // fishing/trawling + // Continuation C + if (NULL != catreastr && + strpbrk(catrea, "\001\010\011\014\016\023\025\031")) + symb = _T(";SY(FSHRES51)"); + else { + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = _T(";SY(FSHRES71)"); + else { + if (NULL != catreastr && + strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030")) + symb = _T(";SY(FSHRES71)"); + else + symb = _T(";SY(FSHRES51)"); + } + } + + if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND)) + line = _T(";LC(FSHRES51)"); + else + line = _T(";LS(DASH,2,CHMGD)"); + + prio = _T(";OP(6---)"); // display prio set to 6 + + } else { + if (strpbrk(restrn, + "\011\012\013\014\015")) // diving, dredging, waking... + symb = _T(";SY(INFARE51)"); + else + symb = _T(";SY(RSRDEF51)"); + + if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND)) + line = _T(";LC(CTYARE51)"); + else + line = _T(";LS(DASH,2,CHMGD)"); + } + // Todo more for s57 3.1 Look at caris catalog ATTR::RESARE + } + } + } else { + // Continuation D + if (NULL != catreastr) { + if (strpbrk(catrea, "\001\010\011\014\016\023\025\031")) { + if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030")) + symb = _T(";SY(CTYARE71)"); + else + symb = _T(";SY(CTYARE51)"); + } else { + if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030")) + symb = _T(";SY(INFARE51)"); + else + symb = _T(";SY(RSRDEF51)"); + } + } else + symb = _T(";SY(RSRDEF51)"); + + if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND)) + line = _T(";LC(CTYARE51)"); + else + line = _T(";LS(DASH,2,CHMGD)"); + } + + // create command word + if (prio.Len()) resare02.Append(prio); + resare02.Append(line); + resare02.Append(symb); + + resare02.Append('\037'); + + char *r = (char *)malloc(resare02.Len() + 1); + strcpy(r, resare02.mb_str()); + + delete restrnstr; + delete catreastr; + + return r; +} + +static void *_RESCSP01(void *param); +static void *RESTRN01(void *param) +// Remarks: Objects subject to RESTRN01 are actually symbolised in sub-process +// RESCSP01, since the latter can also be accessed from other conditional +// symbology procedures. RESTRN01 merely acts as a "signpost" for +// RESCSP01. +// +// Object class RESARE is symbolised for the effect of attribute RESTRN in a +// separate conditional symbology procedure called RESARE02. +// +// Since many of the areas concerned cover shipping channels, the number of +// symbols used is minimised to reduce clutter. To do this, values of RESTRN are +// ranked for significance as follows: "Traffic Restriction" values of RESTRN: +// (1) RESTRN 7,8: entry prohibited or restricted +// RESTRN 14: IMO designated "area to be avoided" part of a TSS +// (2) RESTRN 1,2: anchoring prohibited or restricted +// (3) RESTRN 3,4,5,6: fishing or trawling prohibited or restricted +// (4) "Other Restriction" values of RESTRN are: +// RESTRN 9, 10: dredging prohibited or restricted, +// RESTRN 11,12: diving prohibited or restricted, +// RESTRN 13 : no wake area. +{ + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + wxString *restrnstr = GetStringAttrWXS(obj, "RESTRN"); + + // GString *restrn01str = S57_getAttVal(geo, "RESTRN"); + char *restrn01 = NULL; + + if (NULL != restrnstr) + restrn01 = (char *)_RESCSP01(param); + else + restrn01 = NULL; + + delete restrnstr; + return restrn01; +} + +static void *_RESCSP01(void *param) +// Remarks: See procedure RESTRN01 +{ + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + wxString rescsp01; + // char *rescsp01 = NULL; + wxString *restrnstr = GetStringAttrWXS(obj, "RESTRN"); + // GString *restrnstr = S57_getAttVal(geo, "RESTRN"); + char restrn[LISTSIZE] = {'\0'}; // restriction list + wxString symb; + char *r = NULL; + + if (restrnstr->Len()) { + _parseList(restrnstr->mb_str(), restrn, sizeof(restrn)); + + if (strpbrk(restrn, "\007\010\016")) { + // continuation A + if (strpbrk(restrn, "\001\002\003\004\005\006")) + symb = _T(";SY(ENTRES61)"); + else { + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = _T(";SY(ENTRES71)"); + else + symb = _T(";SY(ENTRES51)"); + } + } else { + if (strpbrk(restrn, "\001\002")) { + // continuation B + if (strpbrk(restrn, "\003\004\005\006")) + symb = _T(";SY(ACHRES61)"); + else { + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = _T( ";SY(ACHRES71)"); + else + symb = _T(";SY(ACHRES51)"); + } + + } else { + if (strpbrk(restrn, "\003\004\005\006")) { + // continuation C + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = _T(";SY(FSHRES71)"); + else + symb = _T(";SY(FSHRES51)"); + + } else { + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = _T(";SY(INFARE51)"); + else + symb = _T(";SY(RSRDEF51)"); + } + } + } + + rescsp01.Append(symb); + rescsp01.Append('\037'); + + r = (char *)malloc(rescsp01.Len() + 1); + strcpy(r, rescsp01.mb_str()); + + delete restrnstr; + } + + return r; +} + +static void *SEABED01(void *param) { + ObjRazRules *rzRules = (ObjRazRules *)param; + // S57Obj *obj = rzRules->obj; + +// CPLError((CPLErr)0, 0, +// "s52csny : SEABED01 ERROR no conditional symbology for: %s\n", +// rzRules->LUP->OBCL); + return NULL; +} + +/* +static void *SNDFRM02(void *param) +{ + ObjRazRules *rzRules = (ObjRazRules *)param; +// S57Obj *obj = rzRules->obj; + + CPLError((CPLErr)0, 0,"s52csny : SNDFRM02 ERROR no conditional symbology +for: %s\n",rzRules->LUP->OBCL); return NULL; +} +*/ + +wxString SNDFRM02(S57Obj *obj, double depth_value); + +static void *SOUNDG02(void *param) +// Remarks: In S-57 soundings are elements of sounding arrays rather than +// individual objects. Thus the conditional symbology methodology must examine +// each sounding of a sounding array one by one. To symbolize the depth values +// it calls the procedure SNDFRM02 which in turn translates the depth values +// into a set of symbols to be shown at the soundings position. +{ + // Shortcut. This CS method causes a branch to an S52plib method + // which splits multi-point soundings into separate point objects, + // and then calls CS(SOUNDG03) on successive points below. + char *r = (char *)malloc(6); + strcpy(r, "MP();"); + + return r; +} + +static void *SOUNDG03(void *param) +// Remarks: SOUNDG03 is a private conditional symbology, +// called to render individual points of a multi-point sounding set. +{ + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + wxString s = SNDFRM02(obj, obj->z); + + char *r = (char *)malloc(s.Len() + 1); + strcpy(r, s.mb_str()); + + return r; +} + +wxString SNDFRM02(S57Obj *obj, double depth_value_in) +// Remarks: Soundings differ from plain text because they have to be readable +// under all circumstances and their digits are placed according to special +// rules. This conditional symbology procedure accesses a set of carefully +// designed sounding symbols provided by the symbol library and composes them to +// sounding labels. It symbolizes swept depth and it also symbolizes for low +// reliability as indicated by attributes QUASOU and QUAPOS. +{ + wxString sndfrm02; + char temp_str[LISTSIZE] = {'\0'}; + wxString symbol_prefix; + + char symbol_prefix_a[200]; + + wxString *tecsoustr = GetStringAttrWXS(obj, "TECSOU"); + char tecsou[LISTSIZE] = {'\0'}; + + wxString *quasoustr = GetStringAttrWXS(obj, "QUASOU"); + char quasou[LISTSIZE] = {'\0'}; + + wxString *statusstr = GetStringAttrWXS(obj, "STATUS"); + char status[LISTSIZE] = {'\0'}; + + double leading_digit = 0.0; + + double safety_depth = S52_getMarinerParam(S52_MAR_SAFETY_DEPTH); + + // Do the math to convert soundings to ft/metres/fathoms on request + double depth_value = depth_value_in; + + // If the sounding value from the ENC (or SENC) is bogus, so state + if (depth_value_in > 40000.) depth_value = 99999.; + if (depth_value_in < -1000.) depth_value = 0.; + + switch (ps52plib->m_nDepthUnitDisplay) { + case 0: + depth_value = depth_value * (1.0 / 0.3048); // feet + safety_depth = safety_depth * (1.0 / 0.3048); + break; + case 2: + depth_value = depth_value * (1.0 / 0.3048) / 6; // fathoms + safety_depth = safety_depth * (1.0 / 0.3048) / 6; + break; + default: + break; + } + + // FIXME: test to fix the rounding error (!?) + depth_value += (depth_value > 0.0) ? 0.01 : -0.01; + leading_digit = (int)fabs(depth_value); + + if (depth_value <= safety_depth) // S52_getMarinerParam(S52_MAR_SAFETY_DEPTH) + symbol_prefix = _T("SOUNDS"); + else + symbol_prefix = _T("SOUNDG"); + + strcpy(symbol_prefix_a, symbol_prefix.mb_str()); + + if (NULL != tecsoustr) { + _parseList(tecsoustr->mb_str(), tecsou, sizeof(tecsou)); + if (strpbrk(tecsou, "\006")) { + chk_snprintf(temp_str, LISTSIZE, ";SY(%sB1)", symbol_prefix_a); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + } + } + + if (NULL != quasoustr) + _parseList(quasoustr->mb_str(), quasou, sizeof(quasou)); + if (NULL != statusstr) + _parseList(statusstr->mb_str(), status, sizeof(status)); + + if (strpbrk(quasou, "\003\004\005\010\011") || strpbrk(status, "\022")) { + chk_snprintf(temp_str, LISTSIZE, ";SY(%sC2)", symbol_prefix_a); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + } else { + int quapos = 0; + GetIntAttr(obj, "QUAPOS", quapos); + if (0 != quapos) { + if (2 <= quapos && quapos < 10) { + chk_snprintf(temp_str, LISTSIZE, ";SY(%sC2)", symbol_prefix_a); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + } + } + } + + // Continuation A + if (fabs(depth_value) < 10.0) { + // If showing as "feet", round off to one digit only + if ((ps52plib->m_nDepthUnitDisplay == 0) && (depth_value > 0)) { + double r1 = depth_value; + depth_value = wxRound(r1); + leading_digit = (int)depth_value; + } + + if (depth_value < 10.0) { + // can be above water (negative) + int fraction = (int)abs((fabs(depth_value) - leading_digit) * 10); + + chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a, + (int)abs(leading_digit)); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + if (fraction > 0) { + chk_snprintf(temp_str, LISTSIZE, ";SY(%s5%1i)", symbol_prefix_a, + fraction); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + } + + // above sea level (negative) + if (depth_value < 0.0) { + chk_snprintf(temp_str, LISTSIZE, ";SY(%sA1)", symbol_prefix_a); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + } + goto return_point; + } + } + + if (fabs(depth_value) < 31.0) { + bool b_2digit = false; + double depth_value_pos = fabs(depth_value); + + // If showing as "feet", round off to two digits only + if ((ps52plib->m_nDepthUnitDisplay == 0) && (depth_value_pos > 0)) { + double r1 = depth_value; + depth_value = wxRound(r1); + leading_digit = (int)depth_value_pos; + b_2digit = true; + } + + double fraction = fabs(depth_value_pos - floor(leading_digit)); + + if (fraction != 0.0) { + fraction = fraction * 10; + if (leading_digit >= 10.0) { + chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)", symbol_prefix_a, + (int)leading_digit / 10); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + } + + double first_digit = floor(leading_digit / 10); + int secnd_digit = (int)(floor(leading_digit - (first_digit * 10))); + chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a, + secnd_digit /*(int)leading_digit*/); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + + if (!b_2digit) { + if ((int)fraction > 0) { + chk_snprintf(temp_str, LISTSIZE, ";SY(%s5%1i)", symbol_prefix_a, + (int)fraction); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + } + } + + if (depth_value < 0.0) { + chk_snprintf(temp_str, LISTSIZE, ";SY(%sA1)", symbol_prefix_a); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + } + + goto return_point; + } + } + + // Continuation B + if (fabs(depth_value) < 100.0) { + leading_digit = fabs(leading_digit); + + double first_digit = floor(leading_digit / 10); + double secnd_digit = floor(leading_digit - (first_digit * 10)); + + if (depth_value < 0.0) { + chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)", symbol_prefix_a, + (int)first_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a, + (int)secnd_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + chk_snprintf(temp_str, LISTSIZE, ";SY(%sA1)", symbol_prefix_a); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + } else { + chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a, + (int)first_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + chk_snprintf(temp_str, LISTSIZE, ";SY(%s0%1i)", symbol_prefix_a, + (int)secnd_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + } + goto return_point; + } + + if (depth_value < 1000.0) { + double first_digit = floor(leading_digit / 100); + double secnd_digit = floor((leading_digit - (first_digit * 100)) / 10); + double third_digit = + floor(leading_digit - (first_digit * 100) - (secnd_digit * 10)); + + chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)", symbol_prefix_a, + (int)first_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a, + (int)secnd_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + chk_snprintf(temp_str, LISTSIZE, ";SY(%s0%1i)", symbol_prefix_a, + (int)third_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + + goto return_point; + } + + if (depth_value < 10000.0) { + double first_digit = floor(leading_digit / 1000); + double secnd_digit = floor((leading_digit - (first_digit * 1000)) / 100); + double third_digit = floor( + (leading_digit - (first_digit * 1000) - (secnd_digit * 100)) / 10); + double last_digit = floor(leading_digit - (first_digit * 1000) - + (secnd_digit * 100) - (third_digit * 10)); + + chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)", symbol_prefix_a, + (int)first_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a, + (int)secnd_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + chk_snprintf(temp_str, LISTSIZE, ";SY(%s0%1i)", symbol_prefix_a, + (int)third_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + chk_snprintf(temp_str, LISTSIZE, ";SY(%s4%1i)", symbol_prefix_a, + (int)last_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + + goto return_point; + } + + // Continuation C + { + double first_digit = floor(leading_digit / 10000); + double secnd_digit = floor((leading_digit - (first_digit * 10000)) / 1000); + double third_digit = floor( + (leading_digit - (first_digit * 10000) - (secnd_digit * 1000)) / 100); + double fourth_digit = floor((leading_digit - (first_digit * 10000) - + (secnd_digit * 1000) - (third_digit * 100)) / + 10); + double last_digit = + floor(leading_digit - (first_digit * 10000) - (secnd_digit * 1000) - + (third_digit * 100) - (fourth_digit * 10)); + + chk_snprintf(temp_str, LISTSIZE, ";SY(%s3%1i)", symbol_prefix_a, + (int)first_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)", symbol_prefix_a, + (int)secnd_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)", symbol_prefix_a, + (int)third_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + chk_snprintf(temp_str, LISTSIZE, ";SY(%s0%1i)", symbol_prefix_a, + (int)fourth_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + chk_snprintf(temp_str, LISTSIZE, ";SY(%s4%1i)", symbol_prefix_a, + (int)last_digit); + sndfrm02.Append(wxString(temp_str, wxConvUTF8)); + + goto return_point; + } + +return_point: + sndfrm02.Append('\037'); + + delete tecsoustr; + delete quasoustr; + delete statusstr; + + return sndfrm02; +} + +static void *TOPMAR01(void *param) +// Remarks: Topmark objects are to be symbolized through consideration of their +// platforms e.g. a buoy. Therefore this conditional symbology procedure +// searches for platforms by looking for other objects that are located at the +// same position.. Based on the finding whether the platform is rigid or +// floating, the respective upright or sloping symbol is selected and presented +// at the objects location. Buoyf symbols and topmark symbols have been +// carefully designed to fit to each other when combined at the same position. +// The result is a composed symbol that looks like the traditional symbols the +// mariner is used to. +{ + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + int top_int = 0; + bool battr = GetIntAttr(obj, "TOPSHP", top_int); + + wxString sy; + + if (!battr) + sy = _T(";SY(QUESMRK1)"); + else { + int floating = FALSE; // not a floating platform + int topshp = (!battr) ? 0 : top_int; + + if (TRUE == _atPtPos(obj, GetChartFloatingATONArray(rzRules), false)) + floating = TRUE; + + if (floating) { + // floating platform + switch (topshp) { + case 1: + sy = _T(";SY(TOPMAR02)"); + break; + case 2: + sy = _T(";SY(TOPMAR04)"); + break; + case 3: + sy = _T(";SY(TOPMAR10)"); + break; + case 4: + sy = _T(";SY(TOPMAR12)"); + break; + + case 5: + sy = _T(";SY(TOPMAR13)"); + break; + case 6: + sy = _T(";SY(TOPMAR14)"); + break; + case 7: + sy = _T(";SY(TOPMAR65)"); + break; + case 8: + sy = _T(";SY(TOPMAR17)"); + break; + + case 9: + sy = _T(";SY(TOPMAR16)"); + break; + case 10: + sy = _T(";SY(TOPMAR08)"); + break; + case 11: + sy = _T(";SY(TOPMAR07)"); + break; + case 12: + sy = _T(";SY(TOPMAR14)"); + break; + + case 13: + sy = _T(";SY(TOPMAR05)"); + break; + case 14: + sy = _T(";SY(TOPMAR06)"); + break; + case 17: + sy = _T(";SY(TMARDEF2)"); + break; + case 18: + sy = _T(";SY(TOPMAR10)"); + break; + + case 19: + sy = _T(";SY(TOPMAR13)"); + break; + case 20: + sy = _T(";SY(TOPMAR14)"); + break; + case 21: + sy = _T(";SY(TOPMAR13)"); + break; + case 22: + sy = _T(";SY(TOPMAR14)"); + break; + + case 23: + sy = _T(";SY(TOPMAR14)"); + break; + case 24: + sy = _T(";SY(TOPMAR02)"); + break; + case 25: + sy = _T(";SY(TOPMAR04)"); + break; + case 26: + sy = _T(";SY(TOPMAR10)"); + break; + + case 27: + sy = _T(";SY(TOPMAR17)"); + break; + case 28: + sy = _T(";SY(TOPMAR18)"); + break; + case 29: + sy = _T(";SY(TOPMAR02)"); + break; + case 30: + sy = _T(";SY(TOPMAR17)"); + break; + + case 31: + sy = _T(";SY(TOPMAR14)"); + break; + case 32: + sy = _T(";SY(TOPMAR10)"); + break; + case 33: + sy = _T(";SY(TMARDEF2)"); + break; + default: + sy = _T(";SY(TMARDEF2)"); + break; + } + } else { + // not a floating platform + switch (topshp) { + case 1: + sy = _T(";SY(TOPMAR22)"); + break; + case 2: + sy = _T(";SY(TOPMAR24)"); + break; + case 3: + sy = _T(";SY(TOPMAR30)"); + break; + case 4: + sy = _T(";SY(TOPMAR32)"); + break; + + case 5: + sy = _T(";SY(TOPMAR33)"); + break; + case 6: + sy = _T(";SY(TOPMAR34)"); + break; + case 7: + sy = _T(";SY(TOPMAR85)"); + break; + case 8: + sy = _T(";SY(TOPMAR86)"); + break; + + case 9: + sy = _T(";SY(TOPMAR36)"); + break; + case 10: + sy = _T(";SY(TOPMAR28)"); + break; + case 11: + sy = _T(";SY(TOPMAR27)"); + break; + case 12: + sy = _T(";SY(TOPMAR14)"); + break; + + case 13: + sy = _T(";SY(TOPMAR25)"); + break; + case 14: + sy = _T(";SY(TOPMAR26)"); + break; + case 15: + sy = _T(";SY(TOPMAR88)"); + break; + case 16: + sy = _T(";SY(TOPMAR87)"); + break; + + case 17: + sy = _T(";SY(TMARDEF1)"); + break; + case 18: + sy = _T(";SY(TOPMAR30)"); + break; + case 19: + sy = _T(";SY(TOPMAR33)"); + break; + case 20: + sy = _T(";SY(TOPMAR34)"); + break; + + case 21: + sy = _T(";SY(TOPMAR33)"); + break; + case 22: + sy = _T(";SY(TOPMAR34)"); + break; + case 23: + sy = _T(";SY(TOPMAR34)"); + break; + case 24: + sy = _T(";SY(TOPMAR22)"); + break; + + case 25: + sy = _T(";SY(TOPMAR24)"); + break; + case 26: + sy = _T(";SY(TOPMAR30)"); + break; + case 27: + sy = _T(";SY(TOPMAR86)"); + break; + case 28: + sy = _T(";SY(TOPMAR89)"); + break; + + case 29: + sy = _T(";SY(TOPMAR22)"); + break; + case 30: + sy = _T(";SY(TOPMAR86)"); + break; + case 31: + sy = _T(";SY(TOPMAR14)"); + break; + case 32: + sy = _T(";SY(TOPMAR30)"); + break; + case 33: + sy = _T(";SY(TMARDEF1)"); + break; + default: + sy = _T(";SY(TMARDEF1)"); + break; + } + } + } + + wxString topmar; + topmar.Append(sy); + topmar.Append('\037'); + + char *r = (char *)malloc(topmar.Len() + 1); + strcpy(r, topmar.mb_str()); + + return r; +} + +static void *UDWHAZ03(void *param) { + ObjRazRules *rzRules = (ObjRazRules *)param; + // S57Obj *obj = rzRules->obj; + +// CPLError((CPLErr)0, 0, +// "s52csny : UDWHAZ03 ERROR no conditional symbology for: %s\n", +// rzRules->LUP->OBCL); + return NULL; +} + +static void *VESSEL01(void *param) { + ObjRazRules *rzRules = (ObjRazRules *)param; + // S57Obj *obj = rzRules->obj; + +// CPLError((CPLErr)0, 0, +// "s52csny : VESSEL01 ERROR no conditional symbology for: %s\n", +// rzRules->LUP->OBCL); + return NULL; +} + +static void *VRMEBL01(void *param) { + ObjRazRules *rzRules = (ObjRazRules *)param; + // S57Obj *obj = rzRules->obj; + +//FIXME plib +// CPLError((CPLErr)0, 0, +// "s52csny : VRMEBL01 ERROR no conditional symbology for: %s\n", +// rzRules->LUP->OBCL); + return NULL; +} + +/* +static void *WRECKS02a(void *param) +{ + ObjRazRules *rzRules = (ObjRazRules *)param; +// S57Obj *obj = rzRules->obj; + + static int f07; + if(!f07) + CPLError((CPLErr)0, 0,"s52csny : WRECKS02 ERROR no conditional +symbology for: %s\n",rzRules->LUP->OBCL); f07++; return NULL; +} +*/ + +static void *WRECKS02(void *param) +// Remarks: Wrecks of depths less than the safety contour which lie within the +// safe waters defined by the safety contour are to be presented by a specific +// isolated danger symbol and put in IMO category DISPLAYBASE (see (3), App.2, +// 1.3). This task is performed by the sub-procedure "UDWHAZ03" which is +// called by this symbology procedure. +{ + wxString wrecks02str; + wxString sndfrm02str; + wxString *udwhaz03str = NULL; + wxString *quapnt01str = NULL; + double least_depth = UNKNOWN; + double depth_value = UNKNOWN; + // GString *valsoustr = S57_getAttVal(geo, "VALSOU"); + double valsou = UNKNOWN; + bool b_promote = false; + + ObjRazRules *rzRules = (ObjRazRules *)param; + S57Obj *obj = rzRules->obj; + + GetDoubleAttr(obj, "VALSOU", valsou); + + int watlev = -9; + GetIntAttr(obj, "WATLEV", watlev); + int catwrk = -9; + GetIntAttr(obj, "CATWRK", catwrk); + + int quasou = -9; + // QUASOU is a list ie a string for us + wxString *quasoustr = GetStringAttrWXS(obj, "QUASOU"); + char quasouchar[LISTSIZE] = {'\0'}; + + double safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR); + + if (UNKNOWN != valsou) { + depth_value = valsou; + sndfrm02str = SNDFRM02(obj, depth_value); + } else { + if (GEO_AREA == obj->Primitive_type) + least_depth = _DEPVAL01(obj, least_depth); + + if (least_depth == UNKNOWN) + /* + { + // WARNING: ambiguity removed in WRECKS03 (see update) + + if (-9 == watlev) // default + depth_value = -15.0; + else + switch (watlev) + { // ambiguous + case 1: + case 2: depth_value = -15.0 ; break; + case 3: depth_value = 0.01; break; + case 4: depth_value = -15.0 ; break; + case 5: depth_value = 0.0 ; break; + case 6: depth_value = -15.0 ; break; + default : + { + if (-9 != catwrk) + { + switch (catwrk) + { + case 1: depth_value = 20.0; + break; case 2: depth_value = 0.0; break; case 4: case 5: depth_value = + -15.0; break; + } + } + } + } + } + */ + //////////////////////////////////////////////// + // DSR New Logic Here (FIXME) + { + if (-9 != catwrk) { + switch (catwrk) { + case 1: + depth_value = 20.0; + break; // safe + case 2: + depth_value = 0.0; + break; // dangerous + case 4: + case 5: + depth_value = -15.0; + break; + } + } else { + if (-9 == watlev) // default + depth_value = -15.0; + else + switch (watlev) { + case 1: + case 2: + depth_value = -15.0; + break; + case 3: + depth_value = 0.01; + break; + case 4: + depth_value = -15.0; + break; + case 5: + depth_value = 0.0; + break; + case 6: + depth_value = -15.0; + break; + } + } + + } + + else + depth_value = least_depth; + } + if (NULL != quasoustr) + _parseList(quasoustr->mb_str(), quasouchar, sizeof(quasouchar)); + + if (quasouchar[0] == 0 || NULL == strpbrk(quasouchar, "\07")) { + // Fixes FS 165 XXX where it is? + // 7 is 'least depth unknown, safe clearance at value shown' + udwhaz03str = _UDWHAZ03(obj, depth_value, rzRules, &b_promote); + + } else { + quasou = 7; + udwhaz03str = new wxString(); + } + quapnt01str = CSQUAPNT01(obj); + + if (GEO_POINT == obj->Primitive_type) { + if (0 != udwhaz03str->Len()) { + wrecks02str = wxString(*udwhaz03str); + + wrecks02str.Append(*quapnt01str); + + } else { + // Continuation A (POINT_T) + if (UNKNOWN != valsou) { + /////////////////////////////////////////// + // DSR New logic here, FIXME check s52 specs + + /* + if (valsou <= 20.0) + { + wrecks02str = wxString(";SY(DANGER51)"); + if (NULL != sndfrm02str) + wrecks02str.Append(sndfrm02str); + } + else + wrecks02str = wxString(";SY(DANGER52)"); + */ + if ((valsou < + safety_contour) /* || (2 == catwrk)*/) // maybe redundant, seems + // like wrecks with valsou + // < 20 are always coded as + // "dangerous wrecks" + // Excluding (2 == catwrk) + // matches Caris logic + wrecks02str = wxString(_T(";SY(DANGER51)")); + else + wrecks02str = wxString(_T(";SY(DANGER52)")); + wrecks02str.Append(_T(";TX('Wk',3,1,2,'15110',2,0,CHBLK,21)")); + if (7 == quasou) // Fixes FS 165 + wrecks02str.Append(_T(";SY(WRECKS07)")); + + wrecks02str.Append(sndfrm02str); // always show valsou depth + /////////////////////////////////////////// + + wrecks02str.Append(*udwhaz03str); + wrecks02str.Append(*quapnt01str); + + } else { + wxString sym; + + if (-9 != catwrk && -9 != watlev) { + if (1 == catwrk && 3 == watlev) + sym = _T(";SY(WRECKS04)"); + else { + if (2 == catwrk && 3 == watlev) + sym = _T(";SY(WRECKS05)"); + else { + if (4 == catwrk || 5 == catwrk) + sym = _T(";SY(WRECKS01)"); + else { + if (1 == watlev || 2 == watlev || 5 == watlev || 4 == watlev) { + sym = _T(";SY(WRECKS01)"); + } else + sym = _T(";SY(WRECKS05)"); // default + } + } + } + } + + wrecks02str = sym; + if (NULL != quapnt01str) wrecks02str.Append(*quapnt01str); + } + } + + } else { + // Continuation B (AREAS_T) + int quapos = 0; + GetIntAttr(obj, "QUAPOS", quapos); + + wxString line; + + if (2 <= quapos && quapos < 10) + line = _T(";LC(LOWACC41)"); + else { + if (0 != udwhaz03str->Len()) + line = _T(";LS(DOTT,2,CHBLK)"); + else { + if (UNKNOWN != valsou) { + if (valsou <= 20) + line = _T(";LS(DOTT,2,CHBLK)"); + else + line = _T(";LS(DASH,2,CHBLK)"); + } else { + if (-9 == watlev) + line = _T(";LS(DOTT,2,CSTLN)"); + else { + switch (watlev) { + case 1: + case 2: + line = _T(";LS(SOLD,2,CSTLN)"); + break; + case 4: + line = _T(";LS(DASH,2,CSTLN)"); + break; + case 3: + case 5: + + default: + line = _T(";LS(DOTT,2,CSTLN)"); + break; + } + } + } + } + } + wrecks02str = wxString(line); + + if (UNKNOWN != valsou) { + if (valsou <= 20) { + wrecks02str.Append(*udwhaz03str); + wrecks02str.Append(*quapnt01str); + wrecks02str.Append(sndfrm02str); + + } else { + // NOTE: ??? same as above ??? + wrecks02str.Append(*udwhaz03str); + wrecks02str.Append(*quapnt01str); + } + } else { + wxString ac; + + if (-9 == watlev) + ac = _T(";AC(DEPVS)"); + else + switch (watlev) { + case 1: + case 2: + ac = _T(";AC(CHBRN)"); + break; + case 4: + ac = _T(";AC(DEPIT)"); + break; + case 5: + case 3: + default: + ac = _T(";AC(DEPVS)"); + break; + } + + wrecks02str.Append(ac); + + wrecks02str.Append(*udwhaz03str); + wrecks02str.Append(*quapnt01str); + } + } + + wrecks02str.Append('\037'); + + char *r = (char *)malloc(wrecks02str.Len() + 1); + strcpy(r, wrecks02str.mb_str()); + + delete udwhaz03str; + delete quapnt01str; + delete quasoustr; + return r; +} + +static wxString _LITDSN01(S57Obj *obj) +// Remarks: In S-57 the light characteristics are held as a series of attributes +// values. The mariner may wish to see a light description text string displayed +// on the screen similar to the string commonly found on a paper chart. This +// conditional procedure, reads the attribute values from the above list of +// attributes and composes a light description string which can be displayed. +// This procedure is provided as a C function which has as input, the above +// listed attribute values and as output, the light description. +{ + // CATLIT, LITCHR, COLOUR, HEIGHT, LITCHR, SIGGRP, SIGPER, STATUS, VALNMR + + char colist[20]; + wxString return_value; +#if 0 + // XXX CATLIT + int catlit = -9; + GetIntAttr(obj, "CATLIT", catlit); + + if(-9 != catlit) + { + } +#endif + + /* + 1: directional function IP 30.1-3; 475.7; + 2: rear/upper light + 3: front/lower light + 4: leading light IP 20.1-3; 475.6; + 5: aero light IP 60; 476.1; + 6: air obstruction light IP 61; 476.2; + 7: fog detector light IP 62; 477; + 8: flood light IP 63; 478.2; + 9: strip light IP 64; 478.5; + 10: subsidiary light IP 42; 471.8; + 11: spotlight + 12: front + 13: rear + 14: lower + 15: upper + 16: moire' effect IP 31; 475.8; + 17: emergency + 18: bearing light 478.1; + 19: horizontally disposed + 20: vertically disposed + */ + + // LITCHR + int litchr = -9; + wxString spost(_T("")); + GetIntAttr(obj, "LITCHR", litchr); + + bool b_grp2 = false; // 2 GRP attributes expected + if (-9 != litchr) { + switch (litchr) { + /* + case 1: return_value.Append(_T("F")); break; + case 2: return_value.Append(_T("Fl")); break; + case 3: return_value.Append(_T("Fl")); break; + case 4: return_value.Append(_T("Q")); break; + case 7: return_value.Append(_T("Iso")); break; + case 8: return_value.Append(_T("Occ")); break; + case 12: return_value.Append(_T("Mo")); break; + */ + + case 1: + return_value.Append(_T("F")); + break; // fixed IP 10.1; + case 2: + return_value.Append(_T("Fl")); + break; // flashing IP 10.4; + case 3: + return_value.Append(_T("LFl")); + break; // long-flashing IP 10.5; + case 4: + return_value.Append(_T("Q")); + break; // quick-flashing IP 10.6; + case 5: + return_value.Append(_T("VQ")); + break; // very quick-flashing IP 10.7; + case 6: + return_value.Append(_T("UQ")); + break; // ultra quick-flashing IP 10.8; + case 7: + return_value.Append(_T("Iso")); + break; // isophased IP 10.3; + case 8: + return_value.Append(_T("Occ")); + break; // occulting IP 10.2; + case 9: + return_value.Append(_T("IQ")); + break; // interrupted quick-flashing IP 10.6; + case 10: + return_value.Append(_T("IVQ")); + break; // interrupted very quick-flashing IP 10.7; + case 11: + return_value.Append(_T("IUQ")); + break; // interrupted ultra quick-flashing IP 10.8; + case 12: + return_value.Append(_T("Mo")); + break; // morse IP 10.9; + case 13: + return_value.Append(_T("F + Fl")); + b_grp2 = true; + break; // fixed/flash IP 10.10; + case 14: + return_value.Append(_T("Fl + LFl")); + b_grp2 = true; + break; // flash/long-flash + case 15: + return_value.Append(_T("Occ + Fl")); + b_grp2 = true; + break; // occulting/flash + case 16: + return_value.Append(_T("F + LFl")); + b_grp2 = true; + break; // fixed/long-flash + case 17: + return_value.Append(_T("Al Occ")); + break; // occulting alternating + case 18: + return_value.Append(_T("Al LFl")); + break; // long-flash alternating + case 19: + return_value.Append(_T("Al Fl")); + break; // flash alternating + case 20: + return_value.Append(_T("Al Grp")); + break; // group alternating + case 21: + return_value.Append(_T("F")); + spost = _T(" (vert)"); + break; // 2 fixed (vertical) + case 22: + return_value.Append(_T("F")); + spost = _T(" (horz)"); + break; // 2 fixed (horizontal) + case 23: + return_value.Append(_T("F")); + spost = _T(" (vert)"); + break; // 3 fixed (vertical) + case 24: + return_value.Append(_T("F")); + spost = _T(" (horz)"); + break; // 3 fixed (horizontal) + case 25: + return_value.Append(_T("Q + LFl")); + b_grp2 = true; + break; // quick-flash plus long-flash + case 26: + return_value.Append(_T("VQ + LFl")); + b_grp2 = true; + break; // very quick-flash plus long-flash + case 27: + return_value.Append(_T("UQ + LFl")); + b_grp2 = true; + break; // ultra quick-flash plus long-flash + case 28: + return_value.Append(_T("Alt")); + break; // alternating + case 29: + return_value.Append(_T("F + Alt")); + b_grp2 = true; + break; // fixed and alternating flashing + + default: + break; + } + } + + int nfirst_grp = -1; + if (b_grp2) { + wxString ret_new; + nfirst_grp = return_value.Find(_T(" ")); + if (wxNOT_FOUND != nfirst_grp) { + ret_new = return_value.Mid(0, nfirst_grp); + ret_new.Append(_T("(?)")); + ret_new.Append(return_value.Mid(nfirst_grp)); + return_value = ret_new; + nfirst_grp += 1; + } + } + + // SIGGRP, (c)(c) ... + char grp_str[20] = {'\0'}; + GetStringAttr(obj, "SIGGRP", grp_str, 19); + if (strlen(grp_str)) { + wxString ss(grp_str, wxConvUTF8); + + if (b_grp2) { + wxStringTokenizer tkz(ss, _T("()")); + + int n_tok = 0; + while (tkz.HasMoreTokens() && (n_tok < 2)) { + wxString s = tkz.GetNextToken(); + if (s.Len()) { + if ((n_tok == 0) && (nfirst_grp > 0)) { + return_value[nfirst_grp] = s[0]; + } else { + if (s != _T("1")) { + return_value.Append(_T("(")); + return_value.Append(s); + return_value.Append(_T(")")); + } + } + + n_tok++; + } + } + } else { + if (ss != _T("(1)")) return_value.Append(ss); + } + } + + // COLOUR, + char col_str[20] = {'\0'}; + + // Don't show for sectored lights since we are only showing one of the + // sectors. + double sectrTest; + bool hasSectors = GetDoubleAttr(obj, "SECTR1", sectrTest); + + if (!hasSectors) { + GetStringAttr(obj, "COLOUR", col_str, 19); + + int n_cols = 0; + if (strlen(col_str)) n_cols = _parseList(col_str, colist, sizeof(colist)); + + if (n_cols) return_value.Append(_T(" ")); + + for (int i = 0; i < n_cols; i++) { + switch (colist[i]) { + case 1: + return_value.Append(_T("W")); + break; + case 3: + return_value.Append(_T("R")); + break; + case 4: + return_value.Append(_T("G")); + break; + case 6: + return_value.Append(_T("Y")); + break; + default: + break; + } + } + } + + /* + 1: white IP 11.1; 450.2-3; + 2: black + 3: red IP 11.2; 450.2-3; + 4: green IP 11.3; 450.2-3; + 5: blue IP 11.4; 450.2-3; + 6: yellow IP 11.6; 450.2-3; + 7: grey + 8: brown + 9: amber IP 11.8; 450.2-3; + 10: violet IP 11.5; 450.2-3; + 11: orange IP 11.7; 450.2-3; + 12: magenta + 13: pink + */ + + // SIGPER, xx.xx + double sigper = UNKNOWN; + GetDoubleAttr(obj, "SIGPER", sigper); + + if (UNKNOWN != sigper) { + wxString s; + if (fabs(wxRound(sigper) - sigper) > 0.01) + s.Printf(_T("%4.1fs"), sigper); + else + s.Printf(_T("%2.0fs"), sigper); + + s.Trim(false); // remove leading spaces + s.Prepend(_T(" ")); + return_value.Append(s); + } + + // HEIGHT, xxx.x + double height = UNKNOWN; + GetDoubleAttr(obj, "HEIGHT", height); + + if (UNKNOWN != height) { + wxString s; + if (ps52plib->m_nHeightUnitDisplay == 1) { // feet + s.Printf(_T("%3.0fft"), height * (1.0 / 0.3048)); + } else { // meters + s.Printf(_T("%3.0fm"), height); + } + + s.Trim(false); // remove leading spaces + s.Prepend(_T(" ")); + return_value.Append(s); + } + + // VALNMR, xx.x + double valnmr = UNKNOWN; + GetDoubleAttr(obj, "VALNMR", valnmr); + + if (UNKNOWN != valnmr && !hasSectors) { + wxString s; + s.Printf(_T("%2.0fNm"), valnmr); + s.Trim(false); // remove leading spaces + s.Prepend(_T(" ")); + return_value.Append(s); + } + +#if 0 + + // STATUS, + gstr = S57_getAttVal(geo, "STATUS"); + if (NULL != gstr) + g_string_append(litdsn01, gstr->str); + + /* + 1: permanent + 2: occasional IP 50; 473.2; + 3: recommended IN 10; 431.1; + 4: not in use IL 14, 44; 444.7; + 5: periodic/intermittent IC 21; IQ 71; 353.3; 460.5; + 6: reserved IN 12.9; + 7: temporary IP 54; + 8: private IQ 70; + 9: mandatory + 10: destroyed/ruined + 11: extinguished + 12: illuminated + 13: historic + 14: public + 15: synchronized + 16: watched + 17: un-watched + 18: existence doubtful + */ + +#endif + + return_value.Append(spost); // add any final modifiers + + return return_value; +} + +static void *SYMINS01(void *param) { + ObjRazRules *rzRules = static_cast(param); + if (!rzRules || !rzRules->obj) return NULL; + + char symins[258] = {'\0'}; + GetStringAttr(rzRules->obj, "SYMINS", symins, sizeof(symins) - 2); + + size_t len = strlen(symins); + symins[len] = '\037'; + symins[len + 1] = '\0'; + + char *r = static_cast(malloc(len + 2)); + if (!r) return NULL; + + memcpy(r, symins, len + 2); + return r; +} + +//-------------------------------- +// +// JUMP TABLE SECTION +// +//-------------------------------- +Cond condTable[] = { + {"CLRLIN01", CLRLIN01}, + {"DATCVR01", DATCVR01}, + {"DATCVR01", DATCVR01}, + {"DEPARE01", DEPARE01}, + {"DEPARE02", DEPARE01}, // new in PLIB 3_3, opencpn defaults to DEPARE01 + {"DEPCNT02", DEPCNT02}, + {"DEPVAL01", DEPVAL01}, + {"LEGLIN02", LEGLIN02}, + {"LIGHTS05", LIGHTS05}, // new in PLIB 3_3, replaces LIGHTS04 + {"LITDSN01", LITDSN01}, + {"OBSTRN04", OBSTRN04}, + {"OWNSHP02", OWNSHP02}, + {"PASTRK01", PASTRK01}, + {"QUAPOS01", QUAPOS01}, + {"QUALIN01", QUALIN01}, + {"QUAPNT01", QUAPNT01}, + {"SLCONS03", SLCONS03}, + {"RESARE02", RESARE02}, + {"RESTRN01", RESTRN01}, + // {"RESCSP01",RESCSP01}, + {"SEABED01", SEABED01}, + // {"SNDFRM02",SNDFRM02}, + {"SOUNDG02", SOUNDG02}, + {"TOPMAR01", TOPMAR01}, + {"UDWHAZ03", UDWHAZ03}, + {"VESSEL01", VESSEL01}, + {"VRMEBL01", VRMEBL01}, + {"WRECKS02", WRECKS02}, + {"SOUNDG03", SOUNDG03}, // special case for MPS + {"SYMINS01", SYMINS01}, // Container for Virtual AIS ATONS, special case + {"########", NULL}}; + +#if 0 +// S52CS.c : Conditional Symbologie procedure 3.2 (CS) +// +// Project: OpENCview + +/* + This file is part of the OpENCview project, a viewer of ENC + Copyright (C) 2000-2004 Sylvain Duclos sduclos@users.sourceforgue.net + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +// NOTE: remarks commenting each CS are extracted from pslb03_2.pdf (sec. 12) + +// FIXME: DEPCNT02: call DB for area DEPARE & DRGARE that intersect this line +// FIXME:_DEPVAL01: call DB for area DEPARE & UNSARE that intersect this area +// FIXME:_UDWHAZ03: call DB for area DRGARE & DEPARE that intersect this point/area + +#include "S52CS.h" + +#include "S52utils.h" // PRINTF() + +#include // atof() +#include // fabsf(), HUGE_VAL +#include // isdigit() + +#define UNKNOWN 1e6 // HUGE_VAL // INFINITY/NAN + +#define COALNE 30 // Coastline +#define DEPARE 42 // Depth area +#define DEPCNT 43 // Depth contour +#define DRGARE 46 // Dredged area +#define UWTROC 153 // Underwater rock / awash rock +#define WRECKS 159 // Wreck + +// point list name +#define LIGHTLIST 0 +#define SECTRLIST 1 +#define FLOATLIST 2 // floating platform +#define RIGIDLIST 3 // rigid platform +//static GPtrArray *_lightList = NULL; +//static GPtrArray *_sectrList = NULL; +//static GPtrArray *_flaotList = NULL; +static GPtrArray *_ptList[] = {NULL, NULL, NULL, NULL}; + +int S52_state = 1; + +// size of attributes value list buffer +#define LISTSIZE 16 // list size + +#define version "3.2.0" +char *S52_CS_version() +{ + return version; +} + +int S52_CS_init() +{ + _ptList[LIGHTLIST] = g_ptr_array_new(); + _ptList[SECTRLIST] = g_ptr_array_new(); + _ptList[FLOATLIST] = g_ptr_array_new(); + _ptList[RIGIDLIST] = g_ptr_array_new(); + + return 1; +} + +int S52_CS_done() +{ + g_ptr_array_free(_ptList[LIGHTLIST], TRUE); + g_ptr_array_free(_ptList[SECTRLIST], TRUE); + g_ptr_array_free(_ptList[FLOATLIST], TRUE); + g_ptr_array_free(_ptList[RIGIDLIST], TRUE); + + return 1; +} + +int S52_CS_setPtPos(S57_geo *geoData, char *name) +{ + printf("name = %s\n", name); + + if (POINT_T == S57_getObjtype(geoData)) { + + // set floating platform + if ((0==strncmp(name, "LITFLT", 6)) || + (0==strncmp(name, "LITVES", 6)) || + (0==strncmp(name, "BOY", 3))) + g_ptr_array_add(_ptList[FLOATLIST], (gpointer) geoData); + + // set rigid platform + if (0==strncmp(name, "BCN", 3)) + g_ptr_array_add(_ptList[RIGIDLIST], (gpointer) geoData); + } + + return 1; +} + +static int _atPtPos(S57_geo *geoNew, int listNm) +// return TRUE if there is a light at this position +// or if its an extended arc radius else FALSE +{ + int i; + GPtrArray *curntList = _ptList[listNm]; + + for (i=0; ilen; i++) { + S57_geo *geoOld = g_ptr_array_index(curntList, i); + + if (S57_samePtPos(geoNew, geoOld)) { + + if (SECTRLIST != listNm) + return TRUE; + else { + // check for extend arc radius + GString *Asectr1str = S57_getAttVal(geoOld, "SECTR1"); + GString *Asectr2str = S57_getAttVal(geoOld, "SECTR2"); + GString *Bsectr1str = S57_getAttVal(geoNew, "SECTR1"); + GString *Bsectr2str = S57_getAttVal(geoNew, "SECTR2"); + + // check present + if (NULL == Asectr1str || + NULL == Asectr1str || + NULL == Asectr1str || + NULL == Asectr1str) + return FALSE; + + { + double Asectr1 = atof(Asectr1str->str); + double Asectr2 = atof(Asectr2str->str); + double Bsectr1 = atof(Bsectr1str->str); + double Bsectr2 = atof(Bsectr2str->str); + double Asweep = (Asectr1 > Asectr2) ? + Asectr2-Asectr1+360 : Asectr2-Asectr1; + double Bsweep = (Bsectr1 > Bsectr2) ? + Bsectr2-Bsectr1+360 : Bsectr2-Bsectr1; + + // check sector overlap + if (Asectr2<=Bsectr1 || Asectr1>=Bsectr2) { + if (Asweep == Bsweep) { + g_string_truncate(Bsectr2str, 0); + g_string_sprintf(Bsectr2str, "%f",Bsectr2-1); + S57_setAtt(geoNew, "SECTR2", Bsectr2str->str); + } + + return FALSE; + } + + // check if other sector larger + if (Asweep >= Bsweep) + return TRUE; + } + } + } + } + + return FALSE; +} + +static int _setPtPos(S57_geo *geo, int listNm) +// TRUE if set new position of a light +// else FALSE (ie there is a light at this position) +{ + GPtrArray *curntList = _ptList[listNm]; + + if (_atPtPos(geo, listNm)) + return 1; + else + g_ptr_array_add(curntList, (gpointer) geo); + + return 0; +} + +static int _parseList(const char *str, char *buf) +// Put a string of comma delimited number in an array (buf). +// Return: the number of value in buf. +// Assume: - number < 256, +// - list size less then LISTSIZE-1 . +// Note: buf is \0 terminated for strpbrk(). +{ + int i = 0; + + if (NULL != str && *str != '\0') { + do { + if ( i>= LISTSIZE-1) { + PRINTF("OVERFLOW --value in list lost!!\n"); + break; + } + + /* + if (255 < (unsigned char) atoi(str)) { + PRINTF("value overflow (>255)\n"); + exit(0); + } + */ + + buf[i++] = (unsigned char) atoi(str); + + while(isdigit(*str++)); // next + //while( g_ascii_isdigit(c)); // next + + } while(*str++ != '\0'); // skip ',' or exit + } + + buf[i] = '\0'; + + return i; +} + + +static GString *CLRLIN01 (S57_geo *geo) +// Remarks: A clearing line shows a single arrow head at one of its ends. The direction +// of the clearing line must be calculated from its line object in order to rotate +// the arrow head symbol and place it at the correct end. This cannot be +// achieved with a complex linestyle since linestyle symbols cannot be sized +// to the length of the clearing line. Instead a linestyle with a repeating pattern +// of arrow symbols had to be used which does not comply with the required +// symbolization. +{ + + PRINTF("Mariner's object not drawn\n"); + + return NULL; +} + +static GString *DATCVR01 (S57_geo *geo) +// Remarks: This conditional symbology procedure describes procedures for: +// - symbolizing the limit of ENC coverage; +// - symbolizing navigational purpose boundaries ("scale boundarie"); and +// - indicating overscale display. +// +// Note that the mandatory meta object CATQUA is symbolized by the look-up table. +// +// Because the methods adopted by an ECDIS to meet the IMO and IHO requirements +// listed on the next page will depend on the manufacturer's software, and cannot be +// described in terms of a flow chart in the same way as other conditional procedures, +// this procedure is in the form of written notes. +{ + GString *datcvr01 = NULL; + + /////////////////////// + // 1- REQUIREMENT + // (IMO/IHO specs. explenation) + + /////////////////////// + // 2- ENC COVERAGE + // + // 2.1- Limit of ENC coverage + //datcvr01 = g_string_new(";OP(3OD11060);LC(HODATA01)"); + // FIXME: get cell extend + + // 2.2- No data areas + // This can be done outside of CS (ie when clearing the screen in Mesa) + // FIXME: ";OP(0---);AC(NODATA)" + // FIXME: set geo to cover earth (!) + + ////////////////////// + // 3- SCALE BOUNDARIES + // + // 3.1- Chart scale boundaties + // FIXME; + //g_string_append(datcvr01, ";LS(SOLD,1,CHGRD)"); + // -OR- LC(SCLBDYnn) (?) + // + // ;OP(3OS21030) + + // 3.2- Graphical index of navigational purpose + // FIXME: draw extent of available SENC in DB + + ////////////////////// + // 4- OVERSCALE + // + // FIXME: get meta date CSCL of DSPM field + // FIXME: get object M_CSCL or CSCALE + // + // 4.1- Overscale indication + // FIXME: compute, scale = [denominator of the compilation scale] / + // [denominator of the display scale] + // FIXME: draw overscale indication (ie TX("X%3.1f",scale)) + // + // 4.2- Ovescale area at a chart scale boundary + // FIXME: test if next chart is over scale (ie going from large scale chart + // to a small scale chart) + // FIXME: draw AP(OVERSC01) on overscale part of display + //g_string(";OP(3OS21030)"); + + // + // 4.3- Larger scale data available + // FIXME: display indication of better scale available (?) + + + PRINTF("not computed\n"); + + return datcvr01; +} + +static GString *_SEABED01(double drval1, double drval2); +static GString *_RESCSP01(S57_geo *geo); +static GString *DEPARE01 (S57_geo *geo) +// Remarks: An object of the class "depth area" is coloured and covered with fill patterns +// according to the mariners selections of shallow contour, safety contour and +// deep contour. This requires a decision making process provided by the sub-procedure +// "SEABED01" which is called by this symbology procedure. +// Objects of the class "dredged area" are handled by this routine as well to +// ensure a consistent symbolization of areas that represent the surface of the +// seabed. +{ + GString *depare01 = NULL; + int objl = 0; + GString *objlstr = NULL; + GString *drval1str = S57_getAttVal(geo, "DRVAL1"); + double drval1 = UNKNOWN; + GString *drval2str = S57_getAttVal(geo, "DRVAL2"); + double drval2 = UNKNOWN; + + if (NULL == drval1str || NULL == drval2str) { + PRINTF("ERROR: drval1 or drval2 should have the value UNKNOWN\n"); + return NULL; + } + + drval1 = (NULL == drval1str) ? -1.0 : atof(drval1str->str); + drval2 = (NULL == drval2str) ? drval1+0.01 : atof(drval2str->str); + + depare01 = _SEABED01(drval1, drval2); + + objlstr = S57_getAttVal(geo, "OBJL"); + objl = (NULL == objlstr) ? 0 : atoi(objlstr->str); + + if (DRGARE == objl) { + g_string_append(depare01, ";AP(DRGARE01)"); + g_string_append(depare01, ";LS(DASH,1,CHGRF)"); + + if (NULL != S57_getAttVal(geo, "RESTRN")) { + GString *rescsp01 = _RESCSP01(geo); + if (NULL != rescsp01) { + g_string_append(depare01, rescsp01->str); + g_string_free(rescsp01, TRUE); + } + } + + } + + return depare01; +} + +static GString *_SNDFRM02(S57_geo *geo, double depth_value); +static GString *DEPCNT02 (S57_geo *geo) +// Remarks: An object of the class "depth contour" or "line depth area" is highlighted and must +// be shown under all circumstances if it matches the safety contour depth value +// entered by the mariner (see IMO PS 3.6). But, while the mariner is free to enter any +// safety contour depth value that he thinks is suitable for the safety of his ship, the +// SENC only contains a limited choice of depth contours. This symbology procedure +// determines whether a contour matches the selected safety contour. If the selected +// safety contour does not exist in the data, the procedure will default to the next deeper +// contour. The contour selected is highlighted as the safety contour and put in +// DISPLAYBASE. The procedure also identifies any line segment of the spatial +// component of the object that has a "QUAPOS" value indicating unreliable +// positioning, and symbolizes it with a double dashed line. +// +// Note: Depth contours are not normally labeled. The ECDIS may provide labels, on demand +// only as with other text, or provide the depth value on cursor picking +{ + GString *depcnt02 = NULL; + int safe = FALSE; // initialy not a safety contour + GString *objlstr = NULL; + int objl = 0; + GString *quaposstr = NULL; + int quapos = 0; + double depth_value; + + objlstr = S57_getAttVal(geo, "OBJL"); + objl = (NULL == objlstr) ? 0 : atoi(objlstr->str); + + if (DEPARE==objl && LINES_T==S57_getObjtype(geo)) + { + GString *drval1str = S57_getAttVal(geo, "DRVAL1"); + double drval1 = (NULL == drval1str) ? 0.0 : atof(drval1str->str); + GString *drval2str = S57_getAttVal(geo, "DRVAL2"); + double drval2 = (NULL == drval2str) ? drval1 : atof(drval2str->str); + + if (drval1 <= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) + { + if (drval2 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) + safe = TRUE; + } + else + { + if (1 == S52_state) + return NULL; + else + { + S57_geo *geoTmp = geo; + + // get area DEPARE & DRGARE that intersect this line + while (NULL != (geoTmp = S57_nextObj(geoTmp))) { + drval1str = S57_getAttVal(geoTmp, "DRVAL1"); + drval1 = (NULL == drval1str) ? 0.0 : atof(drval1str->str); + + if (NULL == drval1str) { + safe = TRUE; + break; + } + + if (drval1 < S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) { + safe = TRUE; + break; + } + } + // debug trace + //if (safe) PRINTF("** DEPARE: SAFE FOUND**\n"); + } + } + + depth_value = drval1; + + } + else + { + // continuation A (DEPCNT) + GString *valdcostr = S57_getAttVal(geo, "VALDCO"); + double valdco = (NULL == valdcostr) ? 0.0 : atof(valdcostr->str); + + depth_value = valdco; + + if (valdco == S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) + safe = TRUE; // this is useless !?!? + else + { + if (valdco > S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) { + if (1 == S52_state) + return NULL; + else { + S57_geo *geoTmp = geo; + + // get area DEPARE & DRGARE that intersect this line + while (NULL != (geoTmp = S57_nextObj(geoTmp))){ + GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1"); + double drval1 = (NULL == drval1str) ? 0.0 : atof(drval1str->str); + + if (NULL == drval1str) { + safe = TRUE; + break; + } + + if (drval1 < S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) { + safe = TRUE; + break; + } + } + // debug trace + //if (safe) PRINTF("** DEPCN: SAFE FOUND**\n"); + } + } + } + } + + // Continuation B + quaposstr = S57_getAttVal(geo, "QUAPOS"); + if (NULL != quaposstr) { + quapos = atoi(quaposstr->str); + if ( 2 <= quapos && quapos < 10) { + if (safe) + depcnt02 = g_string_new(";LS(DASH,2,DEPSC)"); + else + depcnt02 = g_string_new(";LS(DASH,1,DEPCN)"); + } + } else { + if (safe) + depcnt02 = g_string_new(";LS(SOLD,2,DEPSC)"); + else + depcnt02 = g_string_new(";LS(SOLD,1,DEPCN)"); + } + + if (safe) { + S57_setAtt(geo, "SCAMIN", "INFINITE"); + depcnt02 = g_string_prepend(depcnt02, ";OP(8OD13010)"); + } else + depcnt02 = g_string_prepend(depcnt02, ";OP(---33020)"); + + // facultative in S-52 + //if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) { + // GString *sndfrm02 = _SNDFRM02(geo, depth_value); + // depcnt02 = g_string_append(depcnt02, sndfrm02->str); + // g_string_free(sndfrm02, TRUE); + //} + + // debug + //PRINTF("depth= %f\n", depth_value); + + S57_unlinkObj(geo); + + return depcnt02; +} + +static double _DEPVAL01(S57_geo *geo, double least_depth) +// Remarks: S-57 Appendix B1 Annex A requires in Section 6 that areas of rocks be +// encoded as area obstruction, and that area OBSTRNs and area WRECKS +// be covered by either group 1 object DEPARE or group 1 object UNSARE. +// If the value of the attribute VALSOU for an area OBSTRN or WRECKS +// is missing, the DRVAL1 of an underlying DEPARE is the preferred default +// for establishing a depth value. This procedure either finds the shallowest +// DRVAL1 of the one or more underlying DEPAREs, or returns an +// "unknown"" depth value to the main procedure for the next default +// procedure. + +// NOTE: UNSARE test is useless since least_depth is already UNKNOWN +{ + least_depth = UNKNOWN; + + + S57_geo *geoTmp = geo; + + // NOTE: the geo list is unchange (_UDWHAZ03 will unlink geo) + while (NULL != (geoTmp = S57_nextObj(geoTmp))) { + GString *objlstr = S57_getAttVal(geoTmp, "OBJL"); + int objl = (NULL == objlstr) ? 0 : atoi(objlstr->str); + + // get area DEPARE that intersect this area + if (DEPARE==objl && LINES_T==S57_getObjtype(geo)) { + GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1"); + double drval1 = (NULL == drval1str) ? 9.0 : atof(drval1str->str); + + if (NULL != drval1str) { + if (UNKNOWN==least_depth || least_depthstr); + + if ( NULL != catlitstr) { + _parseList(catlitstr->str, catlit); + + // FIXME: OR vs AND/OR + if (strpbrk(catlit, "\010\013")) { + g_string_append(lights05, ";SY(LIGHTS82)"); + return lights05; + } + + if (strpbrk(catlit, "\011")) { + g_string_append(lights05, ";SY(LIGHTS81)"); + return lights05; + } + + if (strpbrk(catlit, "\001\020")) { + orientstr = S57_getAttVal(geo, "ORIENT"); + if (NULL != orientstr) { + // FIXME: create a geo object (!?) LINE of lenght VALNMR + // using ORIENT (from seaward) & POINT_T position + g_string_append(lights05, ";LS(DASH,1,CHBLK)"); + } + } + } + + // Continuation A + colourstr = S57_getAttVal(geo, "COLOUR"); + if (NULL != colourstr) + _parseList(colourstr->str, colist); + else { + colist[0] = '\014'; // maganta (12) + colist[1] = '\000'; + } + + sectr1str = S57_getAttVal(geo, "SECTR1"); + sectr1 = (NULL == sectr1str) ? 0.0 : atof(sectr1str->str); + sectr2str = S57_getAttVal(geo, "SECTR2"); + sectr2 = (NULL == sectr2str) ? 0.0 : atof(sectr2str->str); + + if (NULL==sectr1str || NULL==sectr2str) { + // not a sector light + char *sym; + + if (1==S52_state) { + _setPtPos(geo, LIGHTLIST); + g_string_free(lights05, TRUE); + return NULL; + } else + flare_at_45 = _atPtPos(geo, LIGHTLIST); + + sym = _selSYcol(colist); + + if (strpbrk(catlit, "\001\020")) { + if (NULL != orientstr){ + g_string_append(lights05, sym); + g_string_sprintfa(lights05, ",%s)", orientstr->str); + g_string_append(lights05, ";TE('%03.0lf deg','ORIENT',3,3,3,'15110',3,1,CHBLK,23)" ); + } else + g_string_append(lights05, ";SY(QUSMRK1)"); + } else { + g_string_append(lights05, sym); + if (flare_at_45) + g_string_append(lights05, ",145)"); + else + g_string_append(lights05, ",135)"); + } + + if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) { + GString *litdsn01 = _LITDSN01(geo); + if (NULL != litdsn01){ + g_string_append(lights05, ";TX('"); + g_string_append(lights05, litdsn01->str); + g_string_free(litdsn01, TRUE); + + if (flare_at_45) + g_string_append(lights05, "',3,3,3,'15110',2,-1,CHBLK,23)" ); + else + g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)" ); + } + } + + return lights05; + } + + // Continuation B --sector light + if (NULL == sectr1str) { + sectr1 = 0.0; + sectr2 = 0.0; + } else + sweep = (sectr1 > sectr2) ? sectr2-sectr1+360 : sectr2-sectr1; + + + if (sweep<1.0 || sweep==360.0) { + // handle all round light + char *sym = _selSYcol(colist);; + + g_string_append(lights05, sym); + g_string_append(lights05, ",135)"); + + if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) { + GString *litdsn01 = _LITDSN01(geo); + if (NULL != litdsn01) { + g_string_append(lights05, ";TX('"); + g_string_append(lights05, litdsn01->str); + g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)" ); + g_string_free(litdsn01, TRUE); + } + + } + + return lights05; + } + + // scan for other lights with sector overlap at this position + // compute light sector radius acording to other sector + if (1 == S52_state) { + _setPtPos(geo, SECTRLIST); + g_string_free(lights05, TRUE); + return NULL; + } else { + extend_arc_radius = _atPtPos(geo, SECTRLIST); + + // passe value via attribs to _renderAC + if (extend_arc_radius) + // FIXME: draw radius 25 mm + S57_setAtt(geo, "extend_arc_radius", "Y"); + else + // FIXME: draw radius 20 mm + S57_setAtt(geo, "extend_arc_radius", "N"); + } + + // setup sector + { + char litvis[LISTSIZE] = {'\0'}; // list visibility + GString *litvisstr = S57_getAttVal(geo, "LITVIS"); + + // sector leg --logic is _renderLS() + g_string_append(lights05, ";LS(DASH,1,CHBLK)"); + + // get light vis. + if (NULL != litvisstr) _parseList(litvisstr->str, litvis); + + // faint light + // FIXME: spec say OR (ie 1 number) the code is AND/OR + if (strpbrk(litvis, "\003\007\010")) { + // NOTE: LS(DASH,1,CHBLK) + // pass flag to _renderAC() + g_string_append(lights05, ";AC(CHBLK)"); + S57_setAtt(geo, "faint_light", "Y"); + + } else { + // set arc colour + char *sym = ";AC(CHMGD)"; // other + + // max 1 color + if ('\0' == colist[1]) { + if (strpbrk(colist, "\003")) + sym = ";AC(LITRD)"; + else if (strpbrk(colist, "\004")) + sym = ";AC(LITGN)"; + else if (strpbrk(colist, "\001\006\013")) + sym = ";AC(LITYW)"; + } else { + // max 2 color + if ('\0' == colist[2]) { + if (strpbrk(colist, "\001") && strpbrk(colist, "\003")) + sym = ";AC(LITRD)"; + else if (strpbrk(colist, "\001") && strpbrk(colist, "\004")) + sym = ";AC(LITGN)"; + } + } + + g_string_append(lights05, sym); + } + } + + return lights05; +} + +static GString *_LITDSN01(S57_geo *geo) +// Remarks: In S-57 the light characteristics are held as a series of attributes values. The +// mariner may wish to see a light description text string displayed on the +// screen similar to the string commonly found on a paper chart. This +// conditional procedure, reads the attribute values from the above list of +// attributes and composes a light description string which can be displayed. +// This procedure is provided as a C function which has as input, the above +// listed attribute values and as output, the light description. +{ + GString *litdsn01 = g_string_new(""); + GString *gstr = NULL; // tmp + + // FIXME: need grammar to create light's text + + // CATLIT, LITCHR, COLOUR, HEIGHT, LITCHR, SIGGRP, SIGPER, STATUS, VALNMR + + // CATLIT + gstr = S57_getAttVal(geo, "CATLIT"); + if (NULL != gstr) + g_string_append(litdsn01, gstr->str); + + /* + 1: directional function IP 30.1-3; 475.7; + 2: rear/upper light + 3: front/lower light + 4: leading light IP 20.1-3; 475.6; + 5: aero light IP 60; 476.1; + 6: air obstruction light IP 61; 476.2; + 7: fog detector light IP 62; 477; + 8: flood light IP 63; 478.2; + 9: strip light IP 64; 478.5; + 10: subsidiary light IP 42; 471.8; + 11: spotlight + 12: front + 13: rear + 14: lower + 15: upper + 16: moire' effect IP 31; 475.8; + 17: emergency + 18: bearing light 478.1; + 19: horizontally disposed + 20: vertically disposed + */ + + // LITCHR + gstr = S57_getAttVal(geo, "LITCHR"); + if (NULL != gstr) + g_string_append(litdsn01, gstr->str); + + /* + 1: fixed IP 10.1; + 2: flashing IP 10.4; + 3: long-flashing IP 10.5; + 4: quick-flashing IP 10.6; + 5: very quick-flashing IP 10.7; + 6: ultra quick-flashing IP 10.8; + 7: isophased IP 10.3; + 8: occulting IP 10.2; + 9: interrupted quick-flashing IP 10.6; + 10: interrupted very quick-flashing IP 10.7; + 11: interrupted ultra quick-flashing IP 10.8; + 12: morse IP 10.9; + 13: fixed/flash IP 10.10; + 14: flash/long-flash + 15: occulting/flash + 16: fixed/long-flash + 17: occulting alternating + 18: long-flash alternating + 19: flash alternating + 20: group alternating + 21: 2 fixed (vertical) + 22: 2 fixed (horizontal) + 23: 3 fixed (vertical) + 24: 3 fixed (horizontal) + 25: quick-flash plus long-flash + 26: very quick-flash plus long-flash + 27: ultra quick-flash plus long-flash + 28: alternating + 29: fixed and alternating flashing + */ + + // COLOUR, + gstr = S57_getAttVal(geo, "COLOUR"); + if (NULL != gstr) + g_string_append(litdsn01, gstr->str); + + /* + 1: white IP 11.1; 450.2-3; + 2: black + 3: red IP 11.2; 450.2-3; + 4: green IP 11.3; 450.2-3; + 5: blue IP 11.4; 450.2-3; + 6: yellow IP 11.6; 450.2-3; + 7: grey + 8: brown + 9: amber IP 11.8; 450.2-3; + 10: violet IP 11.5; 450.2-3; + 11: orange IP 11.7; 450.2-3; + 12: magenta + 13: pink + */ + + // HEIGHT, xxx.x + gstr = S57_getAttVal(geo, "HEIGHT"); + if (NULL != gstr) + g_string_append(litdsn01, gstr->str); + + + // SIGGRP, (c)(c) ... + gstr = S57_getAttVal(geo, "SIGGRP"); + if (NULL != gstr) + g_string_append(litdsn01, gstr->str); + + + // SIGPER, xx.xx + gstr = S57_getAttVal(geo, "SIGPER"); + if (NULL != gstr) + g_string_append(litdsn01, gstr->str); + + + // STATUS, + gstr = S57_getAttVal(geo, "STATUS"); + if (NULL != gstr) + g_string_append(litdsn01, gstr->str); + + /* + 1: permanent + 2: occasional IP 50; 473.2; + 3: recommended IN 10; 431.1; + 4: not in use IL 14, 44; 444.7; + 5: periodic/intermittent IC 21; IQ 71; 353.3; 460.5; + 6: reserved IN 12.9; + 7: temporary IP 54; + 8: private IQ 70; + 9: mandatory + 10: destroyed/ruined + 11: extinguished + 12: illuminated + 13: historic + 14: public + 15: synchronized + 16: watched + 17: un-watched + 18: existence doubtful + */ + + // VALNMR, xx.x + gstr = S57_getAttVal(geo, "VALNMR"); + if (NULL != gstr) + g_string_append(litdsn01, gstr->str); + + + PRINTF("FIXME: lights description not translated into text\n"); + + return litdsn01; +} + +static GString *_UDWHAZ03(S57_geo *geo, double depth_value); +static GString *_QUAPNT01(S57_geo *geo); + +static GString *OBSTRN04 (S57_geo *geo) +// Remarks: Obstructions or isolated underwater dangers of depths less than the safety +// contour which lie within the safe waters defined by the safety contour are +// to be presented by a specific isolated danger symbol and put in IMO +// category DISPLAYBASE (see (3), App.2, 1.3). This task is performed +// by the sub-procedure "UDWHAZ03" which is called by this symbology +// procedure. Objects of the class "under water rock" are handled by this +// routine as well to ensure a consistent symbolization of isolated dangers on +// the seabed. +{ + GString *obstrn04str = g_string_new(""); + GString *sndfrm02str = NULL; + GString *udwhaz03str = NULL; + GString *valsoustr = S57_getAttVal(geo, "VALSOU"); + double valsou = UNKNOWN; + double depth_value = UNKNOWN; + double least_depth = UNKNOWN; + + // exit if not in drawing state + if (1 == S52_state) + return NULL; + + if (NULL != valsoustr) { + valsou = atof(valsoustr->str); + depth_value = valsou; + sndfrm02str = _SNDFRM02(geo, depth_value); + } else { + if (AREAS_T == S57_getObjtype(geo)) + least_depth = _DEPVAL01(geo, least_depth); + + if (UNKNOWN != least_depth) { + GString *catobsstr = S57_getAttVal(geo, "CATOBS"); + GString *watlevstr = S57_getAttVal(geo, "WATLEV"); + + if (NULL != catobsstr && '6' == *catobsstr->str) + depth_value = 0.01; + else + if (NULL == watlevstr) // default + depth_value = -15.0; + else { + switch (*watlevstr->str){ + case '5': depth_value = 0.0 ; break; + case '3': depth_value = 0.01; break; + case '4': + case '1': + case '2': + default : depth_value = -15.0 ; break; + } + } + } else + depth_value = least_depth; + } + + udwhaz03str = _UDWHAZ03(geo, depth_value); + + if (POINT_T == S57_getObjtype(geo)) { + // Continuation A + int sounding = FALSE; + GString *quapnt01str = _QUAPNT01(geo); + + if (NULL != udwhaz03str){ + g_string_append(obstrn04str, udwhaz03str->str); + if (NULL != quapnt01str) + g_string_append(obstrn04str, quapnt01str->str); + + if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE); + if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE); + if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE); + + return obstrn04str; + } + + if (UNKNOWN != valsou) { + if (valsou <= 20.0) { + GString *objlstr = S57_getAttVal(geo, "OBJL"); + int objl = (NULL == objlstr)? 0 : atoi(objlstr->str); + GString *watlevstr = S57_getAttVal(geo, "WATLEV"); + + if (UWTROC == objl) { + if (NULL == watlevstr) { // default + g_string_append(obstrn04str, ";SY(DANGER01)"); + sounding = TRUE; + } else { + switch (*watlevstr->str){ + case '3': g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE ; break; + case '4': + case '5': g_string_append(obstrn04str, ";SY(UWTROC04)"); sounding = FALSE; break; + default : g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE ; break; + } + } + } else { // OBSTRN + if (NULL == watlevstr) { // default + g_string_append(obstrn04str, ";SY(DANGER01)"); + sounding = TRUE; + } else { + switch (*watlevstr->str) { + case '1': + case '2': g_string_append(obstrn04str, ";SY(OBSTRN11)"); sounding = FALSE; break; + case '3': g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE; break; + case '4': + case '5': g_string_append(obstrn04str, ";SY(DANGER03)"); sounding = TRUE; break; + default : g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE; break; + } + } + } + } else { // valsou > 20.0 + g_string_append(obstrn04str, ";SY(DANGER02)"); + sounding = FALSE; + } + + } else { // NO valsou + GString *objlstr = S57_getAttVal(geo, "OBJL"); + int objl = (NULL == objlstr)? 0 : atoi(objlstr->str); + GString *watlevstr = S57_getAttVal(geo, "WATLEV"); + + if (UWTROC == objl) { + if (NULL == watlevstr) // default + g_string_append(obstrn04str, ";SY(UWTROC04)"); + else { + if ('3' == *watlevstr->str) + g_string_append(obstrn04str, ";SY(UWTROC03)"); + else + g_string_append(obstrn04str, ";SY(UWTROC04)"); + } + + } else { // OBSTRN + if ( NULL == watlevstr) // default + g_string_append(obstrn04str, ";SY(OBSTRN01)"); + else { + switch (*watlevstr->str) { + case '1': + case '2': g_string_append(obstrn04str, ";SY(OBSTRN11)"); break; + case '3': g_string_append(obstrn04str, ";SY(OBSTRN01)"); break; + case '4': + case '5': + default : g_string_append(obstrn04str, ";SY(OBSTRN01)"); break; + } + } + } + + } + + if (sounding && NULL != sndfrm02str) + g_string_append(obstrn04str, sndfrm02str->str); + + if (NULL != quapnt01str) + g_string_append(obstrn04str, quapnt01str->str); + + if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE); + if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE); + if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE); + + return obstrn04str; + + } else { + if (LINES_T == S57_getObjtype(geo)) { + // Continuation B + GString *quaposstr = S57_getAttVal(geo, "QUAPOS"); + int quapos = 0; + + if (NULL != quaposstr) { + quapos = atoi(quaposstr->str); + if ( 2 <= quapos && quapos < 10){ + if (NULL != udwhaz03str) + g_string_append(obstrn04str, ";LC(LOWACC41)"); + else + g_string_append(obstrn04str, ";LC(LOWACC31)"); + } + } + + if (NULL != udwhaz03str) + g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)"); + + if (UNKNOWN != valsou) + if (valsou <= 20.0) + g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)"); + else + g_string_append(obstrn04str, ";LS(DASH,2,CHBLK)"); + else + g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)"); + + + if (NULL != udwhaz03str) + g_string_append(obstrn04str, udwhaz03str->str); + else { + if (UNKNOWN != valsou) + if (valsou <= 20.0) + g_string_append(obstrn04str, sndfrm02str->str); + } + + if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE); + if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE); + + return obstrn04str; + + } else { + // Continuation C (AREAS_T) + GString *quapnt01str = _QUAPNT01(geo); + if (NULL != udwhaz03str) { + g_string_append(obstrn04str, ";AC(DEPVS);AP(FOULAR01)"); + g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)"); + g_string_append(obstrn04str, udwhaz03str->str); + if (NULL != quapnt01str) + g_string_append(obstrn04str, quapnt01str->str); + + if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE); + if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE); + if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE); + + return obstrn04str; + } + + if (UNKNOWN != valsou) { + // BUG in CA49995B.000 if we get here because there is no color + // beside NODATA (ie there is a hole in group 1 area!) + //g_string_append(obstrn04, ";AC(UINFR)"); + + if (valsou <= 20.0) + g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)"); + else + g_string_append(obstrn04str, ";LS(DASH,2,CHBLK)"); + + g_string_append(obstrn04str, sndfrm02str->str); + + } else { + GString *watlevstr = S57_getAttVal(geo, "WATLEV"); + + if (NULL == watlevstr) // default + g_string_append(obstrn04str, ";AC(DEPVS);LS(DOTT,2,CHBLK)"); + else { + if ('3' == *watlevstr->str) { + GString *catobsstr = S57_getAttVal(geo, "CATOBS"); + if (NULL != catobsstr && '6' == *catobsstr->str) + g_string_append(obstrn04str, ";AC(DEPVS);AP(FOULAR01);LS(DOTT,2,CHBLK)"); + } else { + switch (*watlevstr->str) { + case '1': + case '2': g_string_append(obstrn04str, ";AC(CHBRN);LS(SOLD,2,CSTLN)"); break; + case '4': g_string_append(obstrn04str, ";AC(DEPIT);LS(DASH,2,CSTLN)"); break; + case '5': + case '3': + default : g_string_append(obstrn04str, ";AC(DEPVS);LS(DOTT,2,CHBLK)"); break; + } + } + } + } + + g_string_append(obstrn04str, quapnt01str->str); + + if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE); + if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE); + if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE); + + + return obstrn04str; + } + + } + + + // check if one exit point could do!!! + return NULL; +} + +static GString *OWNSHP02 (S57_geo *geo) +// Remarks: +// 1. CONNING POSITION +// 1.1 When own-ship is drawn to scale, the conning position must be correctly located in +// relation to the ship's outline. The conning position then serves as the pivot point for +// the own-ship symbol, to be located by the ECDIS at the correct latitude, longitude +// for the conning point, as computed from the positioning system, correcting for +// antenna offset. +// 1.2 In this procedure it is assumed that the heading line, beam bearing line and course +// and speed vector originate at the conning point. If another point of origin is used, +// for example to account for the varying position of the ships turning centre, this must +// be made clear to the mariner. +// +// 2. DISPLAY OPTIONS +// 2.1 Only the ship symbol is mandatory for an ECDIS. The mariner should be prompted +// to select from the following additional optional features: +// - display own-ship as: +// 1. symbol, or +// 2. scaled outline +// - select time period determining vector length for own-ship and other vessel course and speed +// vectors, (all vectors must be for the same time period), +// - display own-ship vector, +// - select ground or water stabilization for all vectors, and select whether to display the type of +// stabilization, (by arrowhead), +// - select one-minute or six-minute vector time marks, +// - select whether to show a heading line, to the edge of the display window, +// - select whether to show a beam bearing line, and if so what length (default: 10mm total +// length). +{ + PRINTF("Mariner's object not drawn\n"); + return NULL; +} + +static GString *PASTRK01 (S57_geo *geo) +// Remarks: This conditional symbology procedure was designed to allow the mariner +// to select time labels at the pasttrack (see (3) 10.5.11.1). The procedure also +// cares for the presentation of primary and secondary pasttrack. +// +// The manufacturer should define his own data class (spatial primitive) in xyt +// (position and time) in order to represent Pastrk. +{ + + PRINTF("Mariner's object not drawn\n"); + return NULL; +} + +static GString *_QUALIN01(S57_geo *geo); +static GString *QUAPOS01 (S57_geo *geo) +// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached +// to the spatial object, not the feature object. +// +// This procedure passes the object to procedure QUALIN01 or QUAPNT01, +// which traces back to the spatial object, retrieves any QUAPOS attributes, +// and returns the appropriate symbolization to QUAPOS01. +{ + GString *quapos01 = NULL; + + if (LINES_T == S57_getObjtype(geo)) + quapos01 = _QUALIN01(geo); + else + quapos01 = _QUAPNT01(geo); + + return quapos01; +} + +static GString *_QUALIN01(S57_geo *geo) +// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached +// only to the spatial component(s) of an object. +// +// A line object may be composed of more than one spatial object. +// +// This procedure looks at each of the spatial +// objects, and symbolizes the line according to the positional accuracy. +{ + GString *qualino1 = NULL; + GString *quaposstr = S57_getAttVal(geo, "QUAPOS"); + int quapos = 0; + char *line = NULL; + + if (NULL != quaposstr) { + quapos = atoi(quaposstr->str); + if ( 2 <= quapos && quapos < 10) + line = ";LC(LOWACC21)"; + } else { + GString *objlstr = S57_getAttVal(geo, "OBJL"); + int objl = (NULL == objlstr)? 0 : atoi(objlstr->str); + + if (COALNE == objl) { + GString *conradstr = S57_getAttVal(geo, "CONRAD"); + + if (NULL != conradstr) { + if ('1' == *conradstr->str) + line = ";LS(SOLD,3,CHMGF);LS(SOLD,1,CSTLN)"; + else + line = ";LS(SOLD,1,CSTLN)"; + } else + line = ";LS(SOLD,1,CSTLN)"; + + } else //LNDARE + line = ";LS(SOLD,1,CSTLN)"; + } + + if (NULL != line) + qualino1 = g_string_new(line); + + return qualino1; +} + +static GString *_QUAPNT01(S57_geo *geo) +// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached +// only to the spatial component(s) of an object. +// +// This procedure retrieves any QUAPOS attributes, and returns the +// appropriate symbols to the calling procedure. +{ + GString *quapnt01 = NULL; + int accurate = TRUE; + GString *quaposstr = S57_getAttVal(geo, "QUAPOS"); + int quapos = (NULL == quaposstr)? 0 : atoi(quaposstr->str); + + if (NULL != quaposstr) { + if ( 2 <= quapos && quapos < 10) + accurate = FALSE; + } + + if (accurate) + quapnt01 = g_string_new(";SY(LOWACC01)"); + + return quapnt01; +} + +static GString *SLCONS03 (S57_geo *geo) +// Remarks: Shoreline construction objects which have a QUAPOS attribute on their +// spatial component indicating that their position is unreliable are symbolized +// by a special linestyle in the place of the varied linestyles normally used. +// Otherwise this procedure applies the normal symbolization. +{ + GString *slcons03 = NULL; + GString *valstr = NULL; + char *cmdw = NULL; // command word + GString *quaposstr = S57_getAttVal(geo, "QUAPOS"); + int quapos = (NULL == quaposstr)? 0 : atoi(quaposstr->str); + + if (POINT_T == S57_getObjtype(geo)) { + if (NULL != quaposstr) { + if (2 <= quapos && quapos < 10) + cmdw =";SY(LOWACC01)"; + } + } else { + // LINE_T and AREA_T are the same + if (NULL != quaposstr) { + if (2 <= quapos && quapos < 10) + cmdw =";LC(LOWACC01)"; + } else { + valstr = S57_getAttVal(geo, "CONDTN"); + + if (NULL != valstr && ( '1' == *valstr->str || '2' == *valstr->str)) + cmdw = ";LS(DASH,1,CSTLN)"; + else { + int val = 0; + valstr = S57_getAttVal(geo, "CATSLC"); + val = (NULL == valstr)? 0 : atoi(valstr->str); + + if (NULL != valstr && ( 6 == val || 15 == val || 16 == val )) + cmdw = ";LS(SOLD,4,CSTLN)"; + else { + valstr = S57_getAttVal(geo, "WATLEV"); + + if (NULL != valstr && '2' == *valstr->str) + cmdw = ";LS(SOLD,2,CSTLN)"; + else + if (NULL != valstr && ('3' == *valstr->str || '4' == *valstr->str)) + cmdw = ";LS(DASH,2,CSTLN)"; + else + cmdw = ";LS(SOLD,2,CSTLN)"; // default + + } + } + } + } + + // WARNING: not explicitly specified in S-52 !! + // FIXME:this is to put AC(DEPIT) --intertidal area + // Could this be bug in OGR ? + /* + if (AREAS_T == S57_getObjtype(geo)) { + GString *seabed01 = NULL; + GString *drval1str = S57_getAttVal(geo, "DRVAL1"); + double drval1 = (NULL == drval1str)? -UNKNOWN : atof(drval1str->str); + GString *drval2str = S57_getAttVal(geo, "DRVAL2"); + double drval2 = (NULL == drval2str)? -UNKNOWN : atof(drval2str->str); + // NOTE: change sign of infinity (minus) to get out of bound in seabed01 + + + PRINTF("***********drval1=%f drval2=%f \n", drval1, drval2); + seabed01 = _SEABED01(drval1, drval2); + slcons03 = g_string_new(seabed01->str); + g_string_free(seabed01, TRUE); + + } + */ + + if (NULL != cmdw) { + if (NULL == slcons03) + slcons03 = g_string_new(cmdw); + else + g_string_append(slcons03, cmdw); + } + + return slcons03; +} + +static GString *RESARE02 (S57_geo *geo) +// Remarks: A list-type attribute is used because an area of the object class RESARE may +// have more than one category (CATREA). For example an inshore traffic +// zone might also have fishing and anchoring prohibition and a prohibited +// area might also be a bird sanctuary or a mine field. +// +// This conditional procedure is set up to ensure that the categories of most +// importance to safe navigation are prominently symbolized, and to pass on +// all given information with minimum clutter. Only the most significant +// restriction is symbolized, and an indication of further limitations is given by +// a subscript "!" or "I". Further details are given under conditional +// symbology procedure RESTRN01 +// +// Other object classes affected by attribute RESTRN are handled by +// conditional symbology procedure RESTRN01. +{ + GString *resare02 = g_string_new(""); + GString *restrnstr = S57_getAttVal(geo, "RESTRN"); + char restrn[LISTSIZE] = {'\0'}; + GString *catreastr = S57_getAttVal(geo, "CATREA"); + char catrea[LISTSIZE] = {'\0'}; + char *symb = NULL; + char *line = NULL; + char *prio = NULL; + + if ( NULL != restrnstr) { + _parseList(restrnstr->str, restrn); + + if (NULL != catreastr) _parseList(catreastr->str, catrea); + + if (strpbrk(restrn, "\007\010\016")) { + // Continuation A + if (strpbrk(restrn, "\001\002\003\004\005\006")) + symb = ";SY(ENTRES61)"; + else { + if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031")) + symb = ";SY(ENTRES61)"; + else { + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = ";SY(ENTRES71)"; + else { + if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030")) + symb = ";SY(ENTRES71)"; + else + symb = ";SY(ENTRES51)"; + } + } + } + + if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND)) + line = ";LC(CTYARE51)"; + else + line = ";LS(DASH,2,CHMGD)"; + + prio = ";OP(6---)"; // display prio set to 6 + + } else { + if (strpbrk(restrn, "\001\002")) { + // Continuation B + if (strpbrk(restrn, "\003\004\005\006")) + symb = ";SY(ACHRES61)"; + else { + if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031")) + symb = ";SY(ACHRES61)"; + else { + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = ";SY(ACHRES71)"; + else { + if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030")) + symb = ";SY(ACHRES71)"; + else + symb = ";SY(ACHRES51)"; + } + } + } + + if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND)) + line = ";LC(ACHRE51)"; + else + line = ";LS(DASH,2,CHMGD)"; + + prio = ";OP(6---)"; // display prio set to 6 + + } else { + if (strpbrk(restrn, "\003\004\005\006")) { + // Continuation C + if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031")) + symb = ";SY(FSHRES51)"; + else { + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = ";SY(FSHRES71)"; + else{ + if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030")) + symb = ";SY(FSHRES71)"; + else + symb = ";SY(FSHRES51)"; + } + } + + if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND)) + line = ";LC(FSHRES51)"; + else + line = ";LS(DASH,2,CHMGD)"; + + prio = ";OP(6---)"; // display prio set to 6 + + } else { + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = ";SY(INFARE51)"; + else + symb = ";SY(RSRDEF51)"; + + if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND)) + line = ";LC(CTYARE51)"; + else + line = ";LS(DASH,2,CHMGD)"; + + } + } + } + + } else { + // Continuation D + if (NULL != catreastr) { + if (strpbrk(catrea, "\001\010\011\014\016\023\025\031")) { + if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030")) + symb = ";SY(CTYARE71)"; + else + symb = ";SY(CTYARE51)"; + } else { + if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030")) + symb = ";SY(INFARE71)"; + else + symb = ";SY(RSRDEF51)"; + } + } else + symb = ";SY(RSRDEF51)"; + + if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND)) + line = ";LC(CTYARE51)"; + else + line = ";LS(DASH,2,CHMGD)"; + } + + // create command word + if (NULL != prio) + g_string_append(resare02, prio); + g_string_append(resare02, line); + g_string_append(resare02, symb); + + return resare02; +} + +static GString *RESTRN01 (S57_geo *geo) +// Remarks: Objects subject to RESTRN01 are actually symbolised in sub-process +// RESCSP01, since the latter can also be accessed from other conditional +// symbology procedures. RESTRN01 merely acts as a "signpost" for +// RESCSP01. +// +// Object class RESARE is symbolised for the effect of attribute RESTRN in a separate +// conditional symbology procedure called RESARE02. +// +// Since many of the areas concerned cover shipping channels, the number of symbols used +// is minimised to reduce clutter. To do this, values of RESTRN are ranked for significance +// as follows: +// "Traffic Restriction" values of RESTRN: +// (1) RESTRN 7,8: entry prohibited or restricted +// RESTRN 14: IMO designated "area to be avoided" part of a TSS +// (2) RESTRN 1,2: anchoring prohibited or restricted +// (3) RESTRN 3,4,5,6: fishing or trawling prohibited or restricted +// (4) "Other Restriction" values of RESTRN are: +// RESTRN 9, 10: dredging prohibited or restricted, +// RESTRN 11,12: diving prohibited or restricted, +// RESTRN 13 : no wake area. +{ + GString *restrn01str = S57_getAttVal(geo, "RESTRN"); + GString *restrn01 = NULL; + + if (NULL != restrn01str) + restrn01 = _RESCSP01(geo); + else + restrn01 = g_string_new(";OP(----)"); // return NOOP to silence error msg + + return restrn01; +} + +static GString *_RESCSP01(S57_geo *geo) +// Remarks: See procedure RESTRN01 +{ + GString *rescsp01 = NULL; + GString *restrnstr = S57_getAttVal(geo, "RESTRN"); + char restrn[LISTSIZE] = {'\0'}; // restriction list + char *symb = NULL; + + if ( NULL != restrnstr) { + _parseList(restrnstr->str, restrn); + + if (strpbrk(restrn, "\007\010\016")) { + // continuation A + if (strpbrk(restrn, "\001\002\003\004\005\006")) + symb = ";SY(ENTRES61)"; + else { + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = ";SY(ENTRES71)"; + else + symb = ";SY(ENTRES51)"; + + } + } else { + if (strpbrk(restrn, "\001\002")) { + // continuation B + if (strpbrk(restrn, "\003\004\005\006")) + symb = ";SY(ACHRES61)"; + else { + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = ";SY(ACHRES71)"; + else + symb = ";SY(ACHRES51)"; + } + + + } else { + if (strpbrk(restrn, "\003\004\005\006")) { + // continuation C + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = ";SY(FSHRES71)"; + else + symb = ";SY(FSHRES51)"; + + + } else { + if (strpbrk(restrn, "\011\012\013\014\015")) + symb = ";SY(INFARE51)"; + else + symb = ";SY(RSRDEF51)"; + + } + } + } + + rescsp01 = g_string_new(symb); + } + + return rescsp01; +} + +static GString *_SEABED01(double drval1, double drval2) +// Remarks: An area object that is part of the seabed is coloured as necessary according +// to the mariners selection of two shades, (shallow contour, safety contour, +// deep contour), or four shades (safety contour only). This requires a decision +// making process provided by this conditional symbology procedure. Note +// that this procedure is called as a sub-procedure by other conditional +// symbology procedures. +// +// Note: The requirement to show four depth shades is not mandatory. Also, +// the requirement to show the shallow pattern is not mandatory. However, +// both these features are strongly recommended. + +// return: is never NULL + +{ + GString *seabed01 = NULL; + gboolean shallow = TRUE; + char *arecol = ";AC(DEPIT)"; + + if (drval1 >= 0.0 && drval2 > 0.0) + arecol = ";AC(DEPVS)"; + + if (TRUE == S52_getMarinerParam(S52_MAR_TWO_SHADES)){ + if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR) && + drval2 > S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) { + arecol = ";AC(DEPDW)"; + shallow = FALSE; + } + } else { + if (drval1 >= S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR) && + drval2 > S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR)) + arecol = ";AC(DEPMS)"; + + if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR) && + drval2 > S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) { + arecol = ";AC(DEPMD)"; + shallow = FALSE; + } + + if (drval1 >= S52_getMarinerParam(S52_MAR_DEEP_CONTOUR) && + drval2 > S52_getMarinerParam(S52_MAR_DEEP_CONTOUR)) { + arecol = ";AC(DEPDW)"; + shallow = FALSE; + } + + } + + seabed01 = g_string_new(arecol); + + if (TRUE==S52_getMarinerParam(S52_MAR_SHALLOW_PATTERN) && TRUE==shallow) + g_string_append(seabed01, ";AP(DIAMOND1)"); + + return seabed01; +} + +static GString *SOUNDG02 (S57_geo *geo) +// Remarks: In S-57 soundings are elements of sounding arrays rather than individual +// objects. Thus this conditional symbology procedure examines each +// sounding of a sounding array one by one. To symbolize the depth values it +// calls the procedure SNDFRM02 which in turn translates the depth values +// into a set of symbols to be shown at the soundings position. +{ + guint npt = 0; + double *ppt = NULL; + + if (POINT_T != S57_getObjtype(geo)) { + PRINTF("invalid object type (not M_PNT_T)\n"); + //return NULL; + exit(0); + } + + S57_getGeoData(geo, 0, &npt, &ppt); + + return _SNDFRM02(geo, ppt[2]); +} + +static GString *_SNDFRM02(S57_geo *geo, double depth_value) +// Remarks: Soundings differ from plain text because they have to be readable under all +// circumstances and their digits are placed according to special rules. This +// conditional symbology procedure accesses a set of carefully designed +// sounding symbols provided by the symbol library and composes them to +// sounding labels. It symbolizes swept depth and it also symbolizes for low +// reliability as indicated by attributes QUASOU and QUAPOS. +{ + GString *sndfrm02 = g_string_new(""); + char *symbol_prefix = NULL; + GString *tecsoustr = S57_getAttVal(geo, "TECSOU"); + char tecsou[LISTSIZE] = {'\0'}; + GString *quasoustr = S57_getAttVal(geo, "QUASOU"); + char quasou[LISTSIZE] = {'\0'}; + GString *statusstr = S57_getAttVal(geo, "STATUS"); + char status[LISTSIZE] = {'\0'}; + double leading_digit = 0.0; + + // FIXME: test to fix the rounding error (!?) + depth_value += (depth_value > 0.0)? 0.01: -0.01; + leading_digit = (int) depth_value; + + if (depth_value <= S52_getMarinerParam(S52_MAR_SAFETY_DEPTH)) + symbol_prefix = "SOUNDS"; + else + symbol_prefix = "SOUNDG"; + + if (NULL != tecsoustr) { + _parseList(tecsoustr->str, tecsou); + if (strpbrk(tecsou, "\006")) + g_string_sprintfa(sndfrm02, ";SY(%sB1)", symbol_prefix); + } + + if (NULL != quasoustr) _parseList(quasoustr->str, quasou); + if (NULL != statusstr) _parseList(statusstr->str, status); + + if (strpbrk(quasou, "\003\004\005\010\011") || strpbrk(status, "\022")) + g_string_sprintfa(sndfrm02, ";SY(%sC2)", symbol_prefix); + else { + GString *quaposstr = S57_getAttVal(geo, "QUAPOS"); + int quapos = (NULL == quaposstr)? 0 : atoi(quaposstr->str); + + if (NULL != quaposstr) { + if (2 <= quapos && quapos < 10) + g_string_sprintfa(sndfrm02, ";SY(%sC2)", symbol_prefix); + } + } + + // Continuation A + if (depth_value < 10.0) { + // can be above water (negative) + int fraction = (int)ABS((depth_value - leading_digit)*10); + + g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)ABS(leading_digit)); + g_string_sprintfa(sndfrm02, ";SY(%s5%1i)", symbol_prefix, fraction); + + // above sea level (negative) + if (depth_value < 0.0) + g_string_sprintfa(sndfrm02, ";SY(%sA1)", symbol_prefix); + + return sndfrm02; + } + + if (depth_value < 31.0) { + double fraction = depth_value - leading_digit; + + if (fraction != 0.0) { + fraction = fraction * 10; + if (leading_digit >= 10.0) + g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)leading_digit/10); + + g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)leading_digit); + g_string_sprintfa(sndfrm02, ";SY(%s5%1i)", symbol_prefix, (int)fraction); + + return sndfrm02; + } + } + + // Continuation B + depth_value = leading_digit; // truncate to integer + if (depth_value < 100.0) { + double first_digit = leading_digit / 10; + double secnd_digit = leading_digit - (first_digit * 10); + + g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)first_digit); + g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)secnd_digit); + + return sndfrm02; + } + + if (depth_value < 1000.0) { + double first_digit = leading_digit / 100; + double secnd_digit = leading_digit - (first_digit * 100); + double third_digit = leading_digit - (first_digit * 100) - (secnd_digit * 10); + + g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)first_digit); + g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)secnd_digit); + g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)third_digit); + + return sndfrm02; + } + + if (depth_value < 10000.0) { + double first_digit = leading_digit / 1000; + double secnd_digit = leading_digit - (first_digit * 1000); + double third_digit = leading_digit - (first_digit * 1000) - (secnd_digit * 100); + double last_digit = leading_digit - (first_digit * 1000) - (secnd_digit * 100) - (third_digit * 100) ; + + g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)first_digit); + g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)secnd_digit); + g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)third_digit); + g_string_sprintfa(sndfrm02, ";SY(%s4%1i)", symbol_prefix, (int)last_digit); + + return sndfrm02; + } + + // Continuation C + { + double first_digit = leading_digit / 10000; + double secnd_digit = leading_digit - (first_digit * 10000); + double third_digit = leading_digit - (first_digit * 10000) - (secnd_digit * 1000); + double fourth_digit = leading_digit - (first_digit * 10000) - (secnd_digit * 1000) - (third_digit * 100) ; + double last_digit = leading_digit - (first_digit * 10000) - (secnd_digit * 1000) - (third_digit * 100) - (fourth_digit * 10) ; + + g_string_sprintfa(sndfrm02, ";SY(%s3%1i)", symbol_prefix, (int)first_digit); + g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)secnd_digit); + g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)third_digit); + g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)fourth_digit); + g_string_sprintfa(sndfrm02, ";SY(%s4%1i)", symbol_prefix, (int)last_digit); + + return sndfrm02; + } + + return sndfrm02; +} + +static GString *TOPMAR01 (S57_geo *geo) +// Remarks: Topmark objects are to be symbolized through consideration of their +// platforms e.g. a buoy. Therefore this conditional symbology procedure +// searches for platforms by looking for other objects that are located at the +// same position.. Based on the finding whether the platform is rigid or +// floating, the respective upright or sloping symbol is selected and presented +// at the objects location. Buoy symbols and topmark symbols have been +// carefully designed to fit to each other when combined at the same position. +// The result is a composed symbol that looks like the traditional symbols the +// mariner is used to. +{ + GString *topshpstr = S57_getAttVal(geo, "TOPSHP"); + GString *topmar = NULL; + char *sy = NULL; + + if (NULL == topshpstr) + sy = ";SY(QUESMRK1)"; + else { + int floating = FALSE; // not a floating platform + int topshp = (NULL==topshpstr) ? 0 : atoi(topshpstr->str); + + if (TRUE == _atPtPos(geo, FLOATLIST)) + floating = TRUE; + else + // FIXME: this test is wierd since it doesn't affect 'floating' + if (TRUE == _atPtPos(geo, RIGIDLIST)) + floating = FALSE; + + + if (floating) { + // floating platform + switch (topshp) { + case 1 : sy = ";SY(TOPMAR02)"; break; + case 2 : sy = ";SY(TOPMAR04)"; break; + case 3 : sy = ";SY(TOPMAR10)"; break; + case 4 : sy = ";SY(TOPMAR12)"; break; + + case 5 : sy = ";SY(TOPMAR13)"; break; + case 6 : sy = ";SY(TOPMAR14)"; break; + case 7 : sy = ";SY(TOPMAR65)"; break; + case 8 : sy = ";SY(TOPMAR17)"; break; + + case 9 : sy = ";SY(TOPMAR16)"; break; + case 10: sy = ";SY(TOPMAR08)"; break; + case 11: sy = ";SY(TOPMAR07)"; break; + case 12: sy = ";SY(TOPMAR14)"; break; + + case 13: sy = ";SY(TOPMAR05)"; break; + case 14: sy = ";SY(TOPMAR06)"; break; + case 17: sy = ";SY(TMARDEF2)"; break; + case 18: sy = ";SY(TOPMAR10)"; break; + + case 19: sy = ";SY(TOPMAR13)"; break; + case 20: sy = ";SY(TOPMAR14)"; break; + case 21: sy = ";SY(TOPMAR13)"; break; + case 22: sy = ";SY(TOPMAR14)"; break; + + case 23: sy = ";SY(TOPMAR14)"; break; + case 24: sy = ";SY(TOPMAR02)"; break; + case 25: sy = ";SY(TOPMAR04)"; break; + case 26: sy = ";SY(TOPMAR10)"; break; + + case 27: sy = ";SY(TOPMAR17)"; break; + case 28: sy = ";SY(TOPMAR18)"; break; + case 29: sy = ";SY(TOPMAR02)"; break; + case 30: sy = ";SY(TOPMAR17)"; break; + + case 31: sy = ";SY(TOPMAR14)"; break; + case 32: sy = ";SY(TOPMAR10)"; break; + case 33: sy = ";SY(TMARDEF2)"; break; + default: sy = ";SY(TMARDEF2)"; break; + } + } else { + // not a floating platform + switch (topshp) { + case 1 : sy = ";SY(TOPMAR22)"; break; + case 2 : sy = ";SY(TOPMAR24)"; break; + case 3 : sy = ";SY(TOPMAR30)"; break; + case 4 : sy = ";SY(TOPMAR32)"; break; + + case 5 : sy = ";SY(TOPMAR33)"; break; + case 6 : sy = ";SY(TOPMAR34)"; break; + case 7 : sy = ";SY(TOPMAR85)"; break; + case 8 : sy = ";SY(TOPMAR86)"; break; + + case 9 : sy = ";SY(TOPMAR36)"; break; + case 10: sy = ";SY(TOPMAR28)"; break; + case 11: sy = ";SY(TOPMAR27)"; break; + case 12: sy = ";SY(TOPMAR14)"; break; + + case 13: sy = ";SY(TOPMAR25)"; break; + case 14: sy = ";SY(TOPMAR26)"; break; + case 15: sy = ";SY(TOPMAR88)"; break; + case 16: sy = ";SY(TOPMAR87)"; break; + + case 17: sy = ";SY(TMARDEF1)"; break; + case 18: sy = ";SY(TOPMAR30)"; break; + case 19: sy = ";SY(TOPMAR33)"; break; + case 20: sy = ";SY(TOPMAR34)"; break; + + case 21: sy = ";SY(TOPMAR33)"; break; + case 22: sy = ";SY(TOPMAR34)"; break; + case 23: sy = ";SY(TOPMAR34)"; break; + case 24: sy = ";SY(TOPMAR22)"; break; + + case 25: sy = ";SY(TOPMAR24)"; break; + case 26: sy = ";SY(TOPMAR30)"; break; + case 27: sy = ";SY(TOPMAR86)"; break; + case 28: sy = ";SY(TOPMAR89)"; break; + + case 29: sy = ";SY(TOPMAR22)"; break; + case 30: sy = ";SY(TOPMAR86)"; break; + case 31: sy = ";SY(TOPMAR14)"; break; + case 32: sy = ";SY(TOPMAR30)"; break; + case 33: sy = ";SY(TMARDEF1)"; break; + default: sy = ";SY(TMARDEF1)"; break; + } + } + + } + + topmar = g_string_new(sy); + + return topmar; +} + +/* +static GString *_UDWHAZ03(S57_geo *geo, double depth_value) +// Remarks: Obstructions or isolated underwater dangers of depths less than the safety +// contour which lie within the safe waters defined by the safety contour are +// to be presented by a specific isolated danger symbol as hazardous objects +// and put in IMO category DISPLAYBASE (see (3), App.2, 1.3). This task +// is performed by this conditional symbology procedure. +{ + GString *udwhaz03str = NULL; + int danger = FALSE; + double safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR); + S57_geo *geoTmp = geo; + + if (depth_value <= safety_contour) { + // that intersect this point/line/area for OBSTRN04 + // that intersect this point/area for WRECKS02 + + // get area DEPARE & DRGARE that intersect this point/line/area + while (NULL != (geoTmp = S57_nextObj(geoTmp))) { + + if (LINES_T == S57_getObjtype(geoTmp)) { + GString *drval2str = S57_getAttVal(geoTmp, "DRVAL2"); + double drval2 = (NULL == drval2str) ? 0.0 : atof(drval2str->str); + + if (drval2 < safety_contour) { + danger = TRUE; + break; + } + + } else { + // + GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1"); + double drval1 = (NULL == drval1str) ? 0.0 : atof(drval1str->str); + + if (drval1 >= safety_contour) { + danger = TRUE; + break; + } + } + + } + + + //danger = TRUE; // true + if (TRUE == danger) { + GString *watlevstr = S57_getAttVal(geo, "WATLEV"); + if (NULL != watlevstr && ('1' == *watlevstr->str || '2' == *watlevstr->str)) + udwhaz03str = g_string_new(";OP(--D14050"); + else { + udwhaz03str = g_string_new(";OP(8OD14010);SY(ISODGR51)"); + S57_setAtt(geo, "SCAMIN", "INFINITE"); + } + } + } + + S57_unlinkObj(geo); + + return udwhaz03str; +} + +static GString *VESSEL01 (S57_geo *geo) +// Remarks: The mariner should be prompted to select from the following options: +// - ARPA target or AIS report (overall decision or vessel by vessel) (vesrce) +// - *time-period determining vector-length for all vectors (vecper) +// - whether to show a vector (overall or vessel by vessel) (vestat) +// - *whether to symbolize vector stabilization (vecstb) +// - *whether to show one-minute or six-minute vector time marks (vecmrk) +// - whether to show heading line on AIS vessel reports (headng) +// * Note that the same vector parameters should be used for own-ship and all vessel +// vectors. +{ + PRINTF("Mariner's object not drawn\n"); + return NULL; +} + +static GString *VRMEBL01 (S57_geo *geo) +// Remarks: This conditional symbology procedure symbolizes the three cases of range +// circle, bearing line and range/bearing line. VRM's and EBL's can be ship-centred +// or freely movable, and two line-styles are available +{ + PRINTF("Mariner's object not drawn\n"); + return NULL; +} + +static GString *WRECKS02 (S57_geo *geo) +// Remarks: Wrecks of depths less than the safety contour which lie within the safe waters +// defined by the safety contour are to be presented by a specific isolated +// danger symbol and put in IMO category DISPLAYBASE (see (3), App.2, +// 1.3). This task is performed by the sub-procedure "UDWHAZ03" which is +// called by this symbology procedure. +{ + GString *wrecks02str = NULL; + GString *sndfrm02str = NULL; + GString *udwhaz03str = NULL; + GString *quapnt01str = NULL; + double least_depth = UNKNOWN; + double depth_value = UNKNOWN; + GString *valsoustr = S57_getAttVal(geo, "VALSOU"); + double valsou = UNKNOWN; + + // exit if not in drawing state + if (1 == S52_state) + return NULL; + + if (NULL != valsoustr) { + valsou = atof(valsoustr->str); + depth_value = valsou; + sndfrm02str = _SNDFRM02(geo, depth_value); + } else { + if (AREAS_T == S57_getObjtype(geo)) + least_depth = _DEPVAL01(geo, least_depth); + + if (least_depth == UNKNOWN) { + // WARNING: ambiguity removed in WRECKS03 (see update) + GString *watlevstr = S57_getAttVal(geo, "WATLEV"); + GString *catwrkstr = S57_getAttVal(geo, "CATWRK"); + + if (NULL == watlevstr) // default + depth_value = -15.0; + else + switch (*watlevstr->str) { // ambiguous + case '1': + case '2': depth_value = -15.0 ; break; + case '3': depth_value = 0.01; break; + case '4': depth_value = -15.0 ; break; + case '5': depth_value = 0.0 ; break; + case '6': depth_value = -15.0 ; break; + default :{ + if (NULL != catwrkstr) { + switch (*catwrkstr->str) { + case '1': depth_value = 20.0; break; + case '2': depth_value = 0.0; break; + case '4': + case '5': depth_value = -15.0; break; + } + } + } + } + } else + depth_value = least_depth; + + + } + + udwhaz03str = _UDWHAZ03(geo, depth_value); + quapnt01str = _QUAPNT01(geo); + + if (POINT_T == S57_getObjtype(geo)) { + if (NULL != udwhaz03str) { + wrecks02str = g_string_new(udwhaz03str->str); + + if (NULL != quapnt01str) + g_string_append(wrecks02str, quapnt01str->str); + + } else { + // Continuation A (POINT_T) + if (UNKNOWN != valsou) { + + if (valsou <= 20.0) { + wrecks02str = g_string_new(";SY(DANGER01)"); + if (NULL != sndfrm02str) + g_string_append(wrecks02str, sndfrm02str->str); + + } else + wrecks02str = g_string_new(";SY(DANGER02)"); + + if (NULL != udwhaz03str) + g_string_append(wrecks02str, udwhaz03str->str); + if (NULL != quapnt01str) + g_string_append(wrecks02str, quapnt01str->str); + + } else { + char *sym = NULL; + GString *catwrkstr = S57_getAttVal(geo, "CATWRK"); + GString *watlevstr = S57_getAttVal(geo, "WATLEV"); + + if (NULL != catwrkstr && NULL != watlevstr) { + if ('1' == *catwrkstr->str && '3' == *watlevstr->str) + sym =";SY(WRECKS04)"; + else { + if ('2' == *catwrkstr->str && '3' == *watlevstr->str) + sym = ";SY(WRECKS05)"; + else { + if ('4' == *catwrkstr->str || '5' == *catwrkstr->str) + sym = ";SY(WRECKS01)"; + else { + if ('1' == *watlevstr->str || + '2' == *watlevstr->str || + '5' == *watlevstr->str || + '4' == *watlevstr->str ){ + sym = ";SY(WRECKS01)"; + } else + sym = ";SY(WRECKS05)"; // default + + } + } + } + } + + wrecks02str = g_string_new(sym); + if (NULL != quapnt01str) + g_string_append(wrecks02str, quapnt01str->str); + + } + + } + + + } else { + // Continuation B (AREAS_T) + GString *quaposstr = S57_getAttVal(geo, "QUAPOS"); + int quapos = (NULL == quaposstr)? 0 : atoi(quaposstr->str); + char *line = NULL; + + if (2 <= quapos && quapos < 10) + line = ";LC(LOWACC41)"; + else { + if ( NULL != udwhaz03str) + line = ";LS(DOTT,2,CHBLK)"; + else { + if (UNKNOWN != valsou){ + if (valsou <= 20) + line = ";LS(DOTT,2,CHBLK)"; + else + line = ";LS(DASH,2,CHBLK)"; + } else { + GString *watlevstr = S57_getAttVal(geo, "WATLEV"); + + if (NULL == watlevstr) + line = ";LS(DOTT,2,CSTLN)"; + else { + switch (*watlevstr->str){ + case '1': + case '2': line = ";LS(SOLD,2,CSTLN)"; break; + case '4': line = ";LS(DASH,2,CSTLN)"; break; + case '3': + case '5': + + default : line = ";LS(DOTT,2,CSTLN)"; break; + } + } + + } + } + } + wrecks02str = g_string_new(line); + + if (UNKNOWN != valsou) { + if (valsou <= 20) { + if (NULL != udwhaz03str) + g_string_append(wrecks02str, udwhaz03str->str); + + if (NULL != quapnt01str) + g_string_append(wrecks02str, quapnt01str->str); + + if (NULL != sndfrm02str) + g_string_append(wrecks02str, sndfrm02str->str); + + } else { + // NOTE: ??? same as above ??? + if (NULL != udwhaz03str) + g_string_append(wrecks02str, udwhaz03str->str); + + if (NULL != quapnt01str) + g_string_append(wrecks02str, quapnt01str->str); + } + } else { + char *ac = NULL; + GString *watlevstr = S57_getAttVal(geo, "WATLEV"); + + if (NULL == watlevstr) + ac = ";AC(DEPVS)"; + else + switch (*watlevstr->str) { + case '1': + case '2': ac = ";AC(CHBRN)"; break; + case '4': ac = ";AC(DEPIT)"; break; + case '5': + case '3': + default : ac = ";AC(DEPVS)"; break; + } + + g_string_append(wrecks02str, ac); + + if (NULL != udwhaz03str) + g_string_append(wrecks02str, udwhaz03str->str); + + if (NULL != quapnt01str) + g_string_append(wrecks02str, quapnt01str->str); + } + } + + if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE); + if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE); + if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE); + + return wrecks02str; +} + + +//-------------------------------- +// +// JUMP TABLE SECTION +// +//-------------------------------- + + +CondSymb condTable[] = { + // name call Sub-Procedure + {"CLRLIN01", CLRLIN01}, // + {"DATCVR01", DATCVR01}, // + {"DEPARE01", DEPARE01}, // _RESCSP01, _SEABED01 + {"DEPCNT02", DEPCNT02}, // + {"LEGLIN02", LEGLIN02}, // str + {"LIGHTS05", LIGHTS05}, // _LITDSN01 + {"OBSTRN04", OBSTRN04}, // _DEPVAL01, _QUAPNT01, _SNDFRM02, _UDWHAZ03 + {"OWNSHP02", OWNSHP02}, // + {"PASTRK01", PASTRK01}, // + {"QUAPOS01", QUAPOS01}, // _QUALIN01, _QUAPNT01 + {"SLCONS03", SLCONS03}, // + {"RESARE02", RESARE02}, // + {"RESTRN01", RESTRN01}, // _RESCSP01 + {"SOUNDG02", SOUNDG02}, // _SNDFRM02 + {"TOPMAR01", TOPMAR01}, // + {"VESSEL01", VESSEL01}, // + {"VRMEBL01", VRMEBL01}, // + {"WRECKS02", WRECKS02}, // _DEPVAL01, _QUAPNT01, _SNDFRM02, _UDWHAZ03 + + {"########",NULL} +}; +*/ +#endif diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.cpp new file mode 100644 index 0000000..eac4589 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.cpp @@ -0,0 +1,12765 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: S52 Presentation Library + * Authors: David Register + * Jesper Weissglas + * + *************************************************************************** + * Copyright (C) 2010 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif // precompiled headers + +#include +#include + +#ifdef __OCPN__ANDROID__ +// Handle occasional SIG on Android +#include +#include + +thread_local struct sigaction sa_all_plib; +thread_local struct sigaction sa_all_plib_previous; + +thread_local sigjmp_buf env_plib; // the context saved by sigsetjmp(); + +void catch_signals_plib(int signo) { + switch (signo) { + case SIGSEGV: + siglongjmp(env_plib, 1); // jump back to the setjmp() point + break; + + default: + break; + } +} +#endif + +#ifndef PI +#define PI 3.1415926535897931160E0 /* pi */ +#endif +#define DEGREE (PI / 180.0) +#define RADIAN (180.0 / PI) + +static const double WGS84_semimajor_axis_meters = + 6378137.0; // WGS84 semimajor axis +static const double mercator_k0 = 0.9996; + +#include "s52plib.h" +#include "mygeom.h" +#include "s52utils.h" +#include "chartsymbols.h" +// #include "TexFont.h" +#include "line_clip.h" +#include "poly_math.h" +#include "LOD_reduce.h" +#include "linmath.h" +#ifdef ocpnUSE_GL +#include "Cs52_shaders.h" +#endif + +#include +#include +#include +#include + +#ifndef PROJECTION_MERCATOR +#define PROJECTION_MERCATOR 1 +#endif + +s52plib *ps52plib; ///< Global instance + +// Some methods that are required to be declared differently +// depending on whether the library is build for core, or plugin +#ifdef BUILDING_PLUGIN +#include "ocpn_plugin.h" +#else +wxColour GetFontColour_PlugIn(wxString TextElement); +extern "C" bool GetGlobalColor(wxString colorName, wxColour *pcolour); +wxFont *FindOrCreateFont_PlugIn( + int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, + bool underline = false, const wxString &facename = wxEmptyString, + wxFontEncoding encoding = wxFONTENCODING_DEFAULT); +wxFont *GetOCPNScaledFont_PlugIn(wxString TextElement, int default_size = 0); +float GetOCPNChartScaleFactor_Plugin(); +extern "C" wxString *GetpSharedDataLocation(); + +#endif + +#ifdef __OCPN__ANDROID__ +#include "qdebug.h" +#endif + +float g_scaminScale; + +// #ifndef __MSVC__ +#define _GLUfuncptrA _GLUfuncptr +// #endif + +void DrawAALine(wxDC *pDC, int x0, int y0, int x1, int y1, wxColour clrLine, + int dash, int space); +extern bool GetDoubleAttr(S57Obj *obj, const char *AttrName, double &val); + +void LoadS57Config(); +bool loadS52Shaders(); + +// Simple and fast CRC32 calculator +unsigned int crc32buf(unsigned char *buf, size_t len); + +GLint S52color_tri_shader_program; +GLint S52texture_2D_shader_program; +GLint S52texture_2D_ColorMod_shader_program; +GLint S52circle_filled_shader_program; +GLint S52ring_shader_program; +GLint S52Dash_shader_program; +GLint S52AP_shader_program; + +std::string s_color; +std::string s_uTex; +std::string s_position; +std::string s_aUV; +std::string s_TransformMatrix; + +// Implement all lists +#include +WX_DEFINE_LIST(TextObjList); + +// Implement all arrays +#include +#include "s52_plib_utils.h" +WX_DEFINE_OBJARRAY(ArrayOfNoshow); + +// S52_TextC Implementation +S52_TextC::S52_TextC() { + pcol = NULL; + pFont = NULL; + texobj = 0; + bnat = false; + bspecial_char = false; +} + +S52_TextC::~S52_TextC() { + if (texobj) { + glDeleteTextures(1, (GLuint *)(&this->texobj)); + } +} + +wxRect2D::wxRect2D() : x(0), y(0), width(0), height(0) {} +wxRect2D:: wxRect2D(double xx, double yy, double w, double h) + : x(xx), y(yy), width(w), height(h) {} + +bool wxRect2D::Intersects(const wxRect2D& other, + double epsilon) const { + return !((x + width) <= (other.x + epsilon) || + (other.x + other.width) <= (x + epsilon) || + (y + height) <= (other.y + epsilon) || + (other.y + other.height) <= (y + epsilon)); +} + +wxRect2D wxRect2D::Union(const wxRect2D& other) const +{ + if (IsEmpty()) + return other; + + if (other.IsEmpty()) + return *this; + + double left = std::min(x, other.x); + double top = std::min(y, other.y); + double right = std::max(x + width, other.x + other.width); + double bottom = std::max(y + height, other.y + other.height); + + return wxRect2D(left, top, right - left, bottom - top); +} + +// This is a verbatim copy of same struct found in ocpn_plugin.h +// Used for some types of plugin charts +class PI_line_segment_element { +public: + size_t vbo_offset; + size_t n_points; + int priority; + float lat_max; // segment bounding box + float lat_min; + float lon_max; + float lon_min; + int type; + void *private0; + + PI_line_segment_element *next; +}; + +//----------------------------------------------------------------------------- +// Comparison Function for LUPArray sorting +// Note Global Scope +//----------------------------------------------------------------------------- +#ifndef _COMPARE_LUP_DEFN_ +#define _COMPARE_LUP_DEFN_ + +int CompareLUPObjects(LUPrec *item1, LUPrec *item2) { + // sort the items by their name... + int ir = strcmp(item1->OBCL, item2->OBCL); + + if (ir != 0) return ir; + int c1 = item1->ATTArray.size(); + int c2 = item2->ATTArray.size(); + + if (c1 != c2) return c2 - c1; + return item1->nSequence - item2->nSequence; +} + +#endif + +//----------------------------------------------------------------------------- +// LUPArrayContainer implementation +//----------------------------------------------------------------------------- +LUPArrayContainer::LUPArrayContainer() { + // Build the initially empty sorted arrays of LUP Records, per LUP type. + // Sorted on object name, e.g. ACHARE. Why sorted? Helps in the + // S52_LUPLookup method.... + LUPArray = new wxArrayOfLUPrec(CompareLUPObjects); +} + +LUPArrayContainer::~LUPArrayContainer() { + if (LUPArray) { + for (unsigned int il = 0; il < LUPArray->GetCount(); il++) + s52plib::DestroyLUP(LUPArray->Item(il)); + + LUPArray->Clear(); + delete LUPArray; + } + + LUPArrayIndexHash::iterator it; + for (it = IndexHash.begin(); it != IndexHash.end(); ++it) { + free(it->second); + } +} + +LUPHashIndex *LUPArrayContainer::GetArrayIndexHelper(const char *objectName) { + // Look for the key + wxString key(objectName, wxConvUTF8); + LUPArrayIndexHash::iterator it = IndexHash.find(key); + + if (it == IndexHash.end()) { + // Key not found, needs to be added + LUPHashIndex *pindex = (LUPHashIndex *)malloc(sizeof(LUPHashIndex)); + pindex->n_start = -1; + pindex->count = 0; + IndexHash[key] = pindex; + + // Find the first matching entry in the LUP Array + int index = 0; + int index_max = LUPArray->GetCount(); + int first_match = 0; + int ocnt = 0; + LUPrec *LUPCandidate; + + // This technique of extracting proper LUPs depends on the fact that + // the LUPs have been sorted in their array, by OBCL. + // Thus, all the LUPS with the same OBCL will be grouped together + + while (!first_match && (index < index_max)) { + LUPCandidate = LUPArray->Item(index); + if (!strcmp(objectName, LUPCandidate->OBCL)) { + pindex->n_start = index; + first_match = 1; + ocnt++; + index++; + break; + } + index++; + } + + while (first_match && (index < index_max)) { + LUPCandidate = LUPArray->Item(index); + if (!strcmp(objectName, LUPCandidate->OBCL)) { + ocnt++; + } else { + break; + } + + index++; + } + + pindex->count = ocnt; + + return pindex; + } else + return it->second; // return a pointer to the found record +} + +//----------------------------------------------------------------------------- +// s52plib implementation +//----------------------------------------------------------------------------- +s52plib::s52plib(const wxString &PLib, bool b_forceLegacy) { + m_plib_file = PLib; + + pOBJLArray = new wxArrayPtrVoid; + + condSymbolLUPArray = NULL; // Dynamic Conditional Symbology + + _symb_sym = NULL; + + m_txf_ready = false; + m_txf = NULL; + + m_chartSymbols.InitializeTables(); + m_chartSymbols.SetTextureFormat(GL_TEXTURE_2D); + InitializeNatsurHash(); + + m_bOK = !(S52_load_Plib(PLib, b_forceLegacy) == 0); + + m_bShowS57Text = false; + m_bShowS57ImportantTextOnly = false; + m_colortable_index = 0; + + _symb_symR = NULL; + bUseRasterSym = false; + + // Sensible defaults + m_nSymbolStyle = PAPER_CHART; + m_nBoundaryStyle = PLAIN_BOUNDARIES; + m_nDisplayCategory = OTHER; + m_nDepthUnitDisplay = 1; // meters + m_nHeightUnitDisplay = 0; // meters + + UpdateMarinerParams(); + + ledge = new int[2000]; + redge = new int[2000]; + + // Defaults + m_VersionMajor = 3; + m_VersionMinor = 2; + + canvas_pix_per_mm = 3.; + m_rv_scale_factor = 1.0; + + // Set up some default flags + m_bDeClutterText = false; + m_bShowAtonText = true; + m_bShowNationalTexts = false; + + m_bShowSoundg = true; + m_bShowLdisText = true; + m_bExtendLightSectors = true; + + // Set a few initial states + AddObjNoshow("M_QUAL"); + m_lightsOff = false; + m_anchorOn = true; + m_qualityOfDataOn = false; + + m_SoundingsScaleFactor = 1.0; + m_SoundingsFontSizeMM = 0; + m_soundFontDelta = 0; + + GenerateStateHash(); + + HPGL = new RenderFromHPGL(this); + + // Set defaults for OCPN version, may be overridden later + m_coreVersionMajor = 4; + m_coreVersionMinor = 6; + m_coreVersionPatch = 0; + + m_myConfig = GetStateHash(); + + // GL Options/capabilities + m_useStencil = false; + m_useStencilAP = false; + m_useScissors = false; + m_useFBO = false; + m_GLAC_VBO = false; + m_useVBO = false; + m_useGLSL = false; + m_TextureFormat = -1; + m_GLMinCartographicLineWidth = 1.0; + m_GLMinSymbolLineWidth = 1.0; + + m_display_size_mm = 300; + SetGLPolygonSmoothing(false); + SetGLLineSmoothing(false); + + m_displayScale = 1.0; + m_display_width = 0; + + // Clear the TexFont cache + unsigned int i; + for (i = 0; i < TXF_CACHE; i++) { + s_txf[i].key = 0; + s_txf[i].cache = 0; + } + m_dipfactor = 1.0; + m_ContentScaleFactor = 1.0; + m_FinalTextScaleFactor = 0; + m_TextScaleFactor = 1; + m_nTextFactor = 0; + m_isSoundingFontSet = false; + + s_color = "color"; + s_uTex = "uTex"; + s_position = "position"; + s_aUV = "aUV"; + s_TransformMatrix = "TransformMatrix"; +} + +s52plib::~s52plib() { + delete areaPlain_LAC; + delete line_LAC; + delete areaSymbol_LAC; + delete pointSimple_LAC; + delete pointPaper_LAC; + + S52_flush_Plib(); + + // Free the OBJL Array Elements + for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++) + free(pOBJLArray->Item(iPtr)); + + delete pOBJLArray; + + delete[] ledge; + delete[] redge; + + m_chartSymbols.DeleteGlobals(); + + delete HPGL; + for (int i = 0; i < TXF_CACHE; i++) { + if (s_txf[i].cache) { + delete (s_txf[i].cache); + } + } +} + +void s52plib::InitializeNatsurHash() { + std::unordered_map surmap({{1, "mud"}, + {2, "clay"}, + {3, "silt"}, + {4, "sand"}, + {5, "stone"}, + {6, "gravel"}, + {7, "pebbles"}, + {8, "cobbles"}, + {9, "rock"}, + {11, "lava"}, + {14, "coral"}, + {17, "shells"}, + {18, "boulder"}, + {56, "Bo"}, + {51, "Wd"}}); + m_natsur_hash = surmap; +} + +void s52plib::SetVPointCompat(int pix_width, int pix_height, + double view_scale_ppm, double rotation, + double clat, double clon, double chart_scale, + wxRect rv_rect, LLBBox &bbox, double ref_scale, + double display_scale) { + vp_plib.pix_width = pix_width; + vp_plib.pix_height = pix_height; + vp_plib.view_scale_ppm = view_scale_ppm; + vp_plib.rotation = rotation; + vp_plib.clat = clat; + vp_plib.clon = clon; + vp_plib.chart_scale = chart_scale; + vp_plib.rv_rect = rv_rect; + BBox = bbox; + reducedBBox = bbox; + vp_plib.ref_scale = ref_scale; + m_displayScale = display_scale; +} + +void s52plib::SetOCPNVersion(int major, int minor, int patch) { + m_coreVersionMajor = major; + m_coreVersionMinor = minor; + m_coreVersionPatch = patch; +} + +void s52plib::SetGLOptions(bool b_useStencil, bool b_useStencilAP, + bool b_useScissors, bool b_useFBO, bool b_useVBO, + int nTextureFormat, float MinCartographicLineWidth, + float MinSymbolLineWidth) { + // Set GL Options/capabilities + m_useStencil = b_useStencil; + m_useStencilAP = b_useStencilAP; + m_useScissors = b_useScissors; + m_useFBO = b_useFBO; + m_useVBO = b_useVBO; + m_useGLSL = true; + m_TextureFormat = nTextureFormat; + m_chartSymbols.SetTextureFormat(nTextureFormat); + m_GLMinCartographicLineWidth = MinCartographicLineWidth; + m_GLMinSymbolLineWidth = MinSymbolLineWidth; +} + +void s52plib::SetDIPFactor(double factor) { m_dipfactor = factor; } + +void s52plib::SetContentScaleFactor(double factor) { + m_ContentScaleFactor = factor; + if (HPGL) HPGL->SetContentScaleFactor(factor); +} + +void s52plib::SetPPMM(float ppmm) { + canvas_pix_per_mm = ppmm; + + // We need a supplemental scale factor for HPGL vector symbol rendering. + // to allow raster and vector symbols to be rendered harmoniously + // We do this empirically, making it look "nice" on average displays. + m_rv_scale_factor = 0.8; + + // Estimate the display size + m_display_size_mm = + m_display_width / GetPPMM(); // accurate enough for internal use + + m_display_size_mm /= m_displayScale; +} + +void s52plib::SetScaleFactorExp(double ChartScaleFactorExp) { + m_ChartScaleFactorExp = ChartScaleFactorExp; +} + +void s52plib::SetScaleFactorZoomMod(int chart_zoom_modifier_vector) { + m_chart_zoom_modifier_vector = chart_zoom_modifier_vector; +} + +// Various static helper methods + +void s52plib::DestroyLUP(LUPrec *pLUP) { + Rules *top = pLUP->ruleList; + DestroyRulesChain(top); + + delete pLUP; +} + +void s52plib::DestroyRulesChain(Rules *top) { + while (top != NULL) { + Rules *Rtmp = top->next; + + free(top->INST0); // free the Instruction string head + + if (top->b_private_razRule) // need to free razRule? + { + Rule *pR = top->razRule; + delete pR->exposition.LXPO; + + free(pR->vector.LVCT); + + delete pR->bitmap.SBTM; + + free(pR->colRef.SCRF); + + ClearRulesCache(pR); + + free(pR); + } + + free(top); + top = Rtmp; + } +} + +DisCat s52plib::findLUPDisCat(const char *objectName, LUPname TNAM) { + LUPArrayContainer *plac = SelectLUPArrayContainer(TNAM); + + wxArrayOfLUPrec *LUPArray = SelectLUPARRAY(TNAM); + + // Find the first matching entry in the LUP Array + int index = 0; + int index_max = LUPArray->GetCount(); + LUPrec *LUPCandidate; + + while (index < index_max) { + LUPCandidate = LUPArray->Item(index); + if (!strcmp(objectName, LUPCandidate->OBCL)) { + return LUPCandidate->DISC; + } + index++; + } + + return (DisCat)(-1); +} + +bool s52plib::GetAnchorOn() { + // Investigate and report the logical condition that "Anchoring Condition" is + // shown + + int old_vis = 0; + + if (MARINERS_STANDARD == GetDisplayCategory()) { + old_vis = m_anchorOn; + } else if (OTHER == GetDisplayCategory()) + old_vis = true; + + // other cat + // const char * categories[] = { "ACHBRT", "ACHARE", "CBLSUB", "PIPARE", + // "PIPSOL", "TUNNEL", "SBDARE" }; + + old_vis &= !IsObjNoshow("SBDARE"); + + return (old_vis != 0); +} + +bool s52plib::GetQualityOfData() { + // Investigate and report the logical condition that "Quality of Data + // Condition" is shown + + int old_vis = 0; + + if (MARINERS_STANDARD == GetDisplayCategory()) { + for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++) { + OBJLElement *pOLE = (OBJLElement *)(pOBJLArray->Item(iPtr)); + if (!strncmp(pOLE->OBJLName, "M_QUAL", 6)) { + old_vis = pOLE->nViz; + break; + } + } + } else if (OTHER == GetDisplayCategory()) + old_vis = true; + + old_vis &= !IsObjNoshow("M_QUAL"); + + return (old_vis != 0); +} + +void s52plib::SetGLRendererString(const wxString &renderer) { + m_renderer_string = renderer; + + // No chart type in current use requires VBO for GLAC rendering + // Experimentation has shown that VBO is slower for GLAC rendering, + // since the per-object state change of glBindBuffer() is slow + // on most hardware, especially RPi. + // However, we have found that NVidea GPUs + // perform much better with VBO on GLAC operations, so set that up. + + if ((renderer.Upper().Contains("NVIDIA")) || + (renderer.Upper().Contains("QUADRO")) || + (renderer.Upper().Contains("GEFORCE"))) + m_GLAC_VBO = true; + + // ANGLE/Vulkan implementations have also been found to require/prefer + // VBOs for stability and performance when rendering dense tessellated + // area geometry. + if (renderer.Upper().Contains("ANGLE")) + m_GLAC_VBO = true; +} + +/* + Update the S52 Conditional Symbology Parameter Set to reflect the + current state of the library member options. + */ + +void s52plib::UpdateMarinerParams(void) { + // Symbol Style + if (SIMPLIFIED == m_nSymbolStyle) + S52_setMarinerParam(S52_MAR_SYMPLIFIED_PNT, 1.0); + else + S52_setMarinerParam(S52_MAR_SYMPLIFIED_PNT, 0.0); + + // Boundary Style + if (SYMBOLIZED_BOUNDARIES == m_nBoundaryStyle) + S52_setMarinerParam(S52_MAR_SYMBOLIZED_BND, 1.0); + else + S52_setMarinerParam(S52_MAR_SYMBOLIZED_BND, 0.0); +} + +void s52plib::GenerateStateHash() { + unsigned char state_buffer[512]; // Needs to be at least this big... + memset(state_buffer, 0, sizeof(state_buffer)); + + // int time = ::wxGetUTCTime(); + // memcpy(state_buffer, &time, sizeof(int)); + + size_t offset = sizeof(int); // skipping the time int, first element + + for (int i = 0; i < S52_MAR_NUM; i++) { + if ((offset + sizeof(double)) < sizeof(state_buffer)) { + double t = S52_getMarinerParam((S52_MAR_param_t)i); + memcpy(&state_buffer[offset], &t, sizeof(double)); + offset += sizeof(double); + } + } + + for (unsigned int i = 0; i < m_noshow_array.GetCount(); i++) { + if ((offset + 6) < sizeof(state_buffer)) { + memcpy(&state_buffer[offset], m_noshow_array[i].obj, 6); + offset += 6; + } + } + + if (offset + sizeof(bool) < sizeof(state_buffer)) { + memcpy(&state_buffer[offset], &m_bShowSoundg, sizeof(bool)); + offset += sizeof(bool); + } + + if (offset + sizeof(bool) < sizeof(state_buffer)) { + memcpy(&state_buffer[offset], &m_bShowS57Text, sizeof(bool)); + offset += sizeof(bool); + } + + if (offset + sizeof(bool) < sizeof(state_buffer)) { + memcpy(&state_buffer[offset], &m_bShowS57ImportantTextOnly, sizeof(bool)); + offset += sizeof(bool); + } + + if (offset + sizeof(bool) < sizeof(state_buffer)) { + memcpy(&state_buffer[offset], &m_bDeClutterText, sizeof(bool)); + offset += sizeof(bool); + } + + if (offset + sizeof(bool) < sizeof(state_buffer)) { + memcpy(&state_buffer[offset], &m_bShowNationalTexts, sizeof(bool)); + offset += sizeof(bool); + } + + if (offset + sizeof(bool) < sizeof(state_buffer)) { + memcpy(&state_buffer[offset], &m_bShowAtonText, sizeof(bool)); + offset += sizeof(bool); + } + + if (offset + sizeof(bool) < sizeof(state_buffer)) { + memcpy(&state_buffer[offset], &m_bShowLdisText, sizeof(bool)); + offset += sizeof(bool); + } + + if (offset + sizeof(bool) < sizeof(state_buffer)) { + memcpy(&state_buffer[offset], &m_bExtendLightSectors, sizeof(bool)); + offset += sizeof(bool); + } + + if (offset + sizeof(bool) < sizeof(state_buffer)) { + memcpy(&state_buffer[offset], &m_nSoundingFactor, sizeof(int)); + offset += sizeof(int); + } + + if (offset + sizeof(bool) < sizeof(state_buffer)) { + memcpy(&state_buffer[offset], &m_nTextFactor, sizeof(int)); + offset += sizeof(int); + } + + if (offset + sizeof(bool) < sizeof(state_buffer)) { + memcpy(&state_buffer[offset], &m_nDisplayCategory, sizeof(int)); + offset += sizeof(int); + } + + long p = (long)GetS52Utils()->GetDefaultFont(_("ChartTexts"), 0); + if (offset + sizeof(long) < sizeof(state_buffer)) { + memcpy(&state_buffer[offset], &p, sizeof(long)); + offset += sizeof(long); + } + + m_state_hash = crc32buf(state_buffer, offset); +} + +wxArrayOfLUPrec *s52plib::SelectLUPARRAY(LUPname TNAM) { + switch (TNAM) { + case SIMPLIFIED: + return pointSimple_LAC->GetLUPArray(); + case PAPER_CHART: + return pointPaper_LAC->GetLUPArray(); + case LINES: + return line_LAC->GetLUPArray(); + case PLAIN_BOUNDARIES: + return areaPlain_LAC->GetLUPArray(); + case SYMBOLIZED_BOUNDARIES: + return areaSymbol_LAC->GetLUPArray(); + default: + return NULL; + } +} + +LUPArrayContainer *s52plib::SelectLUPArrayContainer(LUPname TNAM) { + switch (TNAM) { + case SIMPLIFIED: + return pointSimple_LAC; + case PAPER_CHART: + return pointPaper_LAC; + case LINES: + return line_LAC; + case PLAIN_BOUNDARIES: + return areaPlain_LAC; + case SYMBOLIZED_BOUNDARIES: + return areaSymbol_LAC; + default: + return NULL; + } +} + +extern Cond condTable[]; + +LUPrec *s52plib::FindBestLUP(wxArrayOfLUPrec *LUPArray, unsigned int startIndex, + unsigned int count, S57Obj *pObj, bool bStrict) { + // Check the parameters + if (0 == count) return NULL; + if (startIndex >= LUPArray->GetCount()) return NULL; + + // setup default return to the first LUP that matches Feature name. + LUPrec *LUP = LUPArray->Item(startIndex); + + int nATTMatch = 0; + int countATT = 0; + bool bmatch_found = false; + + if (pObj->att_array == NULL) + goto check_LUP; // object has no attributes to compare, so return "best" + // LUP + + for (unsigned int i = 0; i < count; ++i) { + LUPrec *LUPCandidate = LUPArray->Item(startIndex + i); + + if (!LUPCandidate->ATTArray.size()) + continue; // this LUP has no attributes coded + + countATT = 0; + char *currATT = pObj->att_array; + int attIdx = 0; + + for (unsigned int iLUPAtt = 0; iLUPAtt < LUPCandidate->ATTArray.size(); + iLUPAtt++) { + // Get the LUP attribute name + const char *slatc = LUPCandidate->ATTArray[iLUPAtt].c_str(); + + if (slatc && (strlen(slatc) < 6)) + goto next_LUP_Attr; // LUP attribute value not UTF8 convertible (never + // seen in PLIB 3.x) + + if (slatc) { + const char *slatv = slatc + 6; + while (attIdx < pObj->n_attr) { + if (0 == strncmp(slatc, currATT, 6)) { + // OK we have an attribute name match + + bool attValMatch = false; + + // special case (i) + if (!strncmp(slatv, " ", 1)) { // any object value will match wild + // card (S52 para 8.3.3.4) + ++countATT; + goto next_LUP_Attr; + } + + // special case (ii) + // TODO Find an ENC with "UNKNOWN" DRVAL1 or DRVAL2 and debug this + // code + if (!strncmp(slatv, "?", + 1)) { // if LUP attribute value is "undefined" + + // Match if the object does NOT contain this attribute + goto next_LUP_Attr; + } + + // checking against object attribute value + S57attVal *v = (pObj->attVal->Item(attIdx)); + + switch (v->valType) { + case OGR_INT: // S57 attribute type 'E' enumerated, 'I' integer + { + int LUP_att_val = atoi(slatv); + if (LUP_att_val == *(int *)(v->value)) attValMatch = true; + break; + } + + case OGR_INT_LST: // S57 attribute type 'L' list: comma separated + // integer + { + int a; + char ss[41]; + strncpy(ss, slatv, 39); + ss[40] = '\0'; + char *s = &ss[0]; + + int *b = (int *)v->value; + sscanf(s, "%d", &a); + + while (*s != '\0') { + if (a == *b) { + sscanf(++s, "%d", &a); + b++; + attValMatch = true; + + } else + attValMatch = false; + } + break; + } + case OGR_REAL: // S57 attribute type'F' float + { + double obj_val = *(double *)(v->value); + float att_val = atof(slatv); + if (fabs(obj_val - att_val) < 1e-6) + if (obj_val == att_val) attValMatch = true; + break; + } + + case OGR_STR: // S57 attribute type'A' code string, 'S' free text + { + // Strings must be exact match + // n.b. OGR_STR is used for S-57 attribute type 'L', + // comma-separated list + + // wxString cs( (char *) v->value, wxConvUTF8 ); // Attribute + // from object if( LATTC.Mid( 6 ) == cs ) + if (!strcmp((char *)v->value, slatv)) attValMatch = true; + break; + } + + default: + break; + } // switch + + // value match + if (attValMatch) ++countATT; + + goto next_LUP_Attr; + } // if attribute name match + + // Advance to the next S57obj attribute + currATT += 6; + ++attIdx; + + } // while + } // if + + next_LUP_Attr: + + currATT = pObj->att_array; // restart the object attribute list + attIdx = 0; + } // for iLUPAtt + + // Create a "match score", defined as fraction of candidate LUP + // attributes actually matched by feature. Used later for resolving + // "ties" + + int nattr_matching_on_candidate = countATT; + int nattrs_on_candidate = LUPCandidate->ATTArray.size(); + double candidate_score = + (1. * nattr_matching_on_candidate) / (1. * nattrs_on_candidate); + + // According to S52 specs, match must be perfect, + // and the first 100% match is selected + if (candidate_score == 1.0) { + LUP = LUPCandidate; + bmatch_found = true; + break; // selects the first 100% match + } + + } // for loop + +check_LUP: + // In strict mode, we require at least one attribute to match exactly + + if (bStrict) { + if (nATTMatch == 0) // nothing matched + LUP = NULL; + } else { + // If no match found, return the first LUP in the list which has no + // attributes + if (!bmatch_found) { + for (unsigned int j = 0; j < count; ++j) { + LUPrec *LUPtmp = NULL; + + LUPtmp = LUPArray->Item(startIndex + j); + if (!LUPtmp->ATTArray.size()) { + return LUPtmp; + } + } + } + } + + return LUP; +} + +// scan foward stop on ; or end-of-record +#define SCANFWRD \ + while (!(*str == ';' || *str == '\037')) ++str; + +#define INSTRUCTION(s, t) \ + if (0 == strncmp(s, str, 2)) { \ + str += 3; \ + r->ruleType = t; \ + r->INSTstr = str; + +Rules *s52plib::StringToRules(const wxString &str_in) { + wxCharBuffer buffer = str_in.ToUTF8(); + if (!buffer.data()) return NULL; + + size_t len = strlen(buffer.data()); + char *str0 = (char *)calloc(len + 1, 1); + memcpy(str0, buffer.data(), len); + char *str = str0; + + Rules *top; + Rules *last; + char strk[20]; + + // Allocate and pre-clear the Rules structure + Rules *r = (Rules *)calloc(1, sizeof(Rules)); + top = r; + last = top; + + r->INST0 = str0; // save the head for later free + + while (*str != '\0') { + if (r->ruleType) // in the loop, r has been used + { + r = (Rules *)calloc(1, sizeof(Rules)); + last->next = r; + last = r; + } + + // parse Symbology instruction in string + + // Special Case for Circular Arc, (opencpn private) + // Allocate a Rule structure to be used to hold a cached bitmap of the + // created symbol + INSTRUCTION("CA", RUL_ARC_2C) + r->razRule = (Rule *)calloc(1, sizeof(Rule)); + r->b_private_razRule = true; // mark this raxRule to be free'd later + SCANFWRD + } + + // Special Case for MultPoint Soundings + INSTRUCTION("MP", RUL_MUL_SG) + SCANFWRD +} + +// SHOWTEXT +INSTRUCTION("TX", RUL_TXT_TX) +SCANFWRD +} + +INSTRUCTION("TE", RUL_TXT_TE) +SCANFWRD +} + +// SHOWPOINT + +if (0 == strncmp("SY", str, 2)) { + str += 3; + r->ruleType = RUL_SYM_PT; + r->INSTstr = str; + + strncpy(strk, str, 8); + strk[8] = 0; + wxString key(strk, wxConvUTF8); + + r->razRule = (*_symb_sym)[key]; + + if (r->razRule == NULL) r->razRule = (*_symb_sym)[_T ( "QUESMRK1" )]; + + SCANFWRD +} + +// SHOWLINE +INSTRUCTION("LS", RUL_SIM_LN) +SCANFWRD +} + +INSTRUCTION("LC", RUL_COM_LN) +strncpy(strk, str, 8); +strk[8] = 0; +wxString key(strk, wxConvUTF8); + +r->razRule = (*_line_sym)[key]; + +// Substitute generic question mark for undefined symbology +if (r->razRule == NULL) { + r->razRule = (*_symb_sym)[_T ( "QUESMRK1" )]; + r->razRule->pos.line.bnbox_w.SYHL = 600; // manually set the + // rendered symbol length +} +SCANFWRD +} + +// SHOWAREA +INSTRUCTION("AC", RUL_ARE_CO) +SCANFWRD +} + +INSTRUCTION("AP", RUL_ARE_PA) +strncpy(strk, str, 8); +strk[8] = 0; +wxString key(strk, wxConvUTF8); + +r->razRule = (*_patt_sym)[key]; +if (r->razRule == NULL) r->razRule = (*_patt_sym)[_T ( "QUESMRK1V" )]; +SCANFWRD +} + +// CALLSYMPROC + +if (0 == strncmp("CS", str, 2)) { + str += 3; + r->ruleType = RUL_CND_SY; + r->INSTstr = str; + + // INSTRUCTION("CS",RUL_CND_SY) + char stt[9]; + strncpy(stt, str, 8); + stt[8] = 0; + wxString index(stt, wxConvUTF8); + r->razRule = (*_cond_sym)[index]; + if (r->razRule == NULL) r->razRule = (*_cond_sym)[_T ( "QUESMRK1" )]; + SCANFWRD +} + +++str; +} + +// If it should happen that no rule is built, delete the initially allocated +// rule +if (0 == top->ruleType) { + if (top->INST0) free(top->INST0); + + free(top); + + top = NULL; +} + +// Traverse the entire rule set tree, pruning after first unallocated (dead) +// rule +r = top; +while (r) { + if (0 == r->ruleType) { + free(r); + last->next = NULL; + break; + } + + last = r; + Rules *n = r->next; + r = n; +} + +// Traverse the entire rule set tree, adding sequence numbers +r = top; +int i = 0; +while (r) { + r->n_sequence = i++; + + r = r->next; +} + +return top; +} + +int s52plib::_LUP2rules(LUPrec *LUP, S57Obj *pObj) { + if (NULL == LUP) return -1; + // check if already parsed + if (LUP->ruleList != NULL) { + return 0; + } + + if (!LUP->INST.IsEmpty()) { + Rules *top = StringToRules(LUP->INST); + LUP->ruleList = top; + + return 1; + } else + return 0; +} + +int s52plib::S52_load_Plib(const wxString &PLib, bool b_forceLegacy) { + pAlloc = new wxArrayPtrVoid; + + // Create the Rule Lookup Hash Tables + _line_sym = new RuleHash; // line + _patt_sym = new RuleHash; // pattern + _symb_sym = new RuleHash; // symbol + _cond_sym = new RuleHash; // conditional + + line_LAC = new LUPArrayContainer; + areaPlain_LAC = new LUPArrayContainer; + areaSymbol_LAC = new LUPArrayContainer; + pointSimple_LAC = new LUPArrayContainer; + pointPaper_LAC = new LUPArrayContainer; + + condSymbolLUPArray = + new wxArrayOfLUPrec(CompareLUPObjects); // dynamic Cond Sym LUPs + + m_unused_color.R = 2; + m_unused_color.G = 2; + m_unused_color.B = 2; + m_unused_wxColor.Set(2, 2, 2); + + if (!m_chartSymbols.LoadConfigFile(this, PLib)) { + wxString msg(_T("Could not load XML PLib symbol file: ")); + msg += PLib; + wxLogMessage(msg); + + return 0; + } + + // Initialize the _cond_sym Hash Table from the jump table found in + // S52CNSY.CPP Hash Table indices are the literal CS Strings, e.g. + // "RESARE02" Hash Results Values are the Rule *, i.e. the CS procedure + // entry point + + for (int i = 0; condTable[i].condInst != NULL; ++i) { + wxString index(condTable[i].name, wxConvUTF8); + (*_cond_sym)[index] = (Rule *)(condTable[i].condInst); + } + + wxString s57data_dir = *GetpSharedDataLocation(); + s57data_dir += _T("s57data"); + + wxString oc_file(s57data_dir); + oc_file.Append(_T("/s57objectclasses.csv")); + + PreloadOBJLFromCSV(oc_file); + + return 1; +} + +void s52plib::ClearRulesCache( + Rule *pR) // Clear out any existing cached symbology +{ + switch (pR->parm0) { + case ID_wxBitmap: { + wxBitmap *pbm = (wxBitmap *)(pR->pixelPtr); + delete pbm; + pR->pixelPtr = NULL; + pR->parm0 = ID_EMPTY; + break; + } + case ID_RGBA: { + unsigned char *p = (unsigned char *)(pR->pixelPtr); + free(p); + pR->pixelPtr = NULL; + pR->parm0 = ID_EMPTY; + break; + } + case ID_GL_PATT_SPEC: { + render_canvas_parms *pp = (render_canvas_parms *)(pR->pixelPtr); + free(pp->pix_buff); +#ifdef ocpnUSE_GL + if (pp->OGL_tex_name) glDeleteTextures(1, (GLuint *)&pp->OGL_tex_name); +#endif + delete pp; + pR->pixelPtr = NULL; + pR->parm0 = ID_EMPTY; + break; + } + case ID_RGB_PATT_SPEC: { + render_canvas_parms *pp = (render_canvas_parms *)(pR->pixelPtr); + free(pp->pix_buff); + delete pp; + pR->pixelPtr = NULL; + pR->parm0 = ID_EMPTY; + break; + } + case ID_EMPTY: + break; + default: + assert(false); + break; + } +} + +void s52plib::DestroyPatternRuleNode(Rule *pR) { DestroyRuleNode(pR); } + +void s52plib::DestroyRuleNode(Rule *pR) { + if (!pR) return; + + delete pR->exposition.LXPO; + + free(pR->vector.LVCT); + + delete pR->bitmap.SBTM; + + free(pR->colRef.SCRF); + + ClearRulesCache(pR); // Clear out any existing cached symbology +} + +void s52plib::DestroyRules(RuleHash *rh) { + RuleHash::iterator it; + Rule *pR; + + for (it = (*rh).begin(); it != (*rh).end(); ++it) { + pR = it->second; + DestroyRuleNode(pR); + } + + rh->clear(); + delete rh; +} + +void s52plib::FlushSymbolCaches(const ChartCtx &ctx) { + m_chartSymbols.LoadRasterFileForColorTable(m_colortable_index, true, ctx); + RuleHash *rh = _symb_sym; + + if (!rh) return; + + RuleHash::iterator it; + Rule *pR; + + for (it = (*rh).begin(); it != (*rh).end(); ++it) { + pR = it->second; + if (pR) ClearRulesCache(pR); + } + + // Flush any pattern definitions + rh = _patt_sym; + + if (!rh) return; + + for (it = (*rh).begin(); it != (*rh).end(); ++it) { + pR = it->second; + if (pR) ClearRulesCache(pR); + } + + // OpenGL Hashmaps + CARC_Hash::iterator ita; + for (ita = m_CARC_hashmap.begin(); ita != m_CARC_hashmap.end(); ++ita) { + CARC_Buffer buffer = ita->second; + delete[] buffer.data; + } + m_CARC_hashmap.clear(); + + // Flush all texFonts + TexFont *f_cache = 0; + unsigned int i; + for (i = 0; i < TXF_CACHE; i++) { + if (s_txf[i].key != 0) { + f_cache = s_txf[i].cache; + f_cache->Delete(); + s_txf[i].key = 0; + } + } +} + +void s52plib::DestroyPattRules(RuleHash *rh) { DestroyRules(rh); } + +void s52plib::DestroyLUPArray(wxArrayOfLUPrec *pLUPArray) { + if (pLUPArray) { + for (unsigned int il = 0; il < pLUPArray->GetCount(); il++) + DestroyLUP(pLUPArray->Item(il)); + + pLUPArray->Clear(); + + delete pLUPArray; + } +} + +void s52plib::ClearCNSYLUPArray(void) { + if (condSymbolLUPArray) { + for (unsigned int i = 0; i < condSymbolLUPArray->GetCount(); i++) + DestroyLUP(condSymbolLUPArray->Item(i)); + + condSymbolLUPArray->Clear(); + } +} + +bool s52plib::S52_flush_Plib() { + if (!m_bOK) return false; + +#ifdef ocpnUSE_GL + // OpenGL Hashmaps + CARC_Hash::iterator ita; + for (ita = m_CARC_hashmap.begin(); ita != m_CARC_hashmap.end(); ++ita) { + CARC_Buffer buffer = ita->second; + delete[] buffer.data; + } + m_CARC_hashmap.clear(); + +#endif + + DestroyLUPArray(condSymbolLUPArray); + + // Destroy Rules + DestroyRules(_line_sym); + DestroyPattRules(_patt_sym); + DestroyRules(_symb_sym); + + if (_symb_symR) DestroyRules(_symb_symR); + + // Special case for CS + _cond_sym->clear(); + delete (_cond_sym); + + for (unsigned int ipa = 0; ipa < pAlloc->GetCount(); ipa++) { + void *t = pAlloc->Item(ipa); + free(t); + } + + pAlloc->clear(); + delete pAlloc; + + return TRUE; +} + +LUPrec *s52plib::S52_LUPLookup(LUPname LUP_Name, const char *objectName, + S57Obj *pObj, bool bStrict) { + LUPrec *LUP = NULL; + + LUPArrayContainer *plac = SelectLUPArrayContainer(LUP_Name); + + LUPHashIndex *hip = plac->GetArrayIndexHelper(objectName); + int nLUPs = hip->count; + int nStartIndex = hip->n_start; + + LUP = FindBestLUP(plac->GetLUPArray(), nStartIndex, nLUPs, pObj, bStrict); + + return LUP; +} + +void s52plib::SetPLIBColorScheme(ColorScheme cs, const ChartCtx &ctx) { + wxString SchemeName; + switch (cs) { + case GLOBAL_COLOR_SCHEME_DAY: + SchemeName = _T("DAY"); + break; + case GLOBAL_COLOR_SCHEME_DUSK: + SchemeName = _T("DUSK"); + break; + case GLOBAL_COLOR_SCHEME_NIGHT: + SchemeName = _T("NIGHT"); + break; + default: + SchemeName = _T("DAY"); + break; + } + + SetPLIBColorScheme(SchemeName, ctx); +} + +void s52plib::SetPLIBColorScheme(wxString scheme, const ChartCtx &ctx) { + wxString str_find; + str_find = scheme; + m_colortable_index = 0; // default is the first color in the table + + // Of course, it also depends on the plib version... + // plib version 3.2 calls "DAY" colr as "DAY_BRIGHT" + + if ((GetMajorVersion() == 3) && (GetMinorVersion() == 2)) { + if (scheme.IsSameAs(_T ( "DAY" ))) str_find = _T ( "DAY_BRIGHT" ); + } + m_colortable_index = m_chartSymbols.FindColorTable(scheme); + + m_chartSymbols.SetColorTableIndex(m_colortable_index, false, ctx); + + m_ColorScheme = scheme; +} + +S52color *s52plib::getColor(const char *colorName) { + S52color *c; + c = m_chartSymbols.GetColor(colorName, m_colortable_index); + return c; +} + +wxColour s52plib::getwxColour(const wxString &colorName) { + wxColor c; + c = m_chartSymbols.GetwxColor(colorName, m_colortable_index); + return c; +} + +//---------------------------------------------------------------------------------- +// +// Object Rendering Module +// +//---------------------------------------------------------------------------------- + +//----------------------------- +// +// S52 TEXT COMMAND WORD PARSER +// +//----------------------------- +#define APOS '\047' +#define MAXL 512 + +char *s52plib::_getParamVal(ObjRazRules *rzRules, char *str, char *buf, int bsz) +// Symbology Command Word Parameter Value Parser. +// +// str: psz to Attribute of interest +// +// Results:Put in 'buf' one of: +// 1- LUP constant value, +// 2- ENC value, +// 3- LUP default value. +// Return pointer to the next field in the string (delim is ','), NULL to abort +{ + wxString value; + int defval = 0; // default value + int len = 0; + char *ret_ptr = str; + char *tmp = buf; + + if (!buf) return NULL; + + buf[0] = 0; + // parse constant parameter with concatenation operator "'" + if (str != NULL) { + if (*ret_ptr == APOS) { + ret_ptr++; + while (*ret_ptr != APOS && *ret_ptr != '\0' && len < (bsz - 1)) { + ++len; + *tmp++ = *ret_ptr++; + } + *tmp = '\0'; + ret_ptr++; // skip "'" + ret_ptr++; // skip "," + + return ret_ptr; + } + + while (*ret_ptr != ',' && *ret_ptr != ')' && *ret_ptr != '\0' && + len < (bsz - 1)) { + *tmp++ = *ret_ptr++; + ++len; + } + *tmp = '\0'; + + ret_ptr++; // skip ',' or ')' + } + if (len < 6) return ret_ptr; + + // chop string if default value present + if (len > 6 && *(buf + 6) == '=') { + *(buf + 6) = '\0'; + defval = 1; + } + + value = rzRules->obj->GetAttrValueAsString(buf); + wxCharBuffer buffer = value.ToUTF8(); + if (!buffer.data()) return ret_ptr; + + if (value.IsEmpty()) { + if (defval) + _getParamVal(rzRules, buf + 7, buf, + bsz - 7); // default value --recursion + else { + return NULL; // abort + } + } else { + // Special case for conversion of vertical (height) attributes. + // Inputs are meters; convert to user's height unit (meters/feet) as + // selected in s52plib. + if ((!strncmp(buf, "VERCLR", 6)) || (!strncmp(buf, "VERCCL", 6)) || + (!strncmp(buf, "VERCOP", 6)) || (!strncmp(buf, "ELEVAT", 6)) || + (!strncmp(buf, "HEIGHT", 6))) { + if (m_nHeightUnitDisplay == 1) { // feet + double h_val_m; + if (value.ToDouble(&h_val_m)) { + double ft_val = h_val_m * 3.280839895013123; // meters to feet + value.Printf(_T("%4.1f"), ft_val); + } + } + // else meters: leave as-is + } + + // special case when ENC returns an index for particular attribute types + if (!strncmp(buf, "NATSUR", 6)) { + wxString natsur_att(_T ( "NATSUR" )); + wxString result; + wxString svalue = value; + wxStringTokenizer tkz(svalue, _T ( "," )); + + int icomma = 0; + while (tkz.HasMoreTokens()) { + if (icomma) result += _T ( "," ); + + wxString token = tkz.GetNextToken(); + long i; + if (token.ToLong(&i)) { + std::string snat; + snat = m_natsur_hash[(int)i]; + + wxString nat(snat.c_str()); + if (!nat.IsEmpty()) + result += nat; // value from ENC + else + result += _T ( "unk" ); + } else + result += _T ( "unk" ); + + icomma++; + } + + value = result; + } + + wxCharBuffer buffer = value.ToUTF8(); + if (buffer.data()) { + unsigned int len = wxMin(strlen(buffer.data()), (unsigned int)bsz - 1); + memcpy(buf, buffer.data(), len); + buf[len] = 0; + } else + *buf = 0; + } + + return ret_ptr; +} + +char *s52plib::_parseTEXT(ObjRazRules *rzRules, S52_TextC *text, char *str0) { + char buf[MAXL]; // output string + + char *str = str0; + if (text) { + memset(buf, 0, 4); + str = _getParamVal(rzRules, str, &text->hjust, MAXL); // HJUST + str = _getParamVal(rzRules, str, &text->vjust, MAXL); // VJUST + str = _getParamVal(rzRules, str, &text->space, MAXL); // SPACE + + // CHARS + str = _getParamVal(rzRules, str, buf, MAXL); + text->style = buf[0]; + text->weight = buf[1]; + text->width = buf[2]; + text->bsize = atoi(buf + 3); + + str = _getParamVal(rzRules, str, buf, MAXL); + text->xoffs = atoi(buf); + str = _getParamVal(rzRules, str, buf, MAXL); + text->yoffs = atoi(buf); + str = _getParamVal(rzRules, str, buf, MAXL); + text->pcol = getColor(buf); + str = _getParamVal(rzRules, str, buf, MAXL); + text->dis = atoi(buf); // Text Group, used for "Important" text detection + } + return str; +} + +S52_TextC *s52plib::S52_PL_parseTX(ObjRazRules *rzRules, Rules *rules, + char *cmd) { + S52_TextC *text = NULL; + char *str = NULL; + char val[MAXL]; // value of arg + char strnobjnm[7] = {"NOBJNM"}; + char valn[MAXL]; // value of arg + + valn[0] = 0; + str = (char *)rules->INSTstr; + + if (m_bShowNationalTexts && + NULL != + strstr(str, + "OBJNAM")) // in case user wants the national text shown and + // the rule contains OBJNAM, try to get the value + { + _getParamVal(rzRules, strnobjnm, valn, MAXL); + if (!strcmp(strnobjnm, valn)) + valn[0] = '\0'; // NOBJNM is not defined + else + valn[MAXL - 1] = '\0'; // make sure the string terminates + } + + str = _getParamVal(rzRules, str, val, MAXL); // get ATTRIB list + + if (NULL == str) + return 0; // abort this command word if mandatory param absent + + val[MAXL - 1] = '\0'; // make sure the string terminates + + text = new S52_TextC; + str = _parseTEXT(rzRules, text, str); + if (NULL != text) { + if (valn[0] != '\0') { + text->frmtd = wxString(valn, wxConvUTF8); + text->bnat = true; + } else { + text->frmtd = wxString(val, wxConvUTF8); + text->bnat = false; + } + } + + // We check to see if the formatted text has any "special" characters + wxCharBuffer buf = text->frmtd.ToUTF8(); + + unsigned int n = text->frmtd.Length(); + for (unsigned int i = 0; i < n; ++i) { + unsigned char c = buf.data()[i]; + if (c > 127) { + text->bspecial_char = true; + break; + } + } + + return text; +} + +S52_TextC *s52plib::S52_PL_parseTE(ObjRazRules *rzRules, Rules *rules, + char *cmd) +// same as S52_PL_parseTX put parse 'C' format first +{ + char arg[MAXL]; // ATTRIB list + char fmt[MAXL]; // FORMAT + char buf[MAXL]; // output string + char *b = buf; + char *parg = arg; + char *pf = fmt; + S52_TextC *text = NULL; + + char *str = (char *)rules->INSTstr; + + if (str && *str) { + str = _getParamVal(rzRules, str, fmt, MAXL); // get FORMAT + + str = _getParamVal(rzRules, str, arg, MAXL); // get ATTRIB list + if (NULL == str) + return 0; // abort this command word if mandatory param absent + + //*b = *pf; + while (*pf != '\0') { + // begin a convertion specification + if (*pf == '%') { + char val[MAXL]; // value of arg + char tmp[MAXL] = {'\0'}; // temporary format string + char *t = tmp; + int cc = 0; // 1 == Conversion Character found + //*t = *pf; + + // get value for this attribute + parg = _getParamVal(rzRules, parg, val, MAXL); + if (NULL == parg) return 0; // abort + + if (0 == strcmp(val, "2147483641")) return 0; + + *t = *pf; // stuff the '%' + + // scan for end at convertion character + do { + *++t = *++pf; // fill conver spec + + switch (*pf) { + case 'c': + case 's': + b += sprintf(b, tmp, val); + cc = 1; + break; + case 'f': + b += sprintf(b, tmp, atof(val)); + cc = 1; + break; + case 'd': + case 'i': + b += sprintf(b, tmp, atoi(val)); + cc = 1; + break; + } + } while (!cc); + pf++; // skip conv. char + + } else + *b++ = *pf++; + } + + *b = 0; + text = new S52_TextC; + str = _parseTEXT(rzRules, text, str); + if (NULL != text) text->frmtd = wxString(buf, wxConvUTF8); + + // We check to see if the formatted text has any "special" characters + wxCharBuffer buf = text->frmtd.ToUTF8(); + + unsigned int n = text->frmtd.Length(); + for (unsigned int i = 0; i < n; ++i) { + unsigned char c = buf.data()[i]; + if (c > 127) { + text->bspecial_char = true; + break; + } + } + } + + return text; +} + +static void rotate(wxRect *r, VPointCompat const *vp) { + float cx = vp->pix_width / 2.; + float cy = vp->pix_height / 2.; + float c = cosf(vp->rotation); + float s = sinf(vp->rotation); + float x = r->GetX() - cx; + float y = r->GetY() - cy; + r->SetX(x * c - y * s + cx); + r->SetY(x * s + y * c + cy); +} + +bool s52plib::RenderText(wxDC *pdc, S52_TextC *ptext, double x, double y, + wxRect2D *pRectDrawn, S57Obj *pobj, bool bCheckOverlap) { +#ifdef DrawText +#undef DrawText +#define FIXIT +#endif + bool bdraw = true; + + wxFont *scaled_font = ptext->pFont; + wxCoord w_scaled = 0; + wxCoord h_scaled = 0; + wxCoord descent = 0; + wxCoord exlead = 0; + + // FIXME(plib) + double sfactor = 1; // vp_plib.ref_scale / vp_plib.chart_scale; + double scale_factor = wxMax((sfactor) / 4., 1.); + + // Place an upper bound on the scaled text size + scale_factor = wxMin(scale_factor, 4); + + scale_factor /= m_dipfactor; + scale_factor *= m_TextScaleFactor; + + // Has there been a change in scale factor by UI? + if (scale_factor != m_FinalTextScaleFactor) { + ptext->texobj = 0; // This will leak, but only a little + m_FinalTextScaleFactor = scale_factor; + + for (unsigned int i = 0; i < TXF_CACHE; i++) { + s_txf[i].key = 0; + s_txf[i].cache = 0; + } + } + + if (!pdc) // OpenGL + { +#ifdef ocpnUSE_GL + + bool b_force_no_texfont = false; + if (ptext->bspecial_char) b_force_no_texfont = true; + + // Extensive profiling has shown that rendering the full text string + // atomically is much faster than rendering glyph-by-glyph. + // Accordingly, switch to this method for all cases. + b_force_no_texfont = true; + int tex_w, tex_h; + if (b_force_no_texfont) { + if (!ptext->texobj) { // is texture ready? + + int old_size = ptext->pFont->GetPointSize(); + int new_size = old_size * scale_factor / m_ContentScaleFactor; + + scaled_font = GetS52Utils()->GetScaledFont( + new_size, ptext->pFont->GetFamily(), ptext->pFont->GetStyle(), + ptext->pFont->GetWeight(), ptext->pFont->GetFaceName(), 1.0); + // FindOrCreateFont_PlugIn( + // new_size, ptext->pFont->GetFamily(), ptext->pFont->GetStyle(), + // ptext->pFont->GetWeight(), false, ptext->pFont->GetFaceName()); + wxScreenDC sdc; + sdc.GetTextExtent(ptext->frmtd, &w_scaled, &h_scaled, &descent, &exlead, + scaled_font); // measure the text + + // We cannot get the font ascent value to remove the interline spacing + // from the font "height". So we have to estimate based on conventional + // Arial metrics + ptext->rendered_char_height = + (h_scaled - descent) * 8 / 10 * m_dipfactor; + + ptext->text_width = w_scaled * m_dipfactor; + ptext->text_height = h_scaled * m_dipfactor; + + /* make power of 2 */ + for (tex_w = 1; tex_w < w_scaled; tex_w *= 2); + for (tex_h = 1; tex_h < h_scaled; tex_h *= 2); + + wxMemoryDC mdc; + wxBitmap bmp(tex_w, tex_h); + mdc.SelectObject(bmp); + mdc.SetFont(*(scaled_font)); + + if (mdc.IsOk()) { + // Render the text as white on black, so that underlying + // anti-aliasing of wxDC text rendering can be extracted and + // converted to alpha-channel values. + + mdc.SetBackground(wxBrush(wxColour(0, 0, 0))); + mdc.SetBackgroundMode(wxTRANSPARENT); + + mdc.SetTextForeground(wxColour(255, 255, 255)); + + mdc.Clear(); + + mdc.DrawText(ptext->frmtd, 0, 0); + mdc.SelectObject(wxNullBitmap); + + wxImage image = bmp.ConvertToImage(); + int ws = image.GetWidth(), hs = image.GetHeight(); + + ptext->RGBA_width = ws; + ptext->RGBA_height = hs; + unsigned char *pRGBA = (unsigned char *)malloc(4 * ws * hs); + + unsigned char *d = image.GetData(); + unsigned char *pdest = pRGBA; + S52color *ccolor = ptext->pcol; + + if (d) { + for (int y = 0; y < hs; y++) + for (int x = 0; x < ws; x++) { + unsigned char r, g, b; + int off = (y * ws + x); + + r = d[off * 3 + 0]; + g = d[off * 3 + 1]; + b = d[off * 3 + 2]; + + pdest[off * 4 + 0] = ccolor->R; + pdest[off * 4 + 1] = ccolor->G; + pdest[off * 4 + 2] = ccolor->B; + + int alpha = (r + g + b) / 3; + pdest[off * 4 + 3] = (unsigned char)(alpha & 0xff); + } + } + + int draw_width = ptext->RGBA_width; + int draw_height = ptext->RGBA_height; + + glEnable(GL_TEXTURE_2D); + + GLuint texobj; + glGenTextures(1, &texobj); + + glBindTexture(GL_TEXTURE_2D, texobj); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, draw_width, draw_height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, pRGBA); + + free(pRGBA); + + ptext->texobj = texobj; + + } // mdc OK + } // Building texobj + + // Render the texture + if (ptext->texobj) { + int yadjust = 0; + int xadjust = 0; + + // Adjust the y position to account for the convention that S52 text is + // drawn with the lower left corner at the specified point, instead of + // the wx convention using upper left corner. Also, allow for full text + // height in the bitmap/texture, not the estimated "rendered" height. + + yadjust = -ptext->rendered_char_height * 10 / 8; + + // Add in the offsets, specified in units of nominal font height + yadjust += ptext->yoffs * (ptext->rendered_char_height); + // X offset specified in units of average char width + xadjust += ptext->xoffs * ptext->avgCharWidth; + + // adjust for text justification + int w = ptext->text_width; + switch (ptext->hjust) { + case '1': // centered + xadjust -= w / 2; + break; + case '2': // right + xadjust -= w; + break; + case '3': // left (default) + default: + break; + } + + switch (ptext->vjust) { + case '3': // top + yadjust += ptext->rendered_char_height; + break; + case '2': // centered + yadjust += ptext->rendered_char_height / 2; + break; + case '1': // bottom (default) + default: + break; + } + double xp = x; + double yp = y; + + if (fabs(vp_plib.rotation) > 0.01) { + float c = cosf(-vp_plib.rotation); + float s = sinf(-vp_plib.rotation); + float x = xadjust; + float y = yadjust; + xp += x * c - y * s; + yp += x * s + y * c; + + } else { + xp += xadjust; + yp += yadjust; + } + + + pRectDrawn->SetX(xp); + pRectDrawn->SetY(yp); + pRectDrawn->SetWidth(ptext->text_width); + pRectDrawn->SetHeight(ptext->text_height); + + if (bCheckOverlap) { + if (CheckTextRectList(*pRectDrawn, ptext)) bdraw = false; + } + + if (bdraw) { +#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL) +#else + glEnable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + + float uv[8]; + float coords[8]; + + // Note swizzle of points to allow TRIANGLE_STRIP drawing + // normal uv + uv[0] = 0; + uv[1] = 0; + uv[2] = 1; + uv[3] = 0; + uv[6] = 1; + uv[7] = 1; + uv[4] = 0; + uv[5] = 1; + + // pixels + coords[0] = 0; + coords[1] = 0; + coords[2] = ptext->RGBA_width; + coords[3] = 0; + coords[6] = ptext->RGBA_width; + coords[7] = ptext->RGBA_height; + coords[4] = 0; + coords[5] = ptext->RGBA_height; + + glUseProgram(S52texture_2D_shader_program); + + // Get pointers to the attributes in the program. + GLint mPosAttrib = + glGetAttribLocation(S52texture_2D_shader_program, "position"); + GLint mUvAttrib = + glGetAttribLocation(S52texture_2D_shader_program, "aUV"); + + // Select the active texture unit. + glActiveTexture(GL_TEXTURE0); + + // Bind our texture to the texturing target. + glBindTexture(GL_TEXTURE_2D, ptext->texobj); + + // Set up the texture sampler to texture unit 0 + GLint texUni = + glGetUniformLocation(S52texture_2D_shader_program, "uTex"); + glUniform1i(texUni, 0); + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + // Set the attribute mPosAttrib with the vertices in the screen + // coordinates... + glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords); + // ... and enable it. + glEnableVertexAttribArray(mPosAttrib); + + // Set the attribute mUvAttrib with the vertices in the GL + // coordinates... + glVertexAttribPointer(mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv); + // ... and enable it. + glEnableVertexAttribArray(mUvAttrib); + + // Rotate + mat4x4 I, Q; + mat4x4_identity(I); + + mat4x4_translate_in_place(I, x, y, 0); + mat4x4_rotate_Z(Q, I, -vp_plib.rotation); + mat4x4_translate_in_place(Q, xadjust, yadjust, 0); + + GLint matloc = glGetUniformLocation(S52texture_2D_shader_program, + "TransformMatrix"); + glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q); + + // Perform the actual drawing. + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // Restore the per-object transform to Identity Matrix + mat4x4 IM; + mat4x4_identity(IM); + GLint matlocf = glGetUniformLocation(S52texture_2D_shader_program, + "TransformMatrix"); + glUniformMatrix4fv(matlocf, 1, GL_FALSE, (const GLfloat *)IM); + + glDisableVertexAttribArray(mPosAttrib); + glDisableVertexAttribArray(mUvAttrib); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); +#endif + + } // bdraw + } + } + +#if 0 // no longer used, see note above re profile results. + else { // render using cached texture glyphs + // rebuild font if needed + TexFont *f_cache = 0; + unsigned int i; + + for (i = 0; i < TXF_CACHE; i++) { + if (s_txf[i].key == ptext->pFont) { + f_cache = s_txf[i].cache; + break; + } + if (s_txf[i].key == 0) { + break; + } + } + if (i == TXF_CACHE) { + i = rand() & (TXF_CACHE - 1); + } + if (f_cache == 0) { + s_txf[i].key = ptext->pFont; + if(s_txf[i].cache) + delete s_txf[i].cache; + s_txf[i].cache = new TexFont(); + s_txf[i].cache->SetContentScaleFactor(m_ContentScaleFactor); + + f_cache = s_txf[i].cache; + f_cache->Build(*ptext->pFont, m_TextScaleFactor, m_dipfactor); + + int wac; + f_cache->GetTextExtent(_T("M"), &wac, 0); + ptext->avgCharWidth = wac * m_dipfactor; + } + + int w, h; + f_cache->GetTextExtent(ptext->frmtd, &w, &h); + h *= m_dipfactor; + + // We don't store descent/ascent info for font texture cache + // So we have to estimate based on conventional Arial metrics + ptext->rendered_char_height = h * 65 / 100; + + // Adjust the y position to account for the convention that S52 text is + // drawn with the lower left corner at the specified point, instead of + // the wx convention using upper right corner + int yadjust = 0; + int xadjust = 0; + + yadjust = -ptext->rendered_char_height; + + // Add in the offsets, specified in units of nominal font height + yadjust += ptext->yoffs * (ptext->rendered_char_height); + // X offset specified in units of average char width + xadjust += ptext->xoffs * ptext->avgCharWidth; + + // adjust for text justification + switch (ptext->hjust) { + case '1': // centered + xadjust -= w / 2; + break; + case '2': // right + xadjust -= w; + break; + case '3': // left (default) + default: + break; + } + + switch (ptext->vjust) { + case '3': // top + yadjust += ptext->rendered_char_height; + break; + case '2': // centered + yadjust += ptext->rendered_char_height / 2; + break; + case '1': // bottom (default) + default: + break; + } + + int xp = x; + int yp = y; + + if (fabs(vp_plib.rotation) > 0.01) { + float c = cosf(-vp_plib.rotation); + float s = sinf(-vp_plib.rotation); + float x = xadjust; + float y = yadjust; + xadjust = x * c - y * s; + yadjust = x * s + y * c; + } + + xp += xadjust; + yp += yadjust; + + pRectDrawn->SetX(xp); + pRectDrawn->SetY(yp); + pRectDrawn->SetWidth(w); + pRectDrawn->SetHeight(h); + + if (bCheckOverlap) { + if (fabs(vp_plib.rotation) > .01) { + rotate(pRectDrawn, &vp_plib); + } + if (CheckTextRectList(*pRectDrawn, ptext)) bdraw = false; + } + + if (bdraw) { +#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL) +#else + glEnable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + + wxColour wcolor = GetFontColour_PlugIn(_("ChartTexts")); + f_cache->SetColor(wcolor); + + /* undo previous rotation to make text level */ + // glRotatef(vp->rotation*180/PI, 0, 0, -1); + + f_cache->RenderString(ptext->frmtd, xp, yp); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); +#endif + } + } +#endif + +#endif + } else { // Not OpenGL + wxFont oldfont = pdc->GetFont(); // save current font + + if (scale_factor > 1) { + wxFont *pf = ptext->pFont; + int old_size = pf->GetPointSize(); + int new_size = old_size * scale_factor; + wxFont *scaled_font = + FindOrCreateFont_PlugIn(new_size, pf->GetFamily(), pf->GetStyle(), + pf->GetWeight(), false, pf->GetFaceName()); + pdc->SetFont(*scaled_font); + } else { + pdc->SetFont(*(ptext->pFont)); + } + + wxCoord w, h, descent, exlead; + pdc->GetTextExtent(ptext->frmtd, &w, &h, &descent, + &exlead); // measure the text + + // We cannot get the font ascent value to remove the interline spacing. + // So we have to estimate based on conventional Arial metrics + int rendered_text_height = (h - descent) * 8 / 10; + + // Adjust the y position to account for the convention that S52 text is + // drawn with the lower left corner at the specified point, instead of the + // wx convention using upper right corner + int yadjust = 0; + int xadjust = 0; + + yadjust = -(rendered_text_height); + + // Add in the offsets, specified in units of nominal font height + yadjust += ptext->yoffs * (rendered_text_height); + + // X offset specified in units of average char width + xadjust += ptext->xoffs * ptext->avgCharWidth * m_dipfactor; + + // adjust for text justification + switch (ptext->hjust) { + case '1': // centered + xadjust -= w / 2; + break; + case '2': // right + xadjust -= w; + break; + case '3': // left (default) + default: + break; + } + + switch (ptext->vjust) { + case '3': // top + yadjust += rendered_text_height; + break; + case '2': // centered + yadjust += rendered_text_height / 2; + break; + case '1': // bottom (default) + default: + break; + } + + int xp = x; + int yp = y; + + if (fabs(vp_plib.rotation) > 0.01) { + float cx = vp_plib.pix_width / 2.; + float cy = vp_plib.pix_height / 2.; + float c = cosf(vp_plib.rotation); + float s = sinf(vp_plib.rotation); + float x = xp - cx; + float y = yp - cy; + xp = x * c - y * s + cx + vp_plib.rv_rect.x; + yp = x * s + y * c + cy + vp_plib.rv_rect.y; + } + + xp += xadjust; + yp += yadjust; + + pRectDrawn->SetX(xp); + pRectDrawn->SetY(yp); + pRectDrawn->SetWidth(w); + pRectDrawn->SetHeight(h); + + if (bCheckOverlap) { + if (CheckTextRectList(*pRectDrawn, ptext)) bdraw = false; + } + + if (bdraw) { + wxColour wcolor = GetFontColour_PlugIn(_("ChartTexts")); + + // If the user has not changed the color from BLACK, then use the color + // specified in the S52 LUP + if (wcolor == *wxBLACK) + wcolor = wxColour(ptext->pcol->R, ptext->pcol->G, ptext->pcol->B); + pdc->SetTextForeground(wcolor); + + pdc->DrawText(ptext->frmtd, xp, yp); + } + + pdc->SetFont(oldfont); // restore last font + } + return bdraw; + +#ifdef FIXIT +#undef FIXIT +#define DrawText DrawTextA +#endif +} + +// Return true if test_rect overlaps any rect in the current text rectangle +// list, except itself +bool s52plib::CheckTextRectList(const wxRect2D &test_rect, S52_TextC *ptext) { + // Iterate over the current object list, looking at rText + + for (TextObjList::Node *node = m_textObjList.GetFirst(); node; + node = node->GetNext()) { + wxRect2D *pcurrent_rect = &(node->GetData()->rText); + if (node->GetData() == ptext) continue; + + if (pcurrent_rect->Intersects(test_rect)) { + return true; + } + } + return false; +} + +bool s52plib::TextRenderCheck(ObjRazRules *rzRules) { + if (!m_bShowS57Text) return false; + + if (rzRules->obj->bIsAton) { + if (!strncmp(rzRules->obj->FeatureName, "LIGHTS", 6)) { + if (!m_bShowLdisText) return false; + } else { + if (!m_bShowAtonText) return false; + } + } + + // Declutter LIGHTS descriptions + if ((rzRules->obj->bIsAton) && + (!strncmp(rzRules->obj->FeatureName, "LIGHTS", 6))) { + if (lastLightLat == rzRules->obj->m_lat && + lastLightLon == rzRules->obj->m_lon) { + return false; // only render text for the first object at this lat/lon + } else { + lastLightLat = rzRules->obj->m_lat; + lastLightLon = rzRules->obj->m_lon; + } + } + + // An optimization for CM93 charts. + // Don't show the text associated with some objects, since CM93 database + // includes _texto objects aplenty + // FIXME VBO Dave + // chart type is available in member m_chart_context + // Do not use auxParm3 + if (((int)rzRules->obj->auxParm3 == + (int)S52_ChartTypeEnum::S52_CHART_TYPE_CM93) || + ((int)rzRules->obj->auxParm3 == + (int)S52_ChartTypeEnum::S52_CHART_TYPE_CM93COMP)) { + if (!strncmp(rzRules->obj->FeatureName, "BUAARE", 6)) + return false; + else if (!strncmp(rzRules->obj->FeatureName, "SEAARE", 6)) + return false; + else if (!strncmp(rzRules->obj->FeatureName, "LNDRGN", 6)) + return false; + else if (!strncmp(rzRules->obj->FeatureName, "LNDARE", 6)) + return false; + } + + return true; +} + +int s52plib::RenderT_All(ObjRazRules *rzRules, Rules *rules, bool bTX) { + if (!TextRenderCheck(rzRules)) return 0; + + S52_TextC *text = NULL; + bool b_free_text = false; + + // The first Ftext object is cached in the S57Obj. + // If not present, create it on demand + if (!rzRules->obj->bFText_Added) { + if (bTX) + text = S52_PL_parseTX(rzRules, rules, NULL); + else + text = S52_PL_parseTE(rzRules, rules, NULL); + + if (text) { + rzRules->obj->bFText_Added = true; + rzRules->obj->FText = text; + rzRules->obj->FText->rul_seq_creator = rules->n_sequence; + } + } + + // S57Obj already contains a cached text object + // If it was created by this Rule earlier, then render it + // Otherwise, create a new text object, render it, and delete when done + // This will be slower, obviously, but happens infrequently enough? + + else { + if (rules->n_sequence == rzRules->obj->FText->rul_seq_creator) + text = rzRules->obj->FText; + else { + if (bTX) + text = S52_PL_parseTX(rzRules, rules, NULL); + else + text = S52_PL_parseTE(rzRules, rules, NULL); + + b_free_text = true; + } + } + + if (text) { + if (m_bShowS57ImportantTextOnly && (text->dis >= 20)) { + if (b_free_text) delete text; + return 0; + } + + // Establish a font + if (!text->pFont) { + // Process the font specifications from the LUP symbolization rule + int spec_weight = text->weight - 0x30; + wxFontWeight fontweight; + if (spec_weight < 5) + fontweight = wxFONTWEIGHT_LIGHT; + else { + if (spec_weight == 5) + fontweight = wxFONTWEIGHT_NORMAL; + else + fontweight = wxFONTWEIGHT_BOLD; + } + + wxFont *specFont = GetS52Utils()->GetScaledFont( + text->bsize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, fontweight, + wxEmptyString, 1.0); + + // Get the width of a single average character in the spec font + wxScreenDC dc; + dc.SetFont(*specFont); + int width; + dc.GetTextExtent(_T("X"), &width, NULL, NULL, NULL, specFont); + text->avgCharWidth = width; + + int spec_weight_t = text->weight - 0x30; + wxFontWeight fontweight_t; + if (spec_weight_t < 5) + fontweight_t = wxFONTWEIGHT_LIGHT; + else if (spec_weight_t == 5) + fontweight_t = wxFONTWEIGHT_NORMAL; + else + fontweight_t = wxFONTWEIGHT_BOLD; + + // Get the configured "ChartText" font, which may be the default system + // font or another font that has been customized by the user. + // The user may have customized both the font face and the font size. + // We will use the user-configured font size as the base size for the + // text rendering, and adjust the size based on the S52 text size + // specification. + + wxFont *ChartTextDefaultFont = + GetS52Utils()->GetDefaultFont(_("ChartTexts"), 0); + int default_size = ChartTextDefaultFont->GetPointSize(); + + // In S52, the "body size" refers to the base size of the text characters + // in display units (typically pixels). This is part of the text style + // parameters that control how chart text elements are rendered. + // The S52 standard (IHO S-52 Presentation Library) uses this parameter + // as part of the text placement and styling rules for chart text + // elements. The body size is used as a reference unit for Text scaling. + // + // NOAA ENC files requests font size up to 20 points, which looks very + // disproportioned. Let's scale those sizes down to more reasonable + // values. + int fontSize = text->bsize; + + const int maxInputSize = 20; // Largest expected input size. + const int normalizedRange = 4; + // Clamp the input size. + fontSize = wxMin(maxInputSize, fontSize); + + // Normalize the font size by mapping from [0, maxInputSize] to + // [0, normalizedRange] in a linear, proportional way. + // The transformation is inherently monotonic - larger input values will + // always produce larger output values before clamping. + fontSize = + (static_cast(fontSize) / static_cast(maxInputSize)) * + normalizedRange; + // The normalized value is added to the default font size to get the final + // font size. + // This respects user preferences while still adapting to chart + // specifications. + fontSize += default_size; + + // In no case should font size be less than 10, since it becomes + // unreadable + fontSize = wxMax(10, fontSize); + + text->pFont = GetS52Utils()->GetScaledFont( + fontSize, ChartTextDefaultFont->GetFamily(), + ChartTextDefaultFont->GetStyle(), fontweight_t, + ChartTextDefaultFont->GetFaceName(), 1.0); + } + + // Render text at declared x/y of object + wxPoint2DDouble dr; + GetDoublePointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &dr); + + + wxRect2D rect; + bool bwas_drawn = RenderText(m_pdc, text, dr.m_x, dr.m_y, &rect, rzRules->obj, + m_bDeClutterText); + + // If this is an un-cached text render, it probably means that a single + // object has two or more text renders in its rule set. RDOCAL is one + // example. There are others We need to cache only the first text + // structure, but should update the render rectangle to reflect all texts + // rendered for this object, in order to process the declutter logic. + bool b_dupok = false; + if (b_free_text) { + delete text; + + if (!bwas_drawn) { + return 1; + } else { // object was drawn + text = rzRules->obj->FText; + + wxRect2D r0 = text->rText; + r0 = r0.Union(rect); + text->rText = r0; + + b_dupok = true; // OK to add a duplicate text structure to the + // declutter list, just for this case. + } + } else + text->rText = rect; + + // If this text was actually drawn, add a pointer to its rect to the + // de-clutter list if it doesn't already exist + if (m_bDeClutterText) { + if (bwas_drawn) { + bool b_found = false; + for (TextObjList::Node *node = m_textObjList.GetFirst(); node; + node = node->GetNext()) { + S52_TextC *oc = node->GetData(); + + if (oc == text) { + if (!b_dupok) b_found = true; + break; + } + } + if (!b_found) m_textObjList.Append(text); + } + } + + // Update the object Bounding box + // so that subsequent drawing operations will redraw the item fully + // and so that cursor hit testing includes both the text and the object + + // if ( rzRules->obj->Primitive_type == GEO_POINT ) + { + double latmin, lonmin, latmax, lonmax; + + GetPixPointSingleNoRotate(rect.x, rect.y + rect.height, + &latmin, &lonmin); + GetPixPointSingleNoRotate(rect.x + rect.width, rect.y, + &latmax, &lonmax); + LLBBox bbtext; + bbtext.Set(latmin, lonmin, latmax, lonmax); + + rzRules->obj->BBObj.Expand(bbtext); + } + } + + return 1; +} + +// Text +int s52plib::RenderTX(ObjRazRules *rzRules, Rules *rules) { + return RenderT_All(rzRules, rules, true); +} + +// Text formatted +int s52plib::RenderTE(ObjRazRules *rzRules, Rules *rules) { + return RenderT_All(rzRules, rules, false); +} + +bool s52plib::RenderHPGL(ObjRazRules *rzRules, Rule *prule, wxPoint2DDouble &r, + float rot_angle, double uScale) { + float fsf = 100 / canvas_pix_per_mm; + + float xscale = 1.0; + + if ((!strncmp(rzRules->obj->FeatureName, "TSSLPT", 6)) || + (!strncmp(rzRules->obj->FeatureName, "DWRTPT", 6)) || + (!strncmp(rzRules->obj->FeatureName, "TWRTPT", 6)) || + (!strncmp(rzRules->obj->FeatureName, "RCTLPT", 6))) { + // assume the symbol length + float sym_length = 30; + float scaled_length = sym_length / vp_plib.view_scale_ppm; + float target_length = 800; + + xscale = target_length / scaled_length; + xscale = wxMin(xscale, 1.0); + xscale = wxMax(.4, xscale); + + // printf("scaled length: %g xscale: %g\n", scaled_length, xscale); + + fsf *= xscale; + } + + xscale *= uScale; + + // Special case for GEO_AREA objects with centred symbols + if (rzRules->obj->Primitive_type == GEO_AREA) { + wxPoint r; + GetPointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &r); + + double latdraw, londraw; // position of the drawn symbol with pivot applied + GetPixPointSingleNoRotate( + r.x + ((prule->pos.symb.pivot_x.SYCL - prule->pos.symb.bnbox_x.SBXC) / + fsf), + r.y + ((prule->pos.symb.pivot_y.SYRW - prule->pos.symb.bnbox_y.SBXR) / + fsf), + &latdraw, &londraw); + + if (!rzRules->obj->BBObj.Contains( + latdraw, + londraw)) // Symbol reference point is outside base area object + return 1; + } + + double render_angle = rot_angle; + + // Very special case for ATON flare lights at 135 degrees, the standard + // render angle. We don't want them to rotate with the viewport. + if (rzRules->obj->bIsAton && + (!strncmp(rzRules->obj->FeatureName, "LIGHTS", 6))) { +#ifdef __OCPN__ANDROID__ + // Due to popular request, we make the flare lights a little bit + // smaller than S52 specifications + xscale = xscale * 5. / 7.; +#endif + + if (fabs(rot_angle - 135.0) < 1.) + render_angle -= vp_plib.rotation * 180. / PI; + } + + int width = prule->pos.symb.bnbox_x.SBXC + prule->pos.symb.bnbox_w.SYHL; + width *= 4; // Grow the drawing bitmap to allow for rotation of symbols with + // highly offset pivot points + width = (int)(width / fsf); + + int height = prule->pos.symb.bnbox_y.SBXR + prule->pos.symb.bnbox_h.SYVL; + height *= 4; + height = (int)(height / fsf); + + int origin_x = prule->pos.symb.bnbox_x.SBXC; + int origin_y = prule->pos.symb.bnbox_y.SBXR; + wxPoint origin(origin_x, origin_y); + + int pivot_x = prule->pos.symb.pivot_x.SYCL; + int pivot_y = prule->pos.symb.pivot_y.SYRW; + wxPoint pivot(pivot_x, pivot_y); + + char *str = prule->vector.LVCT; + char *col = prule->colRef.LCRF; + wxPoint2DDouble r0((pivot_x / fsf), (pivot_y / fsf)); + + HPGL->SetVP(&vp_plib); + + if (!m_pdc) { // OpenGL Mode, do a direct render + HPGL->SetTargetOpenGl(); + HPGL->Render(str, col, r, pivot, origin, xscale, render_angle, true); + + // Update the object Bounding box + // so that subsequent drawing operations will redraw the item fully + + int r_width = prule->pos.symb.bnbox_w.SYHL; + r_width = (int)(r_width / fsf); + int r_height = prule->pos.symb.bnbox_h.SYVL; + r_height = (int)(r_height / fsf); + int maxDim = wxMax(r_height, r_width); + + double latmin, lonmin, latmax, lonmax; + GetPixPointSingleNoRotate(r.m_x - maxDim, r.m_y + maxDim, &latmin, &lonmin); + GetPixPointSingleNoRotate(r.m_x + maxDim, r.m_y - maxDim, &latmax, &lonmax); + LLBBox symbox; + symbox.Set(latmin, lonmin, latmax, lonmax); + + rzRules->obj->BBObj.Expand(symbox); + + } else { +#if ((defined(__WXGTK__) || defined(__WXMAC__)) && !wxCHECK_VERSION(2, 9, 4)) + wxBitmap *pbm = new wxBitmap(width, height); +#else + wxBitmap *pbm = new wxBitmap(width, height, 32); +#if !wxCHECK_VERSION(2, 9, 4) + pbm->UseAlpha(); +#endif +#endif + wxMemoryDC mdc(*pbm); + if (!mdc.IsOk()) { + wxString msg; + msg.Printf(_T("RenderHPGL: width %d height %d"), width, height); + wxLogMessage(msg); + return false; + } + +#if wxUSE_GRAPHICS_CONTEXT + wxGCDC gdc(mdc); + HPGL->SetTargetGCDC(&gdc); +#else + wxMemoryDC &gdc(mdc); + HPGL->SetTargetDC(&gdc); +#endif + HPGL->Render(str, col, r0, pivot, origin, xscale, (double)rot_angle, true); + + int bm_width = (gdc.MaxX() - gdc.MinX()) + 4; + int bm_height = (gdc.MaxY() - gdc.MinY()) + 4; + int bm_orgx = wxMax(0, gdc.MinX() - 2); + int bm_orgy = wxMax(0, gdc.MinY() - 2); + int screenOriginX = r.m_x + (bm_orgx - (pivot_x / fsf)); + int screenOriginY = r.m_y + (bm_orgy - (pivot_y / fsf)); + + // Pre-clip the sub-bitmap to avoid assert errors + if ((bm_height + bm_orgy) > height) bm_height = height - bm_orgy; + if ((bm_width + bm_orgx) > width) bm_width = width - bm_orgx; + + mdc.SelectObject(wxNullBitmap); + + // Grab a copy of the existing screen DC rectangle + wxBitmap targetBm(bm_width, bm_height, 24); + wxMemoryDC targetDc(targetBm); + if (!targetDc.IsOk()) return false; + targetDc.Blit(0, 0, bm_width, bm_height, m_pdc, screenOriginX, + screenOriginY); + +#if wxUSE_GRAPHICS_CONTEXT /*&& (( defined(__WXGTK__) || defined(__WXMAC__) ) \ + && !wxCHECK_VERSION(2,9,4))*/ + // Re-render onto the screen-grab copy, since wxDC::DrawBitmap() for alpha + // channel bitmaps is broken somehow in wxGCDC + wxGCDC targetGcdc(targetDc); + r0 -= wxPoint(bm_orgx, bm_orgy); + HPGL->SetTargetGCDC(&targetGcdc); + HPGL->Render(str, col, r0, pivot, origin, xscale, (double)rot_angle, true); +#else + // We can use the bitmap already rendered + // Get smallest containing bitmap + wxBitmap *sbm = new wxBitmap( + pbm->GetSubBitmap(wxRect(bm_orgx, bm_orgy, bm_width, bm_height))); + + // render the symbol graphics onto the screen-grab copy, with + // transparency... + targetDc.DrawBitmap(*sbm, 0, 0); + delete sbm; +#endif + + // Render the final bitmap onto the screen DC + m_pdc->Blit(screenOriginX, screenOriginY, bm_width, bm_height, &targetDc, 0, + 0); + + // Clean up + delete pbm; + targetDc.SelectObject(wxNullBitmap); + + // Update the object Bounding box + // so that subsequent drawing operations will redraw the item fully + + double latmin, lonmin, latmax, lonmax; + GetPixPointSingleNoRotate(r.m_x + prule->parm2, + r.m_y + prule->parm3 + bm_height, &latmin, &lonmin); + GetPixPointSingleNoRotate(r.m_x + prule->parm2 + bm_width, r.m_y + prule->parm3, + &latmax, &lonmax); + LLBBox symbox; + symbox.Set(latmin, lonmin, latmax, lonmax); + + rzRules->obj->BBObj.Expand(symbox); + } + + return true; +} + +int s52plib::BuildHPGLTexture(ObjRazRules *rzRules, Rule *prule, wxPoint2DDouble &r, + float rot_angle, double uScale) { + int rv = 0; +#ifdef ocpnUSE_GL + float fsf = 100 / canvas_pix_per_mm; + + float xscale = 1.0; + xscale *= uScale; + + // Flare lights are a little large... + if (rzRules->obj->bIsAton && + (!strncmp(rzRules->obj->FeatureName, "LIGHTS", 6))) { + xscale = xscale * 5. / 7.; + } + + double render_angle = rot_angle; + + int width = prule->pos.symb.bnbox_x.SBXC + prule->pos.symb.bnbox_w.SYHL; + width *= 6 * xscale; // Grow the drawing bitmap to allow for rotation + // of symbols with highly offset pivot points + width = (int)(width / fsf); + + int height = prule->pos.symb.bnbox_y.SBXR + prule->pos.symb.bnbox_h.SYVL; + height *= 6 * xscale; + height = (int)(height / fsf); + + width /= fsf; + height /= fsf; + + // Expand width and height to next larger POT + int xp = width, yp = height; + int width_pot, height_pot; + if (((xp != 0) && !(xp & (xp - 1)))) // detect POT + width_pot = xp; + else { + int a = 0; + while (xp) { + xp = xp >> 1; + a++; + } + width_pot = 1 << a; + } + + if (((yp != 0) && !(yp & (yp - 1)))) // detect POT + height_pot = yp; + else { + int a = 0; + while (yp) { + yp = yp >> 1; + a++; + } + height_pot = 1 << a; + } + + int origin_x = prule->pos.symb.bnbox_x.SBXC; + int origin_y = prule->pos.symb.bnbox_y.SBXR; + wxPoint origin(origin_x, origin_y); + + int pivot_x = prule->pos.symb.pivot_x.SYCL; + int pivot_y = prule->pos.symb.pivot_y.SYRW; + wxPoint pivot(pivot_x, pivot_y); + + char *str = prule->vector.LVCT; + char *col = prule->colRef.LCRF; + wxPoint r0((int)(pivot_x / fsf), (int)(pivot_y / fsf)); + + // Render the symbol on wxMemoryDC + HPGL->SetVP(&vp_plib); + wxMemoryDC mdc; + wxBitmap bmp(width, height, 32); + mdc.SelectObject(bmp); + HPGL->SetTargetDC(&mdc); +#if (defined(__WXMSW__) || defined(__WXMAC__) || defined(__WXQT__)) + mdc.SetBackground(wxBrush(m_unused_wxColor)); + mdc.Clear(); +#endif + + wxPoint2DDouble rt(0, 0); + HPGL->Render(str, col, rt, pivot, origin, xscale, render_angle, true); + mdc.SelectObject(wxNullBitmap); + + wxImage image = bmp.ConvertToImage(); + int ws = image.GetWidth(), hs = image.GetHeight(); + + unsigned char *source = image.GetData(); + unsigned char *alpha = image.GetAlpha(); + +#ifdef __WXQT__ + alpha = nullptr; +#endif + + unsigned char mr, mg, mb; + if (!alpha) { + image.SetMaskColour(m_unused_wxColor.Red(), m_unused_wxColor.Green(), + m_unused_wxColor.Blue()); + if (!alpha && !image.GetOrFindMaskColour(&mr, &mg, &mb)) + printf("trying to use mask to draw a bitmap without alpha or mask\n"); + } + + unsigned char *pRGBA = (unsigned char *)calloc(1, 4 * width_pot * height_pot); + unsigned char *dest = pRGBA; + + if (source) { + for (int y = 0; y < hs; y++) { + for (int x = 0; x < ws; x++) { + int offs = (y * ws + x); + int offd = (y * width_pot + x); + + unsigned int r = source[offs * 3 + 0]; + unsigned int g = source[offs * 3 + 1]; + unsigned int b = source[offs * 3 + 2]; + + dest[offd * 4 + 0] = source[offs * 3 + 0]; + dest[offd * 4 + 1] = source[offs * 3 + 1]; + dest[offd * 4 + 2] = source[offs * 3 + 2]; + unsigned char a = alpha + ? alpha[offs] + : ((r == mr) && (g == mg) && (b == mb) ? 0 : 255); + dest[offd * 4 + 3] = a; + } + } + } + + glEnable(GL_TEXTURE_2D); + GLuint texobj; + glGenTextures(1, &texobj); + glBindTexture(GL_TEXTURE_2D, texobj); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_pot, height_pot, 0, GL_RGBA, + GL_UNSIGNED_BYTE, pRGBA); + + free(pRGBA); + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + // Save the texture details in the object's rule + prule->parm2 = width_pot; + prule->parm3 = height_pot; + rv = texobj; +#endif + return rv; +} + +#if 0 + + // Update the object Bounding box + // so that subsequent drawing operations will redraw the item fully + + int r_width = prule->pos.symb.bnbox_w.SYHL; + r_width = (int)(r_width / fsf); + int r_height = prule->pos.symb.bnbox_h.SYVL; + r_height = (int)(r_height / fsf); + int maxDim = wxMax(r_height, r_width); + + double latmin, lonmin, latmax, lonmax; + GetPixPointSingleNoRotate(r.x - maxDim, r.y + maxDim, &latmin, &lonmin); + GetPixPointSingleNoRotate(r.x + maxDim, r.y - maxDim, &latmax, &lonmax); + LLBBox symbox; + symbox.Set(latmin, lonmin, latmax, lonmax); + + rzRules->obj->BBObj.Expand(symbox); + + } else { +#if ((defined(__WXGTK__) || defined(__WXMAC__)) && !wxCHECK_VERSION(2, 9, 4)) + wxBitmap *pbm = new wxBitmap(width, height); +#else + wxBitmap *pbm = new wxBitmap(width, height, 32); +#if !wxCHECK_VERSION(2, 9, 4) + pbm->UseAlpha(); +#endif +#endif + wxMemoryDC mdc(*pbm); + if (!mdc.IsOk()) { + wxString msg; + msg.Printf(_T("RenderHPGL: width %d height %d"), width, height); + wxLogMessage(msg); + return false; + } + +#if wxUSE_GRAPHICS_CONTEXT + wxGCDC gdc(mdc); + HPGL->SetTargetGCDC(&gdc); +#else + wxMemoryDC &gdc(mdc); + HPGL->SetTargetDC(&gdc); +#endif + HPGL->Render(str, col, r0, pivot, origin, xscale, (double)rot_angle, true); + + int bm_width = (gdc.MaxX() - gdc.MinX()) + 4; + int bm_height = (gdc.MaxY() - gdc.MinY()) + 4; + int bm_orgx = wxMax(0, gdc.MinX() - 2); + int bm_orgy = wxMax(0, gdc.MinY() - 2); + int screenOriginX = r.x + (bm_orgx - (int)(pivot_x / fsf)); + int screenOriginY = r.y + (bm_orgy - (int)(pivot_y / fsf)); + + // Pre-clip the sub-bitmap to avoid assert errors + if ((bm_height + bm_orgy) > height) bm_height = height - bm_orgy; + if ((bm_width + bm_orgx) > width) bm_width = width - bm_orgx; + + mdc.SelectObject(wxNullBitmap); + + // Grab a copy of the existing screen DC rectangle + wxBitmap targetBm(bm_width, bm_height, 24); + wxMemoryDC targetDc(targetBm); + if (!targetDc.IsOk()) return false; + targetDc.Blit(0, 0, bm_width, bm_height, m_pdc, screenOriginX, + screenOriginY); + +#if wxUSE_GRAPHICS_CONTEXT /*&& (( defined(__WXGTK__) || defined(__WXMAC__) ) \ + && !wxCHECK_VERSION(2,9,4))*/ + // Re-render onto the screen-grab copy, since wxDC::DrawBitmap() for alpha + // channel bitmaps is broken somehow in wxGCDC + wxGCDC targetGcdc(targetDc); + r0 -= wxPoint(bm_orgx, bm_orgy); + HPGL->SetTargetGCDC(&targetGcdc); + HPGL->Render(str, col, r0, pivot, origin, xscale, (double)rot_angle, true); +#else + // We can use the bitmap already rendered + // Get smallest containing bitmap + wxBitmap *sbm = new wxBitmap( + pbm->GetSubBitmap(wxRect(bm_orgx, bm_orgy, bm_width, bm_height))); + + // render the symbol graphics onto the screen-grab copy, with + // transparency... + targetDc.DrawBitmap(*sbm, 0, 0); + delete sbm; +#endif + + // Render the final bitmap onto the screen DC + m_pdc->Blit(screenOriginX, screenOriginY, bm_width, bm_height, &targetDc, 0, + 0); + + // Clean up + delete pbm; + targetDc.SelectObject(wxNullBitmap); + + // Update the object Bounding box + // so that subsequent drawing operations will redraw the item fully + + double latmin, lonmin, latmax, lonmax; + GetPixPointSingleNoRotate(r.x + prule->parm2, + r.y + prule->parm3 + bm_height, &latmin, &lonmin); + GetPixPointSingleNoRotate(r.x + prule->parm2 + bm_width, r.y + prule->parm3, + &latmax, &lonmax); + LLBBox symbox; + symbox.Set(latmin, lonmin, latmax, lonmax); + + rzRules->obj->BBObj.Expand(symbox); + } + + return true; +} +#endif + +//----------------------------------------------------------------------------------------- +// Instantiate a Symbol or Pattern stored as XBM ascii in a rule +// Producing a wxImage +//----------------------------------------------------------------------------------------- +wxImage s52plib::RuleXBMToImage(Rule *prule) { + // Decode the color definitions + wxArrayPtrVoid *pColorArray = new wxArrayPtrVoid; + + int i = 0; + char *cstr = prule->colRef.SCRF; + + char colname[6]; + int nl = strlen(cstr); + + while (i < nl) { + i++; + + strncpy(colname, &cstr[i], 5); + colname[5] = 0; + S52color *pColor = getColor(colname); + + pColorArray->Add((void *)pColor); + + i += 5; + } + + // Get geometry + int width = prule->pos.line.bnbox_w.SYHL; + int height = prule->pos.line.bnbox_h.SYVL; + + wxString gstr(*prule->bitmap.SBTM); // the bit array + + wxImage Image(width, height); + + for (int iy = 0; iy < height; iy++) { + wxString thisrow = gstr(iy * width, width); // extract a row + + for (int ix = 0; ix < width; ix++) { + int cref = (int)(thisrow[ix] - 'A'); // make an index + if (cref >= 0) { + S52color *pthisbitcolor = (S52color *)(pColorArray->Item(cref)); + Image.SetRGB(ix, iy, pthisbitcolor->R, pthisbitcolor->G, + pthisbitcolor->B); + } else { + Image.SetRGB(ix, iy, m_unused_color.R, m_unused_color.G, + m_unused_color.B); + } + } + } + + pColorArray->Clear(); + delete pColorArray; + return Image; +} + +// +// Render Raster Symbol +// Symbol is instantiated as a bitmap the first time it is needed +// and re-built on color scheme change +// +bool s52plib::RenderRasterSymbol(ObjRazRules *rzRules, Rule *prule, wxPoint2DDouble &r, + float rot_angle) { + double scale_factor = 1.0; + + scale_factor *= m_ChartScaleFactorExp; + + // Correct scale factor for symbolized soundings, + // as sometimes found in objects OBSTRN, WRECKS, UWTROC + if (!strncmp(prule->name.SYNM, "SOUND", 5)) { + scale_factor /= m_ChartScaleFactorExp; + scale_factor *= m_SoundingsScaleFactor; + } + + scale_factor *= g_scaminScale; + scale_factor /= m_dipfactor; + + if (m_display_size_mm < + 200) { // about 8 inches, implying some sort of smaller mobile device + // Set the onscreen size of the symbol + // Compensate for various display resolutions + // Develop empirically, making a buoy about 4 mm tall + double boyHeight = + 21. / + GetPPMM(); // from raster symbol definitions, boylat is xx pix high + + double targetHeight0 = 4.0; + + // But we want to scale the size for smaller displays + double displaySize = m_display_size_mm; + displaySize = wxMax(displaySize, 100); + + float targetHeight = wxMin(targetHeight0, displaySize / 30); + + double pix_factor = targetHeight / boyHeight; + + // qDebug() << "scaleing" << m_display_size_mm << targetHeight0 << + // targetHeight << GetPPMM() << boyHeight << pix_factor; + + // for Hubert, and my moto + // scaleing 93.98 93 4 3.33333 12.7312 1.64949 2.02082 + // My nvidia tab + // scaleing 144.78 144 4 4 12.6667 1.65789 2.4127 + // judgement: all OK + + scale_factor *= pix_factor; + } + + scale_factor *= m_ContentScaleFactor; + + // a few special cases here + if (!strncmp(rzRules->obj->FeatureName, "notmrk", 6) || + !strncmp(rzRules->obj->FeatureName, "NOTMRK", 6) || + !strncmp(prule->name.SYNM, "ADDMRK", 6)) { + // get the symbol size + wxRect trect; + m_chartSymbols.GetGLTextureRect(trect, prule->name.SYNM); + + int scale_dim = wxMax(trect.width, trect.height); + + double scaled_size = scale_dim / vp_plib.view_scale_ppm; + + double target_size = + 100; // roughly, meters maximum scaled size for these inland signs + + double xscale = target_size / scaled_size; + xscale = wxMin(xscale, 1.0); + xscale = wxMax(.2, xscale); + + scale_factor *= xscale; + } + + int pivot_x = prule->pos.line.pivot_x.SYCL; + int pivot_y = prule->pos.line.pivot_y.SYRW; + + pivot_x *= scale_factor; + pivot_y *= scale_factor; + + // For opengl, hopefully the symbols are loaded in a texture + unsigned int texture = 0; + wxRect texrect; + if (!m_pdc) { + texture = m_chartSymbols.GetGLTextureRect(texrect, prule->name.SYNM); + if (texture) { + prule->parm2 = texrect.width * scale_factor; + prule->parm3 = texrect.height * scale_factor; + } + } + + if (m_pdc || !texture) { + // Check to see if any cached data is valid + bool b_dump_cache = false; + if (prule->pixelPtr) { + // Detect switches between DC and GL modes, flush if switch occurs + if (m_pdc) { + if (prule->parm0 != ID_wxBitmap) b_dump_cache = true; + } else { + if (prule->parm0 != ID_RGBA) b_dump_cache = true; + } + } + + // If the requested scaled symbol size is not the same as is currently + // cached, we have to dump the cache + wxRect trect; + m_chartSymbols.GetGLTextureRect(trect, prule->name.SYNM); + if (prule->parm2 != (int)(trect.width * scale_factor)) b_dump_cache = true; + + wxBitmap *pbm = NULL; + + // Instantiate the symbol if necessary + if ((prule->pixelPtr == NULL) || (prule->parm1 != m_colortable_index) || + b_dump_cache) { + wxImage Image = m_chartSymbols.GetImage(prule->name.SYNM); + + // delete any old private data + ClearRulesCache(prule); + + // always display something, TMARDEF1 as width of 2 + int w0 = wxMax(1, Image.GetWidth() * scale_factor); + int h0 = wxMax(1, Image.GetHeight() * scale_factor); + Image.Rescale(w0, h0, wxIMAGE_QUALITY_HIGH); + + int w = Image.GetWidth(); + int h = Image.GetHeight(); + + if (!m_pdc) // opengl, not using textures + { + // Get the glRGBA format data from the wxImage + unsigned char *d = Image.GetData(); + unsigned char *a = Image.GetAlpha(); + + Image.SetMaskColour(m_unused_wxColor.Red(), m_unused_wxColor.Green(), + m_unused_wxColor.Blue()); + unsigned char mr, mg, mb; + if (!a && !Image.GetOrFindMaskColour(&mr, &mg, &mb)) + printf("trying to use mask to draw a bitmap without alpha or mask\n"); + + unsigned char *e = (unsigned char *)malloc(w * h * 4); + // XXX FIXME a or e ? + if (d && a) { + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + unsigned char r, g, b; + int off = (y * w + x); + r = d[off * 3 + 0]; + g = d[off * 3 + 1]; + b = d[off * 3 + 2]; + + e[off * 4 + 0] = r; + e[off * 4 + 1] = g; + e[off * 4 + 2] = b; + + e[off * 4 + 3] = + a ? a[off] : ((r == mr) && (g == mg) && (b == mb) ? 0 : 255); + } + } + } + + // Save the bitmap ptr and aux parms in the rule + prule->pixelPtr = e; + prule->parm0 = ID_RGBA; + prule->parm1 = m_colortable_index; + prule->parm2 = w; + prule->parm3 = h; + } else { + bool b_has_trans = false; +#if (defined(__WXGTK__) || defined(__WXMAC__)) + + // Blitting of wxBitmap with transparency in wxGTK is broken.... + // We can do it the hard way, by manually alpha blending the + // symbol with a clip taken from the current screen DC contents. + + // Inspect the symbol image, to see if it actually has alpha + // transparency + if (Image.HasAlpha()) { + unsigned char *a = Image.GetAlpha(); + for (int i = 0; i < Image.GetHeight(); i++, a++) { + for (int j = 0; j < Image.GetWidth(); j++) { + if ((*a) && (*a != 255)) { + b_has_trans = true; + break; + } + } + if (b_has_trans) break; + } + } +#ifdef __WXMAC__ + b_has_trans = true; +#endif + + // If the symbol image has no transparency, then a standard + // wxDC:Blit() will work + if (!b_has_trans) { + pbm = new wxBitmap(Image, -1); + wxMask *pmask = new wxMask(*pbm, m_unused_wxColor); + pbm->SetMask(pmask); + } + +#else + pbm = new wxBitmap(Image, 32); // windows + wxMask *pmask = new wxMask(*pbm, m_unused_wxColor); + pbm->SetMask(pmask); +#endif + + // Save the bitmap ptr and aux parms in the rule + prule->pixelPtr = pbm; + prule->parm0 = ID_wxBitmap; + prule->parm1 = m_colortable_index; + prule->parm2 = w; + prule->parm3 = h; + } + } // instantiation + } + + // Get the bounding box for the to-be-drawn symbol + int b_width, b_height; + b_width = prule->parm2; + b_height = prule->parm3; + + LLBBox symbox; + double latmin, lonmin, latmax, lonmax; + + if (!m_pdc && fabs(vp_plib.rotation) > .01) // opengl + { + float cx = vp_plib.pix_width / 2.; + float cy = vp_plib.pix_height / 2.; + float c = cosf(vp_plib.rotation); + float s = sinf(vp_plib.rotation); + float x = r.m_x - pivot_x - cx; + float y = r.m_y - pivot_y + b_height - cy; + GetPixPointSingle(x * c - y * s + cx, x * s + y * c + cy, &latmin, &lonmin); + + x = r.m_x - pivot_x + b_width - cx; + y = r.m_y - pivot_y - cy; + GetPixPointSingle(x * c - y * s + cx, x * s + y * c + cy, &latmax, &lonmax); + } else { + GetPixPointSingle(r.m_x - pivot_x, r.m_y - pivot_y + b_height, &latmin, + &lonmin); + GetPixPointSingle(r.m_x - pivot_x + b_width, r.m_y - pivot_y, &latmax, &lonmax); + } + symbox.Set(latmin, lonmin, latmax, lonmax); + + // Special case for GEO_AREA objects with centred symbols + if (rzRules->obj->Primitive_type == GEO_AREA) { + if (!rzRules->obj->BBObj.IntersectIn( + symbox)) // Symbol is wholly outside base object + return true; + } + + // Now render the symbol + + if (!m_pdc) // opengl + { +#ifdef ocpnUSE_GL + glEnable(GL_BLEND); + + if (texture) { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + + int w = texrect.width, h = texrect.height; + + float tx1 = texrect.x, ty1 = texrect.y; + float tx2 = tx1 + w, ty2 = ty1 + h; + +#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL) +#else + + if (m_TextureFormat == GL_TEXTURE_2D) { + // Normalize the sybmol texture coordinates against the next higher POT + // size + wxSize size = m_chartSymbols.GLTextureSize(); + int rb_x = size.x; + int rb_y = size.y; + tx1 /= rb_x, tx2 /= rb_x; + ty1 /= rb_y, ty2 /= rb_y; + } + + float uv[8]; + float coords[8]; + + // Note swizzle of points to allow TRIANGLE_STRIP drawing + // normal uv + uv[0] = tx1; + uv[1] = ty1; + uv[2] = tx2; + uv[3] = ty1; + uv[6] = tx2; + uv[7] = ty2; + uv[4] = tx1; + uv[5] = ty2; + + w *= scale_factor; + h *= scale_factor; + + // pixels + coords[0] = 0; + coords[1] = 0; + coords[2] = w; + coords[3] = 0; + coords[6] = w; + coords[7] = h; + coords[4] = 0; + coords[5] = h; + + if (pCtexture_2D_shader_program[0]) { + pCtexture_2D_shader_program[0]->Bind(); + + // Select the active texture unit. + glActiveTexture(GL_TEXTURE0); + + pCtexture_2D_shader_program[0]->SetUniform1i("uTex", 0); + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + pCtexture_2D_shader_program[0]->SetAttributePointerf("position", + coords); + pCtexture_2D_shader_program[0]->SetAttributePointerf("aUV", uv); + + // Rotate + mat4x4 I, Q; + mat4x4_identity(I); + + mat4x4_translate_in_place(I, r.m_x, r.m_y, 0); + if (abs(vp_plib.rotation) > 0) + mat4x4_rotate_Z(Q, I, -vp_plib.rotation); + else + mat4x4_dup(Q, I); + mat4x4_translate_in_place(Q, -pivot_x, -pivot_y, 0); + + pCtexture_2D_shader_program[0]->SetUniformMatrix4fv("TransformMatrix", + (GLfloat *)Q); + + // Perform the actual drawing. + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // Restore the per-object transform to Identity Matrix + mat4x4 IM; + mat4x4_identity(IM); + pCtexture_2D_shader_program[0]->SetUniformMatrix4fv("TransformMatrix", + (GLfloat *)IM); + + // Clean up the GL state + pCtexture_2D_shader_program[0]->UnBind(); + } + +#endif // GLES2 + glDisable(m_TextureFormat); + } else { /* this is only for legacy mode, or systems without NPOT textures + */ + float cr = cosf(vp_plib.rotation); + float sr = sinf(vp_plib.rotation); + float ddx = pivot_x * cr + pivot_y * sr; + float ddy = pivot_y * cr - pivot_x * sr; +#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL) +#endif + } + + glDisable(GL_BLEND); +#endif + } else { + if (!(prule->pixelPtr)) // This symbol requires manual alpha blending + { + // Don't bother if the symbol is off the true screen, + // as for instance when an area-centered symbol is called for. + if ((r.m_x - pivot_x < vp_plib.pix_width) && + (r.m_y - pivot_y < vp_plib.pix_height)) { + // Get the current screen contents to a wxImage + wxBitmap b1(b_width, b_height, -1); + wxMemoryDC mdc1(b1); + mdc1.Blit(0, 0, b_width, b_height, m_pdc, r.m_x - pivot_x, r.m_y - pivot_y, + wxCOPY); + wxImage im_back = b1.ConvertToImage(); + + // Get the scaled symbol as a wxImage + wxImage im_sym = m_chartSymbols.GetImage(prule->name.SYNM); + im_sym.Rescale(b_width, b_height, wxIMAGE_QUALITY_HIGH); + + wxImage im_result(b_width, b_height); + unsigned char *pdest = im_result.GetData(); + unsigned char *pback = im_back.GetData(); + unsigned char *psym = im_sym.GetData(); + + unsigned char *asym = NULL; + if (im_sym.HasAlpha()) asym = im_sym.GetAlpha(); + + // Do alpha blending, the hard way + + if (pdest && psym && pback) { + for (int i = 0; i < b_height; i++) { + for (int j = 0; j < b_width; j++) { + double alpha = 1.0; + if (asym) alpha = (*asym++) / 256.0; + unsigned char r = (*psym++ * alpha) + (*pback++ * (1.0 - alpha)); + *pdest++ = r; + unsigned char g = (*psym++ * alpha) + (*pback++ * (1.0 - alpha)); + *pdest++ = g; + unsigned char b = (*psym++ * alpha) + (*pback++ * (1.0 - alpha)); + *pdest++ = b; + } + } + } + + wxBitmap result(im_result); + wxMemoryDC result_dc(result); + + m_pdc->Blit(r.m_x - pivot_x, r.m_y - pivot_y, b_width, b_height, &result_dc, + 0, 0, wxCOPY, false); + + result_dc.SelectObject(wxNullBitmap); + mdc1.SelectObject(wxNullBitmap); + } + } else { + // Get the symbol bitmap into a memory dc + wxBitmap &bmp = (wxBitmap &)(*((wxBitmap *)(prule->pixelPtr))); + wxMemoryDC mdc(bmp); + + // Blit it into the target dc, with mask + m_pdc->Blit(r.m_x - pivot_x, r.m_y - pivot_y, bmp.GetWidth(), bmp.GetHeight(), + &mdc, 0, 0, wxCOPY, true); + + mdc.SelectObject(wxNullBitmap); + } + // Debug + // if(m_pdc){ + // m_pdc->SetPen(wxPen(*wxGREEN, 1)); + // m_pdc->SetBrush(wxBrush(*wxGREEN, wxTRANSPARENT)); + // m_pdc->DrawRectangle(r.x - pivot_x, r.y - pivot_y, b_width, b_height); + //} + } + + // Update the object Bounding box + // so that subsequent drawing operations will redraw the item fully + // We expand the object's BBox to account for objects rendered by multiple + // symbols, such as SOUNGD. so that expansions are cumulative. + if (rzRules->obj->Primitive_type == GEO_POINT) + rzRules->obj->BBObj.Expand(symbox); + + return true; +} + +// SYmbol +int s52plib::RenderSY(ObjRazRules *rzRules, Rules *rules) { + float angle = 0; + double orient; + + if (rules->razRule != NULL) { + if (rules->INSTstr[8] == + ',') // supplementary parameter assumed to be angle, seen in LIGHTSXX + { + char sangle[10]; + int cp = 0; + while (rules->INSTstr[cp + 9] && (rules->INSTstr[cp + 9] != ')')) { + sangle[cp] = rules->INSTstr[cp + 9]; + cp++; + } + sangle[cp] = 0; + int angle_i = atoi(sangle); + angle = angle_i; + } + + if (GetDoubleAttr( + rzRules->obj, "ORIENT", + orient)) // overriding any LIGHTSXX angle, probably TSSLPT + { + angle = orient; + if (strncmp(rzRules->obj->FeatureName, "LIGHTS", 6) == 0) { + angle += 180; + if (angle > 360) angle -= 360; + } + } + + // Render symbol at object's x/y +// wxPoint r, r1; +// GetPointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &r); + wxPoint2DDouble dr; + GetDoublePointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &dr); + + // Render a raster or vector symbol, as specified by LUP rules + if (rules->razRule->definition.SYDF == 'V') { + if (!m_pdc) { // GL mode + if (!RenderCachedVectorSymbol(rzRules, rules->razRule, dr, angle, + m_ChartScaleFactorExp)) + RenderHPGL(rzRules, rules->razRule, dr, angle, m_ChartScaleFactorExp); + } else + RenderHPGL(rzRules, rules->razRule, dr, angle, m_ChartScaleFactorExp); + } else { + if (rules->razRule->definition.SYDF == 'R') + RenderRasterSymbol(rzRules, rules->razRule, dr, angle); + } + } + + return 0; +} + +bool s52plib::RenderCachedVectorSymbol(ObjRazRules *rzRules, Rule *rule_in, + wxPoint2DDouble &r, float rot_angle, + double uScale) { +#ifdef ocpnUSE_GL + // TODO + // temporarily exclude some symbols + // Only handle ATONs for now + if (!rzRules->obj->bIsAton) return false; + + int symbol_texture = 0; + // Build the hash key + std::string key; + key += std::to_string(rzRules->LUP->RCID); // base LUP ID + key += rule_in->colRef.LCRF; // color + + // Check to see it the requested symbol texture is in the cache + auto search = vector_symbol_cache.find(key); + if (search != vector_symbol_cache.end()) + symbol_texture = search->second; + else { + symbol_texture = BuildHPGLTexture(rzRules, rule_in, r, rot_angle, uScale); + vector_symbol_cache[key] = symbol_texture; + } + + // Render the texture + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, symbol_texture); + + int w = rule_in->parm2; + int h = rule_in->parm3; + + if ((w == 0) || (h == 0)) { + // Get the texture dimensions a slower way, first time only + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); + rule_in->parm2 = w; + rule_in->parm3 = h; + } + + float tx1 = 0, ty1 = 0; + float tx2 = tx1 + w, ty2 = ty1 + h; + + if (m_TextureFormat == GL_TEXTURE_2D) { + // Normalize the sybmol texture coordinates against the next higher POT + // size + int rb_x = w; + int rb_y = h; + tx1 /= rb_x, tx2 /= rb_x; + ty1 /= rb_y, ty2 /= rb_y; + } + + float uv[8]; + float coords[8]; + + // Note swizzle of points to allow TRIANGLE_STRIP drawing + // normal uv + uv[0] = tx1; + uv[1] = ty1; + uv[2] = tx2; + uv[3] = ty1; + uv[6] = tx2; + uv[7] = ty2; + uv[4] = tx1; + uv[5] = ty2; + + // pixels + coords[0] = 0; + coords[1] = 0; + coords[2] = w; + coords[3] = 0; + coords[6] = w; + coords[7] = h; + coords[4] = 0; + coords[5] = h; + + if (pCtexture_2D_shader_program[0]) { + pCtexture_2D_shader_program[0]->Bind(); + glEnable(GL_BLEND); + + // Select the active texture unit. + glActiveTexture(GL_TEXTURE0); + + pCtexture_2D_shader_program[0]->SetUniform1i("uTex", 0); + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + pCtexture_2D_shader_program[0]->SetAttributePointerf("position", coords); + pCtexture_2D_shader_program[0]->SetAttributePointerf("aUV", uv); + + // Rotate + mat4x4 I, Q; + mat4x4_identity(I); + + mat4x4_translate_in_place(I, r.m_x, r.m_y, 0); + if (abs(vp_plib.rotation) > 0) + mat4x4_rotate_Z(Q, I, -vp_plib.rotation); + else + mat4x4_dup(Q, I); + // mat4x4_translate_in_place(Q, -pivot_x, -pivot_y, 0); + + pCtexture_2D_shader_program[0]->SetUniformMatrix4fv("TransformMatrix", + (GLfloat *)Q); + + // Perform the actual drawing. + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // Restore the per-object transform to Identity Matrix + mat4x4 IM; + mat4x4_identity(IM); + pCtexture_2D_shader_program[0]->SetUniformMatrix4fv("TransformMatrix", + (GLfloat *)IM); + + // Clean up the GL state + pCtexture_2D_shader_program[0]->UnBind(); + } +#endif + return true; +} + +void s52plib::SetupSoundingFont() { + if (m_isSoundingFontSet) return; + + double scale_factor = 1.0; + + if (m_display_size_mm < + 200) { // about 8 inches, implying some sort of smaller mobile device + // Set the onscreen size of the symbol + // Compensate for various display resolutions + // Develop empirically, making a buoy about 4 mm tall + double boyHeight = + 21. / + GetPPMM(); // from raster symbol definitions, boylat is xx pix high + + double targetHeight0 = 4.0; + + // But we want to scale the size for smaller displays + double displaySize = m_display_size_mm; + displaySize = wxMax(displaySize, 100); + + float targetHeight = wxMin(targetHeight0, displaySize / 30); + + double pix_factor = targetHeight / boyHeight; + + // qDebug() << "scaleing" << m_display_size_mm << targetHeight0 << + // targetHeight << GetPPMM() << boyHeight << pix_factor; + + // for Hubert, and my moto + // scaleing 93.98 93 4 3.33333 12.7312 1.64949 2.02082 + // My nvidia tab + // scaleing 144.78 144 4 4 12.6667 1.65789 2.4127 + // judgement: all OK + + // scale_factor *= pix_factor; + } + + wxFontWeight fontWeight = wxFONTWEIGHT_NORMAL; + wxString fontFacename = wxEmptyString; + double defaultHeight = 3.0; + +#ifdef __OCPN__ANDROID__ + fontWeight = wxFONTWEIGHT_BOLD; + fontFacename = _T("Roboto"); + defaultHeight = 2.2; +#endif + + int point_size = 6; + int charWidth, charHeight, charDescent; + charWidth = 1; + + double target = defaultHeight * scale_factor; + + if (abs(m_SoundingsFontSizeMM - target) > (m_soundFontDelta + .5)) { + // Recalculate the required point size to give specified height + wxScreenDC sdc; + + double font_size_mm = 0; + bool not_done = true; + while ((point_size < 32) && not_done) { + wxFont *tentativeFont = FindOrCreateFont_PlugIn( + point_size, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, fontWeight, false, + fontFacename); + sdc.GetTextExtent(_T("0"), &charWidth, &charHeight, &charDescent, NULL, + tentativeFont); // measure the text + double font_size_mm = (double)(charHeight - charDescent) / GetPPMM(); + + if (font_size_mm >= target) { + not_done = false; + m_SoundingsPointSize = point_size; + m_SoundingsFontSizeMM = font_size_mm; + m_soundFont = tentativeFont; + m_soundFontDelta = abs(font_size_mm - target); + break; + } + point_size++; + } + } else { + if (m_pdc) { + wxScreenDC sdc; + sdc.GetTextExtent(_T("0"), &charWidth, &charHeight, &charDescent, NULL, + m_soundFont); // measure the text for DC mode + } + point_size = m_SoundingsPointSize; + point_size /= m_dipfactor; // Apply Windows display scaling. + } + double postmult = m_SoundingsScaleFactor; + if ((postmult <= 2.0) && (postmult >= 0.5)) { + point_size *= postmult; + scale_factor *= postmult; + charWidth *= postmult; + } + + // Build the texDepth object, if required + if (!m_pdc) { // OpenGL + if (!m_texSoundings.IsBuilt() || + (fabs(m_texSoundings.GetScale() - scale_factor) > 0.05)) { + m_texSoundings.Delete(); + m_texSoundings.SetContentScaleFactor(m_ContentScaleFactor); + + m_soundFont = FindOrCreateFont_PlugIn(point_size, wxFONTFAMILY_SWISS, + wxFONTSTYLE_NORMAL, fontWeight, + false, fontFacename); + m_texSoundings.Build(m_soundFont, scale_factor, + m_dipfactor); // texSounding owns the font + } + } else { + m_soundFont = FindOrCreateFont_PlugIn(point_size, wxFONTFAMILY_SWISS, + wxFONTSTYLE_NORMAL, fontWeight, false, + fontFacename); + m_pdc->SetFont(*m_soundFont); + } + + // Get and record some final metrics + wxScreenDC sdc; + sdc.GetTextExtent(_T("0"), &m_sounding_char_width, &m_sounding_char_height, + &m_sounding_char_descent, NULL, + m_soundFont); // measure the text + +#ifdef ocpnUSE_GL + // Prefetch the uniform and attribute location in the 2d texture shader + if (!m_pdc) { + pCtexture_2D_Color_shader_program[0]->Bind(); + m_sounding_shader_uni_color = + pCtexture_2D_Color_shader_program[0]->getUniformLocation(s_color); + // pCtexture_2D_Color_shader_program[0]->SetUniform4fv( s_color, colorv); + + m_sounding_shader_uni_uTex = + pCtexture_2D_Color_shader_program[0]->getUniformLocation(s_uTex); + // pCtexture_2D_Color_shader_program[0]->SetUniform1i( s_uTex, 0); + + m_sounding_shader_attr_position = + pCtexture_2D_Color_shader_program[0]->getAttributeLocation(s_position); + // pCtexture_2D_Color_shader_program[0]->SetAttributePointerf( s_position, + // coords); + + m_sounding_shader_attr_aUV = + pCtexture_2D_Color_shader_program[0]->getAttributeLocation(s_aUV); + // pCtexture_2D_Color_shader_program[0]->SetAttributePointerf( s_aUV, uv); + + m_sounding_shader_uni_transform = + pCtexture_2D_Color_shader_program[0]->getUniformLocation( + s_TransformMatrix); + // pCtexture_2D_Color_shader_program[0]->SetUniformMatrix4fv( + // s_TransformMatrix, (GLfloat *)Q); + + pCtexture_2D_Color_shader_program[0]->UnBind(); + } +#endif + + m_isSoundingFontSet = true; +} + +void s52plib::RenderMPSArray(ObjRazRules *rzRules, + std::vector array) { + if (!array.size()) return; + + // Get some metrics + int charWidth = m_sounding_char_width; + int charHeight = m_sounding_char_height; + int charDescent = m_sounding_char_descent; + + int pivot_x; + int pivot_y; + unsigned int texture = 0; + wxUint32 last_color_RGB = 0; + +#ifdef ocpnUSE_GL + if (!m_pdc) { // preconfigure the shader + texture = m_texSoundings.GetTexture(); + + glEnable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + + pCtexture_2D_Color_shader_program[0]->Bind(); + // Select the active texture unit. + glActiveTexture(GL_TEXTURE0); + glUniform1i(m_sounding_shader_uni_uTex, 0); + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } +#endif + + for (auto sym : array) { + // Parse the symbol name + // The digit + char symDigit = sym.prule->name.SYNM[7]; + int symIndex = symDigit - 0x30; + + // The pivot point offset group + char symCPivot = sym.prule->name.SYNM[6]; + int symPivot = symCPivot - 0x30; + + int pivotWidth, pivotHeight; + // For opengl, the symbols are loaded in a texture + wxRect texrect; + if (!m_pdc) { // GL + m_texSoundings.GetGLTextureRect(texrect, symIndex); + + if (texture) { + sym.prule->parm2 = texrect.width; + sym.prule->parm3 = texrect.height; + } + + pivotWidth = texrect.width; + pivotHeight = texrect.height; + + } else { + pivotWidth = charWidth; + pivotHeight = charHeight - charDescent; + } + + if (symPivot < 4) { + pivot_x = (pivotWidth * symPivot); // - (pivotWidth / 4); + pivot_y = pivotHeight / 2; + } else if (symPivot == 4) { + pivot_x = -pivotWidth; // - (pivotWidth / 4); + pivot_y = pivotHeight / 2; + } else { + pivot_x = 0; //-(pivotWidth / 4); + pivot_y = pivotHeight / 5; + } + pivot_x *= m_dipfactor; + pivot_y *= m_dipfactor; + + // Now render the symbol + if (!m_pdc) // opengl + { +#ifdef ocpnUSE_GL + { + int w = texrect.width, h = texrect.height; + + float tx1 = texrect.x, ty1 = texrect.y; + float tx2 = tx1 + w, ty2 = ty1 + h; + + if (m_TextureFormat == GL_TEXTURE_2D) { + // Normalize the sybmol texture coordinates against the next higher + // POT size + wxSize size = m_texSoundings.GLTextureSize(); + int rb_x = size.x; + int rb_y = size.y; + + tx1 /= rb_x, tx2 /= rb_x; + ty1 /= rb_y, ty2 /= rb_y; + } + + float uv[8]; + float coords[8]; + + // Note swizzle of points to allow TRIANGLE_STRIP drawing + // normal uv + uv[0] = tx1; + uv[1] = ty1; + uv[2] = tx2; + uv[3] = ty1; + uv[6] = tx2; + uv[7] = ty2; + uv[4] = tx1; + uv[5] = ty2; + + // pixels + coords[0] = 0; + coords[1] = 0; + coords[2] = w; + coords[3] = 0; + coords[6] = w; + coords[7] = h; + coords[4] = 0; + coords[5] = h; + + if (sym.color_RGB != last_color_RGB) { + wxColor c; + c.SetRGB(sym.color_RGB); + float colorv[4]; + colorv[0] = c.Red() / float(256); + colorv[1] = c.Green() / float(256); + colorv[2] = c.Blue() / float(256); + colorv[3] = 1.0; + + glUniform4fv(m_sounding_shader_uni_color, 1, colorv); + last_color_RGB = sym.color_RGB; + } + + glVertexAttribPointer(m_sounding_shader_attr_position, 2, GL_FLOAT, + GL_FALSE, 2 * sizeof(float), coords); + glEnableVertexAttribArray(m_sounding_shader_attr_position); + + glVertexAttribPointer(m_sounding_shader_attr_aUV, 2, GL_FLOAT, GL_FALSE, + 2 * sizeof(float), uv); + glEnableVertexAttribArray(m_sounding_shader_attr_aUV); + + // Rotate + mat4x4 I, Q; + mat4x4_identity(I); + mat4x4_identity(Q); + + mat4x4_translate_in_place(I, sym.r.m_x, sym.r.m_y, 0); + mat4x4_rotate_Z(Q, I, -vp_plib.rotation); + mat4x4_translate_in_place(Q, -pivot_x, -pivot_y, 0); + + glUniformMatrix4fv(m_sounding_shader_uni_transform, 1, GL_FALSE, + (GLfloat *)Q); + + // Perform the actual drawing. + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } +#endif + } else { + wxString text; + text.Printf(_T("%d"), symIndex); + wxColor c; + c.SetRGB(sym.color_RGB); + m_pdc->SetTextForeground(c); + + m_pdc->DrawText(text, sym.r.m_x - pivot_x, sym.r.m_y - pivot_y); + } + } + +#ifdef ocpnUSE_GL + if (!m_pdc) { // OpenGL + // Restore the per-object transform to Identity Matrix + mat4x4 IM; + mat4x4_identity(IM); + glUniformMatrix4fv(m_sounding_shader_uni_transform, 1, GL_FALSE, + (GLfloat *)IM); + + pCtexture_2D_Color_shader_program[0]->UnBind(); + glDisable(m_TextureFormat); + glDisable(GL_BLEND); + } +#endif +} + +bool s52plib::RenderSoundingSymbol(ObjRazRules *rzRules, Rule *prule, + wxPoint &r, wxColor symColor, + float rot_angle) { + // Get some metrics + int charWidth = m_sounding_char_width; + int charHeight = m_sounding_char_height; + int charDescent = m_sounding_char_descent; + + int pivot_x; + int pivot_y; + + // Parse the symbol name + // The digit + char symDigit = prule->name.SYNM[7]; + int symIndex = symDigit - 0x30; + + // The pivot point offset group + char symCPivot = prule->name.SYNM[6]; + int symPivot = symCPivot - 0x30; + + int pivotWidth, pivotHeight; + // For opengl, the symbols are loaded in a texture + unsigned int texture = 0; + wxRect texrect; + if (!m_pdc) { // GL + texture = m_texSoundings.GetTexture(); + m_texSoundings.GetGLTextureRect(texrect, symIndex); + + if (texture) { + prule->parm2 = texrect.width; + prule->parm3 = texrect.height; + } + + pivotWidth = texrect.width; + pivotHeight = texrect.height; + + } else { + pivotWidth = charWidth; + pivotHeight = charHeight - charDescent; + } + + if (symPivot < 4) { + pivot_x = (pivotWidth * symPivot); // - (pivotWidth / 4); + pivot_y = pivotHeight / 2; + } else if (symPivot == 4) { + pivot_x = -pivotWidth; // - (pivotWidth / 4); + pivot_y = pivotHeight / 2; + } else { + pivot_x = 0; //-(pivotWidth / 4); + pivot_y = pivotHeight / 5; + } + pivot_x *= m_dipfactor; + pivot_y *= m_dipfactor; + + // Get the bounding box for the to-be-drawn symbol + int b_width, b_height; + b_width = prule->parm2; + b_height = prule->parm3; + + LLBBox symbox; + double latmin, lonmin, latmax, lonmax; + + if (!m_pdc && fabs(vp_plib.rotation) > .01) // opengl + { + float cx = vp_plib.pix_width / 2.; + float cy = vp_plib.pix_height / 2.; + float c = cosf(vp_plib.rotation); + float s = sinf(vp_plib.rotation); + float x = r.x - pivot_x - cx; + float y = r.y - pivot_y + b_height - cy; + GetPixPointSingle(x * c - y * s + cx, x * s + y * c + cy, &latmin, &lonmin); + + x = r.x - pivot_x + b_width - cx; + y = r.y - pivot_y - cy; + GetPixPointSingle(x * c - y * s + cx, x * s + y * c + cy, &latmax, &lonmax); + } else { + GetPixPointSingle(r.x - pivot_x, r.y - pivot_y + b_height, &latmin, + &lonmin); + GetPixPointSingle(r.x - pivot_x + b_width, r.y - pivot_y, &latmax, &lonmax); + } + symbox.Set(latmin, lonmin, latmax, lonmax); + + // Now render the symbol + + if (!m_pdc) // opengl + { +#ifdef ocpnUSE_GL + glEnable(GL_BLEND); + + if (texture) { + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, texture); + + int w = texrect.width, h = texrect.height; + + float tx1 = texrect.x, ty1 = texrect.y; + float tx2 = tx1 + w, ty2 = ty1 + h; + + if (m_TextureFormat == GL_TEXTURE_2D) { + // Normalize the sybmol texture coordinates against the next higher POT + // size + wxSize size = m_texSoundings.GLTextureSize(); + int rb_x = size.x; + int rb_y = size.y; + + tx1 /= rb_x, tx2 /= rb_x; + ty1 /= rb_y, ty2 /= rb_y; + } + + float uv[8]; + float coords[8]; + + // Note swizzle of points to allow TRIANGLE_STRIP drawing + // normal uv + uv[0] = tx1; + uv[1] = ty1; + uv[2] = tx2; + uv[3] = ty1; + uv[6] = tx2; + uv[7] = ty2; + uv[4] = tx1; + uv[5] = ty2; + + // pixels + coords[0] = 0; + coords[1] = 0; + coords[2] = w; + coords[3] = 0; + coords[6] = w; + coords[7] = h; + coords[4] = 0; + coords[5] = h; + + float colorv[4]; + colorv[0] = symColor.Red() / float(256); + colorv[1] = symColor.Green() / float(256); + colorv[2] = symColor.Blue() / float(256); + colorv[3] = 1.0; + + pCtexture_2D_Color_shader_program[0]->Bind(); + // Select the active texture unit. + glActiveTexture(GL_TEXTURE0); + glUniform4fv(m_sounding_shader_uni_color, 1, colorv); + glUniform1i(m_sounding_shader_uni_uTex, 0); + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + glVertexAttribPointer(m_sounding_shader_attr_position, 2, GL_FLOAT, + GL_FALSE, 2 * sizeof(float), coords); + glEnableVertexAttribArray(m_sounding_shader_attr_position); + + glVertexAttribPointer(m_sounding_shader_attr_aUV, 2, GL_FLOAT, GL_FALSE, + 2 * sizeof(float), uv); + glEnableVertexAttribArray(m_sounding_shader_attr_aUV); + + // Rotate + mat4x4 I, Q; + mat4x4_identity(I); + mat4x4_identity(Q); + + mat4x4_translate_in_place(I, r.x, r.y, 0); + mat4x4_rotate_Z(Q, I, -vp_plib.rotation); + mat4x4_translate_in_place(Q, -pivot_x, -pivot_y, 0); + + glUniformMatrix4fv(m_sounding_shader_uni_transform, 1, GL_FALSE, + (GLfloat *)Q); + + // Perform the actual drawing. + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // Restore the per-object transform to Identity Matrix + mat4x4 IM; + mat4x4_identity(IM); + + glUniformMatrix4fv(m_sounding_shader_uni_transform, 1, GL_FALSE, + (GLfloat *)IM); + + pCtexture_2D_Color_shader_program[0]->UnBind(); + glDisable(m_TextureFormat); + } else { /* this is only for legacy mode, or systems without NPOT textures + */ + float cr = cosf(vp_plib.rotation); + float sr = sinf(vp_plib.rotation); + float ddx = pivot_x * cr + pivot_y * sr; + float ddy = pivot_y * cr - pivot_x * sr; +#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL) +#endif + } + + glDisable(GL_BLEND); +#endif + } else { + wxString text; + text.Printf(_T("%d"), symIndex); + m_pdc->SetTextForeground(symColor); + + m_pdc->DrawText(text, r.x - pivot_x, r.y - pivot_y); + } + + return true; +} + +// Line Simple Style, OpenGL +int s52plib::RenderGLLS(ObjRazRules *rzRules, Rules *rules) { + // for now don't use vbo model in non-mercator + // if (vp->m_projection_type != PROJECTION_MERCATOR) + // return RenderLS(rzRules, rules, vp); + + if (!ObjectRenderCheckPosReduced(rzRules)) return false; + + if (!m_benableGLLS) // root chart cannot support VBO model + return RenderLS(rzRules, rules); + + if (!rzRules->obj->m_chart_context->chart) + return RenderLS(rzRules, rules); // this is where S63 PlugIn gets caught + + if ((GetBBox().GetMaxLon() >= 180.) || (GetBBox().GetMinLon() <= -180.)) + return RenderLS(rzRules, rules); // cm93 has trouble at IDL + + bool b_useVBO = false; + float *vertex_buffer = 0; + + if (rzRules->obj->auxParm2 > 0) // Has VBO been defined and uploaded? + b_useVBO = true; + + if (!b_useVBO) { + vertex_buffer = rzRules->obj->m_chart_context->vertex_buffer; + } + +#ifdef ocpnUSE_GL + + char *str = (char *)rules->INSTstr; + + if ((!strncmp(str, "DASH", 4)) || (!strncmp(str, "DOTT", 4))) + return RenderLS_Dash_GLSL(rzRules, rules); + + LLBBox BBView = GetReducedBBox(); + + // Allow a little slop in calculating whether a segment + // is within the requested VPointCompat + double margin = BBView.GetLonRange() * .05; + BBView.EnLarge(margin); + + // Get the current display priority + // Default comes from the LUP, unless overridden + int priority_current = rzRules->LUP->DPRI - '0'; + if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI; + + line_segment_element *ls_list = rzRules->obj->m_ls_list; + + S52color *c = getColor(str + 7); // Colour + int w = atoi(str + 5); // Width + +#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL) +#endif + + // Set drawing width + float lineWidth = w; + lineWidth = wxMax(m_GLMinCartographicLineWidth, w); + + // Manage super high density displays + float target_w_mm = 0.5 * w; + if (GetPPMM() > 7) { // arbitrary + target_w_mm = + ((float)w) / + 6.0; // Target width in mm + // The value "w" comes from S52 library CNSY procedures, in + // "nominal" pixels + // the value "6" comes from semi-standard LCD display densities + // or something like 0.18 mm pitch, or 6 pix per mm. + lineWidth = wxMax(m_GLMinCartographicLineWidth, target_w_mm * GetPPMM()); + } + + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); + +#ifdef __OCPN__ANDROID__ + // if( w > 1 ) + // lineWidth = wxMin(lineWidth, parms[1]); + glLineWidth(lineWidth); + +#else + glLineWidth(lineWidth); + if (lineWidth > 4.0 && m_GLLineSmoothing) { + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + } +#endif + +#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL) +#endif + +#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL) +#endif + + // Has line segment PBO been allocated for this chart? + if (b_useVBO) { + glBindBuffer(GL_ARRAY_BUFFER, rzRules->obj->auxParm2); + } + + CGLShaderProgram *shader = pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/]; + shader->Bind(); + + // Disable VBO's (vertex buffer objects) for attributes. + if (!b_useVBO) glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + GLint pos = shader->getAttributeLocation("position"); + float angle = 0; + + // We cannot use the prepared shader uniforms, as we can (and should for + // performance) do the per-object transforms all at once in the shader matrix. + // But we also must restore the prepared matrix for later rendering of other + // object classes. + + // Build Transform matrix + // First, the VP transform + mat4x4 I, Q; + mat4x4_identity(I); + + // Scale per object + I[0][0] *= rzRules->obj->x_rate; + I[1][1] *= rzRules->obj->y_rate; + + // Translate per object + I[3][0] = -(rzRules->sm_transform_parms->easting_vp_center - + rzRules->obj->x_origin) * + vp_plib.view_scale_ppm; + I[3][1] = -(rzRules->sm_transform_parms->northing_vp_center - + rzRules->obj->y_origin) * + -vp_plib.view_scale_ppm; + + // Scale for screen + I[0][0] *= vp_plib.view_scale_ppm; + I[1][1] *= -vp_plib.view_scale_ppm; + + // Rotate + mat4x4_rotate_Z(Q, I, angle); + + // Translate for screen + Q[3][0] += vp_plib.pix_width / 2; + Q[3][1] += vp_plib.pix_height / 2; + + shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)Q); + + float colorv[4]; + colorv[0] = c->R / float(256); + colorv[1] = c->G / float(256); + colorv[2] = c->B / float(256); + colorv[3] = 1.0; + + shader->SetUniform4fv("color", colorv); + + if (!b_useVBO) { + unsigned char *buffer = (unsigned char *)vertex_buffer; + float *bufBase = (float *)buffer; + glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), + bufBase); + } else { + glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), + (GLvoid *)(0)); + } + glEnableVertexAttribArray(pos); + + // from above ls_list is the first drawable segment + while (ls_list) { + if (ls_list->priority == priority_current) { + size_t seg_vbo_offset = 0; + size_t point_count = 0; + + // Check visibility of the segment + bool b_drawit = false; + if ((ls_list->ls_type == TYPE_EE) || (ls_list->ls_type == TYPE_EE_REV)) { + if (BBView.GetMinLat() < ls_list->pedge->edgeBBox.GetMaxLat() && + BBView.GetMaxLat() > ls_list->pedge->edgeBBox.GetMinLat() && + BBView.GetMinLon() <= ls_list->pedge->edgeBBox.GetMaxLon() && + BBView.GetMaxLon() >= ls_list->pedge->edgeBBox.GetMinLon()) { + // render the segment + b_drawit = true; + seg_vbo_offset = ls_list->pedge->vbo_offset; + point_count = ls_list->pedge->nCount; + } + + } else { + if (BBView.GetMinLat() < ls_list->pcs->cs_lat_avg && + BBView.GetMaxLat() > ls_list->pcs->cs_lat_avg && + BBView.GetMinLon() <= ls_list->pcs->cs_lon_avg && + BBView.GetMaxLon() >= ls_list->pcs->cs_lon_avg) { + // render the segment + b_drawit = true; + seg_vbo_offset = ls_list->pcs->vbo_offset; + point_count = 2; + } + } + + if (b_drawit) { + // render the segment + glDrawArrays(GL_LINE_STRIP, seg_vbo_offset / (2 * sizeof(float)), + point_count); + } + } + ls_list = ls_list->next; + } + + if (b_useVBO) glBindBuffer(GL_ARRAY_BUFFER, 0); + + // Restore shader TransForm Matrix to identity. + mat4x4 IM; + mat4x4_identity(IM); + shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)IM); + shader->UnBind(); + + glDisable(GL_LINE_STIPPLE); + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); + +#endif // OpenGL + + return 1; +} + +// Line Simple Style +int s52plib::RenderLS(ObjRazRules *rzRules, Rules *rules) { + + // catch legacy PlugIns (e.g.s63_pi) + if (rzRules->obj->m_n_lsindex && !rzRules->obj->m_ls_list) + return RenderLSLegacy(rzRules, rules); + + // catch improperly coded edge arrays, usually seen on cm93 + if (!rzRules->obj->m_n_lsindex && !rzRules->obj->m_ls_list) return 0; + + S52color *c; + int w; + + char *str = (char *)rules->INSTstr; + c = getColor(str + 7); // Colour + wxColour color(c->R, c->G, c->B); + w = atoi(str + 5); // Width + + double scale_factor = vp_plib.ref_scale / vp_plib.chart_scale; + double scaled_line_width = wxMax((scale_factor), 1); + bool b_wide_line = false; + + wxPen wide_pen(*wxBLACK_PEN); + wxDash dashw[2]; + dashw[0] = 3; + dashw[1] = 1; + + if (b_wide_line) { + int w = wxMax(scaled_line_width, 2); // looks better + w = wxMin(w, 50); // upper bound + wide_pen.SetWidth(w); + wide_pen.SetColour(color); + + if (!strncmp(str, "DOTT", 4)) { + dashw[0] = 1; + wide_pen.SetStyle(wxPENSTYLE_USER_DASH); + wide_pen.SetDashes(2, dashw); + } else if (!strncmp(str, "DASH", 4)) { + wide_pen.SetStyle(wxPENSTYLE_USER_DASH); + if (m_pdc) { // DC mode + dashw[0] = 1; + dashw[1] = 2; + } + + wide_pen.SetDashes(2, dashw); + } + } + + wxPen thispen(color, w, wxPENSTYLE_SOLID); + wxDash dash1[2]; + + if (m_pdc) // DC mode + { + if (!strncmp(str, "DOTT", 4)) { + thispen.SetStyle(wxPENSTYLE_USER_DASH); + dash1[0] = 1; + dash1[1] = 2; + thispen.SetDashes(2, dash1); + } else if (!strncmp(str, "DASH", 4)) { + thispen.SetStyle(wxPENSTYLE_SHORT_DASH); + } + + if (b_wide_line) + m_pdc->SetPen(wide_pen); + else + m_pdc->SetPen(thispen); + + } +#ifdef ocpnUSE_GL + else // OpenGL mode + { + glDisable(GL_LINE_SMOOTH); + + // Set drawing width + if (w > 1) { + GLint parms[2]; + glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, &parms[0]); + if (w > parms[1]) + glLineWidth(wxMax(m_GLMinCartographicLineWidth, parms[1])); + else + glLineWidth(wxMax(m_GLMinCartographicLineWidth, w)); + } else + glLineWidth(wxMax(m_GLMinCartographicLineWidth, 1)); + +#ifndef __OCPN__ANDROID__ + if (w >= 2 && m_GLLineSmoothing) { + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + } +#endif + } +#endif + + // Get a true pixel clipping/bounding box from the vp + wxPoint pbb = GetPixFromLL(vp_plib.clat, vp_plib.clon); + int xmin_ = pbb.x - (vp_plib.rv_rect.width / 2) - (4 * scaled_line_width); + int xmax_ = xmin_ + vp_plib.rv_rect.width + (8 * scaled_line_width); + int ymin_ = pbb.y - (vp_plib.rv_rect.height / 2) - (4 * scaled_line_width); + int ymax_ = ymin_ + vp_plib.rv_rect.height + (8 * scaled_line_width); + + int x0, y0, x1, y1; + + // Get the current display priority + // Default comes from the LUP, unless overridden + int priority_current = rzRules->LUP->DPRI - '0'; + if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI; + + if (rzRules->obj->m_ls_list) { + float *ppt; + + unsigned char *vbo_point = + (unsigned char *)rzRules->obj->m_chart_context->vertex_buffer; + line_segment_element *ls = rzRules->obj->m_ls_list; + + while (ls) { + if (ls->priority == priority_current) { + int nPoints; + // fetch the first point + if ((ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV)) { + ppt = (float *)(vbo_point + ls->pedge->vbo_offset); + nPoints = ls->pedge->nCount; + } else { + ppt = (float *)(vbo_point + ls->pcs->vbo_offset); + nPoints = 2; + } + + wxPoint l; + GetPointPixSingle(rzRules, ppt[1], ppt[0], &l); + ppt += 2; + + for (int ip = 0; ip < nPoints - 1; ip++) { + wxPoint r; + GetPointPixSingle(rzRules, ppt[1], ppt[0], &r); + // Draw the edge as point-to-point + x0 = l.x, y0 = l.y; + x1 = r.x, y1 = r.y; + + // Do not draw null segments + if ((x0 != x1) || (y0 != y1)) { + if (m_pdc) { + if (cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, xmin_, xmax_, + ymin_, ymax_) != Invisible) + m_pdc->DrawLine(x0, y0, x1, y1); + } + } + + l = r; + ppt += 2; + } + } + + ls = ls->next; + } + } + +#ifdef ocpnUSE_GL + if (!m_pdc) { + glDisable(GL_LINE_STIPPLE); + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); + } +#endif + return 1; +} + +// Line Simple Style +int s52plib::RenderLSLegacy(ObjRazRules *rzRules, Rules *rules) { + if (!rzRules->obj->m_chart_context->chart) + return RenderLSPlugIn(rzRules, rules); + + // Must be cm93 + S52color *c; + int w; + GLint pos = 0; + + char *str = (char *)rules->INSTstr; + c = getColor(str + 7); // Colour + wxColour color(c->R, c->G, c->B); + w = atoi(str + 5); // Width + + double scale_factor = vp_plib.ref_scale / vp_plib.chart_scale; + double scaled_line_width = wxMax((scale_factor), 1); + bool b_wide_line = false; + + wxPen wide_pen(*wxBLACK_PEN); + wxDash dashw[2]; + dashw[0] = 3; + dashw[1] = 1; + + if (b_wide_line) { + int w = wxMax(scaled_line_width, 2); // looks better + w = wxMin(w, 50); // upper bound + wide_pen.SetWidth(w); + wide_pen.SetColour(color); + + if (!strncmp(str, "DOTT", 4)) { + dashw[0] = 1; + wide_pen.SetStyle(wxPENSTYLE_USER_DASH); + wide_pen.SetDashes(2, dashw); + } else if (!strncmp(str, "DASH", 4)) { + wide_pen.SetStyle(wxPENSTYLE_USER_DASH); + if (m_pdc) { // DC mode + dashw[0] = 1; + dashw[1] = 2; + } + + wide_pen.SetDashes(2, dashw); + } + } + + wxPen thispen(color, w, wxPENSTYLE_SOLID); + wxDash dash1[2]; + + if (m_pdc) // DC mode + { + if (!strncmp(str, "DOTT", 4)) { + thispen.SetStyle(wxPENSTYLE_USER_DASH); + dash1[0] = 1; + dash1[1] = 2; + thispen.SetDashes(2, dash1); + } else if (!strncmp(str, "DASH", 4)) { + thispen.SetStyle(wxPENSTYLE_SHORT_DASH); + } + + if (b_wide_line) + m_pdc->SetPen(wide_pen); + else + m_pdc->SetPen(thispen); + + } +#ifdef ocpnUSE_GL + else // OpenGL mode + { + glDisable(GL_LINE_SMOOTH); + + // Set drawing width + if (w > 1) { + GLint parms[2]; + glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, &parms[0]); + if (w > parms[1]) + glLineWidth(wxMax(m_GLMinCartographicLineWidth, parms[1])); + else + glLineWidth(wxMax(m_GLMinCartographicLineWidth, w)); + } else + glLineWidth(wxMax(m_GLMinCartographicLineWidth, 1)); + +#ifndef __OCPN__ANDROID__ + if (w >= 2 && m_GLLineSmoothing) { + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + } +#endif + } +#endif + + // Get a true pixel clipping/bounding box from the vp + wxPoint pbb = GetPixFromLL(vp_plib.clat, vp_plib.clon); + int xmin_ = pbb.x - (vp_plib.rv_rect.width / 2) - (4 * scaled_line_width); + int xmax_ = xmin_ + vp_plib.rv_rect.width + (8 * scaled_line_width); + int ymin_ = pbb.y - (vp_plib.rv_rect.height / 2) - (4 * scaled_line_width); + int ymax_ = ymin_ + vp_plib.rv_rect.height + (8 * scaled_line_width); + + int x0, y0, x1, y1; + + if (rzRules->obj->m_n_lsindex) { + // This is cm93 + auto ve_hash = rzRules->obj->m_chart_context->m_pve_hash; + auto vc_hash = rzRules->obj->m_chart_context->m_pvc_hash; + + // Get the current display priority + // Default comes from the LUP, unless overridden + int priority_current = rzRules->LUP->DPRI - '0'; + if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI; + + int *index_run; + float *ppt; + VC_Element *pnode; +#ifdef ocpnUSE_GL + CGLShaderProgram *shader = + pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/]; + shader->Bind(); + + float fBuf[4]; + pos = shader->getAttributeLocation("position"); + glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), fBuf); + glEnableVertexAttribArray(pos); + + float colorv[4]; + colorv[0] = c->R / float(256); + colorv[1] = c->G / float(256); + colorv[2] = c->B / float(256); + colorv[3] = 1.0; + + shader->SetUniform4fv("color", colorv); +#endif + + for (int iseg = 0; iseg < rzRules->obj->m_n_lsindex; iseg++) { + int seg_index = iseg * 3; + index_run = &rzRules->obj->m_lsindex_array[seg_index]; + + // Get first connected node + unsigned int inode = *index_run++; + + // Get the edge + unsigned int enode = *index_run++; + VE_Element *pedge = 0; + if (enode) pedge = (*ve_hash)[enode]; + + // Get last connected node + unsigned int jnode = *index_run++; + + int nls; + if (pedge) { + // Here we decide to draw or not based on the highest priority seen for + // this segment That is, if this segment is going to be drawn at a + // higher priority later, then "continue", and don't draw it here. + + // This logic is not perfectly right for one case: + // If the segment has only two end connected nodes, and no intermediate + // edge, then we have no good way to evaluate the priority. This is due + // to the fact that priority is only precalculated for edge segments, + // not connector nodes. Only thing to do is take the conservative + // approach and draw the segment, in this case. + if (pedge->nCount && pedge->max_priority != priority_current) continue; + nls = pedge->nCount + 1; + } else + nls = 1; + + wxPoint l; + bool lastvalid = false; + for (int ipc = 0; ipc < nls + 1; ipc++) { + ppt = 0; + if (ipc == 0) { + if (inode) { + pnode = (*vc_hash)[inode]; + if (pnode) ppt = pnode->pPoint; + } + } else if (ipc == nls) { + if ((jnode)) { + pnode = (*vc_hash)[jnode]; + if (pnode) ppt = pnode->pPoint; + } + } else if (pedge) + ppt = pedge->pPoints + 2 * (ipc - 1); + + if (ppt) { + wxPoint r; + GetPointPixSingle(rzRules, ppt[1], ppt[0], &r); + + if (1 /*r.x != INVALID_COORD*/) { + if (lastvalid) { + // Draw the edge as point-to-point + x0 = l.x, y0 = l.y; + x1 = r.x, y1 = r.y; + + // Do not draw null segments + if ((x0 == x1) && (y0 == y1)) continue; + + if (m_pdc) { + if (cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, xmin_, + xmax_, ymin_, + ymax_) != Invisible) + m_pdc->DrawLine(x0, y0, x1, y1); + } else { +#ifdef ocpnUSE_GL + fBuf[0] = x0; + fBuf[1] = y0; + fBuf[2] = x1; + fBuf[3] = y1; + + glDrawArrays(GL_LINES, 0, 2); +#endif + } + } + + l = r; + lastvalid = true; + } else + lastvalid = false; + } else + lastvalid = false; + } // for + } // for + +#ifdef ocpnUSE_GL + glDisableVertexAttribArray(pos); + shader->UnBind(); +#endif + } + +#ifdef ocpnUSE_GL + if (!m_pdc) { + glDisable(GL_LINE_STIPPLE); + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); + } +#endif + return 1; +} + +class PI_connector_segment // This was extracted verbatim from S63_pi private + // definition +{ +public: + void *start; + void *end; + SegmentType type; + int vbo_offset; + int max_priority; +}; + +int s52plib::RenderLSPlugIn(ObjRazRules *rzRules, Rules *rules) { + S52color *c; + int w; + + char *str = (char *)rules->INSTstr; + c = getColor(str + 7); // Colour + wxColour color(c->R, c->G, c->B); + w = atoi(str + 5); // Width + + double scale_factor = vp_plib.ref_scale / vp_plib.chart_scale; + double scaled_line_width = wxMax((scale_factor), 1); + bool b_wide_line = false; + + wxPen thispen(color, w, wxPENSTYLE_SOLID); + wxDash dash1[2]; + + if (m_pdc) // DC mode + { + if (!strncmp(str, "DOTT", 4)) { + thispen.SetStyle(wxPENSTYLE_USER_DASH); + dash1[0] = 1; + dash1[1] = 2; + thispen.SetDashes(2, dash1); + } else if (!strncmp(str, "DASH", 4)) { + thispen.SetStyle(wxPENSTYLE_SHORT_DASH); + } + + m_pdc->SetPen(thispen); + + } else // OpenGL mode + { +#ifdef ocpnUSE_GL + // Set drawing width + if (w > 1) { + GLint parms[2]; + glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, &parms[0]); + if (w > parms[1]) + glLineWidth(wxMax(m_GLMinCartographicLineWidth, parms[1])); + else + glLineWidth(wxMax(m_GLMinCartographicLineWidth, w)); + } else + glLineWidth(wxMax(m_GLMinCartographicLineWidth, 1)); +#endif + } + + // Get a true pixel clipping/bounding box from the vp + wxPoint pbb = GetPixFromLL(vp_plib.clat, vp_plib.clon); + int xmin_ = pbb.x - (vp_plib.rv_rect.width / 2) - (4 * scaled_line_width); + int xmax_ = xmin_ + vp_plib.rv_rect.width + (8 * scaled_line_width); + int ymin_ = pbb.y - (vp_plib.rv_rect.height / 2) - (4 * scaled_line_width); + int ymax_ = ymin_ + vp_plib.rv_rect.height + (8 * scaled_line_width); + + int x0, y0, x1, y1; + + // Get the current display priority + // Default comes from the LUP, unless overridden + int priority_current = rzRules->LUP->DPRI - '0'; + if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI; + + if (rzRules->obj->m_ls_list_legacy) { +#ifdef ocpnUSE_GL + CGLShaderProgram *shader = + pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/]; + shader->Bind(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + float colorv[4]; + colorv[0] = color.Red() / float(256); + colorv[1] = color.Green() / float(256); + colorv[2] = color.Blue() / float(256); + colorv[3] = 1.0; // transparency; + + shader->SetUniform4fv("color", colorv); + + GLint pos = shader->getAttributeLocation("position"); + glEnableVertexAttribArray(pos); +#endif + float *ppt; + VE_Element *pedge; + PI_connector_segment *pcs; + + unsigned char *vbo_point = + (unsigned char *)rzRules->obj->m_chart_context->vertex_buffer; + PI_line_segment_element *ls = rzRules->obj->m_ls_list_legacy; + + while (ls) { + if (ls->priority == priority_current) { + int nPoints; + // fetch the first point + if (ls->type == TYPE_EE) { + pedge = (VE_Element *)ls->private0; + ppt = (float *)(vbo_point + pedge->vbo_offset); + nPoints = pedge->nCount; + } else { + pcs = (PI_connector_segment *)ls->private0; + ppt = (float *)(vbo_point + pcs->vbo_offset); + nPoints = 2; + } + + wxPoint l; + GetPointPixSingle(rzRules, ppt[1], ppt[0], &l); + ppt += 2; + + for (int ip = 0; ip < nPoints - 1; ip++) { + wxPoint r; + GetPointPixSingle(rzRules, ppt[1], ppt[0], &r); + // Draw the edge as point-to-point + x0 = l.x, y0 = l.y; + x1 = r.x, y1 = r.y; + + // Do not draw null segments + if ((x0 != x1) || (y0 != y1)) { + if (m_pdc) { + if (cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, xmin_, xmax_, + ymin_, ymax_) != Invisible) + m_pdc->DrawLine(x0, y0, x1, y1); + } else { +#ifdef ocpnUSE_GL + // simplified faster test, let opengl do the rest + if ((x0 > xmin_ || x1 > xmin_) && (x0 < xmax_ || x1 < xmax_) && + (y0 > ymin_ || y1 > ymin_) && (y0 < ymax_ || y1 < ymax_)) { + float pts[4]; + pts[0] = x0; + pts[1] = y0; + pts[2] = x1; + pts[3] = y1; + + glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, + 2 * sizeof(float), pts); + + glDrawArrays(GL_LINES, 0, 2); + } +#endif + } + } + + l = r; + ppt += 2; + } + } + ls = ls->next; + } + +#ifdef ocpnUSE_GL + shader->UnBind(); +#endif + } + + return 1; +} + +// Line Simple Style, Dashed, using GLSL +#ifdef ocpnUSE_GL +int s52plib::RenderLS_Dash_GLSL(ObjRazRules *rzRules, Rules *rules) { + // Retrieve the current clipping rectangle + wxRect clip_rect = wxRect(0, 0, vp_plib.pix_width, vp_plib.pix_height); + if (glIsEnabled(GL_SCISSOR_TEST)) { + int rect[4]; + glGetIntegerv(GL_SCISSOR_BOX, rect); + clip_rect.x = rect[0]; + clip_rect.y = vp_plib.pix_height - (rect[1] + rect[3]); + clip_rect.width = rect[2]; + clip_rect.height = rect[3]; + } + + S52color *c; + + char *instr_str = (char *)rules->INSTstr; + c = getColor(instr_str + 7); // Colour + wxColour color(c->R, c->G, c->B); + int w = atoi(instr_str + 5); // Width + + double scale_factor = vp_plib.ref_scale / vp_plib.chart_scale; + double scaled_line_width = wxMax((scale_factor), 1); + + // Set drawing width + + float lineWidth = w; + GLint parms[2]; + glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, &parms[0]); + GLint parmsa[2]; + glGetIntegerv(GL_SMOOTH_LINE_WIDTH_RANGE, &parmsa[0]); + GLint parmsb[2]; + glGetIntegerv(GL_SMOOTH_LINE_WIDTH_GRANULARITY, &parmsb[0]); + + if (w > 1) { + if (w > parms[1]) + lineWidth = wxMax(m_GLMinCartographicLineWidth, parms[1]); + else + lineWidth = wxMax(m_GLMinCartographicLineWidth, w); + } else + lineWidth = wxMax(m_GLMinCartographicLineWidth, 1); + + // Manage super high density displays + float target_w_mm = 0.5 * w; + ; + if (GetPPMM() > + 7) { // arbitrary, leaves average desktop/laptop display untweaked... + target_w_mm = ((float)w) / 6.0; // Target width in mm + // The value "w" comes from S52 library CNSY procedures, in "nominal" + // pixels + // the value "6" comes from semi-standard LCD display densities + // or something like 0.18 mm pitch, or 6 pix per mm. + lineWidth = wxMax(m_GLMinCartographicLineWidth, target_w_mm * GetPPMM()); + } + + glDisable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); // for shader + +#ifdef __OCPN__ANDROID__ + lineWidth = wxMin(lineWidth, parms[1]); + glLineWidth(lineWidth); + +#else + glLineWidth(lineWidth); + if (lineWidth > 4.0) { + glEnable(GL_LINE_SMOOTH); + } +#endif + + // Get a true pixel clipping/bounding box from the vp + wxPoint pbb = GetPixFromLL(vp_plib.clat, vp_plib.clon); + int xmin_ = pbb.x - (vp_plib.rv_rect.width / 2) - (4 * scaled_line_width); + int xmax_ = xmin_ + vp_plib.rv_rect.width + (8 * scaled_line_width); + int ymin_ = pbb.y - (vp_plib.rv_rect.height / 2) - (4 * scaled_line_width); + int ymax_ = ymin_ + vp_plib.rv_rect.height + (8 * scaled_line_width); + + double x0, y0, x1, y1; + + // Get the current display priority + // Default comes from the LUP, unless overridden + int priority_current = rzRules->LUP->DPRI - '0'; + if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI; + + glUseProgram(S52Dash_shader_program); + + // Get pointers to the attributes in the program. + GLint mPosAttrib = glGetAttribLocation(S52Dash_shader_program, "position"); + glEnableVertexAttribArray(mPosAttrib); + + GLint startPos = glGetUniformLocation(S52Dash_shader_program, "startPos"); + GLint texWidth = glGetUniformLocation(S52Dash_shader_program, "texWidth"); + GLint dashFactor = glGetUniformLocation(S52Dash_shader_program, "dashFactor"); + + float colorv[4]; + colorv[0] = color.Red() / float(256); + colorv[1] = color.Green() / float(256); + colorv[2] = color.Blue() / float(256); + colorv[3] = 1.0; // transparency; + + GLint colloc = glGetUniformLocation(S52Dash_shader_program, "color"); + glUniform4fv(colloc, 1, colorv); + + // Select the active texture unit. + glActiveTexture(GL_TEXTURE0); + + // Set up the texture sampler to texture unit 0 + GLint texUni = glGetUniformLocation(S52Dash_shader_program, "uTex"); + glUniform1i(texUni, 0); + + if (!strncmp(instr_str, "DASH", 4)) { + // reduced from s52 specs (5.4), 3.6mm dash, 1.8mm space + float width = GetPPMM() * 3; // looks better + glUniform1f(texWidth, width); + glUniform1f(dashFactor, 0.66); + } else { + float width = GetPPMM() * 1; + glUniform1f(texWidth, width); + glUniform1f(dashFactor, 0.5); + } + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + // Rotate + mat4x4 I; + mat4x4_identity(I); + + GLint matloc = + glGetUniformLocation(S52Dash_shader_program, "TransformMatrix"); + glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)I); + + if (rzRules->obj->m_ls_list) { + float *ppt; + + unsigned char *vbo_point = + (unsigned char *)rzRules->obj->m_chart_context->vertex_buffer; + line_segment_element *ls = rzRules->obj->m_ls_list; + + while (ls) { + if (ls->priority == priority_current) { + int nPoints; + // fetch the first point + if ((ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV)) { + ppt = (float *)(vbo_point + ls->pedge->vbo_offset); + nPoints = ls->pedge->nCount; + } else { + ppt = (float *)(vbo_point + ls->pcs->vbo_offset); + nPoints = 2; + } + + wxPoint2DDouble l; + GetDoublePointPixSingle(rzRules, ppt[1], ppt[0], &l); + ppt += 2; + + for (int ip = 0; ip < nPoints - 1; ip++) { + wxPoint2DDouble r; + GetDoublePointPixSingle(rzRules, ppt[1], ppt[0], &r); + // Draw the edge as point-to-point + x0 = l.m_x, y0 = l.m_y; + x1 = r.m_x, y1 = r.m_y; + + // Do not draw null segments + if ((x0 != x1) || (y0 != y1)) { + // segment must be at least on-screen.... + if ((x0 > xmin_ || x1 > xmin_) && (x0 < xmax_ || x1 < xmax_) && + (y0 > ymin_ || y1 > ymin_) && (y0 < ymax_ || y1 < ymax_)) { + // And intersecting the current clip rectangle + double xa = x0; + double xw = x1 - x0; + if (xw < 0) { + xa = x1; + xw = -xw; + } + double ya = y0; + double yh = y1 - y0; + if (yh < 0) { + ya = y1; + yh = -yh; + } + + wxRect rseg(xa, ya, xw, yh); + rseg.Inflate(1); // avoid zero width/height + + if (rseg.Intersects(clip_rect)) { + float coords[4]; + + coords[0] = x0; + coords[1] = y0; + coords[2] = x1; + coords[3] = y1; + + float start[2]; + start[0] = x0; + start[1] = vp_plib.pix_height - y0; + glUniform2fv(startPos, 1, start); + + // Set the attribute mPosAttrib with the vertices in the screen + // coordinates... + glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, + coords); + glEnableVertexAttribArray(mPosAttrib); + + // Perform the actual drawing. + glDrawArrays(GL_LINES, 0, 2); + } + } + } + + l = r; + ppt += 2; + } + } + + ls = ls->next; + } + } + + glUseProgram(0); + glDisableVertexAttribArray(mPosAttrib); + + return 1; +} +#endif + +// Line Complex +int s52plib::RenderLCTexture(ObjRazRules *rzRules, Rules *rules) { + + // catch cm93 and legacy PlugIns (e.g.s63_pi) + if (rzRules->obj->m_n_lsindex && !rzRules->obj->m_ls_list) + return RenderLCLegacy(rzRules, rules); + + //int wd = ComputeWinding(rzRules, rules); + bool cww = false; + //if (wd == 2) cww = true; + + wxPoint r; + + int isym_len = rules->razRule->pos.line.bnbox_w.SYHL; + float sym_len = isym_len * canvas_pix_per_mm / 100; + + int isym_height = rules->razRule->pos.line.bnbox_h.SYVL; + float sym_height = isym_height * canvas_pix_per_mm / 100; + + float sym_factor = 1.0; /// 1.50; // gives nicer effect + + // Create a color for drawing adjustments outside of HPGL renderer + char *tcolptr = rules->razRule->colRef.LCRF; + S52color *c = getColor(tcolptr + 1); // +1 skips "n" in HPGL SPn format + int w = 1; // arbitrary width + wxColour color(c->R, c->G, c->B); + + double meters_per_senc_unit = rzRules->obj->x_rate; // meters per senc-unit + double lod_2pixel_meters = + 2 / + vp_plib + .view_scale_ppm; // LOD set to 2 pixels, nominal mercator projected + double LOD = lod_2pixel_meters / meters_per_senc_unit; + + // Get the current display priority + // Default comes from the LUP, unless overridden + int priority_current = rzRules->LUP->DPRI - '0'; + if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI; + + if (rzRules->obj->m_n_lsindex) { + // Calculate the size of a work buffer + int max_points = 0; + if (rzRules->obj->m_n_edge_max_points > 0) + max_points = rzRules->obj->m_n_edge_max_points; + else { + line_segment_element *lsa = rzRules->obj->m_ls_list; + + while (lsa) { + if ((lsa->ls_type == TYPE_EE) || (lsa->ls_type == TYPE_EE_REV)) + max_points += lsa->pedge->nCount; + else + max_points += 2; + + lsa = lsa->next; + } + } + + float *ppt; + unsigned char *vbo_point = + (unsigned char *)rzRules->obj->m_chart_context + ->vertex_buffer; // chart->GetLineVertexBuffer(); + + // Allocate some storage for converted points + wxPoint2DDouble *ptp = (wxPoint2DDouble *)malloc((max_points) * sizeof(wxPoint2DDouble)); + double *pdp = (double *)malloc(2 * (max_points) * sizeof(double)); + int *mask = (int *)malloc((max_points) * sizeof(int)); + + line_segment_element *ls = rzRules->obj->m_ls_list; + + unsigned int index = 0; + unsigned int idouble = 0; + int nls = 0; + wxPoint2DDouble lp; + + int ndraw = 0; + while (ls) { + if (ls->priority == priority_current) { + // transcribe the segment in the proper order into the output buffer + int nPoints; + int idir = 1; + bool bcon = false; + // fetch the first point + if ((ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV)) { + ppt = (float *)(vbo_point + ls->pedge->vbo_offset); + nPoints = ls->pedge->nCount; + if (ls->ls_type == TYPE_EE_REV) + idir = -1; + + } else { + ppt = (float *)(vbo_point + ls->pcs->vbo_offset); + nPoints = 2; + bcon = true; + } + + int vbo_index = 0; + int vbo_inc = 2; + if ((idir == -1) && !bcon) { + vbo_index = (nPoints - 1) * 2; + vbo_inc = -2; + } + + double offset = 0; + for (int ip = 0; ip < nPoints; ip++) { + wxPoint2DDouble r; + GetDoublePointPixSingle(rzRules, ppt[vbo_index + 1], ppt[vbo_index], &r); + if ((r.m_x != lp.m_x) || (r.m_y != lp.m_y)) { + mask[index] = (ls->priority == priority_current) ? 1 : 0; + ptp[index++] = r; + pdp[idouble++] = ppt[vbo_index]; + pdp[idouble++] = ppt[vbo_index + 1]; + + nls++; + } else { // sKipping point + } + + lp = r; + vbo_index += vbo_inc; + } + + } // priority + + // inspect the next segment to see if it can be connected, or if the chain + // breaks + int idir = 1; + bool bcon = false; + if (ls->next) { + int nPoints_next; + line_segment_element *lsn = ls->next; + // fetch the first point + if ((lsn->ls_type == TYPE_EE) || (lsn->ls_type == TYPE_EE_REV)) { + ppt = (float *)(vbo_point + lsn->pedge->vbo_offset); + nPoints_next = lsn->pedge->nCount; + if (lsn->ls_type == TYPE_EE_REV) idir = -1; + + } else { + ppt = (float *)(vbo_point + lsn->pcs->vbo_offset); + nPoints_next = 2; + bcon = true; + } + + wxPoint2DDouble ptest; + if (bcon) + GetDoublePointPixSingle(rzRules, ppt[1], ppt[0], &ptest); + + else { + if (idir == 1) + GetDoublePointPixSingle(rzRules, ppt[1], ppt[0], &ptest); + + else { + // fetch the last point + int index_last_next = (nPoints_next - 1) * 2; + GetDoublePointPixSingle(rzRules, ppt[index_last_next + 1], + ppt[index_last_next], &ptest); + } + } + + // try to match the correct point in this segment with the last point in + // the previous segment + + if (lp != ptest) // not connectable? + { + if (nls) { + wxPoint2DDouble *pReduced = 0; + int *pMaskOut = 0; + int nPointReduced = + reduceLOD(LOD, nls, pdp, &pReduced, mask, &pMaskOut); + + wxPoint2DDouble *ptestp = (wxPoint2DDouble *)malloc((max_points) * sizeof(wxPoint2DDouble)); + GetDoublePointPixArray(rzRules, pReduced, ptestp, nPointReduced); + free(pReduced); + + draw_lc_poly_texture(m_pdc, color, w, ptestp, pMaskOut, + nPointReduced, sym_len, sym_height, sym_factor, + rules->razRule, cww); + free(ptestp); + free(pMaskOut); + + ndraw++; + } + + nls = 0; + index = 0; + idouble = 0; + lp = wxPoint(0, 0); + } + + } else { + // no more segments, so render what is available + if (nls) { + wxPoint2DDouble *pReduced = 0; + int *pMaskOut = 0; + int nPointReduced = + reduceLOD(LOD, nls, pdp, &pReduced, mask, &pMaskOut); + + wxPoint2DDouble *ptestp = (wxPoint2DDouble *)malloc((max_points) * sizeof(wxPoint2DDouble)); + GetDoublePointPixArray(rzRules, pReduced, ptestp, nPointReduced); + free(pReduced); + + draw_lc_poly_texture(m_pdc, color, w, ptestp, pMaskOut, nPointReduced, + sym_len, sym_height, sym_factor, rules->razRule, cww); + free(ptestp); + free(pMaskOut); + } + } + + ls = ls->next; + } + + free(ptp); + free(pdp); + free(mask); + } + + return 1; +} + +int s52plib::RenderLC(ObjRazRules *rzRules, Rules *rules) { + //if(rzRules->obj->Index != 5347) + //return 0; + //if(strncmp(rzRules->obj->FeatureName, "FAIRWY", 6)) + // return 0; + +#if !defined(__WXMAC__) + //return RenderLCTexture(rzRules, rules); +#endif + + // catch cm93 and legacy PlugIns (e.g.s63_pi) + if (rzRules->obj->m_n_lsindex && !rzRules->obj->m_ls_list) + return RenderLCLegacy(rzRules, rules); + + // It appears that, at least for NOAA ENCs, the assembled feature + // winding direction is always returned as ANTI-CLOCKWISE + // So, proviswionally assume no need to check + // See also fixed winding declaration in draw_lc_poly() + //int winding = ComputeWinding(rzRules, rules); + + wxPoint r; + + int isym_len = rules->razRule->pos.line.bnbox_w.SYHL + + (rules->razRule->pos.line.bnbox_x.LBXC - + rules->razRule->pos.line.pivot_x.LICL); + float sym_len = isym_len * canvas_pix_per_mm / 100; + float sym_factor = 1.0; /// 1.50; // gives nicer effect + + // Create a color for drawing adjustments outside of HPGL renderer + char *tcolptr = rules->razRule->colRef.LCRF; + S52color *c = getColor(tcolptr + 1); // +1 skips "n" in HPGL SPn format + int w = 1; // arbitrary width + wxColour color(c->R, c->G, c->B); + + double meters_per_senc_unit = rzRules->obj->x_rate; // meters per senc-unit + double lod_2pixel_meters = + 2 / + vp_plib + .view_scale_ppm; // LOD set to 2 pixels, nominal mercator projected + double LOD = lod_2pixel_meters / meters_per_senc_unit; + + // Get the current display priority + // Default comes from the LUP, unless overridden + int priority_current = rzRules->LUP->DPRI - '0'; + if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI; + + if (rzRules->obj->m_n_lsindex) { + // Calculate the size of a work buffer + int max_points = 0; + if (rzRules->obj->m_n_edge_max_points > 0) + max_points = rzRules->obj->m_n_edge_max_points; + else { + line_segment_element *lsa = rzRules->obj->m_ls_list; + + while (lsa) { + if ((lsa->ls_type == TYPE_EE) || (lsa->ls_type == TYPE_EE_REV)) + max_points += lsa->pedge->nCount; + else + max_points += 2; + + lsa = lsa->next; + } + } + + float *ppt; + unsigned char *vbo_point = + (unsigned char *)rzRules->obj->m_chart_context + ->vertex_buffer; // chart->GetLineVertexBuffer(); + + // Allocate some storage for converted points + wxPoint *ptp = (wxPoint *)malloc((max_points) * sizeof(wxPoint)); + double *pdp = (double *)malloc(2 * (max_points) * sizeof(double)); + int *mask = (int *)malloc((max_points) * sizeof(int)); + + line_segment_element *ls = rzRules->obj->m_ls_list; + + unsigned int index = 0; + unsigned int idouble = 0; + int nls = 0; + wxPoint lp; + + int ndraw = 0; + while (ls) { + if (ls->priority == priority_current) { + // transcribe the segment in the proper order into the output buffer + int nPoints; + int idir = 1; + bool bcon = false; + // fetch the first point + if ((ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV)) { + ppt = (float *)(vbo_point + ls->pedge->vbo_offset); + nPoints = ls->pedge->nCount; + if (ls->ls_type == TYPE_EE_REV) idir = -1; + + } else { + ppt = (float *)(vbo_point + ls->pcs->vbo_offset); + nPoints = 2; + bcon = true; + } + + int vbo_index = 0; + int vbo_inc = 2; + if ((idir == -1) && !bcon) { + vbo_index = (nPoints - 1) * 2; + vbo_inc = -2; + } + + double offset = 0; + for (int ip = 0; ip < nPoints; ip++) { + wxPoint r; + GetPointPixSingle(rzRules, ppt[vbo_index + 1], ppt[vbo_index], &r); + if ((r.x != lp.x) || (r.y != lp.y)) { + mask[index] = (ls->priority == priority_current) ? 1 : 0; + ptp[index++] = r; + pdp[idouble++] = ppt[vbo_index]; + pdp[idouble++] = ppt[vbo_index + 1]; + + nls++; + } else { // sKipping point + } + + lp = r; + vbo_index += vbo_inc; + } + + } // priority + + // inspect the next segment to see if it can be connected, or if the chain + // breaks + int idir = 1; + bool bcon = false; + if (ls->next) { + int nPoints_next; + line_segment_element *lsn = ls->next; + // fetch the first point + if ((lsn->ls_type == TYPE_EE) || (lsn->ls_type == TYPE_EE_REV)) { + ppt = (float *)(vbo_point + lsn->pedge->vbo_offset); + nPoints_next = lsn->pedge->nCount; + if (lsn->ls_type == TYPE_EE_REV) idir = -1; + + } else { + ppt = (float *)(vbo_point + lsn->pcs->vbo_offset); + nPoints_next = 2; + bcon = true; + } + + wxPoint ptest; + if (bcon) + GetPointPixSingle(rzRules, ppt[1], ppt[0], &ptest); + + else { + if (idir == 1) + GetPointPixSingle(rzRules, ppt[1], ppt[0], &ptest); + + else { + // fetch the last point + int index_last_next = (nPoints_next - 1) * 2; + GetPointPixSingle(rzRules, ppt[index_last_next + 1], + ppt[index_last_next], &ptest); + } + } + + // try to match the correct point in this segment with the last point in + // the previous segment + + if (lp != ptest) // not connectable? + { + if (nls) { + wxPoint2DDouble *pReduced = 0; + int *pMaskOut = 0; + int nPointReduced = + reduceLOD(LOD, nls, pdp, &pReduced, mask, &pMaskOut); + + wxPoint *ptestp = (wxPoint *)malloc((max_points) * sizeof(wxPoint)); + GetPointPixArray(rzRules, pReduced, ptestp, nPointReduced); + free(pReduced); + + draw_lc_poly(m_pdc, color, w, ptestp, pMaskOut, nPointReduced, + sym_len, 0, sym_factor, rules->razRule); + free(ptestp); + free(pMaskOut); + + ndraw++; + } + + nls = 0; + index = 0; + idouble = 0; + lp = wxPoint(0, 0); + } + + } else { + // no more segments, so render what is available + if (nls) { + wxPoint2DDouble *pReduced = 0; + int *pMaskOut = 0; + int nPointReduced = + reduceLOD(LOD, nls, pdp, &pReduced, mask, &pMaskOut); + + wxPoint *ptestp = (wxPoint *)malloc((max_points) * sizeof(wxPoint)); + GetPointPixArray(rzRules, pReduced, ptestp, nPointReduced); + free(pReduced); + + draw_lc_poly(m_pdc, color, w, ptestp, pMaskOut, nPointReduced, + sym_len, 0, sym_factor, rules->razRule); + free(ptestp); + free(pMaskOut); + } + } + + ls = ls->next; + } + + free(ptp); + free(pdp); + free(mask); + } + + return 1; +} + +int s52plib::ComputeWinding(ObjRazRules *rzRules, Rules *rules) { + if (rzRules->obj->Primitive_type != GEO_AREA) + return -1; + + // catch cm93 and legacy PlugIns (e.g.s63_pi) + if (rzRules->obj->m_n_lsindex && !rzRules->obj->m_ls_list) + return -1; + + std::vector points; + + if (rzRules->obj->m_n_lsindex) { + + float *ppt; + unsigned char *vbo_point = + (unsigned char *)rzRules->obj->m_chart_context + ->vertex_buffer; // chart->GetLineVertexBuffer(); + + line_segment_element *ls = rzRules->obj->m_ls_list; + + unsigned int index = 0; + unsigned int idouble = 0; + int nls = 0; + wxPoint2DDouble lp; + + int ndraw = 0; + while (ls) { + // transcribe the segment in the proper order into the output buffer + int nPoints; + int idir = 1; + bool bcon = false; + // fetch the first point + if ((ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV)) { + ppt = (float *)(vbo_point + ls->pedge->vbo_offset); + nPoints = ls->pedge->nCount; + if (ls->ls_type == TYPE_EE_REV) idir = -1; + } else { + ppt = (float *)(vbo_point + ls->pcs->vbo_offset); + nPoints = 2; + bcon = true; + } + + int vbo_index = 0; + int vbo_inc = 2; + if ((idir == -1) && !bcon) { + vbo_index = (nPoints - 1) * 2; + vbo_inc = -2; + } + + double offset = 0; + for (int ip = 0; ip < nPoints; ip++) { + wxPoint2DDouble r; + r.m_x = ppt[vbo_index + 1]; + r.m_y = ppt[vbo_index]; + nls++; + points.push_back(r); + + lp = r; + vbo_index += vbo_inc; + } + + /* + // inspect the next segment to see if it can be connected, or if the chain + // breaks + int idira = 1; + bool bcona = false; + if (ls->next) { + int nPoints_next; + line_segment_element *lsn = ls->next; + // fetch the first point + if ((lsn->ls_type == TYPE_EE) || (lsn->ls_type == TYPE_EE_REV)) { + ppt = (float *)(vbo_point + lsn->pedge->vbo_offset); + nPoints_next = lsn->pedge->nCount; + if (lsn->ls_type == TYPE_EE_REV) idir = -1; + + } else { + ppt = (float *)(vbo_point + lsn->pcs->vbo_offset); + nPoints_next = 2; + bcona = true; + } + + wxPoint2DDouble ptest; + if (bcona) { + ptest.m_x = ppt[1]; + ptest.m_y = ppt[0]; + } else { + if (idira == 1) { + ptest.m_x = ppt[1]; + ptest.m_y = ppt[0]; + } else { + // fetch the last point + int index_last_next = (nPoints_next - 1) * 2; + ptest.m_x = ppt[index_last_next + 1]; + ptest.m_y = ppt[index_last_next]; + } + } + + } +*/ + ls = ls->next; + } + } else return -1; + + + if (rzRules->obj->Primitive_type == GEO_AREA) { + // Is the line feature closed? + wxPoint2DDouble start = points[0]; + wxPoint2DDouble end = points[sizeof(points) - 1]; + double dx = start.m_x - end.m_x; + double dy = start.m_y - end.m_y; + double delta = sqrt(dx * dx + dy * dy); + if (delta > 1) + points.push_back(start); // close it. + + //calculate winding direction + double dfSum = 0.0; + for (size_t i=0; i < points.size()-1; i++) { + wxPoint2DDouble p1 = points[i]; + wxPoint2DDouble p2 = points[i+1]; + dfSum += (p1.m_x * p2.m_y) - (p1.m_y * p2.m_x); + } + bool cw = dfSum < 0.; + + int rv = 1; + if (cw) rv = 2; + + return rv; + } + return -1; +} + +int s52plib::reduceLOD(double LOD_meters, int nPoints, double *source, + wxPoint2DDouble **dest, int *maskIn, int **maskOut) { + // Reduce the LOD of this linestring + std::vector index_keep; + if (nPoints > 5 && (LOD_meters > .01)) { + index_keep.push_back(0); + index_keep.push_back(nPoints - 1); + index_keep.push_back(nPoints - 2); + + DouglasPeucker(source, 0, nPoints - 2, LOD_meters, &index_keep); + + } else { + index_keep.resize(nPoints); + // Consider using std::iota here when there is C++11 support. + for (int i = 0; i < nPoints; i++) index_keep[i] = i; + } + + wxPoint2DDouble *pReduced = + (wxPoint2DDouble *)malloc((index_keep.size()) * sizeof(wxPoint2DDouble)); + *dest = pReduced; + + int *pmaskOut = NULL; + if (maskIn) { + *maskOut = (int *)malloc((index_keep.size()) * sizeof(int)); + pmaskOut = *maskOut; + } + + double *ppr = source; + int ir = 0; + for (int ip = 0; ip < nPoints; ip++) { + double x = *ppr++; + double y = *ppr++; + int maskval = 1; + if (maskIn) maskval = maskIn[ip]; + // printf("LOD: %10g %10g\n", x, y); + + for (unsigned int j = 0; j < index_keep.size(); j++) { + if (index_keep[j] == ip) { + if (pmaskOut) pmaskOut[ir] = maskval; + pReduced[ir++] = wxPoint2DDouble(x, y); + break; + } + } + } + + return index_keep.size(); +} + +// Line Complex +int s52plib::RenderLCLegacy(ObjRazRules *rzRules, Rules *rules) { + if (!rzRules->obj->m_chart_context->chart) + return RenderLCPlugIn(rzRules, rules); + + // Must be cm93 + + wxPoint r; + + int isym_len = rules->razRule->pos.line.bnbox_w.SYHL; + float sym_len = isym_len * canvas_pix_per_mm / 100; + float sym_factor = 1.0; /// 1.50; // gives nicer + /// effect + + // Create a color for drawing adjustments outside of HPGL renderer + char *tcolptr = rules->razRule->colRef.LCRF; + S52color *c = getColor(tcolptr + 1); // +1 skips "n" in HPGL SPn format + int w = 1; // arbitrary width + wxColour color(c->R, c->G, c->B); + // Get the current display priority + // Default comes from the LUP, unless overridden + int priority_current = rzRules->LUP->DPRI - '0'; + if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI; + + if (rzRules->obj->m_n_lsindex) { + auto ve_hash = rzRules->obj->m_chart_context->m_pve_hash; + auto vc_hash = rzRules->obj->m_chart_context->m_pvc_hash; + + unsigned int nls_max; + if (rzRules->obj->m_n_edge_max_points > + 0) // size has been precalculated on SENC load + nls_max = rzRules->obj->m_n_edge_max_points; + else { + // Calculate max malloc size required + nls_max = 0; + int *index_run_x = rzRules->obj->m_lsindex_array; + for (int imseg = 0; imseg < rzRules->obj->m_n_lsindex; imseg++) { + index_run_x++; // Skip cNode + // Get the edge + unsigned int enode = *index_run_x; + if (enode) { + VE_Element *pedge = (*ve_hash)[enode]; + if (pedge) { + if (pedge->nCount > nls_max) nls_max = pedge->nCount; + } + } + index_run_x += 2; + } + rzRules->obj->m_n_edge_max_points = + nls_max; // Got it, cache for next time + } + + // Allocate some storage for converted points + wxPoint *ptp = (wxPoint *)malloc( + (nls_max + 2) * sizeof(wxPoint)); // + 2 allows for end nodes + + int *index_run; + float *ppt; + double easting, northing; + wxPoint pra(0, 0); + VC_Element *pnode; + + for (int iseg = 0; iseg < rzRules->obj->m_n_lsindex; iseg++) { + int seg_index = iseg * 3; + index_run = &rzRules->obj->m_lsindex_array[seg_index]; + + // Get first connected node + unsigned int inode = *index_run++; + if (inode) { + pnode = (*vc_hash)[inode]; + if (pnode) { + ppt = pnode->pPoint; + easting = *ppt++; + northing = *ppt; + GetPointPixSingle(rzRules, (float)northing, (float)easting, &pra); + } + ptp[0] = pra; // insert beginning node + } + + // Get the edge + unsigned int enode = *index_run++; + VE_Element *pedge = 0; + if (enode) pedge = (*ve_hash)[enode]; + + int nls = 0; + if (pedge) { + // Here we decide to draw or not based on the highest priority seen for + // this segment That is, if this segment is going to be drawn at a + // higher priority later, then don't draw it here. + + // This logic is not perfectly right for one case: + // If the segment has only two end connected nodes, and no intermediate + // edge, then we have no good way to evaluate the priority. This is due + // to the fact that priority is only precalculated for edge segments, + // not connector nodes. Only thing to do is take the conservative + // approach and draw the segment, in this case. + if (pedge->nCount) { + if (pedge->max_priority != priority_current) continue; + } + + if (pedge->max_priority != priority_current) continue; + + nls = pedge->nCount; + + ppt = pedge->pPoints; + for (int ip = 0; ip < nls; ip++) { + easting = *ppt++; + northing = *ppt++; + GetPointPixSingle(rzRules, (float)northing, (float)easting, + &ptp[ip + 1]); + } + } + + // Get last connected node + unsigned int jnode = *index_run++; + if (jnode) { + pnode = (*vc_hash)[jnode]; + if (pnode) { + ppt = pnode->pPoint; + easting = *ppt++; + northing = *ppt; + GetPointPixSingle(rzRules, (float)northing, (float)easting, &pra); + } + ptp[nls + 1] = pra; // insert ending node + } + + if ((inode) && (jnode)) { + draw_lc_poly(m_pdc, color, w, ptp, NULL, nls + 2, sym_len, 0, + sym_factor, rules->razRule); + } else if (nls) { + draw_lc_poly(m_pdc, color, w, &ptp[1], NULL, nls, sym_len, 0, + sym_factor, rules->razRule); + } + } + free(ptp); + } + + else if (rzRules->obj->pPolyTessGeo) { + if (!rzRules->obj->pPolyTessGeo->IsOk()) { // perform deferred tesselation + rzRules->obj->pPolyTessGeo->BuildDeferredTess(); + } + + PolyTriGroup *pptg = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head(); + float *ppolygeo = pptg->pgroup_geom; + if (ppolygeo) { + int ctr_offset = 0; + for (int ic = 0; ic < pptg->nContours; ic++) { + int npt = pptg->pn_vertex[ic]; + wxPoint *ptp = (wxPoint *)malloc((npt + 1) * sizeof(wxPoint)); + wxPoint *pr = ptp; + for (int ip = 0; ip < npt; ip++) { + float plon = ppolygeo[(2 * ip) + ctr_offset]; + float plat = ppolygeo[(2 * ip) + ctr_offset + 1]; + + GetPointPixSingle(rzRules, plat, plon, pr); + pr++; + } + float plon = ppolygeo[ctr_offset]; // close the polyline + float plat = ppolygeo[ctr_offset + 1]; + GetPointPixSingle(rzRules, plat, plon, pr); + + draw_lc_poly(m_pdc, color, w, ptp, NULL, npt + 1, sym_len, 0, + sym_factor, rules->razRule); + + free(ptp); + + ctr_offset += npt * 2; + } + } + } + return 1; +} + +int s52plib::RenderLCPlugIn(ObjRazRules *rzRules, Rules *rules) { + wxPoint r; + + int isym_len = rules->razRule->pos.line.bnbox_w.SYHL; + float sym_len = isym_len * canvas_pix_per_mm / 100; + float sym_factor = 1.0; /// 1.50; // gives nicer + /// effect + + // Create a color for drawing adjustments outside of HPGL renderer + char *tcolptr = rules->razRule->colRef.LCRF; + S52color *c = getColor(tcolptr + 1); // +1 skips "n" in HPGL SPn format + int w = 1; // arbitrary width + wxColour color(c->R, c->G, c->B); + double LOD = 2.0 / vp_plib.view_scale_ppm; // empirical value, by experiment + LOD = 0; // wxMin(LOD, 10.0); + + // Get the current display priority + // Default comes from the LUP, unless overridden + int priority_current = rzRules->LUP->DPRI - '0'; + if (rzRules->obj->m_DPRI >= 0) priority_current = rzRules->obj->m_DPRI; + + // Calculate max malloc size required + + int max_points = 0; + if (rzRules->obj->m_ls_list_legacy) { + VE_Element *pedge; + PI_line_segment_element *ls = rzRules->obj->m_ls_list_legacy; + + while (ls) { + int nPoints; + // fetch the first point + if (ls->type == TYPE_EE) { + pedge = (VE_Element *)ls->private0; + nPoints = pedge->nCount; + } else { + nPoints = 2; + } + + max_points += nPoints; + + ls = ls->next; + } + } + + if (rzRules->obj->m_ls_list_legacy) { + float *ppt; + + VE_Element *pedge; + + // Allocate some storage for converted points + wxPoint *ptp = (wxPoint *)malloc( + (max_points + 2) * sizeof(wxPoint)); // + 2 allows for end nodes + double *pdp = (double *)malloc(2 * (max_points + 2) * sizeof(double)); + + PI_connector_segment *pcs; + + unsigned char *vbo_point = + (unsigned char *)rzRules->obj->m_chart_context->vertex_buffer; + PI_line_segment_element *ls = rzRules->obj->m_ls_list_legacy; + + unsigned int index = 0; + unsigned int idouble = 0; + int nls = 0; + wxPoint lp; + + ls = rzRules->obj->m_ls_list_legacy; + while (ls) { + if (ls->priority == priority_current) { + // transcribe the segment in the proper order into the output buffer + int nPoints; + int idir = 1; + bool bcon = false; + // fetch the first point + if ((ls->type == TYPE_EE) || (ls->type == TYPE_EE_REV)) { + pedge = (VE_Element *)ls->private0; + ppt = (float *)(vbo_point + pedge->vbo_offset); + nPoints = pedge->nCount; + if (ls->type == TYPE_EE_REV) idir = -1; + + } else { + pcs = (PI_connector_segment *)ls->private0; + ppt = (float *)(vbo_point + pcs->vbo_offset); + nPoints = 2; + bcon = true; + } + + int vbo_index = 0; + int vbo_inc = 2; + if ((idir == -1) && !bcon) { + vbo_index = (nPoints - 1) * 2; + vbo_inc = -2; + } + for (int ip = 0; ip < nPoints; ip++) { + wxPoint r; + GetPointPixSingle(rzRules, ppt[vbo_index + 1], ppt[vbo_index], &r); + + if (1 /*(r.x != lp.x) || (r.y != lp.y)*/) { + ptp[index++] = r; + pdp[idouble++] = ppt[vbo_index]; + pdp[idouble++] = ppt[vbo_index + 1]; + + nls++; + } else { // sKipping point + } + + lp = r; + vbo_index += vbo_inc; + } + + } // priority + + // inspect the next segment to see if it can be connected, or if the chain + // breaks + int idir = 1; + bool bcon = false; + if (ls->next) { + int nPoints_next; + PI_line_segment_element *lsn = ls->next; + // fetch the first point + if ((lsn->type == TYPE_EE) || (lsn->type == TYPE_EE_REV)) { + pedge = (VE_Element *)lsn->private0; + ppt = (float *)(vbo_point + pedge->vbo_offset); + nPoints_next = pedge->nCount; + if (lsn->type == TYPE_EE_REV) idir = -1; + + } else { + pcs = (PI_connector_segment *)lsn->private0; + ppt = (float *)(vbo_point + pcs->vbo_offset); + nPoints_next = 2; + bcon = true; + } + + wxPoint ptest; + if (bcon) + GetPointPixSingle(rzRules, ppt[1], ppt[0], &ptest); + + else { + if (idir == 1) + GetPointPixSingle(rzRules, ppt[1], ppt[0], &ptest); + + else { + // fetch the last point + int index_last_next = (nPoints_next - 1) * 2; + GetPointPixSingle(rzRules, ppt[index_last_next + 1], + ppt[index_last_next], &ptest); + } + } + + // try to match the correct point in this segment with the last point in + // the previous segment + + if (lp != ptest) // not connectable? + { + if (nls) { + wxPoint2DDouble *pReduced = 0; + int nPointReduced = reduceLOD(LOD, nls, pdp, &pReduced, NULL, NULL); + + wxPoint *ptestp = + (wxPoint *)malloc((2 * (nPointReduced + 2)) * sizeof(wxPoint)); + GetPointPixArray(rzRules, pReduced, ptestp, nPointReduced); + free(pReduced); + + draw_lc_poly(m_pdc, color, w, ptestp, NULL, nPointReduced, sym_len, + 0, sym_factor, rules->razRule); + free(ptestp); + } + + nls = 0; + index = 0; + idouble = 0; + lp = wxPoint(0, 0); + } + + } else { + // no more segments, so render what is available + if (nls) { + wxPoint2DDouble *pReduced = 0; + int nPointReduced = reduceLOD(LOD, nls, pdp, &pReduced, NULL, NULL); + + wxPoint *ptestp = + (wxPoint *)malloc((2 * (max_points + 2)) * sizeof(wxPoint)); + GetPointPixArray(rzRules, pReduced, ptestp, nPointReduced); + free(pReduced); + + draw_lc_poly(m_pdc, color, w, ptestp, NULL, nPointReduced, sym_len, 0, + sym_factor, rules->razRule); + free(ptestp); + } + } + + ls = ls->next; + } + + free(ptp); + free(pdp); + } + return 1; +} + +int s52plib::BuildLCSymbolTexture(char *str, char *col, wxPoint &r, + wxPoint &pivot, wxPoint origin, float scale, + float sym_len, float sym_height) { + GLint texture = 0; + + double xscale = 1.0; + + int width = sym_len; + width *= 2 * xscale; // Grow the drawing bitmap to allow for rotation + // of symbols with highly offset pivot points + // width = (int)(width / fsf); + + int height = sym_height; + height *= 2 * xscale; + // height = (int)(height / fsf); + + // width /= fsf; + // height /= fsf; + + // Expand width and height to next larger POT + int xp = width, yp = height; + int width_pot, height_pot; + if (((xp != 0) && !(xp & (xp - 1)))) // detect POT + width_pot = xp; + else { + int a = 0; + while (xp) { + xp = xp >> 1; + a++; + } + width_pot = 1 << a; + } + + if (((yp != 0) && !(yp & (yp - 1)))) // detect POT + height_pot = yp; + else { + int a = 0; + while (yp) { + yp = yp >> 1; + a++; + } + height_pot = 1 << a; + } + + // Render the symbol on wxMemoryDC + HPGL->SetVP(&vp_plib); + wxMemoryDC mdc; + +#if (defined(__WXMSW__) || defined(__WXMAC__) || defined(ANDROID)) + wxBitmap bmp(width, height, 24); + mdc.SelectObject(bmp); + mdc.SetBackground(wxBrush(m_unused_wxColor)); + mdc.Clear(); +#else + wxBitmap bmp(width, height, 32); + mdc.SelectObject(bmp); +#endif + + HPGL->SetTargetDC(&mdc); + wxPoint2DDouble rt(0, 0); + HPGL->Render(str, col, rt, origin, wxPoint(0, 0), 1.0, 0, false); + mdc.SelectObject(wxNullBitmap); + + wxImage image = bmp.ConvertToImage(); + int ws = image.GetWidth(), hs = image.GetHeight(); + + unsigned char *source = image.GetData(); + unsigned char *alpha = image.GetAlpha(); + + // Correct for Android bug + // 24 bit image should have no alpha channel data +#ifdef ANDROID + alpha = nullptr; +#endif + + unsigned char mr, mg, mb; + if (!alpha) { + image.SetMaskColour(m_unused_wxColor.Red(), m_unused_wxColor.Green(), + m_unused_wxColor.Blue()); + if (!alpha && !image.GetOrFindMaskColour(&mr, &mg, &mb)) + printf("trying to use mask to draw a bitmap without alpha or mask\n"); + } + + unsigned char *pRGBA = (unsigned char *)calloc(1, 4 * width_pot * height_pot); + unsigned char *dest = pRGBA; + + if (source) { + for (int y = 0; y < hs; y++) { + for (int x = 0; x < ws; x++) { + int offs = (y * ws + x); + int offd = (y * width_pot + x); + + unsigned int r = source[offs * 3 + 0]; + unsigned int g = source[offs * 3 + 1]; + unsigned int b = source[offs * 3 + 2]; + + dest[offd * 4 + 0] = source[offs * 3 + 0]; + dest[offd * 4 + 1] = source[offs * 3 + 1]; + dest[offd * 4 + 2] = source[offs * 3 + 2]; + unsigned char a = alpha + ? alpha[offs] + : ((r == mr) && (g == mg) && (b == mb) ? 0 : 255); + dest[offd * 4 + 3] = a; + } + } + } + + glEnable(GL_TEXTURE_2D); + GLuint texobj; + glGenTextures(1, &texobj); + glBindTexture(GL_TEXTURE_2D, texobj); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + GL_NEAREST /*GL_LINEAR*/); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_pot, height_pot, 0, GL_RGBA, + GL_UNSIGNED_BYTE, pRGBA); + + free(pRGBA); + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + texture = texobj; + + return texture; +} + +// Render Line Complex Polyline +void s52plib::RenderTex(char *str, char *col, wxPoint &r, wxPoint &pivot, + wxPoint origin, float scale, double rot_angle, + float sym_len, float sym_height, float seg_len) { +#ifdef ocpnUSE_GL + int symbol_texture = 0; + // Build the hash key + std::string key; + key += str; // HPGL Render string + key += col; // color + + // Check to see it the requested symbol texture is in the cache + auto search = lc_vector_symbol_cache.find(key); + if (search != lc_vector_symbol_cache.end()) + symbol_texture = search->second; + else { + symbol_texture = BuildLCSymbolTexture(str, col, r, pivot, origin, scale, + sym_len, sym_height); + lc_vector_symbol_cache[key] = symbol_texture; + } + + // Render the texture + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, symbol_texture); + + int w; + int h; + + // Get the texture dimensions + // TODO Could save these dimensions as a rule parm, faster. + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); + + float uv[8]; + float coords[8]; + + float xa = wxMin(sym_len, seg_len); + + // Note swizzle of points to allow TRIANGLE_STRIP drawing + // normal uv + uv[0] = 0; + uv[1] = 0; + uv[2] = xa / w; + uv[3] = 0; + uv[6] = xa / w; + uv[7] = sym_height / h; + uv[4] = 0; + uv[5] = sym_height / h; + + // pixels + coords[0] = 0; + coords[1] = 0; + coords[2] = xa; + coords[3] = 0; + coords[6] = xa; + coords[7] = sym_height; + coords[4] = 0; + coords[5] = sym_height; + + if (pCtexture_2D_shader_program[0]) { + pCtexture_2D_shader_program[0]->Bind(); + glEnable(GL_BLEND); + + // Select the active texture unit. + glActiveTexture(GL_TEXTURE0); + + pCtexture_2D_shader_program[0]->SetUniform1i("uTex", 0); + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + pCtexture_2D_shader_program[0]->SetAttributePointerf("position", coords); + pCtexture_2D_shader_program[0]->SetAttributePointerf("aUV", uv); + + // Rotate + mat4x4 I, Q; + mat4x4_identity(I); + + double yadj = (pivot.y - origin.y) * canvas_pix_per_mm / 100; + double xadj = (pivot.x - origin.x) * canvas_pix_per_mm / 100; + + mat4x4_translate_in_place(I, r.x, r.y, 0); + // mat4x4_translate_in_place(I, xadj, yadj, 0); + // mat4x4_translate_in_place(I, -xadj, -yadj, 0); + mat4x4_rotate_Z(Q, I, rot_angle * PI / 180.); + mat4x4_translate_in_place(Q, -xadj, -yadj, 0); + + pCtexture_2D_shader_program[0]->SetUniformMatrix4fv("TransformMatrix", + (GLfloat *)Q); + + // Perform the actual drawing. + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // Restore the per-object transform to Identity Matrix + mat4x4 IM; + mat4x4_identity(IM); + pCtexture_2D_shader_program[0]->SetUniformMatrix4fv("TransformMatrix", + (GLfloat *)IM); + + // Clean up the GL state + pCtexture_2D_shader_program[0]->UnBind(); + } +#endif +} + +// Render Line Complex Polyline + +void s52plib::draw_lc_poly(wxDC *pdc, wxColor &color, int width, wxPoint *ptp, + int *mask, int npt, float sym_len, float sym_height, + float sym_factor, Rule *draw_rule) { + if (npt < 2) return; + + wxPoint2DDouble r; + + bool cw = false; // Winding is always CCW + + // Get a true pixel clipping/bounding box from the vp + wxPoint pbb = GetPixFromLL(vp_plib.clat, vp_plib.clon); + int xmin_ = pbb.x - vp_plib.rv_rect.width / 2; + int xmax_ = xmin_ + vp_plib.rv_rect.width; + int ymin_ = pbb.y - vp_plib.rv_rect.height / 2; + int ymax_ = ymin_ + vp_plib.rv_rect.height; + + int x0, y0, x1, y1; + + if (pdc) { + wxPen *pthispen = + wxThePenList->FindOrCreatePen(color, width, wxPENSTYLE_SOLID); + m_pdc->SetPen(*pthispen); + + int start_seg = 0; + int end_seg = npt - 1; + int inc = 1; + + if (cw) { + start_seg = npt - 1; + end_seg = 0; + inc = -1; + } + + float dx, dy, seg_len, theta; + + bool done = false; + ClipResult res; + int iseg = start_seg; + while (!done) { + // Do not bother with segments that are invisible + + x0 = ptp[iseg].x; + y0 = ptp[iseg].y; + x1 = ptp[iseg + inc].x; + y1 = ptp[iseg + inc].y; + + // Also, segments marked (by mask) as invisible + if (mask && !mask[iseg]) goto next_seg_dc; + + res = cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, xmin_, xmax_, + ymin_, ymax_); + + if (res == Invisible) goto next_seg_dc; + + dx = ptp[iseg + inc].x - ptp[iseg].x; + dy = ptp[iseg + inc].y - ptp[iseg].y; + seg_len = sqrt(dx * dx + dy * dy); + theta = atan2f(dy, dx); + + if (seg_len >= 1.0) { + if (seg_len <= sym_len * sym_factor) { + int xst1 = ptp[iseg].x; + int yst1 = ptp[iseg].y; + float xst2, yst2; + if (seg_len >= sym_len) { + xst2 = xst1 + (sym_len * dx / seg_len); + yst2 = yst1 + (sym_len * dy / seg_len); + } else { + xst2 = ptp[iseg + inc].x; + yst2 = ptp[iseg + inc].y; + } + + pdc->DrawLine(xst1, yst1, (wxCoord)floor(xst2), (wxCoord)floor(yst2)); + } + + else { + float s = 0; + float xs = ptp[iseg].x; + float ys = ptp[iseg].y; + + while (s + (sym_len * sym_factor) < seg_len) { + r.m_x = (int)xs; + r.m_y = (int)ys; + char *str = draw_rule->vector.LVCT; + char *col = draw_rule->colRef.LCRF; + wxPoint pivot(draw_rule->pos.line.pivot_x.LICL, + draw_rule->pos.line.pivot_y.LIRW); + + HPGL->SetTargetDC(pdc); + HPGL->SetVP(&vp_plib); + HPGL->Render(str, col, r, pivot, pivot, 1.0, theta * 180. / PI, + false); + + xs += sym_len * dx / seg_len * sym_factor; + ys += sym_len * dy / seg_len * sym_factor; + s += sym_len * sym_factor; + } + + pdc->DrawLine((int)xs, (int)ys, ptp[iseg + inc].x, ptp[iseg + inc].y); + } + } + next_seg_dc: + iseg += inc; + if (iseg == end_seg) done = true; + + } // while + } // if pdc + else // opengl + { + // Set up the color +#ifdef ocpnUSE_GL + // Adjust line width up a bit, to improve render quality for + // GL_BLEND/GL_LINE_SMOOTH + float awidth = wxMax(m_GLMinCartographicLineWidth, (float)width * 0.7); + awidth = wxMax(awidth, 1.5); + glLineWidth(awidth); +#endif + + int start_seg = 0; + int end_seg = npt - 1; + int inc = 1; + + if (cw) { + start_seg = npt - 1; + end_seg = 0; + inc = -1; + } + + float dx, dy, seg_len, theta; + ClipResult res; + + bool done = false; + int iseg = start_seg; + while (!done) { + // Do not bother with segments that are invisible + + x0 = ptp[iseg].x; + y0 = ptp[iseg].y; + x1 = ptp[iseg + inc].x; + y1 = ptp[iseg + inc].y; + + // Also, segments marked (by mask) as invisible + if (mask && !mask[iseg]) goto next_seg; + + res = cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, xmin_, xmax_, + ymin_, ymax_); + + if (res == Invisible) goto next_seg; + + dx = ptp[iseg + inc].x - ptp[iseg].x; + dy = ptp[iseg + inc].y - ptp[iseg].y; + seg_len = sqrt(dx * dx + dy * dy); + + if (seg_len >= 1.0) { + if (seg_len <= sym_len * sym_factor) { + int xst1 = ptp[iseg].x; + int yst1 = ptp[iseg].y; + float xst2, yst2; + + if (seg_len >= sym_len) { + xst2 = xst1 + (sym_len * dx / seg_len); + yst2 = yst1 + (sym_len * dy / seg_len); + } else { + xst2 = ptp[iseg + inc].x; + yst2 = ptp[iseg + inc].y; + } + + // Enable anti-aliased lines, at best quality +#ifndef __OCPN__ANDROID__ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + if (m_GLLineSmoothing) { + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + } +#endif + +#ifdef ocpnUSE_GL + CGLShaderProgram *shader = + pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/]; + shader->Bind(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + float colorv[4]; + colorv[0] = color.Red() / float(256); + colorv[1] = color.Green() / float(256); + colorv[2] = color.Blue() / float(256); + colorv[3] = 1.0; // transparency; + + shader->SetUniform4fv("color", colorv); + + float pts[4]; + pts[0] = xst1; + pts[1] = yst1; + pts[2] = xst2; + pts[3] = yst2; + + GLint pos = shader->getAttributeLocation("position"); + glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), + pts); + glEnableVertexAttribArray(pos); + + glDrawArrays(GL_LINES, 0, 2); + + shader->UnBind(); + + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); +#endif + } else { + float s = 0; + float xs = ptp[iseg].x; + float ys = ptp[iseg].y; + + while (s + (sym_len * sym_factor) < seg_len) { + r.m_x = xs; + r.m_y = ys; + char *str = draw_rule->vector.LVCT; + char *col = draw_rule->colRef.LCRF; + wxPoint pivot(draw_rule->pos.line.pivot_x.LICL, + draw_rule->pos.line.pivot_y.LIRW); + + HPGL->SetTargetOpenGl(); + HPGL->SetVP(&vp_plib); + theta = atan2f(dy, dx); + HPGL->Render(str, col, r, pivot, pivot, 1.0, theta * 180. / PI, + false); + + xs += sym_len * dx / seg_len * sym_factor; + ys += sym_len * dy / seg_len * sym_factor; + s += sym_len * sym_factor; + } +#ifndef __OCPN__ANDROID__ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + if (m_GLLineSmoothing) { + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + } + +#endif + +#ifdef ocpnUSE_GL + CGLShaderProgram *shader = + pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/]; + shader->Bind(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + float colorv[4]; + colorv[0] = color.Red() / float(256); + colorv[1] = color.Green() / float(256); + colorv[2] = color.Blue() / float(256); + colorv[3] = 1.0; // transparency; + + shader->SetUniform4fv("color", colorv); + + float pts[4]; + pts[0] = xs; + pts[1] = ys; + pts[2] = ptp[iseg + inc].x; + pts[3] = ptp[iseg + inc].y; + + GLint pos = shader->getAttributeLocation("position"); + glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), + pts); + glEnableVertexAttribArray(pos); + + glDrawArrays(GL_LINES, 0, 2); + glDisableVertexAttribArray(pos); + shader->UnBind(); + + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); +#endif + } + } + next_seg: + iseg += inc; + if (iseg == end_seg) done = true; + } // while + + } // opengl +} + +void s52plib::draw_lc_poly_texture(wxDC *pdc, wxColor &color, int width, + wxPoint2DDouble *ptp, int *mask, int npt, + float sym_len, float sym_height, + float sym_factor, Rule *draw_rule, + bool winding_cw) { + if (npt < 2) return; + + wxPoint r; + + // We calculate the winding direction of the poly + // in order to know which side to draw symbol on + double dfSum = 0.0; + + for (int iseg = 0; iseg < npt - 1; iseg++) { + dfSum += ptp[iseg].m_x * ptp[iseg + 1].m_y - ptp[iseg].m_y * ptp[iseg + 1].m_x; + } + dfSum += ptp[npt - 1].m_x * ptp[0].m_y - ptp[npt - 1].m_y * ptp[0].m_x; + + bool cw = winding_cw; //dfSum < 0.; + + // Get a true pixel clipping/bounding box from the vp + wxPoint pbb = GetPixFromLL(vp_plib.clat, vp_plib.clon); + int xmin_ = pbb.x - vp_plib.rv_rect.width / 2; + int xmax_ = xmin_ + vp_plib.rv_rect.width; + int ymin_ = pbb.y - vp_plib.rv_rect.height / 2; + int ymax_ = ymin_ + vp_plib.rv_rect.height; + + int x0, y0, x1, y1; + +#ifdef ocpnUSE_GL + // Adjust line width up a bit, to improve render quality for + // GL_BLEND/GL_LINE_SMOOTH + float awidth = wxMax(m_GLMinCartographicLineWidth, (float)width * 0.7); + awidth = wxMax(awidth, 1.5); + glLineWidth(awidth); +#endif + + int start_seg = 0; + int end_seg = npt - 1; + int inc = 1; + + if (cw) { + start_seg = npt - 1; + end_seg = 0; + inc = -1; + } + + float dx, dy, seg_len, theta; + ClipResult res; + + bool done = false; + int iseg = start_seg; + while (!done) { + // Do not bother with segments that are invisible + + x0 = ptp[iseg].m_x; + y0 = ptp[iseg].m_y; + x1 = ptp[iseg + inc].m_x; + y1 = ptp[iseg + inc].m_y; + + // Also, segments marked (by mask) as invisible + if (mask && !mask[iseg]) goto next_seg; + + res = cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, xmin_, xmax_, ymin_, + ymax_); + + if (res == Invisible) goto next_seg; + + dx = ptp[iseg + inc].m_x - ptp[iseg].m_x; + dy = ptp[iseg + inc].m_y - ptp[iseg].m_y; + seg_len = sqrt(dx * dx + dy * dy); + theta = atan2f(dy, dx) * 180 / PI; + printf("Theta: %g\n", theta); + + if (seg_len >= 1.0) { + // Render symbols to fill seg_len + float s = 0; + float xs = ptp[iseg].m_x; + float ys = ptp[iseg].m_y; + char *str = draw_rule->vector.LVCT; + char *col = draw_rule->colRef.LCRF; + wxPoint pivot(draw_rule->pos.line.pivot_x.LICL, + draw_rule->pos.line.pivot_y.LIRW); + wxPoint origin(draw_rule->pos.line.bnbox_x.LBXC, + draw_rule->pos.line.bnbox_y.LBXR); + + while (1) { + r.x = (int)xs; + r.y = (int)ys; + + HPGL->SetVP(&vp_plib); + RenderTex(str, col, r, pivot, origin, 1.0, theta, sym_len, + sym_height, seg_len - s); + + xs += sym_len * dx / seg_len * sym_factor; + ys += sym_len * dy / seg_len * sym_factor; + s += sym_len * sym_factor; + if (s > seg_len) break; + } + } + next_seg: + iseg += inc; + if (iseg == end_seg) done = true; + } // while +} + +// Multipoint Sounding +int s52plib::RenderMPS(ObjRazRules *rzRules, Rules *rules) { + if (!m_bShowSoundg) return 0; + + if (m_bUseSCAMIN) { + if (vp_plib.chart_scale > rzRules->obj->Scamin) return 0; + } + + int npt = rzRules->obj->npt; + + // this should never happen + // But it seems that some PlugIns clear the mps rules without resetting the CS + // state machine So fix it + if (rzRules->obj->bCS_Added && !rzRules->mps) rzRules->obj->bCS_Added = false; + + // Build the cached rules list if necessary + if (!rzRules->obj->bCS_Added) { + ObjRazRules point_rzRules; + point_rzRules = *rzRules; // take a copy of attributes, etc + + S57Obj point_obj; + point_obj = *(rzRules->obj); + point_obj.bIsClone = true; + point_rzRules.obj = &point_obj; + + Rules *ru_cs = StringToRules(_T ( "CS(SOUNDG03;" )); + + wxPoint2DDouble p; + double *pd = rzRules->obj->geoPtz; // the SM points + double *pdl = rzRules->obj->geoPtMulti; // and corresponding lat/lon + + mps_container *pmps = (mps_container *)calloc(sizeof(mps_container), 1); + pmps->cs_rules = new ArrayOfRules; + rzRules->mps = pmps; + + for (int ip = 0; ip < npt; ip++) { + double east = *pd++; + double nort = *pd++; + double depth = *pd++; + + point_obj.x = east; + point_obj.y = nort; + point_obj.z = depth; + + double lon = *pdl++; + double lat = *pdl++; + point_obj.BBObj.Set(lat, lon, lat, lon); + point_obj.BBObj.Invalidate(); + + char *rule_str1 = RenderCS(&point_rzRules, ru_cs); + wxString cs_string(rule_str1, wxConvUTF8); + free(rule_str1); + + Rules *rule_chain = StringToRules(cs_string); + + rzRules->mps->cs_rules->Add(rule_chain); + } + + DestroyRulesChain(ru_cs); + rzRules->obj->bCS_Added = 1; // mark the object + } + + double *pdl = rzRules->obj->geoPtMulti; // and corresponding lat/lon + double *pd = rzRules->obj->geoPtz; // the SM points + + // FIXME (plib) + // Need to un-rotate sounding rendering + // We need a private unrotated copy of the Viewport + // VPointCompat vp_local = *vp; + // vp_local.SetRotationAngle(0.); + + SetupSoundingFont(); + // Measure the size of a character + int charWidth, charHeight; + wxScreenDC sdc; + sdc.GetTextExtent("0", &charWidth, &charHeight, NULL, NULL, m_soundFont); + wxRect soundBox = wxRect(0, 0, charWidth * 4, charHeight * 3 / 2); + + // We need a pixel bounding rectangle of the passed ViewPort. + // Very important for partial screen renders, as with dc mode pans or OpenGL + // FBO operation. + + wxPoint cr0 = GetPixFromLLROT(GetReducedBBox().GetMaxLat(), + GetReducedBBox().GetMinLon(), 0); + wxPoint cr1 = GetPixFromLLROT(GetReducedBBox().GetMinLat(), + GetReducedBBox().GetMaxLon(), 0); + wxRect clip_rect(cr0, cr1); + + double box_margin = + wxMax(fabs(GetBBox().GetMaxLon() - GetBBox().GetMinLon()), + fabs(GetBBox().GetMaxLat() - GetBBox().GetMinLat())); + LLBBox screen_box = GetBBox(); + + std::vector sym_array; + + for (int ip = 0; ip < npt; ip++) { + double lon = *pdl++; + double lat = *pdl++; + + double east = *pd++; + double nort = *pd++; + double depth = *pd++; + + // Make a rough inclusion test from lat/lon + // onto the screen coordinates, enlarged a bit + if (!screen_box.ContainsMarge(lat, lon, box_margin)) continue; + + // Some simple inclusion tests + wxPoint2DDouble r = GetDoublePixFromLLROT(lat, lon, 0); + if ((r.m_x == INVALID_COORD) || (r.m_y == INVALID_COORD)) continue; + + // Use measured symbol size + wxRect rr(r.m_x - (soundBox.width / 2), r.m_y - (soundBox.height / 2), + soundBox.width, soundBox.height); + + // After all the setup, the render inclusion test is trivial.... + if (!clip_rect.Intersects(rr)) continue; + + double angle = 0; + if (depth < 0) int yyp = 4; + + Rules *rules = rzRules->mps->cs_rules->Item(ip); + bool bColorSet = false; + wxColor symColor; + GetGlobalColor(_T("SNDG2"), &symColor); + + while (rules) { + // Render a raster or vector symbol, as specified by LUP rules + if (rules->razRule->definition.SYDF == 'V') { + // On OpenGL, arrange to render the drying height "underline" symbol as + // un-rotated. + double dryAngle = 0; + if (!m_pdc && !strncmp(rules->razRule->name.SYNM, "SOUNDSA1", 8)) + dryAngle = -vp_plib.rotation * 180. / PI; + // FIXME (dave) drying height symbol should be wider/bolder. + + RenderHPGL(rzRules, rules->razRule, r, dryAngle, + m_SoundingsScaleFactor); + } else if (rules->razRule->definition.SYDF == 'R') { + // Parse the first rule to determine the color + if (!bColorSet) { + char symColorT = rules->razRule->name.SYNM[5]; + if (symColorT == 'G') GetGlobalColor(_T("SNDG1"), &symColor); + bColorSet = true; + } + + if (!strncmp(rules->razRule->name.SYNM, "SOUNDGC2", 8)) + RenderRasterSymbol(rzRules, rules->razRule, r, angle); + else { + // Add this sounding to the array, for later bulk rendering + SoundingSym sym; + sym.prule = rules->razRule; + sym.r = r; + sym.color_RGB = symColor.GetRGB(); + sym_array.push_back(sym); + } + } + + // Debug + // if(m_pdc){ + // m_pdc->SetPen(wxPen(*wxRED, 1)); + // m_pdc->SetBrush(wxBrush(*wxRED, wxTRANSPARENT)); + // m_pdc->DrawRectangle(rr); + //} + + rules = rules->next; + } + } + + // Render the "normal" soundings in bulk, to optimize GL shader performance + if (sym_array.size()) RenderMPSArray(rzRules, sym_array); + + return 1; +} + +int s52plib::RenderCARC(ObjRazRules *rzRules, Rules *rules) { +#ifdef ocpnUSE_GL + if (m_useGLSL) + return RenderCARC_GLSL(rzRules, rules); + else +#endif + return RenderCARC_VBO(rzRules, rules); +} + +#ifdef ocpnUSE_GL +int s52plib::RenderCARC_GLSL(ObjRazRules *rzRules, Rules *rules) { + // glDisable( GL_SCISSOR_TEST ); + + char *str = (char *)rules->INSTstr; + // extract the parameters from the string + // And creating a unique string hash as we go + wxString inst(str, wxConvUTF8); + + wxStringTokenizer tkz(inst, _T ( ",;" )); + + // outline color + wxString outline_color = tkz.GetNextToken(); + + // outline width + wxString slong = tkz.GetNextToken(); + long outline_width; + slong.ToLong(&outline_width); + + // arc color + wxString arc_color = tkz.GetNextToken(); + + // arc width + slong = tkz.GetNextToken(); + long arc_width; + slong.ToLong(&arc_width); + + // sectr1 + slong = tkz.GetNextToken(); + double sectr1; + slong.ToDouble(§r1); + + // sectr2 + slong = tkz.GetNextToken(); + double sectr2; + slong.ToDouble(§r2); + + // arc radius + slong = tkz.GetNextToken(); + long radius; + slong.ToLong(&radius); + + // sector radius + slong = tkz.GetNextToken(); + long sector_radius; + slong.ToLong(§or_radius); + + // Center point + wxPoint2DDouble dr; + GetDoublePointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &dr); + + if (radius > m_display_size_mm / 10) { + double fact = radius / (m_display_size_mm / 10); + radius /= fact; + sector_radius /= fact; + arc_width /= fact; + arc_width = wxMax(arc_width, 1); + } + + // radius scaled to display + float rad = radius * canvas_pix_per_mm; + float sec_rad = sector_radius * canvas_pix_per_mm; + + float arcw = arc_width * canvas_pix_per_mm; + // On larger screens, make the arc_width 1.0 mm + if (m_display_size_mm > 200) // 200 mm, about 8 inches + arcw = canvas_pix_per_mm; + + // Adjust size + // Some lights have no SCAMIN attribute. e.g. cm93 + // This causes display congestion at small viewing scales, since the objects + // are rendered at fixed pixel dimensions from the LUP rules. As a + // correction, the idea is to not allow the rendered symbol to be larger than + // "X" meters on the chart. + // and scale it down when rendered if necessary. + + float xscale = 1.0; + if (rzRules->obj->Scamin > 1e8) { // huge (unset) SCAMIN) + float radius_meters_target = 1000; + + float radius_meters = (radius * canvas_pix_per_mm) / vp_plib.view_scale_ppm; + + xscale = radius_meters_target / radius_meters; + xscale = wxMin(xscale, 1.0); + xscale = wxMax(.5, xscale); + + rad *= xscale; + arcw *= xscale; + arcw = wxMin(arcw, rad / 10); + sec_rad *= xscale; + } + + // Enable anti-aliased lines, at best quality + glEnable(GL_BLEND); + + // Rotate the center point about vp center + wxPoint2DDouble point = dr; + double sin_rot = sin(vp_plib.rotation); + double cos_rot = cos(vp_plib.rotation); + + double xp = ((point.m_x - vp_plib.pix_width / 2) * cos_rot) - + ((point.m_y - vp_plib.pix_height / 2) * sin_rot); + double yp = ((point.m_x - vp_plib.pix_width / 2) * sin_rot) + + ((point.m_y - vp_plib.pix_height / 2) * cos_rot); + + point.m_x = xp + vp_plib.pix_width / 2; + point.m_y = yp + vp_plib.pix_height / 2; + + float rad_fluff = rad + 20; + float coords[8]; + coords[0] = -rad_fluff; + coords[1] = rad_fluff; + coords[2] = rad_fluff; + coords[3] = rad_fluff; + coords[4] = -rad_fluff; + coords[5] = -rad_fluff; + coords[6] = rad_fluff; + coords[7] = -rad_fluff; + + glUseProgram(S52ring_shader_program); + + // Get pointers to the attributes in the program. + GLint mPosAttrib = glGetAttribLocation(S52ring_shader_program, "aPos"); + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords); + glEnableVertexAttribArray(mPosAttrib); + + // Circle radius + GLint radiusloc = + glGetUniformLocation(S52ring_shader_program, "circle_radius"); + glUniform1f(radiusloc, rad); + + // Circle center point, physical + GLint centerloc = + glGetUniformLocation(S52ring_shader_program, "circle_center"); + float ctrv[2]; + ctrv[0] = point.m_x; + ctrv[1] = vp_plib.pix_height - point.m_y; + glUniform2fv(centerloc, 1, ctrv); + + // Border color + float bcolorv[4]; + bcolorv[0] = 0; + bcolorv[1] = 0; + bcolorv[2] = 0; + bcolorv[3] = 1.0; + + GLint ring_colloc = + glGetUniformLocation(S52ring_shader_program, "ring_color"); + glUniform4fv(ring_colloc, 1, bcolorv); + + // Border Width + GLint ringWidthloc = + glGetUniformLocation(S52ring_shader_program, "ring_width"); + glUniform1f(ringWidthloc, arcw + (1 * outline_width)); + + // Visible sectors, rotated to vp orientation + float sr1 = sectr1 + (vp_plib.rotation * 180 / PI); + if (sr1 > 360.) sr1 -= 360.; + float sr2 = sectr2 + (vp_plib.rotation * 180 / PI); + if (sr2 > 360.) sr2 -= 360.; + + float sb, se; + if (sr2 > sr1) { + sb = sr1; + se = sr2; + } else { + sb = sr1; + se = sr2 + 360; + } + + // Shader can handle angles > 360. + if ((sb < 0) || (se < 0)) { + sb += 360.; + se += 360.; + } + + GLint sector1loc = glGetUniformLocation(S52ring_shader_program, "sector_1"); + glUniform1f(sector1loc, (sb * PI / 180.)); + GLint sector2loc = glGetUniformLocation(S52ring_shader_program, "sector_2"); + glUniform1f(sector2loc, (se * PI / 180.)); + + // Rotate and translate + mat4x4 I; + mat4x4_identity(I); + + mat4x4_translate_in_place(I, dr.m_x, dr.m_y, 0); + + GLint matloc = + glGetUniformLocation(S52ring_shader_program, "TransformMatrix"); + glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)I); + + // Perform the actual drawing. + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // Now draw color ring + // Circle color + wxColour colorb = getwxColour(arc_color); + float colorv[4]; + colorv[0] = colorb.Red() / float(256); + colorv[1] = colorb.Green() / float(256); + colorv[2] = colorb.Blue() / float(256); + colorv[3] = 1.0; + glUniform4fv(ring_colloc, 1, colorv); + + // arc Width + glUniform1f(ringWidthloc, arcw); + + // Perform the actual drawing. + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // Restore the per-object transform to Identity Matrix + mat4x4 IM; + mat4x4_identity(IM); + GLint matlocf = + glGetUniformLocation(S52ring_shader_program, "TransformMatrix"); + glUniformMatrix4fv(matlocf, 1, GL_FALSE, (const GLfloat *)IM); + glUseProgram(0); + glDisableVertexAttribArray(mPosAttrib); + + // Draw the sector legs directly on the target DC + if (sector_radius > 0) { + double leg_len = sec_rad; + + wxDash dash1[2]; + dash1[0] = (int)(3.6 * canvas_pix_per_mm / 3); // 8// Long dash <---------+ + dash1[1] = (int)(1.8 * canvas_pix_per_mm / 3); // 2// Short gap | + + wxPen thispen = *wxBLACK_PEN; + thispen.SetDashes(2, dash1); + thispen.SetWidth(2); + thispen.SetStyle(wxPENSTYLE_USER_DASH); + + float a = (sectr1 - 90) * PI / 180; + a += vp_plib.rotation; + double x = point.m_x + (leg_len * cosf(a)); + double y = point.m_y + (leg_len * sinf(a)); + DrawDashLine(thispen, point.m_x, point.m_y, x, y); + + a = (sectr2 - 90) * PI / 180.; + a += vp_plib.rotation; + x = point.m_x + (leg_len * cosf(a)); + y = point.m_y + (leg_len * sinf(a)); + DrawDashLine(thispen, point.m_x, point.m_y, x, y); + } + glDisable(GL_BLEND); + + // Update the object Bounding box, + // so that subsequent drawing operations will redraw the item fully + + double latmin, lonmin, latmax, lonmax; + + GetPixPointSingleNoRotate(dr.m_x - rad, dr.m_y + rad, &latmin, &lonmin); + GetPixPointSingleNoRotate(dr.m_x + rad, dr.m_y - rad, &latmax, &lonmax); + LLBBox symbox; + symbox.Set(latmin, lonmin, latmax, lonmax); + rzRules->obj->BBObj.Expand(symbox); + + // glEnable( GL_SCISSOR_TEST ); + + return 1; +} +#endif + +int s52plib::RenderCARC_VBO(ObjRazRules *rzRules, Rules *rules) { + char *str = (char *)rules->INSTstr; + // extract the parameters from the string + // And creating a unique string hash as we go + wxString inst(str, wxConvUTF8); + wxString carc_hash; + + wxStringTokenizer tkz(inst, _T ( ",;" )); + + // outline color + wxString outline_color = tkz.GetNextToken(); + carc_hash += outline_color; + carc_hash += _T("."); + + // outline width + wxString slong = tkz.GetNextToken(); + long outline_width; + slong.ToLong(&outline_width); + carc_hash += slong; + carc_hash += _T("."); + + // arc color + wxString arc_color = tkz.GetNextToken(); + carc_hash += arc_color; + carc_hash += _T("."); + + // arc width + slong = tkz.GetNextToken(); + long arc_width; + slong.ToLong(&arc_width); + carc_hash += slong; + carc_hash += _T("."); + + // sectr1 + slong = tkz.GetNextToken(); + double sectr1; + slong.ToDouble(§r1); + carc_hash += slong; + carc_hash += _T("."); + + // sectr2 + slong = tkz.GetNextToken(); + double sectr2; + slong.ToDouble(§r2); + carc_hash += slong; + carc_hash += _T("."); + + // arc radius + slong = tkz.GetNextToken(); + long radius; + slong.ToLong(&radius); + carc_hash += slong; + carc_hash += _T("."); + + // sector radius + slong = tkz.GetNextToken(); + long sector_radius; + slong.ToLong(§or_radius); + carc_hash += slong; + carc_hash += _T("."); + + slong.Printf(_T("%d"), m_colortable_index); + carc_hash += slong; + + int width; + int height; + int rad; + int bm_width; + int bm_height; + int bm_orgx; + int bm_orgy; + + Rule *prule = rules->razRule; + + float scale_factor = 1.0; + + // The dimensions of the light are presented here as pixels on-screen. + // We must scale the rendered size based on the device pixel density + // Let us declare that the width of the arc should be no less than X mm + float wx = 1.0; + + // float pd_scale = 1.0; + float nominal_arc_width_pix = wxMax( + 1.0, + floor(GetPPMM() * wx)); // { wx } mm nominal, but not less than 1 pixel + // pd_scale = nominal_arc_width_pix / arc_width; + + // scale_factor *= pd_scale; + // qDebug() << GetPPMM() << arc_width << nominal_arc_width_pix << pd_scale; + + // Adjust size + // Some plain lights have no SCAMIN attribute. + // This causes display congestion at small viewing scales, since the objects + // are rendered at fixed pixel dimensions from the LUP rules. As a + // correction, the idea is to not allow the rendered symbol to be larger than + // "X" meters on the chart. + // and scale it down when rendered if necessary. + + float xscale = 1.0; + if (rzRules->obj->Scamin > 1e8) { // huge (unset) SCAMIN) + float radius_meters_target = 200; + + float radius_meters = (radius * canvas_pix_per_mm) / vp_plib.view_scale_ppm; + + xscale = radius_meters_target / radius_meters; + xscale = wxMin(xscale, 1.0); + xscale = wxMax(.4, xscale); + + radius *= xscale; + sector_radius *= xscale; + } + + /// scale_factor *= xscale; + + carc_hash += _T("."); + wxString xs; + xs.Printf(_T("%5g"), xscale); + carc_hash += xs; + + if (m_pdc) { // DC rendering + if (fabs(prule->parm7 - xscale) > .00001) { + ClearRulesCache(prule); + } + + // Instantiate the symbol if necessary + if ((rules->razRule->pixelPtr == NULL) || + (rules->razRule->parm1 != m_colortable_index)) { + // Render the sector light to a bitmap + + rad = (int)(radius * canvas_pix_per_mm); + + width = (rad * 2) + 28; + height = (rad * 2) + 28; + wxBitmap bm(width, height, -1); + wxMemoryDC mdc; + mdc.SelectObject(bm); + mdc.SetBackground(wxBrush(m_unused_wxColor)); + mdc.Clear(); + + // Adjust sector math for wxWidgets API + float sb; + float se; + + // For some reason, the __WXMSW__ build flips the sense of + // start and end angles on DrawEllipticArc() +#ifndef __WXMSW__ + if (sectr2 > sectr1) { + sb = 90 - sectr1; + se = 90 - sectr2; + } else { + sb = 360 + (90 - sectr1); + se = 90 - sectr2; + } +#else + if (sectr2 > sectr1) { + se = 90 - sectr1; + sb = 90 - sectr2; + } else { + se = 360 + (90 - sectr1); + sb = 90 - sectr2; + } +#endif + + // Here is a goofy way of computing the dc drawing extents exactly + // Draw a series of fat line segments approximating the arc using + // dc.DrawLine() This will properly establish the drawing box in the + // dc + + int border_fluff = 4; // by how much should the blit bitmap be "fluffed" + + // wxDC min/max calculations are currently broken in wxQT, so we use the + // entire circle instead of arcs... +#ifndef __WXQT__ + if (fabs(sectr2 - sectr1) != 360) // not necessary for all-round lights + { + mdc.ResetBoundingBox(); + + wxPen *pblockpen = + wxThePenList->FindOrCreatePen(*wxBLACK, 10, wxPENSTYLE_SOLID); + mdc.SetPen(*pblockpen); + + float start_angle, end_angle; + if (se < sb) { + start_angle = se; + end_angle = sb; + } else { + start_angle = sb; + end_angle = se; + } + + int x0 = (width / 2) + (int)(rad * cos(start_angle * PI / 180.)); + int y0 = (height / 2) - (int)(rad * sin(start_angle * PI / 180.)); + for (float a = start_angle + .1; a <= end_angle; a += 2.0) { + int x = (width / 2) + (int)(rad * cosf(a * PI / 180.)); + int y = (height / 2) - (int)(rad * sinf(a * PI / 180.)); + mdc.DrawLine(x0, y0, x, y); + x0 = x; + y0 = y; + } + + bm_width = (mdc.MaxX() - mdc.MinX()) + (border_fluff * 2); + bm_height = (mdc.MaxY() - mdc.MinY()) + (border_fluff * 2); + bm_orgx = mdc.MinX() - border_fluff - + width / 2; // wxMax ( 0, mdc.MinX()-border_fluff ); + bm_orgy = mdc.MinY() - border_fluff - + height / 2; // wxMax ( 0, mdc.MinY()-border_fluff ); + + mdc.Clear(); + } + + else { + bm_width = rad * 2 + (border_fluff * 2); + bm_height = rad * 2 + (border_fluff * 2); + bm_orgx = -bm_width / 2; + bm_orgy = -bm_height / 2; + } + +#else + bm_width = rad * 2 + (border_fluff * 2); + bm_height = rad * 2 + (border_fluff * 2); + bm_orgx = -bm_width / 2; + bm_orgy = -bm_height / 2; +#endif + + wxBitmap *sbm = NULL; + + // Draw the outer border + wxColour color = getwxColour(outline_color); + + wxPen *pthispen = wxThePenList->FindOrCreatePen( + color, outline_width * scale_factor, wxPENSTYLE_SOLID); + mdc.SetPen(*pthispen); + wxBrush *pthisbrush = + wxTheBrushList->FindOrCreateBrush(color, wxBRUSHSTYLE_TRANSPARENT); + mdc.SetBrush(*pthisbrush); + + mdc.DrawEllipticArc(width / 2 - rad, height / 2 - rad, rad * 2, rad * 2, + sb, se); + + if (arc_width) { + wxColour colorb = getwxColour(arc_color); + + if (!colorb.IsOk()) colorb = getwxColour(_T("CHMGD")); + + pthispen = wxThePenList->FindOrCreatePen( + colorb, arc_width * scale_factor, wxPENSTYLE_SOLID); + mdc.SetPen(*pthispen); + + mdc.DrawEllipticArc(width / 2 - rad, height / 2 - rad, rad * 2, rad * 2, + sb, se); + } + + mdc.SelectObject(wxNullBitmap); + + // Get smallest containing bitmap + sbm = new wxBitmap(bm.GetSubBitmap(wxRect( + width / 2 + bm_orgx, height / 2 + bm_orgy, bm_width, bm_height))); + + // Make the mask + wxMask *pmask = new wxMask(*sbm, m_unused_wxColor); + + // Associate the mask with the bitmap + sbm->SetMask(pmask); + + // delete any old private data + ClearRulesCache(rules->razRule); + + // Save the bitmap ptr and aux parms in the rule + prule->pixelPtr = sbm; + prule->parm0 = ID_wxBitmap; + prule->parm1 = m_colortable_index; + prule->parm2 = bm_orgx; + prule->parm3 = bm_orgy; + prule->parm5 = bm_width; + prule->parm6 = bm_height; + prule->parm7 = xscale; + } // instantiation + } + + // Render arcs at object's x/y + wxPoint r; + GetPointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &r); + + // Now render the symbol + if (!m_pdc) // opengl + { + } else { + int b_width = prule->parm5; + int b_height = prule->parm6; + + // Get the bitmap into a memory dc + wxMemoryDC mdc; + mdc.SelectObject((wxBitmap &)(*((wxBitmap *)(rules->razRule->pixelPtr)))); + + // Blit it into the target dc, using mask + m_pdc->Blit(r.x + rules->razRule->parm2, r.y + rules->razRule->parm3, + b_width, b_height, &mdc, 0, 0, wxCOPY, true); + + mdc.SelectObject(wxNullBitmap); + + // Draw the sector legs directly on the target DC + // so that anti-aliasing works against the drawn image (cannot be + // cached...) + if (sector_radius > 0) { + int leg_len = (int)(sector_radius * canvas_pix_per_mm); + + wxDash dash1[2]; + dash1[0] = (int)(3.6 * canvas_pix_per_mm); // 8// Long dash <---------+ + dash1[1] = (int)(1.8 * canvas_pix_per_mm); // 2// Short gap | + + /* + wxPen *pthispen = new wxPen(*wxBLACK_PEN); + pthispen->SetStyle(wxPENSTYLE_USER_DASH); + pthispen->SetDashes( 2, dash1 ); + // Undocumented "feature": Pen must be fully specified <<>> + setting into DC pdc->SetPen ( *pthispen ); + */ + // wxColour c = GetGlobalColor( _T ( "CHBLK" ) ); + wxColour c; + GetGlobalColor(_T ( "CHBLK" ), &c); + + float a = (sectr1 - 90) * PI / 180; + int x = r.x + (int)(leg_len * cosf(a)); + int y = r.y + (int)(leg_len * sinf(a)); + DrawAALine(m_pdc, r.x, r.y, x, y, c, dash1[0], dash1[1]); + + a = (sectr2 - 90) * PI / 180.; + x = r.x + (int)(leg_len * cosf(a)); + y = r.y + (int)(leg_len * sinf(a)); + DrawAALine(m_pdc, r.x, r.y, x, y, c, dash1[0], dash1[1]); + } + + // Debug the symbol bounding box..... + /* + if(m_pdc){ + m_pdc->SetPen(wxPen(*wxGREEN, 1)); + m_pdc->SetBrush(wxBrush(*wxGREEN, wxTRANSPARENT)); + m_pdc->DrawRectangle( r.x + rules->razRule->parm2, r.y + + rules->razRule->parm3, b_width, b_height); + } + */ + } + + // Update the object Bounding box, + // so that subsequent drawing operations will redraw the item fully + + double latmin, lonmin, latmax, lonmax; + + GetPixPointSingleNoRotate( + r.x + prule->parm2, r.y + prule->parm3 + prule->parm6, &latmin, &lonmin); + GetPixPointSingleNoRotate(r.x + prule->parm2 + prule->parm5, + r.y + prule->parm3, &latmax, &lonmax); + LLBBox symbox; + symbox.Set(latmin, lonmin, latmax, lonmax); + rzRules->obj->BBObj.Expand(symbox); + return 1; +} + +// Conditional Symbology +char *s52plib::RenderCS(ObjRazRules *rzRules, Rules *rules) { + void *ret; + void *(*f)(void *); + + static int f05; + + if (rules->razRule == NULL) { + if (!f05) + // CPLError ( ( CPLErr ) 0, 0,"S52plib:_renderCS(): ERROR + // no conditional symbology for: %s\n", rules->INSTstr ); + f05++; + return 0; + } + + void *g = (void *)rules->razRule; + +#ifdef FIX_FOR_MSVC //__WXMSW__ + // #warning Fix this cast, somehow... + // dsr sigh... can't get the cast right + _asm + { + mov eax,[dword ptr g] + mov [dword ptr f],eax + } + ret = f((void *)rzRules); // call cond symb +#else + + f = (void *(*)(void *))g; + ret = f((void *)rzRules); + +#endif + + return (char *)ret; +} + +int s52plib::RenderObjectToDC(wxDC *pdcin, ObjRazRules *rzRules) { + return DoRenderObject(pdcin, rzRules); +} + +int s52plib::RenderObjectToGL(const wxGLContext &glcc, ObjRazRules *rzRules) { + m_glcc = (wxGLContext *)&glcc; + return DoRenderObject(NULL, rzRules); +} + +int s52plib::RenderObjectToDCText(wxDC *pdcin, ObjRazRules *rzRules) { + return DoRenderObjectTextOnly(pdcin, rzRules); +} + +int s52plib::RenderObjectToGLText(const wxGLContext &glcc, + ObjRazRules *rzRules) { + m_glcc = (wxGLContext *)&glcc; + return DoRenderObjectTextOnly(NULL, rzRules); +} + +int s52plib::DoRenderObject(wxDC *pdcin, ObjRazRules *rzRules) { + if (!ObjectRenderCheckRules(rzRules, true)) return 0; + +#ifdef __OCPN__ANDROID__ + // Catch a difficult to trap SIGSEGV, avoiding app crash + auto rzRules_current = rzRules; // Save current rules pointer + sigaction(SIGSEGV, NULL, + &sa_all_plib_previous); // save existing action for this signal + + struct sigaction temp; + sigaction(SIGSEGV, NULL, + &temp); // inspect existing action for this signal + + temp.sa_handler = catch_signals_plib; // point to my handler + sigemptyset(&temp.sa_mask); // make the blocking set + // empty, so that all + // other signals will be + // unblocked during my handler + temp.sa_flags = 0; + sigaction(SIGSEGV, &temp, NULL); + + if (sigsetjmp(env_plib, 1)) // Something in the + // code block below this on caused SIGSEGV... + { + // reset signal handler + sigaction(SIGSEGV, &sa_all_plib_previous, NULL); + rzRules_current->obj->bOK_to_Render = false; //Mark object unrenderable + return 0; + } else +#endif + { + + m_pdc = pdcin; // use this DC + Rules *rules = rzRules->LUP->ruleList; + + while (rules != NULL) { + switch (rules->ruleType) { + case RUL_TXT_TX: + RenderTX(rzRules, rules); + break; // TX + case RUL_TXT_TE: + RenderTE(rzRules, rules); + break; // TE + case RUL_SYM_PT: + RenderSY(rzRules, rules); + break; // SY + case RUL_SIM_LN: + if (m_pdc) + RenderLS(rzRules, rules); + else + RenderGLLS(rzRules, rules); + break; // LS + case RUL_COM_LN: + RenderLC(rzRules, rules); + break; // LC + case RUL_MUL_SG: + RenderMPS(rzRules, rules); + break; // MultiPoint Sounding + case RUL_ARC_2C: + RenderCARC(rzRules, rules); + break; // Circular Arc, 2 colors + + case RUL_CND_SY: { + if (!rzRules->obj->bCS_Added) { + rzRules->obj->CSrules = NULL; + GetAndAddCSRules(rzRules, rules); + if (strncmp(rzRules->obj->FeatureName, "SOUNDG", 6)) + rzRules->obj->bCS_Added = 1; // mark the object + } + + Rules *rules_last = rules; + rules = rzRules->obj->CSrules; + + while (NULL != rules) { + switch (rules->ruleType) { + case RUL_TXT_TX: + RenderTX(rzRules, rules); + break; + case RUL_TXT_TE: + RenderTE(rzRules, rules); + break; + case RUL_SYM_PT: + RenderSY(rzRules, rules); + break; + case RUL_SIM_LN: + if (m_pdc) + RenderLS(rzRules, rules); + else + RenderGLLS(rzRules, rules); + break; // LS + case RUL_COM_LN: + RenderLC(rzRules, rules); + break; + case RUL_MUL_SG: + RenderMPS(rzRules, rules); + break; // MultiPoint Sounding + case RUL_ARC_2C: + RenderCARC(rzRules, rules); + break; // Circular Arc, 2 colors + case RUL_NONE: + default: + break; // no rule type (init) + } + rules_last = rules; + rules = rules->next; + } + + rules = rules_last; + break; + } + + case RUL_NONE: + default: + break; // no rule type (init) + } // switch + + rules = rules->next; + } + } +#ifdef __OCPN__ANDROID__ + // reset signal handler + sigaction(SIGSEGV, &sa_all_plib_previous, NULL); +#endif + return 1; +} + +int s52plib::DoRenderObjectTextOnly(wxDC *pdcin, ObjRazRules *rzRules) { + // if(strncmp(rzRules->obj->FeatureName, "RDOCAL", 6)) + // return 0; + + // if(rzRules->obj->Index == 2766) + // int yyp = 4; + + if (!ObjectRenderCheckRules(rzRules, true)) return 0; + + m_pdc = pdcin; // use this DC + Rules *rules = rzRules->LUP->ruleList; + + while (rules != NULL) { + switch (rules->ruleType) { + case RUL_TXT_TX: + RenderTX(rzRules, rules); + break; // TX + case RUL_TXT_TE: + RenderTE(rzRules, rules); + break; // TE + case RUL_CND_SY: { + if (!rzRules->obj->bCS_Added) { + rzRules->obj->CSrules = NULL; + GetAndAddCSRules(rzRules, rules); + if (strncmp(rzRules->obj->FeatureName, "SOUNDG", 6)) + rzRules->obj->bCS_Added = 1; // mark the object + } + + Rules *rules_last = rules; + rules = rzRules->obj->CSrules; + + while (NULL != rules) { + switch (rules->ruleType) { + case RUL_TXT_TX: + RenderTX(rzRules, rules); + break; + case RUL_TXT_TE: + RenderTE(rzRules, rules); + break; + default: + break; // no rule type (init) + } + rules_last = rules; + rules = rules->next; + } + + rules = rules_last; + break; + } + + case RUL_NONE: + default: + break; // no rule type (init) + } // switch + + rules = rules->next; + } + + return 1; +} + +bool s52plib::PreloadOBJLFromCSV(const wxString &csv_file) { + wxTextFile file(csv_file); + if (!file.Exists()) return false; + + file.Open(); + + wxString str; + str = file.GetFirstLine(); + wxChar quote[] = {'\"', 0}; + wxString description; + wxString token; + + while (!file.Eof()) { + str = file.GetNextLine(); + + wxStringTokenizer tkz(str, _T(",")); + token = tkz.GetNextToken(); // code + + description = tkz.GetNextToken(); // May contain comma + if (!description.EndsWith(quote)) description << tkz.GetNextToken(); + description.Replace(_T("\""), _T(""), true); + + token = tkz.GetNextToken(); // Acronym + + if (token.Len()) { + // Filter out any duplicates, in a case insensitive way + // i.e. only the first of "DEPARE" and "depare" is added + bool bdup = false; + for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++) { + OBJLElement *pOLEt = (OBJLElement *)(pOBJLArray->Item(iPtr)); + if (!token.CmpNoCase(wxString(pOLEt->OBJLName, wxConvUTF8))) { + bdup = true; + break; + } + } + + if (!bdup) { + wxCharBuffer buffer = token.ToUTF8(); + if (buffer.data()) { + OBJLElement *pOLE = (OBJLElement *)calloc(sizeof(OBJLElement), 1); + memcpy(pOLE->OBJLName, buffer.data(), 6); + pOLE->nViz = 0; + + pOBJLArray->Add((void *)pOLE); + + OBJLDescriptions.push_back(description); + } + } + } + } + return true; +} + +void s52plib::UpdateOBJLArray(S57Obj *obj) { + // Search the array for this object class + + bool bNeedNew = true; + OBJLElement *pOLE; + + for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++) { + pOLE = (OBJLElement *)(pOBJLArray->Item(iPtr)); + if (!strncmp(pOLE->OBJLName, obj->FeatureName, 6)) { + obj->iOBJL = iPtr; + bNeedNew = false; + break; + } + } + + // Not found yet, so add an element + if (bNeedNew) { + pOLE = (OBJLElement *)calloc(sizeof(OBJLElement), 1); + memcpy(pOLE->OBJLName, obj->FeatureName, OBJL_NAME_LEN); + pOLE->nViz = 1; + + pOBJLArray->Add((void *)pOLE); + obj->iOBJL = pOBJLArray->GetCount() - 1; + } +} + +int s52plib::SetLineFeaturePriority(ObjRazRules *rzRules, int npriority) { + int priority_set = npriority; // may be adjusted + + Rules *rules = rzRules->LUP->ruleList; + + // Do Object Type Filtering + // If the object s not currently visible (i.e. classed as a not-currently + // visible category), then do not set the line segment priorities at all + // + + bool b_catfilter = true; + + // DEPCNT is mutable + if (m_nDisplayCategory == STANDARD) { + if ((DISPLAYBASE != rzRules->LUP->DISC) && + (STANDARD != rzRules->LUP->DISC)) { + b_catfilter = rzRules->obj->m_bcategory_mutable; + } + } else if (m_nDisplayCategory == DISPLAYBASE) { + if (DISPLAYBASE != rzRules->LUP->DISC) { + b_catfilter = rzRules->obj->m_bcategory_mutable; + } + } + + if (IsObjNoshow(rzRules->LUP->OBCL)) b_catfilter = false; + + if (!b_catfilter) // No chance this object is visible + return 0; + + while (rules != NULL) { + switch (rules->ruleType) { + case RUL_SIM_LN: + case RUL_COM_LN: + PrioritizeLineFeature(rzRules, priority_set); + break; // LC + + case RUL_CND_SY: { + if (!rzRules->obj->bCS_Added) { + rzRules->obj->CSrules = NULL; + GetAndAddCSRules(rzRules, rules); + rzRules->obj->bCS_Added = 1; // mark the object + } + Rules *rules_last = rules; + rules = rzRules->obj->CSrules; + + while (NULL != rules) { + switch (rules->ruleType) { + case RUL_SIM_LN: + case RUL_COM_LN: + PrioritizeLineFeature(rzRules, priority_set); + break; + case RUL_NONE: + default: + break; // no rule type (init) + } + rules_last = rules; + rules = rules->next; + } + + rules = rules_last; + break; + } + + case RUL_NONE: + default: + break; // no rule type (init) + } // switch + + rules = rules->next; + } + + return 1; +} + +int s52plib::PrioritizeLineFeature(ObjRazRules *rzRules, int npriority) { + if (rzRules->obj->m_ls_list) { + VE_Element *pedge; + connector_segment *pcs; + line_segment_element *ls = rzRules->obj->m_ls_list; + while (ls) { + switch (ls->ls_type) { + case TYPE_EE: + case TYPE_EE_REV: + + pedge = ls->pedge; //(VE_Element *)ls->private0; + if (pedge) + pedge->max_priority = + npriority; // wxMax(pedge->max_priority, npriority); + break; + + default: + pcs = ls->pcs; //(connector_segment *)ls->private0; + if (pcs) + pcs->max_priority_cs = + npriority; // wxMax(pcs->max_priority, npriority); + break; + } + + ls = ls->next; + } + } + + else if (rzRules->obj->m_ls_list_legacy) { // PlugIn (S63) + + PI_connector_segment *pcs; + VE_Element *pedge; + + PI_line_segment_element *ls = rzRules->obj->m_ls_list_legacy; + while (ls) { + switch (ls->type) { + case TYPE_EE: + + pedge = (VE_Element *)ls->private0; + if (pedge) + pedge->max_priority = + npriority; // wxMax(pedge->max_priority, npriority); + break; + + default: + pcs = (PI_connector_segment *)ls->private0; + if (pcs) + pcs->max_priority = + npriority; // wxMax(pcs->max_priority, npriority); + break; + } + + ls = ls->next; + } + } +#if 0 + else if( rzRules->obj->m_n_lsindex && rzRules->obj->m_lsindex_array) { + VE_Hash *edge_hash; + + if( rzRules->obj->m_chart_context->chart ){ + edge_hash = &rzRules->obj->m_chart_context->chart->Get_ve_hash(); + } + else { + edge_hash = (VE_Hash *)rzRules->obj->m_chart_context->m_pve_hash; + } + + int *index_run = rzRules->obj->m_lsindex_array; + + for( int iseg = 0; iseg < rzRules->obj->m_n_lsindex; iseg++ ) { + // Get first connected node + int inode = *index_run++; + + VE_Element *pedge = 0; + // Get the edge + int enode = *index_run++; + if(enode) + pedge = (*edge_hash)[enode]; + + // Set priority + if(pedge){ + pedge->max_priority = npriority; + } + + // Get last connected node + inode = *index_run++; + + } + } +#endif + + return 1; +} + +class XPOINT { +public: + float x, y; +}; + +class XLINE { +public: + XPOINT o, p; + float m; + float c; +}; + +bool TestLinesIntersection(XLINE &a, XLINE &b) { + XPOINT i; + + if ((a.p.x == a.o.x) && (b.p.x == b.o.x)) // both vertical + { + return (a.p.x == b.p.x); + } + + if (a.p.x == a.o.x) // a line a is vertical + { + // calculate b gradient + b.m = (b.p.y - b.o.y) / (b.p.x - b.o.x); + // calculate axis intersect values + b.c = b.o.y - (b.m * b.o.x); + // calculate y point of intercept + i.y = b.o.y + ((a.o.x - b.o.x) * b.m); + if (i.y < wxMin(a.o.y, a.p.y) || i.y > wxMax(a.o.y, a.p.y)) return false; + return true; + } + + if (b.p.x == b.o.x) // line b is vertical + { + // calculate b gradient + a.m = (a.p.y - a.o.y) / (a.p.x - a.o.x); + // calculate axis intersect values + a.c = a.o.y - (a.m * a.o.x); + // calculate y point of intercept + i.y = a.o.y + ((b.o.x - a.o.x) * a.m); + if (i.y < wxMin(b.o.y, b.p.y) || i.y > wxMax(b.o.y, b.p.y)) return false; + return true; + } + + // calculate gradients + a.m = (a.p.y - a.o.y) / (a.p.x - a.o.x); + b.m = (b.p.y - b.o.y) / (b.p.x - b.o.x); + // parallel lines can't intercept + if (a.m == b.m) { + return false; + } + // calculate axis intersect values + a.c = a.o.y - (a.m * a.o.x); + b.c = b.o.y - (b.m * b.o.x); + // calculate x point of intercept + i.x = (b.c - a.c) / (a.m - b.m); + // is intersection point in segment + if (i.x < wxMin(a.o.x, a.p.x) || i.x > wxMax(a.o.x, a.p.x)) { + return false; + } + if (i.x < wxMin(b.o.x, b.p.x) || i.x > wxMax(b.o.x, b.p.x)) { + return false; + } + // points intercept + return true; +} + +//----------------------------------------------------------------------- +// Check a triangle described by point array, and rectangle described by +// render_canvas_parms for intersection Return false if no intersection +//----------------------------------------------------------------------- +bool s52plib::inter_tri_rect(wxPoint *ptp, render_canvas_parms *pb_spec) { + // First stage + // Check all three points of triangle to see it any are within the render + // rectangle + + BoundingBox rect(pb_spec->lclip, pb_spec->y, pb_spec->rclip, + pb_spec->y + pb_spec->height); + + for (int i = 0; i < 3; i++) { + if (rect.PointInBox(ptp[i].x, ptp[i].y)) return true; + } + + // Next stage + // Check all four points of rectangle to see it any are within the render + // triangle + + double p[6]; + MyPoint *pmp = (MyPoint *)p; + + for (int i = 0; i < 3; i++) { + pmp[i].x = ptp[i].x; + pmp[i].y = ptp[i].y; + } + + if (G_PtInPolygon(pmp, 3, pb_spec->lclip, pb_spec->y)) return true; + + if (G_PtInPolygon(pmp, 3, pb_spec->lclip, pb_spec->y + pb_spec->height)) + return true; + + if (G_PtInPolygon(pmp, 3, pb_spec->rclip, pb_spec->y)) return true; + + if (G_PtInPolygon(pmp, 3, pb_spec->rclip, pb_spec->y + pb_spec->height)) + return true; + + // last step + // Check triangle lines against rect lines for line intersect + + for (int i = 0; i < 3; i++) { + XLINE a; + a.o.x = ptp[i].x; + a.o.y = ptp[i].y; + if (i == 2) { + a.p.x = ptp[0].x; + a.p.y = ptp[0].y; + } else { + a.p.x = ptp[i + 1].x; + a.p.y = ptp[i + 1].y; + } + + XLINE b; + + // top line + b.o.x = pb_spec->lclip; + b.o.y = pb_spec->y; + b.p.x = pb_spec->rclip; + b.p.y = pb_spec->y; + + if (TestLinesIntersection(a, b)) return true; + + // right line + b.o.x = pb_spec->rclip; + b.o.y = pb_spec->y; + b.p.x = pb_spec->rclip; + b.p.y = pb_spec->y + pb_spec->height; + + if (TestLinesIntersection(a, b)) return true; + + // bottom line + b.o.x = pb_spec->rclip; + b.o.y = pb_spec->y + pb_spec->height; + b.p.x = pb_spec->lclip; + b.p.y = pb_spec->y + pb_spec->height; + + if (TestLinesIntersection(a, b)) return true; + + // left line + b.o.x = pb_spec->lclip; + b.o.y = pb_spec->y + pb_spec->height; + b.p.x = pb_spec->lclip; + b.p.y = pb_spec->y; + + if (TestLinesIntersection(a, b)) return true; + } + + return false; // no Intersection +} + +//---------------------------------------------------------------------------------- +// +// Fast Basic Canvas Rendering +// Render triangle +// +//---------------------------------------------------------------------------------- +int s52plib::dda_tri(wxPoint *ptp, S52color *c, render_canvas_parms *pb_spec, + render_canvas_parms *pPatt_spec) { + unsigned char r = 0; + unsigned char g = 0; + unsigned char b = 0; + + if (!inter_tri_rect(ptp, pb_spec)) return 0; + + if (NULL != c) { + if (pb_spec->b_revrgb) { + r = c->R; + g = c->G; + b = c->B; + } else { + b = c->R; + g = c->G; + r = c->B; + } + } + + // Color Debug + /* int fc = rand(); + b = fc & 0xff; + g = fc & 0xff; + r = fc & 0xff; + */ + + int color_int = 0; + if (NULL != c) color_int = ((r) << 16) + ((g) << 8) + (b); + + // Determine ymin and ymax indices + + int ymax = ptp[0].y; + int ymin = ymax; + int xmin, xmax, xmid, ymid; + int imin = 0; + int imax = 0; + int imid; + + for (int ip = 1; ip < 3; ip++) { + if (ptp[ip].y > ymax) { + imax = ip; + ymax = ptp[ip].y; + } + if (ptp[ip].y <= ymin) { + imin = ip; + ymin = ptp[ip].y; + } + } + + imid = 3 - (imin + imax); // do the math... + + xmax = ptp[imax].x; + xmin = ptp[imin].x; + xmid = ptp[imid].x; + ymid = ptp[imid].y; + + // Create edge arrays using fast integer DDA + int m, x, dy, count; + bool cw; + + if ((abs(xmax - xmin) > 32768) || (abs(xmid - xmin) > 32768) || + (abs(xmax - xmid) > 32768) || (abs(ymax - ymin) > 32768) || + (abs(ymid - ymin) > 32768) || (abs(ymax - ymid) > 32768) || + (xmin > 32768) || (xmid > 32768)) { + dy = (ymax - ymin); + if (dy) { + m = (xmax - xmin) << 8; + m /= dy; + + x = xmin << 8; + + for (count = ymin; count <= ymax; count++) { + if ((count >= 0) && (count < 1500)) ledge[count] = x >> 8; + x += m; + } + } + + dy = (ymid - ymin); + if (dy) { + m = (xmid - xmin) << 8; + m /= dy; + + x = xmin << 8; + + for (count = ymin; count <= ymid; count++) { + if ((count >= 0) && (count < 1500)) redge[count] = x >> 8; + x += m; + } + } + + dy = (ymax - ymid); + if (dy) { + m = (xmax - xmid) << 8; + m /= dy; + + x = xmid << 8; + + for (count = ymid; count <= ymax; count++) { + if ((count >= 0) && (count < 1500)) redge[count] = x >> 8; + x += m; + } + } + + double ddfSum = 0; + // Check the triangle edge winding direction + ddfSum += (xmin / 1) * (ymax / 1) - (ymin / 1) * (xmax / 1); + ddfSum += (xmax / 1) * (ymid / 1) - (ymax / 1) * (xmid / 1); + ddfSum += (xmid / 1) * (ymin / 1) - (ymid / 1) * (xmin / 1); + cw = ddfSum < 0; + + } else { + dy = (ymax - ymin); + if (dy) { + m = (xmax - xmin) << 16; + m /= dy; + + x = xmin << 16; + + for (count = ymin; count <= ymax; count++) { + if ((count >= 0) && (count < 1500)) ledge[count] = x >> 16; + x += m; + } + } + + dy = (ymid - ymin); + if (dy) { + m = (xmid - xmin) << 16; + m /= dy; + + x = xmin << 16; + + for (count = ymin; count <= ymid; count++) { + if ((count >= 0) && (count < 1500)) redge[count] = x >> 16; + x += m; + } + } + + dy = (ymax - ymid); + if (dy) { + m = (xmax - xmid) << 16; + m /= dy; + + x = xmid << 16; + + for (count = ymid; count <= ymax; count++) { + if ((count >= 0) && (count < 1500)) redge[count] = x >> 16; + x += m; + } + } + + // Check the triangle edge winding direction + long dfSum = 0; + dfSum += xmin * ymax - ymin * xmax; + dfSum += xmax * ymid - ymax * xmid; + dfSum += xmid * ymin - ymid * xmin; + + cw = dfSum < 0; + + } // else + + // if cw is true, redge is actually on the right + + int y1 = ymax; + int y2 = ymin; + + int ybt = pb_spec->y; + int yt = pb_spec->y + pb_spec->height; + + if (y1 > yt) y1 = yt; + if (y1 < ybt) y1 = ybt; + + if (y2 > yt) y2 = yt; + if (y2 < ybt) y2 = ybt; + + int lclip = pb_spec->lclip; + int rclip = pb_spec->rclip; + if (y1 == y2) return 0; + + // Clip the triangle + if (cw) { + for (int iy = y2; iy <= y1; iy++) { + if (ledge[iy] < lclip) { + if (redge[iy] < lclip) + ledge[iy] = -1; + else + ledge[iy] = lclip; + } + + if (redge[iy] > rclip) { + if (ledge[iy] > rclip) + ledge[iy] = -1; + else + redge[iy] = rclip; + } + } + } else { + for (int iy = y2; iy <= y1; iy++) { + if (redge[iy] < lclip) { + if (ledge[iy] < lclip) + ledge[iy] = -1; + else + redge[iy] = lclip; + } + + if (ledge[iy] > rclip) { + if (redge[iy] > rclip) + ledge[iy] = -1; + else + ledge[iy] = rclip; + } + } + } + + // Fill the triangle + + int ya = y2; + int yb = y1; + + unsigned char *pix_buff = pb_spec->pix_buff; + + int patt_size_x = 0, patt_size_y = 0, patt_pitch = 0; + unsigned char *patt_s0 = NULL; + if (pPatt_spec) { + patt_size_y = pPatt_spec->height; + patt_size_x = pPatt_spec->width; + patt_pitch = pPatt_spec->pb_pitch; + patt_s0 = pPatt_spec->pix_buff; + + if (patt_size_y == 0) /* integer division by this value below */ + return false; + } + + if (pb_spec->depth == 24) { + for (int iyp = ya; iyp < yb; iyp++) { + if ((iyp >= ybt) && (iyp < yt)) { + int yoff = (iyp - pb_spec->y) * pb_spec->pb_pitch; + + unsigned char *py = pix_buff + yoff; + + int ix, ixm; + if (cw) { + ix = ledge[iyp]; + ixm = redge[iyp]; + } else { + ixm = ledge[iyp]; + ix = redge[iyp]; + } + + if (ledge[iyp] != -1) { + // This would be considered a failure of the dda algorithm + // Happens on very high zoom, with very large triangles. + // The integers of the dda algorithm don't have enough bits... + // Anyway, just ignore this triangle if it happens + if (ix > ixm) continue; + + int xoff = (ix - pb_spec->x) * 3; + + unsigned char *px = py + xoff; + + if (pPatt_spec) // Pattern + { + int y_stagger = (iyp - pPatt_spec->y) / patt_size_y; + int x_stagger_off = 0; + if ((y_stagger & 1) && pPatt_spec->b_stagger) + x_stagger_off = pPatt_spec->width / 2; + + int patt_y = abs((iyp - pPatt_spec->y)) % patt_size_y; + + unsigned char *pp0 = patt_s0 + (patt_y * patt_pitch); + + while (ix <= ixm) { + int patt_x = + abs(((ix - pPatt_spec->x) + x_stagger_off) % patt_size_x); + + unsigned char *pp = pp0 + (patt_x * 4); + unsigned char alpha = pp[3]; + double da = (double)alpha / 256.; + + unsigned char r = (unsigned char)(*px * (1.0 - da) + pp[0] * da); + unsigned char g = + (unsigned char)(*(px + 1) * (1.0 - da) + pp[1] * da); + unsigned char b = + (unsigned char)(*(px + 2) * (1.0 - da) + pp[2] * da); + + *px++ = r; + *px++ = g; + *px++ = b; + ix++; + } + } + + else // No Pattern + { +#if defined(__WXGTK__) && defined(__INTEL__) +#define memset3(dest, value, count) \ + __asm__ __volatile__( \ + "cmp $0,%2\n\t" \ + "jg 2f\n\t" \ + "je 3f\n\t" \ + "jmp 4f\n\t" \ + "2:\n\t" \ + "movl %0,(%1)\n\t" \ + "add $3,%1\n\t" \ + "dec %2\n\t" \ + "jnz 2b\n\t" \ + "3:\n\t" \ + "movb %b0,(%1)\n\t" \ + "inc %1\n\t" \ + "movb %h0,(%1)\n\t" \ + "inc %1\n\t" \ + "shr $16,%0\n\t" \ + "movb %b0,(%1)\n\t" \ + "4:\n\t" \ + : \ + : "a"(value), "D"(dest), "r"(count) \ + :); + + int count = ixm - ix; + memset3(px, color_int, count) +#else + + while (ix <= ixm) { + *px++ = b; + *px++ = g; + *px++ = r; + + ix++; + } +#endif + } + } + } + } + } + + if (pb_spec->depth == 32) { + assert(ya <= yb); + + for (int iyp = ya; iyp < yb; iyp++) { + if ((iyp >= ybt) && (iyp < yt)) { + int yoff = (iyp - pb_spec->y) * pb_spec->pb_pitch; + + unsigned char *py = pix_buff + yoff; + + int ix, ixm; + if (cw) { + ix = ledge[iyp]; + ixm = redge[iyp]; + } else { + ixm = ledge[iyp]; + ix = redge[iyp]; + } + + if (ledge[iyp] != -1) { + // This would be considered a failure of the dda algorithm + // Happens on very high zoom, with very large triangles. + // The integers of the dda algorithm don't have enough bits... + // Anyway, just ignore this triangle if it happens + if (ix > ixm) continue; + + int xoff = (ix - pb_spec->x) * pb_spec->depth / 8; + + unsigned char *px = py + xoff; + + if (pPatt_spec) // Pattern + { + int y_stagger = (iyp - pPatt_spec->y) / patt_size_y; + + int x_stagger_off = 0; + if ((y_stagger & 1) && pPatt_spec->b_stagger) + x_stagger_off = pPatt_spec->width / 2; + + int patt_y = abs((iyp - pPatt_spec->y)) % patt_size_y; + + unsigned char *pp0 = patt_s0 + (patt_y * patt_pitch); + + while (ix <= ixm) { + int patt_x = + abs(((ix - pPatt_spec->x) + x_stagger_off) % patt_size_x); + /* + if(pPatt_spec->depth == 24) + { + unsigned char *pp = pp0 + (patt_x * 3); + + // Todo This line assumes unused_color is always 0,0,0 + if( pp[0] && pp[1] && pp[2] ) { + *px++ = *pp++; + *px++ = *pp++; + *px++ = *pp++; + px++; + } else { + px += 4; + // pp += 4; + } + } + else + */ + { + unsigned char *pp = pp0 + (patt_x * 4); + unsigned char alpha = pp[3]; + if (alpha > 128) { + double da = (double)alpha / 256.; + + unsigned char r = (unsigned char)(pp[0] * da); + unsigned char g = (unsigned char)(pp[1] * da); + unsigned char b = (unsigned char)(pp[2] * da); + + *px++ = r; + *px++ = g; + *px++ = b; + px++; + } else + px += 4; + } + ix++; + } + } + + else // No Pattern + { + int *pxi = (int *)px; + while (ix <= ixm) { + *pxi++ = color_int; + ix++; + } + } + } + } + } + } + + return true; +} + +//---------------------------------------------------------------------------------- +// +// Render Trapezoid +// +//---------------------------------------------------------------------------------- +inline int s52plib::dda_trap(wxPoint *segs, int lseg, int rseg, int ytop, + int ybot, S52color *c, + render_canvas_parms *pb_spec, + render_canvas_parms *pPatt_spec) { + unsigned char r = 0, g = 0, b = 0; + + if (NULL != c) { + if (pb_spec->b_revrgb) { + r = c->R; + g = c->G; + b = c->B; + } else { + b = c->R; + g = c->G; + r = c->B; + } + } + + // Color Debug + /* int fc = rand(); + b = fc & 0xff; + g = fc & 0xff; + r = fc & 0xff; + */ + + // int debug = 0; + int ret_val = 0; + + int color_int = 0; + if (NULL != c) color_int = ((r) << 16) + ((g) << 8) + (b); + + // Create edge arrays using fast integer DDA + + int lclip = pb_spec->lclip; + int rclip = pb_spec->rclip; + + int m, x, dy, count; + + // Left edge + int xmax = segs[lseg].x; + int xmin = segs[lseg + 1].x; + int ymax = segs[lseg].y; + int ymin = segs[lseg + 1].y; + + if (ymax < ymin) { + int a = ymax; + ymax = ymin; + ymin = a; + + a = xmax; + xmax = xmin; + xmin = a; + } + + int y_dda_limit = wxMin(ybot, ymax); + y_dda_limit = wxMin(y_dda_limit, 1499); // don't overrun edge array + + // Some peephole optimization: + // if xmax and xmin are both < 0, arrange to simply fill the ledge array + // with 0 + if ((xmax < 0) && (xmin < 0)) { + xmax = -2; + xmin = -2; + } + // if xmax and xmin are both > rclip, arrange to simply fill the ledge + // array with rclip + 1 This may induce special clip case below, and cause + // trap not to be rendered + else if ((xmax > rclip) && (xmin > rclip)) { + xmax = rclip + 1; + xmin = rclip + 1; + } + + dy = (ymax - ymin); + if (dy) { + m = (xmax - xmin) << 16; + m /= dy; + + x = xmin << 16; + + // TODO implement this logic in dda_tri also + count = ymin; + while (count < 0) { + x += m; + count++; + } + + while (count < y_dda_limit) { + ledge[count] = x >> 16; + x += m; + count++; + } + } + + if ((ytop < ymin) || (ybot > ymax)) { + // printf ( "### ledge out of range\n" ); + ret_val = 1; + // r=255; + // g=0; + // b=0; + } + + // Right edge + xmax = segs[rseg].x; + xmin = segs[rseg + 1].x; + ymax = segs[rseg].y; + ymin = segs[rseg + 1].y; + + // Note this never gets hit??? + if (ymax < ymin) { + int a = ymax; + ymax = ymin; + ymin = a; + + a = xmax; + xmax = xmin; + xmin = a; + } + + // Some peephole optimization: + // if xmax and xmin are both < 0, arrange to simply fill the redge array + // with -1 This may induce special clip case below, and cause trap not to + // be rendered + if ((xmax < 0) && (xmin < 0)) { + xmax = -1; + xmin = -1; + } + + // if xmax and xmin are both > rclip, arrange to simply fill the redge + // array with rclip + 1 This may induce special clip case below, and cause + // trap not to be rendered + else if ((xmax > rclip) && (xmin > rclip)) { + xmax = rclip + 1; + xmin = rclip + 1; + } + + y_dda_limit = wxMin(ybot, ymax); + y_dda_limit = wxMin(y_dda_limit, 1499); // don't overrun edge array + + dy = (ymax - ymin); + if (dy) { + m = (xmax - xmin) << 16; + m /= dy; + + x = xmin << 16; + + count = ymin; + while (count < 0) { + x += m; + count++; + } + + while (count < y_dda_limit) { + redge[count] = x >> 16; + x += m; + count++; + } + } + + if ((ytop < ymin) || (ybot > ymax)) { + // printf ( "### redge out of range\n" ); + ret_val = 1; + // r=255; + // g=0; + // b=0; + } + + // Clip trapezoid to height spec + int y1 = ybot; + int y2 = ytop; + + int ybt = pb_spec->y; + int yt = pb_spec->y + pb_spec->height; + + if (y1 > yt) y1 = yt; + if (y1 < ybt) y1 = ybt; + + if (y2 > yt) y2 = yt; + if (y2 < ybt) y2 = ybt; + + // Clip the trapezoid to width + for (int iy = y2; iy <= y1; iy++) { + if (ledge[iy] < lclip) { + if (redge[iy] < lclip) + ledge[iy] = -1; + else + ledge[iy] = lclip; + } + + if (redge[iy] > rclip) { + if (ledge[iy] > rclip) + ledge[iy] = -1; + else + redge[iy] = rclip; + } + } + + // Fill the trapezoid + + int ya = y2; + int yb = y1; + + unsigned char *pix_buff = pb_spec->pix_buff; + + int patt_size_x = 0; + int patt_size_y = 0; + int patt_pitch = 0; + unsigned char *patt_s0 = NULL; + if (pPatt_spec) { + patt_size_y = pPatt_spec->height; + patt_size_x = pPatt_spec->width; + patt_pitch = pPatt_spec->pb_pitch; + patt_s0 = pPatt_spec->pix_buff; + } + + if (pb_spec->depth == 24) { + for (int iyp = ya; iyp < yb; iyp++) { + if ((iyp >= ybt) && (iyp < yt)) { + int yoff = (iyp - pb_spec->y) * pb_spec->pb_pitch; + + unsigned char *py = pix_buff + yoff; + + int ix, ixm; + ix = ledge[iyp]; + ixm = redge[iyp]; + + // if(debug) printf("iyp %d, ix %d, ixm %d\n", + // iyp, ix, ixm); + // int ix = ledge[iyp]; + // if(ix != -1) // special + // clip case + if (ledge[iyp] != -1) { + int xoff = (ix - pb_spec->x) * 3; + + unsigned char *px = py + xoff; + + if (pPatt_spec) // Pattern + { + int y_stagger = (iyp - pPatt_spec->y) / patt_size_y; + int x_stagger_off = 0; + if ((y_stagger & 1) && pPatt_spec->b_stagger) + x_stagger_off = pPatt_spec->width / 2; + + int patt_y = abs((iyp - pPatt_spec->y)) % patt_size_y; + unsigned char *pp0 = patt_s0 + (patt_y * patt_pitch); + + while (ix <= ixm) { + int patt_x = + abs(((ix - pPatt_spec->x) + x_stagger_off) % patt_size_x); + /* + if(pPatt_spec->depth == 24) + { + unsigned char *pp = pp0 + (patt_x * 3); + + // Todo This line assumes unused_color is always 0,0,0 + if( pp[0] && pp[1] && pp[2] ) { + *px++ = *pp++; + *px++ = *pp++; + *px++ = *pp++; + } else { + px += 3; + pp += 3; + } + } + else + */ + { + unsigned char *pp = pp0 + (patt_x * 4); + unsigned char alpha = pp[3]; + if (alpha > 128) { + double da = (double)alpha / 256.; + + unsigned char r = (unsigned char)(pp[0] * da); + unsigned char g = (unsigned char)(pp[1] * da); + unsigned char b = (unsigned char)(pp[2] * da); + + *px++ = r; + *px++ = g; + *px++ = b; + } else + px += 3; + } + + ix++; + } + } + + else // No Pattern + { +#if defined(__WXGTK__WITH_OPTIMIZE_0) && defined(__INTEL__) +#define memset3d(dest, value, count) \ + __asm__ __volatile__( \ + "cmp $0,%2\n\t" \ + "jg ld0\n\t" \ + "je ld1\n\t" \ + "jmp ld2\n\t" \ + "ld0:\n\t" \ + "movl %0,(%1)\n\t" \ + "add $3,%1\n\t" \ + "dec %2\n\t" \ + "jnz ld0\n\t" \ + "ld1:\n\t" \ + "movb %b0,(%1)\n\t" \ + "inc %1\n\t" \ + "movb %h0,(%1)\n\t" \ + "inc %1\n\t" \ + "shr $16,%0\n\t" \ + "movb %b0,(%1)\n\t" \ + "ld2:\n\t" \ + : \ + : "a"(value), "D"(dest), "r"(count) \ + :); + int count = ixm - ix; + memset3d(px, color_int, count) +#else + + while (ix <= ixm) { + *px++ = b; + *px++ = g; + *px++ = r; + + ix++; + } +#endif + } + } + } + } + } + + if (pb_spec->depth == 32) { + assert(ya <= yb); + + for (int iyp = ya; iyp < yb; iyp++) { + if ((iyp >= ybt) && (iyp < yt)) { + int yoff = (iyp - pb_spec->y) * pb_spec->pb_pitch; + + unsigned char *py = pix_buff + yoff; + + int ix, ixm; + ix = ledge[iyp]; + ixm = redge[iyp]; + + if (ledge[iyp] != -1) { + int xoff = (ix - pb_spec->x) * pb_spec->depth / 8; + + unsigned char *px = py + xoff; + + if (pPatt_spec) // Pattern + { + int y_stagger = (iyp - pPatt_spec->y) / patt_size_y; + int x_stagger_off = 0; + if ((y_stagger & 1) && pPatt_spec->b_stagger) + x_stagger_off = pPatt_spec->width / 2; + + int patt_y = abs((iyp - pPatt_spec->y)) % patt_size_y; + unsigned char *pp0 = patt_s0 + (patt_y * patt_pitch); + + while (ix <= ixm) { + int patt_x = + abs(((ix - pPatt_spec->x) + x_stagger_off) % patt_size_x); + /* + if(pPatt_spec->depth == 24) + { + unsigned char *pp = pp0 + (patt_x * 3); + + // Todo This line assumes unused_color is always 0,0,0 + if( pp[0] && pp[1] && pp[2] ) { + *px++ = *pp++; + *px++ = *pp++; + *px++ = *pp++; + px++; + } else { + px += 4; + // pp += 3; + } + } + else + */ + { + unsigned char *pp = pp0 + (patt_x * 4); + unsigned char alpha = pp[3]; + if (alpha > 128) { + double da = (double)alpha / 256.; + + unsigned char r = (unsigned char)(pp[0] * da); + unsigned char g = (unsigned char)(pp[1] * da); + unsigned char b = (unsigned char)(pp[2] * da); + + *px++ = r; + *px++ = g; + *px++ = b; + px++; + } else + px += 4; + } + ix++; + } + } + + else // No Pattern + { + int *pxi = (int *)px; + while (ix <= ixm) { + *pxi++ = color_int; + ix++; + } + } + } + } + } + } + + return ret_val; +} + +void s52plib::RenderToBufferFilledPolygon(ObjRazRules *rzRules, S57Obj *obj, + S52color *c, + render_canvas_parms *pb_spec, + render_canvas_parms *pPatt_spec) { + LLBBox BBView = GetBBox(); + // please untangle this logic with the logic below + if (BBView.GetMaxLon() + 180 < vp_plib.clon) + BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() + 360, BBView.GetMaxLat(), + BBView.GetMaxLon() + 360); + else if (BBView.GetMinLon() - 180 > vp_plib.clon) + BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() - 360, BBView.GetMaxLat(), + BBView.GetMaxLon() - 360); + + S52color cp; + if (NULL != c) { + cp.R = c->R; + cp.G = c->G; + cp.B = c->B; + } + + if (obj->pPolyTessGeo) { + if (!rzRules->obj->pPolyTessGeo->IsOk()) { // perform deferred tesselation + rzRules->obj->pPolyTessGeo->BuildDeferredTess(); + } + + wxPoint *pp3 = (wxPoint *)malloc(3 * sizeof(wxPoint)); + wxPoint *ptp = (wxPoint *)malloc((obj->pPolyTessGeo->GetnVertexMax() + 1) * + sizeof(wxPoint)); + + PolyTriGroup *ppg = obj->pPolyTessGeo->Get_PolyTriGroup_head(); + + TriPrim *p_tp = ppg->tri_prim_head; + while (p_tp) { + LLBBox box; + if (!rzRules->obj->m_chart_context->chart) { // This is a PlugIn Chart + LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp; + box.Set(p_ltp->miny, p_ltp->minx, p_ltp->maxy, p_ltp->maxx); + } else + box = p_tp->tri_box; + + if (!BBView.IntersectOut(box)) { + // Get and convert the points + wxPoint *pr = ptp; + + if (ppg->data_type == DATA_TYPE_DOUBLE) { + double *pvert_list = p_tp->p_vertex; + + for (int iv = 0; iv < p_tp->nVert; iv++) { + double lon = *pvert_list++; + double lat = *pvert_list++; + GetPointPixSingle(rzRules, lat, lon, pr); + + pr++; + } + } else { + float *pvert_list = (float *)p_tp->p_vertex; + + for (int iv = 0; iv < p_tp->nVert; iv++) { + double lon = *pvert_list++; + double lat = *pvert_list++; + GetPointPixSingle(rzRules, lat, lon, pr); + + pr++; + } + } + + switch (p_tp->type) { + case PTG_TRIANGLE_FAN: { + for (int it = 0; it < p_tp->nVert - 2; it++) { + pp3[0].x = ptp[0].x; + pp3[0].y = ptp[0].y; + + pp3[1].x = ptp[it + 1].x; + pp3[1].y = ptp[it + 1].y; + + pp3[2].x = ptp[it + 2].x; + pp3[2].y = ptp[it + 2].y; + + dda_tri(pp3, &cp, pb_spec, pPatt_spec); + } + break; + } + case PTG_TRIANGLE_STRIP: { + for (int it = 0; it < p_tp->nVert - 2; it++) { + pp3[0].x = ptp[it].x; + pp3[0].y = ptp[it].y; + + pp3[1].x = ptp[it + 1].x; + pp3[1].y = ptp[it + 1].y; + + pp3[2].x = ptp[it + 2].x; + pp3[2].y = ptp[it + 2].y; + + dda_tri(pp3, &cp, pb_spec, pPatt_spec); + } + break; + } + case PTG_TRIANGLES: { + for (int it = 0; it < p_tp->nVert; it += 3) { + pp3[0].x = ptp[it].x; + pp3[0].y = ptp[it].y; + + pp3[1].x = ptp[it + 1].x; + pp3[1].y = ptp[it + 1].y; + + pp3[2].x = ptp[it + 2].x; + pp3[2].y = ptp[it + 2].y; + + dda_tri(pp3, &cp, pb_spec, pPatt_spec); + } + break; + } + } + } // if bbox + + // pick up the next in chain + if (!rzRules->obj->m_chart_context->chart) { // This is a PlugIn Chart + LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp; + p_tp = (TriPrim *)p_ltp->p_next; + } else + p_tp = p_tp->p_next; + + } // while + + free(ptp); + free(pp3); + } // if pPolyTessGeo +} + +#ifdef ocpnUSE_GL +int s52plib::RenderToGLAC(ObjRazRules *rzRules, Rules *rules) { + return RenderToGLAC_GLSL(rzRules, rules); +} +#endif + +int n_areaObjs; +int n_areaTris; + +#ifdef ocpnUSE_GL +int s52plib::RenderToGLAC_GLSL(ObjRazRules *rzRules, Rules *rules) { + if (!ObjectRenderCheckPosReduced(rzRules)) return false; + + n_areaObjs++; + + GLenum reset_err = glGetError(); + + S52color *c; + char *str = (char *)rules->INSTstr; + + c = getColor(str); + + LLBBox BBView = GetBBox(); + // please untangle this logic with the logic below + if (BBView.GetMaxLon() + 180 < vp_plib.clon) + BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() + 360, BBView.GetMaxLat(), + BBView.GetMaxLon() + 360); + else if (BBView.GetMinLon() - 180 > vp_plib.clon) + BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() - 360, BBView.GetMaxLat(), + BBView.GetMaxLon() - 360); + + // Allow a little slop in calculating whether a triangle + // is within the requested VPointCompat + double margin = BBView.GetLonRange() * .05; + BBView.EnLarge(margin); + + // Use VBO if instructed by hardware renderer specification + bool b_useVBO = m_GLAC_VBO && !rzRules->obj->auxParm1; + + if (rzRules->obj->pPolyTessGeo) { + bool b_temp_vbo = false; + bool b_transform = false; + + // perform deferred tesselation + if (!rzRules->obj->pPolyTessGeo->IsOk()) { + rzRules->obj->pPolyTessGeo->BuildDeferredTess(); + } + + // Get the vertex data + PolyTriGroup *ppg_vbo = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head(); + + // Has the input vertex buffer been converted to "single_alloc float" + // model? and is it allowed? + if (!ppg_vbo->bsingle_alloc && (rzRules->obj->auxParm1 >= 0)) { + int data_size = sizeof(float); + + // First calculate the required total byte size + int total_byte_size = 0; + TriPrim *p_tp = ppg_vbo->tri_prim_head; + while (p_tp) { + total_byte_size += p_tp->nVert * 2 * data_size; + p_tp = p_tp->p_next; // pick up the next in chain + } + + float *vbuf = (float *)malloc(total_byte_size); + p_tp = ppg_vbo->tri_prim_head; + + if (ppg_vbo->data_type == DATA_TYPE_DOUBLE) { // DOUBLE to FLOAT + float *p_run = vbuf; + while (p_tp) { + float *pfbuf = p_run; + for (int i = 0; i < p_tp->nVert * 2; ++i) { + float x = (float)(p_tp->p_vertex[i]); + *p_run++ = x; + } + + free(p_tp->p_vertex); + p_tp->p_vertex = (double *)pfbuf; + + p_tp = p_tp->p_next; // pick up the next in chain + } + } else { // FLOAT to FLOAT + float *p_run = vbuf; + while (p_tp) { + memcpy(p_run, p_tp->p_vertex, p_tp->nVert * 2 * sizeof(float)); + + free(p_tp->p_vertex); + p_tp->p_vertex = (double *)p_run; + + p_run += p_tp->nVert * 2 * sizeof(float); + + p_tp = p_tp->p_next; // pick up the next in chain + } + } + + ppg_vbo->bsingle_alloc = true; + ppg_vbo->single_buffer = (unsigned char *)vbuf; + ppg_vbo->single_buffer_size = total_byte_size; + ppg_vbo->data_type = DATA_TYPE_FLOAT; + } + + if (b_useVBO) { + // Has a VBO been built for this object? + if (1) { + glGetError(); // clear it + + if (rzRules->obj->auxParm0 <= 0) { +#ifdef xUSE_ANDROID_GLES2 + if (ppg_vbo->data_type != DATA_TYPE_SHORT) { + // We convert the vertex data from FLOAT to GL_SHORT to make the VBO + // smaller, but still keeping enough precision + // This requires a scale factor to reduce the range from existing + // data to +/- 32K + + size_t np = ppg_vbo->single_buffer_size / (2 * sizeof(float)); + np--; + + PolyTriGroup *ppg = + rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head(); + TriPrim *p_tp = ppg->tri_prim_head; + + size_t npp = 0; + while (p_tp) { + npp += p_tp->nVert; + p_tp = (TriPrim *)p_tp->p_next; + } + + // Get the data range + float *pRun = (float *)ppg_vbo->single_buffer; + float north_max = -1e8; + float north_min = 1e8; + float east_max = -1e8; + float east_min = 1e8; + + for (size_t i = 0; i < np; i++) { + float east = *pRun++; + float north = *pRun++; + north_max = wxMax(north, north_max); + north_min = wxMin(north, north_min); + east_max = wxMax(east, east_max); + east_min = wxMin(east, east_min); + } + + float cfactx = wxMax(fabs(east_max), fabs(east_min)); + float cfacty = wxMax(fabs(north_max), fabs(north_min)); + float cfact = wxMax(cfactx, cfacty); + + float sfact = cfact / 32700.0; + + sfact = wxMax(sfact, 1.0); + + // Copy/convert the data + unsigned char *new_buf = + (unsigned char *)malloc(np * 2 * sizeof(short)); + pRun = (float *)ppg_vbo->single_buffer; + short *pd = (short *)new_buf; + for (size_t i = 0; i < np; i++) { + float east = *pRun++; + float north = *pRun++; + // short a = (east / sfact); + // short b = (north / sfact); + *pd++ = (east / sfact); + *pd++ = (north / sfact); + } + + // replace the buffer + free(ppg_vbo->single_buffer); + ppg_vbo->single_buffer = new_buf; + ppg_vbo->single_buffer_size /= 2; + + // Record the scale/offset factors + ppg_vbo->sfactor = sfact; + ppg_vbo->soffset = 0.; + + ppg_vbo->data_type = DATA_TYPE_SHORT; + } + +#endif + b_temp_vbo = (rzRules->obj->auxParm0 == + -5); // Must we use a temporary VBO? Probably slower + // than simple glDrawArrays + + GLuint vboId = 0; + // generate a new VBO and get the associated ID + glGenBuffers(1, &vboId); + + rzRules->obj->auxParm0 = vboId; + + // bind VBO in order to use + glBindBuffer(GL_ARRAY_BUFFER, vboId); + GLenum err = glGetError(); + if (err) { + wxString msg; + msg.Printf(_T("VBO Error A: %d"), err); + wxLogMessage(msg); + return 0; + } + + // upload data to VBO + glBufferData(GL_ARRAY_BUFFER, ppg_vbo->single_buffer_size, + ppg_vbo->single_buffer, GL_STATIC_DRAW); + err = glGetError(); + if (err) { + wxString msg; + msg.Printf(_T("VBO Error B: %d"), err); + wxLogMessage(msg); + return 0; + } + + } else { + glBindBuffer(GL_ARRAY_BUFFER, rzRules->obj->auxParm0); + GLenum err = glGetError(); + if (err) { + wxString msg; + msg.Printf(_T("VBO Error C: %d"), err); + wxLogMessage(msg); + return 0; + } + } + } + } + + PolyTriGroup *ppg = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head(); + + TriPrim *p_tp = ppg->tri_prim_head; + GLintptr vbo_offset = 0; + + // Set up the stride sizes for the array + int array_data_size = sizeof(float); + GLint array_gl_type = GL_FLOAT; + + if (ppg->data_type == DATA_TYPE_DOUBLE) { + array_data_size = sizeof(double); + array_gl_type = GL_DOUBLE; + } + + if (ppg->data_type == DATA_TYPE_SHORT) { + array_data_size = sizeof(short); + array_gl_type = GL_SHORT; + } + + CGLShaderProgram *shader = + pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/]; + shader->Bind(); + + // Disable VBO's (vertex buffer objects) for attributes. + if (!b_useVBO) glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + GLint pos = shader->getAttributeLocation("position"); + glEnableVertexAttribArray(pos); + + float angle = 0; + + // Build Transform matrix + mat4x4 I, Q; + mat4x4_identity(I); + + // Scale + I[0][0] *= rzRules->obj->x_rate; + I[1][1] *= rzRules->obj->y_rate; + + // Translate + float x_origin = rzRules->obj->x_origin; + + if (rzRules->obj->m_chart_context->chart) { // not a PlugIn Chart + if (((int)rzRules->obj->m_chart_context->chart_type == + (int)S52_ChartTypeEnum::S52_CHART_TYPE_CM93) || + ((int)rzRules->obj->m_chart_context->chart_type == + (int)S52_ChartTypeEnum::S52_CHART_TYPE_CM93COMP)) { + // We may need to translate object coordinates by 360 degrees to + // conform. + if (BBView.GetMaxLon() >= 180.) { + if (rzRules->obj->BBObj.GetMinLon() < BBView.GetMaxLon() - 360.) + x_origin += + (float)(mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI); + } else if ((BBView.GetMinLon() <= -180. && + rzRules->obj->BBObj.GetMaxLon() > + BBView.GetMinLon() + 360.) || + (rzRules->obj->BBObj.GetMaxLon() > 180 && + BBView.GetMinLon() + 360 < rzRules->obj->BBObj.GetMaxLon())) + x_origin -= + (float)(mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI); + } + } + + double ppg_scale_factor = 1.0; + if (rzRules->obj->m_chart_context->chart) // This is not a plugin chart + ppg_scale_factor = ppg->sfactor; + + I[3][0] = -(rzRules->sm_transform_parms->easting_vp_center - x_origin) * + vp_plib.view_scale_ppm; + I[3][1] = -(rzRules->sm_transform_parms->northing_vp_center - + rzRules->obj->y_origin) * + -vp_plib.view_scale_ppm; + + // Scale + I[0][0] *= vp_plib.view_scale_ppm * ppg_scale_factor; + I[1][1] *= -vp_plib.view_scale_ppm * ppg_scale_factor; + + // Rotate + mat4x4_rotate_Z(Q, I, angle); + + // Translate + Q[3][0] += vp_plib.pix_width / 2; + Q[3][1] += vp_plib.pix_height / 2; + + shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)Q); + + float colorv[4]; + colorv[0] = c->R / float(256); + colorv[1] = c->G / float(256); + colorv[2] = c->B / float(256); + colorv[3] = 1.0; + + shader->SetUniform4fv("color", colorv); + + if (b_useVBO) { + glBindBuffer(GL_ARRAY_BUFFER, rzRules->obj->auxParm0); + glVertexAttribPointer(pos, 2, array_gl_type, GL_FALSE, 0, (GLvoid *)(0)); + } else + glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 0, + (GLvoid *)(ppg->single_buffer)); + int VBO_offset_index = 0; + + while (p_tp) { + LLBBox box; + if (!rzRules->obj->m_chart_context->chart) { // This is s63 PlugIn Chart + LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp; + box.Set(p_ltp->miny, p_ltp->minx, p_ltp->maxy, p_ltp->maxx); + } else + box = p_tp->tri_box; + + if (!BBView.IntersectOut(box)) { + glDrawArrays(p_tp->type, VBO_offset_index, p_tp->nVert); + } + + VBO_offset_index += p_tp->nVert; + + // pick up the next in chain + if (!rzRules->obj->m_chart_context->chart) { // This is a PlugIn Chart + LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp; + p_tp = (TriPrim *)p_ltp->p_next; + } else + p_tp = p_tp->p_next; + + } // while + + if (b_useVBO) glBindBuffer(GL_ARRAY_BUFFER, 0); + + mat4x4 IM; + mat4x4_identity(IM); + shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)IM); + + glDisableVertexAttribArray(pos); + shader->UnBind(); + + if (b_useVBO && b_temp_vbo) { + glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STATIC_DRAW); + glDeleteBuffers(1, (unsigned int *)&rzRules->obj->auxParm0); + rzRules->obj->auxParm0 = 0; + } + } // if pPolyTessGeo + + return 1; +} +#endif + +void s52plib::SetGLClipRect(const VPointCompat &vp, const wxRect &rect) {} + +void RotateToViewPort(const VPointCompat &vp) { +#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL) + bool g_bskew_comp = true; + + float angle = vp.rotation; + // if (g_bskew_comp) angle -= vp.skew; + + if (fabs(angle) > 0.0001) { + // Rotations occur around 0,0, so translate to rotate around screen + // center + float xt = vp.pix_width / 2.0, yt = vp.pix_height / 2.0; + + glTranslatef(xt, yt, 0); + glRotatef(angle * 180. / PI, 0, 0, 1); + glTranslatef(-xt, -yt, 0); + } +#endif +} + +int s52plib::RenderToGLAP(ObjRazRules *rzRules, Rules *rules) { +#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL) + return RenderToGLAP_GLSL(rzRules, rules); +#else + return 1; +#endif // #ifdef ocpnUSE_GLSL +} +#ifdef ocpnUSE_GL +int s52plib::RenderToGLAP_GLSL(ObjRazRules *rzRules, Rules *rules) { + if (!ObjectRenderCheckPosReduced(rzRules)) return false; + + // Get the pattern definition + if ((rules->razRule->pixelPtr == NULL) || + (rules->razRule->parm1 != m_colortable_index) || + (rules->razRule->parm0 != ID_GL_PATT_SPEC)) { + render_canvas_parms *patt_spec = + CreatePatternBufferSpec(rzRules, rules, false, true); + + ClearRulesCache( + rules->razRule); // Clear out any existing cached symbology + + rules->razRule->pixelPtr = patt_spec; + rules->razRule->parm1 = m_colortable_index; + rules->razRule->parm0 = ID_GL_PATT_SPEC; + } + + // Render the Area using the pattern spec stored in the rules + render_canvas_parms *ppatt_spec = + (render_canvas_parms *)rules->razRule->pixelPtr; + + // Has the pattern been uploaded as a texture? + if (!ppatt_spec->OGL_tex_name) { + GLuint tex_name; + glGenTextures(1, &tex_name); + ppatt_spec->OGL_tex_name = tex_name; + + glBindTexture(GL_TEXTURE_2D, tex_name); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ppatt_spec->w_pot, + ppatt_spec->h_pot, 0, GL_RGBA, GL_UNSIGNED_BYTE, + ppatt_spec->pix_buff); + } + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, ppatt_spec->OGL_tex_name); + + glEnable(GL_BLEND); + // glBlendFunc(GL_SRC_COLOR, GL_ZERO); + + int textureWidth = ppatt_spec->width; // ppatt_spec->w_pot; + int textureHeight = ppatt_spec->height; // ppatt_spec->h_pot; + + wxPoint pr; + GetPointPixSingle(rzRules, rzRules->obj->m_lat, rzRules->obj->m_lat, &pr); + float xOff = pr.x; + float yOff = pr.y; + + LLBBox BBView = GetReducedBBox(); + // please untangle this logic with the logic below + if (BBView.GetMaxLon() + 180 < vp_plib.clon) + BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() + 360, BBView.GetMaxLat(), + BBView.GetMaxLon() + 360); + else if (BBView.GetMinLon() - 180 > vp_plib.clon) + BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() - 360, BBView.GetMaxLat(), + BBView.GetMaxLon() - 360); + + // Allow a little slop in calculating whether a triangle + // is within the requested VPointCompat + double margin = BBView.GetLonRange() * .05; + BBView.EnLarge(margin); + + bool b_useVBO = m_GLAC_VBO && !rzRules->obj->auxParm1; + + if (rzRules->obj->pPolyTessGeo) { + // bool b_temp_vbo = false; + // bool b_transform = false; + + // perform deferred tesselation + if (!rzRules->obj->pPolyTessGeo->IsOk()) + rzRules->obj->pPolyTessGeo->BuildDeferredTess(); + + // Get the vertex data + PolyTriGroup *ppg_vbo = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head(); + + // Has the input vertex buffer been converted to "single_alloc float" + // model? and is it allowed? + if (!ppg_vbo->bsingle_alloc && (rzRules->obj->auxParm1 >= 0)) { + int data_size = sizeof(float); + + // First calculate the required total byte size + int total_byte_size = 0; + TriPrim *p_tp = ppg_vbo->tri_prim_head; + while (p_tp) { + total_byte_size += p_tp->nVert * 2 * data_size; + p_tp = p_tp->p_next; // pick up the next in chain + } + + float *vbuf = (float *)malloc(total_byte_size); + p_tp = ppg_vbo->tri_prim_head; + + if (ppg_vbo->data_type == DATA_TYPE_DOUBLE) { // DOUBLE to FLOAT + float *p_run = vbuf; + while (p_tp) { + float *pfbuf = p_run; + for (int i = 0; i < p_tp->nVert * 2; ++i) { + float x = (float)(p_tp->p_vertex[i]); + *p_run++ = x; + } + + free(p_tp->p_vertex); + p_tp->p_vertex = (double *)pfbuf; + + p_tp = p_tp->p_next; // pick up the next in chain + } + } else { // FLOAT to FLOAT + float *p_run = vbuf; + while (p_tp) { + memcpy(p_run, p_tp->p_vertex, p_tp->nVert * 2 * sizeof(float)); + + free(p_tp->p_vertex); + p_tp->p_vertex = (double *)p_run; + + p_run += p_tp->nVert * 2 * sizeof(float); + + p_tp = p_tp->p_next; // pick up the next in chain + } + } + + ppg_vbo->bsingle_alloc = true; + ppg_vbo->single_buffer = (unsigned char *)vbuf; + ppg_vbo->single_buffer_size = total_byte_size; + ppg_vbo->data_type = DATA_TYPE_FLOAT; + } + + if (b_useVBO) { + // Has a VBO been built for this object? + if (1) { + if (rzRules->obj->auxParm0 <= 0) { + GLuint vboId; + // generate a new VBO and get the associated ID + glGenBuffers(1, &vboId); + + rzRules->obj->auxParm0 = vboId; + + // bind VBO in order to use + glBindBuffer(GL_ARRAY_BUFFER, vboId); + + // upload data to VBO + glBufferData(GL_ARRAY_BUFFER, ppg_vbo->single_buffer_size, + ppg_vbo->single_buffer, GL_STATIC_DRAW); + + } else { + glBindBuffer(GL_ARRAY_BUFFER, rzRules->obj->auxParm0); + } + } + } + + PolyTriGroup *ppg = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head(); + double ppg_scale_factor = 1.0; + if (rzRules->obj->m_chart_context->chart) // This is not a plugin chart + ppg_scale_factor = ppg->sfactor; + + TriPrim *p_tp = ppg->tri_prim_head; + GLintptr vbo_offset = 0; + + // Set up the stride sizes for the array + int array_data_size = sizeof(float); + GLint array_gl_type = GL_FLOAT; + + if (ppg->data_type == DATA_TYPE_DOUBLE) { + array_data_size = sizeof(double); + array_gl_type = GL_DOUBLE; + } + + if (ppg->data_type == DATA_TYPE_SHORT) { + array_data_size = sizeof(short); + array_gl_type = GL_SHORT; + } + + GLint program = S52AP_shader_program; + glUseProgram(program); + + // Disable VBO's (vertex buffer objects) for attributes. + if (!b_useVBO) glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + GLint pos = glGetAttribLocation(program, "position"); + glEnableVertexAttribArray(pos); + + // Select the active texture unit. + glActiveTexture(GL_TEXTURE0); + + // Bind our texture to the texturing target. + glBindTexture(GL_TEXTURE_2D, ppatt_spec->OGL_tex_name); + + // Set up the texture sampler to texture unit 0 + GLint texUni = glGetUniformLocation(program, "uTex"); + glUniform1i(texUni, 0); + + GLint texWidth = glGetUniformLocation(program, "texWidth"); + GLint texHeight = glGetUniformLocation(program, "texHeight"); + glUniform1f(texWidth, textureWidth); + glUniform1f(texHeight, textureHeight); + + GLint texPOTWidth = glGetUniformLocation(program, "texPOTWidth"); + GLint texPOTHeight = glGetUniformLocation(program, "texPOTHeight"); + glUniform1f(texPOTWidth, ppatt_spec->w_pot); + glUniform1f(texPOTHeight, ppatt_spec->h_pot); + + GLint xo = glGetUniformLocation(program, "xOff"); + GLint yo = glGetUniformLocation(program, "yOff"); + + glUniform1f(xo, fmod(xOff, ppatt_spec->w_pot)); + glUniform1f(yo, fmod(yOff, ppatt_spec->h_pot)); + + GLint yom = glGetUniformLocation(program, "yOffM"); + glUniform1f(yom, yOff); + + if (ppatt_spec->b_stagger) { + GLint staggerFact = glGetUniformLocation(program, "staggerFactor"); + glUniform1f(staggerFact, 0.5); + } + + float angle = 0; + + // Build Transform matrix + mat4x4 I, Q; + mat4x4_identity(I); + + // Scale + I[0][0] *= rzRules->obj->x_rate; + I[1][1] *= rzRules->obj->y_rate; + + // Translate + I[3][0] = -(rzRules->sm_transform_parms->easting_vp_center - + rzRules->obj->x_origin) * + vp_plib.view_scale_ppm; + I[3][1] = -(rzRules->sm_transform_parms->northing_vp_center - + rzRules->obj->y_origin) * + -vp_plib.view_scale_ppm; + + // Scale + I[0][0] *= vp_plib.view_scale_ppm * ppg_scale_factor; + I[1][1] *= -vp_plib.view_scale_ppm * ppg_scale_factor; + + // Rotate + mat4x4_rotate_Z(Q, I, angle); + + // Translate + Q[3][0] += vp_plib.pix_width / 2; + Q[3][1] += vp_plib.pix_height / 2; + + GLint matloc = glGetUniformLocation(program, "TransformMatrix"); + glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q); + + if (!b_useVBO) { + float *bufBase = (float *)(&ppg->single_buffer[vbo_offset]); + glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), + bufBase); + } + + while (p_tp) { + LLBBox box; + if (!rzRules->obj->m_chart_context->chart) { // This is a PlugIn Chart + LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp; + box.Set(p_ltp->miny, p_ltp->minx, p_ltp->maxy, p_ltp->maxx); + } else + box = p_tp->tri_box; + + if (!BBView.IntersectOut(box)) { + if (b_useVBO) { + glVertexAttribPointer(pos, 2, array_gl_type, GL_FALSE, + 0 /*2*sizeof(array_gl_type)*/, + (GLvoid *)(vbo_offset)); + glDrawArrays(p_tp->type, 0, p_tp->nVert); + } else { + glDrawArrays(p_tp->type, vbo_offset / (2 * array_data_size), + p_tp->nVert); + } + } + + vbo_offset += p_tp->nVert * 2 * array_data_size; + + // pick up the next in chain + if (!rzRules->obj->m_chart_context->chart) { // This is a PlugIn Chart + LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp; + p_tp = (TriPrim *)p_ltp->p_next; + } else + p_tp = p_tp->p_next; + + } // while + + if (b_useVBO) glBindBuffer(GL_ARRAY_BUFFER_ARB, 0); + + // Restore the transform matrix to identity + mat4x4 IM; + mat4x4_identity(IM); + GLint matlocf = + glGetUniformLocation(S52color_tri_shader_program, "TransformMatrix"); + glUniformMatrix4fv(matlocf, 1, GL_FALSE, (const GLfloat *)IM); + glUseProgram(0); + glDisableVertexAttribArray(pos); + + } // if pPolyTessGeo + + glDisable(GL_TEXTURE_2D); + + return 1; +} +#endif + +#ifdef ocpnUSE_GL + +int s52plib::RenderAreaToGL(const wxGLContext &glcc, ObjRazRules *rzRules) { + if (!ObjectRenderCheckRules(rzRules, true)) return 0; + +#ifdef __OCPN__ANDROID__ + // Catch a difficult to trap SIGSEGV, avoiding app crash + auto rzRules_current = rzRules; // Save current rules pointer + sigaction(SIGSEGV, NULL, + &sa_all_plib_previous); // save existing action for this signal + + struct sigaction temp; + sigaction(SIGSEGV, NULL, + &temp); // inspect existing action for this signal + + temp.sa_handler = catch_signals_plib; // point to my handler + sigemptyset(&temp.sa_mask); // make the blocking set + // empty, so that all + // other signals will be + // unblocked during my handler + temp.sa_flags = 0; + sigaction(SIGSEGV, &temp, NULL); + + if (sigsetjmp(env_plib, 1)) // Something in the + // code block below this on caused SIGSEGV... + { + // reset signal handler + sigaction(SIGSEGV, &sa_all_plib_previous, NULL); + rzRules_current->obj->bOK_to_Render = false; //Mark object unrenderable + return 0; + } else +#endif + { + Rules *rules = rzRules->LUP->ruleList; + + while (rules != NULL) { + switch (rules->ruleType) { + case RUL_ARE_CO: + RenderToGLAC(rzRules, rules); + break; // AC + + case RUL_ARE_PA: + RenderToGLAP(rzRules, rules); + break; // AP + + case RUL_CND_SY: { + { + if (!rzRules->obj->bCS_Added) { + rzRules->obj->CSrules = NULL; + GetAndAddCSRules(rzRules, rules); + rzRules->obj->bCS_Added = 1; // mark the object + } + Rules *rules_last = rules; + rules = rzRules->obj->CSrules; + + while (NULL != rules) { + switch (rules->ruleType) { + case RUL_ARE_CO: + RenderToGLAC(rzRules, rules); + break; + case RUL_ARE_PA: + RenderToGLAP(rzRules, rules); + break; + case RUL_NONE: + default: + break; // no rule type (init) + } + rules_last = rules; + rules = rules->next; + } + + rules = rules_last; + break; + } + } + + case RUL_NONE: + default: + break; // no rule type (init) + } // switch + + rules = rules->next; + } + } +#ifdef __OCPN__ANDROID__ + // reset signal handler + sigaction(SIGSEGV, &sa_all_plib_previous, NULL); +#endif + + return 1; +} +#endif + +render_canvas_parms *s52plib::CreatePatternBufferSpec(ObjRazRules *rzRules, + Rules *rules, + bool b_revrgb, + bool b_pot) { + wxImage Image; + + Rule *prule = rules->razRule; + + bool bstagger_pattern = (prule->fillType.PATP == 'S'); + + wxColour local_unused_wxColor = m_unused_wxColor; + + // Create a wxImage of the pattern drawn on an "unused_color" field + if (prule->definition.SYDF == 'R') { + Image = m_chartSymbols.GetImage(prule->name.PANM); + } + + else // Vector + { + float fsf = 100 / canvas_pix_per_mm; + + // Base bounding box + BoundingBox box( + prule->pos.patt.bnbox_x.PBXC, prule->pos.patt.bnbox_y.PBXR, + prule->pos.patt.bnbox_x.PBXC + prule->pos.patt.bnbox_w.PAHL, + prule->pos.patt.bnbox_y.PBXR + prule->pos.patt.bnbox_h.PAVL); + + // Expand to include pivot + box.Expand(prule->pos.patt.pivot_x.PACL, prule->pos.patt.pivot_y.PARW); + + // Pattern bounding boxes may be offset from origin, to preset the + // spacing So, the bitmap must be delta based. + double dwidth = box.GetWidth(); + double dheight = box.GetHeight(); + + // Add in the pattern spacing parameters + dwidth += prule->pos.patt.minDist.PAMI; + dheight += prule->pos.patt.minDist.PAMI; + + // Prescale + dwidth /= fsf; + dheight /= fsf; + + int width = (int)dwidth + 1; + int height = (int)dheight + 1; + + // float render_scale = 1.0; +#ifdef sUSE_ANDROID_GLES2 + int width_pot = width; + int height_pot = height; + if (b_pot) { + int xp = width; + if (((xp != 0) && !(xp & (xp - 1)))) // detect POT + width_pot = xp; + else { + int a = 0; + while (xp) { + xp = xp >> 1; + a++; + } + width_pot = 1 << a; + } + + xp = height; + if (((xp != 0) && !(xp & (xp - 1)))) + height_pot = xp; + else { + int a = 0; + while (xp) { + xp = xp >> 1; + a++; + } + height_pot = 1 << a; + } + } + + // adjust scaler + render_scale = (float)height_pot / (float)height; + qDebug() << "first" << width << width_pot << height << height_pot + << render_scale; + + width = width_pot; + height = height_pot; +#endif + + // Instantiate the vector pattern to a wxBitmap + wxMemoryDC mdc; + +// TODO +// This ought to work for wxOSX, but DOES NOT. +// We we do not want anti-aliased lines drawn in the pattern spec, since we used +// the solid primary color as a mask for manual blitting from the dc to memory +// buffer. + +// Best we can do is set the background color very dark, and hope for the best +#ifdef __WXOSX__ +#if wxUSE_GRAPHICS_CONTEXT + wxGraphicsContext *pgc = mdc.GetGraphicsContext(); + if (pgc) pgc->SetAntialiasMode(wxANTIALIAS_NONE); +#endif + local_unused_wxColor.Set(2, 2, 2); +#endif + + wxBitmap *pbm = NULL; + + if ((0 != width) && (0 != height)) { + pbm = new wxBitmap(width, height); + + mdc.SelectObject(*pbm); + mdc.SetBackground(wxBrush(local_unused_wxColor)); + mdc.Clear(); + + int pivot_x = prule->pos.patt.pivot_x.PACL; + int pivot_y = prule->pos.patt.pivot_y.PARW; + + char *str = prule->vector.LVCT; + char *col = prule->colRef.LCRF; + wxPoint pivot(pivot_x, pivot_y); + + int origin_x = prule->pos.patt.bnbox_x.PBXC; + int origin_y = prule->pos.patt.bnbox_y.PBXR; + wxPoint origin(origin_x, origin_y); + + wxPoint2DDouble r0(((pivot_x - box.GetMinX()) / fsf) + 1, + ((pivot_y - box.GetMinY()) / fsf) + 1); + + HPGL->SetTargetDC(&mdc); + HPGL->SetVP(&vp_plib); + HPGL->Render(str, col, r0, pivot, origin, 1.0 /*render_scale*/, 0, false); + + // mdc.SetPen( wxPen( wxColor(0, 0, 250), 1, + // wxPENSTYLE_SOLID ) ); + // mdc.SetBrush(*wxTRANSPARENT_BRUSH); + // mdc.DrawRectangle(0,0, width-1, height-1); + + } else { + pbm = new wxBitmap(2, 2); // substitute small, blank pattern + mdc.SelectObject(*pbm); + mdc.SetBackground(wxBrush(local_unused_wxColor)); + mdc.Clear(); + } + + mdc.SelectObject(wxNullBitmap); + + // Build a wxImage from the wxBitmap + Image = pbm->ConvertToImage(); + + delete pbm; + } + + // Convert the wxImage to a populated render_canvas_parms struct + + int sizey = Image.GetHeight(); + int sizex = Image.GetWidth(); + + render_canvas_parms *patt_spec = new render_canvas_parms; + patt_spec->OGL_tex_name = 0; + + if (b_pot) { + int xp = sizex; + if (((xp != 0) && !(xp & (xp - 1)))) // detect POT + patt_spec->w_pot = sizex; + else { + int a = 0; + while (xp) { + xp = xp >> 1; + a++; + } + patt_spec->w_pot = 1 << a; + } + + xp = sizey; + if (((xp != 0) && !(xp & (xp - 1)))) + patt_spec->h_pot = sizey; + else { + int a = 0; + while (xp) { + xp = xp >> 1; + a++; + } + patt_spec->h_pot = 1 << a; + } + + } else { + patt_spec->w_pot = sizex; + patt_spec->h_pot = sizey; + } + + patt_spec->depth = 32; // set the depth, always 32 bit + + patt_spec->pb_pitch = ((patt_spec->w_pot * patt_spec->depth / 8)); + patt_spec->lclip = 0; + patt_spec->rclip = patt_spec->w_pot - 1; + patt_spec->pix_buff = + (unsigned char *)malloc(patt_spec->h_pot * patt_spec->pb_pitch); + + // Preset background + memset(patt_spec->pix_buff, 0, patt_spec->h_pot * patt_spec->pb_pitch); + patt_spec->width = sizex; + patt_spec->height = sizey; + patt_spec->x = 0; + patt_spec->y = 0; + patt_spec->b_stagger = bstagger_pattern; + + unsigned char *pd0 = patt_spec->pix_buff; + unsigned char *pd; + unsigned char *ps0 = Image.GetData(); + unsigned char *imgAlpha = NULL; + bool b_use_alpha = false; + if (Image.HasAlpha()) { + imgAlpha = Image.GetAlpha(); + b_use_alpha = true; + } + +#if defined(__WXMAC__) || defined(__WXQT__) + + if (prule->definition.SYDF == 'V') { + b_use_alpha = true; + imgAlpha = NULL; + } +#endif + + unsigned char primary_r = 0; + unsigned char primary_g = 0; + unsigned char primary_b = 0; + double reference_value = 0.5; + + bool b_filter = false; +#if defined(__WXMAC__) || defined(__WXGTK3__) + S52color *primary_color = 0; + if (prule->definition.SYDF == 'V') { + b_filter = true; + char *col = prule->colRef.LCRF; + primary_color = getColor(col + 1); + if (primary_color) { + primary_r = primary_color->R; + primary_g = primary_color->G; + primary_b = primary_color->B; + wxImage::RGBValue rgb(primary_r, primary_g, primary_b); + wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb); + reference_value = hsv.value; + } + } +#endif + + unsigned char *ps; + + { + unsigned char mr = local_unused_wxColor.Red(); + unsigned char mg = local_unused_wxColor.Green(); + unsigned char mb = local_unused_wxColor.Blue(); + + if (pd0 && ps0) { + for (int iy = 0; iy < sizey; iy++) { +#ifdef __OCPN__ANDROID__ + pd = pd0 + ((sizey - iy - 1) * patt_spec->pb_pitch); +#else + pd = pd0 + (iy * patt_spec->pb_pitch); +#endif + ps = ps0 + (iy * sizex * 3); + for (int ix = 0; ix < sizex; ix++) { + if (ix < sizex) { + unsigned char r = *ps++; + unsigned char g = *ps++; + unsigned char b = *ps++; + + if (b_filter) { + wxImage::RGBValue rgb(r, g, b); + wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb); + double ratio = hsv.value / reference_value; + + if (ratio > 0.5) { + *pd++ = primary_r; + *pd++ = primary_g; + *pd++ = primary_b; + *pd++ = 255; + } else { + *pd++ = 0; + *pd++ = 0; + *pd++ = 0; + *pd++ = 0; + } + } else { +#ifdef ocpnUSE_ocpnBitmap + if (b_revrgb) { + *pd++ = b; + *pd++ = g; + *pd++ = r; + } else { + *pd++ = r; + *pd++ = g; + *pd++ = b; + } + +#else + *pd++ = r; + *pd++ = g; + *pd++ = b; +#endif + if (b_use_alpha && imgAlpha) { + *pd++ = *imgAlpha++; + } else { + *pd++ = ((r == mr) && (g == mg) && (b == mb) ? 0 : 255); + } + } + } + } + } + } + } + + return patt_spec; +} + +int s52plib::RenderToBufferAP(ObjRazRules *rzRules, Rules *rules, + render_canvas_parms *pb_spec) { + // if (vp->m_projection_type != PROJECTION_MERCATOR) return 1; + + wxImage Image; + + if (rules->razRule == NULL) return 0; + + if ((rules->razRule->pixelPtr == NULL) || + (rules->razRule->parm1 != m_colortable_index) || + (rules->razRule->parm0 != ID_RGB_PATT_SPEC)) { + render_canvas_parms *patt_spec = + CreatePatternBufferSpec(rzRules, rules, true); + + ClearRulesCache( + rules->razRule); // Clear out any existing cached symbology + + rules->razRule->pixelPtr = patt_spec; + rules->razRule->parm1 = m_colortable_index; + rules->razRule->parm0 = ID_RGB_PATT_SPEC; + + } // Instantiation done + + // Render the Area using the pattern spec stored in the rules + render_canvas_parms *ppatt_spec = + (render_canvas_parms *)rules->razRule->pixelPtr; + + // Set the pattern reference point + + wxPoint r; + GetPointPixSingle(rzRules, rzRules->obj->y, rzRules->obj->x, &r); + + ppatt_spec->x = + r.x - 2000000; // bias way down to avoid zero-crossing logic in dda + ppatt_spec->y = r.y - 2000000; + + RenderToBufferFilledPolygon(rzRules, rzRules->obj, NULL, pb_spec, ppatt_spec); + + return 1; +} + +int s52plib::RenderToBufferAC(ObjRazRules *rzRules, Rules *rules, + render_canvas_parms *pb_spec) { + // if (vp->m_projection_type != PROJECTION_MERCATOR) return 1; + + S52color *c; + char *str = (char *)rules->INSTstr; + + c = getColor(str); + + RenderToBufferFilledPolygon(rzRules, rzRules->obj, c, pb_spec, NULL); + + // At very small scales, the object could be visible on both the left and + // right sides of the screen. Identify this case...... + if (vp_plib.chart_scale > 5e7) { + // Does the object hang out over the left side of the VP? + if ((rzRules->obj->BBObj.GetMaxLon() > GetBBox().GetMinLon()) && + (rzRules->obj->BBObj.GetMinLon() < GetBBox().GetMinLon())) { + // If we add 360 to the objects lons, does it intersect the the right + // side of the VP? + if (((rzRules->obj->BBObj.GetMaxLon() + 360.) > GetBBox().GetMaxLon()) && + ((rzRules->obj->BBObj.GetMinLon() + 360.) < GetBBox().GetMaxLon())) { + // If so, this area oject should be drawn again, this time for the left + // side + // Do this by temporarily adjusting the objects rendering offset + rzRules->obj->x_origin -= + mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI; + RenderToBufferFilledPolygon(rzRules, rzRules->obj, c, pb_spec, NULL); + rzRules->obj->x_origin += + mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI; + } + } + } + + return 1; +} + +int s52plib::RenderAreaToDC(wxDC *pdcin, ObjRazRules *rzRules, + render_canvas_parms *pb_spec) { + if (!ObjectRenderCheckRules(rzRules, true)) return 0; + + m_pdc = pdcin; // use this DC + Rules *rules = rzRules->LUP->ruleList; + + while (rules != NULL) { + switch (rules->ruleType) { + case RUL_ARE_CO: + RenderToBufferAC(rzRules, rules, pb_spec); + break; // AC + case RUL_ARE_PA: + RenderToBufferAP(rzRules, rules, pb_spec); + break; // AP + + case RUL_CND_SY: { + if (!rzRules->obj->bCS_Added) { + rzRules->obj->CSrules = NULL; + GetAndAddCSRules(rzRules, rules); + rzRules->obj->bCS_Added = 1; // mark the object + } + Rules *rules_last = rules; + rules = rzRules->obj->CSrules; + + // The CS procedure may have changed the Display Category of the + // Object, need to check again for visibility + if (ObjectRenderCheckCat(rzRules)) { + while (NULL != rules) { + // Hve seen drgare fault here, need to code area query to debug + // possible that RENDERtoBUFFERAP/AC is blowing obj->CSRules + // When it faults here, look at new debug field obj->CSLUP + switch (rules->ruleType) { + case RUL_ARE_CO: + RenderToBufferAC(rzRules, rules, pb_spec); + break; + case RUL_ARE_PA: + RenderToBufferAP(rzRules, rules, pb_spec); + break; + case RUL_NONE: + default: + break; // no rule type (init) + } + rules_last = rules; + rules = rules->next; + } + } + + rules = rules_last; + break; + } + + case RUL_NONE: + default: + break; // no rule type (init) + } // switch + + rules = rules->next; + } + + return 1; +} + +void s52plib::GetAndAddCSRules(ObjRazRules *rzRules, Rules *rules) { + LUPrec *NewLUP; + LUPrec *LUP; + LUPrec *LUPCandidate; + wxString cs_string; + + char *rule_str1 = RenderCS(rzRules, rules); + if (rule_str1) cs_string = wxString(rule_str1, wxConvUTF8); + free(rule_str1); // delete rule_str1; + + // Try to find a match for this object/attribute set in dynamic CS LUP Table + + // Do this by checking each LUP in the CS LUPARRAY and checking.... + // a) is Object Name the same? and + // b) was LUP created earlier by exactly the same INSTruction string? + // c) does LUP have same Display Category and Priority? + + wxArrayOfLUPrec *la = condSymbolLUPArray; + int index = 0; + int index_max = la->GetCount(); + LUP = NULL; + + while ((index < index_max)) { + LUPCandidate = la->Item(index); + if (!strcmp(rzRules->LUP->OBCL, LUPCandidate->OBCL)) { + if (LUPCandidate->INST.IsSameAs(cs_string)) { + if (LUPCandidate->DISC == rzRules->LUP->DISC) { + LUP = LUPCandidate; + break; + } + } + } + index++; + } + + // If not found, need to create a dynamic LUP and add to CS LUP Table + + if (NULL == LUP) // Not found + { + NewLUP = new LUPrec(); + NewLUP->DISC = rzRules->LUP->DISC; // as a default + + memcpy(NewLUP->OBCL, rzRules->LUP->OBCL, 6); // the object class name + + // Add the complete CS string to the LUP + if (cs_string.Length()) NewLUP->INST = cs_string; + + _LUP2rules(NewLUP, rzRules->obj); + + // Add LUP to array + wxArrayOfLUPrec *pLUPARRAYtyped = condSymbolLUPArray; + + pLUPARRAYtyped->Add(NewLUP); + + LUP = NewLUP; + + } // if (LUP = NULL) + + Rules *top = LUP->ruleList; + + rzRules->obj->CSrules = top; // patch in a new rule set +} + +bool s52plib::ObjectRenderCheck(ObjRazRules *rzRules) { + if (!ObjectRenderCheckPos(rzRules)) return false; + + if (!ObjectRenderCheckCat(rzRules)) return false; + + return true; +} + +bool s52plib::ObjectRenderCheckCS(ObjRazRules *rzRules) { + // We need to do this test since some CS procedures change the display + // category So we need to tentatively process all objects with CS LUPs + Rules *rules = rzRules->LUP->ruleList; + while (rules != NULL) { + if (RUL_CND_SY == rules->ruleType) return true; + + rules = rules->next; + } + + return false; +} + +bool s52plib::ObjectRenderCheckPos(ObjRazRules *rzRules) { + if (rzRules->obj == NULL) return false; + + // Of course, the object must be at least partly visible in the VPointCompat + const LLBBox &vpBox = GetBBox(); + const LLBBox &testBox = rzRules->obj->BBObj; + + if (vpBox.GetMaxLat() < testBox.GetMinLat() || + vpBox.GetMinLat() > testBox.GetMaxLat()) + return false; + + if (vpBox.GetMaxLon() >= testBox.GetMinLon() && + vpBox.GetMinLon() <= testBox.GetMaxLon()) + return true; + + if (vpBox.GetMaxLon() >= testBox.GetMinLon() + 360 && + vpBox.GetMinLon() <= testBox.GetMaxLon() + 360) + return true; + + if (vpBox.GetMaxLon() >= testBox.GetMinLon() - 360 && + vpBox.GetMinLon() <= testBox.GetMaxLon() - 360) + return true; + + return false; +} + +bool s52plib::ObjectRenderCheckPosReduced(ObjRazRules *rzRules) { + if (rzRules->obj == NULL) return false; + + // Of course, the object must be at least partly visible in the VPointCompat + LLBBox vpBox = GetReducedBBox(); + const LLBBox &testBox = rzRules->obj->BBObj; + + if (vpBox.GetMaxLat() < testBox.GetMinLat() || + vpBox.GetMinLat() > testBox.GetMaxLat()) + return false; + + if (vpBox.GetMaxLon() >= testBox.GetMinLon() && + vpBox.GetMinLon() <= testBox.GetMaxLon()) + return true; + + if (vpBox.GetMaxLon() >= testBox.GetMinLon() + 360 && + vpBox.GetMinLon() <= testBox.GetMaxLon() + 360) + return true; + + if (vpBox.GetMaxLon() >= testBox.GetMinLon() - 360 && + vpBox.GetMinLon() <= testBox.GetMaxLon() - 360) + return true; + + return false; +} + +bool s52plib::ObjectRenderCheckCat(ObjRazRules *rzRules) { + g_scaminScale = 1.0; + + if (rzRules->obj == NULL) return false; + + bool b_catfilter = true; + bool b_visible = false; + + // Do Object Type Filtering + DisCat obj_cat = rzRules->obj->m_DisplayCat; + + // Meta object filter. + // Applied when showing display category OTHER, and + // only for objects whose decoded S52 display category (by LUP) is also OTHER + if (m_nDisplayCategory == OTHER) { + if (OTHER == obj_cat) { + if (!strncmp(rzRules->LUP->OBCL, "M_", 2)) { + if (!m_bShowMeta) + return false; + else { + if (!strncmp(rzRules->LUP->OBCL, "M_QUAL", 6) && !m_qualityOfDataOn) + return false; + } + } + } + } else { + // We want to filter out M_NSYS objects everywhere except "OTHER" category + if (!strncmp(rzRules->LUP->OBCL, "M_", 2)) + if (!m_bShowMeta) return false; + } + +#ifdef __OCPN__ANDROID__ + // We want to filter out M_NSYS objects on Android, as they are of limited use + // on a phone/tablet + if (!strncmp(rzRules->LUP->OBCL, "M_", 2)) + if (!m_bShowMeta) return false; +#endif + + if (m_nDisplayCategory == MARINERS_STANDARD) { + if (-1 == rzRules->obj->iOBJL) UpdateOBJLArray(rzRules->obj); + + if (DISPLAYBASE == obj_cat) { // always display individual objects that + // were moved to DISPLAYBASE by CS Procedures + b_visible = true; + b_catfilter = false; + } else if (!((OBJLElement *)(pOBJLArray->Item(rzRules->obj->iOBJL))) + ->nViz) { + b_catfilter = false; + } + } + + else if (m_nDisplayCategory == OTHER) { + if ((DISPLAYBASE != obj_cat) && (STANDARD != obj_cat) && + (OTHER != obj_cat)) { + b_catfilter = false; + } + } + + else if (m_nDisplayCategory == STANDARD) { + if ((DISPLAYBASE != obj_cat) && (STANDARD != obj_cat)) { + b_catfilter = false; + } + } else if (m_nDisplayCategory == DISPLAYBASE) { + if (DISPLAYBASE != obj_cat) { + b_catfilter = false; + } + } + + // Soundings override + if (!strncmp(rzRules->LUP->OBCL, "SOUNDG", 6)) b_catfilter = m_bShowSoundg; + + if (b_catfilter) { + b_visible = true; + + // SCAMIN Filtering + // Implementation note: + // According to S52 specs, SCAMIN must not apply to GROUP1 objects, + // Meta Objects or DisplayCategoryBase objects. Occasionally, an ENC + // will encode a spurious SCAMIN value for one of these objects. see, + // for example, US5VA18M, in OpenCPN SENC as Feature 350(DEPARE), LNAM + // = 022608187ED20ACC. We shall explicitly ignore SCAMIN filtering for + // these types of objects. + + if (m_bUseSCAMIN) { + if ((DISPLAYBASE == rzRules->LUP->DISC) || + (PRIO_GROUP1 == rzRules->LUP->DPRI)) + b_visible = true; + else { + // if( vp->chart_scale > rzRules->obj->Scamin ) b_visible + // = false; + + double zoom_mod = (double)m_chart_zoom_modifier_vector; + + double modf = zoom_mod / 5.; // -1->1 + double mod = pow(8., modf); + mod = wxMax(mod, .2); + mod = wxMin(mod, 8.0); + + if (mod > 1) { + if (vp_plib.chart_scale > rzRules->obj->Scamin * mod) + b_visible = false; // definitely invisible + else { + // Theoretically invisible, however... + // In the "zoom modified" scale region, + // we render the symbol at reduced size, scaling down to no less + // than half normal size. + + if (vp_plib.chart_scale > rzRules->obj->Scamin) { + double xs = vp_plib.chart_scale - rzRules->obj->Scamin; + double xl = (rzRules->obj->Scamin * mod) - rzRules->obj->Scamin; + g_scaminScale = 1.0 - (0.5 * xs / xl); + } + } + } else { + if (vp_plib.chart_scale > rzRules->obj->Scamin) b_visible = false; + } + } + + // Check for SUPER_SCAMIN, apply if enabled + if (m_bUseSUPER_SCAMIN) { + if (rzRules->obj->SuperScamin < 0) { + if ((strncmp(rzRules->obj->FeatureName, "LNDARE", 6) && + strncmp(rzRules->obj->FeatureName, "DEPARE", 6) && + strncmp(rzRules->obj->FeatureName, "SWPARE", 6) && + strncmp(rzRules->obj->FeatureName, "RECTRK", 6) && + strncmp(rzRules->obj->FeatureName, "TSS", 3) && + strncmp(rzRules->obj->FeatureName, "TSEZNE", 6) && + strncmp(rzRules->obj->FeatureName, "DRGARE", 6) && + strncmp(rzRules->obj->FeatureName, "COALNE", 6)) || + (!strncmp(rzRules->obj->FeatureName, "LNDARE", 6) && + (rzRules->LUP->ruleList->ruleType != RUL_ARE_CO))) { + double chart_ref_scale = rzRules->obj->m_chart_context->chart_scale; + + // Is the ENC cell SCAMIN for this object un-defined? + if (rzRules->obj->Scamin > + 1e8) { // undefined default value is 1e8+2 + // Get the scale of the ENC, and establish SUPERSCAMIN + double super_scamin = chart_ref_scale * 4; + rzRules->obj->SuperScamin = super_scamin; + } + if (rzRules->obj->Scamin > + 9e6) { // Presumed undefined value for Greek ENC Lights + // Get the scale of the ENC, and establish SUPERSCAMIN + double super_scamin = chart_ref_scale * 2; + rzRules->obj->SuperScamin = super_scamin; + } + if (!strncmp(rzRules->obj->FeatureName, "SOUNDG", 6)) { + if (rzRules->obj->Scamin > + 4e6) { // Presumed undefined value for Greek ENC soundings + // Get the scale of the ENC, and establish SUPERSCAMIN + double super_scamin = chart_ref_scale * 2; + rzRules->obj->SuperScamin = super_scamin; + } + } + } + } + + // Make the test + if ((rzRules->obj->SuperScamin > 0) && + (vp_plib.chart_scale > rzRules->obj->SuperScamin)) + b_visible = false; + } + + // On the other hand, $TEXTS features need not really be displayed at + // all scales, always To do so makes a very cluttered display + if ((!strncmp(rzRules->LUP->OBCL, "$TEXTS", 6)) && + (vp_plib.chart_scale > rzRules->obj->Scamin)) + b_visible = false; + } + + return b_visible; + } + + return b_visible; +} + +bool s52plib::ObjectRenderCheckDates(ObjRazRules *rzRules) { + // Check DATSTA/DATEND attributes, if present + // DATSTA + wxString datsta = rzRules->obj->GetAttrValueAsString("DATSTA"); + if (datsta.Len() > 0) { + bool bDateValid = false; + + // CCYYMMDD + wxDateTime objDate; + wxString::const_iterator end; + if (objDate.ParseFormat(datsta, "%Y%m%d", &end)) { + if (end == datsta.end()) { // Require perfect parsing + if (objDate.IsValid()) bDateValid = true; + } + } + if (bDateValid) { + objDate.ResetTime(); // DATSTA specifications take effect at 00:01- on + // the specified date + wxDateTime now = wxDateTime::Now(); + if (now.IsEarlierThan(objDate)) return false; // No Show + } + } + + // DATEND + datsta = rzRules->obj->GetAttrValueAsString("DATEND"); + if (datsta.Len() > 0) { + bool bDateValid = false; + + // CCYYMMDD + wxDateTime objDate; + wxString::const_iterator end; + if (objDate.ParseFormat(datsta, "%Y%m%d", &end)) { + if (end == datsta.end()) { // Require perfect parsing + if (objDate.IsValid()) bDateValid = true; + } + } + if (bDateValid) { + objDate.ResetTime(); // DATEND specifications take effect at 23:59+ on + // the specified date. + objDate += wxTimeSpan(24); + wxDateTime now = wxDateTime::Now(); + if (now.IsLaterThan(objDate)) return false; // No Show + } + } + + // PEREND + datsta = rzRules->obj->GetAttrValueAsString("PEREND"); + if ((datsta.Len() > 0) && !datsta.StartsWith(_T("--"))) { + bool bDateValid = false; + + // CCYYMMDD + wxDateTime objDate; + wxString::const_iterator end; + if (objDate.ParseFormat(datsta, "%Y%m%d", &end)) { + if (end == datsta.end()) { // Require perfect parsing + if (objDate.IsValid()) bDateValid = true; + } + } + if (bDateValid) { + objDate.ResetTime(); // PEREND specifications take effect at 23:59+ on + // the specified date. + objDate += wxTimeSpan(24); + wxDateTime now = wxDateTime::Now(); + if (now.IsLaterThan(objDate)) return false; // No Show + } + } + + return true; +} + +bool s52plib::ObjectRenderCheckRules(ObjRazRules *rzRules, bool check_noshow) { + if (!rzRules->obj->bOK_to_Render) return false; + + if (!ObjectRenderCheckPos(rzRules)) return false; + + // The Feature M_QUAL, in MARINERS_STANDARD catagory, is a special case, + // since it is also controlled by a global hotkey in display category ALL and + // MARINERS_STANDARD + if (m_nDisplayCategory == MARINERS_STANDARD) { + if (strncmp(rzRules->obj->FeatureName, "M_QUAL", + 6)) { // Anything other than M_QUAL + if (check_noshow && IsObjNoshow(rzRules->LUP->OBCL)) return 0; + } else { + if (!m_qualityOfDataOn) return 0; + } + } else { + if (check_noshow && IsObjNoshow(rzRules->LUP->OBCL)) return false; + } + + if (ObjectRenderCheckCat(rzRules)) { + return ObjectRenderCheckDates(rzRules); + } + + // If this object cannot be moved to a higher category by CS procedures, + // then we are done here + if (!rzRules->obj->m_bcategory_mutable) return false; + + // already added, nothing below can change its display category + if (rzRules->obj->bCS_Added) return false; + + // Otherwise, make sure the CS, if present, has been evaluated, + // and then check the category again + // no rules + if (!ObjectRenderCheckCS(rzRules)) return false; + + rzRules->obj->CSrules = NULL; + Rules *rules = rzRules->LUP->ruleList; + while (rules != NULL) { + if (RUL_CND_SY == rules->ruleType) { + GetAndAddCSRules(rzRules, rules); + rzRules->obj->bCS_Added = 1; // mark the object + break; + } + rules = rules->next; + } + + // still not displayable + if (!ObjectRenderCheckCat(rzRules)) return false; + + return ObjectRenderCheckDates(rzRules); +} + +void s52plib::SetDisplayCategory(enum _DisCat cat) { + enum _DisCat old = m_nDisplayCategory; + m_nDisplayCategory = cat; + + if (old != cat) { + ClearNoshow(); + } + GenerateStateHash(); +} + +bool s52plib::IsObjNoshow(const char *objcl) { + for (unsigned int i = 0; i < m_noshow_array.GetCount(); i++) { + if (!strncmp(m_noshow_array[i].obj, objcl, 6)) return true; + } + return false; +} + +void s52plib::AddObjNoshow(const char *objcl) { + if (!IsObjNoshow(objcl)) { + noshow_element element; + memcpy(element.obj, objcl, 6); + m_noshow_array.Add(element); + } +} + +void s52plib::RemoveObjNoshow(const char *objcl) { + for (unsigned int i = 0; i < m_noshow_array.GetCount(); i++) { + if (!strncmp(m_noshow_array[i].obj, objcl, 6)) { + m_noshow_array.RemoveAt(i); + return; + } + } +} + +void s52plib::ClearNoshow(void) { m_noshow_array.Clear(); } + +#if 0 +void s52plib::PLIB_LoadS57Config() { + // Get a pointer to the opencpn configuration object + wxFileConfig *pconfig = GetOCPNConfigObject(); + + int read_int; + double dval; + + pconfig->SetPath(_T ( "/Settings" )); + // pconfig->Read( _T ( "DebugS57" ), &g_PIbDebugS57, 0 ); // Show LUP + // and Feature info in object query + + pconfig->SetPath(_T ( "/Settings/GlobalState" )); + + pconfig->Read(_T ( "bShowS57Text" ), &read_int, 0); + SetShowS57Text(!(read_int == 0)); + + pconfig->Read(_T ( "bShowS57ImportantTextOnly" ), &read_int, 0); + SetShowS57ImportantTextOnly(!(read_int == 0)); + + pconfig->Read(_T ( "bShowLightDescription" ), &read_int, 0); + SetShowLdisText(!(read_int == 0)); + + pconfig->Read(_T ( "bExtendLightSectors" ), &read_int, 0); + SetExtendLightSectors(!(read_int == 0)); + + pconfig->Read(_T ( "nDisplayCategory" ), &read_int, (enum _DisCat)STANDARD); + SetDisplayCategory((enum _DisCat)read_int); + + pconfig->Read(_T ( "nSymbolStyle" ), &read_int, (enum _LUPname)PAPER_CHART); + m_nSymbolStyle = (LUPname)read_int; + + pconfig->Read(_T ( "nBoundaryStyle" ), &read_int, PLAIN_BOUNDARIES); + m_nBoundaryStyle = (LUPname)read_int; + + pconfig->Read(_T ( "bShowSoundg" ), &read_int, 1); + m_bShowSoundg = !(read_int == 0); + + pconfig->Read(_T ( "bShowMeta" ), &read_int, 0); + m_bShowMeta = !(read_int == 0); + + pconfig->Read(_T ( "bUseSCAMIN" ), &read_int, 1); + m_bUseSCAMIN = !(read_int == 0); + + pconfig->Read(_T ( "bShowAtonText" ), &read_int, 1); + m_bShowAtonText = !(read_int == 0); + + pconfig->Read(_T ( "bDeClutterText" ), &read_int, 0); + m_bDeClutterText = !(read_int == 0); + + pconfig->Read(_T ( "bShowNationalText" ), &read_int, 0); + m_bShowNationalTexts = !(read_int == 0); + + if (pconfig->Read(_T ( "S52_MAR_SAFETY_CONTOUR" ), &dval, 5.0)) { + S52_setMarinerParam(S52_MAR_SAFETY_CONTOUR, dval); + S52_setMarinerParam(S52_MAR_SAFETY_DEPTH, + dval); // Set safety_contour and safety_depth the same + } + + if (pconfig->Read(_T ( "S52_MAR_SHALLOW_CONTOUR" ), &dval, 3.0)) + S52_setMarinerParam(S52_MAR_SHALLOW_CONTOUR, dval); + + if (pconfig->Read(_T ( "S52_MAR_DEEP_CONTOUR" ), &dval, 10.0)) + S52_setMarinerParam(S52_MAR_DEEP_CONTOUR, dval); + + if (pconfig->Read(_T ( "S52_MAR_TWO_SHADES" ), &dval, 0.0)) + S52_setMarinerParam(S52_MAR_TWO_SHADES, dval); + + UpdateMarinerParams(); + + pconfig->SetPath(_T ( "/Settings/GlobalState" )); + pconfig->Read(_T ( "S52_DEPTH_UNIT_SHOW" ), &read_int, + 1); // default is metres + read_int = wxMax(read_int, 0); // qualify value + read_int = wxMin(read_int, 2); + m_nDepthUnitDisplay = read_int; + + // S57 Object Class Visibility + + OBJLElement *pOLE; + + pconfig->SetPath(_T ( "/Settings/ObjectFilter" )); + + int iOBJMax = pconfig->GetNumberOfEntries(); + if (iOBJMax) { + wxString str; + long val; + long dummy; + + wxString sObj; + + bool bCont = pconfig->GetFirstEntry(str, dummy); + while (bCont) { + pconfig->Read(str, &val); // Get an Object Viz + + bool bNeedNew = true; + + if (str.StartsWith(_T ( "viz" ), &sObj)) { + for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++) { + pOLE = (OBJLElement *)(pOBJLArray->Item(iPtr)); + if (!strncmp(pOLE->OBJLName, sObj.mb_str(), 6)) { + pOLE->nViz = val; + bNeedNew = false; + break; + } + } + + if (bNeedNew) { + pOLE = (OBJLElement *)calloc(sizeof(OBJLElement), 1); + memcpy(pOLE->OBJLName, sObj.mb_str(), OBJL_NAME_LEN); + pOLE->nViz = 1; + + pOBJLArray->Add((void *)pOLE); + } + } + bCont = pconfig->GetNextEntry(str, dummy); + } + } +} +#endif + +void s52plib::PLIB_LoadS57GlobalConfig(wxFileConfig *pconfig) { + int read_int; + double dval; + + pconfig->SetPath(_T ( "/Settings" )); + + pconfig->SetPath(_T ( "/Settings/GlobalState" )); + + pconfig->Read(_T ( "bShowS57ImportantTextOnly" ), &read_int, 0); + SetShowS57ImportantTextOnly(!(read_int == 0)); + + pconfig->Read(_T ( "nSymbolStyle" ), &read_int, (enum _LUPname)PAPER_CHART); + m_nSymbolStyle = (LUPname)read_int; + + pconfig->Read(_T ( "nBoundaryStyle" ), &read_int, PLAIN_BOUNDARIES); + m_nBoundaryStyle = (LUPname)read_int; + + pconfig->Read(_T ( "bShowMeta" ), &read_int, 0); + m_bShowMeta = !(read_int == 0); + + pconfig->Read(_T ( "bUseSCAMIN" ), &read_int, 1); + m_bUseSCAMIN = !(read_int == 0); + + pconfig->Read(_T ( "bDeClutterText" ), &read_int, 0); + m_bDeClutterText = !(read_int == 0); + + pconfig->Read(_T ( "bShowNationalText" ), &read_int, 0); + m_bShowNationalTexts = !(read_int == 0); + + if (pconfig->Read(_T ( "S52_MAR_SAFETY_CONTOUR" ), &dval, 5.0)) { + S52_setMarinerParam(S52_MAR_SAFETY_CONTOUR, dval); + S52_setMarinerParam(S52_MAR_SAFETY_DEPTH, + dval); // Set safety_contour and safety_depth the same + } + + if (pconfig->Read(_T ( "S52_MAR_SHALLOW_CONTOUR" ), &dval, 3.0)) + S52_setMarinerParam(S52_MAR_SHALLOW_CONTOUR, dval); + + if (pconfig->Read(_T ( "S52_MAR_DEEP_CONTOUR" ), &dval, 10.0)) + S52_setMarinerParam(S52_MAR_DEEP_CONTOUR, dval); + + if (pconfig->Read(_T ( "S52_MAR_TWO_SHADES" ), &dval, 0.0)) + S52_setMarinerParam(S52_MAR_TWO_SHADES, dval); + + UpdateMarinerParams(); + + pconfig->SetPath(_T ( "/Settings/GlobalState" )); + pconfig->Read(_T ( "S52_DEPTH_UNIT_SHOW" ), &read_int, + 1); // default is metres + read_int = wxMax(read_int, 0); // qualify value + read_int = wxMin(read_int, 2); + m_nDepthUnitDisplay = read_int; + + // Load height unit display (meters/feet) from core HeightFormat if available + // Default to meters (0). Values correspond to HEIGHT_* enum: 0=M, 1=FT + if (pconfig->Read(_T ( "HeightFormat" ), &read_int, 0)) { + read_int = wxMax(read_int, 0); + read_int = wxMin(read_int, 1); + m_nHeightUnitDisplay = read_int; + } else { + m_nHeightUnitDisplay = 0; + } +} + +void s52plib::PLIB_LoadS57ObjectConfig(wxFileConfig *pconfig) { + // S57 Object Class Visibility + + OBJLElement *pOLE; + + pconfig->SetPath(_T ( "/Settings/ObjectFilter" )); + + int iOBJMax = pconfig->GetNumberOfEntries(); + if (iOBJMax) { + wxString str; + long val; + long dummy; + + wxString sObj; + + bool bCont = pconfig->GetFirstEntry(str, dummy); + while (bCont) { + pconfig->Read(str, &val); // Get an Object Viz + + bool bNeedNew = true; + + if (str.StartsWith(_T ( "viz" ), &sObj)) { + for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++) { + pOLE = (OBJLElement *)(pOBJLArray->Item(iPtr)); + if (!strncmp(pOLE->OBJLName, sObj.mb_str(), 6)) { + pOLE->nViz = val; + bNeedNew = false; + break; + } + } + + if (bNeedNew) { + pOLE = (OBJLElement *)calloc(sizeof(OBJLElement), 1); + strncpy(pOLE->OBJLName, sObj.mb_str(), 6); + pOLE->nViz = 1; + + pOBJLArray->Add((void *)pOLE); + } + } + bCont = pconfig->GetNextEntry(str, dummy); + } + } +} + +// Do all those things necessary to prepare for a new rendering +void s52plib::PrepareForRender(void) { PrepareForRender(&vp_plib); } + +void s52plib::PrepareForRender(VPointCompat *vp) { + m_benableGLLS = true; // default is to always use RenderToGLLS (VBO support) + +#ifdef ocpnUSE_GL + void PrepareS52ShaderUniforms(VPointCompat * vp); + if (m_useGLSL && vp) { + PrepareS52ShaderUniforms(vp); + + // Prepare TexFont shader + for (unsigned int i = 0; i < TXF_CACHE; i++) { + if (s_txf[i].cache) + s_txf[i].cache->PrepareShader(vp->pix_width, vp->pix_height, + vp->rotation); + } + } +#endif + + float ChartScaleFactorExpNew = GetOCPNChartScaleFactor_Plugin(); + + if (ChartScaleFactorExpNew != m_ChartScaleFactorExp) { + // Clear some cached data to handle new scale value. + + // Cached HPGL Textures + for (const auto &pair : vector_symbol_cache) { + const GLuint tex = pair.second; + glDeleteTextures(1, &tex); + } + vector_symbol_cache.clear(); + } + + m_ChartScaleFactorExp = ChartScaleFactorExpNew; + + // Reset the LIGHTS declutter machine + lastLightLat = 0; + lastLightLon = 0; + + // Precalulate the ENC scale factors + m_SoundingsScaleFactor = (m_nSoundingFactor * .1) + + 1; // exp(m_nSoundingFactor * (log(2.0) / 5.0)); + m_TextScaleFactor = exp(m_nTextFactor * (log(2.0) / 5.0)); +} + +void s52plib::SetAnchorOn(bool val) { + const char *categories[] = {"ACHBRT", "ACHARE", "CBLSUB", "PIPARE", + "PIPSOL", "TUNNEL", "SBDARE"}; + unsigned int num = sizeof(categories) / sizeof(categories[0]); + + if ((m_nDisplayCategory == OTHER) || + (m_nDisplayCategory == MARINERS_STANDARD)) { + bool bAnchor = val; + + if (!bAnchor) { + for (unsigned int c = 0; c < num; c++) { + AddObjNoshow(categories[c]); + } + } else { + for (unsigned int c = 0; c < num; c++) { + RemoveObjNoshow(categories[c]); + } + } + } else { // if not category OTHER, then anchor-related features are always + // shown. + for (unsigned int c = 0; c < num; c++) { + RemoveObjNoshow(categories[c]); + } + } + + m_anchorOn = val; +} + +void s52plib::SetQualityOfData(bool val) { + if (!val) { // going off + AddObjNoshow("M_QUAL"); + } else { // Off, going on + RemoveObjNoshow("M_QUAL"); + + for (unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++) { + OBJLElement *pOLE = (OBJLElement *)(pOBJLArray->Item(iPtr)); + if (!strncmp(pOLE->OBJLName, "M_QUAL", 6)) { + pOLE->nViz = 1; // force on + break; + } + } + } + + m_qualityOfDataOn = val; +} + +void s52plib::ClearTextList(void) { + // Clear the current text rectangle list + m_textObjList.Clear(); +} + +bool s52plib::EnableGLLS(bool b_enable) { + bool return_val = m_benableGLLS; + m_benableGLLS = b_enable; + return return_val; +} + +void s52plib::AdjustTextList(int dx, int dy, int screenw, int screenh) { + return; +} + +bool s52plib::GetPointPixArray(ObjRazRules *rzRules, wxPoint2DDouble *pd, + wxPoint *pp, int nv) { + for (int i = 0; i < nv; i++) { + GetPointPixSingle(rzRules, pd[i].m_y, pd[i].m_x, pp + i); + } + + return true; +} + +bool s52plib::GetDoublePointPixArray(ObjRazRules *rzRules, wxPoint2DDouble *pd, + wxPoint2DDouble *pp, int nv) { + for (int i = 0; i < nv; i++) { + GetDoublePointPixSingle(rzRules, pd[i].m_y, pd[i].m_x, pp + i); + } + + return true; +} + +bool s52plib::GetPointPixSingle(ObjRazRules *rzRules, float north, float east, + wxPoint *r) { + // if (vp->m_projection_type == PROJECTION_MERCATOR) + { + double xr = rzRules->obj->x_rate; + double xo = rzRules->obj->x_origin; + double yr = rzRules->obj->y_rate; + double yo = rzRules->obj->y_origin; + + if (fabs(xo) > 1) { // cm93 hits this + if (GetBBox().GetMaxLon() >= 180. && + rzRules->obj->BBObj.GetMaxLon() < GetBBox().GetMinLon()) + xo += mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI; + else if ((GetBBox().GetMinLon() <= -180. && + rzRules->obj->BBObj.GetMinLon() > GetBBox().GetMaxLon()) || + (rzRules->obj->BBObj.GetMaxLon() >= 180 && + GetBBox().GetMinLon() <= 0.)) + xo -= mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI; + } + + double valx = (east * xr) + xo; + double valy = (north * yr) + yo; + + r->x = roundint(((valx - rzRules->sm_transform_parms->easting_vp_center) * + vp_plib.view_scale_ppm) + + (vp_plib.pix_width / 2)); + r->y = roundint((vp_plib.pix_height / 2) - + ((valy - rzRules->sm_transform_parms->northing_vp_center) * + vp_plib.view_scale_ppm)); + } + // else { + // double lat, lon; + // fromSM_Plugin(east - rzRules->sm_transform_parms->easting_vp_center, + // north - rzRules->sm_transform_parms->northing_vp_center, + // vp->clat, vp->clon, &lat, &lon); + // + // *r = GetPixFromLL(north, east); + // } + + return true; +} + +bool s52plib::GetDoublePointPixSingle(ObjRazRules *rzRules, float north, float east, + wxPoint2DDouble *r) { + // if (vp->m_projection_type == PROJECTION_MERCATOR) + { + double xr = rzRules->obj->x_rate; + double xo = rzRules->obj->x_origin; + double yr = rzRules->obj->y_rate; + double yo = rzRules->obj->y_origin; + + if (fabs(xo) > 1) { // cm93 hits this + if (GetBBox().GetMaxLon() >= 180. && + rzRules->obj->BBObj.GetMaxLon() < GetBBox().GetMinLon()) + xo += mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI; + else if ((GetBBox().GetMinLon() <= -180. && + rzRules->obj->BBObj.GetMinLon() > GetBBox().GetMaxLon()) || + (rzRules->obj->BBObj.GetMaxLon() >= 180 && + GetBBox().GetMinLon() <= 0.)) + xo -= mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI; + } + + double valx = (east * xr) + xo; + double valy = (north * yr) + yo; + + r->m_x = ((valx - rzRules->sm_transform_parms->easting_vp_center) * + vp_plib.view_scale_ppm) + + (vp_plib.pix_width / 2); + r->m_y = (vp_plib.pix_height / 2) - + ((valy - rzRules->sm_transform_parms->northing_vp_center) * + vp_plib.view_scale_ppm); + } + return true; +} + +void s52plib::GetPixPointSingle(int pixx, int pixy, double *plat, + double *plon) { +#if 1 + GetLLFromPix(wxPoint(pixx, pixy), plat, plon); +// if(*plon < 0 && vpt->clon > 180) +// *plon += 360; +#else + // Use Mercator estimator + int dx = pixx - (vpt->pix_width / 2); + int dy = (vpt->pix_height / 2) - pixy; + + double xp = (dx * cos(vpt->skew)) - (dy * sin(vpt->skew)); + double yp = (dy * cos(vpt->skew)) + (dx * sin(vpt->skew)); + + double d_east = xp / vpt->view_scale_ppm; + double d_north = yp / vpt->view_scale_ppm; + + double slat, slon; + fromSM(d_east, d_north, vpt->clat, vpt->clon, &slat, &slon); + + *plat = slat; + *plon = slon; +#endif +} + +void s52plib::GetPixPointSingleNoRotate(int pixx, int pixy, double *plat, + double *plon) { + double rotation = vp_plib.rotation; + vp_plib.rotation = 0; + GetLLFromPix(wxPoint(pixx, pixy), plat, plon); + vp_plib.rotation = rotation; +} + +void DrawAALine(wxDC *pDC, int x0, int y0, int x1, int y1, wxColour clrLine, + int dash, int space) { + int width = 1 + abs(x0 - x1); + int height = 1 + abs(y0 - y1); + wxPoint upperLeft(wxMin(x0, x1), wxMin(y0, y1)); + + wxBitmap bm(width, height); + wxMemoryDC mdc(bm); + + mdc.Blit(0, 0, width, height, pDC, upperLeft.x, upperLeft.y); + +#if wxUSE_GRAPHICS_CONTEXT + wxGCDC gdc(mdc); +#else + wxMemoryDC &gdc(mdc); +#endif + + wxPen pen(clrLine, 1, wxPENSTYLE_USER_DASH); + wxDash dashes[2]; + dashes[0] = dash; + dashes[1] = space; + pen.SetDashes(2, dashes); + gdc.SetPen(pen); + + gdc.DrawLine(x0 - upperLeft.x, y0 - upperLeft.y, x1 - upperLeft.x, + y1 - upperLeft.y); + + pDC->Blit(upperLeft.x, upperLeft.y, width, height, &mdc, 0, 0); + + mdc.SelectObject(wxNullBitmap); + + return; +} + +void s52plib::DrawDashLine(wxPen &pen, double x1, double y1, double x2, + double y2) { +#ifdef ocpnUSE_GL + glLineWidth(pen.GetWidth()); + + CGLShaderProgram *shader = pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/]; + shader->Bind(); + + float fBuf[4]; + GLint pos = shader->getAttributeLocation("position"); + glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), fBuf); + glEnableVertexAttribArray(pos); + + float colorv[4]; + colorv[0] = pen.GetColour().Red() / float(256); + colorv[1] = pen.GetColour().Green() / float(256); + colorv[2] = pen.GetColour().Blue() / float(256); + colorv[3] = 1.0; + + shader->SetUniform4fv("color", colorv); + + if (fabs(vp_plib.rotation) > 0.01) { + float cx = vp_plib.pix_width / 2.; + float cy = vp_plib.pix_height / 2.; + float c = cosf(-vp_plib.rotation); + float s = sinf(-vp_plib.rotation); + + float xn = x1 - cx; + float yn = y1 - cy; + x1 = xn * c - yn * s + cx; + y1 = xn * s + yn * c + cy; + + xn = x2 - cx; + yn = y2 - cy; + x2 = xn * c - yn * s + cx; + y2 = xn * s + yn * c + cy; + } + + wxDash *dashes; + int n_dashes = pen.GetDashes(&dashes); + if (n_dashes) { + float angle = atan2f((float)(y2 - y1), (float)(x2 - x1)); + float cosa = cosf(angle); + float sina = sinf(angle); + float t1 = pen.GetWidth(); + + float lpix = sqrtf(powf(x1 - x2, 2) + powf(y1 - y2, 2)); + float lrun = 0.; + float xa = x1; + float ya = y1; + float ldraw = t1 * dashes[0]; + float lspace = t1 * dashes[1]; + + ldraw = wxMax(ldraw, 4.0); + lspace = wxMax(lspace, 4.0); + lpix = wxMin(lpix, 2000.0); + + while (lrun < lpix) { + // Dash + float xb = xa + ldraw * cosa; + float yb = ya + ldraw * sina; + + if ((lrun + ldraw) >= lpix) // last segment is partial draw + { + xb = x2; + yb = y2; + } + + fBuf[0] = xa; + fBuf[1] = ya; + fBuf[2] = xb; + fBuf[3] = yb; + + glDrawArrays(GL_LINES, 0, 2); + + xa = xa + (lspace + ldraw) * cosa; + ya = ya + (lspace + ldraw) * sina; + lrun += lspace + ldraw; + } + } else { // not dashed + fBuf[0] = x1; + fBuf[1] = y1; + fBuf[2] = x2; + fBuf[3] = y2; + + glDrawArrays(GL_LINES, 0, 2); + } + + shader->UnBind(); +#endif +} + +/****************************************************************************/ +/* Convert Lat/Lon <-> Simple Mercator */ +/****************************************************************************/ +void toSM_plib(double lat, double lon, double lat0, double lon0, double *x, + double *y) { + double xlon = lon; + + /* Make sure lon and lon0 are same phase */ + + if ((lon * lon0 < 0.) && (fabs(lon - lon0) > 180.)) { + lon < 0.0 ? xlon += 360.0 : xlon -= 360.0; + } + + const double z = WGS84_semimajor_axis_meters * mercator_k0; + + *x = (xlon - lon0) * DEGREE * z; + + // y =.5 ln( (1 + sin t) / (1 - sin t) ) + const double s = sin(lat * DEGREE); + const double y3 = (.5 * log((1 + s) / (1 - s))) * z; + + const double s0 = sin(lat0 * DEGREE); + const double y30 = (.5 * log((1 + s0) / (1 - s0))) * z; + *y = y3 - y30; +} + +void fromSM_plib(double x, double y, double lat0, double lon0, double *lat, + double *lon) { + const double z = WGS84_semimajor_axis_meters * mercator_k0; + + const double s0 = sin(lat0 * DEGREE); + const double y0 = (.5 * log((1 + s0) / (1 - s0))) * z; + + *lat = (2.0 * atan(exp((y0 + y) / z)) - PI / 2.) / DEGREE; + + *lon = lon0 + (x / (DEGREE * z)); +} + +// TODO: eliminate the use of this function +wxPoint s52plib::GetPixFromLL(double lat, double lon) { + wxPoint2DDouble p = GetDoublePixFromLL(lat, lon); + if (wxFinite(p.m_x) && wxFinite(p.m_y)) { + if ((abs(p.m_x) < 10000) && (abs(p.m_y) < 10000)) + return wxPoint(wxRound(p.m_x), wxRound(p.m_y)); + } + return wxPoint(INVALID_COORD, INVALID_COORD); +} + +wxPoint2DDouble s52plib::GetDoublePixFromLL(double lat, double lon) { + double easting = 0; + double northing = 0; + double xlon = lon; + + /* Make sure lon and lon0 are same phase */ + if (xlon * vp_plib.clon < 0.) { + if (xlon < 0.) + xlon += 360.; + else + xlon -= 360.; + } + + if (fabs(xlon - vp_plib.clon) > 180.) { + if (xlon > vp_plib.clon) + xlon -= 360.; + else + xlon += 360.; + } + + toSM_plib(lat, xlon, vp_plib.clat, vp_plib.clon, &easting, &northing); + + if (!wxFinite(easting) || !wxFinite(northing)) + return wxPoint2DDouble(easting, northing); + + double epix = easting * vp_plib.view_scale_ppm; + double npix = northing * vp_plib.view_scale_ppm; + double dxr = epix; + double dyr = npix; + + // Apply VP Rotation + double angle = vp_plib.rotation; + + if (angle) { + dxr = epix * cos(angle) + npix * sin(angle); + dyr = npix * cos(angle) - epix * sin(angle); + } + + return wxPoint2DDouble((vp_plib.pix_width / 2.0) + dxr, + (vp_plib.pix_height / 2.0) - dyr); +} + +wxPoint s52plib::GetPixFromLLROT(double lat, double lon, double rotation) { + wxPoint2DDouble p = GetDoublePixFromLLROT(lat, lon, rotation); + if (wxFinite(p.m_x) && wxFinite(p.m_y)) { + if ((abs(p.m_x) < 10000) && (abs(p.m_y) < 10000)) + return wxPoint(wxRound(p.m_x), wxRound(p.m_y)); + } + return wxPoint(INVALID_COORD, INVALID_COORD); +} + +wxPoint2DDouble s52plib::GetDoublePixFromLLROT(double lat, double lon, + double rotation) { + double easting = 0; + double northing = 0; + double xlon = lon; + + /* Make sure lon and lon0 are same phase */ + if (xlon * vp_plib.clon < 0.) { + if (xlon < 0.) + xlon += 360.; + else + xlon -= 360.; + } + + if (fabs(xlon - vp_plib.clon) > 180.) { + if (xlon > vp_plib.clon) + xlon -= 360.; + else + xlon += 360.; + } + + toSM_plib(lat, xlon, vp_plib.clat, vp_plib.clon, &easting, &northing); + + if (!wxFinite(easting) || !wxFinite(northing)) + return wxPoint2DDouble(easting, northing); + + double epix = easting * vp_plib.view_scale_ppm; + double npix = northing * vp_plib.view_scale_ppm; + double dxr = epix; + double dyr = npix; + + // Apply VP Rotation + double angle = rotation; + + if (angle) { + dxr = epix * cos(angle) + npix * sin(angle); + dyr = npix * cos(angle) - epix * sin(angle); + } + + return wxPoint2DDouble((vp_plib.pix_width / 2.0) + dxr, + (vp_plib.pix_height / 2.0) - dyr); +} + +void s52plib::GetLLFromPix(const wxPoint2DDouble &p, double *lat, double *lon) { + double dx = p.m_x - (vp_plib.pix_width / 2.0); + double dy = (vp_plib.pix_height / 2.0) - p.m_y; + + double xpr = dx; + double ypr = dy; + + // Apply VP Rotation + double angle = vp_plib.rotation; + + if (angle) { + xpr = (dx * cos(angle)) - (dy * sin(angle)); + ypr = (dy * cos(angle)) + (dx * sin(angle)); + } + double d_east = xpr / vp_plib.view_scale_ppm; + double d_north = ypr / vp_plib.view_scale_ppm; + + double slat = 0.0, slon = 0.0; + fromSM_plib(d_east, d_north, vp_plib.clat, vp_plib.clon, &slat, &slon); + + *lat = slat; + + if (slon < -180.) + slon += 360.; + else if (slon > 180.) + slon -= 360.; + *lon = slon; +} + +RenderFromHPGL::RenderFromHPGL(s52plib *plibarg) { + plib = plibarg; + renderToDC = false; + renderToOpenGl = false; + renderToGCDC = false; + + if (plib) + scaleFactor = 100.0 / plib->GetPPMM(); + else + scaleFactor = 10.0; // Nominal + + workBufSize = 0; + workBufIndex = 0; + workBuf = NULL; + + s_odc_tess_work_buf = NULL; + s_odc_tess_vertex_idx = 0; + s_odc_tess_vertex_idx_this = 0; + s_odc_tess_buf_len = 0; + + transparency = 255; + m_content_scale_factor = 1.0; +} + +RenderFromHPGL::~RenderFromHPGL() { +#ifdef ocpnUSE_GL + if (renderToOpenGl) { + glDisable(GL_BLEND); + } + + free(workBuf); + free(s_odc_tess_work_buf); + +#endif +} + +void RenderFromHPGL::SetTargetDC(wxDC *pdc) { + targetDC = pdc; + renderToDC = true; + renderToOpenGl = false; + renderToGCDC = false; +} + +void RenderFromHPGL::SetTargetOpenGl() { + renderToOpenGl = true; + renderToDC = false; + renderToGCDC = false; +} + +#if wxUSE_GRAPHICS_CONTEXT +void RenderFromHPGL::SetTargetGCDC(wxGCDC *gdc) { + targetGCDC = gdc; + renderToGCDC = true; + renderToDC = false; + renderToOpenGl = false; +} +#endif + +const char *RenderFromHPGL::findColorNameInRef(char colorCode, char *col) { + int noColors = strlen(col) / 6; + for (int i = 0, j = 0; i < noColors; i++, j += 6) { + if (*(col + j) == colorCode) return col + j + 1; + } + return col + 1; // Default to first color if not found. +} + +wxPoint RenderFromHPGL::ParsePoint(wxString &argument) { + long x, y; + int colon = argument.Index(','); + argument.Left(colon).ToLong(&x); + argument.Mid(colon + 1).ToLong(&y); + return wxPoint(x, y); +} + +void RenderFromHPGL::SetPen() { + float nominal_line_width_pix = + wxMax(1.0, floor(plib->GetPPMM() / + 5.0)); // 0.2 mm nominal, but not less than 1 pixel + + nominal_line_width_pix *= m_content_scale_factor; // esp for Mac Retina + + int pen_width_mod = floor(penWidth * nominal_line_width_pix); + + pen = + wxThePenList->FindOrCreatePen(penColor, pen_width_mod, wxPENSTYLE_SOLID); + brush = wxTheBrushList->FindOrCreateBrush(penColor, wxBRUSHSTYLE_SOLID); + + if (renderToDC) { + targetDC->SetPen(*pen); + targetDC->SetBrush(*brush); + } +#ifdef ocpnUSE_GL + if (renderToOpenGl) { + if (plib->GetGLPolygonSmoothing()) glEnable(GL_POLYGON_SMOOTH); + + int line_width = wxMax(plib->m_GLMinSymbolLineWidth, (float)penWidth * 0.7); + glLineWidth(line_width); + + // Scale the pen width dependent on the platform display resolution + float nominal_line_width_pix = + wxMax(1.0, floor(plib->GetPPMM() / + 5.0)); // 0.2 mm nominal, but not less than 1 pixel + // qDebug() << nominal_line_width_pix; + line_width = wxMax(1 /*m_GLMinSymbolLineWidth*/, + (float)penWidth * nominal_line_width_pix); + glLineWidth(line_width); + +#ifndef __OCPN__ANDROID__ + if (line_width >= 2 && plib->GetGLLineSmoothing()) + glEnable(GL_LINE_SMOOTH); + else + glDisable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); +#endif + } +#endif +#if wxUSE_GRAPHICS_CONTEXT + if (renderToGCDC) { + pen = wxThePenList->FindOrCreatePen(penColor, penWidth, wxPENSTYLE_SOLID); + brush = wxTheBrushList->FindOrCreateBrush(penColor, wxBRUSHSTYLE_SOLID); + targetGCDC->SetPen(*pen); + targetGCDC->SetBrush(*brush); + } +#endif +} + +void RenderFromHPGL::Line(wxPoint2DDouble from, wxPoint2DDouble to) { + if (renderToDC) { + targetDC->DrawLine(wxPoint(from.m_x, from.m_y), wxPoint(to.m_x, to.m_y)); + } +#ifdef ocpnUSE_GL + if (renderToOpenGl) { + CGLShaderProgram *shader = + pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/]; + shader->Bind(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + float colorv[4]; + colorv[0] = penColor.Red() / float(256); + colorv[1] = penColor.Green() / float(256); + colorv[2] = penColor.Blue() / float(256); + colorv[3] = transparency / float(256); + + shader->SetUniform4fv("color", colorv); + + float pts[4]; + pts[0] = from.m_x; + pts[1] = from.m_y; + pts[2] = to.m_x; + pts[3] = to.m_y; + + GLint pos = shader->getAttributeLocation("position"); + glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), pts); + glEnableVertexAttribArray(pos); + + glDrawArrays(GL_LINES, 0, 2); + + shader->UnBind(); + } +#endif +#if wxUSE_GRAPHICS_CONTEXT + if (renderToGCDC) { + targetGCDC->DrawLine(wxPoint(from.m_x, from.m_y), wxPoint(to.m_x, to.m_y)); + } +#endif +} + +void RenderFromHPGL::Circle(wxPoint2DDouble center, int radius, bool filled) { + if (renderToDC) { + if (filled) + targetDC->SetBrush(*brush); + else { +#ifdef __ANDROID__ + // This is strange behavior of wxDC in wxQT + // Fill color is always "text background" color. + // So make it transparent. + targetDC->SetTextBackground(wxColor(0,0,0,0)); +#endif + targetDC->SetBrush(*wxTRANSPARENT_BRUSH); + } + targetDC->DrawCircle(wxPoint(center.m_x, center.m_y), radius); + } +#ifdef ocpnUSE_GL + if (renderToOpenGl) { + if (!m_vp) // oops, forgot to set the VP parameters + return; + + // Enable anti-aliased lines, at best quality + glEnable(GL_BLEND); + + float coords[8]; + coords[0] = center.m_x - radius; + coords[1] = center.m_y + radius; + coords[2] = center.m_x + radius; + coords[3] = center.m_y + radius; + coords[4] = center.m_x - radius; + coords[5] = center.m_y - radius; + coords[6] = center.m_x + radius; + coords[7] = center.m_y - radius; + + glUseProgram(S52circle_filled_shader_program); + + // Get pointers to the attributes in the program. + GLint mPosAttrib = + glGetAttribLocation(S52circle_filled_shader_program, "aPos"); + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords); + glEnableVertexAttribArray(mPosAttrib); + + // Circle radius + GLint radiusloc = + glGetUniformLocation(S52circle_filled_shader_program, "circle_radius"); + glUniform1f(radiusloc, radius); + + // Circle center point + GLint centerloc = + glGetUniformLocation(S52circle_filled_shader_program, "circle_center"); + float ctrv[2]; + ctrv[0] = center.m_x; + ctrv[1] = m_vp->pix_height - center.m_y; + glUniform2fv(centerloc, 1, ctrv); + + // Circle fill color + float colorv[4]; + colorv[3] = 0.0; // transparent default + + if (brush) { + colorv[0] = brush->GetColour().Red() / float(256); + colorv[1] = brush->GetColour().Green() / float(256); + colorv[2] = brush->GetColour().Blue() / float(256); + if (filled) colorv[3] = 1.0; + } + + GLint colloc = + glGetUniformLocation(S52circle_filled_shader_program, "circle_color"); + glUniform4fv(colloc, 1, colorv); + + // Border color + float bcolorv[4]; + bcolorv[0] = penColor.Red() / float(256); + bcolorv[1] = penColor.Green() / float(256); + bcolorv[2] = penColor.Blue() / float(256); + bcolorv[3] = penColor.Alpha() / float(256); + + GLint bcolloc = + glGetUniformLocation(S52circle_filled_shader_program, "border_color"); + glUniform4fv(bcolloc, 1, bcolorv); + + // Border Width + float nominal_line_width_pix = + wxMax(1.0, floor(plib->GetPPMM() / + 5.0)); // 0.2 mm nominal, but not less than 1 pixel + float line_width = wxMax(1 /*g_GLMinSymbolLineWidth*/, + (float)penWidth * nominal_line_width_pix); + + GLint borderWidthloc = + glGetUniformLocation(S52circle_filled_shader_program, "border_width"); + glUniform1f(borderWidthloc, line_width); + + // Perform the actual drawing. + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // Enable anti-aliased lines, at best quality + glDisable(GL_BLEND); + } +#endif +#if wxUSE_GRAPHICS_CONTEXT + if (renderToGCDC) { + if (filled) + targetGCDC->SetBrush(*brush); + else + targetGCDC->SetBrush(*wxTRANSPARENT_BRUSH); + + targetGCDC->DrawCircle(wxPoint(center.m_x, center.m_y), radius); + + // wxGCDC doesn't update min/max X/Y properly for DrawCircle. + targetGCDC->SetPen(*wxTRANSPARENT_PEN); + targetGCDC->DrawPoint(center.m_x - radius, center.m_y); + targetGCDC->DrawPoint(center.m_x + radius, center.m_y); + targetGCDC->DrawPoint(center.m_x, center.m_y - radius); + targetGCDC->DrawPoint(center.m_x, center.m_y + radius); + targetGCDC->SetPen(*pen); + } +#endif +} + +void RenderFromHPGL::Polygon() { + if (renderToDC) { + unsigned char rtrans = transparency; +#if defined(__WXMSW__) || defined(__WXQT__) + rtrans = 255; +#endif + penColor.Set(penColor.Red(), penColor.Green(), penColor.Blue(), rtrans); + pen = wxThePenList->FindOrCreatePen(penColor, penWidth, wxPENSTYLE_SOLID); + brush = wxTheBrushList->FindOrCreateBrush(penColor, wxBRUSHSTYLE_SOLID); + targetDC->SetPen(*pen); + targetDC->SetBrush(*brush); + targetDC->DrawPolygon(noPoints, polygon); + } +#ifdef ocpnUSE_GL + if (renderToOpenGl) { + penColor.Set(penColor.Red(), penColor.Green(), penColor.Blue(), + transparency); + pen = wxThePenList->FindOrCreatePen(penColor, penWidth, wxPENSTYLE_SOLID); + brush = wxTheBrushList->FindOrCreateBrush(penColor, wxBRUSHSTYLE_SOLID); + DrawPolygon(noPoints, polygon, 0, 0, 1.0, 0); + } +#endif // OpenGL + +#if wxUSE_GRAPHICS_CONTEXT + if (renderToGCDC) { + penColor.Set(penColor.Red(), penColor.Green(), penColor.Blue(), + transparency); + pen = wxThePenList->FindOrCreatePen(penColor, penWidth, wxPENSTYLE_SOLID); + brush = wxTheBrushList->FindOrCreateBrush(penColor, wxBRUSHSTYLE_SOLID); + targetGCDC->SetPen(*pen); + targetGCDC->SetBrush(*brush); + + targetGCDC->DrawPolygon(noPoints, polygon); + } +#endif +} + +void RenderFromHPGL::RotatePoint(wxPoint2DDouble &point, wxPoint origin, double angle) { + if (angle == 0.) return; + double sin_rot = sin(angle * PI / 180.); + double cos_rot = cos(angle * PI / 180.); + + double xp = + ((point.m_x - origin.x) * cos_rot) - ((point.m_y - origin.y) * sin_rot); + double yp = + ((point.m_x - origin.x) * sin_rot) + ((point.m_y - origin.y) * cos_rot); + + point.m_x = (int)xp + origin.x; + point.m_y = (int)yp + origin.y; +} + +bool RenderFromHPGL::Render(char *str, char *col, wxPoint2DDouble &r, wxPoint &pivot, + wxPoint origin, float scale, double rot_angle, + bool bSymbol) { + wxPoint2DDouble lineStart; + wxPoint2DDouble lineEnd; + + scaleFactor = 100.0 / plib->GetPPMM(); + + scaleFactor /= scale; + scaleFactor /= g_scaminScale; + + if (bSymbol) scaleFactor /= plib->GetRVScaleFactor(); + + // SW is not always defined, cf. US/US4CA17M/US4CA17M.000 + penWidth = 1; + + wxStringTokenizer commands(wxString(str, wxConvUTF8), _T(";")); + while (commands.HasMoreTokens()) { + wxString command = commands.GetNextToken(); + wxString arguments = command.Mid(2); + command = command.Left(2); + + if (command == _T("SP")) { + S52color *color = + plib->getColor(findColorNameInRef(arguments.GetChar(0), col)); + penColor = wxColor(color->R, color->G, color->B); + brushColor = penColor; + continue; + } + if (command == _T("SW")) { + arguments.ToLong(&penWidth); + continue; + } + if (command == _T("ST")) { + long transIndex; + arguments.ToLong(&transIndex); + transparency = (4 - transIndex) * 64; + transparency = wxMin(transparency, 255); + transparency = wxMax(0, transparency); + continue; + } + if (command == _T("PU")) { + SetPen(); + lineStart = ParsePoint(arguments); + RotatePoint(lineStart, origin, rot_angle); + lineStart -= pivot; + lineStart.m_x /= scaleFactor; + lineStart.m_y /= scaleFactor; + lineStart += r; + continue; + } + if (command == _T("PD")) { + if (arguments.Length() == 0) { + lineEnd = lineStart; + lineEnd.m_x++; + } else { + lineEnd = ParsePoint(arguments); + RotatePoint(lineEnd, origin, rot_angle); + lineEnd -= pivot; + lineEnd.m_x /= scaleFactor; + lineEnd.m_y /= scaleFactor; + lineEnd += r; + } + Line(lineStart, lineEnd); + lineStart = lineEnd; // For next line. + continue; + } + if (command == _T("CI")) { + long radius; + arguments.ToLong(&radius); + radius = (int)radius / scaleFactor; + Circle(lineStart, radius); + continue; + } + if (command == _T("PM")) { + noPoints = 1; + polygon[0] = wxPoint(lineStart.m_x, lineStart.m_y); + + if (arguments == _T("0")) { + do { + command = commands.GetNextToken(); + arguments = command.Mid(2); + command = command.Left(2); + + if (command == _T("AA")) { + wxLogWarning(_T("RenderHPGL: AA instruction not implemented.")); + } + if (command == _T("CI")) { + long radius; + arguments.ToLong(&radius); + radius = (int)radius / scaleFactor; + Circle(lineStart, radius, HPGL_FILLED); + } + if (command == _T("PD")) { + wxStringTokenizer points(arguments, _T(",")); + while (points.HasMoreTokens()) { + long x, y; + points.GetNextToken().ToLong(&x); + points.GetNextToken().ToLong(&y); + lineEnd = wxPoint(x, y); + RotatePoint(lineEnd, origin, rot_angle); + lineEnd -= pivot; + lineEnd.m_x /= scaleFactor; + lineEnd.m_y /= scaleFactor; + lineEnd += r; + polygon[noPoints++] = wxPoint(lineEnd.m_x, lineEnd.m_y); + } + } + } while (command != _T("PM")); + } + continue; + } + if (command == _T("FP")) { + SetPen(); + Polygon(); + continue; + } + + // Only get here if non of the other cases did a continue. + // wxString msg( _T("RenderHPGL: The '%s' instruction is not + // implemented.") ); msg += wxString( command ); wxLogWarning( msg ); + } + + transparency = 255; + +#ifdef ocpnUSE_GL + if (renderToOpenGl) { + glDisable(GL_BLEND); + glUseProgram(0); + } +#endif + + return true; +} + +wxArrayPtrVoid s52gTesselatorVertices; + +void RenderFromHPGL::DrawPolygon(int n, wxPoint points[], wxCoord xoffset, + wxCoord yoffset, float scale, float angle) { +#ifdef ocpnUSE_GL + // if( 0 ) + // dc->DrawPolygon( n, points, xoffset, yoffset ); + // else + { +#ifdef __WXQT__ + glDisable(GL_LINE_SMOOTH); + glDisable(GL_POLYGON_SMOOTH); + glDisable(GL_BLEND); + +#else + glEnable(GL_LINE_SMOOTH); + glEnable(GL_POLYGON_SMOOTH); + glEnable(GL_BLEND); + +#endif + + // ConfigurePen(); + glLineWidth(pen->GetWidth()); + + glEnable(GL_BLEND); + + if (n > 4) + DrawPolygonTessellated(n, points, xoffset, yoffset); + else { // n = 3 or 4, most common case for pre-tesselated shapes + + // Grow the work buffer as necessary + if (workBufSize < (size_t)n * 2) { + workBuf = (float *)realloc(workBuf, (n * 4) * sizeof(float)); + workBufSize = n * 4; + } + + for (int i = 0; i < n; i++) { + workBuf[i * 2] = (points[i].x * scale); // + xoffset; + workBuf[i * 2 + 1] = (points[i].y * scale); // + yoffset; + } + + CGLShaderProgram *shader = + pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/]; + shader->Bind(); + + // Get pointers to the attributes in the program. + GLint mPosAttrib = shader->getAttributeLocation("position"); + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, workBuf); + glEnableVertexAttribArray(mPosAttrib); + + // Border color + float bcolorv[4]; + bcolorv[0] = pen->GetColour().Red() / float(256); + bcolorv[1] = pen->GetColour().Green() / float(256); + bcolorv[2] = pen->GetColour().Blue() / float(256); + bcolorv[3] = pen->GetColour().Alpha() / float(256); + + shader->SetUniform4fv("color", bcolorv); + + // Perform the actual drawing. + glDrawArrays(GL_LINE_LOOP, 0, n); + + // Fill color + bcolorv[0] = brush->GetColour().Red() / float(256); + bcolorv[1] = brush->GetColour().Green() / float(256); + bcolorv[2] = brush->GetColour().Blue() / float(256); + bcolorv[3] = brush->GetColour().Alpha() / float(256); + + shader->SetUniform4fv("color", bcolorv); + + // For the simple common case of a convex rectangle... + // swizzle the array points to enable GL_TRIANGLE_STRIP + if (n == 4) { + float x1 = workBuf[4]; + float y1 = workBuf[5]; + workBuf[4] = workBuf[6]; + workBuf[5] = workBuf[7]; + workBuf[6] = x1; + workBuf[7] = y1; + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } else if (n == 3) { + glDrawArrays(GL_TRIANGLES, 0, 3); + } + + shader->UnBind(); + } + + glDisable(GL_LINE_SMOOTH); + glDisable(GL_POLYGON_SMOOTH); + glDisable(GL_BLEND); + } +#endif +} + +#ifdef ocpnUSE_GL + +// GL callbacks + +typedef union { + GLdouble data[6]; + struct sGLvertex { + GLdouble x; + GLdouble y; + GLdouble z; + GLdouble r; + GLdouble g; + GLdouble b; + } info; +} GLvertex; + +// GLSL callbacks + +static std::list odc_combine_work_data; +static void xs52_combineCallbackD(GLdouble coords[3], GLdouble *vertex_data[4], + GLfloat weight[4], GLdouble **dataOut, + void *data) { + // double *vertex = new double[3]; + // odc_combine_work_data.push_back(vertex); + // memcpy(vertex, coords, 3*(sizeof *coords)); + // *dataOut = vertex; +} + +void xs52_vertexCallbackD_GLSL(GLvoid *vertex, void *data) { + RenderFromHPGL *plib = (RenderFromHPGL *)data; + + // Grow the work buffer if necessary + if (plib->s_odc_tess_vertex_idx > plib->s_odc_tess_buf_len - 8) { + int new_buf_len = plib->s_odc_tess_buf_len + 100; + GLfloat *tmp = plib->s_odc_tess_work_buf; + + plib->s_odc_tess_work_buf = (GLfloat *)realloc( + plib->s_odc_tess_work_buf, new_buf_len * sizeof(GLfloat)); + if (NULL == plib->s_odc_tess_work_buf) { + free(tmp); + tmp = NULL; + } else + plib->s_odc_tess_buf_len = new_buf_len; + } + + GLdouble *pointer = (GLdouble *)vertex; + + plib->s_odc_tess_work_buf[plib->s_odc_tess_vertex_idx++] = (float)pointer[0]; + plib->s_odc_tess_work_buf[plib->s_odc_tess_vertex_idx++] = (float)pointer[1]; + + plib->s_odc_nvertex++; +} + +void xs52_beginCallbackD_GLSL(GLenum mode, void *data) { + RenderFromHPGL *plib = (RenderFromHPGL *)data; + plib->s_odc_tess_vertex_idx_this = plib->s_odc_tess_vertex_idx; + plib->s_odc_tess_mode = mode; + plib->s_odc_nvertex = 0; +} + +void xs52_endCallbackD_GLSL(void *data) { + // qDebug() << "End" << s_odc_nvertex << s_odc_tess_buf_len << + // s_odc_tess_vertex_idx << s_odc_tess_vertex_idx_this; End 5 100 10 0 + RenderFromHPGL *plib = (RenderFromHPGL *)data; + + CGLShaderProgram *shader = pCcolor_tri_shader_program[0 /*GetCanvasIndex()*/]; + shader->Bind(); + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + float *bufPt = &(plib->s_odc_tess_work_buf[plib->s_odc_tess_vertex_idx_this]); + GLint pos = glGetAttribLocation(S52color_tri_shader_program, "position"); + glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), bufPt); + glEnableVertexAttribArray(pos); + + float colorv[4]; + wxColour c = plib->getBrush()->GetColour(); + + colorv[0] = c.Red() / float(256); + colorv[1] = c.Green() / float(256); + colorv[2] = c.Blue() / float(256); + colorv[3] = c.Alpha() / float(256); + + shader->SetUniform4fv("color", colorv); + + glDrawArrays(plib->s_odc_tess_mode, 0, plib->s_odc_nvertex); + shader->UnBind(); +} + +#endif // #ifdef ocpnUSE_GL + +#ifdef __WXMSW__ +// int filterException(int code, PEXCEPTION_POINTERS ex) { +// //std::cout << "Filtering " << std::hex << code << std::endl; +// return EXCEPTION_EXECUTE_HANDLER; +// } +#endif + +void RenderFromHPGL::DrawPolygonTessellated(int n, wxPoint points[], + wxCoord xoffset, wxCoord yoffset) { +#ifndef ocpnUSE_GL + // dc->DrawPolygon( n, points, xoffset, yoffset ); +#else +#if !defined(ocpnUSE_GLES) || \ + defined(USE_ANDROID_GLES2) // tessalator in glues is broken + if (n < 5) +#endif + { + DrawPolygon(n, points, xoffset, yoffset, 1.0, 0); + return; + } + + m_tobj = gluNewTess(); + s_odc_tess_vertex_idx = 0; + + gluTessCallback(m_tobj, GLU_TESS_VERTEX_DATA, + (_GLUfuncptrA)&xs52_vertexCallbackD_GLSL); + gluTessCallback(m_tobj, GLU_TESS_BEGIN_DATA, + (_GLUfuncptrA)&xs52_beginCallbackD_GLSL); + gluTessCallback(m_tobj, GLU_TESS_END_DATA, + (_GLUfuncptrA)&xs52_endCallbackD_GLSL); + gluTessCallback(m_tobj, GLU_TESS_COMBINE_DATA, + (_GLUfuncptrA)&xs52_combineCallbackD); + + gluTessNormal(m_tobj, 0, 0, 1); + gluTessProperty(m_tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO); + + gluTessBeginPolygon(m_tobj, this); + gluTessBeginContour(m_tobj); + + // ViewPort *pvp = cc1->GetpVP(); + double *p = new double[n * 3]; + for (int i = 0; i < n; i++) { + // if(fabs(pvp->rotation) > 0.01){ + // float cx = pvp->pix_width/2.; + // float cy = pvp->pix_height/2.; + // float c = cosf(pvp->rotation ); + // float s = sinf(pvp->rotation ); + // float xn = points[i].x - cx; + // float yn = points[i].y - cy; + // p[0] = xn*c - yn*s + cx; + // p[1] = xn*s + yn*c + cy; + // p[2] = 0; + // } + // else + p[3 * i] = points[i].x, p[3 * i + 1] = points[i].y, p[3 * i + 2] = 0; + + gluTessVertex(m_tobj, p + 3 * i, p + 3 * i); + } + +#ifdef __WXMSW__ + // https://stackoverflow.com/questions/7049502/c-try-and-try-catch-finally + __try { + gluTessEndContour(m_tobj); + gluTessEndPolygon(m_tobj); + gluDeleteTess(m_tobj); + } + // __except(filterException(GetExceptionCode(), GetExceptionInformation())) + // { + __except (EXCEPTION_EXECUTE_HANDLER) { + int yyp = 4; + } +#else + gluTessEndContour(m_tobj); + gluTessEndPolygon(m_tobj); + gluDeleteTess(m_tobj); +#endif + + delete[] p; + + // for(std::list::iterator i = + // odc_combine_work_data.begin(); i!=odc_combine_work_data.end(); + // i++) + // delete [] *i; + // odc_combine_work_data.clear(); + +#endif +} + +#ifdef ocpnUSE_GL +void PrepareS52ShaderUniforms(VPointCompat *vp) { + loadS52Shaders(); + + mat4x4 m; + float vp_transform[16]; + mat4x4_identity(m); + mat4x4_scale_aniso((float (*)[4])vp_transform, m, 2.0 / (float)vp->pix_width, + -2.0 / (float)vp->pix_height, 1.0); + // Rotate + mat4x4 Q; + mat4x4_rotate_Z(Q, (float (*)[4])vp_transform, vp->rotation); + mat4x4_translate_in_place(Q, -vp->pix_width / 2.0, -vp->pix_height / 2.0, 0); + + mat4x4 I; + mat4x4_identity(I); + + // glUseProgram(S52color_tri_shader_program); + // GLint matloc = glGetUniformLocation(S52color_tri_shader_program, + // "MVMatrix"); glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q); + // GLint transloc = + // glGetUniformLocation(S52color_tri_shader_program, "TransformMatrix"); + // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I); + + glUseProgram(S52texture_2D_shader_program); + GLint matloc = glGetUniformLocation(S52texture_2D_shader_program, "MVMatrix"); + glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q); + GLint transloc = + glGetUniformLocation(S52texture_2D_shader_program, "TransformMatrix"); + glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I); + + glUseProgram(S52texture_2D_ColorMod_shader_program); + matloc = + glGetUniformLocation(S52texture_2D_ColorMod_shader_program, "MVMatrix"); + glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q); + transloc = glGetUniformLocation(S52texture_2D_ColorMod_shader_program, + "TransformMatrix"); + glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I); + + glUseProgram(S52circle_filled_shader_program); + matloc = glGetUniformLocation(S52circle_filled_shader_program, "MVMatrix"); + glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q); + transloc = + glGetUniformLocation(S52circle_filled_shader_program, "TransformMatrix"); + glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I); + + glUseProgram(S52ring_shader_program); + matloc = glGetUniformLocation(S52ring_shader_program, "MVMatrix"); + glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q); + transloc = glGetUniformLocation(S52ring_shader_program, "TransformMatrix"); + glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I); + + glUseProgram(S52Dash_shader_program); + matloc = glGetUniformLocation(S52Dash_shader_program, "MVMatrix"); + glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q); + transloc = glGetUniformLocation(S52Dash_shader_program, "TransformMatrix"); + glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I); + + glUseProgram(S52AP_shader_program); + matloc = glGetUniformLocation(S52AP_shader_program, "MVMatrix"); + glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q); + transloc = glGetUniformLocation(S52AP_shader_program, "TransformMatrix"); + glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I); + + glUseProgram(0); + + // Load special shaders + loadCShaders(0); + + CGLShaderProgram *shader = pCcolor_tri_shader_program[0]; + shader->Bind(); + shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)Q); + shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I); + shader->UnBind(); + + shader = pCtexture_2D_shader_program[0]; + shader->Bind(); + shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)Q); + shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I); + shader->UnBind(); + + shader = pCtexture_2D_Color_shader_program[0]; + shader->Bind(); + shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)Q); + shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I); + shader->UnBind(); +} +#endif + +// CRC calculation for a byte buffer + +static unsigned int crc_32_tab[] = + {/* CRC polynomial 0xedb88320 */ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; + +#define UPDC32(octet, crc) \ + (crc_32_tab[((crc) ^ ((unsigned char)octet)) & 0xff] ^ ((crc) >> 8)) + +unsigned int crc32buf(unsigned char *buf, size_t len) { + unsigned int oldcrc32; + + oldcrc32 = 0xFFFFFFFF; + + for (; len; --len, ++buf) { + oldcrc32 = UPDC32(*buf, oldcrc32); + } + + return ~oldcrc32; +} diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.h new file mode 100644 index 0000000..2c2ed7c --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.h @@ -0,0 +1,737 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: S52 Presentation Library + * Author: David Register + * + *************************************************************************** + * Copyright (C) 2010 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +#ifndef _S52PLIB_H_ +#define _S52PLIB_H_ + +#include +#include "s52s57.h" //types + +class wxGLContext; + +#include "LLRegion.h" +#include "DepthFont.h" +#include "chartsymbols.h" +#include "TexFont.h" + +#include // supplemental, for Mac +#include + +// ChartType constants +typedef enum S52_ChartTypeEnum { + S52_CHART_TYPE_UNKNOWN = 0, + S52_CHART_TYPE_DUMMY, + S52_CHART_TYPE_DONTCARE, + S52_CHART_TYPE_KAP, + S52_CHART_TYPE_GEO, + S52_CHART_TYPE_S57, + S52_CHART_TYPE_CM93, + S52_CHART_TYPE_CM93COMP, + S52_CHART_TYPE_PLUGIN, + S52_CHART_TYPE_MBTILES +} _S52_ChartTypeEnum; + +// Correct some deficincies in MacOS OpenGL include files +#ifdef __WXOSX__ +typedef void (*PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers); +typedef void (*PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); +typedef void (*PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers); +typedef void (*PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, + GLint *params); +typedef void (*PFNGLDELETERENDERBUFFERSEXTPROC)(GLsizei n, + const GLuint *renderbuffers); +typedef void (*PFNGLDELETEFRAMEBUFFERSEXTPROC)(GLsizei n, + const GLuint *framebuffers); +typedef void (*PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, + GLint xoffset, GLsizei width, + GLenum format, + GLsizei imageSize, + const GLvoid *data); +typedef void (*PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, + GLvoid *img); +typedef GLenum (*PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)(GLenum target); +typedef void (*PFNGLBINDRENDERBUFFEREXTPROC)(GLenum target, + GLuint renderbuffer); +typedef void (*PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, + const GLvoid *data, GLenum usage); +typedef void (*PFNGLGENFRAMEBUFFERSEXTPROC)(GLsizei n, GLuint *framebuffers); +typedef void (*PFNGLGENRENDERBUFFERSEXTPROC)(GLsizei n, GLuint *renderbuffers); +typedef void (*PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)(GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, GLint level); +typedef void (*PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, + GLenum internalformat, + GLsizei width, GLsizei height, + GLint border, GLsizei imageSize, + const GLvoid *data); +typedef void (*PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)(GLenum target, + GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer); +typedef void (*PFNGLRENDERBUFFERSTORAGEEXTPROC)(GLenum target, + GLenum internalformat, + GLsizei width, GLsizei height); +typedef void (*PFNGLBINDFRAMEBUFFEREXTPROC)(GLenum target, GLuint framebuffer); +#endif + +#define INVALID_COORD (-2147483647 - 1) + +// wxWindows Hash Map Declarations +#include +class RuleHash; + +WX_DECLARE_HASH_MAP(wxString, Rule *, wxStringHash, wxStringEqual, RuleHash); + +WX_DEFINE_SORTED_ARRAY(LUPrec *, wxArrayOfLUPrec); + +WX_DECLARE_LIST(S52_TextC, TextObjList); + +struct CARC_Buffer { + unsigned char color[3][4]; + float line_width[3]; + int steps; + + int size; + float *data; +}; +WX_DECLARE_STRING_HASH_MAP(CARC_Buffer, CARC_Hash); +WX_DECLARE_STRING_HASH_MAP(int, CARC_DL_Hash); + +struct SoundingSym { + Rule *prule; + wxPoint2DDouble r; + wxUint32 color_RGB; +}; + +class PixelCache; + +class RenderFromHPGL; +class TexFont; +class wxFileConfig; + +class noshow_element { +public: + char obj[7]; +}; + +WX_DECLARE_OBJARRAY(noshow_element, ArrayOfNoshow); + +//----------------------------------------------------------------------------- +// LUP Array container, and friends +//----------------------------------------------------------------------------- +typedef struct _LUPHashIndex { + int n_start; + int count; +} LUPHashIndex; + +WX_DECLARE_STRING_HASH_MAP(LUPHashIndex *, LUPArrayIndexHash); + +class s52plib; // Forward +extern s52plib *ps52plib; ///< Global instance + +/** in s52cnsy */ +extern bool GetDoubleAttr(S57Obj *obj, const char *AttrName, double &val); + +class LUPArrayContainer { +public: + LUPArrayContainer(); + ~LUPArrayContainer(); + + wxArrayOfLUPrec *GetLUPArray(void) { return LUPArray; } + LUPHashIndex *GetArrayIndexHelper(const char *objectName); + +private: + wxArrayOfLUPrec *LUPArray; // Sorted Array + LUPArrayIndexHash IndexHash; +}; + +class VPointCompat { +public: + int pix_width; + int pix_height; + double view_scale_ppm; + double rotation; + double clat; + double clon; + double chart_scale; + wxRect rv_rect; + double ref_scale; + double display_scale; +}; + +typedef struct { + TexFont *cache; + wxFont *key; +} TexFontCache; + +void toSM_plib(double lat, double lon, double lat0, double lon0, double *x, + double *y); + +void fromSM_plib(double x, double y, double lat0, double lon0, double *lat, + double *lon); +//----------------------------------------------------------------------------- +// s52plib definition +//----------------------------------------------------------------------------- +class s52plib { +public: + s52plib(const wxString &PLib, bool b_forceLegacy = false); + ~s52plib(); + + // TODO: SetPPM, SetDisplayWidth etc. should be combined to be set together by + // pointing them to info about current monitor + /** + * Set pixels per millimeter for symbol rendering. + * @param ppmm Logical pixels per millimeter of display. + */ + void SetPPMM(float ppmm); + void SetDisplayWidth(size_t pixels) { m_display_width = pixels; } + float GetPPMM() { return canvas_pix_per_mm; } + void SetDIPFactor(double factor); + void SetContentScaleFactor(double factor); + + void SetOCPNVersion(int major, int minor, int patch); + + double GetRVScaleFactor() { return m_rv_scale_factor; } + + LUPrec *S52_LUPLookup(LUPname LUP_name, const char *objectName, S57Obj *pObj, + bool bStrict = 0); + int _LUP2rules(LUPrec *LUP, S57Obj *pObj); + S52color *getColor(const char *colorName); + wxColour getwxColour(const wxString &colorName); + + void UpdateMarinerParams(void); + void ClearCNSYLUPArray(void); + + void GenerateStateHash(); + long GetStateHash() { return m_state_hash; } + + void SetPLIBColorScheme(wxString scheme, const ChartCtx &ctx); + void SetPLIBColorScheme(ColorScheme cs, const ChartCtx &ctx); + wxString GetPLIBColorScheme(void) { return m_ColorScheme; } + + void SetGLRendererString(const wxString &renderer); + wxString GetGLRendererString() { return m_renderer_string; } + void SetGLOptions(bool b_useStencil, bool b_useStencilAP, bool b_useScissors, + bool b_useFBO, bool b_useVBO, int nTextureFormat, + float MinCartographicLineWidth, float MinSymbolLineWidth); + void SetUseGLSL(bool useGLSL) { m_useGLSL = useGLSL; } + + bool ObjectRenderCheck(ObjRazRules *rzRules); + bool ObjectRenderCheckRules(ObjRazRules *rzRules, bool check_noshow = false); + bool ObjectRenderCheckPos(ObjRazRules *rzRules); + bool ObjectRenderCheckPosReduced(ObjRazRules *rzRules); + bool ObjectRenderCheckCat(ObjRazRules *rzRules); + bool ObjectRenderCheckCS(ObjRazRules *rzRules); + bool ObjectRenderCheckDates(ObjRazRules *rzRules); + + static void DestroyLUP(LUPrec *pLUP); + static void ClearRulesCache(Rule *pR); + DisCat findLUPDisCat(const char *objectName, LUPname TNAM); + + // Temporarily save/restore the current colortable index + // Useful for Thumbnail rendering + void SaveColorScheme(void) { m_colortable_index_save = m_colortable_index; } + void RestoreColorScheme(void) {} + + // Rendering stuff + void PrepareForRender(VPointCompat *vp); + void PrepareForRender(void); + void AdjustTextList(int dx, int dy, int screenw, int screenh); + void ClearTextList(void); + int SetLineFeaturePriority(ObjRazRules *rzRules, int npriority); + void FlushSymbolCaches(const ChartCtx &ctx); + + // For DC's + int RenderObjectToDC(wxDC *pdc, ObjRazRules *rzRules); + int RenderObjectToDCText(wxDC *pdc, ObjRazRules *rzRules); + int RenderAreaToDC(wxDC *pdc, ObjRazRules *rzRules, + render_canvas_parms *pb_spec); + + // Accessors + bool GetShowSoundings() { return m_bShowSoundg; } + void SetShowSoundings(bool f) { + m_bShowSoundg = f; + GenerateStateHash(); + } + + bool GetShowS57Text() { return m_bShowS57Text; } + void SetShowS57Text(bool f) { + m_bShowS57Text = f; + GenerateStateHash(); + } + + bool GetShowS57ImportantTextOnly() { return m_bShowS57ImportantTextOnly; } + void SetShowS57ImportantTextOnly(bool f) { + m_bShowS57ImportantTextOnly = f; + GenerateStateHash(); + } + + void SetLightsOff(bool val) { m_lightsOff = val; } + bool GetLightsOff() { return m_lightsOff; } + + void SetAnchorOn(bool val); + bool GetAnchorOn(); + + void SetQualityOfData(bool val); + bool GetQualityOfData(); + + void SetScaleFactorExp(double ChartScaleFactorExp); + void SetScaleFactorZoomMod(int chart_zoom_modifier_vector); + + int GetMajorVersion(void) { return m_VersionMajor; } + int GetMinorVersion(void) { return m_VersionMinor; } + + void SetTextOverlapAvoid(bool f) { m_bDeClutterText = f; } + void SetShowNationalText(bool f) { m_bShowNationalTexts = f; } + void SetShowAtonText(bool f) { m_bShowAtonText = f; } + void SetShowLdisText(bool f) { m_bShowLdisText = f; } + void SetExtendLightSectors(bool f) { m_bExtendLightSectors = f; } + + void SetDisplayCategory(enum _DisCat cat); + DisCat GetDisplayCategory() { return m_nDisplayCategory; } + + void SetGLPolygonSmoothing(bool bset) { m_GLPolygonSmoothing = bset; } + bool GetGLPolygonSmoothing() { return m_GLPolygonSmoothing; } + void SetGLLineSmoothing(bool bset) { m_GLLineSmoothing = bset; } + bool GetGLLineSmoothing() { return m_GLLineSmoothing; } + + wxArrayOfLUPrec *SelectLUPARRAY(LUPname TNAM); + LUPArrayContainer *SelectLUPArrayContainer(LUPname TNAM); + + void DestroyPatternRuleNode(Rule *pR); + void DestroyRuleNode(Rule *pR); + static void DestroyRulesChain(Rules *top); + + // For OpenGL + int RenderObjectToGL(const wxGLContext &glcc, ObjRazRules *rzRules); + int RenderAreaToGL(const wxGLContext &glcc, ObjRazRules *rzRules); + int RenderObjectToGLText(const wxGLContext &glcc, ObjRazRules *rzRules); + + bool EnableGLLS(bool benable); + + bool IsObjNoshow(const char *objcl); + void AddObjNoshow(const char *objcl); + void RemoveObjNoshow(const char *objcl); + void ClearNoshow(void); + void SaveObjNoshow() { m_saved_noshow = m_noshow_array; }; + void RestoreObjNoshow() { m_noshow_array = m_saved_noshow; }; + + void SetVPointCompat(int pix_width, int pix_height, double view_scale_ppm, + double rotation, double clat, double clon, + double chart_scale, wxRect rv_rect, LLBBox &bbox, + double ref_scale, double display_scale); + + // Todo accessors + LUPname m_nSymbolStyle; + LUPname m_nBoundaryStyle; + bool m_bOK; + + bool m_bShowSoundg; + bool m_bShowMeta; + bool m_bShowS57Text; + bool m_bUseSCAMIN; + bool m_bUseSUPER_SCAMIN; + bool m_bShowAtonText; + bool m_bShowLdisText; + bool m_bExtendLightSectors; + bool m_bShowS57ImportantTextOnly; + bool m_bDeClutterText; + bool m_bShowNationalTexts; + int m_nSoundingFactor; + double m_SoundingsScaleFactor; + int m_nTextFactor; + double m_TextScaleFactor; + int m_SoundingsPointSize; + double m_SoundingsFontSizeMM; + double m_soundFontDelta; + double m_ChartScaleFactorExp; + int m_chart_zoom_modifier_vector; + double m_dipfactor; + double m_ContentScaleFactor; + double m_FinalTextScaleFactor; + + int m_VersionMajor; + int m_VersionMinor; + + int m_nDepthUnitDisplay; + bool m_isSoundingFontSet; + int m_sounding_char_width; + int m_sounding_char_height; + int m_sounding_char_descent; +#ifdef ocpnUSE_GL + GLint m_sounding_shader_uni_color; + GLint m_sounding_shader_uni_uTex; + GLint m_sounding_shader_attr_position; + GLint m_sounding_shader_attr_aUV; + GLint m_sounding_shader_uni_transform; +#endif + // Preferred display unit for vertical heights (meters/feet). + // 0 = meters, 1 = feet + int m_nHeightUnitDisplay; + + // Library data + wxArrayPtrVoid *pAlloc; + + RuleHash *_line_sym; // line symbolisation rules + RuleHash *_patt_sym; // pattern symbolisation rules + RuleHash *_cond_sym; // conditional symbolisation rules + RuleHash *_symb_symR; // symbol symbolisation rules, Raster + + LUPArrayContainer *line_LAC; + LUPArrayContainer *areaPlain_LAC; + LUPArrayContainer *areaSymbol_LAC; + LUPArrayContainer *pointSimple_LAC; + LUPArrayContainer *pointPaper_LAC; + + wxArrayOfLUPrec *condSymbolLUPArray; // Dynamic Conditional Symbology + + wxArrayPtrVoid *pOBJLArray; // Used for Display Filtering + std::vector OBJLDescriptions; + + RuleHash *_symb_sym; // symbol symbolisation rules + std::unordered_map m_natsur_hash; + // hash table for cacheing NATSUR string values + // from int attributes + + int m_myConfig; + + double lastLightLat; + double lastLightLon; + float m_GLMinCartographicLineWidth; + float m_GLMinSymbolLineWidth; + + ChartSymbols m_chartSymbols; + + void PLIB_LoadS57GlobalConfig(wxFileConfig *pconfig); + void PLIB_LoadS57ObjectConfig(wxFileConfig *pconfig); + void SetReducedBBox(LLBBox box) { reducedBBox = box; } + + void RenderTex(char *str, char *col, wxPoint &r, wxPoint &pivot, + wxPoint origin, float scale, double rot_angle, float sym_len, + float sym_height, float seg_len); + int BuildLCSymbolTexture(char *str, char *col, wxPoint &r, wxPoint &pivot, + wxPoint origin, float scale, float sym_len, + float sym_height); + +private: + int S52_load_Plib(const wxString &PLib, bool b_forceLegacy); + bool S52_flush_Plib(); + + void PLIB_LoadS57Config(); + + void InitializeNatsurHash(); + bool PreloadOBJLFromCSV(const wxString &csv_file); + + int DoRenderObject(wxDC *pdcin, ObjRazRules *rzRules); + int DoRenderObjectTextOnly(wxDC *pdcin, ObjRazRules *rzRules); + + // Area Renderers + int RenderToBufferAC(ObjRazRules *rzRules, Rules *rules, + render_canvas_parms *pb_spec); + int RenderToBufferAP(ObjRazRules *rzRules, Rules *rules, + render_canvas_parms *pb_spec); + + int RenderToGLAC(ObjRazRules *rzRules, Rules *rules); + int RenderToGLAC_GLSL(ObjRazRules *rzRules, Rules *rules); + + int RenderToGLAP(ObjRazRules *rzRules, Rules *rules); + int RenderToGLAP_GLSL(ObjRazRules *rzRules, Rules *rules); + + // Object Renderers + int RenderTX(ObjRazRules *rzRules, Rules *rules); + int RenderTE(ObjRazRules *rzRules, Rules *rules); + int RenderSY(ObjRazRules *rzRules, Rules *rules); + int RenderLS(ObjRazRules *rzRules, Rules *rules); + int RenderLC(ObjRazRules *rzRules, Rules *rules); + int RenderMPS(ObjRazRules *rzRules, Rules *rules); + void RenderMPSArray(ObjRazRules *rzRules, std::vector array); + int RenderCARC(ObjRazRules *rzRules, Rules *rules); + char *RenderCS(ObjRazRules *rzRules, Rules *rules); + int RenderGLLS(ObjRazRules *rzRules, Rules *rules); + int RenderGLLC(ObjRazRules *rzRules, Rules *rules); + + int RenderCARC_VBO(ObjRazRules *rzRules, Rules *rules); + int RenderCARC_GLSL(ObjRazRules *rzRules, Rules *rules); + + void UpdateOBJLArray(S57Obj *obj); + + int reduceLOD(double LOD_meters, int nPoints, double *source, + wxPoint2DDouble **dest, int *maskIn, int **maskOut); + int ComputeWinding(ObjRazRules *rzRules, Rules *rules); + + int RenderLSLegacy(ObjRazRules *rzRules, Rules *rules); + int RenderLCLegacy(ObjRazRules *rzRules, Rules *rules); + int RenderGLLSLegacy(ObjRazRules *rzRules, Rules *rules); + int RenderGLLCLegacy(ObjRazRules *rzRules, Rules *rules); + int RenderLSPlugIn(ObjRazRules *rzRules, Rules *rules); + int RenderLCPlugIn(ObjRazRules *rzRules, Rules *rules); + int RenderLCTexture(ObjRazRules *rzRules, Rules *rules); + + int RenderLS_Dash_GLSL(ObjRazRules *rzRules, Rules *rules); + + void DrawDashLine(wxPen &pen, double x1, double y1, double x2, double y2); + + render_canvas_parms *CreatePatternBufferSpec(ObjRazRules *rzRules, + Rules *rules, bool b_revrgb, + bool b_pot = false); + + void RenderToBufferFilledPolygon(ObjRazRules *rzRules, S57Obj *obj, + S52color *c, render_canvas_parms *pb_spec, + render_canvas_parms *patt_spec); + + void draw_lc_poly_texture(wxDC *pdc, wxColor &color, int width, wxPoint2DDouble *ptp, + int *mask, int npt, float sym_len, float sym_height, + float sym_factor, Rule *draw_rule, bool winding_cw); + void draw_lc_poly(wxDC *pdc, wxColor &color, int width, wxPoint *ptp, + int *mask, int npt, float sym_len, float sym_height, + float sym_factor, Rule *draw_rule); + + bool RenderHPGL(ObjRazRules *rzRules, Rule *rule_in, wxPoint2DDouble &r, + float rot_angle = 0., double uScale = 1.0); + bool RenderRasterSymbol(ObjRazRules *rzRules, Rule *prule, wxPoint2DDouble &r, + float rot_angle = 0.); + bool RenderCachedVectorSymbol(ObjRazRules *rzRules, Rule *rule_in, wxPoint2DDouble &r, + float rot_angle, double uScale); + int BuildHPGLTexture(ObjRazRules *rzRules, Rule *rule_in, wxPoint2DDouble &r, + float rot_angle, double uScale); + + void SetupSoundingFont(); + bool RenderSoundingSymbol(ObjRazRules *rzRules, Rule *prule, wxPoint &r, + wxColor symColor, float rot_angle = 0.); + wxImage RuleXBMToImage(Rule *prule); + + bool RenderText(wxDC *pdc, S52_TextC *ptext, double x, double y, wxRect2D *pRectDrawn, + S57Obj *pobj, bool bCheckOverlap); + + bool CheckTextRectList(const wxRect2D &test_rect, S52_TextC *ptext); + int RenderT_All(ObjRazRules *rzRules, Rules *rules, bool bTX); + + int PrioritizeLineFeature(ObjRazRules *rzRules, int npriority); + + int dda_tri(wxPoint *ptp, S52color *c, render_canvas_parms *pb_spec, + render_canvas_parms *pPatt_spec); + int dda_trap(wxPoint *segs, int lseg, int rseg, int ytop, int ybot, + S52color *c, render_canvas_parms *pb_spec, + render_canvas_parms *pPatt_spec); + + LUPrec *FindBestLUP(wxArrayOfLUPrec *LUPArray, unsigned int startIndex, + unsigned int count, S57Obj *pObj, bool bStrict); + + void SetGLClipRect(const VPointCompat &vp, const wxRect &rect); + + char *_getParamVal(ObjRazRules *rzRules, char *str, char *buf, int bsz); + S52_TextC *S52_PL_parseTX(ObjRazRules *rzRules, Rules *rules, char *cmd); + char *_parseTEXT(ObjRazRules *rzRules, S52_TextC *text, char *str0); + S52_TextC *S52_PL_parseTE(ObjRazRules *rzRules, Rules *rules, char *cmd); + + Rules *StringToRules(const wxString &str_in); + void GetAndAddCSRules(ObjRazRules *rzRules, Rules *rules); + + void DestroyPattRules(RuleHash *rh); + void DestroyRules(RuleHash *rh); + void DestroyLUPArray(wxArrayOfLUPrec *pLUPArray); + + bool TextRenderCheck(ObjRazRules *rzRules); + bool inter_tri_rect(wxPoint *ptp, render_canvas_parms *pb_spec); + + bool GetPointPixArray(ObjRazRules *rzRules, wxPoint2DDouble *pd, wxPoint *pp, + int nv); + bool GetPointPixSingle(ObjRazRules *rzRules, float north, float east, + wxPoint *r); + void GetPixPointSingle(int pixx, int pixy, double *plat, double *plon); + void GetPixPointSingleNoRotate(int pixx, int pixy, double *plat, + double *plon); + bool GetDoublePointPixSingle(ObjRazRules *rzRules, float north, float east, + wxPoint2DDouble *r); + bool GetDoublePointPixArray(ObjRazRules *rzRules, wxPoint2DDouble *pd, wxPoint2DDouble *pp, + int nv); + + void GetLLFromPix(const wxPoint2DDouble &p, double *lat, double *lon); + wxPoint GetPixFromLL(double lat, double lon); + wxPoint GetPixFromLLROT(double lat, double lon, double rotation); + wxPoint2DDouble GetDoublePixFromLL(double lat, double lon); + wxPoint2DDouble GetDoublePixFromLLROT(double lat, double lon, + double rotation); + + LLBBox &GetBBox() { return BBox; } + LLBBox GetReducedBBox() { return reducedBBox; } + + wxString m_plib_file; + + /** + * The number of pixels per millimeter of the canvas. + * + * @note Set by parent, used to scale symbols/lines/patterns. + */ + float canvas_pix_per_mm; + double m_rv_scale_factor; + float m_display_size_mm; + size_t m_display_width; + + S52color m_unused_color; + wxColor m_unused_wxColor; + + bool bUseRasterSym; + + wxDC *m_pdc; // The current DC + + // #ifdef ocpnUSE_GL + wxGLContext *m_glcc; + // #endif + + int *ledge; + int *redge; + + int m_colortable_index; + int m_colortable_index_save; + + TextObjList m_textObjList; + + wxString m_ColorScheme; + + bool m_lightsOff; + bool m_anchorOn; + bool m_qualityOfDataOn; + + long m_state_hash; + + bool m_txf_ready; + int m_txf_avg_char_width; + int m_txf_avg_char_height; + CARC_Hash m_CARC_hashmap; + CARC_DL_Hash m_CARC_DL_hashmap; + RenderFromHPGL *HPGL; + + TexFont *m_txf; + DepthFont m_texSoundings; + bool m_benableGLLS; + DisCat m_nDisplayCategory; + ArrayOfNoshow m_noshow_array; + ArrayOfNoshow m_saved_noshow; + + int m_coreVersionMajor; + int m_coreVersionMinor; + int m_coreVersionPatch; + + // GL Options, set by core depending on hardware capability + bool m_useStencil; + bool m_useStencilAP; + bool m_useScissors; + bool m_useFBO; + bool m_useVBO; + bool m_GLAC_VBO; + int m_TextureFormat; + bool m_GLLineSmoothing; + bool m_GLPolygonSmoothing; + wxFont *m_soundFont; + bool m_useGLSL; + + double m_displayScale; + + VPointCompat vp_plib; + LLBBox BBox; +#define TXF_CACHE 8 + TexFontCache s_txf[TXF_CACHE]; + wxString m_renderer_string; + wxFont *ChartTextDefaultFont; + + LLBBox reducedBBox; + std::unordered_map vector_symbol_cache; + std::unordered_map lc_vector_symbol_cache; +}; + +#define HPGL_FILLED true + +class RenderFromHPGL { +public: + RenderFromHPGL(s52plib *plibarg); + ~RenderFromHPGL(); + + void SetTargetDC(wxDC *pdc); + void SetTargetOpenGl(); +#if wxUSE_GRAPHICS_CONTEXT + void SetTargetGCDC(wxGCDC *gdc); +#endif + void SetVP(VPointCompat *pVP) { m_vp = pVP; } + void SetContentScaleFactor(double factor) { m_content_scale_factor = factor; } + bool Render(char *str, char *col, wxPoint2DDouble &r, wxPoint &pivot, wxPoint origin, + float scale, double rot_angle, bool bSymbol); + wxBrush *getBrush() { return brush; } + + GLUtesselator *m_tobj; + int s_odc_tess_vertex_idx; + int s_odc_tess_vertex_idx_this; + int s_odc_tess_buf_len; + GLenum s_odc_tess_mode; + int s_odc_nvertex; + GLfloat *s_odc_tess_work_buf; + +private: + const char *findColorNameInRef(char colorCode, char *col); + void RotatePoint(wxPoint2DDouble &point, wxPoint origin, double angle); + wxPoint ParsePoint(wxString &argument); + void SetPen(); + void Line(wxPoint2DDouble from, wxPoint2DDouble to); + void Circle(wxPoint2DDouble center, int radius, bool filled = false); + void Polygon(); + + void DrawPolygonTessellated(int n, wxPoint points[], wxCoord xoffset, + wxCoord yoffset); + void DrawPolygon(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset, + float scale, float angle); + + s52plib *plib; + double scaleFactor; + + wxDC *targetDC; +#if wxUSE_GRAPHICS_CONTEXT + wxGCDC *targetGCDC; +#endif + + wxColor penColor; + wxPen *pen; + wxColor brushColor; + wxBrush *brush; + long penWidth; + int transparency; + double m_content_scale_factor; + + int noPoints; + wxPoint polygon[100]; + + float m_currentColor[4]; + + bool renderToDC; + bool renderToOpenGl; + bool renderToGCDC; + VPointCompat *m_vp; + + float *workBuf; + size_t workBufSize; + unsigned int workBufIndex; +}; + +#endif //_S52PLIB_H_ diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plibGL.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plibGL.h new file mode 100644 index 0000000..60401c9 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plibGL.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * + * Project: OpenCPN + * Purpose: Global Build Options + * Author: David Register + * + *************************************************************************** + * Copyright (C) 2010 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + *************************************************************************** + * + */ + +//---------------------------------------------------------------------------------- +// OpenGL Build options for S52PLIB +//---------------------------------------------------------------------------------- +#ifndef _S52PLIBGL_H_ +#define _S52PLIBGL_H_ + +//#include "config.h" + +#include +#include + + +#if defined(__OCPN__ANDROID__) + #include + #include // this is a cut-down version of gl.h + #include +#elif defined(__MSVC__) + #include "glew.h" +#elif defined(__WXOSX__) + #include + #include + typedef void (* _GLUfuncptr)(); + #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#elif defined(__WXQT__) || defined(__WXGTK__) + #include + #include +#endif + +#endif // __FILE__ diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52s57.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52s57.h new file mode 100644 index 0000000..aba4b3a --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52s57.h @@ -0,0 +1,646 @@ +/****************************************************************************** + * + * Project: OpenCP + * Purpose: S52 PLIB and S57 Chart data types + * Author: David Register + * + *************************************************************************** + * Copyright (C) 2010 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + *************************************************************************** + * + */ + +#ifndef _S52S57_H_ +#define _S52S57_H_ + +#include "bbox.h" +#include "color_types.h" + +#include +#include +#include + +#define CURRENT_SENC_FORMAT_VERSION 201 + +#define OBJL_NAME_LEN 6 + +// Fwd Defns +class wxArrayOfS57attVal; +class OGREnvelope; +class OGRGeometry; +class VE_Element; +class VC_Element; + +class wxRect2D { +public: + double x; + double y; + double width; + double height; + + wxRect2D(); + wxRect2D(double xx, double yy, double w, double h); + void SetX(double _x){ x = _x;} + void SetY(double _y){ y = _y;} + void SetWidth(double _width){ width = _width;} + void SetHeight(double _height){ height = _height;} + + bool Intersects(const wxRect2D& other, + double epsilon = 1e-6) const; + wxRect2D Union(const wxRect2D& other) const; + bool IsEmpty() const {return width <= 0.0 || height <= 0.0;} + +}; + +// name of the addressed look up table set (fifth letter) +typedef enum _LUPname { + SIMPLIFIED = 'L', // points + PAPER_CHART = 'R', // points + LINES = 'S', // lines + PLAIN_BOUNDARIES = 'N', // areas + SYMBOLIZED_BOUNDARIES = 'O', // areas + LUPNAME_NUM = 5 +} LUPname; + +// Addressed Object Type +typedef enum _Object_t { + POINT_T = 'P', + LINES_T = 'L', + AREAS_T = 'A', + OBJ_NUM = 3 // number of object type +} Object_t; + +// Display Priority +typedef enum _DisPrio { + PRIO_NODATA = '0', // no data fill area pattern + PRIO_GROUP1 = '1', // S57 group 1 filled areas + PRIO_AREA_1 = '2', // superimposed areas + PRIO_AREA_2 = '3', // superimposed areas also water features + PRIO_SYMB_POINT = '4', // point symbol also land features + PRIO_SYMB_LINE = '5', // line symbol also restricted areas + PRIO_SYMB_AREA = '6', // area symbol also traffic areas + PRIO_ROUTEING = '7', // routeing lines + PRIO_HAZARDS = '8', // hazards + PRIO_MARINERS = '9', // VRM, EBL, own ship + PRIO_NUM = 10 // number of priority levels + +} DisPrio; + +// RADAR Priority +typedef enum _RadPrio { + RAD_OVER = 'O', // presentation on top of RADAR + RAD_SUPP = 'S', // presentation suppressed by RADAR + RAD_NUM = 2 +} RadPrio; + +// PixelPtr type flags +enum { + ID_EMPTY = 0, + ID_wxBitmap, + ID_RGBA, + ID_GL_PATT_SPEC, + ID_RGB_PATT_SPEC, + ID_GLIST + +}; + +// display category type +typedef enum _DisCat { + DISPLAYBASE = 'D', // + STANDARD = 'S', // + OTHER = 'O', // O for OTHER + MARINERS_STANDARD = 'M', // Mariner specified + MARINERS_OTHER, // value not defined + DISP_CAT_NUM, // value not defined +} DisCat; + +#define MASK_POINT 1 +#define MASK_LINE 2 +#define MASK_AREA 4 +#define MASK_MPS 8 +#define MASK_ALL MASK_POINT + MASK_LINE + MASK_AREA + MASK_MPS + +typedef enum _Rules_t { + RUL_NONE, // no rule type (init) + RUL_TXT_TX, // TX + RUL_TXT_TE, // TE + RUL_SYM_PT, // SY + RUL_SIM_LN, // LS + RUL_COM_LN, // LC + RUL_ARE_CO, // AC + RUL_ARE_PA, // AP + RUL_CND_SY, // CS + RUL_MUL_SG, // Multipoint Sounding + RUL_ARC_2C // Circular Arc, used for sector lights, opencpn private +} Rules_t; + +//-- SYMBOLISATION MODULE STRUCTURE ----------------------------- +// position parameter: LINE, PATTERN, SYMBOL +typedef struct _position { + union { + int dummy1, PAMI, dummy2; + } minDist; + union { + int dummy1, PAMA, dummy2; + } maxDist; + union { + int LICL, PACL, SYCL; + } pivot_x; + union { + int LIRW, PARW, SYRW; + } pivot_y; + union { + int LIHL, PAHL, SYHL; + } bnbox_w; + union { + int LIVL, PAVL, SYVL; + } bnbox_h; + union { + int LBXC, PBXC, SBXC; + } bnbox_x; // UpLft crnr + union { + int LBXR, PBXR, SBXR; + } bnbox_y; // UpLft crnr +} position; + +// rule parameter for : LINE, PATTERN, SYMBOL +typedef struct _Rule { + int RCID; + union { + char LINM[8], PANM[8], SYNM[8]; + } name; + union { + char dummy, PADF, SYDF; + } definition; + union { + char dummy1, PATP, dummy2; + } fillType; + union { + char dummy1, PASP, dummy2; + } spacing; + union { + position line, patt, symb; + } pos; + union { + wxString *LXPO, *PXPO, *SXPO; + } exposition; + union { + wxString *dummy, *PBTM, *SBTM; + } bitmap; + union { + char *LCRF, *PCRF, *SCRF; + } colRef; + union { + char *LVCT, *PVCT, *SVCT; + } vector; + + // Private data + int parm0; // always indicates form of pixelPtr + int parm1; // integer parameters + int parm2; + int parm3; + int parm4; + int parm5; + int parm6; + int parm7; + void *pixelPtr; // opaque pointer + +} Rule; + +typedef struct _Rules { + Rules_t ruleType; + char *INSTstr; // Symbology Instruction string + Rule *razRule; // rule + char *INST0; // Head of the entire object Instruction string + int n_sequence; // sequence number in list, used to identify a particular + // rule + bool b_private_razRule; // marker indicating that razRule should be free'd on + // Rules destroy + struct _Rules *next; +} Rules; + +// LOOKUP MODULE CLASS + +class LUPrec { +public: + ~LUPrec() { ATTArray.clear(); }; + int RCID; // record identifier + char OBCL[7]; // Name (6 char) '\0' terminated + Object_t FTYP; // 'A' Area, 'L' Line, 'P' Point + DisPrio DPRI; // Display Priority + RadPrio RPRI; // 'O' or 'S', Radar Priority + LUPname TNAM; // FTYP: areas, points, lines + std::vector ATTArray; // Array of LUP Attributes + wxString INST; // Instruction Field (rules) + DisCat DISC; // Display Categorie: D/S/O, DisplayBase, Standard, Other + int LUCM; // Look-Up Comment (PLib3.x put 'groupes' here, + // hence 'int', but its a string in the specs) + int nSequence; // A sequence number, indicating order of encounter in + // the PLIB file + Rules *ruleList; // rasterization rule list +}; + +// Conditional Symbology +typedef struct _Cond { + const char *name; + void *(*condInst)(void *param); +} Cond; + +class S52_TextC { +public: + S52_TextC(); + ~S52_TextC(); + + wxString frmtd; // formated text string + char hjust; + char vjust; + char space; + char style; // CHARS + char weight; // CHARS + char width; // CHARS + int bsize; // CHARS -body size + int xoffs; // text offsets, in units of bsize + int yoffs; // + S52color *pcol; // pointer to S52colour + int dis; // display + wxFont *pFont; + int rul_seq_creator; // sequence number of the Rule creating this object + int RGBA_width; + int RGBA_height; + int rendered_char_height; + wxRect2D + rText; // rectangle of the text as currently rendered, used for declutter + bool bnat; // frmtd is National text, UTF-8 encoded + bool bspecial_char; // frmtd has special ASCII characters, i.e. > 127 + int avgCharWidth; + int texobj; + int text_width; + int text_height; +}; + +// +// WARNING: must be in sync OGRatt_t + +typedef enum _OGRatt_t { + OGR_INT, + OGR_INT_LST, + OGR_REAL, + OGR_REAL_LST, + OGR_STR, +} OGRatt_t; + +typedef struct _S57attVal { + void *value; + OGRatt_t valType; +} S57attVal; + +WX_DEFINE_ARRAY(S57attVal *, wxArrayOfS57attVal); + +typedef struct _OBJLElement { + char OBJLName[OBJL_NAME_LEN]; + int nViz; +} OBJLElement; + +// OGR primitive type +typedef enum _geoPrim_t { + GEO_POINT, + GEO_LINE, + GEO_AREA, + GEO_META, + GEO_PRIM, // number of primitive +} GeoPrim_t; + +typedef struct _pt { + double x; + double y; +} pt; + +// Fwd References +class s57chart; +class S57Obj; +class OGRFeature; +class PolyTessGeo; +class line_segment_element; +class PI_line_segment_element; + +struct chart_context { + std::unordered_map *m_pvc_hash; + std::unordered_map *m_pve_hash; + double ref_lat; + double ref_lon; + wxArrayPtrVoid *pFloatingATONArray; + wxArrayPtrVoid *pRigidATONArray; + void *chart; + double safety_contour; + float *vertex_buffer; + int vboID; + int chart_type; + int chart_scale; + std::list *(s57chart::*pt2GetAssociatedObjects)(S57Obj *obj); +}; + +class LineGeometryDescriptor { +public: + double extent_s_lat; + double extent_n_lat; + double extent_w_lon; + double extent_e_lon; + int indexCount; + int *indexTable; +}; + +typedef struct _MultipointGeometryDescriptor { + double extent_s_lat; + double extent_n_lat; + double extent_w_lon; + double extent_e_lon; + int pointCount; + void *pointTable; +} MultipointGeometryDescriptor; + +class S57Obj { +public: + // Public Methods + S57Obj(); + ~S57Obj(); + + S57Obj(const char *featureName); + + wxString GetAttrValueAsString(const char *attr); + int GetAttributeIndex(const char *AttrSeek); + + bool AddIntegerAttribute(const char *acronym, int val); + bool AddIntegerListAttribute(const char *acronym, int *pval, int nValue); + bool AddDoubleAttribute(const char *acronym, double val); + bool AddDoubleListAttribute(const char *acronym, double *pval, int nValue); + bool AddStringAttribute(const char *acronym, char *val); + + bool SetPointGeometry(double lat, double lon, double ref_lat, double ref_lon); + bool SetLineGeometry(LineGeometryDescriptor *pGeo, GeoPrim_t geoType, + double ref_lat, double ref_lon); + bool SetAreaGeometry(PolyTessGeo *ppg, double ref_lat, double ref_lon); + bool SetMultipointGeometry(MultipointGeometryDescriptor *pGeo, double ref_lat, + double ref_lon); + + // Private Methods +private: + void Init(); + +public: + // Instance Data + char FeatureName[8]; + GeoPrim_t Primitive_type; + + char *att_array; + wxArrayOfS57attVal *attVal; + int n_attr; + + int iOBJL; + int Index; + + double x; // for POINT + double y; + double z; + int npt; // number of points as needed by arrays + + pt *geoPt; // used for cm93 line feature select check + + double *geoPtz; // an array[3] for MultiPoint, SM with Z, i.e. depth + double *geoPtMulti; // an array[2] for MultiPoint, lat/lon to make bbox + // of decomposed points + PolyTessGeo *pPolyTessGeo; + + LLBBox BBObj; // lat/lon BBox of the rendered object + double m_lat; // The lat/lon of the object's "reference" point + double m_lon; + + Rules *CSrules; // per object conditional symbology + int bCS_Added; + + S52_TextC *FText; + int bFText_Added; + wxRect rText; + + int Scamin; // SCAMIN attribute decoded during load + int SuperScamin; + bool bIsClone; + int nRef; // Reference counter, to signal OK for deletion + bool bIsAton; // This object is an aid-to-navigation + bool bIsAssociable; // This object is DRGARE or DEPARE + + int m_n_lsindex; + int *m_lsindex_array; + int m_n_edge_max_points; + line_segment_element *m_ls_list; + PI_line_segment_element *m_ls_list_legacy; + + DisCat m_DisplayCat; + int m_DPRI; // display priority, assigned from initial LUP + // May be adjusted by CS + bool m_bcategory_mutable; // CS procedure may move this object to a higher + // category. Used as a hint to rendering filter + // logic + + // This transform converts from object geometry + // to SM coordinates. + double x_rate; // These auxiliary transform coefficients are + double y_rate; // to be used in GetPointPix() and friends + double x_origin; // on a per-object basis if necessary + double y_origin; + + chart_context *m_chart_context; // per-chart constants, carried in each + // object for convenience + int auxParm0; // some per-object auxiliary parameters, used for OpenGL + int auxParm1; + int auxParm2; + int auxParm3; + + bool bBBObj_valid; + bool bOK_to_Render; +}; + +typedef std::vector S57ObjVector; + +typedef struct _sm_parms { + double easting_vp_center; + double northing_vp_center; +} sm_parms; + +WX_DEFINE_ARRAY_PTR(Rules *, ArrayOfRules); + +typedef struct _mps_container { + ArrayOfRules *cs_rules; +} mps_container; + +// object rasterization rules +typedef struct _ObjRazRules { + LUPrec *LUP; + S57Obj *obj; + sm_parms *sm_transform_parms; + struct _ObjRazRules *child; // child list, used only for MultiPoint Soundings + struct _ObjRazRules *next; + struct _mps_container *mps; +} ObjRazRules; + +WX_DECLARE_LIST(ObjRazRules, ListOfObjRazRules); + +//---------------------------------------------------------------------------------- +// Used for s52 Fast Polygon Renderer +//---------------------------------------------------------------------------------- +class render_canvas_parms { +public: + render_canvas_parms(void); + ~render_canvas_parms(void); + + unsigned char *pix_buff; + int lclip; + int rclip; + int pb_pitch; + int x; + int y; + int width; + int height; + int w_pot; + int h_pot; + int depth; + bool b_stagger; + int OGL_tex_name; + bool b_revrgb; +}; + +//---------------------------------------------------------------------------------- +// Classes used to create arrays of geometry elements +//---------------------------------------------------------------------------------- + +class VE_Element { +public: + unsigned int index; + unsigned int nCount; + float *pPoints; + int max_priority; + size_t vbo_offset; + LLBBox edgeBBox; +}; + +class VC_Element { +public: + unsigned int index; + float *pPoint; +}; + +typedef std::vector VE_ElementVector; +typedef std::vector VC_ElementVector; + +typedef enum { + TYPE_CE = 0, + TYPE_CC, + TYPE_EC, + TYPE_EE, + TYPE_EE_REV +} SegmentType; + +class connector_segment { +public: + int vbo_offset; + int max_priority_cs; + float cs_lat_avg; // segment centroid + float cs_lon_avg; +}; + +class line_segment_element { +public: + int priority; + union { + connector_segment *pcs; + VE_Element *pedge; + }; + SegmentType ls_type; + + line_segment_element *next; +}; + +#if 0 // TODO +class line_segment_element_legacy +{ +public: + size_t vbo_offset; + size_t n_points; + int priority; + float lat_max; // segment bounding box + float lat_min; + float lon_max; + float lon_min; + int type; + void *private0; + + line_segment_element *next; +}; + + +class connector_segment +{ +public: + void *start; + void *end; + SegmentType type; + int vbo_offset; + int max_priority; +}; + +#endif + +class connector_key { +public: + connector_key() { memset(k, 0, sizeof k); } + + connector_key(SegmentType t, int a, int b) { set(t, a, b); } + + void set(SegmentType t, int a, int b) { + memcpy(k, &a, sizeof a); + memcpy(&k[sizeof a], &b, sizeof b); + k[sizeof(a) + sizeof(b)] = (unsigned char)t; + } + + unsigned long hash() const; + + unsigned char k[sizeof(int) + sizeof(int) + sizeof(char)]; +}; + +class connHash { +public: + connHash() {} + unsigned long operator()(const connector_key &k) const { return k.hash(); } + + connHash &operator=(const connHash &) { return *this; } +}; + +// comparison operator +class connEqual { +public: + connEqual() {} + bool operator()(const connector_key &a, const connector_key &b) const { + return memcmp(a.k, b.k, sizeof b.k) == 0; + } + + connEqual &operator=(const connEqual &) { return *this; } +}; + +WX_DECLARE_HASH_MAP(connector_key, connector_segment *, connHash, connEqual, + connected_segment_hash); + +#endif diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.cpp new file mode 100644 index 0000000..cf7f3a0 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.cpp @@ -0,0 +1,756 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: S52 Presentation Library + * Authors: David Register + * Jesper Weissglas + * + *************************************************************************** + * Copyright (C) 2010 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + + +#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL) + +#include "s52shaders.h" + +#ifdef USE_ANDROID_GLES2 +const GLchar* S52_preamble = +"\n"; +#else +const GLchar* S52_preamble = +"#version 120\n" +"#define precision\n" +"#define lowp\n" +"#define mediump\n" +"#define highp\n"; +#endif + + +// Simple colored triangle shader + +static const GLchar *S52color_tri_vertex_shader_source = + "attribute vec2 position;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "uniform vec4 color;\n" + "varying vec4 fragColor;\n" + "void main() {\n" + " fragColor = color;\n" + " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n" + "}\n"; + +static const GLchar *S52color_tri_fragment_shader_source = + "precision highp float;\n" + "varying vec4 fragColor;\n" + "void main() {\n" + " gl_FragColor = fragColor;\n" + "}\n"; + +// Simple 2D texture shader +static const GLchar *S52texture_2D_vertex_shader_source = + "attribute vec2 position;\n" + "attribute vec2 aUV;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "varying vec2 varCoord;\n" + "void main() {\n" + " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n" + " //varCoord = aUV.st;\n" + " varCoord = aUV;\n" + "}\n"; + +static const GLchar *S52texture_2D_fragment_shader_source = + "precision highp float;\n" + "uniform sampler2D uTex;\n" + "varying vec2 varCoord;\n" + "void main() {\n" + " gl_FragColor = texture2D(uTex, varCoord);\n" + "}\n"; + +// 2D texture shader with color modulation, used for colored text +static const GLchar *S52texture_2D_ColorMod_vertex_shader_source = + "precision highp float;\n" + "attribute vec2 position;\n" + "attribute vec2 aUV;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "varying vec2 varCoord;\n" + "void main() {\n" + " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n" + " //varCoord = aUV.st;\n" + " varCoord = aUV;\n" + "}\n"; + +static const GLchar *S52texture_2D_ColorMod_fragment_shader_source = + "precision highp float;\n" + "uniform sampler2D uTex;\n" + "uniform vec4 color;\n" + "varying vec2 varCoord;\n" + "void main() {\n" + " vec4 col=texture2D(uTex, varCoord);\n" + " gl_FragColor = color;\n" + " gl_FragColor.a = col.a;\n" + "}\n"; + +// Circle shader + +static const GLchar *S52circle_filled_vertex_shader_source = + "precision highp float;\n" + "attribute vec2 aPos;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "void main() {\n" + " gl_Position = MVMatrix * TransformMatrix * vec4(aPos, 0.0, 1.0);\n" + "}\n"; + +static const GLchar *S52circle_filled_fragment_shader_source = + "precision highp float;\n" + "uniform float border_width;\n" + "uniform float circle_radius;\n" + "uniform vec4 circle_color;\n" + "uniform vec4 border_color;\n" + "uniform vec2 circle_center;\n" + "void main(){\n" + "float d = distance(gl_FragCoord.xy, circle_center);\n" + "if (d < (circle_radius - border_width)) { gl_FragColor = circle_color; }\n" + "else if (d < circle_radius) { gl_FragColor = border_color; }\n" + "else { gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); }\n" + "}\n"; + +// Ring shader + +static const GLchar *S52ring_vertex_shader_source = + "precision highp float;\n" + "attribute vec2 aPos;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "void main() {\n" + " gl_Position = MVMatrix * TransformMatrix * vec4(aPos, 0.0, 1.0);\n" + "}\n"; + +static const GLchar *S52ring_fragment_shader_source_old = + "precision highp float;\n" + "uniform float border_width;\n" + "uniform float circle_radius;\n" + "uniform float ring_width;\n" + "uniform vec4 circle_color;\n" + "uniform vec4 border_color;\n" + "uniform vec2 circle_center;\n" + "uniform float sector_1;\n" + "uniform float sector_2;\n" + + "void main(){\n" + "const float PI = 3.14159265358979323846264;\n" + "bool bdraw = false;\n" + + "float angle = atan(gl_FragCoord.y-circle_center.y, " + "gl_FragCoord.x-circle_center.x);\n" + "angle = PI/2.0 - angle;\n" + "if(angle < 0.0) angle += PI * 2.0;\n" + + "if(sector_2 > PI * 2.0){\n" + " if((angle > sector_1) && (angle < (PI * 2.0) )){\n" + " bdraw = true;\n" + " }\n" + " if(angle < sector_2 - (PI * 2.0)){\n" + " bdraw = true;\n" + " }\n" + "} else {\n" + " if((angle > sector_1) && (angle < sector_2)){\n" + " bdraw = true;\n" + " }\n" + "}\n" + + "if(bdraw){\n" + " float d = distance(gl_FragCoord.xy, circle_center);\n" + " if (d > circle_radius) {\n" + " discard;\n" + " } else if( d > (circle_radius - border_width)) {\n" + " gl_FragColor = border_color;\n" + " } else if( d > (circle_radius - border_width - ring_width)) {\n" + " gl_FragColor = circle_color;\n" + " } else if( d > (circle_radius - border_width - ring_width - " + "border_width)) {\n" + " gl_FragColor = border_color;\n" + " } else {\n" + " discard;\n" + " }\n" + "} else{\n" + " discard;\n" + "}\n" + "}\n"; + + +static const GLchar *S52ring_fragment_shader_source = + "precision highp float;\n" + "uniform float ring_width;\n" + "uniform float circle_radius;\n" + "uniform vec4 ring_color;\n" + "uniform vec2 circle_center;\n" + "uniform float sector_1;\n" + "uniform float sector_2;\n" + + "void main(){\n" + "const float PI = 3.14159265358979323846264;\n" + "bool bdraw = false;\n" + + "float angle = atan(gl_FragCoord.y-circle_center.y, " + "gl_FragCoord.x-circle_center.x);\n" + "angle = PI/2.0 - angle;\n" + "if(angle < 0.0) angle += PI * 2.0;\n" + + "if(sector_2 > PI * 2.0){\n" + " if((angle > sector_1) && (angle < (PI * 2.0) )){\n" + " bdraw = true;\n" + " }\n" + " if(angle < sector_2 - (PI * 2.0)){\n" + " bdraw = true;\n" + " }\n" + "} else {\n" + " if((angle > sector_1) && (angle < sector_2)){\n" + " bdraw = true;\n" + " }\n" + "}\n" + + "if(bdraw){\n" + " float d = distance(gl_FragCoord.xy, circle_center);\n" + " float delta = 0.9;\n" + " if (d > circle_radius + ring_width/2.) {\n" + " discard;\n" + " } else if( d > (circle_radius )) {\n" + " float alpha = smoothstep(circle_radius + ring_width/2. -delta, circle_radius + ring_width/2., d);\n" + " gl_FragColor = ring_color;\n" + " gl_FragColor.w = 1.-alpha;\n" + " }\n" + " else if( d > (circle_radius - ring_width/2. - delta)) {\n" + " gl_FragColor = ring_color;\n" + " float alpha = smoothstep(circle_radius - ring_width/2. - delta, circle_radius - ring_width/2., d);\n" + " gl_FragColor.w = alpha;\n" + " }\n" + " else {\n" + " discard;\n" + " }\n" + "} else{\n" + " discard;\n" + "}\n" + "}\n"; + +// Dash/Dot line shader +static const GLchar *S52Dash_vertex_shader_source = + "attribute vec2 position;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "void main() {\n" + " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n" + "}\n"; + +static const GLchar *S52Dash_fragment_shader_source = + "precision highp float;\n" + "uniform sampler2D uTex;\n" + "uniform vec2 startPos;\n" + "uniform float texWidth;\n" + "uniform float dashFactor;\n" + "uniform vec4 color;\n" + "void main() {\n" + " float d = distance(gl_FragCoord.xy, startPos);\n" + " float x = mod(d,texWidth) / texWidth;\n" + " if(x < dashFactor) gl_FragColor = color;\n" + " else gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);\n" + "}\n"; + +// Texture shader for Area Pattern +static const GLchar *S52AP_vertex_shader_source = + "attribute vec2 position;\n" + "attribute vec2 aUV;\n" + "uniform mat4 MVMatrix;\n" + "uniform mat4 TransformMatrix;\n" + "void main() {\n" + " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n" + "}\n"; + +#if 0 +static const GLchar* S52AP_fragment_shader_source = + "precision highp float;\n" + "uniform sampler2D uTex;\n" + "uniform float texWidth;\n" + "uniform float texHeight;\n" + "uniform float texPOTWidth;\n" + "uniform float texPOTHeight;\n" + "uniform float staggerFactor;\n" + "uniform vec4 color;\n" + "uniform float xOff;\n" + "uniform float yOff;\n" + "void main() {\n" + " float yp = floor((gl_FragCoord.y + yOff) / texHeight);\n" + " float fstagger = 0.0;\n" + " //if(mod(yp, 2.0) < 0.1) fstagger = staggerFactor;\n" + " float xStag = xOff + (fstagger * texWidth);\n" + " float x = mod((gl_FragCoord.x - xStag),texWidth) / texPOTWidth;\n" + " float y = mod((gl_FragCoord.y + yOff),texHeight) / texPOTHeight;\n" + " gl_FragColor = texture2D(uTex, vec2(x, (texHeight / texPOTHeight) - y));\n" + "}\n"; +#else + +static const GLchar *S52AP_fragment_shader_source = + "precision highp float;\n" + "uniform sampler2D uTex;\n" + "uniform float texWidth;\n" + "uniform float texHeight;\n" + "uniform float texPOTWidth;\n" + "uniform float texPOTHeight;\n" + "uniform float staggerFactor;\n" + "uniform vec4 color;\n" + "uniform float xOff;\n" + "uniform float yOff;\n" + "uniform float yOffM;\n" + "void main() {\n" + " float yp = floor((gl_FragCoord.y + yOffM ) / texPOTHeight);\n" + " float fstagger = 0.0;\n" + " if(mod(yp, 2.0) < 0.1) fstagger = 0.5;\n" + " float x = (gl_FragCoord.x - xOff) / texPOTWidth;\n" + " float y = (gl_FragCoord.y + yOff) / texPOTHeight;\n" + " gl_FragColor = texture2D(uTex, vec2(x + fstagger, -y));\n" + "}\n"; +#endif + +GLint S52color_tri_fragment_shader; +GLint S52color_tri_vertex_shader; + +GLint S52texture_2D_fragment_shader; +GLint S52texture_2D_vertex_shader; + +GLint S52texture_2D_ColorMod_fragment_shader; +GLint S52texture_2D_ColorMod_vertex_shader; + +GLint S52circle_filled_vertex_shader; +GLint S52circle_filled_fragment_shader; + +GLint S52ring_vertex_shader; +GLint S52ring_fragment_shader; + +GLint S52Dash_vertex_shader; +GLint S52Dash_fragment_shader; + +GLint S52AP_vertex_shader; +GLint S52AP_fragment_shader; + +extern GLint S52color_tri_shader_program; +extern GLint S52texture_2D_shader_program; +extern GLint S52texture_2D_ColorMod_shader_program; +extern GLint S52circle_filled_shader_program; +extern GLint S52ring_shader_program; +extern GLint S52Dash_shader_program; +extern GLint S52AP_shader_program; + +S52_GLShaderProgram *pS52texture_2D_ColorMod_shader_program; + + +bool shadersLoaded = false; + +bool loadS52Shaders() { + bool ret_val = true; + //GLint success; + + enum Consts { INFOLOG_LEN = 512 }; +#if 0 + GLchar infoLog[INFOLOG_LEN]; +#endif + + // Are the shaders ready? + if(shadersLoaded) + return true; + + // Simple colored triangle shader + + if (!S52color_tri_vertex_shader) { + auto shaderProgram = S52_GLShaderProgram::Builder() + .addShaderFromSource(S52color_tri_vertex_shader_source, GL_VERTEX_SHADER) + .addShaderFromSource(S52color_tri_fragment_shader_source, GL_FRAGMENT_SHADER) + .linkProgram(); + + S52color_tri_shader_program = shaderProgram.programId(); + } + + + if (!S52texture_2D_shader_program) { + auto shaderProgram = S52_GLShaderProgram::Builder() + .addShaderFromSource(S52texture_2D_vertex_shader_source, GL_VERTEX_SHADER) + .addShaderFromSource(S52texture_2D_fragment_shader_source, GL_FRAGMENT_SHADER) + .linkProgram(); + + S52texture_2D_shader_program = shaderProgram.programId(); + } + +#if 0 + // Simple 2D texture shader + + if (!S52texture_2D_vertex_shader) { + /* Vertex shader */ + S52texture_2D_vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(S52texture_2D_vertex_shader, 1, + &S52texture_2D_vertex_shader_source, NULL); + glCompileShader(S52texture_2D_vertex_shader); + glGetShaderiv(S52texture_2D_vertex_shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(S52texture_2D_vertex_shader, INFOLOG_LEN, NULL, + infoLog); + // printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } + + if (!S52texture_2D_fragment_shader) { + /* Fragment shader */ + S52texture_2D_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(S52texture_2D_fragment_shader, 1, + &S52texture_2D_fragment_shader_source, NULL); + glCompileShader(S52texture_2D_fragment_shader); + glGetShaderiv(S52texture_2D_fragment_shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(S52texture_2D_fragment_shader, INFOLOG_LEN, NULL, + infoLog); + // printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } + + if (!S52texture_2D_shader_program) { + /* Link shaders */ + S52texture_2D_shader_program = glCreateProgram(); + glAttachShader(S52texture_2D_shader_program, S52texture_2D_vertex_shader); + glAttachShader(S52texture_2D_shader_program, S52texture_2D_fragment_shader); + glLinkProgram(S52texture_2D_shader_program); + glGetProgramiv(S52texture_2D_shader_program, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(S52texture_2D_shader_program, INFOLOG_LEN, NULL, + infoLog); + // printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog); + ret_val = false; + } + } +#endif + + // 2D texture shader with color Modulate + if (!S52texture_2D_ColorMod_shader_program) { + + auto shaderProgram = S52_GLShaderProgram::Builder() + .addShaderFromSource(S52texture_2D_ColorMod_vertex_shader_source, GL_VERTEX_SHADER) + .addShaderFromSource(S52texture_2D_ColorMod_fragment_shader_source, GL_FRAGMENT_SHADER) + .linkProgram(); + + S52texture_2D_ColorMod_shader_program = shaderProgram.programId(); + + } + +#if 0 + if (!S52texture_2D_ColorMod_vertex_shader) { + /* Vertex shader */ + S52texture_2D_ColorMod_vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(S52texture_2D_ColorMod_vertex_shader, 1, + &S52texture_2D_ColorMod_vertex_shader_source, NULL); + glCompileShader(S52texture_2D_ColorMod_vertex_shader); + glGetShaderiv(S52texture_2D_ColorMod_vertex_shader, GL_COMPILE_STATUS, + &success); + if (!success) { + glGetShaderInfoLog(S52texture_2D_ColorMod_vertex_shader, INFOLOG_LEN, + NULL, infoLog); + // printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } + + + if (!S52texture_2D_ColorMod_fragment_shader) { + /* Fragment shader */ + S52texture_2D_ColorMod_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(S52texture_2D_ColorMod_fragment_shader, 1, + &S52texture_2D_ColorMod_fragment_shader_source, NULL); + glCompileShader(S52texture_2D_ColorMod_fragment_shader); + glGetShaderiv(S52texture_2D_ColorMod_fragment_shader, GL_COMPILE_STATUS, + &success); + if (!success) { + glGetShaderInfoLog(S52texture_2D_ColorMod_fragment_shader, INFOLOG_LEN, + NULL, infoLog); + // printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } + + if (!S52texture_2D_ColorMod_shader_program) { + /* Link shaders */ + S52texture_2D_ColorMod_shader_program = glCreateProgram(); + glAttachShader(S52texture_2D_ColorMod_shader_program, + S52texture_2D_ColorMod_vertex_shader); + glAttachShader(S52texture_2D_ColorMod_shader_program, + S52texture_2D_ColorMod_fragment_shader); + glLinkProgram(S52texture_2D_ColorMod_shader_program); + glGetProgramiv(S52texture_2D_ColorMod_shader_program, GL_LINK_STATUS, + &success); + if (!success) { + glGetProgramInfoLog(S52texture_2D_ColorMod_shader_program, INFOLOG_LEN, + NULL, infoLog); + // printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog); + ret_val = false; + } + } +#endif + + // Circle shader + if (!S52circle_filled_shader_program) { + auto shaderProgram = S52_GLShaderProgram::Builder() + .addShaderFromSource(S52circle_filled_vertex_shader_source, GL_VERTEX_SHADER) + .addShaderFromSource(S52circle_filled_fragment_shader_source, GL_FRAGMENT_SHADER) + .linkProgram(); + + S52circle_filled_shader_program = shaderProgram.programId(); + } + +#if 0 + if (!S52circle_filled_vertex_shader) { + /* Vertex shader */ + S52circle_filled_vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(S52circle_filled_vertex_shader, 1, + &S52circle_filled_vertex_shader_source, NULL); + glCompileShader(S52circle_filled_vertex_shader); + glGetShaderiv(S52circle_filled_vertex_shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(S52circle_filled_vertex_shader, INFOLOG_LEN, NULL, + infoLog); + // printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } + + if (!S52circle_filled_fragment_shader) { + /* Fragment shader */ + S52circle_filled_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(S52circle_filled_fragment_shader, 1, + &S52circle_filled_fragment_shader_source, NULL); + glCompileShader(S52circle_filled_fragment_shader); + glGetShaderiv(S52circle_filled_fragment_shader, GL_COMPILE_STATUS, + &success); + if (!success) { + glGetShaderInfoLog(S52circle_filled_fragment_shader, INFOLOG_LEN, NULL, + infoLog); + // printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } + + if (!S52circle_filled_shader_program) { + /* Link shaders */ + S52circle_filled_shader_program = glCreateProgram(); + glAttachShader(S52circle_filled_shader_program, + S52circle_filled_vertex_shader); + glAttachShader(S52circle_filled_shader_program, + S52circle_filled_fragment_shader); + glLinkProgram(S52circle_filled_shader_program); + glGetProgramiv(S52circle_filled_shader_program, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(S52circle_filled_shader_program, INFOLOG_LEN, NULL, + infoLog); + // printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } +#endif + + // Ring shader + if (!S52ring_shader_program) { + auto shaderProgram = S52_GLShaderProgram::Builder() + .addShaderFromSource(S52ring_vertex_shader_source, GL_VERTEX_SHADER) + .addShaderFromSource(S52ring_fragment_shader_source, GL_FRAGMENT_SHADER) + .linkProgram(); + + S52ring_shader_program = shaderProgram.programId(); + } + +#if 0 + if (!S52ring_vertex_shader) { + /* Vertex shader */ + S52ring_vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(S52ring_vertex_shader, 1, &S52ring_vertex_shader_source, + NULL); + glCompileShader(S52ring_vertex_shader); + glGetShaderiv(S52ring_vertex_shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(S52ring_vertex_shader, INFOLOG_LEN, NULL, infoLog); + // printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } + + if (!S52ring_fragment_shader) { + /* Fragment shader */ + S52ring_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(S52ring_fragment_shader, 1, &S52ring_fragment_shader_source, + NULL); + glCompileShader(S52ring_fragment_shader); + glGetShaderiv(S52ring_fragment_shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(S52ring_fragment_shader, INFOLOG_LEN, NULL, infoLog); + // printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } + + if (!S52ring_shader_program) { + /* Link shaders */ + S52ring_shader_program = glCreateProgram(); + glAttachShader(S52ring_shader_program, S52ring_vertex_shader); + glAttachShader(S52ring_shader_program, S52ring_fragment_shader); + glLinkProgram(S52ring_shader_program); + glGetProgramiv(S52ring_shader_program, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(S52ring_shader_program, INFOLOG_LEN, NULL, infoLog); + // printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } +#endif + + // Dash shader + if (!S52Dash_shader_program) { + auto shaderProgram = S52_GLShaderProgram::Builder() + .addShaderFromSource(S52Dash_vertex_shader_source, GL_VERTEX_SHADER) + .addShaderFromSource(S52Dash_fragment_shader_source, GL_FRAGMENT_SHADER) + .linkProgram(); + + S52Dash_shader_program = shaderProgram.programId(); + } + +#if 0 + if (!S52Dash_vertex_shader) { + /* Vertex shader */ + S52Dash_vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(S52Dash_vertex_shader, 1, &S52Dash_vertex_shader_source, + NULL); + glCompileShader(S52Dash_vertex_shader); + glGetShaderiv(S52Dash_vertex_shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(S52ring_vertex_shader, INFOLOG_LEN, NULL, infoLog); + // printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } + + if (!S52Dash_fragment_shader) { + /* Fragment shader */ + S52Dash_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(S52Dash_fragment_shader, 1, &S52Dash_fragment_shader_source, + NULL); + glCompileShader(S52Dash_fragment_shader); + glGetShaderiv(S52Dash_fragment_shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(S52Dash_fragment_shader, INFOLOG_LEN, NULL, infoLog); + // printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } + + if (!S52Dash_shader_program) { + /* Link shaders */ + S52Dash_shader_program = glCreateProgram(); + glAttachShader(S52Dash_shader_program, S52Dash_vertex_shader); + glAttachShader(S52Dash_shader_program, S52Dash_fragment_shader); + glLinkProgram(S52Dash_shader_program); + glGetProgramiv(S52Dash_shader_program, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(S52Dash_shader_program, INFOLOG_LEN, NULL, infoLog); + // printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } +#endif + + // AP shader + if (!S52AP_shader_program) { + auto shaderProgram = S52_GLShaderProgram::Builder() + .addShaderFromSource(S52AP_vertex_shader_source, GL_VERTEX_SHADER) + .addShaderFromSource(S52AP_fragment_shader_source, GL_FRAGMENT_SHADER) + .linkProgram(); + + S52AP_shader_program = shaderProgram.programId(); + } + +#if 0 + if (!S52AP_vertex_shader) { + /* Vertex shader */ + S52AP_vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(S52AP_vertex_shader, 1, &S52AP_vertex_shader_source, NULL); + glCompileShader(S52AP_vertex_shader); + glGetShaderiv(S52AP_vertex_shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(S52ring_vertex_shader, INFOLOG_LEN, NULL, infoLog); + // printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } + + if (!S52AP_fragment_shader) { + /* Fragment shader */ + S52AP_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(S52AP_fragment_shader, 1, &S52AP_fragment_shader_source, + NULL); + glCompileShader(S52AP_fragment_shader); + glGetShaderiv(S52AP_fragment_shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(S52AP_fragment_shader, INFOLOG_LEN, NULL, infoLog); + // printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } + + if (!S52AP_shader_program) { + /* Link shaders */ + S52AP_shader_program = glCreateProgram(); + glAttachShader(S52AP_shader_program, S52AP_vertex_shader); + glAttachShader(S52AP_shader_program, S52AP_fragment_shader); + glLinkProgram(S52AP_shader_program); + glGetProgramiv(S52AP_shader_program, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(S52AP_shader_program, INFOLOG_LEN, NULL, infoLog); + // printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", + // infoLog); + ret_val = false; + } + } +#endif + shadersLoaded = true; + + return ret_val; +} + + +#endif diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.h new file mode 100755 index 0000000..aeecc74 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.h @@ -0,0 +1,139 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: S52 Presentation Library + * Authors: David Register + * Jesper Weissglas + * + *************************************************************************** + * Copyright (C) 2010 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +#ifndef __S52SHADERS_H__ +#define __S52SHADERS_H__ + +#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL) +#include +#include + +#include "s52plibGL.h" + +extern const GLchar* S52_preamble; + +enum Consts { INFOLOG_LEN = 512 }; + +class S52_GLShaderProgram +{ +public: + + class Builder { + public: + Builder() : linked_(false) { + programId_ = glCreateProgram(); + } + + Builder & addShaderFromSource(std::string const &shaderSource, GLenum shaderType) { + char const *shaderCStr = shaderSource.c_str(); + GLuint shaderId = glCreateShader(shaderType); + + GLchar const* files[] = { S52_preamble, shaderCStr }; + GLint lengths[] = { (GLint)strlen(S52_preamble), (GLint)strlen(shaderCStr) }; + + glShaderSource(shaderId, 2, files, lengths); + //glShaderSource(shaderId, 1, &shaderCStr, nullptr); + + glCompileShader(shaderId); + glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(shaderId, INFOLOG_LEN, NULL, infoLog); + printf("ERROR::SHADER::COMPILATION_FAILED\n%s\n", infoLog); + //ret_val = false; + } + + glAttachShader(programId_, shaderId); + return *this; + } + +// void addShaderFromFile(std::string const &shaderFile, GLenum shaderType) { +// std::ifstream fileName(shaderFile); +// std::istreambuf_iterator fileBegin(fileName), fileEnd; +// std::string fileContents(fileBegin, fileEnd); +// return addShaderFromSource(fileContents, shaderType); +// } + + S52_GLShaderProgram linkProgram() { + glLinkProgram(programId_); + glGetProgramiv( programId_, GL_LINK_STATUS, &linkSuccess); //requesting the status + if (linkSuccess == GL_FALSE) { + glGetProgramInfoLog(programId_, INFOLOG_LEN, NULL, infoLog); + printf("ERROR::SHADER::LINK_FAILED\n%s\n", infoLog); + } + + S52_GLShaderProgram theProgram(programId_); + //deleteAttachedShaders(programId_); + linked_ = true; + return theProgram; + } + + ~Builder() { + if(!linked_) { + glDeleteProgram(programId_); + } + } + private: + GLuint programId_; + bool linked_; + GLint success; + GLint linkSuccess; + GLchar infoLog[INFOLOG_LEN]; + }; + + S52_GLShaderProgram() : programId_(0) { } + + S52_GLShaderProgram(S52_GLShaderProgram &&other) { + *this = std::move(other); + } + + S52_GLShaderProgram &operator=(S52_GLShaderProgram &&other) + { + programId_ = other.programId_; + other.programId_ = 0; + + if (other.programId_ != 0) { + glDeleteProgram(other.programId_); + } + + return *this; + } + + ~S52_GLShaderProgram() { } + + GLuint programId() const { return programId_; } + + S52_GLShaderProgram(S52_GLShaderProgram const &other) = delete; + S52_GLShaderProgram &operator=(S52_GLShaderProgram const &other) = delete; +private: + S52_GLShaderProgram(GLuint programId) : programId_(programId) { } + GLuint programId_; +}; + +#endif + + + +#endif diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.cpp b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.cpp new file mode 100644 index 0000000..eba8e1a --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.cpp @@ -0,0 +1,189 @@ +/****************************************************************************** + * + * Project: OpenCPN + * Purpose: S52 Utility Library + * Author: David Register, Sylvain Duclos + * + *************************************************************************** + * Copyright (C) 2010 by David S. Register * + * * + * Copyright (C) 2000-2004 Sylvain Duclos * + * sduclos@users.sourceforgue.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + *************************************************************************** + * + */ + + +#include "s52utils.h" +//#include // FILE +//#include // strncmp() + +//#include // exit() + +// configuration file +#define CONF_NAME "./s52test.conf" + +#define TRUE 1 +#define FALSE 0 + +//#define CPATH_MAX 255 +/////////////////////////////////////////////////////////////////// +// +// GLOBAL MARINER PARAMETER (will move out) +// +// NOTE: value for Chart No 1 found in README +// +// Soundings ON +// Text ON +// Depth Shades 4 +// Safety Contour 10 m +// Safety Depth 7 m +// Shallow 5 m +// Deep 30 m + +/* A) value for Chart No 1 */ +/* +double SHOW_TEXT = TRUE; // view group 23 +double TWO_SHADES = FALSE; // flag indicating selection of two depth +shades (on/off) [default ON] double SAFETY_CONTOUR = 10.0; // selected +safety contour (meters) [IMO PS 3.6] double SAFETY_DEPTH = 7.0; // +selected safety depth (meters) [IMO PS 3.7] double SHALLOW_CONTOUR = 5.0; // +selected shallow water contour (meters) (optional) double DEEP_CONTOUR = 30.0; +// selected deepwatercontour (meters) (optional) +*/ + +/* B) value for testing */ +/* +//gboolean TWO_SHADES = TRUE; // flag indicating selection of two depth +shades (on/off) [default ON] gboolean TWO_SHADES = FALSE; // flag +indicating selection of two depth shades (on/off) [default ON] gboolean +SHOW_TEXT = TRUE; // view group 23 +//double SAFETY_DEPTH = 30.0; // selected safety depth (meters) [IMO +PS 3.7] +//double SHALLOW_CONTOUR = 2.0; // selected shallow water contour +(meters) (optional) double SAFETY_DEPTH = 15.0; // selected safety +depth (meters) [IMO PS 3.7] double SHALLOW_CONTOUR = 5.0; // selected +shallow water contour (meters) (optional) +//double SAFETY_CONTOUR = 30.0; // selected safety contour (meters) [IMO +PS 3.6] +//double DEEP_CONTOUR = 30.0; // selected deepwatercontour (meters) +(optional) +//double SAFETY_CONTOUR = 5.0; // selected safety contour (meters) [IMO +PS 3.6] +//double DEEP_CONTOUR = 10.0; // selected deepwatercontour (meters) +(optional) double SAFETY_CONTOUR = 10.0; // selected safety contour +(meters) [IMO PS 3.6] double DEEP_CONTOUR = 15.0; // selected +deepwatercontour (meters) (optional) +*/ + +/* param needed for certain conditional symbology */ +/* +gboolean SHALLOW_PATTERN = FALSE; // flag indicating selection of shallow +water highlight (on/off)(optional) [default OFF] gboolean SHIPS_OUTLINE = +FALSE; // flag indicating selection of ship scale symbol (on/off) [IMO +PS 8.4] double DISTANCE_TAGS = 0.0; // selected spacing of "distance to +run" tags at a route (nm) double TIME_TAGS = 0.0; // selected +spacing of time tags at the pasttrack (min) gboolean FULL_SECTORS = TRUE; // +show full length light sector lines gboolean SYMBOLIZED_BND = TRUE; // +symbolized area boundaries +*/ + +// WARNING: must be in sync with S52_MAR_param_t +/* +static char *_MARparamNm[] = { + "S52_MAR_NONE", //= 0, // default + "S52_MAR_SHOW_TEXT", //= 1, // view group 23 + "S52_MAR_TWO_SHADES", //= 2, // flag indicating selection of two +depth shades (on/off) [default ON] "S52_MAR_SAFETY_CONTOUR", //= 3, // +selected safety contour (meters) [IMO PS 3.6] "S52_MAR_SAFETY_DEPTH", //= 4, +// selected safety depth (meters) [IMO PS 3.7] "S52_MAR_SHALLOW_CONTOUR", //= +5, // selected shallow water contour (meters) (optional) + "S52_MAR_DEEP_CONTOUR", //= 6, // selected deepwatercontour (meters) +(optional) "S52_MAR_SHALLOW_PATTERN", //= 7, // flag indicating selection of +shallow water highlight (on/off)(optional) [default OFF] + "S52_MAR_SHIPS_OUTLINE", //= 8, // flag indicating selection of ship +scale symbol (on/off) [IMO PS 8.4] "S52_MAR_DISTANCE_TAGS", //= 9, // +selected spacing of "distance to run" tags at a route (nm) "S52_MAR_TIME_TAGS", +//= 10, // selected spacing of time tags at the pasttrack (min) + "S52_MAR_FULL_SECTORS", //= 11, // show full length light sector lines + "S52_MAR_SYMBOLIZED_BND", //= 12, // symbolized area boundaries + "S52_MAR_SYMPLIFIED_PNT", //= 13, // simplified point + "S52_MAR_DISP_CATEGORY", //= 14, // display category + "S52_MAR_COLOR_PALETTE", //= 15, // color palette + + "S52_MAR_NUM" //= 16 // number of parameters +}; +*/ + +// WARNING: must be in sync with _MARparamNm +static double _MARparamVal[] = { + 0.0, // NONE + TRUE, // SHOW_TEXT + FALSE, // TWO_SHADES + + 3.0, // SAFETY_CONTOUR + // 0.0, // SAFETY_CONTOUR --to test DEPCNT02 selection (GL) in + // CA49995A.000 0.5, // SAFETY_CONTOUR --to test DEPCNT02 selection (GL) + // in CA49995A.000 + + 3.0, // SAFETY_DEPTH + // 5.0, // SAFETY_DEPTH + + 2.0, // SHALLOW_CONTOUR + 6.0, // DEEP_CONTOUR + + // FALSE, // SHALLOW_PATTERN + TRUE, // SHALLOW_PATTERN + + FALSE, // SHIPS_OUTLINE + 0.0, // DISTANCE_TAGS + 0.0, // TIME_TAGS + TRUE, // FULL_SECTORS + FALSE, // SYMBOLIZED_BND + + FALSE, // SYMPLIFIED_PNT + + // 'D', // S52_MAR_DISP_CATEGORY --DISPLAYBASE + // 'S', // S52_MAR_DISP_CATEGORY --STANDARD + 'O', // S52_MAR_DISP_CATEGORY --OTHER + + // 0, // S52_MAR_COLOR_PALETTE --DAY_BRIGHT + 1, // S52_MAR_COLOR_PALETTE --DAY_BLACKBACK + // 2, // S52_MAR_COLOR_PALETTE --DAY_WHITEBACK + // 3, // S52_MAR_COLOR_PALETTE --DUSK + // 4, // S52_MAR_COLOR_PALETTE --NIGHT + + 16.0 // NUM +}; + +double S52_getMarinerParam(S52_MAR_param_t param) +// return Mariner parameter or '0.0' if fail +// FIXME: check mariner param against groups selection +{ + // DSR + return _MARparamVal[param]; +} + +int S52_setMarinerParam(S52_MAR_param_t param, double val) { + if (S52_MAR_NONE < param && param < S52_MAR_NUM) + _MARparamVal[param] = val; + else + return FALSE; + + return TRUE; +} diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.h b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.h new file mode 100644 index 0000000..421e3a5 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.h @@ -0,0 +1,91 @@ +// S52utils.h: utility +// +// Project: OpENCview + +/* + This file is part of the OpENCview project, a viewer of ENC + Copyright (C) 2000-2004 Sylvain Duclos sduclos@users.sourceforgue.net + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef __S52UTILS_H__ +#define __S52UTILS_H__ + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif // precompiled headers + +inline int roundint (double x) +{ +#ifdef __WXOSX__ + return wxRound(x); //FS#1278 +#else + int tmp = static_cast(x); + tmp += (x - tmp >= .5) - (x - tmp <= -.5); + return tmp; +#endif +} + +#ifdef SOLARIS +// well should be cc +#define PRINTF printf(__FILE__ ":%i: : ", __LINE__), printf +#else +#define PRINTF printf(__FILE__ ":%i: %s(): ", __LINE__, __FUNCTION__), printf +#endif + +// valid label in .conf file +#define CONF_CATALOG "CATALOG" +#define CONF_PLIB "PLIB" +#define CONF_CHART "CHART" +#define CONF_RGB "DUMMY_RGB_BRIGHT" + +// global parameter for mariners' selection +typedef enum _S52_MAR_param_t { + S52_MAR_NONE = 0, // default + S52_MAR_SHOW_TEXT = 1, // view group 23 + S52_MAR_TWO_SHADES = + 2, // flag indicating selection of two depth shades (on/off) [default ON] + S52_MAR_SAFETY_CONTOUR = 3, // selected safety contour (meters) [IMO PS 3.6] + S52_MAR_SAFETY_DEPTH = 4, // selected safety depth (meters) [IMO PS 3.7] + S52_MAR_SHALLOW_CONTOUR = + 5, // selected shallow water contour (meters) (optional) + S52_MAR_DEEP_CONTOUR = 6, // selected deepwatercontour (meters) (optional) + S52_MAR_SHALLOW_PATTERN = 7, // flag indicating selection of shallow water + // highlight (on/off)(optional) [default OFF] + S52_MAR_SHIPS_OUTLINE = 8, // flag indicating selection of ship scale symbol + // (on/off) [IMO PS 8.4] + S52_MAR_DISTANCE_TAGS = + 9, // selected spacing of "distance to run" tags at a route (nm) + S52_MAR_TIME_TAGS = + 10, // selected spacing of time tags at the pasttrack (min) + S52_MAR_FULL_SECTORS = 11, // show full length light sector lines + S52_MAR_SYMBOLIZED_BND = 12, // symbolized area boundaries + + S52_MAR_SYMPLIFIED_PNT = 13, // simplified point + + S52_MAR_DISP_CATEGORY = 14, // display category + + S52_MAR_COLOR_PALETTE = 15, // color palette + + S52_MAR_NUM = 16 // number of parameters +} S52_MAR_param_t; + +extern double S52_getMarinerParam(S52_MAR_param_t param); +extern int S52_setMarinerParam(S52_MAR_param_t param, double val); +#endif diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/文件校验.sha256 b/docs/外部参考/OpenCPN源码基线_f7211b4e/文件校验.sha256 new file mode 100644 index 0000000..3f81cb9 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/文件校验.sha256 @@ -0,0 +1,25 @@ +b09e4785c5ef8418993606c2ba875422f113f30ccee4deadaef3f53aa06318d9 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/gui/src/cm93.cpp +e693b1dbbc40927c3a69a6cf3c16d295393ba378909d8fbae1216d79ab364a70 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.cpp +738f294ed682e5bd8a379f125a80bf07c431f8ee780bc2c2024880286f1c34e3 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/Cs52_shaders.h +d5428f5265d1a82999b50ea6a05a054bf69230ddd2048bb2d9e0cee3a666da43 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.cpp +c3fd65c8d5013d359230bde03173d0711e1652a079e3e5a4af0232a9bb949324 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/DepthFont.h +9e8ec4c5b43b0499b5388e155f7de45f61a4f82edda156957552d7e488939ee5 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.cpp +617f486076ede172c50b129a65df5765d2314594e6355f77920ba55567d338d5 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/TexFont.h +561d9fc303bca922a5b285184ba9e3b82f0ebac72e6c9f4bed99a6b7c51607fa /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.cpp +75b1eb93beb0aaff3a2693c12dde6c597fa5cac5b04c1e2c06896fbb5fb8283e /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/chartsymbols.h +0b226162ee9dd15140db40f16495caf1bd999f3691b15b43cf6efd6306826446 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/color_types.h +1594f55ee1c88752be8004ca0b43a254312cc1dda96d98e7ee74db8fdf4a7093 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.cpp +cc713bce1cceb54f44e95007c173b49f48336e8771072429950991cdcc1fac6b /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/mygeom.h +baee3344a78848fc589828ac0c8bab6c147937a60d18d3864ce72265b2fe2238 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.cpp +87f531bb7acc8d8a487f5237a85cebb663d86578e320f3ec566b1b42f18d55b4 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52_plib_utils.h +d094d9345fa76acf601ca2c1a48da7d65f06f3086b8f810e6d1ab9f8a5014ea4 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52cnsy.cpp +b376900cc16a5d2358feda178b9aa82c0853aa84fa61106b1590c49136f98970 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.cpp +2a8fe104e646f52459d98c9aa1a0a014dde79421de6df96987650d7577436a40 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.h +8f08fc6af340e1965b268f8f26c7ed7e7052e488a7cb5150016ebed99c74e98b /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plibGL.h +96d561915b867f5764c7d26b9577a52d121591665a1209a9feca106502cfccfc /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52s57.h +5fea9dbbcd3ea21318792114ea436336f52dc19947acd2bac2af1d626816bbaf /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.cpp +72b23f6ad581612b4a7881c83747170f039405bec2766bf465ca89af37575f0b /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52shaders.h +cee04cc331ff70d94a88ec3061e6714f7ec7b8226727bd14d87be3c2c3c30ba6 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.cpp +4feeefb368bf1aafd1620c15fe54662b8d2f9651cecc4123648b18aecfd1f74d /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.h +ead06c5e9b243275923928f60659a759507ab7ce56abebdefbcf6fcd22dba96c /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv2 +3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986 /root/sourceserver/cm95/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv3 diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv2 b/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv2 new file mode 100644 index 0000000..7d5393a --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv2 @@ -0,0 +1,278 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv3 b/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv3 new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/许可证/COPYING.gplv3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/docs/外部参考/OpenCPN源码基线_f7211b4e/说明.md b/docs/外部参考/OpenCPN源码基线_f7211b4e/说明.md new file mode 100644 index 0000000..da78993 --- /dev/null +++ b/docs/外部参考/OpenCPN源码基线_f7211b4e/说明.md @@ -0,0 +1,25 @@ +# OpenCPN 对照源码基线说明 + +本目录保存 CM95 → PBF → S-52 / OpenCPN 流程等价审计使用的 OpenCPN 上游源码快照。 + +## 固定上游基线 + +- OpenCPN commit:`f7211b4e1874c453e37a7f190aa191c55a25c32e` +- 本地取得来源:`/tmp/opencpn-s52-audit/` +- 保存方式:按上游原始文件内容复制,不对源码做修改。 +- `gui/src/cm93.cpp` 因原 checkout 使用 sparse-checkout 未落到工作树,使用 `git show :gui/src/cm93.cpp` 从同一 commit 直接导出。 +- `/tmp/opencpn_cm93.cpp` 与该 commit 的 `gui/src/cm93.cpp` SHA-256 已确认完全一致。 + +## 保存范围 + +- `上游源码/gui/src/cm93.cpp`:CM93 对象创建、M_COVR 与 CM93→S57/S-52 类转换参考。 +- `上游源码/libs/s52plib/src/`:完整 S-52 Presentation Library 源码目录,包含 CSP、Lookup/rule parser、primitive renderer、chartsymbols 与相关辅助实现。 +- `许可证/`:上游 GPL v2 / GPL v3 许可证原文。 +- `文件校验.sha256`:本目录上游源码与许可证文件的 SHA-256 校验清单。 +- `上游提交.txt`:固定 commit 全长 hash。 + +## 审计规则 + +后续 CM95 的 OpenCPN Code-First S-52 Rendering Equivalence Audit 应优先引用本目录,不再依赖 `/tmp` 临时文件。 +本目录是只读参考快照;如果以后切换 OpenCPN 基线,不覆盖本目录,而应新增新的 commit 基线目录并在审计报告中明确使用哪一版。 +源码文件保留其原始版权与许可证声明;CM95 自有代码与此上游参考快照必须保持清晰边界。