Merge pull request #3 from solvespace/master

Merge from origin master.
pull/493/head
Yuan 2019-11-27 13:56:01 +08:00 committed by GitHub
commit cfd630f4ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 336 additions and 370 deletions

View File

@ -1,24 +1,27 @@
language: c language: c
os: git:
- linux submodules: false
- osx jobs:
sudo: required include:
dist: trusty - stage: test
osx_image: xcode8.2 name: "Debian"
install: os: linux
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./.travis/install-debian.sh; fi dist: bionic
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./.travis/install-macos.sh; fi install: ./.travis/install-debian.sh
script: script: ./.travis/build-debian.sh
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./.travis/build-debian.sh; fi - stage: deploy
# the awk command is a workaround for https://github.com/travis-ci/travis-ci/issues/4704. name: "OSX"
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./.travis/build-macos.sh | awk '/.{0,32}/ {print $0}'; fi os: osx
deploy: osx_image: xcode8.3
- provider: releases install: ./.travis/install-macos.sh
api_key: # the awk command is a workaround for https://github.com/travis-ci/travis-ci/issues/4704.
secure: dDlkIawHcODlW9B/20/cQCtzeoocvs0hKuNngRKXKqzXLWTRq33oq/B7+39tAixWbmv6exTpijiKrRNFiSCW5Z4iwHLwaRD4XJznxw63e/Hus/dxg2Tvqx7XFpkCz8mT1Z+gZQE5YxAngeZPpI/sZbZtF1UO3yH5eLeeokZ15p26ZskQUPoYuzrTgTzYL3XfpG3F+20rNBawH1ycsCTVD/08/n31d2m3CrKAsbW7er92ek6w4fzKr7NW8WeXjrPJETVpw5fQg1Od3pRGW8dPQaJcvKQEogMp8Mm0ETYd0qigg89/giBz7QwOgmAWQ4dH+DfZH4Ojl//127QztBolMvyDMQBykWrtJoGcij05sT6K2IJr2FHeUBO12MAEdjiVvhQj3DtTzjPiZAHHDBSLWxLKWWhlhHE4pq7g1MQhqXkaAHI2BLNzwLmaowbMT0bECf9yfz6xx18h6XPQFX44oOktraobVALFlyHqeKa8zdcUt22LF6uAL1m5dxL0tny3eXCIPE4UH/RZgua/cHV9G3cUvKQa/QnFSLRhvWVSbGB+7YsHouBJcsUOOW1gmd5442XuC7mpppccRldh+GSxUk6TBJRAx7TeQ0ybDUaoco9MUqp2twv3KreR2+8Q12PDaAhfQVNEGdF3wTm1sShImjCN4VN3eSLlBEbve1QRQXM= script: ./.travis/build-macos.sh | awk '/.{0,32}/ {print $0}'
skip_cleanup: true deploy:
file: build/SolveSpace.dmg provider: releases
on: api_key:
repo: solvespace/solvespace secure: dDlkIawHcODlW9B/20/cQCtzeoocvs0hKuNngRKXKqzXLWTRq33oq/B7+39tAixWbmv6exTpijiKrRNFiSCW5Z4iwHLwaRD4XJznxw63e/Hus/dxg2Tvqx7XFpkCz8mT1Z+gZQE5YxAngeZPpI/sZbZtF1UO3yH5eLeeokZ15p26ZskQUPoYuzrTgTzYL3XfpG3F+20rNBawH1ycsCTVD/08/n31d2m3CrKAsbW7er92ek6w4fzKr7NW8WeXjrPJETVpw5fQg1Od3pRGW8dPQaJcvKQEogMp8Mm0ETYd0qigg89/giBz7QwOgmAWQ4dH+DfZH4Ojl//127QztBolMvyDMQBykWrtJoGcij05sT6K2IJr2FHeUBO12MAEdjiVvhQj3DtTzjPiZAHHDBSLWxLKWWhlhHE4pq7g1MQhqXkaAHI2BLNzwLmaowbMT0bECf9yfz6xx18h6XPQFX44oOktraobVALFlyHqeKa8zdcUt22LF6uAL1m5dxL0tny3eXCIPE4UH/RZgua/cHV9G3cUvKQa/QnFSLRhvWVSbGB+7YsHouBJcsUOOW1gmd5442XuC7mpppccRldh+GSxUk6TBJRAx7TeQ0ybDUaoco9MUqp2twv3KreR2+8Q12PDaAhfQVNEGdF3wTm1sShImjCN4VN3eSLlBEbve1QRQXM=
tags: true skip_cleanup: true
condition: "$TRAVIS_OS_NAME == osx" file: build/SolveSpace.dmg
on:
repo: solvespace/solvespace
tags: true

View File

@ -4,12 +4,8 @@ if echo $TRAVIS_TAG | grep ^v; then BUILD_TYPE=RelWithDebInfo; else BUILD_TYPE=D
mkdir build mkdir build
cd build cd build
# We build without the GUI until Travis updates to an Ubuntu version with GTK 3.16+.
cmake .. \ cmake .. \
-DCMAKE_C_COMPILER=gcc-5 \
-DCMAKE_CXX_COMPILER=g++-5 \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DENABLE_GUI=OFF \
-DENABLE_SANITIZERS=ON -DENABLE_SANITIZERS=ON
make VERBOSE=1 make VERBOSE=1
make test_solvespace make test_solvespace

View File

@ -1,8 +1,10 @@
#!/bin/sh -xe #!/bin/sh -xe
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -q -y \ sudo apt-get install -q -y \
cmake cmake-data libc6-dev libpng12-dev zlib1g-dev libjson0-dev libfontconfig1-dev \ zlib1g-dev libpng-dev libcairo2-dev libfreetype6-dev libjson-c-dev \
libgtkmm-3.0-dev libpangomm-1.4-dev libcairo2-dev libgl1-mesa-dev libglu-dev \ libfontconfig1-dev libgtkmm-3.0-dev libpangomm-1.4-dev libgl-dev \
libfreetype6-dev dpkg-dev gcc-5 g++-5 lcov libgl-dev libglu-dev libspnav-dev
git submodule update --init extlib/libdxfrw extlib/flatbuffers extlib/q3d

View File

@ -2,3 +2,4 @@
brew update brew update
brew install freetype cairo brew install freetype cairo
git submodule update --init

View File

@ -42,6 +42,8 @@ New constraint features:
constraints on line segments. constraints on line segments.
* Automatic creation of constraints no longer happens if the constraint * Automatic creation of constraints no longer happens if the constraint
would have been redundant with other ones. would have been redundant with other ones.
* New option to open the constraint editor for newly created constraints
with a value.
New export/import features: New export/import features:
* Three.js: allow configuring projection for exported model, and initially * Three.js: allow configuring projection for exported model, and initially

View File

@ -8,7 +8,7 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
endif() endif()
cmake_minimum_required(VERSION 3.2.0 FATAL_ERROR) cmake_minimum_required(VERSION 3.2.0 FATAL_ERROR)
cmake_policy(VERSION 3.2.0) cmake_policy(VERSION 3.11)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_SOURCE_DIR}/cmake/") "${CMAKE_SOURCE_DIR}/cmake/")
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
@ -49,6 +49,9 @@ set(OPENGL 3 CACHE STRING "OpenGL version to use (one of: 1 3)")
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
if("${CMAKE_GENERATOR}" STREQUAL "Xcode")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_BINARY_DIR}/bin>)
endif()
if(NOT CMAKE_C_COMPILER_ID STREQUAL CMAKE_CXX_COMPILER_ID) if(NOT CMAKE_C_COMPILER_ID STREQUAL CMAKE_CXX_COMPILER_ID)
message(FATAL_ERROR "C and C++ compilers should be supplied by the same vendor") message(FATAL_ERROR "C and C++ compilers should be supplied by the same vendor")
@ -63,6 +66,15 @@ endif()
# common compiler flags # common compiler flags
include(CheckCXXCompilerFlag)
set(FILE_PREFIX_MAP "-ffile-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.")
check_cxx_compiler_flag("${FILE_PREFIX_MAP}" HAS_FILE_PREFIX_MAP)
if(HAS_FILE_PREFIX_MAP)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FILE_PREFIX_MAP}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FILE_PREFIX_MAP}")
endif()
if(MINGW) if(MINGW)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")

View File

