public Chain(Stone Start, Stone End) { Stone cur = Start; List<Stone> stones = new List<Stone>(); stones.Add(cur); while (cur != End) { cur = cur.Next; stones.Add(cur); } this.Length = stones.Count - 1; this.Stones = stones; }
/// <summary> /// Prepares this leader for the next entry. Returns false if there are no entries left to introduce. /// </summary> public bool Reset(World World, Stone Stone) { this.PressureThreshold = Settings.Current.StoneIntroductionPressureThreshold; this.Timeout = Settings.Current.StoneIntroductionTimeout; this.Next = GetNext(World, Stone); return this.Next != null; }
/// <summary> /// Gets the next entry to be introduced by the given stone, or null if there are none left. /// </summary> public static Entry GetNext(World World, Stone Stone) { Entry entry = Stone.Entry; Entry n = entry.Next; if (n != null && !World.ContainsEntry(n)) return n; uint maxweight = 0; Entry next = null; foreach (Entry p in entry.Previous) if (!World.ContainsEntry(p) && p.Weight > maxweight) { maxweight = p.Weight; next = p; } return next; }
/// <summary> /// Removes a stone from a zone. /// </summary> private void _Remove(Stone Stone, ZoneIndex Index, List<Stone> Zone) { Zone.Remove(Stone); if (Zone.Count == 0) this._Zones.Remove(Index); }
/// <summary> /// Gets the repulsive pressure felt by the given stone. /// </summary> private double _Pressure(Stone Stone, out Vector Direction) { double pressure = 0.0; Direction = Vector.Zero; ZoneIndex centerindex = _GetZone(Stone.Position); for (int x = centerindex.X - 1; x <= centerindex.X + 1; x++) for (int y = centerindex.Y - 1; y <= centerindex.Y + 1; y++) { List<Stone> zone; if (this._Zones.TryGetValue(new ZoneIndex(x, y), out zone)) foreach (Stone other in zone) if (other != Stone) { Vector dif = Stone.Position - other.Position; double len = Math.Max(dif.Length, other.Radius); double mag = 1.0 / (len * len); Vector force = dif * (mag / len); Direction += force; pressure += mag; } } return pressure; }
/// <summary> /// Introduces a stone to the world. /// </summary> private void _Introduce(Stone Stone, Entry Entry) { foreach (Entry p in Entry.Previous) { Stone prev; if (this._Stones.TryGetValue(p, out prev)) prev.Next = Stone; } Entry n = Entry.Next; Stone next; if (n != null && this._Stones.TryGetValue(n, out next)) Stone.Next = next; this._Stones[Entry] = Stone; this._Insert(Stone, _GetZone(Stone.Position)); this.MakeLeader(Stone); }
/// <summary> /// Allows a stone to interact with all other stones in the zone with the given index. /// </summary> private void _Interact(Stone Stone, ZoneIndex Index, double Time) { List<Stone> zone; if (this._Zones.TryGetValue(Index, out zone)) { foreach (Stone other in zone) { Stone.Interact(Stone, other, Time); } } }
/// <summary> /// Handles the interaction between two unique stones. /// </summary> public static void Interact(Stone A, Stone B, double Time) { Vector dif = B.Position - A.Position; double len = Math.Max(dif.Length, A.Radius + B.Radius); Vector force = dif * (Time * Settings.Current.StoneRepelForce / (len * len * len)); A.Velocity -= force; B.Velocity += force; }
/// <summary> /// Tries to get a stone for the given entry. Returns false if none exists in this world. /// </summary> public bool TryGetStone(Entry Entry, out Stone Stone) { return this._Stones.TryGetValue(Entry, out Stone); }
/// <summary> /// Makes the given stone in this world a leader stone, allowing it to admit new stones for /// adjacent entries in the associated domain. /// </summary> public void MakeLeader(Stone Stone) { _Leader leader = new _Leader(); if (leader.Reset(this, Stone)) this._Leaders.Add(Stone, leader); }
/// <summary> /// Inserts a stone for the given entry. There must not be any existing stones for the entry. /// </summary> public void Insert(Stone Stone) { this._Introduce(Stone, Stone.Entry); }
/// <summary> /// Inserts a stone for the given entry. There must not be any existing stones for the entry. /// </summary> public Stone Insert(Entry Entry, Vector Position, Vector Velocity) { Stone stone = new Stone(Entry); stone.Position = Position; stone.Velocity = Velocity; this._Introduce(stone, Entry); return stone; }
/// <summary> /// Determines whether this world contains the given stone. /// </summary> public bool ContainsStone(Stone Stone) { return this._Stones.ContainsValue(Stone); }
/// <summary> /// Updates this leader state by the given amount of time. Returns true if a new stone was introduced. /// </summary> public bool Update(World World, Stone Stone, double Time, double Expansion, out Stone Introduced) { Vector dir; if (World._Pressure(Stone, out dir) < this.PressureThreshold) { if (World.ContainsEntry(this.Next)) { this.Next = GetNext(World, Stone); if (this.Next == null) { Introduced = null; return true; } } Introduced = new Stone(this.Next); Introduced.Position = Stone.Position; double dirlen = dir.Length; if (dirlen > 0.001) { dir = dir.Normal; Introduced.Velocity = Stone.Velocity + dir * Settings.Current.StoneIntroductionSpeed; Introduced.Position += dir * Stone.Radius; } _Tweak(this.Next, ref Introduced.Position); return true; } else { this.PressureThreshold *= Expansion; this.Timeout -= Time; Introduced = null; return false; } }
/// <summary> /// Inserts a STONE into a ZONE. /// </summary> private void _Insert(Stone Stone, ZoneIndex Index) { List<Stone> zone; if (!this._Zones.TryGetValue(Index, out zone)) this._Zones[Index] = zone = new List<Stone>(); zone.Add(Stone); }
/// <summary> /// Draws a stone along with its "Next" link. /// </summary> public static void DrawStone(Render Render, Stone Stone, double Extent) { Color4 fillcolor; Color4 bordercolor; Color4 linecolor; Color4 highlightcolor; double numbersize = Settings.Current.StoneNumberSize; uint selection = Stone.GetSelectionIndex(Stone); if (selection != uint.MaxValue) { selection = (uint)Stone.Selection.Length - selection; float glow = (float)Math.Sin((Stone.SelectionPulsePhase + selection / Settings.Current.StonePulseLength) * Math.PI * 2.0) * 0.5f + 0.5f; fillcolor = Settings.Current.StoneFillColor.GetSelected(glow); bordercolor = Settings.Current.StoneBorderColor.GetSelected(glow); highlightcolor = Settings.Current.StoneHighlightColor.GetSelected(glow); linecolor = (selection != 0) ? bordercolor : Settings.Current.StoneBorderColor.GetUnselected(); if (selection != 0 && Stone.Next != null && Stone.Next != Stone) { Vector markerpos = Stone.Position + (Stone.Next.Position - Stone.Position).Normal.Cross * (Stone.Radius + 0.5); Atlas.DrawNumber(Render, selection, new Color4(1.0f, 0.4f, 0.4f, 0.7f), markerpos, numbersize, numbersize); } } else { fillcolor = Settings.Current.StoneFillColor.GetUnselected(); bordercolor = Settings.Current.StoneBorderColor.GetUnselected(); highlightcolor = Settings.Current.StoneHighlightColor.GetUnselected(); linecolor = bordercolor; } double size = Math.Max(1.0, Extent * 0.01); float light = (float)Math.Max(0.0, Math.Min(1.0, (Extent - 50.0) / 100.0)); fillcolor = fillcolor.Mix(light, highlightcolor); Atlas.DrawFilledCircle(Render, fillcolor, Stone.Position, Stone.Radius * size); if (Extent < 120.0) { if (Stone.Next != null && Stone.Next != Stone) { Vector dif = Stone.Next.Position - Stone.Position; double len = dif.Length; Vector dir = dif / len; Vector start = Stone.Position + dir * Stone.Radius; double linklen = len - Stone.Radius - Stone.Next.Radius; if (linklen > 0.0) { double linkwidth = Settings.Current.LinkWidth; if (linklen >= Settings.Current.LinkMinimumArrowLength) Atlas.DrawArrow(Render, linecolor, start, dir, linklen, linkwidth); else Atlas.DrawLine(Render, linecolor, start, dir, linklen, linkwidth); } } Atlas.DrawCircle(Render, bordercolor, Stone.Position, Stone.Radius); Atlas.DrawNumber(Render, Stone.Entry.Value, (Color4)Settings.Current.StoneNumberColor, Stone.Position, numbersize, numbersize * 0.8); } }
/// <summary> /// Gets the selection index for the given stone. /// </summary> public static uint GetSelectionIndex(Stone Stone) { return Stone._SelectionIndex; }