From 5f347bc999a7e34764725584645a6eb092eac2b2 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Wed, 15 Dec 2010 17:20:26 +0000 Subject: [PATCH] Updated the path flattening code to correctly observe tolerance values, and made the tolerances adapt to the scaling being used when drawing to a transformed graphics context. Fixed a small issue with focus listeners, and a mac menu dismissal problem. --- .../wrapper/juce_ActiveX_GlueCode.cpp | 2 +- .../wrapper/juce_NPAPI_GlueCode.cpp | 2 +- juce_amalgamated.cpp | 273 +++++++++++------- juce_amalgamated.h | 95 +++--- src/core/juce_MathsFunctions.h | 22 +- src/core/juce_StandardHeader.h | 2 +- src/gui/components/juce_Desktop.cpp | 4 +- src/gui/components/menus/juce_PopupMenu.cpp | 7 +- src/gui/components/menus/juce_PopupMenu.h | 2 +- src/gui/graphics/contexts/juce_Graphics.cpp | 2 +- .../contexts/juce_LowLevelGraphicsContext.h | 1 + ...uce_LowLevelGraphicsPostScriptRenderer.cpp | 6 + .../juce_LowLevelGraphicsPostScriptRenderer.h | 1 + .../juce_LowLevelGraphicsSoftwareRenderer.cpp | 10 + .../juce_LowLevelGraphicsSoftwareRenderer.h | 1 + src/gui/graphics/drawables/juce_SVGParser.cpp | 2 +- .../geometry/juce_AffineTransform.cpp | 5 + .../graphics/geometry/juce_AffineTransform.h | 6 + src/gui/graphics/geometry/juce_Line.h | 3 +- src/gui/graphics/geometry/juce_Path.cpp | 29 +- src/gui/graphics/geometry/juce_Path.h | 24 +- .../graphics/geometry/juce_PathIterator.cpp | 34 +-- src/gui/graphics/geometry/juce_PathIterator.h | 15 +- .../graphics/geometry/juce_PathStrokeType.cpp | 18 +- .../graphics/geometry/juce_PathStrokeType.h | 15 +- src/gui/graphics/geometry/juce_Point.h | 4 +- .../mac/juce_mac_CoreGraphicsContext.mm | 6 + src/native/mac/juce_mac_MainMenu.mm | 146 +++++----- .../juce_win32_Direct2DGraphicsContext.cpp | 6 + 29 files changed, 432 insertions(+), 311 deletions(-) diff --git a/extras/browser plugins/wrapper/juce_ActiveX_GlueCode.cpp b/extras/browser plugins/wrapper/juce_ActiveX_GlueCode.cpp index 49b0ad3382..0eb169f118 100644 --- a/extras/browser plugins/wrapper/juce_ActiveX_GlueCode.cpp +++ b/extras/browser plugins/wrapper/juce_ActiveX_GlueCode.cpp @@ -40,9 +40,9 @@ #include #pragma warning (disable:4584) +#include "juce_IncludeBrowserPluginInfo.h" #include "../../../juce_amalgamated.h" #include "juce_BrowserPluginComponent.h" -#include "juce_IncludeBrowserPluginInfo.h" #ifndef JuceBrowserPlugin_ActiveXCLSID #error "For an activeX plugin, you need to define JuceBrowserPlugin_ActiveXCLSID in your BrowserPluginCharacteristics.h file!" diff --git a/extras/browser plugins/wrapper/juce_NPAPI_GlueCode.cpp b/extras/browser plugins/wrapper/juce_NPAPI_GlueCode.cpp index c21b7fcfe2..c7e920d556 100644 --- a/extras/browser plugins/wrapper/juce_NPAPI_GlueCode.cpp +++ b/extras/browser plugins/wrapper/juce_NPAPI_GlueCode.cpp @@ -66,9 +66,9 @@ #endif //============================================================================== +#include "juce_IncludeBrowserPluginInfo.h" #include "../../../juce_amalgamated.h" #include "juce_BrowserPluginComponent.h" -#include "juce_IncludeBrowserPluginInfo.h" #if JUCE_MAC && JUCE_DEBUG && 0 #include diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 7c5fd1df68..ff64544a67 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -42956,7 +42956,9 @@ void Desktop::triggerFocusCallback() void Desktop::handleAsyncUpdate() { - Component* currentFocus = Component::getCurrentlyFocusedComponent(); + // The component may be deleted during this operation, but we'll use a SafePointer rather than a + // BailOutChecker so that any remaining listeners will still get a callback (with a null pointer). + Component::SafePointer currentFocus (Component::getCurrentlyFocusedComponent()); focusListeners.call (&FocusChangeListener::globalFocusChanged, currentFocus); } @@ -70204,15 +70206,18 @@ int PopupMenu::showAt (Component* componentToAttachTo, } } -void JUCE_CALLTYPE PopupMenu::dismissAllActiveMenus() +bool JUCE_CALLTYPE PopupMenu::dismissAllActiveMenus() { - for (int i = Window::getActiveWindows().size(); --i >= 0;) + const int numWindows = Window::getActiveWindows().size(); + for (int i = numWindows; --i >= 0;) { Window* const pmw = Window::getActiveWindows()[i]; if (pmw != 0) pmw->dismissMenu (0); } + + return numWindows > 0; } int PopupMenu::getNumItems() const throw() @@ -82175,7 +82180,7 @@ void Graphics::strokePath (const Path& path, const AffineTransform& transform) const { Path stroke; - strokeType.createStrokedPath (stroke, path, transform); + strokeType.createStrokedPath (stroke, path, transform, context->getScaleFactor()); fillPath (stroke); } @@ -82660,6 +82665,12 @@ void LowLevelGraphicsPostScriptRenderer::addTransform (const AffineTransform& /* jassertfalse; } +float LowLevelGraphicsPostScriptRenderer::getScaleFactor() +{ + jassertfalse; //xxx + return 1.0f; +} + bool LowLevelGraphicsPostScriptRenderer::clipToRectangle (const Rectangle& r) { needToClip = true; @@ -84873,6 +84884,11 @@ public: } } + float getScaleFactor() const + { + return isOnlyTranslated ? 1.0f : complexTransform.getScaleFactor(); + } + bool clipToRectangle (const Rectangle& r) { if (clip != 0) @@ -85278,6 +85294,11 @@ void LowLevelGraphicsSoftwareRenderer::addTransform (const AffineTransform& tran currentState->addTransform (transform); } +float LowLevelGraphicsSoftwareRenderer::getScaleFactor() +{ + return currentState->getScaleFactor(); +} + bool LowLevelGraphicsSoftwareRenderer::clipToRectangle (const Rectangle& r) { return currentState->clipToRectangle (r); @@ -88682,7 +88703,7 @@ private: float x = getCoordLength (strokeWidth, viewBoxW), y = 0.0f; transform.transformPoints (ox, oy, x, y); - return PathStrokeType (strokeWidth.isNotEmpty() ? juce_hypotf (x - ox, y - oy) : 1.0f, + return PathStrokeType (strokeWidth.isNotEmpty() ? juce_hypot (x - ox, y - oy) : 1.0f, joinStyle, capStyle); } @@ -91443,6 +91464,11 @@ bool AffineTransform::isOnlyTranslation() const throw() && (mat11 == 1.0f); } +float AffineTransform::getScaleFactor() const throw() +{ + return juce_hypot (mat00 + mat01, mat10 + mat11); +} + END_JUCE_NAMESPACE /*** End of inlined file: juce_AffineTransform.cpp ***/ @@ -91581,6 +91607,11 @@ namespace PathHelpers return String (start, (int) (t - start)); } + + inline double lengthOf (float x1, float y1, float x2, float y2) throw() + { + return juce_hypot ((double) (x1 - x2), (double) (y1 - y2)); + } } const float Path::lineMarker = 100001.0f; @@ -92481,13 +92512,13 @@ const AffineTransform Path::getTransformToScaleToFit (const float x, const float } } -bool Path::contains (const float x, const float y, const float tolerence) const +bool Path::contains (const float x, const float y, const float tolerance) const { if (x <= pathXMin || x >= pathXMax || y <= pathYMin || y >= pathYMax) return false; - PathFlatteningIterator i (*this, AffineTransform::identity, tolerence); + PathFlatteningIterator i (*this, AffineTransform::identity, tolerance); int positiveCrossings = 0; int negativeCrossings = 0; @@ -92512,14 +92543,14 @@ bool Path::contains (const float x, const float y, const float tolerence) const : ((negativeCrossings + positiveCrossings) & 1) != 0; } -bool Path::contains (const Point& point, const float tolerence) const +bool Path::contains (const Point& point, const float tolerance) const { - return contains (point.getX(), point.getY(), tolerence); + return contains (point.getX(), point.getY(), tolerance); } -bool Path::intersectsLine (const Line& line, const float tolerence) +bool Path::intersectsLine (const Line& line, const float tolerance) { - PathFlatteningIterator i (*this, AffineTransform::identity, tolerence); + PathFlatteningIterator i (*this, AffineTransform::identity, tolerance); Point intersection; while (i.next()) @@ -92672,8 +92703,7 @@ const Path Path::createPathWithRoundedCorners (const float cornerRadius) const if (lastWasLine) { - const double len1 = juce_hypot (startX - joinX, - startY - joinY); + const double len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY); if (len1 > 0) { @@ -92683,8 +92713,7 @@ const Path Path::createPathWithRoundedCorners (const float cornerRadius) const p.data.elements [p.numElements - 1] = (float) (joinY - (joinY - startY) * propNeeded); } - const double len2 = juce_hypot (endX - joinX, - endY - joinY); + const double len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY); if (len2 > 0) { @@ -92714,8 +92743,7 @@ const Path Path::createPathWithRoundedCorners (const float cornerRadius) const endX = data.elements [indexOfPathStartThis + 4]; endY = data.elements [indexOfPathStartThis + 5]; - const double len1 = juce_hypot (startX - joinX, - startY - joinY); + const double len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY); if (len1 > 0) { @@ -92725,8 +92753,7 @@ const Path Path::createPathWithRoundedCorners (const float cornerRadius) const p.data.elements [p.numElements - 1] = (float) (joinY - (joinY - startY) * propNeeded); } - const double len2 = juce_hypot (endX - joinX, - endY - joinY); + const double len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY); if (len2 > 0) { @@ -93095,9 +93122,11 @@ BEGIN_JUCE_NAMESPACE #pragma optimize ("t", on) #endif +const float PathFlatteningIterator::defaultTolerance = 0.6f; + PathFlatteningIterator::PathFlatteningIterator (const Path& path_, const AffineTransform& transform_, - float tolerence_) + const float tolerance) : x2 (0), y2 (0), closesSubPath (false), @@ -93105,7 +93134,7 @@ PathFlatteningIterator::PathFlatteningIterator (const Path& path_, path (path_), transform (transform_), points (path_.data.elements), - tolerence (tolerence_ * tolerence_), + toleranceSquared (tolerance * tolerance), subPathCloseX (0), subPathCloseY (0), isIdentityTransform (transform_.isIdentity()), @@ -93221,11 +93250,6 @@ bool PathFlatteningIterator::next() stackPos = stackBase + offset; } - const float dx1 = x1 - x2; - const float dy1 = y1 - y2; - const float dx2 = x2 - x3; - const float dy2 = y2 - y3; - const float m1x = (x1 + x2) * 0.5f; const float m1y = (y1 + y2) * 0.5f; const float m2x = (x2 + x3) * 0.5f; @@ -93233,7 +93257,10 @@ bool PathFlatteningIterator::next() const float m3x = (m1x + m2x) * 0.5f; const float m3y = (m1y + m2y) * 0.5f; - if (dx1*dx1 + dy1*dy1 + dx2*dx2 + dy2*dy2 > tolerence) + const float errorX = m3x - x2; + const float errorY = m3y - y2; + + if (errorX * errorX + errorY * errorY > toleranceSquared) { *stackPos++ = y3; *stackPos++ = x3; @@ -93271,13 +93298,6 @@ bool PathFlatteningIterator::next() stackPos = stackBase + offset; } - const float dx1 = x1 - x2; - const float dy1 = y1 - y2; - const float dx2 = x2 - x3; - const float dy2 = y2 - y3; - const float dx3 = x3 - x4; - const float dy3 = y3 - y4; - const float m1x = (x1 + x2) * 0.5f; const float m1y = (y1 + y2) * 0.5f; const float m2x = (x3 + x2) * 0.5f; @@ -93289,8 +93309,13 @@ bool PathFlatteningIterator::next() const float m5x = (m3x + m2x) * 0.5f; const float m5y = (m3y + m2y) * 0.5f; - if (dx1*dx1 + dy1*dy1 + dx2*dx2 - + dy2*dy2 + dx3*dx3 + dy3*dy3 > tolerence) + const float error1X = m4x - x2; + const float error1Y = m4y - y2; + const float error2X = m5x - x3; + const float error2Y = m5y - y3; + + if (error1X * error1X + error1Y * error1Y > toleranceSquared + || error2X * error2X + error2Y * error2Y > toleranceSquared) { *stackPos++ = y4; *stackPos++ = x4; @@ -93628,7 +93653,7 @@ namespace PathStrokeHelpers float dx = x2 - x1; float dy = y2 - y1; - const float len = juce_hypotf (dx, dy); + const float len = juce_hypot (dx, dy); if (len == 0) { @@ -93705,7 +93730,7 @@ namespace PathStrokeHelpers LineSection& l = subPath.getReference (subPath.size() - 1); float dx = l.rx2 - l.rx1; float dy = l.ry2 - l.ry1; - const float len = juce_hypotf (dx, dy); + const float len = juce_hypot (dx, dy); if (len <= amountAtEnd && subPath.size() > 1) { @@ -93733,7 +93758,7 @@ namespace PathStrokeHelpers LineSection& l = subPath.getReference (0); float dx = l.rx2 - l.rx1; float dy = l.ry2 - l.ry1; - const float len = juce_hypotf (dx, dy); + const float len = juce_hypot (dx, dy); if (len <= amountAtStart && subPath.size() > 1) { @@ -93876,6 +93901,8 @@ namespace PathStrokeHelpers const AffineTransform& transform, const float extraAccuracy, const Arrowhead* const arrowhead) { + jassert (extraAccuracy > 0); + if (thickness <= 0) { destPath.clear(); @@ -93902,7 +93929,7 @@ namespace PathStrokeHelpers // Iterate the path, creating a list of the // left/right-hand lines along either side of it... - PathFlatteningIterator it (*sourcePath, transform, 9.0f / extraAccuracy); + PathFlatteningIterator it (*sourcePath, transform, PathFlatteningIterator::defaultTolerance / extraAccuracy); Array subPath; subPath.ensureStorageAllocated (512); @@ -93910,7 +93937,7 @@ namespace PathStrokeHelpers l.x1 = 0; l.y1 = 0; - const float minSegmentLength = 2.0f / (extraAccuracy * extraAccuracy); + const float minSegmentLength = 0.0001f; while (it.next()) { @@ -93994,6 +94021,8 @@ void PathStrokeType::createDashedStroke (Path& destPath, const AffineTransform& transform, const float extraAccuracy) const { + jassert (extraAccuracy > 0); + if (thickness <= 0) return; @@ -94001,7 +94030,7 @@ void PathStrokeType::createDashedStroke (Path& destPath, jassert ((numDashLengths & 1) == 0); Path newDestPath; - PathFlatteningIterator it (sourcePath, transform, 9.0f / extraAccuracy); + PathFlatteningIterator it (sourcePath, transform, PathFlatteningIterator::defaultTolerance / extraAccuracy); bool first = true; int dashNum = 0; @@ -94037,7 +94066,7 @@ void PathStrokeType::createDashedStroke (Path& destPath, dx = it.x2 - it.x1; dy = it.y2 - it.y1; - lineLen = juce_hypotf (dx, dy); + lineLen = juce_hypot (dx, dy); lineEndPos += lineLen; first = it.closesSubPath; } @@ -240547,6 +240576,12 @@ public: jassertfalse; } + float getScaleFactor() + { + jassertfalse; //xxx + return 1.0f; + } + bool clipToRectangle (const Rectangle& r) { currentState->clipToRectangle (r); @@ -265647,6 +265682,12 @@ public: lastClipRectIsValid = false; } + float getScaleFactor() + { + CGAffineTransform t = CGContextGetCTM (context); + return (float) juce_hypot (t.a + t.c, t.b + t.d); + } + bool clipToRectangle (const Rectangle& r) { CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); @@ -270377,6 +270418,12 @@ public: lastClipRectIsValid = false; } + float getScaleFactor() + { + CGAffineTransform t = CGContextGetCTM (context); + return (float) juce_hypot (t.a + t.c, t.b + t.d); + } + bool clipToRectangle (const Rectangle& r) { CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); @@ -273823,7 +273870,6 @@ class JuceMainMenuHandler : private MenuBarModel::Listener, private DeletedAtShutdown { public: - static JuceMainMenuHandler* instance; JuceMainMenuHandler() : currentModel (0), @@ -273918,64 +273964,6 @@ public: } } - static void flashMenuBar (NSMenu* menu) - { - if ([[menu title] isEqualToString: @"Apple"]) - return; - - [menu retain]; - - const unichar f35Key = NSF35FunctionKey; - NSString* f35String = [NSString stringWithCharacters: &f35Key length: 1]; - - NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"x" - action: nil - keyEquivalent: f35String]; - [item setTarget: nil]; - [menu insertItem: item atIndex: [menu numberOfItems]]; - [item release]; - - if ([menu indexOfItem: item] >= 0) - { - NSEvent* f35Event = [NSEvent keyEventWithType: NSKeyDown - location: NSZeroPoint - modifierFlags: NSCommandKeyMask - timestamp: 0 - windowNumber: 0 - context: [NSGraphicsContext currentContext] - characters: f35String - charactersIgnoringModifiers: f35String - isARepeat: NO - keyCode: 0]; - - [menu performKeyEquivalent: f35Event]; - - if ([menu indexOfItem: item] >= 0) - [menu removeItem: item]; // (this throws if the item isn't actually in the menu) - } - - [menu release]; - } - - static NSMenuItem* findMenuItem (NSMenu* const menu, const ApplicationCommandTarget::InvocationInfo& info) - { - for (NSInteger i = [menu numberOfItems]; --i >= 0;) - { - NSMenuItem* m = [menu itemAtIndex: i]; - if ([m tag] == info.commandID) - return m; - - if ([m submenu] != 0) - { - NSMenuItem* found = findMenuItem ([m submenu], info); - if (found != 0) - return found; - } - } - - return 0; - } - void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info) { NSMenuItem* item = findMenuItem ([NSApp mainMenu], info); @@ -273984,8 +273972,16 @@ public: flashMenuBar ([item menu]); } - void updateMenus() + void updateMenus (NSMenu* menu) { + if (PopupMenu::dismissAllActiveMenus()) + { + // If we were running a juce menu, then we should let that modal loop finish before allowing + // the OS menus to start their own modal loop - so cancel the menu that was being opened.. + if ([menu respondsToSelector: @selector (cancelTracking)]) + [menu performSelector: @selector (cancelTracking)]; + } + if (Time::getMillisecondCounter() > lastUpdateTime + 500) menuBarItemsChanged (0); } @@ -274006,9 +274002,6 @@ public: } } - MenuBarModel* currentModel; - uint32 lastUpdateTime; - void addMenuItem (PopupMenu::MenuItemIterator& iter, NSMenu* menuToAddTo, const int topLevelMenuId, const int topLevelIndex) { @@ -274098,7 +274091,12 @@ public: } } + static JuceMainMenuHandler* instance; + + MenuBarModel* currentModel; + uint32 lastUpdateTime; JuceMenuCallback* callback; + private: NSMenu* createMenu (const PopupMenu menu, @@ -274119,11 +274117,70 @@ private: [m update]; return m; } + + static NSMenuItem* findMenuItem (NSMenu* const menu, const ApplicationCommandTarget::InvocationInfo& info) + { + for (NSInteger i = [menu numberOfItems]; --i >= 0;) + { + NSMenuItem* m = [menu itemAtIndex: i]; + if ([m tag] == info.commandID) + return m; + + if ([m submenu] != 0) + { + NSMenuItem* found = findMenuItem ([m submenu], info); + if (found != 0) + return found; + } + } + + return 0; + } + + static void flashMenuBar (NSMenu* menu) + { + if ([[menu title] isEqualToString: @"Apple"]) + return; + + [menu retain]; + + const unichar f35Key = NSF35FunctionKey; + NSString* f35String = [NSString stringWithCharacters: &f35Key length: 1]; + + NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"x" + action: nil + keyEquivalent: f35String]; + [item setTarget: nil]; + [menu insertItem: item atIndex: [menu numberOfItems]]; + [item release]; + + if ([menu indexOfItem: item] >= 0) + { + NSEvent* f35Event = [NSEvent keyEventWithType: NSKeyDown + location: NSZeroPoint + modifierFlags: NSCommandKeyMask + timestamp: 0 + windowNumber: 0 + context: [NSGraphicsContext currentContext] + characters: f35String + charactersIgnoringModifiers: f35String + isARepeat: NO + keyCode: 0]; + + [menu performKeyEquivalent: f35Event]; + + if ([menu indexOfItem: item] >= 0) + [menu removeItem: item]; // (this throws if the item isn't actually in the menu) + } + + [menu release]; + } }; JuceMainMenuHandler* JuceMainMenuHandler::instance = 0; END_JUCE_NAMESPACE + @implementation JuceMenuCallback - (JuceMenuCallback*) initWithOwner: (JuceMainMenuHandler*) owner_ @@ -274177,10 +274234,8 @@ END_JUCE_NAMESPACE - (void) menuNeedsUpdate: (NSMenu*) menu; { - (void) menu; - if (JuceMainMenuHandler::instance != 0) - JuceMainMenuHandler::instance->updateMenus(); + JuceMainMenuHandler::instance->updateMenus (menu); } @end diff --git a/juce_amalgamated.h b/juce_amalgamated.h index f904a5b2cd..55f7c50524 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -64,7 +64,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 103 +#define JUCE_BUILDNUMBER 104 /** Current Juce version number. @@ -1261,25 +1261,15 @@ inline void swapVariables (Type& variable1, Type& variable2) // Some useful maths functions that aren't always present with all compilers and build settings. -/** Using juce_hypot and juce_hypotf is easier than dealing with all the different - versions of these functions of various platforms and compilers. */ -inline double juce_hypot (double a, double b) throw() +/** Using juce_hypot is easier than dealing with the different types of hypot function + that are provided by the various platforms and compilers. */ +template +inline Type juce_hypot (Type a, Type b) throw() { #if JUCE_WINDOWS - return _hypot (a, b); + return static_cast (_hypot (a, b)); #else - return hypot (a, b); - #endif -} - -/** Using juce_hypot and juce_hypotf is easier than dealing with all the different - versions of these functions of various platforms and compilers. */ -inline float juce_hypotf (float a, float b) throw() -{ - #if JUCE_WINDOWS - return (float) _hypot (a, b); - #else - return hypotf (a, b); + return static_cast (hypot (a, b)); #endif } @@ -19796,6 +19786,12 @@ public: */ float getTranslationY() const throw() { return mat12; } + /** Returns the approximate scale factor by which lengths will be transformed. + Obviously a length may be scaled by entirely different amounts depending on its + direction, so this is only appropriate as a rough guide. + */ + float getScaleFactor() const throw(); + /* The transform matrix is: (mat00 mat01 mat02) @@ -19902,10 +19898,10 @@ public: const Point operator-() const throw() { return Point (-x, -y); } /** Returns the straight-line distance between this point and another one. */ - ValueType getDistanceFromOrigin() const throw() { return (ValueType) juce_hypot (x, y); } + ValueType getDistanceFromOrigin() const throw() { return juce_hypot (x, y); } /** Returns the straight-line distance between this point and another one. */ - ValueType getDistanceFrom (const Point& other) const throw() { return (ValueType) juce_hypot (x - other.x, y - other.y); } + ValueType getDistanceFrom (const Point& other) const throw() { return juce_hypot (x - other.x, y - other.y); } /** Returns the angle from this point to another one. @@ -20872,7 +20868,8 @@ public: ValueType perpendicularDistance) const throw() { const Point delta (end - start); - const double length = juce_hypot (delta.getX(), delta.getY()); + const double length = juce_hypot ((double) delta.getX(), + (double) delta.getY()); if (length == 0) return start; @@ -21985,14 +21982,14 @@ public: The path's winding rule is taken into account by this method. - The tolerence parameter is passed to the PathFlatteningIterator that - is used to trace the path - for more info about it, see the notes for - the PathFlatteningIterator constructor. + The tolerance parameter is the maximum error allowed when flattening the path, + so this method could return a false positive when your point is up to this distance + outside the path's boundary. @see closeSubPath, setUsingNonZeroWinding */ bool contains (float x, float y, - float tolerence = 10.0f) const; + float tolerance = 1.0f) const; /** Checks whether a point lies within the path. @@ -22001,14 +21998,14 @@ public: The path's winding rule is taken into account by this method. - The tolerence parameter is passed to the PathFlatteningIterator that - is used to trace the path - for more info about it, see the notes for - the PathFlatteningIterator constructor. + The tolerance parameter is the maximum error allowed when flattening the path, + so this method could return a false positive when your point is up to this distance + outside the path's boundary. @see closeSubPath, setUsingNonZeroWinding */ bool contains (const Point& point, - float tolerence = 10.0f) const; + float tolerance = 1.0f) const; /** Checks whether a line crosses the path. @@ -22016,12 +22013,12 @@ public: lines or curves. It doesn't take into account whether the line is inside or outside the path, or whether the path is open or closed. - The tolerence parameter is passed to the PathFlatteningIterator that - is used to trace the path - for more info about it, see the notes for - the PathFlatteningIterator constructor. + The tolerance parameter is the maximum error allowed when flattening the path, + so this method could return a false positive when your point is up to this distance + outside the path's boundary. */ bool intersectsLine (const Line& line, - float tolerence = 10.0f); + float tolerance = 1.0f); /** Cuts off parts of a line to keep the parts that are either inside or outside this path. @@ -23214,8 +23211,9 @@ public: @param transform an optional transform to apply to the points from the source path as they are being used @param extraAccuracy if this is greater than 1.0, it will subdivide the path to - a higher resolution, which improved the quality if you'll later want - to enlarge the stroked path + a higher resolution, which improves the quality if you'll later want + to enlarge the stroked path. So for example, if you're planning on drawing + the stroke at 3x the size that you're creating it, you should set this to 3. @see createDashedStroke */ @@ -23243,8 +23241,9 @@ public: @param transform an optional transform to apply to the points from the source path as they are being used @param extraAccuracy if this is greater than 1.0, it will subdivide the path to - a higher resolution, which improved the quality if you'll later want - to enlarge the stroked path + a higher resolution, which improves the quality if you'll later want + to enlarge the stroked path. So for example, if you're planning on drawing + the stroke at 3x the size that you're creating it, you should set this to 3. */ void createDashedStroke (Path& destPath, const Path& sourcePath, @@ -23267,8 +23266,9 @@ public: @param transform an optional transform to apply to the points from the source path as they are being used @param extraAccuracy if this is greater than 1.0, it will subdivide the path to - a higher resolution, which improved the quality if you'll later want - to enlarge the stroked path + a higher resolution, which improves the quality if you'll later want + to enlarge the stroked path. So for example, if you're planning on drawing + the stroke at 3x the size that you're creating it, you should set this to 3. @see createDashedStroke */ void createStrokeWithArrowheads (Path& destPath, @@ -37307,7 +37307,7 @@ public: by some means other than a user action, and you'd like to make sure that menus aren't left hanging around. */ - static void JUCE_CALLTYPE dismissAllActiveMenus(); + static bool JUCE_CALLTYPE dismissAllActiveMenus(); /** Specifies a look-and-feel for the menu and any sub-menus that it has. @@ -60277,6 +60277,7 @@ public: */ virtual void setOrigin (int x, int y) = 0; virtual void addTransform (const AffineTransform& transform) = 0; + virtual float getScaleFactor() = 0; virtual bool clipToRectangle (const Rectangle& r) = 0; virtual bool clipToRectangleList (const RectangleList& clipRegion) = 0; @@ -60342,6 +60343,7 @@ public: bool isVectorDevice() const; void setOrigin (int x, int y); void addTransform (const AffineTransform& transform); + float getScaleFactor(); bool clipToRectangle (const Rectangle& r); bool clipToRectangleList (const RectangleList& clipRegion); @@ -60440,6 +60442,7 @@ public: void setOrigin (int x, int y); void addTransform (const AffineTransform& transform); + float getScaleFactor(); bool clipToRectangle (const Rectangle& r); bool clipToRectangleList (const RectangleList& clipRegion); @@ -61417,13 +61420,13 @@ public: @param path the path to iterate along @param transform a transform to apply to each point in the path being iterated - @param tolerence the amount by which the curves are allowed to deviate from the - lines into which they are being broken down - a higher tolerence - is a bit faster, but less smooth. + @param tolerance the amount by which the curves are allowed to deviate from the lines + into which they are being broken down - a higher tolerance contains + less lines, so can be generated faster, but will be less smooth. */ PathFlatteningIterator (const Path& path, const AffineTransform& transform = AffineTransform::identity, - float tolerence = 6.0f); + float tolerance = defaultTolerance); /** Destructor. */ ~PathFlatteningIterator(); @@ -61460,12 +61463,16 @@ public: bool isLastInSubpath() const throw() { return stackPos == stackBase.getData() && (index >= path.numElements || points [index] == Path::moveMarker); } + /** This is the default value that should be used for the tolerance value (see the constructor parameters). */ + static const float defaultTolerance; + private: const Path& path; const AffineTransform transform; float* points; - float tolerence, subPathCloseX, subPathCloseY; + const float toleranceSquared; + float subPathCloseX, subPathCloseY; const bool isIdentityTransform; HeapBlock stackBase; diff --git a/src/core/juce_MathsFunctions.h b/src/core/juce_MathsFunctions.h index 1f68a4dffe..703c99e69d 100644 --- a/src/core/juce_MathsFunctions.h +++ b/src/core/juce_MathsFunctions.h @@ -292,25 +292,15 @@ inline void swapVariables (Type& variable1, Type& variable2) //============================================================================== // Some useful maths functions that aren't always present with all compilers and build settings. -/** Using juce_hypot and juce_hypotf is easier than dealing with all the different - versions of these functions of various platforms and compilers. */ -inline double juce_hypot (double a, double b) throw() +/** Using juce_hypot is easier than dealing with the different types of hypot function + that are provided by the various platforms and compilers. */ +template +inline Type juce_hypot (Type a, Type b) throw() { #if JUCE_WINDOWS - return _hypot (a, b); + return static_cast (_hypot (a, b)); #else - return hypot (a, b); - #endif -} - -/** Using juce_hypot and juce_hypotf is easier than dealing with all the different - versions of these functions of various platforms and compilers. */ -inline float juce_hypotf (float a, float b) throw() -{ - #if JUCE_WINDOWS - return (float) _hypot (a, b); - #else - return hypotf (a, b); + return static_cast (hypot (a, b)); #endif } diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 5efc8d45b0..d77d9e401e 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 103 +#define JUCE_BUILDNUMBER 104 /** Current Juce version number. diff --git a/src/gui/components/juce_Desktop.cpp b/src/gui/components/juce_Desktop.cpp index 659a0f2ce4..1fc1bb49eb 100644 --- a/src/gui/components/juce_Desktop.cpp +++ b/src/gui/components/juce_Desktop.cpp @@ -274,7 +274,9 @@ void Desktop::triggerFocusCallback() void Desktop::handleAsyncUpdate() { - Component* currentFocus = Component::getCurrentlyFocusedComponent(); + // The component may be deleted during this operation, but we'll use a SafePointer rather than a + // BailOutChecker so that any remaining listeners will still get a callback (with a null pointer). + Component::SafePointer currentFocus (Component::getCurrentlyFocusedComponent()); focusListeners.call (&FocusChangeListener::globalFocusChanged, currentFocus); } diff --git a/src/gui/components/menus/juce_PopupMenu.cpp b/src/gui/components/menus/juce_PopupMenu.cpp index a1d4c02d2f..2a3bc1291a 100644 --- a/src/gui/components/menus/juce_PopupMenu.cpp +++ b/src/gui/components/menus/juce_PopupMenu.cpp @@ -1528,15 +1528,18 @@ int PopupMenu::showAt (Component* componentToAttachTo, } } -void JUCE_CALLTYPE PopupMenu::dismissAllActiveMenus() +bool JUCE_CALLTYPE PopupMenu::dismissAllActiveMenus() { - for (int i = Window::getActiveWindows().size(); --i >= 0;) + const int numWindows = Window::getActiveWindows().size(); + for (int i = numWindows; --i >= 0;) { Window* const pmw = Window::getActiveWindows()[i]; if (pmw != 0) pmw->dismissMenu (0); } + + return numWindows > 0; } //============================================================================== diff --git a/src/gui/components/menus/juce_PopupMenu.h b/src/gui/components/menus/juce_PopupMenu.h index 498c936727..4984797769 100644 --- a/src/gui/components/menus/juce_PopupMenu.h +++ b/src/gui/components/menus/juce_PopupMenu.h @@ -295,7 +295,7 @@ public: by some means other than a user action, and you'd like to make sure that menus aren't left hanging around. */ - static void JUCE_CALLTYPE dismissAllActiveMenus(); + static bool JUCE_CALLTYPE dismissAllActiveMenus(); //============================================================================== diff --git a/src/gui/graphics/contexts/juce_Graphics.cpp b/src/gui/graphics/contexts/juce_Graphics.cpp index 6ce2580966..b41276c37c 100644 --- a/src/gui/graphics/contexts/juce_Graphics.cpp +++ b/src/gui/graphics/contexts/juce_Graphics.cpp @@ -375,7 +375,7 @@ void Graphics::strokePath (const Path& path, const AffineTransform& transform) const { Path stroke; - strokeType.createStrokedPath (stroke, path, transform); + strokeType.createStrokedPath (stroke, path, transform, context->getScaleFactor()); fillPath (stroke); } diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h b/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h index d0a675943e..b5da58ce61 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h @@ -67,6 +67,7 @@ public: */ virtual void setOrigin (int x, int y) = 0; virtual void addTransform (const AffineTransform& transform) = 0; + virtual float getScaleFactor() = 0; virtual bool clipToRectangle (const Rectangle& r) = 0; virtual bool clipToRectangleList (const RectangleList& clipRegion) = 0; diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp index 10f5543347..71587b27db 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp @@ -118,6 +118,12 @@ void LowLevelGraphicsPostScriptRenderer::addTransform (const AffineTransform& /* jassertfalse; } +float LowLevelGraphicsPostScriptRenderer::getScaleFactor() +{ + jassertfalse; //xxx + return 1.0f; +} + bool LowLevelGraphicsPostScriptRenderer::clipToRectangle (const Rectangle& r) { needToClip = true; diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h index f563c58c51..15043c0f55 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h @@ -50,6 +50,7 @@ public: bool isVectorDevice() const; void setOrigin (int x, int y); void addTransform (const AffineTransform& transform); + float getScaleFactor(); bool clipToRectangle (const Rectangle& r); bool clipToRectangleList (const RectangleList& clipRegion); diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index b742845942..554131e402 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -1843,6 +1843,11 @@ public: } } + float getScaleFactor() const + { + return isOnlyTranslated ? 1.0f : complexTransform.getScaleFactor(); + } + bool clipToRectangle (const Rectangle& r) { if (clip != 0) @@ -2254,6 +2259,11 @@ void LowLevelGraphicsSoftwareRenderer::addTransform (const AffineTransform& tran currentState->addTransform (transform); } +float LowLevelGraphicsSoftwareRenderer::getScaleFactor() +{ + return currentState->getScaleFactor(); +} + bool LowLevelGraphicsSoftwareRenderer::clipToRectangle (const Rectangle& r) { return currentState->clipToRectangle (r); diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h index 8854912c17..64e4cbd17f 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h @@ -50,6 +50,7 @@ public: //============================================================================== void setOrigin (int x, int y); void addTransform (const AffineTransform& transform); + float getScaleFactor(); bool clipToRectangle (const Rectangle& r); bool clipToRectangleList (const RectangleList& clipRegion); diff --git a/src/gui/graphics/drawables/juce_SVGParser.cpp b/src/gui/graphics/drawables/juce_SVGParser.cpp index c5c2aee63a..6ef65bb68a 100644 --- a/src/gui/graphics/drawables/juce_SVGParser.cpp +++ b/src/gui/graphics/drawables/juce_SVGParser.cpp @@ -819,7 +819,7 @@ private: float x = getCoordLength (strokeWidth, viewBoxW), y = 0.0f; transform.transformPoints (ox, oy, x, y); - return PathStrokeType (strokeWidth.isNotEmpty() ? juce_hypotf (x - ox, y - oy) : 1.0f, + return PathStrokeType (strokeWidth.isNotEmpty() ? juce_hypot (x - ox, y - oy) : 1.0f, joinStyle, capStyle); } diff --git a/src/gui/graphics/geometry/juce_AffineTransform.cpp b/src/gui/graphics/geometry/juce_AffineTransform.cpp index c8fdac7018..2581da5d9f 100644 --- a/src/gui/graphics/geometry/juce_AffineTransform.cpp +++ b/src/gui/graphics/geometry/juce_AffineTransform.cpp @@ -244,5 +244,10 @@ bool AffineTransform::isOnlyTranslation() const throw() && (mat11 == 1.0f); } +float AffineTransform::getScaleFactor() const throw() +{ + return juce_hypot (mat00 + mat01, mat10 + mat11); +} + END_JUCE_NAMESPACE diff --git a/src/gui/graphics/geometry/juce_AffineTransform.h b/src/gui/graphics/geometry/juce_AffineTransform.h index 1283b3f86c..0f79d3624a 100644 --- a/src/gui/graphics/geometry/juce_AffineTransform.h +++ b/src/gui/graphics/geometry/juce_AffineTransform.h @@ -232,6 +232,12 @@ public: */ float getTranslationY() const throw() { return mat12; } + /** Returns the approximate scale factor by which lengths will be transformed. + Obviously a length may be scaled by entirely different amounts depending on its + direction, so this is only appropriate as a rough guide. + */ + float getScaleFactor() const throw(); + //============================================================================== /* The transform matrix is: diff --git a/src/gui/graphics/geometry/juce_Line.h b/src/gui/graphics/geometry/juce_Line.h index 9746ca34c7..27c1d7f1e7 100644 --- a/src/gui/graphics/geometry/juce_Line.h +++ b/src/gui/graphics/geometry/juce_Line.h @@ -209,7 +209,8 @@ public: ValueType perpendicularDistance) const throw() { const Point delta (end - start); - const double length = juce_hypot (delta.getX(), delta.getY()); + const double length = juce_hypot ((double) delta.getX(), + (double) delta.getY()); if (length == 0) return start; diff --git a/src/gui/graphics/geometry/juce_Path.cpp b/src/gui/graphics/geometry/juce_Path.cpp index 05ba8d187c..4415d44d30 100644 --- a/src/gui/graphics/geometry/juce_Path.cpp +++ b/src/gui/graphics/geometry/juce_Path.cpp @@ -55,6 +55,11 @@ namespace PathHelpers return String (start, (int) (t - start)); } + + inline double lengthOf (float x1, float y1, float x2, float y2) throw() + { + return juce_hypot ((double) (x1 - x2), (double) (y1 - y2)); + } } //============================================================================== @@ -966,13 +971,13 @@ const AffineTransform Path::getTransformToScaleToFit (const float x, const float } //============================================================================== -bool Path::contains (const float x, const float y, const float tolerence) const +bool Path::contains (const float x, const float y, const float tolerance) const { if (x <= pathXMin || x >= pathXMax || y <= pathYMin || y >= pathYMax) return false; - PathFlatteningIterator i (*this, AffineTransform::identity, tolerence); + PathFlatteningIterator i (*this, AffineTransform::identity, tolerance); int positiveCrossings = 0; int negativeCrossings = 0; @@ -997,14 +1002,14 @@ bool Path::contains (const float x, const float y, const float tolerence) const : ((negativeCrossings + positiveCrossings) & 1) != 0; } -bool Path::contains (const Point& point, const float tolerence) const +bool Path::contains (const Point& point, const float tolerance) const { - return contains (point.getX(), point.getY(), tolerence); + return contains (point.getX(), point.getY(), tolerance); } -bool Path::intersectsLine (const Line& line, const float tolerence) +bool Path::intersectsLine (const Line& line, const float tolerance) { - PathFlatteningIterator i (*this, AffineTransform::identity, tolerence); + PathFlatteningIterator i (*this, AffineTransform::identity, tolerance); Point intersection; while (i.next()) @@ -1158,8 +1163,7 @@ const Path Path::createPathWithRoundedCorners (const float cornerRadius) const if (lastWasLine) { - const double len1 = juce_hypot (startX - joinX, - startY - joinY); + const double len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY); if (len1 > 0) { @@ -1169,8 +1173,7 @@ const Path Path::createPathWithRoundedCorners (const float cornerRadius) const p.data.elements [p.numElements - 1] = (float) (joinY - (joinY - startY) * propNeeded); } - const double len2 = juce_hypot (endX - joinX, - endY - joinY); + const double len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY); if (len2 > 0) { @@ -1200,8 +1203,7 @@ const Path Path::createPathWithRoundedCorners (const float cornerRadius) const endX = data.elements [indexOfPathStartThis + 4]; endY = data.elements [indexOfPathStartThis + 5]; - const double len1 = juce_hypot (startX - joinX, - startY - joinY); + const double len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY); if (len1 > 0) { @@ -1211,8 +1213,7 @@ const Path Path::createPathWithRoundedCorners (const float cornerRadius) const p.data.elements [p.numElements - 1] = (float) (joinY - (joinY - startY) * propNeeded); } - const double len2 = juce_hypot (endX - joinX, - endY - joinY); + const double len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY); if (len2 > 0) { diff --git a/src/gui/graphics/geometry/juce_Path.h b/src/gui/graphics/geometry/juce_Path.h index e2ade518de..bf6f3d0f9a 100644 --- a/src/gui/graphics/geometry/juce_Path.h +++ b/src/gui/graphics/geometry/juce_Path.h @@ -107,14 +107,14 @@ public: The path's winding rule is taken into account by this method. - The tolerence parameter is passed to the PathFlatteningIterator that - is used to trace the path - for more info about it, see the notes for - the PathFlatteningIterator constructor. + The tolerance parameter is the maximum error allowed when flattening the path, + so this method could return a false positive when your point is up to this distance + outside the path's boundary. @see closeSubPath, setUsingNonZeroWinding */ bool contains (float x, float y, - float tolerence = 10.0f) const; + float tolerance = 1.0f) const; /** Checks whether a point lies within the path. @@ -123,14 +123,14 @@ public: The path's winding rule is taken into account by this method. - The tolerence parameter is passed to the PathFlatteningIterator that - is used to trace the path - for more info about it, see the notes for - the PathFlatteningIterator constructor. + The tolerance parameter is the maximum error allowed when flattening the path, + so this method could return a false positive when your point is up to this distance + outside the path's boundary. @see closeSubPath, setUsingNonZeroWinding */ bool contains (const Point& point, - float tolerence = 10.0f) const; + float tolerance = 1.0f) const; /** Checks whether a line crosses the path. @@ -138,12 +138,12 @@ public: lines or curves. It doesn't take into account whether the line is inside or outside the path, or whether the path is open or closed. - The tolerence parameter is passed to the PathFlatteningIterator that - is used to trace the path - for more info about it, see the notes for - the PathFlatteningIterator constructor. + The tolerance parameter is the maximum error allowed when flattening the path, + so this method could return a false positive when your point is up to this distance + outside the path's boundary. */ bool intersectsLine (const Line& line, - float tolerence = 10.0f); + float tolerance = 1.0f); /** Cuts off parts of a line to keep the parts that are either inside or outside this path. diff --git a/src/gui/graphics/geometry/juce_PathIterator.cpp b/src/gui/graphics/geometry/juce_PathIterator.cpp index e7d2a7456b..0a7d8cae89 100644 --- a/src/gui/graphics/geometry/juce_PathIterator.cpp +++ b/src/gui/graphics/geometry/juce_PathIterator.cpp @@ -33,10 +33,14 @@ BEGIN_JUCE_NAMESPACE #pragma optimize ("t", on) #endif +//============================================================================== +const float PathFlatteningIterator::defaultTolerance = 0.6f; + + //============================================================================== PathFlatteningIterator::PathFlatteningIterator (const Path& path_, const AffineTransform& transform_, - float tolerence_) + const float tolerance) : x2 (0), y2 (0), closesSubPath (false), @@ -44,7 +48,7 @@ PathFlatteningIterator::PathFlatteningIterator (const Path& path_, path (path_), transform (transform_), points (path_.data.elements), - tolerence (tolerence_ * tolerence_), + toleranceSquared (tolerance * tolerance), subPathCloseX (0), subPathCloseY (0), isIdentityTransform (transform_.isIdentity()), @@ -160,11 +164,6 @@ bool PathFlatteningIterator::next() stackPos = stackBase + offset; } - const float dx1 = x1 - x2; - const float dy1 = y1 - y2; - const float dx2 = x2 - x3; - const float dy2 = y2 - y3; - const float m1x = (x1 + x2) * 0.5f; const float m1y = (y1 + y2) * 0.5f; const float m2x = (x2 + x3) * 0.5f; @@ -172,7 +171,10 @@ bool PathFlatteningIterator::next() const float m3x = (m1x + m2x) * 0.5f; const float m3y = (m1y + m2y) * 0.5f; - if (dx1*dx1 + dy1*dy1 + dx2*dx2 + dy2*dy2 > tolerence) + const float errorX = m3x - x2; + const float errorY = m3y - y2; + + if (errorX * errorX + errorY * errorY > toleranceSquared) { *stackPos++ = y3; *stackPos++ = x3; @@ -210,13 +212,6 @@ bool PathFlatteningIterator::next() stackPos = stackBase + offset; } - const float dx1 = x1 - x2; - const float dy1 = y1 - y2; - const float dx2 = x2 - x3; - const float dy2 = y2 - y3; - const float dx3 = x3 - x4; - const float dy3 = y3 - y4; - const float m1x = (x1 + x2) * 0.5f; const float m1y = (y1 + y2) * 0.5f; const float m2x = (x3 + x2) * 0.5f; @@ -228,8 +223,13 @@ bool PathFlatteningIterator::next() const float m5x = (m3x + m2x) * 0.5f; const float m5y = (m3y + m2y) * 0.5f; - if (dx1*dx1 + dy1*dy1 + dx2*dx2 - + dy2*dy2 + dx3*dx3 + dy3*dy3 > tolerence) + const float error1X = m4x - x2; + const float error1Y = m4y - y2; + const float error2X = m5x - x3; + const float error2Y = m5y - y3; + + if (error1X * error1X + error1Y * error1Y > toleranceSquared + || error2X * error2X + error2Y * error2Y > toleranceSquared) { *stackPos++ = y4; *stackPos++ = x4; diff --git a/src/gui/graphics/geometry/juce_PathIterator.h b/src/gui/graphics/geometry/juce_PathIterator.h index 70fe8fe899..81ecdace0b 100644 --- a/src/gui/graphics/geometry/juce_PathIterator.h +++ b/src/gui/graphics/geometry/juce_PathIterator.h @@ -50,13 +50,13 @@ public: @param path the path to iterate along @param transform a transform to apply to each point in the path being iterated - @param tolerence the amount by which the curves are allowed to deviate from the - lines into which they are being broken down - a higher tolerence - is a bit faster, but less smooth. + @param tolerance the amount by which the curves are allowed to deviate from the lines + into which they are being broken down - a higher tolerance contains + less lines, so can be generated faster, but will be less smooth. */ PathFlatteningIterator (const Path& path, const AffineTransform& transform = AffineTransform::identity, - float tolerence = 6.0f); + float tolerance = defaultTolerance); /** Destructor. */ ~PathFlatteningIterator(); @@ -94,12 +94,17 @@ public: bool isLastInSubpath() const throw() { return stackPos == stackBase.getData() && (index >= path.numElements || points [index] == Path::moveMarker); } + + /** This is the default value that should be used for the tolerance value (see the constructor parameters). */ + static const float defaultTolerance; + private: //============================================================================== const Path& path; const AffineTransform transform; float* points; - float tolerence, subPathCloseX, subPathCloseY; + const float toleranceSquared; + float subPathCloseX, subPathCloseY; const bool isIdentityTransform; HeapBlock stackBase; diff --git a/src/gui/graphics/geometry/juce_PathStrokeType.cpp b/src/gui/graphics/geometry/juce_PathStrokeType.cpp index afecfe9b73..b682c6ee19 100644 --- a/src/gui/graphics/geometry/juce_PathStrokeType.cpp +++ b/src/gui/graphics/geometry/juce_PathStrokeType.cpp @@ -303,7 +303,7 @@ namespace PathStrokeHelpers float dx = x2 - x1; float dy = y2 - y1; - const float len = juce_hypotf (dx, dy); + const float len = juce_hypot (dx, dy); if (len == 0) { @@ -380,7 +380,7 @@ namespace PathStrokeHelpers LineSection& l = subPath.getReference (subPath.size() - 1); float dx = l.rx2 - l.rx1; float dy = l.ry2 - l.ry1; - const float len = juce_hypotf (dx, dy); + const float len = juce_hypot (dx, dy); if (len <= amountAtEnd && subPath.size() > 1) { @@ -408,7 +408,7 @@ namespace PathStrokeHelpers LineSection& l = subPath.getReference (0); float dx = l.rx2 - l.rx1; float dy = l.ry2 - l.ry1; - const float len = juce_hypotf (dx, dy); + const float len = juce_hypot (dx, dy); if (len <= amountAtStart && subPath.size() > 1) { @@ -551,6 +551,8 @@ namespace PathStrokeHelpers const AffineTransform& transform, const float extraAccuracy, const Arrowhead* const arrowhead) { + jassert (extraAccuracy > 0); + if (thickness <= 0) { destPath.clear(); @@ -577,7 +579,7 @@ namespace PathStrokeHelpers // Iterate the path, creating a list of the // left/right-hand lines along either side of it... - PathFlatteningIterator it (*sourcePath, transform, 9.0f / extraAccuracy); + PathFlatteningIterator it (*sourcePath, transform, PathFlatteningIterator::defaultTolerance / extraAccuracy); Array subPath; subPath.ensureStorageAllocated (512); @@ -585,7 +587,7 @@ namespace PathStrokeHelpers l.x1 = 0; l.y1 = 0; - const float minSegmentLength = 2.0f / (extraAccuracy * extraAccuracy); + const float minSegmentLength = 0.0001f; while (it.next()) { @@ -669,6 +671,8 @@ void PathStrokeType::createDashedStroke (Path& destPath, const AffineTransform& transform, const float extraAccuracy) const { + jassert (extraAccuracy > 0); + if (thickness <= 0) return; @@ -676,7 +680,7 @@ void PathStrokeType::createDashedStroke (Path& destPath, jassert ((numDashLengths & 1) == 0); Path newDestPath; - PathFlatteningIterator it (sourcePath, transform, 9.0f / extraAccuracy); + PathFlatteningIterator it (sourcePath, transform, PathFlatteningIterator::defaultTolerance / extraAccuracy); bool first = true; int dashNum = 0; @@ -712,7 +716,7 @@ void PathStrokeType::createDashedStroke (Path& destPath, dx = it.x2 - it.x1; dy = it.y2 - it.y1; - lineLen = juce_hypotf (dx, dy); + lineLen = juce_hypot (dx, dy); lineEndPos += lineLen; first = it.closesSubPath; } diff --git a/src/gui/graphics/geometry/juce_PathStrokeType.h b/src/gui/graphics/geometry/juce_PathStrokeType.h index fce3851daa..5da77d08c1 100644 --- a/src/gui/graphics/geometry/juce_PathStrokeType.h +++ b/src/gui/graphics/geometry/juce_PathStrokeType.h @@ -96,8 +96,9 @@ public: @param transform an optional transform to apply to the points from the source path as they are being used @param extraAccuracy if this is greater than 1.0, it will subdivide the path to - a higher resolution, which improved the quality if you'll later want - to enlarge the stroked path + a higher resolution, which improves the quality if you'll later want + to enlarge the stroked path. So for example, if you're planning on drawing + the stroke at 3x the size that you're creating it, you should set this to 3. @see createDashedStroke */ @@ -127,8 +128,9 @@ public: @param transform an optional transform to apply to the points from the source path as they are being used @param extraAccuracy if this is greater than 1.0, it will subdivide the path to - a higher resolution, which improved the quality if you'll later want - to enlarge the stroked path + a higher resolution, which improves the quality if you'll later want + to enlarge the stroked path. So for example, if you're planning on drawing + the stroke at 3x the size that you're creating it, you should set this to 3. */ void createDashedStroke (Path& destPath, const Path& sourcePath, @@ -152,8 +154,9 @@ public: @param transform an optional transform to apply to the points from the source path as they are being used @param extraAccuracy if this is greater than 1.0, it will subdivide the path to - a higher resolution, which improved the quality if you'll later want - to enlarge the stroked path + a higher resolution, which improves the quality if you'll later want + to enlarge the stroked path. So for example, if you're planning on drawing + the stroke at 3x the size that you're creating it, you should set this to 3. @see createDashedStroke */ void createStrokeWithArrowheads (Path& destPath, diff --git a/src/gui/graphics/geometry/juce_Point.h b/src/gui/graphics/geometry/juce_Point.h index a59062a41a..ec40ead08f 100644 --- a/src/gui/graphics/geometry/juce_Point.h +++ b/src/gui/graphics/geometry/juce_Point.h @@ -120,10 +120,10 @@ public: const Point operator-() const throw() { return Point (-x, -y); } /** Returns the straight-line distance between this point and another one. */ - ValueType getDistanceFromOrigin() const throw() { return (ValueType) juce_hypot (x, y); } + ValueType getDistanceFromOrigin() const throw() { return juce_hypot (x, y); } /** Returns the straight-line distance between this point and another one. */ - ValueType getDistanceFrom (const Point& other) const throw() { return (ValueType) juce_hypot (x - other.x, y - other.y); } + ValueType getDistanceFrom (const Point& other) const throw() { return juce_hypot (x - other.x, y - other.y); } /** Returns the angle from this point to another one. diff --git a/src/native/mac/juce_mac_CoreGraphicsContext.mm b/src/native/mac/juce_mac_CoreGraphicsContext.mm index af395b2584..c11c303840 100644 --- a/src/native/mac/juce_mac_CoreGraphicsContext.mm +++ b/src/native/mac/juce_mac_CoreGraphicsContext.mm @@ -192,6 +192,12 @@ public: lastClipRectIsValid = false; } + float getScaleFactor() + { + CGAffineTransform t = CGContextGetCTM (context); + return (float) juce_hypot (t.a + t.c, t.b + t.d); + } + bool clipToRectangle (const Rectangle& r) { CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); diff --git a/src/native/mac/juce_mac_MainMenu.mm b/src/native/mac/juce_mac_MainMenu.mm index de7c058fb0..af9e12e7e7 100644 --- a/src/native/mac/juce_mac_MainMenu.mm +++ b/src/native/mac/juce_mac_MainMenu.mm @@ -57,8 +57,6 @@ class JuceMainMenuHandler : private MenuBarModel::Listener, private DeletedAtShutdown { public: - static JuceMainMenuHandler* instance; - //============================================================================== JuceMainMenuHandler() : currentModel (0), @@ -153,64 +151,6 @@ public: } } - static void flashMenuBar (NSMenu* menu) - { - if ([[menu title] isEqualToString: @"Apple"]) - return; - - [menu retain]; - - const unichar f35Key = NSF35FunctionKey; - NSString* f35String = [NSString stringWithCharacters: &f35Key length: 1]; - - NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"x" - action: nil - keyEquivalent: f35String]; - [item setTarget: nil]; - [menu insertItem: item atIndex: [menu numberOfItems]]; - [item release]; - - if ([menu indexOfItem: item] >= 0) - { - NSEvent* f35Event = [NSEvent keyEventWithType: NSKeyDown - location: NSZeroPoint - modifierFlags: NSCommandKeyMask - timestamp: 0 - windowNumber: 0 - context: [NSGraphicsContext currentContext] - characters: f35String - charactersIgnoringModifiers: f35String - isARepeat: NO - keyCode: 0]; - - [menu performKeyEquivalent: f35Event]; - - if ([menu indexOfItem: item] >= 0) - [menu removeItem: item]; // (this throws if the item isn't actually in the menu) - } - - [menu release]; - } - - static NSMenuItem* findMenuItem (NSMenu* const menu, const ApplicationCommandTarget::InvocationInfo& info) - { - for (NSInteger i = [menu numberOfItems]; --i >= 0;) - { - NSMenuItem* m = [menu itemAtIndex: i]; - if ([m tag] == info.commandID) - return m; - - if ([m submenu] != 0) - { - NSMenuItem* found = findMenuItem ([m submenu], info); - if (found != 0) - return found; - } - } - - return 0; - } - void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info) { NSMenuItem* item = findMenuItem ([NSApp mainMenu], info); @@ -219,8 +159,16 @@ public: flashMenuBar ([item menu]); } - void updateMenus() + void updateMenus (NSMenu* menu) { + if (PopupMenu::dismissAllActiveMenus()) + { + // If we were running a juce menu, then we should let that modal loop finish before allowing + // the OS menus to start their own modal loop - so cancel the menu that was being opened.. + if ([menu respondsToSelector: @selector (cancelTracking)]) + [menu performSelector: @selector (cancelTracking)]; + } + if (Time::getMillisecondCounter() > lastUpdateTime + 500) menuBarItemsChanged (0); } @@ -241,9 +189,6 @@ public: } } - MenuBarModel* currentModel; - uint32 lastUpdateTime; - void addMenuItem (PopupMenu::MenuItemIterator& iter, NSMenu* menuToAddTo, const int topLevelMenuId, const int topLevelIndex) { @@ -333,9 +278,14 @@ public: } } - JuceMenuCallback* callback; -private: + static JuceMainMenuHandler* instance; + MenuBarModel* currentModel; + uint32 lastUpdateTime; + JuceMenuCallback* callback; + +private: + //============================================================================== NSMenu* createMenu (const PopupMenu menu, const String& menuName, const int topLevelMenuId, @@ -354,11 +304,71 @@ private: [m update]; return m; } + + static NSMenuItem* findMenuItem (NSMenu* const menu, const ApplicationCommandTarget::InvocationInfo& info) + { + for (NSInteger i = [menu numberOfItems]; --i >= 0;) + { + NSMenuItem* m = [menu itemAtIndex: i]; + if ([m tag] == info.commandID) + return m; + + if ([m submenu] != 0) + { + NSMenuItem* found = findMenuItem ([m submenu], info); + if (found != 0) + return found; + } + } + + return 0; + } + + static void flashMenuBar (NSMenu* menu) + { + if ([[menu title] isEqualToString: @"Apple"]) + return; + + [menu retain]; + + const unichar f35Key = NSF35FunctionKey; + NSString* f35String = [NSString stringWithCharacters: &f35Key length: 1]; + + NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"x" + action: nil + keyEquivalent: f35String]; + [item setTarget: nil]; + [menu insertItem: item atIndex: [menu numberOfItems]]; + [item release]; + + if ([menu indexOfItem: item] >= 0) + { + NSEvent* f35Event = [NSEvent keyEventWithType: NSKeyDown + location: NSZeroPoint + modifierFlags: NSCommandKeyMask + timestamp: 0 + windowNumber: 0 + context: [NSGraphicsContext currentContext] + characters: f35String + charactersIgnoringModifiers: f35String + isARepeat: NO + keyCode: 0]; + + [menu performKeyEquivalent: f35Event]; + + if ([menu indexOfItem: item] >= 0) + [menu removeItem: item]; // (this throws if the item isn't actually in the menu) + } + + [menu release]; + } }; JuceMainMenuHandler* JuceMainMenuHandler::instance = 0; END_JUCE_NAMESPACE + +//============================================================================== @implementation JuceMenuCallback - (JuceMenuCallback*) initWithOwner: (JuceMainMenuHandler*) owner_ @@ -412,10 +422,8 @@ END_JUCE_NAMESPACE - (void) menuNeedsUpdate: (NSMenu*) menu; { - (void) menu; - if (JuceMainMenuHandler::instance != 0) - JuceMainMenuHandler::instance->updateMenus(); + JuceMainMenuHandler::instance->updateMenus (menu); } @end diff --git a/src/native/windows/juce_win32_Direct2DGraphicsContext.cpp b/src/native/windows/juce_win32_Direct2DGraphicsContext.cpp index de1c022a3d..bb2efc5221 100644 --- a/src/native/windows/juce_win32_Direct2DGraphicsContext.cpp +++ b/src/native/windows/juce_win32_Direct2DGraphicsContext.cpp @@ -125,6 +125,12 @@ public: jassertfalse; } + float getScaleFactor() + { + jassertfalse; //xxx + return 1.0f; + } + bool clipToRectangle (const Rectangle& r) { currentState->clipToRectangle (r);