<commit_before>/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "skin.h"
#include "utils/polygonUtils.h"

#define MIN_AREA_SIZE (0.4 * 0.4)

namespace cura
{

void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
{
    generateSkinAreas(layerNr, storage, innermost_wall_extrusion_width, downSkinCount, upSkinCount, wall_line_count, no_small_gaps_heuristic);

    SliceLayer* layer = &storage.layers[layerNr];
    for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
    {
        SliceLayerPart* part = &layer->parts[partNr];
        generateSkinInsets(part, extrusionWidth, insetCount, avoidOverlappingPerimeters_0, avoidOverlappingPerimeters);
    }
}

void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic)
{
    SliceLayer& layer = storage.layers[layer_nr];

    if (downSkinCount == 0 && upSkinCount == 0)
    {
        return;
    }

    for(unsigned int partNr = 0; partNr < layer.parts.size(); partNr++)
    {
        SliceLayerPart& part = layer.parts[partNr];

        if (int(part.insets.size()) < wall_line_count)
        {
            continue; // the last wall is not present, the part should only get inter preimeter gaps, but no skin.
        }

        Polygons upskin = part.insets.back().offset(-innermost_wall_extrusion_width/2);
        Polygons downskin = (downSkinCount == 0)? Polygons() : upskin;
        if (upSkinCount == 0) upskin = Polygons();

        auto getInsidePolygons = [&part, wall_line_count](SliceLayer& layer2)
            {
                Polygons result;
                for(SliceLayerPart& part2 : layer2.parts)
                {
                    if (part.boundaryBox.hit(part2.boundaryBox))
                    {
                        unsigned int wall_idx = std::min(wall_line_count, (int) part2.insets.size()) - 1;
                        result.add(part2.insets[wall_idx]);
                    }
                }
                return result;
            };

        if (no_small_gaps_heuristic)
        {
            if (static_cast<int>(layer_nr - downSkinCount) >= 0)
            {
                downskin = downskin.difference(getInsidePolygons(storage.layers[layer_nr - downSkinCount])); // skin overlaps with the walls
            }

            if (static_cast<int>(layer_nr + upSkinCount) < static_cast<int>(storage.layers.size()))
            {
                upskin = upskin.difference(getInsidePolygons(storage.layers[layer_nr + upSkinCount])); // skin overlaps with the walls
            }
        }
        else
        {
            if (layer_nr >= downSkinCount && downSkinCount > 0)
            {
                Polygons not_air = getInsidePolygons(storage.layers[layer_nr - 1]);
                for (int downskin_layer_nr = layer_nr - downSkinCount; downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
                {
                    not_air = not_air.intersection(getInsidePolygons(storage.layers[downskin_layer_nr]));
                }
                downskin = downskin.difference(not_air); // skin overlaps with the walls
            }

            if (layer_nr < static_cast<int>(storage.layers.size()) - downSkinCount && upSkinCount > 0)
            {
                Polygons not_air = getInsidePolygons(storage.layers[layer_nr + 1]);
                for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < layer_nr + upSkinCount + 1; upskin_layer_nr++)
                {
                    not_air = not_air.intersection(getInsidePolygons(storage.layers[upskin_layer_nr]));
                }
                upskin = upskin.difference(not_air); // skin overlaps with the walls
            }
        }

        Polygons skin = upskin.unionPolygons(downskin);

        skin.removeSmallAreas(MIN_AREA_SIZE);

        for (PolygonsPart& skin_area_part : skin.splitIntoParts())
        {
            part.skin_parts.emplace_back();
            part.skin_parts.back().outline = skin_area_part;
        }
    }
}

void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
{
    if (insetCount == 0)
    {
        return;
    }

    for (SkinPart& skin_part : part->skin_parts)
    {
        for(int i=0; i<insetCount; i++)
        {
            skin_part.insets.push_back(Polygons());
            if (i == 0)
            {
                PolygonUtils::offsetSafe(skin_part.outline, - extrusionWidth/2, extrusionWidth, skin_part.insets[0], avoidOverlappingPerimeters_0);
                Polygons in_between = skin_part.outline.difference(skin_part.insets[0].offset(extrusionWidth/2));
                skin_part.perimeterGaps.add(in_between);
            } else
            {
                PolygonUtils::offsetExtrusionWidth(skin_part.insets[i-1], true, extrusionWidth, skin_part.insets[i], &skin_part.perimeterGaps, avoidOverlappingPerimeters);
            }

            // optimize polygons: remove unnnecesary verts
            skin_part.insets[i].simplify();
            if (skin_part.insets[i].size() < 1)
            {
                skin_part.insets.pop_back();
                break;
            }
        }
    }
}

void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count)
{
    SliceLayer& layer = storage.layers[layerNr];

    for(SliceLayerPart& part : layer.parts)
    {
        if (int(part.insets.size()) < wall_line_count)
        {
            part.infill_area.emplace_back(); // put empty polygon as (uncombined) infill
            continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
        }
        Polygons infill = part.insets.back().offset(-innermost_wall_extrusion_width / 2 - infill_skin_overlap);

        for(SliceLayerPart& part2 : layer.parts)
        {
            if (part.boundaryBox.hit(part2.boundaryBox))
            {
                for(SkinPart& skin_part : part2.skin_parts)
                {
                    infill = infill.difference(skin_part.outline);
                }
            }
        }
        infill.removeSmallAreas(MIN_AREA_SIZE);

        part.infill_area.push_back(infill.offset(infill_skin_overlap));
    }
}

void combineInfillLayers(SliceMeshStorage& storage,unsigned int amount)
{
    if(amount <= 1) //If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid.
    {
        return;
    }
    if(storage.layers.empty() || storage.layers.size() - 1 < static_cast<size_t>(storage.getSettingAsCount("top_layers")) || storage.getSettingAsCount("infill_line_distance") <= 0) //No infill is even generated.
    {
        return;
    }
    /* We need to round down the layer index we start at to the nearest
    divisible index. Otherwise we get some parts that have infill at divisible
    layers and some at non-divisible layers. Those layers would then miss each
    other. */
    size_t min_layer = storage.getSettingAsCount("bottom_layers") + amount - 1;
    min_layer -= min_layer % amount; //Round upwards to the nearest layer divisible by infill_sparse_combine.
    size_t max_layer = storage.layers.size() - 1 - storage.getSettingAsCount("top_layers");
    max_layer -= max_layer % amount; //Round downwards to the nearest layer divisible by infill_sparse_combine.
    for(size_t layer_idx = min_layer;layer_idx <= max_layer;layer_idx += amount) //Skip every few layers, but extrude more.
    {
        SliceLayer* layer = &storage.layers[layer_idx];

        for(unsigned int n = 1;n < amount;n++)
        {
            if(layer_idx < n)
            {
                break;
            }

            SliceLayer* layer2 = &storage.layers[layer_idx - n];
            for(SliceLayerPart& part : layer->parts)
            {
                Polygons result;
                for(SliceLayerPart& part2 : layer2->parts)
                {
                    if(part.boundaryBox.hit(part2.boundaryBox))
                    {
                        Polygons intersection = part.infill_area[n - 1].intersection(part2.infill_area[0]).offset(-200).offset(200);
                        result.add(intersection);
                        part.infill_area[n - 1] = part.infill_area[n - 1].difference(intersection);
                        part2.infill_area[0] = part2.infill_area[0].difference(intersection);
                    }
                }

                part.infill_area.push_back(result);
            }
        }
    }
}

void generatePerimeterGaps(int layer_nr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
{
    SliceLayer& layer = storage.layers[layer_nr];

    for (SliceLayerPart& part : layer.parts)
    { // handle gaps between perimeters etc.
        if (downSkinCount > 0 && upSkinCount > 0 && // note: if both are zero or less, then all gaps will be used
            layer_nr >= downSkinCount && layer_nr < static_cast<int>(storage.layers.size() - upSkinCount)) // remove gaps which appear within print, i.e. not on the bottom most or top most skin
        {
            Polygons outlines_above;
            for (SliceLayerPart& part_above : storage.layers[layer_nr + upSkinCount].parts)
            {
                if (part.boundaryBox.hit(part_above.boundaryBox))
                {
                    outlines_above.add(part_above.outline);
                }
            }
            Polygons outlines_below;
            for (SliceLayerPart& part_below : storage.layers[layer_nr - downSkinCount].parts)
            {
                if (part.boundaryBox.hit(part_below.boundaryBox))
                {
                    outlines_below.add(part_below.outline);
                }
            }
            part.perimeterGaps = part.perimeterGaps.intersection(outlines_above.xorPolygons(outlines_below));
        }
        part.perimeterGaps.removeSmallAreas(MIN_AREA_SIZE);
    }
}

}//namespace cura
<commit_msg>Fix typo. downSkinCount -> upSkinCount (CURA-1299)<commit_after>/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "skin.h"
#include "utils/polygonUtils.h"

#define MIN_AREA_SIZE (0.4 * 0.4)

namespace cura
{

void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
{
    generateSkinAreas(layerNr, storage, innermost_wall_extrusion_width, downSkinCount, upSkinCount, wall_line_count, no_small_gaps_heuristic);

    SliceLayer* layer = &storage.layers[layerNr];
    for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
    {
        SliceLayerPart* part = &layer->parts[partNr];
        generateSkinInsets(part, extrusionWidth, insetCount, avoidOverlappingPerimeters_0, avoidOverlappingPerimeters);
    }
}

void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic)
{
    SliceLayer& layer = storage.layers[layer_nr];

    if (downSkinCount == 0 && upSkinCount == 0)
    {
        return;
    }

    for(unsigned int partNr = 0; partNr < layer.parts.size(); partNr++)
    {
        SliceLayerPart& part = layer.parts[partNr];

        if (int(part.insets.size()) < wall_line_count)
        {
            continue; // the last wall is not present, the part should only get inter preimeter gaps, but no skin.
        }

        Polygons upskin = part.insets.back().offset(-innermost_wall_extrusion_width/2);
        Polygons downskin = (downSkinCount == 0)? Polygons() : upskin;
        if (upSkinCount == 0) upskin = Polygons();

        auto getInsidePolygons = [&part, wall_line_count](SliceLayer& layer2)
            {
                Polygons result;
                for(SliceLayerPart& part2 : layer2.parts)
                {
                    if (part.boundaryBox.hit(part2.boundaryBox))
                    {
                        unsigned int wall_idx = std::min(wall_line_count, (int) part2.insets.size()) - 1;
                        result.add(part2.insets[wall_idx]);
                    }
                }
                return result;
            };

        if (no_small_gaps_heuristic)
        {
            if (static_cast<int>(layer_nr - downSkinCount) >= 0)
            {
                downskin = downskin.difference(getInsidePolygons(storage.layers[layer_nr - downSkinCount])); // skin overlaps with the walls
            }

            if (static_cast<int>(layer_nr + upSkinCount) < static_cast<int>(storage.layers.size()))
            {
                upskin = upskin.difference(getInsidePolygons(storage.layers[layer_nr + upSkinCount])); // skin overlaps with the walls
            }
        }
        else
        {
            if (layer_nr >= downSkinCount && downSkinCount > 0)
            {
                Polygons not_air = getInsidePolygons(storage.layers[layer_nr - 1]);
                for (int downskin_layer_nr = layer_nr - downSkinCount; downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
                {
                    not_air = not_air.intersection(getInsidePolygons(storage.layers[downskin_layer_nr]));
                }
                downskin = downskin.difference(not_air); // skin overlaps with the walls
            }

            if (layer_nr < static_cast<int>(storage.layers.size()) - upSkinCount && upSkinCount > 0)
            {
                Polygons not_air = getInsidePolygons(storage.layers[layer_nr + 1]);
                for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < layer_nr + upSkinCount + 1; upskin_layer_nr++)
                {
                    not_air = not_air.intersection(getInsidePolygons(storage.layers[upskin_layer_nr]));
                }
                upskin = upskin.difference(not_air); // skin overlaps with the walls
            }
        }

        Polygons skin = upskin.unionPolygons(downskin);

        skin.removeSmallAreas(MIN_AREA_SIZE);

        for (PolygonsPart& skin_area_part : skin.splitIntoParts())
        {
            part.skin_parts.emplace_back();
            part.skin_parts.back().outline = skin_area_part;
        }
    }
}

void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
{
    if (insetCount == 0)
    {
        return;
    }

    for (SkinPart& skin_part : part->skin_parts)
    {
        for(int i=0; i<insetCount; i++)
        {
            skin_part.insets.push_back(Polygons());
            if (i == 0)
            {
                PolygonUtils::offsetSafe(skin_part.outline, - extrusionWidth/2, extrusionWidth, skin_part.insets[0], avoidOverlappingPerimeters_0);
                Polygons in_between = skin_part.outline.difference(skin_part.insets[0].offset(extrusionWidth/2));
                skin_part.perimeterGaps.add(in_between);
            } else
            {
                PolygonUtils::offsetExtrusionWidth(skin_part.insets[i-1], true, extrusionWidth, skin_part.insets[i], &skin_part.perimeterGaps, avoidOverlappingPerimeters);
            }

            // optimize polygons: remove unnnecesary verts
            skin_part.insets[i].simplify();
            if (skin_part.insets[i].size() < 1)
            {
                skin_part.insets.pop_back();
                break;
            }
        }
    }
}

void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count)
{
    SliceLayer& layer = storage.layers[layerNr];

    for(SliceLayerPart& part : layer.parts)
    {
        if (int(part.insets.size()) < wall_line_count)
        {
            part.infill_area.emplace_back(); // put empty polygon as (uncombined) infill
            continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
        }
        Polygons infill = part.insets.back().offset(-innermost_wall_extrusion_width / 2 - infill_skin_overlap);

        for(SliceLayerPart& part2 : layer.parts)
        {
            if (part.boundaryBox.hit(part2.boundaryBox))
            {
                for(SkinPart& skin_part : part2.skin_parts)
                {
                    infill = infill.difference(skin_part.outline);
                }
            }
        }
        infill.removeSmallAreas(MIN_AREA_SIZE);

        part.infill_area.push_back(infill.offset(infill_skin_overlap));
    }
}

void combineInfillLayers(SliceMeshStorage& storage,unsigned int amount)
{
    if(amount <= 1) //If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid.
    {
        return;
    }
    if(storage.layers.empty() || storage.layers.size() - 1 < static_cast<size_t>(storage.getSettingAsCount("top_layers")) || storage.getSettingAsCount("infill_line_distance") <= 0) //No infill is even generated.
    {
        return;
    }
    /* We need to round down the layer index we start at to the nearest
    divisible index. Otherwise we get some parts that have infill at divisible
    layers and some at non-divisible layers. Those layers would then miss each
    other. */
    size_t min_layer = storage.getSettingAsCount("bottom_layers") + amount - 1;
    min_layer -= min_layer % amount; //Round upwards to the nearest layer divisible by infill_sparse_combine.
    size_t max_layer = storage.layers.size() - 1 - storage.getSettingAsCount("top_layers");
    max_layer -= max_layer % amount; //Round downwards to the nearest layer divisible by infill_sparse_combine.
    for(size_t layer_idx = min_layer;layer_idx <= max_layer;layer_idx += amount) //Skip every few layers, but extrude more.
    {
        SliceLayer* layer = &storage.layers[layer_idx];

        for(unsigned int n = 1;n < amount;n++)
        {
            if(layer_idx < n)
            {
                break;
            }

            SliceLayer* layer2 = &storage.layers[layer_idx - n];
            for(SliceLayerPart& part : layer->parts)
            {
                Polygons result;
                for(SliceLayerPart& part2 : layer2->parts)
                {
                    if(part.boundaryBox.hit(part2.boundaryBox))
                    {
                        Polygons intersection = part.infill_area[n - 1].intersection(part2.infill_area[0]).offset(-200).offset(200);
                        result.add(intersection);
                        part.infill_area[n - 1] = part.infill_area[n - 1].difference(intersection);
                        part2.infill_area[0] = part2.infill_area[0].difference(intersection);
                    }
                }

                part.infill_area.push_back(result);
            }
        }
    }
}

void generatePerimeterGaps(int layer_nr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
{
    SliceLayer& layer = storage.layers[layer_nr];

    for (SliceLayerPart& part : layer.parts)
    { // handle gaps between perimeters etc.
        if (downSkinCount > 0 && upSkinCount > 0 && // note: if both are zero or less, then all gaps will be used
            layer_nr >= downSkinCount && layer_nr < static_cast<int>(storage.layers.size() - upSkinCount)) // remove gaps which appear within print, i.e. not on the bottom most or top most skin
        {
            Polygons outlines_above;
            for (SliceLayerPart& part_above : storage.layers[layer_nr + upSkinCount].parts)
            {
                if (part.boundaryBox.hit(part_above.boundaryBox))
                {
                    outlines_above.add(part_above.outline);
                }
            }
            Polygons outlines_below;
            for (SliceLayerPart& part_below : storage.layers[layer_nr - downSkinCount].parts)
            {
                if (part.boundaryBox.hit(part_below.boundaryBox))
                {
                    outlines_below.add(part_below.outline);
                }
            }
            part.perimeterGaps = part.perimeterGaps.intersection(outlines_above.xorPolygons(outlines_below));
        }
        part.perimeterGaps.removeSmallAreas(MIN_AREA_SIZE);
    }
}

}//namespace cura
<|endoftext|>


<commit_before>/*
  This file is part of INDDGO.

  Copyright (C) 2012, Oak Ridge National Laboratory

  This product includes software produced by UT-Battelle, LLC under Contract No.
  DE-AC05-00OR22725 with the Department of Energy.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the New BSD 3-clause software license (LICENSE).

  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
  LICENSE for more details.

  For more information please contact the INDDGO developers at:
  inddgo-info@googlegroups.com

*/

#include "GraphDecomposition.h"
#include "TreeDecomposition.h"
#include <fstream>
#include <unistd.h>
#include <sstream>

/*
 * This file generates one or more tree decompositions and
 * calculates a variety of statistics (width, bag size distribution,
 * average bag score), as well as optional treewidth lower bounds.
 * It generates a single output file containing all requested
 * information in a csv format.
 * As usual, this operates on the largest connected component.
 */
int main(int argc, char **argv)
{
  /*current required arguments (in this order, no flags):
   * graph_file (dimacs)
   * kcore_file (scorefile format)
   * output_file_basename
   * tree_outfile_basename
   */
  vector<double> *kcore_score = new vector<double>();
  vector<double> *degree_score = new vector<double>();
  int tdt[] = {TD_SUPERETREE, TD_GAVRIL, TD_BK, TD_NICE};
  int et[] = {GD_AMD, GD_METIS_NODE_ND, GD_METIS_MMD};
  const char* tdtype[] = {"Super-E-Tree", "Gavril"}; /*,"Bodlaender-Koster", "Nice"};*/ //Edited to speed up computations
  const char* elimtype[] = {"AMD", "MetisNodeND", "MetisMultMinD"};

  vector<int> td_types(tdt, tdt+sizeof(tdt)/sizeof(tdt[0]));
  vector<int> elim_types(et, et+sizeof(et)/sizeof(et[0]));
  vector<double>* st[] = {degree_score, kcore_score};
  vector<vector<double> *> scores(st, st+sizeof(st)/sizeof(st[0]));

  /*File names; this needs some checking to catch NULLS/miscalls*/
  char *graph_file = argv[1];
  char *kcore_file = argv[2];
  /*we'll print to stdout otherwise*/
  char *out_file_base = NULL;
  if(argc > 3)
    out_file_base = argv[3];
  char *tree_file_base = NULL;
  if(argc > 4)
    tree_file_base = argv[4];

  int t, e, i, s;
  Graph::WeightedMutableGraph *G;
  TDTree *T;
  int treewidth, treelength, minecc;
  double kcore_max, kcore_min;
  int degree_max, degree_min;
  std::ofstream out;
  std::ostream outStream(cout.rdbuf());
  std::stringstream sstm;
  int td_id;

  /*initialize log files if needed*/
  int pid;
#if WIN32 || _WIN32
  pid=_getpid();
#else
  pid=(int)getpid();
#endif
  char lfname[100];
  char efname[100];
  sprintf(lfname, "stats-%d.log",pid);
  sprintf(efname, "stats-%d.log",pid);
  //0: debug, 5: critical
  LOG_INIT(lfname, efname, 0);

  try
    {
      if(kcore_file == NULL || graph_file == NULL || out_file_base == NULL || tree_file_base == NULL )
	throw(Graph::GraphException("Call with four arguments: graph_file kcore_file outfile_base treefile_base\n"));

      /*populate the graph*/
      Graph::create_largestcomponent_graph(graph_file, G);

      /*populate appropriate score vectors*/
      bool range = read_color_file(kcore_file,kcore_max,kcore_min,*kcore_score);
      Graph::GraphUtil gutil;
      gutil.recompute_degrees(G);
      vector<int> idegree_score = G->get_degree();
      (*degree_score).resize(idegree_score.size());
      for(int i = 0; i < idegree_score.size(); i++)
	(*degree_score)[i] = idegree_score[i];

      /*loop over tree decomposition algorithms*/
      for(t = 0; t < td_types.size(); t++)
	{
	  /*loop over elimination order heuristics*/
	  for(e = 0; e < elim_types.size(); e++)
	    {
	      td_id = 10*(t+1) + e; //gives a unique number for the decomposition

	      /*form the tree decomposition*/
	      create_tree_decomposition(G, &T, false, NULL, false,
					false, NULL, elim_types[e],
					GD_UNDEFINED, td_types[t],
					false, true);
	      /*create a place to store all the statistics for this particular decomposition*/
	      vector<vector<double> > stats(T->num_tree_nodes);
	      vector<double> mystats;
	      vector<double>::iterator it;

	      //fill the bag vectors
	      T->fill_bag_vecs();
	      cout << "T has " << T->num_tree_nodes << " tree nodes\n";

	      //Non-score-specific statistics - width, length, eccentricity

	      /* Width - uses degree scores for non-negativity check*/
	      /* We define width = |B| - 1 to coincide with treewidth*/
	      bag_statistics(T, *(scores[0]), &mystats, GD_STAT_COUNT);
	      treewidth = 0;
	      for(int i = 0; i < mystats.size(); i++)
		{
		  if(mystats[i]-1 > treewidth)
		    treewidth = (int)(mystats[i])-1;
		  stats[i].push_back(mystats[i]-1);
		}

	      /*Length*/
	      vector<int> lengths;
	      treelength = 0;
	      bag_lengths(T, &lengths);
	      for(int i = 0; i < lengths.size(); i++)
		{
		  if(lengths[i] > treelength)
		    treelength = lengths[i];
		  stats[i].push_back((double)lengths[i]);
		}

	      /*Eccentricity*/
	      Graph::MutableGraph *GT = T->export_tree();
	      vector<int> ecc;
	      minecc = INT_MAX;
	      gutil.find_ecc(GT, &ecc);
	      for(int i = 0; i < ecc.size(); i++)
		{
		  if(ecc[i] < minecc)
		    minecc = ecc[i];
		  stats[i].push_back(ecc[i]);
		}

	      /*loop over scores and calculate mean med stddev*/
	      for(s = 0; s < scores.size(); s++)
		{
		  /*Mean*/
		  bag_statistics(T, *(scores[s]), &mystats, GD_STAT_MEAN);
		  for(int i = 0; i < mystats.size(); i++)
		    stats[i].push_back(mystats[i]);

		  /*Median*/
		  bag_statistics(T, *(scores[s]), &mystats, GD_STAT_MED);
		  for(int i = 0; i < mystats.size(); i++)
		    stats[i].push_back(mystats[i]);

		  /*Standard Deviation*/
		  bag_statistics(T, *(scores[s]), &mystats, GD_STAT_STD);
		  for(int i = 0; i < mystats.size(); i++)
		    stats[i].push_back(mystats[i]);
		}

	      /*
	       * Open output file for writing results
	       */
	      if(out_file_base == NULL)
		outStream.rdbuf(std::cout.rdbuf());
	      else
		{
		  sstm << out_file_base << td_id;
		  out.open((sstm.str()).c_str(), fstream::out);
		  if(out.fail())
		    fatal_error("%s:  Error opening file %s for writing graphviz output\n", __FUNCTION__, (sstm.str()).c_str());
		  outStream.rdbuf(out.rdbuf());
		  sstm.str("");//clears the stream
		}

	      /*
	       * Print the file header
	       */
	      outStream << "# " << graph_file << endl;
	      outStream << "# " << tdtype[t] << endl;
	      outStream << "# " << elimtype[e] << endl;
	      outStream << "# " << "Width " << (treewidth-1) << endl;
	      outStream << "# " << "Length " << treelength << endl;
	      outStream << "# " << "MinEcc " << minecc << endl;
	      outStream << "# Bag\t Cardinality\t Length\t Eccentricity\t MeanDegree\t MedianDegree\t StdDevDegree\t MeanScore\t MedianScore\t StdDevScore" << endl;

	      /*
	       * Print the statistics for each bag
	       */
	      for(int i = 0; i < mystats.size(); i++)
		{
		  outStream << i << "\t";
		  for(it = stats[i].begin(); it != stats[i].end(); ++it)
		    outStream << *it << "\t";
		  outStream << endl;
		}

	      /*
	       * Close the output file.
	       */
	      out.close();

	      /*
	       * Write the tree decomposition to file, if required.
	       */
	      if(tree_file_base != NULL)
		{
		  sstm << tree_file_base << td_id;
		  T->write_DIMACS_file((sstm.str()).c_str());
		  sstm.str("");//clears the stream
		}

	      /*delete the tree decomposition*/
	      delete T;

	    }
	}

      delete G;
      LOG_CLOSE();
      return 1;

    }
    catch(Graph::GraphException& e)
    {
      cerr << "exception caught: " << e.what() << endl;
      	LOG_CLOSE();
      return -1;
    }
}

