
/*

  KLayout Layout Viewer
  Copyright (C) 2006-2016 Matthias Koefferlein

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

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

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/


#include "gsiDecl.h"
#include "dbShape.h"
#include "dbShapes.h"
#include "dbLayout.h"

namespace gsi
{

// ---------------------------------------------------------------
//  db::Shape binding

static db::Shapes *shapes_checked (db::Shape *s)
{
  db::Shapes *shapes = s->shapes ();
  if (! shapes) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not reside inside a shape container - cannot change the shape")));
  }
  return shapes;
}

static void check_is_path (const db::Shape *s)
{
  if (! s->is_path ()) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape is not a path")));
  }
}

static void check_is_text (const db::Shape *s)
{
  if (! s->is_text ()) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape is not a text")));
  }
}

static void check_is_box (const db::Shape *s)
{
  if (! s->is_box ()) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape is not a box")));
  }
}

static void transform_shape (db::Shape *s, const db::Trans &trans)
{
  db::Shapes *shapes = shapes_checked (s);
  *s = shapes->transform (*s, trans);
}

static void transform_shape_cplx (db::Shape *s, const db::CplxTrans &trans)
{
  db::Shapes *shapes = shapes_checked (s);
  *s = shapes->transform (*s, db::ICplxTrans (trans));
}

static void transform_shape_icplx (db::Shape *s, const db::ICplxTrans &trans)
{
  db::Shapes *shapes = shapes_checked (s);
  *s = shapes->transform (*s, trans);
}

static void delete_shape (db::Shape *s)
{
  db::Shapes *shapes = shapes_checked (s);
  shapes->erase_shape (*s);
  *s = db::Shape ();
}

static bool shape_is_valid (const db::Shape *s)
{
  db::Shapes *shapes = s->shapes ();
  return shapes != 0 && shapes->is_valid (*s);
}

static int object_type (const db::Shape *s)
{
  return int (s->type ());
}

static std::string text_string (const db::Shape *s)
{
  check_is_text (s);
  return s->text_string ();
}

static void set_text_string (db::Shape *s, const std::string &t)
{
  check_is_text (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::text_type p;
  s->text (p);
  p.string (t);
  *s = shapes->replace (*s, p);
}

static db::Point text_pos (const db::Shape *s)
{
  check_is_text (s);
  db::Shape::text_type p;
  s->text (p);
  return p.trans ().disp ();
}

static void set_text_pos (db::Shape *s, const db::Point &q)
{
  check_is_text (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::text_type p;
  s->text (p);
  p.trans (db::Trans (p.trans ().rot (), q));
  *s = shapes->replace (*s, p);
}

static int text_rot (const db::Shape *s)
{
  check_is_text (s);
  db::Shape::text_type p;
  s->text (p);
  return p.trans ().rot ();
}

static void set_text_rot (db::Shape *s, int rot) 
{
  check_is_text (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::text_type p;
  s->text (p);
  p.trans (db::Trans (rot, p.trans ().disp ()));
  *s = shapes->replace (*s, p);
}

static db::Trans text_trans (const db::Shape *s)
{
  check_is_text (s);
  return s->text_trans ();
}

static void set_text_trans (db::Shape *s, const db::Trans &t)
{
  check_is_text (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::text_type p;
  s->text (p);
  p.trans (t);
  *s = shapes->replace (*s, p);
}

static db::Shape::coord_type text_size (const db::Shape *s)
{
  check_is_text (s);
  return s->text_size ();
}

static void set_text_size (db::Shape *s, const db::Shape::coord_type &t)
{
  check_is_text (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::text_type p;
  s->text (p);
  p.size (t);
  *s = shapes->replace (*s, p);
}

static int text_font (const db::Shape *s)
{
  check_is_text (s);
  return int (s->text_font ());
}

static void set_text_font (db::Shape *s, int f)
{
  check_is_text (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::text_type p;
  s->text (p);
  p.font (db::Font (f));
  *s = shapes->replace (*s, p);
}

static int text_halign (const db::Shape *s)
{
  check_is_text (s);
  return int (s->text_halign ());
}

static void set_text_halign (db::Shape *s, int a)
{
  check_is_text (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::text_type p;
  s->text (p);
  p.halign (db::HAlign (a));
  *s = shapes->replace (*s, p);
}

static int text_valign (const db::Shape *s)
{
  check_is_text (s);
  return int (s->text_valign ());
}

static void set_text_valign (db::Shape *s, int a)
{
  check_is_text (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::text_type p;
  s->text (p);
  p.valign (db::VAlign (a));
  *s = shapes->replace (*s, p);
}

static db::Shape::coord_type path_bgnext (const db::Shape *s)
{
  check_is_path (s);
  return s->path_extensions ().first;
}

static void set_path_bgnext (db::Shape *s, db::Shape::coord_type e)
{
  check_is_path (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::path_type p;
  s->path (p);
  p.bgn_ext (e);
  *s = shapes->replace (*s, p);
}

static db::Shape::coord_type path_endext (const db::Shape *s)
{
  check_is_path (s);
  return s->path_extensions ().second;
}

static void set_path_endext (db::Shape *s, db::Shape::coord_type e)
{
  check_is_path (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::path_type p;
  s->path (p);
  p.end_ext (e);
  *s = shapes->replace (*s, p);
}

static bool round_path (const db::Shape *s)
{
  check_is_path (s);
  return s->round_path ();
}

static void set_round_path (db::Shape *s, bool r)
{
  check_is_path (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::path_type p;
  s->path (p);
  p.round (r);
  *s = shapes->replace (*s, p);
}

static db::Shape::coord_type path_length (const db::Shape *s)
{
  check_is_path (s);
  return s->path_length ();
}

static db::Shape::coord_type path_width (const db::Shape *s)
{
  check_is_path (s);
  return s->path_width ();
}

static void set_path_width (db::Shape *s, db::Shape::coord_type w)
{
  check_is_path (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::path_type p;
  s->path (p);
  p.width (w);
  *s = shapes->replace (*s, p);
}

static db::Coord box_width (const db::Shape *s)
{
  check_is_box (s);
  return s->box ().width ();
}

static void set_box_width (db::Shape *s, db::Coord w)
{
  check_is_box (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::box_type p;
  s->box (p);
  *s = shapes->replace (*s, db::Shape::box_type (p.center ().x () - w / 2, p.p1 ().y (), p.center ().x () + (w - w / 2), p. p2 ().y ()));
}

static db::Coord box_height (const db::Shape *s)
{
  check_is_box (s);
  return s->box ().height ();
}

static void set_box_height (db::Shape *s, db::Coord h)
{
  check_is_box (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::box_type p;
  s->box (p);
  *s = shapes->replace (*s, db::Shape::box_type (p.p1 ().x (), p.center ().y () - h / 2, p.p2 ().x (), p.center ().y () + (h - h / 2)));
}

static db::Point box_center (const db::Shape *s)
{
  check_is_box (s);
  return s->box ().center ();
}

static void set_box_center (db::Shape *s, const db::Point &c)
{
  check_is_box (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::box_type p;
  s->box (p);
  *s = shapes->replace (*s, p.moved (c - p.center ())); 
}

static db::Point box_p1 (const db::Shape *s)
{
  check_is_box (s);
  return s->box ().p1 ();
}

static void set_box_p1 (db::Shape *s, const db::Point &p1)
{
  check_is_box (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::box_type p;
  s->box (p);
  *s = shapes->replace (*s, db::Shape::box_type (p1, p.p2 ()));
}

static db::Point box_p2 (const db::Shape *s)
{
  check_is_box (s);
  return s->box ().p2 ();
}

static void set_box_p2 (db::Shape *s, const db::Point &p2)
{
  check_is_box (s);
  db::Shapes *shapes = shapes_checked (s);
  db::Shape::box_type p;
  s->box (p);
  *s = shapes->replace (*s, db::Shape::box_type (p.p1 (), p2));
}

static tl::Variant get_path (const db::Shape *s)
{
  db::Shape::path_type p;
  if (s->path (p)) {
    return tl::Variant (p);
  } else {
    return tl::Variant ();
  }
}

static tl::Variant get_polygon (const db::Shape *s)
{
  db::Shape::polygon_type p;
  if (s->polygon (p)) {
    return tl::Variant (p);
  } else {
    return tl::Variant ();
  }
}

static tl::Variant get_simple_polygon (const db::Shape *s)
{
  db::Shape::simple_polygon_type p;
  if (s->simple_polygon (p)) {
    return tl::Variant (p);
  } else {
    return tl::Variant ();
  }
}

static tl::Variant get_box (const db::Shape *s)
{
  db::Shape::box_type p;
  if (s->box (p)) {
    return tl::Variant (p);
  } else {
    return tl::Variant ();
  }
}

static tl::Variant get_edge (const db::Shape *s)
{
  db::Shape::edge_type p;
  if (s->edge (p)) {
    return tl::Variant (p);
  } else {
    return tl::Variant ();
  }
}

static tl::Variant get_text (const db::Shape *s)
{
  db::Shape::text_type p;
  if (s->text (p)) {
    return tl::Variant (p);
  } else {
    return tl::Variant ();
  }
}

static void set_prop_id (db::Shape *s, db::properties_id_type id)
{
  db::Shapes *shapes = shapes_checked (s);
  *s = shapes->replace_prop_id (*s, id);
}

static db::Shapes *shapes_ptr (db::Shape *s)
{
  return s->shapes ();
}

static db::Layout *layout_ptr (db::Shape *s)
{
  db::Shapes *shapes = s->shapes ();
  return shapes ? shapes->layout () : 0;
}

static unsigned int shape_layer_index (const db::Shape *s)
{
  db::Shapes *shapes = s->shapes ();
  if (! shapes) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not belong to a shape container")));
  }

  db::Cell *cell = shapes->cell ();
  if (! cell) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not belong to a cell")));
  }

  const db::Layout *layout = cell->layout ();
  if (! layout) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not belong to a layout")));
  }

  for (db::Layout::layer_iterator l = layout->begin_layers (); l != layout->end_layers (); ++l) {
    if (& cell->shapes ((*l).first) == shapes) {
      return (*l).first;
    }
  }

  throw tl::Exception (tl::to_string (QObject::tr ("Cannot identify layer of shape")));
}

static void set_shape_layer_index (db::Shape *s, unsigned int layer)
{
  db::Shapes *shapes = s->shapes ();
  if (! shapes) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not belong to a shape container")));
  }

  db::Cell *cell = shapes->cell ();
  if (! cell) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not belong to a cell")));
  }

  const db::Layout *layout = cell->layout ();
  if (! layout) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not belong to a layout")));
  }

  if (! layout->is_valid_layer (layer)) {
    throw tl::Exception (tl::to_string (QObject::tr ("Layer index does not point to a valid layer")));
  }

  if (& cell->shapes (layer) != shapes) {
    db::Shape s_old = *s;
    *s = cell->shapes (layer).insert (s_old);
    shapes->erase_shape (s_old);
  }
}

static db::LayerProperties shape_layer (const db::Shape *s)
{
  db::Shapes *shapes = s->shapes ();
  if (! shapes) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not belong to a shape container")));
  }

  db::Cell *cell = shapes->cell ();
  if (! cell) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not belong to a cell")));
  }

  const db::Layout *layout = cell->layout ();
  if (! layout) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not belong to a layout")));
  }

  for (db::Layout::layer_iterator l = layout->begin_layers (); l != layout->end_layers (); ++l) {
    if (& cell->shapes ((*l).first) == shapes) {
      return *(*l).second;
    }
  }

  throw tl::Exception (tl::to_string (QObject::tr ("Cannot identify layer of shape")));
}

static void set_shape_layer (db::Shape *s, const db::LayerProperties &lp)
{
  db::Shapes *shapes = s->shapes ();
  if (! shapes) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not belong to a shape container")));
  }

  db::Cell *cell = shapes->cell ();
  if (! cell) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not belong to a cell")));
  }

  const db::Layout *layout = cell->layout ();
  if (! layout) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not belong to a layout")));
  }

  for (db::Layout::layer_iterator l = layout->begin_layers (); l != layout->end_layers (); ++l) {

    if ((*l).second->log_equal (lp)) {

      unsigned int layer = (*l).first;
      if (& cell->shapes (layer) != shapes) {
        db::Shape s_old = *s;
        *s = cell->shapes (layer).insert (s_old);
        shapes->erase_shape (s_old);
      }

      return;

    }

  }

  throw tl::Exception (tl::to_string (QObject::tr ("Layer info object is not giving a valid layer")));
}

static db::Cell *cell_ptr (db::Shape *s)
{
  db::Shapes *shapes = s->shapes ();
  return shapes ? shapes->cell () : 0;
}

static void set_cell_ptr (db::Shape *s, db::Cell *new_cell)
{
  db::Shapes *shapes = shapes_checked (s);
  if (layout_ptr (s) != new_cell->layout ()) {
    throw tl::Exception (tl::to_string (QObject::tr ("Current and new cell belong to a different layout")));
  }

  unsigned int l = shape_layer_index (s);

  db::Shape s_old = *s;
  *s = new_cell->shapes (l).insert (s_old);
  shapes->erase_shape (s_old);
}

template <class SH>
void set_shape (db::Shape *s, const SH &obj)
{
  db::Shapes *shapes = shapes_checked (s);
  *s = shapes->replace (*s, obj);
}

static void delete_property (db::Shape *s, const tl::Variant &key)
{
  db::properties_id_type id = s->prop_id ();
  if (id == 0) {
    return;
  }

  db::Layout *layout = layout_ptr (s);
  if (! layout) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not reside inside a layout - cannot delete properties")));
  }

  std::pair<bool, db::property_names_id_type> nid = layout->properties_repository ().get_id_of_name (key);
  if (! nid.first) {
    return;
  }

  db::PropertiesRepository::properties_set props = layout->properties_repository ().properties (id);
  db::PropertiesRepository::properties_set::iterator p = props.find (nid.second);
  if (p != props.end ()) {
    props.erase (p);
  }
  set_prop_id (s, layout->properties_repository ().properties_id (props));
}

static void set_property (db::Shape *s, const tl::Variant &key, const tl::Variant &value)
{
  db::properties_id_type id = s->prop_id ();

  db::Layout *layout = layout_ptr (s);
  if (! layout) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not reside inside a layout - cannot set properties")));
  }

  db::property_names_id_type nid = layout->properties_repository ().prop_name_id (key);

  db::PropertiesRepository::properties_set props = layout->properties_repository ().properties (id);
  db::PropertiesRepository::properties_set::iterator p = props.find (nid);
  if (p != props.end ()) {
    p->second = value;
  } else {
    props.insert (std::make_pair (nid, value));
  }
  set_prop_id (s, layout->properties_repository ().properties_id (props));
}

static tl::Variant get_property (db::Shape *s, const tl::Variant &key)
{
  db::properties_id_type id = s->prop_id ();
  if (id == 0) {
    return tl::Variant ();
  }

  db::Layout *layout = layout_ptr (s);
  if (! layout) {
    throw tl::Exception (tl::to_string (QObject::tr ("Shape does not reside inside a layout - cannot retrieve properties")));
  }

  std::pair<bool, db::property_names_id_type> nid = layout->properties_repository ().get_id_of_name (key);
  if (! nid.first) {
    return tl::Variant ();
  }

  const db::PropertiesRepository::properties_set &props = layout->properties_repository ().properties (id);
  db::PropertiesRepository::properties_set::const_iterator p = props.find (nid.second);
  if (p != props.end ()) {
    return p->second;
  } else {
    return tl::Variant ();
  }
}

#if defined(HAVE_64BIT_COORD)
//  workaround for missing 128bit binding of GSI
static double area (const db::Shape *shape)
#else
static db::Shape::area_type area (const db::Shape *shape)
#endif
{ 
  return shape->area ();
}

static int t_null ()                        { return db::Shape::Null; }
static int t_polygon ()                     { return db::Shape::Polygon; }
static int t_polygonRef ()                  { return db::Shape::PolygonRef; }
static int t_polygonPtrArray ()             { return db::Shape::PolygonPtrArray; }
static int t_polygonPtrArrayMember ()       { return db::Shape::PolygonPtrArrayMember; }
static int t_simplePolygon ()               { return db::Shape::SimplePolygon; }
static int t_simplePolygonRef ()            { return db::Shape::SimplePolygonRef; }
static int t_simplePolygonPtrArray ()       { return db::Shape::SimplePolygonPtrArray; }
static int t_simplePolygonPtrArrayMember () { return db::Shape::SimplePolygonPtrArrayMember; }
static int t_edge ()                        { return db::Shape::Edge; }
static int t_path ()                        { return db::Shape::Path; }
static int t_pathRef ()                     { return db::Shape::PathRef; }
static int t_pathPtrArray ()                { return db::Shape::PathPtrArray; }
static int t_pathPtrArrayMember ()          { return db::Shape::PathPtrArrayMember; }
static int t_box ()                         { return db::Shape::Box; }
static int t_boxArray ()                    { return db::Shape::BoxArray; }
static int t_boxArrayMember ()              { return db::Shape::BoxArrayMember; }
static int t_shortBox ()                    { return db::Shape::ShortBox; }
static int t_shortBoxArray ()               { return db::Shape::ShortBoxArray; }
static int t_shortBoxArrayMember ()         { return db::Shape::ShortBoxArrayMember; }
static int t_text ()                        { return db::Shape::Text; }
static int t_textRef ()                     { return db::Shape::TextRef; }
static int t_textPtrArray ()                { return db::Shape::TextPtrArray; }
static int t_textPtrArrayMember ()          { return db::Shape::TextPtrArrayMember; }
static int t_userObject ()                  { return db::Shape::UserObject; }

Class<db::Shape> decl_Shape ("Shape", 
  gsi::method ("prop_id", (db::properties_id_type (db::Shape::*) () const) &db::Shape::prop_id,
    "@brief Get the properties ID associated with the shape\n"
    "\n"
    "The \\Layout object can be used to retrieve the actual properties associated with the ID."
  ) +
  gsi::method_ext ("prop_id=", &set_prop_id,
    "@brief Sets the properties ID of this shape\n"
    "\n"
    "The \\Layout object can be used to retrieve an ID for a given set of properties. "
    "Calling this method will invalidate any iterators. It should not be called inside a "
    "loop iterating over shapes.\n"
    "\n"
    "This method has been introduced in version 0.22."
  ) +
  gsi::method ("has_prop_id?", &db::Shape::has_prop_id,
    "@brief Returns true, if the shape has properties, i.e. has a properties ID\n"
  ) +
  gsi::method_ext ("shapes", &shapes_ptr,
    "@brief Gets a reference to the Shapes container the shape lives in\n"
    "\n"
    "This reference can be nil, if the Shape object is not referring to an actual shape.\n"
    "\n"
    "This method has been introduced in version 0.22."
  ) +
  gsi::method_ext ("cell", &cell_ptr,
    "@brief Gets a reference to the cell the shape belongs to\n"
    "\n"
    "This reference can be nil, if the Shape object is not living inside a cell\n"
    "\n"
    "This method has been introduced in version 0.22."
  ) +
  gsi::method_ext ("cell=", &set_cell_ptr,
    "@brief Moves the shape to a different cell\n"
    "@args cell\n"
    "\n"
    "Both the current and the target cell must reside in the same layout.\n"
    "\n"
    "This method has been introduced in version 0.23."
  ) +
  gsi::method_ext ("layout", &layout_ptr,
    "@brief Gets a reference to the Layout the shape belongs to\n"
    "\n"
    "This reference can be nil, if the Shape object is not living inside a layout.\n"
    "\n"
    "This method has been introduced in version 0.22."
  ) +
  gsi::method_ext ("box=", &set_shape<db::Box>,
    "@brief Replaces the shape by the given box\n"
    "@args box\n"
    "This method replaces the shape by the given box. This method can only be called "
    "for editable layouts. It does not change the user properties of the shape.\n"
    "Calling this method will invalidate any iterators. It should not be called inside a "
    "loop iterating over shapes.\n"
    "\n"
    "This method has been introduced in version 0.22."
  ) + 
  gsi::method_ext ("path=", &set_shape<db::Path>,
    "@brief Replaces the shape by the given path object\n"
    "@args box\n"
    "This method replaces the shape by the given path object. This method can only be called "
    "for editable layouts. It does not change the user properties of the shape.\n"
    "Calling this method will invalidate any iterators. It should not be called inside a "
    "loop iterating over shapes.\n"
    "\n"
    "This method has been introduced in version 0.22."
  ) + 
  gsi::method_ext ("polygon=", &set_shape<db::Polygon>,
    "@brief Replaces the shape by the given polygon object\n"
    "@args box\n"
    "This method replaces the shape by the given polygon object. This method can only be called "
    "for editable layouts. It does not change the user properties of the shape.\n"
    "Calling this method will invalidate any iterators. It should not be called inside a "
    "loop iterating over shapes.\n"
    "\n"
    "This method has been introduced in version 0.22."
  ) + 
  gsi::method_ext ("text=", &set_shape<db::Text>,
    "@brief Replaces the shape by the given text object\n"
    "@args box\n"
    "This method replaces the shape by the given text object. This method can only be called "
    "for editable layouts. It does not change the user properties of the shape.\n"
    "Calling this method will invalidate any iterators. It should not be called inside a "
    "loop iterating over shapes.\n"
    "\n"
    "This method has been introduced in version 0.22."
  ) + 
  gsi::method_ext ("edge=", &set_shape<db::Edge>,
    "@brief Replaces the shape by the given edge\n"
    "@args box\n"
    "This method replaces the shape by the given edge. This method can only be called "
    "for editable layouts. It does not change the user properties of the shape.\n"
    "Calling this method will invalidate any iterators. It should not be called inside a "
    "loop iterating over shapes.\n"
    "\n"
    "This method has been introduced in version 0.22."
  ) + 
  gsi::method_ext ("delete_property", &delete_property,
    "@brief Deletes the user property with the given key\n"
    "@args key\n"
    "This method is a convenience method that deletes the property with the given key. "
    "It does nothing if no property with that key exists. Using that method is more "
    "convenient than creating a new property set with a new ID and assigning that properties ID.\n"
    "This method may change the properties ID. "
    "Calling this method will invalidate any iterators. It should not be called inside a "
    "loop iterating over shapes.\n"
    "\n"
    "This method has been introduced in version 0.22."
  ) + 
  gsi::method_ext ("set_property", &set_property,
    "@brief Set the user property with the given key to the given value\n"
    "@args key, value\n"
    "This method is a convenience method that sets the property with the given key to the given value. "
    "If no property with that key exists, it will create one. Using that method is more "
    "convenient than creating a new property set with a new ID and assigning that properties ID.\n"
    "This method may change the properties ID. "
    "Calling this method will invalidate any iterators. It should not be called inside a "
    "loop iterating over shapes.\n"
    "\n"
    "This method has been introduced in version 0.22."
  ) + 
  gsi::method_ext ("property", &get_property,
    "@brief Gets the user property with the given key\n"
    "@args key\n"
    "This method is a convenience method that gets the property with the given key. "
    "If no property with that key does not exist, it will return nil. Using that method is more "
    "convenient than using the layout object and the properties ID to retrieve the property value. "
    "\n"
    "This method has been introduced in version 0.22."
  ) + 
  gsi::iterator ("each_point", &db::Shape::begin_point, &db::Shape::end_point,
    "@brief Iterate over all points of the object\n"
    "\n"
    "This method applies to paths and delivers all points of the path's center line.\n"
    "It will throw an exception for other objects.\n"
  ) +
  gsi::iterator ("each_point_hull", &db::Shape::begin_hull, &db::Shape::end_hull,
    "@brief Iterate over the hull contour of the object\n"
    "\n"
    "This method applies to polygons and delivers all points of the polygon hull contour.\n"
    "It will throw an exception for other objects.\n"
  ) +
  gsi::iterator ("each_point_hole", &db::Shape::begin_hole, &db::Shape::end_hole,
    "@brief Iterate over the points of a hole contour\n"
    "@args hole_index\n"
    "\n"
    "This method applies to polygons and delivers all points of the respective hole contour.\n"
    "It will throw an exception for other objects.\n"
    "Simple polygons deliver an empty sequence.\n"
    "\n"
    "@param hole The hole index (see holes () method)\n"
  ) +
  gsi::method ("holes", &db::Shape::holes,
    "@brief Return the number of holes\n"
    "\n"
    "This method applies to polygons and will throw an exception for other objects..\n"
    "Simple polygons deliver a value of zero.\n"
  ) +
  gsi::iterator ("each_edge", (db::Shape::polygon_edge_iterator (db::Shape::*) () const) &db::Shape::begin_edge,
    "@brief Iterate over the edges of the object\n"
    "\n"
    "This method applies to polygons and simple polygons and delivers all edges that form the polygon's contours. "
    "Hole edges are oriented counterclockwise while hull egdes are oriented clockwise.\n"
    "\n"
    "It will throw an exception if the object is not a polygon.\n"
  ) +
  gsi::iterator ("each_edge", (db::Shape::polygon_edge_iterator (db::Shape::*) (unsigned int) const) &db::Shape::begin_edge,
    "@brief Iterate over the edges of the object\n"
    "\n"
    "@args contour\n"
    "@param contour The contour number (0 for hull, 1 for first hole ...)\n"
    "\n"
    "This method applies to polygons and simple polygons and delivers all edges that form the given contour of the polygon. "
    "The hull has contour number 0, the first hole has contour 1 etc.\n"
    "Hole edges are oriented counterclockwise while hull egdes are oriented clockwise.\n"
    "\n"
    "It will throw an exception if the object is not a polygon.\n"
    "\n"
    "This method was introduced in version 0.24."
  ) +
  gsi::method_ext ("box_width", &box_width, 
    "@brief Returns the width of the box\n"
    "\n"
    "Applies to boxes only. Returns the width of the box and throws an exception if the shape is not a box.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("box_width=", &set_box_width, 
    "@brief Sets the width of the box\n"
    "@args w\n"
    "\n"
    "Applies to boxes only. Changes the width of the box and throws an exception if the shape is not a box.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("box_height", &box_height, 
    "@brief Returns the height of the box\n"
    "\n"
    "Applies to boxes only. Returns the height of the box and throws an exception if the shape is not a box.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("box_height=", &set_box_height, 
    "@brief Sets the height of the box\n"
    "@args h\n"
    "\n"
    "Applies to boxes only. Changes the height of the box and throws an exception if the shape is not a box.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("box_center", &box_center, 
    "@brief Returns the center of the box\n"
    "\n"
    "Applies to boxes only. Returns the center of the box and throws an exception if the shape is not a box.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("box_center=", &set_box_center, 
    "@brief Sets the center of the box\n"
    "@args c\n"
    "\n"
    "Applies to boxes only. Changes the center of the box and throws an exception if the shape is not a box.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("box_p1", &box_p1, 
    "@brief Returns the lower left point of the box\n"
    "\n"
    "Applies to boxes only. Returns the lower left point of the box and throws an exception if the shape is not a box.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("box_p1=", &set_box_p1, 
    "@brief Sets the lower left point of the box\n"
    "@args p\n"
    "\n"
    "Applies to boxes only. Changes the lower left point of the box and throws an exception if the shape is not a box.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("box_p2", &box_p2, 
    "@brief Returns the upper right point of the box\n"
    "\n"
    "Applies to boxes only. Returns the upper right point of the box and throws an exception if the shape is not a box.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("box_p2=", &set_box_p2, 
    "@brief Sets the upper right point of the box\n"
    "@args p\n"
    "\n"
    "Applies to boxes only. Changes the upper right point of the box and throws an exception if the shape is not a box.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("type", &object_type, 
    "@brief Return the type of the shape\n"
    "\n"
    "The returned values are the t_... constants available through the corresponding class members.\n"
  ) +
  gsi::method ("is_null?", &db::Shape::is_null,
    "@brief Returns true, if the shape reference is a null reference (not referring to a shape)\n"
  ) +
  gsi::method ("is_polygon?", &db::Shape::is_polygon,
    "@brief Returns true, if the shape is a polygon\n"
    "\n"
    "This method returns true only if the object is a polygon or a simple polygon. "
    "Other objects can convert to polygons, for example paths, so it may be possible to use the \\polygon method also "
    "if is_polygon? does not return true."
  ) +
  gsi::method_ext ("polygon", &get_polygon,
    "@brief Returns the polygon object\n"
    "\n"
    "Returns the polygon object that this shape refers to or converts the object to a polygon. "
    "Paths, boxes and simple polygons are converted to polygons. For paths this operation renders the "
    "path's hull contour.\n"
    "\n"
    "Starting with version 0.23, this method returns nil, if the shape does not represent a geometrical "
    "primitive that can be converted to a polygon.\n"
  ) +
  gsi::method ("is_simple_polygon?", &db::Shape::is_simple_polygon,
    "@brief Returns true, if the shape is a simple polygon\n"
    "\n"
    "This method returns true only if the object is a simple polygon. The simple polygon identity is "
    "contained in the polygon identity, so usually it is sufficient to use \\is_polygon? and "
    "\\polygon instead of specifically handle simply polygons. This method is provided only for "
    "specific optimisation purposes."
  ) +
  gsi::method_ext ("simple_polygon", &get_simple_polygon,
    "@brief Returns the simple polygon object\n"
    "\n"
    "Returns the simple polygon object that this shape refers to or converts the object to a simple polygon. "
    "Paths, boxes and polygons are converted to simple polygons. Polygons with holes will have their holes removed but introducing cut lines that connect the hole contours with the outer contour. "
    "\n"
    "Starting with version 0.23, this method returns nil, if the shape does not represent a geometrical "
    "primitive that can be converted to a simple polygon.\n"
  ) +
  gsi::method_ext ("simple_polygon=", &set_shape<db::SimplePolygon>,
    "@brief Replaces the shape by the given simple polygon object\n"
    "@args box\n"
    "This method replaces the shape by the given simple polygon object. This method can only be called "
    "for editable layouts. It does not change the user properties of the shape.\n"
    "Calling this method will invalidate any iterators. It should not be called inside a "
    "loop iterating over shapes.\n"
    "\n"
    "This method has been introduced in version 0.22."
  ) + 
  gsi::method ("is_path?", &db::Shape::is_path,
    "@brief Returns true, if the shape is a path\n"
  ) +
  gsi::method_ext ("path_width", &path_width,
    "@brief Obtain the path width\n"
    "\n"
    "Applies to paths only. Will throw an exception if the object is not a path.\n"
  ) +
  gsi::method_ext ("path_width=", &set_path_width,
    "@brief Sets the path width\n"
    "@args w\n"
    "\n"
    "Applies to paths only. Will throw an exception if the object is not a path.\n"
    "\n"
    "This method has been introduced in version 0.23."
  ) +
  gsi::method_ext ("round_path?", &round_path,
    "@brief Returns true, if the path has round ends\n"
    "\n"
    "Applies to paths only. Will throw an exception if the object is not a path.\n"
  ) +
  gsi::method_ext ("round_path=", &set_round_path,
    "@brief The path will be a round-ended path if this property is set to true\n"
    "@args r\n"
    "\n"
    "Applies to paths only. Will throw an exception if the object is not a path.\n"
    "Please note that the extensions will apply as well. To get a path with circular ends, set the begin and "
    "end extensions to half the path's width.\n"
    "\n"
    "This method has been introduced in version 0.23."
  ) +
  gsi::method_ext ("path_bgnext", &path_bgnext,
    "@brief Obtain the path's starting vertex extension\n"
    "\n"
    "Applies to paths only. Will throw an exception if the object is not a path.\n"
  ) +
  gsi::method_ext ("path_endext", &path_endext,
    "@brief Obtain the path's end vertex extension\n"
    "\n"
    "Applies to paths only. Will throw an exception if the object is not a path.\n"
  ) +
  gsi::method_ext ("path_bgnext=", &set_path_bgnext,
    "@brief Sets the path's starting vertex extension\n"
    "@args e\n"
    "\n"
    "Applies to paths only. Will throw an exception if the object is not a path.\n"
    "\n"
    "This method has been introduced in version 0.23."
  ) +
  gsi::method_ext ("path_endext=", &set_path_endext,
    "@brief Sets the path's end vertex extension\n"
    "@args e\n"
    "\n"
    "Applies to paths only. Will throw an exception if the object is not a path.\n"
    "\n"
    "This method has been introduced in version 0.23."
  ) +
  gsi::method_ext ("path_length", &path_length,
    "@brief Returns the length of the path\n"
    "\n"
    "Applies to paths only. Will throw an exception if the object is not a path.\n"
    "This method returns the length of the spine plus extensions if present.\n"
    "\n"
    "This method has been added in version 0.23.\n"
  ) +
  gsi::method_ext ("path", &get_path,
    "@brief Returns the path object\n"
    "\n"
    "Starting with version 0.23, this method returns nil, if the shape does not represent a path."
  ) +
  gsi::method ("is_edge?", &db::Shape::is_edge,
    "@brief Returns true, if the object is an edge\n"
  ) +
  gsi::method_ext ("edge", &get_edge,
    "@brief Returns the edge object\n"
    "\n"
    "Starting with version 0.23, this method returns nil, if the shape does not represent an edge."
  ) +
  gsi::method ("is_text?", &db::Shape::is_text,
    "@brief Returns true, if the object is a text\n"
  ) +
  gsi::method_ext ("text", &get_text,
    "@brief Returns the text object\n"
    "\n"
    "Starting with version 0.23, this method returns nil, if the shape does not represent a text."
  ) +
  gsi::method_ext ("text_string", &text_string,
    "@brief Obtain the text string\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
  ) +
  gsi::method_ext ("text_string=", &set_text_string,
    "@brief Sets the text string\n"
    "@args string\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
    "\n"
    "This method has been introduced in version 0.23."
  ) +
  gsi::method_ext ("text_rot", &text_rot,
    "@brief Gets the text's orientation code (see \\Trans)\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
  ) +
  gsi::method_ext ("text_rot=", &set_text_rot,
    "@brief Sets the text's orientation code (see \\Trans)\n"
    "@args o\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
  ) +
  gsi::method_ext ("text_pos", &text_pos,
    "@brief Gets the text's position\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
  ) +
  gsi::method_ext ("text_pos=", &set_text_pos,
    "@brief Sets the text's position\n"
    "@args p\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
  ) +
  gsi::method_ext ("text_trans", &text_trans,
    "@brief Gets the text transformation\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
  ) +
  gsi::method_ext ("text_trans=", &set_text_trans,
    "@brief Sets the text transformation\n"
    "@args trans\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
    "\n"
    "This method has been introduced in version 0.23."
  ) +
  gsi::method_ext ("text_size", &text_size,
    "@brief Gets the text size\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
  ) +
  gsi::method_ext ("text_size=", &set_text_size,
    "@brief Sets the text size\n"
    "@args size\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
    "\n"
    "This method has been introduced in version 0.23."
  ) +
  gsi::method_ext ("text_font", &text_font,
    "@brief Gets the text's font\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
  ) +
  gsi::method_ext ("text_font=", &set_text_font,
    "@brief Sets the text's font\n"
    "@args font\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
    "\n"
    "This method has been introduced in version 0.23."
  ) +
  gsi::method_ext ("text_halign", &text_halign,
    "@brief Gets the text's horizontal alignment\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
    "The return value is 0 for left alignment, 1 for center alignment and 2 to right alignment.\n"
    "\n"
    "This method has been introduced in version 0.22.\n"
  ) +
  gsi::method_ext ("text_halign=", &set_text_halign,
    "@brief Sets the text's horizontal alignment\n"
    "@args a\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
    "See \\text_halign for a description of that property.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("text_valign", &text_valign,
    "@brief Gets the text's vertical alignment\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
    "The return value is 0 for top alignment, 1 for center alignment and 2 to bottom alignment.\n"
    "\n"
    "This method has been introduced in version 0.22.\n"
  ) +
  gsi::method_ext ("text_valign=", &set_text_valign,
    "@brief Sets the text's vertical alignment\n"
    "@args a\n"
    "\n"
    "Applies to texts only. Will throw an exception if the object is not a text.\n"
    "See \\text_valign for a description of that property.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method ("is_box?", &db::Shape::is_box,
    "@brief Returns true if the shape is a box\n"
  ) +
  gsi::method_ext ("box", &get_box,
    "@brief Gets the box object\n"
    "\n"
    "Starting with version 0.23, this method returns nil, if the shape does not represent a box."
  ) +
  gsi::method ("is_user_object?", &db::Shape::is_user_object,
    "@brief Returns true if the shape is a user defined object\n"
  ) +
  gsi::method ("is_array_member?", &db::Shape::is_array_member,
    "@brief Returns true, if the shape is a member of a shape array\n"
  ) +
  gsi::method_ext ("transform", &transform_shape,
    "@brief Transforms the shape with the given transformation\n"
    "@args trans\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("transform", &transform_shape_cplx,
    "@brief Transforms the shape with the given complex transformation\n"
    "@args trans\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("transform", &transform_shape_icplx,
    "@brief Transforms the shape with the given complex transformation\n"
    "@args trans\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("is_valid?", &shape_is_valid,
    "@brief Returns true, if the shape is valid\n"
    "\n"
    "After the shape is deleted, the shape object is no longer valid and this method returns false.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method_ext ("delete", &delete_shape,
    "@brief Deletes the shape\n"
    "\n"
    "After the shape is deleted, the shape object is emptied and points to nothing.\n"
    "\n"
    "This method has been introduced in version 0.23.\n"
  ) +
  gsi::method ("array_trans", &db::Shape::array_trans,
    "@brief Get the array instance member transformation\n"
    "\n"
    "This attribute is valid only if \\is_array_member? is true.\n"
    "The transformation returned describes the relative transformation of the \n"
    "array member addressed.\n"
  ) +
  gsi::method ("bbox", &db::Shape::bbox,
    "@brief Returns the bounding box of the shape\n"
  ) +
  gsi::method_ext ("area", &area,
    "@brief Returns the area of the shape\n"
    "This method has been added in version 0.22.\n"
  ) +
  gsi::method ("perimeter", &db::Shape::perimeter,
    "@brief Returns the perimeter of the shape\n"
    "\n"
    "This method will return an approximation of the perimeter for paths.\n"
    "\n"
    "This method has been added in version 0.23.\n"
  ) +
  gsi::method_ext ("layer_info", &shape_layer,
    "@brief Returns the \\LayerInfo object of the layer the shape is on\n"
    "If the shape does not reside inside a cell, an empty layer is returned.\n"
    "\n"
    "This method has been added in version 0.23.\n"
  ) +
  gsi::method_ext ("layer_info=", &set_shape_layer,
    "@brief Moves the shape to a layer given by a \\LayerInfo object\n"
    "@args layer_info\n"
    "If no layer with the given properties exists, an exception is thrown.\n"
    "\n"
    "This method has been added in version 0.23.\n"
  ) +
  gsi::method_ext ("layer", &shape_layer_index,
    "@brief Returns the layer index of the layer the shape is on\n"
    "Throws an exception if the shape does not reside inside a cell.\n"
    "\n"
    "This method has been added in version 0.23.\n"
  ) +
  gsi::method_ext ("layer=", &set_shape_layer_index,
    "@brief Moves the shape to a layer given by the layer index object\n"
    "@args layer_index\n"
    "\n"
    "This method has been added in version 0.23.\n"
  ) +
  gsi::method ("!=", &db::Shape::operator!=,
    "@brief Inequality operator\n"
    "@args other\n"
  ) +
  gsi::method ("==", &db::Shape::operator==,
    "@brief Equality operator\n"
    "@args other\n"
    "\n"
    "Equality of shapes is not specified by the identity of the objects but by the\n"
    "identity of the pointers - both shapes must refer to the same object.\n"
  ) +
  gsi::method ("to_s", &db::Shape::to_string,
    "@brief Create a string showing the contents of the reference\n"
    "\n"
    "This method has been introduced with version 0.16."
  ) +
  gsi::method ("TNull|#t_null", &t_null) +
  gsi::method ("TPolygon|#t_polygon", &t_polygon) +
  gsi::method ("TPolygonRef|#t_polygon_ref", &t_polygonRef) +
  gsi::method ("TPolygonPtrArray|#t_polygon_ptr_array", &t_polygonPtrArray) +
  gsi::method ("TPolygonPtrArrayMember|#t_polygon_ptr_array_member", &t_polygonPtrArrayMember) +
  gsi::method ("TSimplePolygon|#t_simple_polygon", &t_simplePolygon) +
  gsi::method ("TSimplePolygonRef|#t_simple_polygon_ref", &t_simplePolygonRef) +
  gsi::method ("TSimplePolygonPtrArray|#t_simple_polygon_ptr_array", &t_simplePolygonPtrArray) +
  gsi::method ("TSimplePolygonPtrArrayMember|#t_simple_polygon_ptr_array_member", &t_simplePolygonPtrArrayMember) +
  gsi::method ("TEdge|#t_edge", &t_edge) +
  gsi::method ("TPath|#t_path", &t_path) +
  gsi::method ("TPathRef|#t_path_ref", &t_pathRef) +
  gsi::method ("TPathPtrArray|#t_path_ptr_array", &t_pathPtrArray) +
  gsi::method ("TPathPtrArrayMember|#t_path_ptr_array_member", &t_pathPtrArrayMember) +
  gsi::method ("TBox|#t_box", &t_box) +
  gsi::method ("TBoxArray|#t_box_array", &t_boxArray) +
  gsi::method ("TBoxArrayMember|#t_box_array_member", &t_boxArrayMember) +
  gsi::method ("TShortBox|#t_short_box", &t_shortBox) +
  gsi::method ("TShortBoxArray|#t_short_box_array", &t_shortBoxArray) +
  gsi::method ("TShortBoxArrayMember|#t_short_box_array_member", &t_shortBoxArrayMember) +
  gsi::method ("TText|#t_text", &t_text) +
  gsi::method ("TTextRef|#t_text_ref", &t_textRef) +
  gsi::method ("TTextPtrArray|#t_text_ptr_array", &t_textPtrArray) +
  gsi::method ("TTextPtrArrayMember|#t_text_ptr_array_member", &t_textPtrArrayMember) +
  gsi::method ("TUserObject|#t_user_object", &t_userObject),
  "@brief An object representing a shape in the layout database\n"
  "\n"
  "The shape proxy is basically a pointer to a shape of different kinds.\n"
  "No copy of the shape is created: if the shape proxy is copied the copy still\n"
  "points to the original shape. If the original shape is modified or deleted,\n"
  "the shape proxy will also point to a modified or invalid shape.\n"
  "The proxy can be \"null\" which indicates an invalid reference.\n"
  "\n"
  "Shape objects are used together with the \\Shapes container object which\n"
  "stores the actual shape objects and uses Shape references as pointers inside the\n"
  "actual data storage. Shape references are used in various places, i.e. when removing or\n"
  "transforming objects inside a \\Shapes container.\n"
);

}

