Академический Документы
Профессиональный Документы
Культура Документы
Deelip Menezes
www.deelip.com
OpenCAD
A Step by Step Guide to Developing a Professional CAD Application
Published by:
SYCODE
S1/116, Nova Cidade Complex
NH 17, Alto Porvorim, Goa - 403521 India
www.sycode.com
ISBN: 978-0-557-05592-0
No part of this publication may be reproduced, stored in a retrieval system or transmitted in any form or by any
means, electronic, mechanical, photocopying, recording, scanning or otherwise, without either the prior written
permission of the publisher, or authorization through payment of the appropriate per copy fee to the publisher.
All trademarks referred to in the book are acknowledged as properties of their respective owners.
To my wife Indira and sons - Reuben and Russell
Contents
Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Section I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Chapter 1: The Basic OpenCAD Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7
Creating the Visual C++ Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8
Setting up the Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .17
Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .18
Chapter 2: OpenCAD as a DWG Reader. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .19
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .19
Setting up the Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .19
Initializing and Uninitializing DWGdirect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .21
Reading a DWG file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .22
Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .24
Chapter 3: OpenCAD as a DWG Viewer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .25
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .25
Modifying the View class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .25
Vectorization. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .26
Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .30
Chapter 4: OpenCAD as a 3D Viewer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .31
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .31
Render Modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .31
3D Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .37
i
Chapter 6: Creating a DRX Plug-in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Creating a Visual C++ project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Setting up the Project. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
Creating a DRX Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
Calling the DRX Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
Organizing the Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Chapter 7: The Command Prompt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Connecting the Document and Child Frame. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Designing the Command Prompt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
Creating the Command Prompt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Docking the Command Prompt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
Getting the Command Prompt to work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Launching commands from the command prompt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Repeating commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Handling the unexpected . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
Wrapping up the Import command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Chapter 8: OpenCAD as a DWG Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Setting up the GetPoint mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Creating the Line command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
Creating a line by picking points. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Creating the rubber band visual feedback. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
Taking care of cancelled commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Drawing a chain of lines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
Handling the unexpected . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
The get methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Running the Line command in Bricscad V9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
ii
Acknowledgements
I have been writing about the Open Design Alliance and its technologies on my blog at
www.deelip.com for quite some time now. But the idea of this book was born out of a meeting I
had with Arnold van der Weide, the President of the ODA and its CTO, Neil Peterson, on the
sidelines of COFES 2008. This book was made possible mainly due to the vision of these two
individuals who came to the conclusion that I was made up of author material even before I did.
The Open Design Alliance forum is a treasure trove of technical information, tips and example
code related to ODA technologies. Most of my questions were answered even before I asked
them. I thank all the forum members who helped me understand the intricacies of the
DWGdirect SDK
Shweta Chari, the web designer at SYCODE, was instrumental in helping me format this book.
While writing this book, I realized that while formatting a book may not be as complicated as
C++ programming, it is not that easy either.
I write software for a living, not books. So most of this book was written in my free time at
home. That would never have been possible without the encouragement and support of my wife
Indira, who spent a good deal of her time and energy distracting our two boys so that I could
concentrate on writing this book. Work takes up most of my time and I know how much she
wants me to spend time with the kids. Indira is the inspiration behind everything I do and the
most important reason why this book has seen the light of day.
1
2
Introduction
Ever since the Open Design Alliance (formerly the OpenDWG Alliance) was founded, it has
been busy reverse engineering the DWG file format as and when Autodesk changed it. Due to
this the ODA came to be known as the "hackers group" who give nothing but pain to Autodesk
by offering their members libraries to read and write DWG files. Autodesk already has a library
called RealDWG which reads and writes DWG files, but they are known not to license it to their
business rivals. Hence the need for an organization such the ODA grew and the ODA delivered
every time Autodesk changed the DWG file format.
Every time the ODA reverse engineered the DWG file format, they improved their technology,
not surprisingly, by cloning that of Autodesk. One thing led to another and they finally ended up
cloning Autodesk's ObjectARX SDK, the very foundation on which AutoCAD has been built.
The ODA called their clone DWGdirect and needless to say, ODA members started using
DWGdirect to read and write DWG files. And that is the problem which I hope to address by
means of this book. DWGdirect is not just a SDK to read and write DWG files. It actually offers
a full blown framework that can be used to develop a professional CAD application, complete
with plug-in architecture and all. Applications built using the DWGdirect SDK are called
DWGdirect hosted applications. The not yet released IntelliCAD 7 is one of them. Bricsys
rewrote Bricscad as a DWGdirect hosted application in V8 itself.
This book is my attempt to show that the ODA offers far more than libraries to read and write
DWG files. We will create the framework of a professional CAD application (which I have called
OpenCAD) using nothing but Visual C++ 2005 and a bunch of ODA libraries. You will also
learn how to create plug-ins that extend OpenCAD using the ODA's free DRX SDK. And of
course, OpenCAD will be able to read and write DWG files as well.
This book is divided into two sections. Section I deals with creating the basic OpenCAD
application, wiring it up with required ODA libraries and adding features to make it a full blown
professional DWG viewer. Section II deals with adding plug-in architecture to OpenCAD and
developing a plug-in that converts it into a DWG editor. We will also see how the plug-in
developed for OpenCAD loads and runs in Bricscad V9 as well.
3
If you are an ODA member then you already have access to the DWGdirect SDK and you can
start building OpenCAD or your own DWGdirect hosted application by following the
instructions in Section I of this book. If you are not an ODA member you can download the
OpenCAD source code and binaries from www.open-cad.com and start developing plug-ins for
it or any other DWGdirect hosted application by following the instructions in Section II of this
book.
The point of the OpenCAD software and this book is not to develop a full blown free CAD
application. Rather my intention is to showcase the various technologies offered by the ODA,
apart from reading and writing DWG files. We will first create OpenCAD as a DWG viewer and
then add features as we proceed.
OpenCAD is not open source for the simple reason that the DWGdirect SDK is not open
source. However, all the C++ source code used to build OpenCAD and its plug-ins are available
free of cost at www.open-cad.com. I have also organized the source code by chapter. So if you
want to skip a chapter or two you can do so.
I have used Microsoft Visual C++ 2005 and DWGdirect version 2.06 to develop OpenCAD and
its plug-in. The ODA offers libraries for other compilers as well and you can very well use
another compiler.
I write software for a living, not books. So I am not quite sure how this book is going to turn out.
I am going to need all the criticism that I can get - good, bad and ugly. Please do give it to me.
If this book ends up helping you in any way or gives you a better understanding of the
technologies offered by the ODA, do let me know. It will make me happy.
4
Section I
In this section we will start with a empty MFC Doc-View application. We will then wire it to the
DWGdirect SDK headers and libraries and give it the ability to read DWG files. We will then
create a drawing view where we will display the contents of a DWG file thereby converting it into
a DWG viewer. Finally, we will add 3D viewing features like orbit, pan and zoom to OpenCAD
and convert it into a professional 3D DWG Viewer.
5
6
Chapter 1: The Basic
OpenCAD Application
Introduction
In this chapter we will create the basic OpenCAD application using Visual Studio’s MFC
Application template. To keep things in order create a folder called OpenCAD in your C: drive. In
this folder create another folder called Bin. We will build the OpenCAD executable and other
plug-in DLL files in this Bin folder. We will also copy related DLLs into this folder so that the
OpenCAD executable can locate them as and when required.
OpenCAD
Click OK. This takes us to the first step of the MFC Application Wizard.
8
Chapter 1: The Basic OpenCAD Application
Click Next.
9
OpenCAD
We will create OpenCAD as a Multiple Document Interface (MDI) application. This means it
will be able to open multiple DWG files at the same time. So select Multiple documents for
Application type. We will also be using MFC’s document/view architecture. So ensure that the
Document/View architecture support box is checked. Click Next.
10
Chapter 1: The Basic OpenCAD Application
11
OpenCAD
OpenCAD will use DWG as its native file format. Enter dwg for File extension. Change other
values according to the Fig 1.5 and click Next.
12
Chapter 1: The Basic OpenCAD Application
13
OpenCAD
I prefer to have the main frame and child frame windows maximized. If you prefer the same
check the respective boxes and click Next.
14
Chapter 1: The Basic OpenCAD Application
I also find MAPI quite useful. It will allow a user to send the active DWG file by email directly
from within OpenCAD. Check the MAPI (Messaging API) box and click Next.
15
OpenCAD
We will use CView as the base class for our drawing view.
Click Finish to generate the project. A Visual C++ solution will be created in the location we
specified.
16
Chapter 1: The Basic OpenCAD Application
17
OpenCAD
Conclusion
Congratulations! We have just built an application which does absolutely nothing useful. In the
next chapter we will change that. We will connect OpenCAD to the DWGdirect SDK and give it
the ability to read DWG files.
18
Chapter 2: OpenCAD as a
DWG Reader
Introduction
In the previous chapter we created the basic OpenCAD application as an empty MFC Doc-View
application. In this chapter we will give OpenCAD the ability to read DWG files. To achieve this
we will need to link OpenCAD to the DWGdirect libraries and include some helper C++ files
that are shipped along with the DWGdirect SDK.
We will be using the DLL version of the DWGdirect libraries. For this it is necessary to add
_TOOLKIT_IN_DLL_ to Preprocessor Definitions.
In the Solution Explorer add a new filter to Source Files and call it ExServices. Here is where we will
place the C++ helper files from the DWGdirect SDK so that they are not mixed with our regular
source files. Insert the files ExHostAppServices.cpp, ExSystemServices.cpp,
ExUndoController.cpp and OdFileBuf.cpp from the Extensions/ExServices folder of the
DWGdirect SDK.
In Project Properties set these newly added files not to use Precompiled Headers.
20
Chapter 2: OpenCAD as a DWG Reader
#include "OdaCommon.h"
#include "OdToolKit.h"
#include "/SDK/OpenDesign/DWGdirect/2.06/Extensions/ExServices/ExHostAppServices.h"
#include "/SDK/OpenDesign/DWGdirect/2.06/Extensions/ExServices/ExSystemServices.h"
BOOL COpenCADApp::InitializeDWGdirect()
{
try
{
::odInitialize(this);
}
catch(OdError& /*err*/)
{
AfxMessageBox(_T("DWGdirect Initialization error"));
return FALSE;
}
catch(...)
{
AfxMessageBox(_T("DWGdirect Initialization error"));
return FALSE;
}
21
OpenCAD
return TRUE;
}
void COpenCADApp::UninitializeDWGdirect()
{
::odUninitialize();
}
We need to call InitializeDWGdirect() when OpenCAD starts up and before we use any of the
DWGdirect functionality. A good place to do that would be in COpenCADApp::InitInstance(). Add
the following code after the call to SetRegistryKey().
if(InitializeDWGdirect() == FALSE)
return FALSE;
We also need to call UnintializeDWGdirect() when OpenCAD shuts down. Override ExitInstance()
and call UninitializeDWGdirect() before the call to CWinApp::ExitInstance().
int COpenCADApp::ExitInstance()
{
// TODO: Add your specialized code here and/or call the base class
UninitializeDWGdirect();
return __super::ExitInstance();
}
Copy the following DLLs from the DWGdirect SDK exe/VC8/Release folder into the Bin folder.
DD_Alloc_2.06_8.dll
DD_Db_2.06_8.dll
DD_DbRoot_2.06_8.dll
DD_Ge_2.06_8.dll
DD_Gi_2.06_8.dll
DD_Root_2.06_8.dll
Build and run OpenCAD. It should build without errors. If it does not build verify that you have
followed the instructions properly. We now have an application that is linked to the DWGdirect
libraries and correctly initializes and unintializes the DWGdirect SDK on application startup and
shutdown respectively.
22
Chapter 2: OpenCAD as a DWG Reader
In OpenCADDoc.h add the following include statement before the COpenCADDoc class
definition.
#include "DbDatabase.h"
Next add a public variable of type OdDbDatabasePtr to the Attributes section of COpenCADDoc.
OdDbDatabasePtr is a smart pointer to a OdDbdatabase object. The DWGdirect SDK extensively
uses smart pointers.
// Attributes
public:
OdDbDatabasePtr m_pDatabase;
Further down the road when we read a DWG file, its contents will be stored in the object
pointed to by the m_pDatabase smart pointer. However, for a new empty document we need to
create a new database with the bare minimum requirements. This is done by the createDatabase()
method of the OdDbhostAppServices helper class. We will call this method in
COpenCADDoc::OnNewDocument() just before the return statement.
BOOL COpenCADDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
Build and run OpenCAD. By default OpenCAD is designed to create an empty document on
startup. Although it may not seem obvious to you at this point in time, OpenCAD initializes the
DWGdirect SDK when it starts up, creates an empty database when a new empty document is
created and uninitializes the DWGdirect SDK when it shuts down.
Finally let us make OpenCAD actually read a DWG file and populate the m_pDatabase member
with some real life data. To do that we override the OnOpenDocument() method of CDocument and
use the readFile() method of the ExHostAppServices class. As a test, we will also let OpenCAD
report the file name and version.
23
OpenCAD
try
{
m_pDatabase = theApp.readFile(lpszPathName, true, false);
}
catch(OdError& /*err*/)
{
MessageBox(AfxGetMainWnd()->m_hWnd, _T("Unable to read file"), _T("File Read
Error"), MB_ICONWARNING);
return FALSE;
}
CString s;
s.Format(_T("Filename: %s\nVersion: %d\n"),
m_pDatabase->getFilename().c_str(),
m_pDatabase->originalFileVersion());
AfxMessageBox(s);
return TRUE;
}
Build and run OpenCAD. Open a DWG file and a dialog box will pop up showing us the file
name and version, which is one of the values of the OdDb::DwgVersion enum.
Conclusion
We have converted OpenCAD into a DWG reader. In order to make it into a DWG viewer we
need to display the contents of the DWG file in the drawing view, which is the subject of the next
chapter.
24
Chapter 3: OpenCAD as a
DWG Viewer
Introduction
In the previous chapter we made OpenCAD read a DWG file. In this chapter we will give
OpenCAD the ability to display the contents of a DWG file in the drawing view.
CString s;
s.Format(_T("Filename: %s\nVersion: %d\n"),
CString(m_pDatabase->getFilename().c_str()),
m_pDatabase->originalFileVersion());
AfxMessageBox(s);
We will now add DWG viewing capability to the COpenCADView class. In OpenCADView.h
include the header files GiContextForDbDatabase.h and Gs.h before the COpenCADView class
declaration. Then proceed to derive COpenCADView from OdGiContextForDbDatabase. Just as we
did for COpenCADApp we will need to prevent the ambiguity for the new and delete operators and
define the addRef() and release() methods. The modified code should look like this
#include "GiContextForDbDatabase.h"
#include "Gs/Gs.h"
The OdGiContextForDbDatabase class defines the methods and properties that are used in the
vectorization of an OdDbDatabase object. Basically, we will simply outsource the entire display of
a DWG file to this class.
// Attributes
public:
OdGsDevicePtr m_pDevice; // Vectorizer device
ODCOLORREF m_clrBackground; // Drawing background color
#include "DbGsManager.h"
#include "RxVariantValue.h"
#include "AbstractViewPE.h"
#include "ColorMapping.h"
COpenCADView::COpenCADView()
{
// TODO: add construction code here
m_clrBackground = RGB(0, 0, 0);
}
Vectorization
Next add the bodies of the five newly added methods. Pay special attention to the comments. We
will use DirectX for vectorization.
26
Chapter 3: OpenCAD as a DWG Viewer
// Create a new OdGsDevice object, and associate with the vectorization GsDevice
m_pDevice = pGs->createDevice();
if(m_pDevice.isNull())
return;
if(!database())
return;
// Return true if and only the current layout is a paper space layout
BOOL bModelSpace = (GetDocument()->m_pDatabase->getTILEMODE() == 0);
if(bZoomExtents)
ViewZoomExtents();
27
OpenCAD
// Make it visible
pView->setViewportBorderVisibility(true);
OdGsViewPtr COpenCADView::GetView()
{
return m_pDevice->viewAt(0);
}
void COpenCADView::ViewZoomExtents()
{
// Get the overall viewport
OdGsViewPtr pView = GetView();
Next override the OnInitialUpdate(), OnSize(), OnPaint() and OnEraseBkgnd() functions and add
code to them.
void COpenCADView::OnInitialUpdate()
{
__super::OnInitialUpdate();
// TODO: Add your specialized code here and/or call the base class
OdGiContextForDbDatabase::setDatabase(GetDocument()->m_pDatabase);
enableGsModel(true);
ResetDevice(true);
}
28
Chapter 3: OpenCAD as a DWG Viewer
void COpenCADView::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call __super::OnPaint() for painting messages
//return __super::OnEraseBkgnd(pDC);
return TRUE;
}
Build and run OpenCAD. You will notice that the new empty document created on application
startup has a black background. This means something has worked. Go ahead and open a DWG
file.
29
OpenCAD
Conclusion
In its present condition OpenCAD reads a DWG file, orients the viewpoint to the last saved
viewpoint and zooms to the extents of the drawing. But a professional viewer needs to do much
more. It needs to be interactive. We need to add the ability to zoom, pan, orbit and navigate
around the drawing. This is what we will do in the next chapter. We will convert OpenCAD into
a 3D viewer.
And by the way, OpenCAD is not just a DWG viewer. It can read DXF files as well. Try opening
a DXF file and be pleased.
30
Chapter 4: OpenCAD as a
3D Viewer
Introduction
As part of making OpenCAD a professional 3D viewer, we will give it the ability to display a
drawing in different display styles, technically called render modes. As we will see, this is as easy
as calling a single method. We will also add 3D navigation capability to OpenCAD.
Render Modes
Open the resource editor and add a sub menu to the View menu drop down containing seven
items as shown in the figure below.
In OpenCADView.h add a public variable to COpenCADView to store the current render mode.
m_iRenderMode = OdGsView::k2DOptimized;
void COpenCADView::OnViewRendermode2dwireframe()
{
// TODO: Add your command handler code here
SetRenderMode(OdGsView::k2DOptimized);
}
void COpenCADView::OnViewRendermode3dwireframe()
{
// TODO: Add your command handler code here
SetRenderMode(OdGsView::kWireframe);
}
32
Chapter 4: OpenCAD as a 3D Viewer
void COpenCADView::OnViewRendermode3dhidden()
{
// TODO: Add your command handler code here
SetRenderMode(OdGsView::kHiddenLine);
}
void COpenCADView::OnViewRendermodeFlatshaded()
{
// TODO: Add your command handler code here
SetRenderMode(OdGsView::kFlatShaded);
}
void COpenCADView::OnViewRendermodeFlatshadedwireframe()
{
// TODO: Add your command handler code here
SetRenderMode(OdGsView::kFlatShadedWithWireframe);
}
void COpenCADView::OnViewRendermodeSmoothshaded()
{
// TODO: Add your command handler code here
SetRenderMode(OdGsView::kGouraudShaded);
}
void COpenCADView::OnViewRendermodeSmoothshadedwireframe()
33
OpenCAD
{
// TODO: Add your command handler code here
SetRenderMode(OdGsView::kGouraudShadedWithWireframe);
}
Build and run OpenCAD. Open a 3D DWG file and play around with the different render
modes. Here are some screenshots of a 3D solid created by a booleanm union of a sphere and a
torus.
34
Chapter 4: OpenCAD as a 3D Viewer
35
OpenCAD
36
Chapter 4: OpenCAD as a 3D Viewer
3D Navigation
Let us add 3D navigation capabilities to OpenCAD. Since we are designing OpenCAD to be a
3D viewer, we will set up the navigation as follows:
• Right mouse button click and drag orbits around the drawing.
• Middle mouse button click and drag pans over the drawing.
• Mouse wheel scroll zooms in and out of the drawing.
This leaves us with the left mouse button which we can use to click and drag a zoom window
rectangle, pick points, select objects and so on. First we will implement the mouse wheel scroll to
zoom in and out of the drawing. In OpenCADView.h declare a public method called Dolly().
Define it in OpenCADView.cpp
37
OpenCAD
pView->dolly(Vector);
}
Dolly(-x, -y);
pView->zoom(zDelta > 0 ? 1.0 / 0.9 : 0.9);
Dolly(x, y);
PostMessage(WM_PAINT);
Build and run OpenCAD. Open a DWG file and scroll the mouse wheel. You will see that by
writing just two small functions we have implemented the zoom feature in OpenCAD.
Next we will implement orbit and pan. Override the following Windows messages:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_MBUTTONDOWN
WM_MBUTTONUP
WM_RBUTTONDOWN
WM_RBUTTONUP
WM_MOUSEMOVE
In OpenCADView.h add three variables of type BOOL to keep track of the mouse buttons and
one varibale of type CPoint to keep track of the mouse position.
38
Chapter 4: OpenCAD as a 3D Viewer
39
OpenCAD
// Screen to Eye
Vector.transformBy((GetView()->screenMatrix() * GetView()-
>projectionMatrix()).inverse());
GetView()->dolly(Vector);
m_MousePosition = point;
PostMessage(WM_PAINT);
}
else if(m_bRightButton == TRUE)
{
GetView()->orbit((m_MousePosition.y - point.y) / 100.0, (m_MousePosition.x -
point.x) / 100.0);
m_MousePosition = point;
PostMessage(WM_PAINT);
}
__super::OnMouseMove(nFlags, point);
}
We will handle the left mouse button later. Build and run OpenCAD. Right click and drag the
mouse to orbit around the drawing. Middle click and drag the mouse to pan over the drawing. It’s
really that simple.
Lets implement Zoom Extents first. In OpenCADView.cpp include the following header file
#include "AbstractViewPE.h"
Add a handler for the Zoom Extents menu item and call ViewZoomExtents().
void COpenCADView::OnViewZoomextents()
{
40
Chapter 4: OpenCAD as a 3D Viewer
To implement zoom window we will need to prompt the user to draw a rectangle in the drawing
view and then zoom into that rectanglular portion on the screen. To make this happen we will
need two functions, one to convert screen coordinates of the points picked to world coordinates
in the drawing and the other to zoom the view using the world coordinates of the rectangular
portion.
WCSPoint.transformBy((pView->screenMatrix() * pView-
>projectionMatrix()).inverse());
WCSPoint.z = 0.0;
WCSPoint.transformBy(OdAbstractViewPEPtr(pView)->eyeToWorld(pView));
return WCSPoint;
}
Point1.transformBy(WorldToEye);
Point2.transformBy(WorldToEye);
OdGeVector3d Vector = Point2 - Point1;
Vector.x = fabs(Vector.x);
Vector.y = fabs(Vector.y);
pView->dolly(NewPosition.asVector());
41
OpenCAD
pView->zoom(odmin(wf, hf));
PostMessage(WM_PAINT);
}
}
Now that we have set things up to do the Zoom Window, let us proceed to ask the user to define
the rectangular portion that we need to zoom into. We will use the left mouse button here. Since
this is an interactive operation, we need to put OpenCAD into a zoom window “mode”. We do
that by adding a boolean member variable to COpenCADView. We will also need to store the
points clicked by the user in order to supply it to ZoomWindow(). Instead of storing them as
separate OdGePoint3d member variables, we will use an OdGePoint3dArray instead. By doing it this
way we will be able to handle operations that need more than two points, say drawing a polyline.
Also at any point in time, by counting the number of items in the array, OpenCAD would know
the stage of the zoom window operation it is currently in.
m_bZoomWindow = FALSE;
void COpenCADView::OnViewZoomwindow()
{
// TODO: Add your command handler code here
m_Points.clear();
m_bZoomWindow = !m_bZoomWindow;
}
We will ask the user to specify the zoom rectangle by the normal left click - drag - release
method. So we need to look for the first point when the left mouse button is pressed and the
second point when the left mouse button is released. Add the code highlighted in bold to
COpenCADView::OnLButtonDown() and COpenCADView::OnLButtonUp()
42
Chapter 4: OpenCAD as a 3D Viewer
if(m_bZoomWindow == TRUE)
{
m_Points.clear(); // Empty point array
m_Points.append(GetWorldCoordinates(point)); // Record first point
}
__super::OnLButtonDown(nFlags, point);
}
if(m_bZoomWindow == TRUE)
{
m_Points.append(GetWorldCoordinates(point)); // Record second point
if(m_Points.length() == 2)// Zoom rectangle has been completely defined
{
ZoomWindow(m_Points[0], m_Points[1]);
m_bZoomWindow = FALSE; // Turn off zoom window mode
m_Points.clear(); // Empty point array
}
}
__super::OnLButtonUp(nFlags, point);
}
Build and run OpenCAD. Try the Zoom Window command. It should work as expected.
However, you will notice something missing. We need to draw the rubber band zoom rectangle
as the user click-drags the zoom window. We will do that next. But before you close OpenCAD
try the Zoom Extents command.
To add the rubber band zoom rectangle feature, we need to keep track of where the mouse was
initially clicked. Add a CPoint member variable to COpenCADView.
In COpenCADView::OnLButtonDown() record the mouse click position. Add the line highlighted
in bold in the following code snippet.
if(m_bZoomWindow == TRUE)
{
43
OpenCAD
__super::OnLButtonDown(nFlags, point);
}
if(m_bLeftButton == TRUE)
{
if(m_bZoomWindow == TRUE)
{
CClientDC dc(this);// Get a client DC which accesses the client area of the view
CRect rcZoom, rcZoomOld;
m_MousePosition = point;
}
}
Build and run OpenCAD. This time around the Zoom Window command will feel much better
with the rubber band zoom window giving you visual feedback indicating the portion of the
drawing view you are going to zoom into.
Conclusion
So there you have it. OpenCAD is now a full blown professional DWG Viewer, complete with
3D navigational features. I am not sure whether you have noticed but we have set up 3D
navigation in very efficient manner. Usually a CAD application makes the user click a menu item
or button on a toolbar to get into orbit mode so that the user can then click and drag around the
44
Chapter 4: OpenCAD as a 3D Viewer
model. To pan he has to quit the orbit mode and click another menu item or toolbar button to
get into pan mode. We have set up OpenCAD in such a way that these frequent change of
navigation modes is eliminated. You can orbit using the right mouse button, pan using the
middle mouse button and zoom using the mouse wheel. The navigation modes depend entirely
upon the mouse buttons and change automatically as you mouse around the drawing view. You
need the menu only for Zoom Window and Zoom Extents. This makes OpenCAD a very
powerful and efficient CAD viewer.
45
OpenCAD
46
Section II
We started Section I of this book by creating OpenCAD as a skeleton MFC MDI application. We
then wired the DWGdirect SDK to it and gave it the ability to read DWG files. Next we
converted OpenCAD into a DWG viewer and finally added 3D navigational capability to it.
In Section II we will take OpenCAD to the next level. We will add plug-in architecture to it and
learn how to develop DRX plug-ins containing custom commands that can be called from within
OpenCAD. We will also give OpenCAD a command prompt, similar to AutoCAD, IntelliCAD,
Rhinoceros, etc., where a user can type in and run a custom command defined in an external
DRX plug-in. This is powerful stuff. As you will see, things are going to get quite interesting
from here on.
47
48
Chapter 5: Plug-in
Architecture
Introduction
In this chapter we will add plug-in architecture to OpenCAD. This will transform OpenCAD
into an extremely powerful CAD application. Anyone with C++ knowledge will be able to use
the free DRX SDK provided by the ODA to develop DRX plug-ins that will work inside of
OpenCAD and extend its capabilities.
#include "/SDK/OpenDesign/DWGdirect/2.06/Extensions/ExServices/ExDbCommandContext.h"
If you try to build the project now you will get a “cannot instantiate abstract class” error. This
because we need to define two functions that were declared as pure virtual in OdEdBaseIO. The
reasons for this will become obvious a little later. For now lets just add the functions.
// Operations
public:
OdString getString(const OdString& prompt, int options, OdEdStringTracker*
pTracker);
void putString(const OdString& string);
In OpenCADDoc.cpp add their definitions. We will add code to these functions later.
OpenCAD
At this point, the project will build successfully. Now let us add an important member variable to
COpenCADDoc - a pointer to a command context object. The OdDbCommandContext class defines
the interface for I/O and database access for custom commands during their execution. Stuff
like asking the user for input at the command prompt or letting the user select a point or object
in the drawing window. It also needs to access the database of the document to read and write
information to it. Add a command context pointer to COpenCADDoc.
OdDbCommandContextPtr m_pCommandContext;
OdEdBaseIO* GetIO();
OdDbCommandContextPtr GetCommandContext();
BOOL ExecuteCommand(const OdString& strCommand, BOOL bEcho = TRUE);
#include "Ed/EdCommandStack.h"
OdEdBaseIO* COpenCADDoc::GetIO()
{
return this;
}
OdDbCommandContextPtr COpenCADDoc::GetCommandContext()
{
if(m_pCommandContext.isNull())
m_pCommandContext = ExDbCommandContext::createObject(GetIO(), m_pDatabase);
return m_pCommandContext;
}
50
Chapter 5: Plug-in Architecture
We have just set up COpenCADDoc to handle a custom command. All we now need to do is call
COpenCADDoc::ExecuteCommand() to execute a custom command. As you can see, the method
uses the odedRegCmds() function to get the global command stack and then calls executeComand() to
execute the command. The global command stack is basically a set of custom commands
organized in groups, usually one group for each DRX module. A DRX module is quite simply a
DLL with a .drx file extension. We will learn how to build these modules in the next chapter.
void LoadDRXModules();
Define it in OpenCAD.cpp.
void COpenCADApp::LoadDRXModules()
{
try
{
::odrxDynamicLinker()->loadModule(L"OCKernel.drx", FALSE);
}
catch(OdError& e)
{
51
OpenCAD
AfxMessageBox((LPCTSTR)e.description());
}
}
if(InitializeDWGdirect() == FALSE)
return FALSE;
LoadDRXModules();
If you build and run OpenCAD you will get an error message on startup.
The error message is expected since we have asked OpenCAD to load a DRX module which we
have not yet built. But there is something very important that I would like to highlight here. Look
closely at COpenCADApp::LoadDRXModules() and the error message above. We asked OpenCAD
to load a DRX module called OCKernel.drx but it has reported that it cannot find a module called
OCKernel_2.06_8.drx. This is because of a peculiar versioning system that the ODA uses. The
2.06 stands for the version of the DWGdiect/DRX SDK and the 8 stands for compiler used. In
our case 8 stands for VC 8.0 (Visual C++ 2005).
One might question the necessity of such a naming and versioning system, especially when
Windows has a wonderful versioning system already in place. I took up this matter with the
ODA. This is what Neil Peterson, the CTO of the ODA, had to say:
“The Windows DLL versioning is not sufficient to prevent problems in client applications. For example, consider
a client application which uses plug-ins based on two different versions of DWGdirect. If the two different versions
of DWGdirect use identical DLL names then the plug-in that is loaded second will crash as it tries to use an
incompatible DWGdirect DLL with the same name (which was loaded as part of the first plug-in). We need to
change file names each time binary compatibility is broken to avoid this type of problem.”
Neil has a point. This is a common pitfall when developing DRX plug-ins. So be sure to append
the version of DWGdirect/DRX and the compiler to the name of your DRX file, or a
DWGdirect hosted application may not load it.
52
Chapter 5: Plug-in Architecture
Conclusion
Thats it! We have added plug-in architecture to OpenCAD. It was that simple. In the next chapter
we will create our first DRX plug-in, the OCKernel.drx module mentioned above. You may find
things quite fuzzy at the moment. By the time we reach the end of this section, you will get a far
greater understanding of what DWGdirect hosted applications and DRX modules are and how
they interact with each other and the user.
53
OpenCAD
54
Chapter 6: Creating a DRX
Plug-in
Introduction
At the outset, I would like to reiterate that the DWGdirect SDK, which is required to build
DWGdirect hosted applications like OpenCAD, is available to ODA members only. However,
the DRX SDK, which is required to build DRX plug-ins for DWGdirect hosted applications, is
available free of cost and can be downloaded from the ODA web site (www.opendesign.com). So
if you are not an ODA member and would like to develop DRX plug-ins for OpenCAD or any
other DWGdirect hosted application, this chapter is a good starting point. Thoughout this
chapter and all other chapters which deal with DRX plug-ins, we will be using the free DRX
SDK only. If you are an ODA member you can go ahead and point your compiler to use files
from the DWGdirect SDK because the DRX SDK isa mere subset of the DWGdirect SDK.
Start another instance of Visual Studio 2005 and create a new project. This time we will use the
MFC DLL template. Name the project OCKernel and specify C:\OpenCAD\DRX as the location.
We will try and keep the main OpenCAD MFC application project as clean and simple as we
possibly can and make use of OpenCAD’s plug-in architecture to add features to it by means of
plug-ins. A first step in that direction is this OCKernel plug-in that we are about to create. OC
stands for OpenCAD and Kernel signifies that the core functionality of the application will be
programmed here.
OpenCAD
Click OK. This will take you to the first step of the MFC DLL Wizard.
56
Chapter 6: Creating a DRX Plug-in
Click Next. Or you can click Finish. We will be accepting the default options anyways.
57
OpenCAD
As mentioned before, a DRX module is actually a regular Windows DLL with a .drx file
extension. Click Finish to create the project.
58
Chapter 6: Creating a DRX Plug-in
Similarly, add the DRX SDK’s Include folder to Additional Include Directories.
Finally, add _TOOLKIT_IN_DLL_ to Preprocessor Definitions since we will be using the DLL
version of the DRX SDK.
For each custom command in the DRX module we derive a class from OdEdCommand, the base
class for all custom commands and override four methods, one of them being execute(), which is
called the DWGdirect hosted application to execute the custom command.
#include "OdaCommon.h"
#include "RxDynamicModule.h"
#include "Ed/EdCommandStack.h"
#include "StaticRxObject.h"
#include "DbCommandContext.h"
59
OpenCAD
public:
void initApp() { odedRegCmds()->addCommand(&m_cmdMyCommand); }
void uninitApp() { odedRegCmds()->removeCmd(DD_T("MyGroup"), DD_T("MyCommand")); }
};
ODRX_DEFINE_DYNAMIC_MODULE(CMyModule);
Here we have module class called CMyModule and custom command class called CMyCommand.
The command name is MyComand and belongs to a command group called MyGroup. When
executed, MyCommand simply displays a “Hello World” message. This is as simple as it can get.
We will get to more complicated things later.
Build the project. OCKernel_2.06_8.drx should be created in the Bin folder. To remove the “/
OUT:OCKernel.dll directive in .EXP differs from output filename” warning, you can insert a comment
(semi-colon) before the word LIBRARY in OCKernel.def.
;LIBRARY "OCKernel"
BOOL COpenCADDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
60
Chapter 6: Creating a DRX Plug-in
Congratulations!! The OpenCAD executable has just executed a custom command from an
external DRX plug-in DLL. All this using just 22 lines of C++ code. It’s really that simple!
Move the include statements to stdafx.h since we will need to include the files in all classes. Also
add a define for the group name for all the commands in this module since we are going to need
the group name in all our classes. I have the habit of prefixing names with the name of the
module. This is a good idea and I strongly encourage you to do this. Personalizing class names
will prevent name conflicts with other DRX modules. For example, if someone else writes a
DRX module that also has a class with a same name as yours, then OpenCAD will not load the
plug-in that it encounters second.
#include "OdaCommon.h"
#include "RxDynamicModule.h"
#include "Ed/EdCommandStack.h"
#include "StaticRxObject.h"
#include "DbCommandContext.h"
61
OpenCAD
Split the CMyCommand class into a header and source file and rename it to COCKernelImport.
OCKernelImport.h
#ifndef _OCKERNELIMPORT_H_
#define _OCKERNELIMPORT_H_
#endif // _OCKERNELIMPORT_H_
OCKernelImport.cpp
#include "StdAfx.h"
#include "OCKernelImport.h"
Similarly split the CMyModule class into a header and source file and rename it to
COCKernelModule.
OCKernelModule.h
#ifndef _OCKERNELMODULE_H_
#define _OCKERNELMODULE_H_
#include "OCKernelImport.h"
public:
void initApp();
void uninitApp();
};
#endif // _OCKERNELMODULE_H_
62
Chapter 6: Creating a DRX Plug-in
OCKernelModule.cpp
#include "StdAfx.h"
#include "OCKernelModule.h"
ODRX_DEFINE_DYNAMIC_MODULE(COCKernelModule);
void COCKernelModule::initApp()
{
OdEdCommandStackPtr pCommands = odedRegCmds();
pCommands->addCommand(&m_cmdImport);
}
void COCKernelModule::uninitApp()
{
OdEdCommandStackPtr pCommands = odedRegCmds();
pCommands->removeCmd(OCKERNEL_GROUPNAME, DD_T("Import"));
}
As we add new custom command classes to the OCKernel project, we will add new header and
source files and make necessary changes to the COCKernelModule class to add and remove the
commands from the command stack.
Build the OCKernel project. Switch to the OpenCAD project and remove the line of code last
added to COpenCADDoc::OnNewDocument(). Add a new menu item called Import in File menu
popup and add an event handler for the COpenCADDoc class.
void COpenCADDoc::OnFileImport()
{
// TODO: Add your command handler code here
ExecuteCommand(_T("Import"));
}
It is a good idea to specify the exact file name of the DRX module to load. So rename to
OCKernel.drx to OCKernel_2.06_8.drx in COpenCADApp::LoadDRXModules()
::odrxDynamicLinker()->loadModule(L"OCKernel_2.06_8.drx", FALSE);
Build and run OpenCAD. Click Import from the File menu. You should see the following
message.
63
OpenCAD
So as you can see, we have only shaken up the original 22 lines of code to give the same result - a
message box.
Conclusion
We have accomplished what we had set out to do at the start of this chapter - to build a DRX
plug-in and make it run from within OpenCAD. In the next chapter, we will take OpenCAD to a
higher level. In fact, we will put it into the league of AutoCAD, IntelliCAD, Rhinoceros and
similar CAD applications that have the all powerful command prompt. Yes, we will give our very
own OpenCAD a command prompt. And like just about everything else in this book, you will
see exactly how easy it is.
64
Chapter 7: The Command
Prompt
Introduction
I don’t know about you, but I have been fascinated with the command prompt ever since I first
saw it in AutoCAD. I have developed a few CAD programs during the past decade or so, mainly
small utilities. A few years ago I tried to add a command prompt to one of my programs. It came
out quite nice initially but as I started adding more complicated commands to the program things
started getting convoluted and eventually I had to it give up to attend to more pressing issues.
Today thanks for the wonderful DWGdirect SDK from the ODA, adding a command prompt to
a DWGdirect hosted application is a piece of cake, as you will now see.
class COpenCADView;
class CChildFrame;
Add two methods to get the view and the child frame window.
COpenCADView* GetView();
CChildFrame* GetChildFrame();
#include "OpenCADView.h"
#include "ChildFrm.h"
COpenCADView* COpenCADDoc::GetView()
{
POSITION pos = GetFirstViewPosition();
OpenCAD
return((COpenCADView*)GetNextView(pos));
}
CChildFrame* COpenCADDoc::GetChildFrame()
{
return (CChildFrame*)(GetView()->GetParentFrame());
}
Specify ID_EDT_PROMPT as the ID for the top edit control. Make it read only, multiline and
display the vertical scroll bar. Specify ID_EDT_COMMAND as the ID of the bottom edit
control. We will be repositioning and resizing the entire dialog and its controls at run time. So
you need not worry about the positions and sizes for now. The only size that we will be using for
our command prompt is the height of the dialog box. You can adjust it later on.
66
Chapter 7: The Command Prompt
Using the Class Wizard add a new class called CCommandPrompt for the dialog using CDialog as the
base class.
pEdit = (CEdit*)GetDlgItem(IDC_EDT_INPUT);
::GetWindowRect(pEdit->GetSafeHwnd(), &rcInput);
::MoveWindow(pEdit->GetSafeHwnd(), 2, rcWindow.Height() - rcInput.Height() - 2,
iWidth, rcInput.Height(), TRUE);
pEdit = (CEdit*)GetDlgItem(IDC_EDT_PROMPT);
::GetWindowRect(pEdit->GetSafeHwnd(), &rcPrompt);
67
OpenCAD
CCommandPrompt m_wndCommandPrompt;
m_wndCommandPrompt.EnableDocking(CBRS_ALIGN_BOTTOM);
EnableDocking(CBRS_ALIGN_BOTTOM);
DockControlBar(&m_wndCommandPrompt, AFX_IDW_DOCKBAR_BOTTOM);
return 0;
}
Lastly override OnSize() and add code to resize the command prompt.
68
Chapter 7: The Command Prompt
Build and run OpenCAD. We now have a neat little command prompt at the bottom of every
MDI child window as can be seen in the figure below.
69
OpenCAD
Now I am not a MFC GUI guru and I am quite sure there is a more elegant way to do this, but
for lack of a better solution we will simply trap the WM_LBUTTONDOWN and
WM_LBUTTONDBLCLK messages for CCommandPrompt and do nothing.
// CDialogBar::OnLButtonDown(nFlags, point);
}
// CDialogBar::OnLButtonDblClk(nFlags, point);
}
So now the command prompt is conjoined with the child frame and will live and die with it.
70
Chapter 7: The Command Prompt
Build OCKernel. We have now set up the Import command to print a message to the prompt area
of OpenCAD’s command prompt. Now let us write code to do the actual printing. This involves
adding a method to CCommandPrompt to print to the prompt area and then wiring the command
prompt to the document.
71
OpenCAD
pwndPrompt->GetWindowTextW(strPrompt);
if(bNewLine == TRUE)
strPrompt += _T("\r\n");
strPrompt += strText;
return TRUE;
}
Build and run OpenCAD. Click Import from the File menu. You should see two messages in the
prompt area as shown in the figure below.
The first message was due to the call to putString() in COpenCADDoc::ExecuteCommand(). The
second one was due to the Import command of the OCKernal plug-in. So we have successfully
been able to get the plug-in to send a message to OpenCAD. But for bi-directional interaction
between OpenCAD and OCKernel, we need to be able to get input from the OpenCAD
executable and send it to the plug-in. We do that using the getString() function of COpenCADDoc
which will call a GetString() function of CCommandPrompt.
72
Chapter 7: The Command Prompt
return OdString(strText);
}
In CommandPrompt.h add an enum for various command modes (for now, just two) and a
boolean member variables to tell us if the user cancelled the operation..
public:
enum CommandMode
{
eCommandModeNone = 0,
eCommandModeGetString = 1,
};
CommandMode m_iCommandMode;
BOOL m_bCancel;
m_iCommandMode = eCommandModeNone;
73
OpenCAD
{
PutString(_T(" *Cancel*"), FALSE);
return FALSE;
}
// Once again get the control. This is important because in the while
// loop above the command prompt may have been destroyed
pwndCommand = (CEdit*)GetDlgItem(IDC_EDT_COMMAND);
if(pwndCommand == NULL)
return FALSE;
return TRUE;
}
I will spend some time explaining what we are trying to achieve here. We intend to keep control
in the GetString() function till the user presses Enter or Esc and then pass on the text he entered
over to the plug-in. We do that by means of an infinite while loop that calls
CWinApp::PumpMessage(). The m_iCommandMode member variable keeps track whehter the
program is still in the GetString mode and m_bCancel tell us whether he pressed Esc. Before the
loop begins we set m_iCommandMode to eCommandModeGetString in order to get into the GetString
mode and reset the m_bCancel flag to FALSE. We now need to track the messages being pumped
and exit the while loop when the user presses Enter or Esc. We do that by overriding
OnCommand().
We are not quite done yet. But let try this out. Before we do so switch to the OCKernel project
add the lines highlighted in bold to COCKernelImport::execute()
74
Chapter 7: The Command Prompt
Here the plug-in asks the user to enter the name of the file to be imported and then prints it back
to the command prompt. Let us see if this works. Build OCKernel and then OpenCAD. Run
OpenCAD and click Import from the File menu. Type a file name at the command prompt and
press Enter. You should see something like this.
Great! So it did work. Lets put things into perspective to understand what exactly transpired. The
user clicked a menu item in OpenCAD. This called COpenDoc::ExecuteCommand() which called
COCKernelImport::execute() in the DRX plug-in. The plug-in then used the comand context to get a
OdDbUserIO pointer and called its putString() method, which ended up finding its way to the
OpenCAD executable and called the putString() method of COpenCADDoc. This in turn called
CCommandPrompt::PutString() which finally printed out the message (which was set in the DRX
plug-in) to the prompt area of the command prompt in OpenCAD. Whew! But wait, we have just
reached the half way mark. Control was then returned to the plug-in which called
OdDbUserIO::getString(), which in turn called COpenCADDoc::getString(), which finally called
CCommandPrompt::GetString(). This started an infinite while loop and control was stuck in it till the
user pressed Enter. On exiting CCommandPrompt::GetString(), control was passed to
COpenCADDoc::getString() which returned the text string (entered by the user in the OpenCAD
command prompt) to the plug-in where it was printed to the command prompt using putString().
So it looks like we have successfully achieved two way comunication between the OpenCAD and
OCKernel. Actually, three way if you take the user into account.
75
OpenCAD
class CChildFrame;
CChildFrame* m_pChildFrame;
In ChildFrm.cpp add the following line after the call to creating the comand prompt.
m_wndCommandPrompt.m_pChildFrame = this;
Now the command prompt has access to its child frame. Let us add another method to access
the document. In CommandPrompt.h include OpenCADDoc.h
#include "OpenCADDoc.h"
COpenCADDoc* GetDocument();
#include "ChildFrm.h"
COpenCADDoc* CCommandPrompt::GetDocument()
{
return (COpenCADDoc*)m_pChildFrame->GetActiveDocument();
}
Now that we can access the document from the command prompt, we will add code to initiate a
command when the user presses Enter at the command prompt. Of course, we need to initiate a
command only if the command prompt is in the eCommandModeNone mode. Add the lines
highlighted in bold to CComandPrompt::OnCommand().
76
Chapter 7: The Command Prompt
Build and run OpenCAD. Type Import at the command prompt and press Enter. You should see
something quite similar to Fig 7.6.
Repeating commands
Let us add another useful feature that we see in programs that have a command prompt. If the
user presses the Enter key without first entering a command, the program repeats the last run
command. For this we will need to keep track of the last command in COpenCADDoc. In
OpenCADDoc.h add a member variable to store the last command.
OdString m_strLastCommand;
m_strLastCommand = _T("");
77
OpenCAD
try
{
OdEdCommandStackPtr pCommands = ::odedRegCmds();
OdString sCommand = strCommand.spanExcluding(L" \t\r\n");
if(bEcho)
putString(_T("Command: ") + sCommand);
Build and run OpenCAD. Run the Import command. After the command ends simply press Enter.
The Import command should start again.
78
Chapter 7: The Command Prompt
waiting for user input from a window that no longer exists. Close OpenCAD. It will crash for a
multitude of reasons.
The infinite while loop in CCommand PromptGetString() allows OpenCAD to wait for user input,
but it also responsible for events like the crash described above. We need to take care of such
events. We should not allow a document window to be closed if a command is active. In
OpenCADDoc.h add a member variable to keep track whether a command is active or not.
BOOL m_bCommandActive;
m_bCommandActive = FALSE;
try
{
OdEdCommandStackPtr pCommands = ::odedRegCmds();
OdString sCommand = strCommand.spanExcluding(L" \t\r\n");
if(bEcho)
putString(_T("Command: ") + sCommand);
79
OpenCAD
m_bCommandActive = TRUE;
pCommands->executeCommand(sCommand, pCmdCtx); // Execute the command
m_strLastCommand = sCommand; // Save as last command
GetView()->m_pDevice->invalidate();// Refresh the drawing view
GetView()->PostMessage(WM_PAINT);
m_bCommandActive = FALSE;
}
}
catch(const OdError& e)
{
m_bCommandActive = FALSE;
putString(_T("Error: ") + e.description());
return FALSE;
}
return TRUE;
}
Build and run OpenCAD. Run the Import command. While OpenCAD is waiting for you to enter
a file name, click Import form the File menu to start another Import command. You will see this
warning message and the second Import command will abort.
However, after we dismiss the warning message we can still close the document window and
cause a crash on application exit. To prevent this we override CDocument::CanCloseFrame().
return __super::CanCloseFrame(pFrame);
}
Now if we try to close the document window during the Import command we will see the
following warning message and the window will not close.
80
Chapter 7: The Command Prompt
But we can close OpenCAD and still cause a crash. To prevent this from happenning, before
shutting down OpenCAD, we will need to iterate through all open documents and check if they
have an active command. Override CMDIFrameWnd::OnClose().
#include "OpenCADDoc.h"
void CMainFrame::OnClose()
{
// TODO: Add your message handler code here and/or call default
POSITION DocTemplatePos = AfxGetApp()->GetFirstDocTemplatePosition();
while(DocTemplatePos != NULL)
{
// Get a document template
CDocTemplate *pDocTemplate = AfxGetApp()->GetNextDocTemplate(DocTemplatePos);
CMDIFrameWnd::OnClose();
}
Build and run OpenCAD. With the Import command active, try and close OpenCAD. You will
see the following warning message and the program will not close.
81
OpenCAD
Before we wrap this up, this is more more thing that we need to fix. Run OpenCAD. It will start
with the new empty document titled Drawing1. Run the Import command but do not enter a file
name yet. Click New from the File menu to create another empty document. It will be titled
Drawing2. Click Tile from the Window menu to see both windows clearly. Type Import at the
command prompt of Drawing2. Note that Drawing1 is still waiting for a file name. So now we
have two documents that are waiting for user input. Type C:\Drawing1.sat at the Drawing1
command prompt and press Enter. You should have seen the message “You entered
C:\Drawing1.sat” at the command prompt, but nothing happens. Now type C:\Drawing2.sat at the
Drawing2 command prompt and press Enter. Take a closer look at the command prompts of both
documents.
82
Chapter 7: The Command Prompt
At the Drawing2 command prompt you will see the message “You entered C:\Drawing2.sat”. At the
Drawing1 command prompt you can now see the message that should have appeared when you
pressed Enter earlier.
So why did this happen? Take a look at CCommandPrompt::GetString(). When it was called the first
time for Drawing1, control was stuck in the infinite while loop till we pressed Enter. But instead of
entering a file name and pressing Enter, we ran the Import command in Drawing2 which called
another instance of GetString() and entered into another infinite while loop. So even though you
went back to Drawing1 and pressed Enter, program control was still stuck in the second while loop
waiting for Enter to be pressed in Drawing2. That is why nothing happenned in Drawing1. And
when you returned to Drawing2 and pressed Enter, program control broke free from the second
while loop and found its way to the first while loop, where the condition to break out from the
loop was already met and exited the first loop as well.
So this means that the way this is set up, OpenCAD cannot concurrently run two instances of
CCommandPrompt::GetString(). Technically it can, but in order to get immediate feedback the end
user would have to enter text in exactly the reverse order as the calls to
CCommandPrompt::GetString(). One way of fixing this problem is to rearchitecture just about
everything that we have done so far and set things up in a way that GetString() is called in a new
thread, something which is beyond the scope of this book and not critically important to
OpenCAD. So we will simply prevent a situation wherein a user will be asked for input
concurrently in two documents.
BOOL AllowInteraction();
In OpenCAD.cpp add the function body. Basically, we will iterate through all open documents
and check if any of the command modes are not eCommandModeNone.
BOOL COpenCADApp::AllowInteraction()
{
POSITION DocTemplatePos = AfxGetApp()->GetFirstDocTemplatePosition();
while(DocTemplatePos != NULL)
{
// Get a document template
CDocTemplate *pDocTemplate = AfxGetApp()->GetNextDocTemplate(DocTemplatePos);
83
OpenCAD
return TRUE;
}
CString strText;
GetChildFrame()->m_wndCommandPrompt.GetString((LPCTSTR)prompt, strText);
return OdString(strText);
}
Build and run OpenCAD. Try running the Import command in two documents as was previously
done. You should see the following warning message and the second Import command will abort.
I think we have taken care of the most common unexpected situations. That was quite an
oxymoron - most common unexpected situations. We will revisit this later if required.
Switch to the OCKernel project. In OCKernelImport.cpp add three header files and edit
COCKernelImport::execute() as highlighted in bold below.
84
Chapter 7: The Command Prompt
#include "DbEntity.h"
#include "DbBody.h"
#include "DbBlockTableRecord.h"
CFileDialog Dlg(
TRUE,
_T("sat"),
NULL,
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
_T("ACIS SAT Files (*.sat)|*.sat||"));
if(Dlg.DoModal() == IDCANCEL)
return;
OdString strFileName(Dlg.GetPathName());
OdDbEntityPtrArray Entities;
if(OdDbBody::acisIn(strFileName, Entities) != eOk)
{
pUserIO->putString(_T("Error importing '") + strFileName + "'");
return;
}
Build OCKernel. Start OpenCAD and run the Import command. You will be prompted to select
an ACIS SAT file. Note that the Open dialog box is called from the DRX plug-in and not from
OpenCAD.
85
OpenCAD
After you select a SAT file, the DRX plug-in will read it and add its contents to the database of
the current document in OpenCAD. You should see something like the figure below. You may
need to Zoom Extents to see the objects.
86
Chapter 7: The Command Prompt
Congratulations!! We have successfully written our first DRX file import plug-in for OpenCAD.
Conclusion
In this chapter we added a command prompt to OpenCAD. In the next chapter we will do
something a little bolder. We will write code to add a line object to the drawing, and that too
from the OCKernel plug-in. We will add a new custom command to OCKernel called Line that
will ask the user to pick two points in the drawing view of an OpenCAD document and send the
data back to the plug-in. The plug-in will then create a line object and add it to the database of
the document and update the drawing view to display the line on the screen.
Yes, we are going to convert OpenCAD from a DWG viewer to a DWG editor!
87
OpenCAD
88
Chapter 8: OpenCAD as a
DWG Editor
Introduction
In this chapter we will add a custom command to OCKernel to draw a line to the active drawing
in OpenCAD. For that we will need the user to input the start and end points of the line. The
user can do that by either entering the coordinates of the points at the command prompt or
picking a point in the drawing window. We have already added functionality to COpenCADDoc
and CCommandPrompt to get a string from the user. We will now add similar functionality to get a
point.
enum CommandMode
{
eCommandModeNone= 0,
eCommandModeGetString= 1,
eCommandModeGetPoint= 2
};
Repeat:
if(PutString(strPrompt) == FALSE)// Show prompt
OpenCAD
return FALSE;
// Once again get the control. This is important because in the while
// loop above the command prompt may have been destroyed
pwndCommand = (CEdit*)GetDlgItem(IDC_EDT_COMMAND);
if(pwndCommand == NULL)
return FALSE;
CString strText;
pwndCommand->GetWindowTextW(strText);// Store entered text
if(GetPointFromString(strText, Point) == FALSE)
{
PutString(strText, FALSE);// Append entered text to prompt
PutString(_T("Invalid point"));
goto Repeat;
}
return TRUE;
}
double x, y, z = 0.0;
if(_stscanf(strLine, _T("%lf %lf %lf"), &x, &y, &z) < 2)
90
Chapter 8: OpenCAD as a DWG Editor
return FALSE;
Point.set(x, y, z);
return TRUE;
}
Now that we have set up CCommandPrompt to accept a point from the user, let us wire in
COpenCADDoc as well. In OpenCADDoc.h add a method to get a point.
CString strText;
GetChildFrame()->m_wndCommandPrompt.GetPoint((LPCTSTR)prompt, Point);
return Point;
}
Add a event handler for the document class and call ExecuteCommand().
void COpenCADDoc::OnDrawLine()
{
// TODO: Add your command handler code here
ExecuteCommand(_T("Line"));
}
91
OpenCAD
Build OpenCAD. Don’t run it yet. We have set up OpenCAD to run a command called Line. We
now need to add the command to OCKernel. Switch to the OCKernel project and create a
header file called OCKernelLine.h and a source file called OCKernelLine.cpp. Add code similar
to he we previously did for the Import command.
OCKernelLine.h
#ifndef _OCKERNELLINE_H_
#define _OCKERNELLINE_H_
#endif // _OCKERNELLINE_H_
OCKernelLine.cpp
#include "StdAfx.h"
#include "OCKernelLine.h"
#include "DbLine.h"
#include "DbBlockTableRecord.h"
92
Chapter 8: OpenCAD as a DWG Editor
We also need to add an entry for the Line command to COKKernelModule. In OCKernelModule.h
include OCKernelLine.h and add a member to COCKernelModule for the Line command. Add the
code highlighted in bold below.
#include "OCKernelLine.h"
public:
void initApp();
void uninitApp();
};
In OCKernelModule.cpp add code to initApp() and uninitApp() to add and remove the Line
command from the command stack. Add the code highlighted in bold below.
void COCKernelModule::initApp()
{
OdEdCommandStackPtr pCommands = odedRegCmds();
pCommands->addCommand(&m_cmdImport);
pCommands->addCommand(&m_cmdLine);
}
void COCKernelModule::uninitApp()
{
OdEdCommandStackPtr pCommands = odedRegCmds();
pCommands->removeCmd(OCKERNEL_GROUPNAME, DD_T("Import"));
pCommands->removeCmd(OCKERNEL_GROUPNAME, DD_T("Line"));
}
Build OCKernel. Run OpenCAD. Invoke the Line command and when prompted for the start
and end point, enter the x, y and x coordinates at the command prompt. Zoom Extents to see
the line that was just created.
93
OpenCAD
Congratulations!! We have just converted OpenCAD into a DWG editor. A rudimentary one, but
an editor nonetheless.
94
Chapter 8: OpenCAD as a DWG Editor
pwndCommand->SetWindowText(strText);
if(bEnter)
PostMessage(WM_COMMAND, IDOK);
return TRUE;
}
The best place to get the point picked by the user would be COpenCADView::OnLButtonDown().
For that we need to access the command prompt from the drawing view. In OpenCADView.h
include ChildFrm.h and a method to COpenCADView to retrieve the chile frame window.
#include "ChildFrm.h"
if(m_bZoomWindow == TRUE)
{
m_Points.clear(); // Empty point array
m_Points.append(GetWorldCoordinates(point)); // Record first point
}
else if(GetChildFrame()->m_wndCommandPrompt.m_iCommandMode ==
CCommandPrompt::eCommandModeGetPoint)
{
OdGePoint3d Point = GetWorldCoordinates(point);
CString strCoordinates;
strCoordinates.Format(_T("%f,%f,%f"), Point.x, Point.y, Point.z);
GetChildFrame()->m_wndCommandPrompt.SetCommand(strCoordinates, TRUE);
}
__super::OnLButtonDown(nFlags, point);
}
Build and run OpenCAD. Run the Line command and pick two points in the drawing window.
Observe what happens at the command prompt as you pick the points.
95
OpenCAD
While the Line command works the way we expect it to, anyone with even a litle experience in
using CAD software will notice that there is something missing here - the rubber band like visual
feedback when the user moves the mouse to select the second point. The DWGdirect SDK
offers a wonderful way to provide precisely such visual feedback. You will now see that by using
minimal code we can have the same rubber band visual feedback as any other CAD application.
96
Chapter 8: OpenCAD as a DWG Editor
m_pTracker = NULL;
m_pTracker = pTracker;
if(m_pTracker)
m_pTracker->addDrawables(GetView());
}
And finally add the following else if block towards the end of COpenCADView::OnMouseMove().
else if(GetChildFrame()->m_wndCommandPrompt.m_iCommandMode ==
CCommandPrompt::eCommandModeGetPoint)
{
if(m_pTracker)
{
static_cast<OdEdPointTracker*>(m_pTracker)-
>setValue(GetWorldCoordinates(point));
if(!GetView()->isValid())
PostMessage(WM_PAINT);
}
}
Thats it! Build OpenCAD, but don’t run it just yet. Switch to the OCKernel project and add a
parameter to the second call to getPoint() as highlighted in bold below
Build OCKernel. Start OpenCAD and run the Line command. Click two points and you will see
the rubber band visual feedback. Its really that simple.
While we are here I want to show you something really nice. Open a 3D DWG file in OpenCAD
and run the Line command. Pick the first point, but do not pick the second point yet. Now use
the 3D navigation features of OpenCAD - orbit (eight mouse button), pan (middle mouse
button) and zoom(mouse wheel) to navigate while the Line command is still active. This is due to
the way we have set things up in OpenCAD.
97
OpenCAD
Build both projects and run OpenCAD. Run the Line command. Pick the start point of the line
and press Esc. The command gracefully exits. However you will still see the rubber band line in
the drawing window. In fact, this rubber band line was always present, even when we actually
created a line. Just that we did not notice it because it coincided with the line we had just created.
We need to remove this rubber band line. In fact, we always need to refresh the drawing window
after a command completes or is cancelled. Let us add a function to COpenCADView to do that.
void Refresh();
void COpenCADView::Refresh()
98
Chapter 8: OpenCAD as a DWG Editor
{
// Clean out the tracker
if(m_pTracker)
m_pTracker->removeDrawables(GetView());
m_pTracker = NULL;
try
{
OdEdCommandStackPtr pCommands = ::odedRegCmds();
OdString sCommand = strCommand.spanExcluding(L" \t\r\n");
if(bEcho)
putString(_T("Command: ") + sCommand);
99
OpenCAD
}
}
catch(const OdError& e)
{
m_bCommandActive = FALSE;
putString(_T("Error: ") + e.description());
GetView()->Refresh();
return FALSE;
}
return TRUE;
}
Build and run OpenCAD. Run the Line command, pick the start point and press Esc. The rubber
band line no longer exists in the drawing view.
pLine->setStartPoint(Start);
pLine->setEndPoint(End);
pBlockTableRecord->appendOdDbEntity(pLine);
100
Chapter 8: OpenCAD as a DWG Editor
Start = End;
}
}
catch(OdEdCancel& /*e*/)
{
return;
}
}
Build OCKernel and run OpenCAD. Run the Line command and pick a set of points. A chain of
line segments will be drawn as you pick points, just like how we wanted. However, to stop
drawing lines, we need to cancel the command by pressing Esc, which may not be the most
elegant way of doing it. Ideally we would want the user to press Enter to let OpenCAD know that
he is done drawing lines.
In CCommandPrompt::GetPoint() add the following code just before the call to GetPointFromString().
if(strText == _T(""))
{
throw OdEdCancel();
return FALSE;
}
This cancels the command without printing *Cancel* to the prompt area. Build and run
OpenCAD. This time the press Enter after you are done drawing lines. The command will exit
gracefully.
Repeat:
if(PutString(strPrompt) == FALSE)// Show prompt
{
throw OdError(_T("Unable to output to prompt area"));
return FALSE;
}
101
OpenCAD
// Once again get the control. This is important because in the while
// loop above the command prompt may have been destroyed
pwndCommand = (CEdit*)GetDlgItem(IDC_EDT_COMMAND);
if(pwndCommand == NULL)
{
throw OdError(_T("Invalid command prompt"));
return FALSE;
}
CString strText;
pwndCommand->GetWindowTextW(strText);// Store entered text
if(strText == _T(""))
{
throw OdEdCancel();
return FALSE;
}
if(GetPointFromString(strText, Point) == FALSE)
{
PutString(strText, FALSE);// Append entered text to prompt
PutString(_T("Invalid point"));
goto Repeat;
}
return TRUE;
}
102
Chapter 8: OpenCAD as a DWG Editor
// Once again get the control. This is important because in the while
// loop above the command prompt may have been destroyed
pwndCommand = (CEdit*)GetDlgItem(IDC_EDT_COMMAND);
if(pwndCommand == NULL)
{
throw OdError(_T("Invalid command prompt"));
return FALSE;
}
return TRUE;
103
OpenCAD
This should take care of some unexpected situations. We need to take care of something else. We
called the COpenCADView::Track() method in COpenCADDoc::getPoint(). We need to call it in
COpenCADDoc::getString() as well. Add the code highlighted in bold.
GetView()->Track(pTracker);
CString strText;
GetChildFrame()->m_wndCommandPrompt.GetString((LPCTSTR)prompt, strText);
return OdString(strText);
}
COpenCADDoc additionally derives from OdEdBaseIO and these three methods are actually
virtual members of OdEdBaseIO. The first two methods, putString() and getString() are pure virtual
methods which means that it is mandatory for them to be defined in the derived class, which is
what we did earlier on. There is a reason for that. These two methods are used by the other get
methods of the OdDbUserIO class, namely getAngle(), getColor(), getDist(), getFilePath(), getInt(),
getKeyword() and getReal(). We can simply go ahead and call these get methods and the putString()
and getString() methods of COpenCADDoc will be called internally to make things happen.
Lets see this how this works in the case of the getKeyword(). In COCKernelLine::execute() add the
following test code just after pUserIO is declared and assigned.
104
Chapter 8: OpenCAD as a DWG Editor
Here I am assuming you already know the uppercase-lowercase method of formulating keywords
used in AutoCAD and its clones. Build OCKernel and run the Import command in OpenCAD.
Type y or yes at the command prompt and you should see something like the figure below.
Other get methods behave in a similar fashion. This means that if you do not want your
DWGdirect hosted application to have a command prompt, but something else instead, all you
need to do is set up a putString() and getString() function that will put and get strings from whatever
GUI you have created. The DWGdirect SDK already has the framework in place to let
commands interact with the GUI of your choice.
The OdDbUserIO class has a getPoint() method which is not a pure virtual function. If we had not
added a getPoint() method to COpenCADDoc it would have use the putString() and getString()
methods to get input from the user. Just that the user would have to enter coordinates at the
command prompt. Since we wanted the user to be able to pick points in the drawing view, we
went ahead and implemented getPoint() in COpenCADDoc.
Another interesting method is getDist(). If we had not implemented getPoint(), the putString() and
getString() methods would have been called to get a distance value from the user. But since we
implemented getPoint(), the user will be asked to pick two points in the drawing view, the distance
between which will be returned by the getDist() method. Let us see this in action. Replace the
temporary code we just added to COCKernelLine::execute() with the following.
Build OCKernel and run the Line command in OpenCAD. Pick two points in the drawing view
and you should see something like the figure below.
105
OpenCAD
We will try and load OCKernel into Bricscad V9, run our Line command and draw a line object
in the active Bricscad document. Yes, I know it sounds like a crazy idea, if not impossible. It is
one thing to make OCKernel interact with the command prompt and drawing view of
OpenCAD. We wrote both those pieces of software from scratch and spent this entire section
wiring them together. But how can OCKernel possibly interact with the Bricscad command
prompt, something that we know absolutely nothing about. Remember putString(), getString() and
getPoint()? If you did not understand what I was said back then, I am pretty sure you will now.
First we need to rename our Line command since Bricscad already has a command called Line. In
OCKernelLine.h modify globalName() to return “Line1”.
Build OCKernel. We will now try and load OCKernel_2.06_8.drx into Bricscad V9. Bricsys used
Visual C++ 2005 and DWGdirect version 2.06 to build Bricscad V9 and that is one the reasons I
chose to use those exact versions of compiler and SDK to build OpenCAD and OCKernel - so
that we can do what we are about to do now.
Start Bricscad V9. If you do not have Bricscad V9 installed on your computer, sit back, relax and
enjoy the show. Click Load Applications from the Tools menu. The Load Application Files dialog box
will pop up. Click Add. Browse to C:\OpenCAD\Bin and select OCKernel_2.06_8.drx. It will be
added to the list in the Load Application Files dialog box. Select it and click Load.
106
Chapter 8: OpenCAD as a DWG Editor
Notice the Bricscad command prompt. If our DRX module could not be loaded, Bricscad will
report an error in the prompt area.
Type Line1 at the Bricscad command prompt. the Line1 command from OCKernel will begin.
Pick points in the drawing view or enter coordinates at the command prompt. Line segments will
be added to the document.
107
OpenCAD
So it did work. This is what actually happenned. Bricsys implemented the putString(), getString()
and getPoint() methods for their DWGdirect hosted application - Bricscad V9. To us, it really does
not matter what they did and how they did it. All we need to do is call the methods from within
our DRX plug-in and control will be passed to the appropriate GUI components in Bricscad V9.
It’s that simple and yet so powerful.
108
Conclusion
I guess you can now see my point that the DWGdirect SDK does not just read and write DWG
files. It offers a full blown framework to create a professional CAD application. At the start of
this book we started out with a empty MFC Doc-View MDI application and now we have a
CAD application that looks, feels and works a lot like AutoCAD. I believe the IntelliCAD
Technology Consortium (ITC) is developing the new IntelliCAD 7 pretty much how we
developed OpenCAD, using the same core framework of the DWGdirect SDK. And we know
that Bricsys developed Bricscad V9 using the same framework, otherwise our OCKernel plug-in
would not be able to interact with its command prompt and drawing view. Of course, IntelliCAD
7 and Bricscad are far more complicated and powerful than our simple OpenCAD, but they are
built using the powerful DWGdirect application framework and can be extended using DRX
plug-ins.
I know that in its current state, OpenCAD with just two commands, is nowhere close to being a
complete CAD application. But that was not the point of this book. The aim was not to create a
usable CAD application. Rather it was to show you what can be achieved with the DWGdirect
SDK and completely shred the myth that the DWGdirect SDK is good only to read and write
DWG files. I hope I have done that.
I also hope that you have enjoyed this book as much as I have enjoyed writing it. This is my first
book and if you are still reading this, I guess it means that I have not done that bad a job after all.
I look forward to hearing from you. Drop me a line at deelip@sycode.com.
As far as OpenCAD itself is concerned, I believe it has served its intended purpose of helping
me explain the DWGdirect and DRX SDK’s to you in this book. I really do not have the time or
the need to add commands to it and make it a usable CAD application. The DWGdirect SDK
offers a whole lot more than what I have shown in this book, stuff like object selection, object
snap, editing by means of grips, etc. In fact, I have only scratched the surface.
If you want to take OpenCAD forward, let me know. Doing it all by yourself may be too large a
task for a single person. If there are sufficient people willing to work together on this, we could
actually do something with OpenCAD. For starters we could rewrite it from scratch. To keep it
simple for this book, I left out a lot of stuff. Who knows, we may come up with an OpenCAD
Technology Consortium (OTC) and give the ITC a run for their money. That will indeed be
quite weird because we will, in effect, be making a clone of a clone.
Just kidding. Or am I?
109
110