Skip to content

Instantly share code, notes, and snippets.

@yzorg
Created July 19, 2018 22:16
Show Gist options
  • Save yzorg/1fda998f09dcec2e8b0add5f8ee3b645 to your computer and use it in GitHub Desktop.
Save yzorg/1fda998f09dcec2e8b0add5f8ee3b645 to your computer and use it in GitHub Desktop.
programming coda: detect points on a line
[
{"x": 1, "y": 0 },
{"x": 1, "y": 4 },
{"x": 1, "y": 7 },
{"x": 1.5, "y": 3 },
{"x": 3, "y": 2 },
{"x": 5, "y": 0 },
{"x": 5, "y": 4 }
]
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
//## Output
// starting PointsLines
//m:-1 b:5 has points:
// X:1.00 Y:04
// X:3.00 Y:02
// X:5.00 Y:00
//m:1 b:-1 has points:
// X:1.00 Y:00
// X:3.00 Y:02
// X:5.00 Y:04
//vertical line at x:1 has points:
// X:1.00 Y:00
// X:1.00 Y:04
// X:1.00 Y:07
//Press any key to continue . . .
namespace PointsLines
{
using static Console;
struct Point
{
public float X { get; set; }
public float Y { get; set; }
public override string ToString() =>
$"X:{X:0.00} Y:{Y:00}";
}
class LineInfo : IComparable<LineInfo>
{
/// <summary>
/// Per the spec document, the intersection with Y-axis is "+ b", but in .NET conventions members are capitalized.
/// </summary>
public float B { get; set; }
public float? Slope { get; set; }
/// <summary>
/// A vertical line has no Slope.
/// </summary>
public bool IsVertical => Slope == null;
/// <summary>
/// Vertical line has a fixed X.
/// </summary>
public float? VerticalX { get; set; }
public HashSet<int> PointIndexes { get; } = new HashSet<int>();
public bool HasMultipleMatches => PointIndexes.Count > 2;
public int CompareTo(LineInfo other)
{
if (other == null)
return 1;
if (IsVertical != other.IsVertical)
return Comparer<bool>.Default.Compare(IsVertical, other.IsVertical);
if (IsVertical && other.IsVertical)
return Comparer<float>.Default.Compare(VerticalX ?? float.MinValue, other.VerticalX ?? float.MinValue);
if (Slope.HasValue && other.Slope.HasValue && Slope != other.Slope)
return Comparer<float>.Default.Compare(Slope.Value, other.Slope.Value);
return Comparer<float>.Default.Compare(B, other.B);
}
}
class Program
{
static void Main(string[] args)
{
WriteLine($"starting {nameof(PointsLines)}");
var app = new Program();
#if DEBUG
const string jsonFile = "../../../points.json"; // skip bin\Debug\netcoreapp2.0 when running from IDE (Visual Studio or Code)
#else
const string jsonFile = "points.json";
#endif
app.ReadPoints(File.ReadAllText(jsonFile));
app.BuildLinesFromAllCombinationOfPoints();
app.OutputLinesWithThreeOrMorePoints();
}
public List<Point> Points { get; set; }
public SortedSet<LineInfo> Lines { get; set; } = new SortedSet<LineInfo>();
private void ReadPoints(string pointsJson)
{
Points = JsonConvert.DeserializeObject<List<Point>>(pointsJson);
}
private void BuildLinesFromAllCombinationOfPoints()
{
for (int i = 0; i < Points.Count; ++i)
{
var p1 = Points[i];
for (int j = i + 1; j < Points.Count; ++j)
{
var p2 = Points[j];
LineInfo line;
// we have a vertical line, no slope
if (p1.X == p2.X) // does not handle duplicate points
{
line = new LineInfo() { VerticalX = p1.X };
}
else
{
line = new LineInfo()
{
Slope = (p2.Y - p1.Y) / (p2.X - p1.X)
};
line.B = p1.Y - (line.Slope.Value * p1.X);
}
if (Lines.TryGetValue(line, out var existingLine))
{
existingLine.PointIndexes.Add(j); // i is already in the set, from when the line was created
}
else
{
line.PointIndexes.Add(i);
line.PointIndexes.Add(j);
Lines.Add(line);
}
}
}
}
private void OutputLinesWithThreeOrMorePoints()
{
foreach (var line in Lines)
{
if (line.HasMultipleMatches)
{
if (line.IsVertical)
WriteLine($"vertical line at x:{line.VerticalX} has points:");
else
WriteLine($"m:{line.Slope} b:{line.B} has points:");
foreach (var pointIndex in line.PointIndexes)
{
WriteLine($" {Points[pointIndex]}");
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment