Merge branch 'master' into devel

This commit is contained in:
Matthias Koefferlein 2025-09-06 13:26:11 +02:00
commit 8901359957
16 changed files with 254 additions and 73 deletions

View File

@ -49,7 +49,7 @@ jobs:
- uses: hmarr/debug-action@v3
- name: Cancel Workflow Action
uses: styfle/cancel-workflow-action@0.12.1
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: ccache
if: matrix.os != 'ubuntu-24.04-arm'
uses: hendrikmuhs/ccache-action@v1.2
@ -66,7 +66,7 @@ jobs:
mkdir -p $HOST_CCACHE_DIR
- name: Build wheels (ARM)
if: matrix.os == 'ubuntu-24.04-arm'
uses: pypa/cibuildwheel@v3.1.3
uses: pypa/cibuildwheel@v3.1.4
env:
# override the default CentOS “yum install … ccache” and drop ccache
CIBW_BEFORE_ALL_LINUX: |
@ -81,7 +81,7 @@ jobs:
- name: Build wheels (all other platforms)
if: matrix.os != 'ubuntu-24.04-arm'
uses: pypa/cibuildwheel@v3.1.3
uses: pypa/cibuildwheel@v3.1.4
env:
CIBW_BUILD: ${{ matrix.cibuild }}
CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }}
@ -108,7 +108,7 @@ jobs:
name: Make SDist
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build SDist
run: pipx run build --sdist
@ -122,12 +122,12 @@ jobs:
needs: [build, make_sdist]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v5
with:
merge-multiple: true
path: dist
- uses: pypa/gh-action-pypi-publish@v1.12.4
- uses: pypa/gh-action-pypi-publish@v1.13.0
continue-on-error: true # might fail if we don't bump the version
with:
user: __token__
@ -139,12 +139,12 @@ jobs:
runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.action == 'published'
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v5
with:
merge-multiple: true
path: dist
- uses: pypa/gh-action-pypi-publish@v1.12.4
- uses: pypa/gh-action-pypi-publish@v1.13.0
with:
user: __token__
password: ${{ secrets.pypi_password }}

View File

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Cleanup
run: |

View File

