<commit_before>//
// MIT License
//
// Copyright 2019
//
// 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 <boost/filesystem.hpp>
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/program_options.hpp>
#include <iostream>
#include <string>

#include "cartridge.hh"
#include "disassemble.hh"

namespace fs = boost::filesystem;
namespace ip = boost::interprocess;
namespace po = boost::program_options;

namespace jd = jones::disassemble;
namespace jo = jones;

namespace {

void dump_code(const fs::path &root_path, const jo::cartridge &rom, const uint8_t *const base_address, const size_t length_in_bytes) {
  const fs::path code_path = root_path / "code";
  fs::create_directories(code_path);
  fs::ofstream code_file{code_path / "prgrom.a65"};

  const uint8_t *const prgrom_base_address = base_address + rom.get_prgrom_offset();
  const auto &disassembled_code = jd::disassemble(const_cast<uint8_t *>(prgrom_base_address), rom.get_prgrom_size());
  const auto &disassembled_instructions = disassembled_code.instructions;
  for (auto instruction : disassembled_instructions) {
    code_file << " " << std::hex << std::uppercase << instruction.address;
    code_file << " " << std::hex << std::uppercase << instruction.length_in_bytes;
    for (auto binary : instruction.binary) {
      code_file << std::hex << binary;
    }
    code_file << " " << instruction.opcode << instruction.operand << std::endl;
  }
}

void dump_audio(const fs::path &root_path, const jo::cartridge &rom, const uint8_t *const base_address, const size_t length_in_bytes) {
  const fs::path audio_path = root_path / "audio";
  fs::create_directories(audio_path);
}

void dump_image(const fs::path &root_path, const jo::cartridge &rom, const uint8_t *const base_address, const size_t length_in_bytes) {
  const fs::path image_path = root_path / "image";
  fs::create_directories(image_path);
}
} // namespace

int main(int argc, char *argv[]) {

  std::string file_argument;
  std::string output_argument;

  try {
    po::options_description desc{"Options"};
    desc.add_options()
      ("help,h", "Help screen")
      ("file,f", po::value<std::string>(&file_argument)->required(), "file path to rom")
      ("output,o", po::value<std::string>(&output_argument)->default_value("out"), "directory path to write output");

    po::variables_map vm;
    po::store(parse_command_line(argc, argv, desc), vm);
    po::notify(vm);
  } catch (const po::error &ex) {
    std::cerr << ex.what() << '\n';
    return -1;
  }

  const fs::path file_path(file_argument);
  if (!fs::exists(file_path)) {
    std::cerr << "file path does not exist; please check path" << std::endl;
    return -2;
  }

  if (fs::is_directory(file_path)) {
    std::cerr << "file path is a directory; please check path" << std::endl;
    return -3;
  }

  const fs::path output_path(output_argument);
  if (fs::exists(output_path) && !fs::is_directory(output_path)) {
    std::cerr << "output directory path is a file; please check path : " << output_path << std::endl;
    return -4;
  }

  if (!fs::exists(output_path) && !fs::create_directories(output_path)) {
    std::cerr << "unable to create output directory path; please check path : " << output_path << std::endl;
    return -5;
  }

  jones::cartridge rom(file_path.string());
  if (!rom.is_valid()) {
    std::cerr << "error: must specify valid rom file" << std::endl;
    return -6;
  }

  rom.print_header(std::cout);

  const ip::file_mapping mapped_file(file_path.c_str(), ip::mode_t::read_only);
  const ip::mapped_region mapped_region(mapped_file, ip::mode_t::read_only);
  const auto &mapped_base_address = static_cast<uint8_t const *>(mapped_region.get_address());
  const auto &mapped_size = mapped_region.get_size();

  dump_code(output_path, rom, mapped_base_address, mapped_size);
  dump_audio(output_path, rom, mapped_base_address, mapped_size);
  dump_image(output_path, rom, mapped_base_address, mapped_size);

  return 0;
}
<commit_msg>fixing formatting of output file<commit_after>//
// MIT License
//
// Copyright 2019
//
// 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 <boost/filesystem.hpp>
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/program_options.hpp>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>

#include "cartridge.hh"
#include "disassemble.hh"

namespace fs = boost::filesystem;
namespace ip = boost::interprocess;
namespace po = boost::program_options;

namespace jd = jones::disassemble;
namespace jo = jones;

namespace {

std::string to_hex_string(uint8_t *data, size_t size, size_t width) {
  std::ostringstream oss;
  for (int i = 0; i < size; i++) {
    oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<uint16_t>(data[i]);
  }
  for (int i = 0; i < (width - (size * 2)); i++) {
    oss << " ";
  }
  return oss.str();
}

void dump_code(const fs::path &root_path, const jo::cartridge &rom, const uint8_t *const base_address, const size_t length_in_bytes) {
  const fs::path code_path = root_path / "code";
  fs::create_directories(code_path);
  fs::ofstream code_file{code_path / "prgrom.a65"};

  const uint8_t *const prgrom_base_address = base_address + rom.get_prgrom_offset();
  const auto &disassembled_code = jd::disassemble(const_cast<uint8_t *>(prgrom_base_address), rom.get_prgrom_size());
  const auto &disassembled_instructions = disassembled_code.instructions;
  for (auto instruction : disassembled_instructions) {
    code_file << "   " << to_hex_string(reinterpret_cast<uint8_t *>(&instruction.address), sizeof(instruction.address), 5);
    code_file << "   " << to_hex_string(&instruction.length_in_bytes, sizeof(instruction.length_in_bytes), 5);
    code_file << "   " << to_hex_string(instruction.binary.data(), instruction.length_in_bytes, 10);
    code_file << "   " << instruction.opcode << instruction.operand << std::endl;
  }
}

void dump_audio(const fs::path &root_path, const jo::cartridge &rom, const uint8_t *const base_address, const size_t length_in_bytes) {
  const fs::path audio_path = root_path / "audio";
  fs::create_directories(audio_path);
}

void dump_image(const fs::path &root_path, const jo::cartridge &rom, const uint8_t *const base_address, const size_t length_in_bytes) {
  const fs::path image_path = root_path / "image";
  fs::create_directories(image_path);
}
} // namespace

int main(int argc, char *argv[]) {

  std::string file_argument;
  std::string output_argument;

  try {
    po::options_description desc{"Options"};
    desc.add_options()
      ("help,h", "Help screen")
      ("file,f", po::value<std::string>(&file_argument)->required(), "file path to rom")
      ("output,o", po::value<std::string>(&output_argument)->default_value("out"), "directory path to write output");

    po::variables_map vm;
    po::store(parse_command_line(argc, argv, desc), vm);
    po::notify(vm);
  } catch (const po::error &ex) {
    std::cerr << ex.what() << '\n';
    return -1;
  }

  const fs::path file_path(file_argument);
  if (!fs::exists(file_path)) {
    std::cerr << "file path does not exist; please check path" << std::endl;
    return -2;
  }

  if (fs::is_directory(file_path)) {
    std::cerr << "file path is a directory; please check path" << std::endl;
    return -3;
  }

  const fs::path output_path(output_argument);
  if (fs::exists(output_path) && !fs::is_directory(output_path)) {
    std::cerr << "output directory path is a file; please check path : " << output_path << std::endl;
    return -4;
  }

  if (!fs::exists(output_path) && !fs::create_directories(output_path)) {
    std::cerr << "unable to create output directory path; please check path : " << output_path << std::endl;
    return -5;
  }

  jones::cartridge rom(file_path.string());
  if (!rom.is_valid()) {
    std::cerr << "error: must specify valid rom file" << std::endl;
    return -6;
  }

  rom.print_header(std::cout);

  const ip::file_mapping mapped_file(file_path.c_str(), ip::mode_t::read_only);
  const ip::mapped_region mapped_region(mapped_file, ip::mode_t::read_only);
  const auto &mapped_base_address = static_cast<uint8_t const *>(mapped_region.get_address());
  const auto &mapped_size = mapped_region.get_size();

  dump_code(output_path, rom, mapped_base_address, mapped_size);
  dump_audio(output_path, rom, mapped_base_address, mapped_size);
  dump_image(output_path, rom, mapped_base_address, mapped_size);

  return 0;
}
<|endoftext|>


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

  Module:    VerdictVector.cpp

  Copyright (c) 2006 Sandia Corporation.
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

/*
 *
 * VerdictVector.cpp contains implementation of Vector operations
 *
 * This file is part of VERDICT
 *
 */

#define VERDICT_EXPORTS

#include "verdict.h"
#include <math.h>
#include "VerdictVector.hpp"
#include <float.h>

#if defined(__BORLANDC__)
#pragma warn -8004 /* "assigned a value that is never used" */
#endif

const double TWO_VERDICT_PI = 2.0 * VERDICT_PI;

VerdictVector &VerdictVector::length(const double new_length)
{
  double len = this->length();
  xVal *= new_length / len;
  yVal *= new_length / len;
  zVal *= new_length / len;
  return *this;
}

double VerdictVector::distance_between(const VerdictVector& test_vector)
{
  double xv = xVal - test_vector.x();
  double yv = yVal - test_vector.y();
  double zv = zVal - test_vector.z();

  return( sqrt( xv * xv + yv * yv + zv * zv ) );
}

/*
void VerdictVector::print_me()
{
  PRINT_INFO("X: %f\n",xVal);
  PRINT_INFO("Y: %f\n",yVal);
  PRINT_INFO("Z: %f\n",zVal);

}
*/

double VerdictVector::interior_angle(const VerdictVector &otherVector)
{
  double cosAngle=0., angleRad=0., len1, len2=0.;

  if (((len1 = this->length()) > 0) && ((len2 = otherVector.length()) > 0))
    cosAngle = (*this % otherVector)/(len1 * len2);
  else
  {
    assert(len1 > 0);
    assert(len2 > 0);
  }

  if ((cosAngle > 1.0) && (cosAngle < 1.0001))
  {
    cosAngle = 1.0;
    angleRad = acos(cosAngle);
  }
  else if (cosAngle < -1.0 && cosAngle > -1.0001)
  {
    cosAngle = -1.0;
    angleRad = acos(cosAngle);
  }
  else if (cosAngle >= -1.0 && cosAngle <= 1.0)
    angleRad = acos(cosAngle);
  else
  {
    assert(cosAngle < 1.0001 && cosAngle > -1.0001);
  }

  return( (angleRad * 180.) / VERDICT_PI );
}

// Interpolate between two vectors.
// Returns (1-param)*v1 + param*v2
VerdictVector interpolate(const double param, const VerdictVector &v1,
                        const VerdictVector &v2)
{
  VerdictVector temp = (1.0 - param) * v1;
  temp += param * v2;
  return temp;
}

void VerdictVector::xy_to_rtheta()
{
    //careful about overwriting
  double r_ = length();
  double theta_ = atan2( y(), x() );
  if (theta_ < 0.0)
    theta_ += TWO_VERDICT_PI;

  r( r_ );
  theta( theta_ );
}

void VerdictVector::rtheta_to_xy()
{
    //careful about overwriting
  double x_ =  r() * cos( theta() );
  double y_ =  r() * sin( theta() );

  x( x_ );
  y( y_ );
}

void VerdictVector::rotate(double angle, double )
{
  xy_to_rtheta();
  theta() += angle;
  rtheta_to_xy();
}

void VerdictVector::blow_out(double gamma, double rmin)
{
    // if gamma == 1, then
    // map on a circle : r'^2 = sqrt( 1 - (1-r)^2 )
    // if gamma ==0, then map back to itself
    // in between, linearly interpolate
  xy_to_rtheta();
//  r() = sqrt( (2. - r()) * r() ) * gamma  + r() * (1-gamma);
  assert(gamma > 0.0);
    // the following limits should really be roundoff-based
  if (r() > rmin*1.001 && r() < 1.001) {
    r() = rmin + pow(r(), gamma) * (1.0 - rmin);
  }
  rtheta_to_xy();
}

void VerdictVector::reflect_about_xaxis(double, double )
{
  yVal = -yVal;
}

void VerdictVector::scale_angle(double gamma, double )
{
  const double r_factor = 0.3;
  const double theta_factor = 0.6;

  xy_to_rtheta();

    // if neary 2pi, treat as zero
    // some near zero stuff strays due to roundoff
  if (theta() > TWO_VERDICT_PI - 0.02)
    theta() = 0;
    // the above screws up on big sheets - need to overhaul at the sheet level

  if ( gamma < 1 )
  {
      //squeeze together points of short radius so that
      //long chords won't cross them
    theta() += (VERDICT_PI-theta())*(1-gamma)*theta_factor*(1-r());

      //push away from center of circle, again so long chords won't cross
    r( (r_factor + r()) / (1 + r_factor) );

      //scale angle by gamma
    theta() *= gamma;
  }
  else
  {
      //scale angle by gamma, making sure points nearly 2pi are treated as zero
    double new_theta = theta() * gamma;
    if ( new_theta < 2.5 * VERDICT_PI || r() < 0.2)
      theta( new_theta );
  }
  rtheta_to_xy();
}

double VerdictVector::vector_angle_quick(const VerdictVector& vec1, const VerdictVector& vec2)
{
  //- compute the angle between two vectors in the plane defined by this vector
  // build yAxis and xAxis such that xAxis is the projection of
  // vec1 onto the normal plane of this vector

  // NOTE: vec1 and vec2 are Vectors from the vertex of the angle along
  //       the two sides of the angle.
  //       The angle returned is the right-handed angle around this vector
  //       from vec1 to vec2.

  // NOTE: vector_angle_quick gives exactly the same answer as vector_angle below
  //       providing this vector is normalized.  It does so with two fewer
  //       cross-product evaluations and two fewer vector normalizations.
  //       This can be a substantial time savings if the function is called
  //       a significant number of times (e.g Hexer) ... (jrh 11/28/94)
  // NOTE: vector_angle() is much more robust. Do not use vector_angle_quick()
  //       unless you are very sure of the safety of your input vectors.

  VerdictVector ry = (*this) * vec1;
  VerdictVector rx = ry * (*this);

  double xv = vec2 % rx;
  double yv = vec2 % ry;

  double angle;
  assert( xv != 0.0 || yv != 0.0 );

  angle = atan2( yv, xv );

  if ( angle < 0.0 )
  {
    angle += TWO_VERDICT_PI;
  }
  return angle;
}

