// // (C) Copyright 2002-2006 by Autodesk, Inc. // // Permission to use, copy, modify, and distribute this software in // object code form for any purpose and without fee is hereby granted, // provided that the above copyright notice appears in all copies and // that both that copyright notice and the limited warranty and // restricted rights notice below appear in all supporting // documentation. // // AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. // AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. // DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE // UNINTERRUPTED OR ERROR FREE. // // Use, duplication, or disclosure by the U.S. Government is subject to // restrictions set forth in FAR 52.227-19 (Commercial Computer // Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) // (Rights in Technical Data and Computer Software), as applicable. #include "StdAfx.h" #if defined(_DEBUG) && !defined(AC_FULL_DEBUG) //#error _DEBUG should not be defined except in internal Adesk debug builds #endif #include "resource.h" #include "GsPreviewCtrlEx.h" #include "acgs.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #ifdef _DEBUG #define RXASSERT(f) assert(f) #else #define RXASSERT(f) ((void)0) #endif #define WM_VIEWDBLCLKSEL (WM_USER + 0x1200) BEGIN_MESSAGE_MAP(CGsPreviewCtrlEx, CStatic) //{{AFX_MSG_MAP(CGsPreviewCtrlEx) ON_WM_PAINT() ON_WM_SIZE() ON_WM_MOUSEWHEEL() ON_WM_LBUTTONDOWN() ON_WM_MBUTTONDOWN() ON_WM_MBUTTONUP() ON_WM_MOUSEMOVE() ON_WM_LBUTTONDBLCLK() ON_WM_NCHITTEST() ON_WM_SETFOCUS() ON_WM_LBUTTONUP() //}}AFX_MSG_MAP END_MESSAGE_MAP() void CGsPreviewCtrlEx::OnPaint() { CPaintDC dc(this); //update the gs view if (mpView != NULL) { mpView->invalidate(); mpView->update(); } } // Erase view and delete model void CGsPreviewCtrlEx::erasePreview() { if (mpView) mpView->eraseAll(); if (mpManager && mpModel) { mpManager->destroyAutoCADModel (mpModel); mpModel = NULL; } } void CGsPreviewCtrlEx::setModel(AcGsModel* pModel) { erasePreview(); mpModel = pModel; mbModelCreated =false; } void CGsPreviewCtrlEx::clearAll() { if (mpView) { mpView->eraseAll(); } if (mpDevice) { bool b = mpDevice->erase(mpView); RXASSERT(b); } if (mpFactory) { if (mpView) { mpFactory->deleteView(mpView); mpView = NULL; } if (mpGhostModel) { mpFactory->deleteModel(mpGhostModel); mpGhostModel = NULL; } mpFactory = NULL; } if (mpManager) { if (mpModel) { if (mbModelCreated) mpManager->destroyAutoCADModel(mpModel); mpModel = NULL; } if (mpDevice) { mpManager->destroyAutoCADDevice(mpDevice); mpDevice = NULL; } mpManager = NULL; } } void CGsPreviewCtrlEx::init(HMODULE hRes, bool bCreateModel) { //Load our special cursors mhPanCursor = LoadCursor(hRes,MAKEINTRESOURCE(IDI_PAN)); mhCrossCursor = LoadCursor(hRes,MAKEINTRESOURCE(IDI_CROSS)); mhOrbitCursor = LoadCursor(hRes,MAKEINTRESOURCE(IDI_ORBIT)); #ifdef _WIN64 ::SetClassLongPtr(m_hWnd,GCLP_HCURSOR,NULL); #else ::SetClassLong(m_hWnd,GCL_HCURSOR,NULL); #endif //Instantiate view, a device and a model object CRect rect; if (!mpManager) { // get the AcGsManager object for a specified AutoCAD MDI Client CView mpManager = acgsGetGsManager(); RXASSERT(mpManager); // get the GS class factory #if (ARX >= 20) AcGsKernelDescriptor desc; desc.addRequirement(AcGsKernelDescriptor::k3DDrawing); mpFactory = AcGsManager::acquireGraphicsKernel(desc); if (!mpFactory) return; #else mpFactory = mpManager->getGSClassFactory(); #endif RXASSERT(mpFactory); // create an AcGsDevice object. The window handle passed in to this // function is the display surface onto which the graphics system draws //a device with standard autocad color palette #if (ARX >= 20) //mpDevice = mpFactory->createOffScreenDevice(); mpDevice = mpManager->createAutoCADDevice(*mpFactory, m_hWnd); #else mpDevice = mpManager->createAutoCADDevice(m_hWnd); #endif RXASSERT(mpDevice); // get the size of the window that we are going to draw in GetClientRect( &rect); // make sure the gs device knows how big the window is mpDevice->onSize(rect.Width(), rect.Height()); // construct a simple view mpView = mpFactory->createView(); RXASSERT(mpView); if (bCreateModel) { RXASSERT(mpModel==NULL); // create an AcGsModel object with the AcGsModel::kMain RenderType // (which is a hint to the graphics system that the geometry in this // model should be rasterized into its main frame buffer). This // AcGsModel is created with get and release functions that will open and close AcDb objects. #if (ARX >= 20) mpModel = mpManager->createAutoCADModel(*mpFactory); #else mpModel = mpManager->createAutoCADModel(); #endif RXASSERT(mpModel); mbModelCreated = true; } //another model without open/close for the orbit gadget mpGhostModel = mpFactory->createModel(AcGsModel::kDirect,0,0,0); //mpView->add(&mOrbitGadget,mpGhostModel); //mOrbitGadget.setGsView(mpView); mpDevice->add(mpView); } } void CGsPreviewCtrlEx::OnSize(UINT nType, int cx, int cy) { CRect rect; if (mpDevice) { GetClientRect( &rect); mpDevice->onSize(rect.Width(), rect.Height()); } } BOOL CGsPreviewCtrlEx::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) { if (mpView) { if (zDelta<0) mpView->zoom(0.5); else mpView->zoom(1.5); Invalidate(); } return TRUE; } void CGsPreviewCtrlEx::OnLButtonDown(UINT nFlags, CPoint point) { // if zooming if (mZooming) { mMouseDown = true; mMouseMoving = false; } else { SetFocus(); //start orbit mbOrbiting = true; SetCapture(); //set up the proper cursor #ifdef _WIN64 ::SetClassLongPtr(m_hWnd,GCLP_HCURSOR,NULL); #else ::SetClassLong(m_hWnd,GCL_HCURSOR,NULL); #endif ::SetCursor(mhOrbitCursor); } //store the start point mStartPt = point; ClientToScreen(&point); GetParent()->ScreenToClient(&point); GetParent()->SendMessage(WM_LBUTTONDOWN,nFlags,MAKELPARAM(point.x,point.y)); } void CGsPreviewCtrlEx::OnLButtonDblClk(UINT nFlags, CPoint point) { ClientToScreen(&point); GetParent()->ScreenToClient(&point); GetParent()->SendMessage(WM_LBUTTONDBLCLK,nFlags,MAKELPARAM(point.x,point.y)); GetParent()->SendMessage(WM_VIEWDBLCLKSEL,0,0); } void CGsPreviewCtrlEx::OnLButtonUp(UINT nFlags, CPoint point) { if (mZooming && mMouseDown) { // end zoom mZooming = false; mMouseDown = false; mMouseMoving = false; // AcGsView Y coordinates origin is at the screen bottom RECT rect; GetClientRect(&rect); //mpView->zoomWindow(AcGePoint2d(mStartPt.x,rect.bottom - mStartPt.y),AcGePoint2d(mEndPt.x,rect.bottom - mEndPt.y)); mpView->invalidate(); mpView->update(); } else { //end orbit mbOrbiting = false; #ifdef _WIN64 ::SetClassLongPtr(m_hWnd,GCLP_HCURSOR,(LONG_PTR)mhCrossCursor); #else ::SetClassLong(m_hWnd,GCL_HCURSOR,(long)mhCrossCursor); #endif ReleaseCapture(); } ClientToScreen(&point); GetParent()->ScreenToClient(&point); GetParent()->SendMessage(WM_LBUTTONUP,nFlags,MAKELPARAM(point.x,point.y)); } void CGsPreviewCtrlEx::OnMButtonDown(UINT nFlags, CPoint point) { //start pan mbPanning = true; SetCapture(); //set up the proper cursor #ifdef _WIN64 ::SetClassLongPtr(m_hWnd,GCLP_HCURSOR,NULL); #else ::SetClassLong(m_hWnd,GCL_HCURSOR,NULL); #endif ::SetCursor(mhPanCursor); //store the start point mStartPt = point; } void CGsPreviewCtrlEx::OnMButtonUp(UINT nFlags, CPoint point) { //end pan ReleaseCapture(); mbPanning = false; #ifdef _WIN64 ::SetClassLongPtr(m_hWnd,GCLP_HCURSOR,(LONG_PTR)mhCrossCursor); #else ::SetClassLong(m_hWnd,GCL_HCURSOR,(long)mhCrossCursor); #endif } void CGsPreviewCtrlEx::OnMouseMove(UINT nFlags, CPoint point) { // is the GsView valid if (mpView) { if (mZooming) { // if left button is down if (mMouseDown) { if (mMouseMoving) { // erase the old rectangle RubberRectangle(mStartPt, mEndPt); } // draw the new rectangle RubberRectangle(mStartPt, point); mMouseMoving = true; // save the new point for next time mEndPt = point; } } else { if (mbPanning) { //transform the point from device coordinates to //world coordinates AcGeVector3d pan_vec(-(point.x-mStartPt.x),point.y-mStartPt.y,0); pan_vec.transformBy(mpView->viewingMatrix() * mpView->worldToDeviceMatrix().inverse()); mpView->dolly(pan_vec); Invalidate(); mStartPt = point; } if (mbOrbiting) { const double Half_Pi = 1.570796326795; AcGsDCRect view_rect; mpView->getViewport (view_rect); int nViewportX = (view_rect.m_max.x - view_rect.m_min.x) + 1; int nViewportY = (view_rect.m_max.y - view_rect.m_min.y) + 1; int centerX = (int)(nViewportX / 2.0f + view_rect.m_min.x); int centerY = (int)(nViewportY / 2.0f + view_rect.m_min.y); const double radius = min (nViewportX, nViewportY) * 0.4f; // compute two vectors from last and new cursor positions: AcGeVector3d last_vector ((mStartPt.x - centerX) / radius, -(mStartPt.y - centerY) / radius, 0.0); if (last_vector.lengthSqrd () > 1.0) // outside the radius last_vector.normalize (); else last_vector.z = sqrt (1.0 - last_vector.x * last_vector.x - last_vector.y * last_vector.y); AcGeVector3d new_vector((point.x - centerX) / radius, -(point.y - centerY) / radius, 0.0); if (new_vector.lengthSqrd () > 1.0) // outside the radius new_vector.normalize (); else new_vector.z = sqrt (1.0 - new_vector.x * new_vector.x - new_vector.y * new_vector.y); // determine angles for proper sequence of camera manipulations: AcGeVector3d rotation_vector (last_vector); rotation_vector = rotation_vector.crossProduct (new_vector); // rotation_vector = last_vector x new_vector AcGeVector3d work_vector (rotation_vector); work_vector.z = 0.0f; // projection of rotation_vector onto xy plane double roll_angle = atan2 (work_vector.x, work_vector.y); // assuming that the camera's up vector is "up", // this computes the angle between the up vector // and the work vector, which is the roll required // to make the up vector coincident with the rotation_vector double length = rotation_vector.length (); double orbit_y_angle = (length != 0.0) ? acos (rotation_vector.z / length) + Half_Pi : Half_Pi; // represents inverse cosine of the dot product of the if (length > 1.0f) // rotation_vector and the up_vector divided by the length = 1.0f; // magnitude of both vectors. We add pi/2 because we // are making the up-vector parallel to the the rotation double rotation_angle = asin (length); // vector ... up-vector is perpin. to the eye-vector. // perform view manipulations mpView->roll( roll_angle); // 1: roll camera to make up vector coincident with rotation vector mpView->orbit( 0.0f, orbit_y_angle); // 2: orbit along y to make up vector parallel with rotation vector mpView->orbit( rotation_angle, 0.0f); // 3: orbit along x by rotation angle mpView->orbit( 0.0f, -orbit_y_angle); // 4: orbit along y by the negation of 2 mpView->roll(-roll_angle); // 5: roll camera by the negation of 1 Invalidate(); mStartPt = point; } } } } #if _MSC_VER < 1400 UINT #else LRESULT #endif CGsPreviewCtrlEx::OnNcHitTest(CPoint point) { return HTCLIENT; } void CGsPreviewCtrlEx::OnSetFocus(CWnd* pOldWnd) { #ifdef _WIN64 ::SetClassLongPtr(m_hWnd,GCLP_HCURSOR,(LONG_PTR)mhCrossCursor); #else ::SetClassLong(m_hWnd,GCL_HCURSOR,(long)mhCrossCursor); #endif } void CGsPreviewCtrlEx::RubberRectangle(CPoint startPt, CPoint endPt) { // get the device context for the client area CDC *cdc = this->GetDC(); // if ok if (cdc != NULL) { HDC hdc = cdc->GetSafeHdc(); // Create a black pen with a dotted style to draw the border of the rectangle. HPEN gdiPen = CreatePen(PS_DOT, 1, RGB(0,0,0)); // Set the ROP cdrawint mode to XOR. SetROP2(hdc, R2_XORPEN); // Select the pen into the device context. HGDIOBJ oldPen = SelectObject(hdc, gdiPen); // Create a stock NULL_BRUSH brush and select it into the device // context so that the rectangle isn't filled. HGDIOBJ oldBrush = SelectObject(hdc, GetStockObject(NULL_BRUSH) ); // Now XOR the hollow rectangle on the Graphics object with // a dotted outline. Rectangle(hdc, startPt.x, startPt.y, endPt.x, endPt.y); // Put the old stuff back where it was. SelectObject(hdc, oldBrush); // no need to delete a stock object SelectObject(hdc, oldPen); DeleteObject(gdiPen); // but we do need to delete the pen // Return the device context to Windows. this->ReleaseDC(cdc); } } void CGsPreviewCtrlEx::OnZoomExtents(AcDbDatabase* pDb) { AcDbExtents viewExtents(pDb->extmin(), pDb->extmax()); AcGeMatrix3d viewMatrix; viewExtents.transformBy (viewMatrix); mpView->zoomExtents(viewExtents.minPoint(),viewExtents.maxPoint()); RefreshView(); } void CGsPreviewCtrlEx::RefreshView() { if (mpView != NULL) { mpView->invalidate(); mpView->update(); } }