<commit_before>/*----------------------------------------------------------------------------/
/ JPEG and PNG Image Viewer for the Casio Prizm
/ Copyright 2014 tny. internet media
/ http://i.tny.im  /  admin@tny.im
/-----------------------------------------------------------------------------/
/ Copyright (c) 2014, Gabriel Maia and the tny. internet media group
/ All rights reserved.
/
/ Redistribution and use in source and binary forms, with or without
/ modification, are permitted provided that the following conditions are met:
/
/ 1. Redistributions of source code must retain the above copyright notice,
/    this list of conditions and the following disclaimer.
/
/ 2. Redistributions in binary form must reproduce the above copyright notice,
/    this list of conditions and the following disclaimer in the documentation
/    and/or other materials provided with the distribution.
/
/ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
/ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
/ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
/ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
/ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
/ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
/ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
/ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
/ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
/ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
/ POSSIBILITY OF SUCH DAMAGE.
/
/----------------------------------------------------------------------------*/
#include <fxcg/display.h>
#include <fxcg/file.h>
#include <fxcg/keyboard.h>
#include <fxcg/system.h>
#include <fxcg/misc.h>
#include <fxcg/app.h>
#include <fxcg/serial.h>
#include <fxcg/rtc.h>
#include <fxcg/heap.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "menuGUI.hpp"
#include "fileProvider.hpp"
#include "fileGUI.hpp"
#include "graphicsProvider.hpp"
#include "aboutGUI.hpp"

int fileBrowser(char* filename, unsigned char* filter, unsigned char* filter2, char* title) {
  // returns 1 when user selects a file, 0 when aborts (exit)
  int res = 1;
  char browserbasepath[MAX_FILENAME_SIZE+1] = "\\\\fls0\\";
  while(res) {
    strcpy(filename, (char*)"");
    res = fileBrowserSub(browserbasepath, filename, filter, filter2, title);
    if(res==2) return 1; // user selected a file
  }
  return 0;
}

int fileBrowserSub(char* browserbasepath, char* filename, unsigned char* filter, unsigned char* filter2, char* title) {
  Menu menu;
  MenuItemIcon icontable[12];
  buildIconTable(icontable);

  // first get file count so we know how much to alloc
  GetFiles(NULL, NULL, browserbasepath, &menu.numitems, filter, filter2);
  MenuItem* menuitems = NULL;
  File* files = NULL;
  if(menu.numitems > 0) {
    menuitems = (MenuItem*)alloca(menu.numitems*sizeof(MenuItem));
    files = (File*)alloca(menu.numitems*sizeof(File));
    // populate arrays
    GetFiles(files, menuitems, browserbasepath, &menu.numitems, filter, filter2);
    menu.items = menuitems;
  }

  char titleBuffer[120];
  char titleBufferBuf[120];
  int smemfree;
  unsigned short smemMedia[10]={'\\','\\','f','l','s','0',0};
  Bfile_GetMediaFree_OS( smemMedia, &smemfree );

  char friendlypath[MAX_FILENAME_SIZE];
  strcpy(friendlypath, browserbasepath+6);
  friendlypath[strlen(friendlypath)-1] = '\0'; //remove ending slash like OS does
  // test to see if friendlypath is too big
  int jump4=0;
  while(1) {
    int temptextX=7*18+10; // px length of menu title + 10, like menuGUI goes.
    int temptextY=0;
    PrintMini(&temptextX, &temptextY, (unsigned char*)friendlypath, 0, 0xFFFFFFFF, 0, 0, COLOR_BLACK, COLOR_WHITE, 0, 0); // fake draw
    if(temptextX>LCD_WIDTH_PX-6) {
      char newfriendlypath[MAX_FILENAME_SIZE];
      shortenDisplayPath(friendlypath, newfriendlypath, (jump4 ? 4 : 1));
      if(strlen(friendlypath) > strlen(newfriendlypath) && strlen(newfriendlypath) > 3) { // check if len > 3 because shortenDisplayPath may return just "..." when the folder name is too big
        // shortenDisplayPath still managed to shorten, copy and continue
        jump4 = 1; //it has been shortened already, so next time jump the first four characters
        strcpy(friendlypath, newfriendlypath);
      } else {
        // shortenDisplayPath can't shorten any more even if it still
        // doesn't fit in the screen, so give up.
        break;
      }
    } else break;
  }
  menu.subtitle = friendlypath;
  menu.type = MENUTYPE_MULTISELECT;
  menu.scrollout=1;
  menu.nodatamsg = (char*)"No Data";
  menu.title = title;
  menu.height=7;
  while(1) {
    Bdisp_AllClr_VRAM();
    drawFkeyLabels((menu.numitems ? 0x03B1 : 0), -1, -1, -1, -1, 0x03DF); // OPEN, VERSION
    itoa(smemfree, (unsigned char*)titleBuffer);
    LocalizeMessage1( 340, titleBufferBuf ); //"bytes free"
    strncat((char*)titleBuffer, (char*)titleBufferBuf, 65);
    menu.statusText = (char*)titleBuffer;
    int res = doMenu(&menu, icontable);
    switch(res) {
      case MENU_RETURN_EXIT:
        if(!strcmp(browserbasepath,"\\\\fls0\\")) { //check that we aren't already in the root folder
          //we are, return 0 so we exit
          return 0;
        } else {
          int i=strlen(browserbasepath)-2;
          while (i>=0 && browserbasepath[i] != '\\')
                  i--;
          if (browserbasepath[i] == '\\') {
            char tmp[MAX_FILENAME_SIZE] = "";
            memcpy(tmp,browserbasepath,i+1);
            tmp[i+1] = '\0';
            strcpy(browserbasepath, tmp);
          }
          return 1; //reload at new folder
        }
        break;
      case KEY_CTRL_F1:
      case MENU_RETURN_SELECTION:
        if(!menu.numitems) break;
        if(menuitems[menu.selection-1].isfolder) {
          strcpy(browserbasepath, files[menu.selection-1].filename); //switch to selected folder
          strcat(browserbasepath, "\\");
          return 1; //reload at new folder
        } else {
          strcpy(filename,files[menu.selection-1].filename);
          return 2;
        }
        break;
      case KEY_CTRL_F6:
        showAbout();
        break;
    }
  }
  return 1;
}

void shortenDisplayPath(char* longpath, char* shortpath, int jump) {
  //this function takes a long path for display, like \myfolder\long\display\path
  //and shortens it one level, like this: ...\long\display\path
  //putting the result in shortpath
  strcpy(shortpath, (char*)"...");
  int i = jump; // jump the specified amount of characters... by default it jumps the first /
  // but it can also be made to jump e.g. 4 characters, which would jump ".../" (useful for when the text has been through this function already)
  int max = strlen(longpath);
  while (i<max && longpath[i] != '\\')
          i++;
  if (longpath[i] == '\\') {
    strcat(shortpath, longpath+i);
  }
}

void buildIconTable(MenuItemIcon* icontable) {
  unsigned int msgno;
  unsigned short folder[7]={'\\','\\','f','l','s','0',0};

  const char *bogusFiles[] = {"t", // for folder
                              "t.g3m",
                              "t.g3e",
                              "t.g3a",
                              "t.g3p",
                              "t.g3b",
                              "t.bmp",
                              "t.txt",
                              "t.csv",
                              "t.abc" //for "unsupported" files
                             };

  for(int i = 0; i < 10; i++)
    SMEM_MapIconToExt( (unsigned char*)bogusFiles[i], (i==0 ? folder : (unsigned short*)"\x000\x000"), &msgno, icontable[i].data );
}<commit_msg>fileGUI: this file browser should not return when the user presses EXIT at the top level.<commit_after>/*----------------------------------------------------------------------------/
/ JPEG and PNG Image Viewer for the Casio Prizm
/ Copyright 2014 tny. internet media
/ http://i.tny.im  /  admin@tny.im
/-----------------------------------------------------------------------------/
/ Copyright (c) 2014, Gabriel Maia and the tny. internet media group
/ All rights reserved.
/
/ Redistribution and use in source and binary forms, with or without
/ modification, are permitted provided that the following conditions are met:
/
/ 1. Redistributions of source code must retain the above copyright notice,
/    this list of conditions and the following disclaimer.
/
/ 2. Redistributions in binary form must reproduce the above copyright notice,
/    this list of conditions and the following disclaimer in the documentation
/    and/or other materials provided with the distribution.
/
/ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
/ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
/ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
/ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
/ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
/ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
/ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
/ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
/ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
/ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
/ POSSIBILITY OF SUCH DAMAGE.
/
/----------------------------------------------------------------------------*/
#include <fxcg/display.h>
#include <fxcg/file.h>
#include <fxcg/keyboard.h>
#include <fxcg/system.h>
#include <fxcg/misc.h>
#include <fxcg/app.h>
#include <fxcg/serial.h>
#include <fxcg/rtc.h>
#include <fxcg/heap.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "menuGUI.hpp"
#include "fileProvider.hpp"
#include "fileGUI.hpp"
#include "graphicsProvider.hpp"
#include "aboutGUI.hpp"

int fileBrowser(char* filename, unsigned char* filter, unsigned char* filter2, char* title) {
  // returns 1 when user selects a file, 0 when aborts (exit)
  int res = 1;
  char browserbasepath[MAX_FILENAME_SIZE+1] = "\\\\fls0\\";
  while(res) {
    strcpy(filename, (char*)"");
    res = fileBrowserSub(browserbasepath, filename, filter, filter2, title);
    if(res==2) return 1; // user selected a file
  }
  return 0;
}

int fileBrowserSub(char* browserbasepath, char* filename, unsigned char* filter, unsigned char* filter2, char* title) {
  Menu menu;
  MenuItemIcon icontable[12];
  buildIconTable(icontable);

  // first get file count so we know how much to alloc
  GetFiles(NULL, NULL, browserbasepath, &menu.numitems, filter, filter2);
  MenuItem* menuitems = NULL;
  File* files = NULL;
  if(menu.numitems > 0) {
    menuitems = (MenuItem*)alloca(menu.numitems*sizeof(MenuItem));
    files = (File*)alloca(menu.numitems*sizeof(File));
    // populate arrays
    GetFiles(files, menuitems, browserbasepath, &menu.numitems, filter, filter2);
    menu.items = menuitems;
  }

  char titleBuffer[120];
  char titleBufferBuf[120];
  int smemfree;
  unsigned short smemMedia[10]={'\\','\\','f','l','s','0',0};
  Bfile_GetMediaFree_OS( smemMedia, &smemfree );

  char friendlypath[MAX_FILENAME_SIZE];
  strcpy(friendlypath, browserbasepath+6);
  friendlypath[strlen(friendlypath)-1] = '\0'; //remove ending slash like OS does
  // test to see if friendlypath is too big
  int jump4=0;
  while(1) {
    int temptextX=7*18+10; // px length of menu title + 10, like menuGUI goes.
    int temptextY=0;
    PrintMini(&temptextX, &temptextY, (unsigned char*)friendlypath, 0, 0xFFFFFFFF, 0, 0, COLOR_BLACK, COLOR_WHITE, 0, 0); // fake draw
    if(temptextX>LCD_WIDTH_PX-6) {
      char newfriendlypath[MAX_FILENAME_SIZE];
      shortenDisplayPath(friendlypath, newfriendlypath, (jump4 ? 4 : 1));
      if(strlen(friendlypath) > strlen(newfriendlypath) && strlen(newfriendlypath) > 3) { // check if len > 3 because shortenDisplayPath may return just "..." when the folder name is too big
        // shortenDisplayPath still managed to shorten, copy and continue
        jump4 = 1; //it has been shortened already, so next time jump the first four characters
        strcpy(friendlypath, newfriendlypath);
      } else {
        // shortenDisplayPath can't shorten any more even if it still
        // doesn't fit in the screen, so give up.
        break;
      }
    } else break;
  }
  menu.subtitle = friendlypath;
  menu.type = MENUTYPE_MULTISELECT;
  menu.scrollout=1;
  menu.nodatamsg = (char*)"No Data";
  menu.title = title;
  menu.height=7;
  while(1) {
    Bdisp_AllClr_VRAM();
    drawFkeyLabels((menu.numitems ? 0x03B1 : 0), -1, -1, -1, -1, 0x03DF); // OPEN, VERSION
    itoa(smemfree, (unsigned char*)titleBuffer);
    LocalizeMessage1( 340, titleBufferBuf ); //"bytes free"
    strncat((char*)titleBuffer, (char*)titleBufferBuf, 65);
    menu.statusText = (char*)titleBuffer;
    int res = doMenu(&menu, icontable);
    switch(res) {
      case MENU_RETURN_EXIT:
        if(!strcmp(browserbasepath,"\\\\fls0\\")) { //check that we aren't already in the root folder
          //we are, return 0 so we exit
          //return 0; // in this add-in, the file browser shouldn't return
        } else {
          int i=strlen(browserbasepath)-2;
          while (i>=0 && browserbasepath[i] != '\\')
                  i--;
          if (browserbasepath[i] == '\\') {
            char tmp[MAX_FILENAME_SIZE] = "";
            memcpy(tmp,browserbasepath,i+1);
            tmp[i+1] = '\0';
            strcpy(browserbasepath, tmp);
          }
          return 1; //reload at new folder
        }
        break;
      case KEY_CTRL_F1:
      case MENU_RETURN_SELECTION:
        if(!menu.numitems) break;
        if(menuitems[menu.selection-1].isfolder) {
          strcpy(browserbasepath, files[menu.selection-1].filename); //switch to selected folder
          strcat(browserbasepath, "\\");
          return 1; //reload at new folder
        } else {
          strcpy(filename,files[menu.selection-1].filename);
          return 2;
        }
        break;
      case KEY_CTRL_F6:
        showAbout();
        break;
    }
  }
  return 1;
}