<commit_msg>Correcting td_stats to run only two construction algorithms.<commit_after>/*
  This file is part of INDDGO.

  Copyright (C) 2012, Oak Ridge National Laboratory

  This product includes software produced by UT-Battelle, LLC under Contract No.
  DE-AC05-00OR22725 with the Department of Energy.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the New BSD 3-clause software license (LICENSE).

  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
  LICENSE for more details.

  For more information please contact the INDDGO developers at:
  inddgo-info@googlegroups.com

*/

#include "GraphDecomposition.h"
#include "TreeDecomposition.h"
#include <fstream>
#include <unistd.h>
#include <sstream>

/*
 * This file generates one or more tree decompositions and
 * calculates a variety of statistics (width, bag size distribution,
 * average bag score), as well as optional treewidth lower bounds.
 * It generates a single output file containing all requested
 * information in a csv format.
 * As usual, this operates on the largest connected component.
 */
int main(int argc, char **argv)
{
  /*current required arguments (in this order, no flags):
   * graph_file (dimacs)
   * kcore_file (scorefile format)
   * output_file_basename
   * tree_outfile_basename
   */
  vector<double> *kcore_score = new vector<double>();
  vector<double> *degree_score = new vector<double>();
  int tdt[] = {TD_SUPERETREE, TD_GAVRIL}; //, TD_BK, TD_NICE};
  int et[] = {GD_AMD, GD_METIS_NODE_ND, GD_METIS_MMD};
  const char* tdtype[] = {"Super-E-Tree", "Gavril"}; /*,"Bodlaender-Koster", "Nice"};*/ //Edited to speed up computations
  const char* elimtype[] = {"AMD", "MetisNodeND", "MetisMultMinD"};

  vector<int> td_types(tdt, tdt+sizeof(tdt)/sizeof(tdt[0]));
  vector<int> elim_types(et, et+sizeof(et)/sizeof(et[0]));
  vector<double>* st[] = {degree_score, kcore_score};
  vector<vector<double> *> scores(st, st+sizeof(st)/sizeof(st[0]));

  /*File names; this needs some checking to catch NULLS/miscalls*/
  char *graph_file = argv[1];
  char *kcore_file = argv[2];
  /*we'll print to stdout otherwise*/
  char *out_file_base = NULL;
  if(argc > 3)
    out_file_base = argv[3];
  char *tree_file_base = NULL;
  if(argc > 4)
    tree_file_base = argv[4];

  int t, e, i, s;
  Graph::WeightedMutableGraph *G;
  TDTree *T;
  int treewidth, treelength, minecc;
  double kcore_max, kcore_min;
  int degree_max, degree_min;
  std::ofstream out;
  std::ostream outStream(cout.rdbuf());
  std::stringstream sstm;
  int td_id;

  /*initialize log files if needed*/
  int pid;
#if WIN32 || _WIN32
  pid=_getpid();
#else
  pid=(int)getpid();
#endif
  char lfname[100];
  char efname[100];
  sprintf(lfname, "stats-%d.log",pid);
  sprintf(efname, "stats-%d.log",pid);
  //0: debug, 5: critical
  LOG_INIT(lfname, efname, 0);

  try
    {
      if(kcore_file == NULL || graph_file == NULL || out_file_base == NULL || tree_file_base == NULL )
	throw(Graph::GraphException("Call with four arguments: graph_file kcore_file outfile_base treefile_base\n"));

      /*populate the graph*/
      Graph::create_largestcomponent_graph(graph_file, G);

      /*populate appropriate score vectors*/
      bool range = read_color_file(kcore_file,kcore_max,kcore_min,*kcore_score);
      Graph::GraphUtil gutil;
      gutil.recompute_degrees(G);
      vector<int> idegree_score = G->get_degree();
      (*degree_score).resize(idegree_score.size());
      for(int i = 0; i < idegree_score.size(); i++)
	(*degree_score)[i] = idegree_score[i];

      /*loop over tree decomposition algorithms*/
      for(t = 0; t < td_types.size(); t++)
	{
	  /*loop over elimination order heuristics*/
	  for(e = 0; e < elim_types.size(); e++)
	    {
	      td_id = 10*(t+1) + e; //gives a unique number for the decomposition

	      /*form the tree decomposition*/
	      create_tree_decomposition(G, &T, false, NULL, false,
					false, NULL, elim_types[e],
					GD_UNDEFINED, td_types[t],
					false, true);
	      /*create a place to store all the statistics for this particular decomposition*/
	      vector<vector<double> > stats(T->num_tree_nodes);
	      vector<double> mystats;
	      vector<double>::iterator it;

	      //fill the bag vectors
	      T->fill_bag_vecs();
	      cout << "T has " << T->num_tree_nodes << " tree nodes\n";

	      //Non-score-specific statistics - width, length, eccentricity

	      /* Width - uses degree scores for non-negativity check*/
	      /* We define width = |B| - 1 to coincide with treewidth*/
	      bag_statistics(T, *(scores[0]), &mystats, GD_STAT_COUNT);
	      treewidth = 0;
	      for(int i = 0; i < mystats.size(); i++)
		{
		  if(mystats[i]-1 > treewidth)
		    treewidth = (int)(mystats[i])-1;
		  stats[i].push_back(mystats[i]-1);
		}

	      /*Length*/
	      vector<int> lengths;
	      treelength = 0;
	      bag_lengths(T, &lengths);
	      for(int i = 0; i < lengths.size(); i++)
		{
		  if(lengths[i] > treelength)
		    treelength = lengths[i];
		  stats[i].push_back((double)lengths[i]);
		}

	      /*Eccentricity*/
	      Graph::MutableGraph *GT = T->export_tree();
	      vector<int> ecc;
	      minecc = INT_MAX;
	      gutil.find_ecc(GT, &ecc);
	      for(int i = 0; i < ecc.size(); i++)
		{
		  if(ecc[i] < minecc)
		    minecc = ecc[i];
		  stats[i].push_back(ecc[i]);
		}

	      /*loop over scores and calculate mean med stddev*/
	      for(s = 0; s < scores.size(); s++)
		{
		  /*Mean*/
		  bag_statistics(T, *(scores[s]), &mystats, GD_STAT_MEAN);
		  for(int i = 0; i < mystats.size(); i++)
		    stats[i].push_back(mystats[i]);

		  /*Median*/
		  bag_statistics(T, *(scores[s]), &mystats, GD_STAT_MED);
		  for(int i = 0; i < mystats.size(); i++)
		    stats[i].push_back(mystats[i]);

		  /*Standard Deviation*/
		  bag_statistics(T, *(scores[s]), &mystats, GD_STAT_STD);
		  for(int i = 0; i < mystats.size(); i++)
		    stats[i].push_back(mystats[i]);
		}

	      /*
	       * Open output file for writing results
	       */
	      if(out_file_base == NULL)
		outStream.rdbuf(std::cout.rdbuf());
	      else
		{
		  sstm << out_file_base << td_id;
		  out.open((sstm.str()).c_str(), fstream::out);
		  if(out.fail())
		    fatal_error("%s:  Error opening file %s for writing graphviz output\n", __FUNCTION__, (sstm.str()).c_str());
		  outStream.rdbuf(out.rdbuf());
		  sstm.str("");//clears the stream
		}

	      /*
	       * Print the file header
	       */
	      outStream << "# " << graph_file << endl;
	      outStream << "# " << tdtype[t] << endl;
	      outStream << "# " << elimtype[e] << endl;
	      outStream << "# " << "Width " << (treewidth-1) << endl;
	      outStream << "# " << "Length " << treelength << endl;
	      outStream << "# " << "MinEcc " << minecc << endl;
	      outStream << "# Bag\t Cardinality\t Length\t Eccentricity\t MeanDegree\t MedianDegree\t StdDevDegree\t MeanScore\t MedianScore\t StdDevScore" << endl;

	      /*
	       * Print the statistics for each bag
	       */
	      for(int i = 0; i < mystats.size(); i++)
		{
		  outStream << i << "\t";
		  for(it = stats[i].begin(); it != stats[i].end(); ++it)
		    outStream << *it << "\t";
		  outStream << endl;
		}

	      /*
	       * Close the output file.
	       */
	      out.close();

	      /*
	       * Write the tree decomposition to file, if required.
	       */
	      if(tree_file_base != NULL)
		{
		  sstm << tree_file_base << td_id;
		  T->write_DIMACS_file((sstm.str()).c_str());
		  sstm.str("");//clears the stream
		}

	      /*delete the tree decomposition*/
	      delete T;

	    }
	}

      delete G;
      LOG_CLOSE();
      return 1;

    }
    catch(Graph::GraphException& e)
    {
      cerr << "exception caught: " << e.what() << endl;
      	LOG_CLOSE();
      return -1;
    }
}

<|endoftext|>


<commit_before>/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install, copy or use the software.
//
// Copyright (C) 2009, Farhad Dadgostar
// Intel Corporation and third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <opencv2/contrib/contrib.hpp>
#include <opencv2/highgui/highgui.hpp>

class ASDFrameHolder
{
private:
	IplImage *image;
	double timeStamp;

public:
	ASDFrameHolder();
	virtual ~ASDFrameHolder();
	virtual void assignFrame(IplImage *sourceImage, double frameTime);
	inline IplImage *getImage();
	inline double getTimeStamp();
	virtual void setImage(IplImage *sourceImage);
};

class ASDFrameSequencer
{
public:
	virtual ~ASDFrameSequencer();
	virtual IplImage *getNextImage();
	virtual void close();
	virtual bool isOpen();
	virtual void getFrameCaption(char *caption);
};

class ASDCVFrameSequencer : public ASDFrameSequencer
{
protected:
	CvCapture *capture;

public:
	virtual IplImage *getNextImage();
	virtual void close();
	virtual bool isOpen();
};

class ASDFrameSequencerWebCam : public ASDCVFrameSequencer
{
public:
	virtual bool open(int cameraIndex);
};

class ASDFrameSequencerVideoFile : public ASDCVFrameSequencer
{
public:
	virtual bool open(const char *fileName);
};

class ASDFrameSequencerImageFile : public ASDFrameSequencer {
private:
	char sFileNameMask[2048];
	int nCurrentIndex, nStartIndex, nEndIndex;

public:
	virtual void open(const char *fileNameMask, int startIndex, int endIndex);
	virtual void getFrameCaption(char *caption);
	virtual IplImage *getNextImage();
	virtual void close();
	virtual bool isOpen();
};

//-------------------- ASDFrameHolder -----------------------//
ASDFrameHolder::ASDFrameHolder( )
{
	image = NULL;
	timeStamp = 0;
};

ASDFrameHolder::~ASDFrameHolder( )
{
	cvReleaseImage(&image);
};

void ASDFrameHolder::assignFrame(IplImage *sourceImage, double frameTime)
{
	if (image != NULL)
	{
		cvReleaseImage(&image);
		image = NULL;
	}

	image = cvCloneImage(sourceImage);
	timeStamp = frameTime;
};

IplImage *ASDFrameHolder::getImage()
{
	return image;
};

double ASDFrameHolder::getTimeStamp()
{
	return timeStamp;
};

void ASDFrameHolder::setImage(IplImage *sourceImage)
{
	image = sourceImage;
};

//-------------------- ASDFrameSequencer -----------------------//

ASDFrameSequencer::~ASDFrameSequencer()
{
	close();
};

IplImage *ASDFrameSequencer::getNextImage()
{
	return NULL;
};

void ASDFrameSequencer::close()
{

};

bool ASDFrameSequencer::isOpen()
{
	return false;
};

void ASDFrameSequencer::getFrameCaption(char *caption) {
	return;
};

IplImage* ASDCVFrameSequencer::getNextImage()
{
	IplImage *image;

	image = cvQueryFrame(capture);

	if (image != NULL)
	{
		return cvCloneImage(image);
	}
	else
	{
		return NULL;
	}
};

void ASDCVFrameSequencer::close()
{
	if (capture != NULL)
	{
		cvReleaseCapture(&capture);
	}
};

bool ASDCVFrameSequencer::isOpen()
{
	return (capture != NULL);
};

//-------------------- ASDFrameSequencerWebCam -----------------------//

bool ASDFrameSequencerWebCam::open(int cameraIndex)
{
	close();

	capture = cvCaptureFromCAM(cameraIndex);

	if (!capture)
	{
		return false;
	}
	else
	{
		return true;
	}
};

//-------------------- ASDFrameSequencerVideoFile -----------------------//

bool ASDFrameSequencerVideoFile::open(const char *fileName)
{
	close();

	capture = cvCaptureFromFile(fileName);
	if (!capture)
	{
		return false;
	}
	else
	{
		return true;
	}
};

//-------------------- ASDFrameSequencerImageFile -----------------------//

void ASDFrameSequencerImageFile::open(const char *fileNameMask, int startIndex, int endIndex)
{
	nCurrentIndex = startIndex-1;
	nStartIndex = startIndex;
	nEndIndex = endIndex;

	std::sprintf(sFileNameMask, "%s", fileNameMask);
};

void ASDFrameSequencerImageFile::getFrameCaption(char *caption) {
	std::sprintf(caption, sFileNameMask, nCurrentIndex);
};

IplImage* ASDFrameSequencerImageFile::getNextImage()
{
	char fileName[2048];

	nCurrentIndex++;

	if (nCurrentIndex > nEndIndex)
		return NULL;

	std::sprintf(fileName, sFileNameMask, nCurrentIndex);

	IplImage* img = cvLoadImage(fileName);

	return img;
};

void ASDFrameSequencerImageFile::close()
{
	nCurrentIndex = nEndIndex+1;
};

bool ASDFrameSequencerImageFile::isOpen()
{
	return (nCurrentIndex <= nEndIndex);
};

void putTextWithShadow(IplImage *img, const char *str, CvPoint point, CvFont *font, CvScalar color = CV_RGB(255, 255, 128))
{
	cvPutText(img, str, cvPoint(point.x-1,point.y-1), font, CV_RGB(0, 0, 0));
	cvPutText(img, str, point, font, color);
};

#define ASD_RGB_SET_PIXEL(pointer, r, g, b)	{ (*pointer) = (unsigned char)b; (*(pointer+1)) = (unsigned char)g;	(*(pointer+2)) = (unsigned char)r; }

#define ASD_RGB_GET_PIXEL(pointer, r, g, b) {b = (unsigned char)(*(pointer)); g = (unsigned char)(*(pointer+1)); r = (unsigned char)(*(pointer+2));}

void displayBuffer(IplImage *rgbDestImage, IplImage *buffer, int rValue, int gValue, int bValue)
{
	int x, y, nWidth, nHeight;
	double destX, destY, dx, dy;
	uchar c;
	unsigned char *pSrc;

	nWidth = buffer->width;
	nHeight = buffer->height;

	dx = double(rgbDestImage->width)/double(nWidth);
	dy = double(rgbDestImage->height)/double(nHeight);

	destX = 0;
	for (x = 0; x < nWidth; x++)
	{
		destY = 0;
		for (y = 0; y < nHeight; y++)
		{
			c = ((uchar*)(buffer->imageData + buffer->widthStep*y))[x];

			if (c)
			{
				pSrc = (unsigned char *)rgbDestImage->imageData + rgbDestImage->widthStep*int(destY) + (int(destX)*rgbDestImage->nChannels);
				ASD_RGB_SET_PIXEL(pSrc, rValue, gValue, bValue);
			}
			destY += dy;
		}
		destY = 0;
		destX += dx;
	}
};

int main(int argc, char** argv )
{
	IplImage *img, *filterMask = NULL;
	CvAdaptiveSkinDetector filter(1, CvAdaptiveSkinDetector::MORPHING_METHOD_ERODE_DILATE);
	ASDFrameSequencer *sequencer;
	CvFont base_font;
	char caption[2048], s[256], windowName[256];
	long int clockTotal = 0, numFrames = 0;
	std::clock_t clock;

	if (argc < 4)
	{
		std::cout << "Usage: " << std::endl <<
			argv[0] << " fileMask firstFrame lastFrame" << std::endl << std::endl <<
			"Example: " << std::endl <<
			argv[0] << " C:\\VideoSequences\\sample1\\right_view\\temp_%05d.jpg  0  1000" << std::endl <<
			"	iterates through temp_00000.jpg  to  temp_01000.jpg" << std::endl << std::endl <<
			"If no parameter specified, this application will try to capture from the default Webcam." << std::endl <<
			"Please note: Background should not contain large surfaces with skin tone." <<
			std::endl;

		sequencer = new ASDFrameSequencerWebCam();
		(dynamic_cast<ASDFrameSequencerWebCam*>(sequencer))->open(-1);

		if (! sequencer->isOpen())
		{
			std::cout << std::endl << "Error: Cannot initialize the default Webcam" << std::endl << std::endl;
		}
	}
	else
	{
		sequencer = new ASDFrameSequencerImageFile();
		(dynamic_cast<ASDFrameSequencerImageFile*>(sequencer))->open(argv[1], std::atoi(argv[2]), std::atoi(argv[3]) ); // A sequence of images captured from video source, is stored here

	}
	std::sprintf(windowName, "%s", "Adaptive Skin Detection Algorithm for Video Sequences");

	cvNamedWindow(windowName, CV_WINDOW_AUTOSIZE);
	cvInitFont( &base_font, CV_FONT_VECTOR0, 0.5, 0.5);

	// Usage:
	//		c:\>CvASDSample "C:\VideoSequences\sample1\right_view\temp_%05d.jpg" 0 1000

	std::cout << "Press ESC to stop." << std::endl << std::endl;
	while ((img = sequencer->getNextImage()) != 0)
	{
		numFrames++;

		if (filterMask == NULL)
		{
			filterMask = cvCreateImage( cvSize(img->width, img->height), IPL_DEPTH_8U, 1);
		}
		clock = std::clock();
		filter.process(img, filterMask);	// process the frame
		clockTotal += (std::clock() - clock);

		displayBuffer(img, filterMask, 0, 255, 0);

		sequencer->getFrameCaption(caption);
		std::sprintf(s, "%s - %d x %d", caption, img->width, img->height);
		putTextWithShadow(img, s, cvPoint(10, img->height-35), &base_font);

		std::sprintf(s, "Average processing time per frame: %5.2fms", (double(clockTotal*1000/CLOCKS_PER_SEC))/numFrames);
		putTextWithShadow(img, s, cvPoint(10, img->height-15), &base_font);

		cvShowImage (windowName, img);
		cvReleaseImage(&img);

		if (cvWaitKey(1) == 27)
			break;
	}

	sequencer->close();
	delete sequencer;

	cvReleaseImage(&filterMask);

	cvDestroyWindow(windowName);

	std::cout << "Finished, " << numFrames << " frames processed." << std::endl;

	return 0;
}

<commit_msg>added to and moved comments to top<commit_after>/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install, copy or use the software.
//
// Copyright (C) 2009, Farhad Dadgostar
// Intel Corporation and third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <opencv2/contrib/contrib.hpp>
#include <opencv2/highgui/highgui.hpp>