VerdictVector vectorRotate(const double angle,
                         const VerdictVector &normalAxis,
                         const VerdictVector &referenceAxis)
{
    // A new coordinate system is created with the xy plane corresponding
    // to the plane normal to the normal axis, and the x axis corresponding to
    // the projection of the reference axis onto the normal plane.  The normal
    // plane is the tangent plane at the root point.  A unit vector is
    // constructed along the local x axis and then rotated by the given
    // ccw angle to form the new point.  The new point, then is a unit
    // distance from the global origin in the tangent plane.

  double x, y;

    // project a unit distance from root along reference axis

  VerdictVector yAxis = normalAxis * referenceAxis;
  VerdictVector xAxis = yAxis * normalAxis;
  yAxis.normalize();
  xAxis.normalize();

  x = cos(angle);
  y = sin(angle);

  xAxis *= x;
  yAxis *= y;
  return VerdictVector(xAxis + yAxis);
}

double VerdictVector::vector_angle(const VerdictVector &vector1,
                                 const VerdictVector &vector2) const
{
    // This routine does not assume that any of the input vectors are of unit
    // length. This routine does not normalize the input vectors.
    // Special cases:
    //     If the normal vector is zero length:
    //         If a new one can be computed from vectors 1 & 2:
    //             the normal is replaced with the vector cross product
    //         else the two vectors are colinear and zero or 2PI is returned.
    //     If the normal is colinear with either (or both) vectors
    //         a new one is computed with the cross products
    //         (and checked again).

    // Check for zero length normal vector
  VerdictVector normal = *this;
  double normal_lensq = normal.length_squared();
  double len_tol = 0.0000001;
  if( normal_lensq <= len_tol )
  {
      // null normal - make it the normal to the plane defined by vector1
      // and vector2. If still null, the vectors are colinear so check
      // for zero or 180 angle.
    normal = vector1 * vector2;
    normal_lensq = normal.length_squared();
    if( normal_lensq <= len_tol )
    {
      double cosine = vector1 % vector2;
      if( cosine > 0.0 ) return 0.0;
      else               return VERDICT_PI;
    }
  }

    //Trap for normal vector colinear to one of the other vectors. If so,
    //use a normal defined by the two vectors.
  double dot_tol = 0.985;
  double dot = vector1 % normal;
  if( dot * dot >= vector1.length_squared() * normal_lensq * dot_tol )
  {
    normal = vector1 * vector2;
    normal_lensq = normal.length_squared();

      //Still problems if all three vectors were colinear
    if( normal_lensq <= len_tol )
    {
      double cosine = vector1 % vector2;
      if( cosine >= 0.0 ) return 0.0;
      else                return VERDICT_PI;
    }
  }
  else
  {
      //The normal and vector1 are not colinear, now check for vector2
    dot = vector2 % normal;
    if( dot * dot >= vector2.length_squared() * normal_lensq * dot_tol )
    {
      normal = vector1 * vector2;
    }
  }

    // Assume a plane such that the normal vector is the plane's normal.
    // Create yAxis perpendicular to both the normal and vector1. yAxis is
    // now in the plane. Create xAxis as the perpendicular to both yAxis and
    // the normal. xAxis is in the plane and is the projection of vector1
    // into the plane.

  normal.normalize();
  VerdictVector yAxis = normal;
  yAxis *= vector1;
  double yv = vector2 % yAxis;
    //  yAxis memory slot will now be used for xAxis
  yAxis *= normal;
  double xv = vector2 % yAxis;

    //  assert(x != 0.0 || y != 0.0);
  if( xv == 0.0 && yv == 0.0 )
  {
    return 0.0;
  }
  double angle = atan2( yv, xv );

  if (angle < 0.0)
  {
    angle += TWO_VERDICT_PI;
  }
  return angle;
}

bool VerdictVector::within_tolerance( const VerdictVector &vectorPtr2,
                                            double tolerance) const
{
  if (( fabs (this->x() - vectorPtr2.x()) < tolerance) &&
      ( fabs (this->y() - vectorPtr2.y()) < tolerance) &&
      ( fabs (this->z() - vectorPtr2.z()) < tolerance)
      )
  {
    return true;
  }

  return false;
}

void VerdictVector::orthogonal_vectors( VerdictVector &vector2,
                                      VerdictVector &vector3 )
{
  double xv[3];
  unsigned short i=0;
  unsigned short imin=0;
  double rmin = 1.0E20;
  unsigned short iperm1[3];
  unsigned short iperm2[3];
  unsigned short cont_flag = 1;
  double vec1[3], vec2[3];
  double rmag;

    // Copy the input vector and normalize it
  VerdictVector vector1 = *this;
  vector1.normalize();

    // Initialize perm flags
  iperm1[0] = 1; iperm1[1] = 2; iperm1[2] = 0;
  iperm2[0] = 2; iperm2[1] = 0; iperm2[2] = 1;

    // Get into the array format we can work with
  vector1.get_xyz( vec1 );

  while (i<3 && cont_flag )
  {
    if (fabs(vec1[i]) < 1e-6)
    {
      vec2[i] = 1.0;
      vec2[iperm1[i]] = 0.0;
      vec2[iperm2[i]] = 0.0;
      cont_flag = 0;
    }

    if (fabs(vec1[i]) < rmin)
    {
      imin = i;
      rmin = fabs(vec1[i]);
    }
    ++i;
  }

  if (cont_flag)
  {
    xv[imin] = 1.0;
    xv[iperm1[imin]] = 0.0;
    xv[iperm2[imin]] = 0.0;

      // Determine cross product
    vec2[0] = vec1[1] * xv[2] - vec1[2] * xv[1];
    vec2[1] = vec1[2] * xv[0] - vec1[0] * xv[2];
    vec2[2] = vec1[0] * xv[1] - vec1[1] * xv[0];

      // Unitize
    rmag = sqrt(vec2[0]*vec2[0] + vec2[1]*vec2[1] + vec2[2]*vec2[2]);
    vec2[0] /= rmag;
    vec2[1] /= rmag;
    vec2[2] /= rmag;
  }

    // Copy 1st orthogonal vector into VerdictVector vector2
  vector2.set( vec2 );

    // Cross vectors to determine last orthogonal vector
  vector3 = vector1 * vector2;
}

//- Find next point from this point using a direction and distance
void VerdictVector::next_point( const VerdictVector &direction,
                              double distance, VerdictVector& out_point )
{
  VerdictVector my_direction = direction;
  my_direction.normalize();

    // Determine next point in space
  out_point.x( xVal + (distance * my_direction.x()) );
  out_point.y( yVal + (distance * my_direction.y()) );
  out_point.z( zVal + (distance * my_direction.z()) );

  return;
}

VerdictVector::VerdictVector(const double xyz[3])
  : xVal(xyz[0]), yVal(xyz[1]), zVal(xyz[2])
{}
<commit_msg>ENH: Merge changes from main tree into VTK-5-2 branch. (cvs -q up -j1.6 -j1.7 Utilities/verdict/VerdictVector.cpp)<commit_after>/*=========================================================================

  Module:    VerdictVector.cpp

  Copyright (c) 2006 Sandia Corporation.
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

/*
 *
 * VerdictVector.cpp contains implementation of Vector operations
 *
 * This file is part of VERDICT
 *
 */

#define VERDICT_EXPORTS

#include "verdict.h"
#include <math.h>
#include "VerdictVector.hpp"
#include <float.h>

#if defined(__BORLANDC__)
#pragma warn -8004 /* "assigned a value that is never used" */
#endif

const double TWO_VERDICT_PI = 2.0 * VERDICT_PI;

VerdictVector &VerdictVector::length(const double new_length)
{
  double len = this->length();
  xVal *= new_length / len;
  yVal *= new_length / len;
  zVal *= new_length / len;
  return *this;
}

double VerdictVector::distance_between(const VerdictVector& test_vector)
{
  double xv = xVal - test_vector.x();
  double yv = yVal - test_vector.y();
  double zv = zVal - test_vector.z();

  return( sqrt( xv * xv + yv * yv + zv * zv ) );
}

/*
void VerdictVector::print_me()
{
  PRINT_INFO("X: %f\n",xVal);
  PRINT_INFO("Y: %f\n",yVal);
  PRINT_INFO("Z: %f\n",zVal);

}
*/

double VerdictVector::interior_angle(const VerdictVector &otherVector)
{
  double cosAngle=0., angleRad=0., len1, len2=0.;

  if (((len1 = this->length()) > 0) && ((len2 = otherVector.length()) > 0))
    cosAngle = (*this % otherVector)/(len1 * len2);
  else
  {
    assert(len1 > 0);
    assert(len2 > 0);
  }

  if ((cosAngle > 1.0) && (cosAngle < 1.0001))
  {
    cosAngle = 1.0;
    angleRad = acos(cosAngle);
  }
  else if (cosAngle < -1.0 && cosAngle > -1.0001)
  {
    cosAngle = -1.0;
    angleRad = acos(cosAngle);
  }
  else if (cosAngle >= -1.0 && cosAngle <= 1.0)
    angleRad = acos(cosAngle);
  else
  {
    assert(cosAngle < 1.0001 && cosAngle > -1.0001);
  }

  return( (angleRad * 180.) / VERDICT_PI );
}

// Interpolate between two vectors.
// Returns (1-param)*v1 + param*v2
VerdictVector v_interpolate(const double param, const VerdictVector &v1,
                        const VerdictVector &v2)
{
  VerdictVector temp = (1.0 - param) * v1;
  temp += param * v2;
  return temp;
}

void VerdictVector::xy_to_rtheta()
{
    //careful about overwriting
  double r_ = length();
  double theta_ = atan2( y(), x() );
  if (theta_ < 0.0)
    theta_ += TWO_VERDICT_PI;

  r( r_ );
  theta( theta_ );
}

void VerdictVector::rtheta_to_xy()
{
    //careful about overwriting
  double x_ =  r() * cos( theta() );
  double y_ =  r() * sin( theta() );

  x( x_ );
  y( y_ );
}

void VerdictVector::rotate(double angle, double )
{
  xy_to_rtheta();
  theta() += angle;
  rtheta_to_xy();
}

void VerdictVector::blow_out(double gamma, double rmin)
{
    // if gamma == 1, then
    // map on a circle : r'^2 = sqrt( 1 - (1-r)^2 )
    // if gamma ==0, then map back to itself
    // in between, linearly interpolate
  xy_to_rtheta();
//  r() = sqrt( (2. - r()) * r() ) * gamma  + r() * (1-gamma);
  assert(gamma > 0.0);
    // the following limits should really be roundoff-based
  if (r() > rmin*1.001 && r() < 1.001) {
    r() = rmin + pow(r(), gamma) * (1.0 - rmin);
  }
  rtheta_to_xy();
}

void VerdictVector::reflect_about_xaxis(double, double )
{
  yVal = -yVal;
}

void VerdictVector::scale_angle(double gamma, double )
{
  const double r_factor = 0.3;
  const double theta_factor = 0.6;

  xy_to_rtheta();

    // if neary 2pi, treat as zero
    // some near zero stuff strays due to roundoff
  if (theta() > TWO_VERDICT_PI - 0.02)
    theta() = 0;
    // the above screws up on big sheets - need to overhaul at the sheet level

  if ( gamma < 1 )
  {
      //squeeze together points of short radius so that
      //long chords won't cross them
    theta() += (VERDICT_PI-theta())*(1-gamma)*theta_factor*(1-r());

      //push away from center of circle, again so long chords won't cross
    r( (r_factor + r()) / (1 + r_factor) );

      //scale angle by gamma
    theta() *= gamma;
  }
  else
  {
      //scale angle by gamma, making sure points nearly 2pi are treated as zero
    double new_theta = theta() * gamma;
    if ( new_theta < 2.5 * VERDICT_PI || r() < 0.2)
      theta( new_theta );
  }
  rtheta_to_xy();
}

double VerdictVector::vector_angle_quick(const VerdictVector& vec1, const VerdictVector& vec2)
{
  //- compute the angle between two vectors in the plane defined by this vector
  // build yAxis and xAxis such that xAxis is the projection of
  // vec1 onto the normal plane of this vector

  // NOTE: vec1 and vec2 are Vectors from the vertex of the angle along
  //       the two sides of the angle.
  //       The angle returned is the right-handed angle around this vector
  //       from vec1 to vec2.

  // NOTE: vector_angle_quick gives exactly the same answer as vector_angle below
  //       providing this vector is normalized.  It does so with two fewer
  //       cross-product evaluations and two fewer vector normalizations.
  //       This can be a substantial time savings if the function is called
  //       a significant number of times (e.g Hexer) ... (jrh 11/28/94)
  // NOTE: vector_angle() is much more robust. Do not use vector_angle_quick()
  //       unless you are very sure of the safety of your input vectors.

  VerdictVector ry = (*this) * vec1;
  VerdictVector rx = ry * (*this);

  double xv = vec2 % rx;
  double yv = vec2 % ry;

  double angle;
  assert( xv != 0.0 || yv != 0.0 );

  angle = atan2( yv, xv );

  if ( angle < 0.0 )
  {
    angle += TWO_VERDICT_PI;
  }
  return angle;
}

