question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

'LineCanvas` has two minds about what `Length == 0` means

See original GitHub issue

Related to: #2433 which was fixed by #2436

@tznind, I’ve discovered another issue related to this fix, and the idea that Line.Length == 0 is really a one point long element…

The inArea param to GenerateImage(Rect inArea) is intended to constrain what points are returned to inArea.

Because EndsAt(int x, int y) does this…

private bool EndsAt (int x, int y)
{
	if (Orientation == Orientation.Horizontal) {
		return Start.X + Length == x && Start.Y == y;
	}

	return Start.X == x && Start.Y + Length == y;
}

Specifically Start.X + Length

Instead of Start.X + Length - 1

… it doesn’t constrain the line properly.

I think the crux is

  • A length of 0 means a length of 1
  • A length of 1 means a length of 2

Both these new tests pass:

[InlineData (0, 0, Orientation.Horizontal, "─")]
[InlineData (1, 0, Orientation.Horizontal, "─")]
[InlineData (0, 1, Orientation.Horizontal, "─")]
[InlineData (0, 0, Orientation.Vertical, "│")]
[InlineData (1, 0, Orientation.Vertical, "│")]
[InlineData (0, 1, Orientation.Vertical, "│")]
[Theory, AutoInitShutdown]
public void Length_0_Is_1_Long (int x, int y, Orientation orientation, string expected)
{
	Application.Begin (Application.Top);
	((FakeDriver)Application.Driver).SetBufferSize (20, 20);

	var inArea = new Rect (0, 0, 1, 1);

	var canvas = new LineCanvas ();
	// Add a line at 0, 0 that's has length of 1
	canvas.AddLine (new Point (0, 0), 1, orientation, LineStyle.Single);

	foreach (var p in canvas.GenerateImage (inArea)) {
		Application.Driver.Move (p.Key.X, p.Key.Y);
		Application.Driver.AddRune (p.Value);
	}

	string looksLike = $"{Environment.NewLine}{expected}";

	TestHelpers.AssertDriverContentsAre (looksLike, output);
}

[InlineData (0, 0, Orientation.Horizontal,"─")] //"──")]
[InlineData (1, 0, Orientation.Horizontal,"─")] //"──")]
[InlineData (0, 1, Orientation.Horizontal,"─")] //"──")]
[InlineData (0, 0, Orientation.Vertical,  "│")] //"│\n│")]
[InlineData (1, 0, Orientation.Vertical,  "│")] //"│\n│")]
[InlineData (0, 1, Orientation.Vertical,  "│")] //"│\n│")]
[Theory, AutoInitShutdown]
public void Length_1_Is_1_Long (int x, int y, Orientation orientation, string expected)
{
	Application.Begin (Application.Top);
	((FakeDriver)Application.Driver).SetBufferSize (20, 20);

	var inArea = new Rect (0, 0, 1, 1);

	var canvas = new LineCanvas ();
	// Add a line at 0, 0 that's has length of 1
	canvas.AddLine (new Point (0, 0), 1, orientation, LineStyle.Single);

	foreach (var p in canvas.GenerateImage (inArea)) {
		Application.Driver.Move (p.Key.X, p.Key.Y);
		Application.Driver.AddRune (p.Value);
	}

	string looksLike = $"{Environment.NewLine}{expected}";

	TestHelpers.AssertDriverContentsAre (looksLike, output);
}

I’m wondering if the right thing to do is to make it so the 2nd one is Length_1_Is_2_Long?

Issue Analytics

  • State:closed
  • Created 5 months ago
  • Comments:11

github_iconTop GitHub Comments

1reaction
tigcommented, Apr 11, 2023

I may introduce a new primitve AddPoint instead of (or inaddiiton to) zero-lengh lines. The nice thing about this is it could allow other forms of line endings like arrows!

With this we can refactor LineView to use LineCavnas.

1reaction
tigcommented, Apr 11, 2023

The line measurement of LineCanvas should be consistent with Rect. Maybe we need a new primitive Line?

I think it makes sense if a Line with Length of X takes up the same horizontal/vertical space as a Rect with Width/Height X?

Would simplify unit testing too probably and give us the option of having stuff like Rect.ToLines()

