Leaderboard
Popular Content
Showing content with the highest reputation on 08/06/24 in Posts
-
VSoft.Ulid - A Delphi Implementation of ULID for Delphi XE2 or later.
Vincent Parrett posted a topic in I made this
Hi All I created a Delphi implementation of ULID - A Universally Unique Lexicographically Sortable Identifier. ULID's are a better option than GUIDs where sorting is needed 128-bit compatibility with UUID 1.21e+24 unique ULIDs per millisecond Lexicographically sortable! Canonically encoded as a 26 character string, as opposed to the 36 character UUID Uses Crockford's base32 for better efficiency and readability (5 bits per character) Case insensitive No special characters (URL safe) Monotonic sort order (correctly detects and handles the same millisecond) https://github.com/VSoftTechnologies/VSoft.Ulid -
VSoft.Ulid - A Delphi Implementation of ULID for Delphi XE2 or later.
Vincent Parrett replied to Vincent Parrett's topic in I made this
@Stefan Glienke is way ahead of you already sent me a few suggestions. Using a variant record case Boolean of True: ( FRandomness0 : byte; FRandomness1 : byte; FRandomness2 : byte; FRandomness3 : byte; FRandomness4 : byte; FRandomness5 : byte; FRandomness6 : byte; FRandomness7 : byte; FRandomness8 : byte; FRandomness9 : byte); False: ( FRandomness0_1 : Word; FRandomness2_9 : UInt64); Did squeeze a out few ns better performance. I'll test your idea though - because micro optimising is more fun the boring stuff I was working on... 😉 -
VSoft.Ulid - A Delphi Implementation of ULID for Delphi XE2 or later.
Vincent Parrett replied to Vincent Parrett's topic in I made this
This made no discernable difference. Your version of avoiding the move was slightly faster than the variant array idea. Thanks to a bunch of suggestions from Stefan & Kas it is now a lot faster than the c# version I was comparing it to (64bit create - 63ns) - I did also add some validation to the parse method which slowed it down again! The big speedup in the Parse method was avoiding TEncoding.GetBytes - which uses a dynamic array (slow). D 11.3 - 32bit ------------------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------------------- TUlid.Create 52.1 ns 51.6 ns 10000000 TUlid.Parse - AnsiString 28.4 ns 28.5 ns 23578947 TUlid.Parse 33.7 ns 33.0 ns 20363636 TUlid.ToString 32.0 ns 32.5 ns 23578947 D 11.3 64bit ------------------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------------------- TUlid.Create 10.9 ns 11.0 ns 64000000 TUlid.Parse - AnsiString 28.1 ns 28.5 ns 23578947 TUlid.Parse 29.2 ns 29.5 ns 24888889 TUlid.ToString 29.4 ns 29.3 ns 22400000 D12.1 32bit ------------------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------------------- TUlid.Create 51.2 ns 51.6 ns 10000000 TUlid.Parse - AnsiString 28.5 ns 28.3 ns 24888889 TUlid.Parse 30.2 ns 30.5 ns 23578947 TUlid.ToString 30.2 ns 29.3 ns 22400000 D 12.1 64bit ------------------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------------------- TUlid.Create 8.94 ns 8.79 ns 74666667 TUlid.Parse - AnsiString 28.1 ns 28.5 ns 26352941 TUlid.Parse 32.8 ns 32.2 ns 21333333 TUlid.ToString 33.0 ns 33.0 ns 20363636 Anyway, that's more than fast enough - given that this is more than likely used to generate id's for use with databases or in messaging - it's unlikely you would ever hit a performance issue with this! https://github.com/VSoftTechnologies/VSoft.Ulid/tree/f-optimise I'll merge into main shortly. Edit : forgot to mention, the reason 32bit is still slower than 64bit is due to way the compiler does Int64 division (calls a helper function). -
Upgrading to new RAD Studio vs. project Lib names
Vincent Parrett replied to w0wbagger's topic in General Help
This is a pet peeve of mine, library authors and package naming - not using LibSuffix in their packages and adding the version to the package names. If you are not using runtime packages (most people don't) - then the IDE doesn't really track what is used by the project - it just uses the Library path and project Search path to make those libraires available. I have been working on a solution for a while - a package manager - still a work in progress but getting there - see delphi.dev - it will need library author/vendors to create packages to make it work though. DPM does store the package references in the dproj file - when you load up the project in a new version of RAD Studio (and the ide plugin is installed) - it will automatically download and install the dpm packages (assuming there are versions available for the new delphi version). -
Rounded polygon
vfbb replied to A.M. Hoornweg's topic in Algorithms, Data Structures and Class Design
Here's an example: uses Skia; function MakeCubicSplineInterpolation(const APoints: TArray<TPointF>): ISkPath; var LPathBuilder: ISkPathBuilder; LSegments: Integer; I: Integer; mx: Single; my: Single; LScratches: array of record a, b, c, r, p: TPointF; end; begin LPathBuilder := TSkPathBuilder.Create; if Length(APoints) < 2 then Exit(LPathBuilder.Detach); if Length(APoints) = 2 then begin LPathBuilder.MoveTo(APoints[0]); LPathBuilder.LineTo(APoints[1]); Exit(LPathBuilder.Detach); end; LSegments := Length(APoints) - 1; SetLength(LScratches, LSegments); LScratches[0].a := PointF(0, 0); LScratches[0].b := PointF(2, 2); LScratches[0].c := PointF(1, 1); LScratches[0].r := PointF(APoints[0].X + 2 * APoints[1].X, APoints[0].Y + 2 * APoints[1].Y); for I := 1 to LSegments - 2 do begin LScratches[I].a := PointF(1, 1); LScratches[I].b := PointF(4, 4); LScratches[I].c := PointF(1, 1); LScratches[I].r := PointF(4 * APoints[i].X + 2 * APoints[I + 1].X, 4 * APoints[I].Y + 2 * APoints[I + 1].Y); end; LScratches[LSegments - 1].a := PointF(2, 2); LScratches[LSegments - 1].b := PointF(7, 7); LScratches[LSegments - 1].c := PointF(0, 0); LScratches[LSegments - 1].r := PointF(8 * APoints[LSegments - 1].X + APoints[LSegments].X, 8 * APoints[LSegments - 1].Y + APoints[LSegments].Y); for I := 1 to LSegments - 1 do begin mx := LScratches[I].a.X / LScratches[I - 1].b.X; my := LScratches[I].a.Y / LScratches[I - 1].b.Y; LScratches[I].b := LScratches[I].b - PointF(mx * LScratches[I - 1].c.X, my * LScratches[I - 1].c.Y); LScratches[I].r := LScratches[I].r - PointF(mx * LScratches[I - 1].r.X, my * LScratches[I - 1].r.Y); end; LScratches[LSegments - 1].p := PointF(LScratches[LSegments - 1].r.X / LScratches[LSegments - 1].b.X, LScratches[LSegments - 1].r.Y / LScratches[LSegments - 1].b.Y); for I := Length(APoints) - 3 downto 0 do begin LScratches[I].p := PointF((LScratches[I].r.X - LScratches[I].c.X * LScratches[I + 1].p.X) / LScratches[I].b.X, (LScratches[I].r.Y - LScratches[I].c.Y * LScratches[I + 1].p.Y) / LScratches[I].b.Y); end; LPathBuilder.MoveTo(APoints[0]); for I := 0 to LSegments - 2 do begin LPathBuilder.CubicTo(LScratches[I].p, PointF(2 * APoints[I + 1].X - LScratches[I + 1].p.X, 2 * APoints[I + 1].Y - LScratches[I + 1].p.Y), APoints[I + 1]); end; LPathBuilder.CubicTo(LScratches[LSegments - 1].p, PointF(0.5 * (APoints[LSegments].X + LScratches[LSegments - 1].p.X), 0.5 * (APoints[LSegments].Y + LScratches[LSegments - 1].p.Y)), APoints[LSegments]); Result := LPathBuilder.Detach; end; procedure TForm1.SkPaintBox1Draw(ASender: TObject; const ACanvas: ISkCanvas; const ADest: TRectF; const AOpacity: Single); var LPaint: ISkPaint; LMyPoints: TArray<TPointF>; begin LMyPoints := [PointF(62, 511), PointF(162, 605), PointF(262, 610), PointF(362, 402), PointF(462, 959), PointF(562, 58), PointF(662, 272), PointF(762, 99), PointF(862, 759), PointF(962, 945)]; LPaint := TSkPaint.Create(TSkPaintStyle.Stroke); LPaint.Color := TAlphaColors.Red; LPaint.AntiAlias := True; LPaint.StrokeWidth := 3; LPaint.StrokeCap := TSkStrokeCap.Round; ACanvas.DrawPath(MakeCubicSplineInterpolation(LMyPoints), LPaint); LPaint.StrokeWidth := 10; LPaint.Color := TAlphaColors.Black; ACanvas.DrawPoints(TSkDrawPointsMode.Points, LMyPoints, LPaint); end; Result: Note: You don't need to use Skia, it was just a facilitator for the example. -
Hi! I'm using Synedit to built a small SQL editor, but I need to change some default behavior. 1) I need to trigger Auto-completion when a '.' is typed, but I don't want to display the Completion Form base on a timer interval. Since the data is obtained asynchronously, I want to fill and display the completion form after receiving a specific custom message (WM_GETCOMPLETIONDATA). When handling the message, I will fill the list and display the form. Is there a way to call the completion form on demand? (the filling and displaying follows...) 2) I need to fill the autocompletion list based on the previous token value. The user can type a SQL select as select c.| from categories c (The '|' is the current cursor position). As the user types the '.', I would like to fill the completion list with all the columns from categories table. As '.' is typed, I can get the previous token ( which is "c" in this case), and do some parsing to discover "c" is in fact an alias for "Categories", send a custom message (WM_GETCOMPLETIONDATA) and when receiving the data, I can fill the completion form with all fields from categories table. It seems to me that SynEditProposal.OnExecute is the correct event to handle all the parsing. Is there a better one? TIA, Clément
-
Maybe to use preloaded database schema? In this case completion can be filled synchronously with no significant delay.