Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

An Icon Editor


JUL89: LINE-OF-BEST-FIT

William H. Murray and Chris H. Pappas are professors of Computer Studies at Broome Community College. Together they have written several books, including Presentation Manager Graphics: An Introduction (Osborne/McGraw-Hill, 1989) from which portions of this article were adapted. Bill and Chris can be reached at Broome Community College, Binghamton, NY 13902.


With consistent device-independent programming environments such as Microsoft Windows and OS/2 Presentation Manager, applications can be executed and results ported to any installed hardware driver accordant with Microsoft's specifications. This consistent environment frees you from the necessity of also developing software drivers for zillions (or at least hundreds) of printers, plotters, and graphics display) devices. As a matter of fact, the latest version of Windows (Version 3.0) brings the Windows' environment in much closer alignment with the OS/2 Presentation Manager (PM), making the task of porting programs between real and protected modes easier.

The price you pay for entering into this consistent user interface is a rather steep learning curve. In this article, we will concentrate on a PM program that plots points on the screen and draws a line between them with a linear least-squares-fit. A least-squares-fit works on the principle that data points, collected on a chart, are often related mathematically. Because of this relationship, the group of points takes on the appearance of a line or curve. In the case of a linear mathematical relationship between point values, a single line (called the line-of-best-fit) can be drawn between all points. The data points for this program will be plotted by moving the mouse to a specific region on the screen and clicking the left button. When all points are entered, a click of the right mouse button will end data entry and draw a line-of-best-fit. The equation for that line will be plotted at the bottom of the screen in slope-intercept form. Among other things, this example illustrates how the mouse and other graphics primitives can be utilized under the protected-mode environment.

If you are unfamiliar with Windows or the PM philosophy, we strongly recommend you read Herb Schildt's article, "A Presentation Manager Application Template," which appeared in the March 1989 issued of DDJ. Herb describes one of three Presentation Manager "foundations" -- the Cached-Presentation Space. (The other two are the Micro-Presentation Space and the Normal-Presentation Space.) Each type offers an increasingly greater array of available functions and unfortunately larger code sizes. For this reason, it will be easiest for you to learn simple PM programming with the Cached-Presentation Space, which is what we implemented in this article. From there you can move to the other two presentation spaces as projects demand.

To enter and execute the code you will need the PM (Version 1.1 or later) operating on a 80386/80286 computer. Additionally, you'll need the Microsoft C Compiler (Version 5.1 or later), the Microsoft OS/2 Software Developer's Kit, and a Microsoft mouse. The program for this article was developed on an IBM Model 80 with VGA display and 6 Mbytes of RAM.

The Sum of the Parts Equals the Whole

The program itself is called MFIT (for mouse fit). MFIT does not consist of a single program file, but a group of files that are eventually linked together. As a matter of fact, there are ten files that will be found on your disk after a successful compilation: MFIT, MFIT.DEF, MFIT.EXE, MFIT.H, MFIT.ICO, MFIT.OBJ, MFIT.PTR, MFIT.RC, MFIT.RES, and MFIT.C.

MFIT, shown in Listing One is used by the MAKE utility and is responsible for the compilation of the C code by the C compiler, the assembling of the resource code by the Resource Compiler, and the eventual linking of this code with the icon and pointer information.

MFIT.DEF (Listing Two) is the definition file for the Presentation Manager. Definition files are used by both Windows and the Presentation Manager. The DEF file is responsible for establishing the heap and stack sizes, identifying the operating mode (real or protected), and naming exports. For our example, exports are the individual procedures used in the C program.

MFIT.H is a header file for unique identification numbers that will be used by the resource file and the C program. Listing Three shows a list of #define statements. Usually the #define instruction is followed by an ID name, followed by a unique integer constant. The names and the constants are your choice, but careful planning will sometimes eliminate later problems, such as the insertion of additional ID numbers.

MFIT.ICO is a file containing the shape of the minimum icon. The minimum icon is displayed at the bottom of the screen if that sizing option is chosen from the main menu of the PM. The icon is drawn with the use of the Icon Editor, which is part of the Microsoft Toolkit. (Because MFIT.ICO is not a text file that can be listed, it is only available on an optional diskette or on line.)

MFIT.PTR is a file containing the shape of the screen pointer. The screen pointer, by default, is an arrow. The user has the option of using the Icon Editor to draw a new pointer. This pointer will be present in the application window and can be used to make selections. In this case, a miniature line chart is used as the pointer. When the left button is pushed (in the example program) a small cross symbol will be plotted on the screen at the spot pointed to by the pointer. Again, the file returned by the Icon Editor is not a text file.

MFIT.RC is the resource file needed for describing the Menu, About Box, and Dialog Box and is shown in Listing Four. The resource file is a can of worms in itself. First, the About and Dialog Boxes are created with the Dialog Box Editor provided with the Microsoft Toolkit. Once created, the information is saved in a file with the extension .RES, which is not a text file. A text file version can be selected. We used the .DLG extension for this option. Our experience is that the text file is important for touching up the Dialog Box information. The catch is that this .DLG file cannot be read by the Dialog Box Editor. You will not have that problem with this example, because the placement of the Dialog Box and all controls have already been worked out. You will simply have to type the file MFIT.RC. The MAKE utility will compile that file into a .RES file and link it to the final executable program. The About Box is shown in Figure 1 and the Dialog Box in Figure 2.

MFIT.C is the C program proper. Listing Five contains all the necessary PM overhead required to establish and communicate with an application program. Specifically, two procedures or functions are required to process the Dialog Box messages for the About and LineDiaProc Dialog Boxes. A final procedure, GraphicProc, handles all other messages for the mouse position, buttons, and graphics routines.

Power in the Loop