@ -93,15 +93,21 @@ static bool include_zero_flag (zero_distance_mode zd_mode, const db::Edge &a, co
*/
bool euclidian_near_part_of_edge (zero_distance_mode zd_mode, db::coord_traits<db::Coord>::distance_type d, const db::Edge &e, const db::Edge &other, db::Edge *output)
{
// Handle the case of point-like basic edge: cannot determine
// orientation
if (e.is_degenerate ()) {
return false;
}
db::Edge g (other);
int s1 = e.side_of (g.p1 ());
int s2 = e.side_of (g.p2 ());
// s1 = side of g.p1 wrt e
// s2 = side of g.p2 wrt e
int s1, s2;
if (e.is_degenerate ()) {
if (g.contains (e.p1 ())) {
s1 = s2 = 0;
} else {
s1 = s2 = -1;
}
} else {
s1 = e.side_of (g.p1 ());
s2 = e.side_of (g.p2 ());
}
bool include_zero = include_zero_flag (zd_mode, e, g);
int thr = include_zero ? 0 : -1;
@ -115,10 +121,24 @@ bool euclidian_near_part_of_edge (zero_distance_mode zd_mode, db::coord_traits<d
g = db::Edge (g.cut_point (e).second, g.p2 ());
}
// Handle the case of point vs. edge
// Handle the case of point vs. edge/point
if (g.is_degenerate ()) {
if (e.is_degenerate ()) {
// point vs. point
if (e.p1 ().distance (g.p1 ()) < double (d)) {
if (output) {
*output = g;
}
return true;
}
return false;
} else {
db::Point o = g.p1 ();
if (e.side_of (o) > thr) {
@ -145,15 +165,20 @@ bool euclidian_near_part_of_edge (zero_distance_mode zd_mode, db::coord_traits<d
}
}
// Determine body interactions (projected mode)
double l1 = std::numeric_limits<double>::max (), l2 = -std::numeric_limits<double>::max ();
// handle the parallel case
// NOTE: a point is "parallel" to an edge.
if (e.parallel (g)) {
if (std::abs (double (e.distance (g.p1 ()))) >= double (d)) {
return false;
}
} else {
double ef = 1.0 / e.double_length ();
@ -233,16 +258,21 @@ bool euclidian_near_part_of_edge (zero_distance_mode zd_mode, db::coord_traits<d
*/
static bool var_near_part_of_edge (zero_distance_mode zd_mode, db::coord_traits<db::Coord>::distance_type d, db::coord_traits<db::Coord>::distance_type dd, const db::Edge &e, const db::Edge &other, db::Edge *output)
{
// Handle the case of point-like basic edge: cannot determine
// orientation
if (e.is_degenerate ()) {
return false;
}
db::Edge g (other);
int s1 = e.side_of (g.p1 ());
int s2 = e.side_of (g.p2 ());
// s1 = side of g.p1 wrt e
// s2 = side of g.p2 wrt e
int s1, s2;
if (e.is_degenerate ()) {
if (g.contains (e.p1 ())) {
s1 = s2 = 0;
} else {
s1 = s2 = -1;
}
} else {
s1 = e.side_of (g.p1 ());
s2 = e.side_of (g.p2 ());
}
bool include_zero = include_zero_flag (zd_mode, e, g);
int thr = include_zero ? 0 : -1;
@ -259,37 +289,54 @@ static bool var_near_part_of_edge (zero_distance_mode zd_mode, db::coord_traits<
// Handle the case of point vs. edge
if (g.is_degenerate ()) {
if (! e.is_degenerate ()) {
if (e.side_of (g.p1 ()) > thr) {
return false;
}
if (double (e.distance (g.p1 ())) <= -double (d)) {
return false;
}
if (db::sprod (db::Vector (g.p1 () - e.p1 ()), e.d ()) < -(dd * e.double_length ())) {
return false;
}
if (db::sprod (db::Vector (e.p2 () - g.p1 ()), e.d ()) < -(dd * e.double_length ())) {
return false;
}
if (double (e.distance (g.p1 ())) <= -double (d)) {
return false;
}
} else {
// point to point
if (e.p1 ().distance (g.p1 ()) >= double (d)) {
return false;
}
}
if (output) {
*output = g;
}
return true;
}
// Determine body interactions (projected mode)
double l1 = std::numeric_limits<double>::min (), l2 = std::numeric_limits<double>::max ();
double l1 = std::numeric_limits<double>::lowest (), l2 = std::numeric_limits<double>::max ();
double ef = 1.0 / e.double_length ();
db::DVector ep = db::DVector (ef * e.dx (), ef * e.dy ());
db::DVector en = db::DVector (ef * e.dy (), -ef * e.dx ());
db::DVector ep, en;
double ef = 0.0;
if (! e.is_degenerate ()) {
ef = 1.0 / e.double_length ();
ep = db::DVector (ef * e.dx (), ef * e.dy ());
en = db::DVector (ef * e.dy (), -ef * e.dx ());
}
// handle the parallel case
// NOTE: a point is "parallel" to an edge
if (e.parallel (g)) {
if (std::abs (double (e.distance (g.p1 ()))) >= double (d)) {
return false;
}
} else {
db::DPoint e1d = db::DPoint (e.p1 ()) + en * double (d);
@ -306,11 +353,26 @@ static bool var_near_part_of_edge (zero_distance_mode zd_mode, db::coord_traits<
}
bool allow_zero_projection = false;
if (db::sprod_sign (e, g) == 0) {
if (! g.is_degenerate ()) {
if (db::sprod (db::Vector (g.p1 () - e.p1 ()), e.d ()) < -(dd * e.double_length ()) ||
db::sprod (db::Vector (e.p2 () - g.p1 ()), e.d ()) < -(dd * e.double_length ())) {
return false;
}
double l = db::sprod (db::DVector (e.p1 () - g.p1 ()), db::DVector (g.d ())) / g.double_sq_length ();
double dl = double (dd) / g.double_length ();
l1 = l - dl;
l2 = l + dl;
allow_zero_projection = true;
}
} else {
double det = db::vprod (db::DVector (g.d ()), en);
@ -330,7 +392,7 @@ static bool var_near_part_of_edge (zero_distance_mode zd_mode, db::coord_traits<
l1 = std::max (0.0, l1);
l2 = std::min (1.0, l2);
if (l1 >= l2) {
if (allow_zero_projection ? l1 > l2 + db::epsilon : l1 > l2 - db::epsilon) {
return false;
} else {
if (output) {
@ -410,7 +472,14 @@ EdgeRelationFilter::check (const db::Edge &a, const db::Edge &b, db::EdgePair *o
// Check whether the edges have an angle less than the ignore_angle parameter
if (m_ignore_angle == 90.0) {
if (a.is_degenerate () || b.is_degenerate ()) {
// accept dots as "always good", expect if they are identical and the zero distance mode does not include this case
if (a == b && (m_zero_distance_mode == NeverIncludeZeroDistance ||
m_zero_distance_mode == IncludeZeroDistanceWhenCollinearAndTouching ||
m_zero_distance_mode == IncludeZeroDistanceWhenOverlapping)) {
return false;
}
} else if (m_ignore_angle == 90.0) {
if (db::sprod_sign (aa, b) >= 0) {
return false;
}

View File

@ -463,10 +463,12 @@ void
poly2poly_check<PolygonType>::enter (const PolygonType &o, size_t p)
{
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
if (! (*e).is_degenerate ()) {
m_edge_heap.push_back (*e);
m_scanner.insert (& m_edge_heap.back (), p);
}
}
}
template <class PolygonType>
void
@ -497,7 +499,7 @@ poly2poly_check<PolygonType>::enter (const PolygonType &o, size_t p, const poly2
}
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
if (interact (box, *e)) {
if (! (*e).is_degenerate () && interact (box, *e)) {
m_edge_heap.push_back (*e);
m_scanner.insert (& m_edge_heap.back (), p);
}

View File

@ -135,7 +135,8 @@ TEST(3)
EXPECT_EQ (output.to_string (), "(80,0;-60,-100)");
EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), true);
EXPECT_EQ (output.to_string (), "(-100,0;-100,-100)");
EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), false);
EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), true);
EXPECT_EQ (output.to_string (), "(-100,100;-100,-100)"); // dot vs. line (issue #2141)
EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 50), db::Point (100, 50)), &output), false);
EXPECT_EQ (square_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (100, -50)), &output), true);
EXPECT_EQ (output.to_string (), "(100,-50;100,-50)");
@ -170,14 +171,15 @@ TEST(4)
EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 200), db::Point (200, -200)), &output), false);
EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (120, 200), db::Point (120, -200)), &output), false);
EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 200), db::Point (100, -200)), &output), true);
EXPECT_EQ (output.to_string (), "(100,0;100,-100)");
EXPECT_EQ (output.to_string (), "(100,0;100,0)");
EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 200), db::Point (80, -200)), &output), true);
EXPECT_EQ (output.to_string (), "(80,0;80,-100)");
EXPECT_EQ (output.to_string (), "(80,0;80,0)");
EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-80, 200), db::Point (-80, -200)), &output), false);
EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (80, 0), db::Point (-200, -200)), &output), true);
EXPECT_EQ (output.to_string (), "(80,0;0,-57)");
EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), false);
EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), false);
EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (), db::Edge (db::Point (-100, 200), db::Point (-100, -200)), &output), true);
EXPECT_EQ (output.to_string (), "(-100,0;-100,0)"); // dot vs. line (issue #2141)
EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 50), db::Point (100, 50)), &output), false);
EXPECT_EQ (projected_near_part_of_edge (db::AlwaysIncludeZeroDistance, 100, db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, -50), db::Point (100, -50)), &output), true);
EXPECT_EQ (output.to_string (), "(100,-50;100,-50)");