void shortenDisplayPath(char* longpath, char* shortpath, int jump) {
  //this function takes a long path for display, like \myfolder\long\display\path
  //and shortens it one level, like this: ...\long\display\path
  //putting the result in shortpath
  strcpy(shortpath, (char*)"...");
  int i = jump; // jump the specified amount of characters... by default it jumps the first /
  // but it can also be made to jump e.g. 4 characters, which would jump ".../" (useful for when the text has been through this function already)
  int max = strlen(longpath);
  while (i<max && longpath[i] != '\\')
          i++;
  if (longpath[i] == '\\') {
    strcat(shortpath, longpath+i);
  }
}

void buildIconTable(MenuItemIcon* icontable) {
  unsigned int msgno;
  unsigned short folder[7]={'\\','\\','f','l','s','0',0};

  const char *bogusFiles[] = {"t", // for folder
                              "t.g3m",
                              "t.g3e",
                              "t.g3a",
                              "t.g3p",
                              "t.g3b",
                              "t.bmp",
                              "t.txt",
                              "t.csv",
                              "t.abc" //for "unsupported" files
                             };

  for(int i = 0; i < 10; i++)
    SMEM_MapIconToExt( (unsigned char*)bogusFiles[i], (i==0 ? folder : (unsigned short*)"\x000\x000"), &msgno, icontable[i].data );
}<|endoftext|>


<commit_before>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// CoreSolver plugin
//==============================================================================

#include "corenlasolver.h"
#include "coresolverplugin.h"

//==============================================================================

namespace OpenCOR {
namespace CoreSolver {

//==============================================================================

PLUGININFO_FUNC CoreSolverPluginInfo()
{
    Descriptions descriptions;

    descriptions.insert("en", QString::fromUtf8("the core solver plugin."));
    descriptions.insert("fr", QString::fromUtf8("l'extension de solveur de base."));

    return new PluginInfo(PluginInfo::Solver, false, false,
                          QStringList(),
                          descriptions);
}

//==============================================================================

}   // namespace CoreSolver
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Some minor cleaning up [ci skip].<commit_after>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// CoreSolver plugin
//==============================================================================

#include "coresolverplugin.h"

//==============================================================================

namespace OpenCOR {
namespace CoreSolver {

//==============================================================================

PLUGININFO_FUNC CoreSolverPluginInfo()
{
    Descriptions descriptions;

    descriptions.insert("en", QString::fromUtf8("the core solver plugin."));
    descriptions.insert("fr", QString::fromUtf8("l'extension de solveur de base."));

    return new PluginInfo(PluginInfo::Solver, false, false,
                          QStringList(),
                          descriptions);
}

//==============================================================================

}   // namespace CoreSolver
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>
#include <geometry_msgs/Point.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/video.hpp>
#include <pthread.h>
#include <sensor_msgs/image_encodings.h>

static const std::string RGB_WINDOW_NAME = "RGB image";
static const std::string DEPTH_WINDOW_NAME = "Depth image";

class SearchTomato {
public:
  SearchTomato(): found_flag(false), tick_count(0), last_tick_count(0), not_founding_count(0), kf(6, 4, 0, CV_32F), state(6, 1, CV_32F)
  {
  init_set_kalman(kf);
  }

  ~SearchTomato() {}

bool searchTomatoPoint(const cv::Mat& capture_rgb, cv::Point& tomato_point) {
  cv::Mat binary_mat = cv::Mat::zeros(capture_rgb.size(), CV_8UC1);
  std::vector<cv::Rect> tomato_boxs;
  // reset transition matrix of kalman filter.
  tick_count = cv::getTickCount();
  double d_time = (tick_count - last_tick_count) / cv::getTickFrequency(); // d_time: infinitesimal difference of time.
  kf.transitionMatrix.at<float>(2) = d_time; // x = x + dt*vx
  kf.transitionMatrix.at<float>(9) = d_time; // y = y + dt*vy
  last_tick_count = tick_count;

  // image processing and seach box for tomato
  imageProcessing(capture_rgb, binary_mat);
  imshow(DEPTH_WINDOW_NAME, binary_mat);
  searchTomatoBox(binary_mat, tomato_boxs);
  printf("search box num is %lu", tomato_boxs.size());

  if (tomato_boxs.size() > 0) {
    not_founding_count = 0;
    if (!found_flag) {
      resetKalman();
      found_flag = true;
    } else applyKalman(tomato_boxs);
  } else if (++not_founding_count > 10) {
    not_founding_count = 10;
    found_flag = false;
  }

  // set point of tomato
  state = kf.predict();
  tomato_point.x = state.at<float>(0);
  tomato_point.y = state.at<float>(1);

  return found_flag;
}

private:
  void init_set_kalman(cv::KalmanFilter& kf) {
    // transition state matrix(A). think for [x, y, vx, vy, width, height]. identiry mean state is static.
    cv::setIdentity(kf.transitionMatrix); // NOTE: set state of speed at each step!

    // measurement matrix(H). mean look to only [x, y, width, height].
    kf.measurementMatrix = cv::Mat::zeros(4, 6, CV_32F);
    kf.measurementMatrix.at<float>(0) = 1.0f;
    kf.measurementMatrix.at<float>(7) = 1.0f;
    kf.measurementMatrix.at<float>(16) = 1.0f;
    kf.measurementMatrix.at<float>(23) = 1.0f;

    // process noise covariance matrix(Q). all state have noise is 5e-2;
    cv::setIdentity(kf.processNoiseCov, cv::Scalar(5e-2));
  }

  void imageProcessing(const cv::Mat& rgb, cv::Mat& binary_mat) {
    cv::Mat blur, hsv;
    cv::GaussianBlur(rgb, blur, cv::Size(5, 5), 3.0, 3.0);
    cv::cvtColor(blur, hsv, CV_BGR2HSV);
    redFilter(hsv, binary_mat);
    cv::erode(binary_mat, binary_mat, cv::Mat(), cv::Point(-1, -1), 3);
    cv::dilate(binary_mat, binary_mat, cv::Mat(), cv::Point(-1, -1), 5);
    cv::erode(binary_mat, binary_mat, cv::Mat(), cv::Point(-1, -1), 3);
  }

  void redFilter(cv::Mat& hsv, cv::Mat& binary_mat) {
    int a, x, y;
    for(y = 0; y < hsv.rows; y++) {
      for(x = 0; x < hsv.cols; x++) {
        a = hsv.step*y+(x*3);
        if((hsv.data[a] <= 5 || hsv.data[a] >= 175) && hsv.data[a+1] >= 50 && hsv.data[a+2] >= 50 )
          binary_mat.at<unsigned char>(y,x) = 255;
        else
          binary_mat.at<unsigned char>(y,x) = 0;
      }
    }
  }

  void searchTomatoBox(cv::Mat& binary_mat, std::vector<cv::Rect>& tomato_boxs) {
    std::vector<std::vector<cv::Point> > contours;
    cv::findContours(binary_mat, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
    findMatchTomato(contours, tomato_boxs);
  }

  void findMatchTomato(std::vector<std::vector<cv::Point> >& contours, std::vector<cv::Rect>& tomato_boxs) {
    cv::Rect bounding_box;
    for (unsigned int i = 0; i < contours.size(); i++) {
      bounding_box = cv::boundingRect(contours[i]);

      float ratio_balance = (float)bounding_box.width / (float)bounding_box.height;
      if (ratio_balance > 1.0f) ratio_balance = 1.0f / ratio_balance;

      // delete mismach. that is smaller or spier
      if (bounding_box.area() >= 500 && bounding_box.area() < 2*cv::contourArea(contours[i]) && ratio_balance > 0.4f) tomato_boxs.push_back(bounding_box);
    }
  }

  void applyKalman(std::vector<cv::Rect>& tomato_boxs) {
    cv::Mat meas(4, 1, CV_32F);
    int i = findIndexPlausible(tomato_boxs);

    meas.at<float>(0) = tomato_boxs[i].x + tomato_boxs[i].width/2;
    meas.at<float>(1) = tomato_boxs[i].y + tomato_boxs[i].height/2;
    meas.at<float>(2) = tomato_boxs[i].width;
    meas.at<float>(3) = tomato_boxs[i].height;
    kf.correct(meas);
  }

  int findIndexPlausible(std::vector<cv::Rect>& tomato_boxs) {
    int index = 0;
    unsigned int worst_non_likelihood = 0xffff;
    unsigned int non_likelihood;
    for (int i = 0; i < tomato_boxs.size(); i++) {
      state = kf.predict();
      non_likelihood = 0;
      non_likelihood += abs(tomato_boxs[i].x - state.at<float>(0));
      non_likelihood += abs(tomato_boxs[i].y - state.at<float>(1));
      non_likelihood += abs(tomato_boxs[i].width - state.at<float>(4));
      non_likelihood += abs(tomato_boxs[i].height - state.at<float>(5));
      if (worst_non_likelihood > non_likelihood) {
        worst_non_likelihood = non_likelihood;
        index = i;
      }
    }
    return index;
  }

  void resetKalman() {
    //     kf.errorCovPre.at<float>(0) = 1; // px
    //     kf.errorCovPre.at<float>(7) = 1; // px
    //     kf.errorCovPre.at<float>(14) = 1;
    //     kf.errorCovPre.at<float>(21) = 1;
    //     kf.errorCovPre.at<float>(28) = 1; // px
    //     kf.errorCovPre.at<float>(35) = 1; // px

    for (int i=0; i<6; i++) kf.errorCovPre.at<float>(i*7) = 1;
  }

  bool found_flag;
  long tick_count, last_tick_count; // count while running.
  unsigned int not_founding_count;
  cv::KalmanFilter kf;
  cv::Mat state;
};

class ImageConverter
{
private:
  ros::NodeHandle nh;
  ros::Publisher point_pub;
  image_transport::ImageTransport it;
  image_transport::Subscriber rgb_sub;
  image_transport::Subscriber depth_sub;
  cv_bridge::CvImageConstPtr rgb_ptr;
  cv_bridge::CvImageConstPtr depth_ptr;
  SearchTomato searchTomatoObj;
  cv::Point tomato_point;
  bool found_flag;
  geometry_msgs::Point pub_msg;

public:
  ImageConverter() :
  it(nh), searchTomatoObj(), tomato_point(-1, -1), found_flag(false)
  {
    // subscrive to input video feed.
    point_pub = nh.advertise<geometry_msgs::Point>("tomato_point", 1);
    rgb_sub = it.subscribe("/camera/rgb/image_raw", 1, &ImageConverter::rgbCb, this);
    depth_sub = it.subscribe("/camera/depth_registered/image_raw", 1, &ImageConverter::depthCb, this);
  }

  ~ImageConverter() {}

  /**
   * search tomato function.
   * get rgb image, and search tomato.
   * will tomato_point have data.
   *
   * @author Yusuke Doi
   */
  void rgbCb(const sensor_msgs::ImageConstPtr& msg) {
    // get rgb image on kinect
    try {
      rgb_ptr = cv_bridge::toCvShare(msg, "bgr8");
    } catch (cv_bridge::Exception& e) {
      ROS_ERROR("cv_bridge exception by rgb: %s", e.what());
    }

    cv::Mat buf = rgb_ptr->image.clone();
    // search tomato
    if (found_flag = searchTomatoObj.searchTomatoPoint(rgb_ptr->image, tomato_point))
      cv::circle(buf, tomato_point, 40, CV_RGB(0, 255, 0), 3);

    cv::imshow(RGB_WINDOW_NAME, buf);
    cv::waitKey(100);
  }

  void depthCb(const sensor_msgs::ImageConstPtr& msg) {
    try {
      depth_ptr = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::TYPE_16UC1);
    } catch (cv_bridge::Exception& e) {
      ROS_ERROR("cv_bridge exception by depth: %s", e.what());
    }

    //testShowDepth(depth_ptr->image);
    if (found_flag) {
      printf("depth is %u", depth_ptr->image.at<uint16_t>(tomato_point));
      //      pub_msg.x = tomato_point.x;
      //      pub_msg.y = tomato_point.y;
      //      pub_msg.z = depth_ptr->image.at<uint16_t>(tomato_point);
      pub_msg.x = depth_ptr->image.at<uint16_t>(tomato_point);
      pub_msg.y = tomato_point.x;
      pub_msg.z = -tomato_point.y;
      point_pub.publish(pub_msg);
    }
    cv::waitKey(100);
  }

  void testShowDepth(const cv::Mat& depth) {
    static std::vector<uint16_t> m_gamma(2048);
    static bool firstFlag(false);
    if (!firstFlag) {
      for(unsigned int i = 0; i < 2048; i++) {
        float v = i/2048.0;
        v = std::pow(v, 3) * 6;
        m_gamma[i] = v*6*256;
      }
      firstFlag = true;
    }

    int i;
    cv::Mat buf = cv::Mat::zeros(depth.size(), CV_16UC1);
    cv::Mat output = cv::Mat::zeros(depth.size(), CV_8UC3);
    cv::normalize(depth, buf, 0, 2013, cv::NORM_MINMAX);

    for (i = 0; i < depth.rows * depth.cols; i++) {
      int y = (int)(i / depth.cols);
      int x = (int)(i % depth.cols);
      int pval = m_gamma[buf.at<uint16_t>(i)];
      int lb = pval & 0xff;

      switch(pval >> 8) {
        case 0:
          output.at<cv::Vec3b>(y,x)[2] = 255;
          output.at<cv::Vec3b>(y,x)[1] = 255-lb;
          output.at<cv::Vec3b>(y,x)[0] = 255-lb;
          break;
        case 1:
          output.at<cv::Vec3b>(y,x)[2] = 255;
          output.at<cv::Vec3b>(y,x)[1] = lb;
          output.at<cv::Vec3b>(y,x)[0] = 0;
          break;
        case 2:
          output.at<cv::Vec3b>(y,x)[2] = 255-lb;
          output.at<cv::Vec3b>(y,x)[1] = 255;
          output.at<cv::Vec3b>(y,x)[0] = 0;
          break;
        case 3:
          output.at<cv::Vec3b>(y,x)[2] = 0;
          output.at<cv::Vec3b>(y,x)[1] = 255;
          output.at<cv::Vec3b>(y,x)[0] = lb;
          break;
        case 4:
          output.at<cv::Vec3b>(y,x)[2] = 0;
          output.at<cv::Vec3b>(y,x)[1] = 255-lb;
          output.at<cv::Vec3b>(y,x)[0] = 255;
          break;
        case 5:
          output.at<cv::Vec3b>(y,x)[2] = 0;
          output.at<cv::Vec3b>(y,x)[1] = 0;
          output.at<cv::Vec3b>(y,x)[0] = 255-lb;
          break;
        default:
          output.at<cv::Vec3b>(y,x)[2] = 0;
          output.at<cv::Vec3b>(y,x)[1] = 0;
          output.at<cv::Vec3b>(y,x)[0] = 0;
          break;
      }
    }
      imshow(DEPTH_WINDOW_NAME, output);
  }
};

int main(int argc, char** argv) {
  ros::init(argc, argv, "kinect_tomato_search_node");
  ImageConverter ic;
  cv::namedWindow(RGB_WINDOW_NAME);
  cv::namedWindow(DEPTH_WINDOW_NAME);
  ros::spin();
  cv::destroyWindow(RGB_WINDOW_NAME);
  cv::destroyWindow(DEPTH_WINDOW_NAME);
  return 0;
}
<commit_msg>Marked MATLAB-OpenCV function<commit_after>#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>
#include <geometry_msgs/Point.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/video.hpp>
#include <pthread.h>
#include <sensor_msgs/image_encodings.h>

static const std::string RGB_WINDOW_NAME = "RGB image";
static const std::string DEPTH_WINDOW_NAME = "Depth image";

class SearchTomato {
public:
  SearchTomato(): found_flag(false), tick_count(0), last_tick_count(0), not_founding_count(0), kf(6, 4, 0, CV_32F), state(6, 1, CV_32F)
  {
  init_set_kalman(kf);
  }

  ~SearchTomato() {}

bool searchTomatoPoint(const cv::Mat& capture_rgb, cv::Point& tomato_point) {//MATLAB-OpenCV_function...[ref]:https://kyamagu.github.io/mexopencv/matlab/KalmanFilter.html
  cv::Mat binary_mat = cv::Mat::zeros(capture_rgb.size(), CV_8UC1);
  std::vector<cv::Rect> tomato_boxs;
  // reset transition matrix of kalman filter.
  tick_count = cv::getTickCount();
  double d_time = (tick_count - last_tick_count) / cv::getTickFrequency(); // d_time: infinitesimal difference of time.
  kf.transitionMatrix.at<float>(2) = d_time; // x = x + dt*vx
  kf.transitionMatrix.at<float>(9) = d_time; // y = y + dt*vy
  last_tick_count = tick_count;

  // image processing and seach box for tomato
  imageProcessing(capture_rgb, binary_mat);
  imshow(DEPTH_WINDOW_NAME, binary_mat);
  searchTomatoBox(binary_mat, tomato_boxs);
  printf("search box num is %lu", tomato_boxs.size());

  if (tomato_boxs.size() > 0) {
    not_founding_count = 0;
    if (!found_flag) {
      resetKalman();
      found_flag = true;
    } else applyKalman(tomato_boxs);
  } else if (++not_founding_count > 10) {
    not_founding_count = 10;
    found_flag = false;
  }

  // set point of tomato
  state = kf.predict();
  tomato_point.x = state.at<float>(0);
  tomato_point.y = state.at<float>(1);

  return found_flag;
}

private:
  void init_set_kalman(cv::KalmanFilter& kf) {
    // transition state matrix(A). think for [x, y, vx, vy, width, height]. identiry mean state is static.
    cv::setIdentity(kf.transitionMatrix); // NOTE: set state of speed at each step!

    // measurement matrix(H). mean look to only [x, y, width, height].
    kf.measurementMatrix = cv::Mat::zeros(4, 6, CV_32F);
    kf.measurementMatrix.at<float>(0) = 1.0f;
    kf.measurementMatrix.at<float>(7) = 1.0f;
    kf.measurementMatrix.at<float>(16) = 1.0f;
    kf.measurementMatrix.at<float>(23) = 1.0f;

    // process noise covariance matrix(Q). all state have noise is 5e-2;
    cv::setIdentity(kf.processNoiseCov, cv::Scalar(5e-2));
  }

  void imageProcessing(const cv::Mat& rgb, cv::Mat& binary_mat) {
    cv::Mat blur, hsv;
    cv::GaussianBlur(rgb, blur, cv::Size(5, 5), 3.0, 3.0);
    cv::cvtColor(blur, hsv, CV_BGR2HSV);
    redFilter(hsv, binary_mat);
    cv::erode(binary_mat, binary_mat, cv::Mat(), cv::Point(-1, -1), 3);
    cv::dilate(binary_mat, binary_mat, cv::Mat(), cv::Point(-1, -1), 5);
    cv::erode(binary_mat, binary_mat, cv::Mat(), cv::Point(-1, -1), 3);
  }

  void redFilter(cv::Mat& hsv, cv::Mat& binary_mat) {
    int a, x, y;
    for(y = 0; y < hsv.rows; y++) {
      for(x = 0; x < hsv.cols; x++) {
        a = hsv.step*y+(x*3);
        if((hsv.data[a] <= 5 || hsv.data[a] >= 175) && hsv.data[a+1] >= 50 && hsv.data[a+2] >= 50 )
          binary_mat.at<unsigned char>(y,x) = 255;
        else
          binary_mat.at<unsigned char>(y,x) = 0;
      }
    }
  }

  void searchTomatoBox(cv::Mat& binary_mat, std::vector<cv::Rect>& tomato_boxs) {
    std::vector<std::vector<cv::Point> > contours;
    cv::findContours(binary_mat, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
    findMatchTomato(contours, tomato_boxs);
  }

  void findMatchTomato(std::vector<std::vector<cv::Point> >& contours, std::vector<cv::Rect>& tomato_boxs) {
    cv::Rect bounding_box;
    for (unsigned int i = 0; i < contours.size(); i++) {
      bounding_box = cv::boundingRect(contours[i]);

      float ratio_balance = (float)bounding_box.width / (float)bounding_box.height;
      if (ratio_balance > 1.0f) ratio_balance = 1.0f / ratio_balance;

      // delete mismach. that is smaller or spier
      if (bounding_box.area() >= 500 && bounding_box.area() < 2*cv::contourArea(contours[i]) && ratio_balance > 0.4f) tomato_boxs.push_back(bounding_box);
    }
  }

  void applyKalman(std::vector<cv::Rect>& tomato_boxs) {
    cv::Mat meas(4, 1, CV_32F);
    int i = findIndexPlausible(tomato_boxs);

    meas.at<float>(0) = tomato_boxs[i].x + tomato_boxs[i].width/2;
    meas.at<float>(1) = tomato_boxs[i].y + tomato_boxs[i].height/2;
    meas.at<float>(2) = tomato_boxs[i].width;
    meas.at<float>(3) = tomato_boxs[i].height;
    kf.correct(meas);
  }

  int findIndexPlausible(std::vector<cv::Rect>& tomato_boxs) {
    int index = 0;
    unsigned int worst_non_likelihood = 0xffff;
    unsigned int non_likelihood;
    for (int i = 0; i < tomato_boxs.size(); i++) {
      state = kf.predict();
      non_likelihood = 0;
      non_likelihood += abs(tomato_boxs[i].x - state.at<float>(0));
      non_likelihood += abs(tomato_boxs[i].y - state.at<float>(1));
      non_likelihood += abs(tomato_boxs[i].width - state.at<float>(4));
      non_likelihood += abs(tomato_boxs[i].height - state.at<float>(5));
      if (worst_non_likelihood > non_likelihood) {
        worst_non_likelihood = non_likelihood;
        index = i;
      }
    }
    return index;
  }

  void resetKalman() {
    //     kf.errorCovPre.at<float>(0) = 1; // px
    //     kf.errorCovPre.at<float>(7) = 1; // px
    //     kf.errorCovPre.at<float>(14) = 1;
    //     kf.errorCovPre.at<float>(21) = 1;
    //     kf.errorCovPre.at<float>(28) = 1; // px
    //     kf.errorCovPre.at<float>(35) = 1; // px

    for (int i=0; i<6; i++) kf.errorCovPre.at<float>(i*7) = 1;
  }

  bool found_flag;
  long tick_count, last_tick_count; // count while running.
  unsigned int not_founding_count;
  cv::KalmanFilter kf;
  cv::Mat state;
};

class ImageConverter
{
private:
  ros::NodeHandle nh;
  ros::Publisher point_pub;
  image_transport::ImageTransport it;
  image_transport::Subscriber rgb_sub;
  image_transport::Subscriber depth_sub;
  cv_bridge::CvImageConstPtr rgb_ptr;
  cv_bridge::CvImageConstPtr depth_ptr;
  SearchTomato searchTomatoObj;
  cv::Point tomato_point;
  bool found_flag;
  geometry_msgs::Point pub_msg;

public:
  ImageConverter() :
  it(nh), searchTomatoObj(), tomato_point(-1, -1), found_flag(false)
  {
    // subscrive to input video feed.
    point_pub = nh.advertise<geometry_msgs::Point>("tomato_point", 1);
    rgb_sub = it.subscribe("/camera/rgb/image_raw", 1, &ImageConverter::rgbCb, this);
    depth_sub = it.subscribe("/camera/depth_registered/image_raw", 1, &ImageConverter::depthCb, this);
  }

  ~ImageConverter() {}

