187 lines
3.9 KiB
C#
187 lines
3.9 KiB
C#
|
using System;
|
|||
|
using Cairo;
|
|||
|
using System.Text.RegularExpressions;
|
|||
|
using System.Resources;
|
|||
|
using System.Collections.Generic;
|
|||
|
|
|||
|
namespace SBRL.SimpleTurtle
|
|||
|
{
|
|||
|
public class Area
|
|||
|
{
|
|||
|
public double X;
|
|||
|
public double Y;
|
|||
|
public double Width;
|
|||
|
public double Height;
|
|||
|
|
|||
|
public Area(double inX, double inY, double inWidth, double inHeight)
|
|||
|
{
|
|||
|
X = inX;
|
|||
|
Y = inY;
|
|||
|
Width = inWidth;
|
|||
|
Height = inHeight;
|
|||
|
}
|
|||
|
}
|
|||
|
public class Turtle
|
|||
|
{
|
|||
|
private bool strictMode = false;
|
|||
|
private string commandQueue;
|
|||
|
|
|||
|
private PointD position;
|
|||
|
private Area bounds;
|
|||
|
private double heading;
|
|||
|
private double headingStep;
|
|||
|
public double HeadingStep
|
|||
|
{
|
|||
|
get { return headingStep; }
|
|||
|
set { headingStep = value; }
|
|||
|
}
|
|||
|
private double movementStep ;
|
|||
|
public double MovementStep
|
|||
|
{
|
|||
|
get { return movementStep; }
|
|||
|
set { movementStep = value; }
|
|||
|
}
|
|||
|
|
|||
|
public Turtle ()
|
|||
|
{
|
|||
|
Reset();
|
|||
|
}
|
|||
|
|
|||
|
public void ApplyDefinitions(Dictionary<string, string> definitions)
|
|||
|
{
|
|||
|
foreach(KeyValuePair<string, string> definition in definitions)
|
|||
|
{
|
|||
|
switch(definition.Key.ToLower())
|
|||
|
{
|
|||
|
case "angle":
|
|||
|
HeadingStep = double.Parse(definition.Value);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public bool Commands(string commandText)
|
|||
|
{
|
|||
|
// Remove all whitespace
|
|||
|
commandText = Regex.Replace(commandText, @"\s+", "");
|
|||
|
commandText = commandText.Replace("h", "f");
|
|||
|
|
|||
|
string okCommands = "f+-";
|
|||
|
|
|||
|
foreach(char ch in commandText)
|
|||
|
{
|
|||
|
if(okCommands.Contains(ch.ToString()))
|
|||
|
{
|
|||
|
switch(ch)
|
|||
|
{
|
|||
|
case 'f':
|
|||
|
Forwards();
|
|||
|
break;
|
|||
|
case '+':
|
|||
|
Turn(false);
|
|||
|
break;
|
|||
|
case '-':
|
|||
|
Turn(true);
|
|||
|
break;
|
|||
|
default:
|
|||
|
if (strictMode) {
|
|||
|
Console.WriteLine("The unexpected character '{0}' slipped through the net!", ch);
|
|||
|
return false;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
else if(strictMode)
|
|||
|
{
|
|||
|
Console.Error.WriteLine("Error: unexpected character '{0}'", ch);
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
commandQueue += commandText;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
public void Draw(string filename, bool reset = true)
|
|||
|
{
|
|||
|
ImageSurface canvas = new ImageSurface(Format.ARGB32, (int)Math.Ceiling(bounds.Width + 10), (int)Math.Ceiling(bounds.Height + 10));
|
|||
|
Context context = new Context(canvas);
|
|||
|
position = new PointD(-bounds.X + 5, -bounds.Y + 5);
|
|||
|
heading = 0;
|
|||
|
|
|||
|
context.LineWidth = 3;
|
|||
|
context.MoveTo(position);
|
|||
|
foreach(char ch in commandQueue)
|
|||
|
{
|
|||
|
switch(ch)
|
|||
|
{
|
|||
|
case 'f':
|
|||
|
PointD newPosition = new PointD(
|
|||
|
position.X + movementStep * Math.Sin(heading),
|
|||
|
position.Y + movementStep * Math.Cos(heading)
|
|||
|
);
|
|||
|
context.LineTo(newPosition);
|
|||
|
position = newPosition;
|
|||
|
break;
|
|||
|
case '+':
|
|||
|
heading += headingStep;
|
|||
|
break;
|
|||
|
case '-':
|
|||
|
heading -= headingStep;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
context.Stroke();
|
|||
|
canvas.WriteToPng(string.Format(filename));
|
|||
|
context.Dispose();
|
|||
|
canvas.Dispose();
|
|||
|
if(reset)
|
|||
|
Reset();
|
|||
|
}
|
|||
|
|
|||
|
public void Forwards()
|
|||
|
{
|
|||
|
PointD newPosition = new PointD(
|
|||
|
position.X + MovementStep * Math.Sin(heading),
|
|||
|
position.Y + MovementStep * Math.Cos(heading)
|
|||
|
);
|
|||
|
|
|||
|
if (newPosition.X > bounds.X + bounds.Width)
|
|||
|
bounds.Width += newPosition.X - position.X;
|
|||
|
if (newPosition.Y > bounds.Y + bounds.Height)
|
|||
|
bounds.Height += newPosition.Y - position.Y;
|
|||
|
if (newPosition.X < bounds.X)
|
|||
|
{
|
|||
|
bounds.X = newPosition.X;
|
|||
|
bounds.Width += position.X - newPosition.X;
|
|||
|
}
|
|||
|
if (newPosition.Y < bounds.Y)
|
|||
|
{
|
|||
|
bounds.Y = newPosition.Y;
|
|||
|
bounds.Height += position.Y - newPosition.Y;
|
|||
|
}
|
|||
|
|
|||
|
position = newPosition;
|
|||
|
}
|
|||
|
|
|||
|
public void Turn(bool anticlockwise = false)
|
|||
|
{
|
|||
|
if (!anticlockwise)
|
|||
|
heading += HeadingStep;
|
|||
|
else
|
|||
|
heading -= HeadingStep;
|
|||
|
}
|
|||
|
|
|||
|
public void Reset()
|
|||
|
{
|
|||
|
commandQueue = string.Empty;
|
|||
|
position = new PointD(0, 0);
|
|||
|
bounds = new Area(position.X, position.Y, 1, 1);
|
|||
|
heading = 0;
|
|||
|
headingStep = Math.PI / 2;
|
|||
|
movementStep = 25;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|