Best approach to draw an oblique design?


#1

Simple question, what approach would do you use to draw a design like this with Juce?

 


#2

Create one big Path object, and add the stripes with addLine(). It'll be more efficient to draw it all as one Path than drawing each stripe individually.


#3

Here is a class I just wrote for you:

class ObliqueStripRectangle : public Component, private ComponentListener
{
public:
    ObliqueStripRectangle(const int stripWidth, const int alternateWidth, const Colour& stripColour = Colours::orange, const Colour& bkColour = Colours::black)
        : bk(bkColour)
        , fg(stripColour)
        , sw(stripWidth)
        , aw(alternateWidth)
        , dirty(true)
    {
        jassert(stripWidth > 0 && alternateWidth >= 0);
        setInterceptsMouseClicks(false, false);
        addComponentListener(this);
    }
    ~ObliqueStripRectangle(){ patternCache = nullptr; }
    void paint(Graphics& g) override
    {
        if (dirty)
        {
            patternCache = nullptr;
            const int patternWidth = sw + aw;
            patternCache = new Image(Image::PixelFormat::ARGB, patternWidth, patternWidth, false);
            Path p;
            p.startNewSubPath(0, (float)patternWidth);
            p.lineTo((float)patternWidth, 0);
            p.lineTo((float)patternWidth - sw, 0);
            p.lineTo(0, (float)patternWidth - sw);
            p.closeSubPath();
            p.startNewSubPath((float)patternWidth - sw, (float)patternWidth);
            p.lineTo((float)patternWidth, (float)patternWidth - sw);
            p.lineTo((float)patternWidth, (float)patternWidth);
            p.closeSubPath();
            
            juce::Graphics ig(*patternCache);
            ig.fillAll(bk);
            ig.setColour(fg);
            ig.fillPath(p);
            dirty = false;
        }
        g.setTiledImageFill(*patternCache, 0, 0, 1.0f);
        g.fillAll();
    }
protected:
    int sw, aw;
    bool dirty;
    Colour bk;
    Colour fg;
    ScopedPointer<Image> patternCache;
    void componentMovedOrResized(Component& component,
        bool wasMoved,
        bool wasResized) override
    {
        if (wasResized)
        {
            dirty = true;
            repaint();
        }
    }
};

Usage:

ObliqueStripRectangle shape(stripWidth, alternateWidth, stripColour, bkColour);
shape.setSize(w, h);
addAndMakeVisible(shape);

 

and the way to achive this:

1, make a tile first like this:

2, then set it as tile image for the graphics context

    g.setTiledImageFill

3, and then just call the fillAll method...

 

and simple test case:

class testWin : public TopLevelWindow
{
public:
    testWin()
        : TopLevelWindow("test", true)
    {
        setOpaque(true);
        centreWithSize(800, 600);
        setVisible(true);
        testShape = new ObliqueStripRectangle(20, 20);
        testShape->setSize(getWidth(), getHeight());
        addAndMakeVisible(testShape);
    }
    void paint(Graphics& g) override {
        g.fillAll(Colours::grey);
    }
private:
    ScopedPointer<ObliqueStripRectangle> testShape;
};

#4

Thank you, I'm going to try it right now :)