1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-02-03 03:30:06 +00:00
JUCE/src/gui/graphics/drawables/juce_DrawableShape.cpp

369 lines
14 KiB
C++

/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-10 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../../../core/juce_StandardHeader.h"
BEGIN_JUCE_NAMESPACE
#include "juce_DrawableShape.h"
#include "juce_DrawableComposite.h"
//==============================================================================
DrawableShape::DrawableShape()
: strokeType (0.0f),
mainFill (Colours::black),
strokeFill (Colours::black)
{
}
DrawableShape::DrawableShape (const DrawableShape& other)
: strokeType (other.strokeType),
mainFill (other.mainFill),
strokeFill (other.strokeFill)
{
}
DrawableShape::~DrawableShape()
{
}
void DrawableShape::setFill (const FillType& newFill)
{
mainFill = newFill;
}
void DrawableShape::setStrokeFill (const FillType& newFill)
{
strokeFill = newFill;
}
void DrawableShape::setStrokeType (const PathStrokeType& newStrokeType)
{
strokeType = newStrokeType;
strokeChanged();
}
void DrawableShape::setStrokeThickness (const float newThickness)
{
setStrokeType (PathStrokeType (newThickness, strokeType.getJointStyle(), strokeType.getEndStyle()));
}
bool DrawableShape::isStrokeVisible() const throw()
{
return strokeType.getStrokeThickness() > 0.0f && ! strokeFill.isInvisible();
}
bool DrawableShape::refreshFillTypes (const FillAndStrokeState& newState,
Expression::EvaluationContext* /*nameFinder*/,
ImageProvider* imageProvider)
{
bool hasChanged = false;
{
const FillType f (newState.getMainFill (getParent(), imageProvider));
if (mainFill != f)
{
hasChanged = true;
mainFill = f;
}
}
{
const FillType f (newState.getStrokeFill (getParent(), imageProvider));
if (strokeFill != f)
{
hasChanged = true;
strokeFill = f;
}
}
return hasChanged;
}
void DrawableShape::writeTo (FillAndStrokeState& state, ImageProvider* imageProvider, UndoManager* undoManager) const
{
state.setMainFill (mainFill, 0, 0, 0, imageProvider, undoManager);
state.setStrokeFill (strokeFill, 0, 0, 0, imageProvider, undoManager);
state.setStrokeType (strokeType, undoManager);
}
//==============================================================================
void DrawableShape::paint (Graphics& g)
{
transformContextToCorrectOrigin (g);
g.setFillType (mainFill);
g.fillPath (path);
if (isStrokeVisible())
{
g.setFillType (strokeFill);
g.fillPath (strokePath);
}
}
void DrawableShape::pathChanged()
{
strokeChanged();
}
void DrawableShape::strokeChanged()
{
strokePath.clear();
strokeType.createStrokedPath (strokePath, path, AffineTransform::identity, 4.0f);
setBoundsToEnclose (getDrawableBounds());
repaint();
}
const Rectangle<float> DrawableShape::getDrawableBounds() const
{
if (isStrokeVisible())
return strokePath.getBounds();
else
return path.getBounds();
}
bool DrawableShape::hitTest (int x, int y) const
{
const float globalX = x - originRelativeToComponent.getX();
const float globalY = y - originRelativeToComponent.getY();
return path.contains (globalX, globalY)
|| (isStrokeVisible() && strokePath.contains (globalX, globalY));
}
//==============================================================================
const Identifier DrawableShape::FillAndStrokeState::type ("type");
const Identifier DrawableShape::FillAndStrokeState::colour ("colour");
const Identifier DrawableShape::FillAndStrokeState::colours ("colours");
const Identifier DrawableShape::FillAndStrokeState::fill ("Fill");
const Identifier DrawableShape::FillAndStrokeState::stroke ("Stroke");
const Identifier DrawableShape::FillAndStrokeState::path ("Path");
const Identifier DrawableShape::FillAndStrokeState::jointStyle ("jointStyle");
const Identifier DrawableShape::FillAndStrokeState::capStyle ("capStyle");
const Identifier DrawableShape::FillAndStrokeState::strokeWidth ("strokeWidth");
const Identifier DrawableShape::FillAndStrokeState::gradientPoint1 ("point1");
const Identifier DrawableShape::FillAndStrokeState::gradientPoint2 ("point2");
const Identifier DrawableShape::FillAndStrokeState::gradientPoint3 ("point3");
const Identifier DrawableShape::FillAndStrokeState::radial ("radial");
const Identifier DrawableShape::FillAndStrokeState::imageId ("imageId");
const Identifier DrawableShape::FillAndStrokeState::imageOpacity ("imageOpacity");
DrawableShape::FillAndStrokeState::FillAndStrokeState (const ValueTree& state_)
: Drawable::ValueTreeWrapperBase (state_)
{
}
const FillType DrawableShape::FillAndStrokeState::getMainFill (Expression::EvaluationContext* nameFinder,
ImageProvider* imageProvider) const
{
return readFillType (state.getChildWithName (fill), 0, 0, 0, nameFinder, imageProvider);
}
ValueTree DrawableShape::FillAndStrokeState::getMainFillState()
{
ValueTree v (state.getChildWithName (fill));
if (v.isValid())
return v;
setMainFill (Colours::black, 0, 0, 0, 0, 0);
return getMainFillState();
}
void DrawableShape::FillAndStrokeState::setMainFill (const FillType& newFill, const RelativePoint* gp1, const RelativePoint* gp2,
const RelativePoint* gp3, ImageProvider* imageProvider, UndoManager* undoManager)
{
ValueTree v (state.getOrCreateChildWithName (fill, undoManager));
writeFillType (v, newFill, gp1, gp2, gp3, imageProvider, undoManager);
}
const FillType DrawableShape::FillAndStrokeState::getStrokeFill (Expression::EvaluationContext* nameFinder,
ImageProvider* imageProvider) const
{
return readFillType (state.getChildWithName (stroke), 0, 0, 0, nameFinder, imageProvider);
}
ValueTree DrawableShape::FillAndStrokeState::getStrokeFillState()
{
ValueTree v (state.getChildWithName (stroke));
if (v.isValid())
return v;
setStrokeFill (Colours::black, 0, 0, 0, 0, 0);
return getStrokeFillState();
}
void DrawableShape::FillAndStrokeState::setStrokeFill (const FillType& newFill, const RelativePoint* gp1, const RelativePoint* gp2,
const RelativePoint* gp3, ImageProvider* imageProvider, UndoManager* undoManager)
{
ValueTree v (state.getOrCreateChildWithName (stroke, undoManager));
writeFillType (v, newFill, gp1, gp2, gp3, imageProvider, undoManager);
}
const PathStrokeType DrawableShape::FillAndStrokeState::getStrokeType() const
{
const String jointStyleString (state [jointStyle].toString());
const String capStyleString (state [capStyle].toString());
return PathStrokeType (state [strokeWidth],
jointStyleString == "curved" ? PathStrokeType::curved
: (jointStyleString == "bevel" ? PathStrokeType::beveled
: PathStrokeType::mitered),
capStyleString == "square" ? PathStrokeType::square
: (capStyleString == "round" ? PathStrokeType::rounded
: PathStrokeType::butt));
}
void DrawableShape::FillAndStrokeState::setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager)
{
state.setProperty (strokeWidth, (double) newStrokeType.getStrokeThickness(), undoManager);
state.setProperty (jointStyle, newStrokeType.getJointStyle() == PathStrokeType::mitered
? "miter" : (newStrokeType.getJointStyle() == PathStrokeType::curved ? "curved" : "bevel"), undoManager);
state.setProperty (capStyle, newStrokeType.getEndStyle() == PathStrokeType::butt
? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager);
}
const FillType DrawableShape::FillAndStrokeState::readFillType (const ValueTree& v, RelativePoint* const gp1, RelativePoint* const gp2, RelativePoint* const gp3,
Expression::EvaluationContext* const nameFinder, ImageProvider* imageProvider)
{
const String newType (v[type].toString());
if (newType == "solid")
{
const String colourString (v [colour].toString());
return FillType (Colour (colourString.isEmpty() ? (uint32) 0xff000000
: (uint32) colourString.getHexValue32()));
}
else if (newType == "gradient")
{
RelativePoint p1 (v [gradientPoint1]), p2 (v [gradientPoint2]), p3 (v [gradientPoint3]);
ColourGradient g;
if (gp1 != 0) *gp1 = p1;
if (gp2 != 0) *gp2 = p2;
if (gp3 != 0) *gp3 = p3;
g.point1 = p1.resolve (nameFinder);
g.point2 = p2.resolve (nameFinder);
g.isRadial = v[radial];
StringArray colourSteps;
colourSteps.addTokens (v[colours].toString(), false);
for (int i = 0; i < colourSteps.size() / 2; ++i)
g.addColour (colourSteps[i * 2].getDoubleValue(),
Colour ((uint32) colourSteps[i * 2 + 1].getHexValue32()));
FillType fillType (g);
if (g.isRadial)
{
const Point<float> point3 (p3.resolve (nameFinder));
const Point<float> point3Source (g.point1.getX() + g.point2.getY() - g.point1.getY(),
g.point1.getY() + g.point1.getX() - g.point2.getX());
fillType.transform = AffineTransform::fromTargetPoints (g.point1.getX(), g.point1.getY(), g.point1.getX(), g.point1.getY(),
g.point2.getX(), g.point2.getY(), g.point2.getX(), g.point2.getY(),
point3Source.getX(), point3Source.getY(), point3.getX(), point3.getY());
}
return fillType;
}
else if (newType == "image")
{
Image im;
if (imageProvider != 0)
im = imageProvider->getImageForIdentifier (v[imageId]);
FillType f (im, AffineTransform::identity);
f.setOpacity ((float) v.getProperty (imageOpacity, 1.0f));
return f;
}
jassert (! v.isValid());
return FillType();
}
namespace DrawableShapeHelpers
{
const Point<float> calcThirdGradientPoint (const FillType& fillType)
{
const ColourGradient& g = *fillType.gradient;
const Point<float> point3Source (g.point1.getX() + g.point2.getY() - g.point1.getY(),
g.point1.getY() + g.point1.getX() - g.point2.getX());
return point3Source.transformedBy (fillType.transform);
}
}
void DrawableShape::FillAndStrokeState::writeFillType (ValueTree& v, const FillType& fillType,
const RelativePoint* const gp1, const RelativePoint* const gp2, const RelativePoint* gp3,
ImageProvider* imageProvider, UndoManager* const undoManager)
{
if (fillType.isColour())
{
v.setProperty (type, "solid", undoManager);
v.setProperty (colour, String::toHexString ((int) fillType.colour.getARGB()), undoManager);
}
else if (fillType.isGradient())
{
v.setProperty (type, "gradient", undoManager);
v.setProperty (gradientPoint1, gp1 != 0 ? gp1->toString() : fillType.gradient->point1.toString(), undoManager);
v.setProperty (gradientPoint2, gp2 != 0 ? gp2->toString() : fillType.gradient->point2.toString(), undoManager);
v.setProperty (gradientPoint3, gp3 != 0 ? gp3->toString() : DrawableShapeHelpers::calcThirdGradientPoint (fillType).toString(), undoManager);
v.setProperty (radial, fillType.gradient->isRadial, undoManager);
String s;
for (int i = 0; i < fillType.gradient->getNumColours(); ++i)
s << ' ' << fillType.gradient->getColourPosition (i)
<< ' ' << String::toHexString ((int) fillType.gradient->getColour(i).getARGB());
v.setProperty (colours, s.trimStart(), undoManager);
}
else if (fillType.isTiledImage())
{
v.setProperty (type, "image", undoManager);
if (imageProvider != 0)
v.setProperty (imageId, imageProvider->getIdentifierForImage (fillType.image), undoManager);
if (fillType.getOpacity() < 1.0f)
v.setProperty (imageOpacity, fillType.getOpacity(), undoManager);
else
v.removeProperty (imageOpacity, undoManager);
}
else
{
jassertfalse;
}
}
END_JUCE_NAMESPACE