VerdictVector v_vector_rotate(
  const double angle, const VerdictVector &normalAxis, const VerdictVector &referenceAxis )
{
    // A new coordinate system is created with the xy plane corresponding
    // to the plane normal to the normal axis, and the x axis corresponding to
    // the projection of the reference axis onto the normal plane.  The normal
    // plane is the tangent plane at the root point.  A unit vector is
    // constructed along the local x axis and then rotated by the given
    // ccw angle to form the new point.  The new point, then is a unit
    // distance from the global origin in the tangent plane.

  double x, y;

    // project a unit distance from root along reference axis

  VerdictVector yAxis = normalAxis * referenceAxis;
  VerdictVector xAxis = yAxis * normalAxis;
  yAxis.normalize();
  xAxis.normalize();

  x = cos(angle);
  y = sin(angle);

  xAxis *= x;
  yAxis *= y;
  return VerdictVector(xAxis + yAxis);
}

double VerdictVector::vector_angle(const VerdictVector &vector1,
                                 const VerdictVector &vector2) const
{
    // This routine does not assume that any of the input vectors are of unit
    // length. This routine does not normalize the input vectors.
    // Special cases:
    //     If the normal vector is zero length:
    //         If a new one can be computed from vectors 1 & 2:
    //             the normal is replaced with the vector cross product
    //         else the two vectors are colinear and zero or 2PI is returned.
    //     If the normal is colinear with either (or both) vectors
    //         a new one is computed with the cross products
    //         (and checked again).

    // Check for zero length normal vector
  VerdictVector normal = *this;
  double normal_lensq = normal.length_squared();
  double len_tol = 0.0000001;
  if( normal_lensq <= len_tol )
  {
      // null normal - make it the normal to the plane defined by vector1
      // and vector2. If still null, the vectors are colinear so check
      // for zero or 180 angle.
    normal = vector1 * vector2;
    normal_lensq = normal.length_squared();
    if( normal_lensq <= len_tol )
    {
      double cosine = vector1 % vector2;
      if( cosine > 0.0 ) return 0.0;
      else               return VERDICT_PI;
    }
  }

    //Trap for normal vector colinear to one of the other vectors. If so,
    //use a normal defined by the two vectors.
  double dot_tol = 0.985;
  double dot = vector1 % normal;
  if( dot * dot >= vector1.length_squared() * normal_lensq * dot_tol )
  {
    normal = vector1 * vector2;
    normal_lensq = normal.length_squared();

      //Still problems if all three vectors were colinear
    if( normal_lensq <= len_tol )
    {
      double cosine = vector1 % vector2;
      if( cosine >= 0.0 ) return 0.0;
      else                return VERDICT_PI;
    }
  }
  else
  {
      //The normal and vector1 are not colinear, now check for vector2
    dot = vector2 % normal;
    if( dot * dot >= vector2.length_squared() * normal_lensq * dot_tol )
    {
      normal = vector1 * vector2;
    }
  }

    // Assume a plane such that the normal vector is the plane's normal.
    // Create yAxis perpendicular to both the normal and vector1. yAxis is
    // now in the plane. Create xAxis as the perpendicular to both yAxis and
    // the normal. xAxis is in the plane and is the projection of vector1
    // into the plane.

  normal.normalize();
  VerdictVector yAxis = normal;
  yAxis *= vector1;
  double yv = vector2 % yAxis;
    //  yAxis memory slot will now be used for xAxis
  yAxis *= normal;
  double xv = vector2 % yAxis;

    //  assert(x != 0.0 || y != 0.0);
  if( xv == 0.0 && yv == 0.0 )
  {
    return 0.0;
  }
  double angle = atan2( yv, xv );

  if (angle < 0.0)
  {
    angle += TWO_VERDICT_PI;
  }
  return angle;
}

bool VerdictVector::within_tolerance( const VerdictVector &vectorPtr2,
                                            double tolerance) const
{
  if (( fabs (this->x() - vectorPtr2.x()) < tolerance) &&
      ( fabs (this->y() - vectorPtr2.y()) < tolerance) &&
      ( fabs (this->z() - vectorPtr2.z()) < tolerance)
      )
  {
    return true;
  }

  return false;
}

void VerdictVector::orthogonal_vectors( VerdictVector &vector2,
                                      VerdictVector &vector3 )
{
  double xv[3];
  unsigned short i=0;
  unsigned short imin=0;
  double rmin = 1.0E20;
  unsigned short iperm1[3];
  unsigned short iperm2[3];
  unsigned short cont_flag = 1;
  double vec1[3], vec2[3];
  double rmag;

    // Copy the input vector and normalize it
  VerdictVector vector1 = *this;
  vector1.normalize();

    // Initialize perm flags
  iperm1[0] = 1; iperm1[1] = 2; iperm1[2] = 0;
  iperm2[0] = 2; iperm2[1] = 0; iperm2[2] = 1;

    // Get into the array format we can work with
  vector1.get_xyz( vec1 );

  while (i<3 && cont_flag )
  {
    if (fabs(vec1[i]) < 1e-6)
    {
      vec2[i] = 1.0;
      vec2[iperm1[i]] = 0.0;
      vec2[iperm2[i]] = 0.0;
      cont_flag = 0;
    }

    if (fabs(vec1[i]) < rmin)
    {
      imin = i;
      rmin = fabs(vec1[i]);
    }
    ++i;
  }

  if (cont_flag)
  {
    xv[imin] = 1.0;
    xv[iperm1[imin]] = 0.0;
    xv[iperm2[imin]] = 0.0;

      // Determine cross product
    vec2[0] = vec1[1] * xv[2] - vec1[2] * xv[1];
    vec2[1] = vec1[2] * xv[0] - vec1[0] * xv[2];
    vec2[2] = vec1[0] * xv[1] - vec1[1] * xv[0];

      // Unitize
    rmag = sqrt(vec2[0]*vec2[0] + vec2[1]*vec2[1] + vec2[2]*vec2[2]);
    vec2[0] /= rmag;
    vec2[1] /= rmag;
    vec2[2] /= rmag;
  }

    // Copy 1st orthogonal vector into VerdictVector vector2
  vector2.set( vec2 );

    // Cross vectors to determine last orthogonal vector
  vector3 = vector1 * vector2;
}

//- Find next point from this point using a direction and distance
void VerdictVector::next_point( const VerdictVector &direction,
                              double distance, VerdictVector& out_point )
{
  VerdictVector my_direction = direction;
  my_direction.normalize();

    // Determine next point in space
  out_point.x( xVal + (distance * my_direction.x()) );
  out_point.y( yVal + (distance * my_direction.y()) );
  out_point.z( zVal + (distance * my_direction.z()) );

  return;
}

VerdictVector::VerdictVector(const double xyz[3])
  : xVal(xyz[0]), yVal(xyz[1]), zVal(xyz[2])
{}
<|endoftext|>


<commit_before>//
// Name: NodeDlg.cpp
//
// Copyright (c) 2002-2003 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#ifdef __GNUG__
	#pragma implementation "NodeDlg.cpp"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
	#pragma hdrstop
#endif

#include "NodeDlg.h"
#include "RoadLayer.h"
#include "Frame.h"
#include "BuilderView.h"

#define MULTIPLE	5000

// WDR: class implementations

//----------------------------------------------------------------------------
// NodeDlg
//----------------------------------------------------------------------------

// WDR: event table for NodeDlg

BEGIN_EVENT_TABLE(NodeDlg,AutoDialog)
	EVT_PAINT(NodeDlg::OnPaint)
	EVT_LISTBOX( ID_INTTYPE, NodeDlg::OnIntType )
	EVT_LISTBOX( ID_ROADNUM, NodeDlg::OnLinkNum )
	EVT_LISTBOX( ID_BEHAVIOR, NodeDlg::OnBehavior )
END_EVENT_TABLE()

NodeDlg::NodeDlg( wxWindow *parent, wxWindowID id, const wxString &title,
	const wxPoint &position, const wxSize& size, long style ) :
	AutoDialog( parent, id, title, position, size, style )
{
	NodePropDialogFunc( this, TRUE );
}

void NodeDlg::SetNode(NodeEdit *pSingleNode, vtRoadLayer *pLayer)
{
	m_pNode = pSingleNode;
	m_pLayer = pLayer;
}

// WDR: handler implementations for NodeDlg

void NodeDlg::OnBehavior( wxCommandEvent &event )
{
	int sel = GetLinkNum()->GetSelection();
	//select new behavior and redraw it on the dialog.
	int itype = GetBehavior()->GetSelection();
	m_pNode->SetIntersectType(sel, (IntersectionType)itype );

	// this may have changed the VIT, so check
	m_pNode->DetermineVisualFromLinks();
	GetIntType()->SetSelection(m_pNode->GetVisual());

	Refresh();
}

void NodeDlg::OnLinkNum( wxCommandEvent &event )
{
	int sel = GetLinkNum()->GetSelection();
	//update what the behavior shows to match that of the road
	GetBehavior()->SetSelection(m_pNode->GetIntersectType(sel));
}

void NodeDlg::OnIntType( wxCommandEvent &event )
{
	//new node behavior
	VisualIntersectionType vitype;
	int sel = GetIntType()->GetSelection();
	if (sel == 0 || sel == 6)	// unknown or multiple not allowed
		return;

	vitype = (VisualIntersectionType) sel;

	if (m_pNode)
		ApplyVisualToNode(m_pNode, vitype);
	else
	{
		for (NodeEdit *n = m_pLayer->GetFirstNode(); n; n=n->GetNext())
		{
			if (!n->IsSelected())
				continue;
			ApplyVisualToNode(n, vitype);
		}
	}
	Refresh();
}

void NodeDlg::ApplyVisualToNode(NodeEdit *pNode, VisualIntersectionType vitype)
{
	int i;

	pNode->SetVisual(vitype);

	//overwrite all behaviors at the roads to match new assigned node behavior.
	switch (vitype)
	{
	case VIT_NONE:
		//make all intersections uncontrolled
		for (i = 0; i < m_pNode->m_iLinks; i++) {
			pNode->SetIntersectType(i, IT_NONE);
		}
		GetBehavior()->SetSelection(IT_NONE);
		break;
	case VIT_ALLSTOPS:
		//make all intersections stop signs
		for (i = 0; i < m_pNode->m_iLinks; i++) {
			pNode->SetIntersectType(i, IT_STOPSIGN);
		}
		GetBehavior()->SetSelection(IT_STOPSIGN);
		break;
	case VIT_ALLLIGHTS:
		//make all intersections lights
		for (i = 0; i < m_pNode->m_iLinks; i++) {
			pNode->SetIntersectType(i, IT_LIGHT);
		}
		GetBehavior()->SetSelection(IT_LIGHT);
		pNode->AdjustForLights();
		break;
	}
}

void NodeDlg::OnInitDialog(wxInitDialogEvent& event)
{
	GetIntType()->Append(_T("Unknown"));
	GetIntType()->Append(_T("Uncontrolled"));
	GetIntType()->Append(_T("All Signal Light(s)"));
	GetIntType()->Append(_T("All Stop Sign(s)"));
	GetIntType()->Append(_T("Signal Light(s)"));
	GetIntType()->Append(_T("Stop Sign(s)"));
	GetIntType()->Append(_T("(multiple)"));

	GetBehavior()->Append(_T("Uncontrolled"));  // IT_NONE
	GetBehavior()->Append(_T("Signal Light"));  // IT_LIGHT
	GetBehavior()->Append(_T("Stop Sign")); // IT_STOPSIGN

	// if we are editing multiple nodes at once, disable some of the
	// editing abilities
	if (!m_pNode)
	{
		GetLinkNum()->Enable(false);
		GetBehavior()->Enable(false);

		// Accumulate state
		int viz = -1;
		for (NodeEdit *n = m_pLayer->GetFirstNode(); n; n=n->GetNext())
		{
			if (!n->IsSelected())
				continue;
			if (viz == -1)
				viz = n->GetVisual();
			if (n->GetVisual() != viz)
				viz = MULTIPLE;
		}

		// Transfer state to control
		if (viz == MULTIPLE)
			GetIntType()->SetSelection(5);
		else
			GetIntType()->SetSelection(viz);
	}
	else
	{
		// single road
		wxString string;
		for (int i = 0; i < m_pNode->m_iLinks; i++)
		{
			string.Printf(_T("%i"), i);
			GetLinkNum()->Append(string);
		}
		GetLinkNum()->SetSelection(0);
		GetIntType()->SetSelection(m_pNode->GetVisual());
		int itype = m_pNode->GetIntersectType(0);
		GetBehavior()->SetSelection(itype);
	}
}

void NodeDlg::OnPaint(wxPaintEvent& event)
{
//  wxEvtHandler::OnPaint(event);
	wxPaintDC dc(this);
	OnDraw(dc);
}

static wxPoint buf[10];

//draw the node structure on the dialog box.
void NodeDlg::OnDraw(wxDC &dc)
{
	if (!m_pNode)
		return;

	vtScaledView *pView = GetMainFrame()->GetView();
	float fSaveScale = pView->GetScale();
	pView->SetScale(1.0f);

	wxPoint off;
	off.x = pView->sx(m_pNode->m_p.x);
	off.y = pView->sy(m_pNode->m_p.y);
	dc.SetDeviceOrigin(-off.x + 315, -off.y +125);
	dc.DrawLine(0, 0, 400, 400);
	m_pNode->Draw(&dc, pView);

	wxString string;
	for (int i = 0; i < m_pNode->m_iLinks; i++)
	{
		LinkEdit *pR = m_pNode->GetLink(i);
		pR->Draw(&dc, pView);

		//we need to use the original node here because the roads point to it.
		DPoint2 close = m_pNode->find_adjacent_roadpoint2d(pR);
		DPoint2 vector = close - m_pNode->m_p;
		vector.Normalize();
		IPoint2 vec;

		vec.x = (int)(off.x + vector.x*20);
		vec.y = (int)(off.y - vector.y*20);

		//draw signal lights or stop signs as necessary.
		dc.SetLogicalFunction(wxCOPY);
		wxPen pen;

		switch (m_pNode->GetIntersectType(i))
		{
			case IT_STOPSIGN:
				pen.SetColour(128,0,0);
				dc.SetPen(pen);
				vec.x += 2;
				vec.y += 6;
				buf[0].x = vec.x; buf[0].y = vec.y;
				vec.x -= 4;
				buf[1].x = vec.x; buf[1].y = vec.y;
				vec.x -= 3;
				vec.y -= 3;
				buf[2].x = vec.x; buf[2].y = vec.y;
				vec.y -= 4;
				buf[3].x = vec.x; buf[3].y = vec.y;
				vec.x += 3;
				vec.y -= 3;
				buf[4].x = vec.x; buf[4].y = vec.y;
				vec.x += 4;
				buf[5].x = vec.x; buf[5].y = vec.y;
				vec.x += 3;
				vec.y += 3;
				buf[6].x = vec.x; buf[6].y = vec.y;
				vec.y += 4;
				buf[7].x = vec.x; buf[7].y = vec.y;
				vec.x -= 3;
				vec.y += 3;
				buf[8].x = vec.x; buf[8].y = vec.y;
				dc.DrawLines(9, buf);
				break;
			case IT_LIGHT:
				wxBrush brush;
				switch (m_pNode->GetLightStatus(i)) {
					case LT_INVALID:
						pen.SetColour(0,0,0);
						brush.SetColour(0,0,0);
						break;
					case LT_RED:
						pen.SetColour(128,0,0);
						brush.SetColour(128,0,0);
						break;
					case LT_YELLOW:
						pen.SetColour(0,128,128);
						brush.SetColour(0,128,128);
						break;
					case LT_GREEN:
						pen.SetColour(0,128,0);
						brush.SetColour(0,128,0);
						break;
					default:
				//unrecognized
						pen.SetColour(0,0,255);
						brush.SetColour(0,0,255);
				}

				dc.SetPen(pen);
				dc.SetBrush(brush);
				wxRect box;
				int radius = 4;
				box.y = vec.y - radius;
				box.height = (radius << 1);
				box.x = vec.x - radius;
				box.width = (radius << 1);
				dc.DrawEllipse(box.x, box.y, box.width, box.height);
				break;
		}

		vec.x = (int)(off.x + vector.x*40);
		vec.y = (int)(off.y - vector.y*40);
		string.Printf(_T("%i"), i);
		//draw text labels
		dc.DrawText(string, vec.x-10, vec.y-10);
	}
	pView->SetScale(fSaveScale);
}

<commit_msg>fixed multi-select (RJ)<commit_after>//
// Name: NodeDlg.cpp
//
// Copyright (c) 2002-2003 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#ifdef __GNUG__
	#pragma implementation "NodeDlg.cpp"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
	#pragma hdrstop
#endif

#include "NodeDlg.h"
#include "RoadLayer.h"
#include "Frame.h"
#include "BuilderView.h"

#define MULTIPLE	5000

// WDR: class implementations

//----------------------------------------------------------------------------
// NodeDlg
//----------------------------------------------------------------------------

// WDR: event table for NodeDlg

BEGIN_EVENT_TABLE(NodeDlg,AutoDialog)
	EVT_PAINT(NodeDlg::OnPaint)
	EVT_LISTBOX( ID_INTTYPE, NodeDlg::OnIntType )
	EVT_LISTBOX( ID_ROADNUM, NodeDlg::OnLinkNum )
	EVT_LISTBOX( ID_BEHAVIOR, NodeDlg::OnBehavior )
END_EVENT_TABLE()

NodeDlg::NodeDlg( wxWindow *parent, wxWindowID id, const wxString &title,
	const wxPoint &position, const wxSize& size, long style ) :
	AutoDialog( parent, id, title, position, size, style )
{
	NodePropDialogFunc( this, TRUE );
}

void NodeDlg::SetNode(NodeEdit *pSingleNode, vtRoadLayer *pLayer)
{
	m_pNode = pSingleNode;
	m_pLayer = pLayer;
}

// WDR: handler implementations for NodeDlg

void NodeDlg::OnBehavior( wxCommandEvent &event )
{
	int sel = GetLinkNum()->GetSelection();
	//select new behavior and redraw it on the dialog.
	int itype = GetBehavior()->GetSelection();
	m_pNode->SetIntersectType(sel, (IntersectionType)itype );

	// this may have changed the VIT, so check
	m_pNode->DetermineVisualFromLinks();
	GetIntType()->SetSelection(m_pNode->GetVisual());

	Refresh();
}

void NodeDlg::OnLinkNum( wxCommandEvent &event )
{
	int sel = GetLinkNum()->GetSelection();
	//update what the behavior shows to match that of the road
	GetBehavior()->SetSelection(m_pNode->GetIntersectType(sel));
}

void NodeDlg::OnIntType( wxCommandEvent &event )
{
	//new node behavior
	VisualIntersectionType vitype;
	int sel = GetIntType()->GetSelection();
	if (sel == 0 || sel == 6)	// unknown or multiple not allowed
		return;

	vitype = (VisualIntersectionType) sel;

	if (m_pNode)
		ApplyVisualToNode(m_pNode, vitype);
	else
	{
		for (NodeEdit *n = m_pLayer->GetFirstNode(); n; n=n->GetNext())
		{
			if (!n->IsSelected())
				continue;
			ApplyVisualToNode(n, vitype);
		}
	}
	Refresh();
}

void NodeDlg::ApplyVisualToNode(NodeEdit *pNode, VisualIntersectionType vitype)
{
	int i;

	pNode->SetVisual(vitype);

	//overwrite all behaviors at the roads to match new assigned node behavior.
	switch (vitype)
	{
	case VIT_NONE:
		//make all intersections uncontrolled
		for (i = 0; i < m_pNode->m_iLinks; i++) {
			pNode->SetIntersectType(i, IT_NONE);
		}
		GetBehavior()->SetSelection(IT_NONE);
		break;
	case VIT_ALLSTOPS:
		//make all intersections stop signs
		for (i = 0; i < m_pNode->m_iLinks; i++) {
			pNode->SetIntersectType(i, IT_STOPSIGN);
		}
		GetBehavior()->SetSelection(IT_STOPSIGN);
		break;
	case VIT_ALLLIGHTS:
		//make all intersections lights
		for (i = 0; i < m_pNode->m_iLinks; i++) {
			pNode->SetIntersectType(i, IT_LIGHT);
		}
		GetBehavior()->SetSelection(IT_LIGHT);
		pNode->AdjustForLights();
		break;
	}
}

void NodeDlg::OnInitDialog(wxInitDialogEvent& event)
{
	GetIntType()->Append(_T("Unknown"));
	GetIntType()->Append(_T("Uncontrolled"));
	GetIntType()->Append(_T("All Signal Light(s)"));
	GetIntType()->Append(_T("All Stop Sign(s)"));
	GetIntType()->Append(_T("Signal Light(s)"));
	GetIntType()->Append(_T("Stop Sign(s)"));
	GetIntType()->Append(_T("(multiple)"));

	GetBehavior()->Append(_T("Uncontrolled"));  // IT_NONE
	GetBehavior()->Append(_T("Signal Light"));  // IT_LIGHT
	GetBehavior()->Append(_T("Stop Sign")); // IT_STOPSIGN

	// if we are editing multiple nodes at once, disable some of the
	// editing abilities
	if (!m_pNode)
	{
		GetLinkNum()->Enable(false);
		GetBehavior()->Enable(false);

		// Accumulate state
		int viz = -1;
		for (NodeEdit *n = m_pLayer->GetFirstNode(); n; n=n->GetNext())
		{
			if (!n->IsSelected())
				continue;
			if (viz == -1)
				viz = n->GetVisual();
			if (n->GetVisual() != viz)
				viz = MULTIPLE;
		}

		// Transfer state to control
		if (viz == MULTIPLE)
			GetIntType()->SetSelection(6);
		else
			GetIntType()->SetSelection(viz);
	}
	else
	{
		// single road
		wxString string;
		for (int i = 0; i < m_pNode->m_iLinks; i++)
		{
			string.Printf(_T("%i"), i);
			GetLinkNum()->Append(string);
		}
		GetLinkNum()->SetSelection(0);
		GetIntType()->SetSelection(m_pNode->GetVisual());
		int itype = m_pNode->GetIntersectType(0);
		GetBehavior()->SetSelection(itype);
	}
}

void NodeDlg::OnPaint(wxPaintEvent& event)
{
//  wxEvtHandler::OnPaint(event);
	wxPaintDC dc(this);
	OnDraw(dc);
}

static wxPoint buf[10];

//draw the node structure on the dialog box.
void NodeDlg::OnDraw(wxDC &dc)
{
	if (!m_pNode)
		return;

	vtScaledView *pView = GetMainFrame()->GetView();
	float fSaveScale = pView->GetScale();
	pView->SetScale(1.0f);

	wxPoint off;
	off.x = pView->sx(m_pNode->m_p.x);
	off.y = pView->sy(m_pNode->m_p.y);
	dc.SetDeviceOrigin(-off.x + 315, -off.y +125);
	dc.DrawLine(0, 0, 400, 400);
	m_pNode->Draw(&dc, pView);

	wxString string;
	for (int i = 0; i < m_pNode->m_iLinks; i++)
	{
		LinkEdit *pR = m_pNode->GetLink(i);
		pR->Draw(&dc, pView);

		//we need to use the original node here because the roads point to it.
		DPoint2 close = m_pNode->find_adjacent_roadpoint2d(pR);
		DPoint2 vector = close - m_pNode->m_p;
		vector.Normalize();
		IPoint2 vec;

		vec.x = (int)(off.x + vector.x*20);
		vec.y = (int)(off.y - vector.y*20);

		//draw signal lights or stop signs as necessary.
		dc.SetLogicalFunction(wxCOPY);
		wxPen pen;

		switch (m_pNode->GetIntersectType(i))
		{
			case IT_STOPSIGN:
				pen.SetColour(128,0,0);
				dc.SetPen(pen);
				vec.x += 2;
				vec.y += 6;
				buf[0].x = vec.x; buf[0].y = vec.y;
				vec.x -= 4;
				buf[1].x = vec.x; buf[1].y = vec.y;
				vec.x -= 3;
				vec.y -= 3;
				buf[2].x = vec.x; buf[2].y = vec.y;
				vec.y -= 4;
				buf[3].x = vec.x; buf[3].y = vec.y;
				vec.x += 3;
				vec.y -= 3;
				buf[4].x = vec.x; buf[4].y = vec.y;
				vec.x += 4;
				buf[5].x = vec.x; buf[5].y = vec.y;
				vec.x += 3;
				vec.y += 3;
				buf[6].x = vec.x; buf[6].y = vec.y;
				vec.y += 4;
				buf[7].x = vec.x; buf[7].y = vec.y;
				vec.x -= 3;
				vec.y += 3;
				buf[8].x = vec.x; buf[8].y = vec.y;
				dc.DrawLines(9, buf);
				break;
			case IT_LIGHT:
				wxBrush brush;
				switch (m_pNode->GetLightStatus(i)) {
					case LT_INVALID:
						pen.SetColour(0,0,0);
						brush.SetColour(0,0,0);
						break;
					case LT_RED:
						pen.SetColour(128,0,0);
						brush.SetColour(128,0,0);
						break;
					case LT_YELLOW:
						pen.SetColour(0,128,128);
						brush.SetColour(0,128,128);
						break;
					case LT_GREEN:
						pen.SetColour(0,128,0);
						brush.SetColour(0,128,0);
						break;
					default:
				//unrecognized
						pen.SetColour(0,0,255);
						brush.SetColour(0,0,255);
				}

				dc.SetPen(pen);
				dc.SetBrush(brush);
				wxRect box;
				int radius = 4;
				box.y = vec.y - radius;
				box.height = (radius << 1);
				box.x = vec.x - radius;
				box.width = (radius << 1);
				dc.DrawEllipse(box.x, box.y, box.width, box.height);
				break;
		}

		vec.x = (int)(off.x + vector.x*40);
		vec.y = (int)(off.y - vector.y*40);
		string.Printf(_T("%i"), i);
		//draw text labels
		dc.DrawText(string, vec.x-10, vec.y-10);
	}
	pView->SetScale(fSaveScale);
}

<|endoftext|>


<commit_before>//
// Name: NodeDlg.cpp
//
// Copyright (c) 2002-2004 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#ifdef __GNUG__
	#pragma implementation "NodeDlg.cpp"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
	#pragma hdrstop
#endif

#include "NodeDlg.h"
#include "RoadLayer.h"
#include "Frame.h"
#include "BuilderView.h"

#define MULTIPLE	5000

static wxPoint buf[10];

