Very Simple Round Gauge Discussion Thread
#1
Posted  in source code forum.

Nice one Kevin,  yours looks better.   I didn't know I had a friendly stalker!  Big Grin

Patrice, I gave it a try,  while I've run out of socks from trying all your other demo's out, I did feel the whoosh and then  goosebumps!  Impressive!  Seems much much faster!   Did you use the native LZNT1 in the last release?

Since I know diddly about obj files I downloaded a free one of the curiosity Rover...
https://free3d.com/3d-model/curiosity-rover-46907.html
...but it's missing the MTL file, so I made one up, unfortunately the conversion still failed.   

Code:
# dummy MTL for Curiosity Rover
newmtl default
Ka 0.200000 0.200000 0.200000
Kd 0.666667 0.666667 0.666667
Ks 0.000000 0.000000 0.000000
Ns 1.000000
illum 2
Pl
P
Reply
#2
(24.09.2025, 11:33 PM)Jules Marchildon Wrote: Nice one Kevin,  yours looks better.   I didn't know I had a friendly stalker!  Big Grin

Certainly not a stalker, friendly or otherwise- just a decades long fan of your code.   

Keep 'em coming!
Reply
#3
1. On the speed / compression:
Yes, starting with the last release, OR can take advantage of native LZNT1 compression on Windows. That’s why the load/unload feels noticeably snappier. It avoids the overhead of external zlib and integrates directly with the system’s fast block compression API.

2. On the Curiosity Rover model:
Many free .obj downloads come without a proper .mtl file, or with broken texture paths. OR expects at least one .mtl entry (even if it’s minimal).
  • If you invent an .mtl, make sure the material name matches the “usemtl” statements in the .obj. Otherwise, the loader won’t be able to attach materials to faces, and the conversion will fail.
  • A barebones .mtl could be as simple as:
    Code:
    newmtl Default
    Kd 0.8 0.8 0.8
    Ka 0.2 0.2 0.2
    Ks 0.0 0.0 0.0
    d 1.0
    illum 2
…and then in the .obj, replace any usemtl ... lines with usemtl Default.

That should let OR import it cleanly (you’ll just get a plain-colored rover, but you can layer in textures later).
But that only, won't fix the bad conversion.

Do you want me to prep a ready-to-use dummy .mtl material, altogether with fixed .obj file, so you can see it in OR?

Added:
BTW, the model is full of small normal errors.
www.objreader.com
GDImage.dll (graphics library), WinLIFT.dll (skin engine)
Reply
#4
Patrice,  in reference to your PlgBlt() suggestion,  I can't seem to get my needle to draw correctly, does anything pop out at you in this snippet?  

Code:
    '=== draw needle using PlgBlt ===
    angle = (225 - (value * 2.7!)) * cherryPi / 180

    '-memory DC and bitmap for the needle
    LOCAL hMemDC, hBitmap, hOldBitmap AS DWORD
    LOCAL hMaskDC, hMaskBitmap, hOldMaskBitmap AS DWORD
    LOCAL needleHeight AS LONG

    needleWidth = 6
    needleHeight = radius * 0.75

    hMemDC = CreateCompatibleDC(hDC)
    hBitmap = CreateCompatibleBitmap(hDC, needleWidth, needleHeight)
    hOldBitmap = SelectObject(hMemDC, hBitmap)

    '-draw a triangular needle in the source bitmap
    hBrush = CreateSolidBrush(RGB(255, 0, 0))
    hOldBrush = SelectObject(hMemDC, hBrush)
    hPen = CreatePen(%PS_SOLID, 1, RGB(0, 0, 0))
    hOldPen = SelectObject(hMemDC, hPen)

    '-triangle points
    DIM bmpPts(0 TO 2) AS POINTAPI
    bmpPts(0).x = needleWidth \ 2 : bmpPts(0).y = needleHeight  '-tip   *bottom center
    bmpPts(1).x = 0               : bmpPts(1).y = 0             '-baseL *top left
    bmpPts(2).x = needleWidth     : bmpPts(2).y = 0             '-baseR *top right

    POLYGON hMemDC, bmpPts(0), 3

    SelectObject hMemDC, hOldBrush
    DeleteObject hBrush
    SelectObject hMemDC, hOldPen
    DeleteObject hPen

    '-a 1-bit mask for transparency
    hMaskDC = CreateCompatibleDC(hDC)
    hMaskBitmap = CreateBitmap(needleWidth, needleHeight, 1, 1, BYVAL %NULL)
    hOldMaskBitmap = SelectObject(hMaskDC, hMaskBitmap)

    hBrush = CreateSolidBrush(RGB(255, 255, 255))
    hOldBrush = SelectObject(hMaskDC, hBrush)
    POLYGON hMaskDC, bmpPts(0), 3
    SelectObject hMaskDC, hOldBrush
    DeleteObject hBrush

    SetStretchBltMode hDC, %HALFTONE  '<-set stretch mode for better quality

    DIM pts(0 TO 2) AS POINTAPI
    pts(0).x = cx + (radius * 0.75) * COS(angle)
    pts(0).y = cy - (radius * 0.75) * SIN(angle)
    offsetRad = 90 * cherryPi / 180
    pts(1).x = cx + needleWidth * COS(angle + offsetRad)
    pts(1).y = cy - needleWidth * SIN(angle + offsetRad)
    pts(2).x = cx + needleWidth * COS(angle - offsetRad)
    pts(2).y = cy - needleWidth * SIN(angle - offsetRad)

    PlgBlt hDC, pts(0), hMemDC, 0, 0, needleWidth, needleHeight, hMaskBitmap, 0, 0

    SelectObject hMemDC, hOldBitmap
    DeleteObject hBitmap
    DeleteDC hMemDC
    SelectObject hMaskDC, hOldMaskBitmap
    DeleteObject hMaskBitmap
    DeleteDC hMaskDC

    '=== center cap ===
    hPen = CreatePen(%PS_SOLID, 1, RGB(0, 0, 0)) '-outline
    hOldPen = SelectObject(hDC, hPen)
    ELLIPSE hDC, cx - 8, cy - 8, cx + 8, cy + 8
    SelectObject hDC, hOldPen
    DeleteObject hPen

             
