<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2015 Manu Marin / @gyakoo
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>

#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>
#define CC_DSHOW
#define CC_IMPLEMENTATION
#include <camcap.hpp>

//===-----------------------------------------------------------===//
//===-----------------------------------------------------------===//
//class SampleGrabberCallback : public ISampleGrabberCB
//{
//public:
//  SampleGrabberCallback()
//  {
//    InitializeCriticalSection(&critSection);
//    freezeCheck = 0;
//    bufferSetup = false;
//    newFrame = false;
//    latestBufferLength = 0;
//    refcount = 1;
//    hEvent = CreateEvent(NULL, true, false, NULL);
//  }
//
//  ~SampleGrabberCallback()
//  {
//    ptrBuffer = NULL;
//    DeleteCriticalSection(&critSection);
//    CloseHandle(hEvent);
//    SafeFree(pixels);
//  }
//
//  bool setupBuffer(int numBytesIn)
//  {
//    if (bufferSetup) return false;
//    numBytes = numBytesIn;
//    pixels = (unsigned char*)malloc(numBytesIn);
//    if (!pixels) return false;
//    bufferSetup = true;
//    newFrame = false;
//    latestBufferLength = 0;
//    return true;
//  }
//
//  STDMETHODIMP_(ULONG) AddRef()
//  {
//    InterlockedIncrement(&refcount);
//    return (ULONG)refcount;
//  }
//  STDMETHODIMP_(ULONG) Release()
//  {
//    InterlockedDecrement(&refcount);
//    if ( !refcount )
//      delete this;
//    return (ULONG)refcount;
//  }
//
//  STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
//  {
//    UNREFERENCED_PARAMETER(riid);
//    *ppvObject = static_cast<ISampleGrabberCB*>(this);
//    return S_OK;
//  }
//
//  //This method is meant to have less overhead
//  STDMETHODIMP SampleCB(double Time, IMediaSample *pSample)
//  {
//    UNREFERENCED_PARAMETER(Time);
//    if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0) return S_OK;
//
//    HRESULT hr = pSample->GetPointer(&ptrBuffer);
//    if (hr == S_OK)
//    {
//      latestBufferLength = pSample->GetActualDataLength();
//      if (latestBufferLength == numBytes)
//      {
//        EnterCriticalSection(&critSection);
//        memcpy(pixels, ptrBuffer, latestBufferLength);
//        newFrame = true;
//        freezeCheck = 1;
//        LeaveCriticalSection(&critSection);
//        SetEvent(hEvent);
//      }
//      else
//      {
//        printf("ERROR: SampleCB() - buffer sizes do not match\n");
//      }
//    }
//    return S_OK;
//  }
//
//  //This method is meant to have more overhead
//  STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long BufferLen)
//  {
//    UNREFERENCED_PARAMETER(Time);
//    UNREFERENCED_PARAMETER(pBuffer);
//    UNREFERENCED_PARAMETER(BufferLen);
//    return E_NOTIMPL;
//  }
//
//  volatile unsigned long long refcount;
//  int freezeCheck;
//  int latestBufferLength;
//  int numBytes;
//  bool newFrame;
//  bool bufferSetup;
//  unsigned char * pixels;
//  unsigned char * ptrBuffer;
//  CRITICAL_SECTION critSection;
//  HANDLE hEvent;
//};

//===-----------------------------------------------------------===//
//===-----------------------------------------------------------===//
void CheckErrExit(HRESULT hr, const char* format, ...)
{
  if (FAILED(hr))
  {
    va_list args;
    fprintf(stderr, "Error: ");
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    exit(E_FAIL);
  }
}

//===-----------------------------------------------------------===//
//===-----------------------------------------------------------===//
struct CaptureDeviceInfo
{
  CaptureDeviceInfo()
    : name(NULL), desc(NULL), devpath(NULL), waveInId(0), pMoniker(NULL)
    , width(0), height(0), caps(NULL), capsCount(0)
  {
  }

  ~CaptureDeviceInfo()
  {
    Release();
  }

  void Release()
  {
    SafeFree(name);
    SafeFree(desc);
    SafeFree(devpath);
    waveInId = 0;
    SafeRelease(pMoniker);
    width = height = 0;
    SafeFree(caps);
    capsCount = 0;
  }

  wchar_t*     name;
  wchar_t*     desc;
  wchar_t*     devpath;
  unsigned int  waveInId;
  IMoniker* pMoniker;
  unsigned int width;
  unsigned int height;
  VIDEO_STREAM_CONFIG_CAPS* caps;
  unsigned int capsCount;
};

//===-----------------------------------------------------------===//
//===-----------------------------------------------------------===//
int ExtractDevicesInfo(REFGUID category, CaptureDeviceInfo* out_devices, unsigned int max_out_devs)
{
  if (!out_devices || !max_out_devs)
    return 0;

  // Create the System Device Enumerator.
  ICreateDevEnum *pDevEnum;
  HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));
  if (FAILED(hr))
    return 0;

  // Create an enumerator for the category.
  IEnumMoniker* pEnum = NULL;
  hr = pDevEnum->CreateClassEnumerator(category, &pEnum, 0);
  SafeRelease(pDevEnum);
  if (FAILED(hr) || hr == S_FALSE || !pEnum) // false is when the category is empty
    return 0;

  VARIANT varprop;
  IPropertyBag *pPropBag = NULL;
  IMoniker *pMoniker = NULL;
  unsigned int devCount = 0;
  CaptureDeviceInfo* devinfo=out_devices;

  // all devices of this enumerator
  while ( pEnum->Next(1, &pMoniker, NULL) == S_OK && devCount < max_out_devs )
  {
    // get properties
    if ( SUCCEEDED(pMoniker->BindToStorage(0,0,IID_IPropertyBag, (void**)&pPropBag)) )
    {
      devinfo->Release();

      VariantInit(&varprop);
      if ( SUCCEEDED(pPropBag->Read(L"Description", &varprop, 0)) )
      {
        devinfo->desc = BSTR2WChar(varprop.bstrVal);
        VariantClear(&varprop);
      }

      VariantInit(&varprop);
      if ( SUCCEEDED(pPropBag->Read(L"FriendlyName", &varprop, 0)) )
      {
        devinfo->name = BSTR2WChar(varprop.bstrVal);
        VariantClear(&varprop);
      }

      VariantInit(&varprop);
      if ( SUCCEEDED(pPropBag->Read(L"DevicePath", &varprop, 0)) )
      {
        devinfo->devpath = BSTR2WChar(varprop.bstrVal);
        VariantClear(&varprop);
      }

      VariantInit(&varprop);
      if ( SUCCEEDED(pPropBag->Read(L"WaveInID", &varprop, 0)) )
      {
        devinfo->waveInId = varprop.uintVal;
        VariantClear(&varprop);
      }
      devinfo->pMoniker = pMoniker;
      devinfo->pMoniker->AddRef(); // we keep a ref to this

      SafeRelease(pPropBag);
      ++devCount;
      ++devinfo;
    }
    SafeRelease(pMoniker);
  }

  pEnum->Release();
  return devCount;
}

//===-----------------------------------------------------------===//
//===-----------------------------------------------------------===//
HRESULT GetDeviceInfoCaps(CaptureDeviceInfo* devinfo, IAMStreamConfig* pStreamCfg)
{
  HRESULT hr = S_OK;
  // some caps
  int capCount = 0, capSize = 0;
  if (SUCCEEDED(pStreamCfg->GetNumberOfCapabilities(&capCount, &capSize)) && capSize == sizeof(VIDEO_STREAM_CONFIG_CAPS) && capCount > 0)
  {
    devinfo->capsCount = (unsigned int)capCount;
    devinfo->caps = (VIDEO_STREAM_CONFIG_CAPS*)realloc(devinfo->caps, sizeof(VIDEO_STREAM_CONFIG_CAPS)*capCount);
    VIDEO_STREAM_CONFIG_CAPS* vscc = devinfo->caps;

    AM_MEDIA_TYPE *pmtCfg = NULL;
    for (int iFormat = 0; iFormat < capCount; ++iFormat, ++vscc)
    {
      ZeroMemory(vscc, sizeof(VIDEO_STREAM_CONFIG_CAPS));
      if (FAILED(pStreamCfg->GetStreamCaps(iFormat, &pmtCfg, (BYTE*)vscc))) continue;
      ReleaseMediaType(pmtCfg);
    }
  }
  else
  {
    hr = E_FAIL;
  }
  return hr;
}

//===-----------------------------------------------------------===//
// From VideoInput
//===-----------------------------------------------------------===//
HRESULT RouteCrossbar(ICaptureGraphBuilder2 **ppBuild, IBaseFilter **pVidInFilter, int conType, GUID captureMode)
{
  //create local ICaptureGraphBuilder2
  ICaptureGraphBuilder2 *pBuild = NULL;
  pBuild = *ppBuild;

  //create local IBaseFilter
  IBaseFilter *pVidFilter = NULL;
  pVidFilter = *pVidInFilter;

  // Search upstream for a crossbar.
  IAMCrossbar *pXBar1 = NULL;
  HRESULT hr = pBuild->FindInterface(&LOOK_UPSTREAM_ONLY, NULL, pVidFilter, IID_IAMCrossbar, (void**)&pXBar1);
  if (SUCCEEDED(hr))
  {
    bool foundDevice = false;
    printf("SETUP: You are not a webcam! Setting Crossbar\n");
    pXBar1->Release();

    IAMCrossbar *Crossbar;
    hr = pBuild->FindInterface(&captureMode, &MEDIATYPE_Interleaved, pVidFilter, IID_IAMCrossbar, (void **)&Crossbar);

    if (hr != NOERROR) {
      hr = pBuild->FindInterface(&captureMode, &MEDIATYPE_Video, pVidFilter, IID_IAMCrossbar, (void **)&Crossbar);
    }

    LONG lInpin, lOutpin;
    hr = Crossbar->get_PinCounts(&lOutpin, &lInpin);

    BOOL IPin = TRUE; LONG pIndex = 0, pRIndex = 0, pType = 0;

    while (pIndex < lInpin)
    {
      hr = Crossbar->get_CrossbarPinInfo(IPin, pIndex, &pRIndex, &pType);

      if (pType == conType) {
        printf("SETUP: Found Physical Interface");

        switch (conType) {

        case PhysConn_Video_Composite:
          printf(" - Composite\n");
          break;
        case PhysConn_Video_SVideo:
          printf(" - S-Video\n");
          break;
        case PhysConn_Video_Tuner:
          printf(" - Tuner\n");
          break;
        case PhysConn_Video_USB:
          printf(" - USB\n");
          break;
        case PhysConn_Video_1394:
          printf(" - Firewire\n");
          break;
        }

        foundDevice = true;
        break;
      }
      pIndex++;

    }

    if (foundDevice) {
      BOOL OPin = FALSE; LONG pOIndex = 0, pORIndex = 0, pOType = 0;
      while (pOIndex < lOutpin)
      {
        hr = Crossbar->get_CrossbarPinInfo(OPin, pOIndex, &pORIndex, &pOType);
        if (pOType == PhysConn_Video_VideoDecoder)
          break;
      }
      Crossbar->Route(pOIndex, pIndex);
    }
    else {
      printf("SETUP: Didn't find specified Physical Connection type. Using Defualt. \n");
    }

    //we only free the crossbar when we close or restart the device
    //we were getting a crash otherwise
    //if(Crossbar)Crossbar->Release();
    //if(Crossbar)Crossbar = NULL;

    if (pXBar1)pXBar1->Release();
    if (pXBar1)pXBar1 = NULL;

  }
  else {
    printf("SETUP: You are a webcam or snazzy firewire cam! No Crossbar needed\n");
    return hr;
  }

  return hr;
}

//===-----------------------------------------------------------===//
//===-----------------------------------------------------------===//
int GetMediaSubtypeBytes(GUID mediatype)
{
  if      (mediatype == MEDIASUBTYPE_RGB24) return 3;
  else if (mediatype == MEDIASUBTYPE_RGB32) return 4;
  else if (mediatype == MEDIASUBTYPE_RGB555) return 2;
  else if (mediatype == MEDIASUBTYPE_RGB565) return 2;
  else
  {
    DebugBreak(); // implement other types
  }
  return 0;
}

//===-----------------------------------------------------------===//
//===-----------------------------------------------------------===//
HRESULT SetSizeFormat(AM_MEDIA_TYPE* pMediaType, IAMStreamConfig* pStreamCfg, int attemptWidth, int attemptHeight, GUID mediatype, int attemptFPS=-1)
{
  VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER*>(pMediaType->pbFormat);

  // store current config
  int lastWidth = pVih->bmiHeader.biWidth;
  int lastHeight = pVih->bmiHeader.biHeight;
  GUID lastFormatType = pMediaType->formattype;
  GUID lastMajorType = pMediaType->majortype;
  GUID lastSubType = pMediaType->subtype;
  ULONG lastSampleSize = pMediaType->lSampleSize;
  REFERENCE_TIME lastAvgTimePerFrame = pVih->AvgTimePerFrame;

  // try these config
  pVih->bmiHeader.biWidth = attemptWidth;
  pVih->bmiHeader.biHeight = attemptHeight;
  pMediaType->formattype = FORMAT_VideoInfo;
  pMediaType->majortype = MEDIATYPE_Video;
  pMediaType->subtype = mediatype;
  pMediaType->lSampleSize = attemptWidth * attemptHeight * GetMediaSubtypeBytes(mediatype); // tries RGB
  if (attemptFPS > 0)
    pVih->AvgTimePerFrame = (REFERENCE_TIME)( (1.0/attemptFPS)*1e7 ); // time per frame in units of 100ns

  // try this format, if error reverts to saved one
  HRESULT hr = pStreamCfg->SetFormat(pMediaType);
  if (hr != S_OK)
  {
    pVih->bmiHeader.biWidth = lastWidth;
    pVih->bmiHeader.biHeight= lastHeight;
    pMediaType->formattype = lastFormatType;
    pMediaType->majortype = lastMajorType;
    pMediaType->subtype = lastSubType;
    pMediaType->lSampleSize = lastSampleSize;
    pVih->AvgTimePerFrame= lastAvgTimePerFrame;
    pStreamCfg->SetFormat(pMediaType);
  }
  return hr;
}

void test_camcap()
{
  camcap* cc = 0;
  camcap_opts opts = { 0 };
  opts.input_flags = CC_FLAG_VIDEOINPUT;

  // initializes lib
  cc_init(&cc, &opts);

  // is there any video input?
  if (cc_idev_count(cc) > 0)
  {
    // we use the first available one
    int idev = 0;

    // initialize input device
    if (cc_idev_init(cc, idev) == CC_OK)
    {
      // how many modes?
      int modescount = cc_idev_modes(cc, idev, NULL, 0);
      if (modescount > 0)
      {
        // get the modes
        camcapidev_mode* modes = (camcapidev_mode*)calloc(modescount, sizeof(camcapidev_mode));
        cc_idev_modes(cc, idev, modes, modescount);

        // look for first rgb24 mode
        for (int i = 0; i < modescount; ++i)
        {
          //printf("%d x %d x %d (%s)\n", modes[i].width, modes[i].height, modes[i].bitcount, cc_get_format_type_name(modes[i].video_format_type));
          if (modes[i].video_format_type == CC_VIDEOFMT_RGB24)
            if (cc_idev_set_mode(cc, idev, modes + i) == CC_OK)
              break;
        }
        SafeFree(modes);
      }
    }

    // deinitializes input device
    cc_idev_deinit(cc, idev);
  }

  // deinitializes lib
  cc_deinit(&cc);
}
//===-----------------------------------------------------------===//
//===-----------------------------------------------------------===//
int main(int argc, const char** argv)
{
  UNREFERENCED_PARAMETER(argc);
  UNREFERENCED_PARAMETER(argv);

  test_camcap();
  exit(0);

  HRESULT hr;
  // Initializing COM
  {
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    //hr = CoInitialize(NULL);
    CheckErrExit(hr, "Cannot initialize COM\n");
  }

  // List capture devices
  static const int MAX_DEVICES = 4;
  CaptureDeviceInfo deviceInfos[MAX_DEVICES];
  int devicesCount=0;
  {
    // CLSID_AudioInputDeviceCategory
    devicesCount = ExtractDevicesInfo(CLSID_VideoInputDeviceCategory, deviceInfos, MAX_DEVICES);
    if (devicesCount)
    {
      printf("** Video Capture Devices:\n");
      for (int i = 0; i < devicesCount; ++i)
        if (deviceInfos[i].name)
          printf("\tName: %S", deviceInfos[i].name);
    }
  }

  // no video input, no continue
  if (!devicesCount)
    CheckErrExit(E_FAIL, "No video input found\n");

  // Build the objects to start the capture from the device
  ICaptureGraphBuilder2* pCaptureGraph = NULL;
  IGraphBuilder* pGraphBuilder = NULL;
  IMediaControl* pMediaControl = NULL;
  IBaseFilter* pVideoCaptureFilter = NULL;
  IBaseFilter* pGrabberFilter = NULL;
  IBaseFilter* pNullRenderFilter = NULL;
  IAMStreamConfig* pStreamCfg = NULL;
  AM_MEDIA_TYPE* pMediaType = NULL;
  ISampleGrabber* pGrabber = NULL;
  GUID captureMode = PIN_CATEGORY_CAPTURE;
  //SampleGrabberCallback* pGrabberCB = NULL;
  char* pFrameBuffer = NULL;
  {
    hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void **)&pCaptureGraph);
    CheckErrExit(hr, "Cannot create capture graph builder\n");
    hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraphBuilder);
    CheckErrExit(hr, "Cannot create graph builder\n");
    hr = pCaptureGraph->SetFiltergraph(pGraphBuilder);
    CheckErrExit(hr, "Cannot set filter graph\n");
    hr = deviceInfos[0].pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pVideoCaptureFilter);
    CheckErrExit(hr, "Cannot bind device to create capture filter object\n");
    hr = pGraphBuilder->AddFilter(pVideoCaptureFilter, deviceInfos[0].name);
    CheckErrExit(hr, "Cannot add capture base filter to graph builder\n");
    // do we have a preview pin through Smart Tee filter?
    hr = pCaptureGraph->FindInterface(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pVideoCaptureFilter, IID_IAMStreamConfig, (void **)&pStreamCfg);
    if (SUCCEEDED(hr))
    {
      captureMode = PIN_CATEGORY_PREVIEW;
      SafeRelease(pStreamCfg);
    }
    // route crossbar (just in case)
    hr = RouteCrossbar(&pCaptureGraph, &pVideoCaptureFilter, PhysConn_Video_Composite, captureMode);
    // config stream for preview mode
    hr = pCaptureGraph->FindInterface(&captureMode, &MEDIATYPE_Video, pVideoCaptureFilter, IID_IAMStreamConfig, (void **)&pStreamCfg);
    CheckErrExit(hr, "Cannot configure the stream\n");
    hr = pStreamCfg->GetFormat(&pMediaType);
    CheckErrExit(hr, "Cannot get format of media\n");

    hr = GetDeviceInfoCaps(deviceInfos, pStreamCfg);
    //SetSizeFormat(pMediaType, pStreamCfg, 320, 240, MEDIASUBTYPE_RGB24); // forcing specific size/format
    //SetSizeFormatFromCaps(pMediaType, pStreamCfg, deviceInfos[0].caps[0]); // or getting one from caps

    VIDEOINFOHEADER *pVideoInfo = reinterpret_cast<VIDEOINFOHEADER*>(pMediaType->pbFormat);
    deviceInfos[0].width = pVideoInfo->bmiHeader.biWidth;
    deviceInfos[0].height = pVideoInfo->bmiHeader.biHeight;

    hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pGrabberFilter);
    CheckErrExit(hr, "Cannot create frame grabber filter\n");

    hr = pGraphBuilder->AddFilter(pGrabberFilter, L"Sample Grabber");
    CheckErrExit(hr, "Cannot add grabber filter\n");

    hr = pGrabberFilter->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber);
    CheckErrExit(hr, "Cannot create/query grabber object\n");

    // frame grabber.
    //pGrabberCB = new SampleGrabberCallback;
    //pGrabberCB->setupBuffer(pMediaType->lSampleSize);
    pFrameBuffer = (char*)malloc(pMediaType->lSampleSize);
    pGrabber->SetOneShot(FALSE); // for video, not photo
    pGrabber->SetBufferSamples(TRUE);
    //hr = pGrabber->SetCallback(pGrabberCB,0);
    //CheckErrExit(hr, "Cannot set callback for grabber\n");

    AM_MEDIA_TYPE mt;
    ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
    mt.majortype = pMediaType->majortype;
    mt.subtype = pMediaType->subtype;
    mt.formattype = pMediaType->formattype;
    pGrabber->SetMediaType(&mt);

    SafeRelease(pStreamCfg); // don't need it anymore

    // need to specify a destination filter, even if it's null
    hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pNullRenderFilter);
    CheckErrExit(hr, "Cannot create Null Renderer\n");
    hr = pGraphBuilder->AddFilter(pNullRenderFilter, L"NullRenderer");

    // Connects an output pin on a source filter to a sink filter, optionally through an intermediate filter.
    // https://msdn.microsoft.com/en-us/library/aa930715.aspx (samples)
    hr = pCaptureGraph->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pVideoCaptureFilter, pGrabberFilter, pNullRenderFilter);
    CheckErrExit(hr, "Cannot connect the Preview PIN (RenderStream)\n");

    // Set null the Sync Source, so filters will run as quickly as possible
    IMediaFilter *pMediaFilter = NULL;
    hr = pGraphBuilder->QueryInterface(IID_IMediaFilter, (void**)&pMediaFilter);
    CheckErrExit(hr, "Cannot get IID_IMediaFilter\n");
    pMediaFilter->SetSyncSource(NULL);
    SafeRelease(pMediaFilter);

    hr = pGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&pMediaControl);
    CheckErrExit(hr, "Cannot create media control object\n");
    hr = pMediaControl->Run();
    CheckErrExit(hr, "Cannot run the stream/start the graph\n");

    // In case of NO Callback, block until we're getting frames
    {
      long bufferSize = pMediaType->lSampleSize;
      while (hr != S_OK)
      {
        hr = pGrabber->GetCurrentBuffer(&bufferSize, (long *)pFrameBuffer);
        Sleep(10);
      }
    }
  }

  // release filters
  SafeRelease(pNullRenderFilter);
  SafeRelease(pGrabberFilter);
  SafeRelease(pVideoCaptureFilter);

  // Capture loop
  {
    //regular capture method
    long bufferSize = pMediaType->lSampleSize;
    hr = pGrabber->GetCurrentBuffer(&bufferSize, (long *)pFrameBuffer);
    if (hr == S_OK)
    {
      if (bufferSize == (long)pMediaType->lSampleSize)
      {
        stbi_write_png("capture.png", deviceInfos[0].width, deviceInfos[0].height, 3, pFrameBuffer, deviceInfos[0].width*3);
      }
    }
  }

  SafeFree(pFrameBuffer);
  //SafeRelease(pGrabberCB);
  SafeRelease(pGrabber);
  ReleaseMediaType(pMediaType);
  SafeRelease(pMediaControl);
  SafeRelease(pGraphBuilder);
  SafeRelease(pCaptureGraph);

  for (int i = 0; i < MAX_DEVICES; ++i)
    deviceInfos[i].Release();

  // Deinitializing COM
  {
    CoUninitialize();
  }
  return S_OK;
}<commit_msg>flip buffer. test samples.<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2015 Manu Marin / @gyakoo
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include <stdio.h>
#include <stdlib.h>

#pragma warning(disable:4996 4456 4457)
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>
#pragma warning(default:4996 4456 4457)

#define CC_DSHOW
#define CC_IMPLEMENTATION
#include <camcap.hpp>

void err_callback(int err, const char* msg)
{
  printf("0x%8x: %s\n", err, msg);
}

double get_cur_time()
{
  double f;
  LARGE_INTEGER t, freq;
  QueryPerformanceFrequency(&freq);
  f = 1000.0 / (double)(freq.QuadPart);
  QueryPerformanceCounter(&t);
  return (double)(t.QuadPart)*f;
}

// flips vertical/horizontally and BGR to RGB
// no cache friendly
void flip_buff(void* buff, int width, int height, int bitcount)
{
  const int size = width*height * (bitcount>>3);
  const int mid = size >> 1;
  unsigned char* a = (unsigned char*)buff;
  unsigned char* b = a + size - 1;
  unsigned char t;
  for (int i = 0; i < mid; ++i)
  {
    t = *b;
    *b = *a;
    *a = t;
    ++a;
    --b;
  }
}

///
/// SIMPLE DEMO TO SHOW HOW TO CAPTURE A QUICK SNAPSHOT
/// THIS SAMPLE DOES A BAD ERR CHECKING AND RESOURCE RELEASE
///
void test_camcap1()
{
  camcap* cc = 0;
  camcap_opts opts = { CC_FLAG_VIDEOINPUT, err_callback };

  cc_init(&cc, &opts);
  if (cc_idev_count(cc) == 0) return;
  if (cc_idev_init(cc, 0) != CC_OK) return;
  if (cc_idev_start(cc, 0) != CC_OK) return;
  camcapidev_mode curmode;
  cc_idev_get_current_mode(cc, 0, &curmode);
  while (cc_idev_grab(cc, 0, 100) != CC_OK) {};
  cc_idev_stop(cc, 0);

  double t0 = get_cur_time();
  flip_buff(cc_idev_get_buffer(cc, 0), curmode.width, curmode.height, curmode.bitcount);
  printf("%lf msecs. flipping\n", (get_cur_time() - t0)/1000.0);
  stbi_write_png("defcap.png", curmode.width, curmode.height, curmode.bitcount>>3, cc_idev_get_buffer(cc, 0), curmode.width * (curmode.bitcount >> 3));

  cc_idev_deinit(cc, 0);
  cc_deinit(&cc);
}

///
/// THIS SAMPLE INITIALIZES A WEBCAM, AND TAKES A SNAPSHOT FOR EVERY SUPPORTED RGB24 MODE
///
void test_camcap2()
{
  camcap* cc = 0;
  camcap_opts opts = { CC_FLAG_VIDEOINPUT, err_callback };
  char fname[256];

  // initializes lib
  cc_init(&cc, &opts);

  // is there any video input?
  if (cc_idev_count(cc) > 0)
  {
    // we use the first available one
    int idev = 0;

    // initialize input device
    if (cc_idev_init(cc, idev) == CC_OK)
    {
      // how many modes?
      int modescount = cc_idev_modes(cc, idev, NULL, 0);
      if (modescount > 0)
      {
        // get the modes
        camcapidev_mode* modes = (camcapidev_mode*)calloc(modescount, sizeof(camcapidev_mode));
        cc_idev_modes(cc, idev, modes, modescount);

        // sample: take a snapshot of every rgb24 format found and save it to png
        camcapidev_mode* curmode = NULL;
        int iCurMode = 0;
        for (; iCurMode < modescount; ++iCurMode)
        {
          curmode = modes + iCurMode;
          if (curmode->video_format_type == CC_VIDEOFMT_RGB24 /*&& curmode->bitcount == 24*/)
          {
            if (cc_idev_set_mode(cc, idev, curmode) == CC_OK)
            {
              // start capture
              if (cc_idev_start(cc, idev) == CC_OK)
              {
                // grab frame to save into a png
                while (cc_idev_grab(cc, idev, 100) != CC_OK); // risky, infinite loop if can't get the buffer, add
                cc_idev_stop(cc, idev);
                flip_buff(cc_idev_get_buffer(cc, 0), curmode->width, curmode->height, curmode->bitcount);
                sprintf_s(fname, "capture_%dx%d.png", curmode->width, curmode->height);
                stbi_write_png(fname, curmode->width, curmode->height, 3, cc_idev_get_buffer(cc, idev), curmode->width * 3);
              }
            }
          }
        }
        SafeFree(modes);
      }
    }

    // deinitializes input device
    cc_idev_deinit(cc, idev);
  }

  // deinitializes lib
  cc_deinit(&cc);
}

//===-----------------------------------------------------------===//
//===-----------------------------------------------------------===//
int main(int argc, const char** argv)
{
  UNREFERENCED_PARAMETER(argc);
  UNREFERENCED_PARAMETER(argv);

  //test_camcap1();
  test_camcap2();
  return S_OK;
}<|endoftext|>


<commit_before>#pragma once

#include <charconv>

#pragma GCC diagnostic push
#ifdef __clang__
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Wimplicit-int-conversion"
#pragma GCC diagnostic ignored "-Wshift-sign-overflow"
#pragma GCC diagnostic ignored "-Wundef"
#endif

#include <date/date.h>
#include <date/iso_week.h>

#pragma GCC diagnostic pop

#include "acmacs-base/fmt.hh"
#include "acmacs-base/string.hh"
#include "acmacs-base/sfinae.hh"

// ----------------------------------------------------------------------

namespace date
{
    class date_parse_error : public std::runtime_error
    {
      public:
        using std::runtime_error::runtime_error;
    };

    using period_diff_t = int;

    enum class throw_on_error { no, yes };
    enum class allow_incomplete { no, yes };

    constexpr year_month_day invalid_date() { return year{0} / 0 / 0; }

    inline year_month_day today() { return floor<days>(std::chrono::system_clock::now()); }
    inline size_t current_year() { return static_cast<size_t>(static_cast<int>(today().year())); }

    inline std::string display(const year_month_day& dt, const char* fmt = "%Y-%m-%d") { return dt.ok() ? format(fmt, dt) : std::string{"*invalid-date*"}; }

    inline std::string display(const year_month_day& dt, allow_incomplete allow)
    {
        if (dt.ok()) {
            return format("%Y-%m-%d", dt);
        }
        else if (allow == allow_incomplete::yes) {
            if (static_cast<unsigned>(dt.month()) == 0)
                return fmt::format("{}", static_cast<int>(dt.year()));
            else if (static_cast<unsigned>(dt.day()) == 0)
                return fmt::format("{}-{:02d}", static_cast<int>(dt.year()), static_cast<unsigned>(dt.month()));
            else
                return fmt::format("{}-{:02d}-{:02d}", static_cast<int>(dt.year()), static_cast<unsigned>(dt.month()), static_cast<unsigned>(dt.day()));
        }
        else
            return fmt::format("*invalid-date: {}-{}-{}*", static_cast<int>(dt.year()), static_cast<unsigned>(dt.month()), static_cast<unsigned>(dt.day()));
    }

    inline auto month_3(const year_month_day& dt) { return format("%b", dt); }
    inline auto year_2(const year_month_day& dt) { return format("%y", dt); }
    inline auto year_4(const year_month_day& dt) { return format("%Y", dt); }
    inline auto month3_year2(const year_month_day& dt) { return format("%b %y", dt); }
    inline auto monthtext_year(const year_month_day& dt) { return format("%B %Y", dt); }
    inline auto year4_month2(const year_month_day& dt) { return format("%Y-%m", dt); }
    inline auto year4_month2_day2(const year_month_day& dt) { return format("%Y-%m-%d", dt); }

    inline auto beginning_of_month(const year_month_day& dt) { unsigned mont = static_cast<unsigned>(dt.month()); if (mont == 0) mont = 7; return year_month_day(dt.year(), month{mont}, day{1}); }
    inline auto beginning_of_year(const year_month_day& dt) { return year_month_day(dt.year(), month{1}, day{1}); }
    inline auto beginning_of_week(const year_month_day& dt)
    {
        const auto make = [](const iso_week::year_weeknum_weekday& yw) { return year_month_day{iso_week::year_weeknum_weekday(yw.year(), yw.weeknum(), iso_week::weekday{Monday})}; };
        if (static_cast<unsigned>(dt.day()) == 0) {
            if (static_cast<unsigned>(dt.month()) == 0)
                return make(iso_week::year_weeknum_weekday(dt.year() / month{7} / day{1}));
            else
                return make(iso_week::year_weeknum_weekday(dt.year() / dt.month() / day{1}));
        }
        else {
            return make(iso_week::year_weeknum_weekday(dt));
        }
    }

    // returns date for the last day of the year-month stored in this
    inline auto end_of_month(const year_month_day& dt) { return year_month_day(year_month_day_last(dt.year(), month_day_last(dt.month()))); }

    inline auto months_ago(const year_month_day& dt, period_diff_t number_of_months) { return dt - date::months(number_of_months); }
    inline auto years_ago(const year_month_day& dt, period_diff_t number_of_years) { return dt - date::years(number_of_years); }
    inline auto weeks_ago(const year_month_day& dt, period_diff_t number_of_weeks) { return static_cast<date::sys_days>(dt) - date::weeks(number_of_weeks); }

    inline auto next_month(const year_month_day& dt) { return dt + date::months(1); }
    inline auto next_year(const year_month_day& dt) { return dt + date::years(1); }
    inline auto next_week(const year_month_day& dt) { return year_month_day{static_cast<sys_days>(dt) + date::weeks(1)}; }

    inline auto& increment_month(year_month_day& dt, period_diff_t number_of_months = 1) { dt += date::months(number_of_months); return dt; }
    inline auto& decrement_month(year_month_day& dt, period_diff_t number_of_months = 1) { dt -= date::months(number_of_months); return dt; }
    inline auto& increment_year(year_month_day& dt, period_diff_t number_of_years = 1) { dt += date::years(number_of_years); return dt; }
    inline auto& decrement_year(year_month_day& dt, period_diff_t number_of_years = 1) { dt -= date::years(number_of_years); return dt; }
    inline auto& increment_week(year_month_day& dt, period_diff_t number_of_weeks = 1) { dt = static_cast<date::sys_days>(dt) + date::weeks{number_of_weeks}; return dt; }
    inline auto& decrement_week(year_month_day& dt, period_diff_t number_of_weeks = 1) { dt = static_cast<date::sys_days>(dt) - date::weeks{number_of_weeks}; return dt; }

    template <typename S> inline year_month_day from_string(S&& source, const char* fmt)
    {
            year_month_day result = invalid_date();
            std::istringstream in(std::string{source});
            if (from_stream(in, fmt, result)) {
                if (result.year() < year{30})
                    result += years(2000);
                else if (result.year() < year{100})
                    result += years(1900);
            }
            return result;
    }

    template <typename S> inline year_month_day from_string(S&& source, allow_incomplete allow = allow_incomplete::no, throw_on_error toe = throw_on_error::yes)
    {
        for (const char* fmt : {"%Y-%m-%d", "%Y%m%d", "%m/%d/%Y", "%d/%m/%Y", "%B%n %d%n %Y", "%B %d,%n %Y", "%b%n %d%n %Y", "%b %d,%n %Y"}) {
            if (const auto result = from_string(std::forward<S>(source), fmt); result.ok())
                return result;
        }
        if (allow == allow_incomplete::yes) {
            for (const char* fmt : {"%Y-00-00", "%Y-%m-00", "%Y-%m", "%Y%m", "%Y"}) {
                // date lib cannot parse incomplete date
                const std::string_view src{source};
                constexpr int invalid = 99999;
                struct tm tm;
                tm.tm_mon = tm.tm_mday = invalid;
                if (strptime(src.data(), fmt, &tm) == &*src.end()) {
                    if (tm.tm_mon == invalid)
                        return year{tm.tm_year + 1900} / 0 / 0;
                    else
                        return year{tm.tm_year + 1900} / month{static_cast<unsigned>(tm.tm_mon) + 1} / 0;
                }
            }
        }
        if (toe == throw_on_error::yes)
            throw date_parse_error(fmt::format("cannot parse date from \"{}\" (allow_incomplete: {})", source, allow == allow_incomplete::yes));
        return invalid_date();
    }

    template <typename S> inline year year_from_string(S&& source)
    {
        int yr;
        if (const auto [p, ec] = std::from_chars(&*source.begin(), &*source.end(), yr); ec == std::errc{} && p == &*source.end())
            return year{yr};
        else
            return year{9999};
    }

    template <typename S> inline month month_from_string(S&& source)
    {
        unsigned mo;
        if (const auto [p, ec] = std::from_chars(&*source.begin(), &*source.end(), mo); ec == std::errc{} && p == &*source.end())
            return month{mo};
        else
            return month{99};
    }

    template <typename S> inline day day_from_string(S&& source)
    {
        unsigned dy;
        if (const auto [p, ec] = std::from_chars(&*source.begin(), &*source.end(), dy); ec == std::errc{} && p == &*source.end())
            return day{dy};
        else
            return day{99};
    }

    inline std::string current_date_time()
    {
        const auto now = std::chrono::system_clock::now();
        const auto in_time_t = std::chrono::system_clock::to_time_t(now);
        return fmt::format("{:%Y-%m-%d %H:%M:%S %Z}", *std::localtime(&in_time_t));
    }

    inline period_diff_t days_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<days>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

    inline period_diff_t weeks_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<weeks>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

    inline period_diff_t months_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<months>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

    inline period_diff_t calendar_months_between_dates(const year_month_day& a, const year_month_day& b)
    {
        return std::chrono::duration_cast<months>(static_cast<sys_days>(beginning_of_month(b)) - static_cast<sys_days>(beginning_of_month(a))).count();
    }

    inline period_diff_t calendar_months_between_dates_inclusive(const year_month_day& a, const year_month_day& b) { return calendar_months_between_dates(a, b) + 1; }

    inline period_diff_t years_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<years>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

} // namespace date

// ----------------------------------------------------------------------

template <typename T> struct fmt::formatter<T, std::enable_if_t<std::is_base_of<date::year_month_day, T>::value, char>> : fmt::formatter<std::string> {
    template <typename FormatCtx> auto format(const date::year_month_day& dt, FormatCtx& ctx) { return fmt::formatter<std::string>::format(date::display(dt, date::allow_incomplete::yes), ctx); }
};

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>date: getting rid of redundant templates<commit_after>#pragma once

#include <charconv>

#pragma GCC diagnostic push
#ifdef __clang__
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Wimplicit-int-conversion"
#pragma GCC diagnostic ignored "-Wshift-sign-overflow"
#pragma GCC diagnostic ignored "-Wundef"
#endif

#include <date/date.h>
#include <date/iso_week.h>

#pragma GCC diagnostic pop

#include "acmacs-base/fmt.hh"
// #include "acmacs-base/string_view.hh"
#include "acmacs-base/sfinae.hh"

// ----------------------------------------------------------------------

namespace date
{
    class date_parse_error : public std::runtime_error
    {
      public:
        using std::runtime_error::runtime_error;
    };

    using period_diff_t = int;

    enum class throw_on_error { no, yes };
    enum class allow_incomplete { no, yes };

    constexpr year_month_day invalid_date() { return year{0} / 0 / 0; }

    inline year_month_day today() { return floor<days>(std::chrono::system_clock::now()); }
    inline size_t current_year() { return static_cast<size_t>(static_cast<int>(today().year())); }

    inline std::string display(const year_month_day& dt, const char* fmt = "%Y-%m-%d") { return dt.ok() ? format(fmt, dt) : std::string{"*invalid-date*"}; }

    inline std::string display(const year_month_day& dt, allow_incomplete allow)
    {
        if (dt.ok()) {
            return format("%Y-%m-%d", dt);
        }
        else if (allow == allow_incomplete::yes) {
            if (static_cast<unsigned>(dt.month()) == 0)
                return fmt::format("{}", static_cast<int>(dt.year()));
            else if (static_cast<unsigned>(dt.day()) == 0)
                return fmt::format("{}-{:02d}", static_cast<int>(dt.year()), static_cast<unsigned>(dt.month()));
            else
                return fmt::format("{}-{:02d}-{:02d}", static_cast<int>(dt.year()), static_cast<unsigned>(dt.month()), static_cast<unsigned>(dt.day()));
        }
        else
            return fmt::format("*invalid-date: {}-{}-{}*", static_cast<int>(dt.year()), static_cast<unsigned>(dt.month()), static_cast<unsigned>(dt.day()));
    }

    inline auto month_3(const year_month_day& dt) { return format("%b", dt); }
    inline auto year_2(const year_month_day& dt) { return format("%y", dt); }
    inline auto year_4(const year_month_day& dt) { return format("%Y", dt); }
    inline auto month3_year2(const year_month_day& dt) { return format("%b %y", dt); }
    inline auto monthtext_year(const year_month_day& dt) { return format("%B %Y", dt); }
    inline auto year4_month2(const year_month_day& dt) { return format("%Y-%m", dt); }
    inline auto year4_month2_day2(const year_month_day& dt) { return format("%Y-%m-%d", dt); }

    inline auto beginning_of_month(const year_month_day& dt) { unsigned mont = static_cast<unsigned>(dt.month()); if (mont == 0) mont = 7; return year_month_day(dt.year(), month{mont}, day{1}); }
    inline auto beginning_of_year(const year_month_day& dt) { return year_month_day(dt.year(), month{1}, day{1}); }
    inline auto beginning_of_week(const year_month_day& dt)
    {
        const auto make = [](const iso_week::year_weeknum_weekday& yw) { return year_month_day{iso_week::year_weeknum_weekday(yw.year(), yw.weeknum(), iso_week::weekday{Monday})}; };
        if (static_cast<unsigned>(dt.day()) == 0) {
            if (static_cast<unsigned>(dt.month()) == 0)
                return make(iso_week::year_weeknum_weekday(dt.year() / month{7} / day{1}));
            else
                return make(iso_week::year_weeknum_weekday(dt.year() / dt.month() / day{1}));
        }
        else {
            return make(iso_week::year_weeknum_weekday(dt));
        }
    }

    // returns date for the last day of the year-month stored in this
    inline auto end_of_month(const year_month_day& dt) { return year_month_day(year_month_day_last(dt.year(), month_day_last(dt.month()))); }

    inline auto months_ago(const year_month_day& dt, period_diff_t number_of_months) { return dt - date::months(number_of_months); }
    inline auto years_ago(const year_month_day& dt, period_diff_t number_of_years) { return dt - date::years(number_of_years); }
    inline auto weeks_ago(const year_month_day& dt, period_diff_t number_of_weeks) { return static_cast<date::sys_days>(dt) - date::weeks(number_of_weeks); }

    inline auto next_month(const year_month_day& dt) { return dt + date::months(1); }
    inline auto next_year(const year_month_day& dt) { return dt + date::years(1); }
    inline auto next_week(const year_month_day& dt) { return year_month_day{static_cast<sys_days>(dt) + date::weeks(1)}; }

    inline auto& increment_month(year_month_day& dt, period_diff_t number_of_months = 1) { dt += date::months(number_of_months); return dt; }
    inline auto& decrement_month(year_month_day& dt, period_diff_t number_of_months = 1) { dt -= date::months(number_of_months); return dt; }
    inline auto& increment_year(year_month_day& dt, period_diff_t number_of_years = 1) { dt += date::years(number_of_years); return dt; }
    inline auto& decrement_year(year_month_day& dt, period_diff_t number_of_years = 1) { dt -= date::years(number_of_years); return dt; }
    inline auto& increment_week(year_month_day& dt, period_diff_t number_of_weeks = 1) { dt = static_cast<date::sys_days>(dt) + date::weeks{number_of_weeks}; return dt; }
    inline auto& decrement_week(year_month_day& dt, period_diff_t number_of_weeks = 1) { dt = static_cast<date::sys_days>(dt) - date::weeks{number_of_weeks}; return dt; }

    inline year_month_day from_string(std::string_view source, const char* fmt)
    {
            year_month_day result = invalid_date();
            std::istringstream in(std::string{source});
            if (from_stream(in, fmt, result)) {
                if (result.year() < year{30})
                    result += years(2000);
                else if (result.year() < year{100})
                    result += years(1900);
            }
            return result;
    }

    inline year_month_day from_string(std::string_view source, allow_incomplete allow = allow_incomplete::no, throw_on_error toe = throw_on_error::yes)
    {
        for (const char* fmt : {"%Y-%m-%d", "%Y%m%d", "%m/%d/%Y", "%d/%m/%Y", "%B%n %d%n %Y", "%B %d,%n %Y", "%b%n %d%n %Y", "%b %d,%n %Y"}) {
            // if (const auto result = from_string(std::forward<S>(source), fmt); result.ok())
            if (const auto result = from_string(source, fmt); result.ok())
                return result;
        }
        if (allow == allow_incomplete::yes) {
            for (const char* fmt : {"%Y-00-00", "%Y-%m-00", "%Y-%m", "%Y%m", "%Y"}) {
                // date lib cannot parse incomplete date
                constexpr int invalid = 99999;
                struct tm tm;
                tm.tm_mon = tm.tm_mday = invalid;
                if (strptime(source.data(), fmt, &tm) == &*source.end()) {
                    if (tm.tm_mon == invalid)
                        return year{tm.tm_year + 1900} / 0 / 0;
                    else
                        return year{tm.tm_year + 1900} / month{static_cast<unsigned>(tm.tm_mon) + 1} / 0;
                }
            }
        }
        if (toe == throw_on_error::yes)
            throw date_parse_error(fmt::format("cannot parse date from \"{}\" (allow_incomplete: {})", source, allow == allow_incomplete::yes));
        return invalid_date();
    }

    inline year year_from_string(std::string_view source)
    {
        int yr;
        if (const auto [p, ec] = std::from_chars(&*source.begin(), &*source.end(), yr); ec == std::errc{} && p == &*source.end())
            return year{yr};
        else
            return year{9999};
    }

    inline month month_from_string(std::string_view source)
    {
        unsigned mo;
        if (const auto [p, ec] = std::from_chars(&*source.begin(), &*source.end(), mo); ec == std::errc{} && p == &*source.end())
            return month{mo};
        else
            return month{99};
    }

    inline day day_from_string(std::string_view source)
    {
        unsigned dy;
        if (const auto [p, ec] = std::from_chars(&*source.begin(), &*source.end(), dy); ec == std::errc{} && p == &*source.end())
            return day{dy};
        else
            return day{99};
    }

    inline std::string current_date_time()
    {
        const auto now = std::chrono::system_clock::now();
        const auto in_time_t = std::chrono::system_clock::to_time_t(now);
        return fmt::format("{:%Y-%m-%d %H:%M:%S %Z}", *std::localtime(&in_time_t));
    }

    inline period_diff_t days_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<days>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

    inline period_diff_t weeks_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<weeks>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

    inline period_diff_t months_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<months>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

    inline period_diff_t calendar_months_between_dates(const year_month_day& a, const year_month_day& b)
    {
        return std::chrono::duration_cast<months>(static_cast<sys_days>(beginning_of_month(b)) - static_cast<sys_days>(beginning_of_month(a))).count();
    }

    inline period_diff_t calendar_months_between_dates_inclusive(const year_month_day& a, const year_month_day& b) { return calendar_months_between_dates(a, b) + 1; }

    inline period_diff_t years_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<years>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

} // namespace date

// ----------------------------------------------------------------------

template <typename T> struct fmt::formatter<T, std::enable_if_t<std::is_base_of<date::year_month_day, T>::value, char>> : fmt::formatter<std::string> {
    template <typename FormatCtx> auto format(const date::year_month_day& dt, FormatCtx& ctx) { return fmt::formatter<std::string>::format(date::display(dt, date::allow_incomplete::yes), ctx); }
};

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>#include <unistd.h>
#include <signal.h>

#include "acmacs-base/argv.hh"
#include "acmacs-base/temp-file.hh"
#include "acmacs-base/string-compare.hh"
#include "acmacs-base/string-split.hh"
#include "acmacs-base/quicklook.hh"
#include "acmacs-base/filesystem.hh"
#include "acmacs-map-draw/setup-dbs.hh"
#include "acmacs-map-draw/log.hh"
#include "acmacs-map-draw/mapi-settings.hh"
#include "acmacs-map-draw/draw.hh"

// ----------------------------------------------------------------------

constexpr const std::string_view sHelpPost = R"(
one input chart: make map
two input charts: make procrustes
output /: do not generate any output
no output: generate temp pdf and open it (and then delete it)

-D <arg> --define <arg>

)";

using namespace acmacs::argv;
struct MapiOptions : public acmacs::argv::v2::argv
{
    MapiOptions(int a_argc, const char* const a_argv[], on_error on_err = on_error::exit) { parse(a_argc, a_argv, on_err); }
    // std::string_view help_pre() const override { return sHelpPost; }
    std::string_view help_post() const override { return sHelpPost; }

    option<str> db_dir{*this, "db-dir"};
    // option<str> seqdb{*this, "seqdb"};

    option<str_array> settings_files{*this, 's'};
    option<str_array> defines{*this, 'D', "define", desc{"see {ACMACSD_ROOT}/share/doc/mapi.org"}};
    option<str_array> apply{*this, 'a', "apply", desc{"comma separated names or json array to use as \"apply\", e.g. [\"/all-grey\",\"/egg\",\"/clades\",\"/labels\"]"}};
    option<bool>      interactive{*this, 'i', "interactive"};
    // option<str>       previous{*this, "previous"};
    option<size_t>    projection{*this, 'p', "projection", dflt{0ul}};
    option<size_t>    secondary_projection{*this, 'r', "secondary-projection", dflt{static_cast<size_t>(-1)}};

    option<bool>      open{*this, "open"};
    option<bool>      ql{*this, "ql"};
    option<str_array> verbose{*this, 'v', "verbose", desc{"comma separated list (or multiple switches) of enablers"}};

    argument<str_array> files{*this, arg_name{"input: chart.ace, chart.save, chart.acd1; output: map.pdf, /"}, mandatory};
};

    // option<str>       apply_from{*this, "apply-from", desc{"read json array to use as \"apply\" from file (or stdin if \"-\""}};
    // option<bool>      clade{*this, "clade"};
    // option<double>    point_scale{*this, "point-scale", dflt{1.0}};
    // option<double>    rotate_degrees{*this, 'r', "rotate-degrees", dflt{0.0}, desc{"counter clockwise"}};
    // option<bool>      flip_ew{*this, "flip-ew"};
    // option<bool>      flip_ns{*this, "flip-ns"};
    // option<str>       save{*this, "save", desc{"save resulting chart with modified projection and plot spec"}};

// ----------------------------------------------------------------------

static std::pair<std::vector<std::string_view>, std::vector<std::string_view>> parse_files(const argument<str_array>& files);
static void signal_handler(int sig_num);

