  # angusj

Members

55

• #### Days Won

5

angusj had the most liked content!

84 Excellent

## Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

1. Here's my GetSmoothPath() routine. It requires no specific graphics library to use, just a few extra functions (also included below). This function generates an array of control points that's very easily converted into a flattened cubic bezier path using just about any 2D graphics library. (nb: The code below has been written with simplicity as the focus rather than performance.) uses SysUtils, Math; type TPointD = record X, Y: double; end; TPathD = array of TPointD; TArrayOfDouble = array of double; function DistanceSqrd(const pt1, pt2: TPointD): double; begin result := Sqr(pt1.X - pt2.X) + Sqr(pt1.Y - pt2.Y); end; function Distance(const pt1, pt2: TPointD): double; begin Result := Sqrt(DistanceSqrd(pt1, pt2)); end; function OffsetPoint(const pt: TPointD; dx, dy: double): TPointD; begin result.x := pt.x + dx; result.y := pt.y + dy; end; function GetAvgUnitVector(const vec1, vec2: TPointD): TPointD; var inverseHypot: Double; begin Result.X := (vec1.X + vec2.X) * 0.5; Result.y := (vec1.Y + vec2.Y) * 0.5; inverseHypot := 1 / Hypot(Result.X, Result.Y); Result.X := Result.X * inverseHypot; Result.Y := Result.Y * inverseHypot; end; procedure MakeSymmetric(var val1, val2: double); begin val1 := (val1 + val2) * 0.5; val2 := val1; end; function GetUnitVector(const pt1, pt2: TPointD): TPointD; var dx, dy, inverseHypot: Double; begin if (pt1.x = pt2.x) and (pt1.y = pt2.y) then begin Result.X := 0; Result.Y := 0; Exit; end; dx := (pt2.X - pt1.X); dy := (pt2.Y - pt1.Y); inverseHypot := 1 / Hypot(dx, dy); dx := dx * inverseHypot; dy := dy * inverseHypot; Result.X := dx; Result.Y := dy; end; // GetSmoothPath - returns cubic bezier control points // parameters: 1. path for smoothing // 2. whether or not the smoothed path will closed // 3. percent smoothness (0..100) // 4. maximum dist control pts from path pts (0 = no limit) // 5. symmetric vs asymmmetric control pts function GetSmoothPath(const path: TPathD; pathIsClosed: Boolean; percentOffset, maxCtrlOffset: double; symmetric: Boolean): TPathD; var i, len, prev: integer; vec: TPointD; pl: TArrayOfDouble; unitVecs: TPathD; d, d1,d2: double; begin Result := nil; len := Length(path); if len < 3 then Exit; d := Max(0, Min(100, percentOffset))/200; if maxCtrlOffset <= 0 then maxCtrlOffset := MaxDouble; SetLength(Result, len *3 +1); prev := len-1; SetLength(pl, len); SetLength(unitVecs, len); for i := 0 to len -1 do begin pl[i] := Distance(path[prev], path[i]); unitVecs[i] := GetUnitVector(path[prev], path[i]); prev := i; end; Result[len*3] := path; for i := 0 to len -1 do begin if i = len -1 then begin vec := GetAvgUnitVector(unitVecs[i], unitVecs); d2 := pl*d; end else begin vec := GetAvgUnitVector(unitVecs[i], unitVecs[i+1]); d2 := pl[i+1]*d; end; d1 := pl[i]*d; if symmetric then MakeSymmetric(d1, d2); if i = 0 then Result[len*3-1] := OffsetPoint(path[i], -vec.X * Min(maxCtrlOffset, d1), -vec.Y * Min(maxCtrlOffset, d1)) else Result[i*3-1] := OffsetPoint(path[i], -vec.X * Min(maxCtrlOffset, d1), -vec.Y * Min(maxCtrlOffset, d1)); Result[i*3] := path[i]; Result[i*3+1] := OffsetPoint(path[i], vec.X * Min(maxCtrlOffset, d2), vec.Y * Min(maxCtrlOffset, d2)); end; if not pathIsClosed then begin Result := Result; dec(len); Result[len*3-1] := Result[len*3]; SetLength(Result, Len*3 +1); end; end; And here's what it produces ... the path to smooth (black), the cubic bezier control path produced by GetSmoothPath() (blue) and the flattened cubic bezier path (2D graphics library of you choice required) (red). var TPathD path; begin path := MakePath([190,120, 260,270, 560,120, 190,490]); path := GetSmoothPath(path, true, 20, 0, false); path := ThirdParty2DGraphicsLibrary.FlattenCBezier(path); end; var TPathD path; begin path := MakePath([190,120, 260,270, 560,120, 190,490]); path := GetSmoothPath(path, true, 80, 0, false); path := ThirdParty2DGraphicsLibrary.FlattenCBezier(path); end; Edit: The best way to avoid intersections is to make sure you have enough data points before generating your curves.
2. ## Enable "Mark Site Read" in mobile view.