void NodeDlgView::OnDraw(wxDC &dc)
{
	if (!m_pNode)
		return;

	wxPoint center;
	screen(m_pNode->m_p, center);
	m_pNode->Draw(&dc, this);

	wxString string;
	for (int i = 0; i < m_pNode->m_iLinks; i++)
	{
		LinkEdit *pR = m_pNode->GetLink(i);
		pR->Draw(&dc, this);

		//we need to use the original node here because the roads point to it.
		DPoint2 close = m_pNode->GetAdjacentLinkPoint2d(i);
		DPoint2 vector = close - m_pNode->m_p;
		vector.Normalize();
		IPoint2 vec;

		vec.x = (int)(center.x + vector.x*20);
		vec.y = (int)(center.y - vector.y*20);

		//draw signal lights or stop signs as necessary.
		dc.SetLogicalFunction(wxCOPY);
		wxPen pen;

		switch (m_pNode->GetIntersectType(i))
		{
		case IT_STOPSIGN:
			pen.SetColour(128,0,0);
			dc.SetPen(pen);
			vec.x += 2;
			vec.y += 6;
			buf[0].x = vec.x; buf[0].y = vec.y;
			vec.x -= 4;
			buf[1].x = vec.x; buf[1].y = vec.y;
			vec.x -= 3;
			vec.y -= 3;
			buf[2].x = vec.x; buf[2].y = vec.y;
			vec.y -= 4;
			buf[3].x = vec.x; buf[3].y = vec.y;
			vec.x += 3;
			vec.y -= 3;
			buf[4].x = vec.x; buf[4].y = vec.y;
			vec.x += 4;
			buf[5].x = vec.x; buf[5].y = vec.y;
			vec.x += 3;
			vec.y += 3;
			buf[6].x = vec.x; buf[6].y = vec.y;
			vec.y += 4;
			buf[7].x = vec.x; buf[7].y = vec.y;
			vec.x -= 3;
			vec.y += 3;
			buf[8].x = vec.x; buf[8].y = vec.y;
			dc.DrawLines(9, buf);
			break;
		case IT_LIGHT:
			wxBrush brush;
			switch (m_pNode->GetLightStatus(i))
			{
			case LT_INVALID:
				pen.SetColour(0,0,0);
				brush.SetColour(0,0,0);
				break;
			case LT_RED:
				pen.SetColour(128,0,0);
				brush.SetColour(128,0,0);
				break;
			case LT_YELLOW:
				pen.SetColour(0,128,128);
				brush.SetColour(0,128,128);
				break;
			case LT_GREEN:
				pen.SetColour(0,128,0);
				brush.SetColour(0,128,0);
				break;
			default:
				//unrecognized
				pen.SetColour(0,0,255);
				brush.SetColour(0,0,255);
				break;
			}

			dc.SetPen(pen);
			dc.SetBrush(brush);
			wxRect box;
			int radius = 4;
			box.y = vec.y - radius;
			box.height = (radius << 1);
			box.x = vec.x - radius;
			box.width = (radius << 1);
			dc.DrawEllipse(box.x, box.y, box.width, box.height);
			break;
		}

		//draw text labels
		vec.x = (int)(center.x + vector.x*40);
		vec.y = (int)(center.y - vector.y*40);
		string.Printf(_T("%i"), i);
		dc.DrawText(string, vec.x-10, vec.y-10);
	}
}

// WDR: class implementations

//----------------------------------------------------------------------------
// NodeDlg
//----------------------------------------------------------------------------

// WDR: event table for NodeDlg

BEGIN_EVENT_TABLE(NodeDlg, AutoDialog)
	EVT_INIT_DIALOG (NodeDlg::OnInitDialog)
	EVT_LISTBOX( ID_INTTYPE, NodeDlg::OnIntType )
	EVT_LISTBOX( ID_ROADNUM, NodeDlg::OnLinkNum )
	EVT_LISTBOX( ID_BEHAVIOR, NodeDlg::OnBehavior )
END_EVENT_TABLE()

NodeDlg::NodeDlg( wxWindow *parent, wxWindowID id, const wxString &title,
	const wxPoint &position, const wxSize& size, long style ) :
	AutoDialog( parent, id, title, position, size, style )
{
	NodePropDialogFunc( this, TRUE );

	GetIntType()->Append(_("Unknown"));
	GetIntType()->Append(_("Uncontrolled"));
	GetIntType()->Append(_("All Traffic Light(s)"));
	GetIntType()->Append(_("All Stop Sign(s)"));
	GetIntType()->Append(_("Traffic Light(s)"));
	GetIntType()->Append(_("Stop Sign(s)"));
	GetIntType()->Append(_("(multiple)"));

	GetBehavior()->Append(_("Uncontrolled"));	// IT_NONE
	GetBehavior()->Append(_("Traffic Light"));	// IT_LIGHT
	GetBehavior()->Append(_("Stop Sign"));		// IT_STOPSIGN

	m_pView = (NodeDlgView *) FindWindow( ID_SCROLLED );

	vtScaledView *pMainView = GetMainFrame()->GetView();
	float fScale = pMainView->GetScale();
	m_pView->SetScale(fScale);
}

void NodeDlg::SetNode(NodeEdit *pSingleNode, vtRoadLayer *pLayer)
{
	m_pNode = pSingleNode;
	m_pLayer = pLayer;
	m_pView->m_pNode = m_pNode;
	if (NULL != m_pNode)
		m_pView->ZoomToPoint(m_pNode->m_p);
}

// WDR: handler implementations for NodeDlg

void NodeDlg::OnBehavior( wxCommandEvent &event )
{
	int sel = GetLinkNum()->GetSelection();
	//select new behavior and redraw it on the dialog.
	int itype = GetBehavior()->GetSelection();
	m_pNode->SetIntersectType(sel, (IntersectionType)itype );

	// this may have changed the VIT, so check
	m_pNode->DetermineVisualFromLinks();
	GetIntType()->SetSelection(m_pNode->GetVisual());

	Refresh();
}

void NodeDlg::OnLinkNum( wxCommandEvent &event )
{
	int sel = GetLinkNum()->GetSelection();
	//update what the behavior shows to match that of the road
	GetBehavior()->SetSelection(m_pNode->GetIntersectType(sel));
}

void NodeDlg::OnIntType( wxCommandEvent &event )
{
	//new node behavior
	VisualIntersectionType vitype;
	int sel = GetIntType()->GetSelection();
	if (sel == 0 || sel == 6)	// unknown or multiple not allowed
		return;

	vitype = (VisualIntersectionType) sel;

	if (m_pNode)
		ApplyVisualToNode(m_pNode, vitype);
	else
	{
		for (NodeEdit *n = m_pLayer->GetFirstNode(); n; n=n->GetNext())
		{
			if (!n->IsSelected())
				continue;
			ApplyVisualToNode(n, vitype);
		}
	}
	Refresh();
}

void NodeDlg::ApplyVisualToNode(NodeEdit *pNode, VisualIntersectionType vitype)
{
	int i;

	pNode->SetVisual(vitype);

	//overwrite all behaviors at the roads to match new assigned node behavior.
	switch (vitype)
	{
	case VIT_NONE:
		//make all intersections uncontrolled
		for (i = 0; i < pNode->m_iLinks; i++) {
			pNode->SetIntersectType(i, IT_NONE);
		}
		GetBehavior()->SetSelection(IT_NONE);
		break;
	case VIT_ALLSTOPS:
		//make all intersections stop signs
		for (i = 0; i < pNode->m_iLinks; i++) {
			pNode->SetIntersectType(i, IT_STOPSIGN);
		}
		GetBehavior()->SetSelection(IT_STOPSIGN);
		break;
	case VIT_ALLLIGHTS:
		//make all intersections lights
		for (i = 0; i < pNode->m_iLinks; i++) {
			pNode->SetIntersectType(i, IT_LIGHT);
		}
		GetBehavior()->SetSelection(IT_LIGHT);
		pNode->AdjustForLights();
		break;
	}
}

void NodeDlg::OnInitDialog(wxInitDialogEvent& event)
{
	// if we are editing multiple nodes at once, disable some of the
	// editing abilities
	if (!m_pNode)
	{
		GetLinkNum()->Enable(false);
		GetBehavior()->Enable(false);

		// Accumulate state
		int viz = -1;
		for (NodeEdit *n = m_pLayer->GetFirstNode(); n; n=n->GetNext())
		{
			if (!n->IsSelected())
				continue;
			if (viz == -1)
				viz = n->GetVisual();
			if (n->GetVisual() != viz)
				viz = MULTIPLE;
		}

		// Transfer state to control
		if (viz == MULTIPLE)
			GetIntType()->SetSelection(6);
		else
			GetIntType()->SetSelection(viz);
	}
	else
	{
		// single road
		wxString string;
		for (int i = 0; i < m_pNode->m_iLinks; i++)
		{
			string.Printf(_T("%i"), i);
			GetLinkNum()->Append(string);
		}
		GetLinkNum()->SetSelection(0);
		GetIntType()->SetSelection(m_pNode->GetVisual());
		int itype = m_pNode->GetIntersectType(0);
		GetBehavior()->SetSelection(itype);
	}
}

<commit_msg>safety check<commit_after>//
// Name: NodeDlg.cpp
//
// Copyright (c) 2002-2004 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#ifdef __GNUG__
	#pragma implementation "NodeDlg.cpp"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
	#pragma hdrstop
#endif

#include "NodeDlg.h"
#include "RoadLayer.h"
#include "Frame.h"
#include "BuilderView.h"

#define MULTIPLE	5000

static wxPoint buf[10];

void NodeDlgView::OnDraw(wxDC &dc)
{
	if (!m_pNode)
		return;

	wxPoint center;
	screen(m_pNode->m_p, center);
	m_pNode->Draw(&dc, this);

	wxString string;
	for (int i = 0; i < m_pNode->m_iLinks; i++)
	{
		LinkEdit *pR = m_pNode->GetLink(i);
		pR->Draw(&dc, this);

		//we need to use the original node here because the roads point to it.
		DPoint2 close = m_pNode->GetAdjacentLinkPoint2d(i);
		DPoint2 vector = close - m_pNode->m_p;
		vector.Normalize();
		IPoint2 vec;

		vec.x = (int)(center.x + vector.x*20);
		vec.y = (int)(center.y - vector.y*20);

		//draw signal lights or stop signs as necessary.
		dc.SetLogicalFunction(wxCOPY);
		wxPen pen;

		switch (m_pNode->GetIntersectType(i))
		{
		case IT_STOPSIGN:
			pen.SetColour(128,0,0);
			dc.SetPen(pen);
			vec.x += 2;
			vec.y += 6;
			buf[0].x = vec.x; buf[0].y = vec.y;
			vec.x -= 4;
			buf[1].x = vec.x; buf[1].y = vec.y;
			vec.x -= 3;
			vec.y -= 3;
			buf[2].x = vec.x; buf[2].y = vec.y;
			vec.y -= 4;
			buf[3].x = vec.x; buf[3].y = vec.y;
			vec.x += 3;
			vec.y -= 3;
			buf[4].x = vec.x; buf[4].y = vec.y;
			vec.x += 4;
			buf[5].x = vec.x; buf[5].y = vec.y;
			vec.x += 3;
			vec.y += 3;
			buf[6].x = vec.x; buf[6].y = vec.y;
			vec.y += 4;
			buf[7].x = vec.x; buf[7].y = vec.y;
			vec.x -= 3;
			vec.y += 3;
			buf[8].x = vec.x; buf[8].y = vec.y;
			dc.DrawLines(9, buf);
			break;
		case IT_LIGHT:
			wxBrush brush;
			switch (m_pNode->GetLightStatus(i))
			{
			case LT_INVALID:
				pen.SetColour(0,0,0);
				brush.SetColour(0,0,0);
				break;
			case LT_RED:
				pen.SetColour(128,0,0);
				brush.SetColour(128,0,0);
				break;
			case LT_YELLOW:
				pen.SetColour(0,128,128);
				brush.SetColour(0,128,128);
				break;
			case LT_GREEN:
				pen.SetColour(0,128,0);
				brush.SetColour(0,128,0);
				break;
			default:
				//unrecognized
				pen.SetColour(0,0,255);
				brush.SetColour(0,0,255);
				break;
			}

			dc.SetPen(pen);
			dc.SetBrush(brush);
			wxRect box;
			int radius = 4;
			box.y = vec.y - radius;
			box.height = (radius << 1);
			box.x = vec.x - radius;
			box.width = (radius << 1);
			dc.DrawEllipse(box.x, box.y, box.width, box.height);
			break;
		}

		//draw text labels
		vec.x = (int)(center.x + vector.x*40);
		vec.y = (int)(center.y - vector.y*40);
		string.Printf(_T("%i"), i);
		dc.DrawText(string, vec.x-10, vec.y-10);
	}
}

// WDR: class implementations

//----------------------------------------------------------------------------
// NodeDlg
//----------------------------------------------------------------------------

// WDR: event table for NodeDlg

BEGIN_EVENT_TABLE(NodeDlg, AutoDialog)
	EVT_INIT_DIALOG (NodeDlg::OnInitDialog)
	EVT_LISTBOX( ID_INTTYPE, NodeDlg::OnIntType )
	EVT_LISTBOX( ID_ROADNUM, NodeDlg::OnLinkNum )
	EVT_LISTBOX( ID_BEHAVIOR, NodeDlg::OnBehavior )
END_EVENT_TABLE()

NodeDlg::NodeDlg( wxWindow *parent, wxWindowID id, const wxString &title,
	const wxPoint &position, const wxSize& size, long style ) :
	AutoDialog( parent, id, title, position, size, style )
{
	NodePropDialogFunc( this, TRUE );

	GetIntType()->Append(_("Unknown"));
	GetIntType()->Append(_("Uncontrolled"));
	GetIntType()->Append(_("All Traffic Light(s)"));
	GetIntType()->Append(_("All Stop Sign(s)"));
	GetIntType()->Append(_("Traffic Light(s)"));
	GetIntType()->Append(_("Stop Sign(s)"));
	GetIntType()->Append(_("(multiple)"));

	GetBehavior()->Append(_("Uncontrolled"));	// IT_NONE
	GetBehavior()->Append(_("Traffic Light"));	// IT_LIGHT
	GetBehavior()->Append(_("Stop Sign"));		// IT_STOPSIGN

	m_pView = (NodeDlgView *) FindWindow( ID_SCROLLED );

	vtScaledView *pMainView = GetMainFrame()->GetView();
	float fScale = pMainView->GetScale();
	m_pView->SetScale(fScale);
}

void NodeDlg::SetNode(NodeEdit *pSingleNode, vtRoadLayer *pLayer)
{
	m_pNode = pSingleNode;
	m_pLayer = pLayer;
	m_pView->m_pNode = m_pNode;
	if (NULL != m_pNode)
		m_pView->ZoomToPoint(m_pNode->m_p);
}

// WDR: handler implementations for NodeDlg

void NodeDlg::OnBehavior( wxCommandEvent &event )
{
	int sel = GetLinkNum()->GetSelection();
	//select new behavior and redraw it on the dialog.
	int itype = GetBehavior()->GetSelection();
	m_pNode->SetIntersectType(sel, (IntersectionType)itype );

	// this may have changed the VIT, so check
	m_pNode->DetermineVisualFromLinks();
	GetIntType()->SetSelection(m_pNode->GetVisual());

	Refresh();
}

void NodeDlg::OnLinkNum( wxCommandEvent &event )
{
	int sel = GetLinkNum()->GetSelection();
	//update what the behavior shows to match that of the road
	GetBehavior()->SetSelection(m_pNode->GetIntersectType(sel));
}

void NodeDlg::OnIntType( wxCommandEvent &event )
{
	//new node behavior
	VisualIntersectionType vitype;
	int sel = GetIntType()->GetSelection();
	if (sel == 0 || sel == 6)	// unknown or multiple not allowed
		return;

	vitype = (VisualIntersectionType) sel;

	if (m_pNode)
		ApplyVisualToNode(m_pNode, vitype);
	else
	{
		for (NodeEdit *n = m_pLayer->GetFirstNode(); n; n=n->GetNext())
		{
			if (!n->IsSelected())
				continue;
			ApplyVisualToNode(n, vitype);
		}
	}
	Refresh();
}

void NodeDlg::ApplyVisualToNode(NodeEdit *pNode, VisualIntersectionType vitype)
{
	int i;

	pNode->SetVisual(vitype);

	//overwrite all behaviors at the roads to match new assigned node behavior.
	switch (vitype)
	{
	case VIT_NONE:
		//make all intersections uncontrolled
		for (i = 0; i < pNode->m_iLinks; i++) {
			pNode->SetIntersectType(i, IT_NONE);
		}
		GetBehavior()->SetSelection(IT_NONE);
		break;
	case VIT_ALLSTOPS:
		//make all intersections stop signs
		for (i = 0; i < pNode->m_iLinks; i++) {
			pNode->SetIntersectType(i, IT_STOPSIGN);
		}
		GetBehavior()->SetSelection(IT_STOPSIGN);
		break;
	case VIT_ALLLIGHTS:
		//make all intersections lights
		for (i = 0; i < pNode->m_iLinks; i++) {
			pNode->SetIntersectType(i, IT_LIGHT);
		}
		GetBehavior()->SetSelection(IT_LIGHT);
		pNode->AdjustForLights();
		break;
	}
}

void NodeDlg::OnInitDialog(wxInitDialogEvent& event)
{
	// if we are editing multiple nodes at once, disable some of the
	// editing abilities
	if (!m_pNode)
	{
		GetLinkNum()->Enable(false);
		GetBehavior()->Enable(false);

		// Accumulate state
		int viz = -1;
		for (NodeEdit *n = m_pLayer->GetFirstNode(); n; n=n->GetNext())
		{
			if (!n->IsSelected())
				continue;
			if (viz == -1)
				viz = n->GetVisual();
			if (n->GetVisual() != viz)
				viz = MULTIPLE;
		}

		// Transfer state to control
		if (viz == MULTIPLE)
			GetIntType()->SetSelection(6);
		else
			GetIntType()->SetSelection(viz);
	}
	else
	{
		// single road
		wxString string;
		for (int i = 0; i < m_pNode->m_iLinks; i++)
		{
			string.Printf(_T("%i"), i);
			GetLinkNum()->Append(string);
		}
		if (m_pNode->m_iLinks > 0)
			GetLinkNum()->SetSelection(0);
		GetIntType()->SetSelection(m_pNode->GetVisual());
		int itype = m_pNode->GetIntersectType(0);
		GetBehavior()->SetSelection(itype);
	}
}

<|endoftext|>


<commit_before>//
// (Natural) Cubic Spline class.
//
// Originally adapted from some LGPL code found around the net.
//
// Copyright (c) 2004 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#include "CubicSpline.h"

CubicSpline::CubicSpline()
{
	m_pCoeff0 = m_pCoeff1 = m_pCoeff2 = m_pCoeff3 = NULL;
	m_iPoints = 0;
}

CubicSpline::~CubicSpline()
{
	Cleanup();
}

/** Re-initialization. */
void CubicSpline::Cleanup()
{
	if (m_veclist.size() > 0)
		m_veclist.erase(m_veclist.begin(), m_veclist.end());

	if (m_pCoeff0!=NULL)
		delete [] m_pCoeff0;
	if (m_pCoeff1!=NULL)
		delete [] m_pCoeff1;
	if (m_pCoeff2!=NULL)
		delete [] m_pCoeff2;
	if (m_pCoeff3!=NULL)
		delete [] m_pCoeff3;
	m_pCoeff0 = m_pCoeff1 = m_pCoeff2 = m_pCoeff3 = NULL;

	m_iPoints = 0;
}

/** Adds an interpolation point. */
int CubicSpline::AddPoint(const DPoint3 &vec)
{
	// TO DO: error check
	int npar = m_veclist.size();
	m_veclist.push_back(vec);
	return npar;
}

/** Generate the spline coeffs. */
bool CubicSpline::Generate()
{
	int i;
	m_iPoints = m_veclist.size();

	if (m_iPoints==0)
		return false; // there are no interp points!!

	// allocate coefficient table
	m_pCoeff0 = new DPoint3[m_iPoints];
	m_pCoeff1 = new DPoint3[m_iPoints];
	m_pCoeff2 = new DPoint3[m_iPoints];
	m_pCoeff3 = new DPoint3[m_iPoints];

	std::list<DPoint3>::const_iterator iter = m_veclist.begin();

	if (m_iPoints==1)
	{
		// cannot calculate spline, return constant polynom
		DPoint3 p0 = *iter;

		m_pCoeff3[0] = DPoint3(0,0,0);
		m_pCoeff2[0] = DPoint3(0,0,0);
		m_pCoeff1[0] = DPoint3(0,0,0);
		m_pCoeff0[0] = p0;
		return true;
	}
	else if (m_iPoints==2)
	{
		DPoint3 p0 = *iter;
		iter++;
		DPoint3 p1 = *iter;

		m_pCoeff3[0] = DPoint3(0,0,0);
		m_pCoeff2[0] = DPoint3(0,0,0);
		m_pCoeff1[0] = p1-p0;
		m_pCoeff0[0] = p0;
		return true;
	}

	///////////////////////////////////////////////////
	// calculate natural spline coeffs

	int intNo = m_iPoints - 1;	// number of intervals
	int equNo = intNo - 1;	// number of equations

	// interval sizes
	double *h = new double[intNo];
	double *ih = new double[intNo];

	// diagonal of tridiagonal matrix
	double *a = new double[equNo];
	// constant part of linear equations
	DPoint3 *dvec = new DPoint3[equNo];

	// LR decomposition of tridiagonal matrix
	double *m = new double[equNo];
	double *l = new double[equNo - 1];
	// ??
	DPoint3 *yvec = new DPoint3[equNo];
	DPoint3 *xvec = new DPoint3[equNo];

	DPoint3 d0, d1;

	DPoint3 *invec = new DPoint3[m_iPoints];
	for (i=0; i<m_iPoints; i++, iter++)
	{
		invec[i] = *iter;
	}

	// calculate interval sizes as distance between points
	for (i = 0; i < intNo; i++)
	{
		//h[i] = Vec3DiffAbs(invec[i], invec[i + 1]);
		h[i] = ::sqrt((invec[i]-invec[i+1]).LengthSquared());
		ih[i] = 1.0 / h[i];
	}

	// calculate diagonal of tridiagonal matrix
	for (i = 0; i < equNo; i++)
		a[i] = 2.0 * (h[i] + h[i + 1]);

	// calculate LR decomposition of tridiagonal matrix
	m[0] = a[0];
	for (i = 0; i < equNo - 1; i++)
	{
		l[i] = h[i + 1] / m[i];
		m[i + 1] = a[i + 1] - l[i] * h[i + 1];
	}

	// interpolation is done separately for all 3 coordinates

	for (i = 0; i < equNo; i++)
	{
		// dvec[i] = 6.0*(ih[i]*(invec[i+1] - invec[i]) - ih[i+1]*(invec[i+2] - invec[i+1]));
		DPoint3 dif1 = invec[i+1] - invec[i];
		DPoint3 dif2 = invec[i+2] - invec[i+1];
		dvec[i] = dif1 * (ih[i]) - dif2 * (ih[i+1]);
		dvec[i] = dvec[i] * (6.0);
	}

	// forward elimination
	yvec[0] = dvec[0];
	for (i = 1; i < equNo; i++)
		yvec[i] = dvec[i] - yvec[i-1] * (l[i-1]);

	// back substitution
	xvec[equNo-1] = yvec[equNo-1] * (-1.0/m[equNo-1]);
	for (i = equNo - 2; i >= 0; i--)
	{
		xvec[i] = yvec[i] + xvec[i+1] * (h[i+1]);
		xvec[i] = xvec[i] * (-1.0/m[i]);
	}

	// calculate spline points
	for (i = 0; i < intNo; i++)
	{
		// calculate polynom coefficients
		if (i == 0)
			d0.Set(0,0,0);	// zero vector
		else
			d0 = xvec[i-1];

		if (i == intNo-1)
			d1.Set(0,0,0);	// zero vector
		else
			d1 = xvec[i];

		double hsq = h[i]*h[i];
		m_pCoeff3[i] = (d1 - d0) * (hsq/6.0);
		m_pCoeff2[i] = d0 * (0.5*hsq);
		m_pCoeff1[i] = invec[i+1] - invec[i] - (d1 + d0 * (2.0)) * (hsq/6.0);
		m_pCoeff0[i] = invec[i];
	}

	delete [] h;
	delete [] ih;
	delete [] a;
	delete [] dvec;
	delete [] m;
	delete [] l;
	delete [] yvec;
	delete [] xvec;
	delete [] invec;

	// discard original vectors (not required...)
	if (m_veclist.size()>0)
		m_veclist.erase(m_veclist.begin(), m_veclist.end());

	return true;
}

/**
 * Perform interpolation.
 *
 * \param par The interpolation parameter, in the range of 0 to N, where
 *		N is the number of segments (one less than the number of control
 *		points that define the curve).
 * \param vec The point that results from the interpolation.
 * \param dvec Tangential vector (optional).
 * \param ddvec Curvature vector (optional).
 */
bool CubicSpline::Interpolate(double par, DPoint3 *vec,
							  DPoint3 *dvec /*= NULL*/,
							  DPoint3 *ddvec /*= NULL*/) const
{
	// check parameter value f
	int ncoeff = (int)::floor(par);
	if (ncoeff<0)
		ncoeff = 0;
	if (ncoeff>=(m_iPoints-1))
		ncoeff = m_iPoints-2;

	const DPoint3 &coeff0 = m_pCoeff0[ncoeff];
	const DPoint3 &coeff1 = m_pCoeff1[ncoeff];
	const DPoint3 &coeff2 = m_pCoeff2[ncoeff];
	const DPoint3 &coeff3 = m_pCoeff3[ncoeff];

	double f = par - (double)ncoeff;

	DPoint3 tmp;
	tmp = coeff3 * (f) + coeff2;
	tmp = tmp * (f) + coeff1;
	tmp = tmp * (f) + coeff0;
	*vec = tmp;

	if (dvec != NULL)
	{
		// calculate tangential vector
		tmp = coeff3 * (3.0*f) + coeff2 * (2.0);
		tmp = tmp * (f) + coeff1;
		*dvec = tmp;
	}

	if (ddvec != NULL)
	{
		// calculate curvature vector
		tmp = coeff3 * (6.0*f) + coeff2 * (2.0);
		*ddvec = tmp;
	}
	return true;
}

<commit_msg>added safety check to catch degenerate points<commit_after>//
// (Natural) Cubic Spline class.
//
// Originally adapted from some LGPL code found around the net.
//
// Copyright (c) 2004 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#include "CubicSpline.h"

CubicSpline::CubicSpline()
{
	m_pCoeff0 = m_pCoeff1 = m_pCoeff2 = m_pCoeff3 = NULL;
	m_iPoints = 0;
}

CubicSpline::~CubicSpline()
{
	Cleanup();
}

/** Re-initialization. */
void CubicSpline::Cleanup()
{
	if (m_veclist.size() > 0)
		m_veclist.erase(m_veclist.begin(), m_veclist.end());

	if (m_pCoeff0!=NULL)
		delete [] m_pCoeff0;
	if (m_pCoeff1!=NULL)
		delete [] m_pCoeff1;
	if (m_pCoeff2!=NULL)
		delete [] m_pCoeff2;
	if (m_pCoeff3!=NULL)
		delete [] m_pCoeff3;
	m_pCoeff0 = m_pCoeff1 = m_pCoeff2 = m_pCoeff3 = NULL;

	m_iPoints = 0;
}

/** Adds an interpolation point. */
int CubicSpline::AddPoint(const DPoint3 &vec)
{
	// TO DO: error check
	int npar = m_veclist.size();
	m_veclist.push_back(vec);
	return npar;
}

