| @@ -87,6 +87,9 @@ void DrawableDocument::checkRootObject() | |||
| DrawableComposite::ValueTreeWrapper rootObject (getRootDrawableNode()); | |||
| StringArray idCache; | |||
| recursivelyUpdateIDs (rootObject, idCache); | |||
| if (rootObject.getNumMarkers (true) < 2 || rootObject.getNumMarkers (false) < 2) | |||
| rootObject.setContentArea (RelativeRectangle ("0, 0, 100, 100"), 0); | |||
| } | |||
| const String DrawableDocument::getUniqueId() const | |||
| @@ -735,12 +735,13 @@ public: | |||
| const RelativePoint getPosition() | |||
| { | |||
| DrawableComposite::ValueTreeWrapper wrapper (item); | |||
| const RelativeParallelogram bounds (wrapper.getBoundingBox()); | |||
| switch (cpNum) | |||
| { | |||
| case 0: return wrapper.getTargetPositionForOrigin(); | |||
| case 1: return wrapper.getTargetPositionForX1Y0(); | |||
| case 2: return wrapper.getTargetPositionForX0Y1(); | |||
| case 0: return bounds.topLeft; | |||
| case 1: return bounds.topRight; | |||
| case 2: return bounds.bottomLeft; | |||
| default: jassertfalse; break; | |||
| } | |||
| @@ -750,14 +751,17 @@ public: | |||
| void setPosition (const RelativePoint& newPoint, UndoManager* undoManager) | |||
| { | |||
| DrawableComposite::ValueTreeWrapper wrapper (item); | |||
| RelativeParallelogram bounds (wrapper.getBoundingBox()); | |||
| switch (cpNum) | |||
| { | |||
| case 0: wrapper.setTargetPositionForOrigin (newPoint, undoManager); break; | |||
| case 1: wrapper.setTargetPositionForX1Y0 (newPoint, undoManager); break; | |||
| case 2: wrapper.setTargetPositionForX0Y1 (newPoint, undoManager); break; | |||
| case 0: bounds.topLeft = newPoint; break; | |||
| case 1: bounds.topRight = newPoint; break; | |||
| case 2: bounds.bottomLeft = newPoint; break; | |||
| default: jassertfalse; break; | |||
| } | |||
| wrapper.setBoundingBox (bounds, undoManager); | |||
| } | |||
| const Value getPositionValue (UndoManager* undoManager) | |||
| @@ -809,15 +813,14 @@ public: | |||
| void buttonClicked() | |||
| { | |||
| RelativePoint topLeft (wrapper.getTargetPositionForOrigin()); | |||
| RelativePoint topRight (wrapper.getTargetPositionForX1Y0()); | |||
| RelativePoint bottomLeft (wrapper.getTargetPositionForX0Y1()); | |||
| RelativeParallelogram bounds (wrapper.getBoundingBox()); | |||
| const RelativeRectangle content (wrapper.getContentArea()); | |||
| const Rectangle<float> resolved (content.resolve (&item)); | |||
| topRight.moveToAbsolute (topLeft.resolve (&item) + Point<float> (1.0f, 0.0f), &item); | |||
| bottomLeft.moveToAbsolute (topLeft.resolve (&item) + Point<float> (0.0f, 1.0f), &item); | |||
| bounds.topRight.moveToAbsolute (bounds.topLeft.resolve (&item) + Point<float> (resolved.getWidth(), 0), &item); | |||
| bounds.bottomLeft.moveToAbsolute (bounds.topLeft.resolve (&item) + Point<float> (0, resolved.getHeight()), &item); | |||
| wrapper.setTargetPositionForX1Y0 (topRight, item.getUndoManager()); | |||
| wrapper.setTargetPositionForX0Y1 (bottomLeft, item.getUndoManager()); | |||
| wrapper.setBoundingBox (bounds, item.getUndoManager()); | |||
| } | |||
| private: | |||
| @@ -68,12 +68,15 @@ public: | |||
| { | |||
| Drawable* newDrawable = Drawable::createFromValueTree (doc.getRootDrawableNode().getState(), &doc); | |||
| drawable = dynamic_cast <DrawableComposite*> (newDrawable); | |||
| drawable->resetBoundingBoxToContentArea(); | |||
| jassert (drawable != 0); | |||
| getComponentHolder()->repaint(); | |||
| } | |||
| else | |||
| { | |||
| doc.getRootDrawableNode().resetBoundingBoxToContentArea (0); | |||
| const Rectangle<float> damage (drawable->refreshFromValueTree (doc.getRootDrawableNode().getState(), &doc)); | |||
| getComponentHolder()->repaint (objectSpaceToScreenSpace (damage.getSmallestIntegerContainer())); | |||
| } | |||
| @@ -302,13 +302,13 @@ public: | |||
| if (isX) | |||
| { | |||
| const float centre = getWidth() / 2 + 0.5f; | |||
| path.addLineSegment (centre, 2.0f, centre, getHeight() + 1.0f, lineThickness); | |||
| path.addLineSegment (Line<float> (centre, 2.0f, centre, getHeight() + 1.0f), lineThickness); | |||
| path.addTriangle (1.0f, 0.0f, centre * 2.0f - 1.0f, 0.0f, centre, headSize + 1.0f); | |||
| } | |||
| else | |||
| { | |||
| const float centre = getHeight() / 2 + 0.5f; | |||
| path.addLineSegment (2.0f, centre, getWidth() + 1.0f, centre, lineThickness); | |||
| path.addLineSegment (Line<float> (2.0f, centre, getWidth() + 1.0f, centre), lineThickness); | |||
| path.addTriangle (0.0f, centre * 2.0f - 1.0f, 0.0f, 1.0f, headSize + 1.0f, centre); | |||
| } | |||
| @@ -265,7 +265,7 @@ private: | |||
| Path marker; | |||
| const float headSize = 4.5f; | |||
| marker.addLineSegment (0.0f, -2.0f, 0.0f, previewArea.getHeight() + 2.0f, 1.5f); | |||
| marker.addLineSegment (Line<float> (0.0f, -2.0f, 0.0f, previewArea.getHeight() + 2.0f), 1.5f); | |||
| marker.addTriangle (0.0f, 1.0f, -headSize, -headSize, headSize, -headSize); | |||
| for (int i = 0; i < gradient.getNumColours(); ++i) | |||
| @@ -42,7 +42,6 @@ class FontsAndTextDemo : public Component, | |||
| Slider* horizontalScaleSlider; | |||
| StretchableLayoutManager verticalLayout; | |||
| StretchableLayoutManager horizontalLayout; | |||
| StretchableLayoutResizerBar* verticalDividerBar; | |||
| @@ -104,22 +103,6 @@ public: | |||
| verticalDividerBar = new StretchableLayoutResizerBar (&verticalLayout, 1, true); | |||
| addAndMakeVisible (verticalDividerBar); | |||
| horizontalLayout.setItemLayout (0, -0.2, -1.0, -0.4); // height of the font text box must be | |||
| // between 20% and 100%, preferably 40% | |||
| horizontalLayout.setItemLayout (1, 8, 8, 8); // the horizontal divider drag-bar thing is always 8 pixels high | |||
| horizontalLayout.setItemLayout (2, 2, 5, 5); // a gap between the controls | |||
| horizontalLayout.setItemLayout (3, 15, 20, 20); // the italic button would like to be 20 pixels high | |||
| horizontalLayout.setItemLayout (4, 2, 5, 5); // a gap between the controls | |||
| horizontalLayout.setItemLayout (5, 15, 20, 20); // the bold button would like to be 20 pixels high | |||
| horizontalLayout.setItemLayout (6, 2, 5, 5); // a gap between the controls | |||
| horizontalLayout.setItemLayout (7, 15, 20, 20); // the italic button would like to be 20 pixels high | |||
| horizontalLayout.setItemLayout (8, 2, 5, 5); // a gap between the controls | |||
| horizontalLayout.setItemLayout (9, 15, 20, 20); // the copy code button would like to be 20 pixels high | |||
| horizontalLayout.setItemLayout (10, 5, -1.0, 5); // add a gap at the bottom that will fill up any | |||
| // space left over - this will stop the | |||
| // sliders from always sticking to the | |||
| // bottom of the window | |||
| } | |||
| ~FontsAndTextDemo() | |||
| @@ -178,7 +178,7 @@ private: | |||
| float size = (float) jmin (getWidth(), getHeight()); | |||
| Path p; | |||
| p.addStar (bouncingPointX[1], bouncingPointY[1], 7, | |||
| p.addStar (Point<float> (bouncingPointX[1], bouncingPointY[1]), 7, | |||
| size * jmax (0.6f, bouncingNumber[4]), | |||
| size * jmax (0.7f, bouncingNumber[5]), | |||
| bouncingNumber[4]); | |||
| @@ -201,8 +201,8 @@ private: | |||
| { | |||
| Path p; | |||
| p.addRectangle (-50, 0, 100, 100); | |||
| p.addStar (100.0f, 0.0f, 7, 30.0f, 70.0f, 0.1f); | |||
| p.addStar (-100.0f, 0.0f, 6, 40.0f, 70.0f, 0.1f); | |||
| p.addStar (Point<float> (100.0f, 0.0f), 7, 30.0f, 70.0f, 0.1f); | |||
| p.addStar (Point<float> (-100.0f, 0.0f), 6, 40.0f, 70.0f, 0.1f); | |||
| p.addEllipse (-60.0f, -100.0f, 120.0f, 90.0f); | |||
| if (linearGradient || radialGradient) | |||
| @@ -336,28 +336,19 @@ private: | |||
| ZipFile icons (&iconsFileStream, false); | |||
| // Load a random SVG file from our embedded icons.zip file. | |||
| InputStream* svgFileStream | |||
| = icons.createStreamForEntry (Random::getSystemRandom().nextInt (icons.getNumEntries())); | |||
| ScopedPointer<InputStream> svgFileStream (icons.createStreamForEntry (Random::getSystemRandom().nextInt (icons.getNumEntries()))); | |||
| if (svgFileStream != 0) | |||
| { | |||
| svgDrawable = dynamic_cast <DrawableComposite*> (Drawable::createFromImageDataStream (*svgFileStream)); | |||
| delete svgFileStream; | |||
| if (svgDrawable != 0) | |||
| { | |||
| // to make our icon the right size, we'll put it inside a DrawableComposite, and apply | |||
| // a transform to get it to the size we want. | |||
| Rectangle<float> bounds = svgDrawable->getBounds(); | |||
| const float scaleFactor = 200.0f / jmax (bounds.getWidth(), bounds.getHeight()); | |||
| Point<float> topLeft (-bounds.getCentreX() * scaleFactor, | |||
| -bounds.getCentreY() * scaleFactor); | |||
| svgDrawable->setTransform (topLeft, | |||
| topLeft + Point<float> (scaleFactor, 0), | |||
| topLeft + Point<float> (0, scaleFactor)); | |||
| // to make our icon the right size, we'll put it inside a DrawableComposite, and | |||
| // set its bounding box to the size and position that we want. | |||
| svgDrawable->setBoundingBox (RelativeParallelogram (Point<float> (-100, -100), | |||
| Point<float> (100, -100), | |||
| Point<float> (-100, 100))); | |||
| } | |||
| } | |||
| } | |||
| @@ -442,7 +442,7 @@ static Component* createRadioButtonPage() | |||
| DrawablePath normal, over; | |||
| Path p; | |||
| p.addStar (0.0f, 0.0f, i + 5, 20.0f, 50.0f, -0.2f); | |||
| p.addStar (Point<float>(), i + 5, 20.0f, 50.0f, -0.2f); | |||
| normal.setPath (p); | |||
| normal.setFill (Colours::lightblue); | |||
| normal.setStrokeFill (Colours::black); | |||
| @@ -500,12 +500,12 @@ public: | |||
| DrawablePath normal, over; | |||
| Path p; | |||
| p.addStar (0.0f, 0.0f, 5, 20.0f, 50.0f, 0.2f); | |||
| p.addStar (Point<float>(), 5, 20.0f, 50.0f, 0.2f); | |||
| normal.setPath (p); | |||
| normal.setFill (Colours::red); | |||
| p.clear(); | |||
| p.addStar (0.0f, 0.0f, 7, 30.0f, 50.0f, 0.0f); | |||
| p.addStar (Point<float>(), 7, 30.0f, 50.0f, 0.0f); | |||
| over.setPath (p); | |||
| over.setFill (Colours::pink); | |||
| over.setStrokeFill (Colours::black); | |||
| @@ -130,8 +130,8 @@ public: | |||
| r << "g.drawImage (" << imageVariable << ",\n " | |||
| << x << ", " << y << ", " << w << ", " << h | |||
| << ",\n 0, 0, " | |||
| << imageVariable << "->getWidth(), " | |||
| << imageVariable << "->getHeight());\n\n"; | |||
| << imageVariable << ".getWidth(), " | |||
| << imageVariable << ".getHeight());\n\n"; | |||
| } | |||
| else | |||
| { | |||
| @@ -64,7 +64,7 @@ | |||
| */ | |||
| #define JUCE_MAJOR_VERSION 1 | |||
| #define JUCE_MINOR_VERSION 52 | |||
| #define JUCE_BUILDNUMBER 11 | |||
| #define JUCE_BUILDNUMBER 12 | |||
| /** Current Juce version number. | |||
| @@ -19023,6 +19023,9 @@ public: | |||
| /** Copies this point from another one. */ | |||
| Point& operator= (const Point& other) throw() { x = other.x; y = other.y; return *this; } | |||
| inline bool operator== (const Point& other) const throw() { return x == other.x && y == other.y; } | |||
| inline bool operator!= (const Point& other) const throw() { return x != other.x || y != other.y; } | |||
| /** Returns true if the point is (0, 0). */ | |||
| bool isOrigin() const throw() { return x == ValueType() && y == ValueType(); } | |||
| @@ -19050,9 +19053,6 @@ public: | |||
| /** Adds a pair of co-ordinates to this value. */ | |||
| void addXY (const ValueType xToAdd, const ValueType yToAdd) throw() { x += xToAdd; y += yToAdd; } | |||
| inline bool operator== (const Point& other) const throw() { return x == other.x && y == other.y; } | |||
| inline bool operator!= (const Point& other) const throw() { return x != other.x || y != other.y; } | |||
| /** Adds two points together. */ | |||
| const Point operator+ (const Point& other) const throw() { return Point (x + other.x, y + other.y); } | |||
| @@ -19093,6 +19093,20 @@ public: | |||
| */ | |||
| ValueType getAngleToPoint (const Point& other) const throw() { return (ValueType) std::atan2 (other.x - x, other.y - y); } | |||
| /** Taking this point to be the centre of a circle, this returns a point on its circumference. | |||
| @param radius the radius of the circle. | |||
| @param angle the angle of the point, in radians clockwise from the 12 o'clock position. | |||
| */ | |||
| const Point getPointOnCircumference (const float radius, const float angle) const throw() { return Point<float> (x + radius * std::sin (angle), | |||
| y - radius * std::cos (angle)); } | |||
| /** Taking this point to be the centre of an ellipse, this returns a point on its circumference. | |||
| @param radiusX the horizontal radius of the circle. | |||
| @param radiusY the vertical radius of the circle. | |||
| @param angle the angle of the point, in radians clockwise from the 12 o'clock position. | |||
| */ | |||
| const Point getPointOnCircumference (const float radiusX, const float radiusY, const float angle) const throw() { return Point<float> (x + radiusX * std::sin (angle), | |||
| y - radiusY * std::cos (angle)); } | |||
| /** Uses a transform to change the point's co-ordinates. | |||
| This will only compile if ValueType = float! | |||
| @see AffineTransform::transformPoint | |||
| @@ -19941,6 +19955,9 @@ public: | |||
| /** Changes this line's end point */ | |||
| void setEnd (const Point<ValueType>& newEnd) throw() { end = newEnd; } | |||
| /** Returns a line that is the same as this one, but with the start and end reversed, */ | |||
| const Line reversed() const throw() { return Line (end, start); } | |||
| /** Applies an affine transform to the line's start and end points. */ | |||
| void applyTransform (const AffineTransform& transform) throw() | |||
| { | |||
| @@ -20313,6 +20330,18 @@ public: | |||
| /** Returns a rectangle with the same size as this one, but a new position. */ | |||
| const Rectangle withPosition (const Point<ValueType>& newPos) const throw() { return Rectangle (newPos.getX(), newPos.getY(), w, h); } | |||
| /** Returns the rectangle's top-left position as a Point. */ | |||
| const Point<ValueType> getTopLeft() const throw() { return getPosition(); } | |||
| /** Returns the rectangle's top-right position as a Point. */ | |||
| const Point<ValueType> getTopRight() const throw() { return Point<float> (x + w, y); } | |||
| /** Returns the rectangle's bottom-left position as a Point. */ | |||
| const Point<ValueType> getBottomLeft() const throw() { return Point<float> (x, y + h); } | |||
| /** Returns the rectangle's bottom-right position as a Point. */ | |||
| const Point<ValueType> getBottomRight() const throw() { return Point<float> (x + w, y + h); } | |||
| /** Changes the rectangle's size, leaving the position of its top-left corner unchanged. */ | |||
| void setSize (const ValueType newWidth, const ValueType newHeight) throw() { w = newWidth; h = newHeight; } | |||
| @@ -21069,6 +21098,19 @@ public: | |||
| */ | |||
| void startNewSubPath (float startX, float startY); | |||
| /** Begins a new subpath with a given starting position. | |||
| This will move the path's current position to the co-ordinates passed in and | |||
| make it ready to draw lines or curves starting from this position. | |||
| After adding whatever lines and curves are needed, you can either | |||
| close the current sub-path using closeSubPath() or call startNewSubPath() | |||
| to move to a new sub-path, leaving the old one open-ended. | |||
| @see lineTo, quadraticTo, cubicTo, closeSubPath | |||
| */ | |||
| void startNewSubPath (const Point<float>& start); | |||
| /** Closes a the current sub-path with a line back to its start-point. | |||
| When creating a closed shape such as a triangle, don't use 3 lineTo() | |||
| @@ -21094,6 +21136,17 @@ public: | |||
| */ | |||
| void lineTo (float endX, float endY); | |||
| /** Adds a line from the shape's last position to a new end-point. | |||
| This will connect the end-point of the last line or curve that was added | |||
| to a new point, using a straight line. | |||
| See the class description for an example of how to add lines and curves to a path. | |||
| @see startNewSubPath, quadraticTo, cubicTo, closeSubPath | |||
| */ | |||
| void lineTo (const Point<float>& end); | |||
| /** Adds a quadratic bezier curve from the shape's last position to a new position. | |||
| This will connect the end-point of the last line or curve that was added | |||
| @@ -21108,6 +21161,18 @@ public: | |||
| float endPointX, | |||
| float endPointY); | |||
| /** Adds a quadratic bezier curve from the shape's last position to a new position. | |||
| This will connect the end-point of the last line or curve that was added | |||
| to a new point, using a quadratic spline with one control-point. | |||
| See the class description for an example of how to add lines and curves to a path. | |||
| @see startNewSubPath, lineTo, cubicTo, closeSubPath | |||
| */ | |||
| void quadraticTo (const Point<float>& controlPoint, | |||
| const Point<float>& endPoint); | |||
| /** Adds a cubic bezier curve from the shape's last position to a new position. | |||
| This will connect the end-point of the last line or curve that was added | |||
| @@ -21124,6 +21189,19 @@ public: | |||
| float endPointX, | |||
| float endPointY); | |||
| /** Adds a cubic bezier curve from the shape's last position to a new position. | |||
| This will connect the end-point of the last line or curve that was added | |||
| to a new point, using a cubic spline with two control-points. | |||
| See the class description for an example of how to add lines and curves to a path. | |||
| @see startNewSubPath, lineTo, quadraticTo, closeSubPath | |||
| */ | |||
| void cubicTo (const Point<float>& controlPoint1, | |||
| const Point<float>& controlPoint2, | |||
| const Point<float>& endPoint); | |||
| /** Returns the last point that was added to the path by one of the drawing methods. | |||
| */ | |||
| const Point<float> getCurrentPosition() const; | |||
| @@ -21294,26 +21372,29 @@ public: | |||
| @see addArrow | |||
| */ | |||
| void addLineSegment (float startX, float startY, | |||
| float endX, float endY, | |||
| float lineThickness); | |||
| void addLineSegment (const Line<float>& line, float lineThickness); | |||
| /** Adds a line with an arrowhead on the end. | |||
| The arrow is added as a new closed sub-path. (Any currently open paths will be | |||
| left open). | |||
| The arrow is added as a new closed sub-path. (Any currently open paths will be left open). | |||
| @see PathStrokeType::createStrokeWithArrowheads | |||
| */ | |||
| void addArrow (float startX, float startY, | |||
| float endX, float endY, | |||
| void addArrow (const Line<float>& line, | |||
| float lineThickness, | |||
| float arrowheadWidth, | |||
| float arrowheadLength); | |||
| /** Adds a star shape to the path. | |||
| /** Adds a polygon shape to the path. | |||
| @see addStar | |||
| */ | |||
| void addPolygon (const Point<float>& centre, | |||
| int numberOfSides, | |||
| float radius, | |||
| float startAngle = 0.0f); | |||
| /** Adds a star shape to the path. | |||
| @see addPolygon | |||
| */ | |||
| void addStar (float centreX, | |||
| float centreY, | |||
| void addStar (const Point<float>& centre, | |||
| int numberOfPoints, | |||
| float innerRadius, | |||
| float outerRadius, | |||
| @@ -22184,6 +22265,28 @@ public: | |||
| const AffineTransform& transform = AffineTransform::identity, | |||
| float extraAccuracy = 1.0f) const; | |||
| /** Applies this stroke type to a path and returns the resultant stroke as another Path. | |||
| @param destPath the resultant stroked outline shape will be copied into this path. | |||
| Note that it's ok for the source and destination Paths to be | |||
| the same object, so you can easily turn a path into a stroked version | |||
| of itself. | |||
| @param sourcePath the path to use as the source | |||
| @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 | |||
| @see createDashedStroke | |||
| */ | |||
| void createStrokeWithArrowheads (Path& destPath, | |||
| const Path& sourcePath, | |||
| float arrowheadStartWidth, float arrowheadStartLength, | |||
| float arrowheadEndWidth, float arrowheadEndLength, | |||
| const AffineTransform& transform = AffineTransform::identity, | |||
| float extraAccuracy = 1.0f) const; | |||
| /** Returns the stroke thickness. */ | |||
| float getStrokeThickness() const throw() { return thickness; } | |||
| @@ -23822,18 +23925,14 @@ public: | |||
| const PathStrokeType& strokeType, | |||
| const AffineTransform& transform = AffineTransform::identity) const; | |||
| /** Draws a line with an arrowhead. | |||
| /** Draws a line with an arrowhead at its end. | |||
| @param startX the line's start x co-ordinate | |||
| @param startY the line's start y co-ordinate | |||
| @param endX the line's end x co-ordinate (the tip of the arrowhead) | |||
| @param endY the line's end y co-ordinate (the tip of the arrowhead) | |||
| @param line the line to draw | |||
| @param lineThickness the thickness of the line | |||
| @param arrowheadWidth the width of the arrow head (perpendicular to the line) | |||
| @param arrowheadLength the length of the arrow head (along the length of the line) | |||
| */ | |||
| void drawArrow (float startX, float startY, | |||
| float endX, float endY, | |||
| void drawArrow (const Line<float>& line, | |||
| float lineThickness, | |||
| float arrowheadWidth, | |||
| float arrowheadLength) const; | |||
| @@ -42521,6 +42620,10 @@ public: | |||
| /** Creates an absolute rectangle, relative to the origin. */ | |||
| explicit RelativeRectangle (const Rectangle<float>& rect, const String& componentName); | |||
| /** Creates a rectangle from four coordinates. */ | |||
| RelativeRectangle (const RelativeCoordinate& left, const RelativeCoordinate& right, | |||
| const RelativeCoordinate& top, const RelativeCoordinate& bottom); | |||
| /** Creates a rectangle from a stringified representation. | |||
| The string must contain a sequence of 4 coordinates, separated by commas, in the order | |||
| left, top, right, bottom. The syntax for the coordinate strings is explained in the | |||
| @@ -58409,38 +58512,38 @@ public: | |||
| */ | |||
| void bringToFront (int index); | |||
| /** Sets the transform to be applied to this drawable, by defining the positions | |||
| where three anchor points should end up in the target rendering space. | |||
| /** Changes the main content area. | |||
| The content area is actually defined by the markers named "left", "right", "top" and | |||
| "bottom", but this method is a shortcut that sets them all at once. | |||
| @see contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName | |||
| */ | |||
| const RelativeRectangle getContentArea() const; | |||
| @param targetPositionForOrigin the position that the local coordinate (0, 0) should be | |||
| mapped onto when rendering this object. | |||
| @param targetPositionForX1Y0 the position that the local coordinate (1, 0) should be | |||
| mapped onto when rendering this object. | |||
| @param targetPositionForX0Y1 the position that the local coordinate (0, 1) should be | |||
| mapped onto when rendering this object. | |||
| /** Returns the main content rectangle. | |||
| The content area is actually defined by the markers named "left", "right", "top" and | |||
| "bottom", but this method is a shortcut that returns them all at once. | |||
| @see setBoundingBox, contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName | |||
| */ | |||
| void setTransform (const RelativePoint& targetPositionForOrigin, | |||
| const RelativePoint& targetPositionForX1Y0, | |||
| const RelativePoint& targetPositionForX0Y1); | |||
| void setContentArea (const RelativeRectangle& newArea); | |||
| /** Returns the position to which the local coordinate (0, 0) should be remapped in the target | |||
| coordinate space when rendering this object. | |||
| @see setTransform | |||
| /** Sets the parallelogram that defines the target position of the content rectangle when the drawable is rendered. | |||
| @see setContentArea | |||
| */ | |||
| const RelativePoint& getTargetPositionForOrigin() const throw() { return controlPoints[0]; } | |||
| void setBoundingBox (const RelativeParallelogram& newBoundingBox); | |||
| /** Returns the position to which the local coordinate (1, 0) should be remapped in the target | |||
| coordinate space when rendering this object. | |||
| @see setTransform | |||
| /** Returns the parallelogram that defines the target position of the content rectangle when the drawable is rendered. | |||
| @see setBoundingBox | |||
| */ | |||
| const RelativePoint& getTargetPositionForX1Y0() const throw() { return controlPoints[1]; } | |||
| const RelativeParallelogram& getBoundingBox() const throw() { return bounds; } | |||
| /** Returns the position to which the local coordinate (0, 1) should be remapped in the target | |||
| coordinate space when rendering this object. | |||
| @see setTransform | |||
| /** Changes the bounding box transform to match the content area, so that any sub-items will | |||
| be drawn at their untransformed positions. | |||
| */ | |||
| const RelativePoint& getTargetPositionForX0Y1() const throw() { return controlPoints[2]; } | |||
| void resetBoundingBoxToContentArea(); | |||
| /** Represents a named marker position. | |||
| @see DrawableComposite::getMarker | |||
| */ | |||
| struct Marker | |||
| { | |||
| Marker (const Marker&); | |||
| @@ -58456,6 +58559,15 @@ public: | |||
| void setMarker (const String& name, bool xAxis, const RelativeCoordinate& position); | |||
| void removeMarker (bool xAxis, int index); | |||
| /** The name of the marker that defines the left edge of the content area. */ | |||
| static const char* const contentLeftMarkerName; | |||
| /** The name of the marker that defines the right edge of the content area. */ | |||
| static const char* const contentRightMarkerName; | |||
| /** The name of the marker that defines the top edge of the content area. */ | |||
| static const char* const contentTopMarkerName; | |||
| /** The name of the marker that defines the bottom edge of the content area. */ | |||
| static const char* const contentBottomMarkerName; | |||
| /** @internal */ | |||
| void render (const Drawable::RenderingContext& context) const; | |||
| /** @internal */ | |||
| @@ -58491,14 +58603,12 @@ public: | |||
| void moveDrawableOrder (int currentIndex, int newIndex, UndoManager* undoManager); | |||
| void removeDrawable (const ValueTree& child, UndoManager* undoManager); | |||
| const RelativePoint getTargetPositionForOrigin() const; | |||
| void setTargetPositionForOrigin (const RelativePoint& newPoint, UndoManager* undoManager); | |||
| const RelativePoint getTargetPositionForX1Y0() const; | |||
| void setTargetPositionForX1Y0 (const RelativePoint& newPoint, UndoManager* undoManager); | |||
| const RelativeParallelogram getBoundingBox() const; | |||
| void setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager); | |||
| void resetBoundingBoxToContentArea (UndoManager* undoManager); | |||
| const RelativePoint getTargetPositionForX0Y1() const; | |||
| void setTargetPositionForX0Y1 (const RelativePoint& newPoint, UndoManager* undoManager); | |||
| const RelativeRectangle getContentArea() const; | |||
| void setContentArea (const RelativeRectangle& newArea, UndoManager* undoManager); | |||
| int getNumMarkers (bool xAxis) const; | |||
| const ValueTree getMarkerState (bool xAxis, int index) const; | |||
| @@ -58524,7 +58634,7 @@ public: | |||
| private: | |||
| OwnedArray <Drawable> drawables; | |||
| RelativePoint controlPoints[3]; | |||
| RelativeParallelogram bounds; | |||
| OwnedArray <Marker> markersX, markersY; | |||
| const Rectangle<float> getUntransformedBounds() const; | |||
| @@ -387,12 +387,13 @@ bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, | |||
| int64 numSamplesToRead) | |||
| { | |||
| const int bufferSize = 16384; | |||
| const int maxChans = 128; | |||
| AudioSampleBuffer tempBuffer (reader.numChannels, bufferSize); | |||
| int* buffers [maxChans]; | |||
| AudioSampleBuffer tempBuffer (numChannels, bufferSize); | |||
| for (int i = maxChans; --i >= 0;) | |||
| buffers[i] = 0; | |||
| int* buffers [128]; | |||
| zerostruct (buffers); | |||
| for (int i = tempBuffer.getNumChannels(); --i >= 0;) | |||
| buffers[i] = (int*) tempBuffer.getSampleData (i, 0); | |||
| if (numSamplesToRead < 0) | |||
| numSamplesToRead = reader.lengthInSamples; | |||
| @@ -401,10 +402,7 @@ bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, | |||
| { | |||
| const int numToDo = (int) jmin (numSamplesToRead, (int64) bufferSize); | |||
| for (int i = tempBuffer.getNumChannels(); --i >= 0;) | |||
| buffers[i] = (int*) tempBuffer.getSampleData (i, 0); | |||
| if (! reader.read (buffers, maxChans, startSample, numToDo, false)) | |||
| if (! reader.read (buffers, numChannels, startSample, numToDo, false)) | |||
| return false; | |||
| if (reader.usesFloatingPointData != isFloatingPoint()) | |||
| @@ -455,9 +453,12 @@ bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, | |||
| int numSamplesToRead, | |||
| const int samplesPerBlock) | |||
| { | |||
| const int maxChans = 128; | |||
| AudioSampleBuffer tempBuffer (getNumChannels(), samplesPerBlock); | |||
| int* buffers [maxChans]; | |||
| int* buffers [128]; | |||
| zerostruct (buffers); | |||
| for (int i = tempBuffer.getNumChannels(); --i >= 0;) | |||
| buffers[i] = (int*) tempBuffer.getSampleData (i, 0); | |||
| while (numSamplesToRead > 0) | |||
| { | |||
| @@ -471,13 +472,6 @@ bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, | |||
| source.getNextAudioBlock (info); | |||
| int i; | |||
| for (i = maxChans; --i >= 0;) | |||
| buffers[i] = 0; | |||
| for (i = tempBuffer.getNumChannels(); --i >= 0;) | |||
| buffers[i] = (int*) tempBuffer.getSampleData (i, 0); | |||
| if (! isFloatingPoint()) | |||
| { | |||
| int** bufferChan = buffers; | |||
| @@ -33,7 +33,7 @@ | |||
| */ | |||
| #define JUCE_MAJOR_VERSION 1 | |||
| #define JUCE_MINOR_VERSION 52 | |||
| #define JUCE_BUILDNUMBER 11 | |||
| #define JUCE_BUILDNUMBER 12 | |||
| /** Current Juce version number. | |||
| @@ -154,8 +154,8 @@ public: | |||
| } | |||
| Path p; | |||
| p.addArrow (x1, y1, x2, y2, 1.5f, hw, hl); | |||
| p.addArrow (x3, y3, x4, y4, 1.5f, hw, hl); | |||
| p.addArrow (Line<float> (x1, y1, x2, y2), 1.5f, hw, hl); | |||
| p.addArrow (Line<float> (x3, y3, x4, y4), 1.5f, hw, hl); | |||
| g.fillPath (p); | |||
| } | |||
| } | |||
| @@ -58,7 +58,7 @@ FileSearchPathListComponent::FileSearchPathListComponent() | |||
| { | |||
| Path arrowPath; | |||
| arrowPath.addArrow (50.0f, 100.0f, 50.0f, 0.0f, 40.0f, 100.0f, 50.0f); | |||
| arrowPath.addArrow (Line<float> (50.0f, 100.0f, 50.0f, 0.0f), 40.0f, 100.0f, 50.0f); | |||
| DrawablePath arrowImage; | |||
| arrowImage.setFill (Colours::black.withAlpha (0.4f)); | |||
| arrowImage.setPath (arrowPath); | |||
| @@ -71,7 +71,7 @@ FileSearchPathListComponent::FileSearchPathListComponent() | |||
| { | |||
| Path arrowPath; | |||
| arrowPath.addArrow (50.0f, 0.0f, 50.0f, 100.0f, 40.0f, 100.0f, 50.0f); | |||
| arrowPath.addArrow (Line<float> (50.0f, 0.0f, 50.0f, 100.0f), 40.0f, 100.0f, 50.0f); | |||
| DrawablePath arrowImage; | |||
| arrowImage.setFill (Colours::black.withAlpha (0.4f)); | |||
| arrowImage.setPath (arrowPath); | |||
| @@ -1577,7 +1577,7 @@ void LookAndFeel::drawRotarySlider (Graphics& g, | |||
| p.addEllipse (-0.4f * rw, -0.4f * rw, rw * 0.8f, rw * 0.8f); | |||
| PathStrokeType (rw * 0.1f).createStrokedPath (p, p); | |||
| p.addLineSegment (0.0f, 0.0f, 0.0f, -radius, rw * 0.2f); | |||
| p.addLineSegment (Line<float> (0.0f, 0.0f, 0.0f, -radius), rw * 0.2f); | |||
| g.fillPath (p, AffineTransform::rotation (angle).translated (centreX, centreY)); | |||
| } | |||
| @@ -1899,21 +1899,21 @@ Button* LookAndFeel::createDocumentWindowButton (int buttonType) | |||
| if (buttonType == DocumentWindow::closeButton) | |||
| { | |||
| shape.addLineSegment (0.0f, 0.0f, 1.0f, 1.0f, crossThickness * 1.4f); | |||
| shape.addLineSegment (1.0f, 0.0f, 0.0f, 1.0f, crossThickness * 1.4f); | |||
| shape.addLineSegment (Line<float> (0.0f, 0.0f, 1.0f, 1.0f), crossThickness * 1.4f); | |||
| shape.addLineSegment (Line<float> (1.0f, 0.0f, 0.0f, 1.0f), crossThickness * 1.4f); | |||
| return new GlassWindowButton ("close", Colour (0xffdd1100), shape, shape); | |||
| } | |||
| else if (buttonType == DocumentWindow::minimiseButton) | |||
| { | |||
| shape.addLineSegment (0.0f, 0.5f, 1.0f, 0.5f, crossThickness); | |||
| shape.addLineSegment (Line<float> (0.0f, 0.5f, 1.0f, 0.5f), crossThickness); | |||
| return new GlassWindowButton ("minimise", Colour (0xffaa8811), shape, shape); | |||
| } | |||
| else if (buttonType == DocumentWindow::maximiseButton) | |||
| { | |||
| shape.addLineSegment (0.5f, 0.0f, 0.5f, 1.0f, crossThickness); | |||
| shape.addLineSegment (0.0f, 0.5f, 1.0f, 0.5f, crossThickness); | |||
| shape.addLineSegment (Line<float> (0.5f, 0.0f, 0.5f, 1.0f), crossThickness); | |||
| shape.addLineSegment (Line<float> (0.0f, 0.5f, 1.0f, 0.5f), crossThickness); | |||
| Path fullscreenShape; | |||
| fullscreenShape.startNewSubPath (45.0f, 100.0f); | |||
| @@ -2613,7 +2613,7 @@ Button* LookAndFeel::createFileBrowserGoUpButton() | |||
| DrawableButton* goUpButton = new DrawableButton ("up", DrawableButton::ImageOnButtonBackground); | |||
| Path arrowPath; | |||
| arrowPath.addArrow (50.0f, 100.0f, 50.0f, 0.0f, 40.0f, 100.0f, 50.0f); | |||
| arrowPath.addArrow (Line<float> (50.0f, 100.0f, 50.0f, 0.0f), 40.0f, 100.0f, 50.0f); | |||
| DrawablePath arrowImage; | |||
| arrowImage.setFill (Colours::black.withAlpha (0.4f)); | |||
| @@ -538,8 +538,8 @@ Button* OldSchoolLookAndFeel::createDocumentWindowButton (int buttonType) | |||
| if (buttonType == DocumentWindow::closeButton) | |||
| { | |||
| shape.addLineSegment (0.0f, 0.0f, 1.0f, 1.0f, 0.35f); | |||
| shape.addLineSegment (1.0f, 0.0f, 0.0f, 1.0f, 0.35f); | |||
| shape.addLineSegment (Line<float> (0.0f, 0.0f, 1.0f, 1.0f), 0.35f); | |||
| shape.addLineSegment (Line<float> (1.0f, 0.0f, 0.0f, 1.0f), 0.35f); | |||
| ShapeButton* const b = new ShapeButton ("close", | |||
| Colour (0x7fff3333), | |||
| @@ -551,7 +551,7 @@ Button* OldSchoolLookAndFeel::createDocumentWindowButton (int buttonType) | |||
| } | |||
| else if (buttonType == DocumentWindow::minimiseButton) | |||
| { | |||
| shape.addLineSegment (0.0f, 0.5f, 1.0f, 0.5f, 0.25f); | |||
| shape.addLineSegment (Line<float> (0.0f, 0.5f, 1.0f, 0.5f), 0.25f); | |||
| DrawableButton* b = new DrawableButton ("minimise", DrawableButton::ImageFitted); | |||
| DrawablePath dp; | |||
| @@ -562,8 +562,8 @@ Button* OldSchoolLookAndFeel::createDocumentWindowButton (int buttonType) | |||
| } | |||
| else if (buttonType == DocumentWindow::maximiseButton) | |||
| { | |||
| shape.addLineSegment (0.5f, 0.0f, 0.5f, 1.0f, 0.25f); | |||
| shape.addLineSegment (0.0f, 0.5f, 1.0f, 0.5f, 0.25f); | |||
| shape.addLineSegment (Line<float> (0.5f, 0.0f, 0.5f, 1.0f), 0.25f); | |||
| shape.addLineSegment (Line<float> (0.0f, 0.5f, 1.0f, 0.5f), 0.25f); | |||
| DrawableButton* b = new DrawableButton ("maximise", DrawableButton::ImageFitted); | |||
| DrawablePath dp; | |||
| @@ -27,8 +27,8 @@ | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "juce_DialogWindow.h" | |||
| #include "../../../application/juce_Application.h" | |||
| //============================================================================== | |||
| @@ -68,6 +68,8 @@ public: | |||
| TempDialogWindow (const String& title, const Colour& colour, const bool escapeCloses) | |||
| : DialogWindow (title, colour, escapeCloses, true) | |||
| { | |||
| if (JUCEApplication::getInstance() == 0) | |||
| setAlwaysOnTop (true); // for a plugin, make it always-on-top because the host windows are often top-level | |||
| } | |||
| ~TempDialogWindow() | |||
| @@ -473,12 +473,10 @@ void Graphics::drawRoundedRectangle (const Rectangle<float>& r, const float corn | |||
| drawRoundedRectangle (r.getX(), r.getY(), r.getWidth(), r.getHeight(), cornerSize, lineThickness); | |||
| } | |||
| void Graphics::drawArrow (const float startX, const float startY, const float endX, const float endY, | |||
| const float lineThickness, const float arrowheadWidth, const float arrowheadLength) const | |||
| void Graphics::drawArrow (const Line<float>& line, const float lineThickness, const float arrowheadWidth, const float arrowheadLength) const | |||
| { | |||
| Path p; | |||
| p.addArrow (startX, startY, endX, endY, | |||
| lineThickness, arrowheadWidth, arrowheadLength); | |||
| p.addArrow (line, lineThickness, arrowheadWidth, arrowheadLength); | |||
| fillPath (p); | |||
| } | |||
| @@ -545,9 +543,7 @@ void Graphics::drawLine (const float startX, const float startY, | |||
| const float endX, const float endY, | |||
| const float lineThickness) const | |||
| { | |||
| Path p; | |||
| p.addLineSegment (startX, startY, endX, endY, lineThickness); | |||
| fillPath (p); | |||
| drawLine (Line<float> (startX, startY, endX, endY),lineThickness); | |||
| } | |||
| void Graphics::drawLine (const Line<float>& line) const | |||
| @@ -557,7 +553,9 @@ void Graphics::drawLine (const Line<float>& line) const | |||
| void Graphics::drawLine (const Line<float>& line, const float lineThickness) const | |||
| { | |||
| drawLine (line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY(), lineThickness); | |||
| Path p; | |||
| p.addLineSegment (line, lineThickness); | |||
| fillPath (p); | |||
| } | |||
| void Graphics::drawDashedLine (const float startX, const float startY, | |||
| @@ -415,18 +415,14 @@ public: | |||
| const PathStrokeType& strokeType, | |||
| const AffineTransform& transform = AffineTransform::identity) const; | |||
| /** Draws a line with an arrowhead. | |||
| /** Draws a line with an arrowhead at its end. | |||
| @param startX the line's start x co-ordinate | |||
| @param startY the line's start y co-ordinate | |||
| @param endX the line's end x co-ordinate (the tip of the arrowhead) | |||
| @param endY the line's end y co-ordinate (the tip of the arrowhead) | |||
| @param line the line to draw | |||
| @param lineThickness the thickness of the line | |||
| @param arrowheadWidth the width of the arrow head (perpendicular to the line) | |||
| @param arrowheadLength the length of the arrow head (along the length of the line) | |||
| */ | |||
| void drawArrow (float startX, float startY, | |||
| float endX, float endY, | |||
| void drawArrow (const Line<float>& line, | |||
| float lineThickness, | |||
| float arrowheadWidth, | |||
| float arrowheadLength) const; | |||
| @@ -502,7 +502,7 @@ void LowLevelGraphicsPostScriptRenderer::drawImage (const Image& sourceImage, co | |||
| void LowLevelGraphicsPostScriptRenderer::drawLine (const Line <float>& line) | |||
| { | |||
| Path p; | |||
| p.addLineSegment (line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY(), 1.0f); | |||
| p.addLineSegment (line, 1.0f); | |||
| fillPath (p, AffineTransform::identity); | |||
| } | |||
| @@ -2140,7 +2140,7 @@ void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, cons | |||
| void LowLevelGraphicsSoftwareRenderer::drawLine (const Line <float>& line) | |||
| { | |||
| Path p; | |||
| p.addLineSegment (line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY(), 1.0f); | |||
| p.addLineSegment (line, 1.0f); | |||
| fillPath (p, AffineTransform::identity); | |||
| } | |||
| @@ -36,18 +36,19 @@ BEGIN_JUCE_NAMESPACE | |||
| //============================================================================== | |||
| DrawableComposite::DrawableComposite() | |||
| : bounds (Point<float>(), Point<float> (100.0f, 0.0f), Point<float> (0.0f, 100.0f)) | |||
| { | |||
| controlPoints[1] = RelativePoint (Point<float> (1.0f, 0.0f)); | |||
| controlPoints[2] = RelativePoint (Point<float> (0.0f, 1.0f)); | |||
| setContentArea (RelativeRectangle (RelativeCoordinate (0.0, true), | |||
| RelativeCoordinate (100.0, true), | |||
| RelativeCoordinate (0.0, false), | |||
| RelativeCoordinate (100.0, false))); | |||
| } | |||
| DrawableComposite::DrawableComposite (const DrawableComposite& other) | |||
| { | |||
| int i; | |||
| for (i = 0; i < 3; ++i) | |||
| controlPoints[i] = other.controlPoints[i]; | |||
| bounds = other.bounds; | |||
| for (i = 0; i < other.drawables.size(); ++i) | |||
| for (int i = 0; i < other.drawables.size(); ++i) | |||
| drawables.add (other.drawables.getUnchecked(i)->createCopy()); | |||
| markersX.addCopiesOf (other.markersX); | |||
| @@ -95,13 +96,9 @@ void DrawableComposite::bringToFront (const int index) | |||
| drawables.move (index, -1); | |||
| } | |||
| void DrawableComposite::setTransform (const RelativePoint& targetPositionForOrigin, | |||
| const RelativePoint& targetPositionForX1Y0, | |||
| const RelativePoint& targetPositionForX0Y1) | |||
| void DrawableComposite::setBoundingBox (const RelativeParallelogram& newBoundingBox) | |||
| { | |||
| controlPoints[0] = targetPositionForOrigin; | |||
| controlPoints[1] = targetPositionForX1Y0; | |||
| controlPoints[2] = targetPositionForX0Y1; | |||
| bounds = newBoundingBox; | |||
| } | |||
| //============================================================================== | |||
| @@ -121,6 +118,37 @@ bool DrawableComposite::Marker::operator!= (const DrawableComposite::Marker& oth | |||
| } | |||
| //============================================================================== | |||
| const char* const DrawableComposite::contentLeftMarkerName ("left"); | |||
| const char* const DrawableComposite::contentRightMarkerName ("right"); | |||
| const char* const DrawableComposite::contentTopMarkerName ("top"); | |||
| const char* const DrawableComposite::contentBottomMarkerName ("bottom"); | |||
| const RelativeRectangle DrawableComposite::getContentArea() const | |||
| { | |||
| jassert (markersX.size() >= 2 && getMarker (true, 0)->name == contentLeftMarkerName && getMarker (true, 1)->name == contentRightMarkerName); | |||
| jassert (markersY.size() >= 2 && getMarker (false, 0)->name == contentTopMarkerName && getMarker (false, 1)->name == contentBottomMarkerName); | |||
| return RelativeRectangle (markersX.getUnchecked(0)->position, markersX.getUnchecked(1)->position, | |||
| markersY.getUnchecked(0)->position, markersY.getUnchecked(1)->position); | |||
| } | |||
| void DrawableComposite::setContentArea (const RelativeRectangle& newArea) | |||
| { | |||
| setMarker (contentLeftMarkerName, true, newArea.left); | |||
| setMarker (contentRightMarkerName, true, newArea.right); | |||
| setMarker (contentTopMarkerName, false, newArea.top); | |||
| setMarker (contentBottomMarkerName, false, newArea.bottom); | |||
| } | |||
| void DrawableComposite::resetBoundingBoxToContentArea() | |||
| { | |||
| const RelativeRectangle content (getContentArea()); | |||
| setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top), | |||
| RelativePoint (content.right, content.top), | |||
| RelativePoint (content.left, content.bottom))); | |||
| } | |||
| int DrawableComposite::getNumMarkers (const bool xAxis) const throw() | |||
| { | |||
| return (xAxis ? markersX : markersY).size(); | |||
| @@ -156,19 +184,23 @@ void DrawableComposite::setMarker (const String& name, const bool xAxis, const R | |||
| void DrawableComposite::removeMarker (const bool xAxis, const int index) | |||
| { | |||
| (xAxis ? markersX : markersY).remove (index); | |||
| jassert (index >= 2); | |||
| if (index >= 2) | |||
| (xAxis ? markersX : markersY).remove (index); | |||
| } | |||
| //============================================================================== | |||
| const AffineTransform DrawableComposite::calculateTransform() const | |||
| { | |||
| Point<float> resolved[3]; | |||
| for (int i = 0; i < 3; ++i) | |||
| resolved[i] = controlPoints[i].resolve (parent); | |||
| bounds.resolveThreePoints (resolved, parent); | |||
| const Rectangle<float> content (getContentArea().resolve (parent)); | |||
| return AffineTransform::fromTargetPoints (resolved[0].getX(), resolved[0].getY(), | |||
| resolved[1].getX(), resolved[1].getY(), | |||
| resolved[2].getX(), resolved[2].getY()); | |||
| return AffineTransform::fromTargetPoints (content.getX(), content.getY(), resolved[0].getX(), resolved[0].getY(), | |||
| content.getRight(), content.getY(), resolved[1].getX(), resolved[1].getY(), | |||
| content.getX(), content.getBottom(), resolved[2].getX(), resolved[2].getY()); | |||
| } | |||
| void DrawableComposite::render (const Drawable::RenderingContext& context) const | |||
| @@ -426,37 +458,43 @@ void DrawableComposite::ValueTreeWrapper::removeDrawable (const ValueTree& child | |||
| getChildList().removeChild (child, undoManager); | |||
| } | |||
| const RelativePoint DrawableComposite::ValueTreeWrapper::getTargetPositionForOrigin() const | |||
| const RelativeParallelogram DrawableComposite::ValueTreeWrapper::getBoundingBox() const | |||
| { | |||
| const String pos (state [topLeft].toString()); | |||
| return pos.isNotEmpty() ? RelativePoint (pos) : RelativePoint (); | |||
| return RelativeParallelogram (state.getProperty (topLeft, "0, 0"), | |||
| state.getProperty (topRight, "100, 0"), | |||
| state.getProperty (bottomLeft, "0, 100")); | |||
| } | |||
| void DrawableComposite::ValueTreeWrapper::setTargetPositionForOrigin (const RelativePoint& newPoint, UndoManager* undoManager) | |||
| void DrawableComposite::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager) | |||
| { | |||
| state.setProperty (topLeft, newPoint.toString(), undoManager); | |||
| state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager); | |||
| state.setProperty (topRight, newBounds.topRight.toString(), undoManager); | |||
| state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager); | |||
| } | |||
| const RelativePoint DrawableComposite::ValueTreeWrapper::getTargetPositionForX1Y0() const | |||
| void DrawableComposite::ValueTreeWrapper::resetBoundingBoxToContentArea (UndoManager* undoManager) | |||
| { | |||
| const String pos (state [topRight].toString()); | |||
| return pos.isNotEmpty() ? RelativePoint (pos) : RelativePoint (Point<float> (1.0f, 0.0f)); | |||
| } | |||
| const RelativeRectangle content (getContentArea()); | |||
| void DrawableComposite::ValueTreeWrapper::setTargetPositionForX1Y0 (const RelativePoint& newPoint, UndoManager* undoManager) | |||
| { | |||
| state.setProperty (topRight, newPoint.toString(), undoManager); | |||
| setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top), | |||
| RelativePoint (content.right, content.top), | |||
| RelativePoint (content.left, content.bottom)), undoManager); | |||
| } | |||
| const RelativePoint DrawableComposite::ValueTreeWrapper::getTargetPositionForX0Y1() const | |||
| const RelativeRectangle DrawableComposite::ValueTreeWrapper::getContentArea() const | |||
| { | |||
| const String pos (state [bottomLeft].toString()); | |||
| return pos.isNotEmpty() ? RelativePoint (pos) : RelativePoint (Point<float> (0.0f, 1.0f)); | |||
| return RelativeRectangle (getMarker (true, getMarkerState (true, 0)).position, | |||
| getMarker (true, getMarkerState (true, 1)).position, | |||
| getMarker (false, getMarkerState (false, 0)).position, | |||
| getMarker (false, getMarkerState (false, 1)).position); | |||
| } | |||
| void DrawableComposite::ValueTreeWrapper::setTargetPositionForX0Y1 (const RelativePoint& newPoint, UndoManager* undoManager) | |||
| void DrawableComposite::ValueTreeWrapper::setContentArea (const RelativeRectangle& newArea, UndoManager* undoManager) | |||
| { | |||
| state.setProperty (bottomLeft, newPoint.toString(), undoManager); | |||
| setMarker (true, Marker (contentLeftMarkerName, newArea.left), undoManager); | |||
| setMarker (true, Marker (contentRightMarkerName, newArea.right), undoManager); | |||
| setMarker (false, Marker (contentTopMarkerName, newArea.top), undoManager); | |||
| setMarker (false, Marker (contentBottomMarkerName, newArea.bottom), undoManager); | |||
| } | |||
| ValueTree DrawableComposite::ValueTreeWrapper::getMarkerList (bool xAxis) const | |||
| @@ -516,58 +554,48 @@ void DrawableComposite::ValueTreeWrapper::setMarker (bool xAxis, const Marker& m | |||
| void DrawableComposite::ValueTreeWrapper::removeMarker (bool xAxis, const ValueTree& state, UndoManager* undoManager) | |||
| { | |||
| return getMarkerList (xAxis).removeChild (state, undoManager); | |||
| if (state [nameProperty].toString() != contentLeftMarkerName | |||
| && state [nameProperty].toString() != contentRightMarkerName | |||
| && state [nameProperty].toString() != contentTopMarkerName | |||
| && state [nameProperty].toString() != contentBottomMarkerName) | |||
| return getMarkerList (xAxis).removeChild (state, undoManager); | |||
| } | |||
| //============================================================================== | |||
| const Rectangle<float> DrawableComposite::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) | |||
| { | |||
| Rectangle<float> damageRect; | |||
| const ValueTreeWrapper controller (tree); | |||
| setName (controller.getID()); | |||
| RelativePoint newControlPoint[3] = { controller.getTargetPositionForOrigin(), | |||
| controller.getTargetPositionForX1Y0(), | |||
| controller.getTargetPositionForX0Y1() }; | |||
| const ValueTreeWrapper wrapper (tree); | |||
| setName (wrapper.getID()); | |||
| const RelativeParallelogram newBounds (wrapper.getBoundingBox()); | |||
| bool redrawAll = false; | |||
| if (controlPoints[0] != newControlPoint[0] | |||
| || controlPoints[1] != newControlPoint[1] | |||
| || controlPoints[2] != newControlPoint[2]) | |||
| if (bounds != newBounds) | |||
| { | |||
| redrawAll = true; | |||
| damageRect = getUntransformedBounds(); | |||
| controlPoints[0] = newControlPoint[0]; | |||
| controlPoints[1] = newControlPoint[1]; | |||
| controlPoints[2] = newControlPoint[2]; | |||
| bounds = newBounds; | |||
| } | |||
| const int numMarkersX = controller.getNumMarkers (true); | |||
| const int numMarkersY = controller.getNumMarkers (false); | |||
| const int numMarkersX = wrapper.getNumMarkers (true); | |||
| const int numMarkersY = wrapper.getNumMarkers (false); | |||
| // Remove deleted markers... | |||
| int i; | |||
| for (i = markersX.size(); --i >= numMarkersX;) | |||
| if (markersX.size() > numMarkersX || markersY.size() > numMarkersY) | |||
| { | |||
| if (damageRect.isEmpty()) | |||
| damageRect = getUntransformedBounds(); | |||
| markersX.remove (i); | |||
| } | |||
| for (i = markersY.size(); --i >= numMarkersY;) | |||
| { | |||
| if (damageRect.isEmpty()) | |||
| damageRect = getUntransformedBounds(); | |||
| markersY.remove (i); | |||
| markersX.removeRange (jmax (2, numMarkersX), markersX.size()); | |||
| markersY.removeRange (jmax (2, numMarkersY), markersY.size()); | |||
| } | |||
| // Update markers and add new ones.. | |||
| int i; | |||
| for (i = 0; i < numMarkersX; ++i) | |||
| { | |||
| const Marker newMarker (controller.getMarker (true, controller.getMarkerState (true, i))); | |||
| const Marker newMarker (wrapper.getMarker (true, wrapper.getMarkerState (true, i))); | |||
| Marker* m = markersX[i]; | |||
| if (m == 0 || newMarker != *m) | |||
| @@ -585,7 +613,7 @@ const Rectangle<float> DrawableComposite::refreshFromValueTree (const ValueTree& | |||
| for (i = 0; i < numMarkersY; ++i) | |||
| { | |||
| const Marker newMarker (controller.getMarker (false, controller.getMarkerState (false, i))); | |||
| const Marker newMarker (wrapper.getMarker (false, wrapper.getMarkerState (false, i))); | |||
| Marker* m = markersY[i]; | |||
| if (m == 0 || newMarker != *m) | |||
| @@ -602,7 +630,7 @@ const Rectangle<float> DrawableComposite::refreshFromValueTree (const ValueTree& | |||
| } | |||
| // Remove deleted drawables.. | |||
| for (i = drawables.size(); --i >= controller.getNumDrawables();) | |||
| for (i = drawables.size(); --i >= wrapper.getNumDrawables();) | |||
| { | |||
| Drawable* const d = drawables.getUnchecked(i); | |||
| damageRect = damageRect.getUnion (d->getBounds()); | |||
| @@ -611,9 +639,9 @@ const Rectangle<float> DrawableComposite::refreshFromValueTree (const ValueTree& | |||
| } | |||
| // Update drawables and add new ones.. | |||
| for (i = 0; i < controller.getNumDrawables(); ++i) | |||
| for (i = 0; i < wrapper.getNumDrawables(); ++i) | |||
| { | |||
| const ValueTree newDrawable (controller.getDrawableState (i)); | |||
| const ValueTree newDrawable (wrapper.getDrawableState (i)); | |||
| Drawable* d = drawables[i]; | |||
| if (d != 0) | |||
| @@ -650,9 +678,7 @@ const ValueTree DrawableComposite::createValueTree (ImageProvider* imageProvider | |||
| ValueTreeWrapper v (tree); | |||
| v.setID (getName(), 0); | |||
| v.setTargetPositionForOrigin (controlPoints[0], 0); | |||
| v.setTargetPositionForX1Y0 (controlPoints[1], 0); | |||
| v.setTargetPositionForX0Y1 (controlPoints[2], 0); | |||
| v.setBoundingBox (bounds, 0); | |||
| int i; | |||
| for (i = 0; i < drawables.size(); ++i) | |||
| @@ -118,39 +118,40 @@ public: | |||
| */ | |||
| void bringToFront (int index); | |||
| /** Sets the transform to be applied to this drawable, by defining the positions | |||
| where three anchor points should end up in the target rendering space. | |||
| @param targetPositionForOrigin the position that the local coordinate (0, 0) should be | |||
| mapped onto when rendering this object. | |||
| @param targetPositionForX1Y0 the position that the local coordinate (1, 0) should be | |||
| mapped onto when rendering this object. | |||
| @param targetPositionForX0Y1 the position that the local coordinate (0, 1) should be | |||
| mapped onto when rendering this object. | |||
| //============================================================================== | |||
| /** Changes the main content area. | |||
| The content area is actually defined by the markers named "left", "right", "top" and | |||
| "bottom", but this method is a shortcut that sets them all at once. | |||
| @see contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName | |||
| */ | |||
| const RelativeRectangle getContentArea() const; | |||
| /** Returns the main content rectangle. | |||
| The content area is actually defined by the markers named "left", "right", "top" and | |||
| "bottom", but this method is a shortcut that returns them all at once. | |||
| @see setBoundingBox, contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName | |||
| */ | |||
| void setTransform (const RelativePoint& targetPositionForOrigin, | |||
| const RelativePoint& targetPositionForX1Y0, | |||
| const RelativePoint& targetPositionForX0Y1); | |||
| void setContentArea (const RelativeRectangle& newArea); | |||
| /** Returns the position to which the local coordinate (0, 0) should be remapped in the target | |||
| coordinate space when rendering this object. | |||
| @see setTransform | |||
| /** Sets the parallelogram that defines the target position of the content rectangle when the drawable is rendered. | |||
| @see setContentArea | |||
| */ | |||
| const RelativePoint& getTargetPositionForOrigin() const throw() { return controlPoints[0]; } | |||
| void setBoundingBox (const RelativeParallelogram& newBoundingBox); | |||
| /** Returns the position to which the local coordinate (1, 0) should be remapped in the target | |||
| coordinate space when rendering this object. | |||
| @see setTransform | |||
| /** Returns the parallelogram that defines the target position of the content rectangle when the drawable is rendered. | |||
| @see setBoundingBox | |||
| */ | |||
| const RelativePoint& getTargetPositionForX1Y0() const throw() { return controlPoints[1]; } | |||
| const RelativeParallelogram& getBoundingBox() const throw() { return bounds; } | |||
| /** Returns the position to which the local coordinate (0, 1) should be remapped in the target | |||
| coordinate space when rendering this object. | |||
| @see setTransform | |||
| /** Changes the bounding box transform to match the content area, so that any sub-items will | |||
| be drawn at their untransformed positions. | |||
| */ | |||
| const RelativePoint& getTargetPositionForX0Y1() const throw() { return controlPoints[2]; } | |||
| void resetBoundingBoxToContentArea(); | |||
| //============================================================================== | |||
| /** Represents a named marker position. | |||
| @see DrawableComposite::getMarker | |||
| */ | |||
| struct Marker | |||
| { | |||
| Marker (const Marker&); | |||
| @@ -166,6 +167,15 @@ public: | |||
| void setMarker (const String& name, bool xAxis, const RelativeCoordinate& position); | |||
| void removeMarker (bool xAxis, int index); | |||
| /** The name of the marker that defines the left edge of the content area. */ | |||
| static const char* const contentLeftMarkerName; | |||
| /** The name of the marker that defines the right edge of the content area. */ | |||
| static const char* const contentRightMarkerName; | |||
| /** The name of the marker that defines the top edge of the content area. */ | |||
| static const char* const contentTopMarkerName; | |||
| /** The name of the marker that defines the bottom edge of the content area. */ | |||
| static const char* const contentBottomMarkerName; | |||
| //============================================================================== | |||
| /** @internal */ | |||
| void render (const Drawable::RenderingContext& context) const; | |||
| @@ -203,14 +213,12 @@ public: | |||
| void moveDrawableOrder (int currentIndex, int newIndex, UndoManager* undoManager); | |||
| void removeDrawable (const ValueTree& child, UndoManager* undoManager); | |||
| const RelativePoint getTargetPositionForOrigin() const; | |||
| void setTargetPositionForOrigin (const RelativePoint& newPoint, UndoManager* undoManager); | |||
| const RelativePoint getTargetPositionForX1Y0() const; | |||
| void setTargetPositionForX1Y0 (const RelativePoint& newPoint, UndoManager* undoManager); | |||
| const RelativeParallelogram getBoundingBox() const; | |||
| void setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager); | |||
| void resetBoundingBoxToContentArea (UndoManager* undoManager); | |||
| const RelativePoint getTargetPositionForX0Y1() const; | |||
| void setTargetPositionForX0Y1 (const RelativePoint& newPoint, UndoManager* undoManager); | |||
| const RelativeRectangle getContentArea() const; | |||
| void setContentArea (const RelativeRectangle& newArea, UndoManager* undoManager); | |||
| int getNumMarkers (bool xAxis) const; | |||
| const ValueTree getMarkerState (bool xAxis, int index) const; | |||
| @@ -237,7 +245,7 @@ public: | |||
| private: | |||
| OwnedArray <Drawable> drawables; | |||
| RelativePoint controlPoints[3]; | |||
| RelativeParallelogram bounds; | |||
| OwnedArray <Marker> markersX, markersY; | |||
| const Rectangle<float> getUntransformedBounds() const; | |||
| @@ -66,8 +66,8 @@ public: | |||
| newState.elementX = getCoordLength (xml.getStringAttribute ("x", String (newState.elementX)), viewBoxW); | |||
| newState.elementY = getCoordLength (xml.getStringAttribute ("y", String (newState.elementY)), viewBoxH); | |||
| newState.width = getCoordLength (xml.getStringAttribute ("width", String (newState.width)), viewBoxW); | |||
| newState.height = getCoordLength (xml.getStringAttribute ("height", String (newState.height)), viewBoxH); | |||
| newState.width = getCoordLength (xml.getStringAttribute ("width", String (newState.width)), viewBoxW); | |||
| newState.height = getCoordLength (xml.getStringAttribute ("height", String (newState.height)), viewBoxH); | |||
| if (xml.hasAttribute ("viewBox")) | |||
| { | |||
| @@ -130,6 +130,13 @@ public: | |||
| newState.parseSubElements (xml, drawable); | |||
| const Rectangle<float> bounds (drawable->getBounds()); | |||
| drawable->setContentArea (RelativeRectangle (RelativeCoordinate (bounds.getX(), true), | |||
| RelativeCoordinate (bounds.getRight(), true), | |||
| RelativeCoordinate (bounds.getY(), false), | |||
| RelativeCoordinate (bounds.getBottom(), false))); | |||
| drawable->resetBoundingBoxToContentArea(); | |||
| return drawable; | |||
| } | |||
| @@ -147,30 +154,18 @@ private: | |||
| { | |||
| Drawable* d = 0; | |||
| if (e->hasTagName ("g")) | |||
| d = parseGroupElement (*e); | |||
| else if (e->hasTagName ("svg")) | |||
| d = parseSVGElement (*e); | |||
| else if (e->hasTagName ("path")) | |||
| d = parsePath (*e); | |||
| else if (e->hasTagName ("rect")) | |||
| d = parseRect (*e); | |||
| else if (e->hasTagName ("circle")) | |||
| d = parseCircle (*e); | |||
| else if (e->hasTagName ("ellipse")) | |||
| d = parseEllipse (*e); | |||
| else if (e->hasTagName ("line")) | |||
| d = parseLine (*e); | |||
| else if (e->hasTagName ("polyline")) | |||
| d = parsePolygon (*e, true); | |||
| else if (e->hasTagName ("polygon")) | |||
| d = parsePolygon (*e, false); | |||
| else if (e->hasTagName ("text")) | |||
| d = parseText (*e); | |||
| else if (e->hasTagName ("switch")) | |||
| d = parseSwitch (*e); | |||
| else if (e->hasTagName ("style")) | |||
| parseCSSStyle (*e); | |||
| if (e->hasTagName ("g")) d = parseGroupElement (*e); | |||
| else if (e->hasTagName ("svg")) d = parseSVGElement (*e); | |||
| else if (e->hasTagName ("path")) d = parsePath (*e); | |||
| else if (e->hasTagName ("rect")) d = parseRect (*e); | |||
| else if (e->hasTagName ("circle")) d = parseCircle (*e); | |||
| else if (e->hasTagName ("ellipse")) d = parseEllipse (*e); | |||
| else if (e->hasTagName ("line")) d = parseLine (*e); | |||
| else if (e->hasTagName ("polyline")) d = parsePolygon (*e, true); | |||
| else if (e->hasTagName ("polygon")) d = parsePolygon (*e, false); | |||
| else if (e->hasTagName ("text")) d = parseText (*e); | |||
| else if (e->hasTagName ("switch")) d = parseSwitch (*e); | |||
| else if (e->hasTagName ("style")) parseCSSStyle (*e); | |||
| parentDrawable->insertDrawable (d); | |||
| } | |||
| @@ -544,8 +539,8 @@ private: | |||
| { | |||
| Path ellipse; | |||
| const float cx = getCoordLength (xml.getStringAttribute ("cx"), viewBoxW); | |||
| const float cy = getCoordLength (xml.getStringAttribute ("cy"), viewBoxH); | |||
| const float cx = getCoordLength (xml.getStringAttribute ("cx"), viewBoxW); | |||
| const float cy = getCoordLength (xml.getStringAttribute ("cy"), viewBoxH); | |||
| const float radiusX = getCoordLength (xml.getStringAttribute ("rx"), viewBoxW); | |||
| const float radiusY = getCoordLength (xml.getStringAttribute ("ry"), viewBoxH); | |||
| @@ -767,8 +767,8 @@ void GlyphArrangement::draw (const Graphics& g, const AffineTransform& transform | |||
| nextX = glyphs.getUnchecked (i + 1)->x; | |||
| Path p; | |||
| p.addLineSegment (pg->x, pg->y + lineThickness * 2.0f, | |||
| nextX, pg->y + lineThickness * 2.0f, | |||
| p.addLineSegment (Line<float> (pg->x, pg->y + lineThickness * 2.0f, | |||
| nextX, pg->y + lineThickness * 2.0f), | |||
| lineThickness); | |||
| g.fillPath (p, transform); | |||
| @@ -115,6 +115,9 @@ public: | |||
| /** Changes this line's end point */ | |||
| void setEnd (const Point<ValueType>& newEnd) throw() { end = newEnd; } | |||
| /** Returns a line that is the same as this one, but with the start and end reversed, */ | |||
| const Line reversed() const throw() { return Line (end, start); } | |||
| /** Applies an affine transform to the line's start and end points. */ | |||
| void applyTransform (const AffineTransform& transform) throw() | |||
| { | |||
| @@ -43,27 +43,6 @@ namespace PathHelpers | |||
| { | |||
| static const float ellipseAngularIncrement = 0.05f; | |||
| static void perpendicularOffset (const float x1, const float y1, | |||
| const float x2, const float y2, | |||
| const float offsetX, const float offsetY, | |||
| float& resultX, float& resultY) throw() | |||
| { | |||
| const float dx = x2 - x1; | |||
| const float dy = y2 - y1; | |||
| const float len = juce_hypotf (dx, dy); | |||
| if (len == 0) | |||
| { | |||
| resultX = x1; | |||
| resultY = y1; | |||
| } | |||
| else | |||
| { | |||
| resultX = x1 + ((dx * offsetX) - (dy * offsetY)) / len; | |||
| resultY = y1 + ((dy * offsetX) + (dx * offsetY)) / len; | |||
| } | |||
| } | |||
| static const String nextToken (const juce_wchar*& t) | |||
| { | |||
| while (CharacterFunctions::isWhitespace (*t)) | |||
| @@ -246,6 +225,11 @@ void Path::startNewSubPath (const float x, const float y) | |||
| data.elements [numElements++] = y; | |||
| } | |||
| void Path::startNewSubPath (const Point<float>& start) | |||
| { | |||
| startNewSubPath (start.getX(), start.getY()); | |||
| } | |||
| void Path::lineTo (const float x, const float y) | |||
| { | |||
| CHECK_COORDS_ARE_VALID (x, y); | |||
| @@ -265,6 +249,11 @@ void Path::lineTo (const float x, const float y) | |||
| pathYMax = jmax (pathYMax, y); | |||
| } | |||
| void Path::lineTo (const Point<float>& end) | |||
| { | |||
| lineTo (end.getX(), end.getY()); | |||
| } | |||
| void Path::quadraticTo (const float x1, const float y1, | |||
| const float x2, const float y2) | |||
| { | |||
| @@ -288,6 +277,13 @@ void Path::quadraticTo (const float x1, const float y1, | |||
| pathYMax = jmax (pathYMax, y1, y2); | |||
| } | |||
| void Path::quadraticTo (const Point<float>& controlPoint, | |||
| const Point<float>& endPoint) | |||
| { | |||
| quadraticTo (controlPoint.getX(), controlPoint.getY(), | |||
| endPoint.getX(), endPoint.getY()); | |||
| } | |||
| void Path::cubicTo (const float x1, const float y1, | |||
| const float x2, const float y2, | |||
| const float x3, const float y3) | |||
| @@ -315,6 +311,15 @@ void Path::cubicTo (const float x1, const float y1, | |||
| pathYMax = jmax (pathYMax, y1, y2, y3); | |||
| } | |||
| void Path::cubicTo (const Point<float>& controlPoint1, | |||
| const Point<float>& controlPoint2, | |||
| const Point<float>& endPoint) | |||
| { | |||
| cubicTo (controlPoint1.getX(), controlPoint1.getY(), | |||
| controlPoint2.getX(), controlPoint2.getY(), | |||
| endPoint.getX(), endPoint.getY()); | |||
| } | |||
| void Path::closeSubPath() | |||
| { | |||
| if (numElements > 0 | |||
| @@ -495,19 +500,12 @@ void Path::addCentredArc (const float centreX, const float centreY, | |||
| { | |||
| if (radiusX > 0.0f && radiusY > 0.0f) | |||
| { | |||
| const Point<float> centre (centreX, centreY); | |||
| const AffineTransform rotation (AffineTransform::rotation (rotationOfEllipse, centreX, centreY)); | |||
| float angle = fromRadians; | |||
| if (startAsNewSubPath) | |||
| { | |||
| float x = centreX + radiusX * std::sin (angle); | |||
| float y = centreY - radiusY * std::cos (angle); | |||
| if (rotationOfEllipse != 0) | |||
| rotation.transformPoint (x, y); | |||
| startNewSubPath (x, y); | |||
| } | |||
| startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation)); | |||
| if (fromRadians < toRadians) | |||
| { | |||
| @@ -516,14 +514,7 @@ void Path::addCentredArc (const float centreX, const float centreY, | |||
| while (angle < toRadians) | |||
| { | |||
| float x = centreX + radiusX * std::sin (angle); | |||
| float y = centreY - radiusY * std::cos (angle); | |||
| if (rotationOfEllipse != 0) | |||
| rotation.transformPoint (x, y); | |||
| lineTo (x, y); | |||
| lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation)); | |||
| angle += PathHelpers::ellipseAngularIncrement; | |||
| } | |||
| } | |||
| @@ -534,25 +525,12 @@ void Path::addCentredArc (const float centreX, const float centreY, | |||
| while (angle > toRadians) | |||
| { | |||
| float x = centreX + radiusX * std::sin (angle); | |||
| float y = centreY - radiusY * std::cos (angle); | |||
| if (rotationOfEllipse != 0) | |||
| rotation.transformPoint (x, y); | |||
| lineTo (x, y); | |||
| lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation)); | |||
| angle -= PathHelpers::ellipseAngularIncrement; | |||
| } | |||
| } | |||
| float x = centreX + radiusX * std::sin (toRadians); | |||
| float y = centreY - radiusY * std::cos (toRadians); | |||
| if (rotationOfEllipse != 0) | |||
| rotation.transformPoint (x, y); | |||
| lineTo (x, y); | |||
| lineTo (centre.getPointOnCircumference (radiusX, radiusY, toRadians).transformedBy (rotation)); | |||
| } | |||
| } | |||
| @@ -562,14 +540,11 @@ void Path::addPieSegment (const float x, const float y, | |||
| const float toRadians, | |||
| const float innerCircleProportionalSize) | |||
| { | |||
| float hw = width * 0.5f; | |||
| float hh = height * 0.5f; | |||
| const float centreX = x + hw; | |||
| const float centreY = y + hh; | |||
| startNewSubPath (centreX + hw * std::sin (fromRadians), | |||
| centreY - hh * std::cos (fromRadians)); | |||
| float radiusX = width * 0.5f; | |||
| float radiusY = height * 0.5f; | |||
| const Point<float> centre (x + radiusX, y + radiusY); | |||
| startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, fromRadians)); | |||
| addArc (x, y, width, height, fromRadians, toRadians); | |||
| if (std::abs (fromRadians - toRadians) > float_Pi * 1.999f) | |||
| @@ -578,29 +553,25 @@ void Path::addPieSegment (const float x, const float y, | |||
| if (innerCircleProportionalSize > 0) | |||
| { | |||
| hw *= innerCircleProportionalSize; | |||
| hh *= innerCircleProportionalSize; | |||
| radiusX *= innerCircleProportionalSize; | |||
| radiusY *= innerCircleProportionalSize; | |||
| startNewSubPath (centreX + hw * std::sin (toRadians), | |||
| centreY - hh * std::cos (toRadians)); | |||
| addArc (centreX - hw, centreY - hh, hw * 2.0f, hh * 2.0f, | |||
| toRadians, fromRadians); | |||
| startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, toRadians)); | |||
| addArc (centre.getX() - radiusX, centre.getY() - radiusY, radiusX * 2.0f, radiusY * 2.0f, toRadians, fromRadians); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| if (innerCircleProportionalSize > 0) | |||
| { | |||
| hw *= innerCircleProportionalSize; | |||
| hh *= innerCircleProportionalSize; | |||
| radiusX *= innerCircleProportionalSize; | |||
| radiusY *= innerCircleProportionalSize; | |||
| addArc (centreX - hw, centreY - hh, hw * 2.0f, hh * 2.0f, | |||
| toRadians, fromRadians); | |||
| addArc (centre.getX() - radiusX, centre.getY() - radiusY, radiusX * 2.0f, radiusY * 2.0f, toRadians, fromRadians); | |||
| } | |||
| else | |||
| { | |||
| lineTo (centreX, centreY); | |||
| lineTo (centre); | |||
| } | |||
| } | |||
| @@ -608,83 +579,62 @@ void Path::addPieSegment (const float x, const float y, | |||
| } | |||
| //============================================================================== | |||
| void Path::addLineSegment (const float startX, const float startY, | |||
| const float endX, const float endY, | |||
| float lineThickness) | |||
| void Path::addLineSegment (const Line<float>& line, float lineThickness) | |||
| { | |||
| const Line<float> reversed (line.reversed()); | |||
| lineThickness *= 0.5f; | |||
| float x, y; | |||
| PathHelpers::perpendicularOffset (startX, startY, endX, endY, | |||
| 0, lineThickness, x, y); | |||
| startNewSubPath (x, y); | |||
| PathHelpers::perpendicularOffset (startX, startY, endX, endY, | |||
| 0, -lineThickness, x, y); | |||
| lineTo (x, y); | |||
| PathHelpers::perpendicularOffset (endX, endY, startX, startY, | |||
| 0, lineThickness, x, y); | |||
| lineTo (x, y); | |||
| PathHelpers::perpendicularOffset (endX, endY, startX, startY, | |||
| 0, -lineThickness, x, y); | |||
| lineTo (x, y); | |||
| startNewSubPath (line.getPointAlongLine (0, lineThickness)); | |||
| lineTo (line.getPointAlongLine (0, -lineThickness)); | |||
| lineTo (reversed.getPointAlongLine (0, lineThickness)); | |||
| lineTo (reversed.getPointAlongLine (0, -lineThickness)); | |||
| closeSubPath(); | |||
| } | |||
| void Path::addArrow (const float startX, const float startY, | |||
| const float endX, const float endY, | |||
| float lineThickness, | |||
| float arrowheadWidth, | |||
| float arrowheadLength) | |||
| void Path::addArrow (const Line<float>& line, float lineThickness, | |||
| float arrowheadWidth, float arrowheadLength) | |||
| { | |||
| const Line<float> reversed (line.reversed()); | |||
| lineThickness *= 0.5f; | |||
| arrowheadWidth *= 0.5f; | |||
| arrowheadLength = jmin (arrowheadLength, 0.8f * juce_hypotf (startX - endX, | |||
| startY - endY)); | |||
| float x, y; | |||
| PathHelpers::perpendicularOffset (startX, startY, endX, endY, | |||
| 0, lineThickness, x, y); | |||
| startNewSubPath (x, y); | |||
| PathHelpers::perpendicularOffset (startX, startY, endX, endY, | |||
| 0, -lineThickness, x, y); | |||
| lineTo (x, y); | |||
| PathHelpers::perpendicularOffset (endX, endY, startX, startY, | |||
| arrowheadLength, lineThickness, x, y); | |||
| lineTo (x, y); | |||
| arrowheadLength = jmin (arrowheadLength, 0.8f * line.getLength()); | |||
| startNewSubPath (line.getPointAlongLine (0, lineThickness)); | |||
| lineTo (line.getPointAlongLine (0, -lineThickness)); | |||
| lineTo (reversed.getPointAlongLine (0, lineThickness)); | |||
| lineTo (reversed.getPointAlongLine (arrowheadLength, arrowheadWidth)); | |||
| lineTo (line.getEnd()); | |||
| lineTo (reversed.getPointAlongLine (arrowheadLength, -arrowheadWidth)); | |||
| lineTo (reversed.getPointAlongLine (arrowheadLength, -lineThickness)); | |||
| closeSubPath(); | |||
| } | |||
| PathHelpers::perpendicularOffset (endX, endY, startX, startY, | |||
| arrowheadLength, arrowheadWidth, x, y); | |||
| lineTo (x, y); | |||
| void Path::addPolygon (const Point<float>& centre, const int numberOfSides, | |||
| const float radius, const float startAngle) | |||
| { | |||
| jassert (numberOfSides > 1); // this would be silly. | |||
| PathHelpers::perpendicularOffset (endX, endY, startX, startY, | |||
| 0, 0, x, y); | |||
| lineTo (x, y); | |||
| if (numberOfSides > 1) | |||
| { | |||
| const float angleBetweenPoints = float_Pi * 2.0f / numberOfSides; | |||
| PathHelpers::perpendicularOffset (endX, endY, startX, startY, | |||
| arrowheadLength, -arrowheadWidth, x, y); | |||
| lineTo (x, y); | |||
| for (int i = 0; i < numberOfSides; ++i) | |||
| { | |||
| const float angle = startAngle + i * angleBetweenPoints; | |||
| const Point<float> p (centre.getPointOnCircumference (radius, angle)); | |||
| PathHelpers::perpendicularOffset (endX, endY, startX, startY, | |||
| arrowheadLength, -lineThickness, x, y); | |||
| lineTo (x, y); | |||
| if (i == 0) | |||
| startNewSubPath (p); | |||
| else | |||
| lineTo (p); | |||
| } | |||
| closeSubPath(); | |||
| closeSubPath(); | |||
| } | |||
| } | |||
| void Path::addStar (const float centreX, | |||
| const float centreY, | |||
| const int numberOfPoints, | |||
| const float innerRadius, | |||
| const float outerRadius, | |||
| const float startAngle) | |||
| void Path::addStar (const Point<float>& centre, const int numberOfPoints, | |||
| const float innerRadius, const float outerRadius, const float startAngle) | |||
| { | |||
| jassert (numberOfPoints > 1); // this would be silly. | |||
| @@ -694,20 +644,15 @@ void Path::addStar (const float centreX, | |||
| for (int i = 0; i < numberOfPoints; ++i) | |||
| { | |||
| float angle = startAngle + i * angleBetweenPoints; | |||
| const float x = centreX + outerRadius * std::sin (angle); | |||
| const float y = centreY - outerRadius * std::cos (angle); | |||
| const float angle = startAngle + i * angleBetweenPoints; | |||
| const Point<float> p (centre.getPointOnCircumference (outerRadius, angle)); | |||
| if (i == 0) | |||
| startNewSubPath (x, y); | |||
| startNewSubPath (p); | |||
| else | |||
| lineTo (x, y); | |||
| angle += angleBetweenPoints * 0.5f; | |||
| lineTo (p); | |||
| lineTo (centreX + innerRadius * std::sin (angle), | |||
| centreY - innerRadius * std::cos (angle)); | |||
| lineTo (centre.getPointOnCircumference (innerRadius, angle + angleBetweenPoints * 0.5f)); | |||
| } | |||
| closeSubPath(); | |||
| @@ -176,6 +176,19 @@ public: | |||
| */ | |||
| void startNewSubPath (float startX, float startY); | |||
| /** Begins a new subpath with a given starting position. | |||
| This will move the path's current position to the co-ordinates passed in and | |||
| make it ready to draw lines or curves starting from this position. | |||
| After adding whatever lines and curves are needed, you can either | |||
| close the current sub-path using closeSubPath() or call startNewSubPath() | |||
| to move to a new sub-path, leaving the old one open-ended. | |||
| @see lineTo, quadraticTo, cubicTo, closeSubPath | |||
| */ | |||
| void startNewSubPath (const Point<float>& start); | |||
| /** Closes a the current sub-path with a line back to its start-point. | |||
| When creating a closed shape such as a triangle, don't use 3 lineTo() | |||
| @@ -201,6 +214,17 @@ public: | |||
| */ | |||
| void lineTo (float endX, float endY); | |||
| /** Adds a line from the shape's last position to a new end-point. | |||
| This will connect the end-point of the last line or curve that was added | |||
| to a new point, using a straight line. | |||
| See the class description for an example of how to add lines and curves to a path. | |||
| @see startNewSubPath, quadraticTo, cubicTo, closeSubPath | |||
| */ | |||
| void lineTo (const Point<float>& end); | |||
| /** Adds a quadratic bezier curve from the shape's last position to a new position. | |||
| This will connect the end-point of the last line or curve that was added | |||
| @@ -215,6 +239,18 @@ public: | |||
| float endPointX, | |||
| float endPointY); | |||
| /** Adds a quadratic bezier curve from the shape's last position to a new position. | |||
| This will connect the end-point of the last line or curve that was added | |||
| to a new point, using a quadratic spline with one control-point. | |||
| See the class description for an example of how to add lines and curves to a path. | |||
| @see startNewSubPath, lineTo, cubicTo, closeSubPath | |||
| */ | |||
| void quadraticTo (const Point<float>& controlPoint, | |||
| const Point<float>& endPoint); | |||
| /** Adds a cubic bezier curve from the shape's last position to a new position. | |||
| This will connect the end-point of the last line or curve that was added | |||
| @@ -231,6 +267,19 @@ public: | |||
| float endPointX, | |||
| float endPointY); | |||
| /** Adds a cubic bezier curve from the shape's last position to a new position. | |||
| This will connect the end-point of the last line or curve that was added | |||
| to a new point, using a cubic spline with two control-points. | |||
| See the class description for an example of how to add lines and curves to a path. | |||
| @see startNewSubPath, lineTo, quadraticTo, closeSubPath | |||
| */ | |||
| void cubicTo (const Point<float>& controlPoint1, | |||
| const Point<float>& controlPoint2, | |||
| const Point<float>& endPoint); | |||
| /** Returns the last point that was added to the path by one of the drawing methods. | |||
| */ | |||
| const Point<float> getCurrentPosition() const; | |||
| @@ -402,26 +451,29 @@ public: | |||
| @see addArrow | |||
| */ | |||
| void addLineSegment (float startX, float startY, | |||
| float endX, float endY, | |||
| float lineThickness); | |||
| void addLineSegment (const Line<float>& line, float lineThickness); | |||
| /** Adds a line with an arrowhead on the end. | |||
| The arrow is added as a new closed sub-path. (Any currently open paths will be | |||
| left open). | |||
| The arrow is added as a new closed sub-path. (Any currently open paths will be left open). | |||
| @see PathStrokeType::createStrokeWithArrowheads | |||
| */ | |||
| void addArrow (float startX, float startY, | |||
| float endX, float endY, | |||
| void addArrow (const Line<float>& line, | |||
| float lineThickness, | |||
| float arrowheadWidth, | |||
| float arrowheadLength); | |||
| /** Adds a star shape to the path. | |||
| /** Adds a polygon shape to the path. | |||
| @see addStar | |||
| */ | |||
| void addPolygon (const Point<float>& centre, | |||
| int numberOfSides, | |||
| float radius, | |||
| float startAngle = 0.0f); | |||
| /** Adds a star shape to the path. | |||
| @see addPolygon | |||
| */ | |||
| void addStar (float centreX, | |||
| float centreY, | |||
| void addStar (const Point<float>& centre, | |||
| int numberOfPoints, | |||
| float innerRadius, | |||
| float outerRadius, | |||
| @@ -74,126 +74,125 @@ bool PathStrokeType::operator!= (const PathStrokeType& other) const throw() | |||
| } | |||
| //============================================================================== | |||
| static bool lineIntersection (const float x1, const float y1, | |||
| const float x2, const float y2, | |||
| const float x3, const float y3, | |||
| const float x4, const float y4, | |||
| float& intersectionX, | |||
| float& intersectionY, | |||
| float& distanceBeyondLine1EndSquared) throw() | |||
| namespace PathStrokeHelpers | |||
| { | |||
| if (x2 != x3 || y2 != y3) | |||
| static bool lineIntersection (const float x1, const float y1, | |||
| const float x2, const float y2, | |||
| const float x3, const float y3, | |||
| const float x4, const float y4, | |||
| float& intersectionX, | |||
| float& intersectionY, | |||
| float& distanceBeyondLine1EndSquared) throw() | |||
| { | |||
| const float dx1 = x2 - x1; | |||
| const float dy1 = y2 - y1; | |||
| const float dx2 = x4 - x3; | |||
| const float dy2 = y4 - y3; | |||
| const float divisor = dx1 * dy2 - dx2 * dy1; | |||
| if (divisor == 0) | |||
| if (x2 != x3 || y2 != y3) | |||
| { | |||
| if (! ((dx1 == 0 && dy1 == 0) || (dx2 == 0 && dy2 == 0))) | |||
| const float dx1 = x2 - x1; | |||
| const float dy1 = y2 - y1; | |||
| const float dx2 = x4 - x3; | |||
| const float dy2 = y4 - y3; | |||
| const float divisor = dx1 * dy2 - dx2 * dy1; | |||
| if (divisor == 0) | |||
| { | |||
| if (dy1 == 0 && dy2 != 0) | |||
| if (! ((dx1 == 0 && dy1 == 0) || (dx2 == 0 && dy2 == 0))) | |||
| { | |||
| const float along = (y1 - y3) / dy2; | |||
| intersectionX = x3 + along * dx2; | |||
| intersectionY = y1; | |||
| if (dy1 == 0 && dy2 != 0) | |||
| { | |||
| const float along = (y1 - y3) / dy2; | |||
| intersectionX = x3 + along * dx2; | |||
| intersectionY = y1; | |||
| distanceBeyondLine1EndSquared = intersectionX - x2; | |||
| distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; | |||
| if ((x2 > x1) == (intersectionX < x2)) | |||
| distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; | |||
| distanceBeyondLine1EndSquared = intersectionX - x2; | |||
| distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; | |||
| if ((x2 > x1) == (intersectionX < x2)) | |||
| distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; | |||
| return along >= 0 && along <= 1.0f; | |||
| } | |||
| else if (dy2 == 0 && dy1 != 0) | |||
| { | |||
| const float along = (y3 - y1) / dy1; | |||
| intersectionX = x1 + along * dx1; | |||
| intersectionY = y3; | |||
| return along >= 0 && along <= 1.0f; | |||
| } | |||
| else if (dy2 == 0 && dy1 != 0) | |||
| { | |||
| const float along = (y3 - y1) / dy1; | |||
| intersectionX = x1 + along * dx1; | |||
| intersectionY = y3; | |||
| distanceBeyondLine1EndSquared = (along - 1.0f) * dx1; | |||
| distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; | |||
| if (along < 1.0f) | |||
| distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; | |||
| distanceBeyondLine1EndSquared = (along - 1.0f) * dx1; | |||
| distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; | |||
| if (along < 1.0f) | |||
| distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; | |||
| return along >= 0 && along <= 1.0f; | |||
| } | |||
| else if (dx1 == 0 && dx2 != 0) | |||
| { | |||
| const float along = (x1 - x3) / dx2; | |||
| intersectionX = x1; | |||
| intersectionY = y3 + along * dy2; | |||
| return along >= 0 && along <= 1.0f; | |||
| } | |||
| else if (dx1 == 0 && dx2 != 0) | |||
| { | |||
| const float along = (x1 - x3) / dx2; | |||
| intersectionX = x1; | |||
| intersectionY = y3 + along * dy2; | |||
| distanceBeyondLine1EndSquared = intersectionY - y2; | |||
| distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; | |||
| distanceBeyondLine1EndSquared = intersectionY - y2; | |||
| distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; | |||
| if ((y2 > y1) == (intersectionY < y2)) | |||
| distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; | |||
| if ((y2 > y1) == (intersectionY < y2)) | |||
| distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; | |||
| return along >= 0 && along <= 1.0f; | |||
| } | |||
| else if (dx2 == 0 && dx1 != 0) | |||
| { | |||
| const float along = (x3 - x1) / dx1; | |||
| intersectionX = x3; | |||
| intersectionY = y1 + along * dy1; | |||
| return along >= 0 && along <= 1.0f; | |||
| } | |||
| else if (dx2 == 0 && dx1 != 0) | |||
| { | |||
| const float along = (x3 - x1) / dx1; | |||
| intersectionX = x3; | |||
| intersectionY = y1 + along * dy1; | |||
| distanceBeyondLine1EndSquared = (along - 1.0f) * dy1; | |||
| distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; | |||
| if (along < 1.0f) | |||
| distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; | |||
| distanceBeyondLine1EndSquared = (along - 1.0f) * dy1; | |||
| distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; | |||
| if (along < 1.0f) | |||
| distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; | |||
| return along >= 0 && along <= 1.0f; | |||
| return along >= 0 && along <= 1.0f; | |||
| } | |||
| } | |||
| } | |||
| intersectionX = 0.5f * (x2 + x3); | |||
| intersectionY = 0.5f * (y2 + y3); | |||
| distanceBeyondLine1EndSquared = 0.0f; | |||
| return false; | |||
| } | |||
| else | |||
| { | |||
| const float along1 = ((y1 - y3) * dx2 - (x1 - x3) * dy2) / divisor; | |||
| intersectionX = x1 + along1 * dx1; | |||
| intersectionY = y1 + along1 * dy1; | |||
| intersectionX = 0.5f * (x2 + x3); | |||
| intersectionY = 0.5f * (y2 + y3); | |||
| if (along1 >= 0 && along1 <= 1.0f) | |||
| distanceBeyondLine1EndSquared = 0.0f; | |||
| return false; | |||
| } | |||
| else | |||
| { | |||
| const float along2 = ((y1 - y3) * dx1 - (x1 - x3) * dy1); | |||
| const float along1 = ((y1 - y3) * dx2 - (x1 - x3) * dy2) / divisor; | |||
| if (along2 >= 0 && along2 <= divisor) | |||
| intersectionX = x1 + along1 * dx1; | |||
| intersectionY = y1 + along1 * dy1; | |||
| if (along1 >= 0 && along1 <= 1.0f) | |||
| { | |||
| distanceBeyondLine1EndSquared = 0.0f; | |||
| return true; | |||
| const float along2 = ((y1 - y3) * dx1 - (x1 - x3) * dy1); | |||
| if (along2 >= 0 && along2 <= divisor) | |||
| { | |||
| distanceBeyondLine1EndSquared = 0.0f; | |||
| return true; | |||
| } | |||
| } | |||
| } | |||
| distanceBeyondLine1EndSquared = along1 - 1.0f; | |||
| distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; | |||
| distanceBeyondLine1EndSquared *= (dx1 * dx1 + dy1 * dy1); | |||
| distanceBeyondLine1EndSquared = along1 - 1.0f; | |||
| distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; | |||
| distanceBeyondLine1EndSquared *= (dx1 * dx1 + dy1 * dy1); | |||
| if (along1 < 1.0f) | |||
| distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; | |||
| if (along1 < 1.0f) | |||
| distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; | |||
| return false; | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| intersectionX = x2; | |||
| intersectionY = y2; | |||
| intersectionX = x2; | |||
| intersectionY = y2; | |||
| distanceBeyondLine1EndSquared = 0.0f; | |||
| return true; | |||
| } | |||
| distanceBeyondLine1EndSquared = 0.0f; | |||
| return true; | |||
| } | |||
| namespace PathFunctions | |||
| { | |||
| // part of stroke drawing stuff | |||
| static void addEdgeAndJoint (Path& destPath, | |||
| const PathStrokeType::JointStyle style, | |||
| const float maxMiterExtensionSquared, const float width, | |||
| @@ -348,23 +347,102 @@ namespace PathFunctions | |||
| } | |||
| } | |||
| struct LineSection | |||
| struct Arrowhead | |||
| { | |||
| LineSection() {} | |||
| LineSection (int) {} | |||
| float startWidth, startLength; | |||
| float endWidth, endLength; | |||
| }; | |||
| static void addArrowhead (Path& destPath, | |||
| const float x1, const float y1, | |||
| const float x2, const float y2, | |||
| const float tipX, const float tipY, | |||
| const float width, | |||
| const float arrowheadWidth) | |||
| { | |||
| Line<float> line (x1, y1, x2, y2); | |||
| destPath.lineTo (line.getPointAlongLine (-(arrowheadWidth / 2.0f - width), 0)); | |||
| destPath.lineTo (tipX, tipY); | |||
| destPath.lineTo (line.getPointAlongLine (arrowheadWidth - (arrowheadWidth / 2.0f - width), 0)); | |||
| destPath.lineTo (x2, y2); | |||
| } | |||
| struct LineSection | |||
| { | |||
| float x1, y1, x2, y2; // original line | |||
| float lx1, ly1, lx2, ly2; // the left-hand stroke | |||
| float rx1, ry1, rx2, ry2; // the right-hand stroke | |||
| }; | |||
| static void addSubPath (Path& destPath, const Array <LineSection>& subPath, | |||
| const bool isClosed, | |||
| const float width, const float maxMiterExtensionSquared, | |||
| const PathStrokeType::JointStyle jointStyle, const PathStrokeType::EndCapStyle endStyle) | |||
| static void shortenSubPath (Array<LineSection>& subPath, float amountAtStart, float amountAtEnd) | |||
| { | |||
| while (amountAtEnd > 0 && subPath.size() > 0) | |||
| { | |||
| 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); | |||
| if (len <= amountAtEnd && subPath.size() > 1) | |||
| { | |||
| LineSection& prev = subPath.getReference (subPath.size() - 2); | |||
| prev.x2 = l.x2; | |||
| prev.y2 = l.y2; | |||
| subPath.removeLast(); | |||
| amountAtEnd -= len; | |||
| } | |||
| else | |||
| { | |||
| const float prop = jmin (0.9999f, amountAtEnd / len); | |||
| dx *= prop; | |||
| dy *= prop; | |||
| l.rx1 += dx; | |||
| l.ry1 += dy; | |||
| l.lx2 += dx; | |||
| l.ly2 += dy; | |||
| break; | |||
| } | |||
| } | |||
| while (amountAtStart > 0 && subPath.size() > 0) | |||
| { | |||
| LineSection& l = subPath.getReference (0); | |||
| float dx = l.rx2 - l.rx1; | |||
| float dy = l.ry2 - l.ry1; | |||
| const float len = juce_hypotf (dx, dy); | |||
| if (len <= amountAtStart && subPath.size() > 1) | |||
| { | |||
| LineSection& next = subPath.getReference (1); | |||
| next.x1 = l.x1; | |||
| next.y1 = l.y1; | |||
| subPath.remove (0); | |||
| amountAtStart -= len; | |||
| } | |||
| else | |||
| { | |||
| const float prop = jmin (0.9999f, amountAtStart / len); | |||
| dx *= prop; | |||
| dy *= prop; | |||
| l.rx2 -= dx; | |||
| l.ry2 -= dy; | |||
| l.lx1 -= dx; | |||
| l.ly1 -= dy; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| static void addSubPath (Path& destPath, Array<LineSection>& subPath, | |||
| const bool isClosed, const float width, const float maxMiterExtensionSquared, | |||
| const PathStrokeType::JointStyle jointStyle, const PathStrokeType::EndCapStyle endStyle, | |||
| const Arrowhead* const arrowhead) | |||
| { | |||
| jassert (subPath.size() > 0); | |||
| if (arrowhead != 0) | |||
| shortenSubPath (subPath, arrowhead->startLength, arrowhead->endLength); | |||
| const LineSection& firstLine = subPath.getReference (0); | |||
| float lastX1 = firstLine.lx1; | |||
| @@ -380,10 +458,11 @@ namespace PathFunctions | |||
| { | |||
| destPath.startNewSubPath (firstLine.rx2, firstLine.ry2); | |||
| addLineEnd (destPath, endStyle, | |||
| firstLine.rx2, firstLine.ry2, | |||
| lastX1, lastY1, | |||
| width); | |||
| if (arrowhead != 0) | |||
| addArrowhead (destPath, firstLine.rx2, firstLine.ry2, lastX1, lastY1, firstLine.x1, firstLine.y1, | |||
| width, arrowhead->startWidth); | |||
| else | |||
| addLineEnd (destPath, endStyle, firstLine.rx2, firstLine.ry2, lastX1, lastY1, width); | |||
| } | |||
| int i; | |||
| @@ -422,10 +501,11 @@ namespace PathFunctions | |||
| { | |||
| destPath.lineTo (lastX2, lastY2); | |||
| addLineEnd (destPath, endStyle, | |||
| lastX2, lastY2, | |||
| lastLine.rx1, lastLine.ry1, | |||
| width); | |||
| if (arrowhead != 0) | |||
| addArrowhead (destPath, lastX2, lastY2, lastLine.rx1, lastLine.ry1, lastLine.x2, lastLine.y2, | |||
| width, arrowhead->endWidth); | |||
| else | |||
| addLineEnd (destPath, endStyle, lastX2, lastY2, lastLine.rx1, lastLine.ry1, width); | |||
| } | |||
| lastX1 = lastLine.rx1; | |||
| @@ -465,114 +545,122 @@ namespace PathFunctions | |||
| destPath.closeSubPath(); | |||
| } | |||
| } | |||
| void PathStrokeType::createStrokedPath (Path& destPath, | |||
| const Path& source, | |||
| const AffineTransform& transform, | |||
| const float extraAccuracy) const | |||
| { | |||
| if (thickness <= 0) | |||
| static void createStroke (const float thickness, const PathStrokeType::JointStyle jointStyle, | |||
| const PathStrokeType::EndCapStyle endStyle, | |||
| Path& destPath, const Path& source, | |||
| const AffineTransform& transform, | |||
| const float extraAccuracy, const Arrowhead* const arrowhead) | |||
| { | |||
| destPath.clear(); | |||
| return; | |||
| } | |||
| if (thickness <= 0) | |||
| { | |||
| destPath.clear(); | |||
| return; | |||
| } | |||
| const Path* sourcePath = &source; | |||
| Path temp; | |||
| const Path* sourcePath = &source; | |||
| Path temp; | |||
| if (sourcePath == &destPath) | |||
| { | |||
| destPath.swapWithPath (temp); | |||
| sourcePath = &temp; | |||
| } | |||
| else | |||
| { | |||
| destPath.clear(); | |||
| } | |||
| if (sourcePath == &destPath) | |||
| { | |||
| destPath.swapWithPath (temp); | |||
| sourcePath = &temp; | |||
| } | |||
| else | |||
| { | |||
| destPath.clear(); | |||
| } | |||
| destPath.setUsingNonZeroWinding (true); | |||
| destPath.setUsingNonZeroWinding (true); | |||
| const float maxMiterExtensionSquared = 9.0f * thickness * thickness; | |||
| const float width = 0.5f * thickness; | |||
| const float maxMiterExtensionSquared = 9.0f * thickness * thickness; | |||
| const float width = 0.5f * thickness; | |||
| // Iterate the path, creating a list of the | |||
| // left/right-hand lines along either side of it... | |||
| PathFlatteningIterator it (*sourcePath, transform, 9.0f / extraAccuracy); | |||
| // Iterate the path, creating a list of the | |||
| // left/right-hand lines along either side of it... | |||
| PathFlatteningIterator it (*sourcePath, transform, 9.0f / extraAccuracy); | |||
| using namespace PathFunctions; | |||
| Array <LineSection> subPath; | |||
| LineSection l; | |||
| l.x1 = 0; | |||
| l.y1 = 0; | |||
| Array <LineSection> subPath; | |||
| subPath.ensureStorageAllocated (512); | |||
| LineSection l; | |||
| l.x1 = 0; | |||
| l.y1 = 0; | |||
| const float minSegmentLength = 2.0f / (extraAccuracy * extraAccuracy); | |||
| const float minSegmentLength = 2.0f / (extraAccuracy * extraAccuracy); | |||
| while (it.next()) | |||
| { | |||
| if (it.subPathIndex == 0) | |||
| while (it.next()) | |||
| { | |||
| if (subPath.size() > 0) | |||
| if (it.subPathIndex == 0) | |||
| { | |||
| addSubPath (destPath, subPath, false, width, maxMiterExtensionSquared, jointStyle, endStyle); | |||
| subPath.clearQuick(); | |||
| } | |||
| l.x1 = it.x1; | |||
| l.y1 = it.y1; | |||
| } | |||
| if (subPath.size() > 0) | |||
| { | |||
| addSubPath (destPath, subPath, false, width, maxMiterExtensionSquared, jointStyle, endStyle, arrowhead); | |||
| subPath.clearQuick(); | |||
| } | |||
| l.x2 = it.x2; | |||
| l.y2 = it.y2; | |||
| l.x1 = it.x1; | |||
| l.y1 = it.y1; | |||
| } | |||
| float dx = l.x2 - l.x1; | |||
| float dy = l.y2 - l.y1; | |||
| l.x2 = it.x2; | |||
| l.y2 = it.y2; | |||
| const float hypotSquared = dx*dx + dy*dy; | |||
| float dx = l.x2 - l.x1; | |||
| float dy = l.y2 - l.y1; | |||
| if (it.closesSubPath || hypotSquared > minSegmentLength || it.isLastInSubpath()) | |||
| { | |||
| const float len = std::sqrt (hypotSquared); | |||
| const float hypotSquared = dx*dx + dy*dy; | |||
| if (len == 0) | |||
| { | |||
| l.rx1 = l.rx2 = l.lx1 = l.lx2 = l.x1; | |||
| l.ry1 = l.ry2 = l.ly1 = l.ly2 = l.y1; | |||
| } | |||
| else | |||
| if (it.closesSubPath || hypotSquared > minSegmentLength || it.isLastInSubpath()) | |||
| { | |||
| const float offset = width / len; | |||
| dx *= offset; | |||
| dy *= offset; | |||
| l.rx2 = l.x1 - dy; | |||
| l.ry2 = l.y1 + dx; | |||
| l.lx1 = l.x1 + dy; | |||
| l.ly1 = l.y1 - dx; | |||
| const float len = std::sqrt (hypotSquared); | |||
| l.lx2 = l.x2 + dy; | |||
| l.ly2 = l.y2 - dx; | |||
| l.rx1 = l.x2 - dy; | |||
| l.ry1 = l.y2 + dx; | |||
| } | |||
| if (len == 0) | |||
| { | |||
| l.rx1 = l.rx2 = l.lx1 = l.lx2 = l.x1; | |||
| l.ry1 = l.ry2 = l.ly1 = l.ly2 = l.y1; | |||
| } | |||
| else | |||
| { | |||
| const float offset = width / len; | |||
| dx *= offset; | |||
| dy *= offset; | |||
| l.rx2 = l.x1 - dy; | |||
| l.ry2 = l.y1 + dx; | |||
| l.lx1 = l.x1 + dy; | |||
| l.ly1 = l.y1 - dx; | |||
| l.lx2 = l.x2 + dy; | |||
| l.ly2 = l.y2 - dx; | |||
| l.rx1 = l.x2 - dy; | |||
| l.ry1 = l.y2 + dx; | |||
| } | |||
| subPath.add (l); | |||
| subPath.add (l); | |||
| if (it.closesSubPath) | |||
| { | |||
| addSubPath (destPath, subPath, true, width, maxMiterExtensionSquared, jointStyle, endStyle); | |||
| subPath.clearQuick(); | |||
| } | |||
| else | |||
| { | |||
| l.x1 = it.x2; | |||
| l.y1 = it.y2; | |||
| if (it.closesSubPath) | |||
| { | |||
| addSubPath (destPath, subPath, true, width, maxMiterExtensionSquared, jointStyle, endStyle, arrowhead); | |||
| subPath.clearQuick(); | |||
| } | |||
| else | |||
| { | |||
| l.x1 = it.x2; | |||
| l.y1 = it.y2; | |||
| } | |||
| } | |||
| } | |||
| if (subPath.size() > 0) | |||
| addSubPath (destPath, subPath, false, width, maxMiterExtensionSquared, jointStyle, endStyle, arrowhead); | |||
| } | |||
| } | |||
| if (subPath.size() > 0) | |||
| addSubPath (destPath, subPath, false, width, maxMiterExtensionSquared, jointStyle, endStyle); | |||
| void PathStrokeType::createStrokedPath (Path& destPath, const Path& sourcePath, | |||
| const AffineTransform& transform, const float extraAccuracy) const | |||
| { | |||
| PathStrokeHelpers::createStroke (thickness, jointStyle, endStyle, destPath, sourcePath, | |||
| transform, extraAccuracy, 0); | |||
| } | |||
| void PathStrokeType::createDashedStroke (Path& destPath, | |||
| @@ -641,5 +729,21 @@ void PathStrokeType::createDashedStroke (Path& destPath, | |||
| } | |||
| } | |||
| void PathStrokeType::createStrokeWithArrowheads (Path& destPath, | |||
| const Path& sourcePath, | |||
| const float arrowheadStartWidth, const float arrowheadStartLength, | |||
| const float arrowheadEndWidth, const float arrowheadEndLength, | |||
| const AffineTransform& transform, | |||
| const float extraAccuracy) const | |||
| { | |||
| PathStrokeHelpers::Arrowhead head; | |||
| head.startWidth = arrowheadStartWidth; | |||
| head.startLength = arrowheadStartLength; | |||
| head.endWidth = arrowheadEndWidth; | |||
| head.endLength = arrowheadEndLength; | |||
| PathStrokeHelpers::createStroke (thickness, jointStyle, endStyle, | |||
| destPath, sourcePath, transform, extraAccuracy, &head); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -137,6 +137,29 @@ public: | |||
| const AffineTransform& transform = AffineTransform::identity, | |||
| float extraAccuracy = 1.0f) const; | |||
| //============================================================================== | |||
| /** Applies this stroke type to a path and returns the resultant stroke as another Path. | |||
| @param destPath the resultant stroked outline shape will be copied into this path. | |||
| Note that it's ok for the source and destination Paths to be | |||
| the same object, so you can easily turn a path into a stroked version | |||
| of itself. | |||
| @param sourcePath the path to use as the source | |||
| @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 | |||
| @see createDashedStroke | |||
| */ | |||
| void createStrokeWithArrowheads (Path& destPath, | |||
| const Path& sourcePath, | |||
| float arrowheadStartWidth, float arrowheadStartLength, | |||
| float arrowheadEndWidth, float arrowheadEndLength, | |||
| const AffineTransform& transform = AffineTransform::identity, | |||
| float extraAccuracy = 1.0f) const; | |||
| //============================================================================== | |||
| /** Returns the stroke thickness. */ | |||
| float getStrokeThickness() const throw() { return thickness; } | |||
| @@ -59,6 +59,9 @@ public: | |||
| /** Copies this point from another one. */ | |||
| Point& operator= (const Point& other) throw() { x = other.x; y = other.y; return *this; } | |||
| inline bool operator== (const Point& other) const throw() { return x == other.x && y == other.y; } | |||
| inline bool operator!= (const Point& other) const throw() { return x != other.x || y != other.y; } | |||
| /** Returns true if the point is (0, 0). */ | |||
| bool isOrigin() const throw() { return x == ValueType() && y == ValueType(); } | |||
| @@ -86,9 +89,6 @@ public: | |||
| /** Adds a pair of co-ordinates to this value. */ | |||
| void addXY (const ValueType xToAdd, const ValueType yToAdd) throw() { x += xToAdd; y += yToAdd; } | |||
| inline bool operator== (const Point& other) const throw() { return x == other.x && y == other.y; } | |||
| inline bool operator!= (const Point& other) const throw() { return x != other.x || y != other.y; } | |||
| /** Adds two points together. */ | |||
| const Point operator+ (const Point& other) const throw() { return Point (x + other.x, y + other.y); } | |||
| @@ -129,6 +129,20 @@ public: | |||
| */ | |||
| ValueType getAngleToPoint (const Point& other) const throw() { return (ValueType) std::atan2 (other.x - x, other.y - y); } | |||
| /** Taking this point to be the centre of a circle, this returns a point on its circumference. | |||
| @param radius the radius of the circle. | |||
| @param angle the angle of the point, in radians clockwise from the 12 o'clock position. | |||
| */ | |||
| const Point getPointOnCircumference (const float radius, const float angle) const throw() { return Point<float> (x + radius * std::sin (angle), | |||
| y - radius * std::cos (angle)); } | |||
| /** Taking this point to be the centre of an ellipse, this returns a point on its circumference. | |||
| @param radiusX the horizontal radius of the circle. | |||
| @param radiusY the vertical radius of the circle. | |||
| @param angle the angle of the point, in radians clockwise from the 12 o'clock position. | |||
| */ | |||
| const Point getPointOnCircumference (const float radiusX, const float radiusY, const float angle) const throw() { return Point<float> (x + radiusX * std::sin (angle), | |||
| y - radiusY * std::cos (angle)); } | |||
| /** Uses a transform to change the point's co-ordinates. | |||
| This will only compile if ValueType = float! | |||
| @see AffineTransform::transformPoint | |||
| @@ -142,6 +142,18 @@ public: | |||
| /** Returns a rectangle with the same size as this one, but a new position. */ | |||
| const Rectangle withPosition (const Point<ValueType>& newPos) const throw() { return Rectangle (newPos.getX(), newPos.getY(), w, h); } | |||
| /** Returns the rectangle's top-left position as a Point. */ | |||
| const Point<ValueType> getTopLeft() const throw() { return getPosition(); } | |||
| /** Returns the rectangle's top-right position as a Point. */ | |||
| const Point<ValueType> getTopRight() const throw() { return Point<float> (x + w, y); } | |||
| /** Returns the rectangle's bottom-left position as a Point. */ | |||
| const Point<ValueType> getBottomLeft() const throw() { return Point<float> (x, y + h); } | |||
| /** Returns the rectangle's bottom-right position as a Point. */ | |||
| const Point<ValueType> getBottomRight() const throw() { return Point<float> (x + w, y + h); } | |||
| /** Changes the rectangle's size, leaving the position of its top-left corner unchanged. */ | |||
| void setSize (const ValueType newWidth, const ValueType newHeight) throw() { w = newWidth; h = newHeight; } | |||
| @@ -538,6 +538,12 @@ RelativeRectangle::RelativeRectangle() | |||
| { | |||
| } | |||
| RelativeRectangle::RelativeRectangle (const RelativeCoordinate& left_, const RelativeCoordinate& right_, | |||
| const RelativeCoordinate& top_, const RelativeCoordinate& bottom_) | |||
| : left (left_), right (right_), top (top_), bottom (bottom_) | |||
| { | |||
| } | |||
| RelativeRectangle::RelativeRectangle (const Rectangle<float>& rect, const String& componentName) | |||
| : left (rect.getX(), true), | |||
| right (rect.getWidth(), componentName + "." + RelativeCoordinate::Strings::left), | |||
| @@ -733,8 +739,7 @@ const ValueTree RelativePointPath::StartSubPath::createTree() const | |||
| void RelativePointPath::StartSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | |||
| { | |||
| const Point<float> p (startPos.resolve (coordFinder)); | |||
| path.startNewSubPath (p.getX(), p.getY()); | |||
| path.startNewSubPath (startPos.resolve (coordFinder)); | |||
| } | |||
| RelativePoint* RelativePointPath::StartSubPath::getControlPoints (int& numPoints) | |||
| @@ -780,8 +785,7 @@ const ValueTree RelativePointPath::LineTo::createTree() const | |||
| void RelativePointPath::LineTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | |||
| { | |||
| const Point<float> p (endPoint.resolve (coordFinder)); | |||
| path.lineTo (p.getX(), p.getY()); | |||
| path.lineTo (endPoint.resolve (coordFinder)); | |||
| } | |||
| RelativePoint* RelativePointPath::LineTo::getControlPoints (int& numPoints) | |||
| @@ -808,9 +812,8 @@ const ValueTree RelativePointPath::QuadraticTo::createTree() const | |||
| void RelativePointPath::QuadraticTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | |||
| { | |||
| const Point<float> p1 (controlPoints[0].resolve (coordFinder)); | |||
| const Point<float> p2 (controlPoints[1].resolve (coordFinder)); | |||
| path.quadraticTo (p1.getX(), p1.getY(), p2.getX(), p2.getY()); | |||
| path.quadraticTo (controlPoints[0].resolve (coordFinder), | |||
| controlPoints[1].resolve (coordFinder)); | |||
| } | |||
| RelativePoint* RelativePointPath::QuadraticTo::getControlPoints (int& numPoints) | |||
| @@ -839,10 +842,9 @@ const ValueTree RelativePointPath::CubicTo::createTree() const | |||
| void RelativePointPath::CubicTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | |||
| { | |||
| const Point<float> p1 (controlPoints[0].resolve (coordFinder)); | |||
| const Point<float> p2 (controlPoints[1].resolve (coordFinder)); | |||
| const Point<float> p3 (controlPoints[2].resolve (coordFinder)); | |||
| path.cubicTo (p1.getX(), p1.getY(), p2.getX(), p2.getY(), p3.getX(), p3.getY()); | |||
| path.cubicTo (controlPoints[0].resolve (coordFinder), | |||
| controlPoints[1].resolve (coordFinder), | |||
| controlPoints[2].resolve (coordFinder)); | |||
| } | |||
| RelativePoint* RelativePointPath::CubicTo::getControlPoints (int& numPoints) | |||
| @@ -896,10 +898,10 @@ void RelativeParallelogram::getPath (Path& path, RelativeCoordinate::NamedCoordi | |||
| Point<float> points[4]; | |||
| resolveFourCorners (points, coordFinder); | |||
| path.startNewSubPath (points[0].getX(), points[0].getY()); | |||
| path.lineTo (points[1].getX(), points[1].getY()); | |||
| path.lineTo (points[3].getX(), points[3].getY()); | |||
| path.lineTo (points[2].getX(), points[2].getY()); | |||
| path.startNewSubPath (points[0]); | |||
| path.lineTo (points[1]); | |||
| path.lineTo (points[3]); | |||
| path.lineTo (points[2]); | |||
| path.closeSubPath(); | |||
| } | |||
| @@ -360,6 +360,10 @@ public: | |||
| /** Creates an absolute rectangle, relative to the origin. */ | |||
| explicit RelativeRectangle (const Rectangle<float>& rect, const String& componentName); | |||
| /** Creates a rectangle from four coordinates. */ | |||
| RelativeRectangle (const RelativeCoordinate& left, const RelativeCoordinate& right, | |||
| const RelativeCoordinate& top, const RelativeCoordinate& bottom); | |||
| /** Creates a rectangle from a stringified representation. | |||
| The string must contain a sequence of 4 coordinates, separated by commas, in the order | |||
| left, top, right, bottom. The syntax for the coordinate strings is explained in the | |||
| @@ -245,11 +245,19 @@ public: | |||
| CGImageRef image = CoreGraphicsImage::createImage (singleChannelImage, true, greyColourSpace); | |||
| if (srcClip != sourceImage.getBounds()) | |||
| { | |||
| CGImageRef fullImage = image; | |||
| image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), srcClip.getY(), | |||
| srcClip.getWidth(), srcClip.getHeight())); | |||
| CGImageRelease (fullImage); | |||
| } | |||
| flip(); | |||
| AffineTransform t (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); | |||
| AffineTransform t (AffineTransform::scale (1.0f, -1.0f).translated (0, srcClip.getHeight()).followedBy (transform)); | |||
| applyTransform (t); | |||
| CGRect r = CGRectMake (srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight()); | |||
| CGRect r = CGRectMake (0, 0, srcClip.getWidth(), srcClip.getHeight()); | |||
| CGContextClipToMask (context, r, image); | |||
| applyTransform (t.inverted()); | |||