Reply
#5
Jules

I have a new ASUS laptop, but not had time yet to install PowerBASIC on it (Only VS2022).

Thus about your problem

Short answer: the three destination points you pass to PlgBlt don’t correspond to the expected corners of the source rectangle.
PlgBlt maps the source RECT (0,0,w,h) to a parallelogram whose corners must be given in this order:

pts(0) → upper-left of the dest parallelogram (maps source (0,0))

pts(1) → upper-right (maps source (w,0))

pts(2) → lower-left (maps source (0,h))

Your code sets pts(0) at the needle tip and the other two as small offsets around the center, so the source bitmap (which has base on top and tip at the bottom) is being warped incorrectly. You also use the full needleWidth instead of half-width for the side offsets.

Fix

Keep the source bitmap exactly as you drew it (base at the top, tip at the bottom). Compute a unit vector along the needle (u) and a perpendicular unit vector (v), then place the three corners as a rotated rectangle whose top edge is the base and whose bottom edge reaches the tip.

PowerBASIC-style math (same symbols you used):

' angle already in radians, pointing from center to tip
ux = COS(angle)          : uy = -SIN(angle)        ' forward (toward tip)
vx = COS(angle + cherryPi/2) : vy = -SIN(angle + cherryPi/2)  ' left normal

halfW = needleWidth / 2
h    = needleHeight

' Base is at the gauge center (cx,cy)
baseX = cx : baseY = cy

' === Parallelogram corners for PlgBlt ===
' UL (maps source 0,0)  → base - halfW * v
pts(0).x = baseX - halfW * vx
pts(0).y = baseY - halfW * vy

' UR (maps source w,0)  → base + halfW * v
pts(1).x = baseX + halfW * vx
pts(1).y = baseY + halfW * vy

' LL (maps source 0,h)  → base + h * u - halfW * v
pts(2).x = baseX + h * ux - halfW * vx
pts(2).y = baseY + h * uy - halfW * vy

PlgBlt hDC, pts(0), hMemDC, 0, 0, needleWidth, needleHeight, hMaskBitmap, 0, 0

Mask note

You create a 1-bit mask and paint the same triangle white on black. That’s fine for PlgBlt (white bits in the mask select the pixels to transfer). If you see inverted transparency, just invert the mask (fill white, draw the triangle black).

Other small nits

Use half width for the side offsets (fixed above).

SetStretchBltMode(hDC, %HALFTONE) is harmless here but not required unless you actually stretch the source.

Ensure you re-select the old bitmap/brush/pen into the DCs before deleting (you already do that—good).

