Converting a Line Graph to a Filled Polygon

At the moment I’ve got a line graph drawing fine. However, I was thinking that it would look nicer filled with a colour.

I’ve read about “RGBSurface.FloodFill” and that could work but there are other program dependent issues that would make this approach unworkable (I think).

I understand that Graphic.DrawPolygon will “auto-fill” with reference to the x,y origin. Unfortunately “PolyLine” is not available where one can “close” the path ie. Adobe Illustrator etc.

Here’s an image of a typical graph where I’d like everything under the red line filled with a colour:

Here’s the code that creates it:

[code]

GraphPicture.Graphics.ForeColor = kGraphBase // Colour for the background
GraphPicture.Graphics.FillRect 0,0,me.Width,me.Height

Dim totalSamples as Integer
totalSamples = recordedGramsArray.Ubound

Dim forceY as integer
Dim X1,Y1,X2,Y2 as Double

GraphPicture.Graphics.ForeColor=kRedDark //Colour for the Graph itself
GraphPicture.Graphics.PenWidth=2 //thicker
GraphPicture.Graphics.PenHeight=2 //thicker

'-------graphing
For timeX as Integer = 0 to totalSamples-1

forceY = timeX
X1 = timeX * standardGridX * graphScaleFactorX
Y1 = kGraphHeight - recordedGramsArray(forceY) * standardGridY * graphScaleFactorY
X2 = (timeX+1) * standardGridX * graphScaleFactorX
Y2 = kGraphHeight - recordedGramsArray(forceY+1) * standardGridY * graphScaleFactorY

//Draw the final graph
GraphPicture.Graphics.DrawLine(X1, Y1, X2, Y2)

Next timeX

//Show the X/Y (Time/Grams) Value Labels
EvaluateAndShowXYlabels

//Update the Canvas
cnvGRAPH.Backdrop = GraphPicture[/code]

This is pretty much a “thought-fart”, as in it has nothing to do with the final calculations, but just makes it look nicer.

Nevertheless, it’s something that’s annoying and distracts me from finishing the main parts of my code. Perhaps an excuse for procrastination?

You realise you can close the shape just by repeating the first point at the end?

Keh?

Well, youre drawing from 0,0 to 20,30 to 60,100, to 200,50
Then you stop, and what you see is a series of lines

Create a figureshape instead

heres a triangle… it has two lines and it fills the enclosure
In your case the first line is points 0 and 1
the second line is points 1 and 2
a third would be 2 and 3 and so on…

Dim fx as New FigureShape fx.AddLine 0, 100, 50, 0 fx.AddLine 50, 0, -50, 0 fx.Border = 100 // opaque border fx.BorderColor = &cFF0000 fx.FillColor = &cFF0000 g.DrawObject fx, 100,100

I didn’t think that was the case with “DrawLine”?

You’re talking about Obect2D?

Yup.

something like:

[code]Dim fx as New FigureShape

For timeX as Integer = 0 to totalSamples-1

forceY = timeX
X1 = timeX * standardGridX * graphScaleFactorX
Y1 = kGraphHeight - recordedGramsArray(forceY) * standardGridY * graphScaleFactorY
X2 = (timeX+1) * standardGridX * graphScaleFactorX
Y2 = kGraphHeight - recordedGramsArray(forceY+1) * standardGridY * graphScaleFactorY

fx.AddLine x1,y1,x2,y2

Next timeX

GraphPicture.Graphics.DrawObject fx, 100,100 //change the 100,100 to something appropriate[/code]

caveat: this is just typed from memory… not tested code…

Ahhh Yes, now I remember.

The problem I had was with scaling Object2D. The line thickness was also scaled, which is exactly as it should be, but didn’t suit my purpose.

Now that I don’t need to scale, it may well be worth looking into Object2D again.

Once resized, set back the BorderWidth you want ?

Hmmm - perhaps so.

I do seem to remember that there was an issue with creating a “picture object” using Object2D then displaying it in the paint event of the canvas.

Probably just my issue though.

I do not remember anything about that.

But if you want to avoid troubles, you can create an offscreen Picture, draw there your Object2D, then in the Paint event you draw the offscreen Picture:

