How to draw lines of a specific width and format

Just starting out? Need help? Post your questions and find answers here.
Braingap
New User
New User
Posts: 1
Joined: Fri Sep 10, 2010 9:25 am

How to draw lines of a specific width and format

Post by Braingap »

Can somebody tell me how to draw lines of a different width and format
More specifically, a line of specific width with straight and/or round ends.
And also how to draw lines in different styles (e.g. solid, dotted, ...)

Thanks in advance

Braingap
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How to draw lines of a specific width and format

Post by srod »

On Windows you need to use a geometric pen.

Code: Select all

Define lBrush.LOGBRUSH

With lBrush
  \lbStyle = #BS_SOLID 
  \lbColor = #Blue
EndWith

hPen1 = ExtCreatePen_(#PS_GEOMETRIC|#PS_ENDCAP_ROUND	|#PS_JOIN_MITER|#PS_SOLID, 20, lBrush, 0, 0) 
hPen2 = ExtCreatePen_(#PS_GEOMETRIC|#PS_ENDCAP_ROUND	|#PS_JOIN_MITER|#PS_DASH, 20, lBrush, 0, 0) 

If hPen1 And hPen2
  If OpenWindow(0, 100, 200, 300, 200, "2D Drawing Test")
    If CreateImage(0, 300, 200)
      hdc = StartDrawing(ImageOutput(0))
      If hdc
        Box(0, 0, 300, 200, #White)
        LineXY(10,10+k*8,200, 0)
        oldPen = SelectObject_(hdc, hPen1)
        MoveToEx_(hdc, 20, 20, 0)
        LineTo_(hdc, 280, 20)
        SelectObject_(hdc, hPen2)
        MoveToEx_(hdc, 20, 180, 0)
        LineTo_(hdc, 280, 180)
        SelectObject_(hdc, oldPen)
      EndIf
      StopDrawing()
    EndIf
    ImageGadget(0, 0, 0, 0, 0, ImageID(0))
  
    Repeat
      EventID = WaitWindowEvent() 
    Until EventID = #PB_Event_CloseWindow  ; If the user has pressed on the window close button
  EndIf
EndIf

If hPen1
  DeleteObject_(hPen1)
EndIf
If hPen2
  DeleteObject_(hPen2)
EndIf
I may look like a mule, but I'm not a complete ass.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Re: How to draw lines of a specific width and format

Post by blueznl »

Yeah, a 'line format' statement in PureBasic would be nice :-)
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: How to draw lines of a specific width and format

Post by RASHAD »

Line Format in PureBasic

Code: Select all

 If OpenWindow(0, 0, 0, 200, 200, "2DDrawing Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    If CreateImage(0, 200, 200) And StartDrawing(ImageOutput(0))
      Box(5,5,180,10,$0506FA)
      RoundBox(5, 30,180,14, 7, 7, $EB9A14)
      For x = 0 To 150 Step 30
        RoundBox(5+x, 60, 18,8, 4, 4, $7DF682)
      Next
      For x = 0 To 150 Step 30
        Box(5+x, 90, 15,4,$03FFFC)
        Circle(27+x,92,2,$03FFFC)
      Next
      StopDrawing() 
      ImageGadget(0, 0, 0, 200, 200, ImageID(0))
    EndIf
    
    Repeat
      Event = WaitWindowEvent()
    Until Event = #PB_Event_CloseWindow
  EndIf

Egypt my love
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How to draw lines of a specific width and format

Post by srod »

And diagonal lines? :wink:
I may look like a mule, but I'm not a complete ass.
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: How to draw lines of a specific width and format

Post by RASHAD »

:D
Sine & Cosine with multiple lines for the width
Beside may be he will not need diagonals :P
All what we need now is a RotateBox :P
Egypt my love
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: How to draw lines of a specific width and format

Post by netmaestro »

The pens as srod demonstrates them are a good solution on Windows. However, versatile as they are they will always look choppy and oldskool because they lack antialias capability. Here is where gdiplus can really take things up a notch. Any pattern or gradient, antialias and alphablending, any curve, arc or line, it's all there. Have you ever used gdiplus?
BERESHEIT
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How to draw lines of a specific width and format

Post by srod »

@Lloyd, a question if I may regarding gdi+. I've used it occasionally, but not in any great depth. The reason being that, well, it requires XP onwards (unless you install the redistributable etc.) But now that I generally only give support for XP onwards with my apps, I have been meaning to take a deeper look. My question; how do you use gdi+ to draw to a printer DC? Do you have to create a printer dc first and then use GdipCreateFromHDC() etc. or is there a more direct way?

Thanks.
I may look like a mule, but I'm not a complete ass.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: How to draw lines of a specific width and format

Post by netmaestro »

I've never used gdi+ directly to a printer! :oops:

But your approach of creating the printer dc and then GdipCreateFromDC is what I'd try first, seems sound.
BERESHEIT
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How to draw lines of a specific width and format

Post by srod »

Aye, I just wonder if StartPage_() / EndPage_() would somehow get in the way?

Right, that's on the 'to-do' list... test out GDI+ with a printer! :)
I may look like a mule, but I'm not a complete ass.
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: How to draw lines of a specific width and format

Post by Michael Vogel »

Would like to have a simple native LineXY routine for PB which allows drawing thicker lines (width=2 at least)...

My quick and dirty code is not only slow but would fail when drawing very thick short lines (see below):

Code: Select all

Procedure Liner(x1,y1,x2,y2,color=#Black,r=1)

    Protected n.f
    Protected c.f
    Protected x
    Protected y

    LineXY(x1,y1,x2,y2,color)
    If r>1 And r<180
        c=360/r/#PI/2/6
        n=0
        While n<360
            x=Sin(n/180*#PI)*r
            y=Cos(n/180*#PI)*r
            LineXY(x1+x,y1+y,x2+x,y2+y,color)
            n+c
        Wend
    EndIf

EndProcedure

OpenWindow(0, 100, 200, 300, 200, "2D Drawing Test")
CreateImage(0, 300, 200)
StartDrawing(ImageOutput(0))

Box(0, 0, 300, 200, #White)
Liner(20,20,280,180,#Blue,8)
Liner(20,120,220,40,#Red,4)
Liner(80,120,180,80,#Green,14)
;Liner(120,170,180,150,#Gray,40);   :-(
StopDrawing()
ImageGadget(0, 0, 0, 0, 0, ImageID(0))

Repeat
    EventID = WaitWindowEvent()
Until EventID = #PB_Event_CloseWindow  ; If the user has pressed on the window close button
galah
New User
New User
Posts: 2
Joined: Mon Sep 13, 2010 1:21 pm
Location: Adelaide, Australia

Re: How to draw lines of a specific width and format

Post by galah »

Michael Vogel wrote:Would like to have a simple native LineXY routine for PB which allows drawing thicker lines (width=2 at least)...

My quick and dirty code is not only slow but would fail when drawing very thick short lines (see below):
I am a beginner to Purebasic, just converting to Purebasic from 3 other dialects of Basic, and was surprised that LineXY has no thickness setting (two of the other Basic dialects did have) but will be doing 95% of my PC programming with Purebasic , so have had to roll my own with a workaround. My code could probably be improved and optimized a lot, but as mentioned I am still a beginner with Purebasic. See code below. I would also prefer a native LineXY with thickness setting.

My code below makes use of the Bresenham line drawing Algorithm, which is very quick, as it ONLY uses integer ADDITION and SUBTRACTION, no multiplication, division or sines or cosines. There is a lot on the web about the Bresenham Line Drawing Algorithm. There are other later algorithms almost as fast that also do antialiasing. I do not understand the line drawing algorithm, but it works. For explanation you will need to look up an explanation on the web.

Essentially the Bresenham line drawing algorithm "thickLineXY(start_x,start_y,end_x,end_y,thickness,color)" is called to calculate the pixels on the line, and instead of the procedure calling plot() to turn each pixel on, it calls circle() to draw a circle at the pixel location using the thickness of the required line as the diameter. This results in a lot of circles being drawn, so I feel that a native LineXY with thickness setting would speed this up.

Hope this helps.







Code: Select all


;thickLineXY(startx,starty,endx,endy,thickness,color)

;------------------------------------
;Bresenhams Line Drawing Algorithm

;The Bresenham line drawing algorithm uses  
; integer addition And subtraction ONLY
; NO multiplication, NO division, NO sines or cosines
;------------------------------------

Procedure thickLineXY (start_x,start_y,end_x,end_y,thickness,whatcolor)
 
err_x = 0 : err_y = 0 : inc_x = 0 : inc_y = 0

     delta_x = end_x - start_x;
     delta_y = end_y - start_y;
 

If(delta_x > 0) :inc_x = 1:ElseIf (delta_x = 0) :inc_x = 0: Else :inc_x = -1:EndIf  
If(delta_y > 0) :inc_y = 1:ElseIf (delta_y = 0) :inc_y = 0: Else :inc_y = -1:EndIf          
      
     delta_x = Abs(delta_x);
     delta_y = Abs(delta_y);
     
     If(delta_x > delta_y) 
       distance = delta_x;
     Else 
       distance = delta_y;
     EndIf  
     
     For  xyz = 0 To  distance+1 Step 1
       
       ; modified to place a circle at the pixel location to get a thick line
          
          ;Plot(start_x,start_y,whatcolor)     
          Circle(start_x,start_y,thickness,whatcolor)     

          err_x = err_x + delta_x
          err_y = err_y + delta_y
          
          If (err_x > distance)
              err_x = err_x - distance
              start_x = start_x + inc_x
            EndIf

           If (err_y > distance)
             err_y = err_y - distance
             start_y = start_y +inc_y
           EndIf  

Next
  
EndProcedure
;-----------------------------------------------------
; End of Bresenhams algorithm
;-----------------------------------------------------


;----------------------------------------------------------
; modified 2DDrawing Example to draw a multihand clock face
;----------------------------------------------------------

If OpenWindow(0, 0, 0, 800, 800, "2DDrawing Thick Lines", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_2DDrawing_Gradient)
    If CreateImage(0, 800, 800) And StartDrawing(ImageOutput(0))
     
      
          thickLineXY( 40,  40, 760,  40,  10, RGB(255,1,1)) ;outer red box top
          thickLineXY(760,  40, 760, 760,  10, RGB(255,1,1));outer red box right side
          thickLineXY(760, 760,  40, 760,  10, RGB(255,1,1));outer red box bottom
          thickLineXY( 40, 760,  40,  40,  10, RGB(255,1,1));outer red box left side
          
          thickLineXY(100, 680,  700,  680,  50, RGB(255,100,1)) ;orange base
              
          thickLineXY(400, 500, 100,100,   1, RGB(255,1,1))   ; red hand
          thickLineXY(100, 100, 100,100,   10, RGB(255,1,1))   ; red hand blob
          thickLineXY(400, 500, 300, 100, 5, RGB(1,255,1))   ; green hand
          thickLineXY(400, 500,  600, 100,10, RGB(1,1,255))   ; blue hand
          thickLineXY(400, 500,  600, 600,15,RGB(1,255,255))   ; teal hand
          thickLineXY(400, 500,  100, 600,20,RGB(255,255,1))   ; yellow hand
          thickLineXY(400, 500,  100, 600,5,RGB(5,25,25))   ; yellow hand
          
          thickLineXY(100, 100, 400, 500,  1, RGB(255,1,1))   ; red hand
          thickLineXY(300, 100, 400, 500,  5, RGB(1,255,1))   ; green hand
          thickLineXY(600, 100, 400, 500, 10, RGB(1,1,255))   ; blue hand
          
          thickLineXY(400, 500, 400, 500, 36, RGB(255,255,255)) ; white dot
          thickLineXY(400, 500, 400, 500, 22, RGB(255,0,255))   ; pink dot
      
      StopDrawing() 
      ImageGadget(0, 0, 0, 200, 200, ImageID(0))
    EndIf
    
    Repeat
      Event = WaitWindowEvent()
    Until Event = #PB_Event_CloseWindow
  EndIf

End
;-------------------------------------------------------

;============================================================================

rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Re: How to draw lines of a specific width and format

Post by rsts »

Nice (and colorful too).

Thanks for sharing.

cheers
ebs
Enthusiast
Enthusiast
Posts: 557
Joined: Fri Apr 25, 2003 11:08 pm

Re: How to draw lines of a specific width and format

Post by ebs »

galah,

Thanks for sharing your simple Bresenham code. I believe a change is needed in this line:

Code: Select all

Circle(start_x,start_y,thickness,whatcolor) 
Since the third parameter in the Circle() command is the radius of the circle, not its diameter,
you are drawing the lines twice as thick as desired. Changing the line to:

Code: Select all

Circle(start_x,start_y,thickness/2,whatcolor)
will make the lines the correct width.

Regards,
Eric
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: How to draw lines of a specific width and format

Post by Michael Vogel »

All Bresenhams routines are quite fast in calculation but slow in drawing the pixels.

As I am not very good in assembl[er|y] programming, I created the following:

Code: Select all

Global DxMem,DxMul,DxPix;	
Global *DX.DxStructure

Declare MergePixel32(x,y,r,g,b,w)
Declare MergePixel24(x,y,r,g,b,w)
Declare MergePixel16(x,y,r,g,b,w)
Declare MergePixel(x,y,r,g,b,w)

Prototype MergePixelType(x,y,r,g,b,w)
Global MergePixelRoutine.MergePixelType

Macro PlotXY(x,y,color)

	CompilerIf #PlotSpeedup
		Box(x,y,1,1,color)
	CompilerElse
		Plot(x,y,color)
	CompilerEndIf

EndMacro
Macro ColMix(c,r,g,b,w,iw)
	;
	; 24/32 Bit-Pixel: bbbbbbbb|gggggggg|rrrrrrrr
	( (r*w+ColRed(c)*(iw))>>8 + (g*w+ColGreen(c)*(iw))&$ff00 + (b*w+ColBlue(c)*(iw))<<8&$ff0000 )
EndMacro

Procedure MergePixel32(x,y,r,g,b,w)

	CompilerIf #AllowAssembler

		MOV eax,x
		MOV ecx,y
		CMP eax,ScreenX		; x >= ScreenX ?
		JGE exitMP32
		CMP ecx,ScreenY		; y >= ScreenY ?
		JGE exitMP32
		Or eax,ecx			; x OR y < 0?
		JS exitMP32

		;*DX=DxMem+x<<2+y*DxMul
		IMUL ecx,DxMul		; y*DxMul
		MOV eax,x				; x
		SHL eax,2				; x*4
		ADD eax,ecx			; x*4+y*DxMul
		ADD eax,DxMem		; DxMem+x<<2+y*DxMul
		MOV *DX,eax			; -> *DX

		;*DX\l=ColMix(*DX\l,b,g,r,w,255-w)
		MOV ebx,*DX\l		; color
		MOV edx,255	; 255
		MOV ecx,w		; w (ecx)
		SUB edx,ecx	; 255-w (edx)

		MOV eax,ebx	; color
		And eax,$ff	; cr=Red(color)
		MUL dl			; cr*(255-w)

		MOV edi,eax	; parken
		MOV eax,b		; r
		MUL cl			; r*w
		ADD eax,edi	; r*w+cr*(255-w)
		SAR eax,8		; [r*w+cr*(255-w)] >>8

		MOV esi,eax	; rot


		MOV eax,ebx	; color
		SAR eax,8		; >>8
		And eax,$ff	; cg=Green(color)
		MUL dl			; cr*(255-w)

		MOV edi,eax	; parken
		MOV eax,g		; g
		MUL cl			; g*w
		ADD eax,edi	; g*w+cg*(255-w)
		And eax,$ff00	; [g*w+cg*(255-w)] &$ff00

		ADD esi,eax;	grün|rot


		MOV eax,ebx	; color
		SAR eax,16	; >>16
		And eax,$ff	; cb=Blue(color)
		MUL dl			; cb*(255-w)

		MOV edi,eax	; parken
		MOV eax,r		; b
		MUL cl			; b*w
		ADD eax,edi	; b*w+cb*(255-w)
		And eax,$ff00	; [b*w+cb*(255-w)] &$ff00
		SAL eax,8

		ADD esi,eax;	blau|grün|rot

		MOV *DX\l,esi

		!exitMP32:

	CompilerElse

		; If x>=0 And x<ScreenX And y>=0 And y<ScreenY
		*DX=DxMem+x<<2+y*DxMul
		*DX\l=ColMix(*DX\l,b,g,r,w,255-w)
		; EndIf

	CompilerEndIf

EndProcedure
Procedure MergePixel24(x,y,r,g,b,w)
	:
ProcedureReturn 
Procedure MergePixel16(x,y,r,g,b,w)
	:
ProcedureReturn 

Procedure MergePixel(x,y,r,g,b,w)

	CompilerIf #AllowAssembler

		CompilerIf #PlotSpeedup=0
			MOV eax,x
			MOV ecx,y
			CMP eax,ScreenX		; x >= ScreenX ?
			JGE exitMP
			CMP ecx,ScreenY		; y >= ScreenY ?
			JGE exitMP
			Or eax,ecx			; x OR y < 0?
			JS exitMP
		CompilerEndIf
		PlotXY(x,y,ColMix(Point(x,y),r,g,b,w,255-w))
		!exitMP:

	CompilerElse

		; If x>=0 And x<ScreenX And y>=0 And y<ScreenY
		PlotXY(x,y,ColMix(Point(x,y),r,g,b,w,255-w))
		; EndIf

	CompilerEndIf

EndProcedure

If DirectMemory

	DxMem=DrawingBuffer()
	DxMul=DrawingBufferPitch()
	DxPix=DrawingBufferPixelFormat(); liefert bei einigen Notebooks (Amilo) leider 0...

	If DxPix=0
		i=GetDC_(0)
		Select GetDeviceCaps_(i,#BITSPIXEL)
		Case 32
			DxPix=#PB_PixelFormat_32Bits_RGB
		Case 24
			DxPix=#PB_PixelFormat_24Bits_RGB
		Case 15,16
			DxPix=#PB_PixelFormat_16Bits
		EndSelect
		ReleaseDC_(0,i)
	EndIf

	If DxPix>=#PB_PixelFormat_32Bits_RGB
		MergePixelRoutine=@MergePixel32()
	ElseIf DxPix>=#PB_PixelFormat_24Bits_RGB
		MergePixelRoutine=@MergePixel24()
	ElseIf DxPix>=#PB_PixelFormat_15Bits
		MergePixelRoutine=@MergePixel16()
	Else
		OptAntialiasing=0
	EndIf

Else
	MergePixelRoutine=@MergePixel()
EndIf
Post Reply