int main(int argc, char* const argv[])
{
    using namespace std::string_view_literals;
    int exit_code = 0;
    try {
        acmacs::log::register_enabler_acmacs_base();
        MapiOptions opt(argc, argv);
        acmacs::log::enable(opt.verbose);
        setup_dbs(opt.db_dir, !opt.verbose.empty());

        const auto [inputs, outputs] = parse_files(opt.files);

        ChartDraw chart_draw{inputs, opt.projection};

        acmacs::mapi::Settings settings{chart_draw};
        settings.load_from_conf({"mapi.json"sv, "clades.json"sv, "vaccines.json"sv});
        settings.load(opt.settings_files);
        settings.set_defines(opt.defines);
        for (size_t chart_no = 0; chart_no < chart_draw.number_of_charts(); ++chart_no)
            settings.setenv_toplevel(fmt::format("chart[{}]", chart_no), chart_draw.chart(chart_no).filename());

        if (opt.interactive)
            signal(SIGHUP, signal_handler);

        for (;;) {
            exit_code = 0;
            try {
                if (!opt.apply.empty()) {
                    for (const auto& to_apply : opt.apply) {
                        if (!to_apply.empty()) {
                            if (to_apply[0] == '{' || to_apply[0] == '[') {
                                settings.apply(to_apply);
                            }
                            else {
                                for (const auto& to_apply_one : acmacs::string::split(to_apply))
                                    settings.apply(to_apply_one);
                            }
                        }
                    }
                }
                else {
                    settings.apply("mapi"sv);
                }

                chart_draw.calculate_viewport();
                AD_INFO("{:.2f}", chart_draw.viewport("mapi main"));
                AD_INFO("transformation: {}", chart_draw.chart(0).modified_transformation());

                if (outputs.empty()) {
                    acmacs::file::temp output{fmt::format("{}--p{}.pdf", fs::path(inputs[0]).stem(), opt.projection)};
                    chart_draw.draw(output, 800, report_time::yes);
                    acmacs::quicklook(output, 2);
                }
                else if (outputs[0] == "/dev/null"sv || outputs[0] == "/"sv) { // do not generate pdf
                }
                else {
                    chart_draw.draw(outputs[0], 800, report_time::yes);
                    acmacs::open_or_quicklook(opt.open, opt.ql, outputs[0]);
                }
            }
            catch (std::exception& err) {
                AD_ERROR("{}", err);
                acmacs::run_and_detach({"submarine"}, 0);
                exit_code = 1;
            }
            if (opt.interactive) {
                fmt::print(stderr, "mapi-i >> ");

                fd_set set;
                FD_ZERO(&set);
                FD_SET(0, &set);
                if (select(FD_SETSIZE, &set, nullptr, nullptr, nullptr) > 0) {
                    // fmt::print(stderr, " (reading)\n");
                    std::string input(20, ' ');
                    if (const auto bytes = ::read(0, input.data(), input.size()); bytes > 0) {
                        input.resize(static_cast<size_t>(bytes));
                        // fmt::print(stderr, " (read {} bytes)\n", bytes);
                    }
                }

                acmacs::run_and_detach({"tink"}, 0);
            }
            else
                break;

            try {
                chart_draw.reset();
                settings.reload();
            }
            catch (std::exception& err) {
                AD_ERROR("{}", err);
                acmacs::run_and_detach({"submarine"}, 0);
            }
        }
    }
    catch (std::exception& err) {
        fmt::print(stderr, "ERROR: {}\n", err);
        exit_code = 2;
    }
    return exit_code;
}

// ----------------------------------------------------------------------

std::pair<std::vector<std::string_view>, std::vector<std::string_view>> parse_files(const argument<str_array>& files)
{
    using namespace std::string_view_literals;
    std::vector<std::string_view> inputs, outputs;
    constexpr std::array input_suffixes{".ace"sv, ".acd1"sv, ".acd1.xz"sv, ".acd1.bz2"sv, ".save"sv, ".save.xz"sv, ".save.bz2"sv};
    const auto is_input = [&input_suffixes](std::string_view name) -> bool {
        return std::any_of(std::begin(input_suffixes), std::end(input_suffixes), [name](std::string_view suff) -> bool { return acmacs::string::endswith(name, suff); });
    };
    for (const auto& file : files) {
        if (is_input(file))
            inputs.push_back(file);
        else
            outputs.push_back(file);
    }
    if (inputs.empty())
        throw std::runtime_error{"no input files (charts) found in the command line"};
    if (inputs.size() > 2)
        throw std::runtime_error{fmt::format("too many input files () found in the command line", inputs)};
    return {inputs, outputs};

} // parse_files

// ----------------------------------------------------------------------

void signal_handler(int sig_num)
{
    fmt::print("SIGNAL {}\n", sig_num);

} // signal_handler

// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>--preview<commit_after>#include <unistd.h>
#include <signal.h>

#include "acmacs-base/argv.hh"
#include "acmacs-base/temp-file.hh"
#include "acmacs-base/string-compare.hh"
#include "acmacs-base/string-split.hh"
#include "acmacs-base/quicklook.hh"
#include "acmacs-base/filesystem.hh"
#include "acmacs-map-draw/setup-dbs.hh"
#include "acmacs-map-draw/log.hh"
#include "acmacs-map-draw/mapi-settings.hh"
#include "acmacs-map-draw/draw.hh"

// ----------------------------------------------------------------------

constexpr const std::string_view sHelpPost = R"(
one input chart: make map
two input charts: make procrustes
output /: do not generate any output
no output: generate temp pdf and open it (and then delete it)

-D <arg> --define <arg>

)";

using namespace acmacs::argv;
struct MapiOptions : public acmacs::argv::v2::argv
{
    MapiOptions(int a_argc, const char* const a_argv[], on_error on_err = on_error::exit) { parse(a_argc, a_argv, on_err); }
    // std::string_view help_pre() const override { return sHelpPost; }
    std::string_view help_post() const override { return sHelpPost; }

    option<str> db_dir{*this, "db-dir"};
    // option<str> seqdb{*this, "seqdb"};

    option<str_array> settings_files{*this, 's'};
    option<str_array> defines{*this, 'D', "define", desc{"see {ACMACSD_ROOT}/share/doc/mapi.org"}};
    option<str_array> apply{*this, 'a', "apply", desc{"comma separated names or json array to use as \"apply\", e.g. [\"/all-grey\",\"/egg\",\"/clades\",\"/labels\"]"}};
    option<bool>      interactive{*this, 'i', "interactive"};
    // option<str>       previous{*this, "previous"};
    option<size_t>    projection{*this, 'p', "projection", dflt{0ul}};
    option<size_t>    secondary_projection{*this, 'r', "secondary-projection", dflt{static_cast<size_t>(-1)}};

    option<bool>      open{*this, "open"};
    option<str>       preview{*this, "preview"};
    // option<bool>      ql{*this, "ql"};
    option<str_array> verbose{*this, 'v', "verbose", desc{"comma separated list (or multiple switches) of enablers"}};

    argument<str_array> files{*this, arg_name{"input: chart.ace, chart.save, chart.acd1; output: map.pdf, /"}, mandatory};
};

    // option<str>       apply_from{*this, "apply-from", desc{"read json array to use as \"apply\" from file (or stdin if \"-\""}};
    // option<bool>      clade{*this, "clade"};
    // option<double>    point_scale{*this, "point-scale", dflt{1.0}};
    // option<double>    rotate_degrees{*this, 'r', "rotate-degrees", dflt{0.0}, desc{"counter clockwise"}};
    // option<bool>      flip_ew{*this, "flip-ew"};
    // option<bool>      flip_ns{*this, "flip-ns"};
    // option<str>       save{*this, "save", desc{"save resulting chart with modified projection and plot spec"}};

// ----------------------------------------------------------------------

static std::pair<std::vector<std::string_view>, std::vector<std::string_view>> parse_files(const argument<str_array>& files);
static void signal_handler(int sig_num);

int main(int argc, char* const argv[])
{
    using namespace std::string_view_literals;
    int exit_code = 0;
    try {
        acmacs::log::register_enabler_acmacs_base();
        MapiOptions opt(argc, argv);
        acmacs::log::enable(opt.verbose);
        setup_dbs(opt.db_dir, !opt.verbose.empty());

        const auto [inputs, outputs] = parse_files(opt.files);

        ChartDraw chart_draw{inputs, opt.projection};

        acmacs::mapi::Settings settings{chart_draw};
        settings.load_from_conf({"mapi.json"sv, "clades.json"sv, "vaccines.json"sv});
        settings.load(opt.settings_files);
        settings.set_defines(opt.defines);
        for (size_t chart_no = 0; chart_no < chart_draw.number_of_charts(); ++chart_no)
            settings.setenv_toplevel(fmt::format("chart[{}]", chart_no), chart_draw.chart(chart_no).filename());

        if (opt.interactive)
            signal(SIGHUP, signal_handler);

        bool opened{false};
        for (;;) {
            exit_code = 0;
            try {
                if (!opt.apply.empty()) {
                    for (const auto& to_apply : opt.apply) {
                        if (!to_apply.empty()) {
                            if (to_apply[0] == '{' || to_apply[0] == '[') {
                                settings.apply(to_apply);
                            }
                            else {
                                for (const auto& to_apply_one : acmacs::string::split(to_apply))
                                    settings.apply(to_apply_one);
                            }
                        }
                    }
                }
                else {
                    settings.apply("mapi"sv);
                }

                chart_draw.calculate_viewport();
                AD_INFO("{:.2f}", chart_draw.viewport("mapi main"));
                AD_INFO("transformation: {}", chart_draw.chart(0).modified_transformation());

                if (outputs.empty()) {
                    acmacs::file::temp output{fmt::format("{}--p{}.pdf", fs::path(inputs[0]).stem(), opt.projection)};
                    chart_draw.draw(output, 800, report_time::yes);
                    acmacs::quicklook(output, 2);
                }
                else if (outputs[0] == "/dev/null"sv || outputs[0] == "/"sv) { // do not generate pdf
                }
                else {
                    chart_draw.draw(outputs[0], 800, report_time::yes);
                    if (opt.open && opened)
                        acmacs::open(outputs[0]);
                    else if (opt.preview) {
                        acmacs::preview(outputs[0], opt.preview);
                        opened = true;
                    }
                }
            }
            catch (std::exception& err) {
                AD_ERROR("{}", err);
                acmacs::run_and_detach({"submarine"}, 0);
                exit_code = 1;
            }
            if (opt.interactive) {
                fmt::print(stderr, "mapi-i >> ");

                fd_set set;
                FD_ZERO(&set);
                FD_SET(0, &set);
                if (select(FD_SETSIZE, &set, nullptr, nullptr, nullptr) > 0) {
                    // fmt::print(stderr, " (reading)\n");
                    std::string input(20, ' ');
                    if (const auto bytes = ::read(0, input.data(), input.size()); bytes > 0) {
                        input.resize(static_cast<size_t>(bytes));
                        // fmt::print(stderr, " (read {} bytes)\n", bytes);
                    }
                }

                acmacs::run_and_detach({"tink"}, 0);
            }
            else
                break;

            try {
                chart_draw.reset();
                settings.reload();
            }
            catch (std::exception& err) {
                AD_ERROR("{}", err);
                acmacs::run_and_detach({"submarine"}, 0);
            }
        }
    }
    catch (std::exception& err) {
        fmt::print(stderr, "ERROR: {}\n", err);
        exit_code = 2;
    }
    return exit_code;
}