I’ve been working on this all day. Here’s what I’ve discovered/settled on:

  • Length == 0 means “a zero length line at this point, which is really just a way of forcing a line-ending or cross on another line.”

  • Length > 0 means “a line Length long.” Previously the code (and tests) were confused in this way

  • Unit tests that test drawing should NOT use a View to do the drawing (at least for primitve aspects). It adds too much complexity and provides false-positives/negatives, hiding both proper behavior and bad.

  • Both TestHelpers.AssertDriverContentsAre and TestHelpers.AssertDriverContentsWithFrameAre have bugs in them that cause weird, indeterminate behavior in tests. Been driving me NUTS!

  • GenerateImage is flawed in that it expects caller to pass a rect. It should figure out the bounds of the lines that have been added. I’ve changed it thusly:

		/// <summary>
		/// Evaluates the lines that have been added to the canvas and returns a map containing
		/// the glyphs and their locations. The glyphs are the characters that should be rendered
		/// so that all lines connect up with the appropriate intersection symbols. 
		/// </summary>
		/// <param name="inArea">A rectangle to constrain the search by.</param>
		/// <returns>A map of the points within the canvas that intersect with <paramref name="inArea"/>.</returns>
		public Dictionary<Point,Rune> GetMap (Rect inArea)
		{
			var map = new Dictionary<Point,Rune>();

			// walk through each pixel of the bitmap
			for (int y = inArea.Y; y < inArea.Y + inArea.Height; y++) {
				for (int x = inArea.X; x < inArea.X + inArea.Width; x++) {

					var intersects = _lines
						.Select (l => l.Intersects (x, y))
						.Where (i => i != null)
						.ToArray ();

					var rune = GetRuneForIntersects (Application.Driver, intersects);

					if(rune != null)
					{
						map.Add(new Point(x,y),rune.Value);
					}
				}
			}

			return map;
		}

		/// <summary>
		/// Evaluates the lines that have been added to the canvas and returns a map containing
		/// the glyphs and their locations. The glyphs are the characters that should be rendered
		/// so that all lines connect up with the appropriate intersection symbols. 
		/// </summary>
		/// <returns>A map of all the points within the canvas.</returns>
		public Dictionary<Point, Rune> GetMap () => GetMap (Canvas);

I do not think the version that takes an inArea is needed. LineCanvas now can compute the Rect that the lines occupy, letting callers do this:

		[InlineData (0, 0, 0, 0)]
		[InlineData (0, 0, 1, 0)]
		[InlineData (0, 0, 0, 1)]
		[InlineData (0, 0, 1, 1)]
		[InlineData (0, 0, 2, 2)]
		[InlineData (0, 0, 10, 10)]
		[InlineData (1, 0, 0, 0)]
		[InlineData (1, 0, 1, 0)]
		[InlineData (1, 0, 0, 1)]
		[InlineData (1, 0, 1, 1)]
		[InlineData (1, 0, 2, 2)]
		[InlineData (1, 0, 10, 10)]
		[InlineData (1, 1, 1, 0)]
		[InlineData (1, 1, 0, 1)]
		[InlineData (1, 1, 1, 1)]
		[InlineData (1, 1, 2, 2)]
		[InlineData (1, 1, 10, 10)]
		[InlineData (-1, -1, 1, 0)]
		[InlineData (-1, -1, 0, 1)]
		[InlineData (-1, -1, 1, 1)]
		[InlineData (-1, -1, 2, 2)]
		[InlineData (-1, -1, 10, 10)]
		[Theory, AutoInitShutdown]
		public void Canvas_Has_Correct_Bounds (int x, int y, int length, int height)
		{
			var canvas = new LineCanvas ();
			canvas.AddLine (new Point (x, y), length, Orientation.Horizontal, LineStyle.Single);
			canvas.AddLine (new Point (x, y), length, Orientation.Vertical, LineStyle.Single);

			int expectedWidth = Math.Max (length, 1);
			int expectedHeight = Math.Max (length, 1);

			Assert.Equal (new Rect (x, y, expectedWidth, expectedHeight), canvas.Canvas);
		}

I’m now almost done, but struggling with negative Lengths. They were poorly tested before and it’s easy to write code that neglects them.

But cool shit is happening… E.g. The two leftmost FrameViews at the top of this are simply X = Pos.Right(leftguy) -1 image

And this is just cool (obviously in a tiled mode we’d prevent dragging that caused overlaps and in an overappled mode we’d ensure the view being dragged occludes any it gets dragged over).

I2ttLTc 1


Read more comments on GitHub >

github_iconTop Results From Across the Web

How do I discriminate two different type of abnormalities in ...
It takes two binary images of contours to return a float that evaluate the distance between both. Python: cv.MatchShapes(object1, object2, ...
Read more >
Today: function call parameter detail, Canvas drawing, draw ...
Say we have a "foo" function with 2 parameters, and it is called by a "caller" function later. What does the run of...
Read more >
Untitled
Scott atkinson training, 2 methylanthracene, Workmans club invercargill, ... Rollertrack, Tempo meaning in english, Imagenes matrimonios cristianos, ...
Read more >
Untitled
Snake found with 6 heads, Best european country for sap jobs, Seit jahrhunderten ... Faisst fensterbau gmbh, Shelby industries 5408b, Budapester strasse 2, ......
Read more >
Drawing with the help of loops
This means that the radius difference of each two adjacent circles is the same. We choose the size of the circles to be...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found