Click here to Skip to main content
15,901,426 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
Hi,
I am trying to develop a software where I can use multi touch functionality and accordingly waves can be generated from that point. I tried Shazzam software for the same thing but effect is not as per my requirement.

What I am trying to do is this
Multi Touch Water Wall - YouTube[^]
and
Multitouch Fish Tank at CeBIT 09 - YouTube[^]

Please tell me which is the best way to do this. When googled I did not got any useful stuff.
thank in advance

What I have tried:

I tried shazzam software and used the effect generated by it in WPF.
Posted
Updated 13-Aug-20 7:28am
v2

 
Share this answer
 
1 New WPF Project

Link Project add:
System.Drawing
System.Windows.Forms
WindowsFormsIntegration

MainWindow.xaml:
<Window x:Class="WpfwaterEffectTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
        xmlns:local="clr-namespace:WpfwaterEffectTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="768" Width="1024" ResizeMode="NoResize" WindowStyle="None" WindowStartupLocation="CenterScreen">
    <Canvas Name="mainCanv">

    </Canvas>
</Window>


MainWindow.xaml.cs:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
using System.Windows.Forms.Integration;
using System.Windows.Threading;

namespace WpfwaterEffectTest
{
    public partial class MainWindow : Window
    {
        private Bitmap _bmp;
        private short[,,] _waves;
        private int _waveWidth;
        private int _waveHeight;
        private int _activeBuffer = 0;
        private bool _weHaveWaves;
        private int _bmpHeight, _bmpWidth;
        private byte[] _bmpBytes;
        private BitmapData _bmpBitmapData;
        private int _scale;

        System.Windows.Forms.PictureBox pbViewport = null;
        DispatcherTimer waterTime = new DispatcherTimer(DispatcherPriority.Render);

        public MainWindow()
        {
            InitializeComponent();

            pbViewport = new PictureBox();
            this.pbViewport.Image = System.Drawing.Image.FromFile("About2.jpg");
            this.pbViewport.Top = 0;
            this.pbViewport.Left = 0;
            this.pbViewport.Height = 360;
            this.pbViewport.Width = 203;
            this.pbViewport.SizeMode = PictureBoxSizeMode.AutoSize;
            this.pbViewport.Paint += new PaintEventHandler(this.WaterEffectControl_Paint);
            this.pbViewport.MouseMove += new System.Windows.Forms.MouseEventHandler(this.WaterEffectControl_MouseMove);
            waterTime.Tick += new EventHandler(effectTimer_Tick);
            WindowsFormsHost wfh = new WindowsFormsHost();
            wfh.Child = pbViewport;
            wfh.Width = 203;
            wfh.Height = 360;
            wfh.Opacity = 0.9;
            mainCanv.Children.Add(wfh);
            Canvas.SetZIndex(wfh, 1);

            this.Width = this.pbViewport.Width;
            this.Height = this.pbViewport.Height;

            this.ImageBitmapNew = (Bitmap)(this.pbViewport.Image);

            waterTime.Interval = TimeSpan.FromMilliseconds(50);

            waterTime.Start();

        }

        private void effectTimer_Tick(object sender, System.EventArgs e)
        {
            if (_weHaveWaves)
            {
                this.pbViewport.Invalidate();

                ProcessWaves();
            }
        }

        private void ProcessWaves()
        {
            int newBuffer = (_activeBuffer == 0) ? 1 : 0;
            bool wavesFound = false;

            for (int x = 1; x < _waveWidth - 1; x++)
            {
                for (int y = 1; y < _waveHeight - 1; y++)
                {
                    _waves[x, y, newBuffer] = (short)(
                                            ((_waves[x - 1, y - 1, _activeBuffer] +
                                            _waves[x, y - 1, _activeBuffer] +
                                            _waves[x + 1, y - 1, _activeBuffer] +
                                            _waves[x - 1, y, _activeBuffer] +
                                            _waves[x + 1, y, _activeBuffer] +
                                            _waves[x - 1, y + 1, _activeBuffer] +
                                            _waves[x, y + 1, _activeBuffer] +
                                            _waves[x + 1, y + 1, _activeBuffer]) >> 2) - _waves[x, y, newBuffer]);

                    //damping
                    if (_waves[x, y, newBuffer] != 0)
                    {
                        _waves[x, y, newBuffer] -= (short)(_waves[x, y, newBuffer] >> 4);
                        wavesFound = true;
                    }
                }
            }

            _weHaveWaves = wavesFound;
            _activeBuffer = newBuffer;
        }

        /// <summary>
        /// This function is used to start a wave by simulating a round drop
        /// </summary>
        /// <param name="x">x position of the drop</param>
        /// <param name="y">y position of the drop</param>
        /// <param name="height">Height position of the drop</param>
        private void PutDrop(int x, int y, short height)
        {
            _weHaveWaves = true;
            int radius = 20;
            double dist;

            for (int i = -radius; i <= radius; i++)
            {
                for (int j = -radius; j <= radius; j++)
                {
                    if (((x + i >= 0) && (x + i < _waveWidth - 1)) && ((y + j >= 0) && (y + j < _waveHeight - 1)))
                    {
                        dist = Math.Sqrt(i * i + j * j);
                        if (dist < radius)
                            _waves[x + i, y + j, _activeBuffer] = (short)(Math.Cos(dist * Math.PI / radius) * height);
                    }
                }
            }
        }