// ----------------------------------------------------------------------

std::pair<std::vector<std::string_view>, std::vector<std::string_view>> parse_files(const argument<str_array>& files)
{
    using namespace std::string_view_literals;
    std::vector<std::string_view> inputs, outputs;
    constexpr std::array input_suffixes{".ace"sv, ".acd1"sv, ".acd1.xz"sv, ".acd1.bz2"sv, ".save"sv, ".save.xz"sv, ".save.bz2"sv};
    const auto is_input = [&input_suffixes](std::string_view name) -> bool {
        return std::any_of(std::begin(input_suffixes), std::end(input_suffixes), [name](std::string_view suff) -> bool { return acmacs::string::endswith(name, suff); });
    };
    for (const auto& file : files) {
        if (is_input(file))
            inputs.push_back(file);
        else
            outputs.push_back(file);
    }
    if (inputs.empty())
        throw std::runtime_error{"no input files (charts) found in the command line"};
    if (inputs.size() > 2)
        throw std::runtime_error{fmt::format("too many input files () found in the command line", inputs)};
    return {inputs, outputs};

} // parse_files

// ----------------------------------------------------------------------

void signal_handler(int sig_num)
{
    fmt::print("SIGNAL {}\n", sig_num);

} // signal_handler

// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>/*
#include <string.h>
#include <iostream>
#include <unordered_set>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Point_3.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include "rply/rply.h"

using namespace std;
using namespace CGAL;

typedef Exact_predicates_inexact_constructions_kernel K;
typedef Point_3<K> Point;
typedef Delaunay_triangulation_3<K> Delaunay;
typedef Delaunay::Vertex_handle Vertex_handle;

// PLC:
map <unsigned, Point> plcVertices; // mapping between vertex coordinates and corresponding unique id

class TriangleFace
{
	public:
		unsigned int vertexIds[3];
}tempFace;

class TetrahedronCell
{
	public:
		unsigned int vertexIds[4];
}tempTet;

list <TriangleFace> plcFaces; // contains ids of vertices/points making the triangle

// Delaunay tetrahedralization:
Delaunay DT;

// CDT:
map <unsigned, Point> cdtVertices;
list <TriangleFace> cdtTets; // contains ids of vertices making tetrahedron

static unsigned tempPoint[3];
unsigned int dimensionId = 0;
static unsigned pointId = 0;

static int vertex_cb(p_ply_argument argument)
{
	long eol;
	ply_get_argument_user_data(argument, NULL, &eol);
	tempPoint[dimensionId++] = ply_get_argument_value(argument);

	// insert the vertex into plcVertex
	if (eol)
	{
		plcVertices[pointId++] = Point(tempPoint[0],tempPoint[1],tempPoint[2]);
		dimensionId = 0;
	}
}

static int face_cb(p_ply_argument argument)
{
	long length, value_index;

	ply_get_argument_property(argument, NULL, &length, &value_index);

        switch (value_index)
	{
        	case 0:
	        case 1:
        		tempFace.vertexIds[pointId++] = ply_get_argument_value(argument);
	                break;
        	case 2:
			tempFace.vertexIds[pointId] = ply_get_argument_value(argument);
			pointId = 0;
			plcFaces.push_front(tempFace);
	                break;
        	default:
                	cout << "Invalid number of points in a face specified :(";
			break;
        }

}

// reads input PLC
void readPLCInput()
{
	// read PLY file(assumed to contain the PLC)
	string fileName;

	cout << "\nPlease enter input filename:\t";
	cin >> fileName;

	p_ply inputPLY = ply_open(fileName.c_str(), NULL, 0, NULL);

	if (!inputPLY) exit(0);

        if (!ply_read_header(inputPLY)) exit(0);

	if (!ply_read(inputPLY))
	{
		cout << "Cannot read the PLY file :(";
		exit(0);
	}

	// Initialize plcVertex and plcFaces
  	ply_set_read_cb(inputPLY, "vertex", "x", vertex_cb, NULL, 0);
	ply_set_read_cb(inputPLY, "vertex", "y", vertex_cb, NULL, 0);
	ply_set_read_cb(inputPLY, "vertex", "z", vertex_cb, NULL, 1);

	ply_set_read_cb(inputPLY, "face", "vertex_indices", face_cb, NULL, 0);

	ply_close(inputPLY);

}

// computes the initial delaunay tetrahedralization
void computeInitialDelaunayTetrahedralization()
{

	list <Point> tempPointList;
	unsigned int i = 0;

	for (map<unsigned, Point>::iterator pit = plcVertices.begin(); pit != plcVertices.end(); pit++, i++)
		tempPointList.push_front(plcVertices.find(i)->second);

    	DT.insert(tempPointList.begin(), tempPointList.end());

	cout << "\nInitial Delaunay tetrahedralization computed!!";
}
*/

/////////////////////////////////////////////// Local Degeneracy Removal ////////////////////////////////////////////////////////////

/*
class DegenerateVertexSetCandidate
{
	Vertex_handle degenSetVertex[5]; // 5 vertices constitute a degeneracy

	bool operator==(const DegenerateVertexSetCandidate &anotherSet)
	{
		bool matched[5];

		for (unsigned int i = 0; i < 5; i++)
		{
			matched[i] = false;

			for (unsigned int n = 0; n < 5; n++)
				{
					if (another.degenSetVertex[i] == degenSetVertex[n])
					{
						matched[i] = true;
						break;
					}
				}
		}

		unsigned int m;
		for (m = 0; m < 5; m++)
		{
			if (matched[m] == true)
				continue;
			else
				break;
		}

		if (m == 5)
			return true; // duplicate(both key and hash value match)
		else
			return false; // unique element(only hash values matches)

	}

};

// implements hash function for unorderd_set used to model degeneracy queue and missing face queue
template <>
struct hash<DegenerateVertexSetCandidate>
{
	size_t operator()(const DegenerateVertexSetCandidate &key) const
	{
		size_t hashValue;
		for (unsigned i = 0; i < 5; i++)
			hashValue ^= hash<Vertex_handle>(key.degenVertSet[i]);

		return (hashValue);
	}
};

*/

// returns true if  given vertices are co-spherical
bool areCospherical(DegenerateVertexSetCandidate degenSet)
{
	Point_3 p[5];

	for (unsigned int i = 0; i < 5; i++)
		p[i] = (degenSet.degenSetVertex[i])->point();

	if (CGAL::side_of_bounded_sphere(p[0],p[1],p[2],p[3],p[4]) == CGAL::ON_BOUNDARY)
		return true;
	else
		return false;
}

// finds all local degeneracies in DT and adds them to a global queue
void addLocalDegeneraciesToQueue(unordered_set<DegenerateVertexSetCandidate> localDegeneracySet)
{

	DegenerateVertexSetCandidate degenerateSet;

	for (delaunay::Finite_cell_iterator cellIter = DT.finite_cells_begin(); cellIter != DT.finite_cells_end(); cellIter++)
	{
		for (unsigned int n = 0; n < 4; n++)
			degenerateSet.degenSetVertex[n] = cellIter->vertex(n);

		for (unsigned int j = 0; j < 4; j++)
			for (unsigned int k = 0; k < 4; k++)
				{
					if ((cellId->neighbor(j))->neighbor(k) == cellId)
						degenerateSet.degenSetVertex[4] = (cellIter->neighbor)->vertex(k);

					if (areCospherical(degenerateSet))
					{
						if (localDegeneracySet.find(degenerateSet) != localDegeneracySet.end())
							localDegeneracySet.insert(degenerateSet);
					}
				}
	}
}

/*
// perturbation should be such that it does not make PLC inconsistent
bool isVertexPerturbable(Vertex)
{
	bool pertubable = false;

	return perturbable;
}

bool isVertexSegmentSafePertubable()
{
	bool segmentSafePerturbable = false;

	return segmentSafePerturbable;
}

bool isDegeneracyRemovable()
{
	bool removable = false;

	if (vertexIsPerturbable(vertex))
		if (vertexIsSegmentSafePerturbable(vertex))
			removable = true;

	return removable;
}
*/

// removes local degeneracies from Delaunay tetrahedralization
void removeLocalDegeneracies()
{
	cout << "\nStarting local degeneracy removal...";

	// compute all local degeneracies in DT and add them to Q
	unordered_set<DegenerateVertexSetCandidate, hashFunction> localDegeneracySet;
	addLocalDegeneraciesToQueue(localDegeneracySet);

	// repeat while Q != NULL
	while (localDegenercySet.size() != 0)
		for (unordered_set<localDegeneracySet, hashFunction>::iterator qIter = localDegeneracySet.begin(); qIter != localDegeneracySet.end(); qIter++)
			{
				if (isDegeneracyRemovable(qIter))
					perturbRemove(qIter, localDegeneracySet);
				else
				{
					Vertex vb;
					computeBreakPoint(vb, qIter, localDegeneracySet);
					if (isEncroachingPLC(vb))
						boundaryProtection(vb);
					else
						inputPLCVertices.push(vb);
				}
			}

	cout << "Local degeneracy removal completed";
}

