March 28, 2024, 03:56:30 AM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


Plotting functions

Started by John S, December 09, 2006, 06:22:35 PM

Previous topic - Next topic

0 Members and 2 Guests are viewing this topic.

John S

Here is some code that Joske posted on CodingMonkeys http://www.codingmonkeys.com/index.php?topic=877.0

I corrected and tweaked the code to add some features.  Eventually, I will rewrite this to be a general purpose fundtion plotter.  This was written for IBPro, but compiles fine in EBasic.


/*
flicker_free_graph.eba by jos de jong
with help from the ibasic forums (see codingmonkeys.com)
tweaked by John Siino

this version of flicker free graph does take a certain scale factor as constant,
and depending on that calculates the boundaries xmin, xmax, ymin, ymax of the graph
when the graph is resized or moved

you can move the graph by dragging the mouse or with the arrow keys
you can zoom the graph with keys + and -
*/

'DECLARATIONS
$INCLUDE "windows.inc"
DECLARE IMPORT, LoadCursor ALIAS LoadCursorA(hInstance AS INT,lpCursorName AS POINTER),INT
DECLARE IMPORT, GetCursorPos(lppoint:POINTER)
DECLARE IMPORT, ScreenToClient(HWND:uint, lppoint:POINTER)

WINDOW figure, plotwin

'axes of the graphics screen
DOUBLE xmin, xmax, ymin, ymax

'factors such that xscreen = x * xfactor + xshift and yscreen = y * yfactor + yshift
DOUBLE xfactor, xshift, yfactor, yshift

DEF MouseDownL, MouseDownR, MouseStartX, MouseStartY:INT
CONST Black=0

Figure_Load()
Refresh_Scale()
Plot_Graph()
SETFOCUS plotwin

WAITUNTIL figure=0
END

'_______________________________________________________________________
SUB Figure_Load()
'load window figure with subwindow plotwin
string figtxt
figtxt = "Figure - Move by dragging mouse or with arrow keys, zoom with + and -"

'load the window (outside the screen)
OPENWINDOW figure, -600,100,500,400, @MINBOX|@MAXBOX|@SIZE|@NOAUTODRAW, 0, figtxt, &RoutineFigure

OPENWINDOW plotwin, 0,0,200,200, @NOCAPTION | @NOAUTODRAW, figure, "plot", &RoutinePlotWin

   'create this window with a sunken border
   _SetWindowLong(plotwin.hwnd,-20,512)
   _SetWindowPos(plotwin.hwnd,NULL,0,0,0,0,39)

   Resize()
   CfgSetDefault()

   'center the window in the screen so it is visible now
   CENTERWINDOW figure

   RETURN
ENDSUB

'_______________________________________________________________________
SUB RoutineFigure()
'handling of messages from window figure

   SELECT @MESSAGE
      CASE @IDCLOSEWINDOW
         'close the figure window
         CLOSEWINDOW plotwin
         CLOSEWINDOW figure     

      CASE @IDSIZECHANGED
         Resize()
         Refresh_Scale()
         Plot_Graph()

   ENDSELECT
   RETURN
ENDSUB

