Initial size handling

This commit is contained in:
IlyaShurupov 2024-10-10 11:17:03 +03:00
parent e028ad1a41
commit b2c393d23a
28 changed files with 543 additions and 135 deletions

View file

@ -17,7 +17,7 @@ Item buff[size];
int main() {
AvlTree<AvlNumericKey<alni>, alni> tree;
for (auto i : Range(size)) {
for (auto i : IterRange(size)) {
buff[i].data = i;
}

View file

@ -9,7 +9,7 @@ SUITE(Buffer) {
TEST(Simple1) {
Buffer<TestClass, TestAllocator> buff;
CHECK(buff.size() == 0);
for (auto i : Range(size * 10)) {
for (auto i : IterRange(size * 10)) {
buff.append(TestClass(i));
}
CHECK(buff.size() == size * 10);
@ -21,7 +21,7 @@ SUITE(Buffer) {
TEST(Simple2) {
Buffer<TestClass, TestAllocator> buff(size);
CHECK(buff.size() == size);
for (auto i : Range(size * 10))
for (auto i : IterRange(size * 10))
buff.append(TestClass(i));
CHECK(buff.size() == size + size * 10);
while (buff.size())

View file

@ -99,7 +99,7 @@ SUITE(IntervalTree) {
};
// initialize
for (auto i : Range<ualni>(NUM_TEST_INTERVALS)) {
for (auto i : IterRange<ualni>(NUM_TEST_INTERVALS)) {
auto interval = Interval();
interval.random(SPAN);
@ -114,7 +114,7 @@ SUITE(IntervalTree) {
test();
// remove some
for (auto i : Range<ualni>(NUM_TEST_INTERVALS / 2)) {
for (auto i : IterRange<ualni>(NUM_TEST_INTERVALS / 2)) {
auto idx = ualni(randomFloat() * (alnf) pool.size());
pool[idx].ignore = true;
intervalTree.remove({ pool[idx].start, pool[idx].end });
@ -139,18 +139,18 @@ SUITE(IntervalTree) {
Buffer<Interval> testIntervals;
auto WOBBLE = SPAN * 0;
for (auto i : Range<ualni>(NUM_TEST_INTERVALS)) {
for (auto i : IterRange<ualni>(NUM_TEST_INTERVALS)) {
auto interval = Interval();
interval.randomSized(SPAN, SCALE, WOBBLE);
intervalTree.insert({ interval.start, interval.end }, i);
// WOBBLE -= 1.f;
}
for (auto i : Range(0))
for (auto i : IterRange(0))
intervalTree.insert({ (halnf) i * 0.01f, SPAN }, 0);
WOBBLE = 0;
for (auto i : Range<ualni>(NUM_CHECKS)) {
for (auto i : IterRange<ualni>(NUM_CHECKS)) {
auto interval = Interval();
interval.randomSized(SPAN, SCALE, WOBBLE);
testIntervals.append(interval);
@ -200,7 +200,7 @@ SUITE(IntervalTree) {
};
Buffer<Stat> stats;
for (auto i : Range(2, 5)) {
for (auto i : IterRange(2, 5)) {
Stat stat = test(pow(10, i), 100);
stats.append(stat);
}

View file

@ -8,26 +8,26 @@ SUITE(HashTable) {
TEST(SimpleReference) {
tp::Map<tp::ualni, TestClass, TestAllocator> map;
for (auto i : Range(1000, 100000)) {
for (auto i : IterRange(1000, 100000)) {
map.put(i, TestClass(i));
}
for (auto i : Range(1000, 100000)) {
for (auto i : IterRange(1000, 100000)) {
CHECK(map.presents(i));
CHECK_EQUAL((tp::ualni) map.get(i).getVal(), (tp::ualni) i);
}
for (auto i : Range(1000, 100000)) {
for (auto i : IterRange(1000, 100000)) {
map.put(i, TestClass(i));
}
for (auto i : Range(1000, 2000)) {
for (auto i : IterRange(1000, 2000)) {
CHECK(map.presents(i));
map.remove(i);
CHECK(!map.presents(i));
}
for (auto i : Range(2000, 100000)) {
for (auto i : IterRange(2000, 100000)) {
CHECK(map.presents(i));
CHECK_EQUAL((tp::ualni) map.get(i).getVal(), (tp::ualni) i);
}
@ -42,29 +42,29 @@ SUITE(HashTable) {
TEST(SimplePointer) {
tp::Map<tp::ualni, TestClass*, TestAllocator> map;
for (auto i : Range(1000)) {
for (auto i : IterRange(1000)) {
map.put(i, new TestClass(i));
}
for (auto i : Range(1000)) {
for (auto i : IterRange(1000)) {
CHECK(map.presents(i));
CHECK_EQUAL((tp::ualni) map.get(i)->getVal(), (tp::ualni) i);
}
for (auto i : Range(1000)) {
for (auto i : IterRange(1000)) {
auto del = map.get(i);
map.put(i, new TestClass(i));
delete del;
}
for (auto i : Range(900, 1000)) {
for (auto i : IterRange(900, 1000)) {
CHECK(map.presents(i));
delete map.get(i);
map.remove(i);
CHECK(!map.presents(i));
}
for (auto i : Range(900)) {
for (auto i : IterRange(900)) {
CHECK(map.presents(i));
CHECK_EQUAL((tp::ualni) map.get(i)->getVal(), (tp::ualni) i);
}
@ -80,7 +80,7 @@ SUITE(HashTable) {
TEST(Copy) {
tp::Map<tp::ualni, TestClass, TestAllocator> map;
for (auto i : Range(10)) {
for (auto i : IterRange(10)) {
map.put(i, TestClass(i));
}
@ -95,7 +95,7 @@ SUITE(HashTable) {
TEST(SaveLoad) {
tp::Map<tp::ualni, TestClass, TestAllocator> map;
for (auto i : Range(10)) {
for (auto i : IterRange(10)) {
map.put(i, TestClass(i));
}
@ -114,7 +114,7 @@ SUITE(HashTable) {
CHECK(map.size() == 10);
for (auto i : Range(10)) {
for (auto i : IterRange(10)) {
CHECK(map.presents(i));
CHECK_EQUAL(map.get(i).getVal(), i);
}

View file

@ -37,7 +37,7 @@ SUITE(AvlTree) {
Item buff[size];
for (auto i : Range(size)) {
for (auto i : IterRange(size)) {
buff[i].data.setVal(i);
}

View file

@ -18,7 +18,7 @@ void loadImage(Buffer<halnf>& output, const char* name) {
output.reserve(x * y);
for (auto i : Range(output.size())) {
for (auto i : IterRange(output.size())) {
output[i] = loadedImage[i * 4] / 255.f;
}

View file

@ -52,7 +52,7 @@ bool loadDataset(Dataset& out, const std::string& location) {
out.labels.reserve(out.length);
out.images.reserve(out.length);
for (auto i : Range(out.length)) {
for (auto i : IterRange(out.length)) {
auto& image = out.images[i];
image.reserve(sizeX * sizeY);
dataset.readBytes((LocalConnection::Byte*) image.getBuff(), sizeX * sizeY);
@ -89,7 +89,7 @@ struct NumberRec {
}
mTestcases.reserve(dataset.images.size());
for (auto i : Range(dataset.images.size())) {
for (auto i : IterRange(dataset.images.size())) {
auto& image = dataset.images[i];
auto label = dataset.labels[i];
@ -97,13 +97,13 @@ struct NumberRec {
testcase.output.reserve(10);
for (auto dig : Range(10)) {
for (auto dig : IterRange(10)) {
testcase.output[dig] = label == dig ? 1 : 0;
}
testcase.input.reserve(image.size());
for (auto pxl : Range(image.size())) {
for (auto pxl : IterRange(image.size())) {
testcase.input[pxl] = (halnf) image[pxl] / 255.f;
}
}
@ -137,7 +137,7 @@ struct NumberRec {
static halni getMaxIdx(const Buffer<halnf>& in) {
halni out = 0;
for (auto i : Range(in.size())) {
for (auto i : IterRange(in.size())) {
if (in[i] > in[out]) {
out = i;
}
@ -165,15 +165,15 @@ struct NumberRec {
void displayImage(ualni idx) {
auto& testcase = mTestcases[idx];
printf("Image : %i\n", int(getMaxIdx(testcase.output)));
for (auto i : Range(28)) {
for (auto j : Range(28)) {
for (auto i : IterRange(28)) {
for (auto j : IterRange(28)) {
printf("%c", char(testcase.input[j * 28 + i] * 255));
}
printf("\n");
}
}
halnf test(const Range<halni>& range) {
halnf test(const IterRange<halni>& range) {
halnf avgCost = 0;
for (auto i : range) {
avgCost += eval(i);
@ -182,7 +182,7 @@ struct NumberRec {
return avgCost;
}
void trainStep(const Range<halni>& range) {
void trainStep(const IterRange<halni>& range) {
nn.clearGrad();
for (auto i : range) {
nn.evaluate(mTestcases[i].input, output);
@ -210,18 +210,19 @@ int main() {
NumberRec app;
auto numBatches = 10;
auto trainRange = Range(0, 50000);
auto testRange = Range(50000, 70000);
auto trainRange = IterRange(0, 50000);
auto testRange = IterRange(50000, 70000);
auto batchSize = trainRange.idxDiff() / numBatches;
for (auto epoch : Range(1)) {
for (auto epoch : IterRange(1)) {
printf("Epoch %i\n", epoch.index());
for (auto batchIdx : Range(trainRange.idxDiff() / batchSize)) {
for (auto batchIdx : IterRange(trainRange.idxDiff() / batchSize)) {
printf(" - Batch :%i \n", batchIdx.index());
auto batchRange = Range(trainRange.idxBegin() + batchSize * batchIdx, trainRange.idxBegin() + batchSize * (batchIdx + 1));
auto batchRange =
IterRange(trainRange.idxBegin() + batchSize * batchIdx, trainRange.idxBegin() + batchSize * (batchIdx + 1));
app.trainStep(batchRange);

View file

@ -32,7 +32,7 @@ void FCNN::initializeRandom(const Buffer<halni>& description) {
mLayers.reserve(description.size());
for (auto i : Range<halni>(0, (halni) description.size())) {
for (auto i : IterRange<halni>(0, (halni) description.size())) {
mLayers[i].neurons.reserve(description[i]);
if (i == 0) {
continue;
@ -50,17 +50,17 @@ void FCNN::initializeRandom(const Buffer<halni>& description) {
void FCNN::evaluate(const Buffer<halnf>& input, Buffer<halnf>& output) {
ASSERT(output.size() == mLayers.last().neurons.size() && input.size() == mLayers.first().neurons.size())
for (auto idx : Range(input.size())) {
for (auto idx : IterRange(input.size())) {
mLayers.first().neurons[idx].activationValue = input[idx];
}
for (auto layerIdx : Range<halni>(1, (halni) mLayers.size())) {
for (auto layerIdx : IterRange<halni>(1, (halni) mLayers.size())) {
auto& layer = mLayers[layerIdx];
auto& layerPrev = mLayers[layerIdx - 1];
for (auto neuron : layer.neurons) {
neuron->activationValue = 0;
for (auto connectionIdx : Range(neuron->weights.size())) {
for (auto connectionIdx : IterRange(neuron->weights.size())) {
neuron->activationValue += neuron->weights[connectionIdx].val * layerPrev.neurons[connectionIdx].activationValue;
}
neuron->activationValue += neuron->bias.val;
@ -69,26 +69,26 @@ void FCNN::evaluate(const Buffer<halnf>& input, Buffer<halnf>& output) {
}
}
for (auto idx : Range(output.size())) {
for (auto idx : IterRange(output.size())) {
output[idx] = mLayers.last().neurons[idx].activationValue;
}
}
halnf FCNN::calcCost(const Buffer<halnf>& output) {
halnf out = 0;
for (auto neuronIdx : Range(mLayers.last().neurons.size())) {
for (auto neuronIdx : IterRange(mLayers.last().neurons.size())) {
out += pow(output[neuronIdx] - mLayers.last().neurons[neuronIdx].activationValue, 2);
}
return out;
}
void FCNN::clearGrad() {
for (auto layIdx : Range<halni>(1, (halni) mLayers.size())) {
for (auto layIdx : IterRange<halni>(1, (halni) mLayers.size())) {
auto& layer = mLayers[layIdx];
for (auto neuron : layer.neurons) {
neuron->bias.grad = 0;
for (auto weightIdx : Range(neuron->weights.size())) {
for (auto weightIdx : IterRange(neuron->weights.size())) {
neuron->weights[weightIdx].grad = 0;
}
}
@ -102,7 +102,7 @@ void FCNN::calcGrad(const Buffer<halnf>& output) {
auto& lastLayer = mLayers.last();
// calculate chaining cache value for each neuron in last layer
for (auto neuronIdx : Range(lastLayer.neurons.size())) {
for (auto neuronIdx : IterRange(lastLayer.neurons.size())) {
auto& neuron = lastLayer.neurons[neuronIdx];
neuron.cache = 2 * (neuron.activationValue - output[neuronIdx]);
}
@ -113,7 +113,7 @@ void FCNN::calcGrad(const Buffer<halnf>& output) {
auto& currentLayer = mLayers[layerIdx];
auto& inputLayer = mLayers[layerIdx - 1];
for (auto currentNeuronIdx : Range(currentLayer.neurons.size())) {
for (auto currentNeuronIdx : IterRange(currentLayer.neurons.size())) {
auto& currentNeuron = currentLayer.neurons[currentNeuronIdx];
// calculate cache value (chaining)
@ -131,7 +131,7 @@ void FCNN::calcGrad(const Buffer<halnf>& output) {
currentNeuron.bias.grad += currentNeuron.cache;
// calculate gradient for weights of current neuron
for (auto weightIdx : Range(currentNeuron.weights.size())) {
for (auto weightIdx : IterRange(currentNeuron.weights.size())) {
currentNeuron.weights[weightIdx].grad += inputLayer.neurons[weightIdx].activationValue * currentNeuron.cache;
}
}
@ -142,12 +142,12 @@ void FCNN::calcGrad(const Buffer<halnf>& output) {
void FCNN::applyGrad(halnf step) {
for (auto layIdx : Range<halni>(1, (halni) mLayers.size())) {
for (auto layIdx : IterRange<halni>(1, (halni) mLayers.size())) {
auto& layer = mLayers[layIdx];
for (auto neuron : layer.neurons) {
neuron->bias.val -= neuron->bias.grad / (halnf) mAvgCount * step;
for (auto weightIdx : Range(neuron->weights.size())) {
for (auto weightIdx : IterRange(neuron->weights.size())) {
neuron->weights[weightIdx].val -= (neuron->weights[weightIdx].grad / (halnf) mAvgCount) * step;
}
}

View file

@ -31,11 +31,11 @@ SUITE(FCNN) {
Buffer<halnf> outputExpected(layers.last());
Buffer<halnf> output(layers.last());
for (auto inputVal : Range(layers.first())) {
for (auto inputVal : IterRange(layers.first())) {
input[inputVal] = inputLayer[inputVal];
}
for (auto outIdx : Range(layers.last())) {
for (auto outIdx : IterRange(layers.last())) {
outputExpected[outIdx] = outputLayer[outIdx];
}
@ -44,7 +44,7 @@ SUITE(FCNN) {
halnf cost = 0;
for (auto i : Range(150)) {
for (auto i : IterRange(150)) {
nn.evaluate(input, output);

View file

@ -88,18 +88,18 @@ void TopologyCache::updateCache() {
}
TransformedPoints.reserve(Source->Points.size());
for (auto idx : Range(TransformedPoints.size())) {
for (auto idx : IterRange(TransformedPoints.size())) {
TransformedPoints[idx] = Source->Basis.transform(Source->Points[idx]);
TransformedPoints[idx] += Source->Origin;
}
TransformedNormals.reserve(Source->Normals.size());
for (auto idx : Range(TransformedNormals.size())) {
for (auto idx : IterRange(TransformedNormals.size())) {
TransformedNormals[idx] = Source->Basis.transform(Source->Normals[idx]);
}
TrigCaches.reserve(Source->Indexes.size());
for (auto idx : Range(TrigCaches.size())) {
for (auto idx : IterRange(TrigCaches.size())) {
TrigCaches[idx].mP1 = Source->Indexes[idx].x;
TrigCaches[idx].mP2 = Source->Indexes[idx].y;
TrigCaches[idx].mP3 = Source->Indexes[idx].z;

39
Math/public/Range.hpp Normal file
View file

@ -0,0 +1,39 @@
#pragma once
#include "Common.hpp"
namespace tp {
template <typename Type>
class Range {
public:
Range() = default;
Range(Type v1, Type v2) : start(v1), end(v2) {}
Type mid() const { return (start + end) / 2.f; }
Type size() const { return (end - start); }
void resizeFromCenter(Type newSize) {
const auto middle = mid();
const auto len = newSize / 2;
start = middle - len;
end = middle + len;
}
void clamp(const Range& inside, const Range& outside) {
start = tp::clamp(start, outside.start, inside.start);
end = tp::clamp(end, inside.end, outside.end);
}
void clamp(const Range& outside) {
start = tp::clamp(start, outside.start, outside.end);
end = tp::clamp(end, outside.start, outside.end);
}
public:
Type start{};
Type end{};
};
using RangeF = Range<halnf>;
}

View file

@ -2,6 +2,7 @@
#pragma once
#include "Vec.hpp"
#include "Range.hpp"
#include "Intersections.hpp"
@ -35,6 +36,11 @@ namespace tp {
this->size = size;
}
Rect(const Range<Type>& rx, const Range<Type>& ry) {
this->pos = { rx.start, ry.start };
this->size = { rx.size(), ry.size() };
}
Rect(Type aPosX, Type posy, Type aSizeX, Type aSizeY) {
pos.assign(aPosX, posy);
size.assign(aSizeX, aSizeY);
@ -64,7 +70,7 @@ namespace tp {
return *this;
}
bool operator==(Rect<Type>& rect) const { return (pos == rect.pos && size == rect.size); }
bool operator==(const Rect<Type>& rect) const { return (pos == rect.pos && size == rect.size); }
bool isEnclosedIn(const Rect<Type>& rect, bool aParent = false) const {
if (aParent) {
@ -102,6 +108,11 @@ namespace tp {
void invertY(Type scr_y) { pos.y = scr_y - pos.y - size.y; }
Rect& move(Vec2<Type> delta) {
move(delta.x, delta.y);
return *this;
}
void move(Type dx, Type dy) {
pos.x += dx;
pos.y += dy;
@ -119,6 +130,9 @@ namespace tp {
return *this;
}
Range<Type> getRangeX() const { return { x, x + z }; }
Range<Type> getRangeY() const { return { y, y + w }; }
// pos
Vec2<Type> p1() const { return pos; }

View file

@ -226,8 +226,8 @@ namespace tp {
Type y;
Vec() :
x(0),
y(0) {}
x{},
y{} {}
// Initialization
template <typename TypeIn>

View file

@ -46,7 +46,7 @@ namespace tp {
};
template <typename tType = ualni>
class Range {
class IterRange {
public:
class Iterator {
public:
@ -71,13 +71,13 @@ namespace tp {
tType mBegin{};
tType mEnd{};
Range() = default;
IterRange() = default;
explicit Range(tType pEndIndex) :
explicit IterRange(tType pEndIndex) :
mBegin(0),
mEnd(pEndIndex) {}
Range(tType pStartIndex, tType pEndIndex) :
IterRange(tType pStartIndex, tType pEndIndex) :
mBegin(pStartIndex),
mEnd(pEndIndex) {}

View file

@ -410,14 +410,14 @@ alni instSize(const Instruction& inst) {
}
void writeConst(ByteCode& out, alni& idx, uint2 data) {
for (auto byte : Range(sizeof(uint2))) {
for (auto byte : IterRange(sizeof(uint2))) {
out.mInstructions[idx] = OpCode((int1) (data >> byte * 8));
idx++;
}
}
void writeParam(ByteCode& out, alni& idx, const int1* data, alni size) {
for (auto byte : Range(size)) {
for (auto byte : IterRange(size)) {
out.mInstructions[idx] = OpCode(data[byte]);
idx++;
}

View file

@ -79,7 +79,7 @@ void RayTracer::cycle(const RayCastData& castData, LightData& out, uhalni depth)
const auto delta1 = castData.trig->mEdgeP1P2.unitV();
const auto delta2 = normal.cross(delta1);
for (auto idx : Range(mSettings.spray)) {
for (auto idx : IterRange(mSettings.spray)) {
RayCastData materialCastData;
LightData lightData;

View file

@ -35,16 +35,16 @@ const RGBA& Stroke::getColor() const { return mColor; }
void Stroke::updateGpuBuffers() { mGPUHandles.sendDataToGPU(&mPoints); }
void Stroke::denoisePos(halni passes) {
for (auto pass : Range(passes)) {
for (auto pi : Range(mPoints.size() - 2)) {
for (auto pass : IterRange(passes)) {
for (auto pi : IterRange(mPoints.size() - 2)) {
mPoints[pi + 1].pos = (mPoints[pi + 1].pos + mPoints[pi].pos + mPoints[pi + 2].pos) / 3.f;
}
}
}
void Stroke::denoiseThickness(halni passes) {
for (auto pass : Range(passes)) {
for (auto pi : Range(mPoints.size() - 2)) {
for (auto pass : IterRange(passes)) {
for (auto pi : IterRange(mPoints.size() - 2)) {
mPoints[pi + 1].thickness = (mPoints[pi].thickness + mPoints[pi + 2].thickness) / 2.f;
}
}
@ -57,7 +57,7 @@ void Stroke::compress(halnf factor) {
List<StrokePoint> passed_poits;
for (auto idx : Range(mPoints.size())) {
for (auto idx : IterRange(mPoints.size())) {
passed_poits.pushBack(mPoints[idx]);
}
@ -100,14 +100,14 @@ void Stroke::subdiv(halnf precision, const Camera* cam, halni passes) {
}
List<StrokePoint> new_points;
for (auto idx : Range(mPoints.size())) {
for (auto idx : IterRange(mPoints.size())) {
new_points.pushBack(mPoints[idx]);
}
auto viewmat = cam->calculateViewMatrix();
auto projmat = cam->calculateProjectionMatrix();
for (auto i : Range(passes)) {
for (auto i : IterRange(passes)) {
auto n_points = new_points.length();

View file

@ -11,7 +11,7 @@ using namespace tp;
class WidgetApplication : public Application {
public:
WidgetApplication() {
setup1();
setup2();
}
void setup1() {
@ -20,8 +20,8 @@ public:
mDockLayout.addChild(&mFloatingMenu);
mDockLayout.addChild(&mFloatingMenu2);
mDockLayout.setCenterWidget(&mButton3);
mDockLayout.dockWidget(&mButton4, DockLayoutWidget::RIGHT);
mDockLayout.setCenterWidget(&mButton4);
mDockLayout.dockWidget(&mButton3, DockLayoutWidget::RIGHT);
mFloatingMenu.addToMenu(&mButton);
mFloatingMenu2.addToMenu(&mButton2);
@ -29,6 +29,11 @@ public:
mButton.setAction([this]() { mButton2.setColor(RGBA::random()); });
mButton2.setAction([this]() { mButton.setColor(RGBA::random()); });
mButton3.setAction([this]() { mDockLayout.dockWidget(&mFloatingMenu, DockLayoutWidget::Side::LEFT); });
mButton3.setText("dock");
mButton4.setAction([this]() { mDockLayout.undockWidget(DockLayoutWidget::Side::LEFT); });
// mLayoutWidget.addChild(&mButton2);
// mWidgetFloating.addChild(&mButton);
@ -48,7 +53,24 @@ public:
}
void setup2() {
mRootWidget.setRootWidget(&mLabel);
mRootWidget.setRootWidget(&mDesktopLayout);
mDesktopLayout.addChild(&mWidget);
mWidget.addChild(&mButton);
mWidget.addChild(&mButton2);
mWidget.addChild(&mButton3);
mButton.setSizePolicy(Widget::SizePolicy::Expand, Widget::SizePolicy::Contract);
mButton2.setSizePolicy(Widget::SizePolicy::Expand, Widget::SizePolicy::Contract);
mButton3.setSizePolicy(Widget::SizePolicy::Expand, Widget::SizePolicy::Contract);
mWidget.setSizePolicy(Widget::SizePolicy::Contract, Widget::SizePolicy::Contract);
mWidget.setLayoutPolicy(Widget::LayoutPolicy::Horizontally);
mButton.setAction([this]() { mButton2.setMinSize(mButton2.getMinSize() + 10); });
mButton2.setAction([this]() { mButton.setMinSize(mButton.getMinSize() + 10); } );
RootWidget::setWidgetArea(mWidget, { 300, 100, 150, 200 });
}
void processFrame(EventHandler* eventHandler, halnf deltaTime) override {
@ -67,6 +89,8 @@ public:
}
private:
Widget mWidget;
ButtonWidget mButton;
ButtonWidget mButton2;
ButtonWidget mButton3;
@ -78,7 +102,7 @@ private:
LabelWidget mLabel;
FloatingWidget mWidgetFloating;
AnimationTestWidget mAnimationTestWidget;
DesktopLayout mLayoutWidget;
DesktopLayout mDesktopLayout;
DockLayoutWidget mDockLayout;

View file

@ -1,4 +1,5 @@
#include "DockLayoutWidget.hpp"
#include "FloatingWidget.hpp"
using namespace tp;
@ -11,9 +12,33 @@ DockLayoutWidget::DockLayoutWidget() {
void DockLayoutWidget::process(const EventHandler& events) {
// calculateHeaderAreas();
// handlePreview(events);
handleResizeEvents(events);
Widget* floater = nullptr;
for (auto child : mChildren) {
if (auto iter = dynamic_cast<FloatingWidget*>(child)) {
if (iter->isFloating()) {
floater = iter;
break;
}
}
}
if (floater) undockWidget(getSideFromWidget(floater), false);
if (floater) {
handlePreview(events);
}
if (mPreviewWidget && !floater) {
dockWidget(mPreviewWidget, mPreviewSide);
}
mPreviewWidget = floater;
if (!mPreviewWidget) {
handleResizeEvents(events);
}
calculateSideAreas();
calculateResizeHandles();
updateChildSideWidgets();
@ -40,7 +65,7 @@ void DockLayoutWidget::drawOverlay(Canvas& canvas) {
canvas.rect(sideWidget.headerArea, mResizeHandleColorActive, 0);
}
if (mPreview) {
if (mPreviewWidget) {
if (mPreviewSide != NONE) canvas.rect(mPreviewArea.shrink(mPadding * 2), mPreviewColor, mRounding);
for (auto& sideWidget : mSideWidgets) {
@ -64,10 +89,16 @@ void DockLayoutWidget::setCenterWidget(Widget* widget) {
}
void DockLayoutWidget::dockWidget(Widget* widget, Side side) {
DEBUG_ASSERT(!sideExists(side))
if (sideExists(side) || side == Side::NONE) return;
addChild(widget);
widget->bringToBack();
if (mCenterWidget) mCenterWidget->bringToBack();
for (auto& order : mSideWidgets) {
if (order.widget) order.widget->bringToBack();
}
auto& sideWidget = mSideWidgets[side];
sideWidget.widget = widget;
@ -79,10 +110,21 @@ void DockLayoutWidget::dockWidget(Widget* widget, Side side) {
}
sideWidget.hidden = false;
sideWidget.areaBeforeDocking = widget->getArea();
}
void DockLayoutWidget::undockWidget(Side side) {
DEBUG_ASSERT(sideExists(side))
void DockLayoutWidget::undockWidget(Side side, bool restoreArea) {
if (!sideExists(side) || (side == Side::NONE)) return;
auto& sideWidget = mSideWidgets[side];
sideWidget.widget->bringToFront();
if (restoreArea) {
sideWidget.widget->setArea(sideWidget.areaBeforeDocking);
} else {
sideWidget.widget->endAnimations();
}
bool removed = false;
for (ualni i = 0; i < 3; i++) {
@ -275,7 +317,9 @@ bool DockLayoutWidget::isSideVisible(DockLayoutWidget::Side side) {
return sideExists(side) && !mSideWidgets[side].hidden;
}
bool DockLayoutWidget::sideExists(DockLayoutWidget::Side side) { return mSideWidgets[side].widget; }
bool DockLayoutWidget::sideExists(DockLayoutWidget::Side side) {
return mSideWidgets[side].widget;
}
ualni DockLayoutWidget::getVisibleSidesSize() {
ualni out = 0;
@ -286,11 +330,6 @@ ualni DockLayoutWidget::getVisibleSidesSize() {
}
void DockLayoutWidget::handlePreview(const EventHandler& events) {
if (!mPreview) {
mPreviewSide = NONE;
return;
}
const halnf factor = 0.3;
const halnf handleFactor = 0.1;
@ -313,6 +352,7 @@ void DockLayoutWidget::handlePreview(const EventHandler& events) {
mPreviewArea = {};
mPreviewSide = NONE;
for (auto& sideWidget : mSideWidgets) {
if (sideWidget.widget) continue;
@ -322,3 +362,10 @@ void DockLayoutWidget::handlePreview(const EventHandler& events) {
}
}
}
auto DockLayoutWidget::getSideFromWidget(Widget* widget) -> Side {
for (auto& sideWidget : mSideWidgets) {
if (sideWidget.widget == widget) return sideWidget.side;
}
return DockLayoutWidget::NONE;
}

View file

@ -28,16 +28,20 @@ void FloatingWidget::process(const EventHandler& events) {
// triggerWidgetUpdate();
}
void FloatingWidget::adjustRect() {
auto area = getArea();
void FloatingWidget::pickRect() {
if (mIsFloating) {
auto area = getArea();
if (mIsResizing) {
area.size = mPointerCurrent + mHandleSize / 2.f;
} else if (mIsFloating) {
area.pos += mPointerCurrent - mPointerStart;
if (mIsResizing) {
area.size = mPointerCurrent + mHandleSize / 2.f;
} else if (mIsFloating) {
area.pos += mPointerCurrent - mPointerStart;
}
setArea(area);
} else {
Widget::pickRect();
}
setArea(area);
}
void FloatingWidget::draw(Canvas& canvas) {
@ -55,4 +59,8 @@ RectF FloatingWidget::resizeHandleRect() {
bool FloatingWidget::propagateEventsToChildren() const {
return !mIsFloating;
}
bool FloatingWidget::isFloating() const {
return mIsFloating;
}

View file

@ -12,6 +12,12 @@ void RootWidget::setRootWidget(Widget* widget) {
}
void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
if (mDebug) {
events->setEnableKeyEvents(true);
if (events->isPressed(InputID::K)) mDebugStopProcessing = !mDebugStopProcessing;
if (mDebugStopProcessing) return;
}
mScreenArea = screenArea;
mRoot->setArea(screenArea);
@ -38,6 +44,7 @@ void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
// check triggered widgets for removal
erase_if(mTriggeredWidgets, [this](auto widget) {
widget->updateAnimations();
if (mWidgetsToProcess.find(widget) == mWidgetsToProcess.end()) return false;
auto end = !widget->needsNextFrame();
if (end) {
@ -47,7 +54,7 @@ void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
});
}
bool RootWidget::needsUpdate() const { return !mTriggeredWidgets.empty(); }
bool RootWidget::needsUpdate() const { return !mTriggeredWidgets.empty() || mDebugRedrawAlways; }
void RootWidget::drawFrame(Canvas& canvas) {
// draw from top to bottom
@ -60,8 +67,44 @@ void RootWidget::drawFrame(Canvas& canvas) {
if (mDebug) {
drawDebug(canvas, mRoot, { 0, 0 }, 0);
ImGui::Text("Triggered Widgets: %i", (int) mTriggeredWidgets.size());
ImGui::Text("Widgets To Process: %i", (int) mWidgetsToProcess.size());
ImGui::Text("To Toggle processing press k");
ImGui::Checkbox("Stop processing", &mDebugStopProcessing);
ImGui::Checkbox("Force new frames", &mDebugRedrawAlways);
if (mInFocusWidget) {
ImGui::Text("Item under cursor %s", mInFocusWidget->mName.c_str());
if (ImGui::BeginListBox("##empty", { -FLT_MIN, 0 })) {
for (auto widget = mInFocusWidget; widget && widget != this; widget = widget->mParent) {
if (ImGui::CollapsingHeader(widget->mName.c_str())) {
ImGui::InputFloat2("min size", &widget->mMinSize.x);
ImGui::InputFloat2("max size", &widget->mMaxSize.x);
int sizePolicyX = int(widget->mSizePolicy.x);
int sizePolicyY = int(widget->mSizePolicy.y);
int layout = int(widget->mLayoutPolicy);
if (ImGui::Combo("Size Policy X", &sizePolicyX, "Passive\0Expand\0Contract\0")) {
widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY));
}
if (ImGui::Combo("Size Policy Y", &sizePolicyY, "Passive\0Expand\0Contract\0")) {
widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY));
}
if (ImGui::Combo("Layout", &layout, "Passive\0Vertical\0Horizontal\0")) {
widget->setLayoutPolicy(LayoutPolicy(layout));
}
}
}
ImGui::EndListBox();
}
}
}
}
@ -205,12 +248,13 @@ void RootWidget::updateWidget(Widget* widget) {
void RootWidget::adjustSizes(ActiveTreeNode* iter) {
if (!iter) return;
iter->widget->adjustRect();
iter->widget->adjustChildrenRect();
for (auto child : iter->children) {
adjustSizes(child);
}
iter->widget->pickRect();
iter->widget->adjustChildrenRect();
// iter->widget->pickRect();
}
void RootWidget::processActiveTree(ActiveTreeNode* iter, EventHandler& events) {
@ -257,6 +301,8 @@ void RootWidget::processFocusItems(EventHandler& events) {
events.setCursorOrigin(widgetGlobalPos[iter]);
widget->mLocalPoint = events.getPointer();
if (!eventsProcessed && widget->processesEvents()) {
events.setEnableKeyEvents(true);
@ -266,6 +312,7 @@ void RootWidget::processFocusItems(EventHandler& events) {
events.setEnableKeyEvents(false);
eventsProcessed = true;
} else {
widget->updateAnimations();
widget->process(events);
}

View file

@ -37,7 +37,7 @@ void ButtonWidget::setColor(const RGBA& in) {
}
void ButtonWidget::process(const EventHandler& eventHandler) {
if (getArea().isInside(eventHandler.getPointer())) {
if (getRelativeArea().isInside(eventHandler.getPointer())) {
if (eventHandler.isPressed(InputID::MOUSE1)) {
mAction();
}

View file

@ -1,5 +1,7 @@
#include "Widget.hpp"
#include <algorithm>
using namespace tp;
WidgetManagerInterface* Widget::getRoot() {
@ -17,26 +19,33 @@ void Widget::triggerWidgetUpdate() {
}
Widget::Widget() {
mArea.setTargetRect({ 10, 10, 100, 40, });
mArea.setTargetRect({ 100, 100, 10, 10, });
mArea.endAnimation();
}
void Widget::setArea(const RectF& area) {
mArea.setTargetRect(area);
if (mArea.getTargetRect() == area) return;
if (!mArea.shouldEndTransition()) {
triggerWidgetUpdate();
}
mArea.setTargetRect(area);
triggerWidgetUpdate();
}
RectF Widget::getArea() const {
return mArea.getCurrentRect();
}
RectF Widget::getAreaT() const {
return mArea.getTargetRect();
}
RectF Widget::getRelativeArea() const {
return { {}, mArea.getCurrentRect().size };
}
RectF Widget::getRelativeAreaT() const {
return { {}, mArea.getTargetRect().size };
}
void Widget::endAnimations() {
mArea.endAnimation();
}
@ -53,20 +62,11 @@ bool Widget::needsNextFrame() const {
return !mArea.shouldEndTransition();
}
void Widget::adjustRect() {
if (mChildren.empty()) return;
auto area = (*mChildren.begin())->getArea();
for (auto widget : mChildren) {
area.expand(widget->getArea());
}
}
void Widget::bringToFront() {
if (!mParent) return;
auto& order = mParent->mDepthOrder;
auto node = order.find(this);
DEBUG_ASSERT(node);
DEBUG_ASSERT(node)
order.detach(node);
order.pushFront(node);
}
@ -75,18 +75,11 @@ void Widget::bringToBack() {
if (!mParent) return;
auto& order = mParent->mDepthOrder;
auto node = order.find(this);
DEBUG_ASSERT(node);
DEBUG_ASSERT(node)
order.detach(node);
order.pushBack(node);
}
void Widget::adjustChildrenRect() {
auto area = RectF({}, getArea().size);
for (auto widget : mChildren) {
widget->setArea(area);
}
}
void Widget::mouseEnter() {
mInFocus = true;
}
@ -100,6 +93,15 @@ bool Widget::propagateEventsToChildren() const {
}
void Widget::addChild(Widget* child) {
if (auto node = mDepthOrder.find(child)) {
node->data->bringToFront();
return;
}
if (child->mParent) {
child->mParent->removeChild(child);
}
mChildren.push_back(child);
mDepthOrder.pushBack(child);
@ -108,3 +110,183 @@ void Widget::addChild(Widget* child) {
triggerWidgetUpdate();
child->triggerWidgetUpdate();
}
void Widget::removeChild(Widget* child) {
auto node = mDepthOrder.find(child);
if (!node) return;
mDepthOrder.removeNode(node);
mChildren.erase(std::remove(mChildren.begin(), mChildren.end(), child), mChildren.end());
triggerWidgetUpdate();
child->triggerWidgetUpdate();
}
void Widget::setSizePolicy(tp::Widget::SizePolicy x, tp::Widget::SizePolicy y) {
mSizePolicy = { x, y };
triggerWidgetUpdate();
}
void Widget::setLayoutPolicy(LayoutPolicy layoutPolicy) {
mLayoutPolicy = layoutPolicy;
triggerWidgetUpdate();
}
const Vec2F& Widget::getMinSize() { return mMinSize; }
void Widget::setMinSize(const Vec2F& size) {
mMinSize = size;
triggerWidgetUpdate();
}
void Widget::pickRect() {
auto current = getAreaT();
auto children = getChildrenEnclosure();
auto parent = getParentEnclosure();
auto rangeX = pickRange(current.getRangeX(), children.getRangeX(), parent.getRangeX(), false);
auto rangeY = pickRange(current.getRangeY(), children.getRangeY(), parent.getRangeY(), true);
auto newArea = RectF(rangeX, rangeY);
for (auto child : mChildren) {
child->setArea(child->getAreaT().move(current.pos - newArea.pos));
}
setArea(newArea);
}
void Widget::clampRect() {
auto current = getAreaT();
auto children = getChildrenEnclosure();
auto parent = getParentEnclosure();
auto rangeX = clampRange(current.getRangeX(), children.getRangeX(), parent.getRangeX(), false);
auto rangeY = clampRange(current.getRangeY(), children.getRangeY(), parent.getRangeY(), true);
setArea(RectF(rangeX, rangeY));
}
void Widget::adjustChildrenRect() {
if (mChildren.empty()) return;
switch (mLayoutPolicy) {
case LayoutPolicy::Passive: break;
case LayoutPolicy::Vertically: adjustLayout(true); break;
case LayoutPolicy::Horizontally: adjustLayout(false); break;
}
}
halnf Widget::changeChildSize(tp::Widget* widget, halnf diff, bool vertical) {
auto prevSize = widget->getAreaT().size[vertical];
{
auto area = widget->getAreaT();
area.size[vertical] += diff;
widget->setArea(area);
widget->clampRect();
}
auto newSize = widget->getAreaT().size[vertical];
return newSize - prevSize;
}
void Widget::adjustLayout(bool vertical) {
std::vector<std::pair<Widget*, bool>> contributors;
Vec2F contentSize = { 0, 0 };
Vec2F availableSize = getRelativeAreaT().size;
for (auto child : mChildren) {
contentSize += child->getAreaT().size;
if (child->mSizePolicy[vertical] == SizePolicy::Expand) {
contributors.emplace_back( child, true );
}
}
auto diff = availableSize - contentSize;
// expand or contract as much as possible
while (!contributors.empty() && (diff[vertical] != 0)) {
auto quota = diff / (halnf) contributors.size();
for (auto& contributor : contributors) {
if (!contributor.second) continue;
auto contribution = changeChildSize(contributor.first, quota[vertical], vertical);
if (contribution == 0) {
contributor.second = false;
}
diff[vertical] -= contribution;
}
contributors.erase(
std::remove_if(contributors.begin(), contributors.end(), [](auto node) { return !node.second; }),
contributors.end()
);
}
// arrange
halnf iterPos = 0;
for (auto child : mChildren) {
auto area = child->getAreaT();
area.pos[vertical] = iterPos;
iterPos += area.size[vertical];
child->setArea(area);
}
}
RangeF Widget::pickRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical) {
RangeF out;
switch (mSizePolicy[vertical]) {
case SizePolicy::Passive: out = current; break;
case SizePolicy::Expand: out = parent; break;
case SizePolicy::Contract: out = children; break;
}
out = clampRange(out, children, parent, vertical);
return out;
}
RangeF Widget::clampRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical) {
auto out = current;
if (!mChildren.empty()) {
auto clampedChild = children;
clampedChild.clamp(parent);
out.clamp(clampedChild, parent);
} else {
out.clamp(parent);
}
// clamp min max sizes
auto len = clamp(current.size(), mMinSize[vertical], mMaxSize[vertical]);
if (len != current.size()) {
out.resizeFromCenter(len);
}
return out;
}
RectF Widget::getChildrenEnclosure() {
RectF out;
if (mChildren.empty()) {
out = { getAreaT().center(), { 0, 0 } };
} else {
for (auto child : mChildren) {
out.expand(child->getAreaT());
}
out.pos += getAreaT().pos;
}
return out;
}
RectF Widget::getParentEnclosure() {
DEBUG_ASSERT(mParent);
if (!mParent) return { { 0, 0 }, mMaxSize };
return mParent->getRelativeAreaT();
}

View file

@ -27,13 +27,15 @@ namespace tp {
RectF previewHandleArea = {};
ResizeHandle resizeHandle;
RectF areaBeforeDocking = {};
};
public:
DockLayoutWidget();
public:
void adjustRect() override {}
void pickRect() override {}
void adjustChildrenRect() override {}
void process(const EventHandler& events) override;
@ -47,10 +49,11 @@ namespace tp {
void setCenterWidget(Widget* widget);
void dockWidget(Widget* widget, Side side);
void undockWidget(Side side);
void undockWidget(Side side, bool restoreArea = true);
void toggleWidgetVisibility(Side side);
private:
Side getSideFromWidget(Widget*);
void calculateSideAreas();
void calculateResizeHandles();
void handleResizeEvents(const EventHandler& events);
@ -86,9 +89,9 @@ namespace tp {
RGBA mResizeHandleColorHovered = RGBA(0.3, 0.3, 0.3, 1);
RGBA mResizeHandleColorActive = RGBA(0.6, 0.6, 0.6, 1);
RGBA mBackgroundColor = RGBA(0, 0, 0, 1);
RGBA mPreviewColor = RGBA(0.6, 0.6, 0.6, 1);
RGBA mPreviewColor = RGBA(0.6, 0.6, 0.6, 0.2);
public:
bool mPreview = false;
Widget* mPreviewWidget = nullptr;
};
}

View file

@ -12,7 +12,7 @@ namespace tp {
void process(const EventHandler& events) override;
void adjustRect() override;
void pickRect() override;
void draw(Canvas& canvas) override;
@ -20,6 +20,8 @@ namespace tp {
[[nodiscard]] bool propagateEventsToChildren() const override;
[[nodiscard]] bool isFloating() const;
private:
bool mIsFloating = false;
bool mIsResizing = false;

View file

@ -11,7 +11,7 @@ namespace tp {
// canvas.rect(getRelativeArea(), RGBA(0, 0, 0, 1));
}
void adjustRect() override {
void pickRect() override {
// do nothing
}

View file

@ -51,5 +51,7 @@ namespace tp {
Widget* mInFocusWidget = nullptr;
bool mDebug = true;
bool mDebugStopProcessing = false;
bool mDebugRedrawAlways = false;
};
}

View file

@ -13,6 +13,19 @@ namespace tp {
class Widget {
friend class RootWidget;
public:
enum class SizePolicy {
Passive,
Expand,
Contract,
};
enum class LayoutPolicy {
Passive,
Vertically,
Horizontally,
};
public:
Widget();
virtual ~Widget() = default;
@ -23,6 +36,7 @@ namespace tp {
}
void addChild(Widget* child);
void removeChild(Widget* child);
WidgetManagerInterface* getRoot();
@ -31,18 +45,33 @@ namespace tp {
void bringToFront();
void bringToBack();
void setSizePolicy(SizePolicy x, SizePolicy y);
void setLayoutPolicy(LayoutPolicy layoutPolicy);
const Vec2F& getMinSize();
void setMinSize(const Vec2F& size);
public:
virtual void process(const EventHandler& events) {}
virtual void draw(Canvas& canvas) { canvas.rect(mArea.getCurrentRect(), RGBA(1.f), 10); }
virtual void draw(Canvas& canvas) {}
virtual void drawOverlay(Canvas& canvas) {}
virtual void mouseEnter();
virtual void mouseLeave();
// size policies
virtual void adjustRect();
virtual void pickRect();
virtual void clampRect();
virtual void adjustChildrenRect();
// resizing
RangeF pickRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical);
RangeF clampRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical);
RectF getChildrenEnclosure();
RectF getParentEnclosure();
void adjustLayout(bool vertical);
halnf changeChildSize(Widget*, halnf diff, bool vertical);
[[nodiscard]] virtual bool processesEvents() const;
[[nodiscard]] virtual bool propagateEventsToChildren() const;
@ -53,7 +82,11 @@ namespace tp {
public:
[[nodiscard]] RectF getArea() const;
[[nodiscard]] RectF getAreaT() const;
[[nodiscard]] RectF getRelativeArea() const;
[[nodiscard]] RectF getRelativeAreaT() const;
void setArea(const RectF& area);
protected:
@ -65,6 +98,12 @@ namespace tp {
// relative to the parent
SpringRect mArea;
// resizing
LayoutPolicy mLayoutPolicy = LayoutPolicy::Passive;
Vec2<SizePolicy> mSizePolicy = { SizePolicy::Passive, SizePolicy::Passive };
Vec2F mMinSize = { 50, 50 };
Vec2F mMaxSize = { FLT_MAX / 2, FLT_MAX / 2 };
bool mInFocus = false;
// debug