void help(char **argv)
{
	std::cout << "\nThis program demonstrates the contributed flesh detector CvAdaptiveSkinDetector which can be found in contrib.cpp\n"
			<< "Usage: " << std::endl <<
		argv[0] << " fileMask firstFrame lastFrame" << std::endl << std::endl <<
		"Example: " << std::endl <<
		argv[0] << " C:\\VideoSequences\\sample1\\right_view\\temp_%05d.jpg  0  1000" << std::endl <<
		"	iterates through temp_00000.jpg  to  temp_01000.jpg" << std::endl << std::endl <<
		"If no parameter specified, this application will try to capture from the default Webcam." << std::endl <<
		"Please note: Background should not contain large surfaces with skin tone." <<
		"\n\n ESC will stop\n" <<
		std::endl;
}

class ASDFrameHolder
{
private:
	IplImage *image;
	double timeStamp;

public:
	ASDFrameHolder();
	virtual ~ASDFrameHolder();
	virtual void assignFrame(IplImage *sourceImage, double frameTime);
	inline IplImage *getImage();
	inline double getTimeStamp();
	virtual void setImage(IplImage *sourceImage);
};

class ASDFrameSequencer
{
public:
	virtual ~ASDFrameSequencer();
	virtual IplImage *getNextImage();
	virtual void close();
	virtual bool isOpen();
	virtual void getFrameCaption(char *caption);
};

class ASDCVFrameSequencer : public ASDFrameSequencer
{
protected:
	CvCapture *capture;

public:
	virtual IplImage *getNextImage();
	virtual void close();
	virtual bool isOpen();
};

class ASDFrameSequencerWebCam : public ASDCVFrameSequencer
{
public:
	virtual bool open(int cameraIndex);
};

class ASDFrameSequencerVideoFile : public ASDCVFrameSequencer
{
public:
	virtual bool open(const char *fileName);
};

class ASDFrameSequencerImageFile : public ASDFrameSequencer {
private:
	char sFileNameMask[2048];
	int nCurrentIndex, nStartIndex, nEndIndex;

public:
	virtual void open(const char *fileNameMask, int startIndex, int endIndex);
	virtual void getFrameCaption(char *caption);
	virtual IplImage *getNextImage();
	virtual void close();
	virtual bool isOpen();
};

//-------------------- ASDFrameHolder -----------------------//
ASDFrameHolder::ASDFrameHolder( )
{
	image = NULL;
	timeStamp = 0;
};

ASDFrameHolder::~ASDFrameHolder( )
{
	cvReleaseImage(&image);
};

void ASDFrameHolder::assignFrame(IplImage *sourceImage, double frameTime)
{
	if (image != NULL)
	{
		cvReleaseImage(&image);
		image = NULL;
	}

	image = cvCloneImage(sourceImage);
	timeStamp = frameTime;
};

IplImage *ASDFrameHolder::getImage()
{
	return image;
};

double ASDFrameHolder::getTimeStamp()
{
	return timeStamp;
};

void ASDFrameHolder::setImage(IplImage *sourceImage)
{
	image = sourceImage;
};

//-------------------- ASDFrameSequencer -----------------------//

ASDFrameSequencer::~ASDFrameSequencer()
{
	close();
};

IplImage *ASDFrameSequencer::getNextImage()
{
	return NULL;
};

void ASDFrameSequencer::close()
{

};

bool ASDFrameSequencer::isOpen()
{
	return false;
};

void ASDFrameSequencer::getFrameCaption(char *caption) {
	return;
};

IplImage* ASDCVFrameSequencer::getNextImage()
{
	IplImage *image;

	image = cvQueryFrame(capture);

	if (image != NULL)
	{
		return cvCloneImage(image);
	}
	else
	{
		return NULL;
	}
};

void ASDCVFrameSequencer::close()
{
	if (capture != NULL)
	{
		cvReleaseCapture(&capture);
	}
};

bool ASDCVFrameSequencer::isOpen()
{
	return (capture != NULL);
};

//-------------------- ASDFrameSequencerWebCam -----------------------//

bool ASDFrameSequencerWebCam::open(int cameraIndex)
{
	close();

	capture = cvCaptureFromCAM(cameraIndex);

	if (!capture)
	{
		return false;
	}
	else
	{
		return true;
	}
};

//-------------------- ASDFrameSequencerVideoFile -----------------------//

bool ASDFrameSequencerVideoFile::open(const char *fileName)
{
	close();

	capture = cvCaptureFromFile(fileName);
	if (!capture)
	{
		return false;
	}
	else
	{
		return true;
	}
};

//-------------------- ASDFrameSequencerImageFile -----------------------//

void ASDFrameSequencerImageFile::open(const char *fileNameMask, int startIndex, int endIndex)
{
	nCurrentIndex = startIndex-1;
	nStartIndex = startIndex;
	nEndIndex = endIndex;

	std::sprintf(sFileNameMask, "%s", fileNameMask);
};

void ASDFrameSequencerImageFile::getFrameCaption(char *caption) {
	std::sprintf(caption, sFileNameMask, nCurrentIndex);
};

IplImage* ASDFrameSequencerImageFile::getNextImage()
{
	char fileName[2048];

	nCurrentIndex++;

	if (nCurrentIndex > nEndIndex)
		return NULL;

	std::sprintf(fileName, sFileNameMask, nCurrentIndex);

	IplImage* img = cvLoadImage(fileName);

	return img;
};

void ASDFrameSequencerImageFile::close()
{
	nCurrentIndex = nEndIndex+1;
};

bool ASDFrameSequencerImageFile::isOpen()
{
	return (nCurrentIndex <= nEndIndex);
};

void putTextWithShadow(IplImage *img, const char *str, CvPoint point, CvFont *font, CvScalar color = CV_RGB(255, 255, 128))
{
	cvPutText(img, str, cvPoint(point.x-1,point.y-1), font, CV_RGB(0, 0, 0));
	cvPutText(img, str, point, font, color);
};

#define ASD_RGB_SET_PIXEL(pointer, r, g, b)	{ (*pointer) = (unsigned char)b; (*(pointer+1)) = (unsigned char)g;	(*(pointer+2)) = (unsigned char)r; }

#define ASD_RGB_GET_PIXEL(pointer, r, g, b) {b = (unsigned char)(*(pointer)); g = (unsigned char)(*(pointer+1)); r = (unsigned char)(*(pointer+2));}

void displayBuffer(IplImage *rgbDestImage, IplImage *buffer, int rValue, int gValue, int bValue)
{
	int x, y, nWidth, nHeight;
	double destX, destY, dx, dy;
	uchar c;
	unsigned char *pSrc;

	nWidth = buffer->width;
	nHeight = buffer->height;

	dx = double(rgbDestImage->width)/double(nWidth);
	dy = double(rgbDestImage->height)/double(nHeight);

	destX = 0;
	for (x = 0; x < nWidth; x++)
	{
		destY = 0;
		for (y = 0; y < nHeight; y++)
		{
			c = ((uchar*)(buffer->imageData + buffer->widthStep*y))[x];

			if (c)
			{
				pSrc = (unsigned char *)rgbDestImage->imageData + rgbDestImage->widthStep*int(destY) + (int(destX)*rgbDestImage->nChannels);
				ASD_RGB_SET_PIXEL(pSrc, rValue, gValue, bValue);
			}
			destY += dy;
		}
		destY = 0;
		destX += dx;
	}
};

int main(int argc, char** argv )
{
	IplImage *img, *filterMask = NULL;
	CvAdaptiveSkinDetector filter(1, CvAdaptiveSkinDetector::MORPHING_METHOD_ERODE_DILATE);
	ASDFrameSequencer *sequencer;
	CvFont base_font;
	char caption[2048], s[256], windowName[256];
	long int clockTotal = 0, numFrames = 0;
	std::clock_t clock;

	if (argc < 4)
	{
		help(argv);
		sequencer = new ASDFrameSequencerWebCam();
		(dynamic_cast<ASDFrameSequencerWebCam*>(sequencer))->open(-1);

		if (! sequencer->isOpen())
		{
			std::cout << std::endl << "Error: Cannot initialize the default Webcam" << std::endl << std::endl;
		}
	}
	else
	{
		sequencer = new ASDFrameSequencerImageFile();
		(dynamic_cast<ASDFrameSequencerImageFile*>(sequencer))->open(argv[1], std::atoi(argv[2]), std::atoi(argv[3]) ); // A sequence of images captured from video source, is stored here

	}
	std::sprintf(windowName, "%s", "Adaptive Skin Detection Algorithm for Video Sequences");

	cvNamedWindow(windowName, CV_WINDOW_AUTOSIZE);
	cvInitFont( &base_font, CV_FONT_VECTOR0, 0.5, 0.5);

	// Usage:
	//		c:\>CvASDSample "C:\VideoSequences\sample1\right_view\temp_%05d.jpg" 0 1000

	std::cout << "Press ESC to stop." << std::endl << std::endl;
	while ((img = sequencer->getNextImage()) != 0)
	{
		numFrames++;

		if (filterMask == NULL)
		{
			filterMask = cvCreateImage( cvSize(img->width, img->height), IPL_DEPTH_8U, 1);
		}
		clock = std::clock();
		filter.process(img, filterMask);	// DETECT SKIN
		clockTotal += (std::clock() - clock);

		displayBuffer(img, filterMask, 0, 255, 0);

		sequencer->getFrameCaption(caption);
		std::sprintf(s, "%s - %d x %d", caption, img->width, img->height);
		putTextWithShadow(img, s, cvPoint(10, img->height-35), &base_font);

		std::sprintf(s, "Average processing time per frame: %5.2fms", (double(clockTotal*1000/CLOCKS_PER_SEC))/numFrames);
		putTextWithShadow(img, s, cvPoint(10, img->height-15), &base_font);

		cvShowImage (windowName, img);
		cvReleaseImage(&img);

		if (cvWaitKey(1) == 27)
			break;
	}

	sequencer->close();
	delete sequencer;

	cvReleaseImage(&filterMask);

	cvDestroyWindow(windowName);

	std::cout << "Finished, " << numFrames << " frames processed." << std::endl;

	return 0;
}

<|endoftext|>


<commit_before>#include <imgui/imgui.h>

#include "game_view.h"
#include "editor/asset_browser.h"
#include "editor/asset_compiler.h"
#include "editor/settings.h"
#include "editor/studio_app.h"
#include "editor/utils.h"
#include "editor/world_editor.h"
#include "engine/crc32.h"
#include "engine/engine.h"
#include "engine/geometry.h"
#include "engine/input_system.h"
#include "engine/lua_wrapper.h"
#include "engine/profiler.h"
#include "engine/resource_manager.h"
#include "engine/universe.h"
#include "gui/gui_system.h"
#include "renderer/gpu/gpu.h"
#include "renderer/pipeline.h"
#include "renderer/render_scene.h"
#include "renderer/renderer.h"
#include "renderer/texture.h"

namespace Lumix
{

struct GUIInterface : GUISystem::Interface
{
	explicit GUIInterface(GameView& game_view)
		: m_game_view(game_view)
	{
	}

	Pipeline* getPipeline() override { return m_game_view.m_pipeline.get(); }
	Vec2 getPos() const override { return m_game_view.m_pos; }
	Vec2 getSize() const override { return m_game_view.m_size; }
	void setCursor(os::CursorType type) override { m_game_view.setCursor(type); }
	void enableCursor(bool enable) override { m_game_view.enableIngameCursor(enable); }

