Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions DynamicShapeLogic.Tests/DynamicShapeGeneratorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
namespace DynamicShapeLogic.Tests;

public class DynamicShapeGeneratorTests
{
private readonly DynamicShapeGenerator _generator;

public DynamicShapeGeneratorTests()
{
_generator = new DynamicShapeGenerator();
}

[Fact]
public void GenerateShape_WithEmptyInput_ReturnsEmptyList()
{
// Arrange
string emptyInput = "";

// Act
var result = _generator.GenerateShape(emptyInput);

// Assert
Assert.Empty(result);
}

[Fact]
public void GenerateShape_WithNullInput_ReturnsEmptyList()
{
// Arrange
string? nullInput = null;

// Act
var result = _generator.GenerateShape(nullInput);

// Assert
Assert.Empty(result);
}

[Fact]
public void GenerateShape_WithSingleCharacter_ReturnsOneShapeLine()
{
// Arrange
string input = "A";

// Act
var result = _generator.GenerateShape(input);

// Assert
Assert.Single(result);

var line = result[0];
Assert.Equal(250, line.X1); // Should start at center X
Assert.Equal(200, line.Y1); // Should start at center Y
Assert.NotEmpty(line.Color);
Assert.InRange(line.Width, 1, 3);
}

[Fact]
public void GenerateShape_WithMultipleCharacters_ReturnsConnectedLines()
{
// Arrange
string input = "AB";

// Act
var result = _generator.GenerateShape(input);

// Assert
Assert.Equal(2, result.Count);

// Second line should start where first line ends
Assert.Equal(result[0].X2, result[1].X1);
Assert.Equal(result[0].Y2, result[1].Y1);
}

[Fact]
public void GenerateShape_LineStaysWithinBounds()
{
// Arrange
string input = "XYZ"; // Some characters that might generate extreme angles

// Act
var result = _generator.GenerateShape(input);

// Assert
foreach (var line in result)
{
Assert.InRange(line.X1, 0, 500);
Assert.InRange(line.Y1, 0, 400);
Assert.InRange(line.X2, 10, 490); // Should respect MARGIN
Assert.InRange(line.Y2, 10, 390); // Should respect MARGIN
}
}

[Fact]
public void GenerateShape_ColorIsGeneratedCorrectly()
{
// Arrange
string input = "A";

// Act
var result = _generator.GenerateShape(input);

// Assert
var line = result[0];
Assert.StartsWith("rgb(", line.Color);
Assert.EndsWith(")", line.Color);
Assert.Contains(",", line.Color);
}

[Fact]
public void GenerateShape_LineWidthIsWithinExpectedRange()
{
// Arrange
string input = "ABCDEF";

// Act
var result = _generator.GenerateShape(input);

// Assert
foreach (var line in result)
{
Assert.InRange(line.Width, 1, 3);
}
}

[Fact]
public void GenerateShape_DifferentCharactersGenerateDifferentAngles()
{
// Arrange
string input1 = "A";
string input2 = "B";

// Act
var result1 = _generator.GenerateShape(input1);
var result2 = _generator.GenerateShape(input2);

// Assert
// The lines should be different (different unicode values should generate different angles)
Assert.NotEqual(result1[0].X2, result2[0].X2);
Assert.NotEqual(result1[0].Y2, result2[0].Y2);
}

[Fact]
public void ShapeLine_PropertiesCanBeSetAndRetrieved()
{
// Arrange
var shapeLine = new ShapeLine();

// Act
shapeLine.X1 = 10.5;
shapeLine.Y1 = 20.3;
shapeLine.X2 = 30.7;
shapeLine.Y2 = 40.1;
shapeLine.Color = "rgb(255,0,0)";
shapeLine.Width = 2;

// Assert
Assert.Equal(10.5, shapeLine.X1);
Assert.Equal(20.3, shapeLine.Y1);
Assert.Equal(30.7, shapeLine.X2);
Assert.Equal(40.1, shapeLine.Y2);
Assert.Equal("rgb(255,0,0)", shapeLine.Color);
Assert.Equal(2, shapeLine.Width);
}

[Fact]
public void ShapeLine_DefaultColorIsEmpty()
{
// Arrange & Act
var shapeLine = new ShapeLine();

// Assert
Assert.Equal("", shapeLine.Color);
}
}
29 changes: 29 additions & 0 deletions DynamicShapeLogic.Tests/DynamicShapeLogic.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\DynamicShapeLogic\DynamicShapeLogic.csproj" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions DynamicShapeLogic.Tests/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
93 changes: 93 additions & 0 deletions DynamicShapeLogic/DynamicShapeGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
namespace DynamicShapeLogic;

public class ShapeLine
{
public double X1 { get; set; }
public double Y1 { get; set; }
public double X2 { get; set; }
public double Y2 { get; set; }
public string Color { get; set; } = "";
public int Width { get; set; }
}

public class DynamicShapeGenerator
{
private const double CENTER_X = 250;
private const double CENTER_Y = 200;
private const double SVG_WIDTH = 500;
private const double SVG_HEIGHT = 400;
private const double MARGIN = 10;

public List<ShapeLine> GenerateShape(string inputText)
{
var shapeLines = new List<ShapeLine>();

if (string.IsNullOrEmpty(inputText))
return shapeLines;

double currentX = CENTER_X;
double currentY = CENTER_Y;

for (int i = 0; i < inputText.Length; i++)
{
char c = inputText[i];
int unicode = (int)c;

// Use unicode value directly as angle (in degrees)
double angle = unicode;
double length = (unicode % 80) + 20; // Line length between 20-99

// Calculate line end position from current position
double lineAngle = angle * Math.PI / 180;
double proposedEndX = currentX + Math.Cos(lineAngle) * length;
double proposedEndY = currentY + Math.Sin(lineAngle) * length;

// Check if line points away from both center lines
bool pointsAwayFromVerticalCenter = Math.Abs(proposedEndX - CENTER_X) > Math.Abs(currentX - CENTER_X);
bool pointsAwayFromHorizontalCenter = Math.Abs(proposedEndY - CENTER_Y) > Math.Abs(currentY - CENTER_Y);

// Flip the line if it points away from both center lines
if (pointsAwayFromVerticalCenter && pointsAwayFromHorizontalCenter)
{
angle += 180;
lineAngle = angle * Math.PI / 180;
proposedEndX = currentX + Math.Cos(lineAngle) * length;
proposedEndY = currentY + Math.Sin(lineAngle) * length;
}

// Ensure the line fits within the SVG bounds
double endX = Math.Max(MARGIN, Math.Min(SVG_WIDTH - MARGIN, proposedEndX));
double endY = Math.Max(MARGIN, Math.Min(SVG_HEIGHT - MARGIN, proposedEndY));

// Generate color based on unicode value
string color = GenerateColor(unicode);
int width = (unicode % 3) + 1; // Line width between 1-3

shapeLines.Add(new ShapeLine
{
X1 = currentX,
Y1 = currentY,
X2 = endX,
Y2 = endY,
Color = color,
Width = width
});

// Update current position to the end of this line for the next line to connect
currentX = endX;
currentY = endY;
}

return shapeLines;
}

private string GenerateColor(int unicode)
{
// Generate color based on unicode value
int r = (unicode * 7) % 256;
int g = (unicode * 13) % 256;
int b = (unicode * 19) % 256;

return $"rgb({r},{g},{b})";
}
}
9 changes: 9 additions & 0 deletions DynamicShapeLogic/DynamicShapeLogic.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
Loading