@ -128,7 +128,6 @@ elseif(APPLE)
-fobjc-arc) -fobjc-arc)
list(APPEND platform_SOURCES list(APPEND platform_SOURCES
render/rendergl.cpp
platform/guimac.mm) platform/guimac.mm)
else() else()
list(APPEND platform_SOURCES list(APPEND platform_SOURCES

View File

@ -102,6 +102,11 @@ void TextWindow::ScreenChangeTurntableNav(int link, uint32_t v) {
} }
} }
void TextWindow::ScreenChangeImmediatelyEditDimension(int link, uint32_t v) {
SS.immediatelyEditDimension = !SS.immediatelyEditDimension;
SS.GW.Invalidate(/*clearPersistent=*/true);
}
void TextWindow::ScreenChangeShowContourAreas(int link, uint32_t v) { void TextWindow::ScreenChangeShowContourAreas(int link, uint32_t v) {
SS.showContourAreas = !SS.showContourAreas; SS.showContourAreas = !SS.showContourAreas;
SS.GW.Invalidate(); SS.GW.Invalidate();
@ -342,6 +347,9 @@ void TextWindow::ShowConfiguration() {
SS.automaticLineConstraints ? CHECK_TRUE : CHECK_FALSE); SS.automaticLineConstraints ? CHECK_TRUE : CHECK_FALSE);
Printf(false, " %Fd%f%Ll%s use turntable mouse navigation%E", &ScreenChangeTurntableNav, Printf(false, " %Fd%f%Ll%s use turntable mouse navigation%E", &ScreenChangeTurntableNav,
SS.turntableNav ? CHECK_TRUE : CHECK_FALSE); SS.turntableNav ? CHECK_TRUE : CHECK_FALSE);
Printf(false, " %Fd%f%Ll%s edit newly added dimensions%E",
&ScreenChangeImmediatelyEditDimension,
SS.immediatelyEditDimension ? CHECK_TRUE : CHECK_FALSE);
Printf(false, ""); Printf(false, "");
Printf(false, "%Ft autosave interval (in minutes)%E"); Printf(false, "%Ft autosave interval (in minutes)%E");
Printf(false, "%Ba %d %Fl%Ll%f[change]%E", Printf(false, "%Ba %d %Fl%Ll%f[change]%E",

View File

@ -196,6 +196,9 @@ void Constraint::MenuConstrain(Command id) {
c.valA = 0; c.valA = 0;
c.ModifyToSatisfy(); c.ModifyToSatisfy();
AddConstraint(&c); AddConstraint(&c);
if (SS.immediatelyEditDimension) {
SS.GW.EditConstraint(c.h);
}
break; break;
} }
@ -607,6 +610,9 @@ void Constraint::MenuConstrain(Command id) {
c.ModifyToSatisfy(); c.ModifyToSatisfy();
AddConstraint(&c); AddConstraint(&c);
if (SS.immediatelyEditDimension) {
SS.GW.EditConstraint(c.h);
}
break; break;
} }

View File

@ -845,7 +845,7 @@ void GraphicsWindow::Paint() {
canvas->SetLighting(lighting); canvas->SetLighting(lighting);
canvas->SetCamera(camera); canvas->SetCamera(camera);
canvas->NewFrame(); canvas->StartFrame();
Draw(canvas.get()); Draw(canvas.get());
canvas->FlushFrame(); canvas->FlushFrame();
@ -902,6 +902,7 @@ void GraphicsWindow::Paint() {
5, 5, renderTimeColor); 5, 5, renderTimeColor);
canvas->FlushFrame(); canvas->FlushFrame();
canvas->FinishFrame();
canvas->Clear(); canvas->Clear();
} }

View File

@ -480,11 +480,6 @@ public:
const T *cbegin() const { return begin(); } const T *cbegin() const { return begin(); }
const T *cend() const { return end(); } const T *cend() const { return end(); }
template<typename F>
size_t CountIf(F &&predicate) const {
return std::count_if(begin(), end(), std::forward<F&&>(predicate));
}
void ClearTags() { void ClearTags() {
for(auto &elt : *this) { elt.tag = 0; } for(auto &elt : *this) { elt.tag = 0; }
} }

View File

@ -50,7 +50,8 @@ bool Group::IsVisible() {
} }
size_t Group::GetNumConstraints() { size_t Group::GetNumConstraints() {
return SK.constraint.CountIf([&](Constraint const & c) { return c.group == h; }); return std::count_if(SK.constraint.begin(), SK.constraint.end(),
[&](Constraint const &c) { return c.group == h; });
} }
Vector Group::ExtrusionGetVector() { Vector Group::ExtrusionGetVector() {

View File

@ -1068,8 +1068,9 @@ public:
} }
}; };
static void ImportDwgDxf(const Platform::Path &filename, static void
std::function<bool(const std::string &data, DRW_Interface *intf)> read) { ImportDwgDxf(const Platform::Path &filename,
const std::function<bool(const std::string &data, DRW_Interface *intf)> &read) {
std::string fileType = ToUpper(filename.Extension()); std::string fileType = ToUpper(filename.Extension());
std::string data; std::string data;

View File

@ -13,7 +13,7 @@ static System SYS;
static int IsInit = 0; static int IsInit = 0;
void SolveSpace::Platform::FatalError(std::string message) { void SolveSpace::Platform::FatalError(const std::string &message) {
fprintf(stderr, "%s", message.c_str()); fprintf(stderr, "%s", message.c_str());
abort(); abort();
} }

View File

@ -1339,73 +1339,77 @@ void GraphicsWindow::MouseLeftUp(double mx, double my, bool shiftDown, bool ctrl
} }
} }
void GraphicsWindow::EditConstraint(hConstraint constraint) {
constraintBeingEdited = constraint;
ClearSuper();
Constraint *c = SK.GetConstraint(constraintBeingEdited);
if(!c->HasLabel()) {
// Not meaningful to edit a constraint without a dimension
return;
}
if(c->reference) {
// Not meaningful to edit a reference dimension
return;
}
Vector p3 = c->GetLabelPos(GetCamera());
Point2d p2 = ProjectPoint(p3);
std::string editValue;
std::string editPlaceholder;
switch(c->type) {
case Constraint::Type::COMMENT:
editValue = c->comment;
editPlaceholder = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
break;
default: {
double value = fabs(c->valA);
// If displayed as radius, also edit as radius.
if(c->type == Constraint::Type::DIAMETER && c->other)
value /= 2;
// Try showing value with default number of digits after decimal first.
if(c->type == Constraint::Type::LENGTH_RATIO) {
editValue = ssprintf("%.3f", value);
} else if(c->type == Constraint::Type::ANGLE) {
editValue = SS.DegreeToString(value);
} else {
editValue = SS.MmToString(value);
value /= SS.MmPerUnit();
}
// If that's not enough to represent it exactly, show the value with as many
// digits after decimal as required, up to 10.
int digits = 0;
while(fabs(std::stod(editValue) - value) > 1e-10) {
editValue = ssprintf("%.*f", digits, value);
digits++;
}
editPlaceholder = "10.000000";
break;
}
}
double width, height;
window->GetContentSize(&width, &height);
hStyle hs = c->disp.style;
if(hs.v == 0) hs.v = Style::CONSTRAINT;
double capHeight = Style::TextHeight(hs);
double fontHeight = VectorFont::Builtin()->GetHeight(capHeight);
double editMinWidth = VectorFont::Builtin()->GetWidth(capHeight, editPlaceholder);
window->ShowEditor(p2.x + width / 2, height / 2 - p2.y,
fontHeight, editMinWidth,
/*isMonospace=*/false, editValue);
}
void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) { void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) {
if(window->IsEditorVisible()) return; if(window->IsEditorVisible()) return;
SS.TW.HideEditControl(); SS.TW.HideEditControl();
if(hover.constraint.v) { if(hover.constraint.v) {
constraintBeingEdited = hover.constraint; EditConstraint(hover.constraint);
ClearSuper();
Constraint *c = SK.GetConstraint(constraintBeingEdited);
if(!c->HasLabel()) {
// Not meaningful to edit a constraint without a dimension
return;
}
if(c->reference) {
// Not meaningful to edit a reference dimension
return;
}
Vector p3 = c->GetLabelPos(GetCamera());
Point2d p2 = ProjectPoint(p3);
std::string editValue;
std::string editPlaceholder;
switch(c->type) {
case Constraint::Type::COMMENT:
editValue = c->comment;
editPlaceholder = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
break;
default: {
double value = fabs(c->valA);
// If displayed as radius, also edit as radius.
if(c->type == Constraint::Type::DIAMETER && c->other)
value /= 2;
// Try showing value with default number of digits after decimal first.
if(c->type == Constraint::Type::LENGTH_RATIO) {
editValue = ssprintf("%.3f", value);
} else if(c->type == Constraint::Type::ANGLE) {
editValue = SS.DegreeToString(value);
} else {
editValue = SS.MmToString(value);
value /= SS.MmPerUnit();
}
// If that's not enough to represent it exactly, show the value with as many
// digits after decimal as required, up to 10.
int digits = 0;
while(fabs(std::stod(editValue) - value) > 1e-10) {
editValue = ssprintf("%.*f", digits, value);
digits++;
}
editPlaceholder = "10.000000";
break;
}
}
double width, height;
window->GetContentSize(&width, &height);
hStyle hs = c->disp.style;
if(hs.v == 0) hs.v = Style::CONSTRAINT;
double capHeight = Style::TextHeight(hs);
double fontHeight = VectorFont::Builtin()->GetHeight(capHeight);
double editMinWidth = VectorFont::Builtin()->GetWidth(capHeight, editPlaceholder);
window->ShowEditor(p2.x + width / 2, height / 2 - p2.y,
fontHeight, editMinWidth,
/*isMonospace=*/false, editValue);
} }
} }

View File

