mirror of
https://github.com/QIDITECH/QIDISlicer.git
synced 2026-01-30 23:48:44 +03:00
PRUSA 2.7.0
This commit is contained in:
@@ -1,23 +1,17 @@
|
||||
# TODO Add individual tests as executables in separate directories
|
||||
# add_subirectory(<testcase>)
|
||||
|
||||
find_package(Catch2 2.9 REQUIRED)
|
||||
|
||||
include(Catch)
|
||||
set(TEST_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data)
|
||||
file(TO_NATIVE_PATH "${TEST_DATA_DIR}" TEST_DATA_DIR)
|
||||
|
||||
add_library(Catch2 INTERFACE)
|
||||
list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/Catch2)
|
||||
target_include_directories(Catch2 INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
add_library(Catch2::Catch2 ALIAS Catch2)
|
||||
if (APPLE)
|
||||
# OSX builds targeting OSX 10.9 do not support new std::uncought_exception()
|
||||
# see https://github.com/catchorg/Catch2/issues/1218
|
||||
target_compile_definitions(Catch2 INTERFACE -DCATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS)
|
||||
endif()
|
||||
include(Catch)
|
||||
|
||||
set(CATCH_EXTRA_ARGS "" CACHE STRING "Extra arguments for catch2 test suites.")
|
||||
|
||||
add_library(test_common INTERFACE)
|
||||
target_include_directories(test_common INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_compile_definitions(test_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)" CATCH_CONFIG_FAST_COMPILE)
|
||||
target_link_libraries(test_common INTERFACE Catch2::Catch2)
|
||||
|
||||
@@ -28,6 +22,7 @@ endif()
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
add_subdirectory(arrange)
|
||||
add_subdirectory(thumbnails)
|
||||
add_subdirectory(libslic3r)
|
||||
add_subdirectory(slic3rutils)
|
||||
add_subdirectory(fff_print)
|
||||
|
||||
@@ -202,10 +202,10 @@ static void check_nfp(const std::string & outfile_prefix,
|
||||
ExPolygons bed_negative = diff_ex(bedrect, bedpoly);
|
||||
ExPolygons orb_ex_r = to_expolygons(orbiter);
|
||||
ExPolygons orb_ex_r_ch = {ExPolygon(Geometry::convex_hull(orb_ex_r))};
|
||||
auto orb_ex_offs_pos_r = offset_ex(orb_ex_r, SCALED_EPSILON);
|
||||
auto orb_ex_offs_neg_r = offset_ex(orb_ex_r, -SCALED_EPSILON);
|
||||
auto orb_ex_offs_pos_r_ch = offset_ex(orb_ex_r_ch, SCALED_EPSILON);
|
||||
auto orb_ex_offs_neg_r_ch = offset_ex(orb_ex_r_ch, -SCALED_EPSILON);
|
||||
auto orb_ex_offs_pos_r = offset_ex(orb_ex_r, scaled<float>(EPSILON));
|
||||
auto orb_ex_offs_neg_r = offset_ex(orb_ex_r, -scaled<float>(EPSILON));
|
||||
auto orb_ex_offs_pos_r_ch = offset_ex(orb_ex_r_ch, scaled<float>(EPSILON));
|
||||
auto orb_ex_offs_neg_r_ch = offset_ex(orb_ex_r_ch, -scaled<float>(EPSILON));
|
||||
|
||||
auto bedpoly_offs = offset_ex(bedpoly, SCALED_EPSILON);
|
||||
|
||||
|
||||
@@ -738,8 +738,8 @@ bool is_collision_free(const Slic3r::Range<It> &item_range)
|
||||
|
||||
bool collision_free = true;
|
||||
foreach_combo(item_range, [&collision_free](auto &itm1, auto &itm2) {
|
||||
auto outline1 = offset(arr2::fixed_outline(itm1), -SCALED_EPSILON);
|
||||
auto outline2 = offset(arr2::fixed_outline(itm2), -SCALED_EPSILON);
|
||||
auto outline1 = offset(arr2::fixed_outline(itm1), -scaled<float>(EPSILON));
|
||||
auto outline2 = offset(arr2::fixed_outline(itm2), -scaled<float>(EPSILON));
|
||||
|
||||
auto inters = intersection(outline1, outline2);
|
||||
collision_free = collision_free && inters.empty();
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,2 +0,0 @@
|
||||
2.9.2 g2c869e1
|
||||
|
||||
17937
tests/catch2/catch.hpp
17937
tests/catch2/catch.hpp
File diff suppressed because it is too large
Load Diff
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Created by Justin R. Wilson on 2/19/2017.
|
||||
* Copyright 2017 Justin R. Wilson. All rights reserved.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#ifndef TWOBLUECUBES_CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED
|
||||
|
||||
// Don't #include any Catch headers here - we can assume they are already
|
||||
// included before this header.
|
||||
// This is not good practice in general but is necessary in this case so this
|
||||
// file can be distributed as a single header that works with the main
|
||||
// Catch single header.
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct AutomakeReporter : StreamingReporterBase<AutomakeReporter> {
|
||||
AutomakeReporter( ReporterConfig const& _config )
|
||||
: StreamingReporterBase( _config )
|
||||
{}
|
||||
|
||||
~AutomakeReporter() override;
|
||||
|
||||
static std::string getDescription() {
|
||||
return "Reports test results in the format of Automake .trs files";
|
||||
}
|
||||
|
||||
void assertionStarting( AssertionInfo const& ) override {}
|
||||
|
||||
bool assertionEnded( AssertionStats const& /*_assertionStats*/ ) override { return true; }
|
||||
|
||||
void testCaseEnded( TestCaseStats const& _testCaseStats ) override {
|
||||
// Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR.
|
||||
stream << ":test-result: ";
|
||||
if (_testCaseStats.totals.assertions.allPassed()) {
|
||||
stream << "PASS";
|
||||
} else if (_testCaseStats.totals.assertions.allOk()) {
|
||||
stream << "XFAIL";
|
||||
} else {
|
||||
stream << "FAIL";
|
||||
}
|
||||
stream << ' ' << _testCaseStats.testInfo.name << '\n';
|
||||
StreamingReporterBase::testCaseEnded( _testCaseStats );
|
||||
}
|
||||
|
||||
void skipTest( TestCaseInfo const& testInfo ) override {
|
||||
stream << ":test-result: SKIP " << testInfo.name << '\n';
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#ifdef CATCH_IMPL
|
||||
AutomakeReporter::~AutomakeReporter() {}
|
||||
#endif
|
||||
|
||||
CATCH_REGISTER_REPORTER( "automake", AutomakeReporter)
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED
|
||||
@@ -1,253 +0,0 @@
|
||||
/*
|
||||
* Created by Colton Wolkins on 2015-08-15.
|
||||
* Copyright 2015 Martin Moene. All rights reserved.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#ifndef TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED
|
||||
|
||||
|
||||
// Don't #include any Catch headers here - we can assume they are already
|
||||
// included before this header.
|
||||
// This is not good practice in general but is necessary in this case so this
|
||||
// file can be distributed as a single header that works with the main
|
||||
// Catch single header.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct TAPReporter : StreamingReporterBase<TAPReporter> {
|
||||
|
||||
using StreamingReporterBase::StreamingReporterBase;
|
||||
|
||||
~TAPReporter() override;
|
||||
|
||||
static std::string getDescription() {
|
||||
return "Reports test results in TAP format, suitable for test harnesses";
|
||||
}
|
||||
|
||||
ReporterPreferences getPreferences() const override {
|
||||
return m_reporterPrefs;
|
||||
}
|
||||
|
||||
void noMatchingTestCases( std::string const& spec ) override {
|
||||
stream << "# No test cases matched '" << spec << "'" << std::endl;
|
||||
}
|
||||
|
||||
void assertionStarting( AssertionInfo const& ) override {}
|
||||
|
||||
bool assertionEnded( AssertionStats const& _assertionStats ) override {
|
||||
++counter;
|
||||
|
||||
stream << "# " << currentTestCaseInfo->name << std::endl;
|
||||
AssertionPrinter printer( stream, _assertionStats, counter );
|
||||
printer.print();
|
||||
|
||||
stream << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
void testRunEnded( TestRunStats const& _testRunStats ) override {
|
||||
printTotals( _testRunStats.totals );
|
||||
stream << "\n" << std::endl;
|
||||
StreamingReporterBase::testRunEnded( _testRunStats );
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t counter = 0;
|
||||
class AssertionPrinter {
|
||||
public:
|
||||
AssertionPrinter& operator= ( AssertionPrinter const& ) = delete;
|
||||
AssertionPrinter( AssertionPrinter const& ) = delete;
|
||||
AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter )
|
||||
: stream( _stream )
|
||||
, result( _stats.assertionResult )
|
||||
, messages( _stats.infoMessages )
|
||||
, itMessage( _stats.infoMessages.begin() )
|
||||
, printInfoMessages( true )
|
||||
, counter(_counter)
|
||||
{}
|
||||
|
||||
void print() {
|
||||
itMessage = messages.begin();
|
||||
|
||||
switch( result.getResultType() ) {
|
||||
case ResultWas::Ok:
|
||||
printResultType( passedString() );
|
||||
printOriginalExpression();
|
||||
printReconstructedExpression();
|
||||
if ( ! result.hasExpression() )
|
||||
printRemainingMessages( Colour::None );
|
||||
else
|
||||
printRemainingMessages();
|
||||
break;
|
||||
case ResultWas::ExpressionFailed:
|
||||
if (result.isOk()) {
|
||||
printResultType(passedString());
|
||||
} else {
|
||||
printResultType(failedString());
|
||||
}
|
||||
printOriginalExpression();
|
||||
printReconstructedExpression();
|
||||
if (result.isOk()) {
|
||||
printIssue(" # TODO");
|
||||
}
|
||||
printRemainingMessages();
|
||||
break;
|
||||
case ResultWas::ThrewException:
|
||||
printResultType( failedString() );
|
||||
printIssue( "unexpected exception with message:" );
|
||||
printMessage();
|
||||
printExpressionWas();
|
||||
printRemainingMessages();
|
||||
break;
|
||||
case ResultWas::FatalErrorCondition:
|
||||
printResultType( failedString() );
|
||||
printIssue( "fatal error condition with message:" );
|
||||
printMessage();
|
||||
printExpressionWas();
|
||||
printRemainingMessages();
|
||||
break;
|
||||
case ResultWas::DidntThrowException:
|
||||
printResultType( failedString() );
|
||||
printIssue( "expected exception, got none" );
|
||||
printExpressionWas();
|
||||
printRemainingMessages();
|
||||
break;
|
||||
case ResultWas::Info:
|
||||
printResultType( "info" );
|
||||
printMessage();
|
||||
printRemainingMessages();
|
||||
break;
|
||||
case ResultWas::Warning:
|
||||
printResultType( "warning" );
|
||||
printMessage();
|
||||
printRemainingMessages();
|
||||
break;
|
||||
case ResultWas::ExplicitFailure:
|
||||
printResultType( failedString() );
|
||||
printIssue( "explicitly" );
|
||||
printRemainingMessages( Colour::None );
|
||||
break;
|
||||
// These cases are here to prevent compiler warnings
|
||||
case ResultWas::Unknown:
|
||||
case ResultWas::FailureBit:
|
||||
case ResultWas::Exception:
|
||||
printResultType( "** internal error **" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static Colour::Code dimColour() { return Colour::FileName; }
|
||||
|
||||
static const char* failedString() { return "not ok"; }
|
||||
static const char* passedString() { return "ok"; }
|
||||
|
||||
void printSourceInfo() const {
|
||||
Colour colourGuard( dimColour() );
|
||||
stream << result.getSourceInfo() << ":";
|
||||
}
|
||||
|
||||
void printResultType( std::string const& passOrFail ) const {
|
||||
if( !passOrFail.empty() ) {
|
||||
stream << passOrFail << ' ' << counter << " -";
|
||||
}
|
||||
}
|
||||
|
||||
void printIssue( std::string const& issue ) const {
|
||||
stream << " " << issue;
|
||||
}
|
||||
|
||||
void printExpressionWas() {
|
||||
if( result.hasExpression() ) {
|
||||
stream << ";";
|
||||
{
|
||||
Colour colour( dimColour() );
|
||||
stream << " expression was:";
|
||||
}
|
||||
printOriginalExpression();
|
||||
}
|
||||
}
|
||||
|
||||
void printOriginalExpression() const {
|
||||
if( result.hasExpression() ) {
|
||||
stream << " " << result.getExpression();
|
||||
}
|
||||
}
|
||||
|
||||
void printReconstructedExpression() const {
|
||||
if( result.hasExpandedExpression() ) {
|
||||
{
|
||||
Colour colour( dimColour() );
|
||||
stream << " for: ";
|
||||
}
|
||||
std::string expr = result.getExpandedExpression();
|
||||
std::replace( expr.begin(), expr.end(), '\n', ' ');
|
||||
stream << expr;
|
||||
}
|
||||
}
|
||||
|
||||
void printMessage() {
|
||||
if ( itMessage != messages.end() ) {
|
||||
stream << " '" << itMessage->message << "'";
|
||||
++itMessage;
|
||||
}
|
||||
}
|
||||
|
||||
void printRemainingMessages( Colour::Code colour = dimColour() ) {
|
||||
if (itMessage == messages.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// using messages.end() directly (or auto) yields compilation error:
|
||||
std::vector<MessageInfo>::const_iterator itEnd = messages.end();
|
||||
const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
|
||||
|
||||
{
|
||||
Colour colourGuard( colour );
|
||||
stream << " with " << pluralise( N, "message" ) << ":";
|
||||
}
|
||||
|
||||
for(; itMessage != itEnd; ) {
|
||||
// If this assertion is a warning ignore any INFO messages
|
||||
if( printInfoMessages || itMessage->type != ResultWas::Info ) {
|
||||
stream << " '" << itMessage->message << "'";
|
||||
if ( ++itMessage != itEnd ) {
|
||||
Colour colourGuard( dimColour() );
|
||||
stream << " and";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream& stream;
|
||||
AssertionResult const& result;
|
||||
std::vector<MessageInfo> messages;
|
||||
std::vector<MessageInfo>::const_iterator itMessage;
|
||||
bool printInfoMessages;
|
||||
std::size_t counter;
|
||||
};
|
||||
|
||||
void printTotals( const Totals& totals ) const {
|
||||
if( totals.testCases.total() == 0 ) {
|
||||
stream << "1..0 # Skipped: No tests ran.";
|
||||
} else {
|
||||
stream << "1.." << counter;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef CATCH_IMPL
|
||||
TAPReporter::~TAPReporter() {}
|
||||
#endif
|
||||
|
||||
CATCH_REGISTER_REPORTER( "tap", TAPReporter )
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED
|
||||
@@ -1,220 +0,0 @@
|
||||
/*
|
||||
* Created by Phil Nash on 19th December 2014
|
||||
* Copyright 2014 Two Blue Cubes Ltd. All rights reserved.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#ifndef TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
|
||||
|
||||
// Don't #include any Catch headers here - we can assume they are already
|
||||
// included before this header.
|
||||
// This is not good practice in general but is necessary in this case so this
|
||||
// file can be distributed as a single header that works with the main
|
||||
// Catch single header.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wpadded"
|
||||
#endif
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct TeamCityReporter : StreamingReporterBase<TeamCityReporter> {
|
||||
TeamCityReporter( ReporterConfig const& _config )
|
||||
: StreamingReporterBase( _config )
|
||||
{
|
||||
m_reporterPrefs.shouldRedirectStdOut = true;
|
||||
}
|
||||
|
||||
static std::string escape( std::string const& str ) {
|
||||
std::string escaped = str;
|
||||
replaceInPlace( escaped, "|", "||" );
|
||||
replaceInPlace( escaped, "'", "|'" );
|
||||
replaceInPlace( escaped, "\n", "|n" );
|
||||
replaceInPlace( escaped, "\r", "|r" );
|
||||
replaceInPlace( escaped, "[", "|[" );
|
||||
replaceInPlace( escaped, "]", "|]" );
|
||||
return escaped;
|
||||
}
|
||||
~TeamCityReporter() override;
|
||||
|
||||
static std::string getDescription() {
|
||||
return "Reports test results as TeamCity service messages";
|
||||
}
|
||||
|
||||
void skipTest( TestCaseInfo const& /* testInfo */ ) override {
|
||||
}
|
||||
|
||||
void noMatchingTestCases( std::string const& /* spec */ ) override {}
|
||||
|
||||
void testGroupStarting( GroupInfo const& groupInfo ) override {
|
||||
StreamingReporterBase::testGroupStarting( groupInfo );
|
||||
stream << "##teamcity[testSuiteStarted name='"
|
||||
<< escape( groupInfo.name ) << "']\n";
|
||||
}
|
||||
void testGroupEnded( TestGroupStats const& testGroupStats ) override {
|
||||
StreamingReporterBase::testGroupEnded( testGroupStats );
|
||||
stream << "##teamcity[testSuiteFinished name='"
|
||||
<< escape( testGroupStats.groupInfo.name ) << "']\n";
|
||||
}
|
||||
|
||||
|
||||
void assertionStarting( AssertionInfo const& ) override {}
|
||||
|
||||
bool assertionEnded( AssertionStats const& assertionStats ) override {
|
||||
AssertionResult const& result = assertionStats.assertionResult;
|
||||
if( !result.isOk() ) {
|
||||
|
||||
ReusableStringStream msg;
|
||||
if( !m_headerPrintedForThisSection )
|
||||
printSectionHeader( msg.get() );
|
||||
m_headerPrintedForThisSection = true;
|
||||
|
||||
msg << result.getSourceInfo() << "\n";
|
||||
|
||||
switch( result.getResultType() ) {
|
||||
case ResultWas::ExpressionFailed:
|
||||
msg << "expression failed";
|
||||
break;
|
||||
case ResultWas::ThrewException:
|
||||
msg << "unexpected exception";
|
||||
break;
|
||||
case ResultWas::FatalErrorCondition:
|
||||
msg << "fatal error condition";
|
||||
break;
|
||||
case ResultWas::DidntThrowException:
|
||||
msg << "no exception was thrown where one was expected";
|
||||
break;
|
||||
case ResultWas::ExplicitFailure:
|
||||
msg << "explicit failure";
|
||||
break;
|
||||
|
||||
// We shouldn't get here because of the isOk() test
|
||||
case ResultWas::Ok:
|
||||
case ResultWas::Info:
|
||||
case ResultWas::Warning:
|
||||
CATCH_ERROR( "Internal error in TeamCity reporter" );
|
||||
// These cases are here to prevent compiler warnings
|
||||
case ResultWas::Unknown:
|
||||
case ResultWas::FailureBit:
|
||||
case ResultWas::Exception:
|
||||
CATCH_ERROR( "Not implemented" );
|
||||
}
|
||||
if( assertionStats.infoMessages.size() == 1 )
|
||||
msg << " with message:";
|
||||
if( assertionStats.infoMessages.size() > 1 )
|
||||
msg << " with messages:";
|
||||
for( auto const& messageInfo : assertionStats.infoMessages )
|
||||
msg << "\n \"" << messageInfo.message << "\"";
|
||||
|
||||
|
||||
if( result.hasExpression() ) {
|
||||
msg <<
|
||||
"\n " << result.getExpressionInMacro() << "\n"
|
||||
"with expansion:\n" <<
|
||||
" " << result.getExpandedExpression() << "\n";
|
||||
}
|
||||
|
||||
if( currentTestCaseInfo->okToFail() ) {
|
||||
msg << "- failure ignore as test marked as 'ok to fail'\n";
|
||||
stream << "##teamcity[testIgnored"
|
||||
<< " name='" << escape( currentTestCaseInfo->name )<< "'"
|
||||
<< " message='" << escape( msg.str() ) << "'"
|
||||
<< "]\n";
|
||||
}
|
||||
else {
|
||||
stream << "##teamcity[testFailed"
|
||||
<< " name='" << escape( currentTestCaseInfo->name )<< "'"
|
||||
<< " message='" << escape( msg.str() ) << "'"
|
||||
<< "]\n";
|
||||
}
|
||||
}
|
||||
stream.flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
void sectionStarting( SectionInfo const& sectionInfo ) override {
|
||||
m_headerPrintedForThisSection = false;
|
||||
StreamingReporterBase::sectionStarting( sectionInfo );
|
||||
}
|
||||
|
||||
void testCaseStarting( TestCaseInfo const& testInfo ) override {
|
||||
m_testTimer.start();
|
||||
StreamingReporterBase::testCaseStarting( testInfo );
|
||||
stream << "##teamcity[testStarted name='"
|
||||
<< escape( testInfo.name ) << "']\n";
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
void testCaseEnded( TestCaseStats const& testCaseStats ) override {
|
||||
StreamingReporterBase::testCaseEnded( testCaseStats );
|
||||
if( !testCaseStats.stdOut.empty() )
|
||||
stream << "##teamcity[testStdOut name='"
|
||||
<< escape( testCaseStats.testInfo.name )
|
||||
<< "' out='" << escape( testCaseStats.stdOut ) << "']\n";
|
||||
if( !testCaseStats.stdErr.empty() )
|
||||
stream << "##teamcity[testStdErr name='"
|
||||
<< escape( testCaseStats.testInfo.name )
|
||||
<< "' out='" << escape( testCaseStats.stdErr ) << "']\n";
|
||||
stream << "##teamcity[testFinished name='"
|
||||
<< escape( testCaseStats.testInfo.name ) << "' duration='"
|
||||
<< m_testTimer.getElapsedMilliseconds() << "']\n";
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
private:
|
||||
void printSectionHeader( std::ostream& os ) {
|
||||
assert( !m_sectionStack.empty() );
|
||||
|
||||
if( m_sectionStack.size() > 1 ) {
|
||||
os << getLineOfChars<'-'>() << "\n";
|
||||
|
||||
std::vector<SectionInfo>::const_iterator
|
||||
it = m_sectionStack.begin()+1, // Skip first section (test case)
|
||||
itEnd = m_sectionStack.end();
|
||||
for( ; it != itEnd; ++it )
|
||||
printHeaderString( os, it->name );
|
||||
os << getLineOfChars<'-'>() << "\n";
|
||||
}
|
||||
|
||||
SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
|
||||
|
||||
if( !lineInfo.empty() )
|
||||
os << lineInfo << "\n";
|
||||
os << getLineOfChars<'.'>() << "\n\n";
|
||||
}
|
||||
|
||||
// if string has a : in first line will set indent to follow it on
|
||||
// subsequent lines
|
||||
static void printHeaderString( std::ostream& os, std::string const& _string, std::size_t indent = 0 ) {
|
||||
std::size_t i = _string.find( ": " );
|
||||
if( i != std::string::npos )
|
||||
i+=2;
|
||||
else
|
||||
i = 0;
|
||||
os << Column( _string )
|
||||
.indent( indent+i)
|
||||
.initialIndent( indent ) << "\n";
|
||||
}
|
||||
private:
|
||||
bool m_headerPrintedForThisSection = false;
|
||||
Timer m_testTimer;
|
||||
};
|
||||
|
||||
#ifdef CATCH_IMPL
|
||||
TeamCityReporter::~TeamCityReporter() {}
|
||||
#endif
|
||||
|
||||
CATCH_REGISTER_REPORTER( "teamcity", TeamCityReporter )
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
|
||||
20
tests/data/20mm_cube.obj
Normal file
20
tests/data/20mm_cube.obj
Normal file
@@ -0,0 +1,20 @@
|
||||
v 20.000000 20.000000 0.000000
|
||||
v 20.000000 0.000000 0.000000
|
||||
v 0.000000 0.000000 0.000000
|
||||
v 0.000000 20.000000 0.000000
|
||||
v 20.000000 20.000000 20.000000
|
||||
v 0.000000 20.000000 20.000000
|
||||
v 0.000000 0.000000 20.000000
|
||||
v 20.000000 0.000000 20.000000
|
||||
f 1 2 3
|
||||
f 1 3 4
|
||||
f 5 6 7
|
||||
f 5 7 8
|
||||
f 1 5 8
|
||||
f 1 8 2
|
||||
f 2 8 7
|
||||
f 2 7 3
|
||||
f 3 7 6
|
||||
f 3 6 4
|
||||
f 5 1 4
|
||||
f 5 4 6
|
||||
20
tests/data/2x20x10.obj
Normal file
20
tests/data/2x20x10.obj
Normal file
@@ -0,0 +1,20 @@
|
||||
v 2.000000 20.000000 0.000000
|
||||
v 2.000000 0.000000 0.000000
|
||||
v 0.000000 0.000000 0.000000
|
||||
v 0.000000 20.000000 0.000000
|
||||
v 2.000000 20.000000 10.000000
|
||||
v 0.000000 20.000000 10.000000
|
||||
v 0.000000 0.000000 10.000000
|
||||
v 2.000000 0.000000 10.000000
|
||||
f 1 2 3
|
||||
f 1 3 4
|
||||
f 5 6 7
|
||||
f 5 7 8
|
||||
f 1 5 8
|
||||
f 1 8 2
|
||||
f 2 8 7
|
||||
f 2 7 3
|
||||
f 3 7 6
|
||||
f 3 6 4
|
||||
f 5 1 4
|
||||
f 5 4 6
|
||||
1867
tests/data/A.obj
Normal file
1867
tests/data/A.obj
Normal file
File diff suppressed because it is too large
Load Diff
2504
tests/data/A_upsidedown.obj
Normal file
2504
tests/data/A_upsidedown.obj
Normal file
File diff suppressed because it is too large
Load Diff
76
tests/data/U_overhang.obj
Normal file
76
tests/data/U_overhang.obj
Normal file
@@ -0,0 +1,76 @@
|
||||
####
|
||||
#
|
||||
# OBJ File Generated by Meshlab
|
||||
#
|
||||
####
|
||||
# Object U_overhang.obj
|
||||
#
|
||||
# Vertices: 16
|
||||
# Faces: 28
|
||||
#
|
||||
####
|
||||
vn 1.570797 1.570796 1.570796
|
||||
v 10.000000 10.000000 11.000000
|
||||
vn 4.712389 1.570796 -1.570796
|
||||
v 10.000000 1.000000 10.000000
|
||||
vn 1.570796 1.570796 -1.570796
|
||||
v 10.000000 10.000000 10.000000
|
||||
vn 1.570796 -1.570796 1.570796
|
||||
v 10.000000 0.000000 11.000000
|
||||
vn 4.712389 1.570796 1.570796
|
||||
v 10.000000 1.000000 1.000000
|
||||
vn 1.570797 1.570796 -1.570796
|
||||
v 10.000000 10.000000 0.000000
|
||||
vn 1.570796 1.570796 1.570796
|
||||
v 10.000000 10.000000 1.000000
|
||||
vn 1.570796 -1.570796 -1.570796
|
||||
v 10.000000 0.000000 0.000000
|
||||
vn -1.570796 1.570796 1.570796
|
||||
v 0.000000 10.000000 1.000000
|
||||
vn -4.712389 1.570796 1.570796
|
||||
v 0.000000 1.000000 1.000000
|
||||
vn -1.570796 -1.570796 -1.570796
|
||||
v 0.000000 0.000000 0.000000
|
||||
vn -1.570797 1.570796 -1.570796
|
||||
v 0.000000 10.000000 0.000000
|
||||
vn -4.712389 1.570796 -1.570796
|
||||
v 0.000000 1.000000 10.000000
|
||||
vn -1.570797 1.570796 1.570796
|
||||
v 0.000000 10.000000 11.000000
|
||||
vn -1.570796 1.570796 -1.570796
|
||||
v 0.000000 10.000000 10.000000
|
||||
vn -1.570796 -1.570796 1.570796
|
||||
v 0.000000 0.000000 11.000000
|
||||
# 16 vertices, 0 vertices normals
|
||||
|
||||
f 1//1 2//2 3//3
|
||||
f 2//2 4//4 5//5
|
||||
f 4//4 2//2 1//1
|
||||
f 5//5 6//6 7//7
|
||||
f 5//5 8//8 6//6
|
||||
f 8//8 5//5 4//4
|
||||
f 9//9 5//5 7//7
|
||||
f 5//5 9//9 10//10
|
||||
f 11//11 6//6 8//8
|
||||
f 6//6 11//11 12//12
|
||||
f 12//12 10//10 9//9
|
||||
f 10//10 11//11 13//13
|
||||
f 11//11 10//10 12//12
|
||||
f 13//13 14//14 15//15
|
||||
f 13//13 16//16 14//14
|
||||
f 16//16 13//13 11//11
|
||||
f 6//6 9//9 7//7
|
||||
f 9//9 6//6 12//12
|
||||
f 11//11 4//4 16//16
|
||||
f 4//4 11//11 8//8
|
||||
f 13//13 3//3 2//2
|
||||
f 3//3 13//13 15//15
|
||||
f 5//5 13//13 2//2
|
||||
f 13//13 5//5 10//10
|
||||
f 14//14 4//4 1//1
|
||||
f 4//4 14//14 16//16
|
||||
f 3//3 14//14 1//1
|
||||
f 14//14 3//3 15//15
|
||||
# 28 faces, 0 coords texture
|
||||
|
||||
# End of File
|
||||
68
tests/data/V.obj
Normal file
68
tests/data/V.obj
Normal file
@@ -0,0 +1,68 @@
|
||||
####
|
||||
#
|
||||
# OBJ File Generated by Meshlab
|
||||
#
|
||||
####
|
||||
# Object V.obj
|
||||
#
|
||||
# Vertices: 14
|
||||
# Faces: 24
|
||||
#
|
||||
####
|
||||
vn -0.835484 -0.584839 0.000000
|
||||
v 41.480000 44.020000 0.000000
|
||||
vn 0.000000 0.000000 1.768192
|
||||
v 51.480000 44.020000 15.000000
|
||||
vn 0.000000 0.000000 0.960070
|
||||
v 41.480000 44.020000 15.000000
|
||||
vn 0.000000 0.000000 0.413330
|
||||
v 55.480000 24.020000 15.000000
|
||||
vn 0.000000 -0.982794 0.000000
|
||||
v 65.480003 24.020000 15.000000
|
||||
vn -0.000000 -0.000000 -2.776433
|
||||
v 60.480000 31.162861 0.000000
|
||||
vn -0.000000 -0.000000 -0.099828
|
||||
v 79.480003 44.020000 0.000000
|
||||
vn -0.000000 -0.000000 -0.265332
|
||||
v 55.480000 24.020000 0.000000
|
||||
vn 0.661947 0.463363 0.000000
|
||||
v 51.480000 44.020000 0.000000
|
||||
vn 0.624900 0.437430 0.000000
|
||||
v 60.480000 31.162861 15.000000
|
||||
vn -0.661947 0.463363 0.000000
|
||||
v 69.480003 44.020000 15.000000
|
||||
vn -1.286846 0.900793 0.000000
|
||||
v 69.480003 44.020000 0.000000
|
||||
vn 0.000000 0.982794 0.000000
|
||||
v 79.480003 44.020000 15.000000
|
||||
vn 0.451362 -0.315954 0.000000
|
||||
v 65.480003 24.020000 0.000000
|
||||
# 14 vertices, 0 vertices normals
|
||||
|
||||
f 3//3 1//1 4//4
|
||||
f 4//4 1//1 8//8
|
||||
f 1//1 3//3 2//2
|
||||
f 9//9 1//1 2//2
|
||||
f 2//2 3//3 4//4
|
||||
f 10//10 2//2 4//4
|
||||
f 5//5 10//10 4//4
|
||||
f 13//13 11//11 5//5
|
||||
f 11//11 10//10 5//5
|
||||
f 4//4 8//8 5//5
|
||||
f 5//5 8//8 14//14
|
||||
f 1//1 9//9 8//8
|
||||
f 8//8 9//9 6//6
|
||||
f 6//6 12//12 7//7
|
||||
f 6//6 7//7 8//8
|
||||
f 8//8 7//7 14//14
|
||||
f 9//9 2//2 10//10
|
||||
f 6//6 9//9 10//10
|
||||
f 10//10 11//11 6//6
|
||||
f 6//6 11//11 12//12
|
||||
f 12//12 11//11 13//13
|
||||
f 7//7 12//12 13//13
|
||||
f 13//13 5//5 14//14
|
||||
f 7//7 13//13 14//14
|
||||
# 24 faces, 0 coords texture
|
||||
|
||||
# End of File
|
||||
38
tests/data/V_standing.obj
Normal file
38
tests/data/V_standing.obj
Normal file
@@ -0,0 +1,38 @@
|
||||
v -14.000000 0.000000 20.000000
|
||||
v -14.000000 15.000000 20.000000
|
||||
v 0.000000 0.000000 0.000000
|
||||
v 0.000000 15.000000 0.000000
|
||||
v -4.000000 0.000000 20.000000
|
||||
v -4.000000 15.000000 20.000000
|
||||
v 5.000000 0.000000 7.142860
|
||||
v 10.000000 0.000000 0.000000
|
||||
v 24.000000 0.000000 20.000000
|
||||
v 14.000000 0.000000 20.000000
|
||||
v 10.000000 15.000000 0.000000
|
||||
v 5.000000 15.000000 7.142860
|
||||
v 14.000000 15.000000 20.000000
|
||||
v 24.000000 15.000000 20.000000
|
||||
f 1 2 3
|
||||
f 3 2 4
|
||||
f 2 1 5
|
||||
f 6 2 5
|
||||
f 5 1 3
|
||||
f 7 5 3
|
||||
f 8 7 3
|
||||
f 9 10 8
|
||||
f 10 7 8
|
||||
f 3 4 8
|
||||
f 8 4 11
|
||||
f 2 6 4
|
||||
f 4 6 12
|
||||
f 12 13 14
|
||||
f 12 14 4
|
||||
f 4 14 11
|
||||
f 6 5 7
|
||||
f 12 6 7
|
||||
f 7 10 12
|
||||
f 12 10 13
|
||||
f 13 10 9
|
||||
f 14 13 9
|
||||
f 9 8 11
|
||||
f 14 9 11
|
||||
44
tests/data/bridge.obj
Normal file
44
tests/data/bridge.obj
Normal file
@@ -0,0 +1,44 @@
|
||||
v 75.000000 84.500000 8.000000
|
||||
v 125.000000 84.500000 8.000000
|
||||
v 75.000000 94.500000 8.000000
|
||||
v 120.000000 84.500000 5.000000
|
||||
v 125.000000 94.500000 8.000000
|
||||
v 75.000000 84.500000 0.000000
|
||||
v 80.000000 84.500000 5.000000
|
||||
v 125.000000 84.500000 0.000000
|
||||
v 125.000000 94.500000 0.000000
|
||||
v 80.000000 94.500000 5.000000
|
||||
v 75.000000 94.500000 0.000000
|
||||
v 120.000000 94.500000 5.000000
|
||||
v 120.000000 84.500000 0.000000
|
||||
v 80.000000 94.500000 0.000000
|
||||
v 80.000000 84.500000 0.000000
|
||||
v 120.000000 94.500000 0.000000
|
||||
f 1 2 3
|
||||
f 2 1 4
|
||||
f 3 2 5
|
||||
f 3 6 1
|
||||
f 1 7 4
|
||||
f 2 4 8
|
||||
f 2 9 5
|
||||
f 5 10 3
|
||||
f 11 6 3
|
||||
f 6 7 1
|
||||
f 7 12 4
|
||||
f 4 13 8
|
||||
f 8 9 2
|
||||
f 5 9 12
|
||||
f 5 12 10
|
||||
f 10 11 3
|
||||
f 11 14 6
|
||||
f 15 7 6
|
||||
f 10 12 7
|
||||
f 12 13 4
|
||||
f 13 9 8
|
||||
f 12 9 16
|
||||
f 14 11 10
|
||||
f 6 14 15
|
||||
f 15 14 7
|
||||
f 7 14 10
|
||||
f 16 13 12
|
||||
f 16 9 13
|
||||
4
tests/data/contour_neighbor.svg
Normal file
4
tests/data/contour_neighbor.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#ed6b21" d="m 68.34,199.06 c 0,6.2 -5.04,11.24 -11.24,11.24 l 0.03507,-11.27073 z" />
|
||||
<path fill="#808080" d="m 68.34,199.06 5.608583,1.97096 -14.648722,13.67 L 57.1,210.3 c 6.2,0 11.24,-5.04 11.24,-11.24 z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 271 B |
72
tests/data/cube_with_concave_hole.obj
Normal file
72
tests/data/cube_with_concave_hole.obj
Normal file
@@ -0,0 +1,72 @@
|
||||
v -10.000000 -10.000000 -5.000000
|
||||
v -10.000000 -10.000000 5.000000
|
||||
v -10.000000 10.000000 -5.000000
|
||||
v -10.000000 10.000000 5.000000
|
||||
v 10.000000 -10.000000 -5.000000
|
||||
v 10.000000 -10.000000 5.000000
|
||||
v -5.000000 -5.000000 -5.000000
|
||||
v 5.000000 -5.000000 -5.000000
|
||||
v 5.000000 5.000000 -5.000000
|
||||
v 5.000000 10.000000 -5.000000
|
||||
v -5.000000 5.000000 -5.000000
|
||||
v 0.000000 5.000000 -5.000000
|
||||
v 5.000000 0.000000 -5.000000
|
||||
v 0.000000 0.000000 -5.000000
|
||||
v 10.000000 5.000000 -5.000000
|
||||
v 5.000000 10.000000 5.000000
|
||||
v -5.000000 -5.000000 5.000000
|
||||
v 5.000000 0.000000 5.000000
|
||||
v 5.000000 -5.000000 5.000000
|
||||
v -5.000000 5.000000 5.000000
|
||||
v 10.000000 5.000000 5.000000
|
||||
v 5.000000 5.000000 5.000000
|
||||
v 0.000000 5.000000 5.000000
|
||||
v 0.000000 0.000000 5.000000
|
||||
f 1 2 3
|
||||
f 3 2 4
|
||||
f 2 1 5
|
||||
f 6 2 5
|
||||
f 7 8 5
|
||||
f 9 3 10
|
||||
f 11 3 12
|
||||
f 12 13 14
|
||||
f 1 3 11
|
||||
f 1 11 7
|
||||
f 1 7 5
|
||||
f 12 3 9
|
||||
f 5 8 13
|
||||
f 5 13 9
|
||||
f 13 12 9
|
||||
f 15 5 9
|
||||
f 3 4 10
|
||||
f 10 4 16
|
||||
f 17 2 6
|
||||
f 18 19 6
|
||||
f 20 4 17
|
||||
f 21 22 6
|
||||
f 19 17 6
|
||||
f 4 2 17
|
||||
f 23 4 20
|
||||
f 22 4 23
|
||||
f 22 18 6
|
||||
f 22 23 18
|
||||
f 22 16 4
|
||||
f 24 18 23
|
||||
f 6 5 15
|
||||
f 21 6 15
|
||||
f 21 15 22
|
||||
f 22 15 9
|
||||
f 10 16 22
|
||||
f 9 10 22
|
||||
f 11 20 17
|
||||
f 7 11 17
|
||||
f 12 23 20
|
||||
f 11 12 20
|
||||
f 14 24 12
|
||||
f 12 24 23
|
||||
f 24 14 13
|
||||
f 18 24 13
|
||||
f 18 13 19
|
||||
f 19 13 8
|
||||
f 19 8 17
|
||||
f 17 8 7
|
||||
112
tests/data/cube_with_concave_hole_enlarged.obj
Normal file
112
tests/data/cube_with_concave_hole_enlarged.obj
Normal file
@@ -0,0 +1,112 @@
|
||||
####
|
||||
#
|
||||
# OBJ File Generated by Meshlab
|
||||
#
|
||||
####
|
||||
# Object cube_with_concave_hole_enlarged.obj
|
||||
#
|
||||
# Vertices: 24
|
||||
# Faces: 48
|
||||
#
|
||||
####
|
||||
vn 1.107149 0.000000 0.000000
|
||||
v 76.634163 17.865837 0.000000
|
||||
vn 0.000000 0.000000 0.321750
|
||||
v 68.557083 25.942917 16.154167
|
||||
vn 0.000000 0.000000 2.356194
|
||||
v 52.402920 25.942917 16.154167
|
||||
vn 0.000000 0.000000 0.321751
|
||||
v 76.634163 50.174164 16.154167
|
||||
vn 0.000000 0.000000 2.034444
|
||||
v 68.557083 42.097084 16.154167
|
||||
vn 0.000000 0.000000 0.463648
|
||||
v 76.634163 17.865837 16.154167
|
||||
vn 0.000000 0.000000 0.785398
|
||||
v 52.402920 34.020000 16.154167
|
||||
vn 0.000000 0.000000 0.321750
|
||||
v 44.325836 17.865837 16.154167
|
||||
vn 0.000000 0.982794 0.000000
|
||||
v 52.402920 50.174164 16.154167
|
||||
vn 0.000000 1.570796 0.000000
|
||||
v 52.402920 50.174164 0.000000
|
||||
vn 0.000000 0.000000 -0.463648
|
||||
v 52.402920 34.020000 0.000000
|
||||
vn 0.000000 0.000000 -0.321751
|
||||
v 44.325836 17.865837 0.000000
|
||||
vn 0.000000 0.000000 -0.463648
|
||||
v 76.634163 50.174164 0.000000
|
||||
vn 0.000000 0.000000 -1.570796
|
||||
v 44.325836 42.097084 0.000000
|
||||
vn -0.588003 0.000000 0.000000
|
||||
v 44.325836 42.097084 16.154167
|
||||
vn 0.000000 1.570796 0.000000
|
||||
v 52.402920 42.097084 16.154167
|
||||
vn -1.107149 0.000000 0.000000
|
||||
v 52.402920 42.097084 0.000000
|
||||
vn 0.000000 -0.463648 0.000000
|
||||
v 60.480000 42.097084 16.154167
|
||||
vn 0.000000 -1.107149 0.000000
|
||||
v 68.557083 42.097084 0.000000
|
||||
vn 1.570796 0.000000 0.000000
|
||||
v 60.480000 42.097084 0.000000
|
||||
vn 0.000000 -0.463647 0.000000
|
||||
v 60.480000 34.020000 0.000000
|
||||
vn 0.000000 -1.570796 0.000000
|
||||
v 60.480000 34.020000 16.154167
|
||||
vn 1.107149 0.000000 0.000000
|
||||
v 52.402920 25.942917 0.000000
|
||||
vn 0.000000 0.785398 0.000000
|
||||
v 68.557083 25.942917 0.000000
|
||||
# 24 vertices, 0 vertices normals
|
||||
|
||||
f 6//6 1//1 4//4
|
||||
f 4//4 1//1 13//13
|
||||
f 1//1 6//6 8//8
|
||||
f 12//12 1//1 8//8
|
||||
f 2//2 3//3 8//8
|
||||
f 16//16 4//4 9//9
|
||||
f 5//5 4//4 18//18
|
||||
f 18//18 7//7 22//22
|
||||
f 6//6 4//4 5//5
|
||||
f 6//6 5//5 2//2
|
||||
f 6//6 2//2 8//8
|
||||
f 18//18 4//4 16//16
|
||||
f 8//8 3//3 7//7
|
||||
f 8//8 7//7 16//16
|
||||
f 7//7 18//18 16//16
|
||||
f 15//15 8//8 16//16
|
||||
f 4//4 13//13 9//9
|
||||
f 9//9 13//13 10//10
|
||||
f 24//24 1//1 12//12
|
||||
f 11//11 23//23 12//12
|
||||
f 19//19 13//13 24//24
|
||||
f 14//14 17//17 12//12
|
||||
f 23//23 24//24 12//12
|
||||
f 13//13 1//1 24//24
|
||||
f 20//20 13//13 19//19
|
||||
f 17//17 13//13 20//20
|
||||
f 17//17 11//11 12//12
|
||||
f 17//17 20//20 11//11
|
||||
f 17//17 10//10 13//13
|
||||
f 21//21 11//11 20//20
|
||||
f 12//12 8//8 15//15
|
||||
f 14//14 12//12 15//15
|
||||
f 14//14 15//15 17//17
|
||||
f 17//17 15//15 16//16
|
||||
f 9//9 10//10 17//17
|
||||
f 16//16 9//9 17//17
|
||||
f 5//5 19//19 24//24
|
||||
f 2//2 5//5 24//24
|
||||
f 18//18 20//20 19//19
|
||||
f 5//5 18//18 19//19
|
||||
f 22//22 21//21 18//18
|
||||
f 18//18 21//21 20//20
|
||||
f 21//21 22//22 7//7
|
||||
f 11//11 21//21 7//7
|
||||
f 11//11 7//7 23//23
|
||||
f 23//23 7//7 3//3
|
||||
f 23//23 3//3 24//24
|
||||
f 24//24 3//3 2//2
|
||||
# 48 faces, 0 coords texture
|
||||
|
||||
# End of File
|
||||
112
tests/data/cube_with_concave_hole_enlarged_standing.obj
Normal file
112
tests/data/cube_with_concave_hole_enlarged_standing.obj
Normal file
@@ -0,0 +1,112 @@
|
||||
####
|
||||
#
|
||||
# OBJ File Generated by Meshlab
|
||||
#
|
||||
####
|
||||
# Object cube_with_concave_hole_enlarged.obj
|
||||
#
|
||||
# Vertices: 24
|
||||
# Faces: 48
|
||||
#
|
||||
####
|
||||
vn 0.000000 0.000000 -1.570796
|
||||
v 68.557083 17.865835 0.000000
|
||||
vn 0.000000 0.000000 -1.107149
|
||||
v 52.402916 17.865835 0.000000
|
||||
vn 0.000000 -0.463648 0.000000
|
||||
v 68.557083 17.865835 32.308331
|
||||
vn 0.000000 -1.570796 0.000000
|
||||
v 52.402916 17.865835 32.308331
|
||||
vn 2.356194 0.000000 0.000000
|
||||
v 68.557083 25.942917 24.231247
|
||||
vn 1.570796 0.000000 0.000000
|
||||
v 68.557083 50.174164 24.231247
|
||||
vn 2.034444 0.000000 0.000000
|
||||
v 68.557083 42.097084 8.077083
|
||||
vn 2.677945 0.000000 0.000000
|
||||
v 68.557083 34.020000 24.231247
|
||||
vn 0.000000 1.570796 0.000000
|
||||
v 68.557083 50.174164 0.000000
|
||||
vn 0.000000 1.570796 0.000000
|
||||
v 52.402916 50.174164 24.231247
|
||||
vn -0.321750 0.000000 0.000000
|
||||
v 52.402916 25.942917 8.077083
|
||||
vn -1.570796 0.000000 0.000000
|
||||
v 52.402916 42.097084 32.308331
|
||||
vn -0.321751 0.000000 0.000000
|
||||
v 52.402916 50.174164 0.000000
|
||||
vn -2.356194 0.000000 0.000000
|
||||
v 52.402916 42.097084 8.077083
|
||||
vn -0.785398 0.000000 0.000000
|
||||
v 52.402916 42.097084 16.154165
|
||||
vn 0.000000 0.000000 0.588003
|
||||
v 68.557083 42.097084 32.308331
|
||||
vn 0.000000 1.107149 0.000000
|
||||
v 52.402916 42.097084 24.231247
|
||||
vn 0.000000 1.570796 0.000000
|
||||
v 68.557083 42.097084 24.231247
|
||||
vn 0.000000 0.000000 1.570796
|
||||
v 68.557083 25.942917 8.077083
|
||||
vn 0.000000 -0.463647 0.000000
|
||||
v 68.557083 42.097084 16.154165
|
||||
vn 0.000000 0.000000 -1.570796
|
||||
v 68.557083 34.020000 16.154165
|
||||
vn 0.000000 0.000000 -0.463648
|
||||
v 52.402916 34.020000 16.154165
|
||||
vn 0.000000 0.000000 -1.570796
|
||||
v 52.402916 34.020000 24.231247
|
||||
vn 0.000000 0.000000 -0.463648
|
||||
v 52.402916 25.942917 24.231247
|
||||
# 24 vertices, 0 vertices normals
|
||||
|
||||
f 1//1 2//2 9//9
|
||||
f 9//9 2//2 13//13
|
||||
f 2//2 1//1 3//3
|
||||
f 4//4 2//2 3//3
|
||||
f 19//19 5//5 3//3
|
||||
f 18//18 9//9 6//6
|
||||
f 7//7 9//9 20//20
|
||||
f 20//20 8//8 21//21
|
||||
f 1//1 9//9 7//7
|
||||
f 1//1 7//7 19//19
|
||||
f 1//1 19//19 3//3
|
||||
f 20//20 9//9 18//18
|
||||
f 3//3 5//5 8//8
|
||||
f 3//3 8//8 18//18
|
||||
f 8//8 20//20 18//18
|
||||
f 16//16 3//3 18//18
|
||||
f 9//9 13//13 6//6
|
||||
f 6//6 13//13 10//10
|
||||
f 11//11 2//2 4//4
|
||||
f 23//23 24//24 4//4
|
||||
f 14//14 13//13 11//11
|
||||
f 12//12 17//17 4//4
|
||||
f 24//24 11//11 4//4
|
||||
f 13//13 2//2 11//11
|
||||
f 15//15 13//13 14//14
|
||||
f 17//17 13//13 15//15
|
||||
f 17//17 23//23 4//4
|
||||
f 17//17 15//15 23//23
|
||||
f 17//17 10//10 13//13
|
||||
f 22//22 23//23 15//15
|
||||
f 4//4 3//3 16//16
|
||||
f 12//12 4//4 16//16
|
||||
f 12//12 16//16 17//17
|
||||
f 17//17 16//16 18//18
|
||||
f 6//6 10//10 17//17
|
||||
f 18//18 6//6 17//17
|
||||
f 7//7 14//14 11//11
|
||||
f 19//19 7//7 11//11
|
||||
f 20//20 15//15 14//14
|
||||
f 7//7 20//20 14//14
|
||||
f 21//21 22//22 20//20
|
||||
f 20//20 22//22 15//15
|
||||
f 22//22 21//21 8//8
|
||||
f 23//23 22//22 8//8
|
||||
f 23//23 8//8 24//24
|
||||
f 24//24 8//8 5//5
|
||||
f 24//24 5//5 11//11
|
||||
f 11//11 5//5 19//19
|
||||
# 48 faces, 0 coords texture
|
||||
|
||||
# End of File
|
||||
48
tests/data/cube_with_hole.obj
Normal file
48
tests/data/cube_with_hole.obj
Normal file
@@ -0,0 +1,48 @@
|
||||
v 0.000000 0.000000 0.000000
|
||||
v 0.000000 0.000000 10.000000
|
||||
v 0.000000 20.000000 0.000000
|
||||
v 0.000000 20.000000 10.000000
|
||||
v 20.000000 0.000000 0.000000
|
||||
v 20.000000 0.000000 10.000000
|
||||
v 5.000000 5.000000 0.000000
|
||||
v 15.000000 5.000000 0.000000
|
||||
v 5.000000 15.000000 0.000000
|
||||
v 20.000000 20.000000 0.000000
|
||||
v 15.000000 15.000000 0.000000
|
||||
v 20.000000 20.000000 10.000000
|
||||
v 5.000000 5.000000 10.000000
|
||||
v 5.000000 15.000000 10.000000
|
||||
v 15.000000 5.000000 10.000000
|
||||
v 15.000000 15.000000 10.000000
|
||||
f 1 2 3
|
||||
f 3 2 4
|
||||
f 2 1 5
|
||||
f 6 2 5
|
||||
f 7 8 5
|
||||
f 9 3 10
|
||||
f 1 3 9
|
||||
f 11 9 10
|
||||
f 1 9 7
|
||||
f 1 7 5
|
||||
f 5 8 10
|
||||
f 8 11 10
|
||||
f 3 4 10
|
||||
f 10 4 12
|
||||
f 13 2 6
|
||||
f 14 4 13
|
||||
f 15 13 6
|
||||
f 4 2 13
|
||||
f 12 4 14
|
||||
f 12 16 6
|
||||
f 12 14 16
|
||||
f 16 15 6
|
||||
f 6 5 10
|
||||
f 12 6 10
|
||||
f 9 14 13
|
||||
f 7 9 13
|
||||
f 11 16 14
|
||||
f 9 11 14
|
||||
f 16 11 15
|
||||
f 15 11 8
|
||||
f 15 8 13
|
||||
f 13 8 7
|
||||
7164
tests/data/extruder_idler.obj
Normal file
7164
tests/data/extruder_idler.obj
Normal file
File diff suppressed because it is too large
Load Diff
7348
tests/data/extruder_idler_quads.obj
Normal file
7348
tests/data/extruder_idler_quads.obj
Normal file
File diff suppressed because it is too large
Load Diff
36702
tests/data/frog_legs.obj
Normal file
36702
tests/data/frog_legs.obj
Normal file
File diff suppressed because it is too large
Load Diff
86
tests/data/ipadstand.obj
Normal file
86
tests/data/ipadstand.obj
Normal file
@@ -0,0 +1,86 @@
|
||||
v 17.434467 -0.000000 9.500000
|
||||
v 14.281480 10.000000 9.500000
|
||||
v 0.000000 0.000000 9.500000
|
||||
v 31.715948 10.000000 9.500000
|
||||
v 62.234474 0.000000 20.000000
|
||||
v 31.715948 10.000000 20.000000
|
||||
v 17.434467 -0.000000 20.000000
|
||||
v 62.234474 10.000000 20.000000
|
||||
v 98.207970 10.000000 0.000000
|
||||
v 98.207970 0.000000 10.000000
|
||||
v 98.207970 0.000000 0.000000
|
||||
v 98.207970 10.000000 20.000000
|
||||
v 98.207970 0.000000 20.000000
|
||||
v 81.660965 -0.000000 10.000000
|
||||
v 90.054985 10.000000 10.000000
|
||||
v 78.507980 10.000000 10.000000
|
||||
v 93.207970 0.000000 10.000000
|
||||
v 14.281480 10.000000 20.000000
|
||||
v 0.000000 0.000000 20.000000
|
||||
v 87.434471 0.000000 20.000000
|
||||
v 84.281479 10.000000 20.000000
|
||||
v 0.000000 10.000000 20.000000
|
||||
v 0.000000 0.000000 0.000000
|
||||
v 0.000000 10.000000 0.000000
|
||||
v 62.234474 0.000000 30.000000
|
||||
v 66.960976 10.000000 30.000000
|
||||
v 62.234474 10.000000 30.000000
|
||||
v 70.113960 0.000000 30.000000
|
||||
v 67.705338 10.000000 28.710720
|
||||
v 71.678711 0.000000 27.289770
|
||||
f 1 2 3
|
||||
f 2 1 4
|
||||
f 5 6 7
|
||||
f 6 5 8
|
||||
f 9 10 11
|
||||
f 10 12 13
|
||||
f 12 10 9
|
||||
f 14 15 16
|
||||
f 15 14 17
|
||||
f 18 3 2
|
||||
f 3 18 19
|
||||
f 20 12 21
|
||||
f 12 20 13
|
||||
f 18 22 19
|
||||
f 22 3 19
|
||||
f 3 22 23
|
||||
f 23 22 24
|
||||
f 9 23 24
|
||||
f 23 9 11
|
||||
f 25 26 27
|
||||
f 26 25 28
|
||||
f 24 2 9
|
||||
f 2 24 22
|
||||
f 2 22 18
|
||||
f 6 16 4
|
||||
f 16 6 8
|
||||
f 16 8 29
|
||||
f 29 8 27
|
||||
f 29 27 26
|
||||
f 9 15 12
|
||||
f 15 9 4
|
||||
f 4 9 2
|
||||
f 15 4 16
|
||||
f 12 15 21
|
||||
f 27 5 25
|
||||
f 5 27 8
|
||||
f 13 17 10
|
||||
f 17 13 20
|
||||
f 30 5 14
|
||||
f 5 30 25
|
||||
f 25 30 28
|
||||
f 10 23 11
|
||||
f 23 10 1
|
||||
f 1 10 17
|
||||
f 1 17 14
|
||||
f 1 14 7
|
||||
f 7 14 5
|
||||
f 3 23 1
|
||||
f 20 15 17
|
||||
f 15 20 21
|
||||
f 16 30 14
|
||||
f 30 26 28
|
||||
f 26 30 16
|
||||
f 26 16 29
|
||||
f 7 4 1
|
||||
f 4 7 6
|
||||
179
tests/data/overhang.obj
Normal file
179
tests/data/overhang.obj
Normal file
@@ -0,0 +1,179 @@
|
||||
v 1364.685059 614.398010 20.002499
|
||||
v 1389.685059 614.398010 20.002499
|
||||
v 1377.185059 589.398987 20.002499
|
||||
v 1389.685059 589.398987 20.002499
|
||||
v 1389.685059 564.398987 20.001499
|
||||
v 1364.685059 589.398987 20.002499
|
||||
v 1364.685059 564.398987 20.001499
|
||||
v 1360.935059 589.398987 17.001499
|
||||
v 1360.935059 585.646973 17.001499
|
||||
v 1357.185059 564.398987 17.001499
|
||||
v 1364.685059 589.398987 17.001499
|
||||
v 1364.685059 571.899963 17.001499
|
||||
v 1364.685059 564.398987 17.001499
|
||||
v 1348.436035 564.398987 17.001499
|
||||
v 1352.809082 589.398987 17.001499
|
||||
v 1357.184082 589.398987 17.001499
|
||||
v 1357.183105 614.398010 17.001499
|
||||
v 1364.685059 606.895996 17.001499
|
||||
v 1364.685059 614.398010 17.001499
|
||||
v 1352.186035 564.398987 20.001499
|
||||
v 1363.654053 589.398987 23.300499
|
||||
v 1359.467041 589.398987 23.300499
|
||||
v 1358.371094 564.398987 23.300499
|
||||
v 1385.561035 564.398987 23.300499
|
||||
v 1373.063110 589.398987 23.300499
|
||||
v 1368.808105 564.398987 23.300499
|
||||
v 1387.623047 589.398987 23.300499
|
||||
v 1387.623047 585.276001 23.300499
|
||||
v 1389.685059 589.398987 23.300499
|
||||
v 1389.685059 572.645996 23.300499
|
||||
v 1389.685059 564.398987 23.300499
|
||||
v 1367.777100 589.398987 23.300499
|
||||
v 1366.747070 564.398987 23.300499
|
||||
v 1354.312012 589.398987 23.300499
|
||||
v 1352.186035 564.398987 23.300499
|
||||
v 1389.685059 614.398010 23.300499
|
||||
v 1377.317017 614.398010 23.300499
|
||||
v 1381.439087 589.398987 23.300499
|
||||
v 1368.807007 614.398010 23.300499
|
||||
v 1368.808105 589.398987 23.300499
|
||||
v 1356.439087 614.398010 23.300499
|
||||
v 1357.405029 589.398987 23.300499
|
||||
v 1360.562012 614.398010 23.300499
|
||||
v 1348.705078 614.398010 23.300499
|
||||
v 1350.445068 589.398987 23.300499
|
||||
v 1389.685059 606.153015 23.300499
|
||||
v 1347.352051 589.398987 23.300499
|
||||
v 1346.560059 589.398987 23.300499
|
||||
v 1346.560059 594.159912 17.001499
|
||||
v 1346.560059 589.398987 17.001499
|
||||
v 1346.560059 605.250427 23.300499
|
||||
v 1346.560059 614.398010 23.300499
|
||||
v 1346.560059 614.398010 20.825829
|
||||
v 1346.560059 614.398010 17.001499
|
||||
v 1346.560059 564.398987 19.101339
|
||||
v 1346.560059 567.548584 23.300499
|
||||
v 1346.560059 564.398987 17.002033
|
||||
v 1346.560059 564.398987 23.001850
|
||||
v 1346.560059 564.398987 23.300499
|
||||
v 1346.560059 575.118958 17.001499
|
||||
v 1346.560059 574.754028 23.300499
|
||||
f 1 2 3
|
||||
f 3 4 5
|
||||
f 3 6 1
|
||||
f 5 7 3
|
||||
f 3 7 6
|
||||
f 3 2 4
|
||||
f 8 9 10
|
||||
f 11 10 9
|
||||
f 12 10 11
|
||||
f 13 10 12
|
||||
f 10 14 15
|
||||
f 8 16 17
|
||||
f 11 18 1
|
||||
f 11 1 6
|
||||
f 13 12 7
|
||||
f 19 17 1
|
||||
f 7 20 14
|
||||
f 7 14 10
|
||||
f 10 13 7
|
||||
f 18 19 1
|
||||
f 12 11 6
|
||||
f 12 6 7
|
||||
f 15 17 16
|
||||
f 18 8 19
|
||||
f 17 19 8
|
||||
f 15 16 10
|
||||
f 8 10 16
|
||||
f 8 18 9
|
||||
f 11 9 18
|
||||
f 21 22 23
|
||||
f 24 25 26
|
||||
f 27 24 28
|
||||
f 29 28 24
|
||||
f 30 29 24
|
||||
f 31 30 24
|
||||
f 26 32 33
|
||||
f 23 34 35
|
||||
f 36 37 38
|
||||
f 25 39 40
|
||||
f 22 41 42
|
||||
f 39 43 21
|
||||
f 34 44 45
|
||||
f 7 5 24
|
||||
f 7 24 26
|
||||
f 37 36 2
|
||||
f 2 1 39
|
||||
f 2 39 37
|
||||
f 30 31 5
|
||||
f 26 33 7
|
||||
f 41 43 1
|
||||
f 36 46 2
|
||||
f 5 4 29
|
||||
f 5 29 30
|
||||
f 4 2 46
|
||||
f 4 46 29
|
||||
f 23 35 20
|
||||
f 20 7 33
|
||||
f 20 33 23
|
||||
f 43 39 1
|
||||
f 31 24 5
|
||||
f 1 17 44
|
||||
f 1 44 41
|
||||
f 25 38 37
|
||||
f 39 25 37
|
||||
f 25 24 38
|
||||
f 38 24 27
|
||||
f 23 33 21
|
||||
f 21 33 32
|
||||
f 34 42 41
|
||||
f 44 34 41
|
||||
f 46 36 27
|
||||
f 38 27 36
|
||||
f 34 45 35
|
||||
f 45 44 47
|
||||
f 21 43 22
|
||||
f 41 22 43
|
||||
f 32 40 39
|
||||
f 21 32 39
|
||||
f 34 23 42
|
||||
f 22 42 23
|
||||
f 32 26 40
|
||||
f 25 40 26
|
||||
f 27 28 46
|
||||
f 29 46 28
|
||||
f 48 49 50
|
||||
f 48 51 49
|
||||
f 52 49 51
|
||||
f 53 49 52
|
||||
f 54 49 53
|
||||
f 55 56 57
|
||||
f 58 56 55
|
||||
f 59 56 58
|
||||
f 50 60 48
|
||||
f 61 57 56
|
||||
f 60 57 61
|
||||
f 61 48 60
|
||||
f 49 54 17
|
||||
f 57 14 20
|
||||
f 55 57 20
|
||||
f 57 60 14
|
||||
f 60 50 15
|
||||
f 60 15 14
|
||||
f 50 49 17
|
||||
f 50 17 15
|
||||
f 45 47 61
|
||||
f 45 61 56
|
||||
f 52 51 44
|
||||
f 20 35 59
|
||||
f 20 59 58
|
||||
f 54 53 17
|
||||
f 44 17 53
|
||||
f 44 53 52
|
||||
f 58 55 20
|
||||
f 48 61 47
|
||||
f 56 59 35
|
||||
f 56 35 45
|
||||
f 51 48 47
|
||||
f 51 47 44
|
||||
5981
tests/data/prusaparts.cpp
Normal file
5981
tests/data/prusaparts.cpp
Normal file
File diff suppressed because it is too large
Load Diff
14
tests/data/prusaparts.hpp
Normal file
14
tests/data/prusaparts.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef PRUSAPARTS_H
|
||||
#define PRUSAPARTS_H
|
||||
|
||||
#include <vector>
|
||||
#include <libslic3r/ExPolygon.hpp>
|
||||
|
||||
using TestData = std::vector<Slic3r::Polygon>;
|
||||
using TestDataEx = std::vector<Slic3r::ExPolygons>;
|
||||
|
||||
extern const TestData PRUSA_PART_POLYGONS;
|
||||
extern const TestData PRUSA_STEGOSAUR_POLYGONS;
|
||||
extern const TestDataEx PRUSA_PART_POLYGONS_EX;
|
||||
|
||||
#endif // PRUSAPARTS_H
|
||||
11
tests/data/pyramid.obj
Normal file
11
tests/data/pyramid.obj
Normal file
@@ -0,0 +1,11 @@
|
||||
v 10.000000 10.000000 40.000000
|
||||
v 0.000000 0.000000 0.000000
|
||||
v 20.000000 0.000000 0.000000
|
||||
v 20.000000 20.000000 0.000000
|
||||
v 0.000000 20.000000 0.000000
|
||||
f 1 2 3
|
||||
f 1 4 5
|
||||
f 4 2 5
|
||||
f 2 4 3
|
||||
f 4 1 3
|
||||
f 5 2 1
|
||||
46
tests/data/simplification.obj
Normal file
46
tests/data/simplification.obj
Normal file
@@ -0,0 +1,46 @@
|
||||
v 39.349007 -54.069000 -199.819000
|
||||
v 39.489006 -54.029007 -199.815002
|
||||
v 39.419006 -53.993011 -199.769012
|
||||
v 39.629005 -53.975006 -199.815002
|
||||
v 39.639008 -53.947006 -199.805023
|
||||
v 39.651001 -53.919006 -199.795013
|
||||
v 39.807007 -53.863007 -199.796997
|
||||
v 39.729004 -53.891006 -199.796997
|
||||
v 39.727005 -53.935013 -199.813019
|
||||
v 39.767006 -53.899002 -199.805023
|
||||
v 39.871002 -53.835007 -199.801025
|
||||
v 39.443001 -53.829010 -199.878998
|
||||
v 39.523003 -53.965012 -199.827026
|
||||
v 39.807007 -53.863007 -199.796997
|
||||
v 39.833008 -53.723007 -199.723022
|
||||
v 39.759003 -53.822998 -199.822998
|
||||
v 39.867004 -53.845001 -199.805023
|
||||
v 39.937004 -53.805008 -199.805023
|
||||
f 1 2 3
|
||||
f 4 5 2
|
||||
f 2 6 3
|
||||
f 7 8 4
|
||||
f 9 10 4
|
||||
f 10 9 11
|
||||
f 12 2 1
|
||||
f 13 6 4
|
||||
f 13 4 2
|
||||
f 8 7 9
|
||||
f 6 9 4
|
||||
f 6 14 15
|
||||
f 16 14 6
|
||||
f 17 18 9
|
||||
f 3 6 15
|
||||
f 12 16 6
|
||||
f 12 6 13
|
||||
f 12 13 2
|
||||
f 5 4 8
|
||||
f 6 8 9
|
||||
f 5 6 2
|
||||
f 6 5 8
|
||||
f 17 9 7
|
||||
f 7 11 17
|
||||
f 18 11 9
|
||||
f 11 18 17
|
||||
f 10 7 4
|
||||
f 7 10 11
|
||||
204
tests/data/sloping_hole.obj
Normal file
204
tests/data/sloping_hole.obj
Normal file
@@ -0,0 +1,204 @@
|
||||
v -20.000000 -20.000000 -5.000000
|
||||
v -20.000000 -20.000000 5.000000
|
||||
v -20.000000 20.000000 -5.000000
|
||||
v -20.000000 20.000000 5.000000
|
||||
v 20.000000 -20.000000 -5.000000
|
||||
v 20.000000 -20.000000 5.000000
|
||||
v 4.462940 7.431450 -5.000000
|
||||
v 20.000000 20.000000 -5.000000
|
||||
v -19.142099 0.000000 -5.000000
|
||||
v -18.833099 -2.079120 -5.000000
|
||||
v -17.919500 -4.067370 -5.000000
|
||||
v -16.441200 -5.877850 -5.000000
|
||||
v -14.462900 -7.431450 -5.000000
|
||||
v -12.071100 -8.660250 -5.000000
|
||||
v -9.370160 -9.510560 -5.000000
|
||||
v -3.521740 -9.945220 -5.000000
|
||||
v -6.478260 -9.945220 -5.000000
|
||||
v -0.629840 -9.510560 -5.000000
|
||||
v 2.071070 -8.660250 -5.000000
|
||||
v 6.441230 -5.877850 -5.000000
|
||||
v 4.462940 -7.431450 -5.000000
|
||||
v -12.071100 8.660250 -5.000000
|
||||
v -9.370160 9.510560 -5.000000
|
||||
v 7.919480 -4.067370 -5.000000
|
||||
v 8.833100 -2.079120 -5.000000
|
||||
v -6.478260 9.945220 -5.000000
|
||||
v -0.629840 9.510560 -5.000000
|
||||
v 2.071070 8.660250 -5.000000
|
||||
v 9.142140 0.000000 -5.000000
|
||||
v 8.833100 2.079120 -5.000000
|
||||
v -3.521740 9.945220 -5.000000
|
||||
v 7.919480 4.067370 -5.000000
|
||||
v 6.441230 5.877850 -5.000000
|
||||
v -14.462900 7.431450 -5.000000
|
||||
v -16.441200 5.877850 -5.000000
|
||||
v -17.919500 4.067370 -5.000000
|
||||
v -18.833099 2.079120 -5.000000
|
||||
v 20.000000 20.000000 5.000000
|
||||
v 3.521740 -9.945220 5.000000
|
||||
v -8.833100 -2.079120 5.000000
|
||||
v -9.142140 0.000000 5.000000
|
||||
v -8.833100 2.079120 5.000000
|
||||
v 6.478260 -9.945220 5.000000
|
||||
v -7.919480 4.067370 5.000000
|
||||
v -6.441230 5.877850 5.000000
|
||||
v -4.462940 7.431450 5.000000
|
||||
v -2.071070 8.660250 5.000000
|
||||
v 0.629840 9.510560 5.000000
|
||||
v 12.071100 -8.660250 5.000000
|
||||
v 9.370160 -9.510560 5.000000
|
||||
v 3.521740 9.945220 5.000000
|
||||
v 6.478260 9.945220 5.000000
|
||||
v 9.370160 9.510560 5.000000
|
||||
v 12.071100 8.660250 5.000000
|
||||
v 14.462900 7.431450 5.000000
|
||||
v 16.441200 -5.877850 5.000000
|
||||
v 14.462900 -7.431450 5.000000
|
||||
v 16.441200 5.877850 5.000000
|
||||
v 17.919500 4.067370 5.000000
|
||||
v 18.833099 -2.079120 5.000000
|
||||
v 17.919500 -4.067370 5.000000
|
||||
v 18.833099 2.079120 5.000000
|
||||
v 19.142099 0.000000 5.000000
|
||||
v 0.629840 -9.510560 5.000000
|
||||
v -2.071070 -8.660250 5.000000
|
||||
v -4.462940 -7.431450 5.000000
|
||||
v -6.441230 -5.877850 5.000000
|
||||
v -7.919480 -4.067370 5.000000
|
||||
f 1 2 3
|
||||
f 3 2 4
|
||||
f 2 1 5
|
||||
f 6 2 5
|
||||
f 7 3 8
|
||||
f 1 3 9
|
||||
f 1 9 10
|
||||
f 1 10 11
|
||||
f 1 11 12
|
||||
f 1 12 13
|
||||
f 1 13 14
|
||||
f 1 14 5
|
||||
f 14 15 5
|
||||
f 16 5 17
|
||||
f 18 5 16
|
||||
f 19 5 18
|
||||
f 20 5 21
|
||||
f 19 21 5
|
||||
f 22 3 23
|
||||
f 5 20 24
|
||||
f 5 24 8
|
||||
f 24 25 8
|
||||
f 23 3 26
|
||||
f 27 3 28
|
||||
f 29 30 8
|
||||
f 26 3 31
|
||||
f 30 32 8
|
||||
f 31 3 27
|
||||
f 32 33 8
|
||||
f 28 3 7
|
||||
f 33 7 8
|
||||
f 29 8 25
|
||||
f 34 3 22
|
||||
f 35 3 34
|
||||
f 36 3 35
|
||||
f 37 3 36
|
||||
f 9 3 37
|
||||
f 17 5 15
|
||||
f 3 4 8
|
||||
f 8 4 38
|
||||
f 39 2 6
|
||||
f 4 2 40
|
||||
f 4 40 41
|
||||
f 4 41 42
|
||||
f 43 39 6
|
||||
f 4 42 44
|
||||
f 4 44 45
|
||||
f 38 4 46
|
||||
f 38 46 47
|
||||
f 38 47 48
|
||||
f 49 50 6
|
||||
f 38 48 51
|
||||
f 50 43 6
|
||||
f 38 51 52
|
||||
f 38 52 53
|
||||
f 38 53 54
|
||||
f 38 54 55
|
||||
f 56 57 6
|
||||
f 38 55 58
|
||||
f 38 58 59
|
||||
f 60 61 6
|
||||
f 38 59 62
|
||||
f 38 63 6
|
||||
f 38 62 63
|
||||
f 63 60 6
|
||||
f 61 56 6
|
||||
f 64 2 39
|
||||
f 65 2 64
|
||||
f 66 2 65
|
||||
f 67 2 66
|
||||
f 68 2 67
|
||||
f 40 2 68
|
||||
f 45 46 4
|
||||
f 57 49 6
|
||||
f 6 5 8
|
||||
f 38 6 8
|
||||
f 42 41 37
|
||||
f 37 41 9
|
||||
f 40 10 41
|
||||
f 41 10 9
|
||||
f 44 42 36
|
||||
f 36 42 37
|
||||
f 45 44 35
|
||||
f 35 44 36
|
||||
f 34 46 45
|
||||
f 35 34 45
|
||||
f 22 47 46
|
||||
f 34 22 46
|
||||
f 23 48 47
|
||||
f 22 23 47
|
||||
f 26 51 48
|
||||
f 23 26 48
|
||||
f 31 52 51
|
||||
f 26 31 51
|
||||
f 27 53 52
|
||||
f 31 27 52
|
||||
f 28 54 53
|
||||
f 27 28 53
|
||||
f 7 55 54
|
||||
f 28 7 54
|
||||
f 33 58 55
|
||||
f 7 33 55
|
||||
f 32 59 58
|
||||
f 33 32 58
|
||||
f 30 62 59
|
||||
f 32 30 59
|
||||
f 29 63 62
|
||||
f 30 29 62
|
||||
f 60 63 29
|
||||
f 25 60 29
|
||||
f 61 60 25
|
||||
f 24 61 25
|
||||
f 56 61 24
|
||||
f 20 56 24
|
||||
f 56 20 57
|
||||
f 57 20 21
|
||||
f 57 21 49
|
||||
f 49 21 19
|
||||
f 49 19 50
|
||||
f 50 19 18
|
||||
f 50 18 43
|
||||
f 43 18 16
|
||||
f 43 16 39
|
||||
f 39 16 17
|
||||
f 39 17 64
|
||||
f 64 17 15
|
||||
f 64 15 65
|
||||
f 65 15 14
|
||||
f 65 14 66
|
||||
f 66 14 13
|
||||
f 66 13 67
|
||||
f 67 13 12
|
||||
f 67 12 68
|
||||
f 68 12 11
|
||||
f 68 11 40
|
||||
f 40 11 10
|
||||
14
tests/data/small_dorito.obj
Normal file
14
tests/data/small_dorito.obj
Normal file
@@ -0,0 +1,14 @@
|
||||
v 6.000589 -22.998209 0.000000
|
||||
v 22.001024 -49.999874 0.000000
|
||||
v -9.999578 -49.999870 0.000000
|
||||
v 6.000714 -32.237164 28.001925
|
||||
v 11.167055 -37.972702 18.960167
|
||||
v 6.000602 -26.539246 10.732185
|
||||
f 1 2 3
|
||||
f 4 5 6
|
||||
f 3 2 5
|
||||
f 3 5 4
|
||||
f 3 4 6
|
||||
f 3 6 1
|
||||
f 6 5 2
|
||||
f 6 2 1
|
||||
144
tests/data/two_hollow_squares.obj
Normal file
144
tests/data/two_hollow_squares.obj
Normal file
@@ -0,0 +1,144 @@
|
||||
v 66.713348 104.286667 0.000000
|
||||
v 66.713348 95.713333 0.000000
|
||||
v 65.666687 94.666672 0.000000
|
||||
v 75.286682 95.713333 0.000000
|
||||
v 76.333344 105.333336 0.000000
|
||||
v 76.333344 94.666672 0.000000
|
||||
v 65.666687 105.333328 0.000000
|
||||
v 75.286682 104.286667 0.000000
|
||||
v 71.106682 104.586662 2.800000
|
||||
v 66.413353 104.586662 2.800000
|
||||
v 75.586685 104.586662 2.800000
|
||||
v 66.413353 99.893333 2.800000
|
||||
v 66.413353 95.413338 2.800000
|
||||
v 71.106682 95.413338 2.800000
|
||||
v 75.586685 95.413338 2.800000
|
||||
v 75.586685 100.106667 2.800000
|
||||
v 74.540016 103.540001 2.800000
|
||||
v 70.032013 103.540001 2.800000
|
||||
v 67.460007 103.540001 2.800000
|
||||
v 67.460007 100.968002 2.800000
|
||||
v 67.460007 96.459999 2.800000
|
||||
v 74.540016 99.031998 2.800000
|
||||
v 74.540016 96.459999 2.800000
|
||||
v 70.032013 96.459999 2.800000
|
||||
v 123.666718 94.666672 0.000000
|
||||
v 134.333313 94.666672 0.000000
|
||||
v 124.413361 95.413338 2.800000
|
||||
v 129.106674 95.413338 2.800000
|
||||
v 133.586670 95.413338 2.800000
|
||||
v 123.666718 105.333328 0.000000
|
||||
v 124.413361 104.586662 2.800000
|
||||
v 124.413361 99.893333 2.800000
|
||||
v 134.333313 105.333328 0.000000
|
||||
v 129.106674 104.586662 2.800000
|
||||
v 133.586670 104.586662 2.800000
|
||||
v 133.586670 100.106667 2.800000
|
||||
v 124.713318 104.286667 0.000000
|
||||
v 124.713318 95.713333 0.000000
|
||||
v 133.286713 95.713333 0.000000
|
||||
v 133.286713 104.286667 0.000000
|
||||
v 132.540024 103.540001 2.800000
|
||||
v 128.032028 103.540009 2.800000
|
||||
v 125.460007 103.540001 2.800000
|
||||
v 125.460007 100.968002 2.800000
|
||||
v 125.460007 96.459999 2.800000
|
||||
v 132.540024 99.031998 2.800000
|
||||
v 132.540024 96.459999 2.800000
|
||||
v 128.032028 96.459999 2.800000
|
||||
f 1 2 3
|
||||
f 4 5 6
|
||||
f 7 5 1
|
||||
f 7 1 3
|
||||
f 3 2 6
|
||||
f 8 5 4
|
||||
f 2 4 6
|
||||
f 1 5 8
|
||||
f 5 7 9
|
||||
f 7 10 9
|
||||
f 5 9 11
|
||||
f 7 3 10
|
||||
f 3 12 10
|
||||
f 3 13 12
|
||||
f 3 6 13
|
||||
f 6 14 13
|
||||
f 6 15 14
|
||||
f 5 11 16
|
||||
f 6 5 15
|
||||
f 5 16 15
|
||||
f 8 17 18
|
||||
f 1 8 19
|
||||
f 8 18 19
|
||||
f 2 20 21
|
||||
f 2 1 20
|
||||
f 1 19 20
|
||||
f 8 4 22
|
||||
f 4 23 22
|
||||
f 8 22 17
|
||||
f 4 24 23
|
||||
f 4 2 24
|
||||
f 2 21 24
|
||||
f 25 26 27
|
||||
f 26 28 27
|
||||
f 26 29 28
|
||||
f 30 25 31
|
||||
f 25 32 31
|
||||
f 25 27 32
|
||||
f 33 30 34
|
||||
f 30 31 34
|
||||
f 33 34 35
|
||||
f 33 35 36
|
||||
f 26 33 29
|
||||
f 33 36 29
|
||||
f 37 38 25
|
||||
f 39 33 26
|
||||
f 30 33 37
|
||||
f 30 37 25
|
||||
f 25 38 26
|
||||
f 40 33 39
|
||||
f 38 39 26
|
||||
f 37 33 40
|
||||
f 40 41 42
|
||||
f 37 40 43
|
||||
f 40 42 43
|
||||
f 38 44 45
|
||||
f 38 37 44
|
||||
f 37 43 44
|
||||
f 40 39 46
|
||||
f 39 47 46
|
||||
f 40 46 41
|
||||
f 39 48 47
|
||||
f 39 38 48
|
||||
f 38 45 48
|
||||
f 17 9 10
|
||||
f 17 11 9
|
||||
f 11 17 16
|
||||
f 16 17 22
|
||||
f 23 16 22
|
||||
f 16 23 15
|
||||
f 23 24 15
|
||||
f 24 21 15
|
||||
f 18 17 10
|
||||
f 19 18 10
|
||||
f 20 19 10
|
||||
f 20 10 12
|
||||
f 20 12 21
|
||||
f 14 15 21
|
||||
f 21 12 13
|
||||
f 14 21 13
|
||||
f 42 41 31
|
||||
f 43 42 31
|
||||
f 44 43 31
|
||||
f 44 31 32
|
||||
f 44 32 45
|
||||
f 28 29 45
|
||||
f 45 32 27
|
||||
f 28 45 27
|
||||
f 41 34 31
|
||||
f 41 35 34
|
||||
f 35 41 36
|
||||
f 36 41 46
|
||||
f 47 36 46
|
||||
f 36 47 29
|
||||
f 47 48 29
|
||||
f 48 45 29
|
||||
@@ -14,7 +14,7 @@
|
||||
using namespace Slic3r;
|
||||
|
||||
std::unique_ptr<CoolingBuffer> make_cooling_buffer(
|
||||
GCode &gcode,
|
||||
GCodeGenerator &gcode,
|
||||
const DynamicPrintConfig &config = DynamicPrintConfig{},
|
||||
const std::vector<unsigned int> &extruder_ids = { 0 })
|
||||
{
|
||||
@@ -65,7 +65,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
|
||||
const double print_time = 100. / (3000. / 60.);
|
||||
//FIXME slowdown_below_layer_time is rounded down significantly from 1.8s to 1s.
|
||||
config.set_deserialize_strict({ { "slowdown_below_layer_time", { int(print_time * 0.999) } } });
|
||||
GCode gcodegen;
|
||||
GCodeGenerator gcodegen;
|
||||
auto buffer = make_cooling_buffer(gcodegen, config);
|
||||
std::string gcode = buffer->process_layer("G1 F3000;_EXTRUDE_SET_SPEED\nG1 X100 E1", 0, true);
|
||||
bool speed_not_altered = gcode.find("F3000") != gcode.npos;
|
||||
@@ -83,7 +83,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
|
||||
// Print time of gcode.
|
||||
const double print_time = 50. / (2500. / 60.) + 100. / (3000. / 60.) + 4. / (400. / 60.);
|
||||
config.set_deserialize_strict({ { "slowdown_below_layer_time", { int(print_time * 1.001) } } });
|
||||
GCode gcodegen;
|
||||
GCodeGenerator gcodegen;
|
||||
auto buffer = make_cooling_buffer(gcodegen, config);
|
||||
std::string gcode = buffer->process_layer(gcode_src, 0, true);
|
||||
THEN("speed is altered when elapsed time is lower than slowdown threshold") {
|
||||
@@ -106,7 +106,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
|
||||
{ "fan_below_layer_time" , int(print_time1 * 0.88) },
|
||||
{ "slowdown_below_layer_time" , int(print_time1 * 0.99) }
|
||||
});
|
||||
GCode gcodegen;
|
||||
GCodeGenerator gcodegen;
|
||||
auto buffer = make_cooling_buffer(gcodegen, config);
|
||||
std::string gcode = buffer->process_layer(gcode1, 0, true);
|
||||
bool fan_not_activated = gcode.find("M106") == gcode.npos;
|
||||
@@ -119,7 +119,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
|
||||
{ "fan_below_layer_time", { int(print_time2 + 1.), int(print_time2 + 1.) } },
|
||||
{ "slowdown_below_layer_time", { int(print_time2 + 2.), int(print_time2 + 2.) } }
|
||||
});
|
||||
GCode gcodegen;
|
||||
GCodeGenerator gcodegen;
|
||||
auto buffer = make_cooling_buffer(gcodegen, config, { 0, 1 });
|
||||
std::string gcode = buffer->process_layer(gcode1 + "T1\nG1 X0 E1 F3000\n", 0, true);
|
||||
THEN("fan is activated for the 1st tool") {
|
||||
@@ -134,7 +134,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
|
||||
WHEN("G-code block 2") {
|
||||
THEN("slowdown is computed on all objects printing at the same Z") {
|
||||
config.set_deserialize_strict({ { "slowdown_below_layer_time", int(print_time2 * 0.99) } });
|
||||
GCode gcodegen;
|
||||
GCodeGenerator gcodegen;
|
||||
auto buffer = make_cooling_buffer(gcodegen, config);
|
||||
std::string gcode = buffer->process_layer(gcode2, 0, true);
|
||||
bool ok = gcode.find("F3000") != gcode.npos;
|
||||
@@ -145,7 +145,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
|
||||
{ "fan_below_layer_time", int(print_time2 * 0.65) },
|
||||
{ "slowdown_below_layer_time", int(print_time2 * 0.7) }
|
||||
});
|
||||
GCode gcodegen;
|
||||
GCodeGenerator gcodegen;
|
||||
auto buffer = make_cooling_buffer(gcodegen, config);
|
||||
// use an elapsed time which is < the threshold but greater than it when summed twice
|
||||
std::string gcode = buffer->process_layer(gcode2, 0, true) + buffer->process_layer(gcode2, 1, true);
|
||||
@@ -158,7 +158,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") {
|
||||
{ "fan_below_layer_time", int(print_time2 + 1) },
|
||||
{ "slowdown_below_layer_time", int(print_time2 + 1) }
|
||||
});
|
||||
GCode gcodegen;
|
||||
GCodeGenerator gcodegen;
|
||||
auto buffer = make_cooling_buffer(gcodegen, config);
|
||||
// use an elapsed time which is < the threshold but greater than it when summed twice
|
||||
std::string gcode = buffer->process_layer(gcode2, 0, true) + buffer->process_layer(gcode2, 1, true);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -21,7 +21,7 @@ static inline Slic3r::Point random_point(float LO=-50, float HI=50)
|
||||
// build a sample extrusion entity collection with random start and end points.
|
||||
static Slic3r::ExtrusionPath random_path(size_t length = 20, float LO = -50, float HI = 50)
|
||||
{
|
||||
ExtrusionPath t { ExtrusionRole::Perimeter, 1.0, 1.0, 1.0 };
|
||||
ExtrusionPath t{ ExtrusionAttributes{ ExtrusionRole::Perimeter, ExtrusionFlow{ 1.0, 1.0, 1.0 } } };
|
||||
for (size_t j = 0; j < length; ++ j)
|
||||
t.polyline.append(random_point(LO, HI));
|
||||
return t;
|
||||
@@ -37,9 +37,8 @@ static Slic3r::ExtrusionPaths random_paths(size_t count = 10, size_t length = 20
|
||||
|
||||
SCENARIO("ExtrusionPath", "[ExtrusionEntity]") {
|
||||
GIVEN("Simple path") {
|
||||
Slic3r::ExtrusionPath path{ ExtrusionRole::ExternalPerimeter };
|
||||
path.polyline = { { 100, 100 }, { 200, 100 }, { 200, 200 } };
|
||||
path.mm3_per_mm = 1.;
|
||||
Slic3r::ExtrusionPath path{ { { 100, 100 }, { 200, 100 }, { 200, 200 } },
|
||||
ExtrusionAttributes{ ExtrusionRole::ExternalPerimeter, ExtrusionFlow{ 1., -1.f, -1.f } } };
|
||||
THEN("first point") {
|
||||
REQUIRE(path.first_point() == path.polyline.front());
|
||||
}
|
||||
@@ -52,10 +51,7 @@ SCENARIO("ExtrusionPath", "[ExtrusionEntity]") {
|
||||
|
||||
static ExtrusionPath new_extrusion_path(const Polyline &polyline, ExtrusionRole role, double mm3_per_mm)
|
||||
{
|
||||
ExtrusionPath path(role);
|
||||
path.polyline = polyline;
|
||||
path.mm3_per_mm = 1.;
|
||||
return path;
|
||||
return { polyline, ExtrusionAttributes{ role, ExtrusionFlow{ mm3_per_mm, -1.f, -1.f } } };
|
||||
}
|
||||
|
||||
SCENARIO("ExtrusionLoop", "[ExtrusionEntity]")
|
||||
@@ -67,6 +63,7 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]")
|
||||
loop.paths.emplace_back(new_extrusion_path(square.split_at_first_point(), ExtrusionRole::ExternalPerimeter, 1.));
|
||||
THEN("polygon area") {
|
||||
REQUIRE(loop.polygon().area() == Approx(square.area()));
|
||||
REQUIRE(loop.area() == Approx(square.area()));
|
||||
}
|
||||
THEN("loop length") {
|
||||
REQUIRE(loop.length() == Approx(square.length()));
|
||||
@@ -110,6 +107,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]")
|
||||
loop.paths.emplace_back(new_extrusion_path(polyline1, ExtrusionRole::ExternalPerimeter, 1.));
|
||||
loop.paths.emplace_back(new_extrusion_path(polyline2, ExtrusionRole::OverhangPerimeter, 1.));
|
||||
|
||||
THEN("area") {
|
||||
REQUIRE(loop.area() == Approx(loop.polygon().area()));
|
||||
}
|
||||
double tot_len = polyline1.length() + polyline2.length();
|
||||
THEN("length") {
|
||||
REQUIRE(loop.length() == Approx(tot_len));
|
||||
@@ -212,6 +212,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]")
|
||||
loop.paths.emplace_back(new_extrusion_path(polyline3, ExtrusionRole::ExternalPerimeter, 1.));
|
||||
loop.paths.emplace_back(new_extrusion_path(polyline4, ExtrusionRole::OverhangPerimeter, 1.));
|
||||
double len = loop.length();
|
||||
THEN("area") {
|
||||
REQUIRE(loop.area() == Approx(loop.polygon().area()));
|
||||
}
|
||||
WHEN("splitting at vertex") {
|
||||
Point point(4821067, 9321068);
|
||||
if (! loop.split_at_vertex(point))
|
||||
@@ -234,6 +237,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]")
|
||||
Polyline { { 15896783, 15868739 }, { 24842049, 12117558 }, { 33853238, 15801279 }, { 37591780, 24780128 }, { 37591780, 24844970 },
|
||||
{ 33853231, 33825297 }, { 24842049, 37509013 }, { 15896798, 33757841 }, { 12211841, 24812544 }, { 15896783, 15868739 } },
|
||||
ExtrusionRole::ExternalPerimeter, 1.));
|
||||
THEN("area") {
|
||||
REQUIRE(loop.area() == Approx(loop.polygon().area()));
|
||||
}
|
||||
double len = loop.length();
|
||||
THEN("split_at() preserves total length") {
|
||||
loop.split_at({ 15896783, 15868739 }, false, 0);
|
||||
@@ -378,23 +384,27 @@ TEST_CASE("ExtrusionEntityCollection: Chained path", "[ExtrusionEntity]") {
|
||||
REQUIRE(chained == test.chained);
|
||||
ExtrusionEntityCollection unchained_extrusions;
|
||||
extrusion_entities_append_paths(unchained_extrusions.entities, test.unchained,
|
||||
ExtrusionRole::InternalInfill, 0., 0.4f, 0.3f);
|
||||
ExtrusionAttributes{ ExtrusionRole::InternalInfill, ExtrusionFlow{ 0., 0.4f, 0.3f } });
|
||||
THEN("Chaining works") {
|
||||
ExtrusionEntityCollection chained_extrusions = unchained_extrusions.chained_path_from(test.initial_point);
|
||||
REQUIRE(chained_extrusions.entities.size() == test.chained.size());
|
||||
for (size_t i = 0; i < chained_extrusions.entities.size(); ++ i) {
|
||||
ExtrusionEntityReferences chained_extrusions = chain_extrusion_references(unchained_extrusions, &test.initial_point);
|
||||
REQUIRE(chained_extrusions.size() == test.chained.size());
|
||||
for (size_t i = 0; i < chained_extrusions.size(); ++ i) {
|
||||
const Points &p1 = test.chained[i].points;
|
||||
const Points &p2 = dynamic_cast<const ExtrusionPath*>(chained_extrusions.entities[i])->polyline.points;
|
||||
Points p2 = chained_extrusions[i].cast<ExtrusionPath>()->polyline.points;
|
||||
if (chained_extrusions[i].flipped())
|
||||
std::reverse(p2.begin(), p2.end());
|
||||
REQUIRE(p1 == p2);
|
||||
}
|
||||
}
|
||||
THEN("Chaining produces no change with no_sort") {
|
||||
unchained_extrusions.no_sort = true;
|
||||
ExtrusionEntityCollection chained_extrusions = unchained_extrusions.chained_path_from(test.initial_point);
|
||||
REQUIRE(chained_extrusions.entities.size() == test.unchained.size());
|
||||
for (size_t i = 0; i < chained_extrusions.entities.size(); ++ i) {
|
||||
ExtrusionEntityReferences chained_extrusions = chain_extrusion_references(unchained_extrusions, &test.initial_point);
|
||||
REQUIRE(chained_extrusions.size() == test.unchained.size());
|
||||
for (size_t i = 0; i < chained_extrusions.size(); ++ i) {
|
||||
const Points &p1 = test.unchained[i].points;
|
||||
const Points &p2 = dynamic_cast<const ExtrusionPath*>(chained_extrusions.entities[i])->polyline.points;
|
||||
Points p2 = chained_extrusions[i].cast<ExtrusionPath>()->polyline.points;
|
||||
if (chained_extrusions[i].flipped())
|
||||
std::reverse(p2.begin(), p2.end());
|
||||
REQUIRE(p1 == p2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
#include "libslic3r/GCode.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::GCode::Impl;
|
||||
|
||||
SCENARIO("Origin manipulation", "[GCode]") {
|
||||
Slic3r::GCode gcodegen;
|
||||
Slic3r::GCodeGenerator gcodegen;
|
||||
WHEN("set_origin to (10,0)") {
|
||||
gcodegen.set_origin(Vec2d(10,0));
|
||||
REQUIRE(gcodegen.origin() == Vec2d(10, 0));
|
||||
@@ -20,3 +21,220 @@ SCENARIO("Origin manipulation", "[GCode]") {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ApproxEqualsPoints : public Catch::MatcherBase<Points> {
|
||||
ApproxEqualsPoints(const Points& expected, unsigned tolerance): expected(expected), tolerance(tolerance) {}
|
||||
bool match(const Points& points) const override {
|
||||
if (points.size() != expected.size()) {
|
||||
return false;
|
||||
}
|
||||
for (auto i = 0u; i < points.size(); ++i) {
|
||||
const Point& point = points[i];
|
||||
const Point& expected_point = this->expected[i];
|
||||
if (
|
||||
std::abs(point.x() - expected_point.x()) > this->tolerance
|
||||
|| std::abs(point.y() - expected_point.y()) > this->tolerance
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
std::string describe() const override {
|
||||
std::stringstream ss;
|
||||
ss << std::endl;
|
||||
for (const Point& point : expected) {
|
||||
ss << "(" << point.x() << ", " << point.y() << ")" << std::endl;
|
||||
}
|
||||
ss << "With tolerance: " << this->tolerance;
|
||||
|
||||
return "Equals " + ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
Points expected;
|
||||
unsigned tolerance;
|
||||
};
|
||||
|
||||
Points get_points(const std::vector<DistancedPoint>& result) {
|
||||
Points result_points;
|
||||
std::transform(
|
||||
result.begin(),
|
||||
result.end(),
|
||||
std::back_inserter(result_points),
|
||||
[](const DistancedPoint& point){
|
||||
return point.point;
|
||||
}
|
||||
);
|
||||
return result_points;
|
||||
}
|
||||
|
||||
std::vector<double> get_distances(const std::vector<DistancedPoint>& result) {
|
||||
std::vector<double> result_distances;
|
||||
std::transform(
|
||||
result.begin(),
|
||||
result.end(),
|
||||
std::back_inserter(result_distances),
|
||||
[](const DistancedPoint& point){
|
||||
return point.distance_from_start;
|
||||
}
|
||||
);
|
||||
return result_distances;
|
||||
}
|
||||
|
||||
TEST_CASE("Place points at distances - expected use", "[GCode]") {
|
||||
std::vector<Point> line{
|
||||
scaled(Vec2f{0, 0}),
|
||||
scaled(Vec2f{1, 0}),
|
||||
scaled(Vec2f{2, 1}),
|
||||
scaled(Vec2f{2, 2})
|
||||
};
|
||||
std::vector<double> distances{0, 0.2, 0.5, 1 + std::sqrt(2)/2, 1 + std::sqrt(2) + 0.5, 100.0};
|
||||
std::vector<DistancedPoint> result = slice_xy_path(line, distances);
|
||||
|
||||
REQUIRE_THAT(get_points(result), ApproxEqualsPoints(Points{
|
||||
scaled(Vec2f{0, 0}),
|
||||
scaled(Vec2f{0.2, 0}),
|
||||
scaled(Vec2f{0.5, 0}),
|
||||
scaled(Vec2f{1, 0}),
|
||||
scaled(Vec2f{1.5, 0.5}),
|
||||
scaled(Vec2f{2, 1}),
|
||||
scaled(Vec2f{2, 1.5}),
|
||||
scaled(Vec2f{2, 2})
|
||||
}, 5));
|
||||
|
||||
REQUIRE_THAT(get_distances(result), Catch::Matchers::Approx(std::vector<double>{
|
||||
distances[0], distances[1], distances[2], 1, distances[3], 1 + std::sqrt(2), distances[4], 2 + std::sqrt(2)
|
||||
}));
|
||||
}
|
||||
|
||||
TEST_CASE("Place points at distances - edge case", "[GCode]") {
|
||||
std::vector<Point> line{
|
||||
scaled(Vec2f{0, 0}),
|
||||
scaled(Vec2f{1, 0}),
|
||||
scaled(Vec2f{2, 0})
|
||||
};
|
||||
std::vector<double> distances{0, 1, 1.5, 2};
|
||||
Points result{get_points(slice_xy_path(line, distances))};
|
||||
CHECK(result == Points{
|
||||
scaled(Vec2f{0, 0}),
|
||||
scaled(Vec2f{1, 0}),
|
||||
scaled(Vec2f{1.5, 0}),
|
||||
scaled(Vec2f{2, 0})
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("Generate elevated travel", "[GCode]") {
|
||||
std::vector<Point> xy_path{
|
||||
scaled(Vec2f{0, 0}),
|
||||
scaled(Vec2f{1, 0}),
|
||||
};
|
||||
std::vector<double> ensure_points_at_distances{0.2, 0.5};
|
||||
Points3 result{generate_elevated_travel(xy_path, ensure_points_at_distances, 2.0, [](double x){return 1 + x;})};
|
||||
|
||||
CHECK(result == Points3{
|
||||
scaled(Vec3f{0, 0, 3.0}),
|
||||
scaled(Vec3f{0.2, 0, 3.2}),
|
||||
scaled(Vec3f{0.5, 0, 3.5}),
|
||||
scaled(Vec3f{1, 0, 4.0})
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("Get first crossed line distance", "[GCode]") {
|
||||
// A 2x2 square at 0, 0, with 1x1 square hole in its center.
|
||||
ExPolygon square_with_hole{
|
||||
{
|
||||
scaled(Vec2f{-1, -1}),
|
||||
scaled(Vec2f{1, -1}),
|
||||
scaled(Vec2f{1, 1}),
|
||||
scaled(Vec2f{-1, 1})
|
||||
},
|
||||
{
|
||||
scaled(Vec2f{-0.5, -0.5}),
|
||||
scaled(Vec2f{0.5, -0.5}),
|
||||
scaled(Vec2f{0.5, 0.5}),
|
||||
scaled(Vec2f{-0.5, 0.5})
|
||||
}
|
||||
};
|
||||
// A 2x2 square above the previous square at (0, 3).
|
||||
ExPolygon square_above{
|
||||
{
|
||||
scaled(Vec2f{-1, 2}),
|
||||
scaled(Vec2f{1, 2}),
|
||||
scaled(Vec2f{1, 4}),
|
||||
scaled(Vec2f{-1, 4})
|
||||
}
|
||||
};
|
||||
|
||||
// Bottom-up travel intersecting the squares.
|
||||
Lines travel{Polyline{
|
||||
scaled(Vec2f{0, -2}),
|
||||
scaled(Vec2f{0, -0.7}),
|
||||
scaled(Vec2f{0, 0}),
|
||||
scaled(Vec2f{0, 1}),
|
||||
scaled(Vec2f{0, 1.3}),
|
||||
scaled(Vec2f{0, 2.4}),
|
||||
scaled(Vec2f{0, 4.5}),
|
||||
scaled(Vec2f{0, 5}),
|
||||
}.lines()};
|
||||
|
||||
// Try different cases by skipping lines in the travel.
|
||||
AABBTreeLines::LinesDistancer<Linef> distancer = get_expolygons_distancer({square_with_hole, square_above});
|
||||
CHECK(*get_first_crossed_line_distance(travel, distancer) == Approx(1));
|
||||
CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(1), distancer) == Approx(0.2));
|
||||
CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(2), distancer) == Approx(0.5));
|
||||
CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(3), distancer) == Approx(1.0)); //Edge case
|
||||
CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(4), distancer) == Approx(0.7));
|
||||
CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(5), distancer) == Approx(1.6));
|
||||
CHECK_FALSE(get_first_crossed_line_distance(tcb::span{travel}.subspan(6), distancer));
|
||||
}
|
||||
|
||||
TEST_CASE("Generate regular polygon", "[GCode]") {
|
||||
const unsigned points_count{32};
|
||||
const Point centroid{scaled(Vec2d{5, -2})};
|
||||
const Polygon result{generate_regular_polygon(centroid, scaled(Vec2d{0, 0}), points_count)};
|
||||
const Point oposite_point{centroid * 2};
|
||||
|
||||
REQUIRE(result.size() == 32);
|
||||
CHECK(result[16].x() == Approx(oposite_point.x()));
|
||||
CHECK(result[16].y() == Approx(oposite_point.y()));
|
||||
|
||||
std::vector<double> angles;
|
||||
angles.reserve(points_count);
|
||||
for (unsigned index = 0; index < points_count; index++) {
|
||||
const unsigned previous_index{index == 0 ? points_count - 1 : index - 1};
|
||||
const unsigned next_index{index == points_count - 1 ? 0 : index + 1};
|
||||
|
||||
const Point previous_point = result.points[previous_index];
|
||||
const Point current_point = result.points[index];
|
||||
const Point next_point = result.points[next_index];
|
||||
|
||||
angles.emplace_back(angle(Vec2crd{previous_point - current_point}, Vec2crd{next_point - current_point}));
|
||||
}
|
||||
|
||||
std::vector<double> expected;
|
||||
angles.reserve(points_count);
|
||||
std::generate_n(std::back_inserter(expected), points_count, [&](){
|
||||
return angles.front();
|
||||
});
|
||||
|
||||
CHECK_THAT(angles, Catch::Matchers::Approx(expected));
|
||||
}
|
||||
|
||||
TEST_CASE("Square bed with padding", "[GCode]") {
|
||||
const Bed bed{
|
||||
{
|
||||
Vec2d{0, 0},
|
||||
Vec2d{100, 0},
|
||||
Vec2d{100, 100},
|
||||
Vec2d{0, 100}
|
||||
},
|
||||
10.0
|
||||
};
|
||||
|
||||
CHECK(bed.centroid.x() == 50);
|
||||
CHECK(bed.centroid.y() == 50);
|
||||
CHECK(bed.contains_within_padding(Vec2d{10, 10}));
|
||||
CHECK_FALSE(bed.contains_within_padding(Vec2d{9, 10}));
|
||||
|
||||
}
|
||||
@@ -2,71 +2,10 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "libslic3r/GCodeWriter.hpp"
|
||||
#include "libslic3r/GCode/GCodeWriter.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
SCENARIO("lift() is not ignored after unlift() at normal values of Z", "[GCodeWriter]") {
|
||||
GIVEN("A config from a file and a single extruder.") {
|
||||
GCodeWriter writer;
|
||||
GCodeConfig &config = writer.config;
|
||||
config.load(std::string(TEST_DATA_DIR) + "/fff_print_tests/test_gcodewriter/config_lift_unlift.ini", ForwardCompatibilitySubstitutionRule::Disable);
|
||||
|
||||
std::vector<unsigned int> extruder_ids {0};
|
||||
writer.set_extruders(extruder_ids);
|
||||
writer.set_extruder(0);
|
||||
|
||||
WHEN("Z is set to 203") {
|
||||
double trouble_Z = 203;
|
||||
writer.travel_to_z(trouble_Z);
|
||||
AND_WHEN("GcodeWriter::Lift() is called") {
|
||||
REQUIRE(writer.lift().size() > 0);
|
||||
AND_WHEN("Z is moved post-lift to the same delta as the config Z lift") {
|
||||
REQUIRE(writer.travel_to_z(trouble_Z + config.retract_lift.values[0]).size() == 0);
|
||||
AND_WHEN("GCodeWriter::Unlift() is called") {
|
||||
REQUIRE(writer.unlift().size() == 0); // we're the same height so no additional move happens.
|
||||
THEN("GCodeWriter::Lift() emits gcode.") {
|
||||
REQUIRE(writer.lift().size() > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
WHEN("Z is set to 500003") {
|
||||
double trouble_Z = 500003;
|
||||
writer.travel_to_z(trouble_Z);
|
||||
AND_WHEN("GcodeWriter::Lift() is called") {
|
||||
REQUIRE(writer.lift().size() > 0);
|
||||
AND_WHEN("Z is moved post-lift to the same delta as the config Z lift") {
|
||||
REQUIRE(writer.travel_to_z(trouble_Z + config.retract_lift.values[0]).size() == 0);
|
||||
AND_WHEN("GCodeWriter::Unlift() is called") {
|
||||
REQUIRE(writer.unlift().size() == 0); // we're the same height so no additional move happens.
|
||||
THEN("GCodeWriter::Lift() emits gcode.") {
|
||||
REQUIRE(writer.lift().size() > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
WHEN("Z is set to 10.3") {
|
||||
double trouble_Z = 10.3;
|
||||
writer.travel_to_z(trouble_Z);
|
||||
AND_WHEN("GcodeWriter::Lift() is called") {
|
||||
REQUIRE(writer.lift().size() > 0);
|
||||
AND_WHEN("Z is moved post-lift to the same delta as the config Z lift") {
|
||||
REQUIRE(writer.travel_to_z(trouble_Z + config.retract_lift.values[0]).size() == 0);
|
||||
AND_WHEN("GCodeWriter::Unlift() is called") {
|
||||
REQUIRE(writer.unlift().size() == 0); // we're the same height so no additional move happens.
|
||||
THEN("GCodeWriter::Lift() emits gcode.") {
|
||||
REQUIRE(writer.lift().size() > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// The test above will fail for trouble_Z == 9007199254740992, where trouble_Z + 1.5 will be rounded to trouble_Z + 2.0 due to double mantisa overflow.
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("set_speed emits values with fixed-point output.", "[GCodeWriter]") {
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ add_executable(${_TEST_NAME}_tests
|
||||
test_aabbindirect.cpp
|
||||
test_kdtreeindirect.cpp
|
||||
test_arachne.cpp
|
||||
test_arc_welder.cpp
|
||||
test_clipper_offset.cpp
|
||||
test_clipper_utils.cpp
|
||||
test_color.cpp
|
||||
@@ -38,8 +39,10 @@ add_executable(${_TEST_NAME}_tests
|
||||
test_astar.cpp
|
||||
test_anyptr.cpp
|
||||
test_jump_point_search.cpp
|
||||
../data/qidiparts.cpp
|
||||
../data/qidiparts.hpp
|
||||
test_support_spots_generator.cpp
|
||||
../data/prusaparts.cpp
|
||||
../data/prusaparts.hpp
|
||||
test_static_map.cpp
|
||||
)
|
||||
|
||||
if (TARGET OpenVDB::openvdb)
|
||||
|
||||
@@ -743,4 +743,23 @@ TEST_CASE("Arachne - #10034 - Degenerated Voronoi diagram - That wasn't fixed by
|
||||
#ifdef ARACHNE_DEBUG_OUT
|
||||
export_perimeters_to_svg(debug_out_path("arachne-degenerated-diagram-10034-rotation-not-works.svg"), polygons, perimeters, union_ex(wall_tool_paths.getInnerContour()));
|
||||
#endif
|
||||
}
|
||||
TEST_CASE("Arachne - SPE-1837 - No perimeters generated", "[ArachneNoPerimetersGeneratedSPE1837]") {
|
||||
Polygon poly_0 = {
|
||||
Point( 10000000, 10000000),
|
||||
Point(-10000000, 10000000),
|
||||
Point(-10000000, -10000000),
|
||||
Point( 10000000, -10000000)
|
||||
};
|
||||
|
||||
Polygons polygons = {poly_0};
|
||||
coord_t ext_perimeter_spacing = 300000;
|
||||
coord_t perimeter_spacing = 700000;
|
||||
coord_t inset_count = 1;
|
||||
|
||||
Arachne::WallToolPaths wall_tool_paths(polygons, ext_perimeter_spacing, perimeter_spacing, inset_count, 0, 0.2, PrintObjectConfig::defaults(), PrintConfig::defaults());
|
||||
wall_tool_paths.generate();
|
||||
std::vector<Arachne::VariableWidthLines> perimeters = wall_tool_paths.getToolPaths();
|
||||
|
||||
REQUIRE(!perimeters.empty());
|
||||
}
|
||||
511
tests/libslic3r/test_arc_welder.cpp
Normal file
511
tests/libslic3r/test_arc_welder.cpp
Normal file
@@ -0,0 +1,511 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <test_utils.hpp>
|
||||
|
||||
#include <random>
|
||||
|
||||
#include <libslic3r/Geometry/ArcWelder.hpp>
|
||||
#include <libslic3r/Geometry/Circle.hpp>
|
||||
#include <libslic3r/SVG.hpp>
|
||||
#include <libslic3r/libslic3r.h>
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
TEST_CASE("arc basics", "[ArcWelder]") {
|
||||
using namespace Slic3r::Geometry;
|
||||
|
||||
WHEN("arc from { 2000.f, 1000.f } to { 1000.f, 2000.f }") {
|
||||
Vec2f p1{ 2000.f, 1000.f };
|
||||
Vec2f p2{ 1000.f, 2000.f };
|
||||
float r{ 1000.f };
|
||||
const double s = 1000.f / sqrt(2.);
|
||||
THEN("90 degrees arc, CCW") {
|
||||
Vec2f c = ArcWelder::arc_center(p1, p2, r, true);
|
||||
Vec2f m = ArcWelder::arc_middle_point(p1, p2, r, true);
|
||||
REQUIRE(is_approx(c, Vec2f{ 1000.f, 1000.f }));
|
||||
REQUIRE(ArcWelder::arc_angle(p1, p2, r) == Approx(0.5 * M_PI));
|
||||
REQUIRE(ArcWelder::arc_length(p1, p2, r) == Approx(r * 0.5 * M_PI).epsilon(0.001));
|
||||
REQUIRE(is_approx(m, Vec2f{ 1000.f + s, 1000.f + s }, 0.001f));
|
||||
}
|
||||
THEN("90 degrees arc, CW") {
|
||||
Vec2f c = ArcWelder::arc_center(p1, p2, r, false);
|
||||
Vec2f m = ArcWelder::arc_middle_point(p1, p2, r, false);
|
||||
REQUIRE(is_approx(c, Vec2f{ 2000.f, 2000.f }));
|
||||
REQUIRE(is_approx(m, Vec2f{ 2000.f - s, 2000.f - s }, 0.001f));
|
||||
}
|
||||
THEN("270 degrees arc, CCW") {
|
||||
Vec2f c = ArcWelder::arc_center(p1, p2, - r, true);
|
||||
Vec2f m = ArcWelder::arc_middle_point(p1, p2, - r, true);
|
||||
REQUIRE(is_approx(c, Vec2f{ 2000.f, 2000.f }));
|
||||
REQUIRE(ArcWelder::arc_angle(p1, p2, - r) == Approx(1.5 * M_PI));
|
||||
REQUIRE(ArcWelder::arc_length(p1, p2, - r) == Approx(r * 1.5 * M_PI).epsilon(0.001));
|
||||
REQUIRE(is_approx(m, Vec2f{ 2000.f + s, 2000.f + s }, 0.001f));
|
||||
}
|
||||
THEN("270 degrees arc, CW") {
|
||||
Vec2f c = ArcWelder::arc_center(p1, p2, - r, false);
|
||||
Vec2f m = ArcWelder::arc_middle_point(p1, p2, - r, false);
|
||||
REQUIRE(is_approx(c, Vec2f{ 1000.f, 1000.f }));
|
||||
REQUIRE(is_approx(m, Vec2f{ 1000.f - s, 1000.f - s }, 0.001f));
|
||||
}
|
||||
}
|
||||
WHEN("arc from { 1707.11f, 1707.11f } to { 1000.f, 2000.f }") {
|
||||
Vec2f p1{ 1707.11f, 1707.11f };
|
||||
Vec2f p2{ 1000.f, 2000.f };
|
||||
float r{ 1000.f };
|
||||
Vec2f center1 = Vec2f{ 1000.f, 1000.f };
|
||||
// Center on the other side of the CCW arch.
|
||||
Vec2f center2 = center1 + 2. * (0.5 * (p1 + p2) - center1);
|
||||
THEN("45 degrees arc, CCW") {
|
||||
Vec2f c = ArcWelder::arc_center(p1, p2, r, true);
|
||||
REQUIRE(is_approx(c, center1, 1.f));
|
||||
REQUIRE(ArcWelder::arc_angle(p1, p2, r) == Approx(0.25 * M_PI));
|
||||
REQUIRE(ArcWelder::arc_length(p1, p2, r) == Approx(r * 0.25 * M_PI).epsilon(0.001));
|
||||
}
|
||||
THEN("45 degrees arc, CW") {
|
||||
Vec2f c = ArcWelder::arc_center(p1, p2, r, false);
|
||||
REQUIRE(is_approx(c, center2, 1.f));
|
||||
}
|
||||
THEN("315 degrees arc, CCW") {
|
||||
Vec2f c = ArcWelder::arc_center(p1, p2, - r, true);
|
||||
REQUIRE(is_approx(c, center2, 1.f));
|
||||
REQUIRE(ArcWelder::arc_angle(p1, p2, - r) == Approx((2. - 0.25) * M_PI));
|
||||
REQUIRE(ArcWelder::arc_length(p1, p2, - r) == Approx(r * (2. - 0.25) * M_PI).epsilon(0.001));
|
||||
}
|
||||
THEN("315 degrees arc, CW") {
|
||||
Vec2f c = ArcWelder::arc_center(p1, p2, - r, false);
|
||||
REQUIRE(is_approx(c, center1, 1.f));
|
||||
}
|
||||
}
|
||||
WHEN("arc from { 1866.f, 1500.f } to { 1000.f, 2000.f }") {
|
||||
Vec2f p1{ 1866.f, 1500.f };
|
||||
Vec2f p2{ 1000.f, 2000.f };
|
||||
float r{ 1000.f };
|
||||
Vec2f center1 = Vec2f{ 1000.f, 1000.f };
|
||||
// Center on the other side of the CCW arch.
|
||||
Vec2f center2 = center1 + 2. * (0.5 * (p1 + p2) - center1);
|
||||
THEN("60 degrees arc, CCW") {
|
||||
Vec2f c = ArcWelder::arc_center(p1, p2, r, true);
|
||||
REQUIRE(is_approx(c, center1, 1.f));
|
||||
REQUIRE(is_approx(ArcWelder::arc_angle(p1, p2, r), float(M_PI / 3.), 0.001f));
|
||||
REQUIRE(ArcWelder::arc_length(p1, p2, r) == Approx(r * M_PI / 3.).epsilon(0.001));
|
||||
}
|
||||
THEN("60 degrees arc, CW") {
|
||||
Vec2f c = ArcWelder::arc_center(p1, p2, r, false);
|
||||
REQUIRE(is_approx(c, center2, 1.f));
|
||||
}
|
||||
THEN("300 degrees arc, CCW") {
|
||||
Vec2f c = ArcWelder::arc_center(p1, p2, - r, true);
|
||||
REQUIRE(is_approx(c, center2, 1.f));
|
||||
REQUIRE(is_approx(ArcWelder::arc_angle(p1, p2, - r), float((2. - 1./3.) * M_PI), 0.001f));
|
||||
REQUIRE(ArcWelder::arc_length(p1, p2, - r) == Approx(r * (2. - 1. / 3.) * M_PI).epsilon(0.001));
|
||||
}
|
||||
THEN("300 degrees arc, CW") {
|
||||
Vec2f c = ArcWelder::arc_center(p1, p2, - r, false);
|
||||
REQUIRE(is_approx(c, center1, 1.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("arc discretization", "[ArcWelder]") {
|
||||
using namespace Slic3r::Geometry;
|
||||
WHEN("arc from { 2, 1 } to { 1, 2 }") {
|
||||
const Point p1 = Point::new_scale(2., 1.);
|
||||
const Point p2 = Point::new_scale(1., 2.);
|
||||
const Point center = Point::new_scale(1., 1.);
|
||||
const float radius = scaled<float>(1.);
|
||||
const float resolution = scaled<float>(0.002);
|
||||
auto test = [center, resolution, radius](const Point &p1, const Point &p2, const float r, const bool ccw) {
|
||||
Vec2f c = ArcWelder::arc_center(p1.cast<float>(), p2.cast<float>(), r, ccw);
|
||||
REQUIRE((p1.cast<float>() - c).norm() == Approx(radius));
|
||||
REQUIRE((c - center.cast<float>()).norm() == Approx(0.));
|
||||
Points pts = ArcWelder::arc_discretize(p1, p2, r, ccw, resolution);
|
||||
REQUIRE(pts.size() >= 2);
|
||||
REQUIRE(pts.front() == p1);
|
||||
REQUIRE(pts.back() == p2);
|
||||
for (const Point &p : pts)
|
||||
REQUIRE(std::abs((p.cast<double>() - c.cast<double>()).norm() - double(radius)) < double(resolution + SCALED_EPSILON));
|
||||
};
|
||||
THEN("90 degrees arc, CCW") {
|
||||
test(p1, p2, radius, true);
|
||||
}
|
||||
THEN("270 degrees arc, CCW") {
|
||||
test(p2, p1, - radius, true);
|
||||
}
|
||||
THEN("90 degrees arc, CW") {
|
||||
test(p2, p1, radius, false);
|
||||
}
|
||||
THEN("270 degrees arc, CW") {
|
||||
test(p1, p2, - radius, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_arc_fit_variance(const Point &p1, const Point &p2, const float r, const float r_fit, const bool ccw, const Points::const_iterator begin, const Points::const_iterator end)
|
||||
{
|
||||
using namespace Slic3r::Geometry;
|
||||
double variance = ArcWelder::arc_fit_variance(p1, p2, r, ccw, begin, end);
|
||||
double variance_fit = ArcWelder::arc_fit_variance(p1, p2, r_fit, ccw, begin, end);
|
||||
REQUIRE(variance_fit < variance);
|
||||
};
|
||||
|
||||
void test_arc_fit_max_deviation(const Point &p1, const Point &p2, const float r, const float r_fit, const bool ccw, const Points::const_iterator begin, const Points::const_iterator end)
|
||||
{
|
||||
using namespace Slic3r::Geometry;
|
||||
double max_deviation = ArcWelder::arc_fit_max_deviation(p1, p2, r, ccw, begin, end);
|
||||
double max_deviation_fit = ArcWelder::arc_fit_max_deviation(p1, p2, r_fit, ccw, begin, end);
|
||||
REQUIRE(std::abs(max_deviation_fit) < std::abs(max_deviation));
|
||||
};
|
||||
|
||||
void test_arc_fit(const Point &p1, const Point &p2, const float r, const float r_fit, const bool ccw, const Points::const_iterator begin, const Points::const_iterator end)
|
||||
{
|
||||
test_arc_fit_variance(p1, p2, r, r_fit, ccw, begin, end);
|
||||
test_arc_fit_max_deviation(p1, p2, r, r_fit, ccw, begin, end);
|
||||
};
|
||||
|
||||
TEST_CASE("arc fitting", "[ArcWelder]") {
|
||||
using namespace Slic3r::Geometry;
|
||||
|
||||
WHEN("arc from { 2, 1 } to { 1, 2 }") {
|
||||
const Point p1 = Point::new_scale(2., 1.);
|
||||
const Point p2 = Point::new_scale(1., 2.);
|
||||
const Point center = Point::new_scale(1., 1.);
|
||||
const float radius = scaled<float>(1.);
|
||||
const float resolution = scaled<float>(0.002);
|
||||
auto test = [center, resolution](const Point &p1, const Point &p2, const float r, const bool ccw) {
|
||||
Points pts = ArcWelder::arc_discretize(p1, p2, r, ccw, resolution);
|
||||
ArcWelder::Path path = ArcWelder::fit_path(pts, resolution + SCALED_EPSILON, ArcWelder::default_scaled_resolution);
|
||||
REQUIRE(path.size() == 2);
|
||||
REQUIRE(path.front().point == p1);
|
||||
REQUIRE(path.front().radius == 0.f);
|
||||
REQUIRE(path.back().point == p2);
|
||||
REQUIRE(path.back().ccw() == ccw);
|
||||
test_arc_fit(p1, p2, r, path.back().radius, true, pts.begin(), pts.end());
|
||||
};
|
||||
THEN("90 degrees arc, CCW is fitted") {
|
||||
test(p1, p2, radius, true);
|
||||
}
|
||||
THEN("270 degrees arc, CCW is fitted") {
|
||||
test(p2, p1, - radius, true);
|
||||
}
|
||||
THEN("90 degrees arc, CW is fitted") {
|
||||
test(p2, p1, radius, false);
|
||||
}
|
||||
THEN("270 degrees arc, CW is fitted") {
|
||||
test(p1, p2, - radius, false);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("arc from { 2, 1 } to { 1, 2 }, another arc from { 2, 1 } to { 0, 2 }, tangentially connected") {
|
||||
const Point p1 = Point::new_scale(2., 1.);
|
||||
const Point p2 = Point::new_scale(1., 2.);
|
||||
const Point p3 = Point::new_scale(0., 3.);
|
||||
const Point center1 = Point::new_scale(1., 1.);
|
||||
const Point center2 = Point::new_scale(1., 3.);
|
||||
const float radius = scaled<float>(1.);
|
||||
const float resolution = scaled<float>(0.002);
|
||||
auto test = [center1, center2, resolution](const Point &p1, const Point &p2, const Point &p3, const float r, const bool ccw) {
|
||||
Points pts = ArcWelder::arc_discretize(p1, p2, r, ccw, resolution);
|
||||
size_t num_pts1 = pts.size();
|
||||
{
|
||||
Points pts2 = ArcWelder::arc_discretize(p2, p3, - r, ! ccw, resolution);
|
||||
REQUIRE(pts.back() == pts2.front());
|
||||
pts.insert(pts.end(), std::next(pts2.begin()), pts2.end());
|
||||
}
|
||||
ArcWelder::Path path = ArcWelder::fit_path(pts, resolution + SCALED_EPSILON, ArcWelder::default_scaled_resolution);
|
||||
REQUIRE(path.size() == 3);
|
||||
REQUIRE(path.front().point == p1);
|
||||
REQUIRE(path.front().radius == 0.f);
|
||||
REQUIRE(path[1].point == p2);
|
||||
REQUIRE(path[1].ccw() == ccw);
|
||||
REQUIRE(path.back().point == p3);
|
||||
REQUIRE(path.back().ccw() == ! ccw);
|
||||
test_arc_fit(p1, p2, r, path[1].radius, ccw, pts.begin(), pts.begin() + num_pts1);
|
||||
test_arc_fit(p2, p3, - r, path.back().radius, ! ccw, pts.begin() + num_pts1 - 1, pts.end());
|
||||
};
|
||||
THEN("90 degrees arches, CCW are fitted") {
|
||||
test(p1, p2, p3, radius, true);
|
||||
}
|
||||
THEN("270 degrees arc, CCW is fitted") {
|
||||
test(p3, p2, p1, -radius, true);
|
||||
}
|
||||
THEN("90 degrees arc, CW is fitted") {
|
||||
test(p3, p2, p1, radius, false);
|
||||
}
|
||||
THEN("270 degrees arc, CW is fitted") {
|
||||
test(p1, p2, p3, -radius, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("least squares arc fitting, interpolating end points", "[ArcWelder]") {
|
||||
using namespace Slic3r::Geometry;
|
||||
|
||||
// Generate bunch of random arches.
|
||||
const coord_t max_coordinate = scaled<coord_t>(sqrt(250. - 1.));
|
||||
static constexpr const double min_radius = scaled<double>(0.01);
|
||||
static constexpr const double max_radius = scaled<double>(250.);
|
||||
// static constexpr const double deviation = scaled<double>(0.5);
|
||||
static constexpr const double deviation = scaled<double>(0.1);
|
||||
// Seeded with a fixed seed, to be repeatable.
|
||||
std::mt19937 rng(867092346);
|
||||
std::uniform_int_distribution<int32_t> coord_sampler(0, int32_t(max_coordinate));
|
||||
std::uniform_real_distribution<double> angle_sampler(0.001, 2. * M_PI - 0.001);
|
||||
std::uniform_real_distribution<double> radius_sampler(min_radius, max_radius);
|
||||
std::uniform_int_distribution<int> num_samples_sampler(1, 100);
|
||||
auto test_arc_fitting = [&rng, &coord_sampler, &num_samples_sampler, &angle_sampler, &radius_sampler]() {
|
||||
auto sample_point = [&rng, &coord_sampler]() {
|
||||
return Vec2d(coord_sampler(rng), coord_sampler(rng));
|
||||
};
|
||||
// Start and end point of the arc:
|
||||
Vec2d center_pos = sample_point();
|
||||
double angle0 = angle_sampler(rng);
|
||||
double angle = angle_sampler(rng);
|
||||
double radius = radius_sampler(rng);
|
||||
Vec2d v1 = Eigen::Rotation2D(angle0) * Vec2d(1., 0.);
|
||||
Vec2d v2 = Eigen::Rotation2D(angle0 + angle) * Vec2d(1., 0.);
|
||||
Vec2d start_pos = center_pos + radius * v1;
|
||||
Vec2d end_pos = center_pos + radius * v2;
|
||||
std::vector<Vec2d> samples;
|
||||
size_t num_samples = num_samples_sampler(rng);
|
||||
for (size_t i = 0; i < num_samples; ++ i) {
|
||||
double sample_r = sqrt(std::uniform_real_distribution<double>(sqr(std::max(0., radius - deviation)), sqr(radius + deviation))(rng));
|
||||
double sample_a = std::uniform_real_distribution<double>(0., angle)(rng);
|
||||
Vec2d pt = center_pos + Eigen::Rotation2D(angle0 + sample_a) * Vec2d(sample_r, 0.);
|
||||
samples.emplace_back(pt);
|
||||
assert((pt - center_pos).norm() > radius - deviation - SCALED_EPSILON);
|
||||
assert((pt - center_pos).norm() < radius + deviation + SCALED_EPSILON);
|
||||
}
|
||||
// Vec2d new_center = ArcWelder::arc_fit_center_algebraic_ls(start_pos, end_pos, center_pos, samples.begin(), samples.end());
|
||||
THEN("Center is fitted correctly") {
|
||||
std::optional<Vec2d> new_center_opt = ArcWelder::arc_fit_center_gauss_newton_ls(start_pos, end_pos, center_pos, samples.begin(), samples.end(), 10);
|
||||
REQUIRE(new_center_opt);
|
||||
if (new_center_opt) {
|
||||
Vec2d new_center = *new_center_opt;
|
||||
double new_radius = (new_center - start_pos).norm();
|
||||
double total_deviation = 0;
|
||||
double new_total_deviation = 0;
|
||||
for (const Vec2d &s : samples) {
|
||||
total_deviation += sqr((s - center_pos).norm() - radius);
|
||||
new_total_deviation += sqr((s - new_center).norm() - radius);
|
||||
}
|
||||
total_deviation /= double(num_samples);
|
||||
new_total_deviation /= double(num_samples);
|
||||
REQUIRE(new_total_deviation <= total_deviation);
|
||||
|
||||
#if 0
|
||||
double dist = (center_pos - new_center).norm();
|
||||
printf("Radius: %lf, Angle: %lf deg, Samples: %d, Dist: %lf\n", unscaled<double>(radius), 180. * angle / M_PI, int(num_samples), unscaled<double>(dist));
|
||||
// REQUIRE(is_approx(center_pos, new_center, deviation));
|
||||
if (dist > scaled<double>(1.)) {
|
||||
static int irun = 0;
|
||||
char path[2048];
|
||||
sprintf(path, "d:\\temp\\debug\\circle-fit-%d.svg", irun++);
|
||||
Eigen::AlignedBox<double, 2> bbox(start_pos, end_pos);
|
||||
for (const Vec2d& sample : samples)
|
||||
bbox.extend(sample);
|
||||
bbox.extend(center_pos);
|
||||
Slic3r::SVG svg(path, BoundingBox(bbox.min().cast<coord_t>(), bbox.max().cast<coord_t>()).inflated(bbox.sizes().maxCoeff() * 0.05));
|
||||
Points arc_src;
|
||||
for (size_t i = 0; i <= 1000; ++i)
|
||||
arc_src.emplace_back((center_pos + Eigen::Rotation2D(angle0 + double(i) * angle / 1000.) * Vec2d(radius, 0.)).cast<coord_t>());
|
||||
svg.draw(Polyline(arc_src));
|
||||
Points arc_new;
|
||||
double new_arc_angle = ArcWelder::arc_angle(start_pos, end_pos, (new_center - start_pos).norm());
|
||||
for (size_t i = 0; i <= 1000; ++i)
|
||||
arc_new.emplace_back((new_center + Eigen::Rotation2D(double(i) * new_arc_angle / 1000.) * (start_pos - new_center)).cast<coord_t>());
|
||||
svg.draw(Polyline(arc_new), "magenta");
|
||||
svg.draw(Point(start_pos.cast<coord_t>()), "blue");
|
||||
svg.draw(Point(end_pos.cast<coord_t>()), "blue");
|
||||
svg.draw(Point(center_pos.cast<coord_t>()), "blue");
|
||||
for (const Vec2d &sample : samples)
|
||||
svg.draw(Point(sample.cast<coord_t>()), "red");
|
||||
svg.draw(Point(new_center.cast<coord_t>()), "magenta");
|
||||
}
|
||||
if (!is_approx(center_pos, new_center, scaled<double>(5.))) {
|
||||
printf("Failed\n");
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
printf("Fitting failed\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WHEN("Generating a random arc and randomized arc samples") {
|
||||
for (size_t i = 0; i < 1000; ++ i)
|
||||
test_arc_fitting();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("arc wedge test", "[ArcWelder]") {
|
||||
using namespace Slic3r::Geometry;
|
||||
|
||||
WHEN("test point inside wedge, arc from { 2, 1 } to { 1, 2 }") {
|
||||
const int64_t s = 1000000;
|
||||
const Vec2i64 p1{ 2 * s, s };
|
||||
const Vec2i64 p2{ s, 2 * s };
|
||||
const Vec2i64 center{ s, s };
|
||||
const int64_t radius{ s };
|
||||
auto test = [center](
|
||||
// Arc data
|
||||
const Vec2i64 &p1, const Vec2i64 &p2, const int64_t r, const bool ccw,
|
||||
// Test data
|
||||
const Vec2i64 &ptest, const bool ptest_inside) {
|
||||
const Vec2d c = ArcWelder::arc_center(p1.cast<double>(), p2.cast<double>(), double(r), ccw);
|
||||
REQUIRE(is_approx(c, center.cast<double>()));
|
||||
REQUIRE(ArcWelder::inside_arc_wedge(p1, p2, center, r > 0, ccw, ptest) == ptest_inside);
|
||||
REQUIRE(ArcWelder::inside_arc_wedge(p1.cast<double>(), p2.cast<double>(), double(r), ccw, ptest.cast<double>()) == ptest_inside);
|
||||
};
|
||||
auto test_quadrants = [center, test](
|
||||
// Arc data
|
||||
const Vec2i64 &p1, const Vec2i64 &p2, const int64_t r, const bool ccw,
|
||||
// Test data
|
||||
const Vec2i64 &ptest1, const bool ptest_inside1,
|
||||
const Vec2i64 &ptest2, const bool ptest_inside2,
|
||||
const Vec2i64 &ptest3, const bool ptest_inside3,
|
||||
const Vec2i64 &ptest4, const bool ptest_inside4) {
|
||||
test(p1, p2, r, ccw, ptest1 + center, ptest_inside1);
|
||||
test(p1, p2, r, ccw, ptest2 + center, ptest_inside2);
|
||||
test(p1, p2, r, ccw, ptest3 + center, ptest_inside3);
|
||||
test(p1, p2, r, ccw, ptest4 + center, ptest_inside4);
|
||||
};
|
||||
THEN("90 degrees arc, CCW") {
|
||||
test_quadrants(p1, p2, radius, true,
|
||||
Vec2i64{ s, s }, true,
|
||||
Vec2i64{ s, - s }, false,
|
||||
Vec2i64{ - s, s }, false,
|
||||
Vec2i64{ - s, - s }, false);
|
||||
}
|
||||
THEN("270 degrees arc, CCW") {
|
||||
test_quadrants(p2, p1, -radius, true,
|
||||
Vec2i64{ s, s }, false,
|
||||
Vec2i64{ s, - s }, true,
|
||||
Vec2i64{ - s, s }, true,
|
||||
Vec2i64{ - s, - s }, true);
|
||||
}
|
||||
THEN("90 degrees arc, CW") {
|
||||
test_quadrants(p2, p1, radius, false,
|
||||
Vec2i64{ s, s }, true,
|
||||
Vec2i64{ s, - s }, false,
|
||||
Vec2i64{ - s, s }, false,
|
||||
Vec2i64{ - s, - s }, false);
|
||||
}
|
||||
THEN("270 degrees arc, CW") {
|
||||
test_quadrants(p1, p2, -radius, false,
|
||||
Vec2i64{ s, s }, false,
|
||||
Vec2i64{ s, - s }, true,
|
||||
Vec2i64{ - s, s }, true,
|
||||
Vec2i64{ - s, - s }, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// For quantization
|
||||
//#include <libslic3r/GCode/GCodeWriter.hpp>
|
||||
|
||||
TEST_CASE("arc quantization", "[ArcWelder]") {
|
||||
using namespace Slic3r::Geometry;
|
||||
|
||||
WHEN("generating a bunch of random arches") {
|
||||
static constexpr const size_t len = 100000;
|
||||
static constexpr const coord_t max_coordinate = scaled<coord_t>(250.);
|
||||
static constexpr const float max_radius = scaled<float>(250.);
|
||||
ArcWelder::Segments path;
|
||||
path.reserve(len + 1);
|
||||
// Seeded with a fixed seed, to be repeatable.
|
||||
std::mt19937 rng(987432690);
|
||||
// Generate bunch of random arches.
|
||||
std::uniform_int_distribution<int32_t> coord_sampler(0, int32_t(max_coordinate));
|
||||
std::uniform_real_distribution<float> radius_sampler(- max_radius, max_radius);
|
||||
std::uniform_int_distribution<int> orientation_sampler(0, 1);
|
||||
path.push_back({ Point{coord_sampler(rng), coord_sampler(rng)}, 0, ArcWelder::Orientation::CCW });
|
||||
for (size_t i = 0; i < len; ++ i) {
|
||||
ArcWelder::Segment seg { Point{coord_sampler(rng), coord_sampler(rng)}, radius_sampler(rng), orientation_sampler(rng) ? ArcWelder::Orientation::CCW : ArcWelder::Orientation::CW };
|
||||
while ((seg.point.cast<double>() - path.back().point.cast<double>()).norm() > 2. * std::abs(seg.radius))
|
||||
seg = { Point{coord_sampler(rng), coord_sampler(rng)}, radius_sampler(rng), orientation_sampler(rng) ? ArcWelder::Orientation::CCW : ArcWelder::Orientation::CW };
|
||||
path.push_back(seg);
|
||||
}
|
||||
// Run the test, quantize coordinates and radius, find the maximum error of quantization comparing the two arches.
|
||||
struct ArcEval {
|
||||
double error;
|
||||
double radius;
|
||||
double angle;
|
||||
};
|
||||
std::vector<ArcEval> center_errors_R;
|
||||
center_errors_R.reserve(len);
|
||||
std::vector<double> center_errors_R_exact;
|
||||
center_errors_R_exact.reserve(len);
|
||||
std::vector<double> center_errors_IJ;
|
||||
center_errors_IJ.reserve(len);
|
||||
for (size_t i = 0; i < len; ++ i) {
|
||||
// Source arc:
|
||||
const Vec2d start_point = unscaled<double>(path[i].point);
|
||||
const Vec2d end_point = unscaled<double>(path[i + 1].point);
|
||||
const double radius = unscaled<double>(path[i + 1].radius);
|
||||
const bool ccw = path[i + 1].ccw();
|
||||
const Vec2d center = ArcWelder::arc_center(start_point, end_point, radius, ccw);
|
||||
{
|
||||
const double d1 = (start_point - center).norm();
|
||||
const double d2 = (end_point - center).norm();
|
||||
const double dx = (end_point - start_point).norm();
|
||||
assert(std::abs(d1 - std::abs(radius)) < EPSILON);
|
||||
assert(std::abs(d2 - std::abs(radius)) < EPSILON);
|
||||
}
|
||||
// Quantized arc:
|
||||
const Vec2d start_point_quantized { GCodeFormatter::quantize_xyzf(start_point.x()), GCodeFormatter::quantize_xyzf(start_point.y()) };
|
||||
const Vec2d end_point_quantized { GCodeFormatter::quantize_xyzf(end_point .x()), GCodeFormatter::quantize_xyzf(end_point .y()) };
|
||||
const double radius_quantized { GCodeFormatter::quantize_xyzf(radius) };
|
||||
const Vec2d center_quantized { GCodeFormatter::quantize_xyzf(center .x()), GCodeFormatter::quantize_xyzf(center .y()) };
|
||||
// Evaulate maximum error for the quantized arc given by the end points and radius.
|
||||
const Vec2d center_from_quantized = ArcWelder::arc_center(start_point_quantized, end_point_quantized, radius, ccw);
|
||||
const Vec2d center_reconstructed = ArcWelder::arc_center(start_point_quantized, end_point_quantized, radius_quantized, ccw);
|
||||
#if 0
|
||||
center_errors_R.push_back({
|
||||
std::abs((center_reconstructed - center).norm()),
|
||||
radius,
|
||||
ArcWelder::arc_angle(start_point, end_point, radius) * 180. / M_PI
|
||||
});
|
||||
if (center_errors_R.back().error > 0.15)
|
||||
printf("Fuj\n");
|
||||
#endif
|
||||
center_errors_R_exact.emplace_back(std::abs((center_from_quantized - center).norm()));
|
||||
if (center_errors_R_exact.back() > 0.15)
|
||||
printf("Fuj\n");
|
||||
center_errors_IJ.emplace_back(std::abs((center_quantized - center).norm()));
|
||||
if (center_errors_IJ.back() > 0.15)
|
||||
printf("Fuj\n");
|
||||
|
||||
// Adjust center of the arc to the quantized end points.
|
||||
Vec2d third_point = ArcWelder::arc_middle_point(start_point, end_point, radius, ccw);
|
||||
double third_point_radius = (third_point - center).norm();
|
||||
assert(std::abs(third_point_radius - std::abs(radius)) < EPSILON);
|
||||
std::optional<Vec2d> center_adjusted = try_circle_center(start_point_quantized, end_point_quantized, third_point, EPSILON);
|
||||
//assert(center_adjusted);
|
||||
if (center_adjusted) {
|
||||
const double radius_adjusted = (center_adjusted.value() - start_point_quantized).norm() * (radius > 0 ? 1. : -1.);
|
||||
const double radius_adjusted_quantized = GCodeFormatter::quantize_xyzf(radius_adjusted);
|
||||
// Evaulate maximum error for the quantized arc given by the end points and radius.
|
||||
const Vec2f center_reconstructed = ArcWelder::arc_center(start_point_quantized.cast<float>(), end_point_quantized.cast<float>(), float(radius_adjusted_quantized), ccw);
|
||||
double rtest = std::abs(radius_adjusted_quantized);
|
||||
double d1 = std::abs((center_reconstructed - start_point.cast<float>()).norm() - rtest);
|
||||
double d2 = std::abs((center_reconstructed - end_point.cast<float>()).norm() - rtest);
|
||||
double d3 = std::abs((center_reconstructed - third_point.cast<float>()).norm() - rtest);
|
||||
double d = std::max(d1, std::max(d2, d3));
|
||||
center_errors_R.push_back({
|
||||
d,
|
||||
radius,
|
||||
ArcWelder::arc_angle(start_point, end_point, radius) * 180. / M_PI
|
||||
});
|
||||
} else {
|
||||
printf("Adjusted circle is collinear.\n");
|
||||
}
|
||||
}
|
||||
std::sort(center_errors_R.begin(), center_errors_R.end(), [](auto l, auto r) { return l.error > r.error; });
|
||||
std::sort(center_errors_R_exact.begin(), center_errors_R_exact.end(), [](auto l, auto r) { return l > r; });
|
||||
std::sort(center_errors_IJ.begin(), center_errors_IJ.end(), [](auto l, auto r) { return l > r; });
|
||||
printf("Maximum error R: %lf\n", center_errors_R.back().error);
|
||||
printf("Maximum error R exact: %lf\n", center_errors_R_exact.back());
|
||||
printf("Maximum error IJ: %lf\n", center_errors_IJ.back());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -23,7 +23,8 @@ TEST_CASE("Cut character from surface", "[]")
|
||||
|
||||
Transform3d tr = Transform3d::Identity();
|
||||
tr.translate(Vec3d(0., 0., -z_depth));
|
||||
tr.scale(Emboss::SHAPE_SCALE);
|
||||
double text_shape_scale = 0.001; // Emboss.cpp --> SHAPE_SCALE
|
||||
tr.scale(text_shape_scale);
|
||||
Emboss::OrthoProject cut_projection(tr, Vec3d(0., 0., z_depth));
|
||||
|
||||
auto object = its_make_cube(782 - 49 + 50, 724 + 10 + 50, 5);
|
||||
@@ -158,7 +159,7 @@ TEST_CASE("CutSurface in 3mf", "[Emboss]")
|
||||
|
||||
FontProp fp = tc.style.prop;
|
||||
ExPolygons shapes = Emboss::text2shapes(ff, tc.text.c_str(), fp);
|
||||
double shape_scale = Emboss::get_shape_scale(fp, *ff.font_file);
|
||||
double shape_scale = Emboss::get_text_shape_scale(fp, *ff.font_file);
|
||||
|
||||
Emboss::OrthoProject projection = create_projection_for_cut(
|
||||
cut_projection_tr, shape_scale, get_extents(shapes), z_range);
|
||||
|
||||
@@ -195,15 +195,17 @@ TEST_CASE("Visualize glyph from font", "[Emboss]")
|
||||
#endif // VISUALIZE
|
||||
|
||||
#include "test_utils.hpp"
|
||||
#include "nanosvg/nanosvg.h" // load SVG file
|
||||
#include "libslic3r/NSVGUtils.hpp"
|
||||
#include <nanosvg/nanosvg.h> // load SVG file
|
||||
#include <libslic3r/NSVGUtils.hpp>
|
||||
#include <libslic3r/IntersectionPoints.hpp>
|
||||
ExPolygons heal_and_check(const Polygons &polygons)
|
||||
{
|
||||
Pointfs intersections_prev = intersection_points(polygons);
|
||||
IntersectionsLines intersections_prev = get_intersections(polygons);
|
||||
Points polygons_points = to_points(polygons);
|
||||
Points duplicits_prev = collect_duplicates(polygons_points);
|
||||
|
||||
ExPolygons shape = Emboss::heal_shape(polygons);
|
||||
auto [shape, success] = Emboss::heal_polygons(polygons);
|
||||
CHECK(success);
|
||||
|
||||
// Is default shape for unhealabled shape?
|
||||
bool is_default_shape =
|
||||
@@ -213,7 +215,7 @@ ExPolygons heal_and_check(const Polygons &polygons)
|
||||
shape.front().holes.front().points.size() == 4 ;
|
||||
CHECK(!is_default_shape);
|
||||
|
||||
Pointfs intersections = intersection_points(shape);
|
||||
IntersectionsLines intersections = get_intersections(shape);
|
||||
Points shape_points = to_points(shape);
|
||||
Points duplicits = collect_duplicates(shape_points);
|
||||
//{
|
||||
@@ -244,7 +246,9 @@ void scale(Polygons &polygons, double multiplicator) {
|
||||
Polygons load_polygons(const std::string &svg_file) {
|
||||
std::string file_path = TEST_DATA_DIR PATH_SEPARATOR + svg_file;
|
||||
NSVGimage *image = nsvgParseFromFile(file_path.c_str(), "px", 96.0f);
|
||||
Polygons polygons = NSVGUtils::to_polygons(image);
|
||||
NSVGLineParams param{1000};
|
||||
param.scale = 10.;
|
||||
Polygons polygons = to_polygons(*image, param);
|
||||
nsvgDelete(image);
|
||||
return polygons;
|
||||
}
|
||||
@@ -258,7 +262,8 @@ TEST_CASE("Heal of 'i' in ALIENATO.TTF", "[Emboss]")
|
||||
auto a = heal_and_check(polygons);
|
||||
|
||||
Polygons scaled_shape = polygons; // copy
|
||||
scale(scaled_shape, 1 / Emboss::SHAPE_SCALE);
|
||||
double text_shape_scale = 0.001; // Emboss.cpp --> SHAPE_SCALE
|
||||
scale(scaled_shape, 1 / text_shape_scale);
|
||||
auto b = heal_and_check(scaled_shape);
|
||||
|
||||
// different scale
|
||||
@@ -282,12 +287,37 @@ TEST_CASE("Heal of 'm' in Allura_Script.ttf", "[Emboss]")
|
||||
auto a = heal_and_check(polygons);
|
||||
}
|
||||
|
||||
#include "libslic3r/NSVGUtils.hpp"
|
||||
TEST_CASE("Heal of svg contour overlap", "[Emboss]") {
|
||||
|
||||
std::string svg_file = "contour_neighbor.svg";
|
||||
auto image = nsvgParseFromFile(TEST_DATA_DIR PATH_SEPARATOR + svg_file, "mm");
|
||||
NSVGLineParams param(1e10);
|
||||
ExPolygonsWithIds shapes = create_shape_with_ids(*image, param);
|
||||
Polygons polygons;
|
||||
for (ExPolygonsWithId &shape : shapes)
|
||||
polygons.push_back(shape.expoly.front().contour);
|
||||
auto a = heal_and_check(polygons);
|
||||
}
|
||||
|
||||
// Input contour is extracted from case above "contour_neighbor.svg" with trouble shooted scale
|
||||
TEST_CASE("Heal of overlaping contour", "[Emboss]"){
|
||||
// Extracted from svg:
|
||||
Points contour{{2228926, 1543620}, {745002, 2065101}, {745002, 2065094}, {744990, 2065094}, {684487, 1466338},
|
||||
{510999, 908378}, {236555, 403250}, {-126813, -37014}, {-567074, -400382}, {-1072201, -674822},
|
||||
{-567074, -400378}, {-126813, -37010}, {236555, 403250}, {510999, 908382}, {684487, 1466346},
|
||||
{744990, 2065105}, {-2219648, 2073234}, {-2228926, -908814}, {-1646879, -2073235}};
|
||||
ExPolygons shapes = {ExPolygon{contour}};
|
||||
CHECK(Emboss::heal_expolygons(shapes));
|
||||
}
|
||||
TEST_CASE("Heal of points close to line", "[Emboss]")
|
||||
{
|
||||
std::string file_name = "points_close_to_line.svg";
|
||||
std::string file_path = TEST_DATA_DIR PATH_SEPARATOR + file_name;
|
||||
NSVGimage *image = nsvgParseFromFile(file_path.c_str(), "px", 96.0f);
|
||||
Polygons polygons = NSVGUtils::to_polygons(image);
|
||||
NSVGLineParams param{1000};
|
||||
param.scale = 1.;
|
||||
Polygons polygons = to_polygons(*image, param);
|
||||
nsvgDelete(image);
|
||||
REQUIRE(polygons.size() == 1);
|
||||
Polygon polygon = polygons.front();
|
||||
@@ -309,16 +339,19 @@ The other kids at school nicknamed him Ix,\n\
|
||||
which in the language of Betelgeuse Five translates as\t\n\
|
||||
\"boy who is not able satisfactorily to explain what a Hrung is,\n\
|
||||
nor why it should choose to collapse on Betelgeuse Seven\".";
|
||||
float line_height = 10.f, depth = 2.f;
|
||||
float line_height = 10.f;
|
||||
|
||||
auto font = Emboss::create_font_file(font_path.c_str());
|
||||
REQUIRE(font != nullptr);
|
||||
|
||||
Emboss::FontFileWithCache ffwc(std::move(font));
|
||||
FontProp fp{line_height, depth};
|
||||
ExPolygons shapes = Emboss::text2shapes(ffwc, text.c_str(), fp);
|
||||
FontProp fp{line_height};
|
||||
|
||||
auto was_canceled = []() { return false; };
|
||||
ExPolygons shapes = Emboss::text2shapes(ffwc, text.c_str(), fp, was_canceled);
|
||||
REQUIRE(!shapes.empty());
|
||||
|
||||
float depth = 2.f;
|
||||
Emboss::ProjectZ projection(depth);
|
||||
indexed_triangle_set its = Emboss::polygons2model(shapes, projection);
|
||||
CHECK(!its.indices.empty());
|
||||
@@ -468,7 +501,8 @@ TEST_CASE("Cut surface", "[]")
|
||||
|
||||
Transform3d tr = Transform3d::Identity();
|
||||
tr.translate(Vec3d(0., 0., -z_depth));
|
||||
tr.scale(Emboss::SHAPE_SCALE);
|
||||
double text_shape_scale = 0.001; // Emboss.cpp --> SHAPE_SCALE
|
||||
tr.scale(text_shape_scale);
|
||||
Emboss::OrthoProject cut_projection(tr, Vec3d(0., 0., z_depth));
|
||||
|
||||
auto object = its_make_cube(782 - 49 + 50, 724 + 10 + 50, 5);
|
||||
@@ -491,7 +525,7 @@ TEST_CASE("Cut surface", "[]")
|
||||
#include <sstream>
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <cereal/archives/binary.hpp>
|
||||
TEST_CASE("UndoRedo serialization", "[Emboss]")
|
||||
TEST_CASE("UndoRedo TextConfiguration serialization", "[Emboss]")
|
||||
{
|
||||
TextConfiguration tc;
|
||||
tc.text = "Dovede-li se člověk zasmát sám sobě, nevyjde ze smíchu po celý život.";
|
||||
@@ -500,11 +534,12 @@ TEST_CASE("UndoRedo serialization", "[Emboss]")
|
||||
es.path = "Simply the best";
|
||||
es.type = EmbossStyle::Type::file_path;
|
||||
FontProp &fp = es.prop;
|
||||
fp.angle = 100.;
|
||||
fp.distance = 10.;
|
||||
fp.char_gap = 1;
|
||||
fp.use_surface = true;
|
||||
tc.fix_3mf_tr = Transform3d::Identity();
|
||||
fp.char_gap = 3;
|
||||
fp.line_gap = 7;
|
||||
fp.boldness = 2.3f;
|
||||
fp.skew = 4.5f;
|
||||
fp.collection_number = 13;
|
||||
fp.size_in_mm= 6.7f;
|
||||
|
||||
std::stringstream ss; // any stream can be used
|
||||
{
|
||||
@@ -520,7 +555,45 @@ TEST_CASE("UndoRedo serialization", "[Emboss]")
|
||||
}
|
||||
CHECK(tc.style == tc_loaded.style);
|
||||
CHECK(tc.text == tc_loaded.text);
|
||||
CHECK(tc.fix_3mf_tr.has_value() == tc_loaded.fix_3mf_tr.has_value());
|
||||
}
|
||||
|
||||
#include "libslic3r/EmbossShape.hpp"
|
||||
TEST_CASE("UndoRedo EmbossShape serialization", "[Emboss]")
|
||||
{
|
||||
EmbossShape emboss;
|
||||
emboss.shapes_with_ids = {{0, {{{0, 0}, {10, 0}, {10, 10}, {0, 10}}, {{5, 5}, {6, 5}, {6, 6}, {5, 6}}}}};
|
||||
emboss.scale = 2.;
|
||||
emboss.projection.depth = 5.;
|
||||
emboss.projection.use_surface = true;
|
||||
emboss.fix_3mf_tr = Transform3d::Identity();
|
||||
emboss.svg_file = EmbossShape::SvgFile{};
|
||||
emboss.svg_file->path = "Everything starts somewhere, though many physicists disagree.\
|
||||
But people have always been dimly aware of the problem with the start of things.\
|
||||
They wonder how the snowplough driver gets to work,\
|
||||
or how the makers of dictionaries look up the spelling of words.";
|
||||
emboss.svg_file->path_in_3mf = "Všechno někde začíná, i když mnoho fyziků nesouhlasí.\
|
||||
Ale lidé si vždy jen matně uvědomovali problém se začátkem věcí.\
|
||||
Zajímalo je, jak se řidič sněžného pluhu dostane do práce\
|
||||
nebo jak tvůrci slovníků vyhledávají pravopis slov.";
|
||||
emboss.svg_file->file_data = std::make_unique<std::string>("cite: Terry Pratchett");
|
||||
|
||||
std::stringstream ss; // any stream can be used
|
||||
{
|
||||
cereal::BinaryOutputArchive oarchive(ss); // Create an output archive
|
||||
oarchive(emboss);
|
||||
} // archive goes out of scope, ensuring all contents are flushed
|
||||
|
||||
EmbossShape emboss_loaded;
|
||||
{
|
||||
cereal::BinaryInputArchive iarchive(ss); // Create an input archive
|
||||
iarchive(emboss_loaded);
|
||||
}
|
||||
CHECK(emboss.shapes_with_ids.front().expoly == emboss_loaded.shapes_with_ids.front().expoly);
|
||||
CHECK(emboss.scale == emboss_loaded.scale);
|
||||
CHECK(emboss.projection.depth == emboss_loaded.projection.depth);
|
||||
CHECK(emboss.projection.use_surface == emboss_loaded.projection.use_surface);
|
||||
CHECK(emboss.svg_file->path == emboss_loaded.svg_file->path);
|
||||
CHECK(emboss.svg_file->path_in_3mf == emboss_loaded.svg_file->path_in_3mf);
|
||||
}
|
||||
|
||||
|
||||
@@ -780,7 +853,7 @@ using MyMesh = Slic3r::MeshBoolean::cgal2::CGALMesh;
|
||||
|
||||
// Second Idea
|
||||
// Store original its inside of text configuration[optional]
|
||||
// Cause problem with next editation of object -> cut, simplify, Netfabb, Hollow, ...(transform original vertices)
|
||||
// Cause problem with next editation of object -> cut, simplify, repair by WinSDK, Hollow, ...(transform original vertices)
|
||||
TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
||||
{
|
||||
std::string font_path = get_font_filepath();
|
||||
|
||||
@@ -65,3 +65,99 @@ SCENARIO("Basics", "[ExPolygon]") {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include <sstream>
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <cereal/archives/binary.hpp>
|
||||
#include "libslic3r/ExPolygonSerialize.hpp"
|
||||
TEST_CASE("Serialization of expolygons", "[ExPolygon, Cereal, serialization]")
|
||||
{
|
||||
ExPolygons expolys{{
|
||||
// expolygon 1 - without holes
|
||||
{{0,0}, {10,0}, {10,10}, {0,10}}, // contour
|
||||
// expolygon 2 - with rect 1px hole
|
||||
{{{0,0}, {10,0}, {10,10}, {0,10}},
|
||||
{{5, 5}, {6, 5}, {6, 6}, {5, 6}}}
|
||||
}};
|
||||
|
||||
std::stringstream ss; // any stream can be used
|
||||
{
|
||||
cereal::BinaryOutputArchive oarchive(ss); // Create an output archive
|
||||
oarchive(expolys);
|
||||
} // archive goes out of scope, ensuring all contents are flushed
|
||||
|
||||
std::string data = ss.str();
|
||||
CHECK(!data.empty());
|
||||
|
||||
ExPolygons expolys_loaded;
|
||||
{
|
||||
cereal::BinaryInputArchive iarchive(ss); // Create an input archive
|
||||
iarchive(expolys_loaded);
|
||||
}
|
||||
|
||||
CHECK(expolys == expolys_loaded);
|
||||
}
|
||||
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <regex>
|
||||
// It is used to serialize expolygons into 3mf.
|
||||
TEST_CASE("Serialization of expolygons to string", "[ExPolygon, Cereal, serialization]")
|
||||
{
|
||||
ExPolygons expolys{{
|
||||
// expolygon 1 - without holes
|
||||
{{0,0}, {10,0}, {10,10}, {0,10}}, // contour
|
||||
// expolygon 2 - with rect 1px hole
|
||||
{{{0,0}, {10,0}, {10,10}, {0,10}},
|
||||
{{5, 5}, {6, 5}, {6, 6}, {5, 6}}}
|
||||
}};
|
||||
|
||||
std::stringstream ss_out; // any stream can be used
|
||||
{
|
||||
cereal::JSONOutputArchive oarchive(ss_out); // Create an output archive
|
||||
oarchive(expolys);
|
||||
} // archive goes out of scope, ensuring all contents are flushed
|
||||
|
||||
//Simplify text representation of expolygons
|
||||
std::string data = ss_out.str();
|
||||
// Data contain this JSON string
|
||||
//{
|
||||
// "value0": [
|
||||
// {
|
||||
// "value0": {
|
||||
// "value0":
|
||||
// [{"value0": 0, "value1": 0}, {"value0": 10, "value1": 0}, {"value0": 10, "value1": 10}, {"value0": 0, "value1": 10}]
|
||||
// },
|
||||
// "value1": []
|
||||
// },
|
||||
// {
|
||||
// "value0": {
|
||||
// "value0":
|
||||
// [{"value0": 0, "value1": 0}, {"value0": 10, "value1": 0}, {"value0": 10, "value1": 10}, {"value0": 0, "value1": 10}]
|
||||
// },
|
||||
// "value1": [{
|
||||
// "value0":
|
||||
// [{"value0": 5, "value1": 5}, {"value0": 6, "value1": 5}, {"value0": 6, "value1": 6}, {"value0": 5, "value1": 6}]
|
||||
// }]
|
||||
// }
|
||||
// ]
|
||||
//}
|
||||
|
||||
// Change JSON named object to JSON arrays(without name)
|
||||
|
||||
// RegEx for wihitespace = "[ \t\r\n\v\f]"
|
||||
std::regex r("\"value[0-9]+\":|[ \t\r\n\v\f]");
|
||||
std::string data_short = std::regex_replace(data, r , "");
|
||||
std::replace(data_short.begin(), data_short.end(), '{', '[');
|
||||
std::replace(data_short.begin(), data_short.end(), '}', ']');
|
||||
CHECK(!data_short.empty());
|
||||
// Cereal acceptable string
|
||||
// [[[[[[0,0],[10,0],[10,10],[0,10]]],[]],[[[[0,0],[10,0],[10,10],[0,10]]],[[[[5,5],[6,5],[6,6],[5,6]]]]]]]
|
||||
std::stringstream ss_in(data_short);
|
||||
ExPolygons expolys_loaded;
|
||||
{
|
||||
cereal::JSONInputArchive iarchive(ss_in); // Create an input archive
|
||||
iarchive(expolys_loaded);
|
||||
}
|
||||
|
||||
CHECK(expolys == expolys_loaded);
|
||||
}
|
||||
@@ -84,16 +84,16 @@ TEST_CASE("Line::perpendicular_to", "[Geometry]") {
|
||||
TEST_CASE("Polygon::contains works properly", "[Geometry]"){
|
||||
// this test was failing on Windows (GH #1950)
|
||||
Slic3r::Polygon polygon(Points({
|
||||
Point(207802834,-57084522),
|
||||
Point(196528149,-37556190),
|
||||
Point(173626821,-25420928),
|
||||
Point(171285751,-21366123),
|
||||
Point(118673592,-21366123),
|
||||
Point(116332562,-25420928),
|
||||
Point(93431208,-37556191),
|
||||
Point(82156517,-57084523),
|
||||
Point(129714478,-84542120),
|
||||
Point(160244873,-84542120)
|
||||
{207802834,-57084522},
|
||||
{196528149,-37556190},
|
||||
{173626821,-25420928},
|
||||
{171285751,-21366123},
|
||||
{118673592,-21366123},
|
||||
{116332562,-25420928},
|
||||
{93431208,-37556191},
|
||||
{82156517,-57084523},
|
||||
{129714478,-84542120},
|
||||
{160244873,-84542120}
|
||||
}));
|
||||
Point point(95706562, -57294774);
|
||||
REQUIRE(polygon.contains(point));
|
||||
@@ -196,6 +196,40 @@ TEST_CASE("Offseting a line generates a polygon correctly", "[Geometry]"){
|
||||
REQUIRE(area.area() == Slic3r::Polygon(Points({Point(10,5),Point(20,5),Point(20,15),Point(10,15)})).area());
|
||||
}
|
||||
|
||||
SCENARIO("Circle Fit, 3 points", "[Geometry]") {
|
||||
WHEN("Three points make a circle") {
|
||||
double s1 = scaled<double>(1.);
|
||||
THEN("circle_center(): A center point { 0, 0 } is returned") {
|
||||
Vec2d center = Geometry::circle_center(Vec2d{ s1, 0. }, Vec2d{ 0, s1 }, Vec2d{ -s1, 0. }, SCALED_EPSILON);
|
||||
REQUIRE(is_approx(center, Vec2d(0, 0)));
|
||||
}
|
||||
THEN("circle_center(): A center point { 0, 0 } is returned for points in reverse") {
|
||||
Vec2d center = Geometry::circle_center(Vec2d{ -s1, 0. }, Vec2d{ 0, s1 }, Vec2d{ s1, 0. }, SCALED_EPSILON);
|
||||
REQUIRE(is_approx(center, Vec2d(0, 0)));
|
||||
}
|
||||
THEN("try_circle_center(): A center point { 0, 0 } is returned") {
|
||||
std::optional<Vec2d> center = Geometry::try_circle_center(Vec2d{ s1, 0. }, Vec2d{ 0, s1 }, Vec2d{ -s1, 0. }, SCALED_EPSILON);
|
||||
REQUIRE(center);
|
||||
REQUIRE(is_approx(*center, Vec2d(0, 0)));
|
||||
}
|
||||
THEN("try_circle_center(): A center point { 0, 0 } is returned for points in reverse") {
|
||||
std::optional<Vec2d> center = Geometry::try_circle_center(Vec2d{ -s1, 0. }, Vec2d{ 0, s1 }, Vec2d{ s1, 0. }, SCALED_EPSILON);
|
||||
REQUIRE(center);
|
||||
REQUIRE(is_approx(*center, Vec2d(0, 0)));
|
||||
}
|
||||
}
|
||||
WHEN("Three points are collinear") {
|
||||
double s1 = scaled<double>(1.);
|
||||
THEN("circle_center(): A center point { 2, 0 } is returned") {
|
||||
Vec2d center = Geometry::circle_center(Vec2d{ s1, 0. }, Vec2d{ 2. * s1, 0. }, Vec2d{ 3. * s1, 0. }, SCALED_EPSILON);
|
||||
REQUIRE(is_approx(center, Vec2d(2. * s1, 0)));
|
||||
}
|
||||
THEN("try_circle_center(): Fails for collinear points") {
|
||||
std::optional<Vec2d> center = Geometry::try_circle_center(Vec2d{ s1, 0. }, Vec2d{ 2. * s1, 0. }, Vec2d{ 3. * s1, 0. }, SCALED_EPSILON);
|
||||
REQUIRE(! center);
|
||||
}
|
||||
}
|
||||
}
|
||||
SCENARIO("Circle Fit, TaubinFit with Newton's method", "[Geometry]") {
|
||||
GIVEN("A vector of Vec2ds arranged in a half-circle with approximately the same distance R from some point") {
|
||||
Vec2d expected_center(-6, 0);
|
||||
@@ -288,6 +322,53 @@ SCENARIO("Circle Fit, TaubinFit with Newton's method", "[Geometry]") {
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("Circle Fit, least squares by decomposition or by solving normal equation", "[Geometry]") {
|
||||
auto test_circle_fit = [](const Geometry::Circled &circle, const Vec2d ¢er, const double radius) {
|
||||
THEN("A center point matches.") {
|
||||
REQUIRE(is_approx(circle.center, center));
|
||||
}
|
||||
THEN("Radius matches") {
|
||||
REQUIRE(is_approx(circle.radius, radius));
|
||||
}
|
||||
};
|
||||
|
||||
GIVEN("A vector of Vec2ds arranged in a half-circle with approximately the same distance R from some point") {
|
||||
const Vec2d expected_center(-6., 0.);
|
||||
const double expected_radius = 6.;
|
||||
Vec2ds sample{Vec2d(6.0, 0), Vec2d(5.1961524, 3), Vec2d(3 ,5.1961524), Vec2d(0, 6.0), Vec2d(3, 5.1961524), Vec2d(-5.1961524, 3), Vec2d(-6.0, 0)};
|
||||
std::transform(sample.begin(), sample.end(), sample.begin(), [expected_center] (const Vec2d &a) { return a + expected_center; });
|
||||
|
||||
WHEN("Circle fit is called on the entire array, least squares SVD") {
|
||||
test_circle_fit(Geometry::circle_linear_least_squares_svd(sample), expected_center, expected_radius);
|
||||
}
|
||||
WHEN("Circle fit is called on the first four points, least squares SVD") {
|
||||
test_circle_fit(Geometry::circle_linear_least_squares_svd(Vec2ds(sample.cbegin(), sample.cbegin() + 4)), expected_center, expected_radius);
|
||||
}
|
||||
WHEN("Circle fit is called on the middle four points, least squares SVD") {
|
||||
test_circle_fit(Geometry::circle_linear_least_squares_svd(Vec2ds(sample.cbegin() + 2, sample.cbegin() + 6)), expected_center, expected_radius);
|
||||
}
|
||||
|
||||
WHEN("Circle fit is called on the entire array, least squares QR decomposition") {
|
||||
test_circle_fit(Geometry::circle_linear_least_squares_qr(sample), expected_center, expected_radius);
|
||||
}
|
||||
WHEN("Circle fit is called on the first four points, least squares QR decomposition") {
|
||||
test_circle_fit(Geometry::circle_linear_least_squares_qr(Vec2ds(sample.cbegin(), sample.cbegin() + 4)), expected_center, expected_radius);
|
||||
}
|
||||
WHEN("Circle fit is called on the middle four points, least squares QR decomposition") {
|
||||
test_circle_fit(Geometry::circle_linear_least_squares_qr(Vec2ds(sample.cbegin() + 2, sample.cbegin() + 6)), expected_center, expected_radius);
|
||||
}
|
||||
|
||||
WHEN("Circle fit is called on the entire array, least squares by normal equations") {
|
||||
test_circle_fit(Geometry::circle_linear_least_squares_normal(sample), expected_center, expected_radius);
|
||||
}
|
||||
WHEN("Circle fit is called on the first four points, least squares by normal equations") {
|
||||
test_circle_fit(Geometry::circle_linear_least_squares_normal(Vec2ds(sample.cbegin(), sample.cbegin() + 4)), expected_center, expected_radius);
|
||||
}
|
||||
WHEN("Circle fit is called on the middle four points, least squares by normal equations") {
|
||||
test_circle_fit(Geometry::circle_linear_least_squares_normal(Vec2ds(sample.cbegin() + 2, sample.cbegin() + 6)), expected_center, expected_radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_CASE("smallest_enclosing_circle_welzl", "[Geometry]") {
|
||||
// Some random points in plane.
|
||||
Points pts {
|
||||
@@ -310,6 +391,7 @@ SCENARIO("Path chaining", "[Geometry]") {
|
||||
GIVEN("A path") {
|
||||
Points points = { Point(26,26),Point(52,26),Point(0,26),Point(26,52),Point(26,0),Point(0,52),Point(52,52),Point(52,0) };
|
||||
THEN("Chained with no diagonals (thus 26 units long)") {
|
||||
// if chain_points() works correctly, these points should be joined with no diagonal paths
|
||||
std::vector<Points::size_type> indices = chain_points(points);
|
||||
for (Points::size_type i = 0; i + 1 < indices.size(); ++ i) {
|
||||
double dist = (points.at(indices.at(i)).cast<double>() - points.at(indices.at(i+1)).cast<double>()).norm();
|
||||
|
||||
@@ -5,6 +5,24 @@
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
SCENARIO("Simplify polyne, template", "[Polyline]")
|
||||
{
|
||||
Points polyline{ {0,0}, {1000,0}, {2000,0}, {2000,1000}, {2000,2000}, {1000,2000}, {0,2000}, {0,1000}, {0,0} };
|
||||
WHEN("simplified with Douglas-Peucker with back inserter") {
|
||||
Points out;
|
||||
douglas_peucker<int64_t>(polyline.begin(), polyline.end(), std::back_inserter(out), 10, [](const Point &p) { return p; });
|
||||
THEN("simplified correctly") {
|
||||
REQUIRE(out == Points{ {0,0}, {2000,0}, {2000,2000}, {0,2000}, {0,0} });
|
||||
}
|
||||
}
|
||||
WHEN("simplified with Douglas-Peucker in place") {
|
||||
Points out{ polyline };
|
||||
out.erase(douglas_peucker<int64_t>(out.begin(), out.end(), out.begin(), 10, [](const Point &p) { return p; }), out.end());
|
||||
THEN("simplified correctly") {
|
||||
REQUIRE(out == Points{ {0,0}, {2000,0}, {2000,2000}, {0,2000}, {0,0} });
|
||||
}
|
||||
}
|
||||
}
|
||||
SCENARIO("Simplify polyline", "[Polyline]")
|
||||
{
|
||||
GIVEN("polyline 1") {
|
||||
|
||||
98
tests/libslic3r/test_static_map.cpp
Normal file
98
tests/libslic3r/test_static_map.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <string_view>
|
||||
|
||||
#include "libslic3r/StaticMap.hpp"
|
||||
|
||||
TEST_CASE("Empty static map should be possible to create and should be empty", "[StaticMap]")
|
||||
{
|
||||
using namespace Slic3r;
|
||||
|
||||
static const constexpr StaticSet EmptySet;
|
||||
|
||||
static const constexpr auto EmptyMap = make_staticmap<int, int>();
|
||||
|
||||
constexpr bool is_map_empty = EmptyMap.empty();
|
||||
constexpr bool is_set_empty = EmptySet.empty();
|
||||
|
||||
REQUIRE(is_map_empty);
|
||||
REQUIRE(is_set_empty);
|
||||
}
|
||||
|
||||
TEST_CASE("StaticSet should derive it's type from the initializer", "[StaticMap]") {
|
||||
using namespace Slic3r;
|
||||
static const constexpr StaticSet iOneSet = { 1 };
|
||||
static constexpr size_t iOneSetSize = iOneSet.size();
|
||||
|
||||
REQUIRE(iOneSetSize == 1);
|
||||
|
||||
static const constexpr StaticSet iManySet = { 1, 3, 5, 80, 40 };
|
||||
static constexpr size_t iManySetSize = iManySet.size();
|
||||
|
||||
REQUIRE(iManySetSize == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("StaticMap should derive it's type using make_staticmap", "[StaticMap]") {
|
||||
using namespace Slic3r;
|
||||
static const constexpr auto ciOneMap = make_staticmap<char, int>({
|
||||
{'a', 1},
|
||||
});
|
||||
|
||||
static constexpr size_t ciOneMapSize = ciOneMap.size();
|
||||
static constexpr bool ciOneMapValid = query(ciOneMap, 'a').value_or(0) == 1;
|
||||
|
||||
REQUIRE(ciOneMapSize == 1);
|
||||
REQUIRE(ciOneMapValid);
|
||||
|
||||
static const constexpr auto ciManyMap = make_staticmap<char, int>({
|
||||
{'a', 1}, {'b', 2}, {'A', 10}
|
||||
});
|
||||
|
||||
static constexpr size_t ciManyMapSize = ciManyMap.size();
|
||||
static constexpr bool ciManyMapValid =
|
||||
query(ciManyMap, 'a').value_or(0) == 1 &&
|
||||
query(ciManyMap, 'b').value_or(0) == 2 &&
|
||||
query(ciManyMap, 'A').value_or(0) == 10 &&
|
||||
!contains(ciManyMap, 'B') &&
|
||||
!query(ciManyMap, 'c').has_value();
|
||||
|
||||
REQUIRE(ciManyMapSize == 3);
|
||||
REQUIRE(ciManyMapValid);
|
||||
|
||||
for (auto &[k, v] : ciManyMap) {
|
||||
auto val = query(ciManyMap, k);
|
||||
REQUIRE(val.has_value());
|
||||
REQUIRE(*val == v);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("StaticSet should be able to find contained values", "[StaticMap]")
|
||||
{
|
||||
using namespace Slic3r;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
auto cmp = [](const char *a, const char *b) constexpr {
|
||||
return std::string_view{a} < std::string_view{b};
|
||||
};
|
||||
|
||||
static constexpr StaticSet CStrSet = {cmp, "One", "Two", "Three"};
|
||||
static constexpr StaticSet StringSet = {"One"sv, "Two"sv, "Three"sv};
|
||||
|
||||
static constexpr bool CStrSetValid = query(CStrSet, "One").has_value() &&
|
||||
contains(CStrSet, "Two") &&
|
||||
contains(CStrSet, "Three") &&
|
||||
!contains(CStrSet, "one") &&
|
||||
!contains(CStrSet, "two") &&
|
||||
!contains(CStrSet, "three");
|
||||
|
||||
static constexpr bool StringSetValid = contains(StringSet, "One"sv) &&
|
||||
contains(StringSet, "Two"sv) &&
|
||||
contains(StringSet, "Three"sv) &&
|
||||
!contains(StringSet, "one"sv) &&
|
||||
!contains(StringSet, "two"sv) &&
|
||||
!contains(StringSet, "three"sv);
|
||||
|
||||
REQUIRE(CStrSetValid);
|
||||
REQUIRE(StringSetValid);
|
||||
REQUIRE(CStrSet.size() == 3);
|
||||
REQUIRE(StringSet.size() == 3);
|
||||
}
|
||||
163
tests/libslic3r/test_support_spots_generator.cpp
Normal file
163
tests/libslic3r/test_support_spots_generator.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
#include <libslic3r/SupportSpotsGenerator.hpp>
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace SupportSpotsGenerator;
|
||||
|
||||
|
||||
TEST_CASE("Numerical integral calculation compared with exact solution.", "[SupportSpotsGenerator]") {
|
||||
const float width = 10;
|
||||
const float height = 20;
|
||||
const Polygon polygon = {
|
||||
scaled(Vec2f{-width / 2, -height / 2}),
|
||||
scaled(Vec2f{width / 2, -height / 2}),
|
||||
scaled(Vec2f{width / 2, height / 2}),
|
||||
scaled(Vec2f{-width / 2, height / 2})
|
||||
};
|
||||
|
||||
const Integrals integrals{{polygon}};
|
||||
CHECK(integrals.area == Approx(width * height));
|
||||
CHECK(integrals.x_i.x() == Approx(0));
|
||||
CHECK(integrals.x_i.y() == Approx(0));
|
||||
CHECK(integrals.x_i_squared.x() == Approx(std::pow(width, 3) * height / 12));
|
||||
CHECK(integrals.x_i_squared.y() == Approx(width * std::pow(height, 3) / 12));
|
||||
}
|
||||
|
||||
TEST_CASE("Moment values and ratio check.", "[SupportSpotsGenerator]") {
|
||||
const float width = 40;
|
||||
const float height = 2;
|
||||
|
||||
// Moments are calculated at centroid.
|
||||
// Polygon centroid must not be (0, 0).
|
||||
const Polygon polygon = {
|
||||
scaled(Vec2f{0, 0}),
|
||||
scaled(Vec2f{width, 0}),
|
||||
scaled(Vec2f{width, height}),
|
||||
scaled(Vec2f{0, height})
|
||||
};
|
||||
|
||||
const Integrals integrals{{polygon}};
|
||||
|
||||
const Vec2f x_axis{1, 0};
|
||||
const float x_axis_moment = compute_second_moment(integrals, x_axis);
|
||||
|
||||
const Vec2f y_axis{0, 1};
|
||||
const float y_axis_moment = compute_second_moment(integrals, y_axis);
|
||||
|
||||
const float moment_ratio = std::pow(width / height, 2);
|
||||
|
||||
// Ensure the object transaltion has no effect.
|
||||
CHECK(x_axis_moment == Approx(width * std::pow(height, 3) / 12));
|
||||
CHECK(y_axis_moment == Approx(std::pow(width, 3) * height / 12));
|
||||
// If the object is "wide" the y axis moments should be large compared to x axis moment.
|
||||
CHECK(y_axis_moment / x_axis_moment == Approx(moment_ratio));
|
||||
}
|
||||
|
||||
TEST_CASE("Moments calculation for rotated axis.", "[SupportSpotsGenerator]") {
|
||||
|
||||
Polygon polygon = {
|
||||
scaled(Vec2f{6.362284076172198, 138.9674202217155}),
|
||||
scaled(Vec2f{97.48779843751677, 106.08136606617076}),
|
||||
scaled(Vec2f{135.75221821532384, 66.84428834668765}),
|
||||
scaled(Vec2f{191.5308049852741, 45.77905628725614}),
|
||||
scaled(Vec2f{182.7525148049201, 74.01799041087513}),
|
||||
scaled(Vec2f{296.83210979283473, 196.80022572637228}),
|
||||
scaled(Vec2f{215.16434429179148, 187.45715418834143}),
|
||||
scaled(Vec2f{64.64574271229334, 284.293883209721}),
|
||||
scaled(Vec2f{110.76507036894843, 174.35633141113783}),
|
||||
scaled(Vec2f{77.56229640885199, 189.33057746591336})
|
||||
};
|
||||
|
||||
Integrals integrals{{polygon}};
|
||||
|
||||
// Meassured counterclockwise from (1, 0)
|
||||
const float angle = 1.432f;
|
||||
Vec2f axis{std::cos(angle), std::sin(angle)};
|
||||
|
||||
float moment_calculated_then_rotated = compute_second_moment(
|
||||
integrals,
|
||||
axis
|
||||
);
|
||||
|
||||
// We want to rotate the object clockwise by angle to align the axis with (1, 0)
|
||||
// Method .rotate is counterclockwise for positive angle
|
||||
polygon.rotate(-angle);
|
||||
|
||||
Integrals integrals_rotated{{polygon}};
|
||||
float moment_rotated_polygon = compute_second_moment(
|
||||
integrals_rotated,
|
||||
Vec2f{1, 0}
|
||||
);
|
||||
|
||||
// Up to 0.1% accuracy
|
||||
CHECK_THAT(moment_calculated_then_rotated, Catch::Matchers::WithinRel(moment_rotated_polygon, 0.001f));
|
||||
}
|
||||
|
||||
struct ObjectPartFixture {
|
||||
const Polyline polyline{
|
||||
Point{scaled(Vec2f{0, 0})},
|
||||
Point{scaled(Vec2f{1, 0})},
|
||||
};
|
||||
const float width = 0.1f;
|
||||
bool connected_to_bed = true;
|
||||
coordf_t print_head_z = 0.2;
|
||||
coordf_t layer_height = 0.2;
|
||||
ExtrusionAttributes attributes;
|
||||
ExtrusionEntityCollection collection;
|
||||
std::vector<const ExtrusionEntityCollection*> extrusions{};
|
||||
Polygon expected_polygon{
|
||||
Point{scaled(Vec2f{0, -width / 2})},
|
||||
Point{scaled(Vec2f{1, -width / 2})},
|
||||
Point{scaled(Vec2f{1, width / 2})},
|
||||
Point{scaled(Vec2f{0, width / 2})}
|
||||
};
|
||||
|
||||
ObjectPartFixture() {
|
||||
attributes.width = width;
|
||||
const ExtrusionPath path{polyline, attributes};
|
||||
collection.append(path);
|
||||
extrusions.push_back(&collection);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE_METHOD(ObjectPartFixture, "Constructing ObjectPart using extrusion collections", "[SupportSpotsGenerator]") {
|
||||
ObjectPart part{
|
||||
extrusions,
|
||||
connected_to_bed,
|
||||
print_head_z,
|
||||
layer_height,
|
||||
std::nullopt
|
||||
};
|
||||
|
||||
Integrals expected{{expected_polygon}};
|
||||
|
||||
CHECK(part.connected_to_bed == true);
|
||||
Vec3f volume_centroid{part.volume_centroid_accumulator / part.volume};
|
||||
CHECK(volume_centroid.x() == Approx(0.5));
|
||||
CHECK(volume_centroid.y() == Approx(0));
|
||||
CHECK(volume_centroid.z() == Approx(layer_height / 2));
|
||||
CHECK(part.sticking_area == Approx(expected.area));
|
||||
CHECK(part.sticking_centroid_accumulator.x() == Approx(expected.x_i.x()));
|
||||
CHECK(part.sticking_centroid_accumulator.y() == Approx(expected.x_i.y()));
|
||||
CHECK(part.sticking_second_moment_of_area_accumulator.x() == Approx(expected.x_i_squared.x()));
|
||||
CHECK(part.sticking_second_moment_of_area_accumulator.y() == Approx(expected.x_i_squared.y()));
|
||||
CHECK(part.sticking_second_moment_of_area_covariance_accumulator == Approx(expected.xy).margin(1e-6));
|
||||
CHECK(part.volume == Approx(layer_height * width));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ObjectPartFixture, "Constructing ObjectPart with brim", "[SupportSpotsGenerator]") {
|
||||
float brim_width = 1;
|
||||
Polygons brim = get_brim(ExPolygon{expected_polygon}, BrimType::btOuterOnly, brim_width);
|
||||
|
||||
ObjectPart part{
|
||||
extrusions,
|
||||
connected_to_bed,
|
||||
print_head_z,
|
||||
layer_height,
|
||||
brim
|
||||
};
|
||||
|
||||
CHECK(part.sticking_area == Approx((1 + 2*brim_width) * (width + 2*brim_width)));
|
||||
}
|
||||
|
||||
@@ -18,6 +18,6 @@ if (WIN32)
|
||||
endif()
|
||||
|
||||
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
||||
set(_catch_args "exclude:[NotWorking]")
|
||||
set(_catch_args "exclude:[NotWorking];-s")
|
||||
list(APPEND _catch_args "${CATCH_EXTRA_ARGS}")
|
||||
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${_catch_args})
|
||||
|
||||
@@ -20,6 +20,13 @@ struct Progress: Slic3r::ProgressIndicator {
|
||||
|
||||
using TestClasses = std::tuple< Slic3r::GUI::UIThreadWorker, Slic3r::GUI::BoostThreadWorker >;
|
||||
|
||||
TEMPLATE_LIST_TEST_CASE("Empty worker should not block when queried for idle", "[Jobs]", TestClasses) {
|
||||
TestType worker{std::make_unique<Progress>()};
|
||||
|
||||
worker.wait_for_idle();
|
||||
|
||||
REQUIRE(worker.is_idle());
|
||||
}
|
||||
TEMPLATE_LIST_TEST_CASE("Empty worker should not do anything", "[Jobs]", TestClasses) {
|
||||
TestType worker{std::make_unique<Progress>()};
|
||||
|
||||
@@ -49,6 +56,8 @@ TEMPLATE_LIST_TEST_CASE("State should not be idle while running a job", "[Jobs]"
|
||||
}).wait();
|
||||
});
|
||||
|
||||
// make sure that the job starts BEFORE the worker.wait_for_idle() is called
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
worker.wait_for_idle();
|
||||
|
||||
REQUIRE(worker.is_idle());
|
||||
@@ -85,7 +94,8 @@ TEMPLATE_LIST_TEST_CASE("Cancellation should be recognized be the worker", "[Job
|
||||
for (int s = 0; s <= 100; ++s) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
ctl.update_status(s, "Running");
|
||||
if (ctl.was_canceled()) break;
|
||||
if (ctl.was_canceled())
|
||||
break;
|
||||
}
|
||||
},
|
||||
[](bool cancelled, std::exception_ptr &) { // finalize
|
||||
|
||||
13
tests/thumbnails/CMakeLists.txt
Normal file
13
tests/thumbnails/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
||||
|
||||
add_executable(${_TEST_NAME}_tests
|
||||
${_TEST_NAME}_tests_main.cpp
|
||||
test_thumbnails_input_string.cpp
|
||||
test_thumbnails_ini_string.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
|
||||
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
|
||||
|
||||
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
||||
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS})
|
||||
235
tests/thumbnails/test_thumbnails_ini_string.cpp
Normal file
235
tests/thumbnails/test_thumbnails_ini_string.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
#include <libslic3r/GCode/Thumbnails.hpp>
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace GCodeThumbnails;
|
||||
|
||||
|
||||
static std::string empty_thumbnails()
|
||||
{
|
||||
return "thumbnails = \n"
|
||||
"thumbnails_format = ";
|
||||
}
|
||||
|
||||
static std::string valid_thumbnails()
|
||||
{
|
||||
return "thumbnails = 160x120/JPG, 23x78/QOI, 230x780/JPG\n"
|
||||
"thumbnails_format = JPG";
|
||||
}
|
||||
|
||||
static std::string valid_thumbnails2()
|
||||
{
|
||||
return "thumbnails = 160x120/PNG, 23x78/QOi, 320x240/PNg, 230x780/JPG\n"
|
||||
"thumbnails_format = pnG";
|
||||
}
|
||||
|
||||
static std::string valid_thumbnails3()
|
||||
{
|
||||
return "thumbnails = 160x120/JPG, 23x78/QOI, 230x780/JPG";
|
||||
}
|
||||
|
||||
static std::string old_valid_thumbnails()
|
||||
{
|
||||
return "thumbnails = 160x120\n"
|
||||
"thumbnails_format = JPG";
|
||||
}
|
||||
|
||||
static std::string old_valid_thumbnails2()
|
||||
{
|
||||
return "thumbnails = 160x120, 23x78, 320x240\n"
|
||||
"thumbnails_format = PNG";
|
||||
}
|
||||
|
||||
static std::string old_invalid_thumbnails()
|
||||
{
|
||||
return "thumbnails = 160x\n"
|
||||
"thumbnails_format = JPG";
|
||||
}
|
||||
|
||||
static std::string old_invalid_thumbnails2()
|
||||
{
|
||||
return "thumbnails = 160x120, 23*78, 320x240\n"
|
||||
"thumbnails_format = PNG";
|
||||
}
|
||||
|
||||
static std::string out_of_range_thumbnails()
|
||||
{
|
||||
return "thumbnails = 1160x1200/PNG, 23x78/QOI, 320x240/PNG, 230x780/JPG\n"
|
||||
"thumbnails_format = PNG";
|
||||
}
|
||||
|
||||
static std::string out_of_range_thumbnails2()
|
||||
{
|
||||
return "thumbnails = 1160x120/PNG, 23x78/QOI, -320x240/PNG, 230x780/JPG\n"
|
||||
"thumbnails_format = PNG";
|
||||
}
|
||||
|
||||
static std::string invalid_ext_thumbnails()
|
||||
{
|
||||
return "thumbnails = 1160x120/PNk, 23x78/QOI, 320x240/PNG, 230x780/JPG\n"
|
||||
"thumbnails_format = QOI";
|
||||
}
|
||||
|
||||
static std::string invalid_ext_thumbnails2()
|
||||
{
|
||||
return "thumbnails = 1160x120/PNG, 23x78/QO, 320x240/PNG, 230x780/JPG\n"
|
||||
"thumbnails_format = PNG";
|
||||
}
|
||||
|
||||
static std::string invalid_val_thumbnails()
|
||||
{
|
||||
return "thumbnails = 1160x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n"
|
||||
"thumbnails_format = JPG";
|
||||
}
|
||||
|
||||
static std::string invalid_val_thumbnails2()
|
||||
{
|
||||
return "thumbnails = x120/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n"
|
||||
"thumbnails_format = PNG";
|
||||
}
|
||||
|
||||
static std::string invalid_val_thumbnails3()
|
||||
{
|
||||
return "thumbnails = 1x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n"
|
||||
"thumbnails_format = qoi";
|
||||
}
|
||||
|
||||
static std::string invalid_val_thumbnails4()
|
||||
{
|
||||
return "thumbnails = 123*78/QOI, 320x240/PNG, 230x780/JPG\n"
|
||||
"thumbnails_format = jpG";
|
||||
}
|
||||
|
||||
static DynamicPrintConfig thumbnails_config()
|
||||
{
|
||||
DynamicPrintConfig config;
|
||||
config.apply_only(FullPrintConfig::defaults() , { "thumbnails", "thumbnails_format" });
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
TEST_CASE("Validate Empty Thumbnails", "[Thumbnails in Config]") {
|
||||
DynamicPrintConfig config = thumbnails_config();
|
||||
|
||||
auto test_loaded_config = [](DynamicPrintConfig& config) {
|
||||
REQUIRE(config.opt<ConfigOptionString>("thumbnails")->empty());
|
||||
REQUIRE(config.option("thumbnails_format")->getInt() == (int)GCodeThumbnailsFormat::PNG);
|
||||
};
|
||||
|
||||
SECTION("Load empty init_data") {
|
||||
REQUIRE_NOTHROW(config.load_from_ini_string("", Enable));
|
||||
test_loaded_config(config);
|
||||
}
|
||||
|
||||
SECTION("Load empty format and empty thumbnails") {
|
||||
REQUIRE_THROWS_AS(config.load_from_ini_string(empty_thumbnails(), Enable), BadOptionValueException);
|
||||
test_loaded_config(config);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Validate New Thumbnails", "[Thumbnails in Config]") {
|
||||
|
||||
DynamicPrintConfig config = thumbnails_config();
|
||||
|
||||
auto test_loaded_config = [](DynamicPrintConfig& config, GCodeThumbnailsFormat format) {
|
||||
REQUIRE(!config.opt<ConfigOptionString>("thumbnails")->empty());
|
||||
REQUIRE(config.option("thumbnails_format")->getInt() == (int)format);
|
||||
};
|
||||
|
||||
SECTION("Test 1 (valid)") {
|
||||
REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails(), Enable));
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::JPG);
|
||||
}
|
||||
|
||||
SECTION("Test 2 (valid)") {
|
||||
REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails2(), Enable));
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
|
||||
}
|
||||
|
||||
SECTION("Test 3 (valid)") {
|
||||
REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails3(), Enable));
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
|
||||
}
|
||||
|
||||
|
||||
SECTION("Test 1 (out_of_range)") {
|
||||
REQUIRE_THROWS_AS(config.load_from_ini_string(out_of_range_thumbnails(), Enable), BadOptionValueException);
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
|
||||
}
|
||||
|
||||
SECTION("Test 2 (out_of_range)") {
|
||||
REQUIRE_THROWS_AS(config.load_from_ini_string(out_of_range_thumbnails2(), Enable), BadOptionValueException);
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
|
||||
}
|
||||
|
||||
|
||||
SECTION("Test 1 (invalid_ext)") {
|
||||
REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_ext_thumbnails(), Enable), BadOptionValueException);
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::QOI);
|
||||
}
|
||||
|
||||
SECTION("Test 2 (invalid_ext)") {
|
||||
REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_ext_thumbnails2(), Enable), BadOptionValueException);
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
|
||||
}
|
||||
|
||||
|
||||
SECTION("Test 1 (invalid_val)") {
|
||||
REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails(), Enable), BadOptionValueException);
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::JPG);
|
||||
}
|
||||
|
||||
SECTION("Test 2 (invalid_val)") {
|
||||
REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails2(), Enable), BadOptionValueException);
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
|
||||
}
|
||||
|
||||
SECTION("Test 3 (invalid_val)") {
|
||||
REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails3(), Enable), BadOptionValueException);
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
|
||||
}
|
||||
|
||||
SECTION("Test 4 (invalid_val)") {
|
||||
REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails4(), Enable), BadOptionValueException);
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Validate Old Thumbnails", "[Thumbnails in Config]") {
|
||||
|
||||
DynamicPrintConfig config = thumbnails_config();
|
||||
|
||||
auto test_loaded_config = [](DynamicPrintConfig& config, GCodeThumbnailsFormat format) {
|
||||
REQUIRE(!config.opt<ConfigOptionString>("thumbnails")->empty());
|
||||
REQUIRE(config.option("thumbnails_format")->getInt() == (int)format);
|
||||
};
|
||||
|
||||
SECTION("Test 1 (valid)") {
|
||||
REQUIRE_NOTHROW(config.load_from_ini_string(old_valid_thumbnails(), Enable));
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::JPG);
|
||||
}
|
||||
|
||||
SECTION("Test 2 (valid)") {
|
||||
REQUIRE_NOTHROW(config.load_from_ini_string(old_valid_thumbnails2(), Enable));
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
|
||||
}
|
||||
|
||||
SECTION("Test 1 (invalid)") {
|
||||
REQUIRE_THROWS_AS(config.load_from_ini_string(old_invalid_thumbnails(), Enable), BadOptionValueException);
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::JPG);
|
||||
}
|
||||
|
||||
SECTION("Test 2 (invalid)") {
|
||||
REQUIRE_THROWS_AS(config.load_from_ini_string(old_invalid_thumbnails2(), Enable), BadOptionValueException);
|
||||
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
152
tests/thumbnails/test_thumbnails_input_string.cpp
Normal file
152
tests/thumbnails/test_thumbnails_input_string.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <test_utils.hpp>
|
||||
|
||||
#include <libslic3r/GCode/Thumbnails.hpp>
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace GCodeThumbnails;
|
||||
|
||||
|
||||
// Test Thumbnails lines
|
||||
|
||||
static std::string empty_thumbnails()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
static std::string valid_thumbnails()
|
||||
{
|
||||
return "160x120/PNG, 23x78/QOI, 230x780/JPG";
|
||||
}
|
||||
|
||||
static std::string valid_thumbnails2()
|
||||
{
|
||||
return "160x120/PNG, 23x78/QOi, 320x240/PNg, 230x780/JPG";
|
||||
}
|
||||
|
||||
static std::string out_of_range_thumbnail()
|
||||
{
|
||||
return "160x1200/PNG, 23x78/QOI, 320x240/PNG, 230x780/JPG";
|
||||
}
|
||||
|
||||
static std::string out_of_range_thumbnail2()
|
||||
{
|
||||
return "160x120/PNG, 23x78/QOI, -320x240/PNG, 230x780/JPG";
|
||||
}
|
||||
|
||||
static std::string invalid_ext_thumbnail()
|
||||
{
|
||||
return "160x120/PNk, 23x78/QOI, 320x240/PNG, 230x780/JPG";
|
||||
}
|
||||
|
||||
static std::string invalid_ext_thumbnail2()
|
||||
{
|
||||
return "160x120/PNG, 23x78/QO, 320x240/PNG, 230x780/JPG";
|
||||
}
|
||||
|
||||
static std::string invalid_val_thumbnail()
|
||||
{
|
||||
return "160x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG";
|
||||
}
|
||||
|
||||
static std::string invalid_val_thumbnail2()
|
||||
{
|
||||
return "x120/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG";
|
||||
}
|
||||
|
||||
static std::string invalid_val_thumbnail3()
|
||||
{
|
||||
return "x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG";
|
||||
}
|
||||
|
||||
static std::string invalid_val_thumbnail4()
|
||||
{
|
||||
return "23*78/QOI, 320x240/PNG, 230x780/JPG";
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Empty Thumbnails", "[Thumbnails]") {
|
||||
auto [thumbnails, errors] = make_and_check_thumbnail_list(empty_thumbnails());
|
||||
REQUIRE(errors == enum_bitmask<ThumbnailError>());
|
||||
REQUIRE(thumbnails.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("Valid Thumbnails", "[Thumbnails]") {
|
||||
|
||||
SECTION("Test 1") {
|
||||
auto [thumbnails, errors] = make_and_check_thumbnail_list(valid_thumbnails());
|
||||
REQUIRE(errors == enum_bitmask<ThumbnailError>());
|
||||
REQUIRE(thumbnails.size() == 3);
|
||||
}
|
||||
|
||||
SECTION("Test 2") {
|
||||
auto [thumbnails, errors] = make_and_check_thumbnail_list(valid_thumbnails2());
|
||||
REQUIRE(errors == enum_bitmask<ThumbnailError>());
|
||||
REQUIRE(thumbnails.size() == 4);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Out of range Thumbnails", "[Thumbnails]") {
|
||||
|
||||
SECTION("Test 1") {
|
||||
auto [thumbnails, errors] = make_and_check_thumbnail_list(out_of_range_thumbnail());
|
||||
REQUIRE(errors != enum_bitmask<ThumbnailError>());
|
||||
REQUIRE(errors.has(ThumbnailError::OutOfRange));
|
||||
REQUIRE(thumbnails.size() == 3);
|
||||
}
|
||||
|
||||
SECTION("Test 2") {
|
||||
auto [thumbnails, errors] = make_and_check_thumbnail_list(out_of_range_thumbnail2());
|
||||
REQUIRE(errors != enum_bitmask<ThumbnailError>());
|
||||
REQUIRE(errors.has(ThumbnailError::OutOfRange));
|
||||
REQUIRE(thumbnails.size() == 3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Invalid extention Thumbnails", "[Thumbnails]") {
|
||||
|
||||
SECTION("Test 1") {
|
||||
auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_ext_thumbnail());
|
||||
REQUIRE(errors != enum_bitmask<ThumbnailError>());
|
||||
REQUIRE(errors.has(ThumbnailError::InvalidExt));
|
||||
REQUIRE(thumbnails.size() == 4);
|
||||
}
|
||||
|
||||
SECTION("Test 2") {
|
||||
auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_ext_thumbnail2());
|
||||
REQUIRE(errors != enum_bitmask<ThumbnailError>());
|
||||
REQUIRE(errors.has(ThumbnailError::InvalidExt));
|
||||
REQUIRE(thumbnails.size() == 4);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Invalid value Thumbnails", "[Thumbnails]") {
|
||||
|
||||
SECTION("Test 1") {
|
||||
auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail());
|
||||
REQUIRE(errors != enum_bitmask<ThumbnailError>());
|
||||
REQUIRE(errors.has(ThumbnailError::InvalidVal));
|
||||
REQUIRE(thumbnails.size() == 3);
|
||||
}
|
||||
|
||||
SECTION("Test 2") {
|
||||
auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail2());
|
||||
REQUIRE(errors != enum_bitmask<ThumbnailError>());
|
||||
REQUIRE(errors.has(ThumbnailError::InvalidVal));
|
||||
REQUIRE(thumbnails.size() == 3);
|
||||
}
|
||||
|
||||
SECTION("Test 3") {
|
||||
auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail3());
|
||||
REQUIRE(errors != enum_bitmask<ThumbnailError>());
|
||||
REQUIRE(errors.has(ThumbnailError::InvalidVal));
|
||||
REQUIRE(thumbnails.size() == 3);
|
||||
}
|
||||
|
||||
SECTION("Test 4") {
|
||||
auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail4());
|
||||
REQUIRE(errors != enum_bitmask<ThumbnailError>());
|
||||
REQUIRE(errors.has(ThumbnailError::InvalidVal));
|
||||
REQUIRE(thumbnails.size() == 2);
|
||||
}
|
||||
}
|
||||
1
tests/thumbnails/thumbnails_tests_main.cpp
Normal file
1
tests/thumbnails/thumbnails_tests_main.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include <catch_main.hpp>
|
||||
Reference in New Issue
Block a user