View File

@ -5079,6 +5079,7 @@ CODE
#
# This method works both on edge or polygon layers. Edge merging forms
# single, continuous edges from coincident and connected individual edges.
# The overlap count is only available on polygon layers.
#
# A version that modifies the input layer is \merge.
#
@ -5106,7 +5107,13 @@ CODE
def merged(overlap_count = 1)
@engine._context("merged") do
requires_edges_or_region
aa = [ @engine._prep_value(overlap_count) ]
oc = @engine._prep_value(overlap_count)
if self.data.is_a?(RBA::Edges)
oc == 1 || raise("'overlap_count' (merged) is only available on polygon layers")
aa = []
else
aa = [ oc ]
end
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :merged, *aa))
end
end
@ -5114,7 +5121,13 @@ CODE
def merge(overlap_count = 1)
@engine._context("merge") do
requires_edges_or_region
aa = [ @engine._prep_value(overlap_count) ]
oc = @engine._prep_value(overlap_count)
if self.data.is_a?(RBA::Edges)
oc == 1 || raise("'overlap_count' (merged) is only available on polygon layers")
aa = []
else
aa = [ oc ]
end
if @engine.is_tiled?
# in tiled mode, no modifying versions are available
self.data = @engine._tcmd(self.data, 0, self.data.class, :merged, *aa)

