Browse Source

New arrowhead and polygon methods for Path and PathStrokeType. Tweaked a few Path methods to take Line and Point objects instead of loose coordinate parameters. Various new geometric methods and more refactoring of Drawables. Misc fixes for CoreGraphics, AudioFormat.

tags/2021-05-28
Julian Storer 15 years ago
parent
commit
00b082caf6
36 changed files with 1514 additions and 1152 deletions
  1. +3
    -0
      extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp
  2. +16
    -13
      extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.cpp
  3. +3
    -0
      extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h
  4. +2
    -2
      extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp
  5. +1
    -1
      extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h
  6. +0
    -17
      extras/juce demo/Source/demos/FontsAndTextDemo.cpp
  7. +9
    -18
      extras/juce demo/Source/demos/RenderingTestComponent.cpp
  8. +3
    -3
      extras/juce demo/Source/demos/WidgetsDemo.cpp
  9. +2
    -2
      extras/the jucer/src/model/paintelements/jucer_PaintElementImage.h
  10. +578
    -495
      juce_amalgamated.cpp
  11. +163
    -53
      juce_amalgamated.h
  12. +12
    -18
      src/audio/audio_file_formats/juce_AudioFormat.cpp
  13. +1
    -1
      src/core/juce_StandardHeader.h
  14. +2
    -2
      src/gui/components/controls/juce_Toolbar.cpp
  15. +2
    -2
      src/gui/components/filebrowser/juce_FileSearchPathListComponent.cpp
  16. +7
    -7
      src/gui/components/lookandfeel/juce_LookAndFeel.cpp
  17. +5
    -5
      src/gui/components/lookandfeel/juce_OldSchoolLookAndFeel.cpp
  18. +3
    -1
      src/gui/components/windows/juce_DialogWindow.cpp
  19. +6
    -8
      src/gui/graphics/contexts/juce_Graphics.cpp
  20. +3
    -7
      src/gui/graphics/contexts/juce_Graphics.h
  21. +1
    -1
      src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp
  22. +1
    -1
      src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp
  23. +95
    -69
      src/gui/graphics/drawables/juce_DrawableComposite.cpp
  24. +40
    -32
      src/gui/graphics/drawables/juce_DrawableComposite.h
  25. +23
    -28
      src/gui/graphics/drawables/juce_SVGParser.cpp
  26. +2
    -2
      src/gui/graphics/fonts/juce_GlyphArrangement.cpp
  27. +3
    -0
      src/gui/graphics/geometry/juce_Line.h
  28. +88
    -143
      src/gui/graphics/geometry/juce_Path.cpp
  29. +63
    -11
      src/gui/graphics/geometry/juce_Path.h
  30. +294
    -190
      src/gui/graphics/geometry/juce_PathStrokeType.cpp
  31. +23
    -0
      src/gui/graphics/geometry/juce_PathStrokeType.h
  32. +17
    -3
      src/gui/graphics/geometry/juce_Point.h
  33. +12
    -0
      src/gui/graphics/geometry/juce_Rectangle.h
  34. +17
    -15
      src/gui/graphics/geometry/juce_RelativeCoordinate.cpp
  35. +4
    -0
      src/gui/graphics/geometry/juce_RelativeCoordinate.h
  36. +10
    -2
      src/native/mac/juce_mac_CoreGraphicsContext.mm

+ 3
- 0
extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp View File

@@ -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


+ 16
- 13
extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.cpp View File

@@ -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:


+ 3
- 0
extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h View File

@@ -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()));
}


+ 2
- 2
extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp View File

@@ -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);
}


+ 1
- 1
extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h View File

@@ -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)


+ 0
- 17
extras/juce demo/Source/demos/FontsAndTextDemo.cpp View File

@@ -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()


+ 9
- 18
extras/juce demo/Source/demos/RenderingTestComponent.cpp View File

@@ -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)));
}
}
}


+ 3
- 3
extras/juce demo/Source/demos/WidgetsDemo.cpp View File

@@ -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);


+ 2
- 2
extras/the jucer/src/model/paintelements/jucer_PaintElementImage.h View File

@@ -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
{


+ 578
- 495
juce_amalgamated.cpp
File diff suppressed because it is too large
View File


+ 163
- 53
juce_amalgamated.h View File

@@ -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;


+ 12
- 18
src/audio/audio_file_formats/juce_AudioFormat.cpp View File

@@ -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;


+ 1
- 1
src/core/juce_StandardHeader.h View File

@@ -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.


+ 2
- 2
src/gui/components/controls/juce_Toolbar.cpp View File

@@ -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);
}
}


+ 2
- 2
src/gui/components/filebrowser/juce_FileSearchPathListComponent.cpp View File

@@ -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);


+ 7
- 7
src/gui/components/lookandfeel/juce_LookAndFeel.cpp View File

@@ -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));


+ 5
- 5
src/gui/components/lookandfeel/juce_OldSchoolLookAndFeel.cpp View File

@@ -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;


+ 3
- 1
src/gui/components/windows/juce_DialogWindow.cpp View File

@@ -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()


+ 6
- 8
src/gui/graphics/contexts/juce_Graphics.cpp View File

@@ -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,


+ 3
- 7
src/gui/graphics/contexts/juce_Graphics.h View File

@@ -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;


+ 1
- 1
src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp View File

@@ -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);
}


+ 1
- 1
src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp View File

@@ -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);
}


+ 95
- 69
src/gui/graphics/drawables/juce_DrawableComposite.cpp View File

@@ -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)


+ 40
- 32
src/gui/graphics/drawables/juce_DrawableComposite.h View File

@@ -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;


+ 23
- 28
src/gui/graphics/drawables/juce_SVGParser.cpp View File

@@ -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);


+ 2
- 2
src/gui/graphics/fonts/juce_GlyphArrangement.cpp View File

@@ -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);


+ 3
- 0
src/gui/graphics/geometry/juce_Line.h View File

@@ -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()
{


+ 88
- 143
src/gui/graphics/geometry/juce_Path.cpp View File

@@ -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();


+ 63
- 11
src/gui/graphics/geometry/juce_Path.h View File

@@ -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,


+ 294
- 190
src/gui/graphics/geometry/juce_PathStrokeType.cpp View File

@@ -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

+ 23
- 0
src/gui/graphics/geometry/juce_PathStrokeType.h View File

@@ -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; }


+ 17
- 3
src/gui/graphics/geometry/juce_Point.h View File

@@ -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


+ 12
- 0
src/gui/graphics/geometry/juce_Rectangle.h View File

@@ -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; }


+ 17
- 15
src/gui/graphics/geometry/juce_RelativeCoordinate.cpp View File

@@ -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();
}


+ 4
- 0
src/gui/graphics/geometry/juce_RelativeCoordinate.h View File

@@ -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


+ 10
- 2
src/native/mac/juce_mac_CoreGraphicsContext.mm View File

@@ -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());


Loading…
Cancel
Save