  /**
   * search tomato function.
   * get rgb image, and search tomato.
   * will tomato_point have data.
   *
   * @author Yusuke Doi
   */
  void rgbCb(const sensor_msgs::ImageConstPtr& msg) {
    // get rgb image on kinect
    try {
      rgb_ptr = cv_bridge::toCvShare(msg, "bgr8");
    } catch (cv_bridge::Exception& e) {
      ROS_ERROR("cv_bridge exception by rgb: %s", e.what());
    }

    cv::Mat buf = rgb_ptr->image.clone();
    // search tomato
    if (found_flag = searchTomatoObj.searchTomatoPoint(rgb_ptr->image, tomato_point))
      cv::circle(buf, tomato_point, 40, CV_RGB(0, 255, 0), 3);

    cv::imshow(RGB_WINDOW_NAME, buf);
    cv::waitKey(100);
  }

  void depthCb(const sensor_msgs::ImageConstPtr& msg) {
    try {
      depth_ptr = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::TYPE_16UC1);
    } catch (cv_bridge::Exception& e) {
      ROS_ERROR("cv_bridge exception by depth: %s", e.what());
    }

    //testShowDepth(depth_ptr->image);
    if (found_flag) {
      printf("depth is %u", depth_ptr->image.at<uint16_t>(tomato_point));
      //      pub_msg.x = tomato_point.x;
      //      pub_msg.y = tomato_point.y;
      //      pub_msg.z = depth_ptr->image.at<uint16_t>(tomato_point);
      pub_msg.x = depth_ptr->image.at<uint16_t>(tomato_point);
      pub_msg.y = tomato_point.x;
      pub_msg.z = -tomato_point.y;
      point_pub.publish(pub_msg);
    }
    cv::waitKey(100);
  }

  void testShowDepth(const cv::Mat& depth) {
    static std::vector<uint16_t> m_gamma(2048);
    static bool firstFlag(false);
    if (!firstFlag) {
      for(unsigned int i = 0; i < 2048; i++) {
        float v = i/2048.0;
        v = std::pow(v, 3) * 6;
        m_gamma[i] = v*6*256;
      }
      firstFlag = true;
    }

    int i;
    cv::Mat buf = cv::Mat::zeros(depth.size(), CV_16UC1);
    cv::Mat output = cv::Mat::zeros(depth.size(), CV_8UC3);
    cv::normalize(depth, buf, 0, 2013, cv::NORM_MINMAX);

    for (i = 0; i < depth.rows * depth.cols; i++) {
      int y = (int)(i / depth.cols);
      int x = (int)(i % depth.cols);
      int pval = m_gamma[buf.at<uint16_t>(i)];
      int lb = pval & 0xff;

      switch(pval >> 8) {
        case 0:
          output.at<cv::Vec3b>(y,x)[2] = 255;
          output.at<cv::Vec3b>(y,x)[1] = 255-lb;
          output.at<cv::Vec3b>(y,x)[0] = 255-lb;
          break;
        case 1:
          output.at<cv::Vec3b>(y,x)[2] = 255;
          output.at<cv::Vec3b>(y,x)[1] = lb;
          output.at<cv::Vec3b>(y,x)[0] = 0;
          break;
        case 2:
          output.at<cv::Vec3b>(y,x)[2] = 255-lb;
          output.at<cv::Vec3b>(y,x)[1] = 255;
          output.at<cv::Vec3b>(y,x)[0] = 0;
          break;
        case 3:
          output.at<cv::Vec3b>(y,x)[2] = 0;
          output.at<cv::Vec3b>(y,x)[1] = 255;
          output.at<cv::Vec3b>(y,x)[0] = lb;
          break;
        case 4:
          output.at<cv::Vec3b>(y,x)[2] = 0;
          output.at<cv::Vec3b>(y,x)[1] = 255-lb;
          output.at<cv::Vec3b>(y,x)[0] = 255;
          break;
        case 5:
          output.at<cv::Vec3b>(y,x)[2] = 0;
          output.at<cv::Vec3b>(y,x)[1] = 0;
          output.at<cv::Vec3b>(y,x)[0] = 255-lb;
          break;
        default:
          output.at<cv::Vec3b>(y,x)[2] = 0;
          output.at<cv::Vec3b>(y,x)[1] = 0;
          output.at<cv::Vec3b>(y,x)[0] = 0;
          break;
      }
    }
      imshow(DEPTH_WINDOW_NAME, output);
  }
};

int main(int argc, char** argv) {
  ros::init(argc, argv, "kinect_tomato_search_node");
  ImageConverter ic;
  cv::namedWindow(RGB_WINDOW_NAME);
  cv::namedWindow(DEPTH_WINDOW_NAME);
  ros::spin();
  cv::destroyWindow(RGB_WINDOW_NAME);
  cv::destroyWindow(DEPTH_WINDOW_NAME);
  return 0;
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <uielement/langselectionmenucontroller.hxx>

#include <threadhelp/resetableguard.hxx>
#include "services.h"

#include <com/sun/star/awt/XDevice.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/awt/MenuItemStyle.hpp>
#include <com/sun/star/frame/XDispatchProvider.hpp>

#include <vcl/menu.hxx>
#include <vcl/svapp.hxx>
#include <vcl/i18nhelp.hxx>
#include <rtl/ustrbuf.hxx>
#include <vcl/mnemonic.hxx>
#include <comphelper/processfactory.hxx>

#include <com/sun/star/document/XDocumentLanguages.hpp>
#include <com/sun/star/frame/XPopupMenuController.hpp>
#include <com/sun/star/linguistic2/XLanguageGuessing.hpp>

#include <i18nlangtag/mslangid.hxx>
#include <svl/languageoptions.hxx>
#include <svtools/langtab.hxx>
#include <classes/fwlresid.hxx>

#include <classes/resource.hrc>

#include "helper/mischelper.hxx"
#include <osl/mutex.hxx>

#include <map>
#include <set>

//  Defines

using namespace ::com::sun::star;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::frame;
using namespace com::sun::star::beans;
using namespace com::sun::star::util;

namespace framework
{

DEFINE_XSERVICEINFO_MULTISERVICE_2      (   LanguageSelectionMenuController         ,
                                            OWeakObject                             ,
                                            SERVICENAME_POPUPMENUCONTROLLER         ,
                                            IMPLEMENTATIONNAME_LANGUAGESELECTIONMENUCONTROLLER
                                        )

DEFINE_INIT_SERVICE                     (   LanguageSelectionMenuController, {} )

LanguageSelectionMenuController::LanguageSelectionMenuController( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& xContext ) :
    svt::PopupMenuControllerBase( xContext ),
    m_bShowMenu( sal_True ),
    m_aLangGuessHelper( xContext )
{
}

LanguageSelectionMenuController::~LanguageSelectionMenuController()
{
}

// XEventListener
void SAL_CALL LanguageSelectionMenuController::disposing( const EventObject& ) throw ( RuntimeException, std::exception )
{
    Reference< css::awt::XMenuListener > xHolder(( OWeakObject *)this, UNO_QUERY );

    osl::MutexGuard aLock( m_aMutex );
    m_xFrame.clear();
    m_xDispatch.clear();
    m_xLanguageDispatch.clear();

    if ( m_xPopupMenu.is() )
        m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(( OWeakObject *)this, UNO_QUERY ));
    m_xPopupMenu.clear();
}

// XStatusListener
void SAL_CALL LanguageSelectionMenuController::statusChanged( const FeatureStateEvent& Event ) throw ( RuntimeException, std::exception )
{
    SolarMutexGuard aSolarMutexGuard;

    if (rBHelper.bDisposed || rBHelper.bInDispose)
        return;

    m_bShowMenu = sal_True;
    m_nScriptType = LS_SCRIPT_LATIN | LS_SCRIPT_ASIAN | LS_SCRIPT_COMPLEX;  //set the default value

    Sequence< OUString > aSeq;

    if ( Event.State >>= aSeq )
    {
        if ( aSeq.getLength() == 4 )
        {
            // Retrieve all other values from the sequence and
            // store it members!
            m_aCurLang          = aSeq[0];
            m_nScriptType       = static_cast< sal_Int16 >(aSeq[1].toInt32());
            m_aKeyboardLang     = aSeq[2];
            m_aGuessedTextLang  = aSeq[3];
        }
    }
    else if ( !Event.State.hasValue() )
    {
        m_bShowMenu = sal_False;    // no language -> no sub-menu entries -> disable menu
    }
}

// XMenuListener
void LanguageSelectionMenuController::impl_select(const Reference< XDispatch >& _xDispatch,const ::com::sun::star::util::URL& aTargetURL)
{
    Reference< XDispatch > xDispatch = _xDispatch;

    if ( aTargetURL.Complete == m_aMenuCommandURL_Font )
    {   //open format/character dialog for current selection
        xDispatch = m_xMenuDispatch_Font;
    }
    else if ( aTargetURL.Complete == m_aMenuCommandURL_Lang )
    {   //open language tab-page in tools/options dialog
        xDispatch = m_xMenuDispatch_Lang;
    }
    else if ( aTargetURL.Complete == m_aMenuCommandURL_CharDlgForParagraph )
    {   //open format/character dialog for current selection
        xDispatch = m_xMenuDispatch_CharDlgForParagraph;
    }

    if ( !xDispatch.is() )
    {
        Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
        if ( xDispatchProvider.is() )
            xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
    }

    if ( xDispatch.is() )
    {
        Sequence<PropertyValue>      aArgs;
        xDispatch->dispatch( aTargetURL, aArgs );
    }
}

// XPopupMenuController
void LanguageSelectionMenuController::impl_setPopupMenu()
{
    Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );

    com::sun::star::util::URL aTargetURL;

    // Register for language updates
    aTargetURL.Complete = m_aLangStatusCommandURL;
    m_xURLTransformer->parseStrict( aTargetURL );
    m_xLanguageDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );

    // Register for setting languages and opening language dialog
    aTargetURL.Complete = m_aMenuCommandURL_Lang;
    m_xURLTransformer->parseStrict( aTargetURL );
    m_xMenuDispatch_Lang = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );

    // Register for opening character dialog
    aTargetURL.Complete = m_aMenuCommandURL_Font;
    m_xURLTransformer->parseStrict( aTargetURL );
    m_xMenuDispatch_Font = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );

    // Register for opening character dialog with preselected paragraph
    aTargetURL.Complete = m_aMenuCommandURL_CharDlgForParagraph;
    m_xURLTransformer->parseStrict( aTargetURL );
    m_xMenuDispatch_CharDlgForParagraph = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
}

void LanguageSelectionMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu >& rPopupMenu , const Mode eMode )
{
    VCLXPopupMenu* pVCLPopupMenu = (VCLXPopupMenu *)VCLXMenu::GetImplementation( rPopupMenu );
    PopupMenu*     pPopupMenu    = 0;

    SolarMutexGuard aSolarMutexGuard;

    resetPopupMenu( rPopupMenu );
    if (!m_bShowMenu)
        return;

    if ( pVCLPopupMenu )
        pPopupMenu = (PopupMenu *)pVCLPopupMenu->GetMenu();

    OUString aCmd;
    OUString aCmd_Dialog;
    OUString aCmd_Language;
    if( eMode == MODE_SetLanguageSelectionMenu )
    {
        aCmd_Dialog += ".uno:FontDialog?Language:string=*";
        aCmd_Language += ".uno:LanguageStatus?Language:string=Current_";
    }
    else if ( eMode == MODE_SetLanguageParagraphMenu )
    {
        aCmd_Dialog += ".uno:FontDialogForParagraph";
        aCmd_Language += ".uno:LanguageStatus?Language:string=Paragraph_";
    }
    else if ( eMode == MODE_SetLanguageAllTextMenu )
    {
        aCmd_Dialog += ".uno:LanguageStatus?Language:string=*";
        aCmd_Language += ".uno:LanguageStatus?Language:string=Default_";
    }

    SvtLanguageTable    aLanguageTable;

    // get languages to be displayed in the menu
    std::set< OUString > aLangItems;
    FillLangItems( aLangItems, aLanguageTable, m_xFrame, m_aLangGuessHelper,
            m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedTextLang );

    // now add menu entries
    // the different menus purpose will be handled by the different string
    // for aCmd_Dialog and aCmd_Language

    sal_Int16 nItemId = 1;  // in this control the item id is not important for executing the command
    const OUString sAsterisk("*");  // multiple languages in current selection
    const OUString sEmpty;  // 'no language found' from language guessing
    std::set< OUString >::const_iterator it;
    for (it = aLangItems.begin(); it != aLangItems.end(); ++it)
    {
        const OUString & rStr( *it );
        if (rStr != aLanguageTable.GetString( LANGUAGE_NONE ) &&
            rStr != sAsterisk &&
            rStr != sEmpty)
        {
            pPopupMenu->InsertItem( nItemId, rStr );
            aCmd = aCmd_Language;
            aCmd += rStr;
            pPopupMenu->SetItemCommand( nItemId, aCmd );
            if (rStr == m_aCurLang && eMode == MODE_SetLanguageSelectionMenu )
            {
                //make a sign for the current language
                pPopupMenu->CheckItem( nItemId, true );
            }
            ++nItemId;
        }
    }

    // entry for LANGUAGE_NONE
    ++nItemId;
    pPopupMenu->InsertItem( nItemId, FwlResId(STR_LANGSTATUS_NONE).toString() );
    aCmd = aCmd_Language + "LANGUAGE_NONE";
    pPopupMenu->SetItemCommand( nItemId, aCmd );

    // entry for 'Reset to default language'
    ++nItemId;
    pPopupMenu->InsertItem( nItemId, FwlResId(STR_RESET_TO_DEFAULT_LANGUAGE).toString() );
    aCmd = aCmd_Language + "RESET_LANGUAGES";
    pPopupMenu->SetItemCommand( nItemId, aCmd );

    // entry for opening the Format/Character dialog
    ++nItemId;
    pPopupMenu->InsertItem( nItemId, FwlResId(STR_LANGSTATUS_MORE).toString());
    pPopupMenu->SetItemCommand( nItemId, aCmd_Dialog );
}