	GameView& m_game_view;
};

GameView::GameView(StudioApp& app)
	: m_app(app)
	, m_is_open(false)
	, m_is_fullscreen(false)
	, m_is_mouse_captured(false)
	, m_is_ingame_cursor(false)
	, m_time_multiplier(1.0f)
	, m_paused(false)
	, m_show_stats(false)
	, m_editor(app.getWorldEditor())
{
	Engine& engine = app.getEngine();
	auto f = &LuaWrapper::wrapMethodClosure<&GameView::forceViewport>;
	LuaWrapper::createSystemClosure(engine.getState(), "GameView", this, "forceViewport", f);
}

void GameView::init() {
	IAllocator& allocator = m_app.getAllocator();
	m_toggle_ui.init("Game View", "Toggle game view", "game_view", "", true);
	m_toggle_ui.func.bind<&GameView::onAction>(this);
	m_toggle_ui.is_selected.bind<&GameView::isOpen>(this);
	m_app.addWindowAction(&m_toggle_ui);

	m_fullscreen_action.init("Game View fullscreen", "Game View fullscreen", "game_view_fullscreen", "", true);
	m_fullscreen_action.func.bind<&GameView::toggleFullscreen>(this);
	m_app.addAction(&m_fullscreen_action);

	Engine& engine = m_app.getEngine();
	auto* renderer = (Renderer*)engine.getPluginManager().getPlugin("renderer");
	PipelineResource* pres = engine.getResourceManager().load<PipelineResource>(Path("pipelines/main.pln"));
	m_pipeline = Pipeline::create(*renderer, pres, "GAME_VIEW", engine.getAllocator());

	auto* gui = static_cast<GUISystem*>(engine.getPluginManager().getPlugin("gui"));
	if (gui)
	{
		m_gui_interface = UniquePtr<GUIInterface>::create(engine.getAllocator(), *this);
		gui->setInterface(m_gui_interface.get());
	}
}

GameView::~GameView()
{
	m_app.removeAction(&m_toggle_ui);
	m_app.removeAction(&m_fullscreen_action);
	Engine& engine = m_app.getEngine();
	auto* gui = static_cast<GUISystem*>(engine.getPluginManager().getPlugin("gui"));
	if (gui) {
		gui->setInterface(nullptr);
	}
}

void GameView::setCursor(os::CursorType type)
{
	m_cursor_type = type;
}

void GameView::enableIngameCursor(bool enable)
{
	m_is_ingame_cursor = enable;
	if (!m_is_mouse_captured) return;

	os::showCursor(m_is_ingame_cursor);
}

void GameView::captureMouse(bool capture)
{
	if (m_is_mouse_captured == capture) return;

	m_app.setCursorCaptured(capture);
	m_is_mouse_captured = capture;
	os::showCursor(!capture || m_is_ingame_cursor);

	if (capture) {
		const os::Point cp = os::getMouseScreenPos();
		m_captured_mouse_x = cp.x;
		m_captured_mouse_y = cp.y;
	}
	else {
		os::unclipCursor();
		os::setMouseScreenPos(m_captured_mouse_x, m_captured_mouse_y);
	}
}

void GameView::onSettingsLoaded() {
	m_is_open = m_app.getSettings().getValue("is_game_view_open", false);
}

void GameView::onBeforeSettingsSaved() {
	m_app.getSettings().setValue("is_game_view_open", m_is_open);
}

void GameView::onFullscreenGUI()
{
	processInputEvents();

	ImGuiIO& io = ImGui::GetIO();
	bool open = true;
	ImVec2 size = io.DisplaySize;
	ImGui::SetNextWindowPos(ImGui::GetMainViewport()->Pos);
	ImGui::SetNextWindowSize(size);
	if (!ImGui::Begin("game view fullscreen",
		&open,
		ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse |
		ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings))
	{
		ImGui::End();
		return;
	}

	EntityPtr camera = m_pipeline->getScene()->getActiveCamera();
	if (camera.isValid()) {
		Viewport vp = m_pipeline->getScene()->getCameraViewport((EntityRef)camera);
		vp.w = (int)size.x;
		vp.h = (int)size.y;
		m_pipeline->getScene()->setCameraScreenSize((EntityRef)camera, vp.w, vp.h);
		m_pipeline->setViewport(vp);
		m_pipeline->render(false);
		const gpu::TextureHandle texture_handle = m_pipeline->getOutput();
		if (gpu::isOriginBottomLeft())
		{
			ImGui::Image(texture_handle, size, ImVec2(0, 1), ImVec2(1, 0));
		}
		else
		{
			ImGui::Image(texture_handle, size);
		}
	}
	else {
		ImGuiEx::Rect(size.x, size.y, 0xff0000FF);
	}
	m_pos = ImGui::GetItemRectMin();
	m_size = ImGui::GetItemRectSize();

	ImGui::End();

	if (m_is_fullscreen && (io.KeysDown[ImGui::GetKeyIndex(ImGuiKey_Escape)] || !m_editor.isGameMode()))
	{
		setFullscreen(false);
	}
}

void GameView::toggleFullscreen()
{
	if (!m_editor.isGameMode()) return;
	setFullscreen(!m_is_fullscreen);
}

void GameView::setFullscreen(bool fullscreen)
{
	captureMouse(fullscreen);
	m_app.setFullscreen(fullscreen);
	m_is_fullscreen = fullscreen;
}

void GameView::onStatsGUI(const ImVec2& view_pos)
{
	if (!m_show_stats || !m_is_open) return;

	float toolbar_height = 24 + ImGui::GetStyle().FramePadding.y * 2;
	ImVec2 v = view_pos;
	v.x += ImGui::GetStyle().FramePadding.x;
	v.y += ImGui::GetStyle().FramePadding.y + toolbar_height;
	ImGui::SetNextWindowPos(v);
	auto col = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
	col.w = 0.3f;
	ImGui::PushStyleColor(ImGuiCol_WindowBg, col);
	ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
	if (ImGui::Begin("###stats_overlay", nullptr, flags)) {
		const auto& stats = m_pipeline->getStats();
		ImGui::LabelText("Draw calls", "%d", stats.draw_call_count);
		ImGui::LabelText("Instances", "%d", stats.instance_count);
		char buf[30];
		toCStringPretty(stats.triangle_count, Span(buf));
		ImGui::LabelText("Triangles", "%s", buf);
		ImGui::LabelText("Resolution", "%dx%d", (int)m_size.x, (int)m_size.y);
	}
	ImGui::End();
	ImGui::PopStyleColor();
}

void GameView::forceViewport(bool enable, int w, int h)
{
	m_forced_viewport.enabled = enable;
	m_forced_viewport.width = w;
	m_forced_viewport.height = h;
}

void GameView::processInputEvents()
{
	if (!m_is_mouse_captured) return;

	Engine& engine = m_app.getEngine();
	InputSystem& input = engine.getInputSystem();
	const os::Event* events = m_app.getEvents();
	for (int i = 0, c = m_app.getEventsCount(); i < c; ++i) {
		input.injectEvent(events[i], int(m_pos.x), int(m_pos.y));
	}
}

void GameView::controlsGUI() {
	Engine& engine = m_app.getEngine();
	if (ImGui::Checkbox("Pause", &m_paused)) engine.pause(m_paused);
	if (m_paused) {
		ImGui::SameLine();
		if (ImGui::Button("Next frame")) engine.nextFrame();
	}
	ImGui::SameLine();
	ImGui::PushItemWidth(50);
	if (ImGui::DragFloat("Time multiplier", &m_time_multiplier, 0.01f, 0.01f, 30.0f)) {
		engine.setTimeMultiplier(m_time_multiplier);
	}
	ImGui::PopItemWidth();
	if(m_editor.isGameMode()) {
		ImGui::SameLine();
		if (ImGui::Button("Fullscreen")) setFullscreen(true);
	}
	ImGui::SameLine();
	ImGui::Checkbox("Stats", &m_show_stats);
	ImGui::SameLine();
	m_pipeline->callLuaFunction("onGUI");
}

void GameView::onWindowGUI()
{
	PROFILE_FUNCTION();
	if (!m_pipeline->isReady()) {
		captureMouse(false);
		return;
	}

	m_pipeline->setUniverse(m_editor.getUniverse());

	ImGuiIO& io = ImGui::GetIO();
	if (m_is_mouse_captured && (io.KeysDown[ImGui::GetKeyIndex(ImGuiKey_Escape)] || !m_editor.isGameMode())) {
		captureMouse(false);
	}

	const char* window_name = ICON_FA_CAMERA "Game View###game_view";
	if (m_is_mouse_captured) {
		window_name = ICON_FA_CAMERA "Game View (mouse captured)###game_view";
		os::setCursor(m_cursor_type);
	}

	if (m_is_fullscreen) {
		onFullscreenGUI();
		return;
	}

	if (!m_is_open) {
		captureMouse(false);
		return;
	}

	ImVec2 view_pos;
	bool is_game_view_visible = false;
	ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
	if (ImGui::Begin(window_name, &m_is_open, ImGuiWindowFlags_NoNavInputs)) {
		is_game_view_visible = true;
		view_pos = ImGui::GetCursorScreenPos();

		const ImVec2 content_min = view_pos;
		ImVec2 size = ImGui::GetContentRegionAvail();
		size.y -= ImGui::GetTextLineHeightWithSpacing() + ImGui::GetStyle().ItemSpacing.y * 3;
		ImVec2 content_max(content_min.x + size.x, content_min.y + size.y);
		if (m_forced_viewport.enabled) size = { (float)m_forced_viewport.width, (float)m_forced_viewport.height };
		if (size.x > 0 && size.y > 0) {
			RenderScene* scene = m_pipeline->getScene();
			const EntityPtr camera = scene->getActiveCamera();
			Viewport vp;
			if (camera.isValid()) {
				vp = scene->getCameraViewport((EntityRef)camera);
				vp.w = (int)size.x;
				vp.h = (int)size.y;
				scene->setCameraScreenSize((EntityRef)camera, vp.w, vp.h);
			}
			else {
				vp.w = (int)size.x;
				vp.h = (int)size.y;
				vp.fov = degreesToRadians(90.f);
				vp.is_ortho = false;
				vp.far = 10'000.f;
				vp.near = 1.f;
				vp.pos = DVec3(0);
				vp.rot = Quat(0, 0, 0, 1);
			}
			m_pipeline->setViewport(vp);
			m_pipeline->render(false);
			const gpu::TextureHandle texture_handle = m_pipeline->getOutput();

			if (texture_handle) {
				if (gpu::isOriginBottomLeft()) {
					ImGui::Image(texture_handle, size, ImVec2(0, 1), ImVec2(1, 0));
				}
				else {
					ImGui::Image(texture_handle, size);
				}
			}
			else {
				ImGuiEx::Rect(size.x, size.y, 0xffFF00FF);
			}
			const bool is_hovered = ImGui::IsItemHovered();
			if (is_hovered && ImGui::IsMouseClicked(0) && m_editor.isGameMode()) captureMouse(true);
			m_pos = ImGui::GetItemRectMin();
			m_size = ImGui::GetItemRectSize();

			if (m_is_mouse_captured) {
				os::clipCursor((int)m_pos.x, (int)m_pos.y, (int)m_size.x, (int)m_size.y);
			}

			processInputEvents();
			controlsGUI();
		}

	}
	if (m_is_mouse_captured && os::getFocused() != ImGui::GetWindowViewport()->PlatformHandle) captureMouse(false);
	ImGui::End();
	ImGui::PopStyleVar();
	if (is_game_view_visible) onStatsGUI(view_pos);
}

} // namespace Lumix
<commit_msg>game view does not automatically get focus after  pipeline is reloaded<commit_after>#include <imgui/imgui.h>

#include "game_view.h"
#include "editor/asset_browser.h"
#include "editor/asset_compiler.h"
#include "editor/settings.h"
#include "editor/studio_app.h"
#include "editor/utils.h"
#include "editor/world_editor.h"
#include "engine/crc32.h"
#include "engine/engine.h"
#include "engine/geometry.h"
#include "engine/input_system.h"
#include "engine/lua_wrapper.h"
#include "engine/profiler.h"
#include "engine/resource_manager.h"
#include "engine/universe.h"
#include "gui/gui_system.h"
#include "renderer/gpu/gpu.h"
#include "renderer/pipeline.h"
#include "renderer/render_scene.h"
#include "renderer/renderer.h"
#include "renderer/texture.h"

namespace Lumix
{

struct GUIInterface : GUISystem::Interface
{
	explicit GUIInterface(GameView& game_view)
		: m_game_view(game_view)
	{
	}

	Pipeline* getPipeline() override { return m_game_view.m_pipeline.get(); }
	Vec2 getPos() const override { return m_game_view.m_pos; }
	Vec2 getSize() const override { return m_game_view.m_size; }
	void setCursor(os::CursorType type) override { m_game_view.setCursor(type); }
	void enableCursor(bool enable) override { m_game_view.enableIngameCursor(enable); }

	GameView& m_game_view;
};

GameView::GameView(StudioApp& app)
	: m_app(app)
	, m_is_open(false)
	, m_is_fullscreen(false)
	, m_is_mouse_captured(false)
	, m_is_ingame_cursor(false)
	, m_time_multiplier(1.0f)
	, m_paused(false)
	, m_show_stats(false)
	, m_editor(app.getWorldEditor())
{
	Engine& engine = app.getEngine();
	auto f = &LuaWrapper::wrapMethodClosure<&GameView::forceViewport>;
	LuaWrapper::createSystemClosure(engine.getState(), "GameView", this, "forceViewport", f);
}

void GameView::init() {
	IAllocator& allocator = m_app.getAllocator();
	m_toggle_ui.init("Game View", "Toggle game view", "game_view", "", true);
	m_toggle_ui.func.bind<&GameView::onAction>(this);
	m_toggle_ui.is_selected.bind<&GameView::isOpen>(this);
	m_app.addWindowAction(&m_toggle_ui);

	m_fullscreen_action.init("Game View fullscreen", "Game View fullscreen", "game_view_fullscreen", "", true);
	m_fullscreen_action.func.bind<&GameView::toggleFullscreen>(this);
	m_app.addAction(&m_fullscreen_action);

	Engine& engine = m_app.getEngine();
	auto* renderer = (Renderer*)engine.getPluginManager().getPlugin("renderer");
	PipelineResource* pres = engine.getResourceManager().load<PipelineResource>(Path("pipelines/main.pln"));
	m_pipeline = Pipeline::create(*renderer, pres, "GAME_VIEW", engine.getAllocator());

	auto* gui = static_cast<GUISystem*>(engine.getPluginManager().getPlugin("gui"));
	if (gui)
	{
		m_gui_interface = UniquePtr<GUIInterface>::create(engine.getAllocator(), *this);
		gui->setInterface(m_gui_interface.get());
	}
}

GameView::~GameView()
{
	m_app.removeAction(&m_toggle_ui);
	m_app.removeAction(&m_fullscreen_action);
	Engine& engine = m_app.getEngine();
	auto* gui = static_cast<GUISystem*>(engine.getPluginManager().getPlugin("gui"));
	if (gui) {
		gui->setInterface(nullptr);
	}
}

void GameView::setCursor(os::CursorType type)
{
	m_cursor_type = type;
}

void GameView::enableIngameCursor(bool enable)
{
	m_is_ingame_cursor = enable;
	if (!m_is_mouse_captured) return;

	os::showCursor(m_is_ingame_cursor);
}

void GameView::captureMouse(bool capture)
{
	if (m_is_mouse_captured == capture) return;

	m_app.setCursorCaptured(capture);
	m_is_mouse_captured = capture;
	os::showCursor(!capture || m_is_ingame_cursor);

	if (capture) {
		const os::Point cp = os::getMouseScreenPos();
		m_captured_mouse_x = cp.x;
		m_captured_mouse_y = cp.y;
	}
	else {
		os::unclipCursor();
		os::setMouseScreenPos(m_captured_mouse_x, m_captured_mouse_y);
	}
}

void GameView::onSettingsLoaded() {
	m_is_open = m_app.getSettings().getValue("is_game_view_open", false);
}

void GameView::onBeforeSettingsSaved() {
	m_app.getSettings().setValue("is_game_view_open", m_is_open);
}

void GameView::onFullscreenGUI()
{
	processInputEvents();

	ImGuiIO& io = ImGui::GetIO();
	bool open = true;
	ImVec2 size = io.DisplaySize;
	ImGui::SetNextWindowPos(ImGui::GetMainViewport()->Pos);
	ImGui::SetNextWindowSize(size);
	if (!ImGui::Begin("game view fullscreen",
		&open,
		ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse |
		ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings))
	{
		ImGui::End();
		return;
	}

	EntityPtr camera = m_pipeline->getScene()->getActiveCamera();
	if (camera.isValid()) {
		Viewport vp = m_pipeline->getScene()->getCameraViewport((EntityRef)camera);
		vp.w = (int)size.x;
		vp.h = (int)size.y;
		m_pipeline->getScene()->setCameraScreenSize((EntityRef)camera, vp.w, vp.h);
		m_pipeline->setViewport(vp);
		m_pipeline->render(false);
		const gpu::TextureHandle texture_handle = m_pipeline->getOutput();
		if (gpu::isOriginBottomLeft())
		{
			ImGui::Image(texture_handle, size, ImVec2(0, 1), ImVec2(1, 0));
		}
		else
		{
			ImGui::Image(texture_handle, size);
		}
	}
	else {
		ImGuiEx::Rect(size.x, size.y, 0xff0000FF);
	}
	m_pos = ImGui::GetItemRectMin();
	m_size = ImGui::GetItemRectSize();

	ImGui::End();

	if (m_is_fullscreen && (io.KeysDown[ImGui::GetKeyIndex(ImGuiKey_Escape)] || !m_editor.isGameMode()))
	{
		setFullscreen(false);
	}
}

void GameView::toggleFullscreen()
{
	if (!m_editor.isGameMode()) return;
	setFullscreen(!m_is_fullscreen);
}

void GameView::setFullscreen(bool fullscreen)
{
	captureMouse(fullscreen);
	m_app.setFullscreen(fullscreen);
	m_is_fullscreen = fullscreen;
}

void GameView::onStatsGUI(const ImVec2& view_pos)
{
	if (!m_show_stats || !m_is_open) return;

	float toolbar_height = 24 + ImGui::GetStyle().FramePadding.y * 2;
	ImVec2 v = view_pos;
	v.x += ImGui::GetStyle().FramePadding.x;
	v.y += ImGui::GetStyle().FramePadding.y + toolbar_height;
	ImGui::SetNextWindowPos(v);
	auto col = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
	col.w = 0.3f;
	ImGui::PushStyleColor(ImGuiCol_WindowBg, col);
	ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
	if (ImGui::Begin("###stats_overlay", nullptr, flags)) {
		const auto& stats = m_pipeline->getStats();
		ImGui::LabelText("Draw calls", "%d", stats.draw_call_count);
		ImGui::LabelText("Instances", "%d", stats.instance_count);
		char buf[30];
		toCStringPretty(stats.triangle_count, Span(buf));
		ImGui::LabelText("Triangles", "%s", buf);
		ImGui::LabelText("Resolution", "%dx%d", (int)m_size.x, (int)m_size.y);
	}
	ImGui::End();
	ImGui::PopStyleColor();
}

void GameView::forceViewport(bool enable, int w, int h)
{
	m_forced_viewport.enabled = enable;
	m_forced_viewport.width = w;
	m_forced_viewport.height = h;
}

void GameView::processInputEvents()
{
	if (!m_is_mouse_captured) return;

	Engine& engine = m_app.getEngine();
	InputSystem& input = engine.getInputSystem();
	const os::Event* events = m_app.getEvents();
	for (int i = 0, c = m_app.getEventsCount(); i < c; ++i) {
		input.injectEvent(events[i], int(m_pos.x), int(m_pos.y));
	}
}

void GameView::controlsGUI() {
	Engine& engine = m_app.getEngine();
	if (ImGui::Checkbox("Pause", &m_paused)) engine.pause(m_paused);
	if (m_paused) {
		ImGui::SameLine();
		if (ImGui::Button("Next frame")) engine.nextFrame();
	}
	ImGui::SameLine();
	ImGui::PushItemWidth(50);
	if (ImGui::DragFloat("Time multiplier", &m_time_multiplier, 0.01f, 0.01f, 30.0f)) {
		engine.setTimeMultiplier(m_time_multiplier);
	}
	ImGui::PopItemWidth();
	if(m_editor.isGameMode()) {
		ImGui::SameLine();
		if (ImGui::Button("Fullscreen")) setFullscreen(true);
	}
	ImGui::SameLine();
	ImGui::Checkbox("Stats", &m_show_stats);
	ImGui::SameLine();
	m_pipeline->callLuaFunction("onGUI");
}

void GameView::onWindowGUI()
{
	PROFILE_FUNCTION();
	m_pipeline->setUniverse(m_editor.getUniverse());

	ImGuiIO& io = ImGui::GetIO();
	if (m_is_mouse_captured && (io.KeysDown[ImGui::GetKeyIndex(ImGuiKey_Escape)] || !m_editor.isGameMode())) {
		captureMouse(false);
	}

	const char* window_name = ICON_FA_CAMERA "Game View###game_view";
	if (m_is_mouse_captured) {
		window_name = ICON_FA_CAMERA "Game View (mouse captured)###game_view";
		os::setCursor(m_cursor_type);
	}

	if (m_is_fullscreen && m_pipeline->isReady()) {
		onFullscreenGUI();
		return;
	}

	if (!m_is_open) {
		captureMouse(false);
		return;
	}

	if (!m_pipeline->isReady()) captureMouse(false);

	ImVec2 view_pos;
	bool is_game_view_visible = false;
	ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
	if (ImGui::Begin(window_name, &m_is_open, ImGuiWindowFlags_NoNavInputs)) {
		is_game_view_visible = true;
		view_pos = ImGui::GetCursorScreenPos();

		const ImVec2 content_min = view_pos;
		ImVec2 size = ImGui::GetContentRegionAvail();
		size.y -= ImGui::GetTextLineHeightWithSpacing() + ImGui::GetStyle().ItemSpacing.y * 3;
		ImVec2 content_max(content_min.x + size.x, content_min.y + size.y);
		if (m_forced_viewport.enabled) size = { (float)m_forced_viewport.width, (float)m_forced_viewport.height };
		if (size.x > 0 && size.y > 0) {
			RenderScene* scene = m_pipeline->getScene();
			const EntityPtr camera = scene->getActiveCamera();
			Viewport vp;
			if (camera.isValid()) {
				vp = scene->getCameraViewport((EntityRef)camera);
				vp.w = (int)size.x;
				vp.h = (int)size.y;
				scene->setCameraScreenSize((EntityRef)camera, vp.w, vp.h);
			}
			else {
				vp.w = (int)size.x;
				vp.h = (int)size.y;
				vp.fov = degreesToRadians(90.f);
				vp.is_ortho = false;
				vp.far = 10'000.f;
				vp.near = 1.f;
				vp.pos = DVec3(0);
				vp.rot = Quat(0, 0, 0, 1);
			}
			m_pipeline->setViewport(vp);
			m_pipeline->render(false);
			const gpu::TextureHandle texture_handle = m_pipeline->getOutput();

			if (texture_handle) {
				if (gpu::isOriginBottomLeft()) {
					ImGui::Image(texture_handle, size, ImVec2(0, 1), ImVec2(1, 0));
				}
				else {
					ImGui::Image(texture_handle, size);
				}
			}
			else {
				ImGuiEx::Rect(size.x, size.y, 0xffFF00FF);
			}
			const bool is_hovered = ImGui::IsItemHovered();
			if (is_hovered && ImGui::IsMouseClicked(0) && m_editor.isGameMode()) captureMouse(true);
			m_pos = ImGui::GetItemRectMin();
			m_size = ImGui::GetItemRectSize();

			if (m_is_mouse_captured) {
				os::clipCursor((int)m_pos.x, (int)m_pos.y, (int)m_size.x, (int)m_size.y);
			}

			processInputEvents();
			controlsGUI();
		}

	}
	if (m_is_mouse_captured && os::getFocused() != ImGui::GetWindowViewport()->PlatformHandle) captureMouse(false);
	ImGui::End();
	ImGui::PopStyleVar();
	if (is_game_view_visible) onStatsGUI(view_pos);
}

} // namespace Lumix
<|endoftext|>