Thanks Dale. I have been doing that. I just find it somewhat tedious having to do that every time i visit DP. IMHO, having a "Mark site read" button at the bottom as in desktop view would be much more convenient. Cheers.
3. ## Enable "Mark Site Read" in mobile view.

4. ## A book about Object Pascal Style Guide

See above. While I also missed downloading and reading Normann's book, it seems he wasn't encouraged by the initial feedback. I think there's a lessen in that. Normann had put considerable effort into writing this book and uploaded it as a free resource and, from what I can glean from this thread, it was mostly of high quality.
5. ## Detecting start of drag operations

That seems likely be the solution to Dave Nottage's problem, though it isn't the answer to his question 😁.
6. ## Detecting start of drag operations

Despite the lack of documentation on EVENT_OBJECT_DRAGSTART here: https://docs.microsoft.com/en-us/windows/win32/winauto/event-constants it seems likely that this event is only triggered by "UI Automation" Drag-and-Drop https://docs.microsoft.com/en-us/windows/win32/winauto/ui-automation-support-for-drag-and-drop
7. ## Detecting start of drag operations

Is there a difference between OLE and "the standard windows drag and drop"? "The OLE DoDragDrop function calls this method (IDropSource::QueryContinueDrag) during a drag-and-drop operation." https://docs.microsoft.com/en-us/windows/win32/api/oleidl/nn-oleidl-idropsource Of course, this is very different to Delphi's custom drag drop stuff.
8. ## Detecting start of drag operations

guessing ... startDragEvent := SetWinEventHook(EVENT_OBJECT_DRAGSTART, EVENT_OBJECT_DRAGSTART, 0, @DragStartEvent, 0, 0, WINEVENT_OUTOFCONTEXT); procedure DragStartEvent(hWinEventHook: THandle; event: DWORD; hwnd: HWND; idObject, idChild: Longint; idEventThread, dwmsEventTime: DWORD); stdcall; begin //do stuff end; Note: All OLE drag events are initiated by DoDragDrop https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-dodragdrop Since the drag souce (IDropSource, IDataObject) is initiated by another process so you'll certainly need a system event hook.
9. I wholeheartedly agree with you Anders that TBitmap.AlphaFormat is broken and should be deprecated. Not only does it not behave in an intuitive manner, but Premultiplying and UnPremultiplying images is (mildly) destructive. But there's another potentially bigger problem - when writing an afDefined Bitmap to stream, UnPreMultiplyAlpha is called and hence the image will no longer be properly rendered. ps: Isn't it fun digging up old threads 😜.
10. ## tImagecollection, how to improve downscaling?

Delete this line. It's only (sort of) useful for images without alpha transparency.
11. Edit: New Vcl.Imaging.QOI unit uploaded to GitHub. See https://github.com/AngusJohnson/TQoiImage
12. OK, all fixed now. I've attached a working example. The example loads a QOI file and saves it to BMP, then loads the saved BMP and saves that to a second QOI, and finally loads the second QOI and saves as a second BMP. Edit: Another minor bugfix (with thanks to feedback from kadaif). Edit2: Deleted obsolete attachment. See post below.
13. The following seems OK reading QOI files/streams ... Edit: Just spotted an occassional artefact in the rendered images that I'm now chasing. I've also written the WriteToStream code (that seems bug free) that I'll upload once I've found and fixed the bug in the ReadFromStream code above. Edit2: Code sample above deleted. See bugfixed version in zip package below.
14. Persisting with my dance analogy, being so late to the dance everyone has partnered up. IOW, browers must support formats that webpages use, and webpages will use what browsers support. But as I said above, there's little cost to browsers supporting QOI given the simplicity of the compression and decompression algorithms. Time will tell.
15. QOI is certainly very late to the dance but she's very pretty (very simple to implement and good compression). So I wouldn't be surprised if browsers quickly supported this new format too.
×