///////////////////////////////////////////////////// Local Degeneracy Removal //////////////////////////////////////////////////////

/*
// recovers the constraint faces
void recoverConstraintFaces()
{

}

// main procedure
int main()
{
	readPLCInput();
	computeInitialDelaunayTetrahedralization();
	removeLocalDegeneracies();
	recoverConstraintFaces();
        return 0;
}
*/
<commit_msg>working on facet recovery code<commit_after>/*
#include <string.h>
#include <iostream>
#include <unordered_set>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Point_3.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include "rply/rply.h"

using namespace std;
using namespace CGAL;

typedef Exact_predicates_inexact_constructions_kernel K;
typedef Point_3<K> Point;
typedef Delaunay_triangulation_3<K> Delaunay;
typedef Delaunay::Vertex_handle Vertex_handle;

// PLC:
map <unsigned, Point> plcVertices; // mapping between vertex coordinates and corresponding unique id

class TriangleFace
{
	public:
		unsigned int vertexIds[3];
}tempFace;

class TetrahedronCell
{
	public:
		unsigned int vertexIds[4];
}tempTet;

list <TriangleFace> plcFaces; // contains ids of vertices/points making the triangle

// Delaunay tetrahedralization:
Delaunay DT;

// CDT:
map <unsigned, Point> cdtVertices;
list <TriangleFace> cdtTets; // contains ids of vertices making tetrahedron

static unsigned tempPoint[3];
unsigned int dimensionId = 0;
static unsigned pointId = 0;

static int vertex_cb(p_ply_argument argument)
{
	long eol;
	ply_get_argument_user_data(argument, NULL, &eol);
	tempPoint[dimensionId++] = ply_get_argument_value(argument);

	// insert the vertex into plcVertex
	if (eol)
	{
		plcVertices[pointId++] = Point(tempPoint[0],tempPoint[1],tempPoint[2]);
		dimensionId = 0;
	}
}

static int face_cb(p_ply_argument argument)
{
	long length, value_index;

	ply_get_argument_property(argument, NULL, &length, &value_index);

        switch (value_index)
	{
        	case 0:
	        case 1:
        		tempFace.vertexIds[pointId++] = ply_get_argument_value(argument);
	                break;
        	case 2:
			tempFace.vertexIds[pointId] = ply_get_argument_value(argument);
			pointId = 0;
			plcFaces.push_front(tempFace);
	                break;
        	default:
                	cout << "Invalid number of points in a face specified :(";
			break;
        }

}

// reads input PLC
void readPLCInput()
{
	// read PLY file(assumed to contain the PLC)
	string fileName;

	cout << "\nPlease enter input filename:\t";
	cin >> fileName;

	p_ply inputPLY = ply_open(fileName.c_str(), NULL, 0, NULL);

	if (!inputPLY) exit(0);

        if (!ply_read_header(inputPLY)) exit(0);

	if (!ply_read(inputPLY))
	{
		cout << "Cannot read the PLY file :(";
		exit(0);
	}

	// Initialize plcVertex and plcFaces
  	ply_set_read_cb(inputPLY, "vertex", "x", vertex_cb, NULL, 0);
	ply_set_read_cb(inputPLY, "vertex", "y", vertex_cb, NULL, 0);
	ply_set_read_cb(inputPLY, "vertex", "z", vertex_cb, NULL, 1);

	ply_set_read_cb(inputPLY, "face", "vertex_indices", face_cb, NULL, 0);

	ply_close(inputPLY);

}

// computes the initial delaunay tetrahedralization
void computeInitialDelaunayTetrahedralization()
{

	list <Point> tempPointList;
	unsigned int i = 0;

	for (map<unsigned, Point>::iterator pit = plcVertices.begin(); pit != plcVertices.end(); pit++, i++)
		tempPointList.push_front(plcVertices.find(i)->second);

    	DT.insert(tempPointList.begin(), tempPointList.end());

	cout << "\nInitial Delaunay tetrahedralization computed!!";
}
*/

/////////////////////////////////////////////// Local Degeneracy Removal ////////////////////////////////////////////////////////////

/*
class DegenerateVertexSetCandidate
{
	Vertex_handle degenSetVertex[5]; // 5 vertices constitute a degeneracy

	bool operator==(const DegenerateVertexSetCandidate &anotherSet)
	{
		bool matched[5];

		for (unsigned int i = 0; i < 5; i++)
		{
			matched[i] = false;

			for (unsigned int n = 0; n < 5; n++)
				{
					if (another.degenSetVertex[i] == degenSetVertex[n])
					{
						matched[i] = true;
						break;
					}
				}
		}

		unsigned int m;
		for (m = 0; m < 5; m++)
		{
			if (matched[m] == true)
				continue;
			else
				break;
		}

		if (m == 5)
			return true; // duplicate(both key and hash value match)
		else
			return false; // unique element(only hash values matches)

	}

};

// implements hash function for unorderd_set used to model degeneracy queue and missing face queue
template <>
struct hash<DegenerateVertexSetCandidate>
{
	size_t operator()(const DegenerateVertexSetCandidate &key) const
	{
		size_t hashValue;
		for (unsigned i = 0; i < 5; i++)
			hashValue ^= hash<Vertex_handle>(key.degenVertSet[i]);

		return (hashValue);
	}
};

// returns true if  given vertices are co-spherical
bool areCospherical(DegenerateVertexSetCandidate degenSet)
{
	Point_3 p[5];

	for (unsigned int i = 0; i < 5; i++)
		p[i] = (degenSet.degenSetVertex[i])->point();

	if (CGAL::side_of_bounded_sphere(p[0],p[1],p[2],p[3],p[4]) == CGAL::ON_BOUNDARY)
		return true;
	else
		return false;
}

// finds all local degeneracies in DT and adds them to a global queue
void addLocalDegeneraciesToQueue(unordered_set<DegenerateVertexSetCandidate> localDegeneracySet)
{
	DegenerateVertexSetCandidate degenerateSetCandidate;

	for (delaunay::Finite_cell_iterator cellIter = DT.finite_cells_begin(); cellIter != DT.finite_cells_end(); cellIter++)
	{
		for (unsigned int n = 0; n < 4; n++)
			degenerateSetCandidate.degenSetVertex[n] = cellIter->vertex(n);

		for (unsigned int j = 0; j < 4; j++)
			for (unsigned int k = 0; k < 4; k++)
				{
					if ((cellId->neighbor(j))->neighbor(k) == cellId)
						degenerateSetCandidate.degenSetVertex[4] = (cellIter->neighbor)->vertex(k);

					if (areCospherical(degenerateSetCandidate))
					{
						if (localDegeneracySet.find(degenerateSetCandidate) != localDegeneracySet.end())
							localDegeneracySet.insert(degenerateSetCandidate);
					}
				}
	}
}

*/

// perturbation should be such that it does not make PLC inconsistent
bool isVertexPerturbable(Vertex)
{
	bool pertubable = false;

	// a vertex is perturbable iff its perturbation does not make PLC inconsitent
	// use simulation of simplicity approach to artifically perturb the vertex(if it is perturbable) to remove the degeneracy

	return perturbable;
}

bool isVertexSegmentSafePertubable()
{
	bool segmentSafePerturbable = false;

	return segmentSafePerturbable;
}

bool isDegeneracyRemovable()
{
	bool removable = false;

	if (vertexIsPerturbable(vertex))
		if (vertexIsSegmentSafePerturbable(vertex))
			removable = true;

	return removable;
}

// removes local degeneracies from Delaunay tetrahedralization
void removeLocalDegeneracies()
{
	cout << "\nStarting local degeneracy removal...";

	// compute all local degeneracies in DT and add them to Q
	unordered_set<DegenerateVertexSetCandidate, hashFunction> localDegeneracySet;
	addLocalDegeneraciesToQueue(localDegeneracySet);

	// repeat while Q != NULL
	while (localDegenercySet.size() != 0)
		for (unordered_set<localDegeneracySet, hashFunction>::iterator qIter = localDegeneracySet.begin(); qIter != localDegeneracySet.end(); qIter++)
			{
				if (isDegeneracyRemovable(qIter))
					perturbRemove(qIter, localDegeneracySet);
				else
				{
					Vertex vb;
					computeBreakPoint(vb, qIter, localDegeneracySet);
					if (isEncroachingPLC(vb))
						boundaryProtection(vb);
					else
						inputPLCVertices.push(vb);
				}
			}

	cout << "Local degeneracy removal completed";
}

/////////////////////////////////////////////////////Local Degeneracy Removal Ends//////////////////////////////////////////////////////

// IMPLEMENT UNORDERED_SET OF missingSubfacesQueue ????

void formMissingSubfaceQueue(unordered_set<> missingSubfacesQueue)
{

}

void formCavity(lcc c1, lcc c2, lcc aMissingSubface)
{

}

// recovers the constraint faces
void recoverConstraintFaces()
{

	// input X2, D2
	// output CDT of X2

	// form a queue of missing subfaces
	// while (Q!=0)
	// 	remove an uncovered subface f from Q
	// 	for 2 cavities C1, C2 by formcavity procedure
	// 	for each cavity Ci:
	// 	call cavity retetrahedralization subroutine

        unordered_set<face, key> missingSubfacesQueue;
	formMissingSubfaceQueue(missingSubfacesQueue);
	lcc aMissingSubface;
	lcc C[2]; // seems like we need to implement it using LCC

	while (missingSubfacesQueue.size() != 0)
	{
		 aMissingSubface = missingSubfacesQueue.pop();

		 formCavity(C[0], C[1], aMissingSubface);

		 for (unsigned int cavityId = 0; cavityId < 2; cavityId++)
		 	cavityReterahedralization(C[cavityId], cdtMesh); // 'cdtMesh' points to the final output mesh

	}

	return;
}

// main procedure
int main()
{
	readPLCInput();
	computeInitialDelaunayTetrahedralization();
	removeLocalDegeneracies();
	recoverConstraintFaces();
        return 0;
}

<|endoftext|>