<commit_before>#include "scene/scenegroup.hpp"

#include <algorithm>
#include <tuple>
#include <functional>
#include <cassert>

#include "config/globals.hpp"

#include "sceneitem.hpp"

using namespace scene;

typedef collisiondetection::AxisAlignedBoundingBox AABB;
typedef std::tuple<glm::vec3, glm::vec3> Diagonal;

SceneGroup* SceneGroup::rootNode;
std::recursive_mutex SceneGroup::sceneMutex;

SceneGroup::SceneGroup() : childGroups{nullptr} {
	rootNode = this;
}

SceneGroup::SceneGroup(unsigned int octreeLevels, collisiondetection::AxisAlignedBoundingBox myConstraints)
	: constraints(new AABB(myConstraints)) {
	addOctreeLayers(octreeLevels);

	rootNode = this;
}

void SceneGroup::addOctreeLayers(unsigned int levels) {
	if(levels != 0) {
		childGroups = new std::array<SceneGroup,8>;

		//Calculate and set constraints for all childGroups
		float width = (constraints->maxX - constraints->minX) / 2.0f;
		float height = (constraints->maxY - constraints->minY) / 2.0f;
		float depth = (constraints->maxZ - constraints->minZ) / 2.0f;

		glm::vec3 childMinimum(constraints->minX, constraints->minY, constraints->maxZ);
		glm::vec3 childMaximum(constraints->minX + width, constraints->minY + height, constraints->maxZ - depth);

		(*childGroups)[0].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.x += width;
		childMaximum.x += width;
		(*childGroups)[1].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.z -= depth;
		childMaximum.z -= depth;
		(*childGroups)[2].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.x -= width;
		childMaximum.x -= width;
		(*childGroups)[3].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.y += height;
		childMaximum.y += height;
		(*childGroups)[4].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.z += depth;
		childMaximum.z += depth;
		(*childGroups)[5].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.x += width;
		childMaximum.x += width;
		(*childGroups)[6].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.z -= depth;
		childMaximum.z -= depth;
		(*childGroups)[7].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		//Recursive call
		std::for_each(childGroups->begin(), childGroups->end(),
		[=](SceneGroup& child) {
			child.addOctreeLayers(levels-1);
		});
	}
}

void SceneGroup::visitScene(std::function<void(std::unique_ptr<SceneItem>&)> visitation) {
	std::lock_guard<std::recursive_mutex> guard(sceneMutex);

	std::for_each(childItems.begin(), childItems.end(), visitation);

	if(childGroups != nullptr) {
		std::for_each(childGroups->begin(), childGroups->end(),
		[&](SceneGroup& child) {
			child.visitScene(visitation);
		});
	}
}

void SceneGroup::visitGroups(std::function<void(SceneGroup&)> visitation) {
	visitation(*this);

	if(childGroups != nullptr) {
		std::for_each(childGroups->begin(), childGroups->end(),
		[&](SceneGroup& child) {
			child.visitGroups(visitation);
		});
	}
}

void SceneGroup::bubbleItem(std::unique_ptr<SceneItem> item) {
	std::lock_guard<std::recursive_mutex> guard(sceneMutex);

	if(childGroups != nullptr) {
		std::function<bool(SceneGroup&)> itemInGroup = [&item](SceneGroup& child) {
			return child.constraints->intersects(item->getBounds());
		};

		auto viableGroup = std::find_if(childGroups->begin(), childGroups->end(), itemInGroup);

		assert(viableGroup != childGroups->end());

		if(viableGroup+1 != childGroups->end()) {
			auto otherGroup = std::find_if(viableGroup+1, childGroups->end(), itemInGroup);

			if(otherGroup != childGroups->end()) {
				addItem(std::move(item));
				return;
			}
		}

		viableGroup->bubbleItem(std::move(item));
	}

	addItem(std::move(item));
}

void SceneGroup::addItem(std::unique_ptr<SceneItem> item) {
	if (childGroups == nullptr && childItems.size() == config::globals::maxSceneGroupSize) {
		addOctreeLayers(1);

		auto it = childItems.begin();
		while(it != childItems.end()) {
			std::lock_guard<std::recursive_mutex> guard(rootNode->sceneMutex);
			bubbleItem(std::move(*it));
			it = childItems.erase(it);
		}

		//Finally push in item
		std::lock_guard<std::recursive_mutex> lock(rootNode->sceneMutex);
		bubbleItem(std::move(item));
	} else {
		childItems.push_back(std::move(item));
	}
}

SceneGroup::~SceneGroup() {
	if(childGroups != nullptr) {
		delete childGroups;
	}
}<commit_msg>SceneGroup: Add needed comment<commit_after>#include "scene/scenegroup.hpp"

#include <algorithm>
#include <tuple>
#include <functional>
#include <cassert>

#include "config/globals.hpp"

#include "sceneitem.hpp"

using namespace scene;

typedef collisiondetection::AxisAlignedBoundingBox AABB;
typedef std::tuple<glm::vec3, glm::vec3> Diagonal;

SceneGroup* SceneGroup::rootNode;
std::recursive_mutex SceneGroup::sceneMutex;

SceneGroup::SceneGroup() : childGroups{nullptr} {
	rootNode = this;
}

SceneGroup::SceneGroup(unsigned int octreeLevels, collisiondetection::AxisAlignedBoundingBox myConstraints)
	: constraints(new AABB(myConstraints)) {
	addOctreeLayers(octreeLevels);

	rootNode = this;
}

void SceneGroup::addOctreeLayers(unsigned int levels) {
	if(levels != 0) {
		childGroups = new std::array<SceneGroup,8>;

		//Calculate and set constraints for all childGroups
		float width = (constraints->maxX - constraints->minX) / 2.0f;
		float height = (constraints->maxY - constraints->minY) / 2.0f;
		float depth = (constraints->maxZ - constraints->minZ) / 2.0f;

		glm::vec3 childMinimum(constraints->minX, constraints->minY, constraints->maxZ);
		glm::vec3 childMaximum(constraints->minX + width, constraints->minY + height, constraints->maxZ - depth);

		(*childGroups)[0].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.x += width;
		childMaximum.x += width;
		(*childGroups)[1].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.z -= depth;
		childMaximum.z -= depth;
		(*childGroups)[2].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.x -= width;
		childMaximum.x -= width;
		(*childGroups)[3].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.y += height;
		childMaximum.y += height;
		(*childGroups)[4].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.z += depth;
		childMaximum.z += depth;
		(*childGroups)[5].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.x += width;
		childMaximum.x += width;
		(*childGroups)[6].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		childMinimum.z -= depth;
		childMaximum.z -= depth;
		(*childGroups)[7].constraints.reset(new AABB(Diagonal(childMinimum, childMaximum)));

		//Recursive call
		std::for_each(childGroups->begin(), childGroups->end(),
		[=](SceneGroup& child) {
			child.addOctreeLayers(levels-1);
		});
	}
}

void SceneGroup::visitScene(std::function<void(std::unique_ptr<SceneItem>&)> visitation) {
	std::lock_guard<std::recursive_mutex> guard(sceneMutex);

	std::for_each(childItems.begin(), childItems.end(), visitation);

	if(childGroups != nullptr) {
		std::for_each(childGroups->begin(), childGroups->end(),
		[&](SceneGroup& child) {
			child.visitScene(visitation);
		});
	}
}

void SceneGroup::visitGroups(std::function<void(SceneGroup&)> visitation) {
	visitation(*this);

	if(childGroups != nullptr) {
		std::for_each(childGroups->begin(), childGroups->end(),
		[&](SceneGroup& child) {
			child.visitGroups(visitation);
		});
	}
}

void SceneGroup::bubbleItem(std::unique_ptr<SceneItem> item) {
	std::lock_guard<std::recursive_mutex> guard(sceneMutex);

	if(childGroups != nullptr) {
		std::function<bool(SceneGroup&)> itemInGroup = [&item](SceneGroup& child) {
			return child.constraints->intersects(item->getBounds());
		};

		auto viableGroup = std::find_if(childGroups->begin(), childGroups->end(), itemInGroup);

		assert(viableGroup != childGroups->end());

		if(viableGroup+1 != childGroups->end()) {
			auto otherGroup = std::find_if(viableGroup+1, childGroups->end(), itemInGroup);

			if(otherGroup != childGroups->end()) {
				//The item is on the edge of two groups, add to current group
				addItem(std::move(item));
				return;
			}
		}

		viableGroup->bubbleItem(std::move(item));
	}

	addItem(std::move(item));
}

void SceneGroup::addItem(std::unique_ptr<SceneItem> item) {
	if (childGroups == nullptr && childItems.size() == config::globals::maxSceneGroupSize) {
		addOctreeLayers(1);

		auto it = childItems.begin();
		while(it != childItems.end()) {
			std::lock_guard<std::recursive_mutex> guard(rootNode->sceneMutex);
			bubbleItem(std::move(*it));
			it = childItems.erase(it);
		}

		//Finally push in item
		std::lock_guard<std::recursive_mutex> lock(rootNode->sceneMutex);
		bubbleItem(std::move(item));
	} else {
		childItems.push_back(std::move(item));
	}
}

SceneGroup::~SceneGroup() {
	if(childGroups != nullptr) {
		delete childGroups;
	}
}<|endoftext|>