        /// <summary>
        /// The MouseMove handler.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void WaterEffectControl_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && _bmp != null)
            {
                int realX = (int)((e.X / (double)this.pbViewport.ClientRectangle.Width) * _waveWidth);
                int realY = (int)((e.Y / (double)this.pbViewport.ClientRectangle.Height) * _waveHeight);
                PutDrop(realX, realY, 200);
            }
        }

        #region Properties

        /// <summary>
        /// Our background image
        /// </summary>
        public Bitmap ImageBitmapNew
        {
            get { return _bmp; }
            set
            {
                if (_bmp != null)
                {
                    _bmp.Dispose();
                }
                _bmp = value;
                if (value != null)
                {
                    _bmpHeight = _bmp.Height;
                    _bmpWidth = _bmp.Width;

                    _waveWidth = _bmpWidth >> _scale;
                    _waveHeight = _bmpHeight >> _scale;
                    _waves = new Int16[_waveWidth, _waveHeight, 2];

                    _bmpBytes = new Byte[_bmpWidth * _bmpHeight * 4];
                    _bmpBitmapData = _bmp.LockBits(new Rectangle(0, 0, _bmpWidth, _bmpHeight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                    Marshal.Copy(_bmpBitmapData.Scan0, _bmpBytes, 0, _bmpWidth * _bmpHeight * 4);
                    _bmp.UnlockBits(_bmpBitmapData);
                }
            }
        }

        /// <summary>
        /// The scale of the wave matrix compared to the size of the image.
        /// Use it for large images to reduce processor load.
        ///
        /// 0 : wave resolution is the same than image resolution
        /// 1 : wave resolution is half the image resolution
        /// ...and so on
        /// </summary>
        public int Scale
        {
            get { return _scale; }
            set { _scale = value; }
        }

        #endregion Properties

        /// <summary>
        /// Paint handler
        ///
        /// Calculates the final effect-image out of
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void WaterEffectControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            if (_bmp != null)
            {
                using (Bitmap tmp = (Bitmap)_bmp.Clone())
                {
                    int xOffset, yOffset;
                    byte alpha;

                    if (_weHaveWaves)
                    {
                        BitmapData tmpData = tmp.LockBits(new Rectangle(0, 0, _bmpWidth, _bmpHeight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

                        byte[] tmpBytes = new Byte[_bmpWidth * _bmpHeight * 4];

                        Marshal.Copy(tmpData.Scan0, tmpBytes, 0, _bmpWidth * _bmpHeight * 4);

                        for (int x = 1; x < _bmpWidth - 1; x++)
                        {
                            for (int y = 1; y < _bmpHeight - 1; y++)
                            {
                                int waveX = (int)x >> _scale;
                                int waveY = (int)y >> _scale;

                                //check bounds
                                if (waveX <= 0) waveX = 1;
                                if (waveY <= 0) waveY = 1;
                                if (waveX >= _waveWidth - 1) waveX = _waveWidth - 2;
                                if (waveY >= _waveHeight - 1) waveY = _waveHeight - 2;

                                //this gives us the effect of water breaking the light
                                xOffset = (_waves[waveX - 1, waveY, _activeBuffer] - _waves[waveX + 1, waveY, _activeBuffer]) >> 3;
                                yOffset = (_waves[waveX, waveY - 1, _activeBuffer] - _waves[waveX, waveY + 1, _activeBuffer]) >> 3;

                                if ((xOffset != 0) || (yOffset != 0))
                                {
                                    //check bounds
                                    if (x + xOffset >= _bmpWidth - 1) xOffset = _bmpWidth - x - 1;
                                    if (y + yOffset >= _bmpHeight - 1) yOffset = _bmpHeight - y - 1;
                                    if (x + xOffset < 0) xOffset = -x;
                                    if (y + yOffset < 0) yOffset = -y;

                                    //generate alpha
                                    alpha = (byte)(200 - xOffset);
                                    if (alpha < 0) alpha = 0;
                                    if (alpha > 255) alpha = 254;

                                    //set colors
                                    tmpBytes[4 * (x + y * _bmpWidth)] = _bmpBytes[4 * (x + xOffset + (y + yOffset) * _bmpWidth)];
                                    tmpBytes[4 * (x + y * _bmpWidth) + 1] = _bmpBytes[4 * (x + xOffset + (y + yOffset) * _bmpWidth) + 1];
                                    tmpBytes[4 * (x + y * _bmpWidth) + 2] = _bmpBytes[4 * (x + xOffset + (y + yOffset) * _bmpWidth) + 2];
                                    tmpBytes[4 * (x + y * _bmpWidth) + 3] = alpha;
                                }
                            }
                        }

                        //copy data back
                        Marshal.Copy(tmpBytes, 0, tmpData.Scan0, _bmpWidth * _bmpHeight * 4);
                        tmp.UnlockBits(tmpData);
                    }

                    e.Graphics.DrawImage(tmp, 0, 0, this.pbViewport.ClientRectangle.Width, this.pbViewport.ClientRectangle.Height);
                }
            }
        }
    }
}

pbViewport.Height = 360 and pbViewport.Width = 203 equal to the size of the loaded image

Insert Image File in App Direct("About2.jpg") and include in the project

The code is not mine, limited from UserControl "WaterEffectPictureBox" WinForm
 
Share this answer
 
Comments
Richard Deeming 14-Aug-20 6:43am    
WPF has a rich graphics system. Sticking a Windows Forms PictureBox on a WPF window just because you only know how to write graphics code with Windows Forms controls is a terrible idea. Aside from anything else, the performance will really suck.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900