void SAL_CALL LanguageSelectionMenuController::updatePopupMenu() throw ( ::com::sun::star::uno::RuntimeException, std::exception )
{
    svt::PopupMenuControllerBase::updatePopupMenu();

    // Force status update to get information about the current languages
    osl::ClearableMutexGuard aLock( m_aMutex );
    Reference< XDispatch > xDispatch( m_xLanguageDispatch );
    com::sun::star::util::URL aTargetURL;
    aTargetURL.Complete = m_aLangStatusCommandURL;
    m_xURLTransformer->parseStrict( aTargetURL );
    aLock.clear();

    if ( xDispatch.is() )
    {
        xDispatch->addStatusListener( (static_cast< XStatusListener* >(this)), aTargetURL );
        xDispatch->removeStatusListener( (static_cast< XStatusListener* >(this)), aTargetURL );
    }

    // TODO: Fill menu with the information retrieved by the status update

    if ( m_aCommandURL == ".uno:SetLanguageSelectionMenu" )
    {
        fillPopupMenu(m_xPopupMenu, MODE_SetLanguageSelectionMenu );
    }
    else if ( m_aCommandURL == ".uno:SetLanguageParagraphMenu" )
    {
        fillPopupMenu(m_xPopupMenu, MODE_SetLanguageParagraphMenu );
    }
    else if ( m_aCommandURL == ".uno:SetLanguageAllTextMenu" )
    {
        fillPopupMenu(m_xPopupMenu, MODE_SetLanguageAllTextMenu );
    }
}

// XInitialization
void SAL_CALL LanguageSelectionMenuController::initialize( const Sequence< Any >& aArguments ) throw ( Exception, RuntimeException, std::exception )
{
    osl::MutexGuard aLock( m_aMutex );

    sal_Bool bInitalized( m_bInitialized );
    if ( !bInitalized )
    {
        svt::PopupMenuControllerBase::initialize(aArguments);

        if ( m_bInitialized )
        {
            m_aLangStatusCommandURL               = ".uno:LanguageStatus";
            m_aMenuCommandURL_Lang                = m_aLangStatusCommandURL;
            m_aMenuCommandURL_Font                = ".uno:FontDialog";
            m_aMenuCommandURL_CharDlgForParagraph = ".uno:FontDialogForParagraph";
        }
    }
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>coverity#707884 Uninitialized scalar field<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <uielement/langselectionmenucontroller.hxx>

#include <threadhelp/resetableguard.hxx>
#include "services.h"

#include <com/sun/star/awt/XDevice.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/awt/MenuItemStyle.hpp>
#include <com/sun/star/frame/XDispatchProvider.hpp>

#include <vcl/menu.hxx>
#include <vcl/svapp.hxx>
#include <vcl/i18nhelp.hxx>
#include <rtl/ustrbuf.hxx>
#include <vcl/mnemonic.hxx>
#include <comphelper/processfactory.hxx>

#include <com/sun/star/document/XDocumentLanguages.hpp>
#include <com/sun/star/frame/XPopupMenuController.hpp>
#include <com/sun/star/linguistic2/XLanguageGuessing.hpp>

#include <i18nlangtag/mslangid.hxx>
#include <svl/languageoptions.hxx>
#include <svtools/langtab.hxx>
#include <classes/fwlresid.hxx>

#include <classes/resource.hrc>

#include "helper/mischelper.hxx"
#include <osl/mutex.hxx>

#include <map>
#include <set>

//  Defines

using namespace ::com::sun::star;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::frame;
using namespace com::sun::star::beans;
using namespace com::sun::star::util;

namespace framework
{

DEFINE_XSERVICEINFO_MULTISERVICE_2      (   LanguageSelectionMenuController         ,
                                            OWeakObject                             ,
                                            SERVICENAME_POPUPMENUCONTROLLER         ,
                                            IMPLEMENTATIONNAME_LANGUAGESELECTIONMENUCONTROLLER
                                        )

DEFINE_INIT_SERVICE                     (   LanguageSelectionMenuController, {} )

LanguageSelectionMenuController::LanguageSelectionMenuController( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& xContext )
    : svt::PopupMenuControllerBase(xContext)
    , m_bShowMenu(sal_True)
    , m_nScriptType(LS_SCRIPT_LATIN | LS_SCRIPT_ASIAN | LS_SCRIPT_COMPLEX)
    , m_aLangGuessHelper(xContext)
{
}

LanguageSelectionMenuController::~LanguageSelectionMenuController()
{
}

// XEventListener
void SAL_CALL LanguageSelectionMenuController::disposing( const EventObject& ) throw ( RuntimeException, std::exception )
{
    Reference< css::awt::XMenuListener > xHolder(( OWeakObject *)this, UNO_QUERY );

    osl::MutexGuard aLock( m_aMutex );
    m_xFrame.clear();
    m_xDispatch.clear();
    m_xLanguageDispatch.clear();

    if ( m_xPopupMenu.is() )
        m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(( OWeakObject *)this, UNO_QUERY ));
    m_xPopupMenu.clear();
}

// XStatusListener
void SAL_CALL LanguageSelectionMenuController::statusChanged( const FeatureStateEvent& Event ) throw ( RuntimeException, std::exception )
{
    SolarMutexGuard aSolarMutexGuard;

    if (rBHelper.bDisposed || rBHelper.bInDispose)
        return;

    m_bShowMenu = sal_True;
    m_nScriptType = LS_SCRIPT_LATIN | LS_SCRIPT_ASIAN | LS_SCRIPT_COMPLEX;  //set the default value

    Sequence< OUString > aSeq;

    if ( Event.State >>= aSeq )
    {
        if ( aSeq.getLength() == 4 )
        {
            // Retrieve all other values from the sequence and
            // store it members!
            m_aCurLang          = aSeq[0];
            m_nScriptType       = static_cast< sal_Int16 >(aSeq[1].toInt32());
            m_aKeyboardLang     = aSeq[2];
            m_aGuessedTextLang  = aSeq[3];
        }
    }
    else if ( !Event.State.hasValue() )
    {
        m_bShowMenu = sal_False;    // no language -> no sub-menu entries -> disable menu
    }
}

// XMenuListener
void LanguageSelectionMenuController::impl_select(const Reference< XDispatch >& _xDispatch,const ::com::sun::star::util::URL& aTargetURL)
{
    Reference< XDispatch > xDispatch = _xDispatch;

    if ( aTargetURL.Complete == m_aMenuCommandURL_Font )
    {   //open format/character dialog for current selection
        xDispatch = m_xMenuDispatch_Font;
    }
    else if ( aTargetURL.Complete == m_aMenuCommandURL_Lang )
    {   //open language tab-page in tools/options dialog
        xDispatch = m_xMenuDispatch_Lang;
    }
    else if ( aTargetURL.Complete == m_aMenuCommandURL_CharDlgForParagraph )
    {   //open format/character dialog for current selection
        xDispatch = m_xMenuDispatch_CharDlgForParagraph;
    }

    if ( !xDispatch.is() )
    {
        Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
        if ( xDispatchProvider.is() )
            xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
    }

    if ( xDispatch.is() )
    {
        Sequence<PropertyValue>      aArgs;
        xDispatch->dispatch( aTargetURL, aArgs );
    }
}

// XPopupMenuController
void LanguageSelectionMenuController::impl_setPopupMenu()
{
    Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );

    com::sun::star::util::URL aTargetURL;

    // Register for language updates
    aTargetURL.Complete = m_aLangStatusCommandURL;
    m_xURLTransformer->parseStrict( aTargetURL );
    m_xLanguageDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );

    // Register for setting languages and opening language dialog
    aTargetURL.Complete = m_aMenuCommandURL_Lang;
    m_xURLTransformer->parseStrict( aTargetURL );
    m_xMenuDispatch_Lang = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );

    // Register for opening character dialog
    aTargetURL.Complete = m_aMenuCommandURL_Font;
    m_xURLTransformer->parseStrict( aTargetURL );
    m_xMenuDispatch_Font = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );

    // Register for opening character dialog with preselected paragraph
    aTargetURL.Complete = m_aMenuCommandURL_CharDlgForParagraph;
    m_xURLTransformer->parseStrict( aTargetURL );
    m_xMenuDispatch_CharDlgForParagraph = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
}

void LanguageSelectionMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu >& rPopupMenu , const Mode eMode )
{
    VCLXPopupMenu* pVCLPopupMenu = (VCLXPopupMenu *)VCLXMenu::GetImplementation( rPopupMenu );
    PopupMenu*     pPopupMenu    = 0;

    SolarMutexGuard aSolarMutexGuard;

    resetPopupMenu( rPopupMenu );
    if (!m_bShowMenu)
        return;

    if ( pVCLPopupMenu )
        pPopupMenu = (PopupMenu *)pVCLPopupMenu->GetMenu();

    OUString aCmd;
    OUString aCmd_Dialog;
    OUString aCmd_Language;
    if( eMode == MODE_SetLanguageSelectionMenu )
    {
        aCmd_Dialog += ".uno:FontDialog?Language:string=*";
        aCmd_Language += ".uno:LanguageStatus?Language:string=Current_";
    }
    else if ( eMode == MODE_SetLanguageParagraphMenu )
    {
        aCmd_Dialog += ".uno:FontDialogForParagraph";
        aCmd_Language += ".uno:LanguageStatus?Language:string=Paragraph_";
    }
    else if ( eMode == MODE_SetLanguageAllTextMenu )
    {
        aCmd_Dialog += ".uno:LanguageStatus?Language:string=*";
        aCmd_Language += ".uno:LanguageStatus?Language:string=Default_";
    }

    SvtLanguageTable    aLanguageTable;

    // get languages to be displayed in the menu
    std::set< OUString > aLangItems;
    FillLangItems( aLangItems, aLanguageTable, m_xFrame, m_aLangGuessHelper,
            m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedTextLang );

    // now add menu entries
    // the different menus purpose will be handled by the different string
    // for aCmd_Dialog and aCmd_Language

    sal_Int16 nItemId = 1;  // in this control the item id is not important for executing the command
    const OUString sAsterisk("*");  // multiple languages in current selection
    const OUString sEmpty;  // 'no language found' from language guessing
    std::set< OUString >::const_iterator it;
    for (it = aLangItems.begin(); it != aLangItems.end(); ++it)
    {
        const OUString & rStr( *it );
        if (rStr != aLanguageTable.GetString( LANGUAGE_NONE ) &&
            rStr != sAsterisk &&
            rStr != sEmpty)
        {
            pPopupMenu->InsertItem( nItemId, rStr );
            aCmd = aCmd_Language;
            aCmd += rStr;
            pPopupMenu->SetItemCommand( nItemId, aCmd );
            if (rStr == m_aCurLang && eMode == MODE_SetLanguageSelectionMenu )
            {
                //make a sign for the current language
                pPopupMenu->CheckItem( nItemId, true );
            }
            ++nItemId;
        }
    }

    // entry for LANGUAGE_NONE
    ++nItemId;
    pPopupMenu->InsertItem( nItemId, FwlResId(STR_LANGSTATUS_NONE).toString() );
    aCmd = aCmd_Language + "LANGUAGE_NONE";
    pPopupMenu->SetItemCommand( nItemId, aCmd );

    // entry for 'Reset to default language'
    ++nItemId;
    pPopupMenu->InsertItem( nItemId, FwlResId(STR_RESET_TO_DEFAULT_LANGUAGE).toString() );
    aCmd = aCmd_Language + "RESET_LANGUAGES";
    pPopupMenu->SetItemCommand( nItemId, aCmd );

    // entry for opening the Format/Character dialog
    ++nItemId;
    pPopupMenu->InsertItem( nItemId, FwlResId(STR_LANGSTATUS_MORE).toString());
    pPopupMenu->SetItemCommand( nItemId, aCmd_Dialog );
}

