<commit_before>/* Copyright (C) 2000 MySQL AB

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

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/*
  UNION  of select's
  UNION's  were introduced by Monty and Sinisa <sinisa@mysql.com>
*/

#include "mysql_priv.h"
#include "sql_select.h"

int mysql_union(THD *thd, LEX *lex,select_result *result)
{
  SELECT_LEX *sl, *last_sl, *lex_sl;
  ORDER *order;
  List<Item> item_list;
  TABLE *table;
  int describe=(lex->select_lex.options & SELECT_DESCRIBE) ? 1 : 0;
  int res, add_rows=0;
  bool found_rows_for_union= lex->select_lex.options & OPTION_FOUND_ROWS;
  TABLE_LIST result_table_list;
  TABLE_LIST *first_table=(TABLE_LIST *)lex->select_lex.table_list.first;
  TMP_TABLE_PARAM tmp_table_param;
  select_union *union_result;
  DBUG_ENTER("mysql_union");

  /* Fix tables 'to-be-unioned-from' list to point at opened tables */
  last_sl= &lex->select_lex;
  for (sl= last_sl;
       sl && sl->linkage != NOT_A_SELECT;
       last_sl=sl, sl=sl->next)
  {
    for (TABLE_LIST *cursor= (TABLE_LIST *)sl->table_list.first;
	 cursor;
	 cursor=cursor->next)
    {
      cursor->table= (my_reinterpret_cast(TABLE_LIST*) (cursor->table))->table;
    }
  }

  /* last_sel now points at the last select where the ORDER BY is stored */
  if (sl)
  {
    /*
      The found SL is an extra SELECT_LEX argument that contains
      the ORDER BY and LIMIT parameter for the whole UNION
    */
    lex_sl= sl;
    order=  (ORDER *) lex_sl->order_list.first;
    // This is done to eliminate unnecessary slowing down of the first query
    if (!order || !describe)
      last_sl->next=0;				// Remove this extra element
  }
  else if (!last_sl->braces)
  {
    lex_sl= last_sl;				// ORDER BY is here
    order=  (ORDER *) lex_sl->order_list.first;
  }
  else
  {
    lex_sl=0;
    order=0;
  }

  if (describe)
  {
    Item *item;
    item_list.push_back(new Item_empty_string("table",NAME_LEN));
    item_list.push_back(new Item_empty_string("type",10));
    item_list.push_back(item=new Item_empty_string("possible_keys",
						  NAME_LEN*MAX_KEY));
    item->maybe_null=1;
    item_list.push_back(item=new Item_empty_string("key",NAME_LEN));
    item->maybe_null=1;
    item_list.push_back(item=new Item_int("key_len",0,3));
    item->maybe_null=1;
    item_list.push_back(item=new Item_empty_string("ref",
						    NAME_LEN*MAX_REF_PARTS));
    item->maybe_null=1;
    item_list.push_back(new Item_real("rows",0.0,0,10));
    item_list.push_back(new Item_empty_string("Extra",255));
  }
  else
  {
    Item *item;
    List_iterator<Item> it(lex->select_lex.item_list);
    TABLE_LIST *first_table= (TABLE_LIST*) lex->select_lex.table_list.first;

    /* Create a list of items that will be in the result set */
    while ((item= it++))
      if (item_list.push_back(item))
	DBUG_RETURN(-1);
    if (setup_tables(first_table) ||
	setup_fields(thd,first_table,item_list,0,0,1))
      DBUG_RETURN(-1);
  }

  bzero((char*) &tmp_table_param,sizeof(tmp_table_param));
  tmp_table_param.field_count=item_list.elements;
  if (!(table=create_tmp_table(thd, &tmp_table_param, item_list,
			       (ORDER*) 0, !describe & !lex->union_option,
			       1, 0,
			       (lex->select_lex.options | thd->options |
				TMP_TABLE_ALL_COLUMNS))))
    DBUG_RETURN(-1);
  table->file->extra(HA_EXTRA_WRITE_CACHE);
  table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
  bzero((char*) &result_table_list,sizeof(result_table_list));
  result_table_list.db= (char*) "";
  result_table_list.real_name=result_table_list.alias= (char*) "union";
  result_table_list.table=table;

  if (!(union_result=new select_union(table)))
  {
    res= -1;
    goto exit;
  }
  union_result->not_describe= !describe;
  union_result->tmp_table_param=&tmp_table_param;
  for (sl= &lex->select_lex; sl; sl=sl->next)
  {
    unsigned int rows;
    lex->select=sl;
    thd->offset_limit=sl->offset_limit;
    thd->select_limit=sl->select_limit+sl->offset_limit;
    if (thd->select_limit < sl->select_limit)
      thd->select_limit= HA_POS_ERROR;		// no limit
    if (thd->select_limit == HA_POS_ERROR || sl->braces)
      sl->options&= ~OPTION_FOUND_ROWS;
    else if (found_rows_for_union)
    {
      rows= thd->select_limit;
      sl->options|= OPTION_FOUND_ROWS;
    }

    res=mysql_select(thd, (describe && sl->linkage==NOT_A_SELECT) ?
		     first_table :  (TABLE_LIST*) sl->table_list.first,
		     sl->item_list,
		     sl->where,
		     (sl->braces) ? (ORDER *)sl->order_list.first :
		     (ORDER *) 0,
		     (ORDER*) sl->group_list.first,
		     sl->having,
		     (ORDER*) NULL,
		     sl->options | thd->options | SELECT_NO_UNLOCK |
		     ((describe) ? SELECT_DESCRIBE : 0),
		     union_result);
    if (found_rows_for_union  && !sl->braces && sl->options & OPTION_FOUND_ROWS)
      add_rows+= (thd->limit_found_rows > rows) ?  thd->limit_found_rows - rows : 0;
    if (res)
      goto exit;
  }
  if (union_result->flush())
  {
    res= 1;					// Error is already sent
    goto exit;
  }
  delete union_result;

  /* Send result to 'result' */
  lex->select = &lex->select_lex;
  res =-1;
  {
    /* Create a list of fields in the temporary table */
    List_iterator<Item> it(item_list);
    Field **field;
#if 0
    List<Item_func_match> ftfunc_list;
    ftfunc_list.empty();
#else
    thd->lex.select_lex.ftfunc_list.empty();
#endif

    for (field=table->field ; *field ; field++)
    {
      (void) it++;
      (void) it.replace(new Item_field(*field));
    }
    if (!thd->fatal_error)			// Check if EOM
    {
      if (lex_sl)
      {
	thd->offset_limit=lex_sl->offset_limit;
	thd->select_limit=lex_sl->select_limit+lex_sl->offset_limit;
	if (thd->select_limit < lex_sl->select_limit)
	  thd->select_limit= HA_POS_ERROR;		// no limit
	if (thd->select_limit == HA_POS_ERROR)
	  thd->options&= ~OPTION_FOUND_ROWS;
      }
      else
      {
	thd->offset_limit= 0;
	thd->select_limit= thd->variables.select_limit;
	if (found_rows_for_union && !describe)
	  thd->options|= OPTION_FOUND_ROWS;
      }
      if (describe)
	thd->select_limit= HA_POS_ERROR;		// no limit

      res=mysql_select(thd,&result_table_list,
		       item_list, NULL, (describe) ? 0 : order,
		       (ORDER*) NULL, NULL, (ORDER*) NULL,
		       thd->options, result);
      if (found_rows_for_union && !res)
      {
	thd->limit_found_rows= table->file->records;
	if (!last_sl->braces)
	  thd->limit_found_rows+= add_rows;
      }
    }
  }

exit:
  free_tmp_table(thd,table);
  DBUG_RETURN(res);
}

/***************************************************************************
** store records in temporary table for UNION
***************************************************************************/

select_union::select_union(TABLE *table_par)
  :table(table_par), not_describe(0)
{
  bzero((char*) &info,sizeof(info));
  /*
    We can always use DUP_IGNORE because the temporary table will only
    contain a unique key if we are using not using UNION ALL
  */
  info.handle_duplicates=DUP_IGNORE;
}

select_union::~select_union()
{
}

int select_union::prepare(List<Item> &list)
{
  if (not_describe && list.elements != table->fields)
  {
    my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
	       ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
    return -1;
  }
  return 0;
}

bool select_union::send_data(List<Item> &values)
{
  if (thd->offset_limit)
  {						// using limit offset,count
    thd->offset_limit--;
    return 0;
  }

  fill_record(table->field, values, 1);
  if ((write_record(table,&info)))
  {
    if (create_myisam_from_heap(thd, table, tmp_table_param, info.last_errno,
				1))
      return 1;
  }
  return 0;
}

bool select_union::send_eof()
{
  return 0;
}

bool select_union::flush()
{
  int error;
  if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
  {
    table->file->print_error(error,MYF(0));
    ::send_error(&thd->net);
    return 1;
  }
  return 0;
}
<commit_msg>sql_union.cc:   Merge fix<commit_after>/* Copyright (C) 2000 MySQL AB

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

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/*
  UNION  of select's
  UNION's  were introduced by Monty and Sinisa <sinisa@mysql.com>
*/

#include "mysql_priv.h"
#include "sql_select.h"

int mysql_union(THD *thd, LEX *lex,select_result *result)
{
  SELECT_LEX *sl, *last_sl, *lex_sl;
  ORDER *order;
  List<Item> item_list;
  TABLE *table;
  int res;
  ulonglong add_rows= 0;
  ulong found_rows_for_union= lex->select_lex.options & OPTION_FOUND_ROWS;
  ulong describe= lex->select_lex.options & SELECT_DESCRIBE;
  TABLE_LIST result_table_list;
  TABLE_LIST *first_table=(TABLE_LIST *)lex->select_lex.table_list.first;
  TMP_TABLE_PARAM tmp_table_param;
  select_union *union_result;
  DBUG_ENTER("mysql_union");

  /* Fix tables 'to-be-unioned-from' list to point at opened tables */
  last_sl= &lex->select_lex;
  for (sl= last_sl;
       sl && sl->linkage != NOT_A_SELECT;
       last_sl=sl, sl=sl->next)
  {
    for (TABLE_LIST *cursor= (TABLE_LIST *)sl->table_list.first;
	 cursor;
	 cursor=cursor->next)
    {
      cursor->table= (my_reinterpret_cast(TABLE_LIST*) (cursor->table))->table;
    }
  }

  /* last_sel now points at the last select where the ORDER BY is stored */
  if (sl)
  {
    /*
      The found SL is an extra SELECT_LEX argument that contains
      the ORDER BY and LIMIT parameter for the whole UNION
    */
    lex_sl= sl;
    order=  (ORDER *) lex_sl->order_list.first;
    // This is done to eliminate unnecessary slowing down of the first query
    if (!order || !describe)
      last_sl->next=0;				// Remove this extra element
  }
  else if (!last_sl->braces)
  {
    lex_sl= last_sl;				// ORDER BY is here
    order=  (ORDER *) lex_sl->order_list.first;
  }
  else
  {
    lex_sl=0;
    order=0;
  }

  if (describe)
  {
    Item *item;
    item_list.push_back(new Item_empty_string("table",NAME_LEN));
    item_list.push_back(new Item_empty_string("type",10));
    item_list.push_back(item=new Item_empty_string("possible_keys",
						  NAME_LEN*MAX_KEY));
    item->maybe_null=1;
    item_list.push_back(item=new Item_empty_string("key",NAME_LEN));
    item->maybe_null=1;
    item_list.push_back(item=new Item_int("key_len",0,3));
    item->maybe_null=1;
    item_list.push_back(item=new Item_empty_string("ref",
						    NAME_LEN*MAX_REF_PARTS));
    item->maybe_null=1;
    item_list.push_back(new Item_real("rows",0.0,0,10));
    item_list.push_back(new Item_empty_string("Extra",255));
  }
  else
  {
    Item *item;
    List_iterator<Item> it(lex->select_lex.item_list);
    TABLE_LIST *first_table= (TABLE_LIST*) lex->select_lex.table_list.first;

    /* Create a list of items that will be in the result set */
    while ((item= it++))
      if (item_list.push_back(item))
	DBUG_RETURN(-1);
    if (setup_tables(first_table) ||
	setup_fields(thd,first_table,item_list,0,0,1))
      DBUG_RETURN(-1);
  }

  bzero((char*) &tmp_table_param,sizeof(tmp_table_param));
  tmp_table_param.field_count=item_list.elements;
  if (!(table=create_tmp_table(thd, &tmp_table_param, item_list,
			       (ORDER*) 0, !describe & !lex->union_option,
			       1, 0,
			       (lex->select_lex.options | thd->options |
				TMP_TABLE_ALL_COLUMNS))))
    DBUG_RETURN(-1);
  table->file->extra(HA_EXTRA_WRITE_CACHE);
  table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
  bzero((char*) &result_table_list,sizeof(result_table_list));
  result_table_list.db= (char*) "";
  result_table_list.real_name=result_table_list.alias= (char*) "union";
  result_table_list.table=table;

  if (!(union_result=new select_union(table)))
  {
    res= -1;
    goto exit;
  }
  union_result->not_describe= !describe;
  union_result->tmp_table_param=&tmp_table_param;
  for (sl= &lex->select_lex; sl; sl=sl->next)
  {
    ha_rows records_at_start;
    lex->select=sl;
    /* Don't use offset for the last union if there is no braces */
    thd->offset_limit= sl != lex_sl ? sl->offset_limit : 0;
    thd->select_limit=sl->select_limit+sl->offset_limit;
    if (thd->select_limit < sl->select_limit)
      thd->select_limit= HA_POS_ERROR;		// no limit
    /*
      When using braces, SQL_CALC_FOUND_ROWS affects the whole query.
      We don't calculate found_rows() per union part
    */
    if (thd->select_limit == HA_POS_ERROR || sl->braces)
      sl->options&= ~OPTION_FOUND_ROWS;
    else
    {
      /*
 	We are doing an union without braces.  In this case
 	SQL_CALC_FOUND_ROWS should be done on all sub parts
      */
      sl->options|= found_rows_for_union;
    }

    records_at_start= table->file->records;

    res=mysql_select(thd, (describe && sl->linkage==NOT_A_SELECT) ?
		     first_table :  (TABLE_LIST*) sl->table_list.first,
		     sl->item_list,
		     sl->where,
		     (sl->braces) ? (ORDER *)sl->order_list.first :
		     (ORDER *) 0,
		     (ORDER*) sl->group_list.first,
		     sl->having,
		     (ORDER*) NULL,
		     sl->options | thd->options | SELECT_NO_UNLOCK |
		     describe,
		     union_result);
    if (res)
      goto exit;
    /* Needed for the following test and for records_at_start in next loop */
    table->file->info(HA_STATUS_VARIABLE);
    if (found_rows_for_union & sl->options)
    {
      /*
 	This is a union without braces. Remember the number of rows that could
 	also have been part of the result set.
 	We get this from the difference of between total number of possible
 	rows and actual rows added to the temporary table.
      */
      add_rows+= (ulonglong) (thd->limit_found_rows - (table->file->records -
 						       records_at_start));
    }
  }
  if (union_result->flush())
  {
    res= 1;					// Error is already sent
    goto exit;
  }
  delete union_result;

  /* Send result to 'result' */
  lex->select = &lex->select_lex;
  res =-1;
  {
    /* Create a list of fields in the temporary table */
    List_iterator<Item> it(item_list);
    Field **field;
#if 0
    List<Item_func_match> ftfunc_list;
    ftfunc_list.empty();
#else
    thd->lex.select_lex.ftfunc_list.empty();
#endif

    for (field=table->field ; *field ; field++)
    {
      (void) it++;
      (void) it.replace(new Item_field(*field));
    }
    if (!thd->fatal_error)			// Check if EOM
    {
      if (lex_sl)
      {
	thd->offset_limit=lex_sl->offset_limit;
	thd->select_limit=lex_sl->select_limit+lex_sl->offset_limit;
	if (thd->select_limit < lex_sl->select_limit)
	  thd->select_limit= HA_POS_ERROR;		// no limit
	if (thd->select_limit == HA_POS_ERROR)
	  thd->options&= ~OPTION_FOUND_ROWS;
      }
      else
      {
	thd->offset_limit= 0;
	thd->select_limit= thd->variables.select_limit;
	if (found_rows_for_union && !describe)
	  thd->options|= OPTION_FOUND_ROWS;
      }
      if (describe)
	thd->select_limit= HA_POS_ERROR;		// no limit

      res=mysql_select(thd,&result_table_list,
		       item_list, NULL, (describe) ? 0 : order,
		       (ORDER*) NULL, NULL, (ORDER*) NULL,
		       thd->options, result);
      if (!res)
 	thd->limit_found_rows = (ulonglong)table->file->records + add_rows;
    }
  }

exit:
  free_tmp_table(thd,table);
  DBUG_RETURN(res);
}

/***************************************************************************
** store records in temporary table for UNION
***************************************************************************/

select_union::select_union(TABLE *table_par)
  :table(table_par), not_describe(0)
{
  bzero((char*) &info,sizeof(info));
  /*
    We can always use DUP_IGNORE because the temporary table will only
    contain a unique key if we are using not using UNION ALL
  */
  info.handle_duplicates=DUP_IGNORE;
}

select_union::~select_union()
{
}

int select_union::prepare(List<Item> &list)
{
  if (not_describe && list.elements != table->fields)
  {
    my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
	       ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
    return -1;
  }
  return 0;
}

bool select_union::send_data(List<Item> &values)
{
  if (thd->offset_limit)
  {						// using limit offset,count
    thd->offset_limit--;
    return 0;
  }

  fill_record(table->field, values, 1);
  if ((write_record(table,&info)))
  {
    if (create_myisam_from_heap(thd, table, tmp_table_param, info.last_errno,
				1))
      return 1;
  }
  return 0;
}

bool select_union::send_eof()
{
  return 0;
}

bool select_union::flush()
{
  int error;
  if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
  {
    table->file->print_error(error,MYF(0));
    ::send_error(&thd->net);
    return 1;
  }
  return 0;
}
<|endoftext|>


<commit_before>#include "genomeParametersWrite.h"
#include "streamFuns.h"

void genomeParametersWrite(string fileName, Parameters& P, string errorOut)
{//write the genome information into the genomePar stream
    ofstream & genomePar = ofstrOpen(fileName, errorOut, P);

    genomePar << "### "<<P.commandLineFull <<"\n";

    genomePar << "versionGenome\t" << P.versionSTAR <<"\n";
    genomePar << "pGe.gFastaFiles\t";
    for (uint ii=0;ii<P.pGe.gFastaFiles.size();ii++) genomePar << P.pGe.gFastaFiles.at(ii) << " ";
    genomePar << "\n";
    genomePar << "pGe.gSAindexNbases\t" << P.pGe.gSAindexNbases << "\n";
    genomePar << "pGe.gChrBinNbits\t" << P.pGe.gChrBinNbits << "\n";
    genomePar << "pGe.gSAsparseD\t" << P.pGe.gSAsparseD <<"\n";
    genomePar << "pGe.sjdbOverhang\t" << P.pGe.sjdbOverhang <<"\n";

    genomePar << "pGe.sjdbFileChrStartEnd\t";
    for (uint ii=0;ii<P.pGe.sjdbFileChrStartEnd.size();ii++) genomePar<< P.pGe.sjdbFileChrStartEnd.at(ii) << " ";
    genomePar<<"\n";

    genomePar << "pGe.sjdbGTFfile\t" << P.pGe.sjdbGTFfile <<"\n";
    genomePar << "pGe.sjdbGTFchrPrefix\t" << P.pGe.sjdbGTFchrPrefix <<"\n";
    genomePar << "pGe.sjdbGTFfeatureExon\t" << P.pGe.sjdbGTFfeatureExon <<"\n";
    genomePar << "pGe.sjdbGTFtagExonParentTranscript\t" << P.pGe.sjdbGTFtagExonParentTranscript <<"\n";
    genomePar << "pGe.sjdbGTFtagExonParentGene\t" << P.pGe.sjdbGTFtagExonParentGene <<"\n";

    genomePar << "pGe.sjdbInsertSave\t" << P.pGe.sjdbInsertSave <<"\n";

    genomePar << "pGe.gFileSizes\t" << P.pGe.gFileSizes.at(0);
    for (uint ii=1;ii<P.pGe.gFileSizes.size();ii++)
        genomePar << " " << P.pGe.gFileSizes.at(ii) ;
    genomePar << "\n";

    genomePar.close();
};
<commit_msg>Added pGe to Genome class. Compiles and runs OK.<commit_after>#include "genomeParametersWrite.h"
#include "streamFuns.h"

void genomeParametersWrite(string fileName, Parameters& P, string errorOut)
{//write the genome information into the genomePar stream
    ofstream & genomePar = ofstrOpen(fileName, errorOut, P);

    genomePar << "### "<<P.commandLineFull <<"\n";

    genomePar << "versionGenome\t" << P.versionSTAR <<"\n";
    genomePar << "genomeFastaFiles\t";
    for (uint ii=0;ii<P.pGe.gFastaFiles.size();ii++) genomePar << P.pGe.gFastaFiles.at(ii) << " ";
    genomePar << "\n";
    genomePar << "genomeSAindexNbases\t" << P.pGe.gSAindexNbases << "\n";
    genomePar << "genomeChrBinNbits\t" << P.pGe.gChrBinNbits << "\n";
    genomePar << "genomeSAsparseD\t" << P.pGe.gSAsparseD <<"\n";
    genomePar << "sjdbOverhang\t" << P.pGe.sjdbOverhang <<"\n";

    genomePar << "sjdbFileChrStartEnd\t";
    for (uint ii=0;ii<P.pGe.sjdbFileChrStartEnd.size();ii++) genomePar<< P.pGe.sjdbFileChrStartEnd.at(ii) << " ";
    genomePar<<"\n";

    genomePar << "sjdbGTFfile\t" << P.pGe.sjdbGTFfile <<"\n";
    genomePar << "sjdbGTFchrPrefix\t" << P.pGe.sjdbGTFchrPrefix <<"\n";
    genomePar << "sjdbGTFfeatureExon\t" << P.pGe.sjdbGTFfeatureExon <<"\n";
    genomePar << "sjdbGTFtagExonParentTranscript\t" << P.pGe.sjdbGTFtagExonParentTranscript <<"\n";
    genomePar << "sjdbGTFtagExonParentGene\t" << P.pGe.sjdbGTFtagExonParentGene <<"\n";

    genomePar << "sjdbInsertSave\t" << P.pGe.sjdbInsertSave <<"\n";

    genomePar << "genomeFileSizes\t" << P.pGe.gFileSizes.at(0);
    for (uint ii=1;ii<P.pGe.gFileSizes.size();ii++)
        genomePar << " " << P.pGe.gFileSizes.at(ii) ;
    genomePar << "\n";

    genomePar.close();
};
<|endoftext|>


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

#include <openspace/performance/performancemanager.h>

#include <openspace/scene/scenegraphnode.h>
#include <openspace/performance/performancelayout.h>

#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/sharedmemory.h>
#include <ghoul/misc/onscopeexit.h>
#include <ghoul/filesystem/filesystem.h>

#include <algorithm>
#include <cstring>
#include <iostream>
#include <fstream>

namespace {
    const char* _loggerCat = "PerformanceManager";

    const char* GlobalSharedMemoryName = "OpenSpacePerformanceMeasurementData";
    // Probably 255 performance blocks per node are enough, so we can get away with
    // 4 bytes (one uint8_t for the number, one uint8_t for the reference count to keep
    // the global memory alive, and 2 bytes to enforce alignment)
    const size_t GlobalSharedMemorySize = 4;

    struct GlobalMemory {
        uint8_t number;
        uint8_t referenceCount;

