diff --git a/genlib/bspnode.cpp b/genlib/bspnode.cpp deleted file mode 100644 index f380446b..00000000 --- a/genlib/bspnode.cpp +++ /dev/null @@ -1,151 +0,0 @@ -// genlib - a component of the depthmapX - spatial network analysis platform -// Copyright (C) 2000-2010 University College London, Alasdair Turner -// Copyright (C) 2011-2012, Tasos Varoudis - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "bspnode.h" - -// Binary Space Partition - -void BSPNode::make(Communicator *communicator, time_t atime, const prefvec& lines, BSPNode *par) -{ - m_count++; - - if (communicator) - { - if (communicator->IsCancelled()) { - throw Communicator::CancelledException(); - } - if (qtimer( atime, 500 )) { - communicator->CommPostMessage( Communicator::CURRENT_RECORD, m_count ); - } - } - - prefvec leftlines; - prefvec rightlines; - - parent = par; - - // for optimization of the tree (this reduced a six-minute gen time to a 38 second gen time) - size_t chosen = paftl::npos; - if (lines.size() > 3) { - size_t i; - Point2f midpoint; - for (i = 0; i < lines.size(); i++) { - midpoint += lines[i].line.start() + lines[i].line.end(); - } - midpoint /= 2.0 * lines.size(); - bool ver = true; - if (par && par->line.height() > par->line.width()) { - ver = false; - } - double chosendist = -1.0; - for (i = 0; i < lines.size(); i++) { - const Line& line = lines[i].line; - if (ver) { - if (line.height() > line.width() && (chosen == paftl::npos || dist(line.midpoint(),midpoint) < chosendist)) { - chosen = i; - chosendist = dist(line.midpoint(),midpoint); - } - } - else { - if (line.width() > line.height() && (chosen == paftl::npos || dist(line.midpoint(),midpoint) < chosendist)) { - chosen = i; - chosendist = dist(line.midpoint(),midpoint); - } - } - } - // argh... and again... there weren't any hoz / ver: - if (chosen == paftl::npos) { - for (size_t i = 0; i < lines.size(); i++) { - if (chosen == paftl::npos || dist(lines[i].line.midpoint(),midpoint) < chosendist) { - chosen = i; - chosendist = dist(lines[i].line.midpoint(),midpoint); - } - } - } - } - else { - chosen = pafrand() % lines.size(); - } - - line = lines[chosen].line; - m_tag = lines[chosen].tag; - - Point2f v0 = line.end() - line.start(); - v0.normalise(); - - for (size_t i = 0; i < lines.size(); i++) { - if (i == chosen) { - continue; - } - const Line& testline = lines[i].line; - int tag = lines[i].tag; - Point2f v1 = testline.start()-line.start(); - v1.normalise(); - Point2f v2 = testline.end()-line.start(); - v2.normalise(); - // should use approxeq here: - double a = testline.start() == line.start() ? 0 : det(v0,v1); - double b = testline.end() == line.start() ? 0 : det(v0,v2); - // note sure what to do if a == 0 and b == 0 (i.e., it's parallel... this test at least ensures on the line is one or the other side) - if (a >= 0 && b >= 0) { - leftlines.push_back(TaggedLine(testline,tag)); - } - else if (a <= 0 && b <= 0) { - rightlines.push_back(TaggedLine(testline,tag)); - } - else { - Point2f p = intersection_point(line,testline); - Line x = Line(testline.start(),p); - Line y = Line(p,testline.end()); - if (a >= 0) { - if (x.length() > 0.0) // should use a tolerance here too - leftlines.push_back(TaggedLine(x,tag)); - if (y.length() > 0.0) // should use a tolerance here too - rightlines.push_back(TaggedLine(y,tag)); - } - else { - if (x.length() > 0.0) // should use a tolerance here too - rightlines.push_back(TaggedLine(x,tag)); - if (y.length() > 0.0) // should use a tolerance here too - leftlines.push_back(TaggedLine(y,tag)); - } - } - } - - if (leftlines.size()) { - left = new BSPNode(); - left->make(communicator,atime,leftlines,this); - } - if (rightlines.size()) { - right = new BSPNode(); - right->make(communicator,atime,rightlines,this); - } -} - -int BSPNode::classify(const Point2f& p) -{ - Point2f v0 = line.end() - line.start(); - v0.normalise(); - Point2f v1 = p - line.start(); - v1.normalise(); - if (det(v0,v1) >= 0) { - return BSPLEFT; - } - else { - return BSPRIGHT; - } -} diff --git a/genlib/bspnode.h b/genlib/bspnode.h deleted file mode 100644 index 40a1421e..00000000 --- a/genlib/bspnode.h +++ /dev/null @@ -1,49 +0,0 @@ -// genlib - a component of the depthmapX - spatial network analysis platform -// Copyright (C) 2000-2010 University College London, Alasdair Turner -// Copyright (C) 2011-2012, Tasos Varoudis - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#pragma once - -#include "genlib/paftl.h" -#include "p2dpoly.h" - -// Binary Space Partition - -struct BSPNode -{ -public: - enum { BSPLEFT, BSPRIGHT }; - BSPNode *left; - BSPNode *right; - BSPNode *parent; - Line line; - int m_tag; - int m_count; - // -public: - BSPNode() - { left = NULL; right = NULL; parent = NULL; m_count = 0; m_tag = -1; } - virtual ~BSPNode() - { if (left) delete left; left = NULL; if (right) delete right; right = NULL; } - // - bool isLeaf() { - return left == NULL && right == NULL; - } - void make(Communicator *communicator, time_t atime, const prefvec& lines, BSPNode *par); - int classify(const Point2f& p); - const Line& getLine() const { return line; } - const int getTag() const { return m_tag; } -}; diff --git a/genlib/bsptree.cpp b/genlib/bsptree.cpp new file mode 100644 index 00000000..611330b2 --- /dev/null +++ b/genlib/bsptree.cpp @@ -0,0 +1,191 @@ +// genlib - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010 University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "bsptree.h" +#include + +// Binary Space Partition + +/* Takes a set of lines and creates a binary-space-partition tree by starting from a + * root node, setting its left and right nodes and recursively doing the same process + * over those. Through this process the set of lines is split in two (one set for each + * left and right nodes) and those are split and passed again further down the recursion. + * While the original implementation was actually recursive it was hitting the recursion + * limit when the input was a large number of lines that fell on the same side (i.e. an + * arc divided in 500 pieces). It has been refactored here to an iterative solution, where + * the current node (left or right) is pushed to a stack along with the relevant set of lines. + */ + +void BSPTree::make(Communicator *communicator, time_t atime, const std::vector& lines, BSPNode* root) +{ + + typedef std::pair, std::vector > TagLineVecPair; + + std::stack nodeStack; + std::stack lineStack; + + nodeStack.push(root); + lineStack.push(makeLines(communicator, atime, lines, root)); + + int progress = 0; + while(!nodeStack.empty()) { + progress++; // might need to increase by 2 because it was one for each left/right in the previous iteration + + if (communicator) + { + if (communicator->IsCancelled()) { + throw Communicator::CancelledException(); + } + if (qtimer( atime, 500 )) { + communicator->CommPostMessage( Communicator::CURRENT_RECORD, progress ); + } + } + BSPNode *currNode = nodeStack.top(); + nodeStack.pop(); + TagLineVecPair currLines = lineStack.top(); + lineStack.pop(); + + if (!currLines.first.empty()) { + currNode->m_left = new BSPNode(currNode); + nodeStack.push(currNode->m_left); + lineStack.push(makeLines(communicator,atime,currLines.first,currNode->m_left)); + } + if (!currLines.second.empty()) { + currNode->m_right = new BSPNode(currNode); + nodeStack.push(currNode->m_right); + lineStack.push(makeLines(communicator,atime,currLines.second,currNode->m_right)); + } + } +} + +/* Finds the midpoint from all the lines given and returns the index of the line + * closest to it. + */ + +int BSPTree::pickMidpointLine(const std::vector &lines, BSPNode *par) { + int chosen = -1; + size_t i; + Point2f midpoint; + for (i = 0; i < lines.size(); i++) { + midpoint += lines[i].line.start() + lines[i].line.end(); + } + midpoint /= 2.0 * lines.size(); + bool ver = true; + if (par && par->getLine().height() > par->getLine().width()) { + ver = false; + } + double chosendist = -1.0; + for (i = 0; i < lines.size(); i++) { + const Line& line = lines[i].line; + if (ver) { + if (line.height() > line.width() && (chosen == -1 || dist(line.midpoint(),midpoint) < chosendist)) { + chosen = i; + chosendist = dist(line.midpoint(),midpoint); + } + } + else { + if (line.width() > line.height() && (chosen == -1 || dist(line.midpoint(),midpoint) < chosendist)) { + chosen = i; + chosendist = dist(line.midpoint(),midpoint); + } + } + } + // argh... and again... there weren't any hoz / ver: + if (chosen == -1) { + for (size_t i = 0; i < lines.size(); i++) { + if (chosen == -1 || dist(lines[i].line.midpoint(),midpoint) < chosendist) { + chosen = i; + chosendist = dist(lines[i].line.midpoint(),midpoint); + } + } + } + return chosen; +} + +/* Breaks a set of lines in two (left-right). First chooses a line closest to the midpoint + * of the set ("chosen") and then classifies lines left or right depending on whether they + * lie clockwise or anti-clockwise of the chosen one (with chosen start as centre, angles + * from the chosen end up to 180 are clockwise, down to -180 anti-clockwise). Lines that cross + * from one side of the chosen to the other are split in two and each part goes to the relevant set. + */ + +std::pair, std::vector > BSPTree::makeLines(Communicator *communicator, + time_t atime, + const std::vector &lines, + BSPNode *base) +{ + std::vector leftlines; + std::vector rightlines; + + // for optimization of the tree (this reduced a six-minute gen time to a 38 second gen time) + int chosen = -1; + if (lines.size() > 3) { + chosen = BSPTree::pickMidpointLine(lines, base->m_parent); + } + else { + chosen = pafrand() % lines.size(); + } + + Line chosenLine = lines[chosen].line; + int chosenTag = lines[chosen].tag; + base->setLine(chosenLine); + base->setTag(chosenTag); + + Point2f v0 = chosenLine.end() - chosenLine.start(); + v0.normalise(); + + for (size_t i = 0; i < lines.size(); i++) { + if (i == chosen) { + continue; + } + const Line& testline = lines[i].line; + int tag = lines[i].tag; + Point2f v1 = testline.start()-chosenLine.start(); + v1.normalise(); + Point2f v2 = testline.end()-chosenLine.start(); + v2.normalise(); + // should use approxeq here: + double a = testline.start() == chosenLine.start() ? 0 : det(v0,v1); + double b = testline.end() == chosenLine.start() ? 0 : det(v0,v2); + // note sure what to do if a == 0 and b == 0 (i.e., it's parallel... this test at least ensures on the line is one or the other side) + if (a >= 0 && b >= 0) { + leftlines.push_back(TaggedLine(testline,tag)); + } + else if (a <= 0 && b <= 0) { + rightlines.push_back(TaggedLine(testline,tag)); + } + else { + Point2f p = intersection_point(chosenLine,testline); + Line x = Line(testline.start(),p); + Line y = Line(p,testline.end()); + if (a >= 0) { + if (x.length() > 0.0) // should use a tolerance here too + leftlines.push_back(TaggedLine(x,tag)); + if (y.length() > 0.0) // should use a tolerance here too + rightlines.push_back(TaggedLine(y,tag)); + } + else { + if (x.length() > 0.0) // should use a tolerance here too + rightlines.push_back(TaggedLine(x,tag)); + if (y.length() > 0.0) // should use a tolerance here too + leftlines.push_back(TaggedLine(y,tag)); + } + } + } + + return std::make_pair(leftlines, rightlines); +} diff --git a/genlib/bsptree.h b/genlib/bsptree.h new file mode 100644 index 00000000..379bcd16 --- /dev/null +++ b/genlib/bsptree.h @@ -0,0 +1,70 @@ +// genlib - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010 University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "p2dpoly.h" + +// Binary Space Partition + +struct BSPNode +{ + +private: + Line m_line; + int m_tag; + +public: + enum { BSPLEFT, BSPRIGHT }; + BSPNode *m_left; + BSPNode *m_right; + BSPNode *m_parent; + + BSPNode(BSPNode *parent = NULL) + { m_left = NULL; m_right = NULL; m_parent = parent; m_tag = -1; } + virtual ~BSPNode() + { if (m_left) delete m_left; m_left = NULL; if (m_right) delete m_right; m_right = NULL; } + // + bool isLeaf() { + return m_left == NULL && m_right == NULL; + } + int classify(const Point2f& p) { + Point2f v0 = m_line.end() - m_line.start(); + v0.normalise(); + Point2f v1 = p - m_line.start(); + v1.normalise(); + if (det(v0,v1) >= 0) { + return BSPLEFT; + } + else { + return BSPRIGHT; + } + } + const Line& getLine() const { return m_line; } + void setLine(const Line& line) { m_line = line; } + int getTag() const { return m_tag; } + void setTag(const int tag) { m_tag = tag; } +}; + +namespace BSPTree { +void make(Communicator *communicator, time_t atime, const std::vector &lines, BSPNode *root); +int pickMidpointLine(const std::vector &lines, BSPNode *par); +std::pair, std::vector > makeLines(Communicator *communicator, + time_t atime, + const std::vector& lines, + BSPNode *base); +} diff --git a/genlib/genlib.pro b/genlib/genlib.pro index 63bf69ff..fbfbbc97 100644 --- a/genlib/genlib.pro +++ b/genlib/genlib.pro @@ -31,7 +31,7 @@ SOURCES += \ pafmath.cpp \ xmlparse.cpp \ stringutils.cpp \ - bspnode.cpp + bsptree.cpp HEADERS += \ comm.h \ @@ -46,4 +46,4 @@ HEADERS += \ stringutils.h \ containerutils.h \ linreg.h \ - bspnode.h + bsptree.h diff --git a/genlibTest/genlibTest.pro b/genlibTest/genlibTest.pro index 13ce0693..47406033 100644 --- a/genlibTest/genlibTest.pro +++ b/genlibTest/genlibTest.pro @@ -8,7 +8,8 @@ INCLUDEPATH += ../ThirdParty/Catch SOURCES += convertertest.cpp \ main.cpp \ dxfptest.cpp \ - teststringutils.cpp + teststringutils.cpp \ + testbspnode.cpp HEADERS += diff --git a/genlibTest/testbspnode.cpp b/genlibTest/testbspnode.cpp new file mode 100644 index 00000000..3e8b34bb --- /dev/null +++ b/genlibTest/testbspnode.cpp @@ -0,0 +1,219 @@ +// Copyright (C) 2017 Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "catch.hpp" +#include "genlib/comm.h" +#include "genlib/p2dpoly.h" +#include "genlib/bsptree.h" + +TEST_CASE("BSPTree::pickMidpointLine") +{ + std::vector lines; + lines.push_back(TaggedLine(Line(Point2f(1, 2), Point2f(2, 2)), 0)); + lines.push_back(TaggedLine(Line(Point2f(2, 2), Point2f(3, 2)), 0)); + lines.push_back(TaggedLine(Line(Point2f(3, 2), Point2f(4, 2)), 0)); + + BSPNode node; + + REQUIRE(BSPTree::pickMidpointLine(lines, 0) == 1); + + SECTION("Additional lines") { + lines.push_back(TaggedLine(Line(Point2f(4, 2), Point2f(5, 2)), 0)); + REQUIRE(BSPTree::pickMidpointLine(lines, 0) == 1); + + lines.push_back(TaggedLine(Line(Point2f(5, 1), Point2f(6, 1)), 0)); + REQUIRE(BSPTree::pickMidpointLine(lines, 0) == 2); + + // the only line with height > width becomes chosen + lines.push_back(TaggedLine(Line(Point2f(15, 4), Point2f(15, 0)), 0)); + REQUIRE(BSPTree::pickMidpointLine(lines, 0) == 5); + } + SECTION("rotated middle") { + + // height > width, rotated, close to midpoint + lines.push_back(TaggedLine(Line(Point2f(4.5, 1), Point2f(4.5, 3)), 0)); + + lines.push_back(TaggedLine(Line(Point2f(5, 2), Point2f(6, 2)), 0)); + lines.push_back(TaggedLine(Line(Point2f(6, 2), Point2f(7, 2)), 0)); + + // height > width, rotated, not close to midpoint + lines.push_back(TaggedLine(Line(Point2f(6.5, 1), Point2f(6.5, 3)), 0)); + + REQUIRE(BSPTree::pickMidpointLine(lines, 0) == 3); + } +} + +void compareLines(Line l1, Line l2, float EPSILON) { + REQUIRE(l1.start().x == Approx(l2.start().x).epsilon(EPSILON)); + REQUIRE(l1.start().y == Approx(l2.start().y).epsilon(EPSILON)); + REQUIRE(l1.end().x == Approx(l2.end().x).epsilon(EPSILON)); + REQUIRE(l1.end().y == Approx(l2.end().y).epsilon(EPSILON)); +} + +TEST_CASE("BSPTree::makeLines") +{ + const float EPSILON = 0.001; + typedef std::pair, std::vector > TagLineVecPair; + + std::vector lines; + lines.push_back(TaggedLine(Line(Point2f(1, 2), Point2f(2, 2)), 0)); + lines.push_back(TaggedLine(Line(Point2f(2, 2), Point2f(3, 2)), 0)); + lines.push_back(TaggedLine(Line(Point2f(3, 2), Point2f(4, 2)), 0)); + lines.push_back(TaggedLine(Line(Point2f(4, 2), Point2f(5, 2)), 0)); + + std::unique_ptr node(new BSPNode()); + + TagLineVecPair result = BSPTree::makeLines(0, 0, lines, node.get()); + + REQUIRE(result.first.size() == 3); + REQUIRE(result.second.size() == 0); + + compareLines(result.first[0].line, lines[0].line, EPSILON); + compareLines(result.first[1].line, lines[2].line, EPSILON); + compareLines(result.first[2].line, lines[3].line, EPSILON); + + + SECTION("One on the right") + { + lines.push_back(TaggedLine(Line(Point2f(5, 1), Point2f(6, 1)), 0)); + + result = BSPTree::makeLines(0, 0, lines, node.get()); + + REQUIRE(result.first.size() == 3); + REQUIRE(result.second.size() == 1); + + compareLines(result.second[0].line, lines[4].line, EPSILON); + } + SECTION("One line with height > width becomes chosen") + { + // height > width, rotated, not close to midpoint + lines.push_back(TaggedLine(Line(Point2f(5.5, 1), Point2f(5.5, 3)), 0)); + + lines.push_back(TaggedLine(Line(Point2f(6, 2), Point2f(7, 2)), 0)); + + result = BSPTree::makeLines(0, 0, lines, node.get()); + + REQUIRE(result.first.size() == 4); + REQUIRE(result.second.size() == 1); + + compareLines(result.first[0].line, lines[0].line, EPSILON); + compareLines(result.first[1].line, lines[1].line, EPSILON); + compareLines(result.first[2].line, lines[2].line, EPSILON); + compareLines(result.first[3].line, lines[3].line, EPSILON); + compareLines(result.second[0].line, lines[5].line, EPSILON); + } + + SECTION("One broken between") + { + // height > width, rotated, close to midpoint + lines.push_back(TaggedLine(Line(Point2f(5.5, 1), Point2f(5.5, 3)), 0)); + + lines.push_back(TaggedLine(Line(Point2f(6, 2), Point2f(7, 2)), 0)); + lines.push_back(TaggedLine(Line(Point2f(7, 2), Point2f(8, 2)), 0)); + lines.push_back(TaggedLine(Line(Point2f(8, 2), Point2f(9, 2)), 0)); + lines.push_back(TaggedLine(Line(Point2f(9, 2), Point2f(10, 2)), 0)); + + // line with two points at different sides of chosen + lines.push_back(TaggedLine(Line(Point2f(3, -2), Point2f(6, -2)), 0)); + + result = BSPTree::makeLines(0, 0, lines, node.get()); + + // adds one on each side + REQUIRE(result.first.size() == 5); + REQUIRE(result.second.size() == 5); + + compareLines(result.first[0].line, lines[0].line, EPSILON); + compareLines(result.first[1].line, lines[1].line, EPSILON); + compareLines(result.first[2].line, lines[2].line, EPSILON); + compareLines(result.first[3].line, lines[3].line, EPSILON); + + compareLines(result.second[0].line, lines[5].line, EPSILON); + compareLines(result.second[1].line, lines[6].line, EPSILON); + compareLines(result.second[2].line, lines[7].line, EPSILON); + compareLines(result.second[3].line, lines[8].line, EPSILON); + + compareLines(result.first[4].line, Line(Point2f(3, -2), Point2f(5.5, -2)), EPSILON); + compareLines(result.second[4].line, Line(Point2f(5.5, -2), Point2f(6, -2)), EPSILON); + } +} + +TEST_CASE("BSPTree::make (all horizontal lines)", "all-left tree") +{ + const float EPSILON = 0.001; + + std::vector lines; + lines.push_back(TaggedLine(Line(Point2f(1, 2), Point2f(2, 2)), 0)); + lines.push_back(TaggedLine(Line(Point2f(2, 2), Point2f(3, 2)), 0)); + lines.push_back(TaggedLine(Line(Point2f(3, 2), Point2f(4, 2)), 0)); + lines.push_back(TaggedLine(Line(Point2f(4, 2), Point2f(5, 2)), 0)); + + std::unique_ptr node(new BSPNode()); + + BSPTree::make(0, 0, lines, node.get()); + + compareLines(node->getLine(), lines[1].line, EPSILON); + + REQUIRE(node->m_left != NULL); + REQUIRE(node->m_right == NULL); + + compareLines(node->m_left->getLine(), lines[2].line, EPSILON); + + REQUIRE(node->m_left->m_left != NULL); + REQUIRE(node->m_left->m_right == NULL); + + compareLines(node->m_left->m_left->getLine(), lines[3].line, EPSILON); + + REQUIRE(node->m_left->m_left->m_left != NULL); + REQUIRE(node->m_left->m_left->m_right == NULL); + + compareLines(node->m_left->m_left->m_left->getLine(), lines[0].line, EPSILON); + + REQUIRE(node->m_left->m_left->m_left->m_left == NULL); + REQUIRE(node->m_left->m_left->m_left->m_right == NULL); +} + +TEST_CASE("BSPTree::make (all vertical lines)", "split tree") +{ + const float EPSILON = 0.001; + + std::vector lines; + lines.push_back(TaggedLine(Line(Point2f(1.5, 1), Point2f(1.5, 3)), 0)); + lines.push_back(TaggedLine(Line(Point2f(2.5, 1), Point2f(2.5, 3)), 0)); + lines.push_back(TaggedLine(Line(Point2f(3.5, 1), Point2f(3.5, 3)), 0)); + lines.push_back(TaggedLine(Line(Point2f(4.5, 1), Point2f(4.5, 3)), 0)); + + std::unique_ptr node(new BSPNode()); + + BSPTree::make(0, 0, lines, node.get()); + + compareLines(node->getLine(), lines[1].line, EPSILON); + + REQUIRE(node->m_left != NULL); + REQUIRE(node->m_right != NULL); + + compareLines(node->m_left->getLine(), lines[0].line, EPSILON); + compareLines(node->m_right->getLine(), lines[3].line, EPSILON); + + REQUIRE(node->m_left->m_left == NULL); + REQUIRE(node->m_left->m_right == NULL); + + REQUIRE(node->m_right->m_left != NULL); + REQUIRE(node->m_right->m_right == NULL); + + compareLines(node->m_right->m_left->getLine(), lines[2].line, EPSILON); + + REQUIRE(node->m_right->m_left->m_left == NULL); + REQUIRE(node->m_right->m_left->m_right == NULL); +} diff --git a/salalib/isovist.cpp b/salalib/isovist.cpp index 2e7caa51..3c6fa833 100644 --- a/salalib/isovist.cpp +++ b/salalib/isovist.cpp @@ -148,18 +148,18 @@ void Isovist::make(BSPNode *here) if (m_gaps.size()) { int which = here->classify(m_centre); if (which == BSPNode::BSPLEFT) { - if (here->left) - make(here->left); - drawnode(here->line,here->m_tag); - if (here->right) - make(here->right); + if (here->m_left) + make(here->m_left); + drawnode(here->getLine(),here->getTag()); + if (here->m_right) + make(here->m_right); } else { - if (here->right) - make(here->right); - drawnode(here->line,here->m_tag); - if (here->left) - make(here->left); + if (here->m_right) + make(here->m_right); + drawnode(here->getLine(),here->getTag()); + if (here->m_left) + make(here->m_left); } } } diff --git a/salalib/isovist.h b/salalib/isovist.h index c0deb965..0939a0f0 100644 --- a/salalib/isovist.h +++ b/salalib/isovist.h @@ -21,7 +21,7 @@ #ifndef __ISOVIST_H__ #define __ISOVIST_H__ -#include "genlib/bspnode.h" +#include "genlib/bsptree.h" // this is very much like sparksieve: diff --git a/salalib/mgraph.cpp b/salalib/mgraph.cpp index 5bee3ecc..618f762c 100644 --- a/salalib/mgraph.cpp +++ b/salalib/mgraph.cpp @@ -646,7 +646,7 @@ bool MetaGraph::makeBSPtree(Communicator *communicator) return true; } - prefvec partitionlines; + std::vector partitionlines; for (size_t i = 0; i < SuperSpacePixel::size(); i++) { for (size_t j = 0; j < SuperSpacePixel::at(i).size(); j++) { // chooses the first editable layer it can find: @@ -694,7 +694,7 @@ bool MetaGraph::makeBSPtree(Communicator *communicator) qtimer( atime, 0 ); try { - m_bsp_root->make(communicator,atime,partitionlines,NULL); + BSPTree::make(communicator,atime,partitionlines,m_bsp_root); m_bsp_tree = true; } catch (Communicator::CancelledException) { diff --git a/salalib/shapemap.cpp b/salalib/shapemap.cpp index e9126bfb..56c9aa0e 100644 --- a/salalib/shapemap.cpp +++ b/salalib/shapemap.cpp @@ -3378,7 +3378,7 @@ bool ShapeMap::makeBSPtree() const return true; } - prefvec partitionlines; + std::vector partitionlines; for (size_t i = 0; i < m_shapes.size(); i++) { if (m_shapes[i].isLine()) { partitionlines.push_back(TaggedLine(m_shapes.value(i).getLine(),m_shapes.key(i))); @@ -3395,7 +3395,7 @@ bool ShapeMap::makeBSPtree() const } m_bsp_root = new BSPNode(); - m_bsp_root->make(NULL,0,partitionlines,NULL); + BSPTree::make(NULL,0,partitionlines,m_bsp_root); m_bsp_tree = true; } diff --git a/salalib/shapemap.h b/salalib/shapemap.h index 79438f87..b0848bf0 100644 --- a/salalib/shapemap.h +++ b/salalib/shapemap.h @@ -25,7 +25,7 @@ #include #include #include "salalib/importtypedefs.h" -#include "genlib/bspnode.h" +#include "genlib/bsptree.h" #include /////////////////////////////////////////////////////////////////////////////////////////////////