How to display and operate on hiddenlines with the DeveloCADs'
MNHiddenLines
3D
CAD - calculating hiddenlines (the math way)
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:
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)]);
// ===================================================
// || 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 < -0.001 then startC := startC + 1 else startC := startC
-1;
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.