Find a Ducci sequence in C#

[Ducci sequence]

A Ducci sequence is a sequence of tuples of integers. You start with a sequence of values such as 1-2-3-4-5. TO find the next tuple, you calculate the absolute value of the difference between adjacent numbers in the sequence, wrapping around to the beginning when you reach the end of the sequence. For example, the tuple after 1-2-3-4-5 is 1-1-1-1-4.

This example calculates a Ducci sequence starting from a tuple that you enter.

Ducci sequences have a number of interesting properties. First, all sequences eventually repeat. If the length of the tuples is a power of two, then the tuples eventually become all 0s. If the length of the tuples is not a power of two, then it either eventually becomes a tuple of all 0s, or it enters a binary loop where each tuple contains only 0 and some other value. (For example, 0-0-0-2-2 or 0-0-1-1-0.)

When you enter a start tuple and click Go, the program uses the following code to find the Ducci sequence.

private void btnGo_Click(object sender, EventArgs e)
{
    lstResults.DataSource = null;
    Refresh();

    // Convert into a List<int>.
    string nums_str = txtStart.Text;
    List<string> ints = nums_str.Split('-').ToList();
    List<int> nums = ints.ConvertAll(s => int.Parse(s));
    int nums_length = nums.Count;

    // Build the sequence.
    List<string> sequence = new List<string>();
    sequence.Add(NumsToString(nums));
    for (; ; )
    {
        List<int> new_nums = new List<int>();
        for (int i = 0; i < nums.Count; i++)
            new_nums.Add(Math.Abs(nums[i] - nums[(i + 1) % nums_length]));
        nums = new_nums;

        nums_str = NumsToString(nums);
        if (sequence.Contains(nums_str))
        {
            sequence.Add(nums_str);
            int start = sequence.IndexOf(nums_str);
            int length = sequence.Count - start;
            lblSequenceStart.Text = start.ToString();
            lblSequenceLength.Text = length.ToString();
            lstResults.DataSource = sequence;
            return;
        }
        sequence.Add(nums_str);

        Debug.Assert(sequence.Count < 10000);
    }
}

This code starts by splitting the numbers that you entered at the dash characters. It calls the NumsToString method described shortly to convert the list of values into a string and adds it to the sequence list.

The program then enters an infinite loop. Inside the loop, the code finds the next tuple in the sequence. If that tuple is already in the sequence list, the code displays the Ducci sequence and some information about it and breaks out of the loop. If the new tuple is not yet in the sequence, the code adds it to the sequence list and repeats the loop.

The Debug.Assert is there to prevent the loop from running forever if there is a bug in the program.

The following code shows the NumsToString method.

// Return the numbers separated by commas.
private string NumsToString(List<int> nums)
{
    return string.Join("-", nums.ConvertAll(n => n.ToString()).ToArray());
}

This method uses the Convert.ToAll LINQ extension method to convert the values in the nums list into a list of strings. It converts the list into an array and passes it into the string class’s Join method to join the values with dash characters. The method then returns the result.

Download the example to see additional details.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, mathematics | Tagged , , , , , , , , , | Leave a comment

Encode and decode Futurama’s alienese in C#


[encode and decode]

My last two posts explained a program can pull out pieces of an image. In those examples, the image was a collection of glyphs and the program saved the individual glyphs in png files. This post uses the glyphs to encode and decode messages using Futurama’s alienese language.

The user interface uses a TableLayoutPanel with two columns and six rows to hold the program’s other controls. I’ll let you look over the user interface to see exactly how it works.

The program creates the following form-level dictionary to map characters to glyphs.

// Dictionary to map characters to glyph buttons.
private Dictionary<char, Image> GlyphDict =
    new Dictionary<char, Image>();

When the program starts, the following code prepares the program for use.

private void Form1_Load(object sender, EventArgs e)
{
    // Create the decoding buttons.
    for (char ch = 'A'; ch <= 'M'; ch++)
        MakeButton(GlyphDict, flpButtons1, ch.ToString());
    for (char ch = 'N'; ch <= 'Z'; ch++)
        MakeButton(GlyphDict, flpButtons2, ch.ToString());
    for (char ch = '0'; ch <= '9'; ch++)
        MakeButton(GlyphDict, flpButtons3, ch.ToString());
    foreach (char ch in "\'!“”:-.; ")
        MakeButton(GlyphDict, flpButtons4, ch.ToString());

    Button btnCl = new Button();
    btnCl.Font = this.Font;
    btnCl.Parent = flpButtons4;
    btnCl.Size = new Size(54, 24);
    btnCl.Text = "CL";
    btnCl.Click += btnCl_Click;

    Button btnBs = new Button();
    btnBs.Font = this.Font;
    btnBs.Parent = flpButtons4;
    btnBs.Size = new Size(54, 24);
    btnBs.Text = "BS";
    btnBs.Click += btnBs_Click;
}

This code uses several loops that call the MakeButton method described shortly to create glyph buttons. It then creates two buttons to clear the current message and to act as a backspace.

The following code shows the MakeButton method.

// Make a button.
private const int BTN_WID = 24;
private void MakeButton(
    Dictionary<char, Image> glyph_dict,
    Control parent, string text)
{
    // Make the button.
    Button btn = new Button();
    btn.Parent = parent;
    btn.Size = new Size(BTN_WID, BTN_WID);
    btn.Tag = text;
    btn.Font = this.Font;
    btn.Click += btnDecode_Click;
    ttip.SetToolTip(btn, text);

    // Get the button's image.
    string name = ResourceName(text);
    Bitmap bm = Properties.Resources.ResourceManager.GetObject(name)
        as Bitmap;
    Bitmap new_bm = new Bitmap(BTN_WID, BTN_WID);
    using (Graphics gr = Graphics.FromImage(new_bm))
    {
        gr.InterpolationMode = InterpolationMode.High;
        gr.Clear(Color.White);
        Point[] dest_points =
        {
            new Point(2, 2),
            new Point(btn.ClientSize.Width - 3, 2),
            new Point(2, btn.ClientSize.Height - 3),
        };
        gr.DrawImage(bm, dest_points);
    }
    btn.Image = new_bm;

    // Save the image in the glyph dictionary.
    glyph_dict.Add(text[0], new_bm);
}

This method makes a new button, adds it to a FlowLayoutPanel control, and sets various button properties. In particular, it stores the glyph’s text value in the button’s Tag property and uses the same text for the button’s tooltip.

The method then gets the button’s image from the project’s resources. The Properties.Resources.ResourceManager.GetObject method gets the object by that name. The code resizes the image resource to fit the button.

The method finishes by adding the button’s image to the dictionary using the button’s text as a key. Later, when the program needs to encode a message, it uses the letter to look up the glyph in the dictionary.

Most of the project’s image resources are named after the glyphs they represent. For example, the glyph representing A is stored in the file A.png and its resource is named A. However, special characters such as ! and ; have different names. The following ResourceName method converts a button’s text into its resource name.

// Convert a character into its resource name.
private string ResourceName(string text)
{
    char ch = text[0];
    if ((ch >= 'A') && (ch <= 'Z')) return text;
    if ((ch >= '0') && (ch <= '9')) return "_" + text;
    switch (ch)
    {
        case '\'': return "Apostrophe";
        case '!': return "Bang";
        case '“': return "OpenQuote";
        case '”': return "OpenQuote";
        case ':': return "Colon";
        case '-': return "Dash";
        case '.': return "Period";
        case ';': return "SemiColon";
        case ' ': return "Space";
        default: return null;
    }
}

If the text is a letter, the method simply returns it.

If the text is a digit, Visual Studio prepends an underscore to the digit to make the resource name, so this method does the same.

If the text is a special character, the method uses a switch statement to return the appropriate name.

If the text is none of those, the method returns null. (That should not happen, but that statement makes Visual Studio happy because it means the method always returns something.)

All of that setup makes it relatively easy to encode and decode messages. The program’s glyph buttons all execute the following event handler.

// Add a character to the decoded message.
private void btnDecode_Click(object sender, EventArgs e)
{
    Button btn = sender as Button;
    txtDecode.AppendText(btn.Tag as string);
}

This method simply appends the button’s text value (as stores in its Tag property) to the txtDecode text box.

When you change the text in the encoding text box, the following code displays the text’s encoding.

// Add a character to the decoded message.
private void txtEncode_TextChanged(object sender, EventArgs e)
{
    flpEncoding.Controls.Clear();
    foreach (char ch in txtEncode.Text.ToUpper())
    {
        if (GlyphDict.ContainsKey(ch))
        {
            PictureBox pic = new PictureBox();
            pic.SizeMode = PictureBoxSizeMode.AutoSize;
            pic.Image = GlyphDict[ch];
            pic.Parent = flpEncoding;
        }
    }
}

This method clears the FlowLayoutPanel named flpEncoding. It then loops through the messages characters. It creates a PictureBox, uses the character to look up the corresponding glyph image in the program’s dictionary GlyphDict, and makes the PictureBox display it.

That’s about all there is how this program encodes and decodes messages. Download the example to see additional details.

There are plenty of modifications that you could make to this program. For example, you could trim the glyph images so they contain less spaces around their edges and produce a tighter encoding. You could also make the program save the encoding in a single image and allow the user to specify dimensions for that output so, for example, you could display long messages on a single line. If you make those modifications or others, or if you have suggestions for changes that might be interesting, please post a note in the comments below.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in graphics, image processing | Tagged , , , , , , , , , , | Leave a comment

Separate glyphs in an image in C#, Part 2

[separate glyphs]

This is the second post describing the program that I wrote to separate glyphs in a combined image. The first post explained the program’s menus. This post explains how the program manages its red glyph selection rectangle.

This post describes three pieces of the program: mouse events, keyboard events, and drawing the selection rectangle.

Mouse Events

When you press the mouse down over the glyph PictureBox, the following code begins dragging the selection rectangle.

// Mouse events.
private bool Dragging = false;
private Point DragOffset;
private void picGlyphs_MouseDown(object sender, MouseEventArgs e)
{
    // See if the mouse is inside the current GlyphRect.
    if (!GlyphRect.Contains(e.Location)) return;

    Dragging = true;
    DragOffset = new Point(
        GlyphRect.X - e.Location.X,
        GlyphRect.Y - e.Location.Y);
}

This code first defines the variables Dragging and DragOffset. They are defined at the form level, so all of the form’s code can see them.

The MouseDown event handler uses GlyphRect.Contains to see if the mouse is over the selection rectangle. If the mouse is not over the rectangle, the event handler just exits.

If the mouse is over the rectangle, the code sets Dragging to true to indicate that the user is dragging the rectangle. It saves the offset between the mouse position and the selection rectangle’s upper left corner in variable DragOffset so the program can later position the rectangle with respect to the mouse when it moves.

