15
\$\begingroup\$

Draw a simple ASCII art image containing a straight line. It's similar to this and this but with different specifications.

Input

You can modify this input format to suit your code.

  • integer width
  • integer height
  • integer x0
  • integer y0
  • integer x1
  • integer y1

Output

A filled ASCII art image of the specified width and height containing a line from pixel (x0, y0) to pixel (x1, y1).

Any standard form of text output is acceptable, but don't use built in line drawing functions.

Details

The line must be drawn using a single printable character (such as #), while the background is filled with a different character (such as .). You must print the necessary trailing characters so that the image size is correct.

Pixel coordinates can be 0-indexed or 1-indexed and can start in any corner of the image. The line should be drawn by imagining a 0-width sub-pixel line connecting the centers of the start and end pixels. Every pixel that the line enters should be filled in.

Winning

Usual code-golf rules. Shortest code wins.

Examples

IN: width, height, x0, y0, x1, y1

IN: 7, 6, 0, 0, 6, 5
OUT:
.....##
....##.
...##..
..##...
.##....
##.....

IN: 3, 3, 1, 1, 1, 1
OUT:
...
.#.
...

IN: 3, 3, 0, 2, 2, 0
OUT:
#..
.#.
..#

IN: 6, 3, 0, 0, 5, 2
OUT:
....##
.####.
##....

IN: 4, 4, -1, -1, 0, 3
OUT:
#...
#...
#...
....
\$\endgroup\$
  • 4
    \$\begingroup\$ I'd say "Welcome to PPCG" but you've been registered here almost as long as me. :-) Nice first challenge! \$\endgroup\$ – AdmBorkBork Apr 11 '17 at 15:50
  • \$\begingroup\$ Can we output actual dots instead of spaces? or any other character but spaces? (assuming with still including the trailing <character>s) \$\endgroup\$ – Rɪᴋᴇʀ Apr 11 '17 at 16:06
  • \$\begingroup\$ Sure! I'll make the edits \$\endgroup\$ – Curtis Bechtel Apr 11 '17 at 16:08
  • 1
    \$\begingroup\$ @AlbertRenshaw Actually, when I look at Curve on Wikipedia it states: "In mathematics, a curve (also called a curved line in older texts) is, generally speaking, an object similar to a line but that need not be straight." ;) \$\endgroup\$ – Kevin Cruijssen Apr 12 '17 at 6:48
  • 1
    \$\begingroup\$ @KevinCruijssen Implying that a line need be straight, no? ;) \$\endgroup\$ – Albert Renshaw Apr 12 '17 at 7:00
2
\$\begingroup\$

Mathematica, 166 137 bytes

l:={i,j};s=Sign;f[p_,q_,h_,w_]:=Grid@Table[(1-Max[s[p-l]s[q-l],0])Boole[Abs@Mean[s@Det@{p-l+#,p-q}&/@Tuples[.5{1,-1},2]]<.6],{i,h},{j,w}]

More readable version:

l := {i, j}; s = Sign; 
f[p_, q_, h_, w_] := 
 Grid@Table[(1 - Max[s[p - l] s[q - l], 0]) Boole[
     Abs@Mean[
        s@Det@{p - l + #, p - q} & /@ 
         Tuples[.5 {1, -1}, 2]] < .6], {i, h}, {j, w}]

This defines a function called f. I interpreted the input and output specifications fairly liberally. The function f takes input in the format f[{x0, y0}, {x1, y1}, height, width], and the grid is 1-indexed, starting in the top left. Outputs look like

A sample output

with the line displayed as 1s and the background as 0s (shown here for f[{2, 6}, {4, 2}, 5, 7]). The task of turning a Mathematica matrix of 1s and 0s into a string of #s and .s has been golfed in many other challenges before, so I could just use a standard method, but I don't think that adds anything interesting.

Explanation:

The general idea is that if the line passes through some pixel, then at least one of the four corners of the pixel is above the line, and at least one is below. We check if a corner is above or below the line by examining the angle between the vectors ({x0,y0} to corner) and ({x0,y0} to {x1,y1}): if this angle is positive, the corner is above, and if the angle is negative, the corner is below.

If we have two vectors {a1,b1} and {a2,b2}, we can check if the angle between them is positive or negative by finding the sign of the determinant of the matrix {{a1,b1},{a2,b2}}. (My old method of doing this used arithmetic of complex numbers, which was way too…well, complex.)

The way this works in the code is as follows:

  • {p-l+#,p-q}&/@Tuples[.5{1,-1},2] gets the four vectors from {x0,y0} and the four corners of the pixel (with l:={i,j}, the coordinates of the pixel, defined earlier), and also the vector between {x0,y0} and {x1,y1}.
  • s@Det@... finds the signs of the angles between the line and the four corners (using s=Sign). These will equal -1, 0 or 1.
  • Abs@Mean[...]<.6 checks that some of the angles are positive and some negative. The 4-tuples of signs that have this property all have means between -0.5 and 0.5 (inclusive), so we compare to 0.6 to save a byte by using < instead of <=.

There's still a problem: this code assumes that the line extends forever in both directions. We therefore need to crop the line by multiplying by 1-Max[s[p-l]s[q-l],0] (found by trial and error), which is 1 inside the rectangle defined by the endpoints of the line, and 0 outside it.

Cropping a line by a rectangle

The rest of the code makes a grid of these pixels.

(As a bonus, here's an earlier attempt with a completely different method, for 181 bytes:)

Quiet@Grid@Table[(1-Max[Sign[{i,j}-#3]Sign[{i,j}-#4],0])Boole[#3==#4=={i,j}||2Abs@Tr[Cross@@Thread@{{i,j},#3,#4}]/Norm[d=#3-#4]<2^.5Cos@Abs[Pi/4-Mod[ArcTan@@d,Pi/2]]],{i,#},{j,#2}]&
\$\endgroup\$
  • 1
    \$\begingroup\$ Now that that's finished, time for lunch! (At 6:30 p.m…) \$\endgroup\$ – Not a tree Apr 13 '17 at 8:25
1
\$\begingroup\$

CJam, 122 bytes

{),V>{[I\]E.*A.+}/}:F;l~]2/~@:S~'.*f*\@:A.-_:g:E;:z:D[0=:B{D~I2*)*+:U(2/B/FU2/B/:V;}fID~\:I;F]{_Wf>\S.<+:*},{~_3$=@0tt}/N*

Try it online

This basically combines two answers I previously wrote for other challenges (mainly the calculations from the 2nd one - function l).
(0, 0) is naturally the top left corner, not bottom left like the examples in the statement.

Overview:

{),V>{[I\]E.*A.+}/}:F; defines function F which helps generate all the pixels (coordinate pairs) for a given x coordinate
l~]2/~@:S~'.*f*\@:A.-_:g:E;:z:D reads and processes the input, and creates a matrix of dots
0=:B{D~I2*)*+:U(2/B/FU2/B/:V;}fI iterates over all x coordinates except the last one, and generates all the corresponding pixels
D~\:I;F does the same for the last x coordinate
{_Wf>\S.<+:*}, keeps only the pixels that should appear inside the image
{~_3$=@0tt}/ puts a 0 in the matrix for each pixel
N* joins the matrix with newline characters for display

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.