'_______________________________________________________________________
SUB RoutinePlotwin()              :'handling of messages from window plotwin
   SETTYPE @HITWINDOW, WINDOW

   SELECT @MESSAGE
      CASE @IDPAINT
         Refresh_Scale()
         Plot_Graph()

      CASE @IDLBUTTONDN
         'move axes
         _SetCapture(*@HITWINDOW.hwnd)
         GetCursorPos(&@MOUSEX)
         ScreenToClient(*@HITWINDOW.hwnd, &@MOUSEX)
         MouseStartX = @MOUSEX
         MouseStartY = @MOUSEY
         SETCURSOR plotwin, @CSCUSTOM, LoadCursor(NULL, IDC_SIZEALL)      :'cursor "move"

      CASE @IDLBUTTONUP
         _ReleaseCapture()
         SETCURSOR plotwin, @CSARROW               :' cursor back to normal cursor

      CASE @IDMOUSEMOVE
         if (_GetCapture() = *@HITWINDOW.hwnd)
            GetCursorPos(&@MOUSEX)
            ScreenToClient(*@HITWINDOW.hwnd, &@MOUSEX)
            'Left mousebutton down. move the plot with the mouse movement
            'xmin -= (@MOUSEX-MouseStartX) / xfactor
            'xmax -= (@MOUSEX-MouseStartX) / xfactor
            'ymin -= (@MOUSEY-MouseStartY) / yfactor
            'ymax -= (@MOUSEY-MouseStartY) / yfactor
            xshift += (@MOUSEX-MouseStartX)
            yshift += (@MOUSEY-MouseStartY)

            MouseStartX = @MOUSEX
            MouseStartY = @MOUSEY

            'replot
            Refresh_Scale()
            Plot_Graph()
         ENDIF
      CASE @IDKEYDOWN
         'check keypresses
         'for the keycodes, see the usersguide Appendix, Virtual key codes
         SELECT @WPARAM
            CASE 0x25
               'left arrow is pressed. move the graph to the left
               xshift -= 20
               Refresh_Scale()
               Plot_Graph()
            CASE 0x27
               'right arrow is pressed. move the graph to the right
               xshift += 20
               Refresh_Scale()
               Plot_Graph()
            CASE 0x26
               'up arrow is pressed. move the graph up
               yshift -= 20
               Refresh_Scale()
               Plot_Graph()
            CASE 0x28
               'down arrow is pressed. move the graph to down
               yshift += 20
               Refresh_Scale()
               Plot_Graph()
         ENDSELECT
      CASE @IDCHAR
         'check keypresses
         'for the keycodes, see the usersguide Appendix, Virtual key codes
         SELECT @WPARAM
            CASE ASC("+")
            CASE& ASC("=")
               '+ key is pressed. zoom the graph in
               xfactor *= 1.2
               yfactor *= 1.2
               Refresh_Scale()
               Plot_Graph()
            CASE ASC("-")
            CASE& ASC("_")
               '- key is pressed. zoom the graph out
               xfactor /= 1.2
               yfactor /= 1.2
               Refresh_Scale()
               Plot_Graph()
         ENDSELECT
   ENDSELECT
   RETURN
ENDSUB

'_______________________________________________________________________
SUB Plot_Graph()                         :'this sub repaints the graph
   INT L,T,W,H,  hdc, hdcMem, hbmMem,   oldBmp, oldBrush, oldPen, oldFont
   DOUBLE xscreen, yscreen

   GETCLIENTSIZE(plotwin, L,T,W,H)                :'get size of window

'Create an off-screen DC for double-buffering
   hdc = _GetDC(plotwin.hwnd)
   hdcMem = _CreateCompatibleDC(0)
   hbmMem = _CreateCompatibleBitmap(hdc, W, H)
   oldBmp = _SelectObject(hdcMem, hbmMem)
   oldBrush = _SelectObject(hdcMem, _CreateSolidBrush(RGB(255,255,255)))
   oldPen = _SelectObject(hdcMem, _CreatePen(PS_SOLID,1,RGB(255,255,255)))

'set specific font
   INT textW, textH,  fontsize, fontwt
   STRING fontface
   fontface = "Courier New"   :   fontsize = 12   :   fontwt = 600
   SETFONT plotwin, fontface, fontsize, fontwt, 0
   GETTEXTSIZE(plotwin, "A", textW, textH) :'find the desired text Height
   oldFont = _SelectObject(hdcMem, _CreateFont(textH,0,0,0,fontwt, 0,0,0,0,0,0,0,0, fontface))
   _SetTextColor(hdcMem, RGB(0,0,255))
   _SetBkMode(hdcMem, TRANSPARENT)
   _SetTextAlign(hdcMem,TA_UPDATECP)

   'empty window -> draw white, filled rectangle
   _Rectangle(hdcMem, 0, 0, W, H)
   
   'Paint axes with black frontpen
   _DeleteObject(_SelectObject(hdcMem, _CreatePen(PS_SOLID,1,RGB(0,0,0))))
   _MoveToEx(hdcMem, xmin * xfactor + xshift, 0 * yfactor + yshift, NULL)
   _LineTo(hdcMem, xmax * xfactor + xshift, 0 * yfactor + yshift)
   _MoveToEx(hdcMem, 0 * xfactor + xshift, ymin * yfactor + yshift, NULL)
   _LineTo(hdcMem, 0 * xfactor + xshift, ymax * yfactor + yshift)
   
  'paint the function with red frontpen
   _DeleteObject(_SelectObject(hdcMem, _CreatePen(PS_SOLID,1,RGB(255,0,0))))
   xscreen = 0  :  yscreen = -myfunction((xscreen-xshift)/xfactor) * yfactor + yshift
   _MoveToEx(hdcMem, xscreen, yscreen, NULL)
   FOR n=0 TO w+5 STEP 2
      xscreen = n  :  yscreen = -myfunction((xscreen-xshift)/xfactor) * yfactor + yshift
      _LineTo(hdcMem, xscreen, yscreen)
   NEXT n

  'Print some text with blue frontpen
   _DeleteObject(_SelectObject(hdcMem, _CreatePen(PS_SOLID,1,RGB(0,0,255))))
   STRING mystring   :   mystring = "Sin(x)"
   _MoveToEx(hdcMem, 10, 19 * H / 20, NULL)
   _TextOut(hdcMem, 0, 0, mystring, len(mystring))

  'Print axes labels with black frontpen
   _DeleteObject(_SelectObject(hdcMem, _CreatePen(PS_SOLID,1,RGB(0,0,0))))
   _MoveToEx(hdcMem, 19 * W / 20, yshift, NULL)
   _TextOut(hdcMem, 0, 0, "x", len("x"))
   _MoveToEx(hdcMem, xshift, H /40, NULL)
   _TextOut(hdcMem, 0, 0, " y", len(" y"))

  'Print axes tick marks with black frontpen
   _DeleteObject(_SelectObject(hdcMem, _CreatePen(PS_SOLID,1,RGB(0,0,0))))
   FOR n=0 TO w+5 STEP 1
      _MoveToEx(hdcMem, -n * xfactor + xshift, yshift - H / 50, NULL)
      _LineTo(hdcMem, -n * xfactor + xshift, yshift + H / 50)
      _MoveToEx(hdcMem, n * xfactor + xshift, yshift - H / 50, NULL)
      _LineTo(hdcMem, n * xfactor + xshift, yshift + H / 50)

      _MoveToEx(hdcMem, xshift - H / 50, -n * yfactor + yshift, NULL)
      _LineTo(hdcMem, xshift + H /50, -n * yfactor + yshift)
      _MoveToEx(hdcMem, xshift - H / 50, n * yfactor + yshift, NULL)
      _LineTo(hdcMem, xshift + H /50, n * yfactor + yshift)
   NEXT n

   'Transfer the off-screen DC to the screen
   _BitBlt(hdc, 0, 0, W, H, hdcMem, 0, 0, SRCCOPY)
   
   'Free-up the off-screen DC
   _DeleteObject(_SelectObject(hdcMem, oldFont))
   _DeleteObject(_SelectObject(hdcMem, oldBrush))
   _DeleteObject(_SelectObject(hdcMem, oldPen))
   _DeleteObject(_SelectObject(hdcMem, oldBmp))
   _DeleteDC(hdcMem)
   _ReleaseDC(plotwin.hwnd, hdc)
   RETURN
ENDSUB

'_______________________________________________________________________
SUB myfunction(value:DOUBLE),DOUBLE
'calculate a functionvalue
   RETURN sin(value)
ENDSUB

'_______________________________________________________________________
SUB Refresh_Scale()
'calculate the boundaries xmin, xmax, ymin, ymax for painting in the screen plotwin
'this depends on the scale of the graph and the size of the window plotwin
INT L,T,W,H
   GETCLIENTSIZE plotwin, L,T,W,H

   'xfactor = W / (xmax - xmin)
   'yfactor = -H / (ymax - ymin)
   'xshift = -xmin * xfactor
   'yshift = -ymax * yfactor

   xmin = (0-xshift) / xfactor
   xmax = (w-xshift) / xfactor
   ymin = (0-yshift) / yfactor
   ymax = (h-yshift) / yfactor

   RETURN
ENDSUB


'_______________________________________________________________________
SUB Resize()
'resize figure window with its subwindow
INT L,T,W,H
   GETCLIENTSIZE figure, L,T,W,H
   SETSIZE plotwin, 0,0,W,H

   RETURN
ENDSUB

'_______________________________________________________________________
SUB CfgSetDefault()
'set default values for the properties of the figure

INT L,T,W,H
   GETCLIENTSIZE plotwin, L,T,W,H

   'xmin=-5
   'xmax=5
   'ymin=-5
   'ymax=5

   xfactor = 40
   yfactor = 35
   xshift = W/2 :'start the axis at half the width of the plotwin
   yshift = H/2

   RETURN
ENDSUB


John Siino, Advanced Engineering Services and Software

Barney

Here's a quick addition to make it even easier for the mouse wheel fans like myself.

Just add this in the declaration section:

SETID "IDMOUSEWHEEL",522

and add this just before the last ENDSELECT in the RoutinePlotwin():


CASE @IDMOUSEWHEEL
  IF (@WPARAM & 0x10000000)
    'Wheel rotated downwards. Zoom the graph in
    xfactor *= 1.2
    yfactor *= 1.2
    Refresh_Scale()
    Plot_Graph()
  ELSE
    'Wheel rotated upwards. Zoom the graph out
    xfactor /= 1.2
    yfactor /= 1.2
    Refresh_Scale()
    Plot_Graph()
  ENDIF


Now you can just turn the wheel backwards for zooming in and forwards to zoom out.

Barney

joske


Barney

Feel free to use it as you see fit.

Barney

aurelCB

June 03, 2013, 01:14:10 AM #4 Last Edit: June 03, 2013, 01:45:17 AM by aurelCB
Hi to all.. :)
I just found this old topic with this great example for double buffered window.
I search trough net to find something useful for universal program (in C or C++) which cover
double buffering and cannot found nothing concrete.... :-\

aurelCB

Hi again... :)
After long search and looking into Creative source code i am still confused and don't
know how exactly is window double-buffered in CB/EB/IWB by GDI functions...
which enable call any graphich functions anywhere from code.
Is here anyone who can put me in right directions?

thanks in advance.. ;)
Aurel

LarryMc

Aurel
double buffering is easy and it is explained in detail w/examples in my tutorial for creating a custom control.
The sections you are mainly interested in are 16 and 17
http://www.ionicwind.com/forums/index.php?board=69.0
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

aurelCB

Larry
I have read all your excellent tutorial about GDI,double buffering and all this things work fine
BUT
in all this codes your point is how to use drawing with GDI inside WM_PAINT event(message),right?
And as we all know CB/EB/IWB window is by default double-buffered and don't need WM_PAINT to
draw something on window ,do i have right ?

So if originel IWB window don't need to use WM_PAINT for any drawing where is this double buffering
created? Becuse we can use any drawing function anywhere in the code.
Is this something conected with DIB bitmaps or something like this ,or is maybe hiden class which
contain double buffering capability...

LarryMc

Quote from: aurelCB on June 08, 2013, 03:55:45 PM
...
in all this codes your point is how to use drawing with GDI inside WM_PAINT event(message),right?
And as we all know CB/EB/IWB window is by default double-buffered and don't need WM_PAINT to
draw something on window ,do i have right ?
...
wrong!
That's why IWBasic is so nice to work with.  It hides all that from you.
IWB has a hidden message handler that normally handles WM_PAINT messages.
You can create the IWB window with the ownerdrawn flag and you can write your own code to handle WM_PAINT.
The same goes with all the other messages that are normally handled internally by IWB.
Remember that there is a RETURN 0 at the end of the IWB message handles?
By returning zero you are telling the hidden handler to process the message instead of the user.
Some messages get handled internally anyway regardless of whether or not you return 0 but that is by the design of IWB.

When you create an IWB window you create a structure WINDOW which is unique to IWB.  one element of that structure is .hwnd which is the actual windows handle. another element is .hdc which is the handle to the device context for that window.

To the MS operating system there is no difference between a window, as we know it in IWB, and a control.  They are both created with the same api command.  That's why my tutorial supplies you with EXACTLY what you need for the question you are asking.  It is doing out in the open for you to see what is hidden from view in IWB.
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

aurelCB

Larry...
I understand what you are trying to tell me and from your answer i only can see
that this technique of hiding looks like some sort of secret.
And you tell me that in your examples you open this secret and that there is a answer to my
question but is not if i work only on api level this things not work as i espected.
And let me clear something to..
I don't wont clone IWB doublebuffered window than just simplify things to use and avoid need
to use classic
CASE WM_PAINT
ooohh is to late here...good night... ;)

LarryMc

Aurel
You asked how to create a double buffered window.
I answered that.
Double buffering is accomplished in response to a WM_PAINT message being sent by the operating system.
So, with my previous response I showed you where to find an example of where to find an example.

So everyone understands, I understand you are asking about this because you are wanting to enhance the GUI capabilities of another basic language you are currently using.  As a result I'm not going to spend a lot of my time writing code to show you exactly how to better a competitor.

So, I will only tell you what you need that can be gleaned from reading my control tutorial.
The following ignores any "secrets" of IWBasic.
When any window or control is created via API one of the required parameters is the "class".
Before a window/control can be used the "class" has to be defined and registered (there's an API for that:RegisterClassEx). One of the pieces of information required during the registration process is the name of the message handler to be used for all windows/controls of that class.

You then have to supply the code for the message handler.
Then minimum you can get by with would be:
MyWndProc(hwnd, uMsg,  wParam, lParam)
{
   switch (uMsg)
   {
      default:
         return DefWindowProc(hwnd, uMsg, wParam, lParam);
   }
   return 0;
}

Since there are no other CASE statements, messages will be passed straight through to the OS default window message handler.
But you want to double buffering so you are going to have to write code in response to the WM_PAINT message so the above starts to look like this:
MyWndProc(hwnd, uMsg,  wParam, lParam)
{
   switch (uMsg)
   {
      case WM_PAINT:
         /* code like in my control tutorial */
         return 0;
      default:
         return DefWindowProc(hwnd, uMsg, wParam, lParam);
   }
   return 0;
}

If you want to allow the initialization of something you can add code in a WM_CREATE case or any other message that the OS sends.
That's about it for me on the subject.
As much as you program you should be able to figure it out from there.
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

aurelCB

Larry
I understand you and yes this is connected with another compiler but i don't have in plan to
add this in that another compler than use this in exe shape of my new interpreter.
And maybe i am not quite clear,when i say Creative and source code,CB have little bit different way
of double buffered window than IWB because is interpreted and is everything created automatically.
However thanks on respond... ;)

LarryMc

I don't use CB and don't know anything about its internals.
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

aurelCB

hm..
then maybe i ask on wrong place ,i think that you maybe have CB src.
Do i can ask on CB board  ???

LarryMc

Quote from: aurelCB on June 09, 2013, 12:02:35 AM
hm..
then maybe i ask on wrong place ,i think that you maybe have CB src.
Do i can ask on CB board  ???
Sure
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library