void SAL_CALL LanguageSelectionMenuController::updatePopupMenu() throw ( ::com::sun::star::uno::RuntimeException, std::exception )
{
    svt::PopupMenuControllerBase::updatePopupMenu();

    // Force status update to get information about the current languages
    osl::ClearableMutexGuard aLock( m_aMutex );
    Reference< XDispatch > xDispatch( m_xLanguageDispatch );
    com::sun::star::util::URL aTargetURL;
    aTargetURL.Complete = m_aLangStatusCommandURL;
    m_xURLTransformer->parseStrict( aTargetURL );
    aLock.clear();

    if ( xDispatch.is() )
    {
        xDispatch->addStatusListener( (static_cast< XStatusListener* >(this)), aTargetURL );
        xDispatch->removeStatusListener( (static_cast< XStatusListener* >(this)), aTargetURL );
    }

    // TODO: Fill menu with the information retrieved by the status update

    if ( m_aCommandURL == ".uno:SetLanguageSelectionMenu" )
    {
        fillPopupMenu(m_xPopupMenu, MODE_SetLanguageSelectionMenu );
    }
    else if ( m_aCommandURL == ".uno:SetLanguageParagraphMenu" )
    {
        fillPopupMenu(m_xPopupMenu, MODE_SetLanguageParagraphMenu );
    }
    else if ( m_aCommandURL == ".uno:SetLanguageAllTextMenu" )
    {
        fillPopupMenu(m_xPopupMenu, MODE_SetLanguageAllTextMenu );
    }
}

