Implement a command-line interface.
This commit adds a basic command-line interface. This interface allows rendering thumbnails and exporting data in batch mode.pull/36/merge
parent
dbc567ed89
commit
47244c5e89
|
@ -29,6 +29,7 @@ New rendering features:
|
||||||
* The "Show/hide outlines" button is now independent from "Show/hide edges".
|
* The "Show/hide outlines" button is now independent from "Show/hide edges".
|
||||||
|
|
||||||
Other new features:
|
Other new features:
|
||||||
|
* New command-line interface, for batch exporting and more.
|
||||||
* New command for measuring total length of selected entities,
|
* New command for measuring total length of selected entities,
|
||||||
"Analyze → Measure Perimeter".
|
"Analyze → Measure Perimeter".
|
||||||
* New link to match the on-screen size of the sketch with its actual size,
|
* New link to match the on-screen size of the sketch with its actual size,
|
||||||
|
|
18
appveyor.yml
18
appveyor.yml
|
@ -9,28 +9,22 @@ before_build:
|
||||||
- cmake -G"Visual Studio 12" -T v120 ..
|
- cmake -G"Visual Studio 12" -T v120 ..
|
||||||
build_script:
|
build_script:
|
||||||
- msbuild "src\solvespace.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
- msbuild "src\solvespace.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
- msbuild "test\solvespace_testsuite.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
- msbuild "src\solvespace-cli.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
|
- msbuild "test\solvespace-testsuite.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
test_script:
|
test_script:
|
||||||
- bin\%BUILD_TYPE%\solvespace_testsuite.exe
|
- bin\%BUILD_TYPE%\solvespace-testsuite.exe
|
||||||
artifacts:
|
artifacts:
|
||||||
- path: build\bin\%BUILD_TYPE%\solvespace.exe
|
- path: build\bin\%BUILD_TYPE%\solvespace.exe
|
||||||
name: solvespace.exe
|
name: solvespace.exe
|
||||||
|
- path: build\bin\%BUILD_TYPE%\solvespace-cli.exe
|
||||||
|
name: solvespace-cli.exe
|
||||||
- path: build\bin\%BUILD_TYPE%\solvespace.pdb
|
- path: build\bin\%BUILD_TYPE%\solvespace.pdb
|
||||||
name: solvespace.pdb
|
name: solvespace.pdb
|
||||||
deploy:
|
deploy:
|
||||||
# Releases to solvespace/solvespace
|
|
||||||
- provider: GitHub
|
- provider: GitHub
|
||||||
auth_token:
|
auth_token:
|
||||||
secure: P9/pf2nM+jlWKe7pCjMp41HycBNP/+5AsmE/TETrDUoBOa/9WFHelqdVFrbRn9IC
|
secure: P9/pf2nM+jlWKe7pCjMp41HycBNP/+5AsmE/TETrDUoBOa/9WFHelqdVFrbRn9IC
|
||||||
description: ""
|
description: ""
|
||||||
artifact: solvespace.exe
|
artifact: solvespace.exe,solvespace-cli.exe,solvespace.pdb
|
||||||
on:
|
|
||||||
appveyor_repo_tag: true
|
|
||||||
# Releases to whitequark/solvespace (to be removed)
|
|
||||||
- provider: GitHub
|
|
||||||
auth_token:
|
|
||||||
secure: Flqxu1cz6PyxVT1wzTP4bSrQOY8wFrO7pJxYxvjEkLqIUU4dsDQrs2rac/A9deet
|
|
||||||
description: ""
|
|
||||||
artifact: solvespace.exe
|
|
||||||
on:
|
on:
|
||||||
appveyor_repo_tag: true
|
appveyor_repo_tag: true
|
||||||
|
|
|
@ -5,12 +5,13 @@ foreach(pkg_config_lib CAIRO)
|
||||||
link_directories(${${pkg_config_lib}_LIBRARY_DIRS})
|
link_directories(${${pkg_config_lib}_LIBRARY_DIRS})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
add_executable(solvespace_benchmark
|
add_executable(solvespace-benchmark
|
||||||
harness.cpp
|
harness.cpp
|
||||||
$<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
|
$<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
|
||||||
|
|
||||||
target_link_libraries(solvespace_benchmark
|
target_link_libraries(solvespace-benchmark
|
||||||
solvespace_headless)
|
solvespace-core
|
||||||
|
solvespace-headless)
|
||||||
|
|
||||||
add_dependencies(solvespace_benchmark
|
add_dependencies(solvespace-benchmark
|
||||||
resources)
|
resources)
|
||||||
|
|
|
@ -127,7 +127,7 @@ endif()
|
||||||
|
|
||||||
# solvespace library
|
# solvespace library
|
||||||
|
|
||||||
set(solvespace_cad_HEADERS
|
set(solvespace_core_HEADERS
|
||||||
config.h
|
config.h
|
||||||
dsc.h
|
dsc.h
|
||||||
expr.h
|
expr.h
|
||||||
|
@ -139,7 +139,7 @@ set(solvespace_cad_HEADERS
|
||||||
render/gl2shader.h
|
render/gl2shader.h
|
||||||
srf/surface.h)
|
srf/surface.h)
|
||||||
|
|
||||||
set(solvespace_cad_SOURCES
|
set(solvespace_core_SOURCES
|
||||||
bsp.cpp
|
bsp.cpp
|
||||||
clipboard.cpp
|
clipboard.cpp
|
||||||
confscreen.cpp
|
confscreen.cpp
|
||||||
|
@ -186,16 +186,16 @@ set(solvespace_cad_SOURCES
|
||||||
srf/surfinter.cpp
|
srf/surfinter.cpp
|
||||||
srf/triangulate.cpp)
|
srf/triangulate.cpp)
|
||||||
|
|
||||||
set(solvespace_cad_gl_SOURCES
|
set(solvespace_core_gl_SOURCES
|
||||||
export.cpp
|
export.cpp
|
||||||
solvespace.cpp)
|
solvespace.cpp)
|
||||||
|
|
||||||
add_library(solvespace_cad STATIC
|
add_library(solvespace-core STATIC
|
||||||
${util_SOURCES}
|
${util_SOURCES}
|
||||||
${solvespace_cad_HEADERS}
|
${solvespace_core_HEADERS}
|
||||||
${solvespace_cad_SOURCES})
|
${solvespace_core_SOURCES})
|
||||||
|
|
||||||
target_link_libraries(solvespace_cad
|
target_link_libraries(solvespace-core
|
||||||
dxfrw
|
dxfrw
|
||||||
${util_LIBRARIES}
|
${util_LIBRARIES}
|
||||||
${ZLIB_LIBRARY}
|
${ZLIB_LIBRARY}
|
||||||
|
@ -203,13 +203,13 @@ target_link_libraries(solvespace_cad
|
||||||
${FREETYPE_LIBRARY}
|
${FREETYPE_LIBRARY}
|
||||||
${Backtrace_LIBRARIES})
|
${Backtrace_LIBRARIES})
|
||||||
|
|
||||||
target_compile_options(solvespace_cad
|
target_compile_options(solvespace-core
|
||||||
PRIVATE ${COVERAGE_FLAGS})
|
PRIVATE ${COVERAGE_FLAGS})
|
||||||
|
|
||||||
# solvespace gui executable
|
# solvespace graphical executable
|
||||||
|
|
||||||
add_executable(solvespace WIN32 MACOSX_BUNDLE
|
add_executable(solvespace WIN32 MACOSX_BUNDLE
|
||||||
${solvespace_cad_gl_SOURCES}
|
${solvespace_core_gl_SOURCES}
|
||||||
${platform_SOURCES}
|
${platform_SOURCES}
|
||||||
$<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
|
$<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
|
||||||
|
|
||||||
|
@ -217,69 +217,93 @@ add_dependencies(solvespace
|
||||||
resources)
|
resources)
|
||||||
|
|
||||||
target_link_libraries(solvespace
|
target_link_libraries(solvespace
|
||||||
solvespace_cad
|
solvespace-core
|
||||||
${OPENGL_LIBRARIES}
|
${OPENGL_LIBRARIES}
|
||||||
${platform_LIBRARIES}
|
${platform_LIBRARIES}
|
||||||
${COVERAGE_LIBRARY})
|
${COVERAGE_LIBRARY})
|
||||||
|
|
||||||
if(WIN32 AND NOT MINGW)
|
if(MSVC)
|
||||||
set_target_properties(solvespace PROPERTIES
|
set_target_properties(solvespace PROPERTIES
|
||||||
LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO")
|
LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
foreach(lib ${platform_BUNDLED_LIBS})
|
|
||||||
get_filename_component(name ${lib} NAME)
|
|
||||||
set(target ${CMAKE_CURRENT_BINARY_DIR}/solvespace.app/Contents/MacOS/${name})
|
|
||||||
|
|
||||||
execute_process(COMMAND otool -D ${lib}
|
|
||||||
OUTPUT_VARIABLE canonical_lib OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
||||||
string(REGEX REPLACE "^.+:\n" "" canonical_lib ${canonical_lib})
|
|
||||||
add_custom_command(TARGET solvespace POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy ${lib} ${target}
|
|
||||||
COMMAND install_name_tool -change ${canonical_lib} @executable_path/${name}
|
|
||||||
$<TARGET_FILE:solvespace>
|
|
||||||
COMMENT "Bundling shared library ${lib}"
|
|
||||||
VERBATIM)
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
set(bundle solvespace)
|
|
||||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/${bundle}.dmg
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/${bundle}.dmg
|
|
||||||
COMMAND hdiutil create -srcfolder ${CMAKE_CURRENT_BINARY_DIR}/${bundle}.app
|
|
||||||
${CMAKE_BINARY_DIR}/${bundle}.dmg
|
|
||||||
DEPENDS $<TARGET_FILE:${bundle}>
|
|
||||||
COMMENT "Building ${bundle}.dmg"
|
|
||||||
VERBATIM)
|
|
||||||
add_custom_target(${bundle}-dmg ALL
|
|
||||||
DEPENDS ${CMAKE_BINARY_DIR}/${bundle}.dmg)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT WIN32)
|
|
||||||
install(TARGETS solvespace
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
BUNDLE DESTINATION .)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# solvespace headless library
|
# solvespace headless library
|
||||||
|
|
||||||
set(headless_SOURCES
|
set(headless_SOURCES
|
||||||
platform/headless.cpp
|
platform/headless.cpp
|
||||||
render/rendercairo.cpp)
|
render/rendercairo.cpp)
|
||||||
|
|
||||||
add_library(solvespace_headless STATIC EXCLUDE_FROM_ALL
|
add_library(solvespace-headless STATIC EXCLUDE_FROM_ALL
|
||||||
${solvespace_cad_gl_SOURCES}
|
${solvespace_core_gl_SOURCES}
|
||||||
${headless_SOURCES})
|
${headless_SOURCES})
|
||||||
|
|
||||||
target_compile_definitions(solvespace_headless
|
target_compile_definitions(solvespace-headless
|
||||||
PRIVATE -DHEADLESS)
|
PRIVATE -DHEADLESS)
|
||||||
|
|
||||||
target_include_directories(solvespace_headless
|
target_include_directories(solvespace-headless
|
||||||
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
target_link_libraries(solvespace_headless
|
target_link_libraries(solvespace-headless
|
||||||
solvespace_cad
|
solvespace-core
|
||||||
${CAIRO_LIBRARIES})
|
${CAIRO_LIBRARIES})
|
||||||
|
|
||||||
target_compile_options(solvespace_headless
|
target_compile_options(solvespace-headless
|
||||||
PRIVATE ${COVERAGE_FLAGS})
|
PRIVATE ${COVERAGE_FLAGS})
|
||||||
|
|
||||||
|
# solvespace command-line executable
|
||||||
|
|
||||||
|
add_executable(solvespace-cli
|
||||||
|
platform/climain.cpp
|
||||||
|
$<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
|
||||||
|
|
||||||
|
target_link_libraries(solvespace-cli
|
||||||
|
solvespace-core
|
||||||
|
solvespace-headless)
|
||||||
|
|
||||||
|
add_dependencies(solvespace-cli
|
||||||
|
resources)
|
||||||
|
|
||||||
|
# solvespace unix package
|
||||||
|
|
||||||
|
if(NOT (WIN32 OR APPLE))
|
||||||
|
install(TARGETS solvespace solvespace-cli
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# solvespace macOS package
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
set(bundle solvespace)
|
||||||
|
set(bundle_bin ${EXECUTABLE_OUTPUT_PATH}/${bundle}.app/Contents/MacOS)
|
||||||
|
|
||||||
|
add_custom_command(TARGET solvespace POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:solvespace-cli> ${bundle_bin}
|
||||||
|
COMMENT "Bundling executable solvespace-cli"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
foreach(lib ${platform_BUNDLED_LIBS})
|
||||||
|
get_filename_component(name ${lib} NAME)
|
||||||
|
|
||||||
|
execute_process(COMMAND otool -D ${lib}
|
||||||
|
OUTPUT_VARIABLE canonical_lib OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
string(REGEX REPLACE "^.+:\n" "" canonical_lib ${canonical_lib})
|
||||||
|
add_custom_command(TARGET ${bundle} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy ${lib} ${bundle_bin}/${name}
|
||||||
|
COMMAND install_name_tool -change ${canonical_lib} @executable_path/${name}
|
||||||
|
$<TARGET_FILE:${bundle}>
|
||||||
|
COMMAND install_name_tool -change ${canonical_lib} @executable_path/${name}
|
||||||
|
$<TARGET_FILE:solvespace-cli>
|
||||||
|
COMMENT "Bundling shared library ${lib}"
|
||||||
|
VERBATIM)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT ${EXECUTABLE_OUTPUT_PATH}/${bundle}.dmg
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E remove ${EXECUTABLE_OUTPUT_PATH}/${bundle}.dmg
|
||||||
|
COMMAND hdiutil create -srcfolder ${EXECUTABLE_OUTPUT_PATH}/${bundle}.app
|
||||||
|
${EXECUTABLE_OUTPUT_PATH}/${bundle}.dmg
|
||||||
|
DEPENDS $<TARGET_FILE:${bundle}>
|
||||||
|
COMMENT "Building ${bundle}.dmg"
|
||||||
|
VERBATIM)
|
||||||
|
add_custom_target(${bundle}-dmg ALL
|
||||||
|
DEPENDS ${EXECUTABLE_OUTPUT_PATH}/${bundle}.dmg)
|
||||||
|
endif()
|
||||||
|
|
|
@ -0,0 +1,317 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Our main() function for the command-line interface.
|
||||||
|
//
|
||||||
|
// Copyright 2016 whitequark
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#include "solvespace.h"
|
||||||
|
|
||||||
|
namespace SolveSpace {
|
||||||
|
// These are defined in headless.cpp, and aren't exposed in solvespace.h.
|
||||||
|
extern std::shared_ptr<Pixmap> framebuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ShowUsage(const char *argv0) {
|
||||||
|
fprintf(stderr, "Usage: %s <command> <options> <filename> [filename...]", argv0);
|
||||||
|
//-----------------------------------------------------------------------------> 80 col */
|
||||||
|
fprintf(stderr, R"(
|
||||||
|
When run, performs an action specified by <command> on every <filename>.
|
||||||
|
|
||||||
|
Common options:
|
||||||
|
-o, --output <pattern>
|
||||||
|
For an input file <basename>.slvs, replaces the '%%' symbol in <pattern>
|
||||||
|
with <basename> and uses it as output file. For example, when using
|
||||||
|
--output %%-2d.png for input files a.slvs and b.slvs, output files
|
||||||
|
a-2d.png and b-2d.png will be written.
|
||||||
|
-v, --view <direction>
|
||||||
|
Selects the camera direction. <direction> can be one of "top", "bottom",
|
||||||
|
"left", "right", "front", "back", or "isometric".
|
||||||
|
-t, --chord-tol <tolerance>
|
||||||
|
Selects the chord tolerance, used for converting exact curves to
|
||||||
|
piecewise linear, and exact surfaces into triangle meshes.
|
||||||
|
For export commands, the unit is mm, and the default is 1.0 mm.
|
||||||
|
For non-export commands, the unit is %%, and the default is 1.0 %%.
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
thumbnail --output <pattern> --size <size> --view <direction>
|
||||||
|
[--chord-tol <tolerance>]
|
||||||
|
Outputs a rendered view of the sketch, like the SolveSpace GUI would.
|
||||||
|
<size> is <width>x<height>, in pixels. Graphics acceleration is
|
||||||
|
not used, and the output may look slightly different from the GUI.
|
||||||
|
export-view --output <pattern> --view <direction> [--chord-tol <tolerance>]
|
||||||
|
Exports a view of the sketch, in a 2d vector format.
|
||||||
|
export-wireframe --output <pattern> [--chord-tol <tolerance>]
|
||||||
|
Exports a wireframe of the sketch, in a 3d vector format.
|
||||||
|
export-mesh --output <pattern> [--chord-tol <tolerance>]
|
||||||
|
Exports a triangle mesh of solids in the sketch, with exact surfaces
|
||||||
|
being triangulated first.
|
||||||
|
export-surfaces --output <pattern>
|
||||||
|
Exports exact surfaces of solids in the sketch, if any.
|
||||||
|
)");
|
||||||
|
|
||||||
|
auto FormatListFromFileFilter = [](const FileFilter *filter) {
|
||||||
|
std::string descr;
|
||||||
|
while(filter->name) {
|
||||||
|
descr += "\n ";
|
||||||
|
descr += filter->name;
|
||||||
|
descr += " (";
|
||||||
|
const char *const *patterns = filter->patterns;
|
||||||
|
while(*patterns) {
|
||||||
|
descr += *patterns;
|
||||||
|
if(*++patterns) {
|
||||||
|
descr += ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
descr += ")";
|
||||||
|
filter++;
|
||||||
|
}
|
||||||
|
return descr;
|
||||||
|
};
|
||||||
|
|
||||||
|
fprintf(stderr, R"(
|
||||||
|
File formats:
|
||||||
|
thumbnail:%s
|
||||||
|
export-view:%s
|
||||||
|
export-wireframe:%s
|
||||||
|
export-mesh:%s
|
||||||
|
export-surfaces:%s
|
||||||
|
)", FormatListFromFileFilter(PngFileFilter).c_str(),
|
||||||
|
FormatListFromFileFilter(VectorFileFilter).c_str(),
|
||||||
|
FormatListFromFileFilter(Vector3dFileFilter).c_str(),
|
||||||
|
FormatListFromFileFilter(MeshFileFilter).c_str(),
|
||||||
|
FormatListFromFileFilter(SurfaceFileFilter).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool RunCommand(size_t argc, char **argv) {
|
||||||
|
if(argc < 2) return false;
|
||||||
|
|
||||||
|
std::function<void(const std::string &)> runner;
|
||||||
|
|
||||||
|
std::vector<std::string> inputFiles;
|
||||||
|
auto ParseInputFile = [&](size_t &argn) {
|
||||||
|
std::string arg = argv[argn];
|
||||||
|
if(arg[0] != '-') {
|
||||||
|
inputFiles.push_back(arg);
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string outputPattern;
|
||||||
|
auto ParseOutputPattern = [&](size_t &argn) {
|
||||||
|
if(argn + 1 < argc && (!strcmp(argv[argn], "--output") ||
|
||||||
|
!strcmp(argv[argn], "-o"))) {
|
||||||
|
argn++;
|
||||||
|
outputPattern = argv[argn];
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector projUp, projRight;
|
||||||
|
auto ParseViewDirection = [&](size_t &argn) {
|
||||||
|
if(argn + 1 < argc && (!strcmp(argv[argn], "--view") ||
|
||||||
|
!strcmp(argv[argn], "-v"))) {
|
||||||
|
argn++;
|
||||||
|
if(!strcmp(argv[argn], "top")) {
|
||||||
|
projRight = Vector::From(1, 0, 0);
|
||||||
|
projUp = Vector::From(0, 1, 0);
|
||||||
|
} else if(!strcmp(argv[argn], "bottom")) {
|
||||||
|
projRight = Vector::From(-1, 0, 0);
|
||||||
|
projUp = Vector::From(0, 1, 0);
|
||||||
|
} else if(!strcmp(argv[argn], "left")) {
|
||||||
|
projRight = Vector::From(0, 1, 0);
|
||||||
|
projUp = Vector::From(0, 0, 1);
|
||||||
|
} else if(!strcmp(argv[argn], "right")) {
|
||||||
|
projRight = Vector::From(0, -1, 0);
|
||||||
|
projUp = Vector::From(0, 0, 1);
|
||||||
|
} else if(!strcmp(argv[argn], "front")) {
|
||||||
|
projRight = Vector::From(-1, 0, 0);
|
||||||
|
projUp = Vector::From(0, 0, 1);
|
||||||
|
} else if(!strcmp(argv[argn], "back")) {
|
||||||
|
projRight = Vector::From(1, 0, 0);
|
||||||
|
projUp = Vector::From(0, 0, 1);
|
||||||
|
} else if(!strcmp(argv[argn], "isometric")) {
|
||||||
|
projRight = Vector::From(0.707, 0.000, -0.707);
|
||||||
|
projUp = Vector::From(-0.408, 0.816, -0.408);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unrecognized view direction '%s'\n", argv[argn]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
double chordTol = 1.0;
|
||||||
|
auto ParseChordTolerance = [&](size_t &argn) {
|
||||||
|
if(argn + 1 < argc && (!strcmp(argv[argn], "--chord-tol") ||
|
||||||
|
!strcmp(argv[argn], "-t"))) {
|
||||||
|
argn++;
|
||||||
|
if(sscanf(argv[argn], "%lf", &chordTol) == 1) {
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
} else return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!strcmp(argv[1], "thumbnail")) {
|
||||||
|
unsigned width, height;
|
||||||
|
auto ParseSize = [&](size_t &argn) {
|
||||||
|
if(argn + 1 < argc && !strcmp(argv[argn], "--size")) {
|
||||||
|
argn++;
|
||||||
|
if(sscanf(argv[argn], "%ux%u", &width, &height) == 2) {
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
} else return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
for(size_t argn = 2; argn < argc; argn++) {
|
||||||
|
if(!(ParseInputFile(argn) ||
|
||||||
|
ParseOutputPattern(argn) ||
|
||||||
|
ParseViewDirection(argn) ||
|
||||||
|
ParseChordTolerance(argn) ||
|
||||||
|
ParseSize(argn))) {
|
||||||
|
fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(width == 0 || height == 0) {
|
||||||
|
fprintf(stderr, "Non-zero viewport size must be specified.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(EXACT(projUp.Magnitude() == 0 || projRight.Magnitude() == 0)) {
|
||||||
|
fprintf(stderr, "View direction must be specified.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
runner = [&](const std::string &output) {
|
||||||
|
SS.GW.width = width;
|
||||||
|
SS.GW.height = height;
|
||||||
|
SS.GW.projRight = projRight;
|
||||||
|
SS.GW.projUp = projUp;
|
||||||
|
SS.chordTol = chordTol;
|
||||||
|
|
||||||
|
SS.GW.ZoomToFit(/*includingInvisibles=*/false);
|
||||||
|
SS.GenerateAll();
|
||||||
|
PaintGraphics();
|
||||||
|
framebuffer->WritePng(output, /*flip=*/true);
|
||||||
|
};
|
||||||
|
} else if(!strcmp(argv[1], "export-view")) {
|
||||||
|
for(size_t argn = 2; argn < argc; argn++) {
|
||||||
|
if(!(ParseInputFile(argn) ||
|
||||||
|
ParseOutputPattern(argn) ||
|
||||||
|
ParseViewDirection(argn) ||
|
||||||
|
ParseChordTolerance(argn))) {
|
||||||
|
fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(EXACT(projUp.Magnitude() == 0 || projRight.Magnitude() == 0)) {
|
||||||
|
fprintf(stderr, "View direction must be specified.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
runner = [&](const std::string &output) {
|
||||||
|
SS.GW.projRight = projRight;
|
||||||
|
SS.GW.projUp = projUp;
|
||||||
|
SS.exportChordTol = chordTol;
|
||||||
|
|
||||||
|
SS.ExportViewOrWireframeTo(output, /*exportWireframe=*/false);
|
||||||
|
};
|
||||||
|
} else if(!strcmp(argv[1], "export-wireframe")) {
|
||||||
|
for(size_t argn = 2; argn < argc; argn++) {
|
||||||
|
if(!(ParseInputFile(argn) ||
|
||||||
|
ParseOutputPattern(argn) ||
|
||||||
|
ParseChordTolerance(argn))) {
|
||||||
|
fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runner = [&](const std::string &output) {
|
||||||
|
SS.exportChordTol = chordTol;
|
||||||
|
|
||||||
|
SS.ExportViewOrWireframeTo(output, /*exportWireframe=*/true);
|
||||||
|
};
|
||||||
|
} else if(!strcmp(argv[1], "export-mesh")) {
|
||||||
|
for(size_t argn = 2; argn < argc; argn++) {
|
||||||
|
if(!(ParseInputFile(argn) ||
|
||||||
|
ParseOutputPattern(argn) ||
|
||||||
|
ParseChordTolerance(argn))) {
|
||||||
|
fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runner = [&](const std::string &output) {
|
||||||
|
SS.exportChordTol = chordTol;
|
||||||
|
|
||||||
|
SS.ExportMeshTo(output);
|
||||||
|
};
|
||||||
|
} else if(!strcmp(argv[1], "export-surfaces")) {
|
||||||
|
for(size_t argn = 2; argn < argc; argn++) {
|
||||||
|
if(!(ParseInputFile(argn) ||
|
||||||
|
ParseOutputPattern(argn))) {
|
||||||
|
fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runner = [&](const std::string &output) {
|
||||||
|
StepFileWriter sfw = {};
|
||||||
|
sfw.ExportSurfacesTo(output);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unrecognized command '%s'.\n", argv[1]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(outputPattern.empty()) {
|
||||||
|
fprintf(stderr, "An output pattern must be specified.\n");
|
||||||
|
return false;
|
||||||
|
} else if(outputPattern.find('%') == std::string::npos && inputFiles.size() > 1) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Output pattern must include a %% symbol when using multiple inputs!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(inputFiles.size() == 0) {
|
||||||
|
fprintf(stderr, "At least one input file must be specified.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const std::string &inputFile : inputFiles) {
|
||||||
|
std::string outputFile = outputPattern;
|
||||||
|
size_t replaceAt = outputFile.find('%');
|
||||||
|
if(replaceAt != std::string::npos) {
|
||||||
|
outputFile.replace(replaceAt, 1, Basename(inputFile, /*stripExtension=*/true));
|
||||||
|
}
|
||||||
|
|
||||||
|
SS.Init();
|
||||||
|
if(!SS.LoadFromFile(inputFile)) {
|
||||||
|
fprintf(stderr, "Cannot load '%s'!\n", inputFile.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SS.AfterNewFile();
|
||||||
|
runner(outputFile);
|
||||||
|
SK.Clear();
|
||||||
|
SS.Clear();
|
||||||
|
|
||||||
|
fprintf(stderr, "Written '%s'.\n", outputFile.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
InitPlatform();
|
||||||
|
|
||||||
|
if(argc == 1) {
|
||||||
|
ShowUsage(argv[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!RunCommand(argc, argv)) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Our main() function for the headless (no OpenGL) test runner.
|
// Our platform support functions for the headless (no OpenGL) test runner.
|
||||||
//
|
//
|
||||||
// Copyright 2016 whitequark
|
// Copyright 2016 whitequark
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
@ -96,10 +96,8 @@ void ssremove(const std::string &filename)
|
||||||
|
|
||||||
static std::string ExpandPath(std::string path) {
|
static std::string ExpandPath(std::string path) {
|
||||||
char *expanded_c_path = realpath(path.c_str(), NULL);
|
char *expanded_c_path = realpath(path.c_str(), NULL);
|
||||||
if(expanded_c_path == NULL) {
|
if(expanded_c_path == NULL) return "";
|
||||||
fprintf(stderr, "realpath(%s): %s\n", path.c_str(), strerror(errno));
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
std::string expanded_path = expanded_c_path;
|
std::string expanded_path = expanded_c_path;
|
||||||
free(expanded_c_path);
|
free(expanded_c_path);
|
||||||
return expanded_path;
|
return expanded_path;
|
||||||
|
|
|
@ -182,7 +182,7 @@ const FileFilter SlvsFileFilter[] = {
|
||||||
};
|
};
|
||||||
// PNG format bitmap
|
// PNG format bitmap
|
||||||
const FileFilter PngFileFilter[] = {
|
const FileFilter PngFileFilter[] = {
|
||||||
{ "PNG", { "png" } },
|
{ "PNG file", { "png" } },
|
||||||
{ NULL, {} }
|
{ NULL, {} }
|
||||||
};
|
};
|
||||||
// Triangle mesh
|
// Triangle mesh
|
||||||
|
|
|
@ -57,19 +57,19 @@ set(testsuite_SOURCES
|
||||||
group/translate_nd/test.cpp
|
group/translate_nd/test.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(solvespace_testsuite
|
add_executable(solvespace-testsuite
|
||||||
${testsuite_SOURCES}
|
${testsuite_SOURCES}
|
||||||
$<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
|
$<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
|
||||||
|
|
||||||
target_link_libraries(solvespace_testsuite
|
target_link_libraries(solvespace-testsuite
|
||||||
solvespace_headless
|
solvespace-headless
|
||||||
${COVERAGE_LIBRARY})
|
${COVERAGE_LIBRARY})
|
||||||
|
|
||||||
add_dependencies(solvespace_testsuite
|
add_dependencies(solvespace-testsuite
|
||||||
resources)
|
resources)
|
||||||
|
|
||||||
add_custom_target(solvespace-test ALL
|
add_custom_target(test_solvespace ALL
|
||||||
COMMAND $<TARGET_FILE:solvespace_testsuite>
|
COMMAND $<TARGET_FILE:solvespace-testsuite>
|
||||||
COMMENT "Testing SolveSpace"
|
COMMENT "Testing SolveSpace"
|
||||||
VERBATIM)
|
VERBATIM)
|
||||||
|
|
||||||
|
@ -86,11 +86,11 @@ if(ENABLE_COVERAGE)
|
||||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/coverage_base.info
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/coverage_base.info
|
||||||
COMMAND ${LCOV} ${LCOV_FLAGS} ${LCOV_COLLECT}
|
COMMAND ${LCOV} ${LCOV_FLAGS} ${LCOV_COLLECT}
|
||||||
-o ${CMAKE_BINARY_DIR}/coverage_base.info -i
|
-o ${CMAKE_BINARY_DIR}/coverage_base.info -i
|
||||||
DEPENDS solvespace_testsuite
|
DEPENDS solvespace-testsuite
|
||||||
COMMENT "Importing baseline coverage data"
|
COMMENT "Importing baseline coverage data"
|
||||||
VERBATIM)
|
VERBATIM)
|
||||||
|
|
||||||
add_custom_target(solvespace-coverage ALL
|
add_custom_target(coverage_solvespace ALL
|
||||||
COMMAND ${LCOV} ${LCOV_FLAGS} ${LCOV_COLLECT}
|
COMMAND ${LCOV} ${LCOV_FLAGS} ${LCOV_COLLECT}
|
||||||
-o ${CMAKE_BINARY_DIR}/coverage_test.info
|
-o ${CMAKE_BINARY_DIR}/coverage_test.info
|
||||||
COMMAND ${LCOV} ${LCOV_FLAGS}
|
COMMAND ${LCOV} ${LCOV_FLAGS}
|
||||||
|
@ -104,7 +104,7 @@ if(ENABLE_COVERAGE)
|
||||||
-o ${CMAKE_BINARY_DIR}/coverage/
|
-o ${CMAKE_BINARY_DIR}/coverage/
|
||||||
-t "SolveSpace testbench"
|
-t "SolveSpace testbench"
|
||||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/coverage_base.info
|
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/coverage_base.info
|
||||||
DEPENDS solvespace-test
|
DEPENDS test_solvespace
|
||||||
COMMENT "Generating coverage report"
|
COMMENT "Generating coverage report"
|
||||||
VERBATIM)
|
VERBATIM)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/sh -ex
|
#!/bin/sh -ex
|
||||||
|
|
||||||
make -C build solvespace_testsuite
|
make -C build solvespace-testsuite
|
||||||
./build/test/solvespace_testsuite $* || true
|
./build/test/solvespace-testsuite $* || true
|
||||||
for e in slvs png; do
|
for e in slvs png; do
|
||||||
for i in `find . -name *.out.$e`; do
|
for i in `find . -name *.out.$e`; do
|
||||||
mv $i `dirname $i`/`basename $i .out.$e`.$e;
|
mv $i `dirname $i`/`basename $i .out.$e`.$e;
|
||||||
|
|
|
@ -228,7 +228,7 @@ bool Test::Helper::CheckRender(const char *file, int line, const char *reference
|
||||||
|
|
||||||
std::shared_ptr<Pixmap> refPixmap = Pixmap::ReadPng(refPath.c_str(), /*flip=*/true);
|
std::shared_ptr<Pixmap> refPixmap = Pixmap::ReadPng(refPath.c_str(), /*flip=*/true);
|
||||||
if(!RecordCheck(refPixmap && refPixmap->Equals(*framebuffer))) {
|
if(!RecordCheck(refPixmap && refPixmap->Equals(*framebuffer))) {
|
||||||
framebuffer->WritePng(outPath.c_str(), /*flip=*/true);
|
framebuffer->WritePng(outPath, /*flip=*/true);
|
||||||
|
|
||||||
if(!refPixmap) {
|
if(!refPixmap) {
|
||||||
PrintFailure(file, line, "reference render not present");
|
PrintFailure(file, line, "reference render not present");
|
||||||
|
|
Loading…
Reference in New Issue