Jump to content
Paint.NET 5.1 is now available! ×

Recommended Posts

Posted

I know @toe_head2001 has an excellent graph paper plugin, however, it doesn't include labels and shape drawing.

 

So, I rolled my own.

 

I won't publish this as his is better than mine. But, I thought someone might find it interesting as it is GPU accelerated. 

 

CodeLab source for BBGraphPaper.cs :

 

Spoiler
// Name: Graph Paper
// Submenu: Render
// Author: BoltBait
// Title: BoltBait's Graph Paper v1.1
// Version: 1.1
// Desc: Render Graph Paper
// Keywords: graph paper
// URL: https://BoltBait.com/pdn
// NSC

#region UICode
IntSliderControl cellSize = 20; // [10,50] Cell Size
IntSliderControl offsetWidth = 0; // [-10,10] Center Offset X / Y
IntSliderControl offsetHeight = 0; // [-10,10] 
MultiLineTextboxControl rawPoints = ""; // [32767] Points (one per line) x,y
LabelComment comment = "You can close the shape by entering a comma alone on a line. You can start a new shape by entering a blank line."; // 
CheckboxControl labelPoints = false; // Label points
#endregion

protected override unsafe void OnDraw(IDeviceContext deviceContext)
{
    deviceContext.AntialiasMode = AntialiasMode.PerPrimitive;
    deviceContext.TextAntialiasMode = TextAntialiasMode.Grayscale;
    deviceContext.UseTextRenderingMode(TextRenderingMode.Outline);

    IDirect2DFactory d2dFactory = this.Services.GetService<IDirect2DFactory>();
    IDirectWriteFactory textFactory = this.Services.GetService<IDirectWriteFactory>();

    IGdiFontMap fm = textFactory.GetGdiFontMap();
    FontProperties fp = fm.TryGetFontProperties("Courier New");

    ITextFormat textFormat = textFactory.CreateTextFormat(fp.FontFamilyName, null, fp.Weight, fp.Style, fp.Stretch, cellSize/2.5f);

    deviceContext.Clear(LinearColors.White);

    RectFloat sourceBounds = new RectFloat(Point2Float.Zero, Environment.Document.Size);
            
    float CenterX = sourceBounds.Center.X + (offsetWidth * cellSize);
    float CenterY = sourceBounds.Center.Y - (offsetHeight * cellSize);

    float FirstX = CenterX % cellSize;
    float FirstY = CenterY % cellSize;
    ISolidColorBrush grayBrush = deviceContext.CreateSolidColorBrush(LinearColors.Gray);
    ISolidColorBrush blackBrush = deviceContext.CreateSolidColorBrush(LinearColors.DarkGray);
    ISolidColorBrush blueBrush = deviceContext.CreateSolidColorBrush(LinearColors.SkyBlue);

    // draw vertical lines

    for (float x = FirstX; x < sourceBounds.Width; x += cellSize)
    {
        if (x == CenterX)
        {
            // Y axis
            deviceContext.DrawLine(new Point2Float(x,0),new Point2Float(x,sourceBounds.Bottom), blueBrush, 3);
        }
        else
        {
            if ((x-CenterX) % (cellSize * 5) == 0)
            {
                // make every 5 lines darker
                deviceContext.DrawLine(new Point2Float(x,0),new Point2Float(x,sourceBounds.Bottom), blackBrush, 2);
            }
            else 
            {
                // normal line
                deviceContext.DrawLine(new Point2Float(x,0),new Point2Float(x,sourceBounds.Bottom), grayBrush, 1, d2dFactory.CreateStrokeStyle(new StrokeStyleProperties(CapStyle.Flat, CapStyle.Flat, CapStyle.Flat, LineJoin.Round, 10f, PaintDotNet.Direct2D1.DashStyle.Dash, 0f, StrokeTransformType.Normal)));
            }
        }
        // X labels
        RectFloat topTextOffset = RectFloat.Offset(sourceBounds, x+1, CenterY);
        deviceContext.DrawText(((x-CenterX) / (cellSize)).ToString(),textFormat,topTextOffset, blueBrush);
    }

    // draw horizontal lines

    for (float y = FirstY; y < sourceBounds.Height; y += cellSize)
    {
        if (y == CenterY)
        {
            // X axis
            deviceContext.DrawLine(new Point2Float(0,y),new Point2Float(sourceBounds.Right,y), blueBrush, 3);
        }
        else
        {
            if ((y-CenterY) % (cellSize * 5) == 0)
            {
                // make every 5 lines darker
                deviceContext.DrawLine(new Point2Float(0,y),new Point2Float(sourceBounds.Right,y), blackBrush, 2);
            }
            else
            {
                // normal line
                deviceContext.DrawLine(new Point2Float(0,y),new Point2Float(sourceBounds.Right,y), grayBrush, 1, d2dFactory.CreateStrokeStyle(new StrokeStyleProperties(CapStyle.Flat, CapStyle.Flat, CapStyle.Flat, LineJoin.Round, 10f, PaintDotNet.Direct2D1.DashStyle.Dash, 0f, StrokeTransformType.Normal)));
            }
            // Y labels
            RectFloat topTextOffset = RectFloat.Offset(sourceBounds, CenterX +1, y);
            deviceContext.DrawText(((CenterY-y) / (cellSize)).ToString(),textFormat,topTextOffset, blueBrush);
        }
    }

    // Draw shapes on the graph paper
    List<string> points = (rawPoints + "\n").Split('\n').ToList();
    float previousX = 0, previousY = 0;
    bool prev = false;
    bool first = true;
    int brushIndex = 2;
    float firstX = 0, firstY = 0;
    char label = 'A';
    List<Point2Float> currentShape = new List<Point2Float>();

    FontProperties labelFP = fm.TryGetFontProperties("Arial");
    
    // use your font properties to create a font
    ITextFormat labelTextFormat = textFactory.CreateTextFormat(
        labelFP.FontFamilyName, // font family name
        null,
        FontWeight.Bold, // font weight
        FontStyle.Normal, // font style
        labelFP.Stretch, // how to stretch the font
        12); // size in points


    IReadOnlyList<ColorBgra> DefaultColors = Services.GetService<IPalettesService>().DefaultPalette; 
    //IReadOnlyList<ColorBgra> DefaultColors = Services.GetService<IPalettesService>().CurrentPalette; 

    foreach (string rawPoint in points)
    {
        string myPoint = rawPoint.Trim();
        // process each line in the text box
        if (myPoint.Contains(","))
        {
            float x,y;

            ISolidColorBrush shapeBrush = deviceContext.CreateSolidColorBrush(DefaultColors[brushIndex]);
    
            if (myPoint == ",")
            {
                x = firstX;
                y = firstY;
            }
            else
            {
                if (!float.TryParse(myPoint.Substring(0,myPoint.IndexOf(",")), out x))
                {
                    //x = 0;
                }
                if (!float.TryParse(myPoint.Substring(myPoint.IndexOf(",")+1), out y))
                {
                    //y = 0;
                }
                else
                {
                    y = -y;
                }
                currentShape.Add(new Point2Float(x,y));
            }

            if (first)
            {
                firstX = x;
                firstY = y;
                first = false;
            }

            // draw a dot at our specified x,y position
            deviceContext.DrawRoundedRectangle(new 
                RoundedRect(
                    CenterX+(cellSize * x)-1.5f,
                    CenterY+(cellSize * y)-1.5f,
                    3, 3, 3
                ),
                shapeBrush,3);

            // if we have a previous dot, draw a line between them
            if (prev)
            {
                deviceContext.DrawLine(
                    new Point2Float(CenterX+(cellSize * x),CenterY+(cellSize * y)),
                    new Point2Float(CenterX+(cellSize * previousX),CenterY+(cellSize * previousY)),
                    shapeBrush, 2);
            }
            previousX = x;
            previousY = y;
            prev = true;
        }
        else
        {
            if (labelPoints)
            {
                // Determine the center of our shape
                float totalX = 0;
                float totalY = 0;
                foreach (Point2Float point in currentShape)
                {
                    totalX += point.X;
                    totalY += point.Y;
                }
                float shapeCenterX = totalX / currentShape.Count;
                float shapeCenterY = totalY / currentShape.Count;

                // Draw point labels
                foreach (Point2Float point in currentShape)
                {
                    float differenceInX = shapeCenterX - point.X;
                    float differenceInY = shapeCenterY - point.Y;

                    float distance = (float)Math.Sqrt((shapeCenterX - point.X)*(shapeCenterX - point.X) + (shapeCenterY - point.Y)*(shapeCenterY - point.Y));

                    if (distance == 0)
                    {
                        differenceInX = 1;
                        differenceInY = 1;
                        distance = 1;
                    }

                    differenceInX /= distance;
                    differenceInY /= distance;
                    
                    ISolidColorBrush shapeBrush = deviceContext.CreateSolidColorBrush(DefaultColors[brushIndex]);

                    float myX = CenterX+(cellSize * (point.X - (differenceInX * 0.5f)))-4.5f;
                    float myY = CenterY+(cellSize * (point.Y - (differenceInY * 0.5f)))-4.5f;

                    deviceContext.DrawText(label.ToString(),labelTextFormat,new RectFloat(myX,myY,100,100),shapeBrush,DrawTextOptions.None);

                    if (label == 'Z')
                    {
                        label = 'a';
                    }
                    else if (label == 'z')
                    {
                        label = 'A';
                    } 
                    else
                    {
                        label++;
                    }
                }
            }
            // start the next shape
            prev = false;
            first = true;
            brushIndex += 1;
            currentShape.Clear();
        }
    }
}

 

 

 

image.png

 

I use this when helping my kids with their geometry homework.  Since the script is just for my own use, it doesn't have a lot of error checking.  And, I haven't bothered to go back and clean it up.  // you've been warned!

 

Each shape gets a different color.   The first shape gets color index 2, red.  Then, each shape after that gets the next color from the default color palette.

 

EDIT:  Download v2.1 below!

 

  • Like 3
  • Upvote 3
Posted

Nice!

 

I wanted to have a play with this, but I'm unable to build the DLL from the source (using Codelab 6.11).

 

image.png

Appears to Render fine from Codelab ...but has build errors.

 

Edit: Is there a clever way to copy the Error List in Codelab?

Posted
10 minutes ago, BoltBait said:

Make sure the file name does not have a space in it.

 

You'd think by now I'd have learned this ^.  Solved :)

 

Thank you @BoltBait

Posted

Published v1.1 with ability to label points.

 

Basically, it calculates the center of each shape, follows a ray from the center of the object through a specific vertex to slightly beyond it and draws the label letter there.  So, for regular shapes, the labels will always be outside the shape. For irregular shapes, ymmv. :P 

 

Posted

@BoltBait I followed the instructions throughout (using VS 2022) and I'm getting these error messages in Codelab 6.11:

 

GraphPaperErrorList.png

A lot has changed since I was messing with code. Can you guide me?

Posted
On 2/6/2024 at 5:47 AM, lynxster4 said:

Can you guide me?

 

Before pasting the script into CodeLab, do this:

 

Run CodeLab.

 

File > New > GPU Drawing Effect:

image.png

Only THEN should you select all and paste into the CodeLab editor.

 

OR

 

You could use your favorite text editor (Notepad++) to save the source code to a file BBGraphPaper.cs and then run CodeLab and open it from there.

 

HISTORY:

 

Way back in the day, there used to only be one type of CodeLab script. So, pasting a script into the editor just worked. Now, there are 4+ different types of CodeLab scripts (Classic, Bitmap, GPU Image, and GPU Drawing--not to mention FileTypes and Shapes).  In order to compile your script, CodeLab needs to know which type of script it is.  This detection happens when you open a new window (through File > New > ...) or when you open a file from disk.  Once determined, the effect type can't be changed.  In the future, CodeLab may get better at this, but for now, that's just the way it works.

 

EDIT: CodeLab v6.12 no longer has this problem. It will detect what type of script you're trying to compile and adjust itself accordingly.

 

  • Like 1
Posted
1 hour ago, BoltBait said:

Run CodeLab.

 

File > New > GPU Drawing Effect:

 

Thank you, BB!  Worked perfectly. This is a neat plugin being able to make geometric shapes.  🤩

Posted

Version 2.0!

 

image.png

By turning off "Draw lines connecting points", changing the "Label points" to numeric, I believe someone could use this to create dot-to-dot puzzles for kids.

 

Leave the grid "on" while creating your dot-to-dot, then turn it off for the final render.

 

If you notice in the upper left corner, I did add the Perimeter or Distance.  I also found an algorithm for calculating Area... it can get confused if the shape's lines cross, but otherwise works pretty well.  According to the site:

 

image.png

 

Screenshots:

 

image.png    image.png

 

Download:

 

<Snip!> Download v2.1 in a post below...

 

 

 

Source Code:

 

Spoiler

I had to switch from CodeLab script into a full project to add the tabs into the UI.

 

I'll leave the CodeLab script for v1.1 in the first post, but it's too long now for posting.  Sorry.

 

 

Plans:

 

I'm pretty much done tinkering with this. I probably won't add it into my plugin pack as it's pretty specialized.  But, you can always download it here.

 

Enjoy! B) :beer: 

 

  • Like 2
  • Upvote 2
Posted

Version 2.1!

 

Just a little clean up of the UI...

 

image.png

Added a few more controls to change colors and other options.

 

My son really finds this helpful in his Geometry class. Often, problems will be something like, "You have a triangle with points A(-5,0) B(2,6) and C(6,-2)..." etc. This gives him a quick way to create his drawing for working the problem.  This is why I wrote this plugin.

 

Another use for this plugin is to make simple dot-to-dot puzzles for children.  Here's one I made:

 

image.png

Spoiler

0,0
-5,0
0.5,10
6,0
1,0
1,-2
7,-2
6,-4
-5,-4
-6,-2
0,-2
,
 

 

Anyway, NOW I'm done tinkering with this!

 

BBGraphPaper.zip

 

Enjoy. :beer: B) 

 

  • Like 3
  • Upvote 3

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...