@ -208,9 +208,10 @@ static bool RunCommand(const std::vector<std::string> args) {
pixmapCanvas.SetCamera(camera); pixmapCanvas.SetCamera(camera);
pixmapCanvas.Init(); pixmapCanvas.Init();
pixmapCanvas.NewFrame(); pixmapCanvas.StartFrame();
SS.GW.Draw(&pixmapCanvas); SS.GW.Draw(&pixmapCanvas);
pixmapCanvas.FlushFrame(); pixmapCanvas.FlushFrame();
pixmapCanvas.FinishFrame();
pixmapCanvas.ReadFrame()->WritePng(output, /*flip=*/true); pixmapCanvas.ReadFrame()->WritePng(output, /*flip=*/true);
pixmapCanvas.Clear(); pixmapCanvas.Clear();

View File

@ -102,7 +102,7 @@ std::string AcceleratorDescription(const KeyboardEvent &accel);
#if defined(__GNUC__) #if defined(__GNUC__)
__attribute__((noreturn)) __attribute__((noreturn))
#endif #endif
void FatalError(std::string message); void FatalError(const std::string &message);
// A native settings store. // A native settings store.
class Settings { class Settings {

View File

@ -50,7 +50,7 @@ static std::string PrepareMnemonics(std::string label) {
return label; return label;
} }
static std::string PrepareTitle(std::string title) { static std::string PrepareTitle(const std::string &title) {
return title + " — SolveSpace"; return title + " — SolveSpace";
} }
@ -58,7 +58,7 @@ static std::string PrepareTitle(std::string title) {
// Fatal errors // Fatal errors
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void FatalError(std::string message) { void FatalError(const std::string &message) {
fprintf(stderr, "%s", message.c_str()); fprintf(stderr, "%s", message.c_str());
abort(); abort();
} }

View File

@ -84,7 +84,7 @@ crash_info_t crashAnnotation __attribute__((section("__DATA,__crash_info"))) = {
CRASH_VERSION, NULL, NULL, NULL, NULL, NULL, NULL CRASH_VERSION, NULL, NULL, NULL, NULL, NULL, NULL
}; };
void FatalError(std::string message) { void FatalError(const std::string &message) {
crashAnnotation.message = message.c_str(); crashAnnotation.message = message.c_str();
abort(); abort();
} }
@ -225,9 +225,9 @@ public:
NSUInteger modifierMask = 0; NSUInteger modifierMask = 0;
if(accel.shiftDown) if(accel.shiftDown)
modifierMask |= NSShiftKeyMask; modifierMask |= NSEventModifierFlagShift;
if(accel.controlDown) if(accel.controlDown)
modifierMask |= NSCommandKeyMask; modifierMask |= NSEventModifierFlagCommand;
nsMenuItem.keyEquivalentModifierMask = modifierMask; nsMenuItem.keyEquivalentModifierMask = modifierMask;
} }
@ -236,7 +236,7 @@ public:
} }
void SetActive(bool active) override { void SetActive(bool active) override {
nsMenuItem.state = active ? NSOnState : NSOffState; nsMenuItem.state = active ? NSControlStateValueOn : NSControlStateValueOff;
} }
void SetEnabled(bool enabled) override { void SetEnabled(bool enabled) override {
@ -344,7 +344,7 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
// Cocoa NSView and NSWindow extensions // Cocoa NSView and NSWindow extensions
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@interface SSView : NSView @interface SSView : NSOpenGLView
@property Platform::Window *receiver; @property Platform::Window *receiver;
@property BOOL acceptsFirstResponder; @property BOOL acceptsFirstResponder;
@ -362,8 +362,6 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
@implementation SSView @implementation SSView
{ {
GlOffscreen offscreen;
NSOpenGLContext *glContext;
NSTrackingArea *trackingArea; NSTrackingArea *trackingArea;
NSTextField *editor; NSTextField *editor;
} }
@ -371,17 +369,15 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
@synthesize acceptsFirstResponder; @synthesize acceptsFirstResponder;
- (id)initWithFrame:(NSRect)frameRect { - (id)initWithFrame:(NSRect)frameRect {
if(self = [super initWithFrame:frameRect]) { NSOpenGLPixelFormatAttribute attrs[] = {
NSOpenGLPFAColorSize, 24,
NSOpenGLPFADepthSize, 24,
0
};
NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
if(self = [super initWithFrame:frameRect pixelFormat:pixelFormat]) {
self.wantsBestResolutionOpenGLSurface = YES;
self.wantsLayer = YES; self.wantsLayer = YES;
NSOpenGLPixelFormatAttribute attrs[] = {
NSOpenGLPFAColorSize, 24,
NSOpenGLPFADepthSize, 24,
0
};
NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:NULL];
editor = [[NSTextField alloc] init]; editor = [[NSTextField alloc] init];
editor.editable = YES; editor.editable = YES;
[[editor cell] setWraps:NO]; [[editor cell] setWraps:NO];
@ -394,7 +390,6 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
} }
- (void)dealloc { - (void)dealloc {
offscreen.Clear();
} }
- (BOOL)isFlipped { - (BOOL)isFlipped {
@ -404,29 +399,11 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
@synthesize receiver; @synthesize receiver;
- (void)drawRect:(NSRect)aRect { - (void)drawRect:(NSRect)aRect {
[glContext makeCurrentContext]; [[self openGLContext] makeCurrentContext];
if(receiver->onRender) {
NSSize size = [self convertSizeToBacking:self.bounds.size]; receiver->onRender();
int width = (int)size.width, }
height = (int)size.height; [[self openGLContext] flushBuffer];
offscreen.Render(width, height, [&] {
if(receiver->onRender) {
receiver->onRender();
}
});
CGDataProviderRef provider = CGDataProviderCreateWithData(
NULL, &offscreen.data[0], width * height * 4, NULL);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGImageRef image = CGImageCreate(width, height, 8, 32,
width * 4, colorspace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
provider, NULL, true, kCGRenderingIntentDefault);
CGContextDrawImage((CGContextRef) [[NSGraphicsContext currentContext] graphicsPort],
[self bounds], image);
CGImageRelease(image);
CGDataProviderRelease(provider);
} }
- (BOOL)acceptsFirstMouse:(NSEvent *)event { - (BOOL)acceptsFirstMouse:(NSEvent *)event {
@ -453,8 +430,8 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
event.y = self.bounds.size.height - nsPoint.y; event.y = self.bounds.size.height - nsPoint.y;
NSUInteger nsFlags = [nsEvent modifierFlags]; NSUInteger nsFlags = [nsEvent modifierFlags];
if(nsFlags & NSShiftKeyMask) event.shiftDown = true; if(nsFlags & NSEventModifierFlagShift) event.shiftDown = true;
if(nsFlags & NSCommandKeyMask) event.controlDown = true; if(nsFlags & NSEventModifierFlagCommand) event.controlDown = true;
return event; return event;
} }
@ -587,9 +564,9 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
KeyboardEvent event = {}; KeyboardEvent event = {};
NSUInteger nsFlags = [nsEvent modifierFlags]; NSUInteger nsFlags = [nsEvent modifierFlags];
if(nsFlags & NSShiftKeyMask) if(nsFlags & NSEventModifierFlagShift)
event.shiftDown = true; event.shiftDown = true;
if(nsFlags & NSCommandKeyMask) if(nsFlags & NSEventModifierFlagCommand)
event.controlDown = true; event.controlDown = true;
unichar chr = 0; unichar chr = 0;
@ -610,7 +587,7 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
- (void)keyDown:(NSEvent *)nsEvent { - (void)keyDown:(NSEvent *)nsEvent {
using Platform::KeyboardEvent; using Platform::KeyboardEvent;
if([NSEvent modifierFlags] & ~(NSShiftKeyMask|NSCommandKeyMask)) { if([NSEvent modifierFlags] & ~(NSEventModifierFlagShift|NSEventModifierFlagCommand)) {
[super keyDown:nsEvent]; [super keyDown:nsEvent];
return; return;
} }
@ -629,7 +606,7 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
- (void)keyUp:(NSEvent *)nsEvent { - (void)keyUp:(NSEvent *)nsEvent {
using Platform::KeyboardEvent; using Platform::KeyboardEvent;
if([NSEvent modifierFlags] & ~(NSShiftKeyMask|NSCommandKeyMask)) { if([NSEvent modifierFlags] & ~(NSEventModifierFlagShift|NSEventModifierFlagCommand)) {
[super keyUp:nsEvent]; [super keyUp:nsEvent];
return; return;
} }
@ -811,16 +788,16 @@ public:
switch(kind) { switch(kind) {
case Window::Kind::TOPLEVEL: case Window::Kind::TOPLEVEL:
nsWindow = [[NSWindow alloc] init]; nsWindow = [[NSWindow alloc] init];
nsWindow.styleMask = NSTitledWindowMask | NSResizableWindowMask | nsWindow.styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable |
NSClosableWindowMask | NSMiniaturizableWindowMask; NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenPrimary; nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenPrimary;
ssView.acceptsFirstResponder = YES; ssView.acceptsFirstResponder = YES;
break; break;
case Window::Kind::TOOL: case Window::Kind::TOOL:
NSPanel *nsPanel = [[NSPanel alloc] init]; NSPanel *nsPanel = [[NSPanel alloc] init];
nsPanel.styleMask = NSTitledWindowMask | NSResizableWindowMask | nsPanel.styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable |
NSClosableWindowMask | NSUtilityWindowMask; NSWindowStyleMaskClosable | NSWindowStyleMaskUtilityWindow;
[nsPanel standardWindowButton:NSWindowMiniaturizeButton].hidden = YES; [nsPanel standardWindowButton:NSWindowMiniaturizeButton].hidden = YES;
[nsPanel standardWindowButton:NSWindowZoomButton].hidden = YES; [nsPanel standardWindowButton:NSWindowZoomButton].hidden = YES;
nsPanel.floatingPanel = YES; nsPanel.floatingPanel = YES;
@ -892,7 +869,7 @@ public:
} }
void GetContentSize(double *width, double *height) override { void GetContentSize(double *width, double *height) override {
NSSize nsSize = [ssView frame].size; NSSize nsSize = ssView.frame.size;
*width = nsSize.width; *width = nsSize.width;
*height = nsSize.height; *height = nsSize.height;
} }
@ -975,9 +952,10 @@ public:
} }
void SetScrollbarPosition(double pos) override { void SetScrollbarPosition(double pos) override {
if(pos > ssView.scrollerMax) { if(pos > ssView.scrollerMax)
pos = ssView.scrollerMax; pos = ssView.scrollerMax;
} if(GetScrollbarPosition() == pos)
return;
[nsScroller setDoubleValue:(pos / (ssView.scrollerMax - ssView.scrollerMin))]; [nsScroller setDoubleValue:(pos / (ssView.scrollerMax - ssView.scrollerMin))];
if(onScrollbarAdjusted) { if(onScrollbarAdjusted) {
onScrollbarAdjusted(pos); onScrollbarAdjusted(pos);
@ -1128,17 +1106,17 @@ void Open3DConnexion() {
connexionClient = registerConnexionClient(connexionSignature, connexionName, connexionClient = registerConnexionClient(connexionSignature, connexionName,
kConnexionClientModeTakeOver, kConnexionMaskButtons | kConnexionMaskAxis); kConnexionClientModeTakeOver, kConnexionMaskButtons | kConnexionMaskAxis);
[NSEvent addLocalMonitorForEventsMatchingMask:(NSKeyDownMask | NSFlagsChangedMask) [NSEvent addLocalMonitorForEventsMatchingMask:(NSEventMaskKeyDown | NSEventMaskFlagsChanged)
handler:^(NSEvent *event) { handler:^(NSEvent *event) {
connexionShiftIsDown = (event.modifierFlags & NSShiftKeyMask); connexionShiftIsDown = (event.modifierFlags & NSEventModifierFlagShift);
connexionCommandIsDown = (event.modifierFlags & NSCommandKeyMask); connexionCommandIsDown = (event.modifierFlags & NSEventModifierFlagCommand);
return event; return event;
}]; }];
[NSEvent addLocalMonitorForEventsMatchingMask:(NSKeyUpMask | NSFlagsChangedMask) [NSEvent addLocalMonitorForEventsMatchingMask:(NSEventMaskKeyUp | NSEventMaskFlagsChanged)
handler:^(NSEvent *event) { handler:^(NSEvent *event) {
connexionShiftIsDown = (event.modifierFlags & NSShiftKeyMask); connexionShiftIsDown = (event.modifierFlags & NSEventModifierFlagShift);
connexionCommandIsDown = (event.modifierFlags & NSCommandKeyMask); connexionCommandIsDown = (event.modifierFlags & NSEventModifierFlagCommand);
return event; return event;
}]; }];
} }
@ -1173,12 +1151,12 @@ public:
switch(type) { switch(type) {
case Type::INFORMATION: case Type::INFORMATION:
case Type::QUESTION: case Type::QUESTION:
nsAlert.alertStyle = NSInformationalAlertStyle; nsAlert.alertStyle = NSAlertStyleInformational;
break; break;
case Type::WARNING: case Type::WARNING:
case Type::ERROR: case Type::ERROR:
nsAlert.alertStyle = NSWarningAlertStyle; nsAlert.alertStyle = NSAlertStyleWarning;
break; break;
} }
} }
@ -1289,7 +1267,7 @@ public:
} }
bool RunModal() override { bool RunModal() override {
if([nsPanel runModal] == NSFileHandlingPanelOKButton) { if([nsPanel runModal] == NSModalResponseOK) {
return true; return true;
} else { } else {
return false; return false;
@ -1431,6 +1409,9 @@ void OpenInBrowser(const std::string &url) {
@implementation SSApplicationDelegate @implementation SSApplicationDelegate
- (IBAction)preferences:(id)sender { - (IBAction)preferences:(id)sender {
if (!SS.GW.showTextWindow) {
SolveSpace::SS.GW.MenuView(SolveSpace::Command::SHOW_TEXT_WND);
}
SolveSpace::SS.TW.GoToScreen(SolveSpace::TextWindow::Screen::CONFIGURATION); SolveSpace::SS.TW.GoToScreen(SolveSpace::TextWindow::Screen::CONFIGURATION);
SolveSpace::SS.ScheduleShowTW(); SolveSpace::SS.ScheduleShowTW();
} }

View File

@ -22,7 +22,7 @@ namespace Platform {
// Fatal errors // Fatal errors
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void FatalError(std::string message) { void FatalError(const std::string &message) {
fprintf(stderr, "%s", message.c_str()); fprintf(stderr, "%s", message.c_str());
#if !defined(LIBRARY) && defined(HAVE_BACKTRACE) #if !defined(LIBRARY) && defined(HAVE_BACKTRACE)

View File

@ -152,13 +152,12 @@ static int Clamp(int x, int a, int b) {
bool handlingFatalError = false; bool handlingFatalError = false;
void FatalError(std::string message) { void FatalError(const std::string &message) {
// Indicate that we're handling a fatal error, to avoid re-entering application code // Indicate that we're handling a fatal error, to avoid re-entering application code
// and potentially crashing even harder. // and potentially crashing even harder.
handlingFatalError = true; handlingFatalError = true;
message += "\nGenerate debug report?"; switch(MessageBoxW(NULL, Platform::Widen(message + "\nGenerate debug report?").c_str(),
switch(MessageBoxW(NULL, Platform::Widen(message).c_str(),
L"Fatal error — SolveSpace", L"Fatal error — SolveSpace",
MB_ICONERROR|MB_TASKMODAL|MB_SETFOREGROUND|MB_TOPMOST| MB_ICONERROR|MB_TASKMODAL|MB_SETFOREGROUND|MB_TOPMOST|
MB_OKCANCEL|MB_DEFBUTTON2)) { MB_OKCANCEL|MB_DEFBUTTON2)) {
@ -1355,7 +1354,11 @@ public:
SCROLLINFO si = {}; SCROLLINFO si = {};
si.cbSize = sizeof(si); si.cbSize = sizeof(si);
si.fMask = SIF_POS; si.fMask = SIF_POS;
si.nPos = (UINT)(pos * SCROLLBAR_UNIT); sscheck(GetScrollInfo(hWindow, SB_VERT, &si));
if(si.nPos == (int)(pos * SCROLLBAR_UNIT))
return;
si.nPos = (int)(pos * SCROLLBAR_UNIT);
sscheck(SetScrollInfo(hWindow, SB_VERT, &si, /*redraw=*/TRUE)); sscheck(SetScrollInfo(hWindow, SB_VERT, &si, /*redraw=*/TRUE));
// Windows won't synthesize a WM_VSCROLL for us here. // Windows won't synthesize a WM_VSCROLL for us here.

View File

@ -302,16 +302,10 @@ bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir) co
// but they are considered to cross if they are coincident and overlapping. // but they are considered to cross if they are coincident and overlapping.
// If pi is not NULL, then a crossing is returned in that. // If pi is not NULL, then a crossing is returned in that.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
int SEdgeList::AnyEdgeCrossings(Vector a, Vector b, int SEdgeList::AnyEdgeCrossings(Vector a, Vector b, Vector *ppi, SPointList *spl) const {
Vector *ppi, SPointList *spl) const auto cnt = std::count_if(l.begin(), l.end(),
{ [&](SEdge const &se) { return se.EdgeCrosses(a, b, ppi, spl); });
int cnt = 0; return static_cast<int>(cnt);
for(const SEdge *se = l.First(); se; se = l.NextAfter(se)) {
if(se->EdgeCrosses(a, b, ppi, spl)) {
cnt++;
}
}
return cnt;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -706,15 +700,10 @@ bool SPolygon::ContainsPoint(Vector p) const {
return (WindingNumberForPoint(p) % 2) == 1; return (WindingNumberForPoint(p) % 2) == 1;
} }
int SPolygon::WindingNumberForPoint(Vector p) const { size_t SPolygon::WindingNumberForPoint(Vector p) const {
int winding = 0; auto winding = std::count_if(l.begin(), l.end(), [&](const SContour &sc) {
int i; return sc.ContainsPointProjdToNormal(normal, p);
for(i = 0; i < l.n; i++) { });
const SContour *sc = &(l[i]);
if(sc->ContainsPointProjdToNormal(normal, p)) {
winding++;
}
}
return winding; return winding;
} }

View File

@ -149,7 +149,7 @@ public:
Vector ComputeNormal() const; Vector ComputeNormal() const;
void AddEmptyContour(); void AddEmptyContour();
int WindingNumberForPoint(Vector p) const; size_t WindingNumberForPoint(Vector p) const;
double SignedArea() const; double SignedArea() const;
bool ContainsPoint(Vector p) const; bool ContainsPoint(Vector p) const;
void MakeEdgesInto(SEdgeList *el) const; void MakeEdgesInto(SEdgeList *el) const;
@ -184,7 +184,6 @@ public:
Vector Normal() const; Vector Normal() const;
void FlipNormal(); void FlipNormal();
double MinAltitude() const; double MinAltitude() const;
int WindingNumberForPoint(Vector p) const;
bool ContainsPoint(Vector p) const; bool ContainsPoint(Vector p) const;
bool ContainsPointProjd(Vector n, Vector p) const; bool ContainsPointProjd(Vector n, Vector p) const;
STriangle Transform(Vector o, Vector u, Vector v) const; STriangle Transform(Vector o, Vector u, Vector v) const;
@ -403,11 +402,10 @@ public:
Vertex *AddVertex(const Vector &pos); Vertex *AddVertex(const Vector &pos);
Edge *AddEdge(const Vector &p0, const Vector &p1, uint32_t kind, uintptr_t data = 0); Edge *AddEdge(const Vector &p0, const Vector &p1, uint32_t kind, uintptr_t data = 0);
void Generate( void Generate(std::function<void(Vertex *start, Vertex *next, Edge *edge)> const &startFunc,
std::function<void(Vertex *start, Vertex *next, Edge *edge)> startFunc, std::function<void(Vertex *next, Edge *edge)> const &nextFunc,
std::function<void(Vertex *next, Edge *edge)> nextFunc, std::function<void(Edge *)> const &aloneFunc,
std::function<void(Edge *)> aloneFunc, std::function<void()> const &endFunc = []() {});
std::function<void()> endFunc = [](){});
void MakeFromEdges(const SEdgeList &sel); void MakeFromEdges(const SEdgeList &sel);
void MakeFromOutlines(const SOutlineList &sol); void MakeFromOutlines(const SOutlineList &sol);

View File

@ -141,10 +141,9 @@ PolylineBuilder::Edge *PolylineBuilder::AddEdge(const Vector &p0, const Vector &
} }
void PolylineBuilder::Generate( void PolylineBuilder::Generate(
std::function<void(Vertex *start, Vertex *next, Edge *edge)> startFunc, std::function<void(Vertex *start, Vertex *next, Edge *edge)> const &startFunc,
std::function<void(Vertex *next, Edge *edge)> nextFunc, std::function<void(Vertex *next, Edge *edge)> const &nextFunc,
std::function<void(Edge *alone)> aloneFunc, std::function<void(Edge *alone)> const &aloneFunc, std::function<void()> const &endFunc) {
std::function<void()> endFunc) {
bool found; bool found;
bool loop = false; bool loop = false;
do { do {

View File

@ -162,7 +162,7 @@ void Shader::Clear() {
glDeleteProgram(program); glDeleteProgram(program);
} }
void Shader::SetUniformMatrix(const char *name, double *md) { void Shader::SetUniformMatrix(const char *name, const double *md) {
Enable(); Enable();
float mf[16]; float mf[16];
for(int i = 0; i < 16; i++) mf[i] = (float)md[i]; for(int i = 0; i < 16; i++) mf[i] = (float)md[i];
@ -388,8 +388,9 @@ GLuint Generate(const std::vector<double> &pattern) {
GLint size; GLint size;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
RgbaColor *textureData = new RgbaColor[size]; size /= 2;
RgbaColor *textureData = new RgbaColor[size];
int mipCount = (int)log2(size) + 1; int mipCount = (int)log2(size) + 1;
for(int mip = 0; mip < mipCount; mip++) { for(int mip = 0; mip < mipCount; mip++) {
int dashI = 0; int dashI = 0;
@ -423,8 +424,8 @@ GLuint Generate(const std::vector<double> &pattern) {
textureData); textureData);
size /= 2; size /= 2;
} }
delete []textureData; delete []textureData;
return texture; return texture;
} }
@ -621,11 +622,11 @@ void EdgeRenderer::Draw(const SEdgeList &edges) {
Remove(handle); Remove(handle);
} }
void EdgeRenderer::SetModelview(double *matrix) { void EdgeRenderer::SetModelview(const double *matrix) {
shader.SetUniformMatrix("modelview", matrix); shader.SetUniformMatrix("modelview", matrix);
} }
void EdgeRenderer::SetProjection(double *matrix) { void EdgeRenderer::SetProjection(const double *matrix) {
shader.SetUniformMatrix("projection", matrix); shader.SetUniformMatrix("projection", matrix);
} }
@ -833,11 +834,11 @@ void OutlineRenderer::Draw(const SOutlineList &outlines, Canvas::DrawOutlinesAs
Remove(handle); Remove(handle);
} }
void OutlineRenderer::SetModelview(double *matrix) { void OutlineRenderer::SetModelview(const double *matrix) {
shader.SetUniformMatrix("modelview", matrix); shader.SetUniformMatrix("modelview", matrix);
} }
void OutlineRenderer::SetProjection(double *matrix) { void OutlineRenderer::SetProjection(const double *matrix) {
shader.SetUniformMatrix("projection", matrix); shader.SetUniformMatrix("projection", matrix);
} }
@ -1037,14 +1038,14 @@ bool IndexedMeshRenderer::NeedsTexture() const {
selectedShader == &pointShader; selectedShader == &pointShader;
} }
void IndexedMeshRenderer::SetModelview(double *matrix) { void IndexedMeshRenderer::SetModelview(const double *matrix) {
colShader.SetUniformMatrix("modelview", matrix); colShader.SetUniformMatrix("modelview", matrix);
texShader.SetUniformMatrix("modelview", matrix); texShader.SetUniformMatrix("modelview", matrix);
texaShader.SetUniformMatrix("modelview", matrix); texaShader.SetUniformMatrix("modelview", matrix);
pointShader.SetUniformMatrix("modelview", matrix); pointShader.SetUniformMatrix("modelview", matrix);
} }
void IndexedMeshRenderer::SetProjection(double *matrix) { void IndexedMeshRenderer::SetProjection(const double *matrix) {
colShader.SetUniformMatrix("projection", matrix); colShader.SetUniformMatrix("projection", matrix);
texShader.SetUniformMatrix("projection", matrix); texShader.SetUniformMatrix("projection", matrix);
texaShader.SetUniformMatrix("projection", matrix); texaShader.SetUniformMatrix("projection", matrix);

View File

@ -74,7 +74,7 @@ public:
const std::vector<std::pair<GLuint, std::string>> &locations = {}); const std::vector<std::pair<GLuint, std::string>> &locations = {});
void Clear(); void Clear();
void SetUniformMatrix(const char *name, double *md); void SetUniformMatrix(const char *name, const double *md);
void SetUniformVector(const char *name, const Vector &v); void SetUniformVector(const char *name, const Vector &v);
void SetUniformVector(const char *name, const Vector4f &v); void SetUniformVector(const char *name, const Vector4f &v);
void SetUniformColor(const char *name, RgbaColor c); void SetUniformColor(const char *name, RgbaColor c);
@ -163,8 +163,8 @@ public:
void Draw(const Handle &handle); void Draw(const Handle &handle);
void Draw(const SEdgeList &edges); void Draw(const SEdgeList &edges);
void SetModelview(double *matrix); void SetModelview(const double *matrix);
void SetProjection(double *matrix); void SetProjection(const double *matrix);
void SetStroke(const Canvas::Stroke &stroke, double pixel); void SetStroke(const Canvas::Stroke &stroke, double pixel);
}; };
@ -203,8 +203,8 @@ public:
void Draw(const Handle &handle, Canvas::DrawOutlinesAs mode); void Draw(const Handle &handle, Canvas::DrawOutlinesAs mode);
void Draw(const SOutlineList &outlines, Canvas::DrawOutlinesAs mode); void Draw(const SOutlineList &outlines, Canvas::DrawOutlinesAs mode);
void SetModelview(double *matrix); void SetModelview(const double *matrix);
void SetProjection(double *matrix); void SetProjection(const double *matrix);
void SetStroke(const Canvas::Stroke &stroke, double pixel); void SetStroke(const Canvas::Stroke &stroke, double pixel);
}; };
@ -252,8 +252,8 @@ public:
void Draw(const Handle &handle); void Draw(const Handle &handle);
void Draw(const SIndexedMesh &mesh); void Draw(const SIndexedMesh &mesh);
void SetModelview(double *matrix); void SetModelview(const double *matrix);
void SetProjection(double *matrix); void SetProjection(const double *matrix);
bool NeedsTexture() const; bool NeedsTexture() const;

View File

@ -444,12 +444,11 @@ void ObjectPicker::DrawPixmap(std::shared_ptr<const Pixmap> pm,
DrawQuad(o, o.Plus(u), o.Plus(u).Plus(v), o.Plus(v), hcf); DrawQuad(o, o.Plus(u), o.Plus(u).Plus(v), o.Plus(v), hcf);
} }
bool ObjectPicker::Pick(std::function<void()> drawFn) { bool ObjectPicker::Pick(const std::function<void()> &drawFn) {
minDistance = VERY_POSITIVE; minDistance = VERY_POSITIVE;
maxZIndex = INT_MIN; maxZIndex = INT_MIN;
drawFn(); drawFn();
return minDistance < selRadius; return minDistance < selRadius;
} }
} }

View File

@ -186,8 +186,9 @@ public:
virtual void SetCamera(const Camera &camera) = 0; virtual void SetCamera(const Camera &camera) = 0;
virtual void SetLighting(const Lighting &lighting) = 0; virtual void SetLighting(const Lighting &lighting) = 0;
virtual void NewFrame() = 0; virtual void StartFrame() = 0;
virtual void FlushFrame() = 0; virtual void FlushFrame() = 0;
virtual void FinishFrame() = 0;
virtual std::shared_ptr<Pixmap> ReadFrame() = 0; virtual std::shared_ptr<Pixmap> ReadFrame() = 0;
virtual void GetIdent(const char **vendor, const char **renderer, const char **version) = 0; virtual void GetIdent(const char **vendor, const char **renderer, const char **version) = 0;
@ -260,7 +261,7 @@ public:
void DoQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d, void DoQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
int zIndex, int comparePosition = 0); int zIndex, int comparePosition = 0);
bool Pick(std::function<void()> drawFn); bool Pick(const std::function<void()> &drawFn);
}; };
// A canvas that renders onto a 2d surface, performing z-index sorting, occlusion testing, etc, // A canvas that renders onto a 2d surface, performing z-index sorting, occlusion testing, etc,
@ -342,8 +343,9 @@ public:
void Clear() override; void Clear() override;
void NewFrame() override {} void StartFrame() override {}
void FlushFrame() override; void FlushFrame() override;
void FinishFrame() override {}
std::shared_ptr<Pixmap> ReadFrame() override; std::shared_ptr<Pixmap> ReadFrame() override;
void GetIdent(const char **vendor, const char **renderer, const char **version) override; void GetIdent(const char **vendor, const char **renderer, const char **version) override;
@ -373,22 +375,6 @@ public:
std::shared_ptr<Pixmap> ReadFrame() override; std::shared_ptr<Pixmap> ReadFrame() override;
}; };
//-----------------------------------------------------------------------------
// 3d renderers
//-----------------------------------------------------------------------------
// An offscreen renderer based on OpenGL framebuffers.
class GlOffscreen {
public:
unsigned int framebuffer = 0;
unsigned int colorRenderbuffer = 0;
unsigned int depthRenderbuffer = 0;
std::vector<uint8_t> data;
bool Render(int width, int height, std::function<void()> renderFn);
void Clear();
};
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Factories // Factories
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -1,58 +0,0 @@
//-----------------------------------------------------------------------------
// Offscreen rendering in OpenGL using EGL and framebuffer objects.
//
// Copyright 2015-2016 whitequark
//-----------------------------------------------------------------------------
#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#endif
#include "solvespace.h"
void GlOffscreen::Clear() {
glDeleteRenderbuffersEXT(1, &depthRenderbuffer);
glDeleteRenderbuffersEXT(1, &colorRenderbuffer);
glDeleteFramebuffersEXT(1, &framebuffer);
*this = {};
}
bool GlOffscreen::Render(int width, int height, std::function<void()> renderFn) {
data.resize(width * height * 4);
if(framebuffer == 0)
glGenFramebuffersEXT(1, &framebuffer);
if(colorRenderbuffer == 0)
glGenRenderbuffersEXT(1, &colorRenderbuffer);
if(depthRenderbuffer == 0)
glGenRenderbuffersEXT(1, &depthRenderbuffer);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, colorRenderbuffer);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_RENDERBUFFER_EXT, colorRenderbuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthRenderbuffer);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, depthRenderbuffer);
bool framebufferComplete =
glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
if(framebufferComplete) {
renderFn();
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &data[0]);
#else
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, &data[0]);
#endif
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
return framebufferComplete;
}

View File

@ -220,8 +220,9 @@ public:
void SetCamera(const Camera &camera) override; void SetCamera(const Camera &camera) override;
void SetLighting(const Lighting &lighting) override; void SetLighting(const Lighting &lighting) override;
void NewFrame() override; void StartFrame() override;
void FlushFrame() override; void FlushFrame() override;
void FinishFrame() override;
std::shared_ptr<Pixmap> ReadFrame() override; std::shared_ptr<Pixmap> ReadFrame() override;
void GetIdent(const char **vendor, const char **renderer, const char **version) override; void GetIdent(const char **vendor, const char **renderer, const char **version) override;
@ -751,7 +752,7 @@ void OpenGl1Renderer::UpdateProjection() {
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT);
} }
void OpenGl1Renderer::NewFrame() { void OpenGl1Renderer::StartFrame() {
glEnable(GL_NORMALIZE); glEnable(GL_NORMALIZE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -806,7 +807,12 @@ void OpenGl1Renderer::NewFrame() {
void OpenGl1Renderer::FlushFrame() { void OpenGl1Renderer::FlushFrame() {
UnSelectPrimitive(); UnSelectPrimitive();
glFlush(); glFlush();
}
void OpenGl1Renderer::FinishFrame() {
glFinish();
GLenum error = glGetError(); GLenum error = glGetError();
if(error != GL_NO_ERROR) { if(error != GL_NO_ERROR) {

View File

@ -87,6 +87,9 @@ public:
Fill *fill; Fill *fill;
std::weak_ptr<const Pixmap> texture; std::weak_ptr<const Pixmap> texture;
} current; } current;
const char *vendor = "<uninitialized>";
const char *renderer = "<uninitialized>";
const char *version = "<uninitialized>";
// List-initialize current to work around MSVC bug 746973. // List-initialize current to work around MSVC bug 746973.
OpenGl3Renderer() : OpenGl3Renderer() :
@ -134,8 +137,9 @@ public:
void SetCamera(const Camera &c) override; void SetCamera(const Camera &c) override;
void SetLighting(const Lighting &l) override; void SetLighting(const Lighting &l) override;
void NewFrame() override; void StartFrame() override;
void FlushFrame() override; void FlushFrame() override;
void FinishFrame() override;
void Clear() override; void Clear() override;
std::shared_ptr<Pixmap> ReadFrame() override; std::shared_ptr<Pixmap> ReadFrame() override;
@ -439,6 +443,10 @@ void OpenGl3Renderer::Init() {
meshRenderer.Init(); meshRenderer.Init();
imeshRenderer.Init(); imeshRenderer.Init();
vendor = (const char *)glGetString(GL_VENDOR);
renderer = (const char *)glGetString(GL_RENDERER);
version = (const char *)glGetString(GL_VERSION);
#if !defined(HAVE_GLES) && !defined(__APPLE__) #if !defined(HAVE_GLES) && !defined(__APPLE__)
GLuint array; GLuint array;
glGenVertexArrays(1, &array); glGenVertexArrays(1, &array);
@ -618,7 +626,7 @@ void OpenGl3Renderer::UpdateProjection() {
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT);
} }
void OpenGl3Renderer::NewFrame() { void OpenGl3Renderer::StartFrame() {
if(!initialized) { if(!initialized) {
Init(); Init();
initialized = true; initialized = true;
@ -667,6 +675,10 @@ void OpenGl3Renderer::FlushFrame() {
} }
points.Clear(); points.Clear();
glFlush();
}
void OpenGl3Renderer::FinishFrame() {
glFinish(); glFinish();
GLenum error = glGetError(); GLenum error = glGetError();
@ -691,9 +703,9 @@ std::shared_ptr<Pixmap> OpenGl3Renderer::ReadFrame() {
} }
void OpenGl3Renderer::GetIdent(const char **vendor, const char **renderer, const char **version) { void OpenGl3Renderer::GetIdent(const char **vendor, const char **renderer, const char **version) {
*vendor = (const char *)glGetString(GL_VENDOR); *vendor = this->vendor;
*renderer = (const char *)glGetString(GL_RENDERER); *renderer = this->renderer;
*version = (const char *)glGetString(GL_VERSION); *version = this->version;
} }
void OpenGl3Renderer::SetCamera(const Camera &c) { void OpenGl3Renderer::SetCamera(const Camera &c) {

View File

@ -918,7 +918,7 @@ Vector VectorFont::GetExtents(double forCapHeight, const std::string &str) {
} }
void VectorFont::Trace(double forCapHeight, Vector o, Vector u, Vector v, const std::string &str, void VectorFont::Trace(double forCapHeight, Vector o, Vector u, Vector v, const std::string &str,
std::function<void(Vector, Vector)> traceEdge) { const std::function<void(Vector, Vector)> &traceEdge) {
ssassert(!IsEmpty(), "Expected a loaded font"); ssassert(!IsEmpty(), "Expected a loaded font");
double scale = (forCapHeight / capHeight); double scale = (forCapHeight / capHeight);
@ -945,7 +945,7 @@ void VectorFont::Trace(double forCapHeight, Vector o, Vector u, Vector v, const
} }
void VectorFont::Trace(double forCapHeight, Vector o, Vector u, Vector v, const std::string &str, void VectorFont::Trace(double forCapHeight, Vector o, Vector u, Vector v, const std::string &str,
std::function<void(Vector, Vector)> traceEdge, const Camera &camera) { const std::function<void(Vector, Vector)> &traceEdge, const Camera &camera) {
ssassert(!IsEmpty(), "Expected a loaded font"); ssassert(!IsEmpty(), "Expected a loaded font");
// Perform grid-fitting only when the text is parallel to the view plane. // Perform grid-fitting only when the text is parallel to the view plane.

View File

@ -103,9 +103,9 @@ public:
Vector GetExtents(double forCapHeight, const std::string &str); Vector GetExtents(double forCapHeight, const std::string &str);
void Trace(double forCapHeight, Vector o, Vector u, Vector v, const std::string &str, void Trace(double forCapHeight, Vector o, Vector u, Vector v, const std::string &str,
std::function<void(Vector, Vector)> traceEdge); const std::function<void(Vector, Vector)> &traceEdge);
void Trace(double forCapHeight, Vector o, Vector u, Vector v, const std::string &str, void Trace(double forCapHeight, Vector o, Vector u, Vector v, const std::string &str,
std::function<void(Vector, Vector)> traceEdge, const Camera &camera); const std::function<void(Vector, Vector)> &traceEdge, const Camera &camera);
}; };
#endif #endif

View File

@ -72,6 +72,8 @@ void SolveSpaceUI::Init() {
drawBackFaces = settings->ThawBool("DrawBackFaces", true); drawBackFaces = settings->ThawBool("DrawBackFaces", true);
// Use turntable mouse navigation // Use turntable mouse navigation
turntableNav = settings->ThawBool("TurntableNav", false); turntableNav = settings->ThawBool("TurntableNav", false);
// Immediately edit dimension
immediatelyEditDimension = settings->ThawBool("ImmediatelyEditDimension", false);
// Check that contours are closed and not self-intersecting // Check that contours are closed and not self-intersecting
checkClosedContour = settings->ThawBool("CheckClosedContour", true); checkClosedContour = settings->ThawBool("CheckClosedContour", true);
// Enable automatic constrains for lines // Enable automatic constrains for lines
@ -251,6 +253,8 @@ void SolveSpaceUI::Exit() {
settings->FreezeBool("CheckClosedContour", checkClosedContour); settings->FreezeBool("CheckClosedContour", checkClosedContour);
// Use turntable mouse navigation // Use turntable mouse navigation
settings->FreezeBool("TurntableNav", turntableNav); settings->FreezeBool("TurntableNav", turntableNav);
// Immediately edit dimensions
settings->FreezeBool("ImmediatelyEditDimension", immediatelyEditDimension);
// Enable automatic constrains for lines // Enable automatic constrains for lines
settings->FreezeBool("AutomaticLineConstraints", automaticLineConstraints); settings->FreezeBool("AutomaticLineConstraints", automaticLineConstraints);
// Export shaded triangles in a 2d view // Export shaded triangles in a 2d view
@ -799,7 +803,6 @@ void SolveSpaceUI::MenuAnalyze(Command id) {
Group *g = SK.GetGroup(SS.GW.activeGroup); Group *g = SK.GetGroup(SS.GW.activeGroup);
SS.GW.GroupSelection(); SS.GW.GroupSelection();
auto const &gs = SS.GW.gs; auto const &gs = SS.GW.gs;
double scale = SS.MmPerUnit();
if(gs.faces > 0) { if(gs.faces > 0) {
std::vector<uint32_t> faces; std::vector<uint32_t> faces;

View File

@ -587,6 +587,7 @@ public:
bool showContourAreas; bool showContourAreas;
bool checkClosedContour; bool checkClosedContour;
bool turntableNav; bool turntableNav;
bool immediatelyEditDimension;
bool automaticLineConstraints; bool automaticLineConstraints;
bool showToolbar; bool showToolbar;
Platform::Path screenshotFile; Platform::Path screenshotFile;

View File

@ -193,28 +193,24 @@ void SSurface::TrimFromEdgeList(SEdgeList *el, bool asUv) {
static bool KeepRegion(SSurface::CombineAs type, bool opA, SShell::Class shell, SShell::Class orig) static bool KeepRegion(SSurface::CombineAs type, bool opA, SShell::Class shell, SShell::Class orig)
{ {
bool inShell = (shell == SShell::Class::INSIDE), bool inShell = (shell == SShell::Class::INSIDE),
outSide = (shell == SShell::Class::OUTSIDE),
inSame = (shell == SShell::Class::COINC_SAME), inSame = (shell == SShell::Class::COINC_SAME),
inOpp = (shell == SShell::Class::COINC_OPP),
inOrig = (orig == SShell::Class::INSIDE); inOrig = (orig == SShell::Class::INSIDE);
bool inFace = inSame || inOpp;
// If these are correct, then they should be independent of inShell
// if inFace is true.
if(!inOrig) return false; if(!inOrig) return false;
switch(type) { switch(type) {
case SSurface::CombineAs::UNION: case SSurface::CombineAs::UNION:
if(opA) { if(opA) {
return (!inShell && !inFace); return outSide;
} else { } else {
return (!inShell && !inFace) || inSame; return outSide || inSame;
} }
case SSurface::CombineAs::DIFFERENCE: case SSurface::CombineAs::DIFFERENCE:
if(opA) { if(opA) {
return (!inShell && !inFace); return outSide;
} else { } else {
return (inShell && !inFace) || inSame; return inShell || inSame;
} }
default: ssassert(false, "Unexpected combine type"); default: ssassert(false, "Unexpected combine type");

View File

@ -563,21 +563,24 @@ void TextWindow::Show() {
} }
} }
if(window) { if(window) Resize();
double width, height; }
window->GetContentSize(&width, &height);
halfRows = (int)height / (LINE_HEIGHT/2); void TextWindow::Resize()
{
double width, height;
window->GetContentSize(&width, &height);
int bottom = top[rows-1] + 2; halfRows = (int)height / (LINE_HEIGHT/2);
scrollPos = min(scrollPos, bottom - halfRows);
scrollPos = max(scrollPos, 0);
window->ConfigureScrollbar(0, top[rows - 1] + 1, halfRows); int bottom = top[rows-1] + 2;
window->SetScrollbarPosition(scrollPos); scrollPos = min(scrollPos, bottom - halfRows);
window->SetScrollbarVisible(top[rows - 1] + 1 > halfRows); scrollPos = max(scrollPos, 0);
window->Invalidate();
} window->ConfigureScrollbar(0, top[rows - 1] + 1, halfRows);
window->SetScrollbarPosition(scrollPos);
window->SetScrollbarVisible(top[rows - 1] + 1 > halfRows);
window->Invalidate();
} }
void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow how, void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow how,
@ -909,6 +912,8 @@ void TextWindow::Paint() {
double width, height; double width, height;
window->GetContentSize(&width, &height); window->GetContentSize(&width, &height);
if(halfRows != (int)height / (LINE_HEIGHT/2))
Resize();
Camera camera = {}; Camera camera = {};
camera.width = width; camera.width = width;
@ -924,7 +929,7 @@ void TextWindow::Paint() {
canvas->SetLighting(lighting); canvas->SetLighting(lighting);
canvas->SetCamera(camera); canvas->SetCamera(camera);
canvas->NewFrame(); canvas->StartFrame();
UiCanvas uiCanvas = {}; UiCanvas uiCanvas = {};
uiCanvas.canvas = canvas; uiCanvas.canvas = canvas;
@ -1041,6 +1046,7 @@ void TextWindow::Paint() {
DrawOrHitTestColorPicker(&uiCanvas, PAINT, false, 0, 0); DrawOrHitTestColorPicker(&uiCanvas, PAINT, false, 0, 0);
canvas->FlushFrame(); canvas->FlushFrame();
canvas->FinishFrame();
canvas->Clear(); canvas->Clear();
} }
@ -1123,8 +1129,10 @@ void TextWindow::MouseLeave() {
} }
void TextWindow::ScrollbarEvent(double newPos) { void TextWindow::ScrollbarEvent(double newPos) {
if(window->IsEditorVisible()) if(window->IsEditorVisible()) {
window->SetScrollbarPosition(scrollPos);
return; return;
}
int bottom = top[rows-1] + 2; int bottom = top[rows-1] + 2;
newPos = min((int)newPos, bottom - halfRows); newPos = min((int)newPos, bottom - halfRows);

View File

@ -97,9 +97,9 @@ TtfFont *TtfFontList::LoadFont(const std::string &font)
if(tf != l.end()) { if(tf != l.end()) {
if(tf->fontFace == NULL) { if(tf->fontFace == NULL) {
if(tf->IsResource()) if(tf->IsResource())
tf->LoadFromResource(fontLibrary, /*nameOnly=*/false); tf->LoadFromResource(fontLibrary, /*keepOpen=*/true);
else else
tf->LoadFromFile(fontLibrary, /*nameOnly=*/false); tf->LoadFromFile(fontLibrary, /*keepOpen=*/true);
} }
return tf; return tf;
} else { } else {
@ -155,7 +155,7 @@ bool TtfFont::IsResource() const {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Load a TrueType font into memory. // Load a TrueType font into memory.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
bool TtfFont::LoadFromFile(FT_Library fontLibrary, bool nameOnly) { bool TtfFont::LoadFromFile(FT_Library fontLibrary, bool keepOpen) {
ssassert(!IsResource(), "Cannot load a font provided by a resource as a file."); ssassert(!IsResource(), "Cannot load a font provided by a resource as a file.");
FT_Open_Args args = {}; FT_Open_Args args = {};
@ -171,14 +171,14 @@ bool TtfFont::LoadFromFile(FT_Library fontLibrary, bool nameOnly) {
return false; return false;
} }
return ExtractTTFData(nameOnly); return ExtractTTFData(keepOpen);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Load a TrueType from resource in memory. Implemented to load bundled fonts // Load a TrueType from resource in memory. Implemented to load bundled fonts
// through theresource system. // through theresource system.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
bool TtfFont::LoadFromResource(FT_Library fontLibrary, bool nameOnly) { bool TtfFont::LoadFromResource(FT_Library fontLibrary, bool keepOpen) {
ssassert(IsResource(), "Font to be loaded as resource is not provided by a resource " ssassert(IsResource(), "Font to be loaded as resource is not provided by a resource "
"or does not have the 'res://' prefix."); "or does not have the 'res://' prefix.");
@ -196,13 +196,13 @@ bool TtfFont::LoadFromResource(FT_Library fontLibrary, bool nameOnly) {
return false; return false;
} }
return ExtractTTFData(nameOnly); return ExtractTTFData(keepOpen);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Extract font information. We care about the font name and unit size. // Extract font information. We care about the font name and unit size.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
bool TtfFont::ExtractTTFData(bool nameOnly) { bool TtfFont::ExtractTTFData(bool keepOpen) {
if(int fterr = FT_Select_Charmap(fontFace, FT_ENCODING_UNICODE)) { if(int fterr = FT_Select_Charmap(fontFace, FT_ENCODING_UNICODE)) {
dbp("freetype: loading unicode CMap for file '%s' failed: %s", dbp("freetype: loading unicode CMap for file '%s' failed: %s",
fontFile.raw.c_str(), ft_error_string(fterr)); fontFile.raw.c_str(), ft_error_string(fterr));
@ -214,12 +214,6 @@ bool TtfFont::ExtractTTFData(bool nameOnly) {
name = std::string(fontFace->family_name) + name = std::string(fontFace->family_name) +
" (" + std::string(fontFace->style_name) + ")"; " (" + std::string(fontFace->style_name) + ")";
if(nameOnly) {
FT_Done_Face(fontFace);
fontFace = NULL;
return true;
}
// We always ask Freetype to give us a unit size character. // We always ask Freetype to give us a unit size character.
// It uses fixed point; put the unit size somewhere in the middle of the dynamic // It uses fixed point; put the unit size somewhere in the middle of the dynamic
// range of its 26.6 fixed point type, and adjust the factors so that the unit // range of its 26.6 fixed point type, and adjust the factors so that the unit
@ -231,8 +225,8 @@ bool TtfFont::ExtractTTFData(bool nameOnly) {
sizeRequest.horiResolution = 128; sizeRequest.horiResolution = 128;
sizeRequest.vertResolution = 128; sizeRequest.vertResolution = 128;
if(int fterr = FT_Request_Size(fontFace, &sizeRequest)) { if(int fterr = FT_Request_Size(fontFace, &sizeRequest)) {
dbp("freetype: cannot set character size: %s", dbp("freetype: size request for file '%s' failed: %s",
ft_error_string(fterr)); fontFile.raw.c_str(), ft_error_string(fterr));
FT_Done_Face(fontFace); FT_Done_Face(fontFace);
fontFace = NULL; fontFace = NULL;
return false; return false;
@ -241,16 +235,17 @@ bool TtfFont::ExtractTTFData(bool nameOnly) {
char chr = 'A'; char chr = 'A';
uint32_t gid = FT_Get_Char_Index(fontFace, 'A'); uint32_t gid = FT_Get_Char_Index(fontFace, 'A');
if (gid == 0) { if (gid == 0) {
dbp("freetype: CID-to-GID mapping for CID 0x%04x failed: %s; using CID as GID", dbp("freetype: CID-to-GID mapping for CID 0x%04x in file '%s' failed: %s; "
chr, ft_error_string(gid)); "using CID as GID",
chr, fontFile.raw.c_str(), ft_error_string(gid));
dbp("Assuming cap height is the same as requested height (this is likely wrong)."); dbp("Assuming cap height is the same as requested height (this is likely wrong).");
capHeight = (double)sizeRequest.height; capHeight = (double)sizeRequest.height;
} }
if(gid) { if(gid) {
if(int fterr = FT_Load_Glyph(fontFace, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) { if(int fterr = FT_Load_Glyph(fontFace, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) {
dbp("freetype: cannot load glyph for GID 0x%04x: %s", dbp("freetype: cannot load glyph for GID 0x%04x in file '%s': %s",
gid, ft_error_string(fterr)); gid, fontFile.raw.c_str(), ft_error_string(fterr));
FT_Done_Face(fontFace); FT_Done_Face(fontFace);
fontFace = NULL; fontFace = NULL;
return false; return false;
@ -261,6 +256,15 @@ bool TtfFont::ExtractTTFData(bool nameOnly) {
capHeight = (double)bbox.yMax; capHeight = (double)bbox.yMax;
} }
// If we just wanted to get the font's name and figure out if it's actually usable, close
// it now. If we don't do this, and there are a lot of fonts, we can bump into the file
// descriptor limit (especially on Windows), breaking all file operations.
if(!keepOpen) {
FT_Done_Face(fontFace);
fontFace = NULL;
return true;
}
return true; return true;
} }
@ -343,8 +347,9 @@ void TtfFont::PlotString(const std::string &str,
for(char32_t cid : ReadUTF8(str)) { for(char32_t cid : ReadUTF8(str)) {
uint32_t gid = FT_Get_Char_Index(fontFace, cid); uint32_t gid = FT_Get_Char_Index(fontFace, cid);
if (gid == 0) { if (gid == 0) {
dbp("freetype: CID-to-GID mapping for CID 0x%04x failed: %s; using CID as GID", dbp("freetype: CID-to-GID mapping for CID 0x%04x in file '%s' failed: %s; "
cid, ft_error_string(gid)); "using CID as GID",
cid, fontFile.raw.c_str(), ft_error_string(gid));
gid = cid; gid = cid;
} }
@ -357,8 +362,8 @@ void TtfFont::PlotString(const std::string &str,
* ones, antialiasing mitigates this considerably though. * ones, antialiasing mitigates this considerably though.
*/ */
if(int fterr = FT_Load_Glyph(fontFace, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) { if(int fterr = FT_Load_Glyph(fontFace, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) {
dbp("freetype: cannot load glyph for GID 0x%04x: %s", dbp("freetype: cannot load glyph for GID 0x%04x in file '%s': %s",
gid, ft_error_string(fterr)); gid, fontFile.raw.c_str(), ft_error_string(fterr));
return; return;
} }
@ -390,8 +395,8 @@ void TtfFont::PlotString(const std::string &str,
data.factor = (float)(1.0 / capHeight); data.factor = (float)(1.0 / capHeight);
data.bx = bx; data.bx = bx;
if(int fterr = FT_Outline_Decompose(&fontFace->glyph->outline, &outlineFuncs, &data)) { if(int fterr = FT_Outline_Decompose(&fontFace->glyph->outline, &outlineFuncs, &data)) {
dbp("freetype: bezier decomposition failed (gid %d): %s", dbp("freetype: bezier decomposition failed for GID 0x%4x in file '%s': %s",
gid, ft_error_string(fterr)); gid, fontFile.raw.c_str(), ft_error_string(fterr));
} }
// And we're done, so advance our position by the requested advance // And we're done, so advance our position by the requested advance
@ -408,13 +413,14 @@ double TtfFont::AspectRatio(const std::string &str) {
for(char32_t chr : ReadUTF8(str)) { for(char32_t chr : ReadUTF8(str)) {
uint32_t gid = FT_Get_Char_Index(fontFace, chr); uint32_t gid = FT_Get_Char_Index(fontFace, chr);
if (gid == 0) { if (gid == 0) {
dbp("freetype: CID-to-GID mapping for CID 0x%04x failed: %s; using CID as GID", dbp("freetype: CID-to-GID mapping for CID 0x%04x in file '%s' failed: %s; "
chr, ft_error_string(gid)); "using CID as GID",
chr, fontFile.raw.c_str(), ft_error_string(gid));
} }
if(int fterr = FT_Load_Glyph(fontFace, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) { if(int fterr = FT_Load_Glyph(fontFace, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) {
dbp("freetype: cannot load glyph (GID 0x%04x): %s", dbp("freetype: cannot load glyph for GID 0x%04x in file '%s': %s",
gid, ft_error_string(fterr)); gid, fontFile.raw.c_str(), ft_error_string(fterr));
break; break;
} }

View File

@ -20,14 +20,14 @@ public:
bool IsResource() const; bool IsResource() const;
std::string FontFileBaseName() const; std::string FontFileBaseName() const;
bool LoadFromFile(FT_LibraryRec_ *fontLibrary, bool nameOnly = true); bool LoadFromFile(FT_LibraryRec_ *fontLibrary, bool keepOpen = false);
bool LoadFromResource(FT_LibraryRec_ *fontLibrary, bool nameOnly = true); bool LoadFromResource(FT_LibraryRec_ *fontLibrary, bool keepOpen = false);
void PlotString(const std::string &str, void PlotString(const std::string &str,
SBezierList *sbl, Vector origin, Vector u, Vector v); SBezierList *sbl, Vector origin, Vector u, Vector v);
double AspectRatio(const std::string &str); double AspectRatio(const std::string &str);
bool ExtractTTFData(bool nameOnly); bool ExtractTTFData(bool keepOpen);
}; };
class TtfFontList { class TtfFontList {

View File

@ -253,6 +253,7 @@ public:
void ClearScreen(); void ClearScreen();
void Show(); void Show();
void Resize();
// State for the screen that we are showing in the text window. // State for the screen that we are showing in the text window.
enum class Screen : uint32_t { enum class Screen : uint32_t {
@ -429,6 +430,7 @@ public:
static void ScreenChangeShowContourAreas(int link, uint32_t v); static void ScreenChangeShowContourAreas(int link, uint32_t v);
static void ScreenChangeCheckClosedContour(int link, uint32_t v); static void ScreenChangeCheckClosedContour(int link, uint32_t v);
static void ScreenChangeTurntableNav(int link, uint32_t v); static void ScreenChangeTurntableNav(int link, uint32_t v);
static void ScreenChangeImmediatelyEditDimension(int link, uint32_t v);
static void ScreenChangeAutomaticLineConstraints(int link, uint32_t v); static void ScreenChangeAutomaticLineConstraints(int link, uint32_t v);
static void ScreenChangePwlCurves(int link, uint32_t v); static void ScreenChangePwlCurves(int link, uint32_t v);
static void ScreenChangeCanvasSizeAuto(int link, uint32_t v); static void ScreenChangeCanvasSizeAuto(int link, uint32_t v);
@ -689,6 +691,7 @@ public:
void RemoveConstraintsForPointBeingDeleted(hEntity hpt); void RemoveConstraintsForPointBeingDeleted(hEntity hpt);
void FixConstraintsForRequestBeingDeleted(hRequest hr); void FixConstraintsForRequestBeingDeleted(hRequest hr);
void FixConstraintsForPointBeingDeleted(hEntity hpt); void FixConstraintsForPointBeingDeleted(hEntity hpt);
void EditConstraint(hConstraint constraint);
// A selected entity. // A selected entity.
class Selection { class Selection {

View File

@ -255,9 +255,10 @@ bool Test::Helper::CheckRender(const char *file, int line, const char *reference
pixmapCanvas.SetCamera(camera); pixmapCanvas.SetCamera(camera);
pixmapCanvas.Init(); pixmapCanvas.Init();
pixmapCanvas.NewFrame(); pixmapCanvas.StartFrame();
SS.GW.Draw(&pixmapCanvas); SS.GW.Draw(&pixmapCanvas);
pixmapCanvas.FlushFrame(); pixmapCanvas.FlushFrame();
pixmapCanvas.FinishFrame();
std::shared_ptr<Pixmap> frame = pixmapCanvas.ReadFrame(); std::shared_ptr<Pixmap> frame = pixmapCanvas.ReadFrame();
pixmapCanvas.Clear(); pixmapCanvas.Clear();