Speaking of which, when you move the mouse, the following event handler executes.

private void picGlyphs_MouseMove(object sender, MouseEventArgs e)
{
    if (!Dragging) return;
    int x = e.Location.X + DragOffset.X;
    int y = e.Location.Y + DragOffset.Y;
    GlyphRect = new Rectangle(x, y, GlyphRect.Width, GlyphRect.Height);
    picGlyphs.Refresh();
}

If Dragging is false, then no drag is in progress so the event handler simply exits.

If a drag is in progress, the code calculates the new location for the selection rectangle, keeping it the same distance from the mouse’s position. It updates GlyphRect and then refreshes the glyph PictureBox to draw the repositioned rectangle.

When you release the mouse over the PictureBox, the following event handler ends the drag.

private void picGlyphs_MouseUp(object sender, MouseEventArgs e)
{
    Dragging = false;
}

This code simply sets Dragging to false to indicate that a drag is no longer in progress.

Note that the MouseUp event may fire when no drag is already in progress. For example, if you press the mouse down over part of the form not over the PictureBox, drag the mouse over the PictureBox, and then release the mouse, the event is raised. You could modify the event handler’s code to check whether Dragging is true before setting it to false, but it doesn’t hurt to set it to false in either case so the test isn’t necessary.

Also note that the MouseUp event handler does not refresh the PictureBox. Usually in programs that allow the user to drag something, the MouseMove event handler has already updated the display just before the MouseUp event occurs, so that event handler doesn’t need also update the display.

Keyboard Events>

At design time, I set the form’s KeyPreview property to true to make the form receive keyboard events. When you press a key, the following event handler executes.

// Handle arrow keys.
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    int distance = 1;
    if (e.Shift) distance = 5;

    switch (e.KeyCode)
    {
        case Keys.Left:
            GlyphRect.X -= distance;
            break;
        case Keys.Right:
            GlyphRect.X += distance;
            break;
        case Keys.Up:
            GlyphRect.Y -= distance;
            break;
        case Keys.Down:
            GlyphRect.Y += distance;
            break;
    }
    picGlyphs.Refresh();
}

The code first sets distance to 1. This is the number of pixels that the program will move the glyph selection rectangle if you pressed an arrow key. If the Shift key is pressed, the code updates distance to 5 so the program moves the rectangle farther.

Next, the code uses a switch statement to see what key you pressed. If you pressed an arrow key, the code adds distance to the appropriate rectangle coordinate.

When the switch block ends, the program refreshes its PictureBox to show the rectangle in its new position.

Note that if you press some key other than an arrow key, the program still refreshes the PictureBox. In my tests, you can’t tell. If there is a flicker, you could modify the code to only refresh if the rectangle has moved.

Drawing the Selection Rectangle

When the program’s glyph PictureBox refreshes, the following Paint event handler executes.

// Draw the glyphs and glyph rectangle.
private void picGlyphs_Paint(object sender, PaintEventArgs e)
{
    if (GlyphsBm == null) return;

    e.Graphics.DrawImage(GlyphsBm, 0, 0);
    using (Pen pen = new Pen(Color.Red, 3))
    {
        e.Graphics.DrawRectangle(pen, GlyphRect);
    }
}

If the GlyphBm bitmap is null, then the user has not loaded a bitmap yet so the method simply exits.

If a bitmap is loaded, the code uses the Graphics object’s DrawImage method to draw the loaded glyph image onto the PictureBox. It then creates a thick pen and uses it to draw the glyph selection rectangle.

Summary

That’s the end of this example. Now that we can separate glyphs that are included in a single image, we can use the glyphs to make the alienese encoder/decoder. I’ll describe that program in my next post.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in graphics, image processing | Tagged , , , , , , , , , | Leave a comment

Separate glyphs in an image in C#, Part 1

[separate glyphs]

I’m working on a program to translate to and from Futurama’s alienese alphabet. Before I do that, I wrote this program to separate glyphs stored in an image that contains the alphabet. It’s a fairly full-featured program, so I’ll describe in in a couple of posts. In this post, I’ll explain how the program’s menus work. In the next post, I’ll explain the program’s other pieces such as how the glyph selection rectangle works.

Using the Program

To use the program, you use the File menu’s Open command to open an image containing the glyphs. To separate glyphs, you then drag a red glyph selection rectangle around on the image to select a glyph. You can then save the selected area in an image file named so you can tell which glyph it contains.

The program has a couple of other extra features. For example, it lets you resize the glyph selection rectangle and it lets you use arrow keys to move the selection rectangle more precisely.

Controls

The design is straightforward. The form simply contains a PictureBox and a menu. The menu has four menu items:

  • File
    • Open – Opens a file containing the glyphs
    • Save Glyph – Saves the currently selected glyph
    • Exit – Closes the program
  • Data
    • Dimensions – Lets you specify the glyphs’ dimensions

The following selections show how the program implements its menu commands.

File ⮞ Open

The program uses the following code to define a Bitmap to hold the glyph image.

// The image containing the glyphs.
private Bitmap GlyphsBm = null;

When you select the File menu’s Open command, the following code executes.