/** Generate the spline coeffs. */
bool CubicSpline::Generate()
{
	int i;
	m_iPoints = m_veclist.size();

	if (m_iPoints==0)
		return false; // there are no interp points!!

	// allocate coefficient table
	m_pCoeff0 = new DPoint3[m_iPoints];
	m_pCoeff1 = new DPoint3[m_iPoints];
	m_pCoeff2 = new DPoint3[m_iPoints];
	m_pCoeff3 = new DPoint3[m_iPoints];

	std::list<DPoint3>::const_iterator iter = m_veclist.begin();

	if (m_iPoints==1)
	{
		// cannot calculate spline, return constant polynom
		DPoint3 p0 = *iter;

		m_pCoeff3[0] = DPoint3(0,0,0);
		m_pCoeff2[0] = DPoint3(0,0,0);
		m_pCoeff1[0] = DPoint3(0,0,0);
		m_pCoeff0[0] = p0;
		return true;
	}
	else if (m_iPoints==2)
	{
		DPoint3 p0 = *iter;
		iter++;
		DPoint3 p1 = *iter;

		m_pCoeff3[0] = DPoint3(0,0,0);
		m_pCoeff2[0] = DPoint3(0,0,0);
		m_pCoeff1[0] = p1-p0;
		m_pCoeff0[0] = p0;
		return true;
	}

	///////////////////////////////////////////////////
	// calculate natural spline coeffs

	int intNo = m_iPoints - 1;	// number of intervals
	int equNo = intNo - 1;	// number of equations

	// interval sizes
	double *h = new double[intNo];
	double *ih = new double[intNo];

	// diagonal of tridiagonal matrix
	double *a = new double[equNo];
	// constant part of linear equations
	DPoint3 *dvec = new DPoint3[equNo];

	// LR decomposition of tridiagonal matrix
	double *m = new double[equNo];
	double *l = new double[equNo - 1];
	// ??
	DPoint3 *yvec = new DPoint3[equNo];
	DPoint3 *xvec = new DPoint3[equNo];

	DPoint3 d0, d1;

	DPoint3 *invec = new DPoint3[m_iPoints];
	for (i=0; i<m_iPoints; i++, iter++)
	{
		invec[i] = *iter;
	}

	// calculate interval sizes as distance between points
	for (i = 0; i < intNo; i++)
	{
		//h[i] = Vec3DiffAbs(invec[i], invec[i + 1]);
		h[i] = ::sqrt((invec[i]-invec[i+1]).LengthSquared());

		// identical points are not allowed, because a curve through them is
		//  not mathematically defined
		if (h[i] == 0.0)
			return false;

		ih[i] = 1.0 / h[i];
	}

	// calculate diagonal of tridiagonal matrix
	for (i = 0; i < equNo; i++)
		a[i] = 2.0 * (h[i] + h[i + 1]);

	// calculate LR decomposition of tridiagonal matrix
	m[0] = a[0];
	for (i = 0; i < equNo - 1; i++)
	{
		l[i] = h[i + 1] / m[i];
		m[i + 1] = a[i + 1] - l[i] * h[i + 1];
	}

	// interpolation is done separately for all 3 coordinates

	for (i = 0; i < equNo; i++)
	{
		// dvec[i] = 6.0*(ih[i]*(invec[i+1] - invec[i]) - ih[i+1]*(invec[i+2] - invec[i+1]));
		DPoint3 dif1 = invec[i+1] - invec[i];
		DPoint3 dif2 = invec[i+2] - invec[i+1];
		dvec[i] = dif1 * (ih[i]) - dif2 * (ih[i+1]);
		dvec[i] = dvec[i] * (6.0);
	}

	// forward elimination
	yvec[0] = dvec[0];
	for (i = 1; i < equNo; i++)
		yvec[i] = dvec[i] - yvec[i-1] * (l[i-1]);

	// back substitution
	xvec[equNo-1] = yvec[equNo-1] * (-1.0/m[equNo-1]);
	for (i = equNo - 2; i >= 0; i--)
	{
		xvec[i] = yvec[i] + xvec[i+1] * (h[i+1]);
		xvec[i] = xvec[i] * (-1.0/m[i]);
	}

	// calculate spline points
	for (i = 0; i < intNo; i++)
	{
		// calculate polynom coefficients
		if (i == 0)
			d0.Set(0,0,0);	// zero vector
		else
			d0 = xvec[i-1];

		if (i == intNo-1)
			d1.Set(0,0,0);	// zero vector
		else
			d1 = xvec[i];

		double hsq = h[i]*h[i];
		m_pCoeff3[i] = (d1 - d0) * (hsq/6.0);
		m_pCoeff2[i] = d0 * (0.5*hsq);
		m_pCoeff1[i] = invec[i+1] - invec[i] - (d1 + d0 * (2.0)) * (hsq/6.0);
		m_pCoeff0[i] = invec[i];
	}

	delete [] h;
	delete [] ih;
	delete [] a;
	delete [] dvec;
	delete [] m;
	delete [] l;
	delete [] yvec;
	delete [] xvec;
	delete [] invec;

	// discard original vectors (not required...)
	if (m_veclist.size()>0)
		m_veclist.erase(m_veclist.begin(), m_veclist.end());

	return true;
}

/**
 * Perform interpolation.
 *
 * \param par The interpolation parameter, in the range of 0 to N, where
 *		N is the number of segments (one less than the number of control
 *		points that define the curve).
 * \param vec The point that results from the interpolation.
 * \param dvec Tangential vector (optional).
 * \param ddvec Curvature vector (optional).
 */
bool CubicSpline::Interpolate(double par, DPoint3 *vec,
							  DPoint3 *dvec /*= NULL*/,
							  DPoint3 *ddvec /*= NULL*/) const
{
	// check parameter value f
	int ncoeff = (int)::floor(par);
	if (ncoeff<0)
		ncoeff = 0;
	if (ncoeff>=(m_iPoints-1))
		ncoeff = m_iPoints-2;

	const DPoint3 &coeff0 = m_pCoeff0[ncoeff];
	const DPoint3 &coeff1 = m_pCoeff1[ncoeff];
	const DPoint3 &coeff2 = m_pCoeff2[ncoeff];
	const DPoint3 &coeff3 = m_pCoeff3[ncoeff];

	double f = par - (double)ncoeff;

	DPoint3 tmp;
	tmp = coeff3 * (f) + coeff2;
	tmp = tmp * (f) + coeff1;
	tmp = tmp * (f) + coeff0;
	*vec = tmp;

	if (dvec != NULL)
	{
		// calculate tangential vector
		tmp = coeff3 * (3.0*f) + coeff2 * (2.0);
		tmp = tmp * (f) + coeff1;
		*dvec = tmp;
	}

	if (ddvec != NULL)
	{
		// calculate curvature vector
		tmp = coeff3 * (6.0*f) + coeff2 * (2.0);
		*ddvec = tmp;
	}
	return true;
}

<|endoftext|>


<commit_before>/*=========================================================================
*
*  Copyright Insight Software Consortium
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*         http://www.apache.org/licenses/LICENSE-2.0.txt
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*
*=========================================================================*/
#include <SimpleITK.h>
#include <memory>
#include <itksys/SystemTools.hxx>

#include "sitkImageCompare.h"

namespace sitk = itk::simple;

void ImageCompare::NormalizeAndSave ( const sitk::Image &input, const std::string &filename )
{
  sitk::Image image = input;

  if (image.GetPixelIDValue() == sitk::sitkLabelUInt8)
    {
    image = sitk::Cast( image, sitk::sitkUInt8);
    }
  else if (image.GetPixelIDValue() == sitk::sitkLabelUInt16)
    {
    image = sitk::Cast( image, sitk::sitkUInt16);
    }
  else if (image.GetPixelIDValue() == sitk::sitkLabelUInt32)
    {
    image = sitk::Cast( image, sitk::sitkUInt32);
    }
  else if (image.GetPixelIDValue() == sitk::sitkLabelUInt64)
    {
    image = sitk::Cast( image, sitk::sitkUInt64);
    }
  else if (image.GetPixelIDValue() == sitk::sitkComplexFloat64
           || image.GetPixelIDValue() == sitk::sitkComplexFloat32)
    {
    image = sitk::ComplexToModulus(image);
    }
  else if (image.GetNumberOfComponentsPerPixel() != 1)
    {
    // just use the magnitude for vector images
    image = sitk::VectorMagnitude(image);
    }

  // Extract the center slice of our image
  image = extractSlice(image);

  sitk::Image out = sitk::RescaleIntensity( image, 0, 255 );

  out = sitk::Cast ( out, sitk::sitkUInt8 );
  sitk::WriteImage ( out, filename );
}

ImageCompare::ImageCompare()
{
  mTolerance = 0.0;
  mMessage = "";
}

float ImageCompare::testImages( const itk::simple::Image& testImage,
                                const itk::simple::Image& baselineImage,
                                bool reportErrors,
                                const std::string &baselineImageFilename)
{

  const std::string OutputDir = dataFinder.GetOutputDirectory();

  const std::string shortFilename = itksys::SystemTools::GetFilenameName( baselineImageFilename );

    // verify they have the same size
  if ( baselineImage.GetHeight() != testImage.GetHeight()
       || baselineImage.GetWidth() != testImage.GetWidth()
       || baselineImage.GetDepth() != testImage.GetDepth() )
    {
    mMessage = "ImageCompare: Image dimensions are different";
    return -1;
    }

    // Compute image difference squared
    sitk::Image diffSquared( 0, 0, itk::simple::sitkUInt8 );
    try
      {

      if ( baselineImage.GetPixelID() == sitk::sitkComplexFloat32 ||
           baselineImage.GetPixelID() == sitk::sitkComplexFloat64 )
        {

        const sitk::Image diff =  sitk::Subtract( testImage, baselineImage );
        // for complex number we multiply the image by it's complex
        // conjugate, this will produce only a real value result
        const sitk::Image conj = sitk::RealAndImaginaryToComplex( sitk::ComplexToReal( diff ),
                                                                  sitk::Multiply( sitk::ComplexToImaginary( diff ), -1.0 ) );
        diffSquared = sitk::ComplexToReal( sitk::Multiply( diff, conj ) );
        }
      else if ( baselineImage.GetNumberOfComponentsPerPixel() > 1 )
        {
        const sitk::Image diff =  sitk::Subtract( sitk::Cast( testImage, sitk::sitkVectorFloat32 ), sitk::Cast( baselineImage, sitk::sitkVectorFloat32 ) );

        // for vector image just do a sum of the components
        diffSquared  = sitk::Pow( sitk::VectorIndexSelectionCast( diff, 0 ), 2.0 );
        for ( unsigned int i = 1; i < diff.GetNumberOfComponentsPerPixel(); ++i )
          {
          const sitk::Image temp = sitk::Pow( sitk::VectorIndexSelectionCast( diff, i ), 2.0 );
          diffSquared = sitk::Add( temp, diffSquared );
          }

        diffSquared = sitk::Divide( diffSquared, diff.GetNumberOfComponentsPerPixel() );
        }
      else
        {
        sitk::Image diff =  sitk::Subtract( sitk::Cast( testImage, sitk::sitkFloat32 ), sitk::Cast( baselineImage, sitk::sitkFloat32 ) );
        diffSquared = sitk::Multiply( diff, diff );
        }

      }
    catch ( std::exception& e )
      {
      mMessage = "ImageCompare: Failed to subtract image " + baselineImageFilename + " because: " + e.what();
      return -1;
      }

    sitk::StatisticsImageFilter stats;
    stats.Execute ( diffSquared );
    const double rms = std::sqrt ( stats.GetMean() );

    if ( !reportErrors )
      {
      // The measurement errors should be reported for both success and errors
      // to facilitate setting tight tolerances of tests.
      std::cout << "<DartMeasurement name=\"RMSeDifference " << shortFilename <<  "\" type=\"numeric/float\">" << rms << "</DartMeasurement>" << std::endl;
      }
    else
      {
      std::ostringstream msg;
      msg << "ImageCompare: image Root Mean Square (RMS) difference was " << rms << " which exceeds the tolerance of " << mTolerance;
      msg << "\n";
      mMessage = msg.str();

      std::cout << "<DartMeasurement name=\"RMSeDifference\" type=\"numeric/float\">" << rms << "</DartMeasurement>" << std::endl;
      std::cout << "<DartMeasurement name=\"Tolerance\" type=\"numeric/float\">" << mTolerance << "</DartMeasurement>" << std::endl;

      std::string volumeName = OutputDir + "/" + shortFilename + ".nrrd";
      sitk::ImageFileWriter().SetFileName ( volumeName ).Execute ( testImage );

      // Save pngs
      std::string ExpectedImageFilename = OutputDir + "/" + shortFilename + "_Expected.png";
      std::string ActualImageFilename = OutputDir + "/" + shortFilename + "_Actual.png";
      std::string DifferenceImageFilename = OutputDir + "/" + shortFilename + "_Difference.png";

      try
        {
        NormalizeAndSave ( baselineImage, ExpectedImageFilename );
        NormalizeAndSave ( testImage, ActualImageFilename );
        NormalizeAndSave ( sitk::Sqrt(diffSquared), DifferenceImageFilename );

        // Let ctest know about it
        std::cout << "<DartMeasurementFile name=\"ExpectedImage\" type=\"image/png\">";
        std::cout << ExpectedImageFilename << "</DartMeasurementFile>" << std::endl;
        std::cout << "<DartMeasurementFile name=\"ActualImage\" type=\"image/png\">";
        std::cout << ActualImageFilename << "</DartMeasurementFile>" << std::endl;
        std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
        std::cout << DifferenceImageFilename << "</DartMeasurementFile>" << std::endl;

        }
      catch( std::exception &e )
        {
        std::cerr << "Exception encountered while trying to normalize and save images for dashboard!" << std::endl;
        std::cerr << e.what() << std::endl;
        }
      catch(...)
        {
        std::cerr << "Unexpected error while trying to normalize and save images for dashboard!" << std::endl;
        }

      }

    return (rms > fabs ( mTolerance )) ? rms  : 0.0;
}

sitk::Image ImageCompare::extractSlice( const sitk::Image & image )
{
  // Extract the center slice of our test image
  if ( image.GetDimension() == 3 )
    {
    std::vector<int> idx( 3, 0 );
    std::vector<unsigned int> sz = image.GetSize();

    // set to just the center slice
    idx[2] = (int)( image.GetDepth() / 2 );
    sz[2] = 1;
    return  sitk::RegionOfInterest( image, sz, idx );
    }

  return image;

}

bool ImageCompare::compare ( const sitk::Image& image, std::string inTestCase, std::string inTag )
{
  sitk::Image centerSlice( 0, 0, sitk::sitkUInt8 );
  std::string testCase = inTestCase;
  std::string tag = inTag;
  std::string testName = ::testing::UnitTest::GetInstance()->current_test_info()->name();

  if ( testCase == "" )
    {
    testCase = ::testing::UnitTest::GetInstance()->current_test_info()->test_case_name();
    }

  // Does the baseline exist?
  std::string extension = ".nrrd";
  std::string OutputDir = dataFinder.GetOutputDirectory();

  std::string name = testCase
    .append( "_" )
    .append(testName);

  if ( tag != "" )
    {
    name.append("_").append ( tag );
    }

  centerSlice = extractSlice(image);

  const std::string baselineFileName = dataFinder.GetFile( "Baseline/" + name + extension );

  if ( !itksys::SystemTools::FileExists ( baselineFileName.c_str(), true ) )
    {
    // Baseline does not exist, write out what we've been given
    std::string newBaselineDir = OutputDir + "/Newbaseline/";
    itksys::SystemTools::MakeDirectory ( newBaselineDir.c_str() );
    std::cout << "Making directory " << newBaselineDir << std::endl;
    std::string newBaseline = newBaselineDir + name + extension;
    sitk::ImageFileWriter().SetFileName ( newBaseline ).Execute ( centerSlice );
    mMessage = "Baseline does not exist, wrote " + newBaseline + "\ncp " + newBaseline + " " + baselineFileName;
    return false;
    }

  // Generate all possible baseline filenames
  std::vector<std::string> baselineFileNames;

  {
  int x = 0;

  baselineFileNames.push_back(baselineFileName);

  while ( ++x )
    {
    std::ostringstream filename_stream;
    filename_stream <<  "Baseline/" << name  << "." << x << extension;

    std::string filename = dataFinder.GetFile( filename_stream.str() );

    if (!itksys::SystemTools::FileExists ( filename, true ) )
      {
      break;
      }
    baselineFileNames.push_back(filename);
    }

  }

  std::vector<std::string>::const_iterator iterName;

  std::string bestBaselineName =  *baselineFileNames.begin();
  float bestRMS = std::numeric_limits<float>::max();

  for ( iterName = baselineFileNames.begin(); iterName != baselineFileNames.end(); ++iterName )
    {

    sitk::Image baseline( 0, 0, sitk::sitkUInt8 );

    try
      {
      baseline = sitk::ImageFileReader().SetFileName (*iterName ).Execute();
      }
    catch ( std::exception& e )
      {
      mMessage = "ImageCompare: Failed to load image " +*iterName + " because: " + e.what();
      return false;
      }

    float RMS = testImages( centerSlice, baseline, false,  *iterName);

    if ( RMS >= 0.0 && RMS < bestRMS )
      {
      bestBaselineName = *iterName;
      bestRMS = RMS;
      }

    }

  if ( bestRMS > fabs ( mTolerance ) )
    {
    sitk::Image baseline =  sitk::ImageFileReader().SetFileName (bestBaselineName ).Execute();
    testImages( centerSlice, baseline, true, bestBaselineName );
    return false;
    }
  else
    {
    return true;
    }

}
<commit_msg>Also Display Dart Measurements for difference image<commit_after>/*=========================================================================
*
*  Copyright Insight Software Consortium
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*         http://www.apache.org/licenses/LICENSE-2.0.txt
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*
*=========================================================================*/
#include <SimpleITK.h>
#include <memory>
#include <itksys/SystemTools.hxx>

#include "sitkImageCompare.h"

namespace sitk = itk::simple;

void ImageCompare::NormalizeAndSave ( const sitk::Image &input, const std::string &filename )
{
  sitk::Image image = input;

  if (image.GetPixelIDValue() == sitk::sitkLabelUInt8)
    {
    image = sitk::Cast( image, sitk::sitkUInt8);
    }
  else if (image.GetPixelIDValue() == sitk::sitkLabelUInt16)
    {
    image = sitk::Cast( image, sitk::sitkUInt16);
    }
  else if (image.GetPixelIDValue() == sitk::sitkLabelUInt32)
    {
    image = sitk::Cast( image, sitk::sitkUInt32);
    }
  else if (image.GetPixelIDValue() == sitk::sitkLabelUInt64)
    {
    image = sitk::Cast( image, sitk::sitkUInt64);
    }
  else if (image.GetPixelIDValue() == sitk::sitkComplexFloat64
           || image.GetPixelIDValue() == sitk::sitkComplexFloat32)
    {
    image = sitk::ComplexToModulus(image);
    }
  else if (image.GetNumberOfComponentsPerPixel() != 1)
    {
    // just use the magnitude for vector images
    image = sitk::VectorMagnitude(image);
    }

  // Extract the center slice of our image
  image = extractSlice(image);

  sitk::Image out = sitk::RescaleIntensity( image, 0, 255 );

  out = sitk::Cast ( out, sitk::sitkUInt8 );
  sitk::WriteImage ( out, filename );
}

ImageCompare::ImageCompare()
{
  mTolerance = 0.0;
  mMessage = "";
}

float ImageCompare::testImages( const itk::simple::Image& testImage,
                                const itk::simple::Image& baselineImage,
                                bool reportErrors,
                                const std::string &baselineImageFilename)
{

  const std::string OutputDir = dataFinder.GetOutputDirectory();

  const std::string shortFilename = itksys::SystemTools::GetFilenameName( baselineImageFilename );

    // verify they have the same size
  if ( baselineImage.GetHeight() != testImage.GetHeight()
       || baselineImage.GetWidth() != testImage.GetWidth()
       || baselineImage.GetDepth() != testImage.GetDepth() )
    {
    mMessage = "ImageCompare: Image dimensions are different";
    return -1;
    }

    // Compute image difference squared
    sitk::Image diffSquared( 0, 0, itk::simple::sitkUInt8 );
    try
      {

      if ( baselineImage.GetPixelID() == sitk::sitkComplexFloat32 ||
           baselineImage.GetPixelID() == sitk::sitkComplexFloat64 )
        {

        const sitk::Image diff =  sitk::Subtract( testImage, baselineImage );
        // for complex number we multiply the image by it's complex
        // conjugate, this will produce only a real value result
        const sitk::Image conj = sitk::RealAndImaginaryToComplex( sitk::ComplexToReal( diff ),
                                                                  sitk::Multiply( sitk::ComplexToImaginary( diff ), -1.0 ) );
        diffSquared = sitk::ComplexToReal( sitk::Multiply( diff, conj ) );
        }
      else if ( baselineImage.GetNumberOfComponentsPerPixel() > 1 )
        {
        const sitk::Image diff =  sitk::Subtract( sitk::Cast( testImage, sitk::sitkVectorFloat32 ), sitk::Cast( baselineImage, sitk::sitkVectorFloat32 ) );

        // for vector image just do a sum of the components
        diffSquared  = sitk::Pow( sitk::VectorIndexSelectionCast( diff, 0 ), 2.0 );
        for ( unsigned int i = 1; i < diff.GetNumberOfComponentsPerPixel(); ++i )
          {
          const sitk::Image temp = sitk::Pow( sitk::VectorIndexSelectionCast( diff, i ), 2.0 );
          diffSquared = sitk::Add( temp, diffSquared );
          }

        diffSquared = sitk::Divide( diffSquared, diff.GetNumberOfComponentsPerPixel() );
        }
      else
        {
        sitk::Image diff =  sitk::Subtract( sitk::Cast( testImage, sitk::sitkFloat32 ), sitk::Cast( baselineImage, sitk::sitkFloat32 ) );
        diffSquared = sitk::Multiply( diff, diff );
        }

      }
    catch ( std::exception& e )
      {
      mMessage = "ImageCompare: Failed to subtract image " + baselineImageFilename + " because: " + e.what();
      return -1;
      }

    sitk::StatisticsImageFilter stats;
    stats.Execute ( diffSquared );
    const double rms = std::sqrt ( stats.GetMean() );

    if ( !reportErrors )
      {
      // The measurement errors should be reported for both success and errors
      // to facilitate setting tight tolerances of tests.
      std::cout << "<DartMeasurement name=\"RMSeDifference " << shortFilename <<  "\" type=\"numeric/float\">" << rms << "</DartMeasurement>" << std::endl;
      }
    else
      {
      std::ostringstream msg;
      msg << "ImageCompare: image Root Mean Square (RMS) difference was " << rms << " which exceeds the tolerance of " << mTolerance;
      msg << "\n";
      mMessage = msg.str();

      std::cout << "<DartMeasurement name=\"RMSeDifference\" type=\"numeric/float\">" << rms << "</DartMeasurement>" << std::endl;
      std::cout << "<DartMeasurement name=\"Tolerance\" type=\"numeric/float\">" << mTolerance << "</DartMeasurement>" << std::endl;

      std::string volumeName = OutputDir + "/" + shortFilename + ".nrrd";
      sitk::ImageFileWriter().SetFileName ( volumeName ).Execute ( testImage );

      // Save pngs
      std::string ExpectedImageFilename = OutputDir + "/" + shortFilename + "_Expected.png";
      std::string ActualImageFilename = OutputDir + "/" + shortFilename + "_Actual.png";
      std::string DifferenceImageFilename = OutputDir + "/" + shortFilename + "_Difference.png";

      try
        {
        NormalizeAndSave ( baselineImage, ExpectedImageFilename );
        NormalizeAndSave ( testImage, ActualImageFilename );
        NormalizeAndSave ( sitk::Sqrt(diffSquared), DifferenceImageFilename );

        // Let ctest know about it
        std::cout << "<DartMeasurementFile name=\"ExpectedImage\" type=\"image/png\">";
        std::cout << ExpectedImageFilename << "</DartMeasurementFile>" << std::endl;
        std::cout << "<DartMeasurementFile name=\"ActualImage\" type=\"image/png\">";
        std::cout << ActualImageFilename << "</DartMeasurementFile>" << std::endl;
        std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
        std::cout << DifferenceImageFilename << "</DartMeasurementFile>" << std::endl;

        stats.Execute( sitk::Sqrt(diffSquared) );

        std::cout << "<DartMeasurement name=\"DifferenceImage Minimum\" type=\"numeric/double\">";
        std::cout << stats.GetMinimum() << "</DartMeasurement>" << std::endl;

        std::cout << "<DartMeasurement name=\"DifferenceImage Maximum\" type=\"numeric/double\">";
        std::cout << stats.GetMaximum() << "</DartMeasurement>" << std::endl;

        std::cout << "<DartMeasurement name=\"DifferenceImage Mean\" type=\"numeric/double\">";
        std::cout << stats.GetMean() << "</DartMeasurement>" << std::endl;

        }
      catch( std::exception &e )
        {
        std::cerr << "Exception encountered while trying to normalize and save images for dashboard!" << std::endl;
        std::cerr << e.what() << std::endl;
        }
      catch(...)
        {
        std::cerr << "Unexpected error while trying to normalize and save images for dashboard!" << std::endl;
        }

      }

    return (rms > fabs ( mTolerance )) ? rms  : 0.0;
}

sitk::Image ImageCompare::extractSlice( const sitk::Image & image )
{
  // Extract the center slice of our test image
  if ( image.GetDimension() == 3 )
    {
    std::vector<int> idx( 3, 0 );
    std::vector<unsigned int> sz = image.GetSize();

    // set to just the center slice
    idx[2] = (int)( image.GetDepth() / 2 );
    sz[2] = 1;
    return  sitk::RegionOfInterest( image, sz, idx );
    }

  return image;

}

bool ImageCompare::compare ( const sitk::Image& image, std::string inTestCase, std::string inTag )
{
  sitk::Image centerSlice( 0, 0, sitk::sitkUInt8 );
  std::string testCase = inTestCase;
  std::string tag = inTag;
  std::string testName = ::testing::UnitTest::GetInstance()->current_test_info()->name();

  if ( testCase == "" )
    {
    testCase = ::testing::UnitTest::GetInstance()->current_test_info()->test_case_name();
    }

  // Does the baseline exist?
  std::string extension = ".nrrd";
  std::string OutputDir = dataFinder.GetOutputDirectory();

  std::string name = testCase
    .append( "_" )
    .append(testName);

  if ( tag != "" )
    {
    name.append("_").append ( tag );
    }

  centerSlice = extractSlice(image);

  const std::string baselineFileName = dataFinder.GetFile( "Baseline/" + name + extension );

  if ( !itksys::SystemTools::FileExists ( baselineFileName.c_str(), true ) )
    {
    // Baseline does not exist, write out what we've been given
    std::string newBaselineDir = OutputDir + "/Newbaseline/";
    itksys::SystemTools::MakeDirectory ( newBaselineDir.c_str() );
    std::cout << "Making directory " << newBaselineDir << std::endl;
    std::string newBaseline = newBaselineDir + name + extension;
    sitk::ImageFileWriter().SetFileName ( newBaseline ).Execute ( centerSlice );
    mMessage = "Baseline does not exist, wrote " + newBaseline + "\ncp " + newBaseline + " " + baselineFileName;
    return false;
    }

  // Generate all possible baseline filenames
  std::vector<std::string> baselineFileNames;

  {
  int x = 0;

  baselineFileNames.push_back(baselineFileName);

  while ( ++x )
    {
    std::ostringstream filename_stream;
    filename_stream <<  "Baseline/" << name  << "." << x << extension;

    std::string filename = dataFinder.GetFile( filename_stream.str() );

    if (!itksys::SystemTools::FileExists ( filename, true ) )
      {
      break;
      }
    baselineFileNames.push_back(filename);
    }

  }

  std::vector<std::string>::const_iterator iterName;

  std::string bestBaselineName =  *baselineFileNames.begin();
  float bestRMS = std::numeric_limits<float>::max();

  for ( iterName = baselineFileNames.begin(); iterName != baselineFileNames.end(); ++iterName )
    {

    sitk::Image baseline( 0, 0, sitk::sitkUInt8 );

    try
      {
      baseline = sitk::ImageFileReader().SetFileName (*iterName ).Execute();
      }
    catch ( std::exception& e )
      {
      mMessage = "ImageCompare: Failed to load image " +*iterName + " because: " + e.what();
      return false;
      }

    float RMS = testImages( centerSlice, baseline, false,  *iterName);

    if ( RMS >= 0.0 && RMS < bestRMS )
      {
      bestBaselineName = *iterName;
      bestRMS = RMS;
      }

    }

  if ( bestRMS > fabs ( mTolerance ) )
    {
    sitk::Image baseline =  sitk::ImageFileReader().SetFileName (bestBaselineName ).Execute();
    testImages( centerSlice, baseline, true, bestBaselineName );
    return false;
    }
  else
    {
    return true;
    }

}
<|endoftext|>