// SpinnerDesignDlg.cpp : implementation file // #include "stdafx.h" #include "SpinnerDesign.h" #include "SpinnerDesignDlg.h" #include "ColourPicker.h" #include #include "ObjectStorage.h" #include "storageStructs.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define DLGHEIGHT 50 #define GRIDSIZE 10 #define PI 3.141592654 ///////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About class CAboutDlg : public CDialog { public: CAboutDlg(); // Dialog Data //{{AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CAboutDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: //{{AFX_MSG(CAboutDlg) //}}AFX_MSG DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { //{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //{{AFX_MSG_MAP(CAboutDlg) // No message handlers //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CSpinnerDesignDlg dialog CSpinnerDesignDlg::CSpinnerDesignDlg(CWnd* pParent /*=NULL*/) : CDialog(CSpinnerDesignDlg::IDD, pParent) { //{{AFX_DATA_INIT(CSpinnerDesignDlg) m_scale = 1.0; //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CSpinnerDesignDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CSpinnerDesignDlg) DDX_Control(pDX, IDC_COMBO2, m_combo2); DDX_Control(pDX, IDC_COMBO1, m_combo1); DDX_Text(pDX, IDC_EDIT1, m_scale); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CSpinnerDesignDlg, CDialog) //{{AFX_MSG_MAP(CSpinnerDesignDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_CLOSE() ON_BN_CLICKED(IDC_BUTTON1, OnButton1) ON_BN_CLICKED(IDC_BUTTON3, OnButton3) ON_WM_SIZE() ON_EN_UPDATE(IDC_EDIT1, OnUpdateEdit1) ON_WM_LBUTTONDOWN() ON_BN_CLICKED(IDC_CLEAR, OnClear) ON_BN_CLICKED(IDC_GENERATE, OnGenerate) ON_BN_CLICKED(IDC_RESCALE, OnRescale) ON_WM_DRAWITEM() ON_BN_CLICKED(IDC_TEST, OnTest) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CSpinnerDesignDlg message handlers BOOL CSpinnerDesignDlg::OnInitDialog() { CDialog::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here m_combo1.ReadFromIniSection("Background"); m_combo2.ReadFromIniSection("Foreground"); m_combo1.SetCurSel(0); m_combo2.SetCurSel(0); m_rot2d=0; m_scale=1.; bTestViewMode = false; CheckDlgButton(IDC_RADIOPOLY,1); m_scale = atof(AfxGetApp()->GetProfileString("Settings","Scale","1")); UpdateData(false); goImage.LoadBitmap(IDB_BITMAP1); clrImage.LoadBitmap(IDB_BITMAP2); SendDlgItemMessage(IDC_RESCALE,BM_SETIMAGE,IMAGE_BITMAP,reinterpret_cast(goImage.GetSafeHandle())); SendDlgItemMessage(IDC_BUTTON1,BM_SETIMAGE,IMAGE_BITMAP,reinterpret_cast(clrImage.GetSafeHandle())); SendDlgItemMessage(IDC_BUTTON3,BM_SETIMAGE,IMAGE_BITMAP,reinterpret_cast(clrImage.GetSafeHandle())); return TRUE; // return TRUE unless you set the focus to a control } void CSpinnerDesignDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } } void CSpinnerDesignDlg::DrawGrid(CDC *pDC) { CRect rcClient; GetClientRect(rcClient); // make the background of the drawable area white. CBrush cBr(RGB(255,255,255)); pDC->SelectObject(&cBr); pDC->Rectangle(0,DLGHEIGHT,rcClient.right,rcClient.bottom); // Draw the big line thru the center markings rcClient.top=DLGHEIGHT; CPen cPen(PS_DASHDOT,1,RGB(100,100,100)); pDC->SelectObject(&cPen); pDC->SetBkMode(TRANSPARENT); pDC->MoveTo(rcClient.right/2,rcClient.top); pDC->LineTo(rcClient.right/2,rcClient.bottom); pDC->MoveTo(rcClient.left,(rcClient.bottom/2)+(rcClient.top/2)); pDC->LineTo(rcClient.right,(rcClient.bottom/2)+(rcClient.top/2)); } void CSpinnerDesignDlg::DrawItem(CDC *pDC) { // intialize the drawing objects and regions. int offsetdeg = GetDlgItemInt(IDC_EDIT1); double offsetangle = PI * ((double)offsetdeg / 180.); CRect rcClient; GetClientRect(rcClient); rcClient.top=DLGHEIGHT; CPoint centre(rcClient.right/2,(rcClient.bottom/2)+(rcClient.top/2)); CPen solidblack(PS_SOLID,0,RGB(0,0,0)); CPen *old = pDC->SelectObject(&solidblack); CBrush solidbrush(RGB(255,255,255)); CBrush *oldbrush = pDC->SelectObject(&solidbrush); CRgn rgn; rgn.CreateRectRgn(0,DLGHEIGHT,rcClient.right,rcClient.bottom); pDC->SelectClipRgn(&rgn); // Draw the grid, only clear the screen if were in Test mode. if(bTestViewMode) pDC->Rectangle(0,DLGHEIGHT,rcClient.right,rcClient.bottom); else DrawGrid(pDC); // draw all objects Ive stored in my drawing list. POSITION pos = mylist.GetHeadPosition(); while(pos!=NULL) { CObjectStorage *pStore = (CObjectStorage*) mylist.GetNext(pos); pStore->Draw(pDC,m_scale,m_rot2d,centre); } // now put the grid points on last, this will draw the grid on top // of all objects, which is what we require. But dont draw them in test mode. if(!bTestViewMode) { for(int yPos=DLGHEIGHT;yPosSetPixel(xPos,yPos,RGB(100,100,100)); } } } // put old objects back into dc pDC->SelectClipRgn(NULL); pDC->SelectObject(old); pDC->SelectObject(oldbrush); } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CSpinnerDesignDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CPaintDC dc(this); // device context for painting DrawItem(&dc); CRect rcClient; GetClientRect(rcClient); CDialog::OnPaint(); } } // The system calls this to obtain the cursor to display while the user drags // the minimized window. HCURSOR CSpinnerDesignDlg::OnQueryDragIcon() { return (HCURSOR) m_hIcon; } void CSpinnerDesignDlg::DrawColourList(LPDRAWITEMSTRUCT lpDraw, CComboBox& wnd) { } void CSpinnerDesignDlg::OnClose() { m_combo1.WriteToIniSection("Background"); m_combo2.WriteToIniSection("Foreground"); CString strDouble; strDouble.Format("%f",m_scale); AfxGetApp()->WriteProfileString("Settings","Scale",strDouble); ClearItems(); CDialog::OnClose(); } void CSpinnerDesignDlg::ClearItems() { POSITION pos = mylist.GetHeadPosition(); while(pos) { delete mylist.GetNext(pos); } mylist.RemoveAll(); } void CSpinnerDesignDlg::OnButton1() { CColourPicker pick; pick.m_comboClr = &m_combo1; pick.DoModal(); } void CSpinnerDesignDlg::OnButton3() { CColourPicker pick; pick.m_comboClr = &m_combo2; pick.DoModal(); } void CSpinnerDesignDlg::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); CRect rcInvalid; GetClientRect(rcInvalid); rcInvalid.top=DLGHEIGHT; InvalidateRect(rcInvalid); //reset the window size variables here? } void CSpinnerDesignDlg::OnUpdateEdit1() { } void CSpinnerDesignDlg::ConvertXYtoTheta(CPoint point,double &ang,double &len) { // now correct them to the screen CRect rcClient; GetClientRect(rcClient); rcClient.top=DLGHEIGHT; CPoint centre(rcClient.right/2,(rcClient.bottom/2)+(rcClient.top/2)); point-=centre; point.y = -point.y; // y coords are reversed in my model // (line length) square root of the squares double xsq = pow((0. - (double)point.x),2); double ysq = pow((0. - (double)point.y),2); len = sqrt(xsq + ysq); len/=m_scale; // find the angle of the line. (TO THETA) double xdt = 0. - (double)point.x; double ydt = 0. - (double)point.y; ang = atan(xdt/ydt); // we are on the other side of the circle. // the above only calculates the position in 180deg // we find out if its on "the other side" :) if(ydt>0) { ang += PI; } ang-=m_rot2d; } void CSpinnerDesignDlg::OnLButtonDown(UINT nFlags, CPoint point) { // correct the coordinates. to grid point.x = point.x-(point.x % GRIDSIZE); point.y = point.y-(point.y % GRIDSIZE); // choose what coordinate type we are currently drawing. AddToDiagram(point); // do a forced redraw. InvalidateRect(NULL,1); } void CSpinnerDesignDlg::AddToDiagram(CPoint point) { int count=0; double angle,length; ConvertXYtoTheta(point,angle,length); CObjectStorage *pPoly; if(IsDlgButtonChecked(IDC_RADIOPOLY)) pPoly= new CPolyStorage(); else pPoly= new CLineStorage(); count=1; BOOL bStillDrawing = TRUE; CPoint newPoint(0,0); CPoint oldpoint; CPoint oldendPoint; oldpoint.x = -99999999; if (this != GetCapture()) SetCapture(); ASSERT(this == GetCapture()); while(bStillDrawing) { MSG msg; VERIFY(::GetMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST)); if (CWnd::GetCapture() != this) { break; } /// get point, and adjust from screen coords to window coords /// newPoint = msg.pt; ScreenToClient(&newPoint); switch(msg.message) { case WM_LBUTTONUP: if(count>=MAX_POLY) bStillDrawing=FALSE; else { if((newPoint.x % GRIDSIZE)>(GRIDSIZE/2)) newPoint.x = newPoint.x + (GRIDSIZE-(newPoint.x % GRIDSIZE)); else newPoint.x = newPoint.x - (newPoint.x % GRIDSIZE); if((newPoint.y % GRIDSIZE)>(GRIDSIZE/2)) newPoint.y = newPoint.y + (GRIDSIZE-(newPoint.y % GRIDSIZE)); else newPoint.y = newPoint.y - (newPoint.y % GRIDSIZE); newPoint.y = newPoint.y - (newPoint.y % GRIDSIZE); ConvertXYtoTheta(newPoint,angle,length); pPoly->AddPoint(angle,length); DrawTracked(point,newPoint); count++; point = newPoint; } break; case WM_MOUSEMOVE: if((newPoint.x % GRIDSIZE)>(GRIDSIZE/2)) newPoint.x = newPoint.x + (GRIDSIZE-(newPoint.x % GRIDSIZE)); else newPoint.x = newPoint.x - (newPoint.x % GRIDSIZE); if((newPoint.y % GRIDSIZE)>(GRIDSIZE/2)) newPoint.y = newPoint.y + (GRIDSIZE-(newPoint.y % GRIDSIZE)); else newPoint.y = newPoint.y - (newPoint.y % GRIDSIZE); if(-99999999!=oldpoint.x) DrawTracked(oldpoint,oldendPoint); oldpoint = point; oldendPoint = newPoint; DrawTracked(point,newPoint); break; case WM_RBUTTONDBLCLK: case WM_RBUTTONUP: case WM_RBUTTONDOWN: bStillDrawing=FALSE; case WM_LBUTTONDOWN: break; default: DispatchMessage(&msg); break; } } // store away as angle length pairs. ReleaseCapture(); int iPos=m_combo1.GetCurSel(); int bg = m_combo1.GetItemData(iPos); iPos=m_combo2.GetCurSel(); int fg = m_combo2.GetItemData(iPos); pPoly->SetFgRGB(fg&0xff, (fg>>8)&0xff, (fg>>16)&0xff); pPoly->SetBkRGB(bg&0xff, (bg>>8)&0xff, (bg>>16)&0xff); mylist.AddTail(pPoly); } // Draw a tracker type line on the display which if drawn twice // will "undisplay" itself. void CSpinnerDesignDlg::DrawTracked(CPoint startPoint,CPoint endPoint) { CRgn rgnNew; CBrush* pBrush = CDC::GetHalftoneBrush(); CDC* pDC = GetDC(); CRect rect(startPoint, endPoint); rect.NormalizeRect(); int x_delta, y_delta; /// choose some deltas to make the tracking line look OK in each orientation /// if (rect.Height() > rect.Width()) { x_delta = 1; y_delta = 0; } else { x_delta = 0; y_delta = 1; } CPoint newPoints[] = { CPoint(startPoint.x + x_delta, startPoint.y - y_delta), CPoint(startPoint.x - x_delta, startPoint.y + y_delta), CPoint(endPoint.x - x_delta, endPoint.y + y_delta), CPoint(endPoint.x + x_delta, endPoint.y - y_delta) }; rgnNew.CreatePolygonRgn(newPoints, 4, ALTERNATE); pDC->SelectClipRgn(&rgnNew); pDC->GetClipBox(&rect); CBrush* pBrushOld = pDC->SelectObject(pBrush); pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATINVERT); /// restore the device context /// pDC->SelectObject(pBrushOld); pDC->SelectClipRgn(NULL); ReleaseDC(pDC); } void CSpinnerDesignDlg::OnClear() { if(mylist.GetCount()>0) { CString str; str.LoadString(IDS_CLEAR_ARE_YOU_SURE); if(MessageBox(str,"Are you sure",MB_OKCANCEL|MB_ICONQUESTION)==IDOK) { ClearItems(); } } else ClearItems(); Invalidate(); } void CSpinnerDesignDlg::OnGenerate() { CFileDialog dlg(FALSE,"JVF"); if(dlg.DoModal()==IDOK) { try { CFile file(dlg.GetFileName(),CFile::modeCreate|CFile::modeWrite); StorageHeader hdr; hdr.magic = 0xfade0ff; hdr.byReserved=0; hdr.elements=mylist.GetCount(); hdr.byRed=255; hdr.byBlue=255; hdr.byGreen=255; file.Write(&hdr,sizeof StorageHeader); POSITION pos = mylist.GetHeadPosition(); while(pos) { CObjectStorage *obj = (CObjectStorage*)mylist.GetNext(pos); obj->OutputCoordinates(&file); } file.Close(); } catch(CFileException *e) { char szError[255]; e->GetErrorMessage(szError,sizeof szError); MessageBox(CString("Problem opening file!\n")+szError); } } } void CSpinnerDesignDlg::OnRescale() { UpdateData(); Invalidate(); } void CSpinnerDesignDlg::OnTest() { // start by settting test=true, store scale and rotation. bTestViewMode=true; double scl = m_scale; double rot = m_rot2d; // work from rotation of 0 and scale of 0.1 going up in increments. m_scale=0.1; for(int i=0;i<360;i++) { m_scale+=0.004; m_rot2d+=0.03; Sleep(30); // delay between frames Invalidate(); UpdateWindow(); } // restore our screen.. m_scale = scl; m_rot2d = rot; bTestViewMode=false; Invalidate(); UpdateWindow(); }