ID:147797
 
I need to draw a line on the screen. A single, plain, one-pixel line. Eventually I'll use images (or even HUD objects) for this, but I'll settle for anything that works right now. =) (BYOND really needs a built-in proc for this. Once it's fixed, this snippet will be good material for a library.)

Anyway, I've searched the hub and the forum. AbyssDragon's BasicMath library has a nice line-finding algorithm, but it only works using tiles. I found an old post of Lummox's ([link]) that had an untested snippet in it; it works, with a bit of modification, except that there are odd pixels scattered here and there. Here's the code I'm using, complete with verb to test it out:

var/lasercache[0] mob/verb/makeline(dx as num, dy as num) var/L=CreateLine(dx,dy) for (var/X in L) var/turf/T=locate(text2num(X)+1,text2num(copytext(X,findtext (X,",")+1))+1,1) T.overlays+=L[X] proc/CreateLine(dx,dy) var/cachekey="[dx],[dy]" var/list/L=list() var/icon/lasershifted=new /icon('laser.dmi') var/unitstomove=max(dx,dy)*32 var/xinc1=0 var/yinc1=0 var/xinc2=0 var/yinc2=0 var/maindir=EAST var/sidedir=(dy>=0)?NORTH :SOUTH if(dx>dy) xinc1=1 yinc2=(dy>=0)?1:-1 else yinc1=(dy>=0)?1:-1 xinc2=1 maindir=sidedir sidedir=EAST var/swap=dx dx=dy dy=swap var/mainsub=(dx>=0)?16:15 var/sidesub=(dy>=0)?16:15 dx=abs(dx) dy=abs(dy) var/centerx=0 // coords of current tile var/centery=0 var/aside=dx var/icon/current for(var/n=0,n<=unitstomove,++n) current=L["[centerx],[centery]"] if(!current) current=new /icon(lasershifted) L["[centerx],[centery]"]=current else current.Blend(lasershifted,ICON_OVERLAY) lasershifted.Shift(maindir,1,1) // wrap aside+=dy mainsub=(mainsub+1)&31 if(!mainsub) centerx+=xinc1 centery+=yinc1 if(aside>dx+dx) aside%=dx lasershifted.Shift(sidedir,1,1) // wrap sidesub=(sidesub+1)&31 if(!sidesub) centerx+=xinc2 centery+=yinc2 lasercache[cachekey]=L return L

Any suggestions? As usual, I don't understand half of Lummox's code. =P
So you want a regular ol' black line going straight down the screen? You could use regular hub to do that.

Siientx
In response to Siientx
I assume you mean HUD, not hub. =) And no, I couldn't, because the line needs to be able to be drawn from an arbitrary turf to any other arbitrary turf. It's not just a straight down or straight across line.

Actually, for what I'm thinking of, I *could* use two lines (one diagonal, one cardinal) to form it... but I'd much rather have a perfectly straight one, otherwise it'll be very confusing.
In response to Crispy
Erm. I don't follow. Draw a picture of what you mean in paint?

Siientx
In response to Siientx
He means draw line from point A to point B, simple as that.
In response to Jotdaniel
HUD.

Siientx
In response to Siientx
Using a hud would require him to make at least 90 different icons, one for each angle (I'm asuming a lot of judicious rotating). More likely 180 icons. If he wants it to be pixel_a to pixel_b, then multiply the number of icons by about 16-32 since you now need to account for every pixel on a given angle. In short, HUD is not the way to go without his basic needs being answered first (and I wish I could help you with that Crispy).
In response to sapphiremagus
It has to be from the centre of any given turf to the centre of any other given turf (sorry, I guess I should have clarified that to start with). I could make hundreds of different icons, but it would take absolutely forever and be nearly as hard to get working properly... and imagine if I wanted to change anything. =P
In response to Crispy
Pixel offsets and a sinlge-pixel icon are the way to go here... The pixel in the icon needs to go in the bottom left corner of the icon (pixel location of 1,1)...

Once you've got your line's equation, simply run through each point, and calculate that point's turf location, and necessary offsets, then stick the point there...

How to find the location and offsets? Just some simple math... Whatever it's value is in terms of x,y (not location, I mean simple mathematic x,y values for each point given by the equation, in terms of single point/pixel values), just divided those values by 32 to find the right turf location, then use the remainders for the offsets...

Say you have a point whose x,y values from the line's equation end up being (345,35)... That ends up being x = 10 r25 and y = 1 r3...

So, the pixel for that point goes in turf (11,2) and has a pixel_x = 25 offset, and a pixel_y = 3 offset... It goes in (11,2) while our division gave us (10,1) because the division gives us the number of turfs "below" its location... Just to think of it simply, look at the y value of 35... Each turf has 32x32 pixels, so a value of 35 puts the point 3 pixels into the second tile...

Anyways, that's how I'd place all of the points... It would best work as turf overlays, though... Or even perhaps objects (which could get very numerous very quickly)... It wouldn't work well with screen objects...since you'd need to calculate all of those relative to the player's mob's location, rather than actual map space... But even that could be done...

Now, the line needs to be bounded by two end points... How shall we do that?

Also rather simple... Only plot the points whose x,y point values are bounded by the point values of those endpoints...

Let's take our previous example of the point (345,35) again...

Perhaps the line has an endpoint at (5,1) (turf location, not point values, this time... and also, I doubt this exact location would lie on the same line as our example value, but for the sake of illustration, this works)

Anyways, that endopoint needs to be in the center of the turf, correct? Let's call that (16,16) (pixel values within this tile)... For the center of the turf at (5,1), the x,y point values are equal to ((x-1)*32+16,(y-1)*32+16) or (4*32+16,0+16) or (144,16)...

So, since our example point of (345,35) falls outside of this bound, we don't plot it...

Hmmm, now that I re-read all of that above, it isn't very concise, precise, or well-explained... But, I hope the point gets across...
In response to Crispy
Crispy wrote:
It has to be from the centre of any given turf to the centre of any other given turf (sorry, I guess I should have clarified that to start with). I could make hundreds of different icons, but it would take absolutely forever and be nearly as hard to get working properly... and imagine if I wanted to change anything. =P

What you probably need is some kind of smart cache system that can store icons as you make them. Each icon would have to be classified by:

- x,y gradient (y should always be >=0 for clarity, x>0 if y==0, and common factors taken out)
- x,y offset from bottommost (or leftmost, if y==0) turf
- special icon: combines the first and last parts of a line

The special icon would for example be for cases where you wanted to draw a line from (1,1) to (3,3), and it would go in (2,2). The line is has a (1,1) gradient, which would normally mean two icons: Beginning and ending. However, at turf (2,2) you'd want a line that went southwest to northeast across the whole icon.

A quick algorithm for culling out common factors:
proc/GCF(a, b)
var/r
a = round(abs(a), 1); b = round(abs(b), 1)
if(a < b)
r = a; a = b; b = r
if(b<=1) return 1
do
r = a % b; a = b; b = r
while(r)
return a

Lummox JR
I'd suggest just using an icon of a line, and use the turn proc to generate the needed icons. drawing the line pixel by pixel would be more precise, but it'd take a lot longer and use up a lot of objects. I just use objects, they could easily be screen objects (or something else) instead.
The demo (the link below) isn't perfect, but it could be made better easily, and, of course, includes only the dmb and rsc.

http://home.earthlink.net/~rob051/line.zip
In response to Lummox JR
I'll probably try that, thanks.