Page 1 of 1

Drawing lines of variable thickness

Posted: Wed Oct 29, 2003 4:47 pm
by srod
Hello all,

Have recently returned to programming after too many years in the wilderness and am new to PureBasic. What an excellent language.
Have made rapid gains in the space of a few weeks thanks to all the tips and snippets within these forums.

To the reason I am posting.

I am working upon what is becoming quite a nice little painting program which I am using as a learning platform.
Is it possible to draw / plot a line of variable thickness (rather than 1 pixel wide) ? I suspect that if it is possible then it might involve the Win API (which I am slowly coming to understand through these forums and PB itself.) Unfortunately, I don't know enough about hdc's, device contexts, brushes etc. to set about tackling this.
I am prepared to code such a procedure directly in PB but I can't help thinking that, with a little persuasion, Windows should be able to perform this task!

Thanks and keep up the excellent work.

Posted: Wed Oct 29, 2003 6:35 pm
by LarsG
Don't know about WinAPI, but you can use a series of ovals or rects do draw thicker line.. :roll:

-Lars

Re: Drawing lines of variable thickness

Posted: Wed Oct 29, 2003 6:41 pm
by einander
Hi srod:
Maybe this helps.
Is it possible to draw / plot a line of variable thickness

Code: Select all

Procedure Lin(x,y,x1,y1,Gros,Color)
   hDC=GETDC_(WindowID())
   pen=CreatePen_(#Ps_Solid,GROS,color)  
   hPenOld=SelectObject_(hDC,pen)
   MoveToEx_(hDC,x,y,0):LineTo_(hDC,x1,y1) 
   DeleteObject_(pen)
   DeleteObject_(hPenOld)
EndProcedure

;_____________________________________________
_X=GetSystemMetrics_(#SM_CXSCREEN)-8 : _Y=GetSystemMetrics_(#SM_CYSCREEN)-68
OpenWindow(0,0,0,_X,_Y,#WS_OVERLAPPEDWINDOW | #WS_MAXIMIZE,"")

lin(50,200,300,400,15,#red)
MessageRequester("","DONE",0)

Posted: Wed Oct 29, 2003 6:52 pm
by srod
Excellent. I was (with the aid of Petzold's book) almost there and experimenting with the very same function calls. You've just saved me a whole heap of trial and error!

Problem now is that I am drawing on an image. Will the same code work if I replace the window handle with that of an image ?

Thanks.

Posted: Wed Oct 29, 2003 7:00 pm
by srod
Got it!

The code doesn't work with an image, but it does with an image gadget!

Thanks again LarsG and einander. Thiis is better than any text book!

Posted: Wed Oct 29, 2003 7:08 pm
by LarsG
Don't know why you're thanking me.. it's Einander who had the cool function.. 8)

-Lars

Re: Drawing lines of variable thickness

Posted: Mon Sep 10, 2012 6:15 pm
by Puffolino
Has anyone found a solution which can be used to draw thick lines on images as well as to the printer? Maybe even OS independent?

All non-windows specific procedures for drawing lines in the forum which work also for printing have some disadvantages (very slow, not working for all angles or using the faulty circle command etc.)

Code: Select all

Procedure thickLineXY(start_x,start_y,end_x,end_y,thickness,whatcolor)

	thickness>>1

	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
Procedure ClipLine(x1.w, y1.w, x2.w, y2.w, color,thickness.w)

	; Draw line here with stupid PB Line command syntax
	w=x2 - x1
	h=y2 - y1
	If wh
		w=w+1
	Else
		h=h+1
		If w=h
			w=w+1
		EndIf
	EndIf

	extra=0
	For i.w=1 To thickness
		If w>h
			y1=y1+extra
		Else
			x1=x1+extra
		EndIf
		Line(x1, y1, w, h,color)
		If extra<0
			extra=extra-1
		Else
			extra=extra+1
		EndIf
		extra=-extra
	Next
	
EndProcedure

ProcedureDLL.l ColorBlending(Color1.l, Color2.l, Blend.f)
	
	Protected Red, Green, Blue, Red2, Green2, Blue2

	Red=Color1 & $FF
	Green=Color1 >> 8 & $FF
	Blue=Color1 >> 16
	Red2=Color2 & $FF
	Green2=Color2 >> 8 & $FF
	Blue2=Color2 >> 16

	Red=Red * Blend + Red2 *(1-Blend)
	Green=Green * Blend + Green2 *(1-Blend)
	Blue=Blue * Blend + Blue2 *(1-Blend)

	ProcedureReturn(Red | Green <<8 | Blue << 16)
	
EndProcedure
Procedure NormalL(X,Y, x3, y3, Color, Thickness=1)
	
	Width=x3-X
	Hight=y3-Y
	Protected SignX, SignY, n, nn, Thick.f, x2.f, y2.f, Color_Found.l, Application.f, Hypo.f

	If Width >= 0
		SignX=1
	Else
		SignX=-1
		Width=- Width
	EndIf
	If Hight >= 0
		SignY=1
	Else
		SignY=-1
		Hight=- Hight
	EndIf


	Thick.f=Thickness / 2

	Hypo.f=Sqr(Width * Width + Hight * Hight)
	CosPhi.f=Width / Hypo
	SinPhi.f=-Sin(ACos(CosPhi))


	For n=-Thickness To Width + Thickness
		For nn=-Thickness To Hight + Thickness


			x2=n * CosPhi - nn * SinPhi
			y2=Abs(n * SinPhi + nn * CosPhi)

			If y2 <= Thick + 0.5
				Application= 0.5 + Thick - y2
				If Application>1
					Application=1
				EndIf
				If x2>-1 And x2<Hypo + 1
					If x2<0
						Application *(1 + x2)
					ElseIf x2>Hypo
						Application *(1 - x2 + Hypo)
					EndIf
				Else
					Application=0
				EndIf
				If Application>0
					If Application<1
						Color_Found=Point(X + n * SignX, Y + nn * SignY)
						Plot(X + n * SignX, Y + nn * SignY, ColorBlending(Color, Color_Found, Application))
					Else
						Plot(X + n * SignX, Y + nn * SignY, Color)
					EndIf
				EndIf
			EndIf
		Next
	Next

EndProcedure

Procedure LineAA(X, Y, Width, Hight, Color, Thickness=1)
	
	Protected SensX, SensY, n, nn, Epaisseur.f, x2.f, y2.f, Couleur_Fond.l, Application.f, Distance.f
	; On mets la droite toujours dans le même sens pour l'analyse
	; La sauvegarde du sens permettra de dessiner la droite ensuite dans le bon sens
	If Width >= 0
		SensX=1
	Else
		SensX=-1
		Width=- Width
	EndIf
	If Hight >= 0
		SensY=1
	Else
		SensY=-1
		Hight=- Hight
	EndIf


	; Demi épaisseur de la ligne
	Epaisseur.f=Thickness / 2

	; calcul pour le changement de repère qui permet de connaitre l'épaisseur du trait et de gérer l'AA
	Distance.f=Sqr(Width * Width + Hight * Hight)
	CosAngle.f=Width / Distance
	SinAngle.f=-Sin(ACos(CosAngle))

	; Dessin de la ligne
	For n=-Thickness To Width + Thickness
		For nn=-Thickness To Hight + Thickness

			; changement de base
			; les y représentent l'épaisseur de la ligne
			x2=n * CosAngle - nn * SinAngle
			y2=Abs(n * SinAngle + nn * CosAngle)

			If y2 <= Epaisseur + 0.5
				Application= 0.5 + Epaisseur - y2
				If Application>1
					Application=1
				EndIf
				If x2>-1 And x2<Distance + 1
					If x2<0
						Application *(1 + x2)
					ElseIf x2>Distance
						Application *(1 - x2 + Distance)
					EndIf
				Else
					Application=0
				EndIf
				If Application>0
					If Application<1
						Couleur_Fond=Point(X + n * SensX, Y + nn * SensY)
						Plot(X + n * SensX, Y + nn * SensY, ColorBlending(Color, Couleur_Fond, Application))
					Else
						Plot(X + n * SensX, Y + nn * SensY, Color)
					EndIf
				EndIf
			EndIf

		Next
	Next

EndProcedure

OpenWindow(0, 0, 0, 500, 500, "Test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget)

CreateImage(0, 500, 500)
StartDrawing(ImageOutput(0))

#repeat=99
#angle=35

For i=0 To 3

	t=ElapsedMilliseconds()

	For m=0 To #repeat
		p=0

		For n=0 To #angle
			If n&3=0 : p+1 : EndIf
			Select i
			Case 0
				ClipLine(120,120,120+Sin(n*#PI/#angle*2)*100,120+Cos(n*#PI/#angle*2)*100,#Red,p); 1s
			Case 1
				thickLineXY(350,120,350+Sin(n*#PI/#angle*2)*100,120+Cos(n*#PI/#angle*2)*100,p,#Red); 15s
			Case 2
				NormalL(120,350,120+Sin(n*#PI/#angle*2)*100,350+Cos(n*#PI/#angle*2)*100,#Red,p); 15s
			Case 3
				LineAA(350,350,Sin(n*#PI/#angle*2)*100,Cos(n*#PI/#angle*2)*100,#Red,p); 15s
			EndSelect
		Next n
	Next m

	t-ElapsedMilliseconds()

	Select i
	Case 0
		DrawText(200,30,Str(-t))
	Case 1
		DrawText(430,30,Str(-t))
	Case 2
		DrawText(200,240,Str(-t))
	Case 3
		DrawText(430,240,Str(-t))
	EndSelect

Next i

StopDrawing()
ImageGadget(0, 0, 0, 300, 300, ImageID(0))

Repeat
	Event=WaitWindowEvent()
Until Event=#PB_Event_CloseWindow

Re: Drawing lines of variable thickness

Posted: Mon Sep 10, 2012 7:03 pm
by srod
Wow, one of my first postings! :)

Re: Drawing lines of variable thickness

Posted: Mon Sep 10, 2012 7:18 pm
by kernadec
Hi
PureBasic now has a range of functions and 3D animations very magical
but I think it's time to power with PB native 2DDrawing and CanvasGadget
draw a BOX with an angle
draw an Ellipse with an angle
draw an ARC with an angle would welcome
draw a LINE mode with Thickness and Styles
the thicknesses and styles also applied to ARC BOX ELLIPSE

Goodday

Re: Drawing lines of variable thickness

Posted: Mon Sep 10, 2012 8:28 pm
by Puffolino
I thought to use a workaround meanwhile, by drawing multiple standard lines (instead of the slow and ugly circles), but my quick try has holes like a cheese :P

Code: Select all

Procedure MyLineXY(x1,y1,x2,y2,Color,Thickness=1)
	
	#FineShift=10
	#FineMore=1
	#FineOne=1<<(#FineShift-#FineMore-1)
	#FineTwo=2<<(#FineShift-#FineMore-1)

	Protected n,h,w,sx,sy
	Protected hypo.f,dx.f,dy.f
	
	w=x2-x1
	h=y2-y1

	If w>=0
		sx=1
	Else
		sx=-1
		w=-w
	EndIf
	If h>= 0
		sy=1
	Else
		sy=-1
		h=-h
	EndIf

	hypo.f=Sqr(w*w+h*h)
	dy=w/hypo
	dx=-Sin(ACos(dy))*sx
	dy=dy*sy
		
	x1=x1<<#FineShift-(dx*Thickness)/#FineOne
	y1=y1<<#FineShift-(dy*Thickness)/#FineOne
	x2=x2<<#FineShift-(dx*Thickness)/#FineOne
	y2=y2<<#FineShift-(dy*Thickness)/#FineOne
	
	x0=dx*#FineTwo
	y0=dy*#FineTwo
	
	For n=0 To Thickness<<#FineMore
		LineXY(x1>>#FineShift,y1>>#FineShift,x2>>#FineShift,y2>>#FineShift,#Blue)
		x1+x0
		y1+y0
		x2+x0
		y2+y0
	Next n

EndProcedure