Shape#hash and Shape#< allow using Shape objects as hash keys

This commit is contained in:
Matthias Koefferlein 2024-03-29 21:47:37 +01:00
parent 69423e37ac
commit c43b70b783
7 changed files with 144 additions and 0 deletions

View File

@ -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

View File

@ -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
{

View File

@ -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

View File

@ -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."
) +
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."
) +
gsi::method ("to_s", &db::Shape::to_string,
"@brief Create a string showing the contents of the reference\n"
"\n"

View File

@ -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")

59
testdata/python/dbShapesTest.py vendored Normal file
View File

@ -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)

View File

@ -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")