// Open a glyph file.
private void mnuFileOpen_Click(object sender, EventArgs e)
{
    Cursor = Cursors.WaitCursor;
    Refresh();

    if (ofdFile.ShowDialog() == DialogResult.OK)
    {
        try
        {
            GlyphsBm = LoadBitmapUnlocked(ofdFile.FileName);
            picGlyphs.ClientSize = GlyphsBm.Size;
            picGlyphs.Visible = true;
            mnuFileSaveGlyph.Enabled = true;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
    Cursor = Cursors.Default;
}

This code displays the OpenFileDialog named ofdFile. If you select a file and press Open, the code uses the LoadBitmapUnlocked method to load the image file without locking it. (For information on that method, see Load images without locking their files in C#.)

The code saves the bitmap in the GlyphsBm variable and makes the PictureBox visible. That raises that control’s Paint event hanlder, which I’ll describe later. The code also enables the File ⮞ Save Glyph, which is described next.

File ⮞ Save Glyph

When you invoke this command, the following code executes.

// Save a glyph.
private void mnuFileSaveGlyph_Click(object sender, EventArgs e)
{
    if (sfdGlyph.ShowDialog() == DialogResult.OK)
    {
        using (Bitmap bm = new Bitmap(GlyphRect.Width, GlyphRect.Height))
        {
            using (Graphics gr = Graphics.FromImage(bm))
            {
                Rectangle dest_rect = new Rectangle(
                    0, 0, GlyphRect.Width, GlyphRect.Height);
                gr.DrawImage(GlyphsBm, dest_rect, GlyphRect,
                    GraphicsUnit.Pixel);
            }
            SaveImage(bm, sfdGlyph.FileName);
        }
    }
}

This code displays the SaveFileDialog named sfdGlyph. If you select a file and click Save, the program creates a Bitmap with the dimensions of the GlyphRect. (GlyphRect is a Rectangle that I’ll describe later when I talk about mouse and keyboard events.) It then makes an associated Graphics object.

Note that the code uses a using block for the Bitmap and Graphics objects so they are automatically disposed when the program is done with them.

Next, the code makes a Rectangle covering the entire Bitmap. It then calls the Graphics object’s DrawImage method to copy the part of the glyphs image onto the Bitmap. (The GlyphRect rectangle already holds the area that should be copied so we don’t need to make a new rectangle to define that area.)

After copying the part off the image, the code calls the SaveImage method to save the image with an appropriate format for the filename’s extension. For example, if the file’s name ends with .png, then SaveImage saves the image with the PNG format. (For more information about that method, see Save images with an appropriate format depending on the file name’s extension in C#.)

File ⮞ Exit

This one is easy.

// Close the application.
private void mnuFileExit_Click(object sender, EventArgs e)
{
    Close();
}

Data ⮞ Dimensions

[separate glyphs]

This command displays the dialog on the right where you can enter new dimensions for the glyph selection rectangle.

The dialog is a simple Windows Form that contains a Label, TextBox, and two buttons. I only set a few properties to make the dialog work.

The form’s AcceptButton and CancelButton properties are set to the two buttons. Setting those properties automatically sets the Cancel button’s DialogResult property to Cancel. When you click this button, it sets the form’s DialogResult property to Cancel and that closes the dialog.

Setting the AcceptButton property does not set the OK button’s DialogResult property. The thought is that you might want that button to do some error checking or other processing before closing the dialog. I don’t want that in this example, so I set the button’s DialogResult property to OK. Now when you click this button, it sets the form’s DialogResult property so the dialog closes.

The last important property I set was the text box control’s Modifiers property. By default, a form’s controls are only visible to code within the form. I set this property to Public so code int he main program can see the control. (A more “pure” way to do this would be to give the form a property that gets and sets the control’s value, but that’s extra work for not much benefit in this example.)

The following code shows how the program uses the dialog.

// Let the user select the glyph dimensions.
private void mnuDataDimensions_Click(object sender, EventArgs e)
{
    DimensionsDialog dlg = new DimensionsDialog();
    dlg.txtDimensions.Text = GlyphRect.Width.ToString() +
        " x " + GlyphRect.Height.ToString();
    if (dlg.ShowDialog() == DialogResult.OK)
    {
        try
        {
            string[] fields = dlg.txtDimensions.Text.ToLower().Split('x');
            int width = int.Parse(fields[0]);
            int height = int.Parse(fields[1]);
            GlyphRect = new Rectangle(GlyphRect.X, GlyphRect.Y,
                width, height);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
        picGlyphs.Refresh();
    }
}

The code creates a new DimensionsDialog and sets its text box’s text to show the current width and height of the glyph selection rectangle.

Next, the code displays the dialog. If you click OK, the code parses the text that you entered and updates the glyph selection rectangle’s dimensions.

After it is done, the code refreshes the PictureBox to make it show the new selection rectangle.

Summary

That’s the end of the program’s menu code. In my next post, I’ll explain how the program manages its glyph selection rectangle.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in graphics, image processing | Tagged , , , , , , , , , | 1 Comment

Videos vs Animated GIFs – Pros and Cons

[animated GIFs]

The modern web and mobile applications are rich in media content because it’s one of the crucial things that keeps the user engaged. An image is worth a thousand words, and a video is worth a million. Obviously, meaningful animated content has more worth than a static image or a paragraph of text. In this article, we’re going explore the two popular media files and which one’s actually better for the web.

Animated GIF vs Video

A GIF is a bitmap image format that supports animation. It’s popular on the web, social and other multimedia platforms because of its widespread adoption and support for animation. Common examples include a rotating logo, a banner with animated characters etc. An animated GIF can either loop endlessly or can play a few sequences and then stop. Web ad banners often make use of animated GIFs in their advertising or marketing campaigns.

Animated GIFs essentially make use of the frame feature within the GIF format. GIFs have the ability to host a number of frames within a single file and then display each frame in quick succession. You can also pull apart the GIF file if you want to see the individual images.

A video file on the other hand comprises of a container that stores the video coding data, the audio coding data and other metadata. The coded video and audio inside a container is known as essence, and a script that can decode an audio/video is known as a codec. Video formats standards like webm are profiles that have restrictions on the type of container format and the audio and video compression format used. However, you can use a video encoder library like FFMpegSharpSharpFFmpeg and MeWiG – MEncoder or a video transcoding service to make videos viewable across different platforms and devices. 

Pros and Cons

File Size

The file size of the final product is one of the big concerns when it comes to using animated GIFs. As mentioned earlier, since an animated GIF is basically a collection of individual images, the compression can only take place at the image level. On the other hand, videos and movie codecs have the ability to provide x/y compression for each frame, as well as allow for a compression across a third dimension – time.

If you have viewed an animated GIF on platforms like Imgur or Gfycat and inspected it, you might find that the underlying GIF is actually a video. The main reason why everyone uses video, even for displaying GIFs is the file size. Animated GIFs can have quite a large file size.

It is actually quite common for GIFs to measure up to several MBs, based largely on its quality, frame rate, length, etc. For developers attempting to improve the load times of their websites or to assist users to reduce their data usage, animated GIFs are not the ideal solution in this scenario.

Presentation of Messages

An advantage of animated GIFs is that it can show any message in a better way than static GIFs while at the same time, being easier to create than a video message. Animated GIFs are the perfect option when creating instruction type animations for tutorials, etc. Additionally, for younger audiences, animations tend to keep them involved for a longer period of time and inhibit distraction.

Color Options

Animated GIFs limit the creator to a color scheme of 256 colors. For this reason, animated GIFs can often look to be of a lower quality when compared to other image file types. In some cases, they can come across as a little pixelated or blocky.

Ability to Edit

Another disadvantage of animated GIFs is that the user is not able to edit the animated GIF once the animation has been coded into the GIF file. Users, therefore, have to wait for the final image before working on the animation. Video files, on the other hand, allow for required editing to be included at any time during the creation of the video. In animated GIFs, users would have to redo the whole process from the beginning to accommodate even for a minor edit.

Audio and Sound Effects

As animated GIFs are a collection of a set of static images, users are unable to add sound to the animation. Needless to say, videos have the upper hand here.

Available Formats

As animated GIFs started out as normal GIF images to which animation was added on later, it is essentially limited to just one file format. On the other hand, videos can make use of a number of different formats. These range from video formats that display only the pixels that have changed between each frame to formats that track the movement of pixel groups. Ex- the movement of an object like a ball on the screen.

Compatibility

Animated GIFs continue to have a large audience on the web largely due to its compatibility with most platforms. This ensures that animated GIFs can be posted almost anywhere. Video formats have some limitations in this regard.

Cost

When compared to videos, animated GIFs are fairly economical and easy to create. Though being around since the 1990s, they are fast becoming a trend to include them in a landing page, especially when attempting to demonstrate a product or a service.

Summary

To conclude, animated GIFs come across as a cool and fun way to connect with different target audiences with funny, informative and engaging content. However, their large file size poses a hindrance by possibly increasing the page load time, and consuming a large part of your customer’s data plan.

Here are a few things to consider when trying to decide whether to use a video or an animated GIF on your landing page –

  1. Videos are great if they are well made and professional. A poorly made video is going to do more harm than good for landing page conversion rate.
  2. At times, animated GIFs can serve as a distraction for the visitor.
  3. If your video is just a demo of a product, you can consider using an animated GIF
  4. If you want to display emotions or need sound effects, use videos. Animated GIFs are not ideal when trying to portray emotions.

That said, in case you still want to make use of animated GIFs in your website or app, you can choose to consider converting them to video files which will save your customer’s data usage as well as not slowing down your website or app’s speed.

About the Guest Author

[animated GIFs]

Limor is a technical writer and editor at Agile SEO, a boutique digital marketing agency focused on technology and SaaS markets. She has over 10 years’ experience writing technical articles and documentation for various audiences, including technical on-site content, software documentation, and dev guides. She specializes in big data analytics, computer/network security, middleware, software development and APIs.

LinkedIn: https://www.linkedin.com/in/limormaayan/
Twitter: @LimiMaayan


Follow me on Twitter   RSS feed   Donate




Posted in graphics, multimedia, web | Tagged , , , , , , , , | Leave a comment

Graph points in WPF and C#

[graph points]

This example extends the previous example Draw a graph in WPF and C# to draw ellipses marking the data sets’ points. See that example for an explanation of how most of the program works.

The following code shows how the program draws its data sets. Scroll down to the blue lines to see the code that is used to graph points. (You can read the rest of the code if you like. See the previous post for information about that code.)

// Draw a simple graph.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    const double margin = 10;
    double xmin = margin;
    double xmax = canGraph.Width - margin;
    double ymin = margin;
    double ymax = canGraph.Height - margin;
    const double step = 10;

    // Make the X axis.
    GeometryGroup xaxis_geom = new GeometryGroup();
    xaxis_geom.Children.Add(new LineGeometry(
        new Point(0, ymax), new Point(canGraph.Width, ymax)));
    for (double x = xmin + step; x <= canGraph.Width - step; x += step)
    {
        xaxis_geom.Children.Add(new LineGeometry(
            new Point(x, ymax - margin / 2),
            new Point(x, ymax + margin / 2)));
    }

    Path xaxis_path = new Path();
    xaxis_path.StrokeThickness = 1;
    xaxis_path.Stroke = Brushes.Black;
    xaxis_path.Data = xaxis_geom;

    canGraph.Children.Add(xaxis_path);

    // Make the Y ayis.
    GeometryGroup yaxis_geom = new GeometryGroup();
    yaxis_geom.Children.Add(new LineGeometry(
        new Point(xmin, 0), new Point(xmin, canGraph.Height)));
    for (double y = step; y <= canGraph.Height - step; y += step)
    {
        yaxis_geom.Children.Add(new LineGeometry(
            new Point(xmin - margin / 2, y),
            new Point(xmin + margin / 2, y)));
    }

    Path yaxis_path = new Path();
    yaxis_path.StrokeThickness = 1;
    yaxis_path.Stroke = Brushes.Black;
    yaxis_path.Data = yaxis_geom;

    canGraph.Children.Add(yaxis_path);

    // Make some data sets.
    Brush[] brushes = { Brushes.Red, Brushes.Green, Brushes.Blue };
    Random rand = new Random();
    for (int data_set = 0; data_set < 3; data_set++)
    {
        int last_y = rand.Next((int)ymin, (int)ymax);

        PointCollection points = new PointCollection();
        for (double x = xmin; x <= xmax; x += step)
        {
            last_y = rand.Next(last_y - 10, last_y + 10);
            if (last_y < ymin) last_y = (int)ymin;
            if (last_y > ymax) last_y = (int)ymax;
            points.Add(new Point(x, last_y));
        }

        Polyline polyline = new Polyline();
        polyline.StrokeThickness = 1;
        polyline.Stroke = brushes[data_set];
        polyline.Points = points;

        canGraph.Children.Add(polyline);

        // Display ellipses at the points.
        const float width = 4;
        const float radius = width / 2;
        foreach (Point point in points)
        {
            Ellipse ellipse = new Ellipse();
            ellipse.SetValue(Canvas.LeftProperty, point.X - radius);
            ellipse.SetValue(Canvas.TopProperty, point.Y - radius);
            ellipse.Fill = brushes[data_set];
            ellipse.Stroke = brushes[data_set];
            ellipse.StrokeThickness = 1;
            ellipse.Width = width;
            ellipse.Height = width;
            canGraph.Children.Add(ellipse);
        }
    }
}

The blue lines of code loop through a data set’s points. For each point, the code creates a new Ellipse object. It uses the SetValue method to set the Left and Top dependency properties provided by the Canvas control that will contain the Ellipse. If you create an Ellipse in the Window Designer, those properties appear in the Properties window as Canvas.Left and Canvas.Top.

The code then fills in the Ellipse object’s other properties such as its Fill color, Stroke color, StrokeThickness, Width, and Height. The loop finishes by adding the Ellipse to the Canvas control’s Children collection.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, drawing, geometry, graphics, mathematics, wpf | Tagged , , , , , , , , , , , , , , , , , , | Leave a comment

Search files for target text in C#

[search files]

The .NET tools available in the System.IO namespace and normal string methods make it easy to search files for target text. When you click the Search button, the following code displays the lines in a file that contain a particular string.

// Search for lines containing the target text.
private void btnSearch_Click(object sender, EventArgs e)
{
    lstResults.Items.Clear();
    lblResults.Text = "";
    Refresh();

    string target = txtTarget.Text;
    string[] lines = File.ReadAllLines(txtFile.Text);
    foreach (string line in lines)
        if (line.Contains(target))
            lstResults.Items.Add(line);

    lblResults.Text = lstResults.Items.Count.ToString() + " matches";
}

This code gets the target text. It then uses File.ReadAllLines to read the file into an array holding the file’s lines.

The code then loops through the lines and uses each line’s Contains method to see if the line contains the target text. If the line does contain the text, then the program adds it to the ListBox named lstResults.

The code finishes by displaying the number of lines saved into the ListBox.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in files | Tagged , , , , , , , | Leave a comment

Write Console window output to multiple places in C#

[example]

This example captures output sent to the Console window and displays it in multiple controls plus the Console window. It is based on an excellent reply by user Servy on Stack Overflow.

The following class is a TextWriter that represents a list if places where it should write text destined to the Console window.

// Represent a list of TextWriter objects.
public class ListTextWriter : TextWriter
{
    private List<TextWriter> Writers = new List<TextWriter>();

    // Add a new textWriter to the list.
    public void Add(TextWriter writer)
    {
        Writers.Add(writer);
    }

    public override void Write(char value)
    {
        foreach (var writer in Writers)
            writer.Write(value);
    }

    public override void Write(string value)
    {
        foreach (var writer in Writers)
            writer.Write(value);
    }

    public override void Flush()
    {
        foreach (var writer in Writers)
            writer.Flush();
    }

    public override void Close()
    {
        foreach (var writer in Writers)
            writer.Close();
    }

    public override Encoding Encoding
    {
        get { return Encoding.Unicode; }
    }
}

It’s Add method adds a TextWriter object to this object’s Writers list. The overridden Write, Flush, and Close methods simply loop through the control’s writers and calls the corresponding methods for each.

The main program uses this class in the following code.

// Install a new output TextWriter and the
// Console window's default output writer.
private void Form1_Load(object sender, EventArgs e)
{            
    ListTextWriter list_writer = new ListTextWriter();
    list_writer.Add(new TextBoxWriter(txtConsole));
    list_writer.Add(new TextBoxWriter(lblConsole));
    list_writer.Add(Console.Out);

    Console.SetOut(list_writer);
}

This code makes a new ListTextWriter. It then adds two TextBoxWriter controls (from the previous post) attached to a TextBox and a Label. It also adds the Console window’s default output TextWriter. Finally, the code calls the Console object’s SetOut method to make it use the new ListTextWriter.

Now when the program sends text to the Console window, the ListTextWriter sends the text to the TextBox, Label, and normal Console window output writer.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in Uncategorized | Tagged , , , , , , , , | Leave a comment

Redirect Console window output to a TextBox in C#

[example]

This example captures output sent to the Console window and displays it in a TextBox control. It is based on an excellent reply by user Servy on Stack Overflow.

The Console object has a SetOut method that lets you change the TextWriter object to which it sends output. To redirect the output, you create a TextWriter subclass and then make the Console object send output to it.

The following code shows the TextBoxWriter class.

public class TextBoxWriter : TextWriter
{
    // The control where we will write text.
    private Control MyControl;
    public TextBoxWriter(Control control)
    {
        MyControl = control;
    }

    public override void Write(char value)
    {
        MyControl.Text += value;
    }

    public override void Write(string value)
    {
        MyControl.Text += value;
    }

    public override Encoding Encoding
    {
        get { return Encoding.Unicode; }
    }
}

This method overrides the TextWriter base class’s Write method and Encoding property. The Write(char value) method and the property are marked as abstract, so you must override them in any derived class. Overriding Write(string value) makes output more efficient.

The two versions of the Write method simply append their parameters to the control’s Text property. As in Servy’s example, the writer’s control is a generic Control, so this will work with a TextBox, Label, or other reasonable control.

When the program starts, the following Form_Load event handler installs the new writer.

// Install a new output TextWriter for the Console window.
private void Form1_Load(object sender, EventArgs e)
{
    TextBoxWriter writer = new TextBoxWriter(txtConsole);
    Console.SetOut(writer);
}

This code creates a new TextBoxWriter associated with the form’s TextBox named txtConsole. It then passes the writer into the Console object’s SetOut method.

When you enter text in the form’s TextBox and click Write, the following code executes.

// Write to the Console window.
private void btnWrite_Click(object sender, EventArgs e)
{
    Console.WriteLine(txtOutput.Text);
    txtOutput.Clear();
}

This code simply writes your text into the Console object and then clears the input TextBox.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in console | Tagged , , , , , , , , | 1 Comment

Get a weather forecast from openweathermap.org in C#

[weather forecast]

This example displays a weather forecast from openweathermap.org. To get a forecast or other data, you build a URL describing your request and navigate to it. You then parse the returned XML or JSON data to see what openweathermap.org has to say.

The first step in using openweathermap.org is to get an API ID. You can get one for free here. You will need to sign up for a free account.

After you get an API ID, you can use it in your URLs.

This example begins with the following code.

// Enter your API key here.
// Get an API key by making a free account at:
//      http://home.openweathermap.org/users/sign_in
private const string API_KEY = "12345678901234567890123456789012";

// Query URLs. Replace @LOC@ with the location.
private const string CurrentUrl =
    "http://api.openweathermap.org/data/2.5/weather?" +
    "@QUERY@=@LOC@&mode=xml&units=imperial&APPID=" + API_KEY;
private const string ForecastUrl =
    "http://api.openweathermap.org/data/2.5/forecast?" +
    "@QUERY@=@LOC@&mode=xml&units=imperial&APPID=" + API_KEY;

This code defines your API ID. Replace the number shown here with your ID.

The two string constants hold template URLs to get current weather conditions and forecast conditions. The code will later replace the pieces @QUERY@ and @LOC@ with values for a specific query.

When the program starts, the following Load event handler executes.

// Query codes.
private string[] QueryCodes = { "q", "zip", "id", };

// Fill in query types. These should match the QueryCodes.
private void Form1_Load(object sender, EventArgs e)
{
    cboQuery.Items.Add("City");
    cboQuery.Items.Add("ZIP");
    cboQuery.Items.Add("ID");

    cboQuery.SelectedIndex = 1;
}

The QueryCodes array holds query strings to get data by city name (q), zip code, or ID.

The form’s Load event handler initializes the cboQuery ComboBox control with values corresponding to the query codes. Those values must match up with the ones in the QueryCodes array.

When you click Forecast, the following code executes.

// Get a forecast.
private void btnForecast_Click(object sender, EventArgs e)
{
    // Compose the query URL.
    string url = ForecastUrl.Replace("@LOC@", txtLocation.Text);
    url = url.Replace("@QUERY@", QueryCodes[cboQuery.SelectedIndex]);

    // Create a web client.
    using (WebClient client = new WebClient())
    {
        // Get the response string from the URL.
        try
        {
            DisplayForecast(client.DownloadString(url));
        }
        catch (WebException ex)
        {
            DisplayError(ex);
        }
        catch (Exception ex)
        {
            MessageBox.Show("Unknown error\n" + ex.Message);
        }
    }
}

This code takes the forecast URL template and replaces @LOC@ with the location that you entered. It then replaces @QUERY@ with the appropriate query code.

Next, the creates a WebClient and makes it navigate to the URL. The code then either displays the forecast or an error message. I’ll say more about those methods in a moment. First, the following shows a typical response somewhat abbreviated.

<weatherdata>
  <location>
    <name>Washington D.C.</name>
    <type/>
    <country>US</country>
    <timezone/>
    <location altitude="0" latitude="38.895" longitude="-77.0367" geobase="geonames" geobaseid="420008701"/>
  </location>
  <credit/>
  <meta>
    <lastupdate/>
    <calctime>0.0043</calctime>
    <nextupdate/>
  </meta>
  <sun rise="2018-07-30T10:08:00" set="2018-07-31T00:20:33"/>
  <forecast>
    <time from="2018-07-30T15:00:00" to="2018-07-30T18:00:00">
      <symbol number="500" name="light rain" var="10d"/>
      <precipitation unit="3h" value="0.11" type="rain"/>
      <windDirection deg="124.5" code="SE" name="SouthEast"/>
      <windSpeed mps="10.36" name="Fresh Breeze"/>
      <temperature unit="imperial" value="74.68" min="74.68" max="76.74"/>
      <pressure unit="hPa" value="1026.45"/>
      <humidity value="82" unit="%"/>
      <clouds value="overcast clouds" all="92" unit="%"/>
    </time>
    <time from="2018-07-30T18:00:00" to="2018-07-30T21:00:00">
      <symbol number="500" name="light rain" var="10d"/>
      <precipitation unit="3h" value="0.0075" type="rain"/>
      <windDirection deg="125.004" code="SE" name="SouthEast"/>
      <windSpeed mps="10.27" name="Fresh Breeze"/>
      <temperature unit="imperial" value="73.36" min="73.36" max="74.9"/>
      <pressure unit="hPa" value="1025.8"/>
      <humidity value="83" unit="%"/>
      <clouds value="broken clouds" all="80" unit="%"/>
    </time>
    ...
  </forecast>
</weatherdata>

The following code parses the data to get the forecast.

// Display the forecast.
private void DisplayForecast(string xml)
{
    // Load the response into an XML document.
    XmlDocument xml_doc = new XmlDocument();
    xml_doc.LoadXml(xml);

    // Get the city, country, latitude, and longitude.
    XmlNode loc_node = xml_doc.SelectSingleNode("weatherdata/location");
    txtCity.Text = loc_node.SelectSingleNode("name").InnerText;
    txtCountry.Text = loc_node.SelectSingleNode("country").InnerText;
    XmlNode geo_node = loc_node.SelectSingleNode("location");
    txtLat.Text = geo_node.Attributes["latitude"].Value;
    txtLong.Text = geo_node.Attributes["longitude"].Value;
    txtId.Text = geo_node.Attributes["geobaseid"].Value;

    lvwForecast.Items.Clear();
    char degrees = (char)176;
    
    foreach (XmlNode time_node in xml_doc.SelectNodes("//time"))
    {
        // Get the time in UTC.
        DateTime time =
            DateTime.Parse(time_node.Attributes["from"].Value,
                null, DateTimeStyles.AssumeUniversal);

        // Get the temperature.
        XmlNode temp_node = time_node.SelectSingleNode("temperature");
        string temp = temp_node.Attributes["value"].Value;

        ListViewItem item = lvwForecast.Items.Add(time.DayOfWeek.ToString());
        item.SubItems.Add(time.ToShortTimeString());
        item.SubItems.Add(temp + degrees);
    }
}

This method loads the returned XML data into an XmlDocument object. It then gets various elements in the object to find the location’s city name, country, latitude, longitude, and ID.

NOTE: City names can be hard to use. Typically, you use the city’s name and country name separated by a comma as in Springfield, US. Unfortunately, there doesn’t seem to be a good way to differentiate among different cities with the same name. A better approach is to use city ID or ZIP code.

After displaying the basic information, the program loops through the XML document’s time elements. For each of those elements, the method finds the from value. All openweathermap time values are in UTC, so the code parses the time as a universal time. Later, when the code gets the time’s value, it gets it in local time so it makes more sense.

Next, the code gets the entry’s temperature element. It then adds the weekday, time, and temperature to the lvwForecast ListView control.

If there is an error, the web response object contains additional error information. The following code shows how the program displays that information.

// Display an error message.
private void DisplayError(WebException exception)
{
    try
    {
        StreamReader reader = new StreamReader(exception.Response.GetResponseStream());
        XmlDocument response_doc = new XmlDocument();
        response_doc.LoadXml(reader.ReadToEnd());
        XmlNode message_node = response_doc.SelectSingleNode("//message");
        MessageBox.Show(message_node.InnerText);
    }
    catch (Exception ex)
    {
        MessageBox.Show("Unknown error\n" + ex.Message);
    }
}

This code creates a StreamReader attached to the response stream. It reads the text from the reader and uses it to create an XML document. It then displays that object’s message element.

Unfortunately, the response seems to sometimes return XML code (as when the city doesn’t exist) and sometimes JSON code (as when the API ID is invalid). If you find a way to make it always return XML code, post a comment below. Or if you modify this method to handle both XML and JSON, which I’m too lazy to do right now, post a comment.

Download the example, get your own API ID, and give it a try!


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in internet, web, XML | Tagged , , , , , , , , , , , | Leave a comment