Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using GlmNet;
using System.IO;
using System.Security.Policy;
using SixLabors.ImageSharp;
using Color = SixLabors.ImageSharp.Color;
using Rectangle = SixLabors.ImageSharp.Rectangle;
using PointF = SixLabors.ImageSharp.PointF;
using Graphics = TilingLibrary.Compatibility.GraphicsInterface;
using Pen = TilingLibrary.Compatibility.Primitives.Pen;
using SolidBrush = TilingLibrary.Compatibility.Primitives.SolidBrush;
using Primitives = TilingLibrary.Compatibility.Primitives;
namespace Artwork
{
public class Settings
{
public enum ArtMode
{
Tiling,
QuadTree,
Delaunay
}
public enum TriangleScaleMode
{
Original,
Balanced
}
public Artwork.Tiling.TilingType TileType { get; set; } = Artwork.Tiling.TilingType.Danzer7Fold;
public int BaseTile { get; set; } = 0;
public ArtMode Mode { get; set; } = ArtMode.Tiling;
public int MaxSubDiv { get; set; } = 5;
public float DegreesOff { get; set; } = 0;
public Random Rand { get; set; } = new Random();
public int Threshold { get; set; } = 30;
public bool InvertSource { get; set; } = false;
public bool InvertOutput { get; set; } = false;
public bool ReloadMask { get; set; } = false;
public Color BackGroundColor { get; set; } = Color.Black;
public Color BackgroundHighlight { get; set; }
public int scalesmaller { get; set; }
public bool alwayssubdivide { get; set; }
public int scalesmallerlevel { get; set; }
public bool Symmetry { get; set; }
public bool SuperSymmetry { get; set; }
public int xscalesmallerlevel { get; set; }
public int xscalecenter { get; set; }
public float scalesmallerfactor { get; set; } = 1.0f;
public TriangleScaleMode scalingMode { get; set; } = TriangleScaleMode.Balanced;
public float distanceToMaskScale { get; set; }
public float distanceToMaskRange { get; set; }
public bool MarcelPlating { get; set; } = false;
public float BallRadius { get; set; } = 2000;
public float Gap { get; set; } = 6000;
public float Rounding { get; set; } = 8000;
public string DistanceMaskFile { get; set; } = "";
public void SetupFenixDefault(double scalecomp = 1.0)
{
Threshold = 2;
TileType = Artwork.Tiling.TilingType.RegularTriangle;
DegreesOff = -15;
MarcelPlating = false;
scalesmaller = 0;
scalesmallerfactor = 0;
scalesmallerfactor = -0.02f;
distanceToMaskRange = 0.400f * (float)scalecomp;
distanceToMaskScale = -0.020f;
InvertSource = true;
MaxSubDiv = 8;
}
}
public class Tiling
{
public enum TilingType
{
Danzer7Fold,
Danzer7FoldOriginal,
Maloney,
Conway,
Walton,
Penrose,
RegularTriangle,
SameSameDifferent,
TriangleMultiscale,
SVG14Fold,
HexaTest,
// New tilings
Pinwheel, // Conway/Radin - 1-2-sqrt(5) right triangle
Sphinx, // Classic rep-4 sphinx tile
AmmannBeenker, // Octagonal quasicrystal
HalfHex, // Half hexagon subdivision
Chair // L-shaped chair tiling
}
public class Polygon
{
public List<vec2> Vertices = new List<vec2>();
public int Type = 0;
public int depth = 0;
internal bool divided = false;
internal bool ContainsPoint(PointF b, float rad = .10f)
{
var BC = Triangle.barycentric(Vertices[0], Vertices[1], Vertices[2], new vec2(b.X, b.Y));
if (BC.x > 1 + rad || BC.x < 0 - rad) return false;
if (BC.y > 1 + rad || BC.y < 0 - rad) return false;
if (BC.z > 1 + rad || BC.z < 0 - rad) return false;
return true;
}
internal bool ContainsPointFaster(PointF b)
{
var BC = Triangle.barycentric(Vertices[0], Vertices[1], Vertices[2], new vec2(b.X, b.Y));
if (BC.x > 1 || BC.x < 0) return false;
if (BC.y > 1 || BC.y < 0) return false;
if (BC.z > 1 || BC.z < 0) return false;
return true;
}
public vec2 Mid()
{
return (Vertices[0] + Vertices[1] + Vertices[2]) / 3;
}
public void Trans(vec2 amt)
{
for (int i = 0; i < Vertices.Count; i++)
{
Vertices[i] += amt;
}
}
public int results = 0;
public vec3 AvgColor;
public bool callback(QuadTreeItem Item)
{
if (ContainsPointFaster(new PointF(Item.x, Item.y)))
{
results++;
return false;
}
return true;
}
public bool ContainsPointsInTree(QuadTreeNode tree)
{
float minx = Vertices[0].x;
float miny = Vertices[0].y;
float maxx = Vertices[0].x;
float maxy = Vertices[0].y;
for (int i = 1; i < 3; i++)
{
if (Vertices[i].x > maxx) maxx = Vertices[i].x; else if (Vertices[i].x < minx) minx = Vertices[i].x;
if (Vertices[i].y > maxy) maxy = Vertices[i].y; else if (Vertices[i].y < miny) miny = Vertices[i].y;
}
Rectangle R = new Rectangle() { X = (int)Math.Floor(minx), Y = (int)Math.Floor(miny), Width = (int)Math.Ceiling((maxx - minx) + 1), Height = (int)Math.Ceiling((maxy - miny) + 1) };
results = 0;
tree.CallBackInside(R, callback);
return (results > 0);
}
public void Rotate(float degreesOff, float xoff = 0, float yoff = 0)
{
double h = (degreesOff * Math.PI * 2.0) / 360.0;
double c = Math.Cos(h);
double s = Math.Sin(h);
for (int i = 0; i < Vertices.Count; i++)
{
float x = (float)(c * (Vertices[i].x - xoff) + s * (Vertices[i].y - yoff)) + xoff;
float y = (float)(-s * (Vertices[i].x - xoff) + c * (Vertices[i].y - yoff)) + yoff;
Vertices[i] = new vec2(x, y);
}
}
public Color GetColor()
{
int R = (int)Math.Max(0, Math.Min(255, AvgColor.x));
int G = (int)Math.Max(0, Math.Min(255, AvgColor.y));
int B = (int)Math.Max(0, Math.Min(255, AvgColor.z));
return Color.FromRgba((byte)R, (byte)G, (byte)B, 255);
}
public void AlterToFit(int width, int height)
{
var M = Mid();
for (int i = 0; i < 3; i++)
{
Vertices[i] -= M;
}
bool done = false;
int iterations = 0;
while (!done && iterations < 10000)
{
int corners = 0;
if (ContainsPoint(new PointF(-width / 2, -height / 2), 0)) corners++;
if (ContainsPoint(new PointF(width / 2, -height / 2), 0)) corners++;
if (ContainsPoint(new PointF(width / 2, height / 2), 0)) corners++;
if (ContainsPoint(new PointF(-width / 2, height / 2), 0)) corners++;
if (corners == 4)
{
done = true;
for (int i = 0; i < 3; i++)
{
Vertices[i] += new vec2(width / 2, height / 2);
}
}
else
{
for (int i = 0; i < 3; i++)
{
Vertices[i] *= 1.1f;
}
iterations++;
}
}
}
public void ShiftToEdge(float xoff = 0, float yoff = 0)
{
var centeredge = (Vertices[0] + Vertices[1]) * 0.5f;
for (int i = 0; i < Vertices.Count; i++)
{
Vertices[i] = new vec2(Vertices[i].x - centeredge.x + xoff, Vertices[i].y - centeredge.y + yoff);
}
}
public void Flip(float xoff = 0, float yoff = 0)
{
for (int i = 0; i < Vertices.Count; i++)
{
Vertices[i] = new vec2(-(Vertices[i].x - xoff) + xoff, -(Vertices[i].y - yoff) + yoff);
}
}
public void FlipY(float xoff = 0, float yoff = 0)
{
for (int i = 0; i < Vertices.Count; i++)
{
Vertices[i] = new vec2(Vertices[i].x, -(Vertices[i].y - yoff) + yoff);
}
}
internal void MirrorAround(float xoff, float yoff)
{
// FlipY(xoff, yoff);
// Rotate(180, xoff, yoff);
Reflect(Vertices[0], Vertices[1]);
}
private void Reflect(vec2 AA, vec2 BB)
{
vec2 N = (BB - AA);
N = glm.normalize(N);
for (int i = 0; i < Vertices.Count; i++)
{
//Vect2 = Vect1 - 2 * WallN * (WallN DOT Vect1)
Vertices[i] = Vertices[i] - 2 * N * (glm.dot(Vertices[i], N));
}
}
public void ScaleDown(Settings.TriangleScaleMode scalingMode, float factor)
{
switch (scalingMode)
{
case Settings.TriangleScaleMode.Original:
{
var M = Mid();
var b0 = Vertices[0] - M;
var b1 = Vertices[1] - M;
var b2 = Vertices[2] - M;
Vertices[0] = M + b0 * factor;
Vertices[1] = M + b1 * factor;
Vertices[2] = M + b2 * factor;
}
break;
case Settings.TriangleScaleMode.Balanced:
{
var v0 = new vec2(Vertices[0]);
var b0 = Mid() - v0;
var v1 = new vec2(Vertices[1]);
var v2 = new vec2(Vertices[2]);
var L = (float)Math.Sqrt(b0.x * b0.x + b0.y * b0.y);
//factor /= L;
var d0 = Vertices[0] - Vertices[2];
var d1 = Vertices[1] - Vertices[0];
var d2 = Vertices[2] - Vertices[1];
d0 = Rot90(d0);
d1 = Rot90(d1);
d2 = Rot90(d2);
//if (factor > L) factor = L;
factor = -factor;
vec3 A = new vec3(d0, 0);
vec3 B = new vec3(d1, 0);
float crossAB = GlmNet.glm.cross(A, B).z;
if (crossAB > 0) factor = -factor;
var d0n = GlmNet.glm.normalize(d0) * factor;
var d1n = GlmNet.glm.normalize(d1) * factor;
var d2n = GlmNet.glm.normalize(d2) * factor;
//var b0n = GlmNet.glm.normalize(b0);
//var b1n = GlmNet.glm.normalize(b1);
//var b2n = GlmNet.glm.normalize(b2);
Vertices[0] = SegmentSegmentIntersect(v0 + d1n, v1 + d1n, v2 + d0n, v0 + d0n);
Vertices[1] = SegmentSegmentIntersect(v1 + d2n, v2 + d2n, v0 + d1n, v1 + d1n);
Vertices[2] = SegmentSegmentIntersect(v2 + d0n, v0 + d0n, v1 + d2n, v2 + d2n);
d0 = Vertices[0] - Vertices[2];
d1 = Vertices[1] - Vertices[0];
d0 = Rot90(d0);
d1 = Rot90(d1);
vec3 A2 = new vec3(d0, 0);
vec3 B2 = new vec3(d1, 0);
float crossAB2 = GlmNet.glm.cross(A2, B2).z;
if (Math.Sign(crossAB2) != Math.Sign(crossAB))
{
Vertices[0] = Mid();
Vertices[1] = Mid();
Vertices[2] = Mid();
}
}
break;
}
}
private vec2 Rot90(vec2 d1)
{
vec2 r = new vec2(d1.y, -d1.x);
return r;
}
public void NormalizeToCenter()
{
if (Vertices.Count() == 0) return;
vec2 center = new vec2();
foreach (var r in Vertices)
{
center += r;
}
center /= (float)Vertices.Count();
for (int i = 0; i < Vertices.Count(); i++)
{
Vertices[i] -= center;
}
}
}
public static vec2 SegmentSegmentIntersect(vec2 P0, vec2 P1, vec2 P2, vec2 P3)
{
vec2 S1 = P1 - P0;
vec2 S2 = P3 - P2;
double s, t;
s = (-S1.y * (P0.x - P2.x) + S1.x * (P0.y - P2.y)) / (-S2.x * S1.y + S1.x * S2.y);
t = (S2.x * (P0.y - P2.y) - S2.y * (P0.x - P2.x)) / (-S2.x * S1.y + S1.x * S2.y);
// if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
// {
double X = P0.x + (t * S1.x); ;
double Y = P0.y + (t * S1.y);
return new vec2((float)X, (float)Y);
//}
return new vec2(0, 0); // No collision
}
public class SubPolygon
{
public List<int> VertexIndices = new List<int>();
public int Type = 0;
}
public class RealPolygonMapping
{
public vec2 Center = new vec2(0, 0);
public float Rotation = 0;
public float Scale = 1;
int PolygonID;
}
public class SVGMapping
{
public void LoadFolder(string basefolder)
{
var SVGfiles = Directory.GetFiles(basefolder, "*.svg");
foreach (var a in SVGfiles.OrderBy(x => x))
{
var R = TINRSART.SVG.SVGThings.LoadSVGToPolies(a, 0, 0);
if (R.Count() > 0 && R[0].Count() > 0)
{
InstanceSet IS = new InstanceSet();
IS.Name = Path.GetFileNameWithoutExtension(a);
var FirstPoly = R[0][0];
Polygon pp = new Polygon();
foreach (var v in FirstPoly.Vertices)
{
pp.Vertices.Add(new vec2(v.x, v.y));
};
pp.NormalizeToCenter();
IS.Polygon = pp;
Subdivisions.Add(IS);
}
}
}
RealPolygonMapping SetupFrom2VerticesAndPolygon(Polygon A, Polygon B, int va1, int va2, int vb1, int vb2)
{
return null;
}
List<InstanceSet> Subdivisions = new List<InstanceSet>();
}
public class InstanceSet
{
public string Name;
public Polygon Polygon;
public List<RealPolygonMapping> Subset = new List<RealPolygonMapping>();
void Add(float x, float y, int polygon, float angle, float xscale, float yscale)
{
}
void AddXSymmetric(float x, float y, int polygon, float angle)
{
Add(x, y, polygon, angle, 1, 1);
Add(-x, y, polygon, -angle, -1, 1);
}
void AddYSymmetric(float x, float y, int polygon, float angle)
{
Add(x, y, polygon, angle, 1, 1);
Add(x, -y, polygon, -angle, 1, -1);
}
void AddXYSymmetric(float x, float y, int polygon, float angle)
{
Add(x, y, polygon, angle, 1, 1);
Add(x, -y, polygon, -angle, 1, -1);
Add(-x, y, polygon, -angle, -1, 1);
Add(-x, -y, polygon, angle, -1, -1);
}
}
public class PolygonMapping
{
public vec2 A = new vec2();
public vec2 B = new vec2();
public vec2 C = new vec2();
public double AB;
public double BC;
public double AC;
public double Alpha;
public double Beta;
public double Gamma;
public int TypeID = 0;
public void BuildVertex(float x = 0, float y = 0)
{
A.x = x;
A.y = y;
SolveAngles();
B.x = A.x + (float)AB;
B.y = A.y;
C.x = A.x + (float)(Math.Cos(Alpha) * AC);
C.y = A.y - (float)(Math.Sin(Alpha) * AC);
}
public void SolveAngles()
{
double a = BC;
double b = AC;
double c = AB;
double cosC = ((a * a) + (b * b) - (c * c)) / (2.0 * a * b);
double cosA = ((b * b) + (c * c) - (a * a)) / (2.0 * b * c);
double cosB = ((c * c) + (a * a) - (b * b)) / (2.0 * c * a);
Alpha = Math.Acos(cosA);
Beta = Math.Acos(cosB);
Gamma = Math.Acos(cosC);
}
public void BuildVerticesFromEdgeLengths(double a, double b, double c)
{
AB = a;
BC = b;
AC = c;
SolveAngles();
BuildVertex();
}
public List<vec3> BaryCoords = new List<vec3>();
public List<SubPolygon> SubPolygons = new List<SubPolygon>();
internal int AddCorners()
{
AddPoint(A);
AddPoint(B);
return AddPoint(C);
}
public vec2 GetCartesian(int idx)
{
return Triangle.cartesian(A, B, C, BaryCoords[idx]); ;
}
public int AddPoint(vec2 p)
{
BaryCoords.Add(Triangle.barycentric(A, B, C, p));
return BaryCoords.Count - 1;
}
public int AddBetweenCorners(int idx1, int idx2, double totaledgelength, double distancefrom1)
{
var V1 = Triangle.cartesian(A, B, C, BaryCoords[idx1]);
var V2 = Triangle.cartesian(A, B, C, BaryCoords[idx2]);
var D = (V2 - V1) / (float)totaledgelength;
var V3 = V1 + D * (float)distancefrom1;
return AddPoint(V3);
}
public int AddTriangle(int idx1, int idx2, int idx3, int type)
{
SubPolygon SP = new SubPolygon();
SP.VertexIndices.Add(idx1);
SP.VertexIndices.Add(idx2);
SP.VertexIndices.Add(idx3);
SP.Type = type;
SubPolygons.Add(SP);
return SubPolygons.Count - 1;
}
public string Str(int idx)
{
var V = GetCartesian(idx);
return "(" + V.x.ToString("N2").Replace(",", ".") + ", " + V.y.ToString("N2").Replace(",", ".") + ")";
}
}
public class TilingDefinition
{
public Dictionary<int, PolygonMapping> DivisionSet = new Dictionary<int, PolygonMapping>();
private double InflationFactor;
public List<Polygon> Subdivide(Polygon P, int level)
{
List<Polygon> CurrentSet = new List<Polygon>();
CurrentSet.Add(P);
for (int i = 0; i < level; i++)
{
List<Polygon> NextSet = new List<Polygon>();
foreach (var a in CurrentSet)
{
NextSet.AddRange(TilingDefinition.Subdivide(DivisionSet[a.Type], a.Vertices[0], a.Vertices[1], a.Vertices[2]));
}
CurrentSet = NextSet;
}
return CurrentSet;
}
public static List<Polygon> Subdivide(PolygonMapping map, vec2 a, vec2 b, vec2 c, int depth = 0, int storedepth = -1) // subdivide based on first 3 barycentric coords
{
List<Polygon> T = new List<Polygon>();
List<vec2> Vertices = new List<vec2>();
foreach (var bc in map.BaryCoords)
{
vec2 R = bc.x * a + bc.y * b + bc.z * c;
Vertices.Add(R);
}
if (storedepth == -1) storedepth = depth + 1;
foreach (var sp in map.SubPolygons)
{
int type = sp.Type;
List<vec2> PolyVertices = new List<vec2>();
foreach (var v in sp.VertexIndices)
{
PolyVertices.Add(Vertices[v]);
}
T.Add(new Polygon() { Type = type, Vertices = PolyVertices, depth = storedepth });
}
return T;
}
public void CreateDanzers7FoldOriginal()
{
InflationFactor = 1.0 + Math.Sin((2.0 * Math.PI) / 7.0) / Math.Sin(Math.PI / 7.0);
PolygonMapping t0 = new PolygonMapping();
PolygonMapping t1 = new PolygonMapping();
PolygonMapping t2 = new PolygonMapping();
double a = Math.Sin(Math.PI / 7.0);
double b = Math.Sin((2.0 * Math.PI) / 7.0);
double g = Math.Sin((3.0 * Math.PI) / 7.0);
int vA = 0;
int vB = 1;
int vC = 2;
int vt0H = 0;
int vt0G = 0;
{
t0.BuildVerticesFromEdgeLengths(g, a, b);
t0.AddCorners();
int vD = t0.AddBetweenCorners(0, 1, g + b + g, g);
int vE = t0.AddBetweenCorners(0, 1, g + b + g, g + b);
int vF = t0.AddBetweenCorners(1, 2, a + b, a);
int vG = t0.AddBetweenCorners(2, 0, g + b + a, a);
int vH = t0.AddBetweenCorners(2, 0, g + b + a, a + b);
vt0G = vG;
vt0H = vH;
t0.AddTriangle(vA, vH, vD, 1);
t0.AddTriangle(vE, vH, vD, 0);
t0.AddTriangle(vH, vE, vG, 2);
t0.AddTriangle(vE, vC, vG, 0);
t0.AddTriangle(vC, vE, vF, 2);
t0.AddTriangle(vE, vB, vF, 0);
}
{
t1.BuildVerticesFromEdgeLengths(g, a, g);
t1.AddCorners();
int vD = t1.AddBetweenCorners(0, 1, g + b + g, g);
int vE = t1.AddBetweenCorners(0, 1, g + b + g, g + b);
int vF = t1.AddBetweenCorners(1, 2, a + b, a);
int vG = t1.AddBetweenCorners(2, 0, g + b + g, g);
int vH = t1.AddBetweenCorners(2, 0, g + b + g, g + b);
int vI = t1.AddPoint(((t1.GetCartesian(vD) + t1.GetCartesian(vH)) * 0.5f - t1.GetCartesian(vA)) * 2 + t1.GetCartesian(vA));
t1.AddTriangle(vA, vD, vH, 1);
t1.AddTriangle(vI, vD, vH, 1);
t1.AddTriangle(vH, vI, vG, 0);
t1.AddTriangle(vD, vI, vE, 0);
t1.AddTriangle(vC, vI, vG, 1);
t1.AddTriangle(vB, vI, vE, 1);
t1.AddTriangle(vI, vB, vF, 0);
t1.AddTriangle(vC, vI, vF, 2);
}
{
t2.BuildVerticesFromEdgeLengths(g, b, b);
t2.AddCorners();
int vD = t2.AddBetweenCorners(0, 1, g + b + g, g);
int vE = t2.AddBetweenCorners(0, 1, g + b + g, g + b);
int vF = t2.AddBetweenCorners(1, 2, g + b + a, g);
int vG = t2.AddBetweenCorners(1, 2, g + b + a, g + b);
int vH = t2.AddBetweenCorners(2, 0, g + b + a, g);
int vI = t2.AddBetweenCorners(2, 0, g + b + a, g + b);
int vJ = t2.AddPoint(t2.GetCartesian(vA) + (t0.GetCartesian(vB) - t0.GetCartesian(vt0G)) * new vec2(1, -1));
int vK = t2.AddPoint(t2.GetCartesian(vA) + (t0.GetCartesian(vB) - t0.GetCartesian(vt0H)) * new vec2(1, -1));
t2.AddTriangle(vD, vA, vI, 0);
t2.AddTriangle(vH, vD, vI, 2);
t2.AddTriangle(vD, vH, vJ, 0);
t2.AddTriangle(vK, vD, vJ, 2);
t2.AddTriangle(vD, vK, vE, 0);
t2.AddTriangle(vB, vK, vE, 1);
t2.AddTriangle(vB, vF, vK, 1);
t2.AddTriangle(vJ, vF, vK, 0);
t2.AddTriangle(vF, vJ, vG, 2);
t2.AddTriangle(vJ, vC, vG, 0);
t2.AddTriangle(vC, vH, vJ, 1);
//int vJ = t2.AddBetweenCorners(0, 1, g + b + g, g);
//int vK = t2.AddBetweenCorners(0, 1, g + b + g, g);
}
DivisionSet[0] = t0;
DivisionSet[1] = t1;
DivisionSet[2] = t2;
}
public Polygon CreateBaseTriangle(int type, float renderscale, float x = 0.0f, float y = 0.0f)
{
type = type % DivisionSet.Count;
var T = DivisionSet[type];
vec2 MID = (T.A + T.B + T.C) / 3.0f;
Polygon p = new Polygon();
// todo: do more than a triangle here..
p.Vertices.Add((T.A - MID + new vec2(x, y)) * renderscale);
p.Vertices.Add((T.B - MID + new vec2(x, y)) * renderscale);
p.Vertices.Add((T.C - MID + new vec2(x, y)) * renderscale);
p.Type = type;
return p;
}
public void CreateDanzers7Fold()
{
InflationFactor = 1.0 + Math.Sin((2.0 * Math.PI) / 7.0) / Math.Sin(Math.PI / 7.0);
PolygonMapping t0 = new PolygonMapping();
PolygonMapping t1 = new PolygonMapping();
PolygonMapping t2 = new PolygonMapping();
double a = Math.Sin(Math.PI / 7.0);
double c = Math.Sin((2.0 * Math.PI) / 7.0);
double b = Math.Sin((3.0 * Math.PI) / 7.0);
double d = Math.Sin((3.0 * Math.PI) / 7.0) + Math.Sin((2.0 * Math.PI) / 7.0);
a = 0.44504186791263;
b = 1.000000000000002;
d = 1.801937735804839;
c = 1.246979603717467;
//Console.WriteLine("{0} : {1}", b/c, (d+b)/(c+b+c));
//Console.WriteLine("{0} : {1}", d, b * InflationFactor - a);
//Console.WriteLine("{0} : {1}", d, (d + b)/ InflationFactor );
//double d = b + c;
//Console.WriteLine("a: {0} b: {1} c: {2} d: {3}, inflate:{4}", a, b, c, d, InflationFactor);
int vA = 0;
int vB = 1;
int vC = 2;
{
t0.BuildVerticesFromEdgeLengths(a, b, b);
t0.AddCorners();
int vD = t0.AddBetweenCorners(1, 2, d + b, d);
int vE = t0.AddBetweenCorners(2, 0, d + b, b);
int vF = t0.AddPoint(((t0.GetCartesian(vD) + t0.GetCartesian(vE)) * 0.5f - t0.GetCartesian(vC)) * 2 + t0.GetCartesian(vC));
t0.AddTriangle(vA, vE, vF, 2);
t0.AddTriangle(vA, vB, vF, 1);
t0.AddTriangle(vB, vD, vF, 2);
t0.AddTriangle(vD, vE, vF, 0);
t0.AddTriangle(vE, vD, vC, 0);
}
{
t2.BuildVerticesFromEdgeLengths(d, b, b);
t2.AddCorners();
int vD = t2.AddBetweenCorners(0, 1, b + b + d + c, b);
int vE = t2.AddBetweenCorners(0, 1, b + b + d + c, b + d);
int vF = t2.AddBetweenCorners(0, 1, b + b + d + c, b + d + c);
int vG = t2.AddBetweenCorners(1, 2, d + b, d);
int vH = t2.AddBetweenCorners(2, 0, d + b, d);
int vI = t2.AddPoint(((t2.GetCartesian(vD) + t2.GetCartesian(vH)) * 0.5f - t2.GetCartesian(vA)) * 2 + t2.GetCartesian(vA));
t2.AddTriangle(vD, vH, vA, 0);
t2.AddTriangle(vH, vD, vI, 0);
t2.AddTriangle(vE, vD, vI, 2);
t2.AddTriangle(vC, vH, vI, 2);
t2.AddTriangle(vE, vC, vI, 1);
t2.AddTriangle(vC, vE, vG, 1);
t2.AddTriangle(vE, vF, vG, 1);
t2.AddTriangle(vB, vG, vF, 2);
}
{
t1.BuildVerticesFromEdgeLengths(c, b, b);
t1.AddCorners();
int vD = t1.AddBetweenCorners(0, 1, c + b + c, c);
int vE = t1.AddBetweenCorners(0, 1, c + b + c, c + b);
int vF = t1.AddBetweenCorners(1, 2, b + d, b);
int vG = t1.AddBetweenCorners(2, 0, d + b, d);
var H = ((t1.GetCartesian(vD) - t1.GetCartesian(vC)) / (float)(b + c)) * (float)b + t1.GetCartesian(vC);
int vH = t1.AddPoint(H);
var I = ((t1.GetCartesian(vE) - t1.GetCartesian(vC)) / (float)(b + c)) * (float)d + t1.GetCartesian(vC);
int vI = t1.AddPoint(I);
var J = ((t1.GetCartesian(vE) - t1.GetCartesian(vC)) / (float)(b + c)) * (float)b + t1.GetCartesian(vC);
int vJ = t1.AddPoint(J);
t1.AddTriangle(vA, vD, vG, 1);
t1.AddTriangle(vD, vH, vG, 1);
t1.AddTriangle(vC, vG, vH, 2);
t1.AddTriangle(vE, vI, vD, 0);
t1.AddTriangle(vH, vD, vI, 1);
t1.AddTriangle(vC, vI, vH, 2);
t1.AddTriangle(vB, vE, vF, 1);
t1.AddTriangle(vE, vJ, vF, 1);
t1.AddTriangle(vC, vF, vJ, 2);
}
DivisionSet[0] = t0;
DivisionSet[1] = t1;
DivisionSet[2] = t2;
}
public void CreateDaleWalton1()
{
PolygonMapping t0 = new PolygonMapping() { TypeID = 0 };
PolygonMapping t1 = new PolygonMapping() { TypeID = 1 };
PolygonMapping t2 = new PolygonMapping() { TypeID = 2 };
List<PolygonMapping> T = new List<PolygonMapping>();
int vA = 0;
int vB = 1;
int vC = 2;
T.Add(t0);
T.Add(t1);
T.Add(t2);
int off = 0;
foreach (var t in T)
{
t.BuildVerticesFromEdgeLengths(5, 4, 3);
t.AddCorners();
int vD = t.AddPoint(new vec2(t.C.x, 0));
double FC = Math.Sin(t.Beta) * t.GetCartesian(vC).y;
int vF = t.AddBetweenCorners(2, 1, t.BC, -FC);
int vE = t.AddPoint(new vec2(t.GetCartesian(vF).x, 0));
int vG = t.AddPoint(new vec2(t.GetCartesian(vC).x, t.GetCartesian(vF).y));
t.AddTriangle(vA, vC, vD, (off) % 3);
t.AddTriangle(vC, vF, vG, (off) % 3);
t.AddTriangle(vE, vG, vD, (off + 1) % 3);
t.AddTriangle(vG, vE, vF, (off + 1) % 3);
t.AddTriangle(vF, vB, vE, (off + 2) % 3);
off++;
// t.ad
}
DivisionSet[0] = t0;
DivisionSet[1] = t1;
DivisionSet[2] = t2;
}
public void CreateConway()
{
PolygonMapping t = new PolygonMapping() { TypeID = 0 };
PolygonMapping t2 = new PolygonMapping() { TypeID = 0 };
int vA = 0;
int vB = 1;
int vC = 2;
t.BuildVerticesFromEdgeLengths(2, Math.Sqrt(5), 1);
t2.BuildVerticesFromEdgeLengths(2, Math.Sqrt(5), 1);
t.AddCorners();
t2.AddCorners();
{
int vD = t.AddPoint(new vec2(1, 0));
int vE = t.AddBetweenCorners(1, 2, 5, 2);
int vF = t.AddBetweenCorners(1, 2, 5, 4);
int vG = t.AddPoint((t.GetCartesian(vF) + t.GetCartesian(vA)) / 2.0f);
t.AddTriangle(vF, vA, vC, 1);
t.AddTriangle(vG, vD, vA, 0);
t.AddTriangle(vG, vD, vF, 1);
t.AddTriangle(vE, vF, vD, 0);
t.AddTriangle(vE, vB, vD, 1);
}
{
int vD = t2.AddPoint(new vec2(1, 0));
int vE = t2.AddBetweenCorners(1, 2, 5, 2);
int vF = t2.AddBetweenCorners(1, 2, 5, 4);
int vG = t2.AddPoint((t2.GetCartesian(vF) + t2.GetCartesian(vA)) / 2.0f);
t2.AddTriangle(vF, vA, vC, 0);
t2.AddTriangle(vG, vD, vA, 1);
t2.AddTriangle(vG, vD, vF, 0);
t2.AddTriangle(vE, vF, vD, 1);
t2.AddTriangle(vE, vB, vD, 0);
}
DivisionSet[0] = t;
DivisionSet[1] = t2;
}
vec2 mirror(vec2 p, vec2 P0, vec2 P1)
{
double dx, dy, a, b;
double x2, y2;
vec2 pres; //reflected point to be returned
dx = (double)(P1.x - P0.x);
dy = (double)(P1.y - P0.y);
a = (dx * dx - dy * dy) / (dx * dx + dy * dy);
b = 2 * dx * dy / (dx * dx + dy * dy);
x2 = a * (p.x - P0.x) + b * (p.y - P0.y) + P0.x;
y2 = b * (p.x - P0.x) - a * (p.y - P0.y) + P0.y;
pres = new vec2((float)x2, (float)y2);
return pres;
}
public void CreateMaloney()
{
InflationFactor = 1.0 + Math.Sin((2.0 * Math.PI) / 7.0) / Math.Sin(Math.PI / 7.0);
PolygonMapping t0 = new PolygonMapping();
PolygonMapping t1 = new PolygonMapping();
PolygonMapping t2 = new PolygonMapping();
double a = Math.Sin(Math.PI / 7.0);
double b = Math.Sin((2.0 * Math.PI) / 7.0);
double c = Math.Sin((3.0 * Math.PI) / 7.0);
// Console.WriteLine("{0} {1} {2}", a, b, c);
int vA = 0;
int vB = 1;
int vC = 2;
{
t0.BuildVerticesFromEdgeLengths(a, b, c);
t0.AddCorners();
int vD = t0.AddBetweenCorners(0, 1, c + b, c);
int vE = t0.AddBetweenCorners(1, 2, c + a + b + c, c);
int vF = t0.AddBetweenCorners(1, 2, c + a + b + c, c + a);
int vG = t0.AddBetweenCorners(1, 2, c + a + b + c, c + a + b);
int vH = t0.AddBetweenCorners(2, 0, b + c + a + b + c, b);
int vI = t0.AddBetweenCorners(2, 0, b + c + a + b + c, b + c);
int vJ = t0.AddBetweenCorners(2, 0, b + c + a + b + c, b + c + a);
int vK = t0.AddBetweenCorners(2, 0, b + c + a + b + c, b + c + a + b);
int vL = t0.AddPoint(t0.GetCartesian(vA) + glm.normalize(((t0.GetCartesian(vK) + t0.GetCartesian(vD)) * 0.5f)) * (float)(c / (c + b + a + c + b)));
int vM = t0.AddPoint(mirror(t0.GetCartesian(vK), t0.GetCartesian(vJ), t0.GetCartesian(vL)));
t0.AddTriangle(vD, vL, vA, 1);
t0.AddTriangle(vK, vL, vA, 1);
t0.AddTriangle(vL, vD, vB, 0);
t0.AddTriangle(vL, vM, vB, 0);
t0.AddTriangle(vB, vE, vM, 2);
t0.AddTriangle(vL, vK, vJ, 0);
t0.AddTriangle(vL, vM, vJ, 0);
t0.AddTriangle(vJ, vE, vM, 2);
t0.AddTriangle(vJ, vI, vE, 1);
t0.AddTriangle(vE, vF, vI, 0);
t0.AddTriangle(vI, vG, vF, 2);
t0.AddTriangle(vH, vG, vI, 1);
t0.AddTriangle(vG, vH, vC, 0);
// t0.AddTriangle(vD, vE, vF, 0);
// t0.AddTriangle(vE, vD, vC, 0);
}
{
t1.BuildVerticesFromEdgeLengths(a, c, c);
t1.AddCorners();
int vD = t1.AddBetweenCorners(0, 1, c + b, b);
int vE = t1.AddBetweenCorners(1, 2, b + c + a + b + c, b);
int vF = t1.AddBetweenCorners(1, 2, b + c + a + b + c, b + c);
int vG = t1.AddBetweenCorners(1, 2, b + c + a + b + c, b + c + a);
int vH = t1.AddBetweenCorners(1, 2, b + c + a + b + c, b + c + a + b);
int vI = t1.AddBetweenCorners(2, 0, b + c + a + b + c, b);
int vJ = t1.AddBetweenCorners(2, 0, b + c + a + b + c, b + c);
int vK = t1.AddBetweenCorners(2, 0, b + c + a + b + c, b + c + a);
int vL = t1.AddBetweenCorners(2, 0, b + c + a + b + c, b + c + a + b);
int vM = t1.AddPoint(t1.GetCartesian(vA) + ((t1.GetCartesian(vF) - t1.GetCartesian(vA)) / (float)(b + a + c)) * (float)c);
int vN = t1.AddPoint(t1.GetCartesian(vA) + ((t1.GetCartesian(vF) - t1.GetCartesian(vA)) / (float)(b + a + c)) * (float)(c + a));
int vO = t1.AddPoint(t1.GetCartesian(vA) + glm.normalize(((t1.GetCartesian(vE)))) * (float)((c) / (c + b + a + c + b)));
//int vN = t1.AddPoint(mirror(t0.GetCartesian(vK), t0.GetCartesian(vJ), t0.GetCartesian(vL)));
t1.AddTriangle(vO, vD, vA, 0);
t1.AddTriangle(vO, vM, vA, 1);
t1.AddTriangle(vL, vM, vA, 1);
t1.AddTriangle(vD, vO, vB, 1);
t1.AddTriangle(vB, vO, vE, 2);
t1.AddTriangle(vM, vO, vE, 0);
t1.AddTriangle(vM, vN, vE, 0);
t1.AddTriangle(vE, vF, vN, 2);
t1.AddTriangle(vM, vL, vK, 0);
t1.AddTriangle(vM, vN, vK, 0);
t1.AddTriangle(vK, vF, vN, 2);
t1.AddTriangle(vK, vJ, vF, 1);
t1.AddTriangle(vF, vG, vJ, 0);
t1.AddTriangle(vJ, vH, vG, 2);
t1.AddTriangle(vI, vH, vJ, 1);
t1.AddTriangle(vH, vI, vC, 0);
}
{
t2.BuildVerticesFromEdgeLengths(c, b, b);
t2.AddCorners();
int vD = t2.AddBetweenCorners(0, 1, c + b + a + c + b, c);
int vE = t2.AddBetweenCorners(0, 1, c + b + a + c + b, c + b);
int vF = t2.AddBetweenCorners(0, 1, c + b + a + c + b, c + b + a);
int vG = t2.AddBetweenCorners(0, 1, c + b + a + c + b, c + b + a + c);
int vH = t2.AddBetweenCorners(1, 2, c + a + b + c, c);
int vI = t2.AddBetweenCorners(1, 2, c + a + b + c, c + a);
int vJ = t2.AddBetweenCorners(1, 2, c + a + b + c, c + a + b);
int vK = t2.AddBetweenCorners(2, 0, c + a + b + c, c);
int vL = t2.AddBetweenCorners(2, 0, c + a + b + c, c + a);
int vM = t2.AddBetweenCorners(2, 0, c + a + b + c, c + a + b);
int vP = t2.AddPoint(mirror(t2.GetCartesian(vC), t2.GetCartesian(vK), t2.GetCartesian(vJ)));
int vN = t2.AddBetweenCorners(vA, vP, c + a + b, c);
int vO = t2.AddBetweenCorners(vA, vP, c + a + b, c + a);
int vQ = t2.AddBetweenCorners(vP, vB, c + a + b, a);
int vR = t2.AddBetweenCorners(vP, vB, c + a + b, a + b);
int vS = t2.AddBetweenCorners(vK, vJ, b + a, b);
t2.AddTriangle(vD, vN, vA, 1);
t2.AddTriangle(vM, vN, vA, 1);
t2.AddTriangle(vN, vD, vE, 0);
t2.AddTriangle(vN, vO, vE, 0);
t2.AddTriangle(vE, vP, vO, 2);
t2.AddTriangle(vE, vF, vP, 1);
t2.AddTriangle(vP, vQ, vF, 0);
t2.AddTriangle(vG, vR, vF, 1);
t2.AddTriangle(vR, vG, vB, 0);
t2.AddTriangle(vR, vH, vB, 1);
t2.AddTriangle(vH, vR, vQ, 0);
t2.AddTriangle(vH, vI, vQ, 0);
t2.AddTriangle(vF, vR, vQ, 2);
t2.AddTriangle(vQ, vJ, vI, 2);
t2.AddTriangle(vQ, vP, vJ, 1);
t2.AddTriangle(vJ, vS, vP, 0);
t2.AddTriangle(vJ, vS, vC, 0);
t2.AddTriangle(vC, vK, vS, 2);
t2.AddTriangle(vP, vK, vS, 2);
t2.AddTriangle(vL, vK, vP, 1);
t2.AddTriangle(vL, vP, vO, 2);
t2.AddTriangle(vN, vO, vL, 0);
t2.AddTriangle(vN, vM, vL, 0);
}
DivisionSet[0] = t0;
DivisionSet[1] = t1;
DivisionSet[2] = t2;
}
public void CreatePenrose()
{
InflationFactor = 1.0 + Math.Sin((2.0 * Math.PI) / 7.0) / Math.Sin(Math.PI / 7.0);
PolygonMapping t0 = new PolygonMapping();
PolygonMapping t1 = new PolygonMapping();
double a = 1 / ((1.0 + Math.Sqrt(5)) / 2.0);
double b = 1;
double c = b / a;
int vA = 0;
int vB = 1;
int vC = 2;
{
t0.BuildVerticesFromEdgeLengths(a, b, b);
t0.AddCorners();
int vD = t0.AddBetweenCorners(vC, vA, a + b, b);
t0.AddTriangle(vD, vA, vB, 0);
t0.AddTriangle(vB, vC, vD, 1);
}
{
t1.BuildVerticesFromEdgeLengths(c, b, b);
t1.AddCorners();
int vD = t1.AddBetweenCorners(vA, vB, c + b, c);
int vE = t1.AddBetweenCorners(vC, vA, a + b, a);
t1.AddTriangle(vD, vA, vE, 1);
t1.AddTriangle(vE, vC, vD, 0);
t1.AddTriangle(vB, vC, vD, 1);
}
DivisionSet[0] = t0;
DivisionSet[1] = t1;
}
public vec2 NormalizeSize()
{
double len = 0;
foreach (var a in DivisionSet.Values)
{
len = Math.Max(len, a.AB);
len = Math.Max(len, a.AC);
len = Math.Max(len, a.BC);
}
double div = 1.0 / len;
vec2 mid = new vec2(0, 0);
foreach (var a in DivisionSet.Values)
{
a.A *= (float)div;
a.B *= (float)div;
a.C *= (float)div;
a.AB *= div;
a.AC *= div;
a.BC *= div;
mid += (a.A + a.B + a.C) / 3.0f;
}
mid /= (float)DivisionSet.Count;
return mid;
}
public vec2 Create(TilingType tilingType)
{
switch (tilingType)
{
case TilingType.Danzer7Fold: CreateDanzers7Fold(); break;
case TilingType.Walton: CreateDaleWalton1(); break;
case TilingType.Danzer7FoldOriginal: CreateDanzers7FoldOriginal(); break;
case TilingType.Conway: CreateConway(); break;
case TilingType.Maloney: CreateMaloney(); break;
case TilingType.Penrose: CreatePenrose(); break;
case TilingType.RegularTriangle: CreateRegular(); break;
case TilingType.SameSameDifferent: CreateSameSameDifferent(); break;
case TilingType.TriangleMultiscale: CreateMultiscale(); break;
case TilingType.SVG14Fold: CreateSVGTiling("SVGTilings/14Fold"); break;
case TilingType.HexaTest: CreateSVGTiling("SVGTilings/HexaThing"); break;
// New tilings
case TilingType.Pinwheel: CreatePinwheel(); break;
case TilingType.Sphinx: CreateSphinx(); break;
case TilingType.AmmannBeenker: CreateAmmannBeenker(); break;
case TilingType.HalfHex: CreateHalfHex(); break;
case TilingType.Chair: CreateChair(); break;
}
return NormalizeSize();
}
public void CreateSVGTiling(string basefolder)
{
SVGMapping svm = new SVGMapping();
svm.LoadFolder(basefolder);
}
private void CreateMultiscale()
{
InflationFactor = 1.0 + Math.Sin((2.0 * Math.PI) / 7.0) / Math.Sin(Math.PI / 7.0);
PolygonMapping t0 = new PolygonMapping();
PolygonMapping t1 = new PolygonMapping();
double a = Math.Sin(Math.PI / 7.0);
double c = Math.Sin((2.0 * Math.PI) / 7.0);
double b = Math.Sin((3.0 * Math.PI) / 7.0);
double d = Math.Sin((3.0 * Math.PI) / 7.0) + Math.Sin((2.0 * Math.PI) / 7.0);
a = 1;
b = Math.Sin((60 * Math.PI * 2.0) / 360) * 2.0;
//Console.WriteLine("{0} : {1}", b/c, (d+b)/(c+b+c));
//Console.WriteLine("{0} : {1}", d, b * InflationFactor - a);
//Console.WriteLine("{0} : {1}", d, (d + b)/ InflationFactor );
//double d = b + c;
//Console.WriteLine("a: {0} b: {1} c: {2} d: {3}, inflate:{4}", a, b, c, d, InflationFactor);
int vA = 0;
int vB = 1;
int vC = 2;
{
t0.BuildVerticesFromEdgeLengths(a, a, a);
t0.AddCorners();
int vD = t0.AddBetweenCorners(0, 1, a + a + a, a);
int vE = t0.AddBetweenCorners(0, 1, a + a + a, a + a);
int vF = t0.AddBetweenCorners(1, 2, a + a + a, a);
int vG = t0.AddBetweenCorners(1, 2, a + a + a, a + a);
int vH = t0.AddBetweenCorners(2, 0, a + a + a, a);
int vI = t0.AddBetweenCorners(2, 0, a + a + a, a + a);
t0.AddTriangle(vA, vD, vI, 0);
t0.AddTriangle(vE, vB, vF, 0);
t0.AddTriangle(vG, vC, vH, 0);
t0.AddTriangle(vH, vD, vF, 0);
t0.AddTriangle(vH, vI, vD, 1);
t0.AddTriangle(vD, vE, vF, 1);
t0.AddTriangle(vF, vG, vH, 1);
}
{
t1.BuildVerticesFromEdgeLengths(a, a, b);
t1.AddCorners();
int vD = t1.AddBetweenCorners(0, 1, a + a, a);
int vE = t1.AddBetweenCorners(1, 2, a + a, a);
int vF = t1.AddBetweenCorners(2, 0, a + a, a);
t1.AddTriangle(vD, vB, vF, 0);
t1.AddTriangle(vB, vE, vF, 0);
t1.AddTriangle(vA, vD, vF, 1);
t1.AddTriangle(vF, vE, vC, 1);
}
DivisionSet[0] = t0;
DivisionSet[1] = t1;
}
private void CreateSameSameDifferent()
{
InflationFactor = 1.0 + Math.Sin((2.0 * Math.PI) / 7.0) / Math.Sin(Math.PI / 7.0);
PolygonMapping t0 = new PolygonMapping();
PolygonMapping t1 = new PolygonMapping();
double a = 1;
int vA = 0;
int vB = 1;
int vC = 2;
{
t0.BuildVerticesFromEdgeLengths(a, a, a);
t0.AddCorners();
var mid = (t0.A + t0.B + t0.C);
int vAa = t0.AddPoint(mid + (t0.A - mid) * 0.9f);
int vBb = t0.AddPoint(mid + (t0.B - mid) * 0.9f);
int vCc = t0.AddPoint(mid + (t0.C - mid) * 0.9f);
t0.AddTriangle(vAa, vBb, vCc, 0);
t1.BuildVerticesFromEdgeLengths(a, a, a);
t1.AddCorners();
int vAB = t1.AddBetweenCorners(vA, vB, a, a / 2);
int vBC = t1.AddBetweenCorners(vB, vC, a, a / 2);
int vCA = t1.AddBetweenCorners(vC, vA, a, a / 2);
t1.AddTriangle(vAB, vBC, vCA, 1);
t1.AddTriangle(vA, vAB, vCA, 1);
t1.AddTriangle(vAB, vB, vBC, 1);
t1.AddTriangle(vBC, vC, vCA, 1);
}
DivisionSet[0] = t1;
DivisionSet[1] = t0;
}
private void CreateRegular()
{
InflationFactor = 1.0 + Math.Sin((2.0 * Math.PI) / 7.0) / Math.Sin(Math.PI / 7.0);
PolygonMapping t0 = new PolygonMapping();
double a = 1;
int vA = 0;
int vB = 1;
int vC = 2;
{
t0.BuildVerticesFromEdgeLengths(a, a, a);
t0.AddCorners();
int vAB = t0.AddBetweenCorners(vA, vB, a, a / 2);
int vBC = t0.AddBetweenCorners(vB, vC, a, a / 2);
int vCA = t0.AddBetweenCorners(vC, vA, a, a / 2);
t0.AddTriangle(vAB, vBC, vCA, 0);
t0.AddTriangle(vA, vAB, vCA, 0);
t0.AddTriangle(vAB, vB, vBC, 0);
t0.AddTriangle(vBC, vC, vCA, 0);
}
DivisionSet[0] = t0;
}
/// <summary>
/// Creates the Pinwheel tiling (Conway/Radin).
/// Uses a 1-2-√5 right triangle that subdivides into 5 similar triangles.
/// This creates infinite rotations in the tiling.
/// </summary>
private void CreatePinwheel()
{
// Inflation factor is √5
InflationFactor = Math.Sqrt(5.0);
PolygonMapping t0 = new PolygonMapping();
// 1-2-√5 right triangle (legs 1 and 2, hypotenuse √5)
double a = 1.0; // short leg
double b = 2.0; // long leg
double c = Math.Sqrt(5.0); // hypotenuse
int vA = 0; // right angle vertex
int vB = 1; // opposite short leg
int vC = 2; // opposite long leg
// Build the right triangle
t0.BuildVerticesFromEdgeLengths(a, b, c);
t0.AddCorners();
// Subdivide into 5 similar 1-2-√5 triangles
// Key points for subdivision (using ratios based on 1/√5)
int vD = t0.AddBetweenCorners(vA, vB, a, a / 2); // midpoint of short leg
int vE = t0.AddBetweenCorners(vA, vC, b, b / 2); // midpoint of long leg
int vF = t0.AddBetweenCorners(vB, vC, c, c * 0.4); // point on hypotenuse
// Create the 5 sub-triangles
t0.AddTriangle(vA, vD, vE, 0); // corner triangle at right angle
t0.AddTriangle(vD, vB, vF, 0); // triangle at B
t0.AddTriangle(vE, vF, vC, 0); // triangle at C
t0.AddTriangle(vD, vE, vF, 0); // central triangle
t0.AddTriangle(vE, vD, vF, 0); // mirrored central (creates rotation)
DivisionSet[0] = t0;
}
/// <summary>
/// Creates the Sphinx tiling.
/// The Sphinx is a rep-4 hexiamond (made of 6 equilateral triangles).
/// Each Sphinx subdivides into 4 smaller Sphinxes.
/// </summary>
private void CreateSphinx()
{
// Inflation factor is 2 for rep-4
InflationFactor = 2.0;
PolygonMapping t0 = new PolygonMapping();
// For triangular representation, use equilateral triangle subdivision
// Sphinx approximation using triangular decomposition
double a = 1.0;
int vA = 0;
int vB = 1;
int vC = 2;
t0.BuildVerticesFromEdgeLengths(a, a, a);
t0.AddCorners();
// Divide into 4 triangles (similar to rep-4 structure)
int vAB = t0.AddBetweenCorners(vA, vB, a, a / 2);
int vBC = t0.AddBetweenCorners(vB, vC, a, a / 2);
int vCA = t0.AddBetweenCorners(vC, vA, a, a / 2);
t0.AddTriangle(vA, vAB, vCA, 0);
t0.AddTriangle(vAB, vB, vBC, 0);
t0.AddTriangle(vCA, vBC, vC, 0);
t0.AddTriangle(vAB, vBC, vCA, 0); // central inverted triangle
DivisionSet[0] = t0;
}
/// <summary>
/// Creates the Ammann-Beenker tiling.
/// Octagonal quasicrystal with 8-fold symmetry.
/// Uses triangular representation of the square and rhombus tiles.
/// </summary>
private void CreateAmmannBeenker()
{
// Silver mean inflation factor: 1 + √2
InflationFactor = 1.0 + Math.Sqrt(2.0);
PolygonMapping t0 = new PolygonMapping();
PolygonMapping t1 = new PolygonMapping();
// For octagonal symmetry, use 45-degree angles
double a = 1.0;
double b = Math.Sqrt(2.0); // diagonal of unit square
int vA = 0;
int vB = 1;
int vC = 2;
// Type 0: Isoceles right triangle (half of square)
{
t0.BuildVerticesFromEdgeLengths(a, a, b);
t0.AddCorners();
int vAB = t0.AddBetweenCorners(vA, vB, a, a / 2);
int vBC = t0.AddBetweenCorners(vB, vC, a, a / 2);
int vCA = t0.AddBetweenCorners(vC, vA, b, b / 2);
t0.AddTriangle(vA, vAB, vCA, 0);
t0.AddTriangle(vAB, vB, vBC, 0);
t0.AddTriangle(vAB, vBC, vCA, 1);
t0.AddTriangle(vCA, vBC, vC, 1);
}
// Type 1: 45-135 degree rhombus half
{
double r = a;
t1.BuildVerticesFromEdgeLengths(r, r, r * Math.Sqrt(2 - Math.Sqrt(2)));
t1.AddCorners();
int vD = t1.AddBetweenCorners(vA, vB, r, r / 2);
int vE = t1.AddBetweenCorners(vB, vC, r, r / 2);
t1.AddTriangle(vA, vD, vC, 1);
t1.AddTriangle(vD, vB, vE, 0);
t1.AddTriangle(vD, vE, vC, 0);
}
DivisionSet[0] = t0;
DivisionSet[1] = t1;
}
/// <summary>
/// Creates the Half-Hex tiling.
/// Half-hexagon (trapezoid) that subdivides into 3 smaller copies.
/// </summary>
private void CreateHalfHex()
{
// Inflation factor is √3
InflationFactor = Math.Sqrt(3.0);
PolygonMapping t0 = new PolygonMapping();
// Use equilateral triangle base
double a = 1.0;
int vA = 0;
int vB = 1;
int vC = 2;
t0.BuildVerticesFromEdgeLengths(a, a, a);
t0.AddCorners();
// Subdivide into 3 triangles in a hex-like pattern
var center = (t0.A + t0.B + t0.C) / 3.0f;
int vCenter = t0.AddPoint(center);
t0.AddTriangle(vA, vB, vCenter, 0);
t0.AddTriangle(vB, vC, vCenter, 0);
t0.AddTriangle(vC, vA, vCenter, 0);
DivisionSet[0] = t0;
}
/// <summary>
/// Creates the Chair tiling.
/// L-shaped tile that is a rep-4 reptile.
/// Uses triangular approximation for the substitution.
/// </summary>
private void CreateChair()
{
// Chair is rep-4, inflation factor is 2
InflationFactor = 2.0;
PolygonMapping t0 = new PolygonMapping();
PolygonMapping t1 = new PolygonMapping();
double a = 1.0;
double b = 2.0;
double c = Math.Sqrt(5.0);
int vA = 0;
int vB = 1;
int vC = 2;
// Type 0: Right triangle part of chair (1-2-√5)
{
t0.BuildVerticesFromEdgeLengths(a, b, c);
t0.AddCorners();
int vAB = t0.AddBetweenCorners(vA, vB, a, a / 2);
int vBC = t0.AddBetweenCorners(vB, vC, b, b / 2);
int vCA = t0.AddBetweenCorners(vC, vA, c, c / 2);
t0.AddTriangle(vA, vAB, vCA, 0);
t0.AddTriangle(vAB, vB, vBC, 1);
t0.AddTriangle(vBC, vC, vCA, 0);
t0.AddTriangle(vAB, vBC, vCA, 1);
}
// Type 1: Mirror version
{
t1.BuildVerticesFromEdgeLengths(a, b, c);
t1.AddCorners();
int vAB = t1.AddBetweenCorners(vA, vB, a, a / 2);
int vBC = t1.AddBetweenCorners(vB, vC, b, b / 2);
int vCA = t1.AddBetweenCorners(vC, vA, c, c / 2);
t1.AddTriangle(vA, vAB, vCA, 1);
t1.AddTriangle(vAB, vB, vBC, 0);
t1.AddTriangle(vBC, vC, vCA, 1);
t1.AddTriangle(vAB, vBC, vCA, 0);
}
DivisionSet[0] = t0;
DivisionSet[1] = t1;
}
/// <summary>
/// Subdivides a polygon adaptively based on mask content.
/// </summary>
public List<Polygon> SubdivideAdaptive(Polygon P, int level, QuadTreeNode Tree, bool alwayssubdivide = false)
{
return SubdivideAdaptive(P, level, Tree, alwayssubdivide, CancellationToken.None);
}
/// <summary>
/// Subdivides a polygon adaptively based on mask content.
/// Supports cancellation for background execution.
/// </summary>
public List<Polygon> SubdivideAdaptive(Polygon P, int level, QuadTreeNode Tree, bool alwayssubdivide, CancellationToken cancellationToken)
{
List<Polygon> CurrentSet = new List<Polygon>(1);
CurrentSet.Add(P);
for (int i = 0; i < level; i++)
{
// Check for cancellation at each subdivision level
cancellationToken.ThrowIfCancellationRequested();
List<Polygon> NextSet = new List<Polygon>(CurrentSet.Count + 1000);
foreach (var a in CurrentSet)
{
int divide = 0;
if (alwayssubdivide) divide++;
if (a.divided == false)
{
if (Tree != null) if (a.ContainsPointsInTree(Tree)) divide++;
if (divide > 0)
{
a.divided = true;
NextSet.AddRange(TilingDefinition.Subdivide(DivisionSet[a.Type], a.Vertices[0], a.Vertices[1], a.Vertices[2], a.depth, (alwayssubdivide && divide < 2) ? a.depth : a.depth + 1));
}
else
{
a.divided = true;
NextSet.Add(a);
}
}
else
{
a.divided = true;
NextSet.Add(a);
}
}
CurrentSet = NextSet;
}
cancellationToken.ThrowIfCancellationRequested();
List<Polygon> ResultSet = new List<Polygon>(CurrentSet.Count);
foreach (var a in CurrentSet)
{
if (Tree == null || CheckPoints(a, Tree))
{
ResultSet.Add(a);
}
}
return ResultSet;
}
public bool CheckPoints(Polygon a, QuadTreeNode Tree)
{
if (a.ContainsPointsInTree(Tree)) return false;
return true;
}
}
public class Triangle
{
public static vec3 barycentric(vec2 a, vec2 b, vec2 c, vec2 vec)
{
vec3 lambda = new vec3();
float den = 1 / ((b.y - c.y) * (a.x - c.x) + (c.x - b.x) * (a.y - c.y));
lambda.x = ((b.y - c.y) * (vec.x - c.x) + (c.x - b.x) * (vec.y - c.y)) * den;
lambda.y = ((c.y - a.y) * (vec.x - c.x) + (a.x - c.x) * (vec.y - c.y)) * den;
lambda.z = 1 - lambda.x - lambda.y;
return lambda;
}
public static vec2 cartesian(vec2 a, vec2 b, vec2 c, vec3 p)
{
return a * p.x + b * p.y + c * p.z;
}
public vec2 A;
public vec2 B;
public vec2 C;
public double AB;
public double AC;
public double BC;
public double Alpha;
public double Beta;
public double Gamma;
public double rotation;
public vec2 center;
public int type = 0;
internal void SolveAngles()
{
double a = BC;
double b = AC;
double c = AB;
double cosC = ((a * a) + (b * b) - (c * c)) / (2.0 * a * b);
double cosA = ((b * b) + (c * c) - (a * a)) / (2.0 * b * c);
double cosB = ((c * c) + (a * a) - (b * b)) / (2.0 * c * a);
Alpha = Math.Acos(cosA);
Beta = Math.Acos(cosB);
Gamma = Math.Acos(cosC);
}
public List<Triangle> SubTiles = new List<Triangle>();
public float depth;
public void Render(Graphics G, int Depth = 5)
{
PointF[] Ps = new PointF[3];
Ps[0].X = (float)A.x;
Ps[0].Y = (float)A.y;
Ps[1].X = (float)B.x;
Ps[1].Y = (float)B.y;
Ps[2].X = (float)C.x;
Ps[2].Y = (float)C.y;
bool dAB = false;
bool dBC = false;
bool dCA = false;
Color Cl = Color.Red;
Color Cl2 = Color.FromRgba((byte)0, (byte)0, (byte)0, (byte)100);
switch (type)
{
// case 0: dAB = false; dBC = false; dCA = false; Cl = Color.FromArgb(0xff, 0xcc, 0xca, 0xe4); Cl = Color.Blue; break;
// case 1: dAB = true; dBC = false; dCA = false; Cl = Color.FromArgb(0xff, 0xfc, 0x66, 0x13); Cl = Color.White; break;
// case 2: dAB = true; dBC = true; dCA = true; Cl = Color.FromArgb(0xfc, 0xca, 0x64); Cl = Color.FromArgb(200,255,255); break;
//Cl2 = Color.Yellow;
case 0: dAB = false; dBC = false; dCA = false; Cl = Color.FromRgba((byte)0xcc, (byte)0xca, (byte)0xe4, 255); break;
case 1: dAB = true; dBC = false; dCA = false; Cl = Color.FromRgba((byte)0xfc, (byte)0x66, (byte)0x13, 255); break;
case 2: dAB = true; dBC = true; dCA = true; Cl = Color.FromRgba((byte)0xfc, (byte)0xca, (byte)0x64, 255); break;
case 5: Cl = Color.Red; break;
}
Pen P = new Pen(Cl2, 2);
P.EndCap = Primitives.LineCap.Round;
P.LineJoin = Primitives.LineJoin.Round;
if (SubTiles.Count == 0)
{
G.FillPolygon(new SolidBrush(Cl), Ps);
G.DrawPolygon(P, Ps);
}
else
foreach (var a in SubTiles)
{
a.Render(G, Depth - 1);
}
vec2 aDiff = (B - A);
vec2 bDiff = (C - B);
vec2 cDiff = (A - C);
vec2 Cen = (A + B + C) / 3.0f;
G.SmoothingMode = Primitives.SmoothingMode.AntiAlias;
G.CompositingQuality = Primitives.CompositingQuality.HighQuality;
if (false)
{
drawdot(G, A, B, dAB, Cen);
drawdot(G, B, C, dBC, Cen);
drawdot(G, C, A, dCA, Cen);
}
}
private void drawdot(Graphics G, vec2 A, vec2 B, bool dAB, vec2 Cen)
{
vec2 aA = A;
vec2 bB = B;
if (dAB == true)
{
aA = B;
bB = A;
}
vec2 D = (bB - aA) / 20.0f;
vec2 Rr = ((aA + D) * 10 + Cen) / 11.0f;
G.DrawLine(new Pen(Color.Black, 3), Rr.x, Rr.y, Rr.x, Rr.y + 10);
}
public void SubDivide(int N)
{
double a = Math.Sin(Math.PI / 7.0);
double b = Math.Sin((2.0 * Math.PI) / 7.0);
double g = Math.Sin((3.0 * Math.PI) / 7.0);
switch (type)
{
case 0:
{
// if (N == 0) return;
vec2 dAB = (B - A) / (float)(g * 2 + b);
vec2 dAC = (C - A) / (float)(g + b + a);
vec2 dBC = (C - B) / (float)(a + b);
vec2 D = A + dAB * (float)g;
vec2 E = A + dAB * (float)(g + b);
vec2 F = B + dBC * (float)a;
vec2 G = A + dAC * (float)(g + b);
vec2 H = A + dAC * (float)g;
SubTiles.Add(new Triangle() { type = 1, A = A, B = H, C = D });
SubTiles.Add(new Triangle() { type = 0, A = E, B = H, C = D });
SubTiles.Add(new Triangle() { type = 2, A = E, B = G, C = H });
SubTiles.Add(new Triangle() { type = 0, A = E, B = C, C = G });
SubTiles.Add(new Triangle() { type = 2, A = E, B = F, C = C });
SubTiles.Add(new Triangle() { type = 0, A = E, B = B, C = F });
}
break;
case 1:
{
// if (N == 0) return;
vec2 dAB = (B - A) / (float)(g * 2 + b);
vec2 dAC = (C - A) / (float)(g * 2 + b);
vec2 dBC = (C - B) / (float)(a + b);
vec2 D = A + dAB * (float)g;
vec2 E = A + dAB * (float)(g + b);
vec2 F = B + dBC * (float)a;
vec2 G = A + dAC * (float)(g + b);
vec2 H = A + dAC * (float)g;
vec2 I = (((D + H) / 2.0f) - A) * 2 + A;
//vec2 I = A + dAC * (float)g;
SubTiles.Add(new Triangle() { type = 1, A = A, B = H, C = D });
SubTiles.Add(new Triangle() { type = 1, A = I, B = H, C = D });
SubTiles.Add(new Triangle() { type = 0, A = D, B = I, C = E });
SubTiles.Add(new Triangle() { type = 1, A = B, B = I, C = E });
SubTiles.Add(new Triangle() { type = 0, A = I, B = B, C = F });
SubTiles.Add(new Triangle() { type = 2, A = I, B = F, C = C });
SubTiles.Add(new Triangle() { type = 1, A = C, B = I, C = G });
SubTiles.Add(new Triangle() { type = 0, A = H, B = I, C = G });
}
break;
case 2:
{
vec2 dAB = (B - A) / (float)(g + a + b);
vec2 dAC = (C - A) / (float)(b + g + g);
vec2 dBC = (C - B) / (float)(a + b + g);
vec3 CB = new vec3(C - B, 0);
vec3 AB = new vec3(A - B, 0);
var cross = glm.cross(CB, AB);
double H1 = Math.Atan2(CB.y, CB.x);
Triangle T = new Triangle();
T.AB = a;
T.BC = g;
T.AC = g;
T.SolveAngles();
double Dif = 0;
if (cross.z > 0) Dif = H1 + T.Gamma; else Dif = H1 - T.Gamma;
vec2 D = A + dAB * (float)g;
vec2 E = A + dAB * (float)(g + b);
vec2 F = B + dBC * (float)g;
vec2 G = B + dBC * (float)(g + b);
vec2 H = A + dAC * (float)(g + b);
vec2 I = A + dAC * (float)g;
vec2 norm = glm.normalize((((I + D) / 2.0f) - A));
vec2 Dist = (I - A);
float scale = (float)Math.Sqrt(Dist.x * Dist.x + Dist.y * Dist.y);
vec2 J = A + norm * (float)scale;
vec2 K1;
K1.x = B.x;
K1.y = B.y;
K1.x += (float)Math.Cos(Dif) * scale;
K1.y += (float)Math.Sin(Dif) * scale;
vec2 K = K1;
SubTiles.Add(new Triangle() { type = 1, A = A, B = D, C = J });
SubTiles.Add(new Triangle() { type = 0, A = K, B = D, C = J });
SubTiles.Add(new Triangle() { type = 2, A = K, B = E, C = D });
SubTiles.Add(new Triangle() { type = 1, A = B, B = F, C = K });
SubTiles.Add(new Triangle() { type = 0, A = K, B = B, C = E });
SubTiles.Add(new Triangle() { type = 0, A = H, B = F, C = K });
SubTiles.Add(new Triangle() { type = 2, A = H, B = G, C = F });
SubTiles.Add(new Triangle() { type = 0, A = H, B = C, C = G });
SubTiles.Add(new Triangle() { type = 2, A = H, B = K, C = J });
SubTiles.Add(new Triangle() { type = 0, A = H, B = J, C = I });
SubTiles.Add(new Triangle() { type = 1, A = A, B = J, C = I });
}
break;
}
foreach (var s in SubTiles)
{
s.depth = this.depth + 1;
}
if (N > 0)
{
foreach (var s in SubTiles)
{
s.SubDivide(N - 1);
}
}
}
public void Print()
{
Console.WriteLine("a {0} : b {1} : c {2}", Alpha, Beta, Gamma);
Console.WriteLine("AB {0} : BC {1} : AC {2}", AB, BC, AC);
}
public void BuildVertex(float x = 0, float y = 0)
{
A.x = x;
A.y = y;
SolveAngles();
B.x = A.x + (float)AB;
B.y = A.y;
C.x = A.x + (float)(Math.Cos(Alpha) * AC);
C.y = A.y - (float)(Math.Sin(Alpha) * AC);
}
}
public List<Triangle> TriangleTypes = new List<Triangle>();
public Tiling()
{
double a = Math.Sin(Math.PI / 7.0);
double b = Math.Sin((2.0 * Math.PI) / 7.0);
double g = Math.Sin((3.0 * Math.PI) / 7.0);
Triangle T0 = new Triangle() { type = 0, AB = g, AC = b, BC = a };
Triangle T1 = new Triangle() { type = 1, AB = g, AC = g, BC = a };
Triangle T2 = new Triangle() { type = 2, AB = b, AC = g, BC = b };
T0.SolveAngles();
T1.SolveAngles();
T2.SolveAngles();
TriangleTypes.Add(T0);
TriangleTypes.Add(T1);
TriangleTypes.Add(T2);
}
public static int DefaultTilingDepth(TilingType Type)
{
switch (Type)
{
case TilingType.Conway: return 5;
case TilingType.Maloney: return 3;
case TilingType.Penrose: return 8;
case TilingType.Walton: return 5;
case TilingType.Danzer7Fold: return 5;
case TilingType.Danzer7FoldOriginal: return 5;
}
return 1;
}
}
}
5203ce801This file is pinned to the selected release. Download the original to open it in your design tool.