/// <summary> /// Creates a polygon-based shape around a single point of a /// random size. The radiusMultiplier is used to increase the /// size to ensure that it connects to another shape (if that /// is the requirement). /// </summary> public static IPoly CreateShape( Junction parent, PointF point, float radius) { // Figure out the size of the square, as a radius. On each // retry, we make it a bit bigger. radius *= parent.Random.NextSingle(0.5f, 1.5f); // Get a random angle int count = parent.Random.Next(3, 7); float angle = parent.Random.NextSingle(0, Constants.PI2); // Create the square we are mapping PolyDefault poly = new PolyDefault(); for (int i = 0; i < count; i++) { poly.Add(new PointF( point.X + (float) Math.Cos(angle) * radius, point.Y + (float) Math.Sin(angle) * radius)); angle += Constants.PI2 / count; } // Return the results return poly; }
/// <summary> /// Constructs the junction node's shape and sets internal /// structures to match the new shape. /// </summary> public void Create(Junction junction) { // Figure out the basic points by creating an array // radials, which is determined by the random number generator. int radialCount = junction.Random.Next(8, 32); LinkedList<PointF> internalPoints = new LinkedList<PointF>(); // Populate the radial points for (int i = 0; i < radialCount; i++) { // Create the length from a random number in meters. float length = junction.Random.NextSingle(50, 250); // Figure out the angle we are calculating for the // internal shape, then use that to calculate the // internal point. These points are relative to (0,0). float angle = (float) Math.PI * 2 * i / radialCount; float px = (float) Math.Cos(angle) * length; float py = (float) Math.Sin(angle) * length; internalPoints.Add(new PointF(px, py)); } // TODO: Do some fractal generation // TODO add solid clutter // Create a polygon from the two results. The external // then has the internal taken out of the middle to // represent the "hollow" space of the polygon. IPoly internalPolygon = CreatePolygon(internalPoints); // Set the polygons in the junction junction.InternalShape = internalPolygon; }
public GtkTunnelerCanvas() { // Connect the events Events |= Gdk.EventMask.Button1MotionMask | Gdk.EventMask.Button2MotionMask | Gdk.EventMask.ButtonPressMask | Gdk.EventMask.ButtonReleaseMask | Gdk.EventMask.VisibilityNotifyMask | Gdk.EventMask.PointerMotionMask | Gdk.EventMask.PointerMotionHintMask; ExposeEvent += OnExpose; ConfigureEvent += OnConfigure; ButtonReleaseEvent += OnButtonRelease; MotionNotifyEvent += OnMotionNotify; // Create the root node with a random seed. We also set // the JunctionManage which triggers the update threads // and set it for the user. rootJunction = new Junction(); junctionManager.Junction = rootJunction; }
/// <summary> /// Selects a random segment factory. For a given seed, this /// will always return the same segment. /// </summary> public static ISegmentFactory ChooseSegmentFactory( Junction childJunction) { return new SimpleSegmentFactory(); }
/// <summary> /// Creates a segment from a child junction to its parent. /// </summary> public Segment Create( Junction child, PointF childPoint, double distance) { // Sanity checking if (child.ParentJunction == null) throw new Exception("Cannot generate with a null parent"); // Get the distance between the two points Segment segment = new Segment(); Junction parent = child.ParentJunction; // Add the two end points CenterPointList points = new CenterPointList(); points.Add(new CenterPoint(0, 0)); points.Add(new CenterPoint(childPoint)); // Split apart the line #if DEBUG Stopwatch stopwatch = Stopwatch.StartNew(); #endif // Stagger (fractalize) the points Geometry.StaggerPoints(points, parent.Random, Constants.MinimumSegmentDistance, Constants.StaggerSegmentPasses); #if DEBUG // Show timing information stopwatch.Stop(); Log.Debug("Fractal Time: {0}", stopwatch.Elapsed); stopwatch.Reset(); stopwatch.Start(); #endif // Once we generated a set of center points, we go through // and add a randomly-sized and angled square to each // point, unioning the results to get the shape of the // entire segment. IPoly shape = null; float log = 0; if (distance > 0) log = (float) Math.Log(distance) * 10; foreach (CenterPoint point in points) { // Keep track of our retries int retry = 1; while (true) { // Create a shape IPoly p = Geometry.CreateShape(parent, point.Point, retry * (Constants.SegmentAverageWidth - log)); // If we have no shape, this is automatically // included. if (shape == null) { shape = p; break; } // We have another shape, so we want to build up // an intersection to make sure they are touching. if (!shape.HasIntersection(p)) { // If there is no intersection, then we need // to try again and make the radius range larger. retry++; continue; } // Make a union of the two shapes IPoly union = shape.Union(p); // See if we have two shapes if (union.InnerPolygonCount > 1) { // We didn't quite reach each other retry++; continue; } // Set the new shape shape = union; break; } } // Remove both junction shapes from this //shape = shape.Difference(parent.InternalShape); //shape = shape.Difference(child.InternalShape); #if DEBUG // Show timing information stopwatch.Stop(); Log.Debug(" Shape Time: {0}", stopwatch.Elapsed); stopwatch.Reset(); stopwatch.Start(); #endif // Add the shape to the list segment.InternalShape = shape; // Set up the center line and optimize it segment.CenterPoints.AddAll(points); segment.CenterPoints.Optimize(); #if DEBUG // Show timing information stopwatch.Stop(); Log.Debug("Optimze Time: {0}", stopwatch.Elapsed); #endif // Return the results return segment; }
/// <summary> /// Swaps the contents of one junction with another, moving as /// appropriate. /// </summary> private void SwitchJunctionContents( Junction oldJunction, Junction newJunction) { // Get our segment Segment s = oldJunction.GetSegment(newJunction); if (s == null) { throw new Exception( "Cannot handle switching to non-continous segments"); } PointF translate = s.ChildJunctionPoint; // Get a list of mobiles, including the player, around the // player. JunctionManagerPreload oldPreload = preloadedJunctions[oldJunction]; JunctionManagerPreload newPreload = preloadedJunctions[newJunction]; if (oldPreload == null) throw new Exception("oldPreload is null"); if (newPreload == null) throw new Exception("newPreload is null"); IList<Mobile> list = oldPreload.Physics.GetMobiles( State.Player.Point, Constants.OverlapConnectionDistance); foreach (Mobile m in list) { // Kill them in the current engine m.PhysicsBody.Lifetime.IsExpired = true; oldPreload.Physics.Remove(m); // Keep the old body and force the mobile to recreate // it by getting the new one and then setting the // internal state. Body oldBody = m.PhysicsBody; m.ClearPhysicsBody(); Body newBody = m.PhysicsBody; newBody.State.Position.Linear.X = oldBody.State.Position.Linear.X - translate.X; newBody.State.Position.Linear.Y = oldBody.State.Position.Linear.Y - translate.Y; newBody.State.Position.Angular = oldBody.State.Position.Angular; newBody.State.Velocity.Linear.X = oldBody.State.Velocity.Linear.X; newBody.State.Velocity.Linear.Y = oldBody.State.Velocity.Linear.Y; newBody.State.Velocity.Angular = oldBody.State.Velocity.Angular; // Add it in the new engine newPreload.Physics.Add(m); } }
/// <summary> /// The internal version of the building that functions while /// thread-safe. /// </summary> private void BuildConnectionsLocked() { // Don't bother if we have connections already if (builtConnections) return; // Sanity checking if (isBuildingConnections) throw new Exception("Building connections is not reentrant"); isBuildingConnections = true; // Make sure our shapes are built to keep the order consistent BuildShapes(); // Keep track of the overlap detection code LinkedList<IPoly> overlaps = new LinkedList<IPoly>(); // Process the parent element if (ParentJunction != null) { // Find our input segment Segment ps = ParentJunction.GetSegment(this); if (ps == null) throw new Exception("We got a null segment from parent"); // Add the parent segment with swapped directions ps = ps.Swap(); segments.Add(ps); // Add to the overlap, but don't bother checking (we // are guarenteed to the first). CheckOverlapIntersection(overlaps, ps); } // We want to create a random number of connections int connectionCount = Random.Next( Constants.BuildMinimumConnections, Constants.BuildMaximumConnections); // Go through each connection for (int i = 0; i < connectionCount; i++) { // Build up a connection in a random direction float angle = Random.NextSingle(0, 2 * (float) Math.PI); float length = Random.NextSingle( Constants.MinimumConnectionDistance, Constants.MaximumConnectionDistance); // Junction points are relative to the parent node PointF point = new PointF( (float) Math.Cos(angle) * length, (float) Math.Sin(angle) * length); // Create a new junction at this point Junction junction = new Junction(Random.Next()); junction.ParentJunction = this; junction.BuildShapes(); // Get the segment factory and create the segment ISegmentFactory isf = FactoryManager.ChooseSegmentFactory(junction); Segment segment = isf.Create(junction, point, Distance); segment.ParentJunction = this; segment.ChildJunction = junction; segment.ChildJunctionPoint = point; // Set the junction's distance junction.Distance = Distance + segment.CenterPoints.MaximumRelativeDistance; // Check and add the overlap if (CheckOverlapIntersection(overlaps, segment)) { // We intersect Log.Debug("Rejection because segments overlap"); continue; } // Add some clutter IClutterFactory icf = FactoryManager.ChooseClutterFactory(Random); icf.Create(segment); // Add it to the segments segments.Add(segment); } // Create the physics shapes if (GeneratePhysics) { #if DEBUG Stopwatch stopwatch = Stopwatch.StartNew(); #endif BuildCombinedShape(); #if DEBUG // Show timing information stopwatch.Stop(); Log.Debug(" Physics Shape: {0}", stopwatch.Elapsed); stopwatch.Reset(); stopwatch.Start(); #endif CreateJunctionPhysics(0); #if DEBUG // Show timing information stopwatch.Stop(); Log.Debug("Physics Creation: {0}", stopwatch.Elapsed); #endif } // We are done builtConnections = true; isBuildingConnections = false; }
/// <summary> /// Retrieves a segment based on the child junction. /// </summary> public Segment GetSegment(Junction junction) { foreach (Segment s in segments) if (s.ChildJunction == junction) return s; return null; }
/// <summary> /// Creates a new preloaded stub and prepares for the /// background processing. /// </summary> public JunctionManagerPreload(Junction junction) { this.junction = junction; this.physics = new Physics(); }
/// <summary> /// Constructs the junction node's shape and sets internal /// structures to match the new shape. /// </summary> public void Create(Junction junction) { // We always start with one shape IPoly shape = Geometry.CreateShape(junction, PointF.Empty, Constants.JunctionAverageWidth); // Figure out the basic points by creating an array // simples, which is determined by the random number generator. int simpleCount = junction.Random.Next(8, 32); // Populate the simple points for (int i = 0; i < simpleCount; i++) { // Create the length from a random number in meters. float length = junction.Random .NextSingle(0, Constants.JunctionAverageWidth); // Figure out the angle we are calculating for the // internal shape, then use that to calculate the // internal point. These points are relative to (0,0). float angle = (float) Math.PI * 2 * i / simpleCount; float px = (float) Math.Cos(angle) * length; float py = (float) Math.Sin(angle) * length; int retry = 0; while (true) { // Create a shape at that point IPoly p = Geometry.CreateShape(junction, new PointF(px, py), retry * Constants.JunctionAverageWidth); // If we have no shape, this is automatically // included. if (shape == null) { shape = p; break; } // We have another shape, so we want to build up // an intersection to make sure they are touching. IPoly intersect = shape.Intersection(p); if (intersect.PointCount == 0) { // If there is no intersection, then we need // to try again and make the radius range larger. retry++; continue; } // Make a union of the two shapes IPoly union = shape.Union(p); // See if we have two shapes if (union.InnerPolygonCount > 1) { // We didn't quite reach each other retry++; continue; } // Set the new shape shape = union; break; } } // TODO: Do some fractal generation // TODO add solid clutter // Set the polygons in the junction junction.InternalShape = shape; }
/// <summary> /// Renders out a single junction to the canvas. /// </summary> private void RenderJunction( Context g, Junction junction, PointF point, int recursion) { // Get the shape we need to draw and draw it IPoly poly = junction.InternalShape; RenderPolygon(g, poly, point, new Color(0, 0, 0)); // See if we are recursive if (recursion-- > 0) { // Got through the shapes foreach (Segment s in junction.Segments) { // Render the junction RenderJunction(g, s.ChildJunction, s.ChildJunctionPoint, recursion); } // Got through the shapes foreach (Segment s in junction.Segments) { // Render the segment between the two RenderSegment(g, s); // Render and save the junction handles RenderJunctionHandle( g, s.ChildJunctionPoint, s.ChildJunction == junctionManager.Junction.ParentJunction, s.ChildJunction == hoverJunction); // Save the point junctionHandles.Add(s); } } }
/// <summary> /// Rebuilds the internal junction. /// </summary> public void Rebuild() { rootJunction = new Junction(); junctionManager.Junction = rootJunction; QueueDraw(); }
/// <summary> /// Triggered when the mouse is moved around the widget. /// </summary> public void OnMotionNotify(object sender, MotionNotifyEventArgs args) { // Get the x and y coordinates double x, y; if (args.Event.IsHint) { Gdk.ModifierType m; int ix, iy; args.Event.Window.GetPointer(out ix, out iy, out m); x = ix; y = iy; } else { x = args.Event.X; y = args.Event.Y; } double hs = handleSize / scale; x = x / scale - cx; y = y / scale - cy; // Go through the list and see if we are near a handle hoverJunction = null; foreach (Segment s in junctionHandles) { // Get the point, swapping it if needed Junction jn = s.ChildJunction; PointF p = s.ChildJunctionPoint; // See if we are in range if (p.X - hs <= x && x <= p.X + hs && p.Y - hs <= y && y <= p.Y + hs) { hoverJunction = jn; break; } } // Force the drawing QueueDraw(); }