With the corrected corner mapping the needle will rotate cleanly around (cx,cy) while still using your triangular source + mask.
www.objreader.com
GDImage.dll (graphics library), WinLIFT.dll (skin engine)
Reply
#6
Patrice, I'm not getting any better results, perhaps in this case. It's OK, I do have a custom GDI+ round gauge to finish working on later.  For now, this simple gauge is good enough for the testing PCB application at work.

   
Code:
    '=== draw needle using PlgBlt with corrected points ===
    angle = (225 - (value * 2.7!)) * cherryPi / 180

    '-create a memory DC and bitmap for the needle
    LOCAL hMemDC AS DWORD, hBitmap AS DWORD, hOldBitmap AS DWORD
    LOCAL hMaskDC AS DWORD, hMaskBitmap AS DWORD, hOldMaskBitmap AS DWORD
    LOCAL needleHeight AS LONG
    needleWidth = 14
    needleHeight = radius * 0.80

    hMemDC = CreateCompatibleDC(hDC)
    hBitmap = CreateCompatibleBitmap(hDC, needleWidth, needleHeight)
    hOldBitmap = SelectObject(hMemDC, hBitmap)

    '-draw a triangular needle in the source bitmap
    hBrush = CreateSolidBrush(RGB(255, 0, 0))     '-red fill
    hOldBrush = SelectObject(hMemDC, hBrush)
    hPen = CreatePen(%PS_SOLID, 1, RGB(0, 0, 0))  '-black outline
    hOldPen = SelectObject(hMemDC, hPen)

    '-define triangle points in the bitmap, *base at top, tip at bottom
    DIM bmpPts(0 TO 2) AS POINTAPI
    bmpPts(0).x = needleWidth \ 2 : bmpPts(0).y = needleHeight  '-tip,  bottom center
    bmpPts(1).x = 0               : bmpPts(1).y = 0             '-base, left top left
    bmpPts(2).x = needleWidth     : bmpPts(2).y = 0             '-base, right top right

    POLYGON hMemDC, bmpPts(0), 3

    SelectObject hMemDC, hOldBrush
    DeleteObject hBrush
    SelectObject hMemDC, hOldPen
    DeleteObject hPen

    ' -create a 1-bit mask for transparency
    hMaskDC = CreateCompatibleDC(hDC)
    hMaskBitmap = CreateBitmap(needleWidth, needleHeight, 1, 1, BYVAL %NULL)
    hOldMaskBitmap = SelectObject(hMaskDC, hMaskBitmap)

    hBrush = CreateSolidBrush(RGB(255, 255, 255))  '-white = opaque
    hOldBrush = SelectObject(hMaskDC, hBrush)
    POLYGON hMaskDC, bmpPts(0), 3  '-same triangle for mask
    SelectObject hMaskDC, hOldBrush
    DeleteObject hBrush

    '-set stretch mode for better quality
    'SetStretchBltMode hDC, %HALFTONE

    '-define destination points using unit vectors (per Patrice)
    DIM pts(0 TO 2) AS POINTAPI
    LOCAL ux, uy, vx, vy, halfW, baseX, baseY AS SINGLE
    ux = COS(angle)          : uy = -SIN(angle)          '-forward, toward tip
    vx = COS(angle + cherryPi / 2) : vy = -SIN(angle + cherryPi / 2)  '-left normal
    halfW = needleWidth / 2
    LOCAL h AS LONG
    h = needleHeight
    baseX = cx : baseY = cy

    '-parallelogram corners for PlgBlt
    ' UL (maps source 0,0)  base - halfW * v
    pts(0).x = baseX - halfW * vx
    pts(0).y = baseY - halfW * vy
    ' UR (maps source w,0)  base + halfW * v
    pts(1).x = baseX + halfW * vx
    pts(1).y = baseY + halfW * vy
    ' LL (maps source 0,h)  base + h * u - halfW * v
    pts(2).x = baseX + h * ux - halfW * vx
    pts(2).y = baseY + h * uy - halfW * vy
    '-do the PlgBlt with mask
    PlgBlt hDC, pts(0), hMemDC, 0, 0, needleWidth, needleHeight, hMaskBitmap, 0, 0

    '-clean up
    SelectObject hMemDC, hOldBitmap
    DeleteObject hBitmap
    DeleteDC hMemDC
    SelectObject hMaskDC, hOldMaskBitmap
    DeleteObject hMaskBitmap
    DeleteDC hMaskDC

    '=== center cap ===
    hPen = CreatePen(%PS_SOLID, 1, RGB(0, 0, 0)) '-outline
    hOldPen = SelectObject(hDC, hPen)
    ELLIPSE hDC, cx - 8, cy - 8, cx + 8, cy + 8
    SelectObject hDC, hOldPen
    DeleteObject hPen 
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)