        std::array<uint8_t, 2> alignment;
    };

    const char* LocalSharedMemoryNameBase = "PerformanceMeasurement_";
}

namespace openspace {
namespace performance {

// The Performance Manager will use a level of indirection in order to support multiple
// PerformanceManagers running in parallel:
// The ghoul::SharedData block addressed by OpenSpacePerformanceMeasurementSharedData
// will only get allocated once and contains the total number of allocated shared memory
// blocks alongside a list of names of these blocks
//

void PerformanceManager::createGlobalSharedMemory() {
    static_assert(
        sizeof(GlobalMemory) == GlobalSharedMemorySize,
        "The global memory struct does not fit the allocated global memory space"
    );

    using ghoul::SharedMemory;

    if (SharedMemory::exists(GlobalSharedMemoryName)) {
        SharedMemory sharedMemory(GlobalSharedMemoryName);
        sharedMemory.acquireLock();
        GlobalMemory* m = reinterpret_cast<GlobalMemory*>(sharedMemory.memory());
        ++(m->referenceCount);
        LINFO(
            "Using global shared memory block for performance measurements. "
            "Reference count: " << int(m->referenceCount)
        );
        sharedMemory.releaseLock();
    }
    else {
        LINFO("Creating global shared memory block for performance measurements");
        SharedMemory::create(GlobalSharedMemoryName, GlobalSharedMemorySize);

        // Initialize the data
        SharedMemory sharedMemory(GlobalSharedMemoryName);
        sharedMemory.acquireLock();
        new (sharedMemory.memory()) GlobalMemory;
        GlobalMemory* m = reinterpret_cast<GlobalMemory*>(sharedMemory.memory());
        m->number = 0;
        m->referenceCount = 1;
        sharedMemory.releaseLock();
    }
}

void PerformanceManager::destroyGlobalSharedMemory() {
    using ghoul::SharedMemory;
    if (!SharedMemory::exists(GlobalSharedMemoryName)) {
        LWARNING("Global shared memory for Performance measurements did not exist");
        return;
    }

    SharedMemory sharedMemory(GlobalSharedMemoryName);
    sharedMemory.acquireLock();
    GlobalMemory* m = reinterpret_cast<GlobalMemory*>(sharedMemory.memory());
    --(m->referenceCount);
    LINFO("Global shared performance memory reference count: " << int(m->referenceCount));
    if (m->referenceCount == 0) {
        LINFO("Removing global shared performance memory");

        // When the global memory is deleted, we have to get rid of all local memory as
        // well. In principle, none should be left, but OpenSpace crashing might leave
        // some of the memory orphaned
        for (int i = 0; i < std::numeric_limits<uint8_t>::max(); ++i) {
            std::string localName = LocalSharedMemoryNameBase + std::to_string(i);
            if (SharedMemory::exists(localName)) {
                LINFO("Removing shared memory: " << localName);
                SharedMemory::remove(localName);
            }
        }

        SharedMemory::remove(GlobalSharedMemoryName);
    }
    sharedMemory.releaseLock();
}

PerformanceManager::PerformanceManager()
    : _performanceMemory(nullptr)
    , _tick(0)
    , _loggingEnabled(false)
    , _logDir(absPath("${BASE_PATH}"))
    , _prefix("PM-")
    , _suffix("")
    , _ext("log")
{
    using ghoul::SharedMemory;
    PerformanceManager::createGlobalSharedMemory();

    SharedMemory sharedMemory(GlobalSharedMemoryName);
    sharedMemory.acquireLock();
    OnExit([&](){sharedMemory.releaseLock();});

    GlobalMemory* m = reinterpret_cast<GlobalMemory*>(sharedMemory.memory());

    // The the first free block (which also coincides with the number of blocks
    uint8_t blockIndex = m->number;
    ++(m->number);

    std::string localName = LocalSharedMemoryNameBase + std::to_string(blockIndex);

    // Compute the total size
    const int totalSize = sizeof(PerformanceLayout);
    LINFO("Create shared memory '" + localName + "' of " << totalSize << " bytes");

    if (SharedMemory::exists(localName)) {
        throw ghoul::RuntimeError(
            "Shared Memory '" + localName + "' block already existed"
        );
    }

    ghoul::SharedMemory::create(localName, totalSize);

    _performanceMemory = std::make_unique<ghoul::SharedMemory>(localName);
    // Using the placement-new to create a PerformanceLayout in the shared memory
    new (_performanceMemory->memory()) PerformanceLayout;
}

PerformanceManager::~PerformanceManager() {
    if (_performanceMemory) {
        ghoul::SharedMemory sharedMemory(GlobalSharedMemoryName);
        sharedMemory.acquireLock();
        GlobalMemory* m = reinterpret_cast<GlobalMemory*>(sharedMemory.memory());
        --(m->number);
        sharedMemory.releaseLock();

        LINFO("Remove shared memory '" << _performanceMemory->name() << "'");
        ghoul::SharedMemory::remove(_performanceMemory->name());

        _performanceMemory = nullptr;

    }

    PerformanceManager::destroyGlobalSharedMemory();
}

void PerformanceManager::resetPerformanceMeasurements() {
    // Using the placement-new to create a PerformanceLayout in the shared memory
    _performanceMemory->acquireLock();
    void* ptr = _performanceMemory->memory();
    new (ptr) PerformanceLayout;
    _performanceMemory->releaseLock();

    individualPerformanceLocations.clear();
}

bool PerformanceManager::isMeasuringPerformance() const {
    return _doPerformanceMeasurements;
}

void PerformanceManager::outputLogs() {

    // Log Layout values
    PerformanceLayout* layout = performanceData();

    // Log function performance
    for (size_t n = 0; n < layout->nFunctionEntries; n++) {
        const auto function = layout->functionEntries[n];
        const std::string filename = formatLogName(function.name);
        std::ofstream out = std::ofstream(absPath(filename));

        // Comma separate data
        for (size_t i = 0; i < PerformanceLayout::NumberValues; i++) {
            const std::vector<float> data = { function.time[i] };
            writeData(out, data);
        }
        out.close();
    }

    // Log scene object performance
    for (size_t n = 0; n < layout->nScaleGraphEntries; n++) {
        const auto node = layout->sceneGraphEntries[n];

        // Open file
        const std::string filename = formatLogName(node.name);
        std::ofstream out = std::ofstream(absPath(filename));

        // Comma separate data
        for (size_t i = 0; i < PerformanceLayout::NumberValues; i++) {
            const std::vector<float> data = {
                node.renderTime[i],
                node.updateRenderable[i],
                node.updateRotation[i],
                node.updateScaling[i],
                node.updateTranslation[i]
            };
            writeData(out, data);
        }
        out.close();
    }
}

void PerformanceManager::writeData(std::ofstream& out, const std::vector<float> data) {
    for (size_t i = 0; i < data.size() - 1; i++) {
        out << data[i] << ",";
    }
    out << data[data.size() - 1] << "\n";
}

const std::string PerformanceManager::formatLogName(std::string nodeName) {
    // Replace any colons with dashes
    std::replace(nodeName.begin(), nodeName.end(), ':', '-');
    return  _logDir + "/" + _prefix + nodeName + _suffix + "." + _ext;
}

void PerformanceManager::logDir(std::string dir) {
    _logDir = absPath(dir);
}

std::string PerformanceManager::logDir() {
    return _logDir;
}

void PerformanceManager::prefix(std::string prefix) {
    _prefix = prefix;
}

std::string PerformanceManager::prefix() {
    return _prefix;
}

void PerformanceManager::enableLogging() {
    setLogging(true);
}

void PerformanceManager::disableLogging() {
    setLogging(false);
}

void PerformanceManager::toggleLogging() {
    _loggingEnabled = !_loggingEnabled;
}
void PerformanceManager::setLogging(bool enabled) {
    _loggingEnabled = enabled;
}

bool PerformanceManager::loggingEnabled() {
    return _loggingEnabled;
}

PerformanceLayout* PerformanceManager::performanceData() {
    void* ptr = _performanceMemory->memory();
    return reinterpret_cast<PerformanceLayout*>(ptr);
}

void PerformanceManager::tick() {
    _tick = (_tick + 1) % PerformanceLayout::NumberValues;
}

void PerformanceManager::storeIndividualPerformanceMeasurement
                                          (std::string identifier, long long microseconds)
{
    PerformanceLayout* layout = performanceData();
    _performanceMemory->acquireLock();

    auto it = individualPerformanceLocations.find(identifier);
    PerformanceLayout::FunctionPerformanceLayout* p = nullptr;
    if (it == individualPerformanceLocations.end()) {
        p = &(layout->functionEntries[layout->nFunctionEntries]);
        individualPerformanceLocations[identifier] = layout->nFunctionEntries;
        ++(layout->nFunctionEntries);
    }
    else {
        p = &(layout->functionEntries[it->second]);
    }
#ifdef _MSC_VER
    strcpy_s(p->name, identifier.length() + 1, identifier.c_str());
#else
    strcpy(p->name, identifier.c_str());
#endif

    std::rotate(
        std::begin(p->time),
        std::next(std::begin(p->time)),
        std::end(p->time)
    );
    p->time[PerformanceLayout::NumberValues - 1] =
        static_cast<float>(microseconds);

    _performanceMemory->releaseLock();
}

void PerformanceManager::storeScenePerformanceMeasurements(
                                           const std::vector<SceneGraphNode*>& sceneNodes)
{
    using namespace performance;

    PerformanceLayout* layout = performanceData();
    _performanceMemory->acquireLock();

    int nNodes = static_cast<int>(sceneNodes.size());
    layout->nScaleGraphEntries = static_cast<int16_t>(nNodes);
    for (int i = 0; i < nNodes; ++i) {
        SceneGraphNode* node = sceneNodes[i];

        memset(layout->sceneGraphEntries[i].name, 0, PerformanceLayout::LengthName);
#ifdef _MSC_VER
        strcpy_s(
            layout->sceneGraphEntries[i].name,
            node->name().length() + 1,
            node->name().c_str()
        );
#else
        strcpy(layout->sceneGraphEntries[i].name, node->name().c_str());
#endif

        SceneGraphNode::PerformanceRecord r = node->performanceRecord();
        PerformanceLayout::SceneGraphPerformanceLayout& entry = layout->sceneGraphEntries[i];

        // Covert nano to microseconds
        const float micro = 1000.f;

        std::rotate(
            std::begin(entry.renderTime),
            std::next(std::begin(entry.renderTime)),
            std::end(entry.renderTime)
        );
        entry.renderTime[PerformanceLayout::NumberValues - 1] = r.renderTime / micro;

        std::rotate(
            std::begin(entry.updateTranslation),
            std::next(std::begin(entry.updateTranslation)),
            std::end(entry.updateTranslation)
        );
        entry.updateTranslation[PerformanceLayout::NumberValues - 1] = r.updateTimeTranslation / micro;

        std::rotate(
            std::begin(entry.updateRotation),
            std::next(std::begin(entry.updateRotation)),
            std::end(entry.updateRotation)
        );
        entry.updateRotation[PerformanceLayout::NumberValues - 1] = r.updateTimeRotation / micro;

        std::rotate(
            std::begin(entry.updateScaling),
            std::next(std::begin(entry.updateScaling)),
            std::end(entry.updateScaling)
        );
        entry.updateScaling[PerformanceLayout::NumberValues - 1] = r.updateTimeScaling / micro;

        std::rotate(
            std::begin(entry.updateRenderable),
            std::next(std::begin(entry.updateRenderable)),
            std::end(entry.updateRenderable)
        );
        entry.updateRenderable[PerformanceLayout::NumberValues - 1] = r.updateTimeRenderable / micro;
    }
    _performanceMemory->releaseLock();

    if (_loggingEnabled && _tick == PerformanceLayout::NumberValues - 1) outputLogs();

    tick();
}

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

#include <openspace/performance/performancemanager.h>

#include <openspace/scene/scenegraphnode.h>
#include <openspace/performance/performancelayout.h>

#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/sharedmemory.h>
#include <ghoul/misc/onscopeexit.h>
#include <ghoul/filesystem/filesystem.h>

#include <algorithm>
#include <cstring>
#include <iostream>
#include <fstream>

namespace {
    const char* _loggerCat = "PerformanceManager";

