Initial size handling
This commit is contained in:
parent
e028ad1a41
commit
b2c393d23a
28 changed files with 543 additions and 135 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ SUITE(AvlTree) {
|
|||
|
||||
Item buff[size];
|
||||
|
||||
for (auto i : Range(size)) {
|
||||
for (auto i : IterRange(size)) {
|
||||
buff[i].data.setVal(i);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
39
Math/public/Range.hpp
Normal 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>;
|
||||
}
|
||||
|
|
@ -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; }
|
||||
|
||||
|
|
|
|||
|
|
@ -226,8 +226,8 @@ namespace tp {
|
|||
Type y;
|
||||
|
||||
Vec() :
|
||||
x(0),
|
||||
y(0) {}
|
||||
x{},
|
||||
y{} {}
|
||||
|
||||
// Initialization
|
||||
template <typename TypeIn>
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ void FloatingWidget::process(const EventHandler& events) {
|
|||
// triggerWidgetUpdate();
|
||||
}
|
||||
|
||||
void FloatingWidget::adjustRect() {
|
||||
void FloatingWidget::pickRect() {
|
||||
if (mIsFloating) {
|
||||
auto area = getArea();
|
||||
|
||||
if (mIsResizing) {
|
||||
|
|
@ -38,6 +39,9 @@ void FloatingWidget::adjustRect() {
|
|||
}
|
||||
|
||||
setArea(area);
|
||||
} else {
|
||||
Widget::pickRect();
|
||||
}
|
||||
}
|
||||
|
||||
void FloatingWidget::draw(Canvas& canvas) {
|
||||
|
|
@ -56,3 +60,7 @@ RectF FloatingWidget::resizeHandleRect() {
|
|||
bool FloatingWidget::propagateEventsToChildren() const {
|
||||
return !mIsFloating;
|
||||
}
|
||||
|
||||
bool FloatingWidget::isFloating() const {
|
||||
return mIsFloating;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace tp {
|
|||
// canvas.rect(getRelativeArea(), RGBA(0, 0, 0, 1));
|
||||
}
|
||||
|
||||
void adjustRect() override {
|
||||
void pickRect() override {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,5 +51,7 @@ namespace tp {
|
|||
Widget* mInFocusWidget = nullptr;
|
||||
|
||||
bool mDebug = true;
|
||||
bool mDebugStopProcessing = false;
|
||||
bool mDebugRedrawAlways = false;
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue