Merge from master.

pull/493/head
KmolYuan 2019-11-27 14:09:27 +08:00
commit 5ebb37028b
41 changed files with 335 additions and 379 deletions

View File

@ -1,34 +1,33 @@
matrix:
git:
submodules: false
jobs:
include:
- os: linux
sudo: required
dist: trusty
- stage: test
name: "Debian"
os: linux
dist: bionic
language: c
install:
- ./.travis/install-debian.sh
script:
- ./.travis/build-debian.sh
- os: osx
install: ./.travis/install-debian.sh
script: ./.travis/build-debian.sh
- stage: deploy
name: "OSX"
os: osx
osx_image: xcode8.3
language: c
install:
- ./.travis/install-macos.sh
script:
# the awk command is a workaround for https://github.com/travis-ci/travis-ci/issues/4704.
- ./.travis/build-macos.sh | awk '/.{0,32}/ {print $0}'
install: ./.travis/install-macos.sh
# the awk command is a workaround for https://github.com/travis-ci/travis-ci/issues/4704.
script: ./.travis/build-macos.sh | awk '/.{0,32}/ {print $0}'
deploy:
- provider: releases
api_key:
secure: dDlkIawHcODlW9B/20/cQCtzeoocvs0hKuNngRKXKqzXLWTRq33oq/B7+39tAixWbmv6exTpijiKrRNFiSCW5Z4iwHLwaRD4XJznxw63e/Hus/dxg2Tvqx7XFpkCz8mT1Z+gZQE5YxAngeZPpI/sZbZtF1UO3yH5eLeeokZ15p26ZskQUPoYuzrTgTzYL3XfpG3F+20rNBawH1ycsCTVD/08/n31d2m3CrKAsbW7er92ek6w4fzKr7NW8WeXjrPJETVpw5fQg1Od3pRGW8dPQaJcvKQEogMp8Mm0ETYd0qigg89/giBz7QwOgmAWQ4dH+DfZH4Ojl//127QztBolMvyDMQBykWrtJoGcij05sT6K2IJr2FHeUBO12MAEdjiVvhQj3DtTzjPiZAHHDBSLWxLKWWhlhHE4pq7g1MQhqXkaAHI2BLNzwLmaowbMT0bECf9yfz6xx18h6XPQFX44oOktraobVALFlyHqeKa8zdcUt22LF6uAL1m5dxL0tny3eXCIPE4UH/RZgua/cHV9G3cUvKQa/QnFSLRhvWVSbGB+7YsHouBJcsUOOW1gmd5442XuC7mpppccRldh+GSxUk6TBJRAx7TeQ0ybDUaoco9MUqp2twv3KreR2+8Q12PDaAhfQVNEGdF3wTm1sShImjCN4VN3eSLlBEbve1QRQXM=
skip_cleanup: true
file: build/SolveSpace.dmg
on:
repo: solvespace/solvespace
tags: true
provider: releases
api_key:
secure: dDlkIawHcODlW9B/20/cQCtzeoocvs0hKuNngRKXKqzXLWTRq33oq/B7+39tAixWbmv6exTpijiKrRNFiSCW5Z4iwHLwaRD4XJznxw63e/Hus/dxg2Tvqx7XFpkCz8mT1Z+gZQE5YxAngeZPpI/sZbZtF1UO3yH5eLeeokZ15p26ZskQUPoYuzrTgTzYL3XfpG3F+20rNBawH1ycsCTVD/08/n31d2m3CrKAsbW7er92ek6w4fzKr7NW8WeXjrPJETVpw5fQg1Od3pRGW8dPQaJcvKQEogMp8Mm0ETYd0qigg89/giBz7QwOgmAWQ4dH+DfZH4Ojl//127QztBolMvyDMQBykWrtJoGcij05sT6K2IJr2FHeUBO12MAEdjiVvhQj3DtTzjPiZAHHDBSLWxLKWWhlhHE4pq7g1MQhqXkaAHI2BLNzwLmaowbMT0bECf9yfz6xx18h6XPQFX44oOktraobVALFlyHqeKa8zdcUt22LF6uAL1m5dxL0tny3eXCIPE4UH/RZgua/cHV9G3cUvKQa/QnFSLRhvWVSbGB+7YsHouBJcsUOOW1gmd5442XuC7mpppccRldh+GSxUk6TBJRAx7TeQ0ybDUaoco9MUqp2twv3KreR2+8Q12PDaAhfQVNEGdF3wTm1sShImjCN4VN3eSLlBEbve1QRQXM=
skip_cleanup: true
file: build/SolveSpace.dmg
on:
repo: solvespace/solvespace
tags: true
- &linux
stage: deploy
os: linux
sudo: required
dist: bionic
@ -54,14 +53,12 @@ matrix:
on:
repo: KmolYuan/solvespace
tags: true
- <<: *linux
python: "3.7"
- <<: *linux
python: "3.8"
- &osx
stage: deploy
os: osx
osx_image: xcode10
language: generic
@ -85,16 +82,12 @@ matrix:
python3 setup.py bdist_wheel;
python3 -m twine upload dist/*.whl --skip-existing;
fi
- <<: *osx
env: PYTHON=3.7.0
- <<: *osx
env: PYTHON=3.8.0
before_cache:
- rm -rf $HOME/.cache/pip/log
cache:
directories:
- $HOME/.cache/pip

View File

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

View File

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

View File

@ -42,6 +42,8 @@ New constraint features:
constraints on line segments.
* Automatic creation of constraints no longer happens if the constraint
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:
* 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()
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}
"${CMAKE_SOURCE_DIR}/cmake/")
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(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)
message(FATAL_ERROR "C and C++ compilers should be supplied by the same vendor")
@ -63,6 +66,15 @@ endif()
# 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)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")

View File

@ -128,7 +128,6 @@ elseif(APPLE)
-fobjc-arc)
list(APPEND platform_SOURCES
render/rendergl.cpp
platform/guimac.mm)
else()
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) {
SS.showContourAreas = !SS.showContourAreas;
SS.GW.Invalidate();
@ -342,6 +347,9 @@ void TextWindow::ShowConfiguration() {
SS.automaticLineConstraints ? CHECK_TRUE : CHECK_FALSE);
Printf(false, " %Fd%f%Ll%s use turntable mouse navigation%E", &ScreenChangeTurntableNav,
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, "%Ft autosave interval (in minutes)%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.ModifyToSatisfy();
AddConstraint(&c);
if (SS.immediatelyEditDimension) {
SS.GW.EditConstraint(c.h);
}
break;
}
@ -607,6 +610,9 @@ void Constraint::MenuConstrain(Command id) {
c.ModifyToSatisfy();
AddConstraint(&c);
if (SS.immediatelyEditDimension) {
SS.GW.EditConstraint(c.h);
}
break;
}

View File

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

View File

@ -480,11 +480,6 @@ public:
const T *cbegin() const { return begin(); }
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() {
for(auto &elt : *this) { elt.tag = 0; }
}

View File

@ -50,7 +50,8 @@ bool Group::IsVisible() {
}
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() {

View File

@ -1068,8 +1068,9 @@ public:
}
};
static void ImportDwgDxf(const Platform::Path &filename,
std::function<bool(const std::string &data, DRW_Interface *intf)> read) {
static void
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 data;

View File

@ -13,7 +13,7 @@ static System SYS;
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());
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) {
if(window->IsEditorVisible()) return;
SS.TW.HideEditControl();
if(hover.constraint.v) {
constraintBeingEdited = 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);
EditConstraint(hover.constraint);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -152,13 +152,12 @@ static int Clamp(int x, int a, int b) {
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
// and potentially crashing even harder.
handlingFatalError = true;
message += "\nGenerate debug report?";
switch(MessageBoxW(NULL, Platform::Widen(message).c_str(),
switch(MessageBoxW(NULL, Platform::Widen(message + "\nGenerate debug report?").c_str(),
L"Fatal error — SolveSpace",
MB_ICONERROR|MB_TASKMODAL|MB_SETFOREGROUND|MB_TOPMOST|
MB_OKCANCEL|MB_DEFBUTTON2)) {
@ -1355,7 +1354,11 @@ public:
SCROLLINFO si = {};
si.cbSize = sizeof(si);
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));
// 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.
// If pi is not NULL, then a crossing is returned in that.
//-----------------------------------------------------------------------------
int SEdgeList::AnyEdgeCrossings(Vector a, Vector b,
Vector *ppi, SPointList *spl) const
{
int cnt = 0;
for(const SEdge *se = l.First(); se; se = l.NextAfter(se)) {
if(se->EdgeCrosses(a, b, ppi, spl)) {
cnt++;
}
}
return cnt;
int SEdgeList::AnyEdgeCrossings(Vector a, Vector b, Vector *ppi, SPointList *spl) const {
auto cnt = std::count_if(l.begin(), l.end(),
[&](SEdge const &se) { return se.EdgeCrosses(a, b, ppi, spl); });
return static_cast<int>(cnt);
}
//-----------------------------------------------------------------------------
@ -706,15 +700,10 @@ bool SPolygon::ContainsPoint(Vector p) const {
return (WindingNumberForPoint(p) % 2) == 1;
}
int SPolygon::WindingNumberForPoint(Vector p) const {
int winding = 0;
int i;
for(i = 0; i < l.n; i++) {
const SContour *sc = &(l[i]);
if(sc->ContainsPointProjdToNormal(normal, p)) {
winding++;
}
}
size_t SPolygon::WindingNumberForPoint(Vector p) const {
auto winding = std::count_if(l.begin(), l.end(), [&](const SContour &sc) {
return sc.ContainsPointProjdToNormal(normal, p);
});
return winding;
}

View File

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

View File

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

View File

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

View File

@ -74,7 +74,7 @@ public:
const std::vector<std::pair<GLuint, std::string>> &locations = {});
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 Vector4f &v);
void SetUniformColor(const char *name, RgbaColor c);
@ -163,8 +163,8 @@ public:
void Draw(const Handle &handle);
void Draw(const SEdgeList &edges);
void SetModelview(double *matrix);
void SetProjection(double *matrix);
void SetModelview(const double *matrix);
void SetProjection(const double *matrix);
void SetStroke(const Canvas::Stroke &stroke, double pixel);
};
@ -203,8 +203,8 @@ public:
void Draw(const Handle &handle, Canvas::DrawOutlinesAs mode);
void Draw(const SOutlineList &outlines, Canvas::DrawOutlinesAs mode);
void SetModelview(double *matrix);
void SetProjection(double *matrix);
void SetModelview(const double *matrix);
void SetProjection(const double *matrix);
void SetStroke(const Canvas::Stroke &stroke, double pixel);
};
@ -252,8 +252,8 @@ public:
void Draw(const Handle &handle);
void Draw(const SIndexedMesh &mesh);
void SetModelview(double *matrix);
void SetProjection(double *matrix);
void SetModelview(const double *matrix);
void SetProjection(const double *matrix);
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);
}
bool ObjectPicker::Pick(std::function<void()> drawFn) {
bool ObjectPicker::Pick(const std::function<void()> &drawFn) {
minDistance = VERY_POSITIVE;
maxZIndex = INT_MIN;
drawFn();
return minDistance < selRadius;
}
}

View File

@ -186,8 +186,9 @@ public:
virtual void SetCamera(const Camera &camera) = 0;
virtual void SetLighting(const Lighting &lighting) = 0;
virtual void NewFrame() = 0;
virtual void StartFrame() = 0;
virtual void FlushFrame() = 0;
virtual void FinishFrame() = 0;
virtual std::shared_ptr<Pixmap> ReadFrame() = 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,
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,
@ -342,8 +343,9 @@ public:
void Clear() override;
void NewFrame() override {}
void StartFrame() override {}
void FlushFrame() override;
void FinishFrame() override {}
std::shared_ptr<Pixmap> ReadFrame() override;
void GetIdent(const char **vendor, const char **renderer, const char **version) override;
@ -373,22 +375,6 @@ public:
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
//-----------------------------------------------------------------------------

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 SetLighting(const Lighting &lighting) override;
void NewFrame() override;
void StartFrame() override;
void FlushFrame() override;
void FinishFrame() override;
std::shared_ptr<Pixmap> ReadFrame() 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);
}
void OpenGl1Renderer::NewFrame() {
void OpenGl1Renderer::StartFrame() {
glEnable(GL_NORMALIZE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -806,7 +807,12 @@ void OpenGl1Renderer::NewFrame() {
void OpenGl1Renderer::FlushFrame() {
UnSelectPrimitive();
glFlush();
}
void OpenGl1Renderer::FinishFrame() {
glFinish();
GLenum error = glGetError();
if(error != GL_NO_ERROR) {

View File

@ -87,6 +87,9 @@ public:
Fill *fill;
std::weak_ptr<const Pixmap> texture;
} current;
const char *vendor = "<uninitialized>";
const char *renderer = "<uninitialized>";
const char *version = "<uninitialized>";
// List-initialize current to work around MSVC bug 746973.
OpenGl3Renderer() :
@ -134,8 +137,9 @@ public:
void SetCamera(const Camera &c) override;
void SetLighting(const Lighting &l) override;
void NewFrame() override;
void StartFrame() override;
void FlushFrame() override;
void FinishFrame() override;
void Clear() override;
std::shared_ptr<Pixmap> ReadFrame() override;
@ -439,6 +443,10 @@ void OpenGl3Renderer::Init() {
meshRenderer.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__)
GLuint array;
glGenVertexArrays(1, &array);
@ -618,7 +626,7 @@ void OpenGl3Renderer::UpdateProjection() {
glClear(GL_DEPTH_BUFFER_BIT);
}
void OpenGl3Renderer::NewFrame() {
void OpenGl3Renderer::StartFrame() {
if(!initialized) {
Init();
initialized = true;
@ -667,6 +675,10 @@ void OpenGl3Renderer::FlushFrame() {
}
points.Clear();
glFlush();
}
void OpenGl3Renderer::FinishFrame() {
glFinish();
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) {
*vendor = (const char *)glGetString(GL_VENDOR);
*renderer = (const char *)glGetString(GL_RENDERER);
*version = (const char *)glGetString(GL_VERSION);
*vendor = this->vendor;
*renderer = this->renderer;
*version = this->version;
}
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,
std::function<void(Vector, Vector)> traceEdge) {
const std::function<void(Vector, Vector)> &traceEdge) {
ssassert(!IsEmpty(), "Expected a loaded font");
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,
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");
// 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);
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,
std::function<void(Vector, Vector)> traceEdge, const Camera &camera);
const std::function<void(Vector, Vector)> &traceEdge, const Camera &camera);
};
#endif

View File

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

View File

@ -587,6 +587,7 @@ public:
bool showContourAreas;
bool checkClosedContour;
bool turntableNav;
bool immediatelyEditDimension;
bool automaticLineConstraints;
bool showToolbar;
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)
{
bool inShell = (shell == SShell::Class::INSIDE),
outSide = (shell == SShell::Class::OUTSIDE),
inSame = (shell == SShell::Class::COINC_SAME),
inOpp = (shell == SShell::Class::COINC_OPP),
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;
switch(type) {
case SSurface::CombineAs::UNION:
if(opA) {
return (!inShell && !inFace);
return outSide;
} else {
return (!inShell && !inFace) || inSame;
return outSide || inSame;
}
case SSurface::CombineAs::DIFFERENCE:
if(opA) {
return (!inShell && !inFace);
return outSide;
} else {
return (inShell && !inFace) || inSame;
return inShell || inSame;
}
default: ssassert(false, "Unexpected combine type");

View File

@ -563,21 +563,24 @@ void TextWindow::Show() {
}
}
if(window) {
double width, height;
window->GetContentSize(&width, &height);
if(window) Resize();
}
halfRows = (int)height / (LINE_HEIGHT/2);
void TextWindow::Resize()
{
double width, height;
window->GetContentSize(&width, &height);
int bottom = top[rows-1] + 2;
scrollPos = min(scrollPos, bottom - halfRows);
scrollPos = max(scrollPos, 0);
halfRows = (int)height / (LINE_HEIGHT/2);
window->ConfigureScrollbar(0, top[rows - 1] + 1, halfRows);
window->SetScrollbarPosition(scrollPos);
window->SetScrollbarVisible(top[rows - 1] + 1 > halfRows);
window->Invalidate();
}
int bottom = top[rows-1] + 2;
scrollPos = min(scrollPos, bottom - halfRows);
scrollPos = max(scrollPos, 0);
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,
@ -909,6 +912,8 @@ void TextWindow::Paint() {
double width, height;
window->GetContentSize(&width, &height);
if(halfRows != (int)height / (LINE_HEIGHT/2))
Resize();
Camera camera = {};
camera.width = width;
@ -924,7 +929,7 @@ void TextWindow::Paint() {
canvas->SetLighting(lighting);
canvas->SetCamera(camera);
canvas->NewFrame();
canvas->StartFrame();
UiCanvas uiCanvas = {};
uiCanvas.canvas = canvas;
@ -1041,6 +1046,7 @@ void TextWindow::Paint() {
DrawOrHitTestColorPicker(&uiCanvas, PAINT, false, 0, 0);
canvas->FlushFrame();
canvas->FinishFrame();
canvas->Clear();
}
@ -1123,8 +1129,10 @@ void TextWindow::MouseLeave() {
}
void TextWindow::ScrollbarEvent(double newPos) {
if(window->IsEditorVisible())
if(window->IsEditorVisible()) {
window->SetScrollbarPosition(scrollPos);
return;
}
int bottom = top[rows-1] + 2;
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->fontFace == NULL) {
if(tf->IsResource())
tf->LoadFromResource(fontLibrary, /*nameOnly=*/false);
tf->LoadFromResource(fontLibrary, /*keepOpen=*/true);
else
tf->LoadFromFile(fontLibrary, /*nameOnly=*/false);
tf->LoadFromFile(fontLibrary, /*keepOpen=*/true);
}
return tf;
} else {
@ -155,7 +155,7 @@ bool TtfFont::IsResource() const {
//-----------------------------------------------------------------------------
// 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.");
FT_Open_Args args = {};
@ -171,14 +171,14 @@ bool TtfFont::LoadFromFile(FT_Library fontLibrary, bool nameOnly) {
return false;
}
return ExtractTTFData(nameOnly);
return ExtractTTFData(keepOpen);
}
//-----------------------------------------------------------------------------
// Load a TrueType from resource in memory. Implemented to load bundled fonts
// 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 "
"or does not have the 'res://' prefix.");
@ -196,13 +196,13 @@ bool TtfFont::LoadFromResource(FT_Library fontLibrary, bool nameOnly) {
return false;
}
return ExtractTTFData(nameOnly);
return ExtractTTFData(keepOpen);
}
//-----------------------------------------------------------------------------
// 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)) {
dbp("freetype: loading unicode CMap for file '%s' failed: %s",
fontFile.raw.c_str(), ft_error_string(fterr));
@ -214,12 +214,6 @@ bool TtfFont::ExtractTTFData(bool nameOnly) {
name = std::string(fontFace->family_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.
// 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
@ -231,8 +225,8 @@ bool TtfFont::ExtractTTFData(bool nameOnly) {
sizeRequest.horiResolution = 128;
sizeRequest.vertResolution = 128;
if(int fterr = FT_Request_Size(fontFace, &sizeRequest)) {
dbp("freetype: cannot set character size: %s",
ft_error_string(fterr));
dbp("freetype: size request for file '%s' failed: %s",
fontFile.raw.c_str(), ft_error_string(fterr));
FT_Done_Face(fontFace);
fontFace = NULL;
return false;
@ -241,16 +235,17 @@ bool TtfFont::ExtractTTFData(bool nameOnly) {
char chr = 'A';
uint32_t gid = FT_Get_Char_Index(fontFace, 'A');
if (gid == 0) {
dbp("freetype: CID-to-GID mapping for CID 0x%04x failed: %s; using CID as GID",
chr, ft_error_string(gid));
dbp("freetype: CID-to-GID mapping for CID 0x%04x in file '%s' failed: %s; "
"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).");
capHeight = (double)sizeRequest.height;
}
if(gid) {
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",
gid, ft_error_string(fterr));
dbp("freetype: cannot load glyph for GID 0x%04x in file '%s': %s",
gid, fontFile.raw.c_str(), ft_error_string(fterr));
FT_Done_Face(fontFace);
fontFace = NULL;
return false;
@ -261,6 +256,15 @@ bool TtfFont::ExtractTTFData(bool nameOnly) {
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;
}
@ -343,8 +347,9 @@ void TtfFont::PlotString(const std::string &str,
for(char32_t cid : ReadUTF8(str)) {
uint32_t gid = FT_Get_Char_Index(fontFace, cid);
if (gid == 0) {
dbp("freetype: CID-to-GID mapping for CID 0x%04x failed: %s; using CID as GID",
cid, ft_error_string(gid));
dbp("freetype: CID-to-GID mapping for CID 0x%04x in file '%s' failed: %s; "
"using CID as GID",
cid, fontFile.raw.c_str(), ft_error_string(gid));
gid = cid;
}
@ -357,8 +362,8 @@ void TtfFont::PlotString(const std::string &str,
* ones, antialiasing mitigates this considerably though.
*/
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",
gid, ft_error_string(fterr));
dbp("freetype: cannot load glyph for GID 0x%04x in file '%s': %s",
gid, fontFile.raw.c_str(), ft_error_string(fterr));
return;
}
@ -390,8 +395,8 @@ void TtfFont::PlotString(const std::string &str,
data.factor = (float)(1.0 / capHeight);
data.bx = bx;
if(int fterr = FT_Outline_Decompose(&fontFace->glyph->outline, &outlineFuncs, &data)) {
dbp("freetype: bezier decomposition failed (gid %d): %s",
gid, ft_error_string(fterr));
dbp("freetype: bezier decomposition failed for GID 0x%4x in file '%s': %s",
gid, fontFile.raw.c_str(), ft_error_string(fterr));
}
// 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)) {
uint32_t gid = FT_Get_Char_Index(fontFace, chr);
if (gid == 0) {
dbp("freetype: CID-to-GID mapping for CID 0x%04x failed: %s; using CID as GID",
chr, ft_error_string(gid));
dbp("freetype: CID-to-GID mapping for CID 0x%04x in file '%s' failed: %s; "
"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)) {
dbp("freetype: cannot load glyph (GID 0x%04x): %s",
gid, ft_error_string(fterr));
dbp("freetype: cannot load glyph for GID 0x%04x in file '%s': %s",
gid, fontFile.raw.c_str(), ft_error_string(fterr));
break;
}

View File

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

View File

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

View File

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