Skip to content

Commit 30db25a

Browse files
committed
[layout] patch up flow algorithm and implement more autotests for it
1 parent a2b826d commit 30db25a

File tree

3 files changed

+84
-44
lines changed

3 files changed

+84
-44
lines changed

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ OpenGL and the C++ standard library (C++11).
77

88
The idea is provide a simple scene graph and animation api that is capable of
99
support HTML/CSS style layer composition. That means:
10-
- CSS keyframe animations
11-
- CSS filters (grayscale, blur, etc)
10+
- CSS keyframe inspired animation system
11+
- CSS inspired filters (grayscale, blur, etc)
1212
- (not the css parsing and logic itself, just the rendering and animation
1313
primitives for it)
14-
- only textures and solids
14+
- only textures and rects
1515
- "dumb" 3D support
16-
- at least for now..
1716
- Sort center of layers front to back and render them (like chrome does)
17+
- Could in theory be expanded to proper 3D, but
1818
- replacable backends to handle windowsystem/eventloop/input
1919
- optional backend code to hardware compositor
2020

include/scenegraph/layoutnode.h

+29-34
Original file line numberDiff line numberDiff line change
@@ -263,23 +263,23 @@ void LayoutEngine::updateLayout(Node *parentNode)
263263
int itemLimit;
264264
float sizeLimit;
265265

266-
float flowSign, stepSign;
266+
float posSign, offsetSign;
267267

268268
if (layoutType == Flow_Horizontal) {
269-
flowSign = width < 0 ? -1 : 1;
270-
stepSign = height < 0 ? -1 : 1;
269+
posSign = width < 0 ? -1 : 1;
270+
offsetSign = height < 0 ? -1 : 1;
271271
itemLimit = columnCount > 0 ? columnCount : std::numeric_limits<int>::max();
272-
sizeLimit = width != 0 ? width - flowSign * margin : flowSign * std::numeric_limits<float>::infinity();
272+
sizeLimit = width != 0 ? width - posSign * margin * 2 : posSign * std::numeric_limits<float>::infinity();
273273
} else {
274-
flowSign = height < 0 ? -1 : 1;
275-
stepSign = width < 0 ? -1 : 1;
274+
posSign = height < 0 ? -1 : 1;
275+
offsetSign = width < 0 ? -1 : 1;
276276
itemLimit = rowCount > 0 ? rowCount : std::numeric_limits<int>::max();
277-
sizeLimit = height != 0 ? height - flowSign * margin : flowSign * std::numeric_limits<float>::infinity();
277+
sizeLimit = height != 0 ? height - posSign * margin * 2 : posSign * std::numeric_limits<float>::infinity();
278278
}
279279

280-
float flow = flowSign * margin; // The position in the direction of the flow
281-
float step = stepSign * margin; // The position in the direction normal to the flow
282-
float stepIncrement = 0;
280+
float pos = posSign * margin; // The position to place the node on the current line
281+
float offset = offsetSign * margin; // The position in the direction normal to the pos
282+
float offsetIncrement = 0;
283283

284284
logd << (layoutType == Flow_Horizontal ? "horizontal" : "vertical")
285285
<< "-flow: itemLimit=" << itemLimit << ", sizeLimit=" << sizeLimit
@@ -291,38 +291,33 @@ void LayoutEngine::updateLayout(Node *parentNode)
291291
RectangleNodeBase *rectNode = RectangleNodeBase::from(node);
292292
if (rectNode) {
293293
float dim = layoutType == Flow_Horizontal ? rectNode->width() : rectNode->height();
294-
float end = flow + flowSign * dim;
295-
if (index == 0 || (flowSign * end <= flowSign * sizeLimit && index < itemLimit)) {
296-
index++;
294+
float end = pos + posSign * dim;
295+
bool fitsOnLine = (posSign * end <= posSign * sizeLimit && index < itemLimit);
296+
logd << " - pos=" << pos << ", dim=" << dim << ", end=" << end << ", index=" << index << ", fitsOnLine=" << fitsOnLine << std::endl;
297+
298+
if (fitsOnLine || index == 0) {
297299
if (layoutType == Flow_Horizontal) {
298-
stepIncrement = std::max(stepIncrement, rectNode->height());
299-
rectNode->setPosition(flowSign > 0 ? flow : flow - rectNode->width(),
300-
stepSign > 0 ? step : step - rectNode->height());
300+
offsetIncrement = std::max(offsetIncrement, rectNode->height());
301+
rectNode->setPosition(posSign > 0 ? pos : pos - rectNode->width(),
302+
offsetSign > 0 ? offset : offset - rectNode->height());
301303
} else {
302-
stepIncrement = std::max(stepIncrement, rectNode->width());
303-
rectNode->setPosition(stepSign > 0 ? step : step - rectNode->width(),
304-
flowSign > 0 ? flow : flow - rectNode->height());
304+
offsetIncrement = std::max(offsetIncrement, rectNode->width());
305+
rectNode->setPosition(offsetSign > 0 ? offset : offset - rectNode->width(),
306+
posSign > 0 ? pos : pos - rectNode->height());
305307
}
306-
flow = end + flowSign * spacing;
308+
++index;
309+
pos = end + posSign * spacing;
310+
node = node->sibling();
307311
} else {
312+
// If it doesn't fit, wrap to the next line..
313+
offset += offsetSign * (offsetIncrement + spacing);
314+
pos = posSign * margin;
308315
index = 0;
309-
step += stepSign * (stepIncrement + spacing);
310-
flow = flowSign * (margin + dim + spacing);
311-
if (layoutType == Flow_Horizontal) {
312-
stepIncrement = rectNode->height();
313-
rectNode->setPosition(flowSign > 0 ? margin : -margin - rectNode->width(),
314-
stepSign > 0 ? step : step - rectNode->height());
315-
} else {
316-
stepIncrement = rectNode->width();
317-
rectNode->setPosition(stepSign > 0 ? step : step - rectNode->width(),
318-
flowSign > 0 ? margin : -margin - rectNode->height());
319-
}
320316
}
317+
} else {
318+
node = node->sibling();
321319
}
322-
323-
node = node->sibling();
324320
}
325-
326321
}
327322
}
328323

tests/tst_layout.cpp

+51-6
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,8 @@ static void tst_layoutnode_horizontal_flow()
532532
root->setActivationMode(LayoutNode::Explicit);
533533
root->setLayoutType(LayoutEngine::Flow_Horizontal);
534534

535+
float inf = std::numeric_limits<float>::infinity();
536+
535537
RectangleNode *r[4]; // sizes: 10, 20, 40, 80
536538
for (int i=0; i<4; ++i) {
537539
float s = std::pow(2, (float)i) * 10;
@@ -549,25 +551,68 @@ static void tst_layoutnode_horizontal_flow()
549551
check_equal(r[1]->geometry(), rect2d::fromXywh(13, 2, 20, 20));
550552
check_equal(r[2]->geometry(), rect2d::fromXywh(34, 2, 40, 40));
551553
check_equal(r[3]->geometry(), rect2d::fromXywh(75, 2, 80, 80));
552-
apply_w_h_r_c_m_s(root, 76, 0, 0, 0, 2, 1); // r[2] ends on 74, +2 margin
554+
apply_w_h_r_c_m_s(root, 78, 0, 0, 0, 2, 1); // r[2] ends on 74, +4 (2+2) margin
553555
check_equal(r[0]->geometry(), rect2d::fromXywh( 2, 2, 10, 10));
554556
check_equal(r[1]->geometry(), rect2d::fromXywh(13, 2, 20, 20));
555557
check_equal(r[2]->geometry(), rect2d::fromXywh(34, 2, 40, 40));
556558
check_equal(r[3]->geometry(), rect2d::fromXywh( 2, 43, 80, 80));
557-
apply_w_h_r_c_m_s(root, 75, 0, 0, 0, 2, 1); // will not fit the margin, so r[2] wraps
559+
apply_w_h_r_c_m_s(root, 77.99, 0, 0, 0, 2, 1); // will not fit the, so r[2] wraps
560+
check_equal(r[0]->geometry(), rect2d::fromXywh( 2, 2, 10, 10));
561+
check_equal(r[1]->geometry(), rect2d::fromXywh(13, 2, 20, 20));
562+
check_equal(r[2]->geometry(), rect2d::fromXywh( 2, 23, 40, 40));
563+
check_equal(r[3]->geometry(), rect2d::fromXywh( 2, 64, 80, 80));
564+
apply_w_h_r_c_m_s(root, 4, 0, 0, 0, 2, 1); // too narrow width, but we always have one per row
565+
check_equal(r[0]->geometry(), rect2d::fromXywh(2, 2, 10, 10));
566+
check_equal(r[1]->geometry(), rect2d::fromXywh(2, 13, 20, 20));
567+
check_equal(r[2]->geometry(), rect2d::fromXywh(2, 34, 40, 40));
568+
check_equal(r[3]->geometry(), rect2d::fromXywh(2, 75, 80, 80));
569+
apply_w_h_r_c_m_s(root, 0, 0, 0, 2, 2, 1); // will not fit the number of items, so r[2] wraps
558570
check_equal(r[0]->geometry(), rect2d::fromXywh( 2, 2, 10, 10));
559571
check_equal(r[1]->geometry(), rect2d::fromXywh(13, 2, 20, 20));
560572
check_equal(r[2]->geometry(), rect2d::fromXywh( 2, 23, 40, 40));
561573
check_equal(r[3]->geometry(), rect2d::fromXywh(43, 23, 80, 80));
562-
apply_w_h_r_c_m_s(root, 3, 0, 0, 0, 2, 1); // too narrow width, but we always have one per row
574+
apply_w_h_r_c_m_s(root, 0, 0, 0, 1, 2, 1); // single-column, wrap all
563575
check_equal(r[0]->geometry(), rect2d::fromXywh(2, 2, 10, 10));
564576
check_equal(r[1]->geometry(), rect2d::fromXywh(2, 13, 20, 20));
565577
check_equal(r[2]->geometry(), rect2d::fromXywh(2, 34, 40, 40));
566578
check_equal(r[3]->geometry(), rect2d::fromXywh(2, 75, 80, 80));
567579

568-
569-
// test wrapping by width and columns..
570-
580+
// Horizontal, negative layout direction..
581+
apply_w_h_r_c_m_s(root, -inf, 0, 0, 0, 0, 0);
582+
check_equal(r[0]->geometry(), rect2d::fromXywh( -10, 0, 10, 10));
583+
check_equal(r[1]->geometry(), rect2d::fromXywh( -30, 0, 20, 20));
584+
check_equal(r[2]->geometry(), rect2d::fromXywh( -70, 0, 40, 40));
585+
check_equal(r[3]->geometry(), rect2d::fromXywh(-150, 0, 80, 80));
586+
apply_w_h_r_c_m_s(root, -inf, 0, 0, 0, 2, 1);
587+
check_equal(r[0]->geometry(), rect2d::fromXywh( -12, 2, 10, 10));
588+
check_equal(r[1]->geometry(), rect2d::fromXywh( -33, 2, 20, 20));
589+
check_equal(r[2]->geometry(), rect2d::fromXywh( -74, 2, 40, 40));
590+
check_equal(r[3]->geometry(), rect2d::fromXywh(-155, 2, 80, 80));
591+
apply_w_h_r_c_m_s(root, -78, 0, 0, 0, 2, 1); // r[2] ends on 74, +4 (2+2) margin
592+
check_equal(r[0]->geometry(), rect2d::fromXywh(-12, 2, 10, 10));
593+
check_equal(r[1]->geometry(), rect2d::fromXywh(-33, 2, 20, 20));
594+
check_equal(r[2]->geometry(), rect2d::fromXywh(-74, 2, 40, 40));
595+
check_equal(r[3]->geometry(), rect2d::fromXywh(-82, 43, 80, 80));
596+
apply_w_h_r_c_m_s(root, -77.99, 0, 0, 0, 2, 1); // will not fit the, so r[2] wraps
597+
check_equal(r[0]->geometry(), rect2d::fromXywh(-12, 2, 10, 10));
598+
check_equal(r[1]->geometry(), rect2d::fromXywh(-33, 2, 20, 20));
599+
check_equal(r[2]->geometry(), rect2d::fromXywh(-42, 23, 40, 40));
600+
check_equal(r[3]->geometry(), rect2d::fromXywh(-82, 64, 80, 80));
601+
apply_w_h_r_c_m_s(root, -4, 0, 0, 0, 2, 1); // too narrow width, but we always have one per row
602+
check_equal(r[0]->geometry(), rect2d::fromXywh(-12, 2, 10, 10));
603+
check_equal(r[1]->geometry(), rect2d::fromXywh(-22, 13, 20, 20));
604+
check_equal(r[2]->geometry(), rect2d::fromXywh(-42, 34, 40, 40));
605+
check_equal(r[3]->geometry(), rect2d::fromXywh(-82, 75, 80, 80));
606+
apply_w_h_r_c_m_s(root, -inf, 0, 0, 2, 2, 1); // will not fit rows, so r[2] wraps
607+
check_equal(r[0]->geometry(), rect2d::fromXywh(-12, 2, 10, 10));
608+
check_equal(r[1]->geometry(), rect2d::fromXywh(-33, 2, 20, 20));
609+
check_equal(r[2]->geometry(), rect2d::fromXywh(-42, 23, 40, 40));
610+
check_equal(r[3]->geometry(), rect2d::fromXywh(-123, 23, 80, 80));
611+
apply_w_h_r_c_m_s(root, -inf, 0, 0, 1, 2, 1); // single-column, wrap all
612+
check_equal(r[0]->geometry(), rect2d::fromXywh(-12, 2, 10, 10));
613+
check_equal(r[1]->geometry(), rect2d::fromXywh(-22, 13, 20, 20));
614+
check_equal(r[2]->geometry(), rect2d::fromXywh(-42, 34, 40, 40));
615+
check_equal(r[3]->geometry(), rect2d::fromXywh(-82, 75, 80, 80));
571616

572617

573618
cout << __PRETTY_FUNCTION__ << ": ok" << endl;

0 commit comments

Comments
 (0)