Study a fascinating color illusion in C#


[color illusion]

This example shows how you can generate a fascinating color illusion that tricks your brain trick into thinking that a black and white picture is actually colored. All of the pictures shown above are black and white. All except the one on the lower right also contain colored lines or dots. Your brain uses the color information to infer a color for the black and white areas and produce the color illusion.

If you zoom in on the pictures, you can see that the areas between the colored lines or dots are actually black and white. Here’s a closeup of the picture on the upper left. You can clearly see that the picture is actually black and white.


[color illusion]

To create the color illusion, you can simply draw colored lines, dots, characters, or other small colored areas on top of the black and white image.

This example starts with a full-color image and creates a monochrome version of it. It then uses the color version to draw lines or dots on the monochrome version.

The program uses the following PlaceColorAreas to draw the lines or dots.

// Place areas of color on an image.
private Bitmap PlaceColorAreas(Bitmap bm, Bitmap brush_bm,
    AreaTypes area_type,
    float spacing, float thickness)
{
    // Make a monochrome copy of the image.
    Bitmap result = ToMonochrome(bm);

    // Draw colored stripes on the image.
    using (Graphics gr = Graphics.FromImage(result))
    {
        using (Brush brush = new TextureBrush(brush_bm))
        {
            using (Pen pen = new Pen(brush, thickness))
            {
                int wid = brush_bm.Width;
                int hgt = brush_bm.Height;

                switch (area_type)
                {
                    case AreaTypes.Stripes:
                        // Vertical and horizontal stripes.
                        for (float x = 0; x < wid; x += spacing)
                            gr.DrawLine(pen, x, 0, x, hgt);
                        for (float y = 0; y < hgt; y += spacing)
                            gr.DrawLine(pen, 0, y, wid, y);
                        break;

                    case AreaTypes.DiagonalStripes:
                        // Diagonal stripes.
                        wid *= 2;
                        hgt *= 2;
                        spacing *= (float)Math.Sqrt(2);
                        for (float i = 0; i < wid; i += spacing)
                            gr.DrawLine(pen, i, 0, 0, i);
                        for (float i = -hgt; i < hgt; i += spacing)
                            gr.DrawLine(pen, 0, i, hgt, i + hgt);
                        break;

                    case AreaTypes.Dots:
                        // Dots.
                        for (float x = -spacing / 2f; x < wid;
                                x += spacing)
                            for (float y = -spacing / 2f; y < hgt;
                                    y += spacing)
                                gr.DrawEllipse(pen, x, y,
                                    thickness, thickness);
                        break;

                    case AreaTypes.Text:
                        // Text.
                        using (Font font = new Font("Arial", spacing))
                        {
                            GraphicsUnit unit = GraphicsUnit.Pixel;
                            gr.DrawString(lorem_ipsum, font, brush,
                                brush_bm.GetBounds(ref unit));
                        }
                        break;
                }
            }
        }
    }
    return result;
}

This method first calls the ToMonochrome method to create a monochrome version of the original full-color image. You can read about that method in my earlier post Use an ImageAttributes object to convert an image to monochrome in C#.

Next, the code creates a Graphics object associated with the monochrome image. The program then makes a TextureBrush based on the full-color version of the picture. Anything that the program draws with this brush will copy pieces of the full-color image onto the monochrome version.

The method then uses the brush to create a pen with a desired thickness. Now lines drawn with the pen will show pieces of the TextureBrush, which are bits of the color image.

Now the method gets down to drawing. Depending on the area_type parameter, the method will draw horizontal/vertical stripes, diagonal stripes, dots, or text.

For the stripes, the code simply uses nested loops to draw the appropriate lines. To draw dots, the program uses nested loops to place dots in a grid across the picture.

To draw text, the program simply draws the text stored in the string named lorem_ipsum to it fills the bitmap’s bounds.

That’s about all there is to this example. Download it to experiment with the thickness and spacing of the lines or dots. You can also modify the code to try out filling the image with text. Or try performing the same color illusion on some other images.

[example]

You’ll find that the results are much duller than the original images. The monochrome areas remove a lot of the image’s brightness and the lines don’t restore all of it. For example, the original full-color picture that this example uses is shown on the right.

I tried several experiments that adjusted the colors. For example, I tried increasing the colored image’s brightness to make up for the reduced color in the monochrome areas. The things I tried were a lot of work but didn’t really improve the result very much so I removed them and went back to the basic code shown here.

For more information on this color illusion, see the article This Photo Is Black And White. Here’s The Science That Makes Your Brain See Colour.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in drawing, graphics | Tagged , , , , , , , , , , | Leave a comment

Print circles centered inside other closely packed circles in C#


[print circles]

This example shows how to print circles that are arranged densely in a hexagonal pattern. I needed that to make a cardboard sheet for arranging cupcakes to make a cupcake cake. The holes needed to be 1″ in radius and spaced to allowed the 1.5″ radius cupcake tops to pack tightly. You may never need to do this (in fact, we didn’t end up using it), but you may find the program interesting.

I spent quite a while googling around looking for some sort of tem plate that I could use. Then I realized that this is actually pretty easy. In the end I wrote the program in less time than I wasted in fruitless googling.

A Little Math

[print circles]

The key to arranging the circles is the little bit of high school geometry shown in the picture on the right. Consider the larger circles drawn with dashed lines in the picture at the top of the post. If the circles are packed tightly, then the dashed triangle shown on the right is a 30-60-90 triangle. In that case, it has relative side lengths 1, 2, √3. The shortest side length in the figure has length R where R is the radius of the outer (dashed) circles. That means the triangle’s other sides have lengths R * 2 and R * √3.

That means to get from the upper left circle to the circle to the right on the next lower row, you increase the circle’s X coordinate by R and its Y coordinate by R * √3.

That’s all the math you need. The following sections explain how the program’s code uses those values to arrange the circles.

Launching the Print Out

When you enter the radii and click Preview, the following code executes.

private float InnerRadius, OuterRadius;

// Display the PrintPreviewDialog.
private void btnPreview_Click(object sender, EventArgs e)
{
    InnerRadius = 100 * float.Parse(txtInnerRadius.Text);
    OuterRadius = 100 * float.Parse(txtOuterRadius.Text);
    Form frm = ppdCircles as Form;
    frm.WindowState = FormWindowState.Maximized;
    ppdCircles.PrintPreviewControl.Zoom = 1.0;
    ppdCircles.PrintPreviewControl.UseAntiAlias = true;
    ppdCircles.ShowDialog();
}

The variables InnerRadius and OuterRadius hold the radii of the inner (solid) and outer (dashed) circles.

The btnPreview_Click event handler gets the values that you entered in the text boxes, multiplies them by 100, and saves them in those variables. It multiples them by 100 because the printer’s units are 100ths of inches. For example, a radius of 1.00 inches corresponds to 100 printer units.

Next, the code uses the ppdCircles component. That component is a PrintPreviewDialog that I added to the program’s form at design time. The code creates a variable of type Form and casts the dialog into it. That is possible because a PrintPreviewDialog is a kind of form.

Now the code can use the dialog’s form properties, which are hidden when you work with the dialog as a PrintPreviewDialog. The program uses the Form variable to set the dialog’s WindowState property to Maximized so the dialog begins maximized.

Next, the code sets the dialog’s zoom level to 1.0 so the printout will be displayed at full scale. (In the picture at the top of the post, I reduced the zoom to 50% so you could see more circles.) The code also makes the dialog use anti-aliasing so the print preview is smooth.

Finally, the code displays the dialog.

Printing

When the PrintPreviewDialog is displayed, it uses a PrintDocument object to generate the image that it will display. At design time, I added a PrintDocument object named pdocCircles to the form. I also set the PrintDocument object’s PrintDocument property to ppdocCircles so the dialog could find that object when needed.

When the dialog is displayed, it invokes the PrintDocument object’s PrintPage event handler. The following code shows the event handler used by this example to print circles.

// Generate the printout.
private void pdocCircles_PrintPage(object sender,
    System.Drawing.Printing.PrintPageEventArgs e)
{
    float cx = e.MarginBounds.Left + OuterRadius;
    float cy = e.MarginBounds.Top + OuterRadius;

    float dx = OuterRadius;
    float dy = (float)(OuterRadius * Math.Sqrt(3));

    using (Pen dashed_pen = new Pen(Color.Black))
    {
        dashed_pen.DashPattern = new float[] { 10, 10 };

        while (cy - OuterRadius < e.MarginBounds.Bottom)
        {
            // Make rectangles for the first circle.
            RectangleF outer_rect = new RectangleF(
                cx - OuterRadius,
                cy - OuterRadius,
                2 * OuterRadius,
                2 * OuterRadius);
            RectangleF inner_rect = new RectangleF(
                cx - InnerRadius,
                cy - InnerRadius,
                2 * InnerRadius,
                2 * InnerRadius);

            // Draw a row.
            while (outer_rect.Left < e.MarginBounds.Right)
            {
                e.Graphics.DrawEllipse(dashed_pen, outer_rect);
                e.Graphics.DrawEllipse(Pens.Black, inner_rect);
                outer_rect.X += 2 * OuterRadius;
                inner_rect.X += 2 * OuterRadius;
            }

            // Prepare for the next row.
            cx -= dx;
            if (cx < e.MarginBounds.Left) cx += 2 * OuterRadius;
            cy += dy;
        }
    }
}

This code sets variables cx and cy to the coordinates for the center of the first circle. It starts at the upper left corner of the page inside its margins. It then adds the radius of the outer circle to that point’s X and Y coordinates. The result will place the first circle so it just touches the upper left corner of the page margins.

Next, the code sets variables dx and dy to the distances that it must move a circle’s center to get to a circle in the next lower row. (See the earlier section “A Little Math.”)

The code then creates a dashed pen and sets its DashPattern to produce a nice dashed line. It then enters a while loop that lasts as long as the value cy is small enough that subtracting the outer circle’s radius keeps part of the circle within the page’s bounds. (Alternatively you could change this so it only draws complete circles, but I decided to print any circle that lies at least partly inside the page’s margins.)

Now the code makes two rectangles to draw the inner and outer circles centered at the point (cx, cy) and with the desired radii.

The program then enters a loop to draw the row. The loop lasts as long as a circle centered at (cx, cy) will fit at least partly within the page margins horizontally. Within the loop, the program draws the inner and outer circles and then adds 2 * OuterRadius to the rectangles’ X coordinates to move to the next circle in the row.

After it has finished drawing a row, the code subtracts dx from cx. Then if cx is less than the page’s left margin, the code adds 2 * OuterRadius to it so the first circle on the next row fits within the page margins.

Finally, the code adds dy to cy to move down to the next row.

When the PrintPage event handler ends, it can indicate whether there are more pages to print by setting e.HasMorePages to true or false. This example prints only one page, so it should set e.HasMorePages to false. That is the initial value when the event handler executes, so the code doesn’t need to change it.

Conclusion

That’s all you need to do to print circles that are packed tightly in a hexagonal pattern. You can print directly by calling the PrintDocument object’s Print method if you like. I usually just display a preview. That way I can test the program as many times as necessary without wasting paper. When everything looks correct, I press the preview dialog’s Print button to actually print.

It turned out that natural variation in the side of the cupcakes made them not fit tightly together, so we threw away the cardboard pattern that I had painstakingly made and we simply glued each cupcake down on a pan with a little bit of frosting. That was much easier and allowed more flexibility in positioning. Just use this post as an example showing how arrange circles, print circles, and use the PrintDocument and PrintPreviewDialog components to create a printout in general.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in drawing, graphics | Tagged , , , , , , | Leave a comment

Change a DataGridView entry when the user double-clicks a cell in C#

[DataGridView]

This post is in response to the following question:

Respected Sir,

I need help on how can i change the datagridview cell value by double click on that cell.

Let say cell contain value ‘N’ when i double click it will change to ‘Y’ .

First, note that by default the DataGridView control allows the user to edit its data. If you click on a DataGridView cell and then click on the cell again, the control allows you to edit that cell’s value. For example, see my post Build a DataTable and bind it to a DataGridView in C#.

For the question to make sense, we must have disabled the control’s natural editing capabilities. This example does that in its Form_Load event handler shown shortly. It then detects double-clicks and takes action depending on the cell that was clicked.

To make it easier for the user to know that particular cells are double-clickable, the program changes the cursor for those cells.

The following sections describe the pieces of the program that accomplish those tasks.

Setup

The following code shows the form’s Load event handler.

private void Form1_Load(object sender, EventArgs e)
{
    // Make the DataTable object.
    DataTable dt = new DataTable("Books");

    // Add columns to the DataTable.
    dt.Columns.Add("Title",
        System.Type.GetType("System.String"));
    dt.Columns.Add("Has Download",
        System.Type.GetType("System.Boolean"));
    dt.Columns.Add("Year",
        System.Type.GetType("System.Int32"));
    dt.Columns.Add("URL",
        System.Type.GetType("System.String"));
    
    // Add items to the table.
    dt.Rows.Add(new object[] { "Essential Algorithms", ... );
    dt.Rows.Add(new object[] { "WPF 3d", true, "2018", ... );
    dt.Rows.Add(new object[] { "The C# Helper Top 100", ... );
    dt.Rows.Add(new object[] { "Interview Puzzles Dissected", ... );

    // Make the DataGridView use the DataTable as its data source.
    dgvBooks.DataSource = dt;

    // Draw URLs in a blue, underlined font.
    dgvBooks.Columns["URL"].DefaultCellStyle.Font=
        new Font(dgvBooks.Font, FontStyle.Underline);
    dgvBooks.Columns["URL"].DefaultCellStyle.ForeColor =
        Color.Blue;

    // Set column widths.
    dgvBooks.AutoResizeColumns();

    // Do not allow automatic editing.
    dgvBooks.EditMode = DataGridViewEditMode.EditProgrammatically;
    dgvBooks.AllowUserToAddRows = false;
    dgvBooks.AllowUserToDeleteRows = false;
}

This code creates a DataTable to hold the data. It adds columns of different data types and then inserts data into the table.

Next, the program sets the DataGridView control’s DataSource property to the DataTable. At this point, the control allows the user to view and edit the data.

Next, the program sets the font for the DefaultCellStyle property in the URL column. It sets that property to a new font that is based on the DataGridView control’s font but with the underline style. That makes the values in that column appear underlined.

Similarly, the code sets the URL column’s default cell style to draw blue text so the URL values are drawn with a blue, underlined font.

The code then calls the DataGridView control’s AutoResizeColumns method to make the columns size themselves to fit their data.

Finally, the Load event handler sets the control’s EditMode so the user cannot edit the data value. It also prevents the user from adding new records or deleting existing ones.

Changing Cursors

When the mouse enters a cell, the following event handler sets the appropriate cursor.

// Display an appropriate cursor in the Has Download and URL columns.
private void dgvBooks_CellMouseEnter(object sender,
    DataGridViewCellEventArgs e)
{
    int col = e.ColumnIndex;
    if (col == -1) return;

    if (dgvBooks.Columns[col].Name == "Has Download")
        dgvBooks.Cursor = Cursors.Hand;
    else if (dgvBooks.Columns[col].Name == "URL")
        dgvBooks.Cursor = Cursors.UpArrow;
}

This event handler checks the column number below the mouse. If the mouse is to the left of the first column of data, then the column number is -1. In that case, the method exits. If the mouse is below the DataGridView control’s last row or to the right of the rightmost column, then this event is not raised.

If the column number is at least 0, the code checks the name of the column. If this is the Has Download column, the code sets the control’s cursor to Hand. If this is the URL column, the code sets the control’s cursor to UpArrow.

When the mouse moves off of a column, the following event handler executes.

// Restore the default cursor..
private void dgvBooks_CellMouseLeave(object sender,
    DataGridViewCellEventArgs e)
{
    if (dgvBooks.Cursor != Cursors.Default)
        dgvBooks.Cursor = Cursors.Default;
}

This code simply checks the DataGridView control’s current cursor and resets it to the default if it is not already the default.

Modifying Values

When the user double-clicks on a cell, the following event handler executes.

// Toggle the Has Download field. Open the URL.
private void dgvBooks_CellDoubleClick(object sender,
    DataGridViewCellEventArgs e)
{
    int col = e.ColumnIndex;
    if (col == -1) return;

    int row = e.RowIndex;

    // See which column this is.
    if (dgvBooks.Columns[col].Name == "Has Download")
    {
        // Toggle the value.
        bool value = (bool)dgvBooks.Rows[row].Cells[col].Value;
        dgvBooks.Rows[row].Cells[col].Value = !value;
    }
    else if (dgvBooks.Columns[e.ColumnIndex].Name == "URL")
    {
        // Open the URL.
        string url = (string)dgvBooks.Rows[row].Cells[col].Value;
        System.Diagnostics.Process.Start(url);
    }
}

This code verifies that the column number is at least 0 as before. If user double-clicked the Has Download column, the code gets the current value in the clicked cell and then updates the cell to hold the opposite of that value.

If the user double-clicked the URL column, the program gets the value in the cell and then uses System.Diagnostics.Process.Start to “start” the URL. That launches the URL in the system’s default browser.

Conclusion

I generally don’t recommend that you write your own code to edit DataGridView data because that control already does a pretty good job. Still, it may sometimes be useful to provide custom editing of some sort such as allowing the user to toggle a value as in this example.

An alternative might be to prevent editing in columns that the user should not modify. For example, the following statement prevents the user from editing the values in the first column.

dt.Columns[0].ReadOnly = true;

Download the example to see the code and to experiment with the program.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in controls, database, user interface | Tagged , , , , , , , , | Leave a comment

Flow words around drop caps in C#


[drop caps]

The example Flow blocks around obstacles for document layout in C# implements a document layout algorithm that flows blocks around obstacles. Moving from there to flowing words around obstacles is relatively simple. The following sections describe the main places where I modified the previous algorithm to provide document layout for words instead of blocks.

Block

The following code shows the new example’s Block class.

public class Block
{
    public RectangleF Bounds;
    public float TopHeight;
    public float BottomHeight
    {
        get { return Bounds.Height - TopHeight; }
    }

    public Block()
    {
    }
    public Block(RectangleF bounds, float top_height)
    {
        Bounds = bounds;
        TopHeight = top_height;
    }

    // Draw the block.
    public virtual void Draw(Graphics gr, Brush fg_brush,
        Brush bg_brush, Pen pen)
    {
        if (Bounds.X < 0) return;

        gr.FillRectangle(bg_brush, Bounds);
        gr.DrawRectangle(pen, Bounds);
    }

    // Return true if the block intersects the rectangle.
    public bool IntersectsWith(RectangleF rect)
    {
        return Bounds.IntersectsWith(rect);
    }

    // Return true if the block intersects the other block.
    public bool IntersectsWith(Block other)
    {
        return Bounds.IntersectsWith(other.Bounds);
    }
}

The main differences are the addition of a parameterless constructor and the two versions of the IntersectsWith method. Those methods return true if block’s bounds intersect with a rectangle or with another Block object.

Also note that the Draw method is now virtual. That allows the TextBlock class to override it so it can draw text instead of just a box.

The TextBlock Class

The previous example uses the Block class to represent a rectangle that should be flowed around obstacles. To flow words, this example derives a new TextBlock class from the Block class. The following code shows the new class.

public class TextBlock : Block
{
    public Font Font;
    public String Text;

    public TextBlock(string text, Font font, Graphics gr)
    {
        Font = font;
        Text = text;

        SizeF size = gr.MeasureString(Text, font);
        Bounds = new RectangleF(new PointF(), size);

        FontInfo font_info = new FontInfo(gr, font);
        TopHeight = font_info.AscentPixels;
    }

    // Draw the text block.
    public override void Draw(Graphics gr, Brush fg_brush,
        Brush bg_brush, Pen pen)
    {
        if (Bounds.X < 0) return;

        // Draw the box (optional). 
        base.Draw(gr, fg_brush, bg_brush, pen);

        // Draw the text.
        using (StringFormat sf = new StringFormat())
        {
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Center;
            gr.DrawString(Text, Font, fg_brush, Bounds, sf);
        }
    }

    public override string ToString()
    {
        return Text;
    }
}

This class inherits from the Block class. It provides two new fields: Font and Text. You can probably guess that those are the font that the object should use to draw its text and the text that it should draw.

The constructor takes as parameters the block’s text, its font, and a Graphics object that it can use to measure the text. The code saves the font and text, and then calls the Graphics object’s MeasureString method to measure the text as drawn with the given font. It uses the text’s size to define the block’s bounds.

Next, the program must figure out where the text’s baseline should be. Recall that the previous example’s document layout method aligned blocks on their baselines. For the blocks, that was unnecessary, but for text, that’s important so different words line up properly.

The distance between the top of a piece of text and its baseline is called its ascent. Unfortunately, neither the Font nor Graphics objects give you the ascent directly. Fortunately, you can figure it out. For an explanation of how to do that and to see a picture showing where a piece of text’s ascent is, see my earlier post Get font metrics in C#. That example defines a FontInfo class that gives the font’s ascent. The TextBlock class simply creates a FontInfo object for the font and saves its AscentPixels property in its TopHeight field.

The final piece of the TextBlock class is its Draw method, which overrides the same method defined by the Block class. The new method first calls the Draw class’s version of the method to draw a box where the text will be positioned as shown in the picture at the top of the post. In the final program, you can comment out that call so the program only draws the text as shown in the following picture.


[document layout]

The Draw method then draws the object’s text inside its bounds. Because the constructor made the bounds fit the text exactly, the text fills the bounds completely so the text is centered both vertically and horizontally within the bounds.

PictureBlock

I wanted the final program to be able to make text flow around pictures in fixed positions. The program uses the following PictureBlock class to represent and display those pictures.

public class PictureBlock : Block
{
    public Image Picture;

    public PictureBlock(Image picture, float x, float y)
    {
        Picture = picture;
        Bounds = new RectangleF(x, y,
            picture.Width, picture.Height);
    }

    // Draw the block.
    public override void Draw(Graphics gr, Brush fg_brush,
        Brush bg_brush, Pen pen)
    {
        if (Bounds.X < 0) return;

        // Draw the box (optional). 
        base.Draw(gr, fg_brush, bg_brush, pen);

        // Draw the image.
        gr.DrawImage(Picture, Bounds.Location);
    }
}

Like TextBlock the PictureBlock class inherits from Block. This class’s constructor takes as a parameter the picture that it should draw and the X and Y coordinates where it should be drawn. It saves the picture and uses the coordinates and the picture’s dimensions to set the object’s bounds.

The Draw method calls the base class’s Draw method. It then uses the Graphics object’s DrawImage method to draw the picture.

Form Load

The following code shows the variables that the program uses to hold the text and obstacles that it draws.

// Obstacles.
private List<Block> Obstacles;

// Blocks to flow around obstacles.
private List<List<Block>> ParagraphBlocks;

// The initials.
private List<Block> Initials;

// The text to draw.
private string[] Paragraphs =
{
    "Lorem ipsum dolor sit amet, consectetur...",
    "Etiam at nulla accumsan, fringilla erat...",
    "..."
};

// Extra space between paragraphs.
private const float ParagraphSpacing = 20;

The Obstacles list holds blocks that the text should flow around. These will be PictureBlock objects at fixed locations.

The ParagraphBlocks variable holds lists of blocks. Each list contains the words that make up a paragraph. (The words are Lorem Ipsum words. For more information on this interesting test string, see the site Lorem Ipsum.)

The first letter of the first word in each paragraph is removed and placed in the Initials list. Those values are the letters used for the drop caps. For example, Initials[2] is holds the first letter of paragraph number two.

Notice that the Obstacles, ParagraphBlocks, and Initials lists all contain Block objects. The TextBlock and PictureBlock classes are derived from Block, so TextBlock and PictureBlock objects are kinds of block. That means the program can use those objects as Block objects and can place them inside those lists.

The Paragraphs array holds all of the text. Finally, the value ParagraphSpacing indicates the amount of extra space to add between paragraphs.

The program initializes the data in the following Form_Load event handler.

// Define some blocks.
private void Form1_Load(object sender, EventArgs e)
{
    // Create a PictureBlock at a fixed position.
    PictureBlock picture_block = new PictureBlock(
        Properties.Resources.essential_algorithms_2e, 250, 70);

    // Add the PictureBlock as an obstacle.
    Obstacles = new List<Block>();
    Obstacles.Add(picture_block);

    // Build the lists of paragraphs and initials.
    ParagraphBlocks = new List<List<Block>>();
    using (Graphics gr = CreateGraphics())
    {
        // Give the initials the same size.
        RectangleF initial_bounds = new RectangleF(0, 0, 64, 64);

        // Break each paragraph into words.
        Initials = new List<Block>();
        Font initial_font =
            new Font("Times New Roman", 40, FontStyle.Bold);
        for (int i = 0; i < Paragraphs.Length; i++)
        {
            // Remove the first lettter of the first word.
            Initials.Add(new TextBlock(Paragraphs[i].Substring(0, 1),
                initial_font, gr));
            Paragraphs[i] = Paragraphs[i].Substring(1);
            Initials[i].Bounds = initial_bounds;

            // Break the rest of the paragraph into blocks.
            List<Block> new_blocks = new List<Block>();
            foreach (string word in Paragraphs[i].Split(' '))
            {
                // Make the word's block.
                new_blocks.Add(new TextBlock(word, RandomFont(), gr));
            }
            ParagraphBlocks.Add(new_blocks);
        }

        // Perform the initial flow.
        FlowParagraphBlocks(gr, picWriting.ClientRectangle,
            Obstacles, new SizeF(64, 64), ParagraphBlocks,
            ParagraphSpacing);
    }
}

This method first creates a PictureBlock at a specific position and adds that object to the Obstacles list.

Next, the code makes the ParagraphBlocks list and creates a Graphics object that it can use to measure text. It also creates a 64×64 pixel RectangleF to represent the size of the initials. It uses a single RetangleF so all of the drop caps will have the same size.

The code then loops through the paragraphs in the Paragraphs array. It removes the first letter from the first word in each paragraph and adds it to the Initials list. It then breaks the paragraph into words and places a TextBlock representing each word in the ParagraphBlocks list.

Notice that the code calls the RandomFont method to pick a different font for each word. That method gives each word a random font name, size, and style (bold, italic, regular, strikeout, or underline. If you comment out the compile-time symbol RANDOM_FONTS defined at the top of the file, then the RandomFont method returns a specific font instead of a random one. Download the example to see how it works.

The Load event handler finishes by calling the FlowParagraphBlocks method to flow the drop caps and text around the fixed obstacles.

FlowParagraphBlocks

The following code shows the FlowParagraphBlocks method that does the most interesting work.

// Flow blocks around obstacles.
private void FlowParagraphBlocks(Graphics gr,
    RectangleF bounds,
    List<Block> fixed_obstacles, SizeF min_initial_size, 
    List<List<Block>> paragraph_blocks,
    float paragraph_spacing)
{
    // Place objects to be hidden here.
    PointF hidden_point = new PointF(-1, -1);

    // Make a list to hold fixed obstacles and initials.
    List<Block> obstacles = new List<Block>(fixed_obstacles);

    // Start at the top.
    float y = bounds.Y;

    // Repeat until we place all blocks or run out of room.
    for (int paragraph_num = 0;
        paragraph_num < paragraph_blocks.Count;
        paragraph_num++)
    {
        // Position this paragraph's blocks.
        List<Block> this_paragraphs_blocks =
            paragraph_blocks[paragraph_num];

        // If this paragraph's initial won't fit,
        // move y down so we stop positioning blocks.
        if (y + Initials[paragraph_num].Bounds.Height > bounds.Bottom)
            y = bounds.Bottom + 1;

        // If we have run out of room, place everything at (-1, -1).
        if (y > bounds.Bottom)
        {
            // Position the initial and other blocks at (-1, -1).
            Initials[paragraph_num].Bounds.Location = hidden_point;

            // Position the text blocks.
            for (int i = 0; i < this_paragraphs_blocks.Count; i++)
                this_paragraphs_blocks[i].Bounds.Location = hidden_point;

            // Go on to the next paragraph.
            continue;
        }

        // Position the initial.
        Initials[paragraph_num].Bounds.Location =
            new PointF(bounds.Left, y);
        obstacles.Add(Initials[paragraph_num]);

        // Position the remaining text.
        int first_block = 0;
        while (first_block < this_paragraphs_blocks.Count)
        {
            // Position a row of blocks.
            int num_positioned = PositionOneRow(
                bounds, obstacles, this_paragraphs_blocks,
                ref first_block, ref y);

            // See if any fit.
            if (num_positioned == 0)
            {
                // None fit. Move down.
                MoveDown(bounds, obstacles,
                    this_paragraphs_blocks[first_block], ref y);
            }

            // See if we have run out of room.
            if (y > bounds.Bottom)
            {
                // Position the remaining blocks at (-1, -1).
                for (int i = first_block;
                    i < this_paragraphs_blocks.Count; i++)
                {
                    this_paragraphs_blocks[i].Bounds.Location =
                        hidden_point;
                }

                // Stop positioning the blocks in this
                // paragraph and go on to the next paragraph.
                break;
            }
        }

        // Don't start the next paragraph before the
        // bottom of the current paragraph's initial.
        if ((paragraph_num < paragraph_blocks.Count - 1) &&
            (y < bounds.Bottom))
        {
            if (y < Initials[paragraph_num].Bounds.Bottom)
                y = Initials[paragraph_num].Bounds.Bottom;
        }

        // Add some extra space between paragraphs.
        y += paragraph_spacing;
    }
}

The method makes a new obstacles list and copies the fixed obstacles (the PictureBlock) into it. It sets variable y equal to the top of the bounds where it is allowed to draw.

The code then loops through the paragraphs. It first checks whether the current paragraph’s initial will fit within the allowed bounds. If the initial won’t fit, the code moves y down beyond the bottom of the drawing area so the method won’t try to draw any more text.

Next, if y is below the bottom of the drawing area, the code loops through all of the paragraph’s words and positions them all at (-1, -1) to indicate that they did not fit.

If the initial does fit, then the code sets its position. That places the drop caps at the left edge of the drawing area. (You could modify that if you like. For example, you may want the drop caps indented.)

The code then adds the initial to the obstacles list so the paragraph’s text blocks can flow around it on subsequent lines.

Next, the code loops through the paragraph’s TextBlock objects. It calls the PositionOneRow method as in the previous example. If no word will fit on the current line, the code calls the MoveDown method to move down a bit, again as in the previous example.

After it has drawn text on the current line or moved down, the code checks the y position to see if we have fallen off of the drawing area. If we have left the drawing area, the code loops through the paragraph’s remaining words and places them at (-1, -1).

Before it continues looping through the paragraphs, the program checks that the y coordinate is at least beyond the bottom of the current paragraph’s initial. It then adds the extra spacing between paragraphs and continues looping through the paragraphs.

Download the example and take a look at the code for extra details about the PositionOneRow and MoveDown methods.

Paint

When the form loads, the program calls FlowParagraphBlocks to arrange the words. The program also calls that method when the form resizes.

When the form paints, it simply draws all of the blocks in their assigned positions. The following code shows how the Paint event handler does that.

// Draw the blocks.
private void picWriting_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

    // Draw obstacles.
    foreach (Block obstacle in Obstacles)
    {
        obstacle.Draw(e.Graphics, Brushes.Red, Brushes.Pink, Pens.Red);
    }

    // Draw initials.
    using (Brush bg_brush =
        new TextureBrush(Properties.Resources.Butterflies))
    {
        foreach (TextBlock initial in Initials)
        {
            initial.Draw(e.Graphics, Brushes.Black, bg_brush, Pens.Black);
        }
    }

    // Draw flowed blocks.
    foreach (List<Block> this_paragraphs_blocks in ParagraphBlocks)
    {
        foreach (Block block in this_paragraphs_blocks)
        {
#if DRAW_BOXES
            block.Draw(e.Graphics, Brushes.Black,
                Brushes.Transparent, Pens.Red);
#else
            block.Draw(e.Graphics, Brushes.Black,
                Brushes.Transparent, Pens.Transparent);
#endif
        }
    }
}

[Essential Algorithms, 2e]

This event handler sets the Graphics object’s SmoothingMode. It then loops through the fixed obstacles and draws them. In this example, that draws the single picture of my book Essential Algorithms, Second Edition.

The code then makes a TextureBrush that draws copies of an image. (In this example, it uses a colorful image of some butterflies tiled in the style of M. C. Escher.) The code then loops through the initials’ TextBlocks and calls their Draw methods to draw the drop caps. The parameters that the code uses makes that method fill the drop caps with the butterfly image, outline the rectangle in black, and draw the text in black.

After drawing the drop caps, the code loops through the blocks in the ParagraphBlocks lists and calls each block’s Draw method. The code outlines each block if the DRAW_BOXES symbol is defined.

That’s all the program needs to do to draw the picture, drop caps, and the other text. Flowing the text around the drop caps is fairly complicated, but after the text is positioned, drawing it is simple.

Conclusion

That’s the end of this example. I can’t say it was exactly simple, but the pieces are manageable if you take them slowly and one at a time.

Of course, all of this is just a taste of what a true document layout system must do. A program such as Microsoft Word or LaTeX must be able to flow text around obstacles such as pictures. Those pictures could hold drawings or text used for drop caps. Both pictures and inline text could include text with various colors and fonts, borders, fill colors, equations, tables, and a whole host of other features.

The problem is certainly daunting. Treating every object as a single Block base class helps. That lets you treat every object as something that occupies a rectangle and then you can shuffle the objects around. Exactly how you do that depends on your layout strategy.

As usual, download the example to see additional details and to experiment with the example.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, fonts, graphics, strings | Tagged , , , , , , , , , , , , | Leave a comment

Flow words around obstacles for document layout in C#


[document layout]

The example Flow blocks around obstacles for document layout in C# implements a document layout algorithm that flows blocks around obstacles. Moving from there to flowing words around obstacles is relatively simple. The following sections describe the two main places where I modified the previous algorithm to provide document layout for words instead of blocks.

The TextBlock Class

The previous example uses the Block class to represent a rectangle that should be flowed around obstacles. To flow words, this example derives a new TextBlock class from the Block class. The following code shows the new class.

public class TextBlock : Block
{
    public Font Font;
    public String Text;

    public TextBlock(string text, Font font, Graphics gr)
    {
        Font = font;
        Text = text;

        SizeF size = gr.MeasureString(Text, font);
        Bounds = new RectangleF(new PointF(), size);

        FontInfo font_info = new FontInfo(gr, font);
        TopHeight = font_info.AscentPixels;
    }

    // Draw the text block.
    public override void Draw(Graphics gr)
    {
        if (Bounds.X < 0) return;

        // Draw the box (optional). 
        base.Draw(gr);

        // Draw the text.
        gr.DrawString(Text, Font, Brushes.Black, Bounds);
    }
}

This class inherits from the Block class. It provides two new fields: Font and Text. You can probably guess that those are the font that the object should use to draw its text and the text that it should draw.

The constructor takes as parameters the block’s text, its font, and a Graphics object that it can use to measure the text. The code saves the font and text, and then calls the Graphics object’s MeasureString method to measure the text as drawn with the given font. It uses the text’s size to define the block’s bounds.

Next, the program must figure out where the text’s baseline should be. Recall that the previous example’s document layout method aligned blocks on their baselines. For the blocks, that was unnecessary. For text, that’s important so different words line up properly.

The distance between the top of a piece of text and its baseline is called its ascent. Unfortunately, neither the Font not Graphics objects give you the ascent directly. Fortunately, you can figure it out. For an explanation of how to do that and to see a picture showing how the ascent fits on the text, see my earlier post Get font metrics in C#. That method defines a FontInfo class that gives the font’s ascent. The program simply creates a FontInfo object for the font and saves its AscentPixels property in the TextBlock object’s TopHeight field.

The final piece of the TextBlock class is its Draw method, which overrides the same method in the Block class. The new method first calls the Draw class’s version of the method to draw a green box with a red baseline where the text will be positioned as shown in the picture at the top of the post. In the final program, you should comment out that call so the program only draws the text as in the following picture.


[document layout]

The method then draws the object’s text inside its bounds. Because the constructor defined the bounds so they would fit the text, the text fills the bounds completely. As a side effect, the text is centered both vertically and horizontally within the bounds.

I also made two minor changes to the Block class to make the derived TextBlock class work. First, I have the Block class a new parameterless constructor so the derived class didn’t need to pass the base constructor a bounds rectangle that it would not use.

Second, I changed the Draw method so it is virtual (so the TextBlock class can override it) and so it takes the parameters needed by the TextBlock class.

The Main Program

Instead of creating random blocks, the new example creates words that use random fonts. The following code shows how the program initializes its word data.

// Blocks to flow around obstacles.
private List<Block> Blocks;

// The text to draw.
private string Words = "Lorem ipsum dolor sit amet, ...";

// Define some blocks.
private void Form1_Load(object sender, EventArgs e)
{
    Obstacles = new List();
    Obstacles.Add(new RectangleF(0, 0, 64, 64));
    Obstacles.Add(new RectangleF(200, 50, 50, 100));
    Obstacles.Add(new RectangleF(50, 150, 100, 70));
    Obstacles.Add(new RectangleF(300, 200, 100, 64));

    Blocks = new List<Block>();
    Graphics gr = CreateGraphics();
    foreach (string word in Words.Split(' '))
        Blocks.Add(new TextBlock(word, RandomFont(), gr));
}

Notice that the variable Blocks is still a list of Block objects. The TextBlock class is derived from Block, so a TextBlock is a kind of block. That means the program can place TextBlock objects inside a List<Block>.

The string Words contains a long set of Lorem Ipsum words. For more information on this interesting test string, see the sire Lorem Ipsum.

The form’s Load event handler creates obstacles as before. It then breaks the Words string into words and uses each to create a TextBlock. The only new thing here is that the program calls the RandomFont method to generate each word’s font.

The RandomFont method simply picks a random font name, size, and style (bold, italic, regular, strikeout, or underline). Download the example to see the details.

Conclusion

The rest of the example is about the same as before. The Paint event handler calls the FlowBlocks method to arrange the blocks as before. The blocks are actually TextBlock objects instead of Block objects, but FlowBlocks doesn’t care.

The result is almost what we need to perform document layout with drop caps. The result shown here looks a bit strange because the obstacles are scattered around the drawing area rather than all places on the left edge. The words are also relatively large, so there are some fairly big gaps between words.

As usual, feel free to download the example and experiment with it. In my final post in this series, I’ll show how to handle paragraphs and pull the initial drop caps off of the first word in each paragraph. The result should look a lot more appealing than the one shown here.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, fonts, graphics, strings | Tagged , , , , , , , , , , , , | Leave a comment

Flow blocks around obstacles for document layout in C#


[document layout]

The following two document layout examples draw “illuminated” text by using an initial or drop cap.

Martin asked how we could do something similar when the text that follows the initial is drawn with different fonts. This is the first part of a three-part document layout example that does just that. This first post shows how you can perform layout for boxes, the second post will use these techniques to solve Martin’s problem, and the third will refine the technique to work more easily with drop caps.

This is a fairly long example, so I’ll describe it in pieces. Fortunately each of the methods is reasonably understandable if you focus only on its specific task and you don’t think about how the other pieces work until you get to them.

The Idea

Suppose we need to arrange some boxes inside a drawing area. The area contains some rectangular obstacles where we are not allowed to place our boxes.

The basic idea is to start in the upper left corner and arrange boxes in a row left to right. If we come to an obstacle, we skip over it. As we add boxes to the current row, we arrange the boxes so they all share a common baseline, which is somewhere within the boxes.

See the picture at the top of this post to see the idea. Note that all of the green boxes on the top row are aligned on their red baselines.

The first row skips past the first pink obstacle in the upper left corner. Block 0 is relatively large, so it is the only one that fits before the second obstacle interrupts the row. Blocks 1 through 4 fit on the first row, and then the document layout moves to a new row.

Only block 5, which is relatively small, fits on the second row before another obstacle gets in the way. The program places block 6, skips another obstacle, and then positions blocks 7 and 8 before starting the third row.

You can look at the remaining boxes and obstacles to get an idea about how the method works. You can probably devise other document layout methods, but this one seems to produce a reasonable result.

Also note that this would be a truly bizarre document layout for drop caps. Normally all of the drop caps would be at the left edge and the remaining text would flow to the right. This method handles that normal arrangement. It can also deal with one or two pictures scattered across the page.

The program uses the RectangleF structure to represent obstacles. It uses a Block class to represent the blocks that it should flow. The following section describes that class.

The following sections explain how the program arranges its blocks.

Obstacles and Blocks

The program uses the following Block class to represent the blocks that it should flow around obstacles.

public class Block
{
    public RectangleF Bounds;
    public float TopHeight;
    public float BottomHeight
    {
        get { return Bounds.Height - TopHeight; }
    }

    public Block(RectangleF bounds, float top_height)
    {
        Bounds = bounds;
        TopHeight = top_height;
    }

    // Draw the block.
    public void Draw(Graphics gr, int index, Font font)
    {
        if (Bounds.X < 0) return;

        gr.FillRectangle(Brushes.LightGreen, Bounds);
        gr.DrawRectangle(Pens.Green, Bounds);

        float x1 = Bounds.Left;
        float x2 = Bounds.Right;
        float y = Bounds.Top + TopHeight;
        gr.DrawLine(Pens.Red, x1, y, x2, y);

        gr.DrawString(index.ToString(),
            font, Brushes.Black,
            Bounds);
    }
}

This class defines two fields: Bounds and TopHeight. The Bounds value gives the block’s size and location. Initially only the size matters. [Insert your joke here.] After the block has been positioned, Bounds indicates where it should be drawn.

The TopHeight value is the distance from the block’s top to its baseline. The read-only BottomHeight property gives the distance from the block’s baseline to its bottom. That value is simply the block’s height minus its TopHeight value.

The class defines one simple initializing constructor.

The last part of the class defines a Draw method that draws the block on a Graphics object. That method fills the block’s area with light green, outlines it in green, draws a red line at the block’s baseline, and then draws the block’s index number inside the block’s area. (Look at the picture at the top of the post to see the result.)

The next section provides an overview of the methods that arrange the blocks. The sections after that describe each of those methods in greater detail.

Code Overview

The program uses the following methods to arrange blocks.

FlowBlocks
This is the high-level method that coordinates the document layout. It enters a loop that executes as long as any blocks are not positioned. Inside the loop, it calls PositionOneRow to position blocks on the next row. If no blocks fit on that row, it calls MoveDown to move down where a row might be drawn so it can hopefully position some blocks.
PositionOneRow
This method tries to position blocks in a row within a bounding area starting with a given Y coordinate. To do that, it tries to position one block, then two blocks, then three blocks, and so on until it finds a number of blocks that will not fit. For example, if it can position four blocks but not five blocks, then the method returns 4 and positions four blocks. To determine whether a given set of blocks fits on the row, the method calls BlocksFit.
BlocksFit
This method attempts to flow a given set of blocks around obstacles in a row with the given Y coordinate. If the blocks fit, the method leaves them positioned and returns true.
MoveDown
This method moves a Y coordinate downward to try to find a position for a new row. It is only called if the BlocksFit method was unable to fit even a single block on the next row. To make at least one block fit, the method moves the Y coordinate down so the next block will not overlap vertically with at least one obstacle. Note that the block may still overlap with other obstacles. In that case, the program will call MoveDown again until the block will fit or the document layout runs out of space.

The following sections show the methods’ code and describe them in greater detail. You may want to refer back to the overview as you read through the code.

FlowBlocks

The following code shows the top-level FlowBlocks method.

// Flow blocks around obstacles.
private void FlowBlocks(RectangleF bounds,
    List<RectangleF> obstacles, List<Block> blocks)
{
    float y = bounds.Y;

    // Repeat until we place all blocks or run out of room.
    int first_block = 0;
    while (first_block < blocks.Count)
    {
        // Position a row of blocks.
        int num_positioned = PositionOneRow(
            bounds, obstacles, blocks,
            ref first_block, ref y);

        // See if any fit.
        if (num_positioned == 0)
        {
            // None fit. Move down.
            MoveDown(bounds, obstacles, blocks[first_block], ref y);
        }

        // See if we have run out of room.
        if (y > bounds.Bottom)
        {
            // Position the remaining blocks at (-1, -1).
            for (int i = first_block; i < blocks.Count; i++)
            {
                blocks[i].Bounds.X = -1;
                blocks[i].Bounds.Y = -1;
            }
            break;
        }
    }
}

This method first sets variable y equal to the Y coordinate of the top of the drawing area. It uses variable first_block to keep track of the first block that has not yet been positioned. It sets that value to 0 and then enters a loop that continues until all of the blocks have been positioned.

Inside the loop, the method calls PositionOneRow to position blocks at the current Y coordinate. If no blocks will fit at that position, the method calls MoveDown to move the Y coordinate down so hopefully some blocks will fit.

After positioning some blocks or moving down, the method checks y to see if it has dropped off of the bottom of the document layout area. If we have run out of space, the method places the remaining unpositioned blocks at (-1, -1) and exits.

PositionOneRow

The following code shows the PositionOneRow method.

// Return the maximum number of blocks that will fit
// on one row starting at the indicated Y coordinate.
// If we position any blocks, update first_block and y.
private int PositionOneRow(RectangleF bounds,
    List<RectangleF> obstacles, List<Block> blocks,
    ref int first_block, ref float y)
{
    // Loop through the blocks.
    int last_that_fits = blocks.Count - 1;
    for (int i = first_block; i < blocks.Count; i++)
    {
        // See if we can place blocks[first_block]
        // through blocks[i].
        if (!BlocksFit(bounds, obstacles, blocks, first_block, i, y))
        {
            last_that_fits = i - 1;
            break;
        }
    }

    // If no blocks fit, return 0.
    if (last_that_fits < first_block) return 0;

    // Position the blocks that fit again.
    BlocksFit(bounds, obstacles, blocks,
        first_block, last_that_fits, y);

    // Find the maximum y coordinate for these blocks.
    for (int i = first_block; i <= last_that_fits; i++)
    {
        if (y < blocks[i].Bounds.Bottom)
            y = blocks[i].Bounds.Bottom;
    }

    // Update first_block.
    int num_blocks_positioned = last_that_fits - first_block + 1;
    first_block = last_that_fits + 1;

    // Return the number of blocks that fit.
    return num_blocks_positioned;
}

The parameter first_block gives the index of the first block that has not yet been positioned. This method enters a loop where i runs from first_block to the last block in the list. Inside the loop, it calls the BlocksFit method to see if the blocks with indices first_block through i will fit on the current row.

After it finds a set of blocks that will not fit on the current row, the method backtracks to the previous set. If that set is empty (no blocks will fit on the row), the method returns 0.

If some blocks fit on the row, the method calls BlocksFit again for the set that fit to reposition them. The code then loops through the positioned blocks and finds their maximum Y coordinate. It updates y so the next row is positioned below this one.

Finally, the method updates first_block and returns the number off blocks that it positioned.

BlocksFit

The following code shows the BlocksFit method.

// Return true if the indicated blocks will
// fit starting at the given Y coordinate.
private bool BlocksFit(RectangleF bounds,
    List<RectangleF> obstacles, List<Block> blocks,
    int first_block, int last_block, float y)
{
    // Find the maximum top height.
    float top_height = 0;
    for (int i = first_block; i <= last_block; i++)
    {
        if (top_height < blocks[i].TopHeight)
            top_height = blocks[i].TopHeight;
    }

    // Set the baseline.
    float baseline = y + top_height;

    // Position the blocks.
    float x = bounds.X;
    for (int i = first_block; i <= last_block; i++)
    {
        Block block = blocks[i];
        block.Bounds.X = x;
        x += block.Bounds.Width;
        if (x > bounds.Right) return false;

        block.Bounds.Y = baseline - block.TopHeight;

        // See if the block intersects with an obstacle.
        RectangleF rect = block.Bounds;
        bool had_hit = true;
        while (had_hit)
        {
            had_hit = false;
            foreach (RectangleF obstacle in Obstacles)
            {
                if (obstacle.IntersectsWith(rect))
                {
                    had_hit = true;

                    // Move the block to the right.
                    rect.X = obstacle.Right;

                    // See if we are out of bounds.
                    if (rect.Right > bounds.Right) return false;
                }
            }
        }

        // Update the block's bounds.
        block.Bounds = rect;
        x = rect.Right;

        // Loop to position the next block.
    }

    // If we get this far, then we have
    // positioned all of the blocks.
    return true;
}

This method first uses a loop to find the maximum top height of the blocks in question. It then positions the row’s baseline at that distance from the row’s upper Y coordinate.

The code then loops through the blocks and tries to position each block at the next available X coordinate. The code then loops through the obstacles to see if any intersect with the block at that position. If an obstacle intersects the block, the method moves the block to the right of that obstacle. It also restarts the loop that examines the obstacles in case one of the previous obstacles now intersects the moved block.

The inner loop ends when one of two things occurs. First, if block extends beyond the right edge of the document layout area, then the blocks will not fit on this row so the method returns false.

Second, if the block does not intersect any obstacle, then it is in a safe position. In that case, the method continues its outer loop to position the other blocks in the row.

If the method successfully positions all of the blocks, it returns true.

MoveDown

The following code shows the MoveDown method.

// Move down so the block will clear at least one new obstacle.
private void MoveDown(RectangleF bounds,
    List<RectangleF> obstacles, Block block, ref float y)
{
    float min_y = y + block.Bounds.Height;
    RectangleF rect = new RectangleF(
        bounds.X, y, bounds.Width, block.Bounds.Height);
    foreach (RectangleF obstacle in obstacles)
    {
        if (obstacle.IntersectsWith(rect))
        {
            if (min_y > obstacle.Bottom)
                min_y = obstacle.Bottom;
        }
    }

    y = min_y;
}

This method creates a rectangle as tall as the next block to position and as wide as the document layout area. It then loops through the obstacles to find the smallest Y coordinate of the bottom of any obstacle that intersects with the rectangle. The code updates the reference parameter y to move past that Y value and returns.

This moves the next row past the highest obstacle that intersects the rectangle. The higher-level methods then try to position the row again. If those methods fail again, they may call MoveDown again.

Main Program

That’s all of the document layout code. The following code shows how the program generates some random obstacles and blocks.

// Obstacles.
private List<RectangleF> Obstacles;

// Blocks to flow around obstacles.
private List<Block> Blocks;

// Define some blocks.
private void Form1_Load(object sender, EventArgs e)
{
    Obstacles = new List<RectangleF>();
    Obstacles.Add(new RectangleF(0, 0, 64, 64));
    Obstacles.Add(new RectangleF(200, 50, 50, 100));
    Obstacles.Add(new RectangleF(50, 150, 100, 70));
    Obstacles.Add(new RectangleF(300, 200, 100, 64));

    // Random rand = new Random(4);
    Random rand = new Random(4);
    Blocks = new List<Block>();
    for (int i = 0; i < 25; i++)
    {
        float width = rand.Next(10, 100);
        float height = rand.Next(10, 100);
        Blocks.Add(new Block(
            new RectangleF(0, 0, width, height), height * 0.6f));
    }
}

At the form level, the code defines lists to hold obstacles and blocks. The form’s Load event handler creates some specific obstacles and then uses a Random object to generate blocks with random sizes. It sets each block’s TopHeight value to 0.6 times its height.

The program arranges the blocks in the following Paint event handler.

// Flow and draw the blocks.
private void picWriting_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

    // Flow.
    FlowBlocks(picWriting.ClientRectangle, Obstacles, Blocks);

    // Draw obstacles.
    foreach (RectangleF obstacle in Obstacles)
    {
        e.Graphics.FillRectangle(Brushes.Pink, obstacle);
        e.Graphics.DrawRectangle(Pens.Red, obstacle);
    }

    // Draw flowed blocks.
    for (int i = 0; i < Blocks.Count; i++)
    {
        Blocks[i].Draw(e.Graphics, i, picWriting.Font); 
    }
}

This code calls the FlowBlocks method to position the blocks. It then loops through the obstacles and draws them as pink rectangles.

Next, the code creates a dashed pen and a StringFormat object to center text. It then loops through the blocks. It draws each block in green, draws its baseline with the dashed pen, and draws the block’s index inside it.

Conclusion

This method is rather long, but it seems to produce a reasonable result. The picture shown at the top of the post includes some fairly large blank areas. For example, there’s a pretty big break after block 0 and below block 0. Those should be smaller with a more realistic arrangement where drop caps are on the left and a relatively large amount of text flows to the right. After you see the next two posts, you can decide whether you want to modify the document layout algorithm.

Meanwhile, download the example and experiment with it. In my next post, I’ll show how you can modify the block flow method to flow text with various fonts around obstacles.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, fonts, graphics, strings | Tagged , , , , , , , , , , , , | 2 Comments

Serialize and deserialize multiple images in files in C#

[serialize]

The example Serialize and deserialize multiple images in C# showed how you can List<Image> into a memory stream and then deserialize stream to recover the images. This example shows how you can similarly serialize a list of images into a file and then deserialize the list from that file.

This example actually includes two programs, one to serialize the images and one to deserialize them. I decided to do this in two programs to be certain that the serialization did not include the namespace of the serialization program. Some serialization methods include the serialized object’s namespace and that can make deserializing the objects harder. This example uses two programs to show that this isn’t an issue.

The previous example shows most of what you need to do to serialize and deserialize a list of images in a file. The following sections describe the key pieces of the two example programs included in the download.

Serialization


[serialize]

The howto_save_serialized_images example program displays three images that are placed in PictureBox controls at design time. When you open the File menu and select Save Serialization, the following code executes.

// Save a serialization of the images.
private void mnuFileSave_Click(object sender, EventArgs e)
{
    // Get the serialization file name.
    if (sfdSerialization.ShowDialog() == DialogResult.OK)
    {
        // Add the files to a list.
        List<Image> input_images = new List<Image>();
        input_images.Add((Bitmap)picSource1.Image);
        input_images.Add((Bitmap)picSource2.Image);
        input_images.Add((Bitmap)picSource3.Image);

        // Serialize.
        using (FileStream fs = new FileStream(
            sfdSerialization.FileName, FileMode.Create))
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(fs, input_images);
        }
    }
}

This code displays a SaveFileDialog. If you enter a file name and click Save, the code creates a List<Image> and adds the three images to it.

Next, the program creates a FileStream to create the file. It makes a BinaryFormatter and uses its Serialize method to serialize the list of images into the FileStream.

When the using block ends, the program automatically closes the FileStream.

Deserialization


[serialize]

The howto_load_serialized_images example program lets you read the images in a serialization. When you open the File menu and select Open Serialization, the following code executes.

// Load a serialization.
private void mnuFileOpen_Click(object sender, EventArgs e)
{
    // Get the serialization file name.
    if (ofdSerialization.ShowDialog() == DialogResult.OK)
    {
        // Deserialize.
        using (FileStream fs = new FileStream(
            ofdSerialization.FileName, FileMode.Open))
        {
            BinaryFormatter formattter = new BinaryFormatter();
            List<Image> output_images =
                (List<Image>)formattter.Deserialize(fs);

            // Display the images.
            const int margin = 10;
            int x = margin;
            int y = menuStrip1.Bottom + 4;
            foreach (Image image in output_images)
            {
                PictureBox pic = new PictureBox();
                pic.Location = new Point(x, y);
                pic.SizeMode = PictureBoxSizeMode.AutoSize;
                pic.Image = image;
                pic.BorderStyle = BorderStyle.Fixed3D;
                pic.Parent = this;
                x += pic.Width;
            }
        }
    }
}

This code displays an OpenFileDialog. If you select a file and click Open, the code creates a FileStream attached to that file. It then makes a BinaryFormatter and uses its Deserialize method to read the serialization from the FileStream. It converts the result into a List<Image>.

Next, the program loops through the images in the list and displays them in a sequence of PictureBox controls.

Conclusion

That’s about all there is to the example programs that serialize and deserialize images. This is one of the rare instances when the download doesn’t contain much more than the code shown here. The only important things that are missing are the two following using directives.

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

Using the techniques demonstrated by these examples and the previous post, you should be able to serialize and deserialize a list of images in any stream, whether it’s a memory stream, file stream, or something else.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in database, files, serialization | Tagged , , , , , , , , , , , , , | 3 Comments

Serialize and deserialize multiple images in C#


[serialize]

This example shows how you can serialize and deserialize multiple images together in a single serialization. It also talks a bit about database design. (For more information about database design including this issue, see my book, Beginning Database Design Solutions.)

Recently someone asked if you could store multiple images in a single record in a database. The answer to that kind of question is usually, “Yes, if you want to badly enough.” He eventually found some posts that did it by creating multiple fields in the data table and then storing an image in each.

The basic idea for storing an image in a database is to convert the image into bytes and then store the bytes in some sort of non-specific byte field. In some databases, that kind of field has the Binary Large Object (BLOB) data type. In an Access database, you can use a field with the OLE Object data type. For an example that does this, see the post Save images in an Access database in C#.

From there, it’s not too hard to create multiple BLOB or OLE Object fields and use them to store multiple images in each record. One of my rules of thumb for database design, however, is, “There’s no such thing as two.” The idea is that, if you are asked to add two of the same kind of field to a database table, then what’s to prevent your customer from later asking for three? Or four? Or more?

Adding one instance of something (like a headshot) in a table is fine. But if you then decide to add a second picture (perhaps a spouse picture) sends you down a slippery slope. If you include a spouse picture, then why not pictures of your children? Or pets? Or cars? You can certainly add a second picture field, but you’d better be absolutely certain that you won’t need to change the number of those fields later.

For example, suppose you use six fields, but then later hire an employee who has a spouse and seven children. Now you need to create a new database design, build the new database, copy your old data into it, and modify any programs that use it. (Or convince your new employee to put three children up for adoption.)

In the following sections, I’ll describe two ways that you can handle this issue, the “right” way and a somewhat more interesting way.

The “Right” Way

[serialize]

The officially sanctioned way to handle this issue using classic database design techniques is to create a second detail table and link it to the original table by using a key field. For example, a People table might include a PersonId field. The Pictures table would also include a PersonId field. To find the pictures associated with a particular person, you would search the Pictures table for records with the matching PersonId value. The picture on the right shows the relationship between those two tables.

Now you can add as many pictures as you like to any person’s record. This solution also has the distinct advantage that you can add other fields to the Pictures table. For example, the design shown in the diagram includes a Title field where you can indicate the type of picture such as Headshot, Spouse, Child, Pet, or Car. Later you could search the database to find a specific type of picture. For example, you could use the following SQL query to find employees and their headshots.

SELECT FirstName, LastName, Picture
  FROM People, Pictures
 WHERE People.PersonId = Pictures.PersonId
   AND Pictures.PictureType = 'Headshot'

An Interesting Alternative

An interesting alternative is to serialize a record’s images and place them in a single BLOB field. The technique of serializing objects is useful in other circumstances, too. For example, it lets you save and restore images (or other data) in files or transmit images across a network.

The example program uses a BinaryFormatter to serialize images into a MemoryStream. The program uses the following using directives to make using those classes easier.

using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

When it starts, the program uses the following code to serialize and deserialize three images.

private void Form1_Load(object sender, EventArgs e)
{
    // Add the files to a list.
    List<Image> input_images = new List<Image>();
    input_images.Add((Bitmap)picSource1.Image);
    input_images.Add((Bitmap)picSource2.Image);
    input_images.Add((Bitmap)picSource3.Image);

    // Serialize.
    byte[] bytes;
    using (MemoryStream ms = new MemoryStream())
    {
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(ms, input_images);
        bytes = ms.ToArray();
    }

    // Display the serialization bytes.
    txtHex.Text = BitConverter.ToString(
        bytes, 0).Replace('-', ' ');
    txtHex.Select(0, 0);

    // Deserialize.
    using (MemoryStream ms = new MemoryStream(bytes))
    {
        BinaryFormatter formattter = new BinaryFormatter();
        List<Image> output_images =
            (List<Image>)formattter.Deserialize(ms);
        picDest1.Image = output_images[0];
        picDest2.Image = output_images[1];
        picDest3.Image = output_images[2];
    }
}

This code first creates a List<Image> and adds the three images (stored in PictureBox controls) to it. It then creates a byte[] to hold the binary serialization.

Next, the code creates a MemoryStream. It makes a BinaryFormatter object and uses its Serialize method to serialize the list of images into the stream. It then calls the stream’s ToArray method to convert the bytes that it contains into a byte array. This is the array of bytes that you would store in the database.

The program then uses the BitConverter class to display a textual representation of the serialization, just so you can get an idea that the serialization actually exists. (You can also get an idea of its size.)

Next, the code deserializes the serialized data. To do that, it creates a new MemoryStream associated with the byte array. It creates a new BinaryFormatter and uses its Deserialize method to deserialize the bytes and recreate the list of images. Finally, it displays the images in a new set of PictureBox controls so you can see that they have been recreated correctly.

To Be Continued…

This example shows how to serialize and deserialize a list of images. You could then store the serialization in a database so each record in a table could include any number of images.

That technique has the advantage that it works. You can also use a similar technique to store just about any other kind of data in a database table or file. For example, you could store audio, video, a network, or a hierarchical data structure in database records.

You could even store pieces of data that you didn’t not anticipate in a BLOB field without rebuilding the database. For example, if you’re working with an existing database that already stores images in a BLOB and you now need to add audio data, you could serialize the pictures together with the audio data. You would need to write a program to re-serialize the data, but you would not need to change the database structure.

[Beginning Database Design Solutions]

HOWEVER, this technique has the huge disadvantage that it doesn’t let you search the data stored in the BLOB fields. For example, you could not search for headshot images. For that reason alone I would suggest that you use the “right” way unless the database is already built and you’re not allowed to change its design.

In my next post, I’ll explain how you can use similar techniques to serialize and deserialize a list of images in a file. To learn more about how this example works, download the example program. And to learn a lot more about database design, see my book Beginning Database Design Solutions.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in database, files, serialization | Tagged , , , , , , , , , , , , , | 1 Comment

Let the user drag pictures in a picture list in C#

[picture list]

The post Improve the picture list in C# showed how you can let the user add, remove ,and rearrange images in a picture list. To rearrange the list, the user right-clicks on a picture and selects Move Left or Move Right commands. That works, but can be slow if the user wants to move an image many positions in the picture list. It would be easier to delete and re-add the image. This example shows how you can allow the user to drag a picture to a new position in the list

The previous example stored the images in a list. Whenever the user rearranged the pictures, the program created PictureBox controls to display them. This example uses a list holding the PictureBox controls that contain the pictures. When the user rearranges the pictures, the program rearranges the PictureBox controls instead of creating new ones.

The program uses the following code to store the controls.

// The currently loaded PictureBoxes.
private List PictureBoxes =
    new List();

The code uses the following variables to keep track of the PictureBox that the user is dragging.

// Used to drag PictureBoxes.
private PictureBox DragPic = null;
private Point DragOffset;

The value DragPic is the PictureBox that the user is currently dragging. The DragOffset value gives the X and Y distances between the location of DragPic and the mouse’s position. You’ll see how that works when you look at the mouse handling code.

The following code executes when the user presses the mouse down over a PictureBox.

// Start dragging the control or display the context menu.
private void pic_MouseDown(object sender, MouseEventArgs e)
{
    PictureBox pic = sender as PictureBox;

    if (e.Button == MouseButtons.Left)
    {
        // Start dragging.
        DragPic = pic;
        int dx = -e.Location.X;
        int dy = -e.Location.Y;
        DragOffset = new Point(dx, dy);

        // Move the PictureBox to the top of the
        // panPictures stacking order.
        panPictures.Controls.SetChildIndex(pic, 0);

        // Let panPictures handle the MouseMove and MouseUp.
        DragPic.Capture = false;
        panPictures.Capture = true;
        panPictures.MouseMove += panPictures_MouseMove;
        panPictures.MouseUp += panPictures_MouseUp;
    }
    else
    {
        // Get the mouse's location in panPictures coordinates.
        Point screen_point = pic.PointToScreen(e.Location);
        Point parent_point = panPictures.PointToClient(screen_point);

        // Display the context menu.
        ShowContextMenu(new Point(
            parent_point.X,
            parent_point.Y));
    }
}

If the user has pressed the left mouse button, the code saves the DragPic and sets DragOffset to the negative of the mouse’s location. Because the PictureBox raised this event handler, the mouse’s position is with respect to that control’s origin so DragOffset indicates the X and Y distances between the mouse and the control’s upper left corner.

The code then calls the SetChildIndex method for the collection of PictureBox controls within the Panel control that forms the picture list. It uses that method to move the control under the mouse to the top of the stacking order so it appears above the other pictures in the picture list. (Dragging behind the other controls is weird.)

Next, the program sets dragPic.Capture to false to release the mouse capture that started when the user pressed the mouse down over the control. It sets panPictures.Capture to true to give the Panel control the mouse so it receives future mouse events.

Finally, the event handler installs MouseMove and MouseUp event handlers to capture future mouse events.

If the user pressed the right mouse button over the PictureBox, the code displays a context menu just as the previous example did.

The following code shows the MouseMove event handler.

// Move a PictureBox.
private void panPictures_MouseMove(object sender, MouseEventArgs e)
{
    int x = e.Location.X + DragOffset.X;
    int y = e.Location.Y + DragOffset.Y;
    DragPic.Location = new Point(x, y);
}

This code adds the DragOffset value to the mouse’s current position. Remember that the mouse is now captured by the Panel control that holds the PictureBox, so its position is with respect to that control. The DragOfffset value gives the distance between the mouse’s initial position and the PictureBox control’s upper left corner, so the result is where the PictureBox should be moved to keep the mouse over the same position on the control.

After it calculates the new position, the code simply moves the PictureBox.

The following code shows the new MouseUp event handler.

// Stop dragging DragPic.
private void panPictures_MouseUp(object sender, MouseEventArgs e)
{
    DragPic = null;
    panPictures.MouseMove -= panPictures_MouseMove;
    panPictures.MouseUp -= panPictures_MouseUp;
    OrderPictureBoxes();
}

This code sets DragPic to null to indicate that no drag is in progress. It uninstalls the new MouseMove and MouseUp event handlers, and then calls the following OrderPictureBoxes method.

// Rearrange the picture list so the controls
// are ordered by their X coordinates.
private void OrderPictureBoxes()
{
    // Sort the PictureBoxes list.
    PictureBoxes.Sort((pic1, pic2) =>
        pic1.Location.X.CompareTo(pic2.Location.X));

    // Rearrange the controls.
    ArrangePictureBoxes();
}

Recall that PictureBoxes is the program’s list of PictureBox controls. This method calls that list’s Sort method. It uses a lambda expression to indicate the function that should be used to sort the PictureBox controls in the list. This lambda expression takes two PictureBox controls as parameters and compares their X coordinates.

After the controls are sorted by their X coordinates, the code calls the following ArrangePictureBoxes method.

// Arrange the PictureBoxes.
private void ArrangePictureBoxes()
{
    int ymax = 0;
    int x = PictureMargin;
    int y = PictureMargin;
    foreach (PictureBox pic in PictureBoxes)
    {
        pic.Location = new Point(x, y);
        x += pic.Width + PictureMargin;
        if (ymax < pic.Height) ymax = pic.Height;
    }

    // Position one placeholder PictureBox.
    y = ymax + 2 * PictureMargin;
    Placeholder.Location = new Point(x, y);
}

This method is similar to the code used by the previous example to arrange its picture list. It sets variables x and y to the position where the first control should be placed. It then loops through the controls, positions each, and adds each control’s width plus a margin to the value x.

The method finishes by placing the Placeholder PictureBox to the right of the other controls so there is room for the user to right-click to the right of all of the pictures in the picture list. See the previous example for information about the placeholder control.

With the ability to drag images into new positions, the picture list is quite easy to use. Download the example to experiment with it. To see additional details, look at the code and see the previous example.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in controls, lists, user interface | Tagged , , , , , , , , | Leave a comment

Improve the picture list in C#

[picture list]

My post Make a picture list in C# explained how you can make a picture list that lets the user add, remove, and rearrange pictures. This post adds a small improvement.

The previous example displayed images in PictureBox controls with BorderStyle set to None. Unfortunately, if you set the BorderStyle to something else, then the context menu did not line up properly when you right-clicked on one of those controls. When you pressed and released the mouse, the context menu jumped slightly. This wasn’t a big deal, but it was annoying so I made that example use borderless PictureBox controls for its picture list.

The following code snippet shows how the previous example calculated the mouse’s position in panPictures coordinates.

// Display the context menu.
PictureBox pic = sender as PictureBox;
ShowContextMenu(new Point(pic.Left + e.X, pic.Top + e.Y));

This code adds the PictureBox control’s upper left corner coordinates to the mouse’s coordinates in that control’s coordinate system. In other words, it starts at the control’s upper left corner and then adds the coordinates of the mouse within the control to get the control’s position relative to the panPictures parent control.

The problem is that the coordinates reported by the PictureBox are relative to that control’s client area, and that area does not include the control’s border. That means the mouse’s location is off by the thickness of the control’s left and top borders.

There doesn’t seem to be a way to find the thickness of a control’s border directly, so you can’t just add it into the calculation. Fortunately, you can convert a point from the coordinate system of one control to that of another. The new example does that in the following snippet.

PictureBox pic = sender as PictureBox;

// Get the mouse's location in panPictures coordinates.
Point screen_point = pic.PointToScreen(e.Location);
Point parent_point = panPictures.PointToClient(screen_point);

// Display the context menu.
ShowContextMenu(new Point(
    parent_point.X,
    parent_point.Y));

This code calls the PictureBox control’s PointToScreen method to convert the mouse’s location to screen coordinates. That gives the mouse’s position relative to the screen’s upper left corner. The code then calls the panPictures control’s PointToClient method to convert the screen coordinates into the panPictures control’s coordinate system.

This is actually a more direct way to calculate the mouse’s position. It also works no matter what border style you give to the PictureBox controls used by the picture list.

In my next post, I’ll add another enhancement that will force a redesign of much of the program.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in controls, lists, user interface | Tagged , , , , , , , , | 1 Comment