Skip to content

Commit e71ed2c

Browse files
Merge branch 'bugfix-610-unable-to-pull-wire' into copilot/sub-pr-621
2 parents 2a03dd2 + cf496c7 commit e71ed2c

2 files changed

Lines changed: 88 additions & 35 deletions

File tree

model/group.cc

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,26 +1045,21 @@ namespace minsky
10451045

10461046
}
10471047

1048-
void Group::draw1edge(const vector<VariablePtr>& vars, cairo_t* cairo,
1049-
float x) const
1048+
// Helper to compute edge variable layout (world-space positions) radially
1049+
static void layoutEdgeVariables(const vector<VariablePtr>& vars, float xEdge,
1050+
const Item* groupItem, double groupRotation)
10501051
{
10511052
float top=0, bottom=0;
1052-
const double angle=rotation() * M_PI / 180.0;
10531053
for (size_t i=0; i<vars.size(); ++i)
10541054
{
1055-
const Rotate r(rotation(),0,0);
1055+
const Rotate r(groupRotation,0,0);
10561056
auto& v=vars[i];
1057-
float y=0;
1057+
float yOff=0;
10581058
auto z=v->zoomFactor();
10591059
auto t=v->bb.top()*z, b=v->bb.bottom()*z;
1060-
if (i>0) y = i%2? top-b: bottom-t;
1061-
v->moveTo(r.x(x,y)+this->x(), r.y(x,y)+this->y());
1062-
const cairo::CairoSave cs(cairo);
1063-
cairo_translate(cairo,x,y);
1064-
// cairo context is already rotated, so antirotate
1065-
cairo_rotate(cairo,-angle);
1066-
v->rotation(rotation());
1067-
v->draw(cairo);
1060+
if (i>0) yOff = i%2? top-b: bottom-t;
1061+
v->moveTo(r.x(xEdge,yOff)+groupItem->x(), r.y(xEdge,yOff)+groupItem->y());
1062+
v->rotation(groupRotation);
10681063
if (i==0)
10691064
{
10701065
bottom=b;
@@ -1077,6 +1072,44 @@ namespace minsky
10771072
}
10781073
}
10791074

1075+
void Group::draw1edge(const vector<VariablePtr>& vars, cairo_t* cairo,
1076+
float xEdge) const
1077+
{
1078+
layoutEdgeVariables(vars, xEdge, this, rotation());
1079+
1080+
// Iterate again solely to perform drawing steps.
1081+
// Group::draw has ALREADY called cairo_scale(cairo, z, z), so the `xEdge`
1082+
// parameter is properly matched to the visual coordinates.
1083+
// However, translating `yOff` locally during rendering requires tracking
1084+
// the variable bounding boxes WITHOUT pre-multiplying `z` onto them here.
1085+
const double angle=rotation() * M_PI / 180.0;
1086+
float top=0, bottom=0;
1087+
for (size_t i=0; i<vars.size(); ++i)
1088+
{
1089+
auto& v=vars[i];
1090+
1091+
float yOff = 0;
1092+
auto t = v->bb.top(), b = v->bb.bottom();
1093+
if (i > 0) yOff = i % 2 ? top - b : bottom - t;
1094+
1095+
const cairo::CairoSave cs(cairo);
1096+
cairo_translate(cairo,xEdge,yOff);
1097+
// cairo context is already rotated, so antirotate
1098+
cairo_rotate(cairo,-angle);
1099+
v->draw(cairo);
1100+
1101+
if (i == 0)
1102+
{
1103+
bottom = b;
1104+
top = t;
1105+
}
1106+
else if (i % 2)
1107+
top -= v->height() / v->zoomFactor(); // Remove zoom as Group::draw supplies it
1108+
else
1109+
bottom += v->height() / v->zoomFactor();
1110+
}
1111+
}
1112+
10801113
void Group::drawEdgeVariables(cairo_t* cairo) const
10811114
{
10821115
float left, right; margins(left,right);
@@ -1196,28 +1229,8 @@ namespace minsky
11961229
margins(leftMargin, rightMargin);
11971230
const float z = zoomFactor();
11981231

1199-
auto position1edge = [&](const std::vector<VariablePtr>& vars, float xEdge)
1200-
{
1201-
float top = 0, bottom = 0;
1202-
for (size_t i = 0; i < vars.size(); ++i)
1203-
{
1204-
const Rotate r(rotation(), 0, 0);
1205-
auto& v = vars[i];
1206-
float yOff = 0;
1207-
auto vz = v->zoomFactor();
1208-
auto t = v->bb.top() * vz, b = v->bb.bottom() * vz;
1209-
if (i > 0) yOff = (i % 2) ? top - b : bottom - t;
1210-
v->moveTo(r.x(xEdge, yOff) + this->x(),
1211-
r.y(xEdge, yOff) + this->y());
1212-
v->rotation(rotation());
1213-
if (i == 0) { bottom = b; top = t; }
1214-
else if (i % 2) top -= v->height();
1215-
else bottom += v->height();
1216-
}
1217-
};
1218-
1219-
position1edge(inVariables, -0.5f * (iWidth() * z - leftMargin));
1220-
position1edge(outVariables, 0.5f * (iWidth() * z - rightMargin));
1232+
layoutEdgeVariables(inVariables, -0.5f * (iWidth() * z - leftMargin), this, rotation());
1233+
layoutEdgeVariables(outVariables, 0.5f * (iWidth() * z - rightMargin), this, rotation());
12211234
}
12221235

12231236
ItemPtr Group::select(float x, float y) const

test/testCanvas.cc

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,47 @@ TEST(GroupIOTest, groupIOVarSelect)
124124
EXPECT_NE(nullptr, found)
125125
<< "select() should find the output variable without a prior draw() call (issue #610)";
126126
if (found)
127+
{
127128
EXPECT_EQ(grp->outVariables[0].get(), found.get())
128129
<< "select() should return the output variable, not some other item";
130+
}
131+
}
132+
133+
/// Regression test to verify the zoomed wire-pull path works.
134+
TEST(GroupIOTest, groupIOVarSelectZoomed)
135+
{
136+
// Build a standalone group with an output variable.
137+
auto grp = make_shared<Group>();
138+
grp->self = grp;
139+
grp->moveTo(200, 200);
140+
grp->iWidth(100);
141+
grp->iHeight(100);
142+
143+
// Set a non-default zoom
144+
grp->setZoom(2.5f);
145+
146+
grp->addOutputVar();
147+
ASSERT_EQ(1u, grp->outVariables.size());
148+
149+
// Update bounding box for the output variable so margins() works.
150+
auto& outVar = *grp->outVariables[0];
151+
outVar.bb.update(outVar);
152+
153+
float leftMargin, rightMargin;
154+
grp->margins(leftMargin, rightMargin);
155+
const float z = grp->zoomFactor();
156+
const float xEdge = 0.5f * (grp->iWidth() * z - rightMargin);
157+
const float expectedX = grp->x() + xEdge;
158+
const float expectedY = grp->y();
159+
160+
auto found = grp->select(expectedX, expectedY);
161+
EXPECT_NE(nullptr, found)
162+
<< "select() should find the output variable at a non-default zoom";
163+
164+
// Since `draw()` hasn't positioned the ports dynamically, we check the exact
165+
// underlying static port coordinates of the found variable after positions are set.
166+
auto outPort = outVar.ports(0).lock();
167+
EXPECT_EQ(ClickType::onPort, grp->clickType(outPort->x(), outPort->y()))
168+
<< "clickType() on group should yield onPort at a non-default zoom for wire-pulling";
129169
}
130170

0 commit comments

Comments
 (0)