From d619b1ce42707bd6bc514bcdaaa68f59b44614ad Mon Sep 17 00:00:00 2001 From: Olly Date: Mon, 6 Jan 2025 02:09:13 +0000 Subject: [PATCH] Add polygon type --- .../simba.ide_codetools_includes.pas | 2 +- .../simba.ide_codetools_keywords.pas | 2 +- .../simba.ide_codetools_pasparser.pas | 2 +- Source/ide/simba.form_functionlist.pas | 2 +- Source/ide/simba.ide_editor_docgenerator.pas | 2 +- Source/script/imports/simba.import_base.pas | 4 +- Source/script/imports/simba.import_box.pas | 77 +-- Source/script/imports/simba.import_circle.pas | 57 -- .../imports/simba.import_externalcanvas.pas | 3 +- Source/script/imports/simba.import_image.pas | 27 +- .../script/imports/simba.import_imagebox.pas | 3 +- Source/script/imports/simba.import_math.pas | 178 ------- Source/script/imports/simba.import_point.pas | 134 +---- .../script/imports/simba.import_polygon.pas | 210 ++++++++ Source/script/imports/simba.import_quad.pas | 40 +- Source/script/imports/simba.import_target.pas | 3 +- .../script/imports/simba.import_triangle.pas | 33 +- Source/script/simba.script_imports.pas | 5 +- Source/script/simba.script_threading.pas | 2 +- Source/simba.base.pas | 35 +- Source/simba.component_imageboxcanvas.pas | 3 +- Source/simba.component_shapebox.pas | 4 +- Source/simba.container_kdtree.pas | 3 - Source/simba.externalcanvas.pas | 5 +- Source/simba.geometry.pas | 487 +++--------------- Source/simba.image.pas | 39 +- Source/simba.image_draw.pas | 42 +- Source/simba.math.pas | 1 - Source/simba.target.pas | 5 +- Source/simba.vartype_box.pas | 56 +- Source/simba.vartype_circle.pas | 20 +- Source/simba.vartype_point.pas | 43 +- Source/simba.vartype_pointarray.pas | 258 +++++----- Source/simba.vartype_polygon.pas | 346 +++++++++++++ Source/simba.vartype_quad.pas | 75 +-- Source/simba.vartype_triangle.pas | 77 +-- Tests/point.simba | 21 - Tests/{box.simba => shape_box.simba} | 0 Tests/{circle.simba => shape_circle.simba} | 0 Tests/shape_polygon.simba | 40 ++ Tests/{quad.simba => shape_quad.simba} | 3 +- Tests/tpa_exclude.simba | 13 + 42 files changed, 995 insertions(+), 1367 deletions(-) create mode 100644 Source/script/imports/simba.import_polygon.pas create mode 100644 Source/simba.vartype_polygon.pas delete mode 100644 Tests/point.simba rename Tests/{box.simba => shape_box.simba} (100%) rename Tests/{circle.simba => shape_circle.simba} (100%) create mode 100644 Tests/shape_polygon.simba rename Tests/{quad.simba => shape_quad.simba} (81%) diff --git a/Source/ide/codetools/simba.ide_codetools_includes.pas b/Source/ide/codetools/simba.ide_codetools_includes.pas index 3de00c180..2381d9be8 100644 --- a/Source/ide/codetools/simba.ide_codetools_includes.pas +++ b/Source/ide/codetools/simba.ide_codetools_includes.pas @@ -14,7 +14,7 @@ interface uses Classes, SysUtils, simba.base, simba.threading, - simba.ide_codetools_base, simba.ide_codetools_paslexer, simba.ide_codetools_parser; + simba.ide_codetools_paslexer, simba.ide_codetools_parser; const PurgeThreshold = 35; // If cache miss reaches of a include reaches this, remove the cache diff --git a/Source/ide/codetools/simba.ide_codetools_keywords.pas b/Source/ide/codetools/simba.ide_codetools_keywords.pas index 8908b20f7..58ef565c9 100644 --- a/Source/ide/codetools/simba.ide_codetools_keywords.pas +++ b/Source/ide/codetools/simba.ide_codetools_keywords.pas @@ -13,7 +13,7 @@ interface uses Classes, SysUtils, - simba.base, simba.ide_codetools_base, simba.ide_codetools_parser, simba.ide_initialization; + simba.base, simba.ide_codetools_parser, simba.ide_initialization; function GetKeywords: TDeclarationArray; diff --git a/Source/ide/codetools/simba.ide_codetools_pasparser.pas b/Source/ide/codetools/simba.ide_codetools_pasparser.pas index 20f6cba63..15331f2a5 100644 --- a/Source/ide/codetools/simba.ide_codetools_pasparser.pas +++ b/Source/ide/codetools/simba.ide_codetools_pasparser.pas @@ -11,7 +11,7 @@ interface uses SysUtils, Classes, - simba.ide_codetools_base, simba.ide_codetools_paslexer; + simba.ide_codetools_paslexer; type TPasParser = class(TObject) diff --git a/Source/ide/simba.form_functionlist.pas b/Source/ide/simba.form_functionlist.pas index f453a1303..48e3263fe 100644 --- a/Source/ide/simba.form_functionlist.pas +++ b/Source/ide/simba.form_functionlist.pas @@ -11,7 +11,7 @@ interface uses Classes, SysUtils, Forms, Controls, ComCtrls, ExtCtrls, Menus, StrUtils, - simba.base, simba.ide_codetools_base, simba.ide_codetools_parser, simba.ide_codetools_insight, + simba.base, simba.ide_codetools_parser, simba.ide_codetools_insight, simba.component_treeview, simba.container_dict; type diff --git a/Source/ide/simba.ide_editor_docgenerator.pas b/Source/ide/simba.ide_editor_docgenerator.pas index 60f74fcaa..bfb848da7 100644 --- a/Source/ide/simba.ide_editor_docgenerator.pas +++ b/Source/ide/simba.ide_editor_docgenerator.pas @@ -38,7 +38,7 @@ TSimbaEditorPlugin_DocGenerator = class(TLazSynEditPlugin) implementation uses - simba.ide_codetools_base, simba.ide_codetools_parser, simba.settings, + simba.ide_codetools_parser, simba.settings, simba.dialog; procedure TSimbaEditorPlugin_DocGenerator.DoEditorAdded(Value: TCustomSynEdit); diff --git a/Source/script/imports/simba.import_base.pas b/Source/script/imports/simba.import_base.pas index 80a503013..aad72ee30 100644 --- a/Source/script/imports/simba.import_base.pas +++ b/Source/script/imports/simba.import_base.pas @@ -16,8 +16,8 @@ implementation uses Graphics, Variants, simba.nativeinterface, simba.env, simba.baseclass, simba.vartype_ordarray, - simba.vartype_string, simba.vartype_pointarray, simba.vartype_matrix, - simba.vartype_box, simba.array_algorithm; + simba.vartype_string, simba.vartype_pointarray, + simba.vartype_box; (* Base diff --git a/Source/script/imports/simba.import_box.pas b/Source/script/imports/simba.import_box.pas index a7e193dd2..5c303147c 100644 --- a/Source/script/imports/simba.import_box.pas +++ b/Source/script/imports/simba.import_box.pas @@ -147,70 +147,18 @@ procedure _LapeBox_Expand4(const Params: PParamArray; const Result: Pointer); LA PBox(Result)^ := PBox(Params^[0])^.Expand(PInteger(Params^[1])^, PInteger(Params^[2])^, PBox(Params^[3])^); end; -(* -TBox.Extract ------------- -``` -function TBox.Extract(Points: TPointArray): TPointArray; -``` - -Returns all points that *are inside* the box. -*) -procedure _LapeBox_Extract(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PPointArray(Result)^ := PBox(Params^[0])^.Extract(PPointArray(Params^[1])^); -end; - -(* -TBox.Exclude ------------- -``` -function TBox.Exclude(Points: TPointArray): TPointArray; -``` - -Returns all points that are *not inside* the box. -*) -procedure _LapeBox_Exclude(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PPointArray(Result)^ := PBox(Params^[0])^.Exclude(PPointArray(Params^[1])^); -end; - (* TBox.Contains ------------- ``` -function TBox.Contains(Other: TBox): Boolean; +function TBox.Contains(Point: TPoint): Boolean; ``` *) -procedure _LapeBox_Contains1(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := PBox(Params^[0])^.Contains(PBox(Params^[1])^); -end; - -(* -TBox.Contains -------------- -``` -function TBox.Contains(Other: TPoint): Boolean; -``` -*) -procedure _LapeBox_Contains2(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeBox_Contains(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin PBoolean(Result)^ := PBox(Params^[0])^.Contains(PPoint(Params^[1])^); end; -(* -TBox.Contains -------------- -``` -function TBox.Contains(Other: TQuad): Boolean; -``` -*) -procedure _LapeBox_Contains3(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := PBox(Params^[0])^.Contains(PQuad(Params^[1])^); -end; - (* TBox.Partition -------------- @@ -355,20 +303,6 @@ procedure _LapeBox_Corners(const Params: PParamArray; const Result: Pointer); LA PPointArray(Result)^ := PBox(Params^[0])^.Corners(); end; -(* -TBox.ToQuad ------------ -``` -function TBox.ToQuad: TQuad; -``` - -Converts the TBox to a TQuad type. -*) -procedure _LapeBox_ToQuad(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PQuad(Result)^ := PBox(Params^[0])^.ToQuad(); -end; - (* TBox.RandomPoint ---------------- @@ -665,16 +599,11 @@ procedure ImportBox(Script: TSimbaScript); addGlobalFunc('function TBox.Expand(SizeMod: Integer; MaxBounds: TBox): TBox; overload;', @_LapeBox_Expand3); addGlobalFunc('function TBox.Expand(WidMod, HeiMod: Integer): TBox; overload;', @_LapeBox_Expand2); addGlobalFunc('function TBox.Expand(WidMod, HeiMod: Integer; MaxBounds: TBox): TBox; overload;', @_LapeBox_Expand4); - addGlobalFunc('function TBox.Extract(Points: TPointArray): TPointArray', @_LapeBox_Extract); - addGlobalFunc('function TBox.Exclude(Points: TPointArray): TPointArray', @_LapeBox_Exclude); - addGlobalFunc('function TBox.Contains(Other: TBox): Boolean; overload;', @_LapeBox_Contains1); - addGlobalFunc('function TBox.Contains(Other: TPoint): Boolean; overload;', @_LapeBox_Contains2); - addGlobalFunc('function TBox.Contains(Other: TQuad): Boolean; overload;', @_LapeBox_Contains3); + addGlobalFunc('function TBox.Contains(Point: TPoint): Boolean;', @_LapeBox_Contains); addGlobalFunc('function TBox.Partition(Rows, Cols: Integer): TBoxArray;', @_LapeBox_Partition); addGlobalFunc('function TBox.Offset(P: TPoint): TBox; overload;', @_LapeBox_Offset1); addGlobalFunc('function TBox.Combine(Other: TBox): TBox;', @_LapeBox_Combine); addGlobalFunc('function TBox.Invert(Space: TBox): TBoxArray;', @_LapeBox_Invert); - addGlobalFunc('function TBox.ToQuad: TQuad;', @_LapeBox_ToQuad); addGlobalFunc('function TBox.NearestEdge(P: TPoint): TPoint;', @_LapeBox_NearestEdge); addGlobalFunc('function TBox.Intersect(P: TPoint): TPoint;', @_LapeBox_Intersect); diff --git a/Source/script/imports/simba.import_circle.pas b/Source/script/imports/simba.import_circle.pas index 14a8d89a4..c0418e052 100644 --- a/Source/script/imports/simba.import_circle.pas +++ b/Source/script/imports/simba.import_circle.pas @@ -51,31 +51,6 @@ procedure _LapeCircle_CreateFromPoints(const Params: PParamArray; const Result: PCircle(Result)^ := TCircle.CreateFromPoints(PPointArray(Params^[0])^); end; - -(* -TCircle.Edge ------------- -``` -function TCircle.Edge: TPointArray; -``` -*) -procedure _LapeCircle_Edge_Read(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PPointArray(Result)^ := PCircle(Params^[0])^.Edge; -end; - -(* -TCircle.Filled --------------- -``` -function TCircle.Filled: TPointArray; -``` -*) -procedure _LapeCircle_Filled_Read(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PPointArray(Result)^ := PCircle(Params^[0])^.Filled; -end; - (* TCircle.Bounds -------------- @@ -222,34 +197,6 @@ procedure _LapeCircle_Offset(const Params: PParamArray; const Result: Pointer); PCircle(Result)^ := PCircle(Params^[0])^.Offset(PPoint(Params^[1])^); end; -(* -TCircle.Extract ---------------- -``` -function TCircle.Extract(Points: TPointArray): TPointArray; -``` - -Returns the points that **are** in the circle. -*) -procedure _LapeCircle_Extract(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PPointArray(Result)^ := PCircle(Params^[0])^.Extract(PPointArray(Params^[1])^); -end; - -(* -TCircle.Exclude ---------------- -``` -function TCircle.Exclude(Points: TPointArray): TPointArray; -``` - -Returns the points that are not inside the circle. -*) -procedure _LapeCircle_Exclude(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PPointArray(Result)^ := PCircle(Params^[0])^.Exclude(PPointArray(Params^[1])^); -end; - (* in -- @@ -281,15 +228,11 @@ procedure ImportCircle(Script: TSimbaScript); addGlobalFunc('function TCircle.Circularity(TPA: TPointArray): Double', @_LapeCircle_Circularity); addGlobalFunc('function TCircle.Expand(Amount: Integer): TCircle', @_LapeCircle_Expand); addGlobalFunc('function TCircle.Offset(P: TPoint): TCircle', @_LapeCircle_Offset); - addGlobalFunc('function TCircle.Extract(Points: TPointArray): TPointArray', @_LapeCircle_Extract); - addGlobalFunc('function TCircle.Exclude(Points: TPointArray): TPointArray', @_LapeCircle_Exclude); addProperty('TCircle', 'Circumference', 'Double', @_LapeCircle_Circumference_Read); addProperty('TCircle', 'Area', 'Double', @_LapeCircle_Area_Read); addProperty('TCircle', 'Center', 'TPoint', @_LapeCircle_Center_Read); addProperty('TCircle', 'Bounds', 'TBox', @_LapeCircle_Bounds_Read); - addProperty('TCircle', 'Edge', 'TPointArray', @_LapeCircle_Edge_Read); - addProperty('TCircle', 'Filled', 'TPointArray', @_LapeCircle_Filled_Read); addGlobalFunc('operator in(Left: TPoint; Right: TCircle): Boolean;', @_LapePoint_IN_Cicle); diff --git a/Source/script/imports/simba.import_externalcanvas.pas b/Source/script/imports/simba.import_externalcanvas.pas index fd5b60ca5..73708d08c 100644 --- a/Source/script/imports/simba.import_externalcanvas.pas +++ b/Source/script/imports/simba.import_externalcanvas.pas @@ -14,7 +14,8 @@ implementation uses lptypes, - simba.image, simba.image_textdrawer, simba.externalcanvas; + simba.image, simba.image_textdrawer, simba.externalcanvas, + simba.vartype_quad; type PSimbaExternalCanvas = ^TSimbaExternalCanvas; diff --git a/Source/script/imports/simba.import_image.pas b/Source/script/imports/simba.import_image.pas index c85e656cb..31ec2d794 100644 --- a/Source/script/imports/simba.import_image.pas +++ b/Source/script/imports/simba.import_image.pas @@ -15,7 +15,8 @@ implementation uses Graphics, lptypes, - simba.image, simba.image_textdrawer, simba.colormath, simba.dtm; + simba.image, simba.image_textdrawer, simba.colormath, + simba.vartype_polygon, simba.vartype_quad, simba.vartype_circle; type PBitmap = ^TBitmap; @@ -1038,36 +1039,36 @@ procedure _LapeImage_DrawBoxInverted(const Params: PParamArray); LAPE_WRAPPER_CA TImage.DrawPolygon ------------------ ``` -procedure TImage.DrawPolygon(Points: TPointArray); +procedure TImage.DrawPolygon(Points: TPolygon); ``` *) procedure _LapeImage_DrawPolygon(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawPolygon(PPointArray(Params^[1])^); + PSimbaImage(Params^[0])^.DrawPolygon(PPolygon(Params^[1])^); end; (* TImage.DrawPolygonFilled ------------------------ ``` -procedure TImage.DrawPolygonFilled(Points: TPointArray); +procedure TImage.DrawPolygonFilled(Points: TPolygon); ``` *) procedure _LapeImage_DrawPolygonFilled(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawPolygonFilled(PPointArray(Params^[1])^); + PSimbaImage(Params^[0])^.DrawPolygonFilled(PPolygon(Params^[1])^); end; (* TImage.DrawPolygonInverted -------------------------- ``` -procedure TImage.DrawPolygonInverted(Points: TPointArray); +procedure TImage.DrawPolygonInverted(Points: TPolygon); ``` *) procedure _LapeImage_DrawPolygonInverted(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawPolygonInverted(PPointArray(Params^[1])^); + PSimbaImage(Params^[0])^.DrawPolygonInverted(PPolygon(Params^[1])^); end; (* @@ -1231,12 +1232,12 @@ procedure _LapeImage_DrawBoxArray(const Params: PParamArray); LAPE_WRAPPER_CALLI TImage.DrawPolygonArray ----------------------- ``` -procedure TImage.DrawPolygonArray(Polygons: T2DPointArray; Filled: Boolean); +procedure TImage.DrawPolygonArray(Polygons: TPolygonArray; Filled: Boolean); ``` *) procedure _LapeImage_DrawPolygonArray(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawPolygonArray(P2DPointArray(Params^[1])^, PBoolean(Params^[2])^); + PSimbaImage(Params^[0])^.DrawPolygonArray(PPolygonArray(Params^[1])^, PBoolean(Params^[2])^); end; (* @@ -1922,9 +1923,9 @@ procedure ImportSimbaImage(Script: TSimbaScript); addGlobalFunc('procedure TImage.DrawBoxFilled(B: TBox);', @_LapeImage_DrawBoxFilled); addGlobalFunc('procedure TImage.DrawBoxInverted(B: TBox);', @_LapeImage_DrawBoxInverted); - addGlobalFunc('procedure TImage.DrawPolygon(Points: TPointArray);', @_LapeImage_DrawPolygon); - addGlobalFunc('procedure TImage.DrawPolygonFilled(Points: TPointArray);', @_LapeImage_DrawPolygonFilled); - addGlobalFunc('procedure TImage.DrawPolygonInverted(Points: TPointArray);', @_LapeImage_DrawPolygonInverted); + addGlobalFunc('procedure TImage.DrawPolygon(Points: TPolygon);', @_LapeImage_DrawPolygon); + addGlobalFunc('procedure TImage.DrawPolygonFilled(Points: TPolygon);', @_LapeImage_DrawPolygonFilled); + addGlobalFunc('procedure TImage.DrawPolygonInverted(Points: TPolygon);', @_LapeImage_DrawPolygonInverted); addGlobalFunc('procedure TImage.DrawQuad(Quad: TQuad);', @_LapeImage_DrawQuad); addGlobalFunc('procedure TImage.DrawQuadFilled(Quad: TQuad);', @_LapeImage_DrawQuadFilled); @@ -1945,7 +1946,7 @@ procedure ImportSimbaImage(Script: TSimbaScript); addGlobalFunc('procedure TImage.DrawQuadArray(Quads: TQuadArray; Filled: Boolean);', @_LapeImage_DrawQuadArray); addGlobalFunc('procedure TImage.DrawBoxArray(Boxes: TBoxArray; Filled: Boolean);', @_LapeImage_DrawBoxArray); - addGlobalFunc('procedure TImage.DrawPolygonArray(Polygons: T2DPointArray; Filled: Boolean);', @_LapeImage_DrawPolygonArray); + addGlobalFunc('procedure TImage.DrawPolygonArray(Polygons: TPolygonArray; Filled: Boolean);', @_LapeImage_DrawPolygonArray); addGlobalFunc('procedure TImage.DrawCircleArray(Centers: TPointArray; Radius: Integer; Filled: Boolean);', @_LapeImage_DrawCircleArray1); addGlobalFunc('procedure TImage.DrawCrossArray(Points: TPointArray; Radius: Integer);', @_LapeImage_DrawCrossArray); diff --git a/Source/script/imports/simba.import_imagebox.pas b/Source/script/imports/simba.import_imagebox.pas index 04a2033a7..4d2d1075c 100644 --- a/Source/script/imports/simba.import_imagebox.pas +++ b/Source/script/imports/simba.import_imagebox.pas @@ -17,7 +17,8 @@ implementation simba.component_imagebox, simba.component_imageboxcanvas, simba.image, simba.image_textdrawer, simba.dtm, simba.colormath, - simba.target; + simba.target, + simba.vartype_quad; type PSimbaTarget = ^TSimbaTarget; diff --git a/Source/script/imports/simba.import_math.pas b/Source/script/imports/simba.import_math.pas index 163931596..aee64cd55 100644 --- a/Source/script/imports/simba.import_math.pas +++ b/Source/script/imports/simba.import_math.pas @@ -214,86 +214,6 @@ procedure _LapeDeltaAngle(const Params: PParamArray; const Result: Pointer); LAP PDouble(Result)^ := TSimbaGeometry.DeltaAngle(PDouble(Params^[0])^, PDouble(Params^[1])^, PDouble(Params^[2])^); end; -(* -ExpandPolygon -------------- -``` -function ExpandPolygon(const Polygon: TPointArray; Amount: Integer): TPointArray; -``` -*) -procedure _LapeExpandPolygon(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PPointArray(Result)^ := TSimbaGeometry.ExpandPolygon(PPointArray(Params^[0])^, PInteger(Params^[1])^); -end; - -(* -PolygonArea ------------ -``` -function PolygonArea(const Polygon: TPointArray): Double; -``` -*) -procedure _LapePolygonArea(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PDouble(Result)^ := TSimbaGeometry.PolygonArea(PPointArray(Params^[0])^); -end; - -(* -IsConvexPolygon ------------ -> function IsConvexPolygon(Polygon: TPointArray): Boolean; - -Returns if the polygon is convex, order does not matter. A concave polygon will return False. -*) -procedure _LapeIsConvexPolygon(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := TSimbaGeometry.IsConvexPolygon(PPointArray(Params^[0])^); -end; - -(* -TriangulatePolygon ------------ -> function TriangulatePolygon(Polygon: TPointArray; MinArea: Double=0; MaxDepth: Int32=0): TTriangleArray; - -Break the polygon into triangles, the smallest possible polygon. The order of the -input does matter, if it fails, try to reverse the Poly with Poly.Reversed() - -This is a custom algorithm by slacky, based around the concept of trimming "ears", -if you dont like the output, you may have more luck with rolling the Polygon before calling. - -Two default params exists as well, `MinArea` and `MaxDepth`, they work in tandom, -`MinArea` parameter is for setting a minimum size of triangles added to result, and as this method -works iteratively, removing triangles in a circle around the shape over and over, `MaxDepth` refers -to the max number of rounds it has moved around the shape before it ignores `MinArea` paramater. -*) -procedure _LapeTriangulatePolygon(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PTriangleArray(Result)^ := TSimbaGeometry.TriangulatePolygon(PPointArray(Params^[0])^, PSingle(Params^[1])^, PInt32(Params^[2])^); -end; - -(* -LineInPolygon ------------ -> function LineInPolygon(p,q: TPoint; const Polygon: TPointArray): Boolean; - -Returns True if the line fits within the bounds of the polygon. -*) -procedure _LapeLineInPolygon(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := TSimbaGeometry.LineInPolygon(PPoint(Params^[0])^, PPoint(Params^[1])^, PPointArray(Params^[2])^); -end; - -(* -FurthestPointsPolygon ------------ -> procedure FurthestPointsPolygon(const Polygon: TPointArray; out A,B: TPoint); - -Returns the two points that are furthest away from eachother in a polygon. -*) -procedure _LapeFurthestPointsPolygon(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV -begin - TSimbaGeometry.FurthestPointsPolygon(PPointArray(Params^[0])^, PPoint(Params^[1])^, PPoint(Params^[2])^); -end; (* CrossProduct @@ -367,88 +287,6 @@ procedure _LapeDistToLine2(const Params: PParamArray; const Result: Pointer); LA PDouble(Result)^ := TSimbaGeometry.DistToLine(PPoint(Params^[0])^, PPoint(Params^[1])^, PPoint(Params^[2])^); end; -(* -PointInTriangle ---------------- -``` -function PointInTriangle(const P, P1, P2, P3: TPoint): Boolean; -``` -*) -procedure _LapePointInTriangle1(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := TSimbaGeometry.PointInTriangle(PPoint(Params^[0])^, PPoint(Params^[1])^, PPoint(Params^[2])^, PPoint(Params^[3])^); -end; - -(* -PointInTriangle ---------------- -> function PointInTriangle(const P: TPoint; const Triangle: TTriangle): Boolean; -*) -procedure _LapePointInTriangle2(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := TSimbaGeometry.PointInTriangle(PPoint(Params^[0])^, PTriangle(Params^[1])^.A, PTriangle(Params^[1])^.B, PTriangle(Params^[1])^.C); -end; - -(* -PointInBox ----------- -``` -function PointInBox(const P: TPoint; const Box: TBox): Boolean; -``` -*) -procedure _LapePointInBox(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := TSimbaGeometry.PointInBox(PPoint(Params^[0])^, PBox(Params^[1])^); -end; - -(* -PointInQuad ------------ -``` -function PointInQuad(const P: TPoint; const A,B,C,D: TPoint): Boolean; -``` -*) -procedure _LapePointInQuad(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := TSimbaGeometry.PointInQuad(PPoint(Params^[0])^, PPoint(Params^[1])^, PPoint(Params^[2])^, PPoint(Params^[3])^, PPoint(Params^[4])^); -end; - -(* -PointInPolygon --------------- -``` -function PointInPolygon(const P: TPoint; const Polygon: TPointArray): Boolean; -``` -*) -procedure _LapePointInPolygon(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := TSimbaGeometry.PointInPolygon(PPoint(Params^[0])^, PPointArray(Params^[1])^); -end; - -(* -PointInCircle -------------- -``` -function PointInCircle(const P, Center: TPoint; Radius: Double): Boolean; static; -``` -*) -procedure _LapePointInCircle(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := TSimbaGeometry.PointInCircle(PPoint(Params^[0])^, PPoint(Params^[1])^, PDouble(Params^[2])^); -end; - -(* -PointInEllipse --------------- -``` -function PointInEllipse(const P, Center: TPoint; const YRadius, XRadius: Double): Boolean; -``` -*) -procedure _LapePointInEllipse(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := TSimbaGeometry.PointInEllipse(PPoint(Params^[0])^, PPoint(Params^[1])^, PDouble(Params^[2])^, PDouble(Params^[3])^); -end; - (* IsNumber -------- @@ -507,15 +345,8 @@ procedure ImportMath(Script: TSimbaScript); addGlobalFunc('function Modulo(const X, Y: Integer): Integer; overload', @_LapeModulo); addGlobalFunc('function Modulo(const X, Y: Double): Double; overload', @_LapeModuloF); - - addGlobalFunc('function IsConvexPolygon(const Polygon: TPointArray): Boolean', @_LapeIsConvexPolygon); - addGlobalFunc('function TriangulatePolygon(const Polygon: TPointArray; MinArea: Single=0; MaxDepth: Int32=0): TTriangleArray', @_LapeTriangulatePolygon); - addGlobalFunc('function LineInPolygon(a1, a2: TPoint; const Polygon: TPointArray): Boolean', @_LapeLineInPolygon); - addGlobalFunc('procedure FurthestPointsPolygon(const Polygon: TPointArray; out A,B: TPoint)', @_LapeFurthestPointsPolygon); addGlobalFunc('function DeltaAngle(const DegreesA, DegreesB: Double; R: Double = 360): Double', @_LapeDeltaAngle); - addGlobalFunc('function PolygonArea(const Polygon: TPointArray): Double', @_LapePolygonArea); - addGlobalFunc('function ExpandPolygon(const Polygon: TPointArray; Amount: Integer): TPointArray', @_LapeExpandPolygon); addGlobalFunc('function CrossProduct(const r, p, q: TPoint): Int64; overload', @_LapeCrossProduct1); addGlobalFunc('function CrossProduct(const rx,ry, px,py, qx,qy: Double): Double; overload', @_LapeCrossProduct2); @@ -524,15 +355,6 @@ procedure ImportMath(Script: TSimbaScript); addGlobalFunc('function DistToLine(const P, P1, P2: TPoint; out Nearest: TPoint): Double; overload', @_LapeDistToLine1); addGlobalFunc('function DistToLine(const P, P1, P2: TPoint): Double; overload', @_LapeDistToLine2); - - addGlobalFunc('function PointInTriangle(const P, P1, P2, P3: TPoint): Boolean; overload', @_LapePointInTriangle1); - addGlobalFunc('function PointInTriangle(const P: TPoint; const Triangle: TTriangle): Boolean; overload', @_LapePointInTriangle2); - - addGlobalFunc('function PointInBox(const P: TPoint; const Box: TBox): Boolean', @_LapePointInBox); - addGlobalFunc('function PointInQuad(const P: TPoint; const A,B,C,D: TPoint): Boolean', @_LapePointInQuad); - addGlobalFunc('function PointInPolygon(const P: TPoint; const Polygon: TPointArray): Boolean', @_LapePointInPolygon); - addGlobalFunc('function PointInCircle(const P, Center: TPoint; Radius: Double): Boolean; static', @_LapePointInCircle); - addGlobalFunc('function PointInEllipse(const P, Center: TPoint; const YRadius, XRadius: Double): Boolean', @_LapePointInEllipse); DumpSection := ''; end; diff --git a/Source/script/imports/simba.import_point.pas b/Source/script/imports/simba.import_point.pas index 1f3e8172e..501376628 100644 --- a/Source/script/imports/simba.import_point.pas +++ b/Source/script/imports/simba.import_point.pas @@ -13,13 +13,17 @@ procedure ImportPoint(Script: TSimbaScript); implementation uses - simba.vartype_point, simba.vartype_pointarray, + simba.vartype_point, + simba.vartype_pointarray, + simba.vartype_polygon, + simba.vartype_quad, simba.vartype_circle, lptypes; + (* TPoint ====== -The TPoint type is a record which defines a X,Y coordinate. +The TPoint type is a record which defines a X,Y integer coordinate. *) (* @@ -46,54 +50,6 @@ procedure _LapePoint_Create(const Params: PParamArray; const Result: Pointer); L PPoint(Result)^ := TPoint.Create(PInteger(Params^[0])^, PInteger(Params^[1])^); end; -(* -TPoint.InPolygon ----------------- -``` -function TPoint.InPolygon(Poly: TPointArray): Boolean; -``` -*) -procedure _LapePoint_InPolygon(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := PPoint(Params^[0])^.InPolygon(PPointArray(Params^[1])^); -end; - -(* -TPoint.InCircle ---------------- -``` -function TPoint.InCircle(Center: TPoint; Radius: Double): Boolean; -``` -*) -procedure _LapePoint_InCircle(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := PPoint(Params^[0])^.InCircle(PPoint(Params^[1])^, PDouble(Params^[2])^); -end; - -(* -TPoint.InTriangle ------------------ -``` -function TPoint.InTriangle(A, B, C: TPoint): Boolean; -``` -*) -procedure _LapePoint_InTriangle(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := PPoint(Params^[0])^.InTriangle(PPoint(Params^[1])^, PPoint(Params^[2])^, PPoint(Params^[3])^); -end; - -(* -TPoint.InBox ------------- -``` -function TPoint.InBox(Box: TBox): Boolean; -``` -*) -procedure _LapePoint_InBox(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := PPoint(Params^[0])^.InBox(PBox(Params^[1])^); -end; - (* TPoint.DistanceTo ----------------- @@ -118,18 +74,6 @@ procedure _LapePoint_Rotate(const Params: PParamArray; const Result: Pointer); L PPoint(Result)^ := PPoint(Params^[0])^.Rotate(PDouble(Params^[1])^, PPoint(Params^[2])^); end; -(* -TPoint.RotateFast ------------------ -``` -function TPoint.RotateFast(Degrees: Integer; Center: TPoint): TPoint; -``` -*) -procedure _LapePoint_RotateFast(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PPoint(Result)^ := PPoint(Params^[0])^.RotateFast(PInteger(Params^[1])^, PPoint(Params^[2])^); -end; - (* TPoint.Magnitude ---------------- @@ -288,29 +232,12 @@ procedure _LapePoint_MinusAssign_Point(const Params: PParamArray; const Result: PPoint(Result)^ := PPoint(Params^[0])^; end; -(* -TPoint in ---------- -``` -operator in(Left: TPoint; Right: TBox): Boolean; -``` -*) -procedure _LapePoint_IN_Box(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := PPoint(Params^[0])^ in PBox(Params^[1])^; -end; - (* TPointArray =========== Methods relating to point arrays. *) -procedure _Lape_Point_Remove(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PPointArray(Result)^ := PPointArray(Params^[0])^.Remove(PPointArray(Params^[1])^); -end; - (* TPointArray.CreateFromBox ------------------------- @@ -768,7 +695,7 @@ procedure _LapeTPAExcludeDist(const Params: PParamArray; const Result: Pointer); function TPointArray.ExcludePoints(Points: TPointArray): TPointArray; ``` -Returns all points from `Self` that are **not** inside the `Points` array. +Returns all points from `Self` that are **not** in the `Points` array. *) procedure _LapeTPAExcludePoints(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin @@ -779,7 +706,7 @@ procedure _LapeTPAExcludePoints(const Params: PParamArray; const Result: Pointer TPointArray.ExcludePolygon -------------------------- ``` -function TPointArray.ExcludePolygon(Polygon: TPointArray): TPointArray; +function TPointArray.ExcludePolygon(Polygon: TPolygon): TPointArray; ``` Returns all points from `Self` that are **not** inside the polygon. @@ -843,7 +770,7 @@ procedure _LapeTPAExtractDist(const Params: PParamArray; const Result: Pointer); TPointArray.ExtractPolygon -------------------------- ``` -function TPointArray.ExtractPolygon(Polygon: TPointArray): TPointArray; +function TPointArray.ExtractPolygon(Polygon: TPolygon): TPointArray; ``` Returns all points from `Self` that are **inside** the polygon. @@ -935,12 +862,12 @@ procedure _LapeTPAEdges(const Params: PParamArray; const Result: Pointer); LAPE_ TPointArray.ConvexHull ---------------------- ``` -function TPointArray.ConvexHull: TPointArray; +function TPointArray.ConvexHull: TPolygon; ``` *) procedure _LapeTPAConvexHull(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin - PPointArray(Result)^ := PPointArray(Params^[0])^.ConvexHull(); + PPolygon(Result)^ := PPointArray(Params^[0])^.ConvexHull(); end; (* @@ -1149,40 +1076,28 @@ procedure _LapeTPACircularity(const Params: PParamArray; const Result: Pointer); PDouble(Result)^ := PPointArray(Params^[0])^.Circularity(); end; -(* -TPointArray.DouglasPeucker --------------------------- -``` -function TPointArray.DouglasPeucker(epsilon: Double): TPointArray; -``` -*) -procedure _LapeTPADouglasPeucker(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PPointArray(Result)^ := PPointArray(Params^[0])^.DouglasPeucker(PDouble(Params^[1])^); -end; - (* TPointArray.ConcaveHull ----------------------- ``` -function TPointArray.ConcaveHull(Epsilon:Double=2.5; kCount:Int32=5): TPointArray; +function TPointArray.ConcaveHull(Epsilon:Double=2.5; kCount:Int32=5): TPolygon; ``` *) procedure _LapeTPAConcaveHull(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin - PPointArray(Result)^ := PPointArray(Params^[0])^.ConcaveHull(PDouble(Params^[1])^, PInteger(Params^[2])^); + PPolygon(Result)^ := PPointArray(Params^[0])^.ConcaveHull(PDouble(Params^[1])^, PInteger(Params^[2])^); end; (* TPointArray.ConcaveHullEx ------------------------- ``` -function TPointArray.ConcaveHullEx(MaxLeap: Double=-1; Epsilon:Double=2): T2DPointArray; +function TPointArray.ConcaveHullEx(MaxLeap: Double=-1; Epsilon:Double=2): TPolygonArray; ``` *) procedure _LapeTPAConcaveHullEx(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin - P2DPointArray(Result)^ := PPointArray(Params^[0])^.ConcaveHullEx(PDouble(Params^[1])^, PDouble(Params^[2])^); + PPolygonArray(Result)^ := PPointArray(Params^[0])^.ConcaveHullEx(PDouble(Params^[1])^, PDouble(Params^[2])^); end; (* @@ -1602,13 +1517,8 @@ procedure ImportPoint(Script: TSimbaScript); addGlobalFunc('function Point(X, Y: Integer): TPoint', @_LapePoint); addGlobalFunc('function TPoint.Create(X, Y: Integer): TPoint; static;', @_LapePoint_Create); - addGlobalFunc('function TPoint.InTriangle(A, B, C: TPoint): Boolean;', @_LapePoint_InTriangle); - addGlobalFunc('function TPoint.InPolygon(Poly: TPointArray): Boolean;', @_LapePoint_InPolygon); - addGlobalFunc('function TPoint.InCircle(Center: TPoint; Radius: Double): Boolean;', @_LapePoint_InCircle); - addGlobalFunc('function TPoint.InBox(Box: TBox): Boolean;', @_LapePoint_InBox); addGlobalFunc('function TPoint.DistanceTo(Other: TPoint): Double;', @_LapePoint_DistanceTo); addGlobalFunc('function TPoint.Rotate(Radians: Double; Center: TPoint): TPoint;', @_LapePoint_Rotate); - addGlobalFunc('function TPoint.RotateFast(Degrees: Integer; Center: TPoint): TPoint;', @_LapePoint_RotateFast); addGlobalFunc('function TPoint.Magnitude: Double;', @_LapePoint_Magnitude); addGlobalFunc('function TPoint.AngleBetween(Other: TPoint): Double;', @_LapePoint_AngleBetween); addGlobalFunc('function TPoint.Offset(X, Y: Integer): TPoint; overload;', @_LapePoint_Offset1); @@ -1624,7 +1534,6 @@ procedure ImportPoint(Script: TSimbaScript); addGlobalFunc('operator -= (var L: TPoint; R: TPoint): TPoint;', @_LapePoint_MinusAssign_Point); addGlobalFunc('operator * (L: TPoint; R: Double): TPoint;', @_LapePoint_Multiply_Double); addGlobalFunc('operator *= (var L: TPoint; R: Double): TPoint;', @_LapePoint_MultiplyAssign_Double); - addGlobalFunc('operator in(Left: TPoint; Right: TBox): Boolean;', @_LapePoint_IN_Box); DumpSection := 'TPointArray'; @@ -1639,12 +1548,12 @@ procedure ImportPoint(Script: TSimbaScript); addGlobalFunc('function TPointArray.ExcludePie(StartDegree, EndDegree, MinRadius, MaxRadius: Single; Center: TPoint): TPointArray;', @_LapeTPAExcludePie); addGlobalFunc('function TPointArray.ExcludeDist(Center: TPoint; MinDist, MaxDist: Double): TPointArray', @_LapeTPAExcludeDist); addGlobalFunc('function TPointArray.ExcludePoints(Points: TPointArray): TPointArray', @_LapeTPAExcludePoints); - addGlobalFunc('function TPointArray.ExcludePolygon(Polygon: TPointArray): TPointArray', @_LapeTPAExcludePolygon); + addGlobalFunc('function TPointArray.ExcludePolygon(Polygon: TPolygon): TPointArray', @_LapeTPAExcludePolygon); addGlobalFunc('function TPointArray.ExcludeBox(Box: TBox): TPointArray', @_LapeTPAExcludeBox); addGlobalFunc('function TPointArray.ExcludeQuad(Quad: TQuad): TPointArray', @_LapeTPAExcludeQuad); addGlobalFunc('function TPointArray.ExtractDist(Center: TPoint; MinDist, MaxDist: Single): TPointArray', @_LapeTPAExtractDist); - addGlobalFunc('function TPointArray.ExtractPolygon(Polygon: TPointArray): TPointArray', @_LapeTPAExtractPolygon); + addGlobalFunc('function TPointArray.ExtractPolygon(Polygon: TPolygon): TPointArray', @_LapeTPAExtractPolygon); addGlobalFunc('function TPointArray.ExtractBox(Box: TBox): TPointArray', @_LapeTPAExtractBox); addGlobalFunc('function TPointArray.ExtractQuad(Quad: TQuad): TPointArray', @_LapeTPAExtractQuad); addGlobalFunc('function TPointArray.ExtractPie(StartDegree, EndDegree, MinRadius, MaxRadius: Single; Center: TPoint): TPointArray', @_LapeTPAExtractPie); @@ -1652,7 +1561,7 @@ procedure ImportPoint(Script: TSimbaScript); addGlobalFunc('function TPointArray.Skeleton(FMin: Integer = 2; FMax: Integer = 6): TPointArray;', @_LapeTPASkeleton); addGlobalFunc('function TPointArray.Border: TPointArray;', @_LapeTPABorder); addGlobalFunc('function TPointArray.Edges: TPointArray;', @_LapeTPAEdges); - addGlobalFunc('function TPointArray.ConvexHull: TPointArray;', @_LapeTPAConvexHull); + addGlobalFunc('function TPointArray.ConvexHull: TPolygon;', @_LapeTPAConvexHull); addGlobalFunc('function TPointArray.Erode(Iterations: Integer): TPointArray;', @_LapeTPAErode); addGlobalFunc('function TPointArray.Grow(Iterations: Integer): TPointArray;', @_LapeTPAGrow); @@ -1710,16 +1619,13 @@ procedure ImportPoint(Script: TSimbaScript); addGlobalFunc('function TPointArray.PartitionEx(BoxWidth, BoxHeight: Integer): T2DPointArray; overload', @_LapeTPAPartitionEx1); addGlobalFunc('function TPointArray.PartitionEx(StartPoint: TPoint; BoxWidth, BoxHeight: Integer): T2DPointArray; overload', @_LapeTPAPartitionEx2); - addGlobalFunc('function TPointArray.Remove(Points: TPointArray): TPointArray; overload', @_Lape_Point_Remove); - addGlobalFunc('function TPointArray.DistanceTransform: TSingleMatrix;', @_LapeTPADistanceTransform); addGlobalFunc('function TPointArray.QuickSkeleton(): TPointArray;', @_LapeTPAQuickSkeleton); addGlobalFunc('function TPointArray.Circularity: Double;', @_LapeTPACircularity); - addGlobalFunc('function TPointArray.DouglasPeucker(Epsilon: Double): TPointArray;', @_LapeTPADouglasPeucker); - addGlobalFunc('function TPointArray.ConcaveHull(Epsilon: Double = 2.5; kCount: Integer = 5): TPointArray;', @_LapeTPAConcaveHull); - addGlobalFunc('function TPointArray.ConcaveHullEx(MaxLeap: Double = -1; Epsilon: Double = 2): T2DPointArray;', @_LapeTPAConcaveHullEx); + addGlobalFunc('function TPointArray.ConcaveHull(Epsilon: Double = 2.5; kCount: Integer = 5): TPolygon;', @_LapeTPAConcaveHull); + addGlobalFunc('function TPointArray.ConcaveHullEx(MaxLeap: Double = -1; Epsilon: Double = 2): TPolygonArray;', @_LapeTPAConcaveHullEx); addGlobalType('enum(NONE, ALL, MINIMAL)', 'EConvexityDefects'); addGlobalFunc('function TPointArray.ConvexityDefects(Epsilon: Single = 0; Mode: EConvexityDefects = EConvexityDefects.NONE): TPointArray;', @_LapeTPAConvexityDefects); diff --git a/Source/script/imports/simba.import_polygon.pas b/Source/script/imports/simba.import_polygon.pas new file mode 100644 index 000000000..29ce6ef78 --- /dev/null +++ b/Source/script/imports/simba.import_polygon.pas @@ -0,0 +1,210 @@ +{ + Author: Raymond van Venetië and Merlijn Wajer + Project: Simba (https://github.com/MerlijnWajer/Simba) + License: GNU General Public License (https://www.gnu.org/licenses/gpl-3.0) +} +unit simba.import_polygon; + +{$i simba.inc} + +interface + +uses + Classes, SysUtils, + simba.base, simba.script; + +procedure ImportPolygon(Script: TSimbaScript); + +implementation + +uses + lptypes, + simba.vartype_polygon, + simba.vartype_triangle; + +(* +Polygon +======= +Polygon type +*) + +(* +TPolygon.Bounds +--------------- +``` +function TPolygon.Bounds: TBox; +``` +*) +procedure _LapePolygon_Bounds(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PBox(Result)^ := PPolygon(Params^[0])^.Bounds; +end; + +(* +TPolygon.Mean +------------- +``` +function TPolygon.Mean: TPoint; +``` +*) +procedure _LapePolygon_Mean(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PPoint(Result)^ := PPolygon(Params^[0])^.Mean; +end; + +(* +TPolygon.Area +------------- +``` +function TPolygon.Area: Double; +``` +*) +procedure _LapePolygon_Area(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PDouble(Result)^ := PPolygon(Params^[0])^.Area; +end; + +(* +TPolygon.IsConvex +----------------- +``` +function TPolygon.IsConvex(Polygon: TPointArray): Boolean; +``` +Returns if the polygon is convex, order does not matter. A concave polygon will return False. +*) +procedure _LapePolygon_IsConvex(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PBoolean(Result)^ := PPolygon(Params^[0])^.IsConvex; +end; + +(* +TPolygon.Contains +----------------- +``` +function TPolygon.Contains(p: TPoint): Boolean; +``` +Is the point in the polygon? +*) +procedure _LapePolygon_Contains(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PBoolean(Result)^ := PPolygon(Params^[0])^.Contains(PPoint(Params^[1])^); +end; + +(* +TPolygon.ContainsLine +--------------------- +``` +function TPolygon.ContainsLine(p,q: TPoint): Boolean; +``` +Returns True if the line fits within the bounds of the polygon. +*) +procedure _LapePolygon_ContainsLine(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PBoolean(Result)^ := PPolygon(Params^[0])^.ContainsLine(PPoint(Params^[1])^, PPoint(Params^[2])^); +end; + +(* +TPolygon.NearestEdge +-------------------- +``` +function TPolygon.NearestEdge(P: TPoint): TPoint +``` +*) +procedure _LapePolygon_NearestEdge(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PPoint(Result)^ := PPolygon(Params^[0])^.NearestEdge(PPoint(Params^[1])^); +end; + +(* +TPolygon.Expand +--------------- +``` +function TPolygon.Expand(Amount: Integer): TPolygon +``` +*) +procedure _LapePolygon_Expand(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PPolygon(Result)^ := PPolygon(Params^[0])^.Expand(PInteger(Params^[1])^); +end; + +(* +TPolygon.Triangulate +-------------------- +``` +function TPolygon.Triangulate(MinArea: Double = 0; MaxDepth: Int32 = 0): TTriangleArray; +``` + +Break the polygon into triangles, the smallest possible polygon. The order of the +input does matter, if it fails, try to reverse the Poly with Poly.Reversed() + +This is a custom algorithm by slacky, based around the concept of trimming "ears", +if you dont like the output, you may have more luck with rolling the Polygon before calling. + +Two default params exists as well, `MinArea` and `MaxDepth`, they work in tandom, +`MinArea` parameter is for setting a minimum size of triangles added to result, and as this method +works iteratively, removing triangles in a circle around the shape over and over, `MaxDepth` refers +to the max number of rounds it has moved around the shape before it ignores `MinArea` paramater. +*) +procedure _LapePolygon_Triangulate(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PTriangleArray(Result)^ := PPolygon(Params^[0])^.Triangulate(PInteger(Params^[1])^, PInteger(Params^[2])^); +end; + +(* +TPolygon.DouglasPeucker +----------------------- +> function TPolygon.DouglasPeucker(Epsilon: Double): TPolygon; + +Returns the two points that are furthest away from eachother in a polygon. +*) +procedure _LapePolygon_DouglasPeucker(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PPolygon(Result)^ := PPolygon(Params^[0])^.DouglasPeucker(PDouble(Params^[1])^); +end; + +(* +TPolygon.FurthestPoints +----------------------- +> procedure TPolygon.FurthestPoints(out A,B: TPoint); + +Returns the two points that are furthest away from eachother in a polygon. +*) +procedure _LapePolygon_FurthestPoints(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +begin + PPolygon(Params^[0])^.FurthestPoints(PPoint(Params^[1])^, PPoint(Params^[2])^); +end; + +procedure _LapePoint_IN_Polygon(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PBoolean(Result)^ := PPoint(Params^[0])^ in PPolygon(Params^[1])^; +end; + +procedure ImportPolygon(Script: TSimbaScript); +begin + with Script.Compiler do + begin + DumpSection := 'TPolygon'; + + addGlobalType('array of TPoint', 'TPolygon'); + addGlobalType('array of TPolygon', 'TPolygonArray'); + + addGlobalFunc('function TPolygon.Bounds: TBox', @_LapePolygon_Bounds); + addGlobalFunc('function TPolygon.Mean: TPoint', @_LapePolygon_Mean); + addGlobalFunc('function TPolygon.Area: Double', @_LapePolygon_Area); + addGlobalFunc('function TPolygon.IsConvex: Boolean', @_LapePolygon_IsConvex); + addGlobalFunc('function TPolygon.Contains(p: TPoint): Boolean', @_LapePolygon_Contains); + addGlobalFunc('function TPolygon.ContainsLine(a1, a2: TPoint): Boolean', @_LapePolygon_ContainsLine); + addGlobalFunc('function TPolygon.NearestEdge(P: TPoint): TPoint', @_LapePolygon_NearestEdge); + addGlobalFunc('function TPolygon.Expand(Amount: Integer): TPolygon', @_LapePolygon_Expand); + addGlobalFunc('function TPolygon.Triangulate(MinArea: Single; MaxDepth: Int32): TTriangleArray;', @_LapePolygon_Triangulate); + addGlobalFunc('function TPolygon.DouglasPeucker(Epsilon: Double): TPolygon;', @_LapePolygon_DouglasPeucker); + addGlobalFunc('procedure TPolygon.FurthestPoints(out A, B: TPoint);', @_LapePolygon_FurthestPoints); + + addGlobalFunc('operator in(Left: TPoint; Right: TPolygon): Boolean;', @_LapePoint_IN_Polygon); + + DumpSection := ''; + end; +end; + +end. + diff --git a/Source/script/imports/simba.import_quad.pas b/Source/script/imports/simba.import_quad.pas index 589348ce9..9f5d22845 100644 --- a/Source/script/imports/simba.import_quad.pas +++ b/Source/script/imports/simba.import_quad.pas @@ -89,7 +89,7 @@ procedure _LapeQuad_Rotate(const Params: PParamArray; const Result: Pointer); LA function TQuad.Contains(P: TPoint): Boolean; ``` *) -procedure _LapeQuad_Contains1(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeQuad_Contains(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin PBoolean(Result)^ := PQuad(Params^[0])^.Contains(PPoint(Params^[1])^); end; @@ -106,44 +106,16 @@ procedure _LapeQuad_Offset(const Params: PParamArray; const Result: Pointer); LA PQuad(Result)^ := PQuad(Params^[0])^.Offset(PPoint(Params^[1])^); end; -(* -TQuad.Extract -------------- -``` -function TQuad.Extract(Points: TPointArray): TPointArray; -``` - -Returns all points that are in the quad. -*) -procedure _LapeQuad_Extract(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PPointArray(Result)^ := PQuad(Params^[0])^.Extract(PPointArray(Params^[1])^); -end; - -(* -TQuad.Exclude -------------- -``` -function TQuad.Exclude(Points: TPointArray): TPointArray; -``` - -Returns all points that are *not inside* the quad. -*) -procedure _LapeQuad_Exclude(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PPointArray(Result)^ := PQuad(Params^[0])^.Exclude(PPointArray(Params^[1])^); -end; - (* TQuad.Expand ------------ ``` -function TQuad.Expand(Amount: Double): TQuad; +function TQuad.Expand(Amount: Integer): TQuad; ``` *) procedure _LapeQuad_Expand(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin - PQuad(Result)^ := PQuad(Params^[0])^.Expand(PDouble(Params^[1])^); + PQuad(Result)^ := PQuad(Params^[0])^.Expand(PInteger(Params^[1])^); end; (* @@ -293,11 +265,9 @@ procedure ImportQuad(Script: TSimbaScript); addGlobalFunc('function TQuad.CreateFromPoints(Points: TPointArray): TQuad; static; overload', @_LapeQuad_CreateFromPoints); addGlobalFunc('function TQuad.Rotate(Angle: Double): TQuad', @_LapeQuad_Rotate); - addGlobalFunc('function TQuad.Contains(P: TPoint): Boolean; overload', @_LapeQuad_Contains1); + addGlobalFunc('function TQuad.Contains(P: TPoint): Boolean', @_LapeQuad_Contains); addGlobalFunc('function TQuad.Offset(P: TPoint): TQuad', @_LapeQuad_Offset); - addGlobalFunc('function TQuad.Extract(Points: TPointArray): TPointArray', @_LapeQuad_Extract); - addGlobalFunc('function TQuad.Exclude(Points: TPointArray): TPointArray', @_LapeQuad_Exclude); - addGlobalFunc('function TQuad.Expand(Amount: Double): TQuad', @_LapeQuad_Expand); + addGlobalFunc('function TQuad.Expand(Amount: Integer): TQuad', @_LapeQuad_Expand); addGlobalFunc('function TQuad.NearestEdge(P: TPoint): TPoint', @_LapeQuad_NearestEdge); addGlobalFunc('function TQuad.Normalize: TQuad', @_LapeQuad_Normalize); addGlobalFunc('function TQuad.RandomPoint: TPoint', @_LapeQuad_RandomPoint); diff --git a/Source/script/imports/simba.import_target.pas b/Source/script/imports/simba.import_target.pas index ea46bb996..39f42451d 100644 --- a/Source/script/imports/simba.import_target.pas +++ b/Source/script/imports/simba.import_target.pas @@ -16,7 +16,8 @@ implementation TypInfo, lptypes, lpvartypes, ffi, simba.colormath, simba.dtm, simba.misc, - simba.image, simba.target, simba.externalcanvas, simba.finder_image, simba.finder_color; + simba.image, simba.target, simba.externalcanvas, simba.finder_image, simba.finder_color, + simba.vartype_quad; type PSimbaTarget = ^TSimbaTarget; diff --git a/Source/script/imports/simba.import_triangle.pas b/Source/script/imports/simba.import_triangle.pas index 0eeeaf747..193c9b0fb 100644 --- a/Source/script/imports/simba.import_triangle.pas +++ b/Source/script/imports/simba.import_triangle.pas @@ -19,7 +19,8 @@ implementation uses lptypes, - simba.vartype_triangle; + simba.vartype_triangle, + simba.vartype_circle; (* TTriangle @@ -123,34 +124,6 @@ procedure _LapeTriangle_Offset(const Params: PParamArray; const Result: Pointer) TTriangle(Result^) := TTriangle(Params^[0]^).Offset(TPoint(Params^[1]^)); end; -(* -TTriangle.Extract ------------------ -``` -function TTriangle.Extract(Points: TPointArray): TPointArray; -``` - -Returns all points that are in the triangle. -*) -procedure _LapeTriangle_Extract(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - TPointArray(Result^) := TTriangle(Params^[0]^).Extract(TPointArray(Params^[1]^)); -end; - -(* -TTriangle.Exclude ------------------ -``` -function TTriangle.Exclude(Points: TPointArray): TPointArray; -``` - -Returns all points that are **not inside** the triangle. -*) -procedure _LapeTriangle_Exclude(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - TPointArray(Result^) := TTriangle(Params^[0]^).Exclude(TPointArray(Params^[1]^)); -end; - (* TTriangle.Expand ---------------- @@ -327,8 +300,6 @@ procedure ImportTriangle(Script: TSimbaScript); addGlobalFunc('function TTriangle.Rotate(Angle: Double): TTriangle', @_LapeTriangle_Rotate); addGlobalFunc('function TTriangle.Contains(P: TPoint): Boolean', @_LapeTriangle_Contains); addGlobalFunc('function TTriangle.Offset(P: TPoint): TTriangle', @_LapeTriangle_Offset); - addGlobalFunc('function TTriangle.Extract(Points: TPointArray): TPointArray', @_LapeTriangle_Extract); - addGlobalFunc('function TTriangle.Exclude(Points: TPointArray): TPointArray', @_LapeTriangle_Exclude); addGlobalFunc('function TTriangle.Expand(Amount: Int32): TTriangle', @_LapeTriangle_Expand); addGlobalFunc('function TTriangle.NearestEdge(P: TPoint): TPoint', @_LapeTriangle_NearestEdge); addGlobalFunc('function TTriangle.IsObtuse(): Boolean; overload', @_LapeTriangle_IsObtuse); diff --git a/Source/script/simba.script_imports.pas b/Source/script/simba.script_imports.pas index ea860cf24..3bfedc1a6 100644 --- a/Source/script/simba.script_imports.pas +++ b/Source/script/simba.script_imports.pas @@ -38,7 +38,7 @@ implementation simba.import_async, // Simba shapes - simba.import_quad, simba.import_triangle, simba.import_box, simba.import_point, simba.import_circle, + simba.import_quad, simba.import_triangle, simba.import_box, simba.import_point, simba.import_circle, simba.import_polygon, // Simba classes @@ -68,7 +68,8 @@ procedure AddSimbaImports(Script: TSimbaScript); ImportColorMath(Script); ImportMatrix(Script); ImportWindowHandle(Script); - + + ImportPolygon(Script); ImportQuad(Script); ImportCircle(Script); ImportBox(Script); diff --git a/Source/script/simba.script_threading.pas b/Source/script/simba.script_threading.pas index 3f16c1811..74e29f14c 100644 --- a/Source/script/simba.script_threading.pas +++ b/Source/script/simba.script_threading.pas @@ -11,7 +11,7 @@ interface uses Classes, SysUtils, syncobjs, - lptypes, lpmessages, lpvartypes, lpinterpreter, + lptypes, lpvartypes, lpinterpreter, simba.base, simba.baseclass, simba.threading; type diff --git a/Source/simba.base.pas b/Source/simba.base.pas index d3c6f570f..a4863c609 100644 --- a/Source/simba.base.pas +++ b/Source/simba.base.pas @@ -278,31 +278,6 @@ TBox = record PBox = ^TBox; PBoxArray = ^TBoxArray; - TTriangle = record - A,B,C: TPoint; - end; - TTriangleArray = array of TTriangle; - PTriangle = ^TTriangle; - PTriangleArray = ^TTriangleArray; - - TQuad = record - Top: TPoint; - Right: TPoint; - Bottom: TPoint; - Left: TPoint; - end; - TQuadArray = array of TQuad; - - PQuad = ^TQuad; - PQuadArray = ^TQuadArray; - - TCircle = record - X: Integer; - Y: Integer; - Radius: Integer; - end; - PCircle = ^TCircle; - {$PUSH} {$SCOPEDENUMS ON} type @@ -340,6 +315,7 @@ procedure Swap(var A, B: Single); inline; overload; procedure Swap(var A, B: Double); inline; overload; procedure Swap(var A, B: TColorBGRA); inline; overload; procedure Swap(var A, B: Pointer); inline; overload; +procedure Swap(var A, B: TPoint); inline; overload; function IfThen(const Val: Boolean; const IfTrue, IfFalse: String): String; inline; overload; function IfThen(const Val: Boolean; const IfTrue, IfFalse: Int64): Int64; inline; overload; @@ -606,6 +582,15 @@ procedure Swap(var A, B: Pointer); B := Temp; end; +procedure Swap(var A, B: TPoint); +var + Temp: TPoint; +begin + Temp := A; + A := B; + B := Temp; +end; + function IfThen(const Val: Boolean; const IfTrue, IfFalse: String): String; begin Result := specialize IfThen(Val, IfTrue, IfFalse); diff --git a/Source/simba.component_imageboxcanvas.pas b/Source/simba.component_imageboxcanvas.pas index 5eb4bd797..15c414be8 100644 --- a/Source/simba.component_imageboxcanvas.pas +++ b/Source/simba.component_imageboxcanvas.pas @@ -12,7 +12,8 @@ interface uses Classes, SysUtils, Graphics, LCLType, FPImage, - simba.base, simba.image_lazbridge, simba.component_imageboxdrawers, simba.image_textdrawer; + simba.base, simba.image_lazbridge, simba.component_imageboxdrawers, simba.image_textdrawer, + simba.vartype_quad; type TSimbaImageBoxCanvas = class; diff --git a/Source/simba.component_shapebox.pas b/Source/simba.component_shapebox.pas index d874ded0d..5a0019ffc 100644 --- a/Source/simba.component_shapebox.pas +++ b/Source/simba.component_shapebox.pas @@ -295,7 +295,7 @@ implementation uses LCLType, simba.geometry, simba.vartype_pointarray, - simba.vartype_box, simba.vartype_string, simba.vartype_point; + simba.vartype_box, simba.vartype_string, simba.vartype_point, simba.vartype_polygon; const CLOSE_DISTANCE = 4; @@ -504,7 +504,7 @@ procedure TSimbaShapeBoxShape_Path.Paint(Sender: TSimbaShapeBox; ACanvas: TSimba procedure TSimbaShapeBoxShape_Poly.BuildContainsCache; begin - FContainsCache := TSimbaGeometry.ExpandPolygon(FPoly.ConvexHull(), CLOSE_DISTANCE); + FContainsCache := FPoly.ConvexHull().Expand(CLOSE_DISTANCE); end; procedure TSimbaShapeBoxShape_Poly.SelectingMouseDown(Sender: TSimbaShapeBox; Button: TMouseButton; Shift: TShiftState; MousePoint: TPoint); diff --git a/Source/simba.container_kdtree.pas b/Source/simba.container_kdtree.pas index 46ba8924e..137d6a45e 100644 --- a/Source/simba.container_kdtree.pas +++ b/Source/simba.container_kdtree.pas @@ -65,9 +65,6 @@ TKDTree = record implementation -uses - Math; - const NONE = -1; diff --git a/Source/simba.externalcanvas.pas b/Source/simba.externalcanvas.pas index 7e36875c7..1c5584e91 100644 --- a/Source/simba.externalcanvas.pas +++ b/Source/simba.externalcanvas.pas @@ -15,7 +15,8 @@ interface uses Classes, SysUtils, Graphics, simba.base, simba.baseclass, simba.image, simba.image_utils, simba.image_textdrawer, - simba.threading; + simba.threading, + simba.vartype_quad; type TSimbaExternalCanvas = class(TSimbaBaseClass) @@ -156,7 +157,7 @@ implementation uses Math, - simba.vartype_box, simba.vartype_quad, simba.vartype_pointarray; + simba.vartype_box, simba.vartype_pointarray; procedure TSimbaExternalCanvas.Invalidate(b: TBox); begin diff --git a/Source/simba.geometry.pas b/Source/simba.geometry.pas index 3f7dc51f4..8e9dabae6 100644 --- a/Source/simba.geometry.pas +++ b/Source/simba.geometry.pas @@ -8,12 +8,8 @@ Jarl Holta - https://github.com/slackydev - ExpandPolygon - DistToLine - - PointInEllipse - AngleBetween - DeltaAngle - - IsConvexPolygon - - LineInPolygon - - TriangulatePolygon } { @@ -22,13 +18,10 @@ - CrossProduct - PointInTriangle - PointInPolygon - - FastRotate - - PolygonArea } unit simba.geometry; -{$DEFINE SIMBA_MAX_OPTIMIZATION} {$i simba.inc} interface @@ -40,36 +33,20 @@ interface type TSimbaGeometry = class public - class var - CosTable: array[0..359] of Double; - SinTable: array[0..359] of Double; - public - class constructor Create; - - class function IsConvexPolygon(const Polygon: TPointArray): Boolean; - class function LineInPolygon(a1, a2: TPoint; const Polygon: TPointArray): Boolean; - class function TriangulatePolygon(Polygon: TPointArray; MinArea: Single = 0; MaxDepth: Int32 = 0): TTriangleArray; - class function PolygonArea(const Polygon: TPointArray): Double; static; inline; - class function ExpandPolygon(const Polygon: TPointArray; Amount: Integer): TPointArray; static; - class procedure FurthestPointsPolygon(const Polygon: TPointArray; out A,B: TPoint); - class function CrossProduct(const r, p, q: TPoint): Int64; static; overload; inline; class function CrossProduct(const rx,ry, px,py, qx,qy: Double): Double; static; overload; inline; class function LinesIntersect(const P1, P2, Q1, Q2: TPoint): Boolean; static; overload; class function LinesIntersect(const P1, P2, Q1, Q2: TPoint; out Where: TPoint): Boolean; static; overload; - class function PointInTriangle(const P, P1, P2, P3: TPoint): Boolean; static; inline; - class function PointInBox(const P: TPoint; const Box: TBox): Boolean; static; inline; - class function PointInQuad(const P: TPoint; const A,B,C,D: TPoint): Boolean; static; + + class function PointInTriangle(const P, P1, P2, P3: TPoint): Boolean; static; class function PointInPolygon(const P: TPoint; const Polygon: TPointArray): Boolean; static; - class function PointInCircle(const P, Center: TPoint; const Radius: Double): Boolean; static; inline; + class function PointInCircle(const P, Center: TPoint; const Radius: Double): Boolean; static; class function PointInEllipse(const P, Center: TPoint; const YRadius, XRadius: Double): Boolean; static; - class function RotatePointFast(const P: TPoint; Degrees: Integer; X, Y: Double): TPoint; static; - class function RotatePointsFast(const Points: TPointArray; Degrees: Integer; X, Y: Double): TPointArray; static; class function RotatePoint(const P: TPoint; Radians, X, Y: Double): TPoint; static; class function RotatePoints(const Points: TPointArray; Radians, X, Y: Double): TPointArray; static; - class function AngleBetween(const P1, P2: TPoint): Double; static; inline; + class function AngleBetween(const P1, P2: TPoint): Double; static; class function DeltaAngle(const DegreesA, DegreesB: Double; R: Double = 360): Double; static; class function DistToLine(const P, P1, P2: TPoint; out Nearest: TPoint): Double; static; overload; class function DistToLine(const P, P1, P2: TPoint): Double; static; overload; @@ -77,56 +54,6 @@ TSimbaGeometry = class implementation -uses - simba.vartype_pointarray; - -class constructor TSimbaGeometry.Create; -var - I: Integer; -begin - for I := 0 to High(CosTable) do - begin - CosTable[I] := Cos((1.0 * I) * (PI / 180)); - SinTable[I] := Sin((1.0 * I) * (PI / 180)); - end; -end; - -class function TSimbaGeometry.RotatePointFast(const P: TPoint; Degrees: Integer; X, Y: Double): TPoint; -var - SinValue, CosValue: Double; -begin - Degrees := Degrees mod 360; - if (Degrees < 0) then - Degrees := 360 + Degrees; - - SinValue := SinTable[Degrees]; - CosValue := CosTable[Degrees]; - - Result.X := Round(X + CosValue * (P.X - X) - SinValue * (P.Y - Y)); - Result.Y := Round(Y + SinValue * (P.X - X) + CosValue * (P.Y - Y)); -end; - -class function TSimbaGeometry.RotatePointsFast(const Points: TPointArray; Degrees: Integer; X, Y: Double): TPointArray; -var - I: Integer; - CosValue, SinValue: Double; -begin - SetLength(Result, Length(Points)); - - Degrees := Degrees mod 360; - if (Degrees < 0) then - Degrees := 360 + Degrees; - - SinValue := SinTable[Degrees]; - CosValue := CosTable[Degrees]; - - for I := 0 to High(Result) do - begin - Result[I].X := Round(X + CosValue * (Points[I].X - X) - SinValue * (Points[I].Y - Y)); - Result[I].Y := Round(Y + SinValue * (Points[I].X - X) + CosValue * (Points[I].Y - Y)); - end; -end; - class function TSimbaGeometry.RotatePoint(const P: TPoint; Radians, X, Y: Double): TPoint; var CosValue, SinValue: Double; @@ -172,9 +99,11 @@ class function TSimbaGeometry.DistToLine(const P, P1, P2: TPoint; out Nearest: T dx := P2.X - P1.X; dy := P2.Y - P1.Y; d := dx*dx + dy*dy; - if (d = 0) then Exit(Hypot(P.X-P1.X, P.Y-P1.Y)); + if (d = 0) then + Exit(Hypot(P.X-P1.X, P.Y-P1.Y)); f := ((P.X - P1.X) * (dx) + (P.Y - P1.Y) * (dy)) / d; - if (f < 0) then Exit(Hypot(P.X-P1.X, P.Y-P1.Y)); + if (f < 0) then + Exit(Hypot(P.X-P1.X, P.Y-P1.Y)); if (f > 1) then begin Nearest := P2; @@ -204,113 +133,6 @@ class function TSimbaGeometry.DistToLine(const P, P1, P2: TPoint): Double; Result := Hypot(P.X-(P1.X + f * dx), P.Y-(P1.Y + f * dy)); end; -class function TSimbaGeometry.ExpandPolygon(const Polygon: TPointArray; Amount: Integer): TPointArray; -type - TPointF = record - X, Y: Double; - end; - TPointFArray = array of TPointF; - - function PointF(const X, Y: Double): TPointF; - begin - Result.X := X; - Result.Y := Y; - end; - -var - i,k,Len: Integer; - theta,det: Double; - c1,c2: array[0..2] of Double; - p1,q1,p2,q2: TPointF; - tmp: TPointFArray; - CosValue, SinValue: Double; -begin - if (Length(Polygon) <= 1) then - Exit(Polygon); - - SetLength(Result, Length(Polygon)); - SetLength(tmp, Length(Polygon) * 2); - - for i := 0 to High(Polygon) do - begin - k := (i+1) mod Length(Polygon); - theta := ArcTan2(Polygon[i].Y - Polygon[k].Y, Polygon[i].X - Polygon[k].X) + HALF_PI; - SinCos(theta, SinValue, CosValue); - tmp[i*2] := PointF(Amount*CosValue+Polygon[i].X, Amount*SinValue+Polygon[i].Y); - tmp[i*2+1]:= PointF(Amount*CosValue+Polygon[k].X, Amount*SinValue+Polygon[k].Y); - end; - - i := 0; - Len := Length(tmp); - while (i < Len) do - begin - p1 := tmp[i]; - p2 := tmp[(i+1) mod Len]; - q1 := tmp[(i+2) mod Len]; - q2 := tmp[(i+3) mod Len]; - - c1[0] := p1.y-p2.y; - c1[1] := p2.x-p1.x; - c1[2] := -(p1.x*p2.y-p2.x*p1.y); - - c2[0] := q1.y-q2.y; - c2[1] := q2.x-q1.x; - c2[2] := -(q1.x*q2.y-q2.x*q1.y); - - det := c1[0] * c2[1] - c1[1] * c2[0]; - if (Abs(det) > 0.001) then - begin - Result[i div 2].X := Round((c1[2] * c2[1] - c1[1] * c2[2]) / det); - Result[i div 2].Y := Round((c1[0] * c2[2] - c1[2] * c2[0]) / det); - end else - begin - Result[i div 2].X := Round(p2.x); - Result[i div 2].Y := Round(p2.y); - end; - - Inc(i, 2); - end; -end; - -class procedure TSimbaGeometry.FurthestPointsPolygon(const Polygon: TPointArray; out A,B: TPoint); -var - i, j, n: Integer; - maxDist, dist: Double; -begin - if Length(Polygon) = 0 then - begin - A := TPoint.ZERO; - B := TPoint.ZERO; - Exit; - end; - - if Length(Polygon) = 1 then - begin - A := Polygon[0]; - B := Polygon[0]; - Exit; - end; - - n := Length(Polygon); - j := 1; - maxDist := 0; - for i:=0 to n-1 do - begin - while Abs(TSimbaGeometry.CrossProduct(Polygon[i], Polygon[(i + 1) mod n], Polygon[(j + 1) mod n])) > - Abs(TSimbaGeometry.CrossProduct(Polygon[i], Polygon[(i + 1) mod n], Polygon[j])) do - j := (j + 1) mod n; - - dist := Sqr(Polygon[i].x - Polygon[j].x) + Sqr(Polygon[i].y - Polygon[j].y); - if dist > maxDist then - begin - maxDist := dist; - A := Polygon[i]; - B := Polygon[j]; - end; - end; -end; - - class function TSimbaGeometry.PointInTriangle(const P, P1, P2, P3: TPoint): Boolean; function Orientation(const P1, P2, P: TPoint): Integer; @@ -349,16 +171,6 @@ class function TSimbaGeometry.PointInTriangle(const P, P1, P2, P3: TPoint): Bool end; end; -class function TSimbaGeometry.PointInBox(const P: TPoint; const Box: TBox): Boolean; -begin - Result := InRange(P.X, Box.X1, Box.X2) and InRange(P.Y, Box.Y1, Box.Y2); -end; - -class function TSimbaGeometry.PointInQuad(const P: TPoint; const A, B, C, D: TPoint): Boolean; -begin - Result := PointInTriangle(P, A,C,B) or PointInTriangle(P, A,D,C); -end; - class function TSimbaGeometry.PointInCircle(const P, Center: TPoint; const Radius: Double): Boolean; begin Result := Sqr(P.X - Center.X) + Sqr(P.Y - Center.Y) <= Sqr(Radius); @@ -374,126 +186,6 @@ class function TSimbaGeometry.PointInEllipse(const P, Center: TPoint; const YRad Result := (Sqr(X) * Sqr(YRadius)) + (Sqr(Y) * Sqr(XRadius)) <= (Sqr(YRadius) * Sqr(XRadius)); end; -class function TSimbaGeometry.IsConvexPolygon(const Polygon: TPointArray): Boolean; -var - i,d: Int32; - a,b,c: TPoint; -begin - if Length(Polygon) < 3 then Exit(False); - - d := CrossProduct(Polygon[0], Polygon[1 mod Length(Polygon)], Polygon[2 mod Length(Polygon)]); - for i:=0 to High(Polygon) do - begin - A := Polygon[i]; - B := Polygon[(i+1) mod Length(Polygon)]; - C := Polygon[(i+2) mod Length(Polygon)]; - - if CrossProduct(A,B,C)*d <= 0 then - Exit(False); - end; - - Result := True; -end; - -class function TSimbaGeometry.LineInPolygon(a1, a2: TPoint; const Polygon: TPointArray): Boolean; -var - i: Int32; - p1, p2: TPoint; -begin - for i:=0 to High(Polygon)-1 do - begin - p1 := Polygon[i]; - p2 := Polygon[i + 1]; - if LinesIntersect(a1, a2, p1, p2) and not ((a1 = p1) or (a1 = p2) or (a2 = p1) or (a2 = p2)) then - Exit(False); - end; - - p1 := Polygon[High(Polygon)]; - p2 := Polygon[0]; - if LinesIntersect(a1, a2, p1, p2) and not ((a1 = p1) or (a1 = p2) or (a2 = p1) or (a2 = p2)) then - Exit(False); - - Result := True; -end; - -class function TSimbaGeometry.TriangulatePolygon(Polygon: TPointArray; MinArea: Single=0; MaxDepth: Int32=0): TTriangleArray; -var - i,j: Int32; - A,B,C: TPoint; - tmp1,tmp2: TPointArray; - valid: Boolean; -begin - tmp1 := specialize Reversed(Polygon); - SetLength(tmp2, Length(Polygon)); - - j := 0; - while Length(tmp1) > 3 do - begin - Inc(j); - valid := False; - i := 0; - while i < High(tmp1) do - begin - A := tmp1[i]; - B := tmp1[(i+1) mod Length(tmp1)]; - C := tmp1[(i+2) mod Length(tmp1)]; - - if (CrossProduct(A,B,C) >= 0) and LineInPolygon(A,C, Polygon) then - begin - if (j >= MaxDepth) or (PolygonArea([A,B,C]) > MinArea) then - begin - SetLength(Result, Length(Result)+1); - Result[High(Result)].A := A; - Result[High(Result)].B := B; - Result[High(Result)].C := C; - end; - - tmp2[i] := A; - tmp2[i+1] := C; - valid := True; - Inc(i,2); - end else - begin - tmp2[i] := A; - Inc(i); - end; - end; - - if not valid then Exit(); - //Remove all duplicates without changing order - //This is actually not bad here. - if (i) > Length(tmp1) then SetLength(tmp1, i); - Move(tmp2[0], tmp1[0], i*SizeOf(TPoint)); - - tmp1 := tmp1.Unique(); - end; - - if Length(tmp1) = 3 then - begin - SetLength(Result, Length(Result)+1); - Result[High(Result)].A := tmp1[0]; - Result[High(Result)].B := tmp1[1]; - Result[High(Result)].C := tmp1[2]; - end; -end; - - -class function TSimbaGeometry.PolygonArea(const Polygon: TPointArray): Double; -var - i, j: Integer; -begin - Result := 0; - - j := Length(Polygon) - 1; - for i := 0 to j do - begin - Result := Result + ((Polygon[J].X * Polygon[I].Y) - (Polygon[J].Y * Polygon[I].X)); - j := i; - end; - - Result := Abs(Result) * 0.5; -end; - class function TSimbaGeometry.CrossProduct(const r, p, q: TPoint): Int64; begin Result := (Int64(p.x) - Int64(r.x)) * (Int64(q.y) - Int64(r.y)) - (Int64(p.y) - Int64(r.y)) * (Int64(q.x) - Int64(r.x)); @@ -507,17 +199,15 @@ class function TSimbaGeometry.CrossProduct(const rx, ry, px, py, qx, qy: Double) class function TSimbaGeometry.LinesIntersect(const P1,P2,Q1,Q2: TPoint; out Where: TPoint): Boolean; var UpperX, UpperY, LowerX, LowerY: Integer; - Ax, Bx, Cx: Integer; - Ay, By, Cy: Integer; + ax, bx, cx: Integer; + ay, by, cy: Integer; D, F, E: Integer; Ratio: Double; begin - Result := False; + ax := P2.X - P1.X; + bx := Q1.X - Q2.X; - Ax := P2.X - P1.X; - Bx := Q1.X - Q2.X; - - if (Ax < 0) then + if (ax < 0) then begin LowerX := P2.X; UpperX := P1.X; @@ -527,18 +217,14 @@ class function TSimbaGeometry.LinesIntersect(const P1,P2,Q1,Q2: TPoint; out Wher LowerX := P1.X; end; - if (Bx > 0) then - begin - if (UpperX < Q2.X) or (Q1.X < LowerX) then - Exit; - end else - if (Upperx < Q1.X) or (Q2.X < LowerX) then - Exit; + if ((bx > 0) and ((UpperX < Q2.X) or (Q1.X < LowerX))) or + ((bx <= 0) and ((Upperx < Q1.X) or (Q2.X < LowerX))) then + Exit(False); - Ay := P2.Y - P1.Y; - By := Q1.Y - Q2.Y; + ay := P2.Y - P1.Y; + by := Q1.Y - Q2.Y; - if (Ay < 0) then + if (ay < 0) then begin LowerY := P2.Y; UpperY := P1.Y; @@ -548,73 +234,56 @@ class function TSimbaGeometry.LinesIntersect(const P1,P2,Q1,Q2: TPoint; out Wher LowerY := P1.Y; end; - if (By > 0) then - begin - if (UpperY < Q2.Y) or (Q1.Y < LowerY) then - Exit; - end else - if (UpperY < Q1.Y) or (Q2.Y < LowerY) then - Exit; - - Cx := P1.X - Q1.X; - Cy := P1.Y - Q1.Y; - d := (By * Cx) - (Bx * Cy); - f := (Ay * Bx) - (Ax * By); + if ((by > 0) and ((UpperY < Q2.Y) or (Q1.Y < LowerY))) or + ((by <= 0) and ((UpperY < Q1.Y) or (Q2.Y < LowerY))) then + Exit(False); - if (f > 0) then - begin - if (d < 0) or (d > f) then - Exit; - end else - if (d > 0) or (d < f) then - Exit; + cx := P1.X - Q1.X; + cy := P1.Y - Q1.Y; + d := (by * cx) - (bx * cy); + f := (ay * bx) - (ax * by); - e := (Ax * Cy) - (Ay * Cx); + if ((f > 0) and ((d < 0) or (d > f))) or + ((f <= 0) and ((d > 0) or (d < f))) then + Exit(False); - if (f > 0) then - begin - if (e < 0) or (e > f) then - Exit; - end else - if (e > 0) or (e < f) then - Exit; + e := (ax * cy) - (ay * cx); - Result := True; + if ((f > 0) and ((e < 0) or (e > f))) or + ((f <= 0) and ((e > 0) or (e < f))) then + Exit(False); - Ratio := (Ax * -By) - (Ay * -Bx); + Ratio := (ax * -by) - (ay * -bx); if (Ratio <> 0) then begin - Ratio := ((Cy * -Bx) - (Cx * -By)) / Ratio; + Ratio := ((cy * -bx) - (cx * -by)) / Ratio; - Where.X := P1.X + Round(Ratio * Ax); - Where.Y := P1.Y + Round(Ratio * Ay); + Where.X := P1.X + Round(Ratio * ax); + Where.Y := P1.Y + Round(Ratio * ay); + end else if (ax * -cy) = (-cx * ay) then + begin + Where.X := Q1.X; + Where.Y := Q1.Y; end else begin - if (Ax * -Cy) = (-Cx * Ay) then - begin - Where.X := Q1.X; - Where.Y := Q1.Y; - end else - begin - Where.X := Q2.X; - Where.Y := Q2.Y; - end; + Where.X := Q2.X; + Where.Y := Q2.Y; end; + + Result := True; end; class function TSimbaGeometry.LinesIntersect(const P1,P2,Q1,Q2: TPoint): Boolean; var UpperX, UpperY, LowerX, LowerY: Integer; - Ax, Bx, Cx: Integer; - Ay, By, Cy: Integer; + ax, bx, cx: Integer; + ay, by, cy: Integer; D, F, E: Integer; begin - Result := False; - - Ax := P2.X - P1.X; - Bx := Q1.X - Q2.X; + ax := P2.X - P1.X; + bx := Q1.X - Q2.X; - if (Ax < 0) then + if (ax < 0) then begin LowerX := P2.X; UpperX := P1.X; @@ -624,18 +293,14 @@ class function TSimbaGeometry.LinesIntersect(const P1,P2,Q1,Q2: TPoint): Boolean LowerX := P1.X; end; - if (Bx > 0) then - begin - if (UpperX < Q2.X) or (Q1.X < LowerX) then - Exit; - end else - if (Upperx < Q1.X) or (Q2.X < LowerX) then - Exit; + if ((bx > 0) and ((UpperX < Q2.X) or (Q1.X < LowerX))) or + ((bx <= 0) and ((Upperx < Q1.X) or (Q2.X < LowerX))) then + Exit(False); - Ay := P2.Y - P1.Y; - By := Q1.Y - Q2.Y; + ay := P2.Y - P1.Y; + by := Q1.Y - Q2.Y; - if (Ay < 0) then + if (ay < 0) then begin LowerY := P2.Y; UpperY := P1.Y; @@ -645,36 +310,24 @@ class function TSimbaGeometry.LinesIntersect(const P1,P2,Q1,Q2: TPoint): Boolean LowerY := P1.Y; end; - if (By > 0) then - begin - if (UpperY < Q2.Y) or (Q1.Y < LowerY) then - Exit; - end else - if (UpperY < Q1.Y) or (Q2.Y < LowerY) then - Exit; + if ((by > 0) and ((UpperY < Q2.Y) or (Q1.Y < LowerY))) or + ((by <= 0) and ((UpperY < Q1.Y) or (Q2.Y < LowerY))) then + Exit(False); - Cx := P1.X - Q1.X; - Cy := P1.Y - Q1.Y; - d := (By * Cx) - (Bx * Cy); - f := (Ay * Bx) - (Ax * By); + cx := P1.X - Q1.X; + cy := P1.Y - Q1.Y; + d := (by * cx) - (bx * cy); + f := (ay * bx) - (ax * by); - if (f > 0) then - begin - if (d < 0) or (d > f) then - Exit; - end else - if (d > 0) or (d < f) then - Exit; + if ((f > 0) and ((d < 0) or (d > f))) or + ((f <= 0) and ((d > 0) or (d < f))) then + Exit(False); - e := (Ax * Cy) - (Ay * Cx); + e := (ax * cy) - (ay * cx); - if (f > 0) then - begin - if (e < 0) or (e > f) then - Exit; - end else - if (e > 0) or (e < f) then - Exit; + if ((f > 0) and ((e < 0) or (e > f))) or + ((f <= 0) and ((e > 0) or (e < f))) then + Exit(False); Result := True; end; diff --git a/Source/simba.image.pas b/Source/simba.image.pas index a3af1342f..4449a93cd 100644 --- a/Source/simba.image.pas +++ b/Source/simba.image.pas @@ -12,7 +12,8 @@ interface uses Classes, SysUtils, Graphics, - simba.base, simba.baseclass, simba.image_textdrawer, simba.colormath, simba.dtm; + simba.base, simba.baseclass, simba.image_textdrawer, simba.colormath, + simba.vartype_polygon, simba.vartype_quad; type {$PUSH} @@ -188,9 +189,9 @@ TSimbaImage = class(TSimbaBaseClass) procedure DrawBoxInverted(B: TBox); // Poly - procedure DrawPolygon(Points: TPointArray); - procedure DrawPolygonFilled(Points: TPointArray); - procedure DrawPolygonInverted(Points: TPointArray); + procedure DrawPolygon(Poly: TPolygon); + procedure DrawPolygonFilled(Poly: TPolygon); + procedure DrawPolygonInverted(Poly: TPolygon); // Quad procedure DrawQuad(Quad: TQuad); @@ -210,7 +211,7 @@ TSimbaImage = class(TSimbaBaseClass) // Arrays procedure DrawQuadArray(Quads: TQuadArray; Filled: Boolean); procedure DrawBoxArray(Boxes: TBoxArray; Filled: Boolean); - procedure DrawPolygonArray(Polygons: T2DPointArray; Filled: Boolean); + procedure DrawPolygonArray(Polygons: TPolygonArray; Filled: Boolean); procedure DrawCircleArray(Centers: TPointArray; Radius: Integer; Filled: Boolean); procedure DrawCrossArray(Points: TPointArray; Radius: Integer); @@ -274,7 +275,6 @@ implementation simba.vartype_matrix, simba.vartype_pointarray, simba.vartype_box, - simba.vartype_quad, simba.image_utils, simba.image_lazbridge, simba.image_resizerotate, @@ -284,7 +284,6 @@ implementation simba.colormath_distance, simba.colormath_conversion, simba.zip, - simba.geometry, simba.nativeinterface, simba.containers, simba.threading, @@ -863,31 +862,31 @@ procedure TSimbaImage.DrawLineGap(Start, Stop: TPoint; GapSize: Integer); SimbaImage_DrawLineGapAlpha(Self, Start, Stop, GapSize); end; -procedure TSimbaImage.DrawPolygon(Points: TPointArray); +procedure TSimbaImage.DrawPolygon(Poly: TPolygon); begin - if (Length(Points) >= 3) then - Self.DrawTPA(Points.Connect()); + if (Length(Poly) >= 3) then + Self.DrawTPA(TPointArray(Poly).Connect()); end; -procedure TSimbaImage.DrawPolygonFilled(Points: TPointArray); +procedure TSimbaImage.DrawPolygonFilled(Poly: TPolygon); begin - if (Length(Points) >= 3) then + if (Length(Poly) >= 3) then if (FDrawAlpha = ALPHA_OPAQUE) then - SimbaImage_DrawPolygonFilled(Self, Points) + SimbaImage_DrawPolygonFilled(Self, Poly) else - SimbaImage_DrawPolygonFilledAlpha(Self, Points) + SimbaImage_DrawPolygonFilledAlpha(Self, Poly) end; -procedure TSimbaImage.DrawPolygonInverted(Points: TPointArray); +procedure TSimbaImage.DrawPolygonInverted(Poly: TPolygon); begin - if (Length(Points) >= 3) then + if (Length(Poly) >= 3) then begin - Self.DrawBoxInverted(Points.Bounds().Clip(TBox.Create(0, 0, FWidth-1, FHeight-1))); + Self.DrawBoxInverted(Poly.Bounds().Clip(TBox.Create(0, 0, FWidth-1, FHeight-1))); if (FDrawAlpha = ALPHA_OPAQUE) then - SimbaImage_DrawPolygonInverted(Self, Points) + SimbaImage_DrawPolygonInverted(Self, Poly) else - SimbaImage_DrawPolygonInvertedAlpha(Self, Points); + SimbaImage_DrawPolygonInvertedAlpha(Self, Poly); end; end; @@ -1016,7 +1015,7 @@ procedure TSimbaImage.DrawBoxArray(Boxes: TBoxArray; Filled: Boolean); DrawColor := PrevDrawColor; end; -procedure TSimbaImage.DrawPolygonArray(Polygons: T2DPointArray; Filled: Boolean); +procedure TSimbaImage.DrawPolygonArray(Polygons: TPolygonArray; Filled: Boolean); var I: Integer; PrevDrawColor: TColor; diff --git a/Source/simba.image_draw.pas b/Source/simba.image_draw.pas index 209f9c46f..7c678fd04 100644 --- a/Source/simba.image_draw.pas +++ b/Source/simba.image_draw.pas @@ -5,14 +5,16 @@ } unit simba.image_draw; -{$DEFINE SIMBA_MAX_OPTIMIZATION} {$i simba.inc} interface uses Classes, SysUtils, Math, - simba.base, simba.image; + simba.base, + simba.image, + simba.vartype_polygon, + simba.vartype_quad; procedure SimbaImage_DrawTPA(Image: TSimbaImage; TPA: TPointArray); procedure SimbaImage_DrawTPAAlpha(Image: TSimbaImage; TPA: TPointArray); @@ -37,11 +39,11 @@ procedure SimbaImage_DrawCircleInvertedAlpha(Image: TSimbaImage; ACenter: TPoint procedure SimbaImage_DrawCircleEdge(Image: TSimbaImage; ACenter: TPoint; Radius: Integer); procedure SimbaImage_DrawCircleEdgeAlpha(Image: TSimbaImage; ACenter: TPoint; Radius: Integer); -procedure SimbaImage_DrawPolygonFilled(Image: TSimbaImage; Points: TPointArray); -procedure SimbaImage_DrawPolygonFilledAlpha(Image: TSimbaImage; Points: TPointArray); +procedure SimbaImage_DrawPolygonFilled(Image: TSimbaImage; Poly: TPolygon); +procedure SimbaImage_DrawPolygonFilledAlpha(Image: TSimbaImage; Poly: TPolygon); -procedure SimbaImage_DrawPolygonInverted(Image: TSimbaImage; Points: TPointArray); -procedure SimbaImage_DrawPolygonInvertedAlpha(Image: TSimbaImage; Points: TPointArray); +procedure SimbaImage_DrawPolygonInverted(Image: TSimbaImage; Poly: TPolygon); +procedure SimbaImage_DrawPolygonInvertedAlpha(Image: TSimbaImage; Poly: TPolygon); procedure SimbaImage_DrawQuadInverted(Image: TSimbaImage; Quad: TQuad); procedure SimbaImage_DrawQuadInvertedAlpha(Image: TSimbaImage; Quad: TQuad); @@ -52,8 +54,8 @@ procedure SimbaImage_DrawEllipseAA(Image: TSimbaImage; ACenter: TPoint; XRadius, implementation uses - simba.image_utils, simba.array_algorithm, simba.geometry, - simba.vartype_point, simba.vartype_box, simba.vartype_quad, simba.vartype_pointarray; + simba.image_utils, simba.array_algorithm, + simba.vartype_point, simba.vartype_box; procedure SimbaImage_DrawTPA(Image: TSimbaImage; TPA: TPointArray); var @@ -409,7 +411,7 @@ procedure SimbaImage_DrawCircleEdgeAlpha(Image: TSimbaImage; ACenter: TPoint; Ra _BuildCircle(ACenter.X, ACenter.Y, Radius); end; -procedure SimbaImage_DrawPolygonFilled(Image: TSimbaImage; Points: TPointArray); +procedure SimbaImage_DrawPolygonFilled(Image: TSimbaImage; Poly: TPolygon); var BGRA: TColorBGRA; @@ -428,10 +430,10 @@ procedure SimbaImage_DrawPolygonFilled(Image: TSimbaImage; Points: TPointArray); begin BGRA := Image.DrawColorAsBGRA; - _BuildPolygonFilled(Points, TRect.Create(0, 0, Image.Width-1, Image.Height-1), TPoint.ZERO); + _BuildPolygonFilled(Poly, TRect.Create(0, 0, Image.Width-1, Image.Height-1), TPoint.ZERO); end; -procedure SimbaImage_DrawPolygonFilledAlpha(Image: TSimbaImage; Points: TPointArray); +procedure SimbaImage_DrawPolygonFilledAlpha(Image: TSimbaImage; Poly: TPolygon); var BGRA: TColorBGRA; @@ -463,36 +465,36 @@ procedure SimbaImage_DrawPolygonFilledAlpha(Image: TSimbaImage; Points: TPointAr begin BGRA := Image.DrawColorAsBGRA; - _BuildPolygonFilled(Points, TRect.Create(0, 0, Image.Width-1, Image.Height-1), TPoint.ZERO); + _BuildPolygonFilled(Poly, TRect.Create(0, 0, Image.Width-1, Image.Height-1), TPoint.ZERO); end; -procedure SimbaImage_DrawPolygonInverted(Image: TSimbaImage; Points: TPointArray); +procedure SimbaImage_DrawPolygonInverted(Image: TSimbaImage; Poly: TPolygon); var BGRA: TColorBGRA; B: TBox; X,Y: Integer; begin BGRA := Image.DrawColorAsBGRA; - B := Points.Bounds().Clip(TBox.Create(0, 0, Image.Width-1, Image.Height-1)); + B := Poly.Bounds().Clip(TBox.Create(0, 0, Image.Width-1, Image.Height-1)); for X := B.X1 to B.X2 do for Y := B.Y1 to B.Y2 do - if not TSimbaGeometry.PointInPolygon(TPoint.Create(X, Y), Points) then + if not Poly.Contains(TPoint.Create(X, Y)) then Image.Data[Y * Image.Width + X] := BGRA; end; -procedure SimbaImage_DrawPolygonInvertedAlpha(Image: TSimbaImage; Points: TPointArray); +procedure SimbaImage_DrawPolygonInvertedAlpha(Image: TSimbaImage; Poly: TPolygon); var BGRA: TColorBGRA; B: TBox; X,Y: Integer; begin BGRA := Image.DrawColorAsBGRA; - B := Points.Bounds().Clip(TBox.Create(0, 0, Image.Width-1, Image.Height-1)); + B := Poly.Bounds().Clip(TBox.Create(0, 0, Image.Width-1, Image.Height-1)); for X := B.X1 to B.X2 do for Y := B.Y1 to B.Y2 do - if not TSimbaGeometry.PointInPolygon(TPoint.Create(X, Y), Points) then + if not Poly.Contains(TPoint.Create(X, Y)) then BlendPixel(@Image.Data[Y * Image.Width + X], BGRA); end; @@ -507,7 +509,7 @@ procedure SimbaImage_DrawQuadInverted(Image: TSimbaImage; Quad: TQuad); for X := B.X1 to B.X2 do for Y := B.Y1 to B.Y2 do - if not TSimbaGeometry.PointInQuad(TPoint.Create(X, Y), Quad.Top, Quad.Right, Quad.Bottom, Quad.Left) then + if not Quad.Contains(TPoint.Create(X, Y)) then Image.Data[Y * Image.Width + X] := BGRA; end; @@ -522,7 +524,7 @@ procedure SimbaImage_DrawQuadInvertedAlpha(Image: TSimbaImage; Quad: TQuad); for X := B.X1 to B.X2 do for Y := B.Y1 to B.Y2 do - if not TSimbaGeometry.PointInQuad(TPoint.Create(X, Y), Quad.Top, Quad.Right, Quad.Bottom, Quad.Left) then + if not Quad.Contains(TPoint.Create(X, Y)) then BlendPixel(@Image.Data[Y * Image.Width + X], BGRA); end; diff --git a/Source/simba.math.pas b/Source/simba.math.pas index 62c23fbad..f32cf13e4 100644 --- a/Source/simba.math.pas +++ b/Source/simba.math.pas @@ -5,7 +5,6 @@ } unit simba.math; -{$DEFINE SIMBA_MAX_OPTIMIZATION} {$i simba.inc} interface diff --git a/Source/simba.target.pas b/Source/simba.target.pas index c447884b4..c8eb3aa89 100644 --- a/Source/simba.target.pas +++ b/Source/simba.target.pas @@ -13,7 +13,8 @@ interface Classes, SysUtils, simba.base, simba.image, simba.image_utils, simba.externalcanvas, simba.target_eios, simba.target_window, simba.target_image, simba.target_plugin, - simba.colormath, simba.dtm; + simba.colormath, simba.dtm, + simba.vartype_quad; const DEFAULT_KEY_PRESS_MIN = 20; @@ -263,7 +264,7 @@ TSimbaTarget = record implementation uses - simba.nativeinterface, simba.vartype_box, simba.vartype_quad, simba.target_movemouse, simba.random, + simba.nativeinterface, simba.vartype_box, simba.target_movemouse, simba.random, simba.finder_color, simba.finder_image, simba.finder_dtm; function TSimbaTarget.MousePressed(Button: EMouseButton): Boolean; diff --git a/Source/simba.vartype_box.pas b/Source/simba.vartype_box.pas index 7baa22f71..15de40af2 100644 --- a/Source/simba.vartype_box.pas +++ b/Source/simba.vartype_box.pas @@ -35,22 +35,17 @@ TBoxHelper = record helper for TBox function RandomPoint: TPoint; function RandomPointCenter: TPoint; - function ToQuad: TQuad; function EqualDimensions(Other: TBox): Boolean; function Expand(SizeMod: Integer): TBox; overload; function Expand(SizeMod: Integer; MaxBounds: TBox): TBox; overload; function Expand(WidMod, HeiMod: Integer): TBox; overload; function Expand(WidMod, HeiMod: Integer; MaxBounds: TBox): TBox; overload; - function Contains(Other: TBox): Boolean; overload; inline; - function Contains(Other: TPoint): Boolean; overload; inline; - function Contains(Other: TQuad): Boolean; overload; inline; + function Contains(p: TPoint): Boolean; function Offset(P: TPoint): TBox; function Combine(Other: TBox): TBox; function Invert(Space: TBox): TBoxArray; function Partition(Rows, Cols: Integer): TBoxArray; - function Extract(Points: TPointArray): TPointArray; - function Exclude(Points: TPointArray): TPointArray; function NearestEdge(P: TPoint): TPoint; function Intersect(P: TPoint): TPoint; @@ -104,7 +99,7 @@ implementation uses Math, - simba.random, simba.containers, simba.geometry, simba.math, simba.array_algorithm; + simba.random, simba.geometry, simba.math, simba.array_algorithm; function TBoxHelper.GetTopRight: TPoint; begin @@ -168,13 +163,6 @@ function TBoxHelper.RandomPointCenter: TPoint; Result.Y := RandomMean(Self.Y1, Self.Y2); end; -function TBoxHelper.ToQuad: TQuad; -begin - Result.Top := TPoint.Create(Self.X1, Self.Y1); - Result.Right := TPoint.Create(Self.X2, Self.Y1); - Result.Bottom := TPoint.Create(Self.X2, Self.Y2); - Result.Left := TPoint.Create(Self.X1, Self.Y2); -end; function TBoxHelper.EqualDimensions(Other: TBox): Boolean; begin @@ -214,19 +202,9 @@ function TBoxHelper.Expand(WidMod, HeiMod: Integer; MaxBounds: TBox): TBox; Result.Clip(MaxBounds); end; -function TBoxHelper.Contains(Other: TBox): Boolean; -begin - Result := (Other.X1 >= Self.X1) and (Other.Y1 >= Self.Y1) and (Other.X2 <= Self.X2) and (Other.Y2 <= Self.Y2); -end; - -function TBoxHelper.Contains(Other: TPoint): Boolean; +function TBoxHelper.Contains(p: TPoint): Boolean; begin - Result := (Other.X >= Self.X1) and (Other.Y >= Self.Y1) and (Other.X <= Self.X2) and (Other.Y <= Self.Y2); -end; - -function TBoxHelper.Contains(Other: TQuad): Boolean; -begin - Result := Contains(Other.Left) and Contains(Other.Right) and Contains(Other.Top) and Contains(Other.Bottom); + Result := (p.x >= Self.X1) and (p.y >= Self.Y1) and (p.x <= Self.X2) and (p.y <= Self.Y2); end; function TBoxHelper.Offset(P: TPoint): TBox; @@ -290,32 +268,6 @@ function TBoxHelper.Partition(Rows, Cols: Integer): TBoxArray; end; end; -function TBoxHelper.Extract(Points: TPointArray): TPointArray; -var - I: Integer; - Buffer: TSimbaPointBuffer; -begin - Buffer.Init(Length(Points)); - for I := 0 to High(Points) do - if Contains(Points[I]) then - Buffer.Add(Points[I]); - - Result := Buffer.ToArray(False); -end; - -function TBoxHelper.Exclude(Points: TPointArray): TPointArray; -var - I: Integer; - Buffer: TSimbaPointBuffer; -begin - Buffer.Init(Length(Points)); - for I := 0 to High(Points) do - if not Contains(Points[I]) then - Buffer.Add(Points[I]); - - Result := Buffer.ToArray(False); -end; - function TBoxHelper.NearestEdge(P: TPoint): TPoint; begin Result := P; diff --git a/Source/simba.vartype_circle.pas b/Source/simba.vartype_circle.pas index bd14fb081..1cc3a1732 100644 --- a/Source/simba.vartype_circle.pas +++ b/Source/simba.vartype_circle.pas @@ -14,6 +14,12 @@ interface simba.base; type + TCircle = record + X: Integer; + Y: Integer; + Radius: Integer; + end; + TCircleHelper = type helper for TCircle private function GetCenter: TPoint; @@ -46,6 +52,8 @@ interface function Circularity(TPA: TPointArray): Double; end; + PCircle = ^TCircle; + operator in(const P: TPoint; const Circle: TCircle): Boolean; implementation @@ -53,7 +61,7 @@ implementation uses Math, simba.math, simba.vartype_pointarray, simba.random, simba.containers, simba.geometry, - simba.vartype_box; + simba.vartype_box, simba.vartype_polygon; function TCircleHelper.GetCenter: TPoint; begin @@ -178,16 +186,16 @@ function TCircleHelper.Circularity(TPA: TPointArray): Double; var I: Integer; Smallest, Test: Double; - Hull: TPointArray; + Poly: TPolygon; begin - Hull := TPA.ConvexHull(); - if Length(Hull) <= 1 then + Poly := TPA.ConvexHull(); + if (Length(Poly) <= 1) then Exit(0); Smallest := $FFFFFF; - for I := 0 to High(Hull) do + for I := 0 to High(Poly) do begin - Test := TSimbaGeometry.DistToLine(Self.Center, Hull[I], Hull[(I+1) mod Length(Hull)]); + Test := TSimbaGeometry.DistToLine(Self.Center, Poly[I], Poly[(I+1) mod Length(Poly)]); if (Test < Smallest) then Smallest := Test; end; diff --git a/Source/simba.vartype_point.pas b/Source/simba.vartype_point.pas index eaa8a494b..8e8120385 100644 --- a/Source/simba.vartype_point.pas +++ b/Source/simba.vartype_point.pas @@ -1,3 +1,8 @@ +{ + Author: Raymond van Venetië and Merlijn Wajer + Project: Simba (https://github.com/MerlijnWajer/Simba) + License: GNU General Public License (https://www.gnu.org/licenses/gpl-3.0) +} unit simba.vartype_point; {$i simba.inc} @@ -9,18 +14,13 @@ interface simba.base; type - TPointHelper = record Helper for TPoint + TPointHelper = record helper for TPoint const ZERO: TPoint = (X: 0; Y: 0); public class function Create(const X, Y: Integer): TPoint; static; inline; - function InTriangle(A, B, C: TPoint): Boolean; - function InPolygon(Poly: TPointArray): Boolean; inline; - function InCircle(Center: TPoint; Radius: Double): Boolean; - function InBox(Box: TBox): Boolean; inline; function DistanceTo(Other: TPoint): Double; - function RotateFast(Degrees: Integer; Center: TPoint): TPoint; function Rotate(Radians: Double; Center: TPoint): TPoint; function Magnitude: Double; function AngleBetween(Other: TPoint): Double; @@ -37,7 +37,6 @@ TPointHelper = record Helper for TPoint operator - (const Left, Right: TPoint): TPoint; operator * (const Left: TPoint; const Right: Double): TPoint; operator div (const Left: TPoint; const Right: Integer): TPoint; - operator in (const Left: TPoint; const Right: TBox): Boolean; implementation @@ -50,36 +49,11 @@ class function TPointHelper.Create(const X, Y: Integer): TPoint; Result.Y := Y; end; -function TPointHelper.InTriangle(A, B, C: TPoint): Boolean; -begin - Result := TSimbaGeometry.PointInTriangle(Self, A, B, C); -end; - -function TPointHelper.InPolygon(Poly: TPointArray): Boolean; -begin - Result := TSimbaGeometry.PointInPolygon(Self, Poly); -end; - -function TPointHelper.InCircle(Center: TPoint; Radius: Double): Boolean; -begin - Result := TSimbaGeometry.PointInCircle(Self, Center, Radius); -end; - -function TPointHelper.InBox(Box: TBox): Boolean; -begin - Result := TSimbaGeometry.PointInBox(Self, Box); -end; - function TPointHelper.DistanceTo(Other: TPoint): Double; begin Result := Sqrt(Sqr(Double(Other.X) - Double(X)) + Sqr(Double(Other.Y) - Double(Y))); end; -function TPointHelper.RotateFast(Degrees: Integer; Center: TPoint): TPoint; -begin - Result := TSimbaGeometry.RotatePointFast(Self, Degrees, Center.X, Center.Y); -end; - function TPointHelper.Rotate(Radians: Double; Center: TPoint): TPoint; begin Result := TSimbaGeometry.RotatePoint(Self, Radians, Center.X, Center.Y); @@ -157,10 +131,5 @@ function TPointHelper.Random(Value: Integer): TPoint; Result.Y := Left.Y div Right; end; -operator in(const Left: TPoint; const Right: TBox): Boolean; -begin - Result := Left.InBox(Right); -end; - end. diff --git a/Source/simba.vartype_pointarray.pas b/Source/simba.vartype_pointarray.pas index 121edca80..dc69c43bf 100644 --- a/Source/simba.vartype_pointarray.pas +++ b/Source/simba.vartype_pointarray.pas @@ -31,14 +31,16 @@ unit simba.vartype_pointarray; -{$DEFINE SIMBA_MAX_OPTIMIZATION} {$i simba.inc} interface uses Classes, SysUtils, - simba.base; + simba.base, + simba.vartype_polygon, + simba.vartype_quad, + simba.vartype_circle; type {$PUSH} @@ -60,6 +62,7 @@ interface function IndicesOf(P: TPoint): TIntegerArray; function Equals(Other: TPointArray): Boolean; function Sum: TPoint; + function Reversed: TPointArray; function Offset(P: TPoint): TPointArray; overload; function Offset(X, Y: Integer): TPointArray; overload; @@ -77,7 +80,7 @@ interface function ShapeFill: TPointArray; procedure FurthestPoints(out A, B: TPoint); - function ConvexHull: TPointArray; + function ConvexHull: TPolygon; function Mean: TPoint; function Median: TPoint; @@ -94,7 +97,7 @@ interface // Return points NOT in ... function ExcludeDist(Center: TPoint; MinDist, MaxDist: Double): TPointArray; - function ExcludePolygon(Polygon: TPointArray): TPointArray; + function ExcludePolygon(Polygon: TPolygon): TPointArray; function ExcludeBox(Box: TBox): TPointArray; function ExcludeQuad(Quad: TQuad): TPointArray; function ExcludePie(StartDegree, EndDegree, MinRadius, MaxRadius: Single; Center: TPoint): TPointArray; @@ -102,7 +105,7 @@ interface // return points WITHIN ... function ExtractDist(Center: TPoint; MinDist, MaxDist: Single): TPointArray; - function ExtractPolygon(Polygon: TPointArray): TPointArray; + function ExtractPolygon(Polygon: TPolygon): TPointArray; function ExtractBox(Box: TBox): TPointArray; function ExtractQuad(Quad: TQuad): TPointArray; function ExtractPie(StartDegree, EndDegree, MinRadius, MaxRadius: Single; Center: TPoint): TPointArray; @@ -148,20 +151,17 @@ interface function Intersection(Other: TPointArray): TPointArray; function SymmetricDifference(Other: TPointArray): TPointArray; function Difference(Other: TPointArray): TPointArray; - function Remove(Values: TPointArray): TPointArray; function DistanceTransform: TSingleMatrix; function QuickSkeleton(): TPointArray; function Circularity: Double; - function DouglasPeucker(epsilon: Double): TPointArray; - function ConcaveHull(Epsilon:Double=2.5; kCount:Int32=5): TPointArray; - function ConcaveHullEx(MaxLeap: Double=-1; Epsilon:Double=2): T2DPointArray; + function ConcaveHull(Epsilon:Double=2.5; kCount:Int32=5): TPolygon; + function ConcaveHullEx(MaxLeap: Double=-1; Epsilon:Double=2): TPolygonArray; function ConvexityDefects(Epsilon: Single; Mode: EConvexityDefects = EConvexityDefects.NONE): TPointArray; procedure ToAxes(out X, Y: TIntegerArray); - end; T2DPointArrayHelper = type helper for T2DPointArray @@ -219,7 +219,7 @@ implementation simba.containers, simba.geometry, simba.math, simba.container_slacktree, simba.vartype_matrix, simba.vartype_ordarray, - simba.vartype_box, simba.vartype_point, simba.vartype_quad, simba.vartype_circle, simba.vartype_triangle, + simba.vartype_box, simba.vartype_point, simba.vartype_triangle, simba.array_algorithm; procedure GetAdjacent4(var Adj: TPointArray; const P: TPoint); inline; @@ -528,6 +528,11 @@ function TPointArrayHelper.Sum: TPoint; Result := specialize Sum(Self); end; +function TPointArrayHelper.Reversed: TPointArray; +begin + Result := specialize Reversed(Self); +end; + function TPointArrayHelper.Offset(P: TPoint): TPointArray; var Ptr: PPoint; @@ -603,9 +608,10 @@ function TPointArrayHelper.Connect: TPointArray; function TPointArrayHelper.Density: Double; begin - Result := 0; - if Length(Self) > 0 then - Result := Min(Length(Self) / TSimbaGeometry.PolygonArea(Self.ConvexHull()), 1); + if (Length(Self) > 0) then + Result := Min(Length(Self) / Self.ConvexHull().Area, 1) + else + Result := 0; end; function TPointArrayHelper.Edges: TPointArray; @@ -942,10 +948,10 @@ procedure TPointArrayHelper.FurthestPoints(out A, B: TPoint); Exit; end; - TSimbaGeometry.FurthestPointsPolygon(Self.ConvexHull(), A,B); + Self.ConvexHull().FurthestPoints(A, B); end; -function TPointArrayHelper.ConvexHull: TPointArray; +function TPointArrayHelper.ConvexHull: TPolygon; var pts: TPointArray; h,i,k,u: Integer; @@ -1100,7 +1106,7 @@ function TPointArrayHelper.Bounds: TBox; function TPointArrayHelper.Area: Double; begin - Result := TSimbaGeometry.PolygonArea(Self.ConvexHull()); + Result := Self.ConvexHull().Area; end; function TPointArrayHelper.Erode(Iterations: Integer): TPointArray; @@ -1347,27 +1353,49 @@ function TPointArrayHelper.ExcludeDist(Center: TPoint; MinDist, MaxDist: Double) Result := Buffer.ToArray(False); end; -function TPointArrayHelper.ExcludePolygon(Polygon: TPointArray): TPointArray; +function TPointArrayHelper.ExcludePolygon(Polygon: TPolygon): TPointArray; var - Buffer: TSimbaPointBuffer; - I: Integer; + I, Count: Integer; begin - Buffer.Init(); + Count := 0; + SetLength(Result, Length(Self)); for I := 0 to High(Self) do - if not Self[I].InPolygon(Polygon) then - Buffer.Add(Self[I]); - - Result := Buffer.ToArray(False); + if not Polygon.Contains(Self[I]) then + begin + Result[Count] := Self[I]; + Inc(Count); + end; + SetLength(Result, Count); end; function TPointArrayHelper.ExcludeBox(Box: TBox): TPointArray; +var + I, Count: Integer; begin - Result := Box.Exclude(Self); + Count := 0; + SetLength(Result, Length(Self)); + for I := 0 to High(Self) do + if not Box.Contains(Self[I]) then + begin + Result[Count] := Self[I]; + Inc(Count); + end; + SetLength(Result, Count); end; function TPointArrayHelper.ExcludeQuad(Quad: TQuad): TPointArray; +var + I, Count: Integer; begin - Result := Quad.Exclude(Self); + Count := 0; + SetLength(Result, Length(Self)); + for I := 0 to High(Self) do + if not Quad.Contains(Self[I]) then + begin + Result[Count] := Self[I]; + Inc(Count); + end; + SetLength(Result, Count); end; function TPointArrayHelper.ExcludePie(StartDegree, EndDegree, MinRadius, MaxRadius: Single; Center: TPoint): TPointArray; @@ -1377,29 +1405,29 @@ function TPointArrayHelper.ExcludePie(StartDegree, EndDegree, MinRadius, MaxRadi function TPointArrayHelper.ExcludePoints(Points: TPointArray): TPointArray; var - Matrix: TBooleanMatrix; - B: TBox; - I: Integer; - Buffer: TSimbaPointBuffer; + Box: TBox; + test: TBooleanArray; + w,h: Integer; + i,c: Integer; begin - Result := Default(TPointArray); - if (Length(Self) = 0) then - Exit; - if (Length(Points) = 0) then - Exit(Copy(Self)); - - B := Self.Bounds().Combine(Points.Bounds()); - - Matrix.SetSize(B.Width, B.Height); - for I := 0 to High(Points) do - Matrix[Points[I].Y - B.Y1, Points[I].X - B.X1] := True; - - Buffer.Init(Length(Self)); - for I := 0 to High(Self) do - if not Matrix[Self[I].Y - B.Y1, Self[I].X - B.X1] then - Buffer.Add(Self[I]); + Box := Points.Bounds; + w := box.Width; + h := box.Height; + SetLength(test, box.Area); + for i:=0 to High(Points) do + test[(Points[i].y - box.y1) * w + (Points[i].x - box.x1)] := True; - Result := Buffer.ToArray(False); + SetLength(Result, Length(Self)); + c := 0; + for i:=0 to High(Self) do + begin + if InRange(Self[i].x - box.x1, 0, w-1) and InRange(Self[i].y - box.y1, 0, h-1) and + (test[(Self[i].y - box.y1) * w + (Self[i].x - box.x1)]) then + Continue; + Result[c] := Self[i]; + Inc(c); + end; + SetLength(Result, c); end; function TPointArrayHelper.ExtractDist(Center: TPoint; MinDist, MaxDist: Single): TPointArray; @@ -1422,27 +1450,49 @@ function TPointArrayHelper.ExtractDist(Center: TPoint; MinDist, MaxDist: Single) Result := Buffer.ToArray(False); end; -function TPointArrayHelper.ExtractPolygon(Polygon: TPointArray): TPointArray; +function TPointArrayHelper.ExtractPolygon(Polygon: TPolygon): TPointArray; var - Buffer: TSimbaPointBuffer; - I: Integer; + I, Count: Integer; begin - Buffer.Init(Length(Polygon)); + Count := 0; + SetLength(Result, Length(Self)); for I := 0 to High(Self) do - if Self[I].InPolygon(Polygon) then - Buffer.Add(Self[I]); - - Result := Buffer.ToArray(False); + if Polygon.Contains(Self[I]) then + begin + Result[Count] := Self[I]; + Inc(Count); + end; + SetLength(Result, Count); end; function TPointArrayHelper.ExtractBox(Box: TBox): TPointArray; +var + I, Count: Integer; begin - Result := Box.Extract(Self); + Count := 0; + SetLength(Result, Length(Self)); + for I := 0 to High(Self) do + if Box.Contains(Self[I]) then + begin + Result[Count] := Self[I]; + Inc(Count); + end; + SetLength(Result, Count); end; function TPointArrayHelper.ExtractQuad(Quad: TQuad): TPointArray; +var + I, Count: Integer; begin - Result := Quad.Extract(Self); + Count := 0; + SetLength(Result, Length(Self)); + for I := 0 to High(Self) do + if Quad.Contains(Self[I]) then + begin + Result[Count] := Self[I]; + Inc(Count); + end; + SetLength(Result, Count); end; function TPointArrayHelper.ExtractPie(StartDegree, EndDegree, MinRadius, MaxRadius: Single; Center: TPoint): TPointArray; @@ -2367,33 +2417,6 @@ function TPointArrayHelper.Difference(Other: TPointArray): TPointArray; Result := specialize TArrayRelationship.Difference(Self, Other); end; -function TPointArrayHelper.Remove(Values: TPointArray): TPointArray; -var - Box: TBox; - test: TBooleanArray; - w,h: Integer; - i,c: Integer; -begin - Box := Values.Bounds; - w := box.Width; - h := box.Height; - SetLength(test, box.Area); - for i:=0 to High(Values) do - test[(Values[i].y - box.y1) * w + (Values[i].x - box.x1)] := True; - - SetLength(Result, Length(Self)); - c := 0; - for i:=0 to High(Self) do - begin - if InRange(Self[i].x - box.x1, 0, w-1) and InRange(Self[i].y - box.y1, 0, h-1) and - (test[(Self[i].y - box.y1) * w + (Self[i].x - box.x1)]) then - Continue; - Result[c] := Self[i]; - Inc(c); - end; - SetLength(Result, c); -end; - function TPointArrayHelper.DistanceTransform: TSingleMatrix; function EucDist(const x1,x2:Int32): Int32; inline; @@ -2619,15 +2642,15 @@ function TPointArrayHelper.Circularity: Double; *) function TPointArrayHelper.MinAreaCircle(): TCircle; var - poly: TPointArray; + poly: TPolygon; p1,p2,p3,p,q,t: TPoint; begin poly := Self.ConvexHull(); // O(n log n) - TSimbaGeometry.FurthestPointsPolygon(poly, p1,p2); // O(m^2) call - ugh + poly.FurthestPoints(p1, p2); // O(m^2) call - ugh p.x := (p1.x+p2.x) div 2; p.y := (p1.y+p2.y) div 2; - t := poly.FurthestPoint(p); + t := TPointArray(poly).FurthestPoint(p); // Two Point Circle if (p1 = t) or (p2 = t) then @@ -2635,7 +2658,7 @@ function TPointArrayHelper.MinAreaCircle(): TCircle; // Three Point Circle p := TTriangle.Create(p1, p2, t).Circumcircle().Center; - q := poly.FurthestPoint(p); + q := TPointArray(poly).FurthestPoint(p); if Sign(TSimbaGeometry.CrossProduct(q, p1, p2)) = Sign(TSimbaGeometry.CrossProduct(t, p1, p2)) then p3 := q @@ -2643,7 +2666,7 @@ function TPointArrayHelper.MinAreaCircle(): TCircle; p3 := t; p := TTriangle.Create(p1,p2,p3).Circumcircle().Center; - q := poly.FurthestPoint(p); + q := TPointArray(poly).FurthestPoint(p); if (p1 <> q) and (p2 <> q) and (p3 <> q) then begin p.x := (p3.x+q.x) div 2; @@ -2656,7 +2679,7 @@ function TPointArrayHelper.MinAreaCircle(): TCircle; p2 := q; p := TTriangle.Create(p1,p2,p3).Circumcircle().Center; - q := poly.FurthestPoint(p); + q := TPointArray(poly).FurthestPoint(p); if (p1 <> q) and (p2 <> q) and (p3 <> q) then p1 := q; end; @@ -2664,39 +2687,6 @@ function TPointArrayHelper.MinAreaCircle(): TCircle; Result := TTriangle.Create(p1,p2,p3).Circumcircle(); end; -function TPointArrayHelper.DouglasPeucker(epsilon: Double): TPointArray; -var - H, i, index: Int32; - dmax, d: Double; - Slice1,Slice2: TPointArray; -begin - H := High(Self); - if (H = -1) then - Exit(nil); - - dmax := 0; - index := 0; - for i:=1 to H do - begin - d := TSimbaGeometry.DistToLine(Self[i], Self[0], Self[H]); - if (d > dmax) then - begin - index := i; - dmax := d; - end; - end; - - if (dmax > epsilon) then - begin - Slice1 := Copy(Self, 0, index).DouglasPeucker(epsilon); - Slice2 := Copy(Self, index).DouglasPeucker(epsilon); - - Result := Slice1; - Result += Slice2; - end else - Result := [Self[0], Self[High(Self)]]; -end; - (* Concave hull approximation using k nearest neighbors Instead of describing a specific max distance we assume that the boundary points are evenly spread out @@ -2709,7 +2699,7 @@ function TPointArrayHelper.DouglasPeucker(epsilon: Double): TPointArray; 1. Increase "Epsilon", this will reduce accurate.. But it's faster. 2. Increase "kCount", this will maintain accuracy.. But it's slower. *) -function TPointArrayHelper.ConcaveHull(Epsilon:Double=2.5; kCount:Int32=5): TPointArray; +function TPointArrayHelper.ConcaveHull(Epsilon:Double=2.5; kCount:Int32=5): TPolygon; var TPA, pts: TPointArray; Buffer: TSimbaPointBuffer; @@ -2729,14 +2719,12 @@ function TPointArrayHelper.ConcaveHull(Epsilon:Double=2.5; kCount:Int32=5): TPoi pts := tree.KNearest(tree.data[i].split, kCount, False); if Length(pts) <= 1 then Continue; - pts := pts.ConvexHull().Connect(); + pts := TPointArray(pts.ConvexHull()).Connect(); Buffer.Add(pts); end; - TPA := Buffer.ToArray(False); - - Result := TPA.Border().DouglasPeucker(Max(2, Epsilon/2)); + Result := TPolygon(Buffer.ToArray(False)).DouglasPeucker(Max(2, Epsilon/2)); end; (* @@ -2748,14 +2736,13 @@ function TPointArrayHelper.ConcaveHull(Epsilon:Double=2.5; kCount:Int32=5): TPoi Higher maxleap is slower. Epsilon describes how accurate you want your output, and have some impact on speed. *) -function TPointArrayHelper.ConcaveHullEx(MaxLeap: Double=-1; Epsilon:Double=2): T2DPointArray; +function TPointArrayHelper.ConcaveHullEx(MaxLeap: Double=-1; Epsilon:Double=2): TPolygonArray; var TPA, pts: TPointArray; tree: TSlackTree; i: Int32; B: TBox; Buffer: TSimbaPointBuffer; - BufferResult: TSimbaPointArrayBuffer; begin B := Self.Bounds(); TPA := Self.PartitionEx(TPoint.Create(B.X1-Round(Epsilon), B.Y1-Round(Epsilon)), Round(Epsilon*2-1), Round(Epsilon*2-1)).Means(); @@ -2764,24 +2751,21 @@ function TPointArrayHelper.ConcaveHullEx(MaxLeap: Double=-1; Epsilon:Double=2): tree.Init(TPA); if MaxLeap = -1 then - MaxLeap := Ceil(Sqrt(TSimbaGeometry.PolygonArea(TPA.ConvexHull()) / Length(TPA)) * Sqrt(2)); + MaxLeap := Ceil(Sqrt(TPA.ConvexHull().Area / Length(TPA)) * Sqrt(2)); MaxLeap := Max(MaxLeap, Epsilon*2); - Buffer.Init(256); for i:=0 to High(tree.data) do begin pts := tree.RangeQueryEx(tree.data[i].split, MaxLeap,MaxLeap, False); if Length(pts) <= 1 then Continue; - Buffer.Add(pts.ConvexHull().Connect()); + Buffer.Add(TPointArray(pts.ConvexHull()).Connect()); end; pts := Buffer.ToArray(False); for pts in pts.Cluster(2) do - BufferResult.Add(pts.Border().DouglasPeucker(Epsilon)); - - Result := BufferResult.ToArray(); + Result := Result + [TPolygon(pts.Border()).DouglasPeucker(Epsilon)]; end; (* diff --git a/Source/simba.vartype_polygon.pas b/Source/simba.vartype_polygon.pas new file mode 100644 index 000000000..41263e9d8 --- /dev/null +++ b/Source/simba.vartype_polygon.pas @@ -0,0 +1,346 @@ +{ + Author: Raymond van Venetië and Merlijn Wajer + Project: Simba (https://github.com/MerlijnWajer/Simba) + License: GNU General Public License (https://www.gnu.org/licenses/gpl-3.0) +} + +{ + Jarl Holta - https://github.com/slackydev + - Expand + - Triangulate +} + +unit simba.vartype_polygon; + +{$i simba.inc} + +interface + +uses + Classes, SysUtils, + simba.base, simba.vartype_triangle; + +type + TPolygon = array of TPoint; + TPolygonArray = array of TPolygon; + + TPolygonHelper = type helper for TPolygon + function Bounds: TBox; + function Mean: TPoint; + function Area: Double; + function Contains(p: TPoint): Boolean; + function ContainsLine(a1, a2: TPoint): Boolean; + function NearestEdge(P: TPoint): TPoint; + function IsConvex: Boolean; + function Expand(Amount: Integer): TPolygon; + function DouglasPeucker(Epsilon: Double): TPolygon; + procedure FurthestPoints(out A,B: TPoint); + function Triangulate(MinArea: Single = 0; MaxDepth: Int32 = 0): TTriangleArray; + end; + + PPolygon = ^TPolygon; + PPolygonArray = ^TPolygonArray; + + operator in(const P: TPoint; const Poly: TPolygon): Boolean; + +implementation + +uses + Math, + simba.math, + simba.geometry, + simba.vartype_point, + simba.vartype_pointarray; + +function TPolygonHelper.Bounds: TBox; +begin + Result := TPointArray(Self).Bounds; +end; + +function TPolygonHelper.Mean: TPoint; +begin + Result := TPointArray(Self).Mean; +end; + +function TPolygonHelper.Area: Double; +var + I, J: Integer; +begin + Result := 0; + + J := Length(Self) - 1; + for I := 0 to J do + begin + Result := Result + ((Self[J].X * Self[I].Y) - (Self[J].Y * Self[I].X)); + J := I; + end; + + Result := Abs(Result) * 0.5; +end; + +function TPolygonHelper.Contains(p: TPoint): Boolean; +begin + Result := TSimbaGeometry.PointInPolygon(p, Self); +end; + +function TPolygonHelper.NearestEdge(P: TPoint): TPoint; +var + dist, best: single; + I: Integer; + q: TPoint; +begin + if (Length(Self) = 0) then + Exit(TPoint.ZERO); + + Best := $FFFFFF; + Result := Self[0]; + for I := 0 to High(Self) do + begin + dist := TSimbaGeometry.DistToLine(p, Self[i], Self[(i+1) mod Length(Self)], q); + if (dist < best) then + begin + Best := dist; + Result := q; + end; + end; +end; + +function TPolygonHelper.IsConvex: Boolean; +var + i,d: Int32; + a,b,c: TPoint; +begin + if Length(Self) < 3 then + Exit(False); + + d := TSimbaGeometry.CrossProduct(Self[0], Self[1 mod Length(Self)], Self[2 mod Length(Self)]); + for i:=0 to High(Self) do + begin + A := Self[i]; + B := Self[(i+1) mod Length(Self)]; + C := Self[(i+2) mod Length(Self)]; + + if TSimbaGeometry.CrossProduct(A,B,C)*d <= 0 then + Exit(False); + end; + + Result := True; +end; + +function TPolygonHelper.ContainsLine(a1, a2: TPoint): Boolean; +var + i: Int32; + p1, p2: TPoint; +begin + if (Length(Self) = 0) then + Exit(False); + + for i:=0 to High(Self) - 1 do + begin + p1 := Self[i]; + p2 := Self[i + 1]; + if TSimbaGeometry.LinesIntersect(a1, a2, p1, p2) and not ((a1 = p1) or (a1 = p2) or (a2 = p1) or (a2 = p2)) then + Exit(False); + end; + + p1 := Self[High(Self)]; + p2 := Self[0]; + if TSimbaGeometry.LinesIntersect(a1, a2, p1, p2) and not ((a1 = p1) or (a1 = p2) or (a2 = p1) or (a2 = p2)) then + Exit(False); + + Result := True; +end; + +function TPolygonHelper.Expand(Amount: Integer): TPolygon; +var + i,k,Len: Integer; + theta,det: Double; + c1,c2: array[0..2] of Double; + p1,q1,p2,q2: TPointF; + tmp: TPointFArray; + CosValue, SinValue: Double; +begin + if (Length(Self) > 1) then + begin + SetLength(Result, Length(Self)); + SetLength(tmp, Length(Self) * 2); + for i := 0 to High(Self) do + begin + k := (i+1) mod Length(Self); + theta := ArcTan2(Self[i].Y - Self[k].Y, Self[i].X - Self[k].X) + HALF_PI; + SinCos(theta, SinValue, CosValue); + tmp[i*2].X := Amount*CosValue+Self[i].X; + tmp[i*2].Y := Amount*SinValue+Self[i].Y; + tmp[i*2+1].X := Amount*CosValue+Self[k].X; + tmp[i*2+1].Y := Amount*SinValue+Self[k].Y; + end; + + i := 0; + Len := Length(tmp); + while (i < Len) do + begin + p1 := tmp[i]; + p2 := tmp[(i+1) mod Len]; + q1 := tmp[(i+2) mod Len]; + q2 := tmp[(i+3) mod Len]; + + c1[0] := p1.y-p2.y; + c1[1] := p2.x-p1.x; + c1[2] := -(p1.x*p2.y-p2.x*p1.y); + + c2[0] := q1.y-q2.y; + c2[1] := q2.x-q1.x; + c2[2] := -(q1.x*q2.y-q2.x*q1.y); + + det := c1[0] * c2[1] - c1[1] * c2[0]; + if (Abs(det) > 0.001) then + begin + Result[i div 2].X := Round((c1[2] * c2[1] - c1[1] * c2[2]) / det); + Result[i div 2].Y := Round((c1[0] * c2[2] - c1[2] * c2[0]) / det); + end else + begin + Result[i div 2].X := Round(p2.x); + Result[i div 2].Y := Round(p2.y); + end; + + Inc(i, 2); + end; + end else + Result := Copy(Self); +end; + +function TPolygonHelper.DouglasPeucker(Epsilon: Double): TPolygon; +var + H, i, index: Int32; + dmax, d: Double; +begin + H := High(Self); + if (H < 0) then + Exit([]); + + dmax := 0; + index := 0; + for i:=1 to H do + begin + d := TSimbaGeometry.DistToLine(Self[i], Self[0], Self[H]); + if (d > dmax) then + begin + index := i; + dmax := d; + end; + end; + + if (dmax > Epsilon) then + Result := Copy(Self, 0, index).DouglasPeucker(epsilon) + Copy(Self, index).DouglasPeucker(epsilon) + else + Result := [Self[0], Self[High(Self)]]; +end; + +procedure TPolygonHelper.FurthestPoints(out A, B: TPoint); +var + i, j, n: Integer; + maxDist, dist: Double; +begin + if Length(Self) = 0 then + begin + A := TPoint.ZERO; + B := TPoint.ZERO; + Exit; + end; + + if Length(Self) = 1 then + begin + A := Self[0]; + B := Self[0]; + Exit; + end; + + n := Length(Self); + j := 1; + maxDist := 0; + for i:=0 to n-1 do + begin + while Abs(TSimbaGeometry.CrossProduct(Self[i], Self[(i + 1) mod n], Self[(j + 1) mod n])) > + Abs(TSimbaGeometry.CrossProduct(Self[i], Self[(i + 1) mod n], Self[j])) do + j := (j + 1) mod n; + + dist := Sqr(Self[i].x - Self[j].x) + Sqr(Self[i].y - Self[j].y); + if dist > maxDist then + begin + maxDist := dist; + A := Self[i]; + B := Self[j]; + end; + end; +end; + +function TPolygonHelper.Triangulate(MinArea: Single; MaxDepth: Int32): TTriangleArray; +var + i,j: Int32; + A,B,C: TPoint; + tmp1,tmp2: TPointArray; + valid: Boolean; +begin + Result := []; + + tmp1 := TPointArray(Self).Reversed; + SetLength(tmp2, Length(Self)); + + j := 0; + while Length(tmp1) > 3 do + begin + Inc(j); + valid := False; + i := 0; + while i < High(tmp1) do + begin + A := tmp1[i]; + B := tmp1[(i+1) mod Length(tmp1)]; + C := tmp1[(i+2) mod Length(tmp1)]; + + if (TSimbaGeometry.CrossProduct(A,B,C) >= 0) and ContainsLine(A,C) then + begin + if (j >= MaxDepth) or (TPolygon([A,B,C]).Area > MinArea) then + begin + SetLength(Result, Length(Result)+1); + Result[High(Result)].A := A; + Result[High(Result)].B := B; + Result[High(Result)].C := C; + end; + + tmp2[i] := A; + tmp2[i+1] := C; + Inc(i,2); + valid := True; + end else + begin + tmp2[i] := A; + Inc(i); + end; + end; + + if not valid then Exit(); + //Remove all duplicates without changing order + //This is actually not bad here. + if (i > Length(tmp1)) then SetLength(tmp1, i); + Move(tmp2[0], tmp1[0], i*SizeOf(TPoint)); + + tmp1 := TPointArray(tmp1).Unique(); + end; + + if Length(tmp1) = 3 then + begin + SetLength(Result, Length(Result)+1); + Result[High(Result)].A := tmp1[0]; + Result[High(Result)].B := tmp1[1]; + Result[High(Result)].C := tmp1[2]; + end; +end; + +operator in(const P: TPoint; const Poly: TPolygon): Boolean; +begin + Result := Poly.Contains(P); +end; + +end. + diff --git a/Source/simba.vartype_quad.pas b/Source/simba.vartype_quad.pas index b3f544851..23b2eadcd 100644 --- a/Source/simba.vartype_quad.pas +++ b/Source/simba.vartype_quad.pas @@ -14,6 +14,14 @@ interface simba.base; type + TQuad = record + Top: TPoint; + Right: TPoint; + Bottom: TPoint; + Left: TPoint; + end; + TQuadArray = array of TQuad; + TQuadHelper = type helper for TQuad private function GetCorners: TPointArray; @@ -35,9 +43,7 @@ interface function Rotate(Radians: Double): TQuad; function Contains(P: TPoint): Boolean; function Offset(P: TPoint): TQuad; - function Extract(Points: TPointArray): TPointArray; - function Exclude(Points: TPointArray): TPointArray; - function Expand(Amount: Double): TQuad; + function Expand(Amount: Integer): TQuad; function NearestEdge(P: TPoint): TPoint; function Normalize: TQuad; @@ -49,14 +55,18 @@ interface property LongSideLen: Integer read GetLongSideLen; end; + PQuad = ^TQuad; + PQuadArray = ^TQuadArray; + operator in(const P: TPoint; const Quad: TQuad): Boolean; implementation uses Math, - simba.math, simba.vartype_pointarray, simba.random, simba.geometry, simba.containers, - simba.vartype_point; + simba.math, simba.vartype_pointarray, simba.random, simba.geometry, + simba.vartype_point, + simba.vartype_polygon; class function TQuadHelper.Create(ATop, ARight, ABottom, ALeft: TPoint): TQuad; begin @@ -168,7 +178,8 @@ function TQuadHelper.Rotate(Radians: Double): TQuad; function TQuadHelper.Contains(P: TPoint): Boolean; begin - Result := TSimbaGeometry.PointInQuad(P, Self.Top, Self.Right, Self.Bottom, Self.Left); + Result := TSimbaGeometry.PointInTriangle(P, Self.Top, Self.Bottom, Self.Right) or + TSimbaGeometry.PointInTriangle(P, Self.Top, Self.Left, Self.Bottom); end; function TQuadHelper.Offset(P: TPoint): TQuad; @@ -176,53 +187,17 @@ function TQuadHelper.Offset(P: TPoint): TQuad; Result := TQuad.Create(Top.Offset(P), Right.Offset(P), Bottom.Offset(P), Left.Offset(P)); end; -function TQuadHelper.Extract(Points: TPointArray): TPointArray; -var - I: Integer; - Buffer: TSimbaPointBuffer; -begin - Buffer.Init(Length(Points)); - for I := 0 to High(Points) do - if Contains(Points[I]) then - Buffer.Add(Points[I]); - - Result := Buffer.ToArray(False); -end; - -function TQuadHelper.Exclude(Points: TPointArray): TPointArray; +function TQuadHelper.Expand(Amount: Integer): TQuad; var - I: Integer; - Buffer: TSimbaPointBuffer; + Poly: TPolygon; begin - Buffer.Init(Length(Points)); - for I := 0 to High(Points) do - if not Contains(Points[I]) then - Buffer.Add(Points[I]); - - Result := Buffer.ToArray(False); -end; - -function TQuadHelper.Expand(Amount: Double): TQuad; -var - InTPA, OutTPA: array[0..3] of TPoint; - Theta: Double; - I, J: Integer; -begin - InTPA[0] := Self.Top; - InTPA[1] := Self.Right; - InTPA[2] := Self.Bottom; - InTPA[3] := Self.Left; - - Amount *= Sqrt(2); - for I := 0 to 3 do - begin - J := (I+1) mod 4; - Theta := ArcTan2(InTPA[I].Y - InTPA[J].Y, InTPA[I].X - InTPA[J].X) + PI/4; - OutTPA[I].X := Round(InTPA[I].X + Amount * Cos(Theta)); - OutTPA[I].Y := Round(InTPA[I].Y + Amount * Sin(Theta)); - end; + Poly := TPolygon([Self.Top, Self.Right, Self.Bottom, Self.Left]); + Poly := Poly.Expand(Amount); - Result := TQuad.Create(OutTPA[0], OutTPA[1], OutTPA[2], OutTPA[3]); + Result.Top := Poly[0]; + Result.Right := Poly[1]; + Result.Bottom := Poly[2]; + Result.Left := Poly[3]; end; function TQuadHelper.NearestEdge(P: TPoint): TPoint; diff --git a/Source/simba.vartype_triangle.pas b/Source/simba.vartype_triangle.pas index 568283d76..d608a6673 100644 --- a/Source/simba.vartype_triangle.pas +++ b/Source/simba.vartype_triangle.pas @@ -18,9 +18,15 @@ interface uses Classes, SysUtils, - simba.base; + simba.base, + simba.vartype_circle; type + TTriangle = record + A,B,C: TPoint; + end; + TTriangleArray = array of TTriangle; + TTriangleHelper = type helper for TTriangle private function GetCorners: TPointArray; @@ -30,7 +36,7 @@ interface public const EMPTY: TTriangle = (A: (X:0; Y:0); B: (X:0; Y:0); C: (X:0; Y:0)); public - class function Create(A, B, C: TPoint): TTriangle; static; + class function Create(const A, B, C: TPoint): TTriangle; static; function Centroid(): TPoint; function SymmedianPoint(): TPoint; @@ -39,8 +45,6 @@ interface function Rotate(Radians: Double): TTriangle; function Contains(P: TPoint): Boolean; function Offset(P: TPoint): TTriangle; - function Extract(Points: TPointArray): TPointArray; - function Exclude(Points: TPointArray): TPointArray; function Expand(Amount: Int32): TTriangle; function NearestEdge(P: TPoint): TPoint; function IsObtuse(): Boolean; overload; @@ -57,17 +61,23 @@ interface property Bounds: TBox read GetBounds; end; + PTriangle = ^TTriangle; + PTriangleArray = ^TTriangleArray; + operator in(const P: TPoint; const Triangle: TTriangle): Boolean; implementation uses Math, - simba.math, simba.vartype_pointarray, simba.random, simba.geometry, simba.containers, - simba.vartype_point; + simba.math, + simba.random, + simba.geometry, + simba.vartype_point, + simba.vartype_polygon; -class function TTriangleHelper.Create(A ,B, C: TPoint): TTriangle; +class function TTriangleHelper.Create(const A ,B, C: TPoint): TTriangle; begin Result.A := A; Result.B := B; @@ -134,7 +144,6 @@ function TTriangleHelper.SymmedianPoint(): TPoint; function TTriangleHelper.Incenter(): TPoint; var ar, br, cr: Single; - I: TPoint; begin ar := Distance(B, C); br := Distance(A, C); @@ -146,7 +155,6 @@ function TTriangleHelper.Incenter(): TPoint; function TTriangleHelper.Rotate(Radians: Double): TTriangle; begin - // we can use RotatePointFast for any A,B,C if dist to X,Y < 100 with Self.Mean do begin Result.A := TSimbaGeometry.RotatePoint(Self.A, Radians, X, Y); @@ -167,56 +175,15 @@ function TTriangleHelper.Offset(P: TPoint): TTriangle; Result := TTriangle.Create(A.Offset(P), B.Offset(P), C.Offset(P)); end; - -function TTriangleHelper.Extract(Points: TPointArray): TPointArray; -var - I: Integer; - Buffer: TSimbaPointBuffer; -begin - Buffer.Init(Length(Points)); - for I := 0 to High(Points) do - if Self.Contains(Points[I]) then - Buffer.Add(Points[I]); - - Result := Buffer.ToArray(False); -end; - - -function TTriangleHelper.Exclude(Points: TPointArray): TPointArray; -var - I: Integer; - Buffer: TSimbaPointBuffer; -begin - Buffer.Init(Length(Points)); - for I := 0 to High(Points) do - if not Self.Contains(Points[I]) then - Buffer.Add(Points[I]); - - Result := Buffer.ToArray(False); -end; - - function TTriangleHelper.Expand(Amount: Int32): TTriangle; var - poly: TPointArray; - tmp: TPoint; - o: Int32; + poly: TPolygon; begin - o := TSimbaGeometry.CrossProduct(A,B,C); - SetLength(poly, 3); - poly[0] := A; - poly[1] := B; - poly[2] := C; - - if o < 0 then - begin - tmp := A; - A := C; - C := tmp; - end; - - poly := TSimbaGeometry.ExpandPolygon(poly, Amount); + poly := [A,B,C]; + if (TSimbaGeometry.CrossProduct(Self.A, Self.B, Self.C) < 0) then + Swap(poly[0], poly[2]); + poly := poly.Expand(Amount); Result.A := poly[0]; Result.B := poly[1]; Result.C := poly[2]; diff --git a/Tests/point.simba b/Tests/point.simba deleted file mode 100644 index 50d8a7a93..000000000 --- a/Tests/point.simba +++ /dev/null @@ -1,21 +0,0 @@ -{$assertions on} - -var - p: TPoint; - poly: TPointArray; -begin - p := TPoint.Create(100, 150); - poly := [TPoint.Create(100, 100), TPoint.Create(200, 200), TPoint.Create(50, 300)]; - - Assert(p.InCircle(TPoint.Create(100, 100), 50) = True); - Assert(p.InCircle(TPoint.Create(100, 100), 49) = False); - Assert(p.InBox(TBox.Create(100,100,200,200)) = True); - Assert(p.InBox(TBox.Create(0,0, 99, 149)) = False); - Assert(TPoint.Create(100, 200).InPolygon(poly) = True); - Assert(TPoint.Create(50, 200).InPolygon(poly) = False); - Assert(TPoint.Create(100, 200).InTriangle(poly[0], poly[1], poly[2]) = True); - Assert(TPoint.Create(50, 200).InTriangle(poly[0], poly[1], poly[2]) = False); - Assert(p.DistanceTo(TPoint.Create(100, 100)) = 50); - Assert(p.Offset(-100, -150) = [0, 0]); - Assert(p.Offset(TPoint.Create(-100, -150)) = [0, 0]); -end. diff --git a/Tests/box.simba b/Tests/shape_box.simba similarity index 100% rename from Tests/box.simba rename to Tests/shape_box.simba diff --git a/Tests/circle.simba b/Tests/shape_circle.simba similarity index 100% rename from Tests/circle.simba rename to Tests/shape_circle.simba diff --git a/Tests/shape_polygon.simba b/Tests/shape_polygon.simba new file mode 100644 index 000000000..44c78abcb --- /dev/null +++ b/Tests/shape_polygon.simba @@ -0,0 +1,40 @@ +{$assertions on} + +var + p: TPolygon; + tpa: TPointArray; + x,y,i: Integer; +begin + // concave + p := [[49,8], [87,32], [64,67], [39,34], [14,60], [5,14]]; + + Assert(Abs(p.Area - 2665) <= 5); + Assert(p.IsConvex = False); + + tpa := []; + for y:=0 to 200 do + for x := 0 to 200 do + if p.Contains([x,y]) then + tpa += [x,y]; + + Assert(Abs(tpa.Bounds.X1 - TPointArray(p).Bounds.X1) <= 2); + Assert(Abs(tpa.Bounds.Y1 - TPointArray(p).Bounds.Y1) <= 2); + Assert(Abs(tpa.Bounds.X2 - TPointArray(p).Bounds.X2) <= 2); + Assert(Abs(tpa.Bounds.Y2 - TPointArray(p).Bounds.Y2) <= 2); + + // convex + p := [[39,7], [69,26], [69,56], [40,76], [9,56], [9,25]]; + Assert(Abs(p.Area - 2985) <= 5); + Assert(p.IsConvex = True); + + tpa := []; + for y:=0 to 200 do + for x := 0 to 200 do + if p.Contains([x,y]) then + tpa += [x,y]; + + Assert(Abs(tpa.Bounds.X1 - TPointArray(p).Bounds.X1) <= 2); + Assert(Abs(tpa.Bounds.Y1 - TPointArray(p).Bounds.Y1) <= 2); + Assert(Abs(tpa.Bounds.X2 - TPointArray(p).Bounds.X2) <= 2); + Assert(Abs(tpa.Bounds.Y2 - TPointArray(p).Bounds.Y2) <= 2); +end. diff --git a/Tests/quad.simba b/Tests/shape_quad.simba similarity index 81% rename from Tests/quad.simba rename to Tests/shape_quad.simba index f1cae9462..fce415dc8 100644 --- a/Tests/quad.simba +++ b/Tests/shape_quad.simba @@ -5,6 +5,8 @@ var begin q := TQuad.CreateFromBox([100,100,400,200]); + Assert(q = [[100,100],[400,100],[400,200],[100,200]]); + Assert(q.Expand(1).Expand(-1) = [[400,200],[100,200],[100,100],[400,100]]); Assert(q.ShortSideLen = 100); Assert(q.LongSideLen = 300); Assert(q.Area = 30000); @@ -16,7 +18,6 @@ begin Assert(q.Corners[3] = [100,200]); q := q.Rotate(120); - Assert(q.ShortSideLen = 100); Assert(q.LongSideLen = 300); Assert(q.Area = 30000); diff --git a/Tests/tpa_exclude.simba b/Tests/tpa_exclude.simba index 75dee2286..3bea3e32e 100644 --- a/Tests/tpa_exclude.simba +++ b/Tests/tpa_exclude.simba @@ -6,6 +6,7 @@ var begin TestTPA := TPointArray.CreateFromBox([10,10,40,40],True); + // Box ExcludedTPA := TestTPA.ExcludeBox([20,20,30,30]); Assert(ExcludedTPA.Bounds() = TestTPA.Bounds()); Assert(ExcludedTPA.Length = 840); @@ -29,4 +30,16 @@ begin ExcludedTPA := TestTPA.ExcludePolygon([TPoint.Create(15, 15), TPoint.Create(30, 25), TPoint.Create(15, 35)]); Assert(ExcludedTPA.Bounds() = TestTPA.Bounds()); Assert(ExcludedTPA.Length = 806); + + // Points + ExcludedTPA := TestTPA.ExcludePoints(TPointArray.CreateFromBox([11,11,39,39],True)); + Assert(ExcludedTPA.Bounds() = TestTPA.Bounds()); + Assert(ExcludedTPA.Length = 120); + + ExcludedTPA := TestTPA.ExcludePoints(TPointArray.CreateFromBox([11,11,100,100],True)); + Assert(ExcludedTPA.Bounds() = TestTPA.Bounds()); + Assert(ExcludedTPA.Length = 61); + + ExcludedTPA := TestTPA.ExcludePoints(TPointArray.CreateFromBox([1,1,100,100],True)); + Assert(ExcludedTPA.Length = 0); end.