PM and Windows use the concept of a message loop or queue for receiving and sending information throughout an application program. (Establishing this queue is the job of the main( ) function as described in Schildt's article.) Once the message queue is established, it is your responsibility to tap into the queue with your requests. Our program will use several procedures or functions. One procedure will help establish an About Box. In PM and Windows, the About Box is used to transmit information regarding the program. This usually consists of one or more of the following: the program name, a short description of the program, the developer's name(s), and the copyright date. The About Box is usually selected (as an item) from a program menu. Another procedure will establish a Dialog Box. Dialog Boxes, also selected as items from a user defined menu, establish a sophisticated means of data input. They go beyond the simple buttons and check boxes of menus and allow the user to enter string or numeric data from the keyboard. The Dialog Box in our program will allow the entry of a title, x axis and y axis labels, and maximum integer values for both the x and y axes. Finally, a third procedure will establish the "core" of our current application. It is within this procedure that mouse information is collected on coordinate positions and button status. It should be pointed out that only integers can be passed through the message queue. This does not mean that your program cannot use real numbers, it just means that you cannot pass them through the queue.

There is a similar structure for intercepting messages in the LineDiaProc. This Dialog Box is created if case IDM_INPUT is true during the processing of the WM_COMMAND function in the procedure. This is the Dialog Box that queries the user for labels and plotting ranges. In this case, information will only be returned from the Dialog Box when the "Okay" button is selected. If the "Cancel" button is selected, the Dialog Box will be destroyed, but no new information will be intercepted. The functions WinQueryDlgItemText and WinQueryDlgItemShort return the string information for the labels and the integer information for the plotting ranges, respectively. In both cases, the final outcome of selecting either push button is to process the message, destroy the Dialog Box, and return to the application program.

Processing the GraphicProc Information

There are seven switch-case statements in this procedure for processing message information: WM_CREATE, WM_BUTTON1DOWN, WM_MOUSEMOVE, WM_BUTTON2DOWN, WM_SIZE, WM_COMMAND, and WM_PAINT. It is within these cases that data will be collected and processed.

You have already seen that WM_COMMAND is responsible for establishing one of the two Dialog Boxes when requested. WM_CREATE will replace the default arrow pointer with the pointer created in the Icon Editor by calling Win LoadPointer. This pointer tracks the current mouse position with WM_MOUSEMOVE. In WM_MOUSEMOVE, the WinSetPointer function places the pointer in the window. WM_SIZE processes the request for the mouse menu. The mouse menu allows the selection of the two Dialog Box options mentioned earlier.

WM_BUTTON1DOWN and WM_BUTTON2DOWN report on the status of the two mouse buttons. If the left button is pushed, a marker is placed on the screen at the current mouse location. Additionally, if the left button is pushed, that coordinate information is also placed in two global arrays: ptxorg[] and ptyorg[]. This coordinate information will be used when plotting the line-of-best-fit between the data points. Note that during WM_BUTTON1DOWN, GpiSetMarker is used to draw a system marker symbol. GpiSetColor sets the symbol drawing color to blue. Mouse message data is returned through the parm1 parameter, which is of type MPARAM. MPARAM is a pointer to this additional message information. Up to 100 points can be collected in this manner. When all data points have been entered, the user can push the right mouse button to generate a line of best fit and the corresponding slope-intercept equation.

WM_BUTTON2DOWN uses the information returned to the global arrays, ptxorg[] and ptyorg[], to draw the line. First, however, this information is processed and scaled. When WM_BUTTON2DOWN is called, the slope and intercept of the line are used to establish a beginning and ending point on the graph. When the starting point is found, the GpiMove function places the cursor at that position. The GpiLine function then draws a red line between that point and the final point on the screen. Finally, the equation is drawn to the screen with a series of GpiBox, GpiColor, GpiMove, and GpiCharString function calls.

Numerous lines-of-best-fit can be plotted on the same graph. Simply plot a group of points and request a line, then plot another group and request another line. The screen can be cleared by selecting the mouse data Dialog Box and selecting either push button option.

WM_PAINT is responsible for setting the background color, drawing the coordinate axes, plotting tic marks, setting the character mode for drawing text to the screen, and plotting axis labels. Lines are drawn by moving the cursor to the starting point of the line with the GpiMove function. The coordinates (ptl.x and ptl.y) are of PM type POINTL. GpiLine specifies the ending point. The axes and tic marks are drawn with just these two functions. The GpiCbarStringAt function is used to draw the various labels. GpiCharStringAt requires that a handle, coordinate position, length of string parameter, and the actual label be provided for the function. If you examine the listing, you will see how this is done. It should be noted that for the default drawing mode, used by PM, the screen coordinates start at the lower-left portion of the screen (0,0) and extend to the upper-right portion of the screen. For the VGA monitor the upper-right coordinates are (639,479).

Calculating the Line-of-Best-Fit

When WM_BUTTON1DOWN has completed the process of filling the ptxorg[] and ptyorg[] arrays, it will be possible to calculate the line-of-best-fit. Most of the calculations take place in the GraphicProc procedure, immediately after the data declarations. Two groups of arrays are used: ptxorg[] and ptyorg[] along with ptxscaled[] and ptyscaled[]. The arrays ptxorg[] and ptyorg[] are used to determine the slope-intercept data for the line that will be drawn on the screen. These contain the screen coordinate values returned by the mouse. However, because this information is biased by the starting, ending, and scaling of the screen, it must be adjusted in order to provide "real world" values for the line-of-best-fit equation that will also be printed to the screen. Thus, ptxscaled[] and ptyscaled[] are arrays that hold the unbiased values. The screen window for gathering points varies from (100,100) to (500,400).

Two standard equations (shown in Figure 3) are used for the linear curve fitting program. They are derived from the calculus and give the slope and intercept of the final line -- the line-of-best-fit. More information on general curve fitting can be found in Numerical Recipes in C (Press, Flannery, Teukolsky, and Vetterling, Cambridge University Press, 1988.)

In these equations, n represents the number of data points with each x[i] and y[i] representing the individual x and y values for each data point. If you study the code under the comment "/* Calculate values for screen location */" you will notice that two numerators, num1 and num2, are calculated along with one denominator, deno2. If num1 is divided by deno2 the intercept will be calculated. If num2 is divided by deno2, the slope of the line is calculated. This is done under the call to WM_BUTTON2DOWN. A similar technique is used for the array of scaled data points.

Running the Program

If you have entered the code and compiled the program give it a try. Make sure you have a Microsoft mouse or equivalent connected and installed. Experiment with several lines. Figure 4 shows one line-fitting example.

The program suffers from two weaknesses. First, it only allows you to plot points in the first quadrant. Second, it will fail if a vertical line (an infinite slope) is requested. Both problems are relatively easy to solve. Plotting in all four quadrants can be achieved by remapping the axes and adjusting the scaling factors. The vertical line problem is even easier -- simply use a case statement to capture the request and then use the GpiLine function to draw a vertical line at the current x position.

Availability

All source code for articles in this issue is available on a single disk. To order, send $14.95 (Calif. residents add sales tax) to Dr. Dobb's Journal, 501 Galveston Dr., Redwood City, CA 94063; or call 800-356-2002 (from inside Calif.) or 800-533-4372 (from outside Calif.). Please specify the issue number and format (MS-DOS, Macintosh, Kaypro).

_LINE-OF-BEST-FIT_ by William Murray and Chris Pappas

[LISTING ONE]

<a name="0138_000a">


mfit17.obj : mfit17.c mfit17.h
CL /c /Lp /Zp /Od /G2sw /W2 mfit17.c

mfit17.res : mfit17.rc mfit17.ico mfit17.h mfit17.ptr
RC -r mfit17

mfit17.exe : mfit17.obj mfit17.def mfit17.res
link mfit17, /align:16, NUL, OS2, mfit17
rc mfit17.res


<a name="0138_000b"><a name="0138_000b">
<a name="0138_000c">
[LISTING TWO]
<a name="0138_000c">

;MFIT17.DEF for C Compiling

NAME          mfit17
EXPORTS        About
EXPORTS        LineDiaProc
EXPORTS        GraphicProc
PROTMODE
HEAPSIZE       2048
STACKSIZE      9216



<a name="0138_000d"><a name="0138_000d">
<a name="0138_000e">
[LISTING THREE]
<a name="0138_000e">

#define ID_RESOURCE 10
#define ID_ABOUT    15
#define ID_OK       20
#define ID_CANCEL   25
#define ID_INPUT    30

#define IDM_ABOUT    40
#define IDM_INPUT    45
#define IDM_LINEINPUT 55

#define DM_XAXIS 470
#define DM_YAXIS 475
#define DM_TITLE 480
#define DM_X1  485
#define DM_Y1  490



<a name="0138_000f"><a name="0138_000f">
<a name="0138_0010">
[LISTING FOUR]
<a name="0138_0010">

#include <os2.h>
#include "mfit17.h"

POINTER ID_RESOURCE mfit17.ico
POINTER IDP_POINTER mfit17.ptr

MENU            ID_RESOURCE
BEGIN
  SUBMENU "Mouse_Data",IDM_LINEINPUT
  BEGIN
    MENUITEM "About...",   IDM_ABOUT
    MENUITEM "Program Data...", IDM_INPUT
  END
END

DLGTEMPLATE         ID_ABOUT
BEGIN
  DIALOG "",ID_ABOUT,50,300,180,80,FS_DLGBORDER
  BEGIN
    CTEXT "Mouse Data Input Program",-1,2,60,176,10
    CTEXT "by William H. Murray & Chris H. Pappas",-1,2,45,
     176,10
    CONTROL "OK",ID_OK,75,10,32,14,WC_BUTTON,BS_PUSHBUTTON
       |BS_DEFAULT|WS_GROUP|WS_TABSTOP|WS_VISIBLE
  END
END

DLGTEMPLATE ID_INPUT LOADONCALL MOVEABLE DISCARDABLE
BEGIN
    DIALOG "Mouse Program Information",ID_INPUT,76,247,148,193,
      FS_NOBYTEALIGN|FS_DLGBORDER|WS_VISIBLE|
      WS_CLIPSIBLINGS|WS_SAVEBITS,FCF_TITLEBAR
    BEGIN
   CONTROL "Mouse Program Labels",257,2,92,145,72,WC_STATIC,
      SS_GROUPBOX|WS_GROUP|WS_VISIBLE
   CONTROL "X & Y Maximums",259,2,42,145,42,WC_STATIC,
      SS_GROUPBOX|WS_GROUP|WS_VISIBLE
   CONTROL "Enter Title:",260,5,133,51,8,WC_STATIC,SS_TEXT
      |DT_LEFT|DT_TOP|WS_GROUP|WS_VISIBLE
   CONTROL "     x-axis:",261,5,118,54,8,WC_STATIC,SS_TEXT
      |DT_LEFT|DT_TOP|WS_GROUP|WS_VISIBLE
   CONTROL "     y-axis:",262,5,102,50,8,WC_STATIC,SS_TEXT
      |DT_LEFT|DT_TOP|WS_GROUP|WS_VISIBLE
   CONTROL "Mouse Data Points",DM_TITLE,54,132,89,13,
      WC_ENTRYFIELD,ES_LEFT|ES_AUTOSCROLL|ES_MARGIN
      |WS_TABSTOP|WS_VISIBLE
   CONTROL "x - axis label",DM_XAXIS,54,117,89,13,
      WC_ENTRYFIELD,ES_LEFT|ES_AUTOSCROLL|ES_MARGIN
      |WS_TABSTOP|WS_VISIBLE
   CONTROL "y - axis label",DM_YAXIS,54,101,89,13,
      WC_ENTRYFIELD,ES_LEFT|ES_AUTOSCROLL|ES_MARGIN
      |WS_TABSTOP|WS_VISIBLE
   CONTROL "   x:",273,25,57,20,8,WC_STATIC,SS_TEXT|DT_LEFT|
      DT_TOP|WS_GROUP|WS_VISIBLE
   CONTROL "   y:",283,75,57,20,8,WC_STATIC,SS_TEXT|DT_LEFT|
      DT_TOP|WS_GROUP|WS_VISIBLE
   CONTROL "500",DM_X1,50,57,20,15,WC_ENTRYFIELD,ES_LEFT
      |ES_AUTOSCROLL|ES_MARGIN|WS_TABSTOP|WS_VISIBLE
   CONTROL "400",DM_Y1,100,57,20,15,WC_ENTRYFIELD,ES_LEFT
      |ES_AUTOSCROLL|ES_MARGIN|WS_TABSTOP|WS_VISIBLE
   CONTROL "OK",ID_OK,17,13,24,14,WC_BUTTON,BS_PUSHBUTTON
      |WS_TABSTOP|WS_VISIBLE
   CONTROL "Cancel",ID_CANCEL,101,13,34,14,WC_BUTTON,
      BS_PUSHBUTTON|WS_TABSTOP|WS_VISIBLE
    END
END



<a name="0138_0011"><a name="0138_0011">
<a name="0138_0012">
[LISTING FIVE]
<a name="0138_0012">


/* Cached-PS Window Platform for PM Graphics      */
/* (c) William H. Murray and Chris H. Pappas, 1989 */

#define INCL_PM

#include <os2.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mfit17.h"

MRESULT EXPENTRY About(HWND,USHORT,MPARAM,MPARAM);
MRESULT EXPENTRY LineDiaProc(HWND,USHORT,MPARAM,MPARAM);
MRESULT EXPENTRY GraphicProc(HWND,USHORT,MPARAM,MPARAM);

#define maxpts 100

HPS   hps;
HWND  hfrm,hcnt;
QMSG  qmsg;
long  ptxorg[maxpts];
long  ptyorg[maxpts];
long  xaxismax=500;
long  yaxismax=400;
char  title[80]="Mouse Data Points";
char  xstring[80]="x - axis label";
char  ystring[80]="y - axis label";
POINTL mousepos;

main ()
  {
  static CHAR pszClassName[]="FirstClass";
  HAB   hab;
  HMQ   hmq;
  HDC   hdc;
  SIZEL page;
  ULONG flFrameFlags=FCF_SYSMENU|FCF_TITLEBAR|
           FCF_SIZEBORDER|FCF_MINMAX|
           FCF_ICON|FCF_MENU;
  ULONG flFrameStyle=WS_VISIBLE;

  hab=WinInitialize(0);
  hmq=WinCreateMsgQueue(hab,0);
  WinRegisterClass(hab,pszClassName,GraphicProc,
         CS_SIZEREDRAW,0);
  hfrm=WinCreateStdWindow(HWND_DESKTOP,flFrameStyle,
          &flFrameFlags,pszClassName,
          "Cached-PS PM Graphics",
          0L,NULL,ID_RESOURCE,&hcnt);
  hdc=WinOpenWindowDC(hcnt);
  hps=GpiCreatePS(hab,hdc,&page,PU_PELS|GPIA_ASSOC);
  WinSetWindowPos(hfrm,NULL,10,10,610,470,SWP_SIZE|
          SWP_MOVE|SWP_SHOW);
  while (WinGetMsg(hab,&qmsg,NULL,0,0))
    WinDispatchMsg(hab,&qmsg);
  WinDestroyWindow(hfrm);
  WinDestroyMsgQueue(hmq);
  WinTerminate(hab);
  return 0;
  }

MRESULT EXPENTRY About(hwnd,messg,parm1,parm2)
  HWND    hwnd;
  USHORT messg;
  MPARAM parm1,parm2;
  {
  switch (messg)
    {
    case WM_COMMAND:
      switch (LOUSHORT(parm1))
   {
   case ID_OK:
     WinDismissDlg(hwnd,TRUE);
     return 0;
   }
   break;
    }
    return (WinDefDlgProc(hwnd,messg,parm1,parm2));
  }

MRESULT EXPENTRY LineDiaProc(hwnd,messg,parm1,parm2)
  HWND hwnd;
  USHORT messg;
  MPARAM parm1;
  MPARAM parm2;
  {
  short X1;
  short Y1;
    switch (messg)
      {
      case WM_COMMAND:
   switch (LOUSHORT (parm1))
     {
     case ID_OK:
       WinQueryDlgItemText(hwnd,
            DM_TITLE,
            80,
            title);
       WinQueryDlgItemText(hwnd,
            DM_XAXIS,
            80,
            xstring);
       WinQueryDlgItemText(hwnd,
            DM_YAXIS,
            80,
            ystring);
       WinQueryDlgItemShort(hwnd,
            DM_X1,
            &X1,
            1);
       WinQueryDlgItemShort(hwnd,
            DM_Y1,
            &Y1,
            1);
       xaxismax=(long) X1;
       yaxismax=(long) Y1;
       WinDismissDlg(hwnd,TRUE);
       return 0;

     case ID_CANCEL:
       WinDismissDlg(hwnd,TRUE);
       return 0;
     }
     break;
      }
  return WinDefDlgProc (hwnd,messg,parm1,parm2);
  }

MRESULT EXPENTRY GraphicProc(hwnd,messg,parm1,parm2)
  HWND       hwnd;
  USHORT    messg;
  MPARAM    parm1,parm2;
  {
  static    LONG    ColorDataInfo[]={CLR_NEUTRAL,
                 RGB_BLACK,
                 CLR_BACKGROUND,
                 RGB_WHITE};
  static    HPOINTER linePtr;
  static    HWND     hmenu;
  HPS       hgpi;
  HDC       hdc;
  POINTL    ptl;
  GRADIENTL gradl;
  RECTL     rcl;
  int       i,alength,blength;
  int       lenxstring,lenystring,lentitle,
       lenmaxxlabel,lenmaxylabel;
  long       ptxmax,ptymax,sumx1,sumy1,sumxy1,
       sumxsqr1,deno1,numa1,numb1,
       sumx2,sumy2,sumxy2,sumxsqr2,
       deno2,numa2,numb2;
  long       ptxscaled[maxpts],ptyscaled[maxpts],maxx;
  char       maxxlabel[4],maxylabel[4],
       astring[10],bstring[10];
  double    a1,b1,a2,b2,y;
  static    int j=0;
  static    int npts=0;

  /* Length of various strings */
  lentitle=strlen(title);
  lenxstring=strlen(xstring);
  lenystring=strlen(ystring);

  /* Convert maximum x value to a string */
  itoa((int)xaxismax,maxxlabel,10);
  lenmaxxlabel=strlen(maxxlabel);

  /* Convert maximum y value to a string */
  itoa((int)yaxismax,maxylabel,10);
  lenmaxylabel=strlen(maxylabel);

  /* Scale all x values in original array. */
    for (i=0;i<npts;i++)
    ptxscaled[i]=((ptxorg[i]-100)*xaxismax/400);

  /* Scale all y values in original array. */
    for (i=0;i<npts;i++)
    ptyscaled[i]=((ptyorg[i]-100)*yaxismax/300);

  /* Calculate values for screen equation */
  sumxsqr1=0;
  sumx1=0;
  sumy1=0;
  sumxy1=0;
  for (i=0;i<npts;i++)
  {
    sumxsqr1+=ptxscaled[i]*ptxscaled[i];
    sumx1+=ptxscaled[i];
    sumy1+=ptyscaled[i];
    sumxy1+=ptxscaled[i]*ptyscaled[i];
  }
  deno1=(npts*sumxsqr1)-(sumx1*sumx1);
  numa1=(sumxsqr1*sumy1)-(sumx1*sumxy1);
  numb1=(npts*sumxy1)-(sumx1*sumy1);

  /* Calculate values for drawing line on screen */
  sumxsqr2=0;
  sumx2=0;
  sumy2=0;
  sumxy2=0;
  for (i=0;i<npts;i++)
  {
    sumxsqr2+=ptxorg[i]*ptxorg[i];
    sumx2+=ptxorg[i];
    sumy2+=ptyorg[i];
    sumxy2+=ptxorg[i]*ptyorg[i];
  }
  deno2=(npts*sumxsqr2)-(sumx2*sumx2);
  numa2=(sumxsqr2*sumy2)-(sumx2*sumxy2);
  numb2=(npts*sumxy2)-(sumx2*sumy2);

  switch (messg)
    {

    case WM_CREATE:
      linePtr=WinLoadPointer(HWND_DESKTOP,
              NULL,IDP_POINTER);
      return 0;

    case WM_BUTTON1DOWN:
      GpiSetMarker(hps,MARKSYM_PLUS);
      GpiSetColor(hps,CLR_BLUE);
      mousepos.x=(LONG) LOUSHORT(parm1);
      mousepos.y=(LONG) HIUSHORT(parm1);
      if(mousepos.x>99 & mousepos.x<500 &
    mousepos.y>99 & mousepos.y<400)
    {
    ptxorg[j]=mousepos.x;
    ptyorg[j]=mousepos.y;
    j++;
    npts=j;
    GpiMarker(hps,&mousepos);
    }
      return TRUE;

    case WM_MOUSEMOVE:
      WinSetPointer(HWND_DESKTOP,linePtr);
      return 0;

    case WM_BUTTON2DOWN:
      GpiSetColor(hps,CLR_RED);
      j=0;         /*reset pointer */
      maxx=0;
      /* Slope and intercept for org. and scaled */
      a1=(double) numa1/deno1;
      b1=(double) numb1/deno1;
      a2=(double) numa2/deno2;
      b2=(double) numb2/deno2;

      /* Starting point for line of best fit */
      for (i=99;i<510;i++)
   {
     y=a2+(b2*i);
     if(y>99.0 && y<410.0)
     {
       ptl.x=i;
       ptl.y=(long) y;
       break;
     }
   }
      GpiMove(hps,&ptl);

      /* Ending point for line of best fit */
      for (i=510;i>99;i--)
   {
     y=a2+(b2*i);
     if(y>99.0 && y<410.0)
     {
       ptl.x=i;
       ptl.y=(long) y;
       break;
     }
   }
      GpiLine(hps,&ptl);

    /* Draw the equation to the screen. */
      GpiSetColor(hps,CLR_WHITE);
      ptl.x=100;
      ptl.y=80;
      GpiMove(hps,&ptl);
      ptl.x=500;
      ptl.y=55;
      GpiBox(hps,DRO_FILL,&ptl,0L,0L);
      GpiSetColor(hps,CLR_RED);

      gcvt(a1,7,astring);
      alength=strlen(astring);
      gcvt(b1,7,bstring);
      blength=strlen(bstring);
      ptl.x=300-(LONG)(alength+blength+8)*4;
      ptl.y=62;
      GpiMove(hps,&ptl);
      GpiCharString(hps,4L,"y = ");
      GpiCharString(hps,(long) alength,astring);
      GpiCharString(hps,3L," + ");
      GpiCharString(hps,(long) blength,bstring);
      GpiCharString(hps,3L,"(x)");
      return TRUE;

    case WM_SIZE:
      if (hmenu==NULL)
   hmenu=WinWindowFromID(WinQueryWindow(hwnd,
               QW_PARENT,FALSE),FID_MENU);
      return 0;

    case WM_COMMAND:
      {
      switch (COMMANDMSG(&messg)->cmd)
   {
   case IDM_ABOUT:
     if (WinDlgBox(HWND_DESKTOP,hwnd,About,
         NULL,ID_ABOUT,NULL))
        WinInvalidateRect(hwnd,NULL,FALSE);
   return 0;

   case IDM_INPUT:
     if (WinDlgBox(HWND_DESKTOP,hwnd,LineDiaProc,
         NULL,ID_INPUT,NULL))
         WinInvalidateRect(hwnd,NULL,FALSE);
     return 0;

   return 0;
   }
      }
    break;

    case WM_PAINT:
    hgpi=WinBeginPaint(hwnd,NULL,NULL);
    GpiCreateLogColorTable(hgpi,LCOL_RESET,
    LCOLF_INDRGB,0L,4L,ColorDataInfo);
    GpiErase(hgpi);

/*--------- your routines below ----------*/

    GpiSetColor(hgpi,CLR_BLACK);

    /* Draw X and Y coordinate axis */
    ptl.x=99;
    ptl.y=410;
    GpiMove(hgpi,&ptl);
    ptl.y=99;
    GpiLine(hgpi,&ptl);
    ptl.x=510;
    GpiLine(hgpi,&ptl);

    /* Draw Y axis tic marks */
    ptl.y=130;
    for (i=0;i<10;i++)
      {
      ptl.x=95;
      GpiMove(hgpi,&ptl);
      ptl.x=99;
      GpiLine(hgpi,&ptl);
      ptl.y+=30;
      }

    /* Draw X axis tic marks */
    ptl.x=140;
    for (i=0;i<10;i++)
      {
      ptl.y=99;
      GpiMove(hgpi,&ptl);
      ptl.y=95;
      GpiLine(hgpi,&ptl);
      ptl.x+=40;
      }

    GpiSetCharMode(hgpi,CM_MODE3);

    /* Center and print line chart title */
    ptl.y=410;
    ptl.x=300-((LONG) (lentitle/2)*6);
    GpiCharStringAt(hgpi,&ptl,(LONG) lentitle,title);

    /* Center and print horizontal axis label */
    ptl.y=40;
    ptl.x=300-((LONG) (lenxstring/2)*6);
    GpiCharStringAt(hgpi,&ptl,(LONG) lenxstring,
          xstring);

    /* Print horizontal axis maximum value */
    ptl.y=82;
    ptl.x=490;
    GpiCharStringAt(hgpi,&ptl,(long) lenmaxxlabel,
          maxxlabel);

    /* Print vertical axis maximum value */
    ptl.y=400;
    ptl.x=70;
    GpiCharStringAt(hgpi,&ptl,(long) lenmaxylabel,
          maxylabel);

    /* Center and print vertical axis label */
    ptl.y=240-((LONG) (lenystring/2)*6);
    ptl.x=70;
    gradl.x=0;
    gradl.y=90;
    GpiSetCharAngle(hgpi,&gradl);
    GpiCharStringAt(hgpi,&ptl,(LONG) lenystring,
          ystring);

    /*--------- your routines above ----------*/

    WinEndPaint(hgpi);
    break;

    default:
      return WinDefWindowProc(hwnd,messg,parm1,parm2);
    }
  return 0;
  }








Copyright © 1989, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.