    const char* GlobalSharedMemoryName = "OpenSpacePerformanceMeasurementData";
    // Probably 255 performance blocks per node are enough, so we can get away with
    // 4 bytes (one uint8_t for the number, one uint8_t for the reference count to keep
    // the global memory alive, and 2 bytes to enforce alignment)
    const size_t GlobalSharedMemorySize = 4;

    struct GlobalMemory {
        uint8_t number;
        uint8_t referenceCount;

        std::array<uint8_t, 2> alignment;
    };

    const char* LocalSharedMemoryNameBase = "PerformanceMeasurement_";
}

namespace openspace {
namespace performance {

// The Performance Manager will use a level of indirection in order to support multiple
// PerformanceManagers running in parallel:
// The ghoul::SharedData block addressed by OpenSpacePerformanceMeasurementSharedData
// will only get allocated once and contains the total number of allocated shared memory
// blocks alongside a list of names of these blocks
//

void PerformanceManager::createGlobalSharedMemory() {
    static_assert(
        sizeof(GlobalMemory) == GlobalSharedMemorySize,
        "The global memory struct does not fit the allocated global memory space"
    );

    using ghoul::SharedMemory;

    if (SharedMemory::exists(GlobalSharedMemoryName)) {
        SharedMemory sharedMemory(GlobalSharedMemoryName);
        sharedMemory.acquireLock();
        GlobalMemory* m = reinterpret_cast<GlobalMemory*>(sharedMemory.memory());
        ++(m->referenceCount);
        LINFO(
            "Using global shared memory block for performance measurements. "
            "Reference count: " << int(m->referenceCount)
        );
        sharedMemory.releaseLock();
    }
    else {
        LINFO("Creating global shared memory block for performance measurements");
        SharedMemory::create(GlobalSharedMemoryName, GlobalSharedMemorySize);

        // Initialize the data
        SharedMemory sharedMemory(GlobalSharedMemoryName);
        sharedMemory.acquireLock();
        new (sharedMemory.memory()) GlobalMemory;
        GlobalMemory* m = reinterpret_cast<GlobalMemory*>(sharedMemory.memory());
        m->number = 0;
        m->referenceCount = 1;
        sharedMemory.releaseLock();
    }
}

void PerformanceManager::destroyGlobalSharedMemory() {
    using ghoul::SharedMemory;
    if (!SharedMemory::exists(GlobalSharedMemoryName)) {
        LWARNING("Global shared memory for Performance measurements did not exist");
        return;
    }

    SharedMemory sharedMemory(GlobalSharedMemoryName);
    sharedMemory.acquireLock();
    GlobalMemory* m = reinterpret_cast<GlobalMemory*>(sharedMemory.memory());
    --(m->referenceCount);
    LINFO("Global shared performance memory reference count: " << int(m->referenceCount));
    if (m->referenceCount == 0) {
        LINFO("Removing global shared performance memory");

        // When the global memory is deleted, we have to get rid of all local memory as
        // well. In principle, none should be left, but OpenSpace crashing might leave
        // some of the memory orphaned
        for (int i = 0; i < std::numeric_limits<uint8_t>::max(); ++i) {
            std::string localName = LocalSharedMemoryNameBase + std::to_string(i);
            if (SharedMemory::exists(localName)) {
                LINFO("Removing shared memory: " << localName);
                SharedMemory::remove(localName);
            }
        }

        SharedMemory::remove(GlobalSharedMemoryName);
    }
    sharedMemory.releaseLock();
}

PerformanceManager::PerformanceManager()
    : _performanceMemory(nullptr)
    , _tick(0)
    , _loggingEnabled(false)
    , _logDir(absPath("${BASE_PATH}"))
    , _prefix("PM-")
    , _suffix("")
    , _ext("log")
{
    using ghoul::SharedMemory;
    PerformanceManager::createGlobalSharedMemory();

    SharedMemory sharedMemory(GlobalSharedMemoryName);
    sharedMemory.acquireLock();
    OnExit([&](){sharedMemory.releaseLock();});

    GlobalMemory* m = reinterpret_cast<GlobalMemory*>(sharedMemory.memory());

    // The the first free block (which also coincides with the number of blocks
    uint8_t blockIndex = m->number;
    ++(m->number);

    std::string localName = LocalSharedMemoryNameBase + std::to_string(blockIndex);

    // Compute the total size
    const int totalSize = sizeof(PerformanceLayout);
    LINFO("Create shared memory '" + localName + "' of " << totalSize << " bytes");

    if (SharedMemory::exists(localName)) {
        throw ghoul::RuntimeError(
            "Shared Memory '" + localName + "' block already existed"
        );
    }

    ghoul::SharedMemory::create(localName, totalSize);

    _performanceMemory = std::make_unique<ghoul::SharedMemory>(localName);
    // Using the placement-new to create a PerformanceLayout in the shared memory
    new (_performanceMemory->memory()) PerformanceLayout;
}

PerformanceManager::~PerformanceManager() {
    if (_performanceMemory) {
        ghoul::SharedMemory sharedMemory(GlobalSharedMemoryName);
        sharedMemory.acquireLock();
        GlobalMemory* m = reinterpret_cast<GlobalMemory*>(sharedMemory.memory());
        --(m->number);
        sharedMemory.releaseLock();

        LINFO("Remove shared memory '" << _performanceMemory->name() << "'");
        ghoul::SharedMemory::remove(_performanceMemory->name());

        _performanceMemory = nullptr;

    }

    PerformanceManager::destroyGlobalSharedMemory();
}

void PerformanceManager::resetPerformanceMeasurements() {
    // Using the placement-new to create a PerformanceLayout in the shared memory
    _performanceMemory->acquireLock();
    void* ptr = _performanceMemory->memory();
    new (ptr) PerformanceLayout;
    _performanceMemory->releaseLock();

    individualPerformanceLocations.clear();
}

bool PerformanceManager::isMeasuringPerformance() const {
    return _doPerformanceMeasurements;
}

void PerformanceManager::outputLogs() {

    // Log Layout values
    PerformanceLayout* layout = performanceData();

    // Log function performance
    for (size_t n = 0; n < layout->nFunctionEntries; n++) {
        const auto function = layout->functionEntries[n];
        const std::string filename = formatLogName(function.name);
        std::ofstream out = std::ofstream(absPath(filename), std::ofstream::out | std::ofstream::app);

        // Comma separate data
        for (size_t i = 0; i < PerformanceLayout::NumberValues; i++) {
            const std::vector<float> data = { function.time[i] };
            writeData(out, data);
        }
        out.close();
    }

    // Log scene object performance
    for (size_t n = 0; n < layout->nScaleGraphEntries; n++) {
        const auto node = layout->sceneGraphEntries[n];

        // Open file
        const std::string filename = formatLogName(node.name);
        std::ofstream out = std::ofstream(absPath(filename), std::ofstream::out | std::ofstream::app);

        // Comma separate data
        for (size_t i = 0; i < PerformanceLayout::NumberValues; i++) {
            const std::vector<float> data = {
                node.renderTime[i],
                node.updateRenderable[i],
                node.updateRotation[i],
                node.updateScaling[i],
                node.updateTranslation[i]
            };
            writeData(out, data);
        }
        out.close();
    }
}

void PerformanceManager::writeData(std::ofstream& out, const std::vector<float> data) {
    for (size_t i = 0; i < data.size() - 1; i++) {
        out << data[i] << ",";
    }
    out << data[data.size() - 1] << "\n";
}

const std::string PerformanceManager::formatLogName(std::string nodeName) {
    // Replace any colons with dashes
    std::replace(nodeName.begin(), nodeName.end(), ':', '-');
    return  _logDir + "/" + _prefix + nodeName + _suffix + "." + _ext;
}

void PerformanceManager::logDir(std::string dir) {
    _logDir = absPath(dir);
}

std::string PerformanceManager::logDir() {
    return _logDir;
}

void PerformanceManager::prefix(std::string prefix) {
    _prefix = prefix;
}

std::string PerformanceManager::prefix() {
    return _prefix;
}

void PerformanceManager::enableLogging() {
    setLogging(true);
}

void PerformanceManager::disableLogging() {
    setLogging(false);
}

void PerformanceManager::toggleLogging() {
    _loggingEnabled = !_loggingEnabled;
}
void PerformanceManager::setLogging(bool enabled) {
    _loggingEnabled = enabled;
}

bool PerformanceManager::loggingEnabled() {
    return _loggingEnabled;
}

PerformanceLayout* PerformanceManager::performanceData() {
    void* ptr = _performanceMemory->memory();
    return reinterpret_cast<PerformanceLayout*>(ptr);
}

void PerformanceManager::tick() {
    _tick = (_tick + 1) % PerformanceLayout::NumberValues;
}

void PerformanceManager::storeIndividualPerformanceMeasurement
                                          (std::string identifier, long long microseconds)
{
    PerformanceLayout* layout = performanceData();
    _performanceMemory->acquireLock();

    auto it = individualPerformanceLocations.find(identifier);
    PerformanceLayout::FunctionPerformanceLayout* p = nullptr;
    if (it == individualPerformanceLocations.end()) {
        p = &(layout->functionEntries[layout->nFunctionEntries]);
        individualPerformanceLocations[identifier] = layout->nFunctionEntries;
        ++(layout->nFunctionEntries);
    }
    else {
        p = &(layout->functionEntries[it->second]);
    }
#ifdef _MSC_VER
    strcpy_s(p->name, identifier.length() + 1, identifier.c_str());
#else
    strcpy(p->name, identifier.c_str());
#endif

    std::rotate(
        std::begin(p->time),
        std::next(std::begin(p->time)),
        std::end(p->time)
    );
    p->time[PerformanceLayout::NumberValues - 1] =
        static_cast<float>(microseconds);

    _performanceMemory->releaseLock();
}

void PerformanceManager::storeScenePerformanceMeasurements(
                                           const std::vector<SceneGraphNode*>& sceneNodes)
{
    using namespace performance;

    PerformanceLayout* layout = performanceData();
    _performanceMemory->acquireLock();

    int nNodes = static_cast<int>(sceneNodes.size());
    layout->nScaleGraphEntries = static_cast<int16_t>(nNodes);
    for (int i = 0; i < nNodes; ++i) {
        SceneGraphNode* node = sceneNodes[i];

        memset(layout->sceneGraphEntries[i].name, 0, PerformanceLayout::LengthName);
#ifdef _MSC_VER
        strcpy_s(
            layout->sceneGraphEntries[i].name,
            node->name().length() + 1,
            node->name().c_str()
        );
#else
        strcpy(layout->sceneGraphEntries[i].name, node->name().c_str());
#endif

        SceneGraphNode::PerformanceRecord r = node->performanceRecord();
        PerformanceLayout::SceneGraphPerformanceLayout& entry = layout->sceneGraphEntries[i];

        // Covert nano to microseconds
        const float micro = 1000.f;

        std::rotate(
            std::begin(entry.renderTime),
            std::next(std::begin(entry.renderTime)),
            std::end(entry.renderTime)
        );
        entry.renderTime[PerformanceLayout::NumberValues - 1] = r.renderTime / micro;

        std::rotate(
            std::begin(entry.updateTranslation),
            std::next(std::begin(entry.updateTranslation)),
            std::end(entry.updateTranslation)
        );
        entry.updateTranslation[PerformanceLayout::NumberValues - 1] = r.updateTimeTranslation / micro;

        std::rotate(
            std::begin(entry.updateRotation),
            std::next(std::begin(entry.updateRotation)),
            std::end(entry.updateRotation)
        );
        entry.updateRotation[PerformanceLayout::NumberValues - 1] = r.updateTimeRotation / micro;

        std::rotate(
            std::begin(entry.updateScaling),
            std::next(std::begin(entry.updateScaling)),
            std::end(entry.updateScaling)
        );
        entry.updateScaling[PerformanceLayout::NumberValues - 1] = r.updateTimeScaling / micro;

        std::rotate(
            std::begin(entry.updateRenderable),
            std::next(std::begin(entry.updateRenderable)),
            std::end(entry.updateRenderable)
        );
        entry.updateRenderable[PerformanceLayout::NumberValues - 1] = r.updateTimeRenderable / micro;
    }
    _performanceMemory->releaseLock();

    if (_loggingEnabled && _tick == PerformanceLayout::NumberValues - 1) outputLogs();

    tick();
}

} // namespace performance
} // namespace openspace
<|endoftext|>


<commit_before>#include <node.h>
#include <nan.h>
#include <v8.h>
#include <vector>
#include "mouse.h"
#include "deadbeef_rand.h"
#include "keypress.h"
#include "screen.h"
#include "screengrab.h"
#include "MMBitmap.h"

using namespace v8;

/*
 __  __
|  \/  | ___  _   _ ___  ___
| |\/| |/ _ \| | | / __|/ _ \
| |  | | (_) | |_| \__ \  __/
|_|  |_|\___/ \__,_|___/\___|

*/

NAN_METHOD(moveMouse)
{
	NanScope();
	if (args.Length() < 2)
	{
		return NanThrowError("Invalid number of arguments.");
	}
	size_t x = args[0]->Int32Value();
	size_t y = args[1]->Int32Value();

	MMPoint point;
	point = MMPointMake(x, y);
	moveMouse(point);
	NanReturnValue(NanNew("1"));
}

NAN_METHOD(moveMouseSmooth)
{
	NanScope();
	if (args.Length() < 2)
	{
		return NanThrowError("Invalid number of arguments.");
	}
	size_t x = args[0]->Int32Value();
	size_t y = args[1]->Int32Value();

	MMPoint point;
	point = MMPointMake(x, y);
	smoothlyMoveMouse(point);
	NanReturnValue(NanNew("1"));
}

NAN_METHOD(getMousePos)
{
	NanScope();

	MMPoint pos = getMousePos();

 	//Return object with .x and .y.
	Local<Object> obj = NanNew<Object>();
	obj->Set(NanNew<String>("x"), NanNew<Number>(pos.x));
	obj->Set(NanNew<String>("y"), NanNew<Number>(pos.y));
	NanReturnValue(obj);
}

NAN_METHOD(mouseClick)
{
	NanScope();

	MMMouseButton button = LEFT_BUTTON;

	if (args.Length() == 1)
	{
		char *b = (*v8::String::Utf8Value(args[0]->ToString()));

		if (strcmp(b, "left") == 0)
		{
			button = LEFT_BUTTON;
		}
		else if (strcmp(b, "right") == 0)
		{
			button = RIGHT_BUTTON;
		}
		else if (strcmp(b, "middle") == 0)
		{
			button = CENTER_BUTTON;
		}
		else
		{
			return NanThrowError("Invalid mouse button specified.");
		}
	}
	else if (args.Length() > 1)
	{
		return NanThrowError("Invalid number of arguments.");
	}

	clickMouse(button);

	NanReturnValue(NanNew("1"));
}

NAN_METHOD(mouseToggle)
{
	NanScope();

	MMMouseButton button = LEFT_BUTTON;
	bool down;

	if (args.Length() > 0)
	{
		const char *d = (*v8::String::Utf8Value(args[0]->ToString()));

		if (strcmp(d, "down") == 0)
		{
			down = true;;
		}
		else if (strcmp(d, "up") == 0)
		{
			down = false;
		}
		else
		{
			return NanThrowError("Invalid mouse button state specified.");
		}
	}

	if (args.Length() == 2)
	{
		char *b = (*v8::String::Utf8Value(args[1]->ToString()));

		if (strcmp(b, "left") == 0)
		{
			button = LEFT_BUTTON;
		}
		else if (strcmp(b, "right") == 0)
		{
			button = RIGHT_BUTTON;
		}
		else if (strcmp(b, "middle") == 0)
		{
			button = CENTER_BUTTON;
		}
		else
		{
			return NanThrowError("Invalid mouse button specified.");
		}
	}
	else if (args.Length() > 2)
	{
		return NanThrowError("Invalid number of arguments.");
	}

	toggleMouse(down, button);

	NanReturnValue(NanNew("1"));
}

/*
 _  __          _                         _
| |/ /___ _   _| |__   ___   __ _ _ __ __| |
| ' // _ \ | | | '_ \ / _ \ / _` | '__/ _` |
| . \  __/ |_| | |_) | (_) | (_| | | | (_| |
|_|\_\___|\__, |_.__/ \___/ \__,_|_|  \__,_|
          |___/
*/

int CheckKeyCodes(char* k, MMKeyCode *key)
{
	if (!key) return -1;

	if (strcmp(k, "alt") == 0)
	{
		*key = K_ALT;
	}
  	else if (strcmp(k, "command") == 0)
	{
		*key = K_META;
	}
  	else if (strcmp(k, "control") == 0)
	{
		*key = K_CONTROL;
	}
  	else if (strcmp(k, "shift") == 0)
	{
		*key = K_SHIFT;
	}
	else if (strcmp(k, "backspace") == 0)
	{
		*key = K_BACKSPACE;
	}
	else if (strcmp(k, "enter") == 0)
	{
		*key = K_RETURN;
	}
	else if (strcmp(k, "tab") == 0)
	{
		*key = K_TAB;
	}
	else if (strcmp(k, "up") == 0)
	{
		*key = K_UP;
	}
	else if (strcmp(k, "down") == 0)
	{
		*key = K_DOWN;
	}
	else if (strcmp(k, "left") == 0)
	{
		*key = K_LEFT;
	}
	else if (strcmp(k, "right") == 0)
	{
		*key = K_RIGHT;
	}
	else if (strcmp(k, "escape") == 0)
	{
		*key = K_ESCAPE;
	}
	else if (strcmp(k, "delete") == 0)
	{
		*key = K_DELETE;
	}
	else if (strcmp(k, "home") == 0)
	{
		*key = K_HOME;
	}
	else if (strcmp(k, "end") == 0)
	{
		*key = K_END;
	}
	else if (strcmp(k, "pageup") == 0)
	{
		*key = K_PAGEUP;
	}
	else if (strcmp(k, "pagedown") == 0)
	{
		*key = K_PAGEDOWN;
	}
	else if (strlen(k) == 1)
	{
		*key = keyCodeForChar(*k);
	}
	else
	{
		return -2;
	}

	return 0;
}

int CheckKeyFlags(char* f, MMKeyFlags* flags)
{
	if (!flags) return -1;

	if (strcmp(f, "alt") == 0)
	{
    	*flags = MOD_ALT;
  	}
  	else if(strcmp(f, "command") == 0)
	{
    	*flags = MOD_META;
  	}
  	else if(strcmp(f, "control") == 0)
	{
    	*flags = MOD_CONTROL;
  	}
  	else if(strcmp(f, "shift") == 0)
	{
    	*flags = MOD_SHIFT;
	}
	else if(strcmp(f, "none") == 0)
	{
    	*flags = MOD_NONE;
  	}
 	else
	{
    	return -2;
  	}

	return 0;
}

int mssleep(unsigned long millisecond)
{
	struct timespec req;
	time_t sec=(int)(millisecond/1000);
	millisecond=millisecond-(sec*1000);
	req.tv_sec=sec;
	req.tv_nsec=millisecond*1000000L;
	while(nanosleep(&req,&req)==-1)
		continue;
	return 1;
}

NAN_METHOD(keyTap)
{
	NanScope();

	MMKeyFlags flags = MOD_NONE;
	MMKeyCode key;

  	char *k;
  	char *f;

  	v8::String::Utf8Value fstr(args[1]->ToString());
  	v8::String::Utf8Value kstr(args[0]->ToString());
  	k = *kstr;
  	f = *fstr;

	switch (args.Length())
	{
    	case 2:
			break;
    	case 1:
			f = NULL;
			break;
    	default:
      		return NanThrowError("Invalid number of arguments.");
	}

  	if (f)
	{
    	switch(CheckKeyFlags(f, &flags))
    	{
      		case -1:
        		return NanThrowError("Null pointer in key flag.");
        		break;
      		case -2:
        		return NanThrowError("Invalid key flag specified.");
        		break;
    	}
  	}

	switch(CheckKeyCodes(k, &key))
	{
    	case -1:
      		return NanThrowError("Null pointer in key code.");
      		break;
    	case -2:
      		return NanThrowError("Invalid key code specified.");
      		break;
    	default:
      		tapKeyCode(key, flags);
      		mssleep(10);
	}

	NanReturnValue(NanNew("1"));
}

NAN_METHOD(keyToggle)
{
	NanScope();

  MMKeyFlags flags = MOD_NONE;
	MMKeyCode key;

	char *k;
  bool down;
  char *f;

  v8::String::Utf8Value kstr(args[0]->ToString());
  v8::String::Utf8Value fstr(args[2]->ToString());
  down = args[1]->BooleanValue();
  k = *kstr;
  f = *fstr;

	switch (args.Length())
  {
    case 3:
      break;
    case 2:
      f = NULL;
      break;
    default:
      return NanThrowError("Invalid number of arguments.");
	}

  if (f) {
    switch(CheckKeyFlags(f, &flags))
    {
      case -1:
        return NanThrowError("Null pointer in key flag.");
        break;
      case -2:
        return NanThrowError("Invalid key flag specified.");
        break;
    }
  }

  switch(CheckKeyCodes(k, &key))
  {
    case -1:
      return NanThrowError("Null pointer in key code.");
      break;
    case -2:
      return NanThrowError("Invalid key code specified.");
      break;
    default:
      toggleKeyCode(key, down, flags);
      mssleep(10);
  }

	NanReturnValue(NanNew("1"));
}

NAN_METHOD(typeString)
{
	NanScope();

	char *str;
	NanUtf8String string(args[0]);

	str = *string;

	typeString(str);

	NanReturnValue(NanNew("1"));
}

/*
  ____
 / ___|  ___ _ __ ___  ___ _ __
 \___ \ / __| '__/ _ \/ _ \ '_ \
  ___) | (__| | |  __/  __/ | | |
 |____/ \___|_|  \___|\___|_| |_|

*/

NAN_METHOD(getPixelColor)
{
	NanScope();

	MMBitmapRef bitmap;
	MMRGBHex color;

	size_t x = args[0]->Int32Value();
	size_t y = args[1]->Int32Value();

	bitmap = copyMMBitmapFromDisplayInRect(MMRectMake(x, y, 1, 1));

	color = MMRGBHexAtPoint(bitmap, 0, 0);

	char hex [6];

	//Length needs to be 7 because snprintf includes a terminating null.
	//Use %06x to pad hex value with leading 0s.
	snprintf(hex, 7, "%06x", color);

	destroyMMBitmap(bitmap);

	NanReturnValue(NanNew(hex));
}

NAN_METHOD(getScreenSize)
{
	NanScope();

	//Get display size.
	MMSize displaySize = getMainDisplaySize();

	//Create our return object.
	Local<Object> obj = NanNew<Object>();
	obj->Set(NanNew<String>("width"), NanNew<Number>(displaySize.width));
	obj->Set(NanNew<String>("height"), NanNew<Number>(displaySize.height));

	//Return our object with .width and .height.
	NanReturnValue(obj);
}

void init(Handle<Object> target)
{

	target->Set(NanNew<String>("moveMouse"),
		NanNew<FunctionTemplate>(moveMouse)->GetFunction());

	target->Set(NanNew<String>("moveMouseSmooth"),
		NanNew<FunctionTemplate>(moveMouseSmooth)->GetFunction());

	target->Set(NanNew<String>("getMousePos"),
		NanNew<FunctionTemplate>(getMousePos)->GetFunction());

	target->Set(NanNew<String>("mouseClick"),
		NanNew<FunctionTemplate>(mouseClick)->GetFunction());

	target->Set(NanNew<String>("mouseToggle"),
		NanNew<FunctionTemplate>(mouseToggle)->GetFunction());

	target->Set(NanNew<String>("keyTap"),
		NanNew<FunctionTemplate>(keyTap)->GetFunction());

	target->Set(NanNew<String>("keyToggle"),
		NanNew<FunctionTemplate>(keyToggle)->GetFunction());

	target->Set(NanNew<String>("typeString"),
		NanNew<FunctionTemplate>(typeString)->GetFunction());

	target->Set(NanNew<String>("getPixelColor"),
		NanNew<FunctionTemplate>(getPixelColor)->GetFunction());

	target->Set(NanNew<String>("getScreenSize"),
		NanNew<FunctionTemplate>(getScreenSize)->GetFunction());

}

NODE_MODULE(robotjs, init)
<commit_msg>Fixed style of keyToggle.<commit_after>#include <node.h>
#include <nan.h>
#include <v8.h>
#include <vector>
#include "mouse.h"
#include "deadbeef_rand.h"
#include "keypress.h"
#include "screen.h"
#include "screengrab.h"
#include "MMBitmap.h"

using namespace v8;

/*
 __  __
|  \/  | ___  _   _ ___  ___
| |\/| |/ _ \| | | / __|/ _ \
| |  | | (_) | |_| \__ \  __/
|_|  |_|\___/ \__,_|___/\___|

*/

NAN_METHOD(moveMouse)
{
	NanScope();
	if (args.Length() < 2)
	{
		return NanThrowError("Invalid number of arguments.");
	}
	size_t x = args[0]->Int32Value();
	size_t y = args[1]->Int32Value();

	MMPoint point;
	point = MMPointMake(x, y);
	moveMouse(point);
	NanReturnValue(NanNew("1"));
}

NAN_METHOD(moveMouseSmooth)
{
	NanScope();
	if (args.Length() < 2)
	{
		return NanThrowError("Invalid number of arguments.");
	}
	size_t x = args[0]->Int32Value();
	size_t y = args[1]->Int32Value();

	MMPoint point;
	point = MMPointMake(x, y);
	smoothlyMoveMouse(point);
	NanReturnValue(NanNew("1"));
}

NAN_METHOD(getMousePos)
{
	NanScope();

	MMPoint pos = getMousePos();

 	//Return object with .x and .y.
	Local<Object> obj = NanNew<Object>();
	obj->Set(NanNew<String>("x"), NanNew<Number>(pos.x));
	obj->Set(NanNew<String>("y"), NanNew<Number>(pos.y));
	NanReturnValue(obj);
}

NAN_METHOD(mouseClick)
{
	NanScope();

	MMMouseButton button = LEFT_BUTTON;

	if (args.Length() == 1)
	{
		char *b = (*v8::String::Utf8Value(args[0]->ToString()));

		if (strcmp(b, "left") == 0)
		{
			button = LEFT_BUTTON;
		}
		else if (strcmp(b, "right") == 0)
		{
			button = RIGHT_BUTTON;
		}
		else if (strcmp(b, "middle") == 0)
		{
			button = CENTER_BUTTON;
		}
		else
		{
			return NanThrowError("Invalid mouse button specified.");
		}
	}
	else if (args.Length() > 1)
	{
		return NanThrowError("Invalid number of arguments.");
	}

	clickMouse(button);

	NanReturnValue(NanNew("1"));
}

NAN_METHOD(mouseToggle)
{
	NanScope();

	MMMouseButton button = LEFT_BUTTON;
	bool down;

	if (args.Length() > 0)
	{
		const char *d = (*v8::String::Utf8Value(args[0]->ToString()));

		if (strcmp(d, "down") == 0)
		{
			down = true;;
		}
		else if (strcmp(d, "up") == 0)
		{
			down = false;
		}
		else
		{
			return NanThrowError("Invalid mouse button state specified.");
		}
	}

	if (args.Length() == 2)
	{
		char *b = (*v8::String::Utf8Value(args[1]->ToString()));

		if (strcmp(b, "left") == 0)
		{
			button = LEFT_BUTTON;
		}
		else if (strcmp(b, "right") == 0)
		{
			button = RIGHT_BUTTON;
		}
		else if (strcmp(b, "middle") == 0)
		{
			button = CENTER_BUTTON;
		}
		else
		{
			return NanThrowError("Invalid mouse button specified.");
		}
	}
	else if (args.Length() > 2)
	{
		return NanThrowError("Invalid number of arguments.");
	}

	toggleMouse(down, button);

	NanReturnValue(NanNew("1"));
}

/*
 _  __          _                         _
| |/ /___ _   _| |__   ___   __ _ _ __ __| |
| ' // _ \ | | | '_ \ / _ \ / _` | '__/ _` |
| . \  __/ |_| | |_) | (_) | (_| | | | (_| |
|_|\_\___|\__, |_.__/ \___/ \__,_|_|  \__,_|
          |___/
*/

int CheckKeyCodes(char* k, MMKeyCode *key)
{
	if (!key) return -1;

	if (strcmp(k, "alt") == 0)
	{
		*key = K_ALT;
	}
  	else if (strcmp(k, "command") == 0)
	{
		*key = K_META;
	}
  	else if (strcmp(k, "control") == 0)
	{
		*key = K_CONTROL;
	}
  	else if (strcmp(k, "shift") == 0)
	{
		*key = K_SHIFT;
	}
	else if (strcmp(k, "backspace") == 0)
	{
		*key = K_BACKSPACE;
	}
	else if (strcmp(k, "enter") == 0)
	{
		*key = K_RETURN;
	}
	else if (strcmp(k, "tab") == 0)
	{
		*key = K_TAB;
	}
	else if (strcmp(k, "up") == 0)
	{
		*key = K_UP;
	}
	else if (strcmp(k, "down") == 0)
	{
		*key = K_DOWN;
	}
	else if (strcmp(k, "left") == 0)
	{
		*key = K_LEFT;
	}
	else if (strcmp(k, "right") == 0)
	{
		*key = K_RIGHT;
	}
	else if (strcmp(k, "escape") == 0)
	{
		*key = K_ESCAPE;
	}
	else if (strcmp(k, "delete") == 0)
	{
		*key = K_DELETE;
	}
	else if (strcmp(k, "home") == 0)
	{
		*key = K_HOME;
	}
	else if (strcmp(k, "end") == 0)
	{
		*key = K_END;
	}
	else if (strcmp(k, "pageup") == 0)
	{
		*key = K_PAGEUP;
	}
	else if (strcmp(k, "pagedown") == 0)
	{
		*key = K_PAGEDOWN;
	}
	else if (strlen(k) == 1)
	{
		*key = keyCodeForChar(*k);
	}
	else
	{
		return -2;
	}

	return 0;
}

int CheckKeyFlags(char* f, MMKeyFlags* flags)
{
	if (!flags) return -1;

	if (strcmp(f, "alt") == 0)
	{
    	*flags = MOD_ALT;
  	}
  	else if(strcmp(f, "command") == 0)
	{
    	*flags = MOD_META;
  	}
  	else if(strcmp(f, "control") == 0)
	{
    	*flags = MOD_CONTROL;
  	}
  	else if(strcmp(f, "shift") == 0)
	{
    	*flags = MOD_SHIFT;
	}
	else if(strcmp(f, "none") == 0)
	{
    	*flags = MOD_NONE;
  	}
 	else
	{
    	return -2;
  	}

	return 0;
}

int mssleep(unsigned long millisecond)
{
	struct timespec req;
	time_t sec=(int)(millisecond/1000);
	millisecond=millisecond-(sec*1000);
	req.tv_sec=sec;
	req.tv_nsec=millisecond*1000000L;
	while(nanosleep(&req,&req)==-1)
		continue;
	return 1;
}

NAN_METHOD(keyTap)
{
	NanScope();

	MMKeyFlags flags = MOD_NONE;
	MMKeyCode key;

  	char *k;
  	char *f;

  	v8::String::Utf8Value fstr(args[1]->ToString());
  	v8::String::Utf8Value kstr(args[0]->ToString());
  	k = *kstr;
  	f = *fstr;

	switch (args.Length())
	{
    	case 2:
			break;
    	case 1:
			f = NULL;
			break;
    	default:
      		return NanThrowError("Invalid number of arguments.");
	}

  	if (f)
	{
    	switch(CheckKeyFlags(f, &flags))
    	{
      		case -1:
        		return NanThrowError("Null pointer in key flag.");
        		break;
      		case -2:
        		return NanThrowError("Invalid key flag specified.");
        		break;
    	}
  	}

	switch(CheckKeyCodes(k, &key))
	{
    	case -1:
      		return NanThrowError("Null pointer in key code.");
      		break;
    	case -2:
      		return NanThrowError("Invalid key code specified.");
      		break;
    	default:
      		tapKeyCode(key, flags);
      		mssleep(10);
	}

	NanReturnValue(NanNew("1"));
}

NAN_METHOD(keyToggle)
{
	NanScope();

	MMKeyFlags flags = MOD_NONE;
	MMKeyCode key;

	char *k;
	bool down;
	char *f;

	v8::String::Utf8Value kstr(args[0]->ToString());
	v8::String::Utf8Value fstr(args[2]->ToString());
	down = args[1]->BooleanValue();
	k = *kstr;
	f = *fstr;

	switch (args.Length())
	{
    	case 3:
      		break;
    	case 2:
      		f = NULL;
      		break;
    	default:
      		return NanThrowError("Invalid number of arguments.");
	}

	if (f)
	{
		switch(CheckKeyFlags(f, &flags))
		{
			case -1:
        		return NanThrowError("Null pointer in key flag.");
        		break;
			case -2:
        		return NanThrowError("Invalid key flag specified.");
        		break;
		}
	}

	switch(CheckKeyCodes(k, &key))
	{
		case -1:
    		return NanThrowError("Null pointer in key code.");
			break;
		case -2:
			return NanThrowError("Invalid key code specified.");
			break;
		default:
			toggleKeyCode(key, down, flags);
      		mssleep(10);
	}

	NanReturnValue(NanNew("1"));
}

NAN_METHOD(typeString)
{
	NanScope();

	char *str;
	NanUtf8String string(args[0]);

	str = *string;

	typeString(str);

	NanReturnValue(NanNew("1"));
}

/*
  ____
 / ___|  ___ _ __ ___  ___ _ __
 \___ \ / __| '__/ _ \/ _ \ '_ \
  ___) | (__| | |  __/  __/ | | |
 |____/ \___|_|  \___|\___|_| |_|

*/

NAN_METHOD(getPixelColor)
{
	NanScope();

	MMBitmapRef bitmap;
	MMRGBHex color;

	size_t x = args[0]->Int32Value();
	size_t y = args[1]->Int32Value();

	bitmap = copyMMBitmapFromDisplayInRect(MMRectMake(x, y, 1, 1));

	color = MMRGBHexAtPoint(bitmap, 0, 0);

	char hex [6];

	//Length needs to be 7 because snprintf includes a terminating null.
	//Use %06x to pad hex value with leading 0s.
	snprintf(hex, 7, "%06x", color);

	destroyMMBitmap(bitmap);

	NanReturnValue(NanNew(hex));
}

NAN_METHOD(getScreenSize)
{
	NanScope();

	//Get display size.
	MMSize displaySize = getMainDisplaySize();

	//Create our return object.
	Local<Object> obj = NanNew<Object>();
	obj->Set(NanNew<String>("width"), NanNew<Number>(displaySize.width));
	obj->Set(NanNew<String>("height"), NanNew<Number>(displaySize.height));

	//Return our object with .width and .height.
	NanReturnValue(obj);
}

void init(Handle<Object> target)
{

	target->Set(NanNew<String>("moveMouse"),
		NanNew<FunctionTemplate>(moveMouse)->GetFunction());

	target->Set(NanNew<String>("moveMouseSmooth"),
		NanNew<FunctionTemplate>(moveMouseSmooth)->GetFunction());

	target->Set(NanNew<String>("getMousePos"),
		NanNew<FunctionTemplate>(getMousePos)->GetFunction());

	target->Set(NanNew<String>("mouseClick"),
		NanNew<FunctionTemplate>(mouseClick)->GetFunction());

	target->Set(NanNew<String>("mouseToggle"),
		NanNew<FunctionTemplate>(mouseToggle)->GetFunction());

	target->Set(NanNew<String>("keyTap"),
		NanNew<FunctionTemplate>(keyTap)->GetFunction());

	target->Set(NanNew<String>("keyToggle"),
		NanNew<FunctionTemplate>(keyToggle)->GetFunction());

	target->Set(NanNew<String>("typeString"),
		NanNew<FunctionTemplate>(typeString)->GetFunction());

	target->Set(NanNew<String>("getPixelColor"),
		NanNew<FunctionTemplate>(getPixelColor)->GetFunction());

	target->Set(NanNew<String>("getScreenSize"),
		NanNew<FunctionTemplate>(getScreenSize)->GetFunction());

}

NODE_MODULE(robotjs, init)
<|endoftext|>


<commit_before>/*//////////////////////////////////////////////////////////////////
////       SKIRT -- an advanced radiative transfer code         ////
////       © Astronomical Observatory, Ghent University         ////
///////////////////////////////////////////////////////////////// */

#include "Log.hpp"
#include "NR.hpp"
#include "PanDustSystem.hpp"
#include "PanMonteCarloSimulation.hpp"
#include "PanWavelengthGrid.hpp"
#include "Parallel.hpp"
#include "ParallelFactory.hpp"
#include "PeerToPeerCommunicator.hpp"
#include "PhotonPackage.hpp"
#include "Random.hpp"
#include "SED.hpp"
#include "StellarSystem.hpp"
#include "TimeLogger.hpp"
#include "Units.hpp"

using namespace std;

////////////////////////////////////////////////////////////////////

PanMonteCarloSimulation::PanMonteCarloSimulation()
    : _pds(0)
{
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::setupSelfAfter()
{
    MonteCarloSimulation::setupSelfAfter();

    // properly size the array used to communicate between rundustXXX() and the corresponding parallel loop
    _Ncells = _pds ? _pds->Ncells() : 0;
    if (_pds && _pds->dustemission()) _Labsbolv.resize(_Ncells);
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::setWavelengthGrid(PanWavelengthGrid* value)
{
    if (_lambdagrid) delete _lambdagrid;
    _lambdagrid = value;
    if (_lambdagrid) _lambdagrid->setParent(this);
}

////////////////////////////////////////////////////////////////////

PanWavelengthGrid* PanMonteCarloSimulation::wavelengthGrid() const
{
    return dynamic_cast<PanWavelengthGrid*>(_lambdagrid);
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::setStellarSystem(StellarSystem* value)
{
    if (_ss) delete _ss;
    _ss = value;
    if (_ss) _ss->setParent(this);
}

////////////////////////////////////////////////////////////////////

StellarSystem* PanMonteCarloSimulation::stellarSystem() const
{
    return _ss;
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::setDustSystem(PanDustSystem* value)
{
    if (_ds) delete _ds;
    _ds = value;
    _pds = value;
    if (_ds) _ds->setParent(this);
}

////////////////////////////////////////////////////////////////////

PanDustSystem* PanMonteCarloSimulation::dustSystem() const
{
    return _pds;
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::runSelf()
{
    runstellaremission();
    if (_pds && _pds->dustemission())
    {
        if (_pds && _pds->selfAbsorption()) rundustselfabsorption();
        rundustemission();
    }

    write();
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::rundustselfabsorption()
{
    TimeLogger logger(_log, "the dust self-absorption phase");

    // Initialize the total absorbed luminosity in the previous cycle
    double prevLabsdusttot = 0.;

    // Perform three "stages" of max 100 cycles each; the first stage uses 10 times less photon packages
    const int Nstages = 3;
    const char* stage_name[] = {"first-stage", "second-stage", "last-stage"};
    const double stage_factor[] = {1./10., 1./3., 1.};
    const double stage_epsmax[] = {0.010, 0.007, 0.005};
    for (int stage=0; stage<Nstages; stage++)
    {
        bool fixedNcycles = _pds->cycles();
        const int Ncyclesmax = fixedNcycles ? _pds->cycles() : 100;
        bool convergence = false;
        int cycle = 1;
        while (cycle<=Ncyclesmax && (!convergence || fixedNcycles))
        {
            TimeLogger logger(_log, "the " + QString(stage_name[stage]) + " dust self-absorption cycle "
                              + QString::number(cycle));

            // Construct the dust emission spectra
            _log->info("Calculating dust emission spectra...");
            _pds->calculatedustemission(!(stage+cycle-1));
            _log->info("Dust emission spectra calculated.");

            // Determine the bolometric luminosity that is absorbed in every cell (and that will hence be re-emitted).
            for (int m=0; m<_Ncells; m++)
                _Labsbolv[m] = _pds->Labs(m);

            // Set the absorbed dust luminosity to zero in all cells
            _pds->rebootLabsdust();

            // Perform dust self-absorption, using the appropriate number of packages for the current stage
            setChunkParams(packages()*stage_factor[stage]);
            initprogress(QString(stage_name[stage]) + " dust self-absorption cycle " + QString::number(cycle));
            Parallel* parallel = find<ParallelFactory>()->parallel();
            parallel->call(this, &PanMonteCarloSimulation::dodustselfabsorptionchunk, assigner());

            // Wait for the other processes to reach this point
            _comm->wait("this self-absorption cycle");

            // Determine and log the total absorbed luminosity in the vector Labstotv.
            double Labsdusttot = _pds->Labsdusttot();
            _log->info("The total absorbed stellar luminosity is "
                       + QString::number(_units->obolluminosity(_pds->Labsstellartot())) + " "
                       + _units->ubolluminosity() );
            _log->info("The total absorbed dust luminosity is "
                       + QString::number(_units->obolluminosity(Labsdusttot)) + " "
                       + _units->ubolluminosity() );

            // Check the criteria to terminate the self-absorption cycle:
            // - the total absorbed dust luminosity should change by less than epsmax compared to the previous cycle;
            // - the last stage must perform at least 2 cycles (to make sure that the energy is properly distributed)
            double eps = fabs((Labsdusttot-prevLabsdusttot)/Labsdusttot);
            prevLabsdusttot = Labsdusttot;
            if ( (stage<Nstages-1 || cycle>1) && eps<stage_epsmax[stage])
            {
                _log->info("Convergence reached; the last increase in the absorbed dust luminosity was "
                           + QString::number(eps*100, 'f', 2) + "%");
                convergence = true;
            }
            else
            {
                _log->info("Convergence not yet reached; the increase in the absorbed dust luminosity was "
                           + QString::number(eps*100, 'f', 2) + "%");
            }
            cycle++;
        }
        if (!convergence)
        {
            _log->error("Convergence not yet reached after " + QString::number(Ncyclesmax) + " "
                        + QString(stage_name[stage]) + " cycles!");
        }
    }
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::dodustselfabsorptionchunk(size_t index)
{
    // Determine the wavelength index for this chunk
    int ell = index % _Nlambda;

    // Determine the luminosity to be emitted at this wavelength index
    Array Lv(_Ncells);
    for (int m=0; m<_Ncells; m++)
    {
        double Labsbol = _Labsbolv[m];
        if (Labsbol>0.0) Lv[m] = Labsbol * _pds->dustluminosity(m,ell);
    }
    double Ltot = Lv.sum();

    // Emit photon packages
    if (Ltot > 0)
    {
        Array Xv;
        NR::cdf(Xv, Lv);

        PhotonPackage pp;
        double L = Ltot / _Npp;
        double Lthreshold = L / minWeightReduction();

        quint64 remaining = _chunksize;
        while (remaining > 0)
        {
            quint64 count = qMin(remaining, _logchunksize);
            for (quint64 i=0; i<count; i++)
            {
                double X = _random->uniform();
                int m = NR::locate_clip(Xv,X);
                Position bfr = _pds->randomPositionInCell(m);
                Direction bfk = _random->direction();
                pp.launch(L,ell,bfr,bfk);
                while (true)
                {
                    _pds->fillOpticalDepth(&pp);
                    simulateescapeandabsorption(&pp,true);
                    double L = pp.luminosity();
                    if (L==0.0) break;
                    if (L<=Lthreshold && pp.nScatt()>=minScattEvents()) break;
                    simulatepropagation(&pp);
                    simulatescattering(&pp);
                }
            }
            logprogress(count);
            remaining -= count;
        }
    }
    else logprogress(_chunksize);
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::rundustemission()
{
    TimeLogger logger(_log, "the dust emission phase");

    bool noDustSelfabsorption = !(_pds && _pds->selfAbsorption());

    // Construct the dust emission spectra
    _log->info("Calculating dust emission spectra...");
    _pds->calculatedustemission(noDustSelfabsorption);
    _log->info("Dust emission spectra calculated.");

    // Determine the bolometric luminosity that is absorbed in every cell (and that will hence be re-emitted).
    for (int m=0; m<_Ncells; m++)
        _Labsbolv[m] = _pds->Labs(m);

    // Perform the actual dust emission, possibly using more photon packages to obtain decent resolution
    setChunkParams(packages()*_pds->emissionBoost());
    initprogress("dust emission");
    Parallel* parallel = find<ParallelFactory>()->parallel();
    parallel->call(this, &PanMonteCarloSimulation::dodustemissionchunk, assigner());

    // Wait for the other processes to reach this point
    _comm->wait("the dust emission phase");
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::dodustemissionchunk(size_t index)
{
    // Determine the wavelength index for this chunk
    int ell = index % _Nlambda;

    // Determine the luminosity to be emitted at this wavelength index
    Array Lv(_Ncells);
    for (int m=0; m<_Ncells; m++)
    {
        double Labsbol = _Labsbolv[m];
        if (Labsbol>0.0) Lv[m] = Labsbol * _pds->dustluminosity(m,ell);
    }
    double Ltot = Lv.sum();  // the total luminosity to be emitted at this wavelength index

    // Emit photon packages
    if (Ltot > 0)
    {
        // We consider biasing in the selection of the cell from which the photon packages are emitted.
        // A fraction of the cells is selected from the "natural" distribution, in which each cell is
        // weighted according to its total luminosity (Lv[em]). The other cells are selected from
        // a uniform distribution in which each cell has a equal probability.
        double xi = _pds->emissionBias();    // the fraction to be selected from a uniform distribution

        // the cumulative distribution of the natural pdf (Lv does not need to be normalized before)
        Array cumLv;
        NR::cdf(cumLv, Lv);

        PhotonPackage pp,ppp;
        double Lmean = Ltot/_Ncells;
        double Lem = Ltot / _Npp;
        double Lthreshold = Lem / minWeightReduction();

        quint64 remaining = _chunksize;
        while (remaining > 0)
        {
            quint64 count = qMin(remaining, _logchunksize);
            for (quint64 i=0; i<count; i++)
            {
                int m;
                double X = _random->uniform();
                if (X<xi)
                {
                    // rescale the deviate from [0,xi[ to [0,Ncells[
                    m = max(0,min(_Ncells-1,static_cast<int>(_Ncells/xi)));
                }
                else
                {
                    // rescale the deviate from [xi,1[ to [0,1[
                    m = NR::locate_clip(cumLv,(X-xi)/(1-xi));
                }
                double weight = 1.0/(1-xi+xi*Lmean/Lv[m]);
                Position bfr = _pds->randomPositionInCell(m);
                Direction bfk = _random->direction();
                pp.launch(Lem*weight,ell,bfr,bfk);
                peeloffemission(&pp,&ppp);
                while (true)
                {
                    _pds->fillOpticalDepth(&pp);
                    if (continuousScattering()) continuouspeeloffscattering(&pp,&ppp);
                    simulateescapeandabsorption(&pp,false);
                    double L = pp.luminosity();
                    if (L==0.0) break;
                    if (L<=Lthreshold && pp.nScatt()>=minScattEvents()) break;
                    simulatepropagation(&pp);
                    if (!continuousScattering()) peeloffscattering(&pp,&ppp);
                    simulatescattering(&pp);
                }
            }
            logprogress(count);
            remaining -= count;
        }
    }
    else logprogress(_chunksize);
}

////////////////////////////////////////////////////////////////////
<commit_msg>fix emission bias bug introduced while refactoring<commit_after>/*//////////////////////////////////////////////////////////////////
////       SKIRT -- an advanced radiative transfer code         ////
////       © Astronomical Observatory, Ghent University         ////
///////////////////////////////////////////////////////////////// */

#include "Log.hpp"
#include "NR.hpp"
#include "PanDustSystem.hpp"
#include "PanMonteCarloSimulation.hpp"
#include "PanWavelengthGrid.hpp"
#include "Parallel.hpp"
#include "ParallelFactory.hpp"
#include "PeerToPeerCommunicator.hpp"
#include "PhotonPackage.hpp"
#include "Random.hpp"
#include "SED.hpp"
#include "StellarSystem.hpp"
#include "TimeLogger.hpp"
#include "Units.hpp"

using namespace std;

////////////////////////////////////////////////////////////////////

PanMonteCarloSimulation::PanMonteCarloSimulation()
    : _pds(0)
{
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::setupSelfAfter()
{
    MonteCarloSimulation::setupSelfAfter();

    // properly size the array used to communicate between rundustXXX() and the corresponding parallel loop
    _Ncells = _pds ? _pds->Ncells() : 0;
    if (_pds && _pds->dustemission()) _Labsbolv.resize(_Ncells);
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::setWavelengthGrid(PanWavelengthGrid* value)
{
    if (_lambdagrid) delete _lambdagrid;
    _lambdagrid = value;
    if (_lambdagrid) _lambdagrid->setParent(this);
}

////////////////////////////////////////////////////////////////////

PanWavelengthGrid* PanMonteCarloSimulation::wavelengthGrid() const
{
    return dynamic_cast<PanWavelengthGrid*>(_lambdagrid);
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::setStellarSystem(StellarSystem* value)
{
    if (_ss) delete _ss;
    _ss = value;
    if (_ss) _ss->setParent(this);
}

////////////////////////////////////////////////////////////////////

StellarSystem* PanMonteCarloSimulation::stellarSystem() const
{
    return _ss;
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::setDustSystem(PanDustSystem* value)
{
    if (_ds) delete _ds;
    _ds = value;
    _pds = value;
    if (_ds) _ds->setParent(this);
}

////////////////////////////////////////////////////////////////////

PanDustSystem* PanMonteCarloSimulation::dustSystem() const
{
    return _pds;
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::runSelf()
{
    runstellaremission();
    if (_pds && _pds->dustemission())
    {
        if (_pds && _pds->selfAbsorption()) rundustselfabsorption();
        rundustemission();
    }

    write();
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::rundustselfabsorption()
{
    TimeLogger logger(_log, "the dust self-absorption phase");

    // Initialize the total absorbed luminosity in the previous cycle
    double prevLabsdusttot = 0.;

    // Perform three "stages" of max 100 cycles each; the first stage uses 10 times less photon packages
    const int Nstages = 3;
    const char* stage_name[] = {"first-stage", "second-stage", "last-stage"};
    const double stage_factor[] = {1./10., 1./3., 1.};
    const double stage_epsmax[] = {0.010, 0.007, 0.005};
    for (int stage=0; stage<Nstages; stage++)
    {
        bool fixedNcycles = _pds->cycles();
        const int Ncyclesmax = fixedNcycles ? _pds->cycles() : 100;
        bool convergence = false;
        int cycle = 1;
        while (cycle<=Ncyclesmax && (!convergence || fixedNcycles))
        {
            TimeLogger logger(_log, "the " + QString(stage_name[stage]) + " dust self-absorption cycle "
                              + QString::number(cycle));

            // Construct the dust emission spectra
            _log->info("Calculating dust emission spectra...");
            _pds->calculatedustemission(!(stage+cycle-1));
            _log->info("Dust emission spectra calculated.");

            // Determine the bolometric luminosity that is absorbed in every cell (and that will hence be re-emitted).
            for (int m=0; m<_Ncells; m++)
                _Labsbolv[m] = _pds->Labs(m);

            // Set the absorbed dust luminosity to zero in all cells
            _pds->rebootLabsdust();

            // Perform dust self-absorption, using the appropriate number of packages for the current stage
            setChunkParams(packages()*stage_factor[stage]);
            initprogress(QString(stage_name[stage]) + " dust self-absorption cycle " + QString::number(cycle));
            Parallel* parallel = find<ParallelFactory>()->parallel();
            parallel->call(this, &PanMonteCarloSimulation::dodustselfabsorptionchunk, assigner());

            // Wait for the other processes to reach this point
            _comm->wait("this self-absorption cycle");

            // Determine and log the total absorbed luminosity in the vector Labstotv.
            double Labsdusttot = _pds->Labsdusttot();
            _log->info("The total absorbed stellar luminosity is "
                       + QString::number(_units->obolluminosity(_pds->Labsstellartot())) + " "
                       + _units->ubolluminosity() );
            _log->info("The total absorbed dust luminosity is "
                       + QString::number(_units->obolluminosity(Labsdusttot)) + " "
                       + _units->ubolluminosity() );

            // Check the criteria to terminate the self-absorption cycle:
            // - the total absorbed dust luminosity should change by less than epsmax compared to the previous cycle;
            // - the last stage must perform at least 2 cycles (to make sure that the energy is properly distributed)
            double eps = fabs((Labsdusttot-prevLabsdusttot)/Labsdusttot);
            prevLabsdusttot = Labsdusttot;
            if ( (stage<Nstages-1 || cycle>1) && eps<stage_epsmax[stage])
            {
                _log->info("Convergence reached; the last increase in the absorbed dust luminosity was "
                           + QString::number(eps*100, 'f', 2) + "%");
                convergence = true;
            }
            else
            {
                _log->info("Convergence not yet reached; the increase in the absorbed dust luminosity was "
                           + QString::number(eps*100, 'f', 2) + "%");
            }
            cycle++;
        }
        if (!convergence)
        {
            _log->error("Convergence not yet reached after " + QString::number(Ncyclesmax) + " "
                        + QString(stage_name[stage]) + " cycles!");
        }
    }
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::dodustselfabsorptionchunk(size_t index)
{
    // Determine the wavelength index for this chunk
    int ell = index % _Nlambda;

    // Determine the luminosity to be emitted at this wavelength index
    Array Lv(_Ncells);
    for (int m=0; m<_Ncells; m++)
    {
        double Labsbol = _Labsbolv[m];
        if (Labsbol>0.0) Lv[m] = Labsbol * _pds->dustluminosity(m,ell);
    }
    double Ltot = Lv.sum();

    // Emit photon packages
    if (Ltot > 0)
    {
        Array Xv;
        NR::cdf(Xv, Lv);

        PhotonPackage pp;
        double L = Ltot / _Npp;
        double Lthreshold = L / minWeightReduction();

        quint64 remaining = _chunksize;
        while (remaining > 0)
        {
            quint64 count = qMin(remaining, _logchunksize);
            for (quint64 i=0; i<count; i++)
            {
                double X = _random->uniform();
                int m = NR::locate_clip(Xv,X);
                Position bfr = _pds->randomPositionInCell(m);
                Direction bfk = _random->direction();
                pp.launch(L,ell,bfr,bfk);
                while (true)
                {
                    _pds->fillOpticalDepth(&pp);
                    simulateescapeandabsorption(&pp,true);
                    double L = pp.luminosity();
                    if (L==0.0) break;
                    if (L<=Lthreshold && pp.nScatt()>=minScattEvents()) break;
                    simulatepropagation(&pp);
                    simulatescattering(&pp);
                }
            }
            logprogress(count);
            remaining -= count;
        }
    }
    else logprogress(_chunksize);
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::rundustemission()
{
    TimeLogger logger(_log, "the dust emission phase");

    bool noDustSelfabsorption = !(_pds && _pds->selfAbsorption());

    // Construct the dust emission spectra
    _log->info("Calculating dust emission spectra...");
    _pds->calculatedustemission(noDustSelfabsorption);
    _log->info("Dust emission spectra calculated.");

    // Determine the bolometric luminosity that is absorbed in every cell (and that will hence be re-emitted).
    for (int m=0; m<_Ncells; m++)
        _Labsbolv[m] = _pds->Labs(m);

    // Perform the actual dust emission, possibly using more photon packages to obtain decent resolution
    setChunkParams(packages()*_pds->emissionBoost());
    initprogress("dust emission");
    Parallel* parallel = find<ParallelFactory>()->parallel();
    parallel->call(this, &PanMonteCarloSimulation::dodustemissionchunk, assigner());

    // Wait for the other processes to reach this point
    _comm->wait("the dust emission phase");
}

////////////////////////////////////////////////////////////////////

void PanMonteCarloSimulation::dodustemissionchunk(size_t index)
{
    // Determine the wavelength index for this chunk
    int ell = index % _Nlambda;

    // Determine the luminosity to be emitted at this wavelength index
    Array Lv(_Ncells);
    for (int m=0; m<_Ncells; m++)
    {
        double Labsbol = _Labsbolv[m];
        if (Labsbol>0.0) Lv[m] = Labsbol * _pds->dustluminosity(m,ell);
    }
    double Ltot = Lv.sum();  // the total luminosity to be emitted at this wavelength index

    // Emit photon packages
    if (Ltot > 0)
    {
        // We consider biasing in the selection of the cell from which the photon packages are emitted.
        // A fraction of the cells is selected from the "natural" distribution, in which each cell is
        // weighted according to its total luminosity (Lv[em]). The other cells are selected from
        // a uniform distribution in which each cell has a equal probability.
        double xi = _pds->emissionBias();    // the fraction to be selected from a uniform distribution

        // the cumulative distribution of the natural pdf (Lv does not need to be normalized before)
        Array cumLv;
        NR::cdf(cumLv, Lv);

        PhotonPackage pp,ppp;
        double Lmean = Ltot/_Ncells;
        double Lem = Ltot / _Npp;
        double Lthreshold = Lem / minWeightReduction();

        quint64 remaining = _chunksize;
        while (remaining > 0)
        {
            quint64 count = qMin(remaining, _logchunksize);
            for (quint64 i=0; i<count; i++)
            {
                int m;
                double X = _random->uniform();
                if (X<xi)
                {
                    // rescale the deviate from [0,xi[ to [0,Ncells[
                    m = max(0,min(_Ncells-1,static_cast<int>(_Ncells*X/xi)));
                }
                else
                {
                    // rescale the deviate from [xi,1[ to [0,1[
                    m = NR::locate_clip(cumLv,(X-xi)/(1-xi));
                }
                double weight = 1.0/(1-xi+xi*Lmean/Lv[m]);
                Position bfr = _pds->randomPositionInCell(m);
                Direction bfk = _random->direction();
                pp.launch(Lem*weight,ell,bfr,bfk);
                peeloffemission(&pp,&ppp);
                while (true)
                {
                    _pds->fillOpticalDepth(&pp);
                    if (continuousScattering()) continuouspeeloffscattering(&pp,&ppp);
                    simulateescapeandabsorption(&pp,false);
                    double L = pp.luminosity();
                    if (L==0.0) break;
                    if (L<=Lthreshold && pp.nScatt()>=minScattEvents()) break;
                    simulatepropagation(&pp);
                    if (!continuousScattering()) peeloffscattering(&pp,&ppp);
                    simulatescattering(&pp);
                }
            }
            logprogress(count);
            remaining -= count;
        }
    }
    else logprogress(_chunksize);
}

////////////////////////////////////////////////////////////////////
<|endoftext|>