// XInitialization
void SAL_CALL LanguageSelectionMenuController::initialize( const Sequence< Any >& aArguments ) throw ( Exception, RuntimeException, std::exception )
{
    osl::MutexGuard aLock( m_aMutex );

    sal_Bool bInitalized( m_bInitialized );
    if ( !bInitalized )
    {
        svt::PopupMenuControllerBase::initialize(aArguments);

        if ( m_bInitialized )
        {
            m_aLangStatusCommandURL               = ".uno:LanguageStatus";
            m_aMenuCommandURL_Lang                = m_aLangStatusCommandURL;
            m_aMenuCommandURL_Font                = ".uno:FontDialog";
            m_aMenuCommandURL_CharDlgForParagraph = ".uno:FontDialogForParagraph";
        }
    }
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>#include <iostream>   // std::cout

#include "gtest/gtest.h"

#include "simplesorts.h"
#include "printarray.h"
#include "verifyarrays.h"

#define quiet

TEST(SimpleSorts, InsertionSort)
{
    const int N = 5;

    double *to_sort_data = new double[N];
    double *sorted_data  = new double[N];

    to_sort_data[0] = 21.3;
    to_sort_data[1] = 68.2;
    to_sort_data[2] = 1.3;
    to_sort_data[3] = 44.3;
    to_sort_data[4] = 10.3;

    memcpy(sorted_data, to_sort_data, N*sizeof(double));

    InsertionSort(sorted_data, N);

#ifndef quiet
    std::cout << "Arrays" << std::endl;
    std::cout << "Index Original     Sorted " << std::endl;
    for (int i = 0 ; i < N ; i++)
    {
        std::cout << i+1 << "/" << N << "   "
                    << to_sort_data[i] << "        "
                    << sorted_data[i] << std::endl;
    }
#endif // quiet

    for (int i = 1 ; i < N ; i++)
    {
        EXPECT_GE(sorted_data[i], sorted_data[i-1]);
    }

    for (int i = 0 ; i < N ; i++)
    {
        EXPECT_NE(sorted_data[i], 0);
    }
    ASSERT_TRUE(VerifyIfAllNotNaN(sorted_data, N));
    ASSERT_TRUE(VerifyIfOrdered(sorted_data, N));
    ASSERT_TRUE(VerifySortedUnique(to_sort_data, sorted_data, N));

    delete[] to_sort_data;
    delete[] sorted_data;
}

TEST(SimpleSorts, InsertionSortBestCase)
{
    // Best case is O(N) when array already sorted
    const int N = 1000;

    int *to_sort_data = new int[N];
    int *sorted_data  = new int[N];

    for (int i = 0 ; i < N ; i++)
    {
        to_sort_data[i] = i;
    }

    memcpy(sorted_data, to_sort_data, N*sizeof(int));

    InsertionSort(sorted_data, N);

#ifndef quiet
    std::cout << "Arrays" << std::endl;
    std::cout << "Index Original     Sorted " << std::endl;
    for (int i = 0 ; i < N ; i++)
    {
        std::cout << i+1 << "/" << N << "   "
                    << to_sort_data[i] << "        "
                    << sorted_data[i] << std::endl;
    }
#endif // quiet

    for (int i = 1 ; i < N ; i++)
    {
        EXPECT_GE(sorted_data[i], sorted_data[i-1]);
    }

    ASSERT_TRUE(VerifyIfAllNotNaN(sorted_data, N));
    ASSERT_TRUE(VerifyIfOrdered(sorted_data, N));
    ASSERT_TRUE(VerifySortedUnique(to_sort_data, sorted_data, N));

    delete[] to_sort_data;
    delete[] sorted_data;

}

TEST(SimpleSorts, InsertionSortWorstCase)
{
    // Worst case is O(N^2) when array already sorted, but in reverse order.
    const int N = 1000;

    int *to_sort_data = new int[N];
    int *sorted_data  = new int[N];

    for (int i = 0 ; i < N ; i++)
    {
        to_sort_data[i] = N-i;
    }

    memcpy(sorted_data, to_sort_data, N*sizeof(int));

    InsertionSort(sorted_data, N);

#ifndef quiet
    std::cout << "Arrays" << std::endl;
    std::cout << "Index Original     Sorted " << std::endl;
    for (int i = 0 ; i < N ; i++)
    {
        std::cout << i+1 << "/" << N << "   "
                    << to_sort_data[i] << "        "
                    << sorted_data[i] << std::endl;
    }
#endif // quiet

    for (int i = 1 ; i < N ; i++)
    {
        EXPECT_GE(sorted_data[i], sorted_data[i-1]);
    }

    ASSERT_TRUE(VerifyIfAllNotNaN(sorted_data, N));
    ASSERT_TRUE(VerifyIfOrdered(sorted_data, N));
    ASSERT_TRUE(VerifySortedUnique(to_sort_data, sorted_data, N));

    delete[] to_sort_data;
    delete[] sorted_data;

}
<commit_msg>Test selection sort<commit_after>#include <iostream>   // std::cout

#include "gtest/gtest.h"

#include "simplesorts.h"
#include "printarray.h"
#include "verifyarrays.h"

#define quiet

TEST(SimpleSorts, InsertionSort)
{
    const int N = 5;

    double *to_sort_data = new double[N];
    double *sorted_data  = new double[N];

    to_sort_data[0] = 21.3;
    to_sort_data[1] = 68.2;
    to_sort_data[2] = 1.3;
    to_sort_data[3] = 44.3;
    to_sort_data[4] = 10.3;

    memcpy(sorted_data, to_sort_data, N*sizeof(double));

    InsertionSort(sorted_data, N);

#ifndef quiet
    std::cout << "Arrays" << std::endl;
    std::cout << "Index Original     Sorted " << std::endl;
    for (int i = 0 ; i < N ; i++)
    {
        std::cout << i+1 << "/" << N << "   "
                    << to_sort_data[i] << "        "
                    << sorted_data[i] << std::endl;
    }
#endif // quiet

    for (int i = 1 ; i < N ; i++)
    {
        EXPECT_GE(sorted_data[i], sorted_data[i-1]);
    }

    for (int i = 0 ; i < N ; i++)
    {
        EXPECT_NE(sorted_data[i], 0);
    }
    ASSERT_TRUE(VerifyIfAllNotNaN(sorted_data, N));
    ASSERT_TRUE(VerifyIfOrdered(sorted_data, N));
    ASSERT_TRUE(VerifySortedUnique(to_sort_data, sorted_data, N));

    delete[] to_sort_data;
    delete[] sorted_data;
}

TEST(SimpleSorts, InsertionSortBestCase)
{
    // Best case is O(N) when array already sorted
    const int N = 1000;

    int *to_sort_data = new int[N];
    int *sorted_data  = new int[N];

    for (int i = 0 ; i < N ; i++)
    {
        to_sort_data[i] = i;
    }

    memcpy(sorted_data, to_sort_data, N*sizeof(int));

    InsertionSort(sorted_data, N);

#ifndef quiet
    std::cout << "Arrays" << std::endl;
    std::cout << "Index Original     Sorted " << std::endl;
    for (int i = 0 ; i < N ; i++)
    {
        std::cout << i+1 << "/" << N << "   "
                    << to_sort_data[i] << "        "
                    << sorted_data[i] << std::endl;
    }
#endif // quiet

    for (int i = 1 ; i < N ; i++)
    {
        EXPECT_GE(sorted_data[i], sorted_data[i-1]);
    }

    ASSERT_TRUE(VerifyIfAllNotNaN(sorted_data, N));
    ASSERT_TRUE(VerifyIfOrdered(sorted_data, N));
    ASSERT_TRUE(VerifySortedUnique(to_sort_data, sorted_data, N));

    delete[] to_sort_data;
    delete[] sorted_data;

}

TEST(SimpleSorts, InsertionSortWorstCase)
{
    // Worst case is O(N^2) when array already sorted, but in reverse order.
    const int N = 1000;

    int *to_sort_data = new int[N];
    int *sorted_data  = new int[N];

    for (int i = 0 ; i < N ; i++)
    {
        to_sort_data[i] = N-i;
    }

    memcpy(sorted_data, to_sort_data, N*sizeof(int));

    InsertionSort(sorted_data, N);

#ifndef quiet
    std::cout << "Arrays" << std::endl;
    std::cout << "Index Original     Sorted " << std::endl;
    for (int i = 0 ; i < N ; i++)
    {
        std::cout << i+1 << "/" << N << "   "
                    << to_sort_data[i] << "        "
                    << sorted_data[i] << std::endl;
    }
#endif // quiet

    for (int i = 1 ; i < N ; i++)
    {
        EXPECT_GE(sorted_data[i], sorted_data[i-1]);
    }

    ASSERT_TRUE(VerifyIfAllNotNaN(sorted_data, N));
    ASSERT_TRUE(VerifyIfOrdered(sorted_data, N));
    ASSERT_TRUE(VerifySortedUnique(to_sort_data, sorted_data, N));

    delete[] to_sort_data;
    delete[] sorted_data;

}

TEST(SimpleSorts, SelectionSort)
{
    const int N = 5;

    double *to_sort_data = new double[N];
    double *sorted_data  = new double[N];

    to_sort_data[0] = 21.3;
    to_sort_data[1] = 68.2;
    to_sort_data[2] = 1.3;
    to_sort_data[3] = 44.3;
    to_sort_data[4] = 10.3;

    memcpy(sorted_data, to_sort_data, N*sizeof(double));

    InsertionSort(sorted_data, N);

#ifndef quiet
    std::cout << "Arrays" << std::endl;
    std::cout << "Index Original     Sorted " << std::endl;
    for (int i = 0 ; i < N ; i++)
    {
        std::cout << i+1 << "/" << N << "   "
                    << to_sort_data[i] << "        "
                    << sorted_data[i] << std::endl;
    }
#endif // quiet

    for (int i = 1 ; i < N ; i++)
    {
        EXPECT_GE(sorted_data[i], sorted_data[i-1]);
    }

    for (int i = 0 ; i < N ; i++)
    {
        EXPECT_NE(sorted_data[i], 0);
    }
    ASSERT_TRUE(VerifyIfAllNotNaN(sorted_data, N));
    ASSERT_TRUE(VerifyIfOrdered(sorted_data, N));
    ASSERT_TRUE(VerifySortedUnique(to_sort_data, sorted_data, N));

    delete[] to_sort_data;
    delete[] sorted_data;
}

<|endoftext|>


<commit_before>#ifndef VENTURA_WIN32_SINGLE_DIRECTORY_WATCHER_HPP
#define VENTURA_WIN32_SINGLE_DIRECTORY_WATCHER_HPP

#include <ventura/win32/overlapped_directory_changes.hpp>
#include <ventura/file_notification.hpp>
#include <ventura/absolute_path.hpp>
#include <silicium/observable/error_or_enumerate.hpp>
#include <silicium/observable/ref.hpp>
#include <silicium/observable/transform_if_initialized.hpp>
#include <silicium/observable/function_observer.hpp>
#include <ventura/absolute_path.hpp>
#include <silicium/optional.hpp>
#include <boost/ref.hpp>
#include <boost/utility/in_place_factory.hpp>

#define VENTURA_HAS_SINGLE_DIRECTORY_WATCHER SILICIUM_HAS_ERROR_OR_ENUMERATE

namespace ventura
{
#if defined(_WIN32) && SILICIUM_HAS_SINGLE_DIRECTORY_WATCHER
	namespace win32
	{
		inline Si::optional<file_notification_type> to_portable_file_notification_type(DWORD action)
		{
			switch (action)
			{
			case FILE_ACTION_RENAMED_NEW_NAME:
			case FILE_ACTION_ADDED:
				return file_notification_type::add;

			case FILE_ACTION_RENAMED_OLD_NAME:
			case FILE_ACTION_REMOVED:
				return file_notification_type::remove;

			case FILE_ACTION_MODIFIED:
				return file_notification_type::change_content_or_metadata;

			default:
				return Si::none; //TODO
			}
		}

		inline Si::optional<Si::error_or<ventura::file_notification>> to_portable_file_notification(Si::error_or<win32::file_notification> &&original)
		{
			if (original.is_error())
			{
				return Si::error_or<ventura::file_notification>(original.error());
			}
			auto const type = to_portable_file_notification_type(original.get().action);
			if (!type)
			{
				return Si::none;
			}
			return Si::error_or<ventura::file_notification>(ventura::file_notification(*type, std::move(original.get().name), true));
		}
	}

	struct single_directory_watcher
	{
		typedef Si::error_or<file_notification> element_type;

		single_directory_watcher()
		{
		}

		explicit single_directory_watcher(boost::asio::io_service &io, ventura::absolute_path const &watched)
			: impl(Si::error_or_enumerate(win32::overlapped_directory_changes(io, watched, false)), win32::to_portable_file_notification)
			, work(boost::in_place(boost::ref(io)))
		{
		}

		template <class Observer>
		void async_get_one(Observer &&observer)
		{
			impl.async_get_one(
				Si::function_observer<std::function<void(Si::optional<Si::error_or<file_notification>>)>>(
				[observer
#if SILICIUM_COMPILER_HAS_EXTENDED_CAPTURE
					= std::forward<Observer>(observer)
#endif
				](Si::optional<Si::error_or<file_notification>> element) mutable
				{
					if (element)
					{
						std::move(observer).got_element(std::move(*element));
					}
					else
					{
						std::move(observer).ended();
					}
				})
			);
		}

	private:

		//TODO: save the memory for the function pointer
		Si::conditional_transformer<
			Si::error_or<file_notification>,
			Si::error_or_enumerator<win32::overlapped_directory_changes>,
			Si::optional<Si::error_or<file_notification>>(*)(Si::error_or<win32::file_notification> &&),
			Si::function_observer<std::function<void(Si::optional<Si::error_or<file_notification>>)>>
		> impl;
		boost::optional<boost::asio::io_service::work> work;
	};
#endif
}

#endif
<commit_msg>fix for VC++<commit_after>#ifndef VENTURA_WIN32_SINGLE_DIRECTORY_WATCHER_HPP
#define VENTURA_WIN32_SINGLE_DIRECTORY_WATCHER_HPP

#include <ventura/win32/overlapped_directory_changes.hpp>
#include <ventura/file_notification.hpp>
#include <ventura/absolute_path.hpp>
#include <silicium/observable/error_or_enumerate.hpp>
#include <silicium/observable/ref.hpp>
#include <silicium/observable/transform_if_initialized.hpp>
#include <silicium/observable/function_observer.hpp>
#include <ventura/absolute_path.hpp>
#include <silicium/optional.hpp>
#include <boost/ref.hpp>
#include <boost/utility/in_place_factory.hpp>

#define VENTURA_HAS_SINGLE_DIRECTORY_WATCHER SILICIUM_HAS_ERROR_OR_ENUMERATE

namespace ventura
{
#if defined(_WIN32) && VENTURA_HAS_SINGLE_DIRECTORY_WATCHER
	namespace win32
	{
		inline Si::optional<file_notification_type> to_portable_file_notification_type(DWORD action)
		{
			switch (action)
			{
			case FILE_ACTION_RENAMED_NEW_NAME:
			case FILE_ACTION_ADDED:
				return file_notification_type::add;

			case FILE_ACTION_RENAMED_OLD_NAME:
			case FILE_ACTION_REMOVED:
				return file_notification_type::remove;

			case FILE_ACTION_MODIFIED:
				return file_notification_type::change_content_or_metadata;

			default:
				return Si::none; //TODO
			}
		}

		inline Si::optional<Si::error_or<ventura::file_notification>> to_portable_file_notification(Si::error_or<win32::file_notification> &&original)
		{
			if (original.is_error())
			{
				return Si::error_or<ventura::file_notification>(original.error());
			}
			auto const type = to_portable_file_notification_type(original.get().action);
			if (!type)
			{
				return Si::none;
			}
			return Si::error_or<ventura::file_notification>(ventura::file_notification(*type, std::move(original.get().name), true));
		}
	}

	struct single_directory_watcher
	{
		typedef Si::error_or<file_notification> element_type;

		single_directory_watcher()
		{
		}

		explicit single_directory_watcher(boost::asio::io_service &io, ventura::absolute_path const &watched)
			: impl(Si::error_or_enumerate(win32::overlapped_directory_changes(io, watched, false)), win32::to_portable_file_notification)
			, work(boost::in_place(boost::ref(io)))
		{
		}

		template <class Observer>
		void async_get_one(Observer &&observer)
		{
			impl.async_get_one(
				Si::function_observer<std::function<void(Si::optional<Si::error_or<file_notification>>)>>(
				[observer
#if SILICIUM_COMPILER_HAS_EXTENDED_CAPTURE
					= std::forward<Observer>(observer)
#endif
				](Si::optional<Si::error_or<file_notification>> element) mutable
				{
					if (element)
					{
						std::move(observer).got_element(std::move(*element));
					}
					else
					{
						std::move(observer).ended();
					}
				})
			);
		}

	private:

		//TODO: save the memory for the function pointer
		Si::conditional_transformer<
			Si::error_or<file_notification>,
			Si::error_or_enumerator<win32::overlapped_directory_changes>,
			Si::optional<Si::error_or<file_notification>>(*)(Si::error_or<win32::file_notification> &&),
			Si::function_observer<std::function<void(Si::optional<Si::error_or<file_notification>>)>>
		> impl;
		boost::optional<boost::asio::io_service::work> work;
	};
#endif
}

#endif
<|endoftext|>


<commit_before>/************************************************************************
	filename: 	OgreResourceProvider.cpp
	created:	8/7/2004
	author:		James '_mental_' O'Sullivan

	purpose:	Implements the Resource Provider common functionality
*************************************************************************/
/*************************************************************************
    Crazy Eddie's GUI System (http://www.cegui.org.uk)
    Copyright (C)2004 - 2005 Paul D Turner (paul@cegui.org.uk)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*************************************************************************/
#include "renderers/OgreGUIRenderer/OgreResourceProvider.h"

#include "CEGUIExceptions.h"
#include "OgreArchiveManager.h"

#include <xercesc/framework/MemBufInputSource.hpp>
#include <xercesc/framework/MemoryManager.hpp>
#include <memory.h>

// Start of CEGUI namespace section
namespace CEGUI
{
//    void OgreResourceProvider::loadInputSourceContainer(const String& filename, InputSourceContainer& output)
//   {
//        Ogre::DataStreamPtr input = Ogre::ResourceGroupManager::getSingleton().openResource(filename.c_str());
//
//		if (input.isNull())
//		{
//			throw InvalidRequestException((utf8*)
//				"Scheme::Scheme - Filename supplied for Scheme loading must be valid");
//		}
//
//       XERCES_CPP_NAMESPACE_USE
//        size_t buffsz = input->size();
//        unsigned char* mem = reinterpret_cast<unsigned char*>(XMLPlatformUtils::fgArrayMemoryManager->allocate(buffsz));
//        memcpy(mem, input.getPointer()->getAsString().c_str(), buffsz);
//        InputSource* mInputSource = new MemBufInputSource(mem, buffsz, filename.c_str(), true);
//        input.setNull();
//
//       output.setData(mInputSource);
//    }

    void OgreResourceProvider::loadRawDataContainer(const String& filename, RawDataContainer& output)
    {
		Ogre::DataStreamPtr input = Ogre::ResourceGroupManager::getSingleton().openResource(filename.c_str());

		if (input.isNull())
		{
            throw InvalidRequestException((utf8*)
                "Scheme::Scheme - Filename supplied for Scheme loading must be valid");
        }

        size_t buffsz = input->size();
        unsigned char* mem = reinterpret_cast<unsigned char*>(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgArrayMemoryManager->allocate(buffsz));
        memcpy(mem, input.getPointer()->getAsString().c_str(), buffsz);
        input.setNull();

        output.setData(mem);
        output.setSize(buffsz);
    }
} // End of  CEGUI namespace section
<commit_msg>Patch #1112593: Fixes & tweaks to OgreResourceProvider from sinbad.<commit_after>/************************************************************************
	filename: 	OgreResourceProvider.cpp
	created:	8/7/2004
	author:		James '_mental_' O'Sullivan

	purpose:	Implements the Resource Provider common functionality
*************************************************************************/
/*************************************************************************
    Crazy Eddie's GUI System (http://www.cegui.org.uk)
    Copyright (C)2004 - 2005 Paul D Turner (paul@cegui.org.uk)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*************************************************************************/
#include "renderers/OgreGUIRenderer/OgreResourceProvider.h"

#include "CEGUIExceptions.h"
#include "OgreArchiveManager.h"

#include "OgreNoMemoryMacros.h"
#include <xercesc/framework/MemBufInputSource.hpp>
#include <xercesc/framework/MemoryManager.hpp>
#include <memory.h>
#include "OgreMemoryMacros.h"

// Start of CEGUI namespace section
namespace CEGUI
{
//    void OgreResourceProvider::loadInputSourceContainer(const String& filename, InputSourceContainer& output)
//   {
//        Ogre::DataStreamPtr input = Ogre::ResourceGroupManager::getSingleton().openResource(filename.c_str());
//
//		if (input.isNull())
//		{
//			throw InvalidRequestException((utf8*)
//				"Scheme::Scheme - Filename supplied for Scheme loading must be valid");
//		}
//
//       XERCES_CPP_NAMESPACE_USE
//        size_t buffsz = input->size();
//        unsigned char* mem = reinterpret_cast<unsigned char*>(XMLPlatformUtils::fgArrayMemoryManager->allocate(buffsz));
//        memcpy(mem, input.getPointer()->getAsString().c_str(), buffsz);
//        InputSource* mInputSource = new MemBufInputSource(mem, buffsz, filename.c_str(), true);
//        input.setNull();
//
//       output.setData(mInputSource);
//    }

    void OgreResourceProvider::loadRawDataContainer(const String& filename, RawDataContainer& output)
    {
		Ogre::DataStreamPtr input = Ogre::ResourceGroupManager::getSingleton().openResource(filename.c_str());

		if (input.isNull())
		{
            throw InvalidRequestException((utf8*)
                "Scheme::Scheme - Filename supplied for Scheme loading must be valid");
        }

		Ogre::String buf = input->getAsString();
		size_t buffsz = buf.length();
        unsigned char* mem = reinterpret_cast<unsigned char*>
			(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgArrayMemoryManager->allocate(buffsz));
        memcpy(mem, buf.c_str(), buffsz);

        output.setData(mem);
        output.setSize(buffsz);
    }
} // End of  CEGUI namespace section
<|endoftext|>


<commit_before>#pragma once

// only included in case there's a C++11 compiler out there that doesn't support `#pragma once`
#ifndef DKM_KMEANS_H
#define DKM_KMEANS_H

#include <vector>
#include <array>
#include <tuple>
#include <cstddef>
#include <cstdint>
#include <random>
#include <type_traits>
#include <cassert>

/*
DKM - A k-means implementation that is generic across variable data dimensions.
*/
namespace dkm {

/*
These functions are all private implementation details and shouldn't be referenced outside of this
file.
*/
namespace details {

/*
Calculate the square of the distance between two points.
*/
template <typename T, size_t N>
T distance_squared(const std::array<T, N>& point_a, const std::array<T, N>& point_b) {
	T d_squared = T();
	for (typename std::array<T, N>::size_type i = 0; i < N; ++i) {
		auto delta = point_a[i] - point_b[i];
		d_squared += delta * delta;
	}
	return d_squared;
}

/*
Calculate the smallest distance between each of the data points and any of the input means.
*/
template <typename T, size_t N>
std::vector<T> closest_distance(
	const std::vector<std::array<T, N>>& means, const std::vector<std::array<T, N>>& data, uint32_t k) {
	std::vector<T> distances;
	distances.reserve(k);
	for (auto& d : data) {
		T closest = distance_squared(d, means[0]);
		for (auto& m : means) {
			T distance = distance_squared(d, m);
			if (distance < closest)
				closest = distance;
		}
		distances.push_back(closest);
	}
	return distances;
}

/*
This is an alternate initialization method based on the [kmeans++](https://en.wikipedia.org/wiki/K-means%2B%2B)
initialization algorithm.
*/
template <typename T, size_t N>
std::vector<std::array<T, N>> random_plusplus(const std::vector<std::array<T, N>>& data, uint32_t k) {
	assert(k > 0);
	using input_size_t = typename std::array<T, N>::size_type;
	std::vector<std::array<T, N>> means;
	// Using a very simple PRBS generator, parameters selected according to
	// https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
	std::random_device rand_device;
	std::linear_congruential_engine<uint64_t, 6364136223846793005, 1442695040888963407, UINT64_MAX> rand_engine(
		rand_device());

	// Select first mean at random from the set
	{
		std::uniform_int_distribution<input_size_t> uniform_generator(0, data.size() - 1);
		means.push_back(data[uniform_generator(rand_engine)]);
	}

	for (uint32_t count = 1; count < k; ++count) {
		// Calculate the distance to the closest mean for each data point
		auto distances = details::closest_distance(means, data, k);
		// Pick a random point weighted by the distance from existing means
		std::discrete_distribution<input_size_t> generator(distances.begin(), distances.end());
		means.push_back(data[generator(rand_engine)]);
	}
	return means;
}

/*
Calculate the index of the mean a particular data point is closest to (euclidean distance)
*/
template <typename T, size_t N>
uint32_t closest_mean(const std::array<T, N>& point, const std::vector<std::array<T, N>>& means) {
	assert(!means.empty());
	auto calculate_distance = [](const std::array<T, N>& point, const std::array<T, N>& mean) {
		T distance = T();
		for (size_t i = 0; i < point.size(); ++i) {
			T difference = point[i] - mean[i];
			distance += difference * difference;
		}
		return distance;
	};
	T smallest_distance = calculate_distance(point, means[0]);
	typename std::array<T, N>::size_type index = 0;
	T distance;
	for (size_t i = 1; i < means.size(); ++i) {
		distance = calculate_distance(point, means[i]);
		if (distance < smallest_distance) {
			smallest_distance = distance;
			index = i;
		}
	}
	return index;
}

/*
Calculate the index of the mean each data point is closest to (euclidean distance).
*/
template <typename T, size_t N>
std::vector<uint32_t> calculate_clusters(
	const std::vector<std::array<T, N>>& data, const std::vector<std::array<T, N>>& means) {
	std::vector<uint32_t> clusters;
	for (auto& point : data) {
		clusters.push_back(closest_mean(point, means));
	}
	return clusters;
}

/*
Calculate means based on data points and their cluster assignments.
*/
template <typename T, size_t N>
std::vector<std::array<T, N>> calculate_means(const std::vector<std::array<T, N>>& data,
	const std::vector<uint32_t>& clusters,
	const std::vector<std::array<T, N>>& old_means,
	uint32_t k) {
	std::vector<std::array<T, N>> means(k);
	std::vector<T> count(k, T());
	for (size_t i = 0; i < std::min(clusters.size(), data.size()); ++i) {
		auto& mean = means[clusters[i]];
		count[clusters[i]] += 1;
		for (size_t j = 0; j < std::min(data[i].size(), mean.size()); ++j) {
			mean[j] += data[i][j];
		}
	}
	for (size_t i = 0; i < k; ++i) {
		if (count[i] == 0) {
			means[i] = old_means[i];
		} else {
			for (size_t j = 0; j < means[i].size(); ++j) {
				means[i][j] /= count[i];
			}
		}
	}
	return means;
}

} // namespace details

/*
Implementation of k-means generic across the data type and the dimension of each data item. Expects
the data to be a vector of fixed-size arrays. Generic parameters are the type of the base data (T)
and the dimensionality of each data point (N). All points must have the same dimensionality.

e.g. points of the form (X, Y, Z) would be N = 3.

Returns a std::tuple containing:
  0: A vector holding the means for each cluster from 0 to k-1.
  1: A vector containing the cluster number (0 to k-1) for each corresponding element of the input
	 data vector.

Implementation details:
This implementation of k-means uses [Lloyd's Algorithm](https://en.wikipedia.org/wiki/Lloyd%27s_algorithm)
with the [kmeans++](https://en.wikipedia.org/wiki/K-means%2B%2B)
used for initializing the means.
*/
template <typename T, size_t N>
std::tuple<std::vector<std::array<T, N>>, std::vector<uint32_t>> kmeans_lloyd(
	const std::vector<std::array<T, N>>& data, uint32_t k) {
	static_assert(std::is_arithmetic<T>::value && std::is_signed<T>::value,
		"kmeans_lloyd requires the template parameter T to be a signed arithmetic type (e.g. float, double, int)");
	assert(k > 0); // k must be greater than zero
	assert(data.size() >= k); // there must be at least k data points
	std::vector<std::array<T, N>> means = details::random_plusplus(data, k);

	std::vector<std::array<T, N>> old_means;
	std::vector<uint32_t> clusters;
	// Calculate new means until convergence is reached
	int count = 0;
	do {
		clusters = details::calculate_clusters(data, means);
		old_means = means;
		means = details::calculate_means(data, clusters, old_means, k);
		++count;
	} while (means != old_means);

	return std::tuple<std::vector<std::array<T, N>>, std::vector<uint32_t>>(means, clusters);
}

} // namespace dkm

#endif /* DKM_KMEANS_H */
<commit_msg>Removed some silly duplicated code.<commit_after>#pragma once

// only included in case there's a C++11 compiler out there that doesn't support `#pragma once`
#ifndef DKM_KMEANS_H
#define DKM_KMEANS_H

#include <vector>
#include <array>
#include <tuple>
#include <cstddef>
#include <cstdint>
#include <random>
#include <type_traits>
#include <cassert>

/*
DKM - A k-means implementation that is generic across variable data dimensions.
*/
namespace dkm {

/*
These functions are all private implementation details and shouldn't be referenced outside of this
file.
*/
namespace details {

/*
Calculate the square of the distance between two points.
*/
template <typename T, size_t N>
T distance_squared(const std::array<T, N>& point_a, const std::array<T, N>& point_b) {
	T d_squared = T();
	for (typename std::array<T, N>::size_type i = 0; i < N; ++i) {
		auto delta = point_a[i] - point_b[i];
		d_squared += delta * delta;
	}
	return d_squared;
}

/*
Calculate the smallest distance between each of the data points and any of the input means.
*/
template <typename T, size_t N>
std::vector<T> closest_distance(
	const std::vector<std::array<T, N>>& means, const std::vector<std::array<T, N>>& data, uint32_t k) {
	std::vector<T> distances;
	distances.reserve(k);
	for (auto& d : data) {
		T closest = distance_squared(d, means[0]);
		for (auto& m : means) {
			T distance = distance_squared(d, m);
			if (distance < closest)
				closest = distance;
		}
		distances.push_back(closest);
	}
	return distances;
}

/*
This is an alternate initialization method based on the [kmeans++](https://en.wikipedia.org/wiki/K-means%2B%2B)
initialization algorithm.
*/
template <typename T, size_t N>
std::vector<std::array<T, N>> random_plusplus(const std::vector<std::array<T, N>>& data, uint32_t k) {
	assert(k > 0);
	using input_size_t = typename std::array<T, N>::size_type;
	std::vector<std::array<T, N>> means;
	// Using a very simple PRBS generator, parameters selected according to
	// https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
	std::random_device rand_device;
	std::linear_congruential_engine<uint64_t, 6364136223846793005, 1442695040888963407, UINT64_MAX> rand_engine(
		rand_device());

	// Select first mean at random from the set
	{
		std::uniform_int_distribution<input_size_t> uniform_generator(0, data.size() - 1);
		means.push_back(data[uniform_generator(rand_engine)]);
	}

	for (uint32_t count = 1; count < k; ++count) {
		// Calculate the distance to the closest mean for each data point
		auto distances = details::closest_distance(means, data, k);
		// Pick a random point weighted by the distance from existing means
		std::discrete_distribution<input_size_t> generator(distances.begin(), distances.end());
		means.push_back(data[generator(rand_engine)]);
	}
	return means;
}

/*
Calculate the index of the mean a particular data point is closest to (euclidean distance)
*/
template <typename T, size_t N>
uint32_t closest_mean(const std::array<T, N>& point, const std::vector<std::array<T, N>>& means) {
	assert(!means.empty());
	T smallest_distance = distance_squared(point, means[0]);
	typename std::array<T, N>::size_type index = 0;
	T distance;
	for (size_t i = 1; i < means.size(); ++i) {
		distance = distance_squared(point, means[i]);
		if (distance < smallest_distance) {
			smallest_distance = distance;
			index = i;
		}
	}
	return index;
}

/*
Calculate the index of the mean each data point is closest to (euclidean distance).
*/
template <typename T, size_t N>
std::vector<uint32_t> calculate_clusters(
	const std::vector<std::array<T, N>>& data, const std::vector<std::array<T, N>>& means) {
	std::vector<uint32_t> clusters;
	for (auto& point : data) {
		clusters.push_back(closest_mean(point, means));
	}
	return clusters;
}

/*
Calculate means based on data points and their cluster assignments.
*/
template <typename T, size_t N>
std::vector<std::array<T, N>> calculate_means(const std::vector<std::array<T, N>>& data,
	const std::vector<uint32_t>& clusters,
	const std::vector<std::array<T, N>>& old_means,
	uint32_t k) {
	std::vector<std::array<T, N>> means(k);
	std::vector<T> count(k, T());
	for (size_t i = 0; i < std::min(clusters.size(), data.size()); ++i) {
		auto& mean = means[clusters[i]];
		count[clusters[i]] += 1;
		for (size_t j = 0; j < std::min(data[i].size(), mean.size()); ++j) {
			mean[j] += data[i][j];
		}
	}
	for (size_t i = 0; i < k; ++i) {
		if (count[i] == 0) {
			means[i] = old_means[i];
		} else {
			for (size_t j = 0; j < means[i].size(); ++j) {
				means[i][j] /= count[i];
			}
		}
	}
	return means;
}

} // namespace details

/*
Implementation of k-means generic across the data type and the dimension of each data item. Expects
the data to be a vector of fixed-size arrays. Generic parameters are the type of the base data (T)
and the dimensionality of each data point (N). All points must have the same dimensionality.

e.g. points of the form (X, Y, Z) would be N = 3.

Returns a std::tuple containing:
  0: A vector holding the means for each cluster from 0 to k-1.
  1: A vector containing the cluster number (0 to k-1) for each corresponding element of the input
	 data vector.

Implementation details:
This implementation of k-means uses [Lloyd's Algorithm](https://en.wikipedia.org/wiki/Lloyd%27s_algorithm)
with the [kmeans++](https://en.wikipedia.org/wiki/K-means%2B%2B)
used for initializing the means.
*/
template <typename T, size_t N>
std::tuple<std::vector<std::array<T, N>>, std::vector<uint32_t>> kmeans_lloyd(
	const std::vector<std::array<T, N>>& data, uint32_t k) {
	static_assert(std::is_arithmetic<T>::value && std::is_signed<T>::value,
		"kmeans_lloyd requires the template parameter T to be a signed arithmetic type (e.g. float, double, int)");
	assert(k > 0); // k must be greater than zero
	assert(data.size() >= k); // there must be at least k data points
	std::vector<std::array<T, N>> means = details::random_plusplus(data, k);

	std::vector<std::array<T, N>> old_means;
	std::vector<uint32_t> clusters;
	// Calculate new means until convergence is reached
	int count = 0;
	do {
		clusters = details::calculate_clusters(data, means);
		old_means = means;
		means = details::calculate_means(data, clusters, old_means, k);
		++count;
	} while (means != old_means);

	return std::tuple<std::vector<std::array<T, N>>, std::vector<uint32_t>>(means, clusters);
}

} // namespace dkm

#endif /* DKM_KMEANS_H */
<|endoftext|>