The code below will draw teh offscreen Picture if one valid instance exists (avoid crash):

If MyOffscreenPict <> Nil Then // Place here the code to draw the offscreen Picture End If

There isnt.
If GraphPicture.Graphics.DrawLine(X1, Y1, X2, Y2) works, then
GraphPicture.Graphics.Drawobject … will work too

The canvas draws the Graphpicture

You should be able to use the Graphics.FillPolygon and Graphics.DrawPolygon methods, without using Object2D. Something like this:

Dim PointsArray() as Integer
PointsArray.Append 0 // PointsArray is 1-based, zero element is dummy

// Fill the array
For timeX as Integer = 0 to totalSamples //not I changed the upper limit
    
  forceY = timeX
  PointsArray.Append timeX * standardGridX * graphScaleFactorX
  PointsArray.Append kGraphHeight - recordedGramsArray(forceY) * standardGridY * graphScaleFactorY
    
Next timeX
    
 //Draw the final graph
GraphPicture.ForeColor = RGB(...) // color for the line
GraphPicture.Graphics.DrawPolygon PointsArray
GraphPicture.ForeColor = RGB(...) // color for the fill
GraphPicture.Graphics.FillPolygon PointsArray

Collecting the points and doing a FillPolygon and DrawPolygon (as suggested above) might be your best bet.
Attempting to use FloodFill, you would need to NOT draw your grid first, otherwise it won’t be able to flood the entire area.

Thanks so much everyone, I’ve finally got this sorted. :smiley:

Yeah, that makes me laugh Dave because that’s exactly what I did before reading your post. :S
I got a nice little coloured rectangle! Thing is, I should have known that was going to happen considering I use PhotoShop often. I moved the grid to the top layer so perhaps FloodFill could work, but there was always going to be the issue of determining the xy point to ensure reliability. Nice to know that FloodFill is there though.

I used the FigureShape method similar to what Jeff posted. The collecting points version that Urs posted above looks very interesting - I wonder what the difference would be? All seems to be working fine though, so unless there is a compelling reason to change, I think I will leave well enough alone.

As I said, the code used is pretty much as Jeff posted in post #7 (thanks Jeff):

[code]Dim fx as New FigureShape
fx.FillColor = kGraphColour

For timeX as Integer = 0 to totalSamples-1

forceY = timeX
X1 = timeX * standardGridX * graphScaleFactorX
Y1 = kGraphHeight - recordedGramsArray(forceY) * standardGridY * graphScaleFactorY
X2 = (timeX+1) * standardGridX * graphScaleFactorX
Y2 = kGraphHeight - recordedGramsArray(forceY+1) * standardGridY * graphScaleFactorY

//Draw each point of the polygon
fx.AddLine (X1, Y1, X2, Y2)

Next timeX

GraphPicture.Graphics.DrawObject(fx, 0, 0) // Draw the Graph to the Picture

DrawGrid //draw the grid lines[/code]

And the final graph:

Graph colours aside, one thing I don’t like is the way the grid is overlayed on the graph. It doesn’t look good, and no amount of fiddling with colours makes it have the subtle look I’m after.

I’m aware that we can have transparency (.fill I believe) but it doesn’t have the effect I want.

I’ll start a new thread regarding this, but I’ll post it here first. This is what I mean:

This was drawn using Adobe Illustrator.

Fig. 1 shows 15% black simply laid over the top (too stark)
Fig. 2 shows 15% black with 15% transparency (too light and can’t see lines over the yellow)
Fig. 3 shows 15% black with 50% transparency (better, but too light in the blue)
Fig. 4 shows 15% black multiplied (perfect! - black is added to the underlying colours)

Is possible to achieve the look of Fig. 4 without creating complicated masks?

On the Mac with a few declares yes: CGContext (which is the native macOS object behind Xojo’s graphics class) can handle all kinds of blend modes: https://developer.apple.com/reference/coregraphics/cgcontext/1455994-setblendmode

For the other systems, I don’t know. (I’ve seen you’re using Windows. There probably is a similar system API call somewhere …)

Thanks Ulrich. I’ve have a look into Windows API.

Cheers.