Mega Code Archive

 
Categories / Delphi / Graphic
 

3D CAD calculating hiddenlines (the math way)

Title: 3D CAD - calculating hiddenlines (the math way) Question: How can I display hiddenlines of a 3d shape ? The possibilities to operate on the hiddenline data ? Answer: MNHiddenLines is shipped with an installation program. Run this program and the neccessary library MNHL.DLL, which calculates the hiddenlines, will be installed right into your installation folder. Maybe you prefer to copy the MNHL.DLL to your windows\system folder (Win95/98) or windows\system32 (WinNT/2000), so the MNHL library will be 100% found. Then - let us start a new Delphi project. Overwork your uses list and add the units ImportMNHL and _MNUtils. Now we define a scale constant: Const unitspercm:Double = 50; // tells the canvas that 50 pixels shall be 1 cm then a global hiddenbuffer to operate with: var MNSDHiddenBuffer:THHandle; // a global handle to the hiddenbuffer At the FormCreate event we create the hiddenbuffer: MNSDHiddenBuffer := MNSDOpen (NormalProjection, NormalDistance, Lineto, Moveto); Note that NormalProjection and NormalDistance are predefined functions which you will find in the _MNUtils.pas. They do describe how a 3D vector will be projected to the canvas (depends on the projectionbase, also a global variable in _MNUtils.pas). MoveTo and LineTo are your procedures to paint the calculated data into a window handle you want (e.g. the handle of your Form1): Procedure Moveto (H:THHandle; P:TXY); var xi,yi:integer; BEGIN // a moveto handler for the standard device With Form1.Canvas do BEGIN Xi:=round(P.x*unitspercm +Form1.clientwidth div 2); Yi:=round(-P.y*unitspercm+Form1.clientheight div 2); MoveTo(xi,yi); END; END; procedure Lineto(H:THHandle; Down: WordBool; P:Txy); var Xi,Yi:integer; begin // a lineto handler for the standard device With Form1.canvas do BEGIN Xi:=round(P.x*unitspercm +Form1.clientwidth div 2); Yi:=round(-P.y*unitspercm+Form1.clientheight div 2); If Not Down then Pen.style:=Psdot ELSE Pen.style:=PsSolid; Lineto(xi,yi); END; end; Now we can operate with the created hiddenbuffer - let us add a cube with a hole, this means that we have to add the six outer planes, the four inner planes and the two holes (all in all twelve planes). // top plane MNSDContour (MNSDHiddenBuffer,[XYZ(0.00,0.00,0.00),XYZ(0.00,2.00,0.00), XYZ(2.00,2.00,0.00), XYZ(2.00,0.00,0.00), XYZ(0.00,0.00,0.00)]); // hole in the top plane - BUT IN REVESE DIRECTION MNSDHole (MNSDHiddenBuffer,[XYZ (0.67,0.67,0.00),XYZ (1.33,0.67,0.00), XYZ (1.33,1.33,0.00), XYZ (0.67,1.33,0.00), XYZ (0.67,0.67,0.00)]); // bottom plane MNSDContour (MNSDHiddenBuffer,[XYZ (2.00,0.00,2.00),XYZ (2.00,2.00,2.00), XYZ (0.00,2.00,2.00), XYZ (0.00,0.00,2.00), XYZ (2.00,0.00,2.00)]); // hole in the bottom plane - BUT IN REVESE DIRECTION MNSDHole (MNSDHiddenBuffer,[XYZ (0.67,1.33,2.00),XYZ (1.33,1.33,2.00), XYZ (1.33,0.67,2.00), XYZ (0.67,0.67,2.00), XYZ (0.67,1.33,2.00)]); // the rest of the lateral area MNSDContour (MNSDHiddenBuffer,[XYZ (0.00,0.00,2.00), XYZ (0.00,2.00,2.00), XYZ (0.00,2.00,0.00), XYZ (0.00,0.00,0.00), XYZ (0.00,0.00,2.00)]); MNSDContour (MNSDHiddenBuffer,[XYZ (0.00,2.00,2.00), XYZ (2.00,2.00,2.00), XYZ (2.00,2.00,0.00), XYZ (0.00,2.00,0.00), XYZ (0.00,2.00,2.00)]); MNSDContour (MNSDHiddenBuffer,[XYZ (2.00,2.00,2.00), XYZ (2.00,0.00,2.00), XYZ (2.00,0.00,0.00), XYZ (2.00,2.00,0.00), XYZ (2.00,2.00,2.00)]); MNSDContour (MNSDHiddenBuffer,[XYZ (2.00,0.00,2.00), XYZ (0.00,0.00,2.00), XYZ (0.00,0.00,0.00), XYZ (2.00,0.00,0.00), XYZ (2.00,0.00,2.00)]); // now the inner four planes MNSDContour (MNSDHiddenBuffer,[XYZ (0.67,0.67,2.00), XYZ (1.33,0.67,2.00), XYZ (1.33,0.67,0.00), XYZ (0.67,0.67,0.00), XYZ (0.67,0.67,2.00)]); MNSDContour (MNSDHiddenBuffer,[XYZ (1.33,0.67,2.00), XYZ (1.33,1.33,2.00), XYZ (1.33,1.33,0.00), XYZ (1.33,0.67,0.00), XYZ (1.33,0.67,2.00)]); MNSDContour (MNSDHiddenBuffer,[XYZ (1.33,1.33,2.00), XYZ (0.67,1.33,2.00), XYZ (0.67,1.33,0.00), XYZ (1.33,1.33,0.00), XYZ (1.33,1.33,2.00)]); MNSDContour (MNSDHiddenBuffer,[XYZ (0.67,1.33,2.00), XYZ (0.67,0.67,2.00), XYZ (0.67,0.67,0.00), XYZ (0.67,1.33,0.00), XYZ (0.67,1.33,2.00)]); // =================================================== // || DO ALWAYS DOUBLECHECK THE CORRECT ORIENTATION || // =================================================== Whenever you want to recalculate data, call: If MNSDHiddenBuffer 0 then MNSDChanged(MNSDHiddenBuffer); Whenever you want to start the draw mechanism, call: If MNSDHiddenBuffer 0 then MNSDdraw(MNSDHiddenBuffer); MNSDDraw forces that your MoveTo and LineTo events will be called. When you do not need the hiddenbuffer anymore, just free it with If MNSDHiddenBuffer 0 then MNSDFree (MNSDHiddenBuffer); When you want to see the calculated hiddenlines, call: var Value:Boolean; If MNSDHiddenBuffer 0 then begin Value:=MNSDGetDrawHiddenlines(MNSDHiddenBuffer); MNSDSetDrawHiddenlines(MNSDHiddenBuffer,TRUE); MNSDChanged(MNSDHiddenBuffer); end; To toggle the materialside (the orientation of the polygons), call: var Value:Boolean; If MNSDHiddenBuffer 0 then begin Value:=MNSDGetClockwise(MNSDHiddenBuffer); MNSDSetClockwise(MNSDHiddenBuffer,Not Value); MNSDChanged(MNSDHiddenBuffer); end; By the way, you also have the possibilitity to interrupt the calculation of hiddenlines with an OnInterrupt event (check the delivered samples). To operate on the calculated polygons, use the following commands: please read the comments below and i hope you will understand the theory of masked lines (else check the delivered help files and/or pdf files for detailed documentations) VAR startC : integer; s : String; XYZCount, i, j : integer; cnt:integer; MaskArray : PDoubleList; // to retrieve the mask values d:double; sd, command:String; XYZArray : PXYZList; // to retrieve the original 3D coordinates begin TRY for i := 0 to maxInserted do begin // count of polygons in the hiddenbuffer cnt := ImportMNHL.MNSDGetMaskCount (MNSDHiddenBuffer, i); // get the count GetMem (MaskArray, Cnt*SizeOf(double)); // reserve memory ImportMNHL.MNSDMask (MNSDHiddenBuffer, i, MaskArray^); // fetch the data // the same procedure for the 3D coordinates xyzCount := ImportMNHL.MNSDXYZListCount (MNSDHiddenBuffer, i); GetMem (XYZArray, xyzCount*SizeOf(TXYZ)); ImportMNHL.MNSDGetXYZList (MNSDHiddenBuffer, i, XYZArray^); // NOW the explanation of the mask PEN UPs/DOWNs s := ''; startC := Trunc (maskArray[0]); s := 'Start: ' + IntToStr (startC); If startC = -1 then command := 'PENDOWN - ' else command := 'PENUP - '; // If it starts with a "-1" -- the start is "PEN UP" // else it starts with "PEN DOWN" for j := 1 to cnt-1 do begin // now retrieve all the doubles: // the value is an index for the polygon which was inserted // so if the next value would be 0.76, this would mean that // the mask is in "PEN UP" or "PEN DOWN" mode until position // 0.76 (between the first and the second point of your polygon) d := maskArray[j]; // if the receiving value is the same as the last one -- // don't bother -- IGNORE !!! sd := FloatToStrF (d, ffFixed, 4, 2); s := s + ':: ' + sd; // PartitionPoint would calculate the appropriate point at index d command := command + PartitionPoint (xyzarray, d); // now the state of "PEN UP" or "PEN DOWN" ALWAYS toggles If startC If startC = -1 then command := command + ' - PENDOWN - ' else command := command + ' - PENUP - '; end; FreeMem (MaskArray, Cnt*SizeOf(double)); FreeMem (XYZArray, xyzCount*SizeOf(TXYZ)); end; except end; end; So - you can see - there is nothing dramatic on displaying and operating on hiddenlines. By the way, MNHiddenLines does also support an OpenGL hiddenbuffer - just for speed (because you DO NOT have the possibilty of operating on the calculated hiddenlines). And for all who do not want to use a DLL header file, we also have an ActiveX component. Try it out and let me know your experiences DeveloCAD.com q.spline@develocad.com