View File

@ -2044,3 +2044,25 @@ TEST(144d_combined_antennas)
run_test (_this, "144", true);
}
// issue 2134
TEST(145_edges_merge)
{
run_test (_this, "145", false);
}
TEST(145d_edges_merge)
{
run_test (_this, "145", true);
}
// issue 2141
TEST(146_edges_and_corners)
{
run_test (_this, "146", false);
}
TEST(146d_edges_and_corners)
{
run_test (_this, "146", true);
}

View File

@ -1401,9 +1401,9 @@ MacroEditorPage::replace_in_selection (const QString &replace, bool first)
// restore selection which might have changed due to insert
c.setPosition (anchor_at_end ? be.position () + pe : bs.position () + ps);
c.setPosition (!anchor_at_end ? be.position () + pe : bs.position () + ps, QTextCursor::KeepAnchor);
mp_text->setTextCursor (c);
}
mp_text->setTextCursor (c);
c.endEditBlock ();
}

22
testdata/drc/drcSimpleTests_145.drc vendored Normal file
View File

@ -0,0 +1,22 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
l1 = input(1, 0)
l2 = input(2, 0)
l3 = input(3, 0)
l1.output(1, 0)
l2.output(2, 0)
l3.output(3, 0)
l1.raw.edges.merged.output(100, 0)
l2.raw.edges.merged.output(101, 0)
l3e = l3.raw.edges
l3e.merge
l3e.output(102, 0)

BIN
testdata/drc/drcSimpleTests_145.gds vendored Normal file

Binary file not shown.

51
testdata/drc/drcSimpleTests_146.drc vendored Normal file
View File

@ -0,0 +1,51 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
l1 = input(1, 0)
l2 = input(2, 0)
l1.output(1, 0)
l2.output(2, 0)
l1e = l1.edges
l2e = l2.edges
l1c = l1.corners(as_dots)
l2c = l2.corners(as_dots)
l1e.sep(l2c, 0.6).output(100, 0)
l1c.sep(l2e, 0.6).output(101, 0)
l1c.sep(l2c, 0.6).output(102, 0)
l1e.enc(l2c, 0.6).output(103, 0)
l1c.enc(l2e, 0.6).output(104, 0)
l1c.enc(l2c, 0.6).output(105, 0)
l2e.width(1.0).output(106, 0)
l2c.width(1.0).output(107, 0)
l2e.space(2.0).output(108, 0)
l2c.space(2.0).output(109, 0)
l1e.sep(l2c, 0.6, square).output(110, 0)
l1c.sep(l2e, 0.6, square).output(111, 0)
l1c.sep(l2c, 0.6, square).output(112, 0)
l1e.enc(l2c, 0.6, square).output(113, 0)
l1c.enc(l2e, 0.6, square).output(114, 0)
l1c.enc(l2c, 0.6, square).output(115, 0)
l2e.width(1.0, square).output(116, 0)
l2c.width(1.0, square).output(117, 0)
l2e.space(2.0, square).output(118, 0)
l2c.space(2.0, square).output(119, 0)
l1e.sep(l2c, 0.6, projection).output(120, 0)
l1c.sep(l2e, 0.6, projection).output(121, 0)
l1c.sep(l2c, 0.6, projection).output(122, 0)
l1e.enc(l2c, 0.6, projection).output(123, 0)
l1c.enc(l2e, 0.6, projection).output(124, 0)
l1c.enc(l2c, 0.6, projection).output(125, 0)
l2e.width(1.0, projection).output(126, 0)
l2c.width(1.0, projection).output(127, 0)
l2e.space(2.0, projection).output(128, 0)
l2c.space(2.0, projection).output(129, 0)

BIN
testdata/drc/drcSimpleTests_146.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au145.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au145d.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au146.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au146d.gds vendored Normal file

Binary file not shown.