Jump to content

Recommended Posts

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 2
Link to comment
Share on other sites

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?

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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 

 

Link to comment
Share on other sites

2 hours ago, BoltBait said:

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

 

I thought that issue was solved a very long time ago.

 

Edit: Fixed in CodeLab. Must have been lost during the GPU refactoring.

Edited by toe_head2001

(September 25th, 2023)  Sorry about any broken images in my posts. I am aware of the issue.

bp-sig.png
My Gallery  |  My Plugin Pack

Layman's Guide to CodeLab

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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
Link to comment
Share on other sites

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

Link to comment
Share on other sites

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
Link to comment
Share on other sites

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 2
  • Upvote 3
Link to comment
Share on other sites

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