123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787 |
- //
- // The graphics engine GLXEngine. The unit of GXScene for Delphi
- //
- unit GXS.MultiPolygon;
- (* Object with support for complex polygons *)
- (* TODO
- And I reactivated the TgxVectorPool object. The GXS.VectorLists are not suitable for this job.
- When the tesselator finds an intersection of edges it wants us to give him some storage
- for this new vertex, and he wants a pointer (see tessCombine). The pointers taken from
- TgxAffineVectorList become invalid after enlarging the capacity (makes a ReAllocMem), which
- can happen implicitly while adding. The TgxVectorPool keeps all pointers valid until the
- destruction itself.
- If anyone feels responsible: it would be fine to have a method ImportFromFile (dxf?) in
- the TgxContour and TgxMultiPolygonBase objects...
- *)
- interface
- {$I Stage.Defines.inc}
- uses
- Winapi.OpenGL,
- Winapi.OpenGLext,
- System.Classes,
- System.SysUtils,
- GXS.XOpenGL,
- Stage.Spline,
- GXS.Context,
- Stage.VectorTypes,
- Stage.VectorGeometry,
- GXS.VectorLists,
- GXS.PersistentClasses,
- GXS.Scene,
- GXS.Objects,
- GXS.GeomObjects,
- GXS.Nodes,
- GXS.BaseClasses,
- GXS.Coordinates,
- GXS.RenderContextInfo;
- type
- TgxContourNodes = class(TgxNodes)
- public
- procedure NotifyChange; override;
- end;
- TgxContour = class(TCollectionItem)
- private
- FNodes: TgxContourNodes;
- FDivision: Integer;
- FSplineMode: TgxLineSplineMode;
- FDescription: string;
- procedure SetNodes(const Value: TgxContourNodes);
- procedure SetDivision(Value: Integer);
- procedure SetSplineMode(const Value: TgxLineSplineMode);
- procedure SetDescription(const Value: string);
- protected
- procedure CreateNodes; virtual;
- procedure NodesChanged(Sender: TObject);
- function GetDisplayName: string; override;
- public
- constructor Create(Collection: TCollection); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- published
- property Description: string read FDescription write SetDescription;
- // The nodes list.
- property Nodes: TgxContourNodes read FNodes write SetNodes;
- (* Number of divisions for each segment in spline modes.
- Minimum 1 (disabled), ignored in lsmLines mode. *)
- property Division: Integer read FDivision write SetDivision default 10;
- (* Default spline drawing mode.
- This mode is used only for the curve, not for the rotation path. *)
- property SplineMode: TgxLineSplineMode read FSplineMode write SetSplineMode default lsmLines;
- end;
- TgxContourClass = class of TgxContour;
- TgxContours = class(TGNotifyCollection)
- private
- function GetItems(index: Integer): TgxContour;
- procedure SetItems(index: Integer; const Value: TgxContour);
- protected
- public
- constructor Create(AOwner: TComponent); overload;
- function Add: TgxContour;
- function FindItemID(ID: Integer): TgxContour;
- property Items[index: Integer]: TgxContour read GetItems write SetItems; default;
- procedure GetExtents(var min, max: TAffineVector);
- end;
- TgxPolygonList = class(TgxPersistentObjectList)
- private
- FAktList: TgxAffineVectorList;
- function GetList(I: Integer): TgxAffineVectorList;
- public
- procedure Add;
- property AktList: TgxAffineVectorList read FAktList;
- property List[I: Integer]: TgxAffineVectorList read GetList;
- end;
- (* Multipolygon is defined with multiple contours.
- The contours have to be in the X-Y plane, otherwise they are projected
- to it (this is done automatically by the tesselator).
- The plane normal is pointing in +Z. All contours are automatically closed,
- so there is no need to specify the last node equal to the first one.
- Contours should be defined counterclockwise, the first contour (index = 0)
- is taken as is, all following are reversed. This means you can define the
- outer contour first and the holes and cutouts after that. If you give the
- following contours in clockwise order, the first contour is extended.
- TgxMultiPolygonBase will take the input contours and let the tesselator
- make an outline from it (this is done in RetreiveOutline). This outline is
- used for Rendering. Only when there are changes in the contours, the
- outline will be recalculated. The ouline in fact is a list of GXS.VectorLists. *)
- TgxMultiPolygonBase = class(TgxSceneObject)
- private
- FContours: TgxContours;
- FOutline: TgxPolygonList;
- FContoursNormal: TAffineVector;
- FAxisAlignedDimensionsCache: TVector4f;
- procedure SetContours(const Value: TgxContours);
- function GetPath(i: Integer): TgxContourNodes;
- procedure SetPath(i: Integer; const value: TgxContourNodes);
- function GetOutline: TgxPolygonList;
- procedure SetContoursNormal(const Value: TAffineVector);
- protected
- procedure RenderTesselatedPolygon(textured: Boolean;
- normal: PAffineVector; invertNormals: Boolean);
- procedure RetrieveOutline(List: TgxPolygonList);
- procedure ContourChanged(Sender: TObject); virtual;
- //property PNormal:PAffineVector read FPNormal;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- procedure AddNode(const i: Integer; const coords: TgxCoordinates); overload;
- procedure AddNode(const i: Integer; const X, Y, Z: Single); overload;
- procedure AddNode(const i: Integer; const value: TVector4f); overload;
- procedure AddNode(const i: Integer; const value: TAffineVector); overload;
- property Path[i: Integer]: TgxContourNodes read GetPath write SetPath;
- property Outline: TgxPolygonList read GetOutline;
- property ContoursNormal: TAffineVector read FContoursNormal write SetContoursNormal;
- function AxisAlignedDimensionsUnscaled: TVector4f; override;
- procedure StructureChanged; override;
- published
- property Contours: TgxContours read FContours write SetContours;
- end;
- (* A polygon that can have holes and multiple contours.
- Use the Path property to access a contour or one of the AddNode methods
- to add a node to a contour (contours are allocated automatically). *)
- TgxMultiPolygon = class(TgxMultiPolygonBase)
- private
- FParts: TgxPolygonParts;
- protected
- procedure SetParts(const value: TgxPolygonParts);
- public
- constructor Create(AOwner: TComponent); override;
- procedure Assign(Source: TPersistent); override;
- procedure BuildList(var rci: TgxRenderContextInfo); override;
- published
- property Parts: TgxPolygonParts read FParts write SetParts default [ppTop, ppBottom];
- end;
- //============================================================
- implementation
- //============================================================
- type
- (* page oriented pointer array, with persistent pointer target memory.
- In TgxVectorList a pointer to a vector will not be valid any more after
- a call to SetCapacity, which might be done implicitely during Add.
- The TgxVectorPool keeps memory in its original position during its
- whole lifetime. *)
- // removed Notify (only D5)
- // added Destroy (also working with D4)
- TgxVectorPool = class(TList)
- private
- FEntrySize: Integer; // size of each entry
- FPageSize: Integer; // number of entries per page
- FArrSize: Integer; // size of one page
- FUsedEntries: Integer; // used entries in actual page
- FAktArray: PByteArray; // pointer to actual page
- procedure CreatePage; // create new page
- public
- constructor Create(APageSize, AEntrySize: Integer);
- destructor Destroy; override;
- { retrieve pointer to new entry. will create new page if needed }
- procedure GetNewVector(var P: Pointer);
- end;
- //-----------------------------------------------
- // TgxVectorPool
- //-----------------------------------------------
- constructor TgxVectorPool.Create(APageSize, AEntrySize: Integer);
- begin
- inherited Create;
- Assert(APageSize > 0);
- Assert(AEntrySize > 0);
- FPageSize := APageSize;
- FEntrySize := AEntrySize;
- FArrSize := FPageSize * FEntrySize;
- CreatePage;
- end;
- procedure TgxVectorPool.CreatePage;
- begin
- GetMem(FAktArray, FArrSize);
- Add(FAktArray);
- FUsedEntries := 0;
- end;
- destructor TgxVectorPool.Destroy;
- var
- i: Integer;
- begin
- for i := Count - 1 downto 0 do
- FreeMem(Items[i], FArrSize);
- inherited;
- end;
- procedure TgxVectorPool.GetNewVector(var P: Pointer);
- begin
- if FUsedEntries >= FPageSize then
- CreatePage;
- Inc(FUsedEntries);
- P := @(FAktArray[(FUsedEntries - 1) * FEntrySize]);
- end;
- // ------------------
- // ------------------ TgxPolygonList ------------------
- // ------------------
- procedure TgxPolygonList.Add;
- begin
- FAktList := TgxAffineVectorList.Create;
- inherited Add(FAktList);
- end;
- function TgxPolygonList.GetList(i: Integer): TgxAffineVectorList;
- begin
- Result := TgxAffineVectorList(Items[i]);
- end;
- // ------------------
- // ------------------ TgxContour ------------------
- // ------------------
- constructor TgxContour.Create(Collection: TCollection);
- begin
- inherited;
- CreateNodes;
- FDivision := 10;
- FSplineMode := lsmLines;
- end;
- procedure TgxContour.CreateNodes;
- begin
- FNodes := TgxContourNodes.Create(Self);
- end;
- destructor TgxContour.Destroy;
- begin
- FNodes.Free;
- inherited;
- end;
- procedure TgxContour.Assign(Source: TPersistent);
- begin
- if Source is TgxContour then
- begin
- FNodes.Assign(TgxContour(Source).FNodes);
- FDivision := TgxContour(Source).FDivision;
- FSplineMode := TgxContour(Source).FSplineMode;
- FDescription := TgxContour(Source).FDescription;
- end
- else
- inherited;
- end;
- function TgxContour.GetDisplayName: string;
- begin
- result := Description;
- if result = '' then
- result := Format('GXContour: %d nodes', [Nodes.Count]);
- end;
- procedure TgxContour.NodesChanged(Sender: TObject);
- begin
- Changed(false);
- end;
- procedure TgxContour.SetDescription(const Value: string);
- begin
- FDescription := Value;
- end;
- procedure TgxContour.SetDivision(Value: Integer);
- begin
- if Value < 1 then
- Value := 1;
- if Value <> FDivision then
- begin
- FDivision := value;
- Changed(false);
- end;
- end;
- procedure TgxContour.SetNodes(const Value: TgxContourNodes);
- begin
- FNodes.Assign(Value);
- Changed(false);
- end;
- procedure TgxContour.SetSplineMode(const Value: TgxLineSplineMode);
- begin
- if FSplineMode <> value then
- begin
- FSplineMode := value;
- Changed(false);
- end;
- end;
- //--------------------------------------------
- // TgxContours
- //--------------------------------------------
- function TgxContours.Add: TgxContour;
- begin
- Result := TgxContour(inherited Add);
- end;
- constructor TgxContours.Create(AOwner: TComponent);
- begin
- Create(AOwner, TgxContour);
- end;
- function TgxContours.FindItemID(ID: Integer): TgxContour;
- begin
- result := TgxContour(inherited FindItemId(Id));
- end;
- function TgxContours.GetItems(index: Integer): TgxContour;
- begin
- result := TgxContour(inherited Items[index]);
- end;
- procedure TgxContours.SetItems(index: Integer; const Value: TgxContour);
- begin
- inherited Items[index] := value;
- end;
- procedure TgxContours.GetExtents(var min, max: TAffineVector);
- var
- i, k: Integer;
- lMin, lMax: TAffineVector;
- const
- cBigValue: Single = 1e30;
- cSmallValue: Single = -1e30;
- begin
- SetVector(min, cBigValue, cBigValue, cBigValue);
- SetVector(max, cSmallValue, cSmallValue, cSmallValue);
- for i := 0 to Count - 1 do
- begin
- GetItems(i).Nodes.GetExtents(lMin, lMax);
- for k := 0 to 2 do
- begin
- if lMin.V[k] < min.V[k] then
- min.V[k] := lMin.V[k];
- if lMax.V[k] > max.V[k] then
- max.V[k] := lMax.V[k];
- end;
- end;
- end;
- //--------------------------------------------
- // TgxMultiPolygonBase
- //--------------------------------------------
- constructor TgxMultiPolygonBase.Create(AOwner: TComponent);
- begin
- inherited;
- FContours := TgxContours.Create(Self);
- FContours.OnNotifyChange := ContourChanged;
- FContoursNormal := AffineVectorMake(0, 0, 1);
- FAxisAlignedDimensionsCache.X := -1;
- end;
- destructor TgxMultiPolygonBase.Destroy;
- begin
- if FOutline <> nil then
- begin
- FOutline.Clean;
- FreeAndNil(FOutline);
- end;
- FContours.Free;
- inherited;
- end;
- procedure TgxMultiPolygonBase.Assign(Source: TPersistent);
- begin
- if Source is TgxMultiPolygonBase then
- begin
- FContours.Assign(TgxMultiPolygonBase(Source).FContours);
- end;
- inherited;
- end;
- procedure TgxMultiPolygonBase.ContourChanged(Sender: TObject);
- begin
- if Assigned(FOutline) then
- begin
- // force a RetrieveOutline with next Render
- FOutline.Clean;
- FreeAndNil(FOutline);
- StructureChanged;
- end;
- end;
- procedure TgxMultiPolygonBase.AddNode(const i: Integer; const value: TVector4f);
- begin
- Path[i].AddNode(value);
- end;
- procedure TgxMultiPolygonBase.AddNode(const i: Integer; const x, y, z: Single);
- begin
- Path[i].AddNode(x, y, z);
- end;
- procedure TgxMultiPolygonBase.AddNode(const i: Integer; const coords: TgxCoordinates);
- begin
- Path[i].AddNode(coords);
- end;
- procedure TgxMultiPolygonBase.AddNode(const I: Integer; const value: TAffineVector);
- begin
- Path[i].AddNode(value);
- end;
- procedure TgxMultiPolygonBase.SetContours(const Value: TgxContours);
- begin
- FContours.Assign(Value);
- end;
- function TgxMultiPolygonBase.GetOutline: TgxPolygonList;
- begin
- if not Assigned(FOutline) then
- begin
- FOutline := TgxPolygonList.Create;
- RetrieveOutline(FOutline);
- end;
- Result := FOutline;
- end;
- function TgxMultiPolygonBase.GetPath(i: Integer): TgxContourNodes;
- begin
- Assert(i >= 0);
- while i >= Contours.Count do
- Contours.Add;
- Result := Contours[i].Nodes;
- end;
- procedure TgxMultiPolygonBase.SetPath(i: Integer; const value: TgxContourNodes);
- begin
- Assert(i >= 0);
- while i >= Contours.Count do
- Contours.Add;
- Contours[i].Nodes.Assign(value);
- end;
- //
- // Tessellation routines (OpenGL callbacks)
- //
- var
- vVertexPool: TgxVectorPool;
- procedure tessError(errno: GLEnum);
- {$IFDEF Win32} stdcall;{$ELSE} cdecl;{$ENDIF}
- begin
- Assert(False, IntToStr(errno) + ' : ' + string(gluErrorString(errno)));
- end;
- procedure tessIssueVertex(vertexData: Pointer);
- {$IFDEF Win32} stdcall;{$ELSE}cdecl;{$ENDIF}
- begin
- glTexCoord2fv(vertexData);
- glVertex3fv(vertexData);
- end;
- procedure tessCombine(coords: PDoubleVector; vertex_data: Pointer;
- weight: PGLFloat; var outData: Pointer);
- {$IFDEF Win32} stdcall;{$ELSE}cdecl;{$ENDIF}
- begin
- vVertexPool.GetNewVector(outData);
- SetVector(PAffineVector(outData)^, coords^[0], coords^[1], coords^[2]);
- end;
- procedure tessBeginList(typ: GLEnum; polygonData: Pointer);
- {$IFDEF Win32} stdcall;{$ELSE}cdecl;{$ENDIF}
- begin
- TgxPolygonList(polygonData).Add;
- end;
- procedure tessIssueVertexList(vertexData: Pointer; polygonData: Pointer);
- {$IFDEF Win32} stdcall;{$ELSE}cdecl;{$ENDIF}
- begin
- TgxPolygonList(polygonData).AktList.Add(PAffineVector(vertexData)^);
- end;
- procedure TgxMultiPolygonBase.RetrieveOutline(List: TgxPolygonList);
- var
- i, n: Integer;
- tess: GLUTesselator;
- procedure TesselatePath(contour: TgxContour; inverted: Boolean);
- procedure IssueVertex(v: TAffineVector);
- var
- dblVector: TAffineDblVector;
- p: PAffineVector;
- begin
- vVertexPool.GetNewVector(Pointer(p));
- p^ := v;
- SetVector(dblVector, v);
- gluTessVertex(tess, @dblVector, p);
- end;
- var
- i, n: Integer;
- spline: TCubicSpline;
- f: Single;
- splineDivisions: Integer;
- nodes: TgxContourNodes;
- begin
- gluTessBeginContour(tess);
- nodes := contour.Nodes;
- if contour.SplineMode = lsmLines then
- splineDivisions := 0
- else
- splineDivisions := contour.Division;
- if splineDivisions > 1 then
- begin
- spline := nodes.CreateNewCubicSpline;
- try
- f := 1 / splineDivisions;
- n := splineDivisions * (nodes.Count - 1);
- if inverted then
- begin
- for i := n downto 0 do
- IssueVertex(spline.SplineAffineVector(i * f))
- end
- else
- begin
- for i := 0 to n do
- IssueVertex(spline.SplineAffineVector(i * f));
- end;
- finally
- spline.Free;
- end;
- end
- else
- begin
- n := nodes.Count - 1;
- if inverted then
- begin
- for i := n downto 0 do
- IssueVertex(nodes[i].AsAffineVector)
- end
- else
- begin
- for i := 0 to n do
- IssueVertex(nodes[i].AsAffineVector);
- end;
- end;
- gluTessEndContour(tess);
- end;
- begin
- List.Clear;
- if (Contours.Count > 0) and (Path[0].Count > 2) then
- begin
- // Vertex count
- n := 0;
- for i := 0 to Contours.Count - 1 do
- n := n + Path[i].Count;
- // Create and initialize the GLU tesselator
- vVertexPool := TgxVectorPool.Create(n, SizeOf(TAffineVector));
- tess := gluNewTess;
- try
- // register callbacks
- gluTessCallback(tess, GLU_TESS_BEGIN, @tessBeginList);
- gluTessCallback(tess, GLU_TESS_END, nil);
- gluTessCallback(tess, GLU_TESS_VERTEX, @tessIssueVertexList);
- gluTessCallback(tess, GLU_TESS_ERROR, @tessError);
- gluTessCallback(tess, GLU_TESS_COMBINE, @tessCombine);
- // issue normal
- gluTessNormal(tess, FContoursNormal.X, FContoursNormal.Y, FContoursNormal.Z);
- // set properties
- gluTessProperty(Tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE);
- gluTessProperty(Tess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE);
- gluTessBeginPolygon(tess, List);
- // outside contour
- TesselatePath(Contours[0], False);
- // inside contours
- for n := 1 to Contours.Count - 1 do
- TesselatePath(Contours[n], True);
- gluTessEndPolygon(tess);
- finally
- gluDeleteTess(tess);
- vVertexPool.Free;
- vVertexPool := nil;
- end;
- end;
- end;
- procedure TgxMultiPolygonBase.RenderTesselatedPolygon(textured: Boolean;
- normal: PAffineVector;
- invertNormals: Boolean);
- var
- tess: GLUTesselator;
- procedure IssueVertex(v: TAffineVector);
- var
- dblVector: TAffineDblVector;
- p: PAffineVector;
- begin
- vVertexPool.GetNewVector(Pointer(p));
- p^ := v;
- SetVector(dblVector, v);
- gluTessVertex(tess, @dblVector, p);
- end;
- var
- i, n: Integer;
- begin
- // call to Outline will call RetrieveOutline if necessary
- if (Outline.Count = 0) or (Outline.List[0].Count < 2) then
- Exit;
- // Vertex count
- n := 0;
- for i := 0 to Outline.Count - 1 do
- n := n + Outline.List[i].Count;
- // Create and initialize a vertex pool and the GLU tesselator
- vVertexPool := TgxVectorPool.Create(n, Sizeof(TAffineVector));
- tess := gluNewTess;
- try
- gluTessCallback(tess, GLU_TESS_BEGIN, @glBegin);
- if textured then
- gluTessCallback(tess, GLU_TESS_VERTEX, @tessIssueVertex)
- else
- gluTessCallback(tess, GLU_TESS_VERTEX, @glVertex3fv);
- gluTessCallback(tess, GLU_TESS_END, @glEnd);
- gluTessCallback(tess, GLU_TESS_ERROR, @tessError);
- gluTessCallback(tess, GLU_TESS_COMBINE, @tessCombine);
- // Issue normal
- if Assigned(normal) then
- begin
- glNormal3fv(PGLFloat(normal));
- gluTessNormal(tess, normal^.X, normal^.Y, normal^.Z);
- end;
- gluTessProperty(Tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE);
- // Issue polygon
- gluTessBeginPolygon(tess, nil);
- for n := 0 to Outline.Count - 1 do
- begin
- with Outline.List[n] do
- begin
- gluTessBeginContour(tess);
- if invertNormals then
- for i := Count - 1 downto 0 do
- IssueVertex(Items[i])
- else
- for i := 0 to Count - 1 do
- IssueVertex(Items[i]);
- gluTessEndContour(tess);
- end;
- end;
- gluTessEndPolygon(tess);
- finally
- gluDeleteTess(tess);
- vVertexPool.Free;
- vVertexPool := nil;
- end;
- end;
- // ------------------
- // ------------------ TgxMultiPolygon ------------------
- // ------------------
- constructor TgxMultiPolygon.Create(AOwner: TComponent);
- begin
- inherited;
- FParts := [ppTop, ppBottom];
- end;
- procedure TgxMultiPolygon.Assign(Source: TPersistent);
- begin
- if Source is TgxMultiPolygon then
- begin
- FParts := TgxMultiPolygon(Source).FParts;
- end;
- inherited;
- end;
- procedure TgxMultiPolygon.BuildList(var rci: TgxRenderContextInfo);
- var
- normal: TAffineVector;
- begin
- if (Outline.Count < 1) then
- Exit;
- normal := ContoursNormal;
- // Render
- // tessellate top polygon
- if ppTop in FParts then
- RenderTesselatedPolygon(True, @normal, False);
- // tessellate bottom polygon
- if ppBottom in FParts then
- begin
- NegateVector(normal);
- RenderTesselatedPolygon(True, @normal, True)
- end;
- end;
- procedure TgxMultiPolygon.SetParts(const value: TgxPolygonParts);
- begin
- if FParts <> value then
- begin
- FParts := value;
- StructureChanged;
- end;
- end;
- procedure TgxMultiPolygonBase.SetContoursNormal(const Value: TAffineVector);
- begin
- FContoursNormal := Value;
- end;
- function TgxMultiPolygonBase.AxisAlignedDimensionsUnscaled: TVector4f;
- var
- dMin, dMax: TAffineVector;
- begin
- if FAxisAlignedDimensionsCache.X < 0 then
- begin
- Contours.GetExtents(dMin, dMax);
- FAxisAlignedDimensionsCache.X := MaxFloat(Abs(dMin.X), Abs(dMax.X));
- FAxisAlignedDimensionsCache.Y := MaxFloat(Abs(dMin.Y), Abs(dMax.Y));
- FAxisAlignedDimensionsCache.Z := MaxFloat(Abs(dMin.Z), Abs(dMax.Z));
- end;
- SetVector(Result, FAxisAlignedDimensionsCache);
- end;
- procedure TgxMultiPolygonBase.StructureChanged;
- begin
- FAxisAlignedDimensionsCache.X := -1;
- inherited;
- end;
- // ------------------
- // ------------------ TgxContourNodes ------------------
- // ------------------
- procedure TgxContourNodes.NotifyChange;
- begin
- if (GetOwner <> nil) then
- (GetOwner as TgxContour).Changed(False);
- end;
- //-------------------------------------------------------------
- initialization
- //-------------------------------------------------------------
- RegisterClass(TgxMultiPolygon);
- end.
|