mirror of https://github.com/KLayout/klayout.git
commit
1d873d2ea5
|
|
@ -43,6 +43,7 @@
|
|||
using them to select edges.
|
||||
* Enhancement: New RBA/pya Features
|
||||
- Main window title: MainWindow#title (property)
|
||||
- MainWindow#synchronous (getter added)
|
||||
- LayoutView#is_dirty?
|
||||
- Triangulation: Region#delaunay
|
||||
- Quality rasterizer: Region#rasterize
|
||||
|
|
|
|||
|
|
@ -346,4 +346,156 @@ TriangulationProcessor::process (const db::Polygon &poly, std::vector<db::Polygo
|
|||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// DRCHullProcessor implementation
|
||||
|
||||
DRCHullProcessor::DRCHullProcessor (db::Coord d, db::metrics_type metrics, size_t n_circle)
|
||||
: m_d (d), m_metrics (metrics), m_n_circle (n_circle)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
static void create_edge_segment_euclidian (std::vector<db::Point> &points, const db::Edge &e, const db::Edge &ee, db::Coord dist, size_t n_circle)
|
||||
{
|
||||
db::Vector d (e.d ());
|
||||
db::Vector n (-d.y (), d.x ());
|
||||
|
||||
db::Vector dd (ee.d ());
|
||||
db::Vector nn (-dd.y (), dd.x ());
|
||||
|
||||
if ((d.x () == 0 && d.y () == 0) || (dd.x () == 0 && dd.y () == 0)) {
|
||||
// should not happen
|
||||
return;
|
||||
}
|
||||
|
||||
double f = dist / n.double_length ();
|
||||
double ff = dist / nn.double_length ();
|
||||
|
||||
points.push_back (e.p1 () + db::Vector (n * f));
|
||||
points.push_back (e.p2 () + db::Vector (n * f));
|
||||
|
||||
if (db::vprod_sign (nn, n) < 0) {
|
||||
|
||||
// concave corner
|
||||
points.push_back (e.p2 ());
|
||||
points.push_back (e.p2 () + db::Vector (nn * ff));
|
||||
|
||||
} else {
|
||||
|
||||
double amax;
|
||||
if (db::vprod_sign (nn, n) == 0) {
|
||||
amax = db::sprod_sign (nn, n) < 0 ? M_PI : 0.0;
|
||||
} else {
|
||||
amax = atan2 (db::vprod (nn, n), db::sprod (nn, n));
|
||||
}
|
||||
|
||||
double da = M_PI * 2.0 / n_circle;
|
||||
double f2 = f / cos (0.5 * da);
|
||||
|
||||
int na = int (floor (amax / da + db::epsilon));
|
||||
double a0 = 0.5 * (amax - da * (na - 1));
|
||||
|
||||
for (int i = 0; i < na; ++i) {
|
||||
double a = i * da + a0;
|
||||
points.push_back (e.p2 () + db::Vector (d * (f2 * sin (a)) + n * (f2 * cos (a))));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void create_edge_segment_square (std::vector<db::Point> &points, const db::Edge &e, db::Coord dist)
|
||||
{
|
||||
db::Vector d (e.d ());
|
||||
db::Vector n (-d.y (), d.x ());
|
||||
|
||||
if (d.x () == 0 && d.y () == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
double f = dist / n.double_length ();
|
||||
|
||||
points.push_back (e.p1 ());
|
||||
points.push_back (e.p1 () + db::Vector (d * -f));
|
||||
points.push_back (e.p1 () + db::Vector (d * -f + n * f));
|
||||
points.push_back (e.p2 () + db::Vector (d * f + n * f));
|
||||
points.push_back (e.p2 () + db::Vector (d * f));
|
||||
}
|
||||
|
||||
static void create_edge_segment_projection (std::vector<db::Point> &points, const db::Edge &e, db::Coord dist)
|
||||
{
|
||||
db::Vector d (e.d ());
|
||||
db::Vector n (-d.y (), d.x ());
|
||||
|
||||
if (d.x () == 0 && d.y () == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
double f = dist / n.double_length ();
|
||||
|
||||
points.push_back (e.p1 ());
|
||||
points.push_back (e.p1 () + db::Vector (n * f));
|
||||
points.push_back (e.p2 () + db::Vector (n * f));
|
||||
}
|
||||
|
||||
static void create_edge_segment (std::vector<db::Point> &points, db::metrics_type metrics, const db::Edge &e, const db::Edge &ee, db::Coord d, size_t n_circle)
|
||||
{
|
||||
if (metrics == db::Euclidian) {
|
||||
create_edge_segment_euclidian (points, e, ee, d, n_circle);
|
||||
} else if (metrics == db::Square) {
|
||||
create_edge_segment_square (points, e, d);
|
||||
} else if (metrics == db::Projection) {
|
||||
create_edge_segment_projection (points, e, d);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DRCHullProcessor::process (const db::Polygon &poly, std::vector<db::Polygon> &result) const
|
||||
{
|
||||
db::EdgeProcessor ep;
|
||||
std::vector<db::Point> points;
|
||||
|
||||
for (unsigned int i = 0; i < poly.holes () + 1; ++i) {
|
||||
|
||||
points.clear ();
|
||||
|
||||
auto c = poly.contour (i);
|
||||
if (c.size () < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto p = c.begin (); p != c.end (); ++p) {
|
||||
|
||||
auto pp = p;
|
||||
if (++pp == c.end ()) {
|
||||
pp = c.begin ();
|
||||
}
|
||||
|
||||
auto ppp = pp;
|
||||
if (++ppp == c.end ()) {
|
||||
ppp = c.begin ();
|
||||
}
|
||||
|
||||
create_edge_segment (points, m_metrics, db::Edge (*p, *pp), db::Edge (*pp, *ppp), m_d, m_n_circle);
|
||||
|
||||
}
|
||||
|
||||
for (auto p = points.begin (); p != points.end (); ++p) {
|
||||
|
||||
auto pp = p;
|
||||
if (++ pp == points.end ()) {
|
||||
pp = points.begin ();
|
||||
}
|
||||
|
||||
ep.insert (db::Edge (*p, *pp));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
db::SimpleMerge op;
|
||||
db::PolygonContainer psink (result);
|
||||
db::PolygonGenerator pg (psink, false);
|
||||
ep.process (pg, op);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "dbPolygonTools.h"
|
||||
#include "dbEdgesUtils.h"
|
||||
#include "dbTriangles.h"
|
||||
#include "dbEdgePairRelations.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
|
@ -464,6 +465,24 @@ private:
|
|||
db::MagnificationAndOrientationReducer m_vars;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Computes DRC hulls for DRC space visualization
|
||||
*/
|
||||
class DB_PUBLIC_TEMPLATE DRCHullProcessor
|
||||
: public db::PolygonProcessorBase
|
||||
{
|
||||
public:
|
||||
DRCHullProcessor (db::Coord d, db::metrics_type metrics, size_t n_circle = 64);
|
||||
|
||||
void process (const db::Polygon &poly, std::vector<db::Polygon> &result) const;
|
||||
|
||||
private:
|
||||
db::Coord m_d;
|
||||
db::metrics_type m_metrics;
|
||||
size_t m_n_circle;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "dbShape.h"
|
||||
#include "dbBoxConvert.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "dbHash.h"
|
||||
#include "tlCpp.h"
|
||||
|
||||
namespace db
|
||||
|
|
@ -862,6 +863,24 @@ Shape::box_type Shape::rectangle () const
|
|||
return box_type ();
|
||||
}
|
||||
|
||||
size_t
|
||||
Shape::hash_value () const
|
||||
{
|
||||
size_t h = size_t (m_type);
|
||||
h = std::hcombine (h, std::hfunc (m_trans));
|
||||
|
||||
if (m_stable) {
|
||||
// Use the bytes of the iterator binary pattern (see operator<)
|
||||
for (unsigned int i = 0; i < sizeof (tl::reuse_vector<box_type>::const_iterator); ++i) {
|
||||
h = std::hcombine (h, size_t (m_generic.iter[i]));
|
||||
}
|
||||
} else {
|
||||
h = std::hcombine (h, size_t (m_generic.any));
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
std::string
|
||||
Shape::to_string () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2769,6 +2769,11 @@ public:
|
|||
return m_trans < d.m_trans;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Hash value
|
||||
*/
|
||||
size_t hash_value () const;
|
||||
|
||||
/**
|
||||
* @brief Convert to a string
|
||||
*/
|
||||
|
|
@ -2837,6 +2842,21 @@ public:
|
|||
};
|
||||
|
||||
} // namespace db
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
// provide a template specialization for std::hash<T>
|
||||
template <>
|
||||
struct hash <db::Shape>
|
||||
{
|
||||
size_t operator() (const db::Shape &s) const
|
||||
{
|
||||
return s.hash_value ();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -436,6 +436,11 @@ static db::Region refined_delaunay (const db::Region *r, double max_area, double
|
|||
return res;
|
||||
}
|
||||
|
||||
static db::Region drc_hull (const db::Region *r, db::metrics_type metrics, db::Coord space, size_t n_circle)
|
||||
{
|
||||
return r->processed (db::DRCHullProcessor (space, metrics, n_circle));
|
||||
}
|
||||
|
||||
static db::Region minkowski_sum_pe (const db::Region *r, const db::Edge &e)
|
||||
{
|
||||
return r->processed (db::minkowski_sum_computation<db::Edge> (e));
|
||||
|
|
@ -2782,6 +2787,17 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"The resulting polygons are not merged. In order to remove overlaps, use the \\merge or \\merged method."
|
||||
"Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
method_ext ("drc_hull", &drc_hull, gsi::arg ("metrics"), gsi::arg ("space"), gsi::arg ("n_circle", size_t (64)),
|
||||
"@brief Computes a visualization of the forbidden region for a DRC space check\n"
|
||||
"\n"
|
||||
"@param metrics The metrics to apply\n"
|
||||
"@param space The space value to apply\n"
|
||||
"@param n_circle The full-circle number of points for the Euclidian space visualization\n"
|
||||
"\n"
|
||||
"@return The new polygons representing the forbidden region.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.29.1.\n"
|
||||
) +
|
||||
method_ext ("move", &move_p, gsi::arg ("v"),
|
||||
"@brief Moves the region\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -2131,6 +2131,21 @@ Class<db::Shape> decl_Shape ("db", "Shape",
|
|||
"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 ("<", &db::Shape::operator<, gsi::arg ("other"),
|
||||
"@brief Less operator\n"
|
||||
"\n"
|
||||
"The less operator implementation is based on pointers and not strictly reproducible."
|
||||
"However, it is good enough so Shape objects can serve as keys in hashes (see also \\hash).\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.29.1."
|
||||
) +
|
||||
gsi::method ("hash", &db::Shape::hash_value,
|
||||
"@brief Hash function\n"
|
||||
"\n"
|
||||
"The hash function enables Shape objects as keys in hashes.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.29.1."
|
||||
) +
|
||||
gsi::method ("to_s", &db::Shape::to_string,
|
||||
"@brief Create a string showing the contents of the reference\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -36,8 +36,10 @@ DEFINES += MAKE_EDT_LIBRARY
|
|||
|
||||
HEADERS = \
|
||||
edtDialogs.h \
|
||||
edtEditorHooks.h \
|
||||
edtEditorOptionsPages.h \
|
||||
edtInstPropertiesPage.h \
|
||||
edtMoveTrackerService.h \
|
||||
edtPCellParametersPage.h \
|
||||
edtPropertiesPages.h \
|
||||
edtPropertiesPageUtils.h \
|
||||
|
|
@ -45,12 +47,15 @@ HEADERS = \
|
|||
|
||||
SOURCES = \
|
||||
edtDialogs.cc \
|
||||
edtEditorHooks.cc \
|
||||
edtEditorOptionsPages.cc \
|
||||
edtInstPropertiesPage.cc \
|
||||
edtMoveTrackerService.cc \
|
||||
edtPCellParametersPage.cc \
|
||||
edtPropertiesPages.cc \
|
||||
edtPropertiesPageUtils.cc \
|
||||
edtRecentConfigurationPage.cc
|
||||
edtRecentConfigurationPage.cc \
|
||||
gsiDeclEdtEditorHooks.cc
|
||||
|
||||
# Enabled without Qt:
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,161 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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 "edtEditorHooks.h"
|
||||
#include "tlObjectCollection.h"
|
||||
#include "tlStaticObjects.h"
|
||||
|
||||
namespace edt
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// EditorHooksManager definition and implementation
|
||||
|
||||
class EditorHooksManager;
|
||||
|
||||
static EditorHooksManager *sp_instance = 0;
|
||||
static bool sp_instance_initialized = false;
|
||||
|
||||
class EditorHooksManager
|
||||
{
|
||||
public:
|
||||
EditorHooksManager ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
~EditorHooksManager ()
|
||||
{
|
||||
sp_instance = 0;
|
||||
}
|
||||
|
||||
static EditorHooksManager *instance ()
|
||||
{
|
||||
if (! sp_instance && ! sp_instance_initialized) {
|
||||
sp_instance = new EditorHooksManager ();
|
||||
sp_instance_initialized = true;
|
||||
tl::StaticObjects::reg (&sp_instance);
|
||||
}
|
||||
return sp_instance;
|
||||
}
|
||||
|
||||
void register_editor_hooks (EditorHooks *hooks, const std::string &name)
|
||||
{
|
||||
// needed, so we do not loose the object in case we erase it:
|
||||
tl::shared_ptr<EditorHooks> tmp (hooks);
|
||||
|
||||
// remove other hooks with the same name or with an identical address
|
||||
for (auto h = m_hooks.begin (); h != m_hooks.end (); ) {
|
||||
auto hh = h++;
|
||||
if (hh.operator-> () && (hh.operator-> () == hooks || hh->name () == name)) {
|
||||
m_hooks.erase (hh);
|
||||
}
|
||||
}
|
||||
|
||||
hooks->set_name (name);
|
||||
m_hooks.push_back (hooks);
|
||||
}
|
||||
|
||||
tl::weak_collection<EditorHooks>
|
||||
get_editor_hooks (const std::string &for_technology)
|
||||
{
|
||||
tl::weak_collection<EditorHooks> res;
|
||||
for (auto h = m_hooks.begin (); h != m_hooks.end (); ++h) {
|
||||
if (h.operator-> () && (! h->for_technologies () || h->is_for_technology (for_technology))) {
|
||||
res.push_back (h.operator-> ());
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
tl::shared_collection<EditorHooks> m_hooks;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// EditorHooks implementation
|
||||
|
||||
EditorHooks::EditorHooks ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
EditorHooks::~EditorHooks ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool
|
||||
EditorHooks::is_for_technology (const std::string &name) const
|
||||
{
|
||||
return m_technologies.find (name) != m_technologies.end ();
|
||||
}
|
||||
|
||||
bool
|
||||
EditorHooks::for_technologies () const
|
||||
{
|
||||
return ! m_technologies.empty ();
|
||||
}
|
||||
|
||||
void
|
||||
EditorHooks::set_technology (const std::string &t)
|
||||
{
|
||||
m_technologies.clear ();
|
||||
if (! t.empty ()) {
|
||||
m_technologies.insert (t);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EditorHooks::clear_technologies ()
|
||||
{
|
||||
m_technologies.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
EditorHooks::add_technology (const std::string &tech)
|
||||
{
|
||||
m_technologies.insert (tech);
|
||||
}
|
||||
|
||||
void
|
||||
EditorHooks::register_editor_hooks (EditorHooks *hooks, const std::string &name)
|
||||
{
|
||||
if (EditorHooksManager::instance ()) {
|
||||
hooks->keep ();
|
||||
EditorHooksManager::instance ()->register_editor_hooks (hooks, name);
|
||||
}
|
||||
}
|
||||
|
||||
tl::weak_collection<EditorHooks>
|
||||
EditorHooks::get_editor_hooks (const std::string &for_technology)
|
||||
{
|
||||
if (EditorHooksManager::instance ()) {
|
||||
return EditorHooksManager::instance ()->get_editor_hooks (for_technology);
|
||||
} else {
|
||||
return tl::weak_collection<EditorHooks> ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,294 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_edtEditorHooks
|
||||
#define HDR_edtEditorHooks
|
||||
|
||||
#include "edtCommon.h"
|
||||
#include "dbTrans.h"
|
||||
#include "gsiObject.h"
|
||||
#include "tlExceptions.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlObjectCollection.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
class CellViewRef;
|
||||
class LayoutViewBase;
|
||||
class LayerProperties;
|
||||
class ObjectInstPath;
|
||||
}
|
||||
|
||||
namespace db
|
||||
{
|
||||
class Instance;
|
||||
class Shape;
|
||||
}
|
||||
|
||||
namespace edt
|
||||
{
|
||||
|
||||
/**
|
||||
* @ brief The editor hooks handler object
|
||||
*
|
||||
* Editor hooks are a way to hook into the editor feature - for example
|
||||
* to implement dynamic DRC or display hints.
|
||||
*
|
||||
* The protocols are:
|
||||
*
|
||||
* 1. Object Creation
|
||||
*
|
||||
* begin_create_shapes { begin_new_shapes { create_shape } end_new_shapes } [ commit_shapes ] end_create_shapes
|
||||
* begin_create_instances { begin_new_instances { create_instance } end_new_instances } [ commit_instances ] end_create_instances
|
||||
*
|
||||
* 2. Interactive edit (move, transform, interactive clone)
|
||||
*
|
||||
* begin_edit { begin_edits { transformed | modified } end_edits } [ commit_edit ] end_edit
|
||||
*
|
||||
* Notation: { ... } means the sequence can be repeated, [ ... ] means the call is optional.
|
||||
*/
|
||||
|
||||
class EDT_PUBLIC EditorHooks
|
||||
: public gsi::ObjectBase, public tl::Object
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* The name is arbitrary, but should be unique, as hooks with the
|
||||
* same name replace each other. This is a debugging aid for GSI as we can
|
||||
* re-register hooks while we keep them in the system.
|
||||
*/
|
||||
EditorHooks ();
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~EditorHooks ();
|
||||
|
||||
// shape creation protocol
|
||||
virtual void begin_create_shapes (lay::CellViewRef & /*cv*/, const lay::LayerProperties & /*layer*/) { }
|
||||
virtual void begin_new_shapes () { }
|
||||
virtual void create_shape (const db::Shape & /*shape*/, const db::CplxTrans & /*view_trans*/) { }
|
||||
virtual void end_new_shapes () { }
|
||||
virtual void commit_shapes () { }
|
||||
virtual void end_create_shapes () { }
|
||||
|
||||
// instance creation protocol
|
||||
virtual void begin_create_instances (lay::CellViewRef & /*cv*/) { }
|
||||
virtual void begin_new_instances () { }
|
||||
virtual void create_instance (const db::Instance & /*instance*/, const db::CplxTrans & /*view_trans*/) { }
|
||||
virtual void end_new_instances () { }
|
||||
virtual void commit_instances () { }
|
||||
virtual void end_create_instances () { }
|
||||
|
||||
// editing protocol
|
||||
virtual void begin_edit (lay::CellViewRef & /*cv*/) { }
|
||||
virtual void begin_edits () { }
|
||||
virtual void transformed (const lay::ObjectInstPath & /*object*/, const db::ICplxTrans & /*applied*/, const db::CplxTrans & /*view_trans*/) { }
|
||||
virtual void modified (const lay::ObjectInstPath & /*object*/, const db::Shape & /*shape*/, const db::CplxTrans & /*view_trans*/) { }
|
||||
virtual void end_edits () { }
|
||||
virtual void commit_edit () { }
|
||||
virtual void end_edit () { }
|
||||
|
||||
/**
|
||||
* @brief Gets the name
|
||||
*/
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the name
|
||||
*/
|
||||
void set_name (const std::string &name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the technology name this hook is associated with
|
||||
*
|
||||
* If this attribute is non-empty, the hook is selected only when the given technology is
|
||||
* used for the layout.
|
||||
*/
|
||||
const std::set<std::string> &get_technologies () const
|
||||
{
|
||||
return m_technologies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether this hook is associated with the given technology
|
||||
*/
|
||||
bool is_for_technology (const std::string &name) const;
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the hook is associated with any technology
|
||||
*/
|
||||
bool for_technologies () const;
|
||||
|
||||
/**
|
||||
* @brief Sets the technology name this hook is associated with
|
||||
*
|
||||
* This will reset the list of technologies to this one.
|
||||
* If the given technology string is empty, the list of technologies will be cleared.
|
||||
*/
|
||||
void set_technology (const std::string &t);
|
||||
|
||||
/**
|
||||
* @brief Clears the list of technologies this hook is associated with
|
||||
*/
|
||||
void clear_technologies ();
|
||||
|
||||
/**
|
||||
* @brief Additionally associate the hook with the given technology
|
||||
*/
|
||||
void add_technology (const std::string &tech);
|
||||
|
||||
/**
|
||||
* @brief Registers the editor hook
|
||||
*/
|
||||
static void register_editor_hooks (EditorHooks *hooks, const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Gets the editor hooks for a given technology
|
||||
*
|
||||
* The order of the hooks is determined by the registration order.
|
||||
*/
|
||||
static tl::weak_collection<EditorHooks> get_editor_hooks (const std::string &for_technology);
|
||||
|
||||
private:
|
||||
std::set<std::string> m_technologies;
|
||||
std::string m_name;
|
||||
|
||||
// no copying.
|
||||
EditorHooks &operator= (const EditorHooks &);
|
||||
EditorHooks (const EditorHooks &);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper function to call editor hooks in the right sequence and with error handling
|
||||
*/
|
||||
|
||||
inline
|
||||
void call_editor_hooks (const tl::weak_collection<EditorHooks> &hooks, void (EditorHooks::*meth) ())
|
||||
{
|
||||
for (auto h = hooks.begin (); h != hooks.end (); ++h) {
|
||||
try {
|
||||
if (h.operator-> ()) {
|
||||
(const_cast<EditorHooks *> (h.operator-> ())->*meth) ();
|
||||
}
|
||||
} catch (tl::CancelException &) {
|
||||
return;
|
||||
} catch (tl::Exception &ex) {
|
||||
tl::error << ex.msg ();
|
||||
} catch (std::exception &ex) {
|
||||
tl::error << ex.what ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function to call editor hooks in the right sequence and with error handling
|
||||
*
|
||||
* This version provides one argument
|
||||
*/
|
||||
|
||||
template <class A1>
|
||||
inline
|
||||
void call_editor_hooks (const tl::weak_collection<EditorHooks> &hooks, void (EditorHooks::*meth) (A1), A1 a1)
|
||||
{
|
||||
for (auto h = hooks.begin (); h != hooks.end (); ++h) {
|
||||
try {
|
||||
if (h.operator-> ()) {
|
||||
(const_cast<EditorHooks *> (h.operator-> ())->*meth) (a1);
|
||||
}
|
||||
} catch (tl::CancelException &) {
|
||||
return;
|
||||
} catch (tl::Exception &ex) {
|
||||
tl::error << ex.msg ();
|
||||
} catch (std::exception &ex) {
|
||||
tl::error << ex.what ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function to call editor hooks in the right sequence and with error handling
|
||||
*
|
||||
* This version provides two arguments
|
||||
*/
|
||||
|
||||
template <class A1, class A2>
|
||||
inline
|
||||
void call_editor_hooks (const tl::weak_collection<EditorHooks> &hooks, void (EditorHooks::*meth) (A1, A2), A1 a1, A2 a2)
|
||||
{
|
||||
for (auto h = hooks.begin (); h != hooks.end (); ++h) {
|
||||
try {
|
||||
if (h.operator-> ()) {
|
||||
(const_cast<EditorHooks *> (h.operator-> ())->*meth) (a1, a2);
|
||||
}
|
||||
} catch (tl::CancelException &) {
|
||||
return;
|
||||
} catch (tl::Exception &ex) {
|
||||
tl::error << ex.msg ();
|
||||
} catch (std::exception &ex) {
|
||||
tl::error << ex.what ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function to call editor hooks in the right sequence and with error handling
|
||||
*
|
||||
* This version provides three arguments
|
||||
*/
|
||||
|
||||
template <class A1, class A2, class A3>
|
||||
inline
|
||||
void call_editor_hooks (const tl::weak_collection<EditorHooks> &hooks, void (EditorHooks::*meth) (A1, A2, A3), A1 a1, A2 a2, A3 a3)
|
||||
{
|
||||
for (auto h = hooks.begin (); h != hooks.end (); ++h) {
|
||||
try {
|
||||
if (h.operator-> ()) {
|
||||
(const_cast<EditorHooks *> (h.operator-> ())->*meth) (a1, a2, a3);
|
||||
}
|
||||
} catch (tl::CancelException &) {
|
||||
return;
|
||||
} catch (tl::Exception &ex) {
|
||||
tl::error << ex.msg ();
|
||||
} catch (std::exception &ex) {
|
||||
tl::error << ex.what ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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 "layLayoutViewBase.h"
|
||||
#include "edtMoveTrackerService.h"
|
||||
#include "edtService.h"
|
||||
#include "edtPartialService.h"
|
||||
|
||||
namespace edt
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------
|
||||
|
||||
MoveTrackerService::MoveTrackerService (lay::LayoutViewBase *view)
|
||||
: lay::EditorServiceBase (view),
|
||||
mp_view (view)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
MoveTrackerService::~MoveTrackerService ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool
|
||||
MoveTrackerService::begin_move (lay::Editable::MoveMode mode, const db::DPoint & /*p*/, lay::angle_constraint_type /*ac*/)
|
||||
{
|
||||
if (view ()->is_editable () && mode == lay::Editable::Selected) {
|
||||
open_editor_hooks ();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
MoveTrackerService::issue_edit_events ()
|
||||
{
|
||||
if (m_editor_hooks.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_edits);
|
||||
|
||||
// build the transformation variants cache
|
||||
TransformationVariants tv (view ());
|
||||
|
||||
std::vector<edt::Service *> services = view ()->get_plugins<edt::Service> ();
|
||||
std::vector<lay::ObjectInstPath> sel;
|
||||
|
||||
for (auto s = services.begin (); s != services.end (); ++s) {
|
||||
|
||||
edt::Service *svc = *s;
|
||||
|
||||
sel.clear ();
|
||||
svc->get_selection (sel);
|
||||
|
||||
for (auto r = sel.begin (); r != sel.end (); ++r) {
|
||||
|
||||
const lay::CellView &cv = view ()->cellview (r->cv_index ());
|
||||
|
||||
// compute the transformation into context cell's micron space
|
||||
double dbu = cv->layout ().dbu ();
|
||||
db::CplxTrans gt = db::CplxTrans (dbu) * cv.context_trans () * r->trans ();
|
||||
|
||||
// get one representative global transformation
|
||||
const std::vector<db::DCplxTrans> *tv_list = 0;
|
||||
if (r->is_cell_inst ()) {
|
||||
tv_list = tv.per_cv (r->cv_index ());
|
||||
} else {
|
||||
tv_list = tv.per_cv_and_layer (r->cv_index (), r->layer ());
|
||||
}
|
||||
if (tv_list && ! tv_list->empty ()) {
|
||||
gt = tv_list->front () * gt;
|
||||
}
|
||||
|
||||
// compute the move transformation in local object space
|
||||
db::ICplxTrans applied = gt.inverted () * db::DCplxTrans (svc->move_trans ()) * gt;
|
||||
|
||||
call_editor_hooks<const lay::ObjectInstPath &, const db::ICplxTrans &, const db::CplxTrans &> (m_editor_hooks, &edt::EditorHooks::transformed, *r, applied, gt);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// make the Partial Edit Service issue "modify" events
|
||||
|
||||
std::vector<edt::PartialService *> partial_services = view ()->get_plugins<edt::PartialService> ();
|
||||
|
||||
for (auto s = partial_services.begin (); s != partial_services.end (); ++s) {
|
||||
(*s)->issue_editor_hook_calls (m_editor_hooks);
|
||||
}
|
||||
|
||||
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_edits);
|
||||
}
|
||||
|
||||
void
|
||||
MoveTrackerService::move (const db::DPoint & /*pu*/, lay::angle_constraint_type /*ac*/)
|
||||
{
|
||||
// we don't interpret this event, but use it to request status from the editor services
|
||||
issue_edit_events ();
|
||||
}
|
||||
|
||||
void
|
||||
MoveTrackerService::move_transform (const db::DPoint & /*pu*/, db::DFTrans /*tr*/, lay::angle_constraint_type /*ac*/)
|
||||
{
|
||||
// we don't interpret this event, but use it to request status from the editor services
|
||||
issue_edit_events ();
|
||||
}
|
||||
|
||||
void
|
||||
MoveTrackerService::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type /*ac*/)
|
||||
{
|
||||
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::commit_edit);
|
||||
move_cancel (); // formally this functionality fits here
|
||||
}
|
||||
|
||||
void
|
||||
MoveTrackerService::edit_cancel ()
|
||||
{
|
||||
move_cancel ();
|
||||
}
|
||||
|
||||
void
|
||||
MoveTrackerService::move_cancel ()
|
||||
{
|
||||
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_edit);
|
||||
m_editor_hooks.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
MoveTrackerService::open_editor_hooks ()
|
||||
{
|
||||
lay::CellViewRef cv_ref (view ()->cellview_ref (view ()->active_cellview_index ()));
|
||||
if (! cv_ref.is_valid ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string technology;
|
||||
if (cv_ref->layout ().technology ()) {
|
||||
technology = cv_ref->layout ().technology ()->name ();
|
||||
}
|
||||
|
||||
m_editor_hooks = edt::EditorHooks::get_editor_hooks (technology);
|
||||
call_editor_hooks<lay::CellViewRef &> (m_editor_hooks, &edt::EditorHooks::begin_edit, (lay::CellViewRef &) cv_ref);
|
||||
}
|
||||
|
||||
} // namespace edt
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_edtMoveTrackerService
|
||||
#define HDR_edtMoveTrackerService
|
||||
|
||||
#include "edtCommon.h"
|
||||
|
||||
#include "layEditorServiceBase.h"
|
||||
#include "edtEditorHooks.h"
|
||||
|
||||
namespace edt {
|
||||
|
||||
/**
|
||||
* @brief A service tracking move commands a forwarding them to the editor hooks
|
||||
*/
|
||||
|
||||
class EDT_PUBLIC MoveTrackerService
|
||||
: public lay::EditorServiceBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
MoveTrackerService (lay::LayoutViewBase *view);
|
||||
|
||||
/**
|
||||
* @brief The destructor
|
||||
*/
|
||||
~MoveTrackerService ();
|
||||
|
||||
/**
|
||||
* @brief Begin a "move" operation
|
||||
*/
|
||||
virtual bool begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::angle_constraint_type ac);
|
||||
|
||||
/**
|
||||
* @brief Continue a "move" operation
|
||||
*/
|
||||
virtual void move (const db::DPoint &p, lay::angle_constraint_type ac);
|
||||
|
||||
/**
|
||||
* @brief Transform during a move operation
|
||||
*/
|
||||
virtual void move_transform (const db::DPoint &p, db::DFTrans tr, lay::angle_constraint_type ac);
|
||||
|
||||
/**
|
||||
* @brief Terminate a "move" operation
|
||||
*/
|
||||
virtual void end_move (const db::DPoint &p, lay::angle_constraint_type ac);
|
||||
|
||||
/**
|
||||
* @brief Access to the view object
|
||||
*/
|
||||
lay::LayoutViewBase *view () const
|
||||
{
|
||||
tl_assert (mp_view != 0);
|
||||
return mp_view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cancel any edit operations (such as move)
|
||||
*/
|
||||
virtual void edit_cancel ();
|
||||
|
||||
private:
|
||||
lay::LayoutViewBase *mp_view;
|
||||
tl::weak_collection<edt::EditorHooks> m_editor_hooks;
|
||||
|
||||
void move_cancel ();
|
||||
void open_editor_hooks ();
|
||||
void issue_edit_events ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1416,7 +1416,216 @@ PartialService::transform (const db::DCplxTrans &tr)
|
|||
selection_to_view ();
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
PartialService::open_editor_hooks ()
|
||||
{
|
||||
lay::CellViewRef cv_ref (view ()->cellview_ref (view ()->active_cellview_index ()));
|
||||
if (! cv_ref.is_valid ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string technology;
|
||||
if (cv_ref->layout ().technology ()) {
|
||||
technology = cv_ref->layout ().technology ()->name ();
|
||||
}
|
||||
|
||||
m_editor_hooks = edt::EditorHooks::get_editor_hooks (technology);
|
||||
call_editor_hooks<lay::CellViewRef &> (m_editor_hooks, &edt::EditorHooks::begin_edit, (lay::CellViewRef &) cv_ref);
|
||||
}
|
||||
|
||||
void
|
||||
PartialService::close_editor_hooks (bool commit)
|
||||
{
|
||||
if (commit) {
|
||||
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::commit_edit);
|
||||
}
|
||||
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_edit);
|
||||
|
||||
m_editor_hooks.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
PartialService::issue_editor_hook_calls (const tl::weak_collection<edt::EditorHooks> &hooks)
|
||||
{
|
||||
if (hooks.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: needs to be called during move operations
|
||||
db::DTrans move_trans = db::DTrans (m_current - m_start);
|
||||
|
||||
// build the transformation variants cache
|
||||
TransformationVariants tv (view ());
|
||||
|
||||
// Issue editor hook calls for the shape modification events
|
||||
|
||||
// since a shape reference may become invalid while moving it and
|
||||
// because it creates ambiguities, we treat each shape separately:
|
||||
// collect the valid selected items in a selection-per-shape map.
|
||||
std::map <db::Shape, std::vector<partial_objects::iterator> > sel_per_shape;
|
||||
|
||||
for (partial_objects::iterator r = m_selection.begin (); r != m_selection.end (); ++r) {
|
||||
if (! r->first.is_cell_inst ()) {
|
||||
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv_and_layer (r->first.cv_index (), r->first.layer ());
|
||||
if (tv_list && ! tv_list->empty ()) {
|
||||
sel_per_shape.insert (std::make_pair (r->first.shape (), std::vector<partial_objects::iterator> ())).first->second.push_back (r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db::Shapes tmp_shapes;
|
||||
|
||||
for (std::map <db::Shape, std::vector<partial_objects::iterator> >::iterator sps = sel_per_shape.begin (); sps != sel_per_shape.end (); ++sps) {
|
||||
|
||||
db::Shape shape = tmp_shapes.insert (sps->first);
|
||||
for (std::vector<partial_objects::iterator>::const_iterator rr = sps->second.begin (); rr != sps->second.end (); ++rr) {
|
||||
|
||||
std::map <EdgeWithIndex, db::Edge> new_edges;
|
||||
std::map <PointWithIndex, db::Point> new_points;
|
||||
|
||||
shape = modify_shape (tv, shape, (*rr)->first, (*rr)->second, move_trans, new_edges, new_points);
|
||||
|
||||
}
|
||||
|
||||
for (std::vector<partial_objects::iterator>::const_iterator rr = sps->second.begin (); rr != sps->second.end (); ++rr) {
|
||||
|
||||
const lay::ObjectInstPath &sel = (*rr)->first;
|
||||
|
||||
const lay::CellView &cv = view ()->cellview (sel.cv_index ());
|
||||
|
||||
// compute the transformation into context cell's micron space
|
||||
double dbu = cv->layout ().dbu ();
|
||||
db::CplxTrans gt = db::CplxTrans (dbu) * cv.context_trans () * sel.trans ();
|
||||
|
||||
// get one representative global transformation
|
||||
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv_and_layer (sel.cv_index (), sel.layer ());
|
||||
if (tv_list && ! tv_list->empty ()) {
|
||||
gt = tv_list->front () * gt;
|
||||
}
|
||||
|
||||
call_editor_hooks<const lay::ObjectInstPath &, const db::Shape &, const db::CplxTrans &> (hooks, &edt::EditorHooks::modified, (*rr)->first, shape, gt);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Issue editor hook calls for the instance transformation events
|
||||
|
||||
// sort the selected objects (the instances) by the cell they are in
|
||||
// The key is a pair: cell_index, cv_index
|
||||
std::map <std::pair <db::cell_index_type, unsigned int>, std::vector <partial_objects::const_iterator> > insts_by_cell;
|
||||
for (partial_objects::const_iterator r = m_selection.begin (); r != m_selection.end (); ++r) {
|
||||
if (r->first.is_cell_inst ()) {
|
||||
insts_by_cell.insert (std::make_pair (std::make_pair (r->first.cell_index (), r->first.cv_index ()), std::vector <partial_objects::const_iterator> ())).first->second.push_back (r);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::map <std::pair <db::cell_index_type, unsigned int>, std::vector <partial_objects::const_iterator> >::const_iterator ibc = insts_by_cell.begin (); ibc != insts_by_cell.end (); ++ibc) {
|
||||
|
||||
const lay::CellView &cv = view ()->cellview (ibc->first.second);
|
||||
if (cv.is_valid ()) {
|
||||
|
||||
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv (ibc->first.second);
|
||||
db::DCplxTrans tvt;
|
||||
if (tv_list && ! tv_list->empty ()) {
|
||||
tvt = tv_list->front ();
|
||||
}
|
||||
|
||||
for (auto inst = ibc->second.begin (); inst != ibc->second.end (); ++inst) {
|
||||
|
||||
db::CplxTrans gt = tvt * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans () * (*inst)->first.trans ();
|
||||
db::ICplxTrans applied = gt.inverted () * db::DCplxTrans (move_trans) * gt;
|
||||
|
||||
call_editor_hooks<const lay::ObjectInstPath &, const db::ICplxTrans &, const db::CplxTrans &> (hooks, &edt::EditorHooks::transformed, (*inst)->first, applied, gt);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
db::Shape
|
||||
PartialService::modify_shape (TransformationVariants &tv, const db::Shape &shape_in, const lay::ObjectInstPath &path, const std::set <EdgeWithIndex> &sel, const db::DTrans &move_trans, std::map <EdgeWithIndex, db::Edge> &new_edges, std::map <PointWithIndex, db::Point> &new_points)
|
||||
{
|
||||
tl_assert (shape_in.shapes () != 0);
|
||||
db::Shape shape = shape_in;
|
||||
db::Shapes &shapes = *shape_in.shapes ();
|
||||
|
||||
const lay::CellView &cv = view ()->cellview (path.cv_index ());
|
||||
|
||||
// use only the first one of the explicit transformations
|
||||
// TODO: clarify how this can be implemented in a more generic form or leave it thus.
|
||||
|
||||
db::ICplxTrans gt (cv.context_trans () * path.trans ());
|
||||
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv_and_layer (path.cv_index (), path.layer ());
|
||||
db::CplxTrans tt = (*tv_list) [0] * db::CplxTrans (cv->layout ().dbu ()) * gt;
|
||||
db::Vector move_vector = db::Vector ((tt.inverted () * db::DCplxTrans (move_trans) * tt).disp ());
|
||||
|
||||
create_shift_sets (shape, sel, new_points, new_edges, move_vector);
|
||||
|
||||
// modify the shapes and insert
|
||||
|
||||
if (shape.is_polygon ()) {
|
||||
|
||||
db::Polygon poly;
|
||||
shape.polygon (poly);
|
||||
|
||||
// warning: poly is modified:
|
||||
modify_polygon (poly, new_points, new_edges, true /*compress*/);
|
||||
|
||||
shape = shapes.replace (shape, poly);
|
||||
|
||||
} else if (shape.is_path ()) {
|
||||
|
||||
db::Path path;
|
||||
shape.path (path);
|
||||
|
||||
// warning: path is modified:
|
||||
modify_path (path, new_points, new_edges, true /*compress*/);
|
||||
|
||||
shape = shapes.replace (shape, path);
|
||||
|
||||
} else if (shape.is_box ()) {
|
||||
|
||||
db::Polygon poly;
|
||||
shape.polygon (poly);
|
||||
|
||||
// warning: poly is modified:
|
||||
modify_polygon (poly, new_points, new_edges, true /*compress*/);
|
||||
|
||||
shape = shapes.replace (shape, poly.box ());
|
||||
|
||||
} else if (shape.is_text ()) {
|
||||
|
||||
db::Text t;
|
||||
shape.text (t);
|
||||
|
||||
db::Point tp (shape.text_trans () * db::Point ());
|
||||
std::map <PointWithIndex, db::Point>::const_iterator np = new_points.find (PointWithIndex (tp, 0, 0));
|
||||
|
||||
if (np != new_points.end ()) {
|
||||
t.transform (db::Trans (np->second - tp));
|
||||
shape = shapes.replace (shape, t);
|
||||
}
|
||||
|
||||
} else if (shape.is_point ()) {
|
||||
|
||||
db::Point p;
|
||||
shape.point (p);
|
||||
|
||||
std::map <PointWithIndex, db::Point>::const_iterator np = new_points.find (PointWithIndex (p, 0, 0));
|
||||
|
||||
if (np != new_points.end ()) {
|
||||
shape = shapes.replace (shape, np->second);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
void
|
||||
PartialService::transform_selection (const db::DTrans &move_trans)
|
||||
{
|
||||
// build the transformation variants cache
|
||||
|
|
@ -1444,79 +1653,9 @@ PartialService::transform_selection (const db::DTrans &move_trans)
|
|||
|
||||
partial_objects::iterator r = *rr;
|
||||
|
||||
const lay::CellView &cv = view ()->cellview (r->first.cv_index ());
|
||||
|
||||
// use only the first one of the explicit transformations
|
||||
// TODO: clarify how this can be implemented in a more generic form or leave it thus.
|
||||
|
||||
db::ICplxTrans gt (cv.context_trans () * r->first.trans ());
|
||||
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv_and_layer (r->first.cv_index (), r->first.layer ());
|
||||
db::CplxTrans tt = (*tv_list) [0] * db::CplxTrans (cv->layout ().dbu ()) * gt;
|
||||
db::Vector move_vector = db::Vector ((tt.inverted () * db::DCplxTrans (move_trans) * tt).disp ());
|
||||
|
||||
std::map <EdgeWithIndex, db::Edge> new_edges;
|
||||
std::map <PointWithIndex, db::Point> new_points;
|
||||
create_shift_sets (shape, r->second, new_points, new_edges, move_vector);
|
||||
|
||||
// modify the shapes and insert
|
||||
|
||||
db::Shapes &shapes = cv->layout ().cell (r->first.cell_index ()).shapes (r->first.layer ());
|
||||
|
||||
if (shape.is_polygon ()) {
|
||||
|
||||
db::Polygon poly;
|
||||
shape.polygon (poly);
|
||||
|
||||
// warning: poly is modified:
|
||||
modify_polygon (poly, new_points, new_edges, true /*compress*/);
|
||||
|
||||
shape = shapes.replace (shape, poly);
|
||||
|
||||
} else if (shape.is_path ()) {
|
||||
|
||||
db::Path path;
|
||||
shape.path (path);
|
||||
|
||||
// warning: path is modified:
|
||||
modify_path (path, new_points, new_edges, true /*compress*/);
|
||||
|
||||
shape = shapes.replace (shape, path);
|
||||
|
||||
} else if (shape.is_box ()) {
|
||||
|
||||
db::Polygon poly;
|
||||
shape.polygon (poly);
|
||||
|
||||
// warning: poly is modified:
|
||||
modify_polygon (poly, new_points, new_edges, true /*compress*/);
|
||||
|
||||
shape = shapes.replace (shape, poly.box ());
|
||||
|
||||
} else if (shape.is_text ()) {
|
||||
|
||||
db::Text t;
|
||||
shape.text (t);
|
||||
|
||||
db::Point tp (shape.text_trans () * db::Point ());
|
||||
std::map <PointWithIndex, db::Point>::const_iterator np = new_points.find (PointWithIndex (tp, 0, 0));
|
||||
|
||||
if (np != new_points.end ()) {
|
||||
t.transform (db::Trans (np->second - tp));
|
||||
shape = shapes.replace (shape, t);
|
||||
}
|
||||
|
||||
} else if (shape.is_point ()) {
|
||||
|
||||
db::Point p;
|
||||
shape.point (p);
|
||||
|
||||
std::map <PointWithIndex, db::Point>::const_iterator np = new_points.find (PointWithIndex (p, 0, 0));
|
||||
|
||||
if (np != new_points.end ()) {
|
||||
shape = shapes.replace (shape, np->second);
|
||||
}
|
||||
|
||||
}
|
||||
shape = modify_shape (tv, shape, r->first, r->second, move_trans, new_edges, new_points);
|
||||
|
||||
// transform the selection
|
||||
|
||||
|
|
@ -1625,6 +1764,8 @@ PartialService::edit_cancel ()
|
|||
|
||||
ui ()->ungrab_mouse (this);
|
||||
|
||||
close_editor_hooks (false);
|
||||
|
||||
selection_to_view ();
|
||||
}
|
||||
|
||||
|
|
@ -1671,6 +1812,10 @@ PartialService::mouse_move_event (const db::DPoint &p, unsigned int buttons, boo
|
|||
|
||||
selection_to_view ();
|
||||
|
||||
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_edits);
|
||||
issue_editor_hook_calls (m_editor_hooks);
|
||||
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_edits);
|
||||
|
||||
m_alt_ac = lay::AC_Global;
|
||||
|
||||
} else if (prio) {
|
||||
|
|
@ -1796,6 +1941,8 @@ PartialService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bo
|
|||
|
||||
ui ()->grab_mouse (this, true);
|
||||
|
||||
open_editor_hooks ();
|
||||
|
||||
}
|
||||
|
||||
m_alt_ac = lay::AC_Global;
|
||||
|
|
@ -1858,6 +2005,8 @@ PartialService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bo
|
|||
m_dragging = false;
|
||||
selection_to_view ();
|
||||
|
||||
close_editor_hooks (true);
|
||||
|
||||
m_alt_ac = lay::AC_Global;
|
||||
|
||||
return true;
|
||||
|
|
@ -1991,6 +2140,8 @@ PartialService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bo
|
|||
m_current = m_start = p;
|
||||
}
|
||||
|
||||
open_editor_hooks ();
|
||||
|
||||
}
|
||||
|
||||
selection_to_view ();
|
||||
|
|
@ -2023,6 +2174,8 @@ PartialService::mouse_double_click_event (const db::DPoint &p, unsigned int butt
|
|||
|
||||
m_alt_ac = ac_from_buttons (buttons);
|
||||
|
||||
close_editor_hooks (false);
|
||||
|
||||
// stop dragging
|
||||
ui ()->ungrab_mouse (this);
|
||||
m_dragging = false;
|
||||
|
|
@ -2353,6 +2506,8 @@ PartialService::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type a
|
|||
|
||||
clear_mouse_cursors ();
|
||||
|
||||
close_editor_hooks (false);
|
||||
|
||||
m_alt_ac = lay::AC_Global;
|
||||
}
|
||||
|
||||
|
|
@ -2530,6 +2685,8 @@ PartialService::del ()
|
|||
m_dragging = false;
|
||||
selection_to_view ();
|
||||
|
||||
close_editor_hooks (false);
|
||||
|
||||
// clean up the layouts that need to do so.
|
||||
for (std::set<db::Layout *>::const_iterator l = needs_cleanup.begin (); l != needs_cleanup.end (); ++l) {
|
||||
(*l)->cleanup ();
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "tlDeferredExecution.h"
|
||||
#include "edtUtils.h"
|
||||
#include "edtConfig.h"
|
||||
#include "edtEditorHooks.h"
|
||||
|
||||
#if defined(HAVE_QT)
|
||||
# include <QObject>
|
||||
|
|
@ -316,6 +317,11 @@ public:
|
|||
*/
|
||||
virtual void edit_cancel ();
|
||||
|
||||
/**
|
||||
* @brief Issues editor hook calls ("modified") for the current selection and the given move transformation
|
||||
*/
|
||||
void issue_editor_hook_calls (const tl::weak_collection<edt::EditorHooks> &hooks);
|
||||
|
||||
#if defined(HAVE_QT)
|
||||
public slots:
|
||||
void timeout ();
|
||||
|
|
@ -361,6 +367,8 @@ private:
|
|||
bool m_hover_wait;
|
||||
db::DPoint m_hover_point;
|
||||
|
||||
tl::weak_collection<edt::EditorHooks> m_editor_hooks;
|
||||
|
||||
// Deferred method to update the selection
|
||||
tl::DeferredMethod<edt::PartialService> dm_selection_to_view;
|
||||
|
||||
|
|
@ -392,6 +400,10 @@ private:
|
|||
db::DEdge single_selected_edge () const;
|
||||
bool handle_guiding_shape_changes ();
|
||||
void transform_selection (const db::DTrans &move_trans);
|
||||
db::Shape modify_shape (TransformationVariants &tv, const db::Shape &shape_in, const lay::ObjectInstPath &path, const std::set<EdgeWithIndex> &sel, const db::DTrans &move_trans, std::map <EdgeWithIndex, db::Edge> &new_edges, std::map <PointWithIndex, db::Point> &new_points);
|
||||
|
||||
void open_editor_hooks ();
|
||||
void close_editor_hooks (bool commit);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "edtServiceImpl.h"
|
||||
#include "edtMainService.h"
|
||||
#include "edtPartialService.h"
|
||||
#include "edtMoveTrackerService.h"
|
||||
#if defined(HAVE_QT)
|
||||
# include "edtEditorOptionsPages.h"
|
||||
# include "edtRecentConfigurationPage.h"
|
||||
|
|
@ -565,5 +566,26 @@ static tl::RegisteredClass<lay::PluginDeclaration> config_decl30 (
|
|||
"edt::PartialService"
|
||||
);
|
||||
|
||||
class MoveTrackerPluginDeclaration
|
||||
: public lay::PluginDeclaration
|
||||
{
|
||||
public:
|
||||
MoveTrackerPluginDeclaration ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual lay::Plugin *create_plugin (db::Manager * /*manager*/, lay::Dispatcher * /*root*/, lay::LayoutViewBase *view) const
|
||||
{
|
||||
return new edt::MoveTrackerService (view);
|
||||
}
|
||||
};
|
||||
|
||||
static tl::RegisteredClass<lay::PluginDeclaration> config_decl40 (
|
||||
new MoveTrackerPluginDeclaration (),
|
||||
4100,
|
||||
"edt::MoveTrackerService"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -381,6 +381,22 @@ public:
|
|||
*/
|
||||
std::pair<bool, lay::ObjectInstPath> handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const;
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether a move operation is ongoing
|
||||
*/
|
||||
bool is_moving () const
|
||||
{
|
||||
return m_moving;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the current move transformation (in DBU space on context cell level)
|
||||
*/
|
||||
const db::DTrans &move_trans () const
|
||||
{
|
||||
return m_move_trans;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Update m_markers to reflect the selection
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ ShapeEditService::get_edit_layer ()
|
|||
m_cv_index = (unsigned int) cv_index;
|
||||
m_trans = (cl->trans ().front () * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans ()).inverted ();
|
||||
mp_layout = &(cv->layout ());
|
||||
mp_cell = &(mp_layout->cell (cv.cell_index ()));
|
||||
mp_cell = cv.cell ();
|
||||
|
||||
if (mp_cell->is_proxy ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Cannot put a shape into a PCell or library cell")));
|
||||
|
|
@ -157,7 +157,7 @@ ShapeEditService::update_edit_layer (const lay::LayerPropertiesConstIterator &cl
|
|||
return;
|
||||
}
|
||||
|
||||
if (cv->layout ().cell (cv.cell_index ()).is_proxy ()) {
|
||||
if (cv.cell ()->is_proxy ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -198,7 +198,7 @@ ShapeEditService::update_edit_layer (const lay::LayerPropertiesConstIterator &cl
|
|||
m_cv_index = (unsigned int) cv_index;
|
||||
m_trans = (cl->trans ().front () * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans ()).inverted ();
|
||||
mp_layout = &(cv->layout ());
|
||||
mp_cell = &(mp_layout->cell (cv.cell_index ()));
|
||||
mp_cell = cv.cell ();
|
||||
|
||||
current_layer_changed ();
|
||||
}
|
||||
|
|
@ -400,6 +400,47 @@ ShapeEditService::deliver_shape (const db::Point &point)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ShapeEditService::open_editor_hooks ()
|
||||
{
|
||||
std::string technology;
|
||||
if (mp_layout && mp_layout->technology ()) {
|
||||
technology = mp_layout->technology ()->name ();
|
||||
}
|
||||
|
||||
m_editor_hooks = edt::EditorHooks::get_editor_hooks (technology);
|
||||
|
||||
lay::CellViewRef cv_ref (view ()->cellview_ref (m_cv_index));
|
||||
call_editor_hooks<lay::CellViewRef &, const lay::LayerProperties &> (m_editor_hooks, &edt::EditorHooks::begin_create_shapes, cv_ref, *view ()->current_layer ());
|
||||
}
|
||||
|
||||
void
|
||||
ShapeEditService::close_editor_hooks (bool with_commit)
|
||||
{
|
||||
if (with_commit) {
|
||||
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::commit_shapes);
|
||||
}
|
||||
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_create_shapes);
|
||||
|
||||
m_editor_hooks.clear ();
|
||||
}
|
||||
|
||||
template <class Shape>
|
||||
void
|
||||
ShapeEditService::deliver_shape_to_hooks (const Shape &shape)
|
||||
{
|
||||
db::Shapes tmp (true);
|
||||
db::Shape s = tmp.insert (shape);
|
||||
call_editor_hooks<const db::Shape &, const db::CplxTrans &> (m_editor_hooks, &edt::EditorHooks::create_shape, s, trans ().inverted ());
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template void ShapeEditService::deliver_shape_to_hooks<db::Polygon> (const db::Polygon &);
|
||||
template void ShapeEditService::deliver_shape_to_hooks<db::Path> (const db::Path &);
|
||||
template void ShapeEditService::deliver_shape_to_hooks<db::Box> (const db::Box &);
|
||||
template void ShapeEditService::deliver_shape_to_hooks<db::Point> (const db::Point &);
|
||||
template void ShapeEditService::deliver_shape_to_hooks<db::Text> (const db::Text &);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PolygonService implementation
|
||||
|
||||
|
|
@ -433,6 +474,8 @@ PolygonService::do_begin_edit (const db::DPoint &p)
|
|||
m_points.push_back (pp);
|
||||
m_closure_set = false;
|
||||
|
||||
open_editor_hooks ();
|
||||
|
||||
update_marker ();
|
||||
}
|
||||
|
||||
|
|
@ -503,33 +546,37 @@ PolygonService::do_mouse_click (const db::DPoint &p)
|
|||
void
|
||||
PolygonService::do_finish_edit ()
|
||||
{
|
||||
deliver_shape (get_polygon ());
|
||||
deliver_shape (get_polygon (false));
|
||||
commit_recent (view ());
|
||||
close_editor_hooks (true);
|
||||
}
|
||||
|
||||
db::Polygon
|
||||
PolygonService::get_polygon () const
|
||||
PolygonService::get_polygon (bool editing) const
|
||||
{
|
||||
db::Polygon poly;
|
||||
|
||||
if (m_points.size () < 4) {
|
||||
if (! editing && m_points.size () + (m_closure_set ? 1 : 0) < 4) {
|
||||
throw tl::Exception (tl::to_string (tr ("A polygon must have at least 3 points")));
|
||||
}
|
||||
|
||||
std::vector<db::Point> points_dbu;
|
||||
points_dbu.reserve (m_points.size ());
|
||||
points_dbu.reserve (m_points.size () + 1);
|
||||
|
||||
// one point is reserved for the current one
|
||||
for (std::vector<db::DPoint>::const_iterator p = m_points.begin (); p + 1 != m_points.end (); ++p) {
|
||||
points_dbu.push_back (trans () * *p);
|
||||
}
|
||||
if (editing) {
|
||||
points_dbu.push_back (trans () * m_points.back ());
|
||||
}
|
||||
if (m_closure_set) {
|
||||
points_dbu.push_back (trans () * m_closure);
|
||||
}
|
||||
|
||||
poly.assign_hull (points_dbu.begin (), points_dbu.end (), true, true /*remove reflected*/);
|
||||
poly.assign_hull (points_dbu.begin (), points_dbu.end (), !editing /*compress*/, !editing /*remove reflected*/);
|
||||
|
||||
if (poly.hull ().size () < 3) {
|
||||
if (! editing && poly.hull ().size () < 3) {
|
||||
throw tl::Exception (tl::to_string (tr ("A polygon must have at least 3 effective points")));
|
||||
}
|
||||
|
||||
|
|
@ -539,7 +586,7 @@ PolygonService::get_polygon () const
|
|||
void
|
||||
PolygonService::do_cancel_edit ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
close_editor_hooks (false);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -732,6 +779,17 @@ PolygonService::update_marker ()
|
|||
std::string (" l: ") +
|
||||
tl::micron_to_string (m_points.back ().distance (m_points.end () [-2])));
|
||||
}
|
||||
|
||||
// call hooks with new shape
|
||||
if (! editor_hooks ().empty ()) {
|
||||
call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_shapes);
|
||||
try {
|
||||
deliver_shape_to_hooks (get_polygon (true));
|
||||
} catch (...) {
|
||||
// ignore exceptions
|
||||
}
|
||||
call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_shapes);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -761,6 +819,8 @@ BoxService::do_begin_edit (const db::DPoint &p)
|
|||
db::DPoint pp = snap2 (p);
|
||||
m_p1 = m_p2 = pp;
|
||||
|
||||
open_editor_hooks ();
|
||||
|
||||
set_edit_marker (new lay::Marker (view (), cv_index ()));
|
||||
update_marker ();
|
||||
}
|
||||
|
|
@ -785,6 +845,17 @@ BoxService::update_marker ()
|
|||
tl::micron_to_string (m_p2.y () - m_p1.y ()));
|
||||
|
||||
}
|
||||
|
||||
// call hooks with new shape
|
||||
if (! editor_hooks ().empty ()) {
|
||||
call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_shapes);
|
||||
try {
|
||||
deliver_shape_to_hooks (get_box ());
|
||||
} catch (...) {
|
||||
// ignore exceptions
|
||||
}
|
||||
call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_shapes);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -816,12 +887,13 @@ BoxService::do_finish_edit ()
|
|||
{
|
||||
deliver_shape (get_box ());
|
||||
commit_recent (view ());
|
||||
close_editor_hooks (true);
|
||||
}
|
||||
|
||||
void
|
||||
BoxService::do_cancel_edit ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
close_editor_hooks (false);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -857,6 +929,8 @@ PointService::do_begin_edit (const db::DPoint &p)
|
|||
db::DPoint pp = snap2 (p);
|
||||
m_p = pp;
|
||||
|
||||
open_editor_hooks ();
|
||||
|
||||
set_edit_marker (new lay::Marker (view (), cv_index ()));
|
||||
update_marker ();
|
||||
}
|
||||
|
|
@ -882,6 +956,17 @@ PointService::update_marker ()
|
|||
tl::micron_to_string (m_p.y ()));
|
||||
|
||||
}
|
||||
|
||||
// call hooks with new shape
|
||||
if (! editor_hooks ().empty ()) {
|
||||
call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_shapes);
|
||||
try {
|
||||
deliver_shape_to_hooks (get_point ());
|
||||
} catch (...) {
|
||||
// ignore exceptions
|
||||
}
|
||||
call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_shapes);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -913,12 +998,13 @@ PointService::do_finish_edit ()
|
|||
{
|
||||
deliver_shape (get_point ());
|
||||
commit_recent (view ());
|
||||
close_editor_hooks (true);
|
||||
}
|
||||
|
||||
void
|
||||
PointService::do_cancel_edit ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
close_editor_hooks (false);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -959,6 +1045,8 @@ TextService::do_begin_edit (const db::DPoint &p)
|
|||
|
||||
m_text.trans (db::DTrans (m_rot, snap2 (p) - db::DPoint ()));
|
||||
|
||||
open_editor_hooks ();
|
||||
|
||||
lay::DMarker *marker = new lay::DMarker (view ());
|
||||
marker->set_vertex_shape (lay::ViewOp::Cross);
|
||||
marker->set_vertex_size (9 /*cross vertex size*/);
|
||||
|
|
@ -985,6 +1073,17 @@ TextService::update_marker ()
|
|||
view ()->message (pos);
|
||||
|
||||
}
|
||||
|
||||
// call hooks with new shape
|
||||
if (! editor_hooks ().empty ()) {
|
||||
call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_shapes);
|
||||
try {
|
||||
deliver_shape_to_hooks (get_text ());
|
||||
} catch (...) {
|
||||
// ignore exceptions
|
||||
}
|
||||
call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_shapes);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -1065,12 +1164,14 @@ TextService::do_finish_edit ()
|
|||
|
||||
}
|
||||
#endif
|
||||
|
||||
close_editor_hooks (true);
|
||||
}
|
||||
|
||||
void
|
||||
TextService::do_cancel_edit ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
close_editor_hooks (false);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -1166,6 +1267,8 @@ PathService::do_begin_edit (const db::DPoint &p)
|
|||
m_points.push_back (pp);
|
||||
m_points.push_back (pp);
|
||||
|
||||
open_editor_hooks ();
|
||||
|
||||
set_edit_marker (new lay::Marker (view (), cv_index ()));
|
||||
update_marker ();
|
||||
}
|
||||
|
|
@ -1250,6 +1353,8 @@ PathService::do_finish_edit ()
|
|||
deliver_shape (get_path ());
|
||||
|
||||
commit_recent (view ());
|
||||
|
||||
close_editor_hooks (true);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1271,6 +1376,17 @@ PathService::update_marker ()
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
// call hooks with new shape
|
||||
if (! editor_hooks ().empty ()) {
|
||||
call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_shapes);
|
||||
try {
|
||||
deliver_shape_to_hooks (get_path ());
|
||||
} catch (...) {
|
||||
// ignore exceptions
|
||||
}
|
||||
call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_shapes);
|
||||
}
|
||||
}
|
||||
|
||||
db::Path
|
||||
|
|
@ -1305,7 +1421,7 @@ PathService::get_path () const
|
|||
void
|
||||
PathService::do_cancel_edit ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
close_editor_hooks (false);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -1582,6 +1698,8 @@ InstService::do_begin_edit (const db::DPoint &p)
|
|||
m_trans = db::VCplxTrans (1.0 / cv->layout ().dbu ()) * tv [0] * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans ();
|
||||
}
|
||||
|
||||
open_editor_hooks ();
|
||||
|
||||
update_marker ();
|
||||
}
|
||||
|
||||
|
|
@ -1756,7 +1874,7 @@ InstService::do_finish_edit ()
|
|||
|
||||
cv->layout ().cell (inst.object ().cell_index ()).collect_called_cells (called);
|
||||
called.insert (inst.object ().cell_index ());
|
||||
cv->layout ().cell (cv.cell_index ()).collect_caller_cells (callers);
|
||||
cv.cell ()->collect_caller_cells (callers);
|
||||
callers.insert (cv.cell_index ());
|
||||
|
||||
std::vector <db::cell_index_type> intersection;
|
||||
|
|
@ -1769,7 +1887,7 @@ InstService::do_finish_edit ()
|
|||
manager ()->transaction (tl::to_string (tr ("Create instance")), m_reference_transaction_id);
|
||||
}
|
||||
m_reference_transaction_id = 0;
|
||||
db::Instance i = cv->layout ().cell (cv.cell_index ()).insert (inst);
|
||||
db::Instance i = cv.cell ()->insert (inst);
|
||||
cv->layout ().cleanup ();
|
||||
if (manager ()) {
|
||||
manager ()->commit ();
|
||||
|
|
@ -1792,10 +1910,12 @@ InstService::do_finish_edit ()
|
|||
|
||||
m_has_valid_cell = false;
|
||||
m_in_drag_drop = false;
|
||||
close_editor_hooks (true);
|
||||
|
||||
} catch (...) {
|
||||
m_has_valid_cell = false;
|
||||
m_in_drag_drop = false;
|
||||
close_editor_hooks (false);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
@ -1819,6 +1939,8 @@ InstService::do_cancel_edit ()
|
|||
if (cv.is_valid ()) {
|
||||
cv->layout ().cleanup ();
|
||||
}
|
||||
|
||||
close_editor_hooks (false);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -2128,6 +2250,35 @@ InstService::update_marker ()
|
|||
} else {
|
||||
set_edit_marker (0);
|
||||
}
|
||||
|
||||
// call hooks with new shape
|
||||
if (! editor_hooks ().empty ()) {
|
||||
|
||||
call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_instances);
|
||||
|
||||
try {
|
||||
|
||||
const lay::CellView &cv = view ()->cellview (m_cv_index);
|
||||
|
||||
db::CellInstArray inst;
|
||||
if (cv.is_valid () && get_inst (inst)) {
|
||||
|
||||
// Note: the instance collection is temporary
|
||||
db::Instances instances (cv.cell ());
|
||||
db::Instance i = instances.insert (inst);
|
||||
|
||||
db::CplxTrans view_trans = db::CplxTrans (cv->layout ().dbu ()) * m_trans;
|
||||
call_editor_hooks<const db::Instance &, const db::CplxTrans &> (m_editor_hooks, &edt::EditorHooks::create_instance, i, view_trans);
|
||||
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
// ignore exceptions
|
||||
}
|
||||
|
||||
call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_instances);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -2159,6 +2310,36 @@ InstService::get_inst (db::CellInstArray &inst)
|
|||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
InstService::open_editor_hooks ()
|
||||
{
|
||||
const lay::CellView &cv = view ()->cellview (m_cv_index);
|
||||
if (! cv.is_valid ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string technology;
|
||||
if (cv->layout ().technology ()) {
|
||||
technology = cv->layout ().technology ()->name ();
|
||||
}
|
||||
|
||||
m_editor_hooks = edt::EditorHooks::get_editor_hooks (technology);
|
||||
|
||||
lay::CellViewRef cv_ref (view ()->cellview_ref (m_cv_index));
|
||||
call_editor_hooks<lay::CellViewRef &> (m_editor_hooks, &edt::EditorHooks::begin_create_instances, cv_ref);
|
||||
}
|
||||
|
||||
void
|
||||
InstService::close_editor_hooks (bool with_commit)
|
||||
{
|
||||
if (with_commit) {
|
||||
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::commit_instances);
|
||||
}
|
||||
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_create_instances);
|
||||
|
||||
m_editor_hooks.clear ();
|
||||
}
|
||||
|
||||
} // namespace edt
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "edtService.h"
|
||||
#include "edtConfig.h"
|
||||
#include "edtEditorHooks.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
|
@ -68,6 +69,16 @@ protected:
|
|||
void deliver_shape (const db::Path &path);
|
||||
void deliver_shape (const db::Box &box);
|
||||
void deliver_shape (const db::Point &point);
|
||||
void open_editor_hooks ();
|
||||
template <class Shape>
|
||||
void deliver_shape_to_hooks (const Shape &shape);
|
||||
void close_editor_hooks (bool with_commit);
|
||||
|
||||
const tl::weak_collection<edt::EditorHooks> &editor_hooks ()
|
||||
{
|
||||
return m_editor_hooks;
|
||||
}
|
||||
|
||||
virtual void current_layer_changed () { }
|
||||
|
||||
private:
|
||||
|
|
@ -77,6 +88,7 @@ private:
|
|||
db::Cell *mp_cell;
|
||||
db::Layout *mp_layout;
|
||||
combine_mode_type m_combine_mode;
|
||||
tl::weak_collection<edt::EditorHooks> m_editor_hooks;
|
||||
|
||||
void update_edit_layer (const lay::LayerPropertiesConstIterator &iter);
|
||||
};
|
||||
|
|
@ -109,7 +121,7 @@ private:
|
|||
db::DPoint m_last;
|
||||
|
||||
void update_marker ();
|
||||
db::Polygon get_polygon () const;
|
||||
db::Polygon get_polygon (bool editing) const;
|
||||
void add_closure ();
|
||||
void set_last_point (const db::DPoint &p);
|
||||
};
|
||||
|
|
@ -298,6 +310,7 @@ private:
|
|||
const db::PCellDeclaration *mp_pcell_decl;
|
||||
int m_cv_index;
|
||||
db::ICplxTrans m_trans;
|
||||
tl::weak_collection<edt::EditorHooks> m_editor_hooks;
|
||||
|
||||
void update_marker ();
|
||||
bool get_inst (db::CellInstArray &inst);
|
||||
|
|
@ -305,6 +318,13 @@ private:
|
|||
tl::Variant get_default_layer_for_pcell ();
|
||||
void sync_to_config ();
|
||||
void switch_cell_or_pcell (bool switch_parameters);
|
||||
void open_editor_hooks ();
|
||||
void close_editor_hooks (bool with_commit);
|
||||
|
||||
const tl::weak_collection<edt::EditorHooks> &editor_hooks ()
|
||||
{
|
||||
return m_editor_hooks;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,536 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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 "edtEditorHooks.h"
|
||||
#include "layObjectInstPath.h"
|
||||
#include "layLayoutView.h"
|
||||
|
||||
namespace gsi
|
||||
{
|
||||
|
||||
class EditorHooksImpl
|
||||
: public edt::EditorHooks
|
||||
{
|
||||
public:
|
||||
EditorHooksImpl ()
|
||||
: edt::EditorHooks ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual void begin_create_shapes (lay::CellViewRef &cv, const lay::LayerProperties &layer)
|
||||
{
|
||||
if (f_begin_create_shapes.can_issue ()) {
|
||||
f_begin_create_shapes.issue<edt::EditorHooks, lay::CellViewRef &, const lay::LayerProperties &> (&edt::EditorHooks::begin_create_shapes, cv, layer);
|
||||
} else {
|
||||
edt::EditorHooks::begin_create_shapes (cv, layer);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void begin_new_shapes ()
|
||||
{
|
||||
if (f_begin_new_shapes.can_issue ()) {
|
||||
f_begin_new_shapes.issue<edt::EditorHooks> (&edt::EditorHooks::begin_new_shapes);
|
||||
} else {
|
||||
edt::EditorHooks::begin_new_shapes ();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void create_shape (const db::Shape &shape, const db::CplxTrans &view_trans)
|
||||
{
|
||||
if (f_create_shape.can_issue ()) {
|
||||
f_create_shape.issue<edt::EditorHooks, const db::Shape &, const db::CplxTrans &> (&edt::EditorHooks::create_shape, shape, view_trans);
|
||||
} else {
|
||||
edt::EditorHooks::create_shape (shape, view_trans);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void end_new_shapes ()
|
||||
{
|
||||
if (f_end_new_shapes.can_issue ()) {
|
||||
f_end_new_shapes.issue<edt::EditorHooks> (&edt::EditorHooks::end_new_shapes);
|
||||
} else {
|
||||
edt::EditorHooks::end_new_shapes ();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void commit_shapes ()
|
||||
{
|
||||
if (f_commit_shapes.can_issue ()) {
|
||||
f_commit_shapes.issue<edt::EditorHooks> (&edt::EditorHooks::commit_shapes);
|
||||
} else {
|
||||
edt::EditorHooks::commit_shapes ();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void end_create_shapes ()
|
||||
{
|
||||
if (f_end_create_shapes.can_issue ()) {
|
||||
f_end_create_shapes.issue<edt::EditorHooks> (&edt::EditorHooks::end_create_shapes);
|
||||
} else {
|
||||
edt::EditorHooks::end_create_shapes ();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void begin_create_instances (lay::CellViewRef &cv)
|
||||
{
|
||||
if (f_begin_create_instances.can_issue ()) {
|
||||
f_begin_create_instances.issue<edt::EditorHooks, lay::CellViewRef &> (&edt::EditorHooks::begin_create_instances, cv);
|
||||
} else {
|
||||
edt::EditorHooks::begin_create_instances (cv);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void begin_new_instances ()
|
||||
{
|
||||
if (f_begin_new_instances.can_issue ()) {
|
||||
f_begin_new_instances.issue<edt::EditorHooks> (&edt::EditorHooks::begin_new_instances);
|
||||
} else {
|
||||
edt::EditorHooks::begin_new_instances ();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void create_instance (const db::Instance &object, const db::CplxTrans &view_trans)
|
||||
{
|
||||
if (f_create_instance.can_issue ()) {
|
||||
f_create_instance.issue<edt::EditorHooks, const db::Instance &, const db::CplxTrans &> (&edt::EditorHooks::create_instance, object, view_trans);
|
||||
} else {
|
||||
edt::EditorHooks::create_instance (object, view_trans);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void end_new_instances ()
|
||||
{
|
||||
if (f_end_new_instances.can_issue ()) {
|
||||
f_end_new_instances.issue<edt::EditorHooks> (&edt::EditorHooks::end_new_instances);
|
||||
} else {
|
||||
edt::EditorHooks::end_new_instances ();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void commit_instances ()
|
||||
{
|
||||
if (f_commit_instances.can_issue ()) {
|
||||
f_commit_instances.issue<edt::EditorHooks> (&edt::EditorHooks::commit_instances);
|
||||
} else {
|
||||
edt::EditorHooks::commit_instances ();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void end_create_instances ()
|
||||
{
|
||||
if (f_end_create_instances.can_issue ()) {
|
||||
f_end_create_instances.issue<edt::EditorHooks> (&edt::EditorHooks::end_create_instances);
|
||||
} else {
|
||||
edt::EditorHooks::end_create_instances ();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void begin_edit (lay::CellViewRef &cv_ref)
|
||||
{
|
||||
if (f_begin_edit.can_issue ()) {
|
||||
f_begin_edit.issue<edt::EditorHooks, lay::CellViewRef &> (&edt::EditorHooks::begin_edit, cv_ref);
|
||||
} else {
|
||||
edt::EditorHooks::begin_edit (cv_ref);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void begin_edits ()
|
||||
{
|
||||
if (f_begin_edits.can_issue ()) {
|
||||
f_begin_edits.issue<edt::EditorHooks> (&edt::EditorHooks::begin_edits);
|
||||
} else {
|
||||
edt::EditorHooks::begin_edits ();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void transformed (const lay::ObjectInstPath &object, const db::ICplxTrans &applied_trans, const db::CplxTrans &view_trans)
|
||||
{
|
||||
if (f_transformed.can_issue ()) {
|
||||
f_transformed.issue<edt::EditorHooks, const lay::ObjectInstPath &, const db::ICplxTrans &, const db::CplxTrans &> (&edt::EditorHooks::transformed, object, applied_trans, view_trans);
|
||||
} else {
|
||||
edt::EditorHooks::transformed (object, applied_trans, view_trans);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void modified (const lay::ObjectInstPath &object, const db::Shape &shape, const db::CplxTrans &view_trans)
|
||||
{
|
||||
if (f_modified.can_issue ()) {
|
||||
f_modified.issue<edt::EditorHooks, const lay::ObjectInstPath &, const db::Shape &, const db::CplxTrans &> (&edt::EditorHooks::modified, object, shape, view_trans);
|
||||
} else {
|
||||
edt::EditorHooks::modified (object, shape, view_trans);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void end_edits ()
|
||||
{
|
||||
if (f_end_edits.can_issue ()) {
|
||||
f_end_edits.issue<edt::EditorHooks> (&edt::EditorHooks::end_edits);
|
||||
} else {
|
||||
edt::EditorHooks::end_edits ();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void commit_edit ()
|
||||
{
|
||||
if (f_commit_edit.can_issue ()) {
|
||||
f_commit_edit.issue<edt::EditorHooks> (&edt::EditorHooks::commit_edit);
|
||||
} else {
|
||||
edt::EditorHooks::commit_edit ();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void end_edit ()
|
||||
{
|
||||
if (f_end_edit.can_issue ()) {
|
||||
f_end_edit.issue<edt::EditorHooks> (&edt::EditorHooks::end_edit);
|
||||
} else {
|
||||
edt::EditorHooks::end_edit ();
|
||||
}
|
||||
}
|
||||
|
||||
gsi::Callback f_begin_create_shapes;
|
||||
gsi::Callback f_begin_new_shapes;
|
||||
gsi::Callback f_create_shape;
|
||||
gsi::Callback f_end_new_shapes;
|
||||
gsi::Callback f_commit_shapes;
|
||||
gsi::Callback f_end_create_shapes;
|
||||
|
||||
gsi::Callback f_begin_create_instances;
|
||||
gsi::Callback f_begin_new_instances;
|
||||
gsi::Callback f_create_instance;
|
||||
gsi::Callback f_end_new_instances;
|
||||
gsi::Callback f_commit_instances;
|
||||
gsi::Callback f_end_create_instances;
|
||||
|
||||
gsi::Callback f_begin_edit;
|
||||
gsi::Callback f_begin_edits;
|
||||
gsi::Callback f_transformed;
|
||||
gsi::Callback f_modified;
|
||||
gsi::Callback f_end_edits;
|
||||
gsi::Callback f_commit_edit;
|
||||
gsi::Callback f_end_edit;
|
||||
};
|
||||
|
||||
static void register_editor_hooks (EditorHooksImpl *hooks, const std::string &name)
|
||||
{
|
||||
edt::EditorHooks::register_editor_hooks (hooks, name);
|
||||
}
|
||||
|
||||
gsi::Class<EditorHooksImpl> decl_EditorHooks ("lay", "EditorHooks",
|
||||
gsi::callback ("begin_create_shapes", &EditorHooksImpl::begin_create_shapes, &EditorHooksImpl::f_begin_create_shapes, gsi::arg ("cellview"), gsi::arg ("layer"),
|
||||
"@brief Shape creation protocol - begin session\n"
|
||||
"This method is called to initiate a shape creation session. The session is ended with "
|
||||
"\\end_create_shapes. Between these calls, new objects are announced with \\begin_new_shapes, "
|
||||
"\\create_shape and \\end_new_shapes calls. These calls are repeated to indicate changes in the objects "
|
||||
"created.\n"
|
||||
"\n"
|
||||
"\\commit_shapes is called once before \\end_create_shapes to indicate that the last set of "
|
||||
"objects is committed to the database."
|
||||
) +
|
||||
gsi::callback ("begin_new_shapes", &EditorHooksImpl::begin_new_shapes, &EditorHooksImpl::f_begin_new_shapes,
|
||||
"@brief Shape creation protocol - begin new shapes\n"
|
||||
"See \\begin_create_shapes for a description of the protocol."
|
||||
) +
|
||||
gsi::callback ("create_shape", &EditorHooksImpl::create_shape, &EditorHooksImpl::f_create_shape, gsi::arg ("shape"), gsi::arg ("view_trans"),
|
||||
"@brief Shape creation protocol - indicate a new object\n"
|
||||
"See \\begin_create_shapes for a description of the protocol."
|
||||
) +
|
||||
gsi::callback ("end_new_shapes", &EditorHooksImpl::end_new_shapes, &EditorHooksImpl::f_end_new_shapes,
|
||||
"@brief Shape creation protocol - finish list of new shapes\n"
|
||||
"See \\begin_create_shapes for a description of the protocol."
|
||||
) +
|
||||
gsi::callback ("commit_shapes", &EditorHooksImpl::commit_shapes, &EditorHooksImpl::f_commit_shapes,
|
||||
"@brief Shape creation protocol - commit new objects\n"
|
||||
"See \\begin_create_shapes for a description of the protocol."
|
||||
) +
|
||||
gsi::callback ("end_create_shapes", &EditorHooksImpl::end_create_shapes, &EditorHooksImpl::f_end_create_shapes,
|
||||
"@brief Shape creation protocol - finish session\n"
|
||||
"See \\begin_create for a description of the protocol."
|
||||
) +
|
||||
gsi::callback ("begin_create_instances", &EditorHooksImpl::begin_create_instances, &EditorHooksImpl::f_begin_create_instances, gsi::arg ("cellview"),
|
||||
"@brief Instance creation protocol - begin session\n"
|
||||
"This method is called to initiate an instance creation session. The session is ended with "
|
||||
"\\end_create_instances. Between these calls, new objects are announced with \\begin_new_instances, "
|
||||
"\\create_instance and \\end_new_instances calls. These calls are repeated to indicate changes in the objects "
|
||||
"created.\n"
|
||||
"\n"
|
||||
"\\commit_instances is called once before \\end_create_instances to indicate that the last set of "
|
||||
"objects is committed to the database."
|
||||
) +
|
||||
gsi::callback ("begin_new_instances", &EditorHooksImpl::begin_new_instances, &EditorHooksImpl::f_begin_new_instances,
|
||||
"@brief Instance creation protocol - begin new instances\n"
|
||||
"See \\begin_create_instances for a description of the protocol."
|
||||
) +
|
||||
gsi::callback ("create_instance", &EditorHooksImpl::create_instance, &EditorHooksImpl::f_create_instance, gsi::arg ("instance"), gsi::arg ("view_trans"),
|
||||
"@brief Instance creation protocol - indicate a new object\n"
|
||||
"See \\begin_create_instances for a description of the protocol."
|
||||
) +
|
||||
gsi::callback ("end_new_instances", &EditorHooksImpl::end_new_instances, &EditorHooksImpl::f_end_new_instances,
|
||||
"@brief Instance creation protocol - finish list of new instances\n"
|
||||
"See \\begin_create_instances for a description of the protocol."
|
||||
) +
|
||||
gsi::callback ("commit_instances", &EditorHooksImpl::commit_instances, &EditorHooksImpl::f_commit_instances,
|
||||
"@brief Instance creation protocol - commit new objects\n"
|
||||
"See \\begin_create_instances for a description of the protocol."
|
||||
) +
|
||||
gsi::callback ("end_create_instances", &EditorHooksImpl::end_create_instances, &EditorHooksImpl::f_end_create_instances,
|
||||
"@brief Instance creation protocol - finish session\n"
|
||||
"See \\begin_create for a description of the protocol."
|
||||
) +
|
||||
gsi::callback ("begin_edit", &EditorHooksImpl::begin_edit, &EditorHooksImpl::f_begin_edit, gsi::arg ("cellview"),
|
||||
"@brief Editing protocol - begin session\n"
|
||||
"This method is called to initiate an object editing session. The session is ended with "
|
||||
"\\end_edit. Between these calls, edits are announced with \\begin_edits, "
|
||||
"\\transformed or \\modified and \\end_edits calls. These calls are repeated to indicate changes in the objects "
|
||||
"modified while moving the mouse for example.\n"
|
||||
"\n"
|
||||
"\\commit_edit is called once before \\end_edit to indicate that the last set of "
|
||||
"objects are committed to the database."
|
||||
) +
|
||||
gsi::callback ("begin_edits", &EditorHooksImpl::begin_edits, &EditorHooksImpl::f_begin_edits,
|
||||
"@brief Editing protocol - begin edits\n"
|
||||
"See \\begin_edit for a description of the protocol."
|
||||
) +
|
||||
gsi::callback ("transformed", &EditorHooksImpl::transformed, &EditorHooksImpl::f_transformed, gsi::arg ("object"), gsi::arg ("applied_trans"), gsi::arg ("view_trans"),
|
||||
"@brief Editing protocol - indicate an object transformation\n"
|
||||
"See \\begin_edit for a description of the protocol.\n"
|
||||
"\n"
|
||||
"@param object A path to the modified object\n"
|
||||
"@param applied_trans The DBU-space of the transformation applied to the object\n"
|
||||
"@param view_trans The combined transformation of DBU space to view space\n"
|
||||
"\n"
|
||||
"Note that 'object' is the original, unmodified objects to which 'applied_trans' will be applied upon commit."
|
||||
) +
|
||||
gsi::callback ("modified", &EditorHooksImpl::modified, &EditorHooksImpl::f_modified, gsi::arg ("object"), gsi::arg ("shape"), gsi::arg ("view_trans"),
|
||||
"@brief Modification protocol - indicate a modified object\n"
|
||||
"See \\begin_edit for a description of the protocol."
|
||||
"\n"
|
||||
"@param object A path to the modified object\n"
|
||||
"@param shape The new, modified shape\n"
|
||||
"@param view_trans The combined transformation of DBU space to view space\n"
|
||||
"\n"
|
||||
"Note that 'object' is the original, unmodified objects while 'shape' is the modified shape. This shape object is a synthetic reference "
|
||||
"and does not exist in the database yet.\n"
|
||||
) +
|
||||
gsi::callback ("end_edits", &EditorHooksImpl::end_edits, &EditorHooksImpl::f_end_edits,
|
||||
"@brief Editing protocol - finish list of edits\n"
|
||||
"See \\begin_edit for a description of the protocol."
|
||||
) +
|
||||
gsi::callback ("commit_edit", &EditorHooksImpl::commit_edit, &EditorHooksImpl::f_commit_edit,
|
||||
"@brief Editing protocol - commit new objects\n"
|
||||
"See \\begin_edit for a description of the protocol."
|
||||
) +
|
||||
gsi::callback ("end_edit", &EditorHooksImpl::end_edit, &EditorHooksImpl::f_end_edit,
|
||||
"@brief Editing protocol - finish session\n"
|
||||
"See \\begin_edit for a description of the protocol."
|
||||
) +
|
||||
gsi::method ("technology=", &EditorHooksImpl::set_technology, gsi::arg ("technology"),
|
||||
"@brief sets the name of the technology the hooks are associated with\n"
|
||||
"This will clear all technology associations and associate the hooks with that technology only.\n"
|
||||
) +
|
||||
gsi::method ("clear_technologies", &EditorHooksImpl::clear_technologies,
|
||||
"@brief Clears the list of technologies the hooks are associated with.\n"
|
||||
"See also \\add_technology.\n"
|
||||
) +
|
||||
gsi::method ("add_technology", &EditorHooksImpl::add_technology, gsi::arg ("tech"),
|
||||
"@brief Additionally associates the hooks with the given technology.\n"
|
||||
"See also \\clear_technologies.\n"
|
||||
) +
|
||||
gsi::method ("is_for_technology", &EditorHooksImpl::is_for_technology, gsi::arg ("tech"),
|
||||
"@brief Returns a value indicating whether the hooks are associated with the given technology.\n"
|
||||
) +
|
||||
gsi::method ("for_technologies", &EditorHooksImpl::for_technologies,
|
||||
"@brief Returns a value indicating whether the hooks are associated with any technology.\n"
|
||||
"The method is equivalent to checking whether the \\technologies list is empty.\n"
|
||||
) +
|
||||
gsi::method ("technologies", &EditorHooksImpl::get_technologies,
|
||||
"@brief Gets the list of technologies these hooks are associated with.\n"
|
||||
) +
|
||||
gsi::method ("name", &EditorHooksImpl::name,
|
||||
"@brief Gets the name of the hooks object.\n"
|
||||
"This is the name, the object was registered under in the system."
|
||||
) +
|
||||
gsi::method_ext ("register", ®ister_editor_hooks, gsi::arg ("name"),
|
||||
"@brief Registers the hooks in the system.\n"
|
||||
"The hooks will not be active before they are registered in the system. Registration will "
|
||||
"also transfer object ownership to the system.\n"
|
||||
"\n"
|
||||
"The name is arbitary, but should be unique. Upon registration, this hooks object will "
|
||||
"replace others with the same name already registered in the system. This will simplify "
|
||||
"debugging as you can re-run the same code, without accumulating hooks.\n"
|
||||
),
|
||||
"@brief An implementation base class for editor hooks\n"
|
||||
"\n"
|
||||
"Editor hooks allow implementing technology-specific callbacks into the editor "
|
||||
"for example to implement visual feedback about DRC rules.\n"
|
||||
"\n"
|
||||
"This class provides the basic interface. To implement callbacks, use the \\EditorHooks class. "
|
||||
"You should not need to instantiate this class.\n"
|
||||
"\n"
|
||||
"The following is an excample for editor hooks that add DRC space indicators for polygon-alike shapes,\n"
|
||||
"It implements the shape creation protocol to capture new shapes and the modification protocol to "
|
||||
"capture shape manipulations. It displays a halo following hard-coded DRC rules to indicate the "
|
||||
"forbidden zones around the shapes:\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"class MyEditorHooks < RBA::EditorHooks\n"
|
||||
"\n"
|
||||
" def initialize()\n"
|
||||
" \n"
|
||||
" register(\"editor_hooks_demo\")\n"
|
||||
"\n"
|
||||
" cleanup \n"
|
||||
"\n"
|
||||
" # some demo values \n"
|
||||
" @spaces = {\n"
|
||||
" RBA::LayerInfo::new(1, 0) => [ 0.2, RBA::Region::Euclidian ],\n"
|
||||
" RBA::LayerInfo::new(2, 0) => [ 0.5, RBA::Region::Projection ]\n"
|
||||
" }\n"
|
||||
" \n"
|
||||
" end\n"
|
||||
"\n"
|
||||
" # Utilities\n"
|
||||
" \n"
|
||||
" # pick the space value from layer or set to nil\n"
|
||||
" def set_space_from_layer(layer_index)\n"
|
||||
" lp = @layout.get_info(layer_index)\n"
|
||||
" if @spaces[lp]\n"
|
||||
" (s, m) = @spaces[lp]\n"
|
||||
" @space = s / @layout.dbu\n"
|
||||
" @metrics = m\n"
|
||||
" else\n"
|
||||
" @space = nil\n"
|
||||
" end\n"
|
||||
" end\n"
|
||||
" \n"
|
||||
" def add_marker_from_shape(shape, trans)\n"
|
||||
" \n"
|
||||
" if !@space\n"
|
||||
" return # no space value set\n"
|
||||
" end\n"
|
||||
" \n"
|
||||
" p = shape.polygon\n"
|
||||
" if !p\n"
|
||||
" return # not a polygon-like object\n"
|
||||
" end\n"
|
||||
" \n"
|
||||
" r = RBA::Region::new\n"
|
||||
" # maintain 2-point polygons for the first edge drawn\n"
|
||||
" r.merged_semantics = (p.num_points != 2)\n"
|
||||
" r.insert(p)\n"
|
||||
" \n"
|
||||
" # compute DRC hull and prepare markers\n"
|
||||
" r.drc_hull(@metrics, @space).each do |pp|\n"
|
||||
" m = RBA::Marker::new(@view)\n"
|
||||
" m.line_style = 2\n"
|
||||
" m.vertex_size = 0\n"
|
||||
" m.set_polygon(trans * pp)\n"
|
||||
" @markers.append(m)\n"
|
||||
" end\n"
|
||||
" \n"
|
||||
" end\n"
|
||||
" \n"
|
||||
" # setup session\n"
|
||||
" def start(cv)\n"
|
||||
" cleanup\n"
|
||||
" @view = cv.view\n"
|
||||
" @layout = cv.layout\n"
|
||||
" end\n"
|
||||
" \n"
|
||||
" # end session\n"
|
||||
" def cleanup\n"
|
||||
" @space = nil\n"
|
||||
" @view = nil\n"
|
||||
" @layout = nil\n"
|
||||
" clear_markers\n"
|
||||
" end\n"
|
||||
" \n"
|
||||
" def clear_markers\n"
|
||||
" @markers && @markers.each do |m|\n"
|
||||
" # this is how a marker gets removed in Ruby:\n"
|
||||
" m._destroy\n"
|
||||
" end\n"
|
||||
" @markers = []\n"
|
||||
" end\n"
|
||||
" \n"
|
||||
" # Shape creation protocol\n"
|
||||
" \n"
|
||||
" def begin_create_shapes(cv, layer)\n"
|
||||
" start(cv)\n"
|
||||
" set_space_from_layer(layer.layer_index)\n"
|
||||
" end\n"
|
||||
"\n"
|
||||
" def begin_new_shapes\n"
|
||||
" clear_markers\n"
|
||||
" end\n"
|
||||
"\n"
|
||||
" def create_shape(shape, trans)\n"
|
||||
" add_marker_from_shape(shape, trans)\n"
|
||||
" end\n"
|
||||
" \n"
|
||||
" def end_create_shapes\n"
|
||||
" cleanup\n"
|
||||
" end\n"
|
||||
" \n"
|
||||
" # Modification protocol\n"
|
||||
" \n"
|
||||
" def begin_edit(cv)\n"
|
||||
" start(cv)\n"
|
||||
" end\n"
|
||||
"\n"
|
||||
" def begin_edits\n"
|
||||
" # create new markers\n"
|
||||
" clear_markers\n"
|
||||
" end\n"
|
||||
"\n"
|
||||
" # transformation of a shape or instance\n"
|
||||
" def transformed(path, applied, trans)\n"
|
||||
" if path.shape\n"
|
||||
" set_space_from_layer(path.layer)\n"
|
||||
" add_marker_from_shape(path.shape, trans * applied)\n"
|
||||
" end\n"
|
||||
" end\n"
|
||||
"\n"
|
||||
" # modification of a shape\n"
|
||||
" def modified(path, shape, trans)\n"
|
||||
" set_space_from_layer(path.layer)\n"
|
||||
" add_marker_from_shape(shape, trans)\n"
|
||||
" end\n"
|
||||
"\n"
|
||||
" def end_edit\n"
|
||||
" cleanup\n"
|
||||
" end\n"
|
||||
" \n"
|
||||
"end\n"
|
||||
"\n"
|
||||
"# instantiation of the hooks object\n"
|
||||
"MyEditorHooks::new\n"
|
||||
"@/code\n"
|
||||
"\n"
|
||||
"The EditorHooks class has been introduced in version 0.29.1."
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -99,6 +99,7 @@ void run_pythontest (tl::TestBase *_this, const std::string &fn)
|
|||
PYTHONTEST (kwargs, "kwargs.py")
|
||||
PYTHONTEST (dbLayoutTest, "dbLayoutTest.py")
|
||||
PYTHONTEST (dbRegionTest, "dbRegionTest.py")
|
||||
PYTHONTEST (dbShapesTest, "dbShapesTest.py")
|
||||
PYTHONTEST (dbReaders, "dbReaders.py")
|
||||
PYTHONTEST (dbPCellsTest, "dbPCells.py")
|
||||
PYTHONTEST (dbPolygonTest, "dbPolygonTest.py")
|
||||
|
|
|
|||
|
|
@ -116,12 +116,10 @@ public:
|
|||
/**
|
||||
* @brief Post-decrement
|
||||
*/
|
||||
weak_or_shared_collection_iterator<T, Holder, Shared> operator-- (int n)
|
||||
weak_or_shared_collection_iterator<T, Holder, Shared> operator-- (int)
|
||||
{
|
||||
weak_or_shared_collection_iterator<T, Holder, Shared> ret = *this;
|
||||
while (n-- > 0) {
|
||||
operator-- ();
|
||||
}
|
||||
operator-- ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -138,12 +136,10 @@ public:
|
|||
/**
|
||||
* @brief Post-increment
|
||||
*/
|
||||
weak_or_shared_collection_iterator<T, Holder, Shared> operator++ (int n)
|
||||
weak_or_shared_collection_iterator<T, Holder, Shared> operator++ (int)
|
||||
{
|
||||
weak_or_shared_collection_iterator<T, Holder, Shared> ret = *this;
|
||||
while (n-- > 0) {
|
||||
operator++ ();
|
||||
}
|
||||
operator++ ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -176,38 +172,36 @@ public:
|
|||
: public weak_or_shared_ptr<T, Shared>
|
||||
{
|
||||
public:
|
||||
holder_type (weak_or_shared_collection<T, Shared> *collection)
|
||||
: weak_or_shared_ptr<T, Shared> (), next (0), prev (0), mp_collection (collection)
|
||||
holder_type (weak_or_shared_collection<T, Shared> *_collection)
|
||||
: weak_or_shared_ptr<T, Shared> (), next (0), prev (0), collection (_collection)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
holder_type (weak_or_shared_collection<T, Shared> *collection, T *t)
|
||||
: weak_or_shared_ptr<T, Shared> (t), next (0), prev (0), mp_collection (collection)
|
||||
holder_type (weak_or_shared_collection<T, Shared> *_collection, T *t)
|
||||
: weak_or_shared_ptr<T, Shared> (t), next (0), prev (0), collection (_collection)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
holder_type (weak_or_shared_collection<T, Shared> *collection, const weak_or_shared_ptr<T, Shared> &d)
|
||||
: weak_or_shared_ptr<T, Shared> (d), next (0), prev (0), mp_collection (collection)
|
||||
holder_type (weak_or_shared_collection<T, Shared> *_collection, const weak_or_shared_ptr<T, Shared> &d)
|
||||
: weak_or_shared_ptr<T, Shared> (d), next (0), prev (0), collection (_collection)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
holder_type *next, *prev;
|
||||
weak_or_shared_collection<T, Shared> *collection;
|
||||
|
||||
protected:
|
||||
virtual void reset_object ()
|
||||
{
|
||||
weak_or_shared_ptr<T, Shared>::reset_object ();
|
||||
if (mp_collection) {
|
||||
if (collection) {
|
||||
// Caution: this will probably delete "this"!
|
||||
mp_collection->remove_element (this);
|
||||
collection->remove_element (this);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
weak_or_shared_collection<T, Shared> *mp_collection;
|
||||
};
|
||||
|
||||
typedef weak_or_shared_collection_iterator<T, holder_type, Shared> iterator;
|
||||
|
|
@ -224,6 +218,24 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The copy constructor
|
||||
*/
|
||||
weak_or_shared_collection (const weak_or_shared_collection<T, Shared> &other)
|
||||
: mp_first (0), mp_last (0), m_size (0)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The move constructor
|
||||
*/
|
||||
weak_or_shared_collection (weak_or_shared_collection<T, Shared> &&other)
|
||||
: mp_first (0), mp_last (0), m_size (0)
|
||||
{
|
||||
swap (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
|
|
@ -234,6 +246,37 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
weak_or_shared_collection &operator= (const weak_or_shared_collection<T, Shared> &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
clear ();
|
||||
for (auto i = other.begin (); i != other.end (); ++i) {
|
||||
push_back (const_cast<T *> (i.operator-> ()));
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swap
|
||||
*/
|
||||
void swap (weak_or_shared_collection<T, Shared> &other)
|
||||
{
|
||||
std::swap (mp_first, other.mp_first);
|
||||
std::swap (mp_last, other.mp_last);
|
||||
std::swap (m_size, other.m_size);
|
||||
|
||||
for (holder_type *h = mp_first; h; h = h->next) {
|
||||
h->collection = this;
|
||||
}
|
||||
for (holder_type *h = other.mp_first; h; h = h->next) {
|
||||
h->collection = &other;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating whether the collection is empty
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -399,6 +399,9 @@ TEST(22)
|
|||
EXPECT_EQ (sc.empty (), false);
|
||||
EXPECT_EQ (sc.front ()->attr (), 1);
|
||||
EXPECT_EQ ((++sc.begin ())->attr (), 1);
|
||||
auto i = sc.begin ();
|
||||
i++;
|
||||
EXPECT_EQ (i->attr (), 1);
|
||||
|
||||
sc.pop_back ();
|
||||
EXPECT_EQ (int (sc.size ()), 1);
|
||||
|
|
@ -418,6 +421,9 @@ TEST(22)
|
|||
EXPECT_EQ (sc.empty (), false);
|
||||
EXPECT_EQ (sc.front ()->attr (), 2);
|
||||
EXPECT_EQ ((++sc.begin ())->attr (), 2);
|
||||
i = sc.begin ();
|
||||
i++;
|
||||
EXPECT_EQ (i->attr (), 2);
|
||||
}
|
||||
|
||||
EXPECT_EQ (MyClass::instances (), 0);
|
||||
|
|
@ -516,6 +522,48 @@ TEST(24)
|
|||
EXPECT_EQ (MyClass::instances (), 0);
|
||||
}
|
||||
|
||||
TEST(25)
|
||||
{
|
||||
MyClass::reset_instance_counter ();
|
||||
MyClass *o1 = new MyClass (1);
|
||||
MyClass *o2 = new MyClass (2);
|
||||
EXPECT_EQ (MyClass::instances (), 2);
|
||||
|
||||
tl::shared_collection<MyClass> sc1, sc2;
|
||||
sc1.push_back (o1);
|
||||
sc1.push_back (o2);
|
||||
EXPECT_EQ (sc1.size (), size_t (2));
|
||||
EXPECT_EQ (sc2.size (), size_t (0));
|
||||
EXPECT_EQ (sc1.front () == o1, true);
|
||||
|
||||
sc1.swap (sc2);
|
||||
EXPECT_EQ (sc1.size (), size_t (0));
|
||||
EXPECT_EQ (sc2.size (), size_t (2));
|
||||
EXPECT_EQ (sc2.front () == o1, true);
|
||||
EXPECT_EQ (MyClass::instances (), 2);
|
||||
|
||||
sc1 = sc2;
|
||||
EXPECT_EQ (sc1.size (), size_t (2));
|
||||
EXPECT_EQ (sc2.size (), size_t (2));
|
||||
EXPECT_EQ (sc1.front () == o1, true);
|
||||
EXPECT_EQ (sc2.front () == o1, true);
|
||||
EXPECT_EQ (MyClass::instances (), 2);
|
||||
|
||||
delete o1;
|
||||
EXPECT_EQ (sc1.size (), size_t (1));
|
||||
EXPECT_EQ (sc2.size (), size_t (1));
|
||||
EXPECT_EQ (sc1.front () == o2, true);
|
||||
EXPECT_EQ (sc2.front () == o2, true);
|
||||
EXPECT_EQ (MyClass::instances (), 1);
|
||||
|
||||
sc1.clear ();
|
||||
sc2.clear ();
|
||||
EXPECT_EQ (sc1.size (), size_t (0));
|
||||
EXPECT_EQ (sc2.size (), size_t (0));
|
||||
|
||||
EXPECT_EQ (MyClass::instances (), 0);
|
||||
}
|
||||
|
||||
TEST(30)
|
||||
{
|
||||
MyClass::reset_instance_counter ();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
# KLayout Layout Viewer
|
||||
# Copyright (C) 2006-2024 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
|
||||
|
||||
|
||||
import pya
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
class DBShapesTest(unittest.TestCase):
|
||||
|
||||
# Shape objects as hashes
|
||||
def test_12(self):
|
||||
|
||||
s = pya.Shapes()
|
||||
s1 = s.insert(pya.Box(1, 2, 3, 4))
|
||||
s2 = s.insert(pya.Polygon(pya.Box(1, 2, 3, 4)))
|
||||
s3 = s.insert(pya.SimplePolygon(pya.Box(1, 2, 3, 4)))
|
||||
|
||||
self.assertEqual(s1.hash != s2.hash, True) # let's hope so ...
|
||||
self.assertEqual(s1.hash != s3.hash, True)
|
||||
self.assertEqual(s2.hash != s3.hash, True)
|
||||
|
||||
self.assertEqual(s1 < s2 or s2 < s1, True)
|
||||
self.assertEqual(s1 < s3 or s3 < s1, True)
|
||||
self.assertEqual(s2 < s3 or s3 < s2, True)
|
||||
|
||||
h = {}
|
||||
h[s1] = 1
|
||||
h[s2] = 2
|
||||
h[s3] = 3
|
||||
|
||||
self.assertEqual(len(h), 3)
|
||||
|
||||
self.assertEqual(h[s1], 1)
|
||||
self.assertEqual(h[s2], 2)
|
||||
self.assertEqual(h[s3], 3)
|
||||
|
||||
# run unit tests
|
||||
if __name__ == '__main__':
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(DBShapesTest)
|
||||
|
||||
if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful():
|
||||
sys.exit(1)
|
||||
|
||||
|
|
@ -1452,6 +1452,25 @@ class DBRegion_TestClass < TestBase
|
|||
|
||||
end
|
||||
|
||||
# DRC hull
|
||||
def test_drc_hull
|
||||
|
||||
r = RBA::Region::new()
|
||||
r.insert(RBA::Polygon::new([[0, 0], [1000, 1000], [1500, 0]]))
|
||||
|
||||
assert_equal(r.drc_hull(RBA::Region::Euclidian, 200, 8).merged.to_s, "(-83,-200;-200,-83;-200,83;-141,141;859,1141;950,1211;1114,1184;1179,1089;1679,89;1714,-35;1627,-176;1500,-200)")
|
||||
assert_equal(r.drc_hull(RBA::Region::Square, 200).merged.to_s, "(-200,-200;-200,-82;-283,0;1000,1283;1039,1243;1089,1268;1768,-89;1700,-123;1700,-200)")
|
||||
assert_equal(r.drc_hull(RBA::Region::Projection, 200).merged.to_s, "(0,-200;0,0;-141,141;859,1141;1000,1000;1179,1089;1679,89;1500,0;1500,-200)")
|
||||
|
||||
r = RBA::Region::new()
|
||||
r.merged_semantics = false
|
||||
r.insert(RBA::Polygon::new([[0, 0], [1000, 1000]], true))
|
||||
assert_equal(r.drc_hull(RBA::Region::Euclidian, 200, 8).merged.to_s, "(-83,-200;-200,-83;-200,83;-141,141;859,1141;917,1200;1083,1200;1200,1083;1200,917;1141,859;141,-141;83,-200)")
|
||||
assert_equal(r.drc_hull(RBA::Region::Square, 200, 8).merged.to_s, "(0,-283;-141,-141;-283,0;1000,1283;1141,1141;1283,1000)")
|
||||
assert_equal(r.drc_hull(RBA::Region::Projection, 200, 8).merged.to_s, "(141,-141;-141,141;859,1141;1141,859)")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
load("test_epilogue.rb")
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,35 @@ class DBShapes_TestClass < TestBase
|
|||
|
||||
end
|
||||
|
||||
# Shape objects as hashes
|
||||
def test_12
|
||||
|
||||
s = RBA::Shapes::new
|
||||
s1 = s.insert(RBA::Box::new(1, 2, 3, 4))
|
||||
s2 = s.insert(RBA::Polygon::new(RBA::Box::new(1, 2, 3, 4)))
|
||||
s3 = s.insert(RBA::SimplePolygon::new(RBA::Box::new(1, 2, 3, 4)))
|
||||
|
||||
assert_equal(s1.hash != s2.hash, true) # let's hope so ...
|
||||
assert_equal(s1.hash != s3.hash, true)
|
||||
assert_equal(s2.hash != s3.hash, true)
|
||||
|
||||
assert_equal(s1 < s2 || s2 < s1, true)
|
||||
assert_equal(s1 < s3 || s3 < s1, true)
|
||||
assert_equal(s2 < s3 || s3 < s2, true)
|
||||
|
||||
h = {}
|
||||
h[s1] = 1
|
||||
h[s2] = 2
|
||||
h[s3] = 3
|
||||
|
||||
assert_equal(h.size, 3)
|
||||
|
||||
assert_equal(h[s1], 1)
|
||||
assert_equal(h[s2], 2)
|
||||
assert_equal(h[s3], 3)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
load("test_epilogue.rb")
|
||||
|
|
|
|||
Loading…
Reference in New Issue