public TemporaryVertex(EdgePlacerBehavior be)
            {
                _behavior = be;
                var target     = _behavior.Target;
                var myPosition = _behavior.Holder.GetPosition();

                _behavior._vertices.Add(_behavior.CreateVertex(target.Entity != null && Vector3D.DistanceSquared(myPosition, target.Position) < ClickDistSq
                    ? target.Position
                    : myPosition));
                EdgePlacerSystem.AnnotateNodes(_behavior.Graph, _behavior._vertices);
            }
        private bool ValidatePlace(IList <string> errors, bool testPermissions)
        {
            var player = MyAPIGateway.Players.GetPlayerControllingEntity(Holder);

            if (player == null)
            {
                return(false);
            }

            if (!TargetIsStatic || PlacedDefinition == null)
            {
                errors?.Add("Can't place node on dynamic physics");
                return(false);
            }

            var vert = CreateVertex(Target.Position);

            if (testPermissions)
            {
                if (!player.HasPermission(vert.Position, MyPermissionsConstants.Build))
                {
                    errors?.Add("You cannot build here");
                    return(false);
                }

                foreach (var t in _vertices)
                {
                    if (!player.HasPermission(t.Position, MyPermissionsConstants.Build))
                    {
                        errors?.Add("You cannot build here");
                        return(false);
                    }
                }
            }

            if (_vertices.Count == 0)
            {
                return(true);
            }
            using (new TemporaryVertex(this))
            {
                return(EdgePlacerSystem.ValidatePath(PlacedDefinition, Graph, _vertices, errors));
            }
        }
        private bool ValidateDeconstruct(out string err, bool testPermission)
        {
            err = null;
            var entity = Target.Entity?.Components.Get <BendyShapeProxy>()?.Owner ?? Target.Entity;

            if (entity == null || entity.Closed)
            {
                return(false);
            }
            var player = MyAPIGateway.Players.GetPlayerControllingEntity(Holder);

            if (player == null)
            {
                return(false);
            }
            if (!testPermission ||
                player.HasPermission(entity.GetPosition(), MyPermissionsConstants.QuickDeconstruct))
            {
                return(EdgePlacerSystem.ValidateQuickRemove(player, entity, out err));
            }
            err = "You cannot quick deconstruct here";
            return(false);
        }
 public override void Init(MyEntity holder, MyHandItem item, MyHandItemBehaviorDefinition definition)
 {
     base.Init(holder, item, definition);
     Definition       = (EdgePlacerBehaviorDefinition)definition;
     PlacedDefinition = EdgePlacerSystem.DefinitionFor(Definition.Placed);
 }
        protected override void Hit()
        {
            if (!IsLocallyControlled)
            {
                return;
            }
            try
            {
                Cleanup();
                switch (ActiveAction)
                {
                case MyHandItemActionEnum.Tertiary:
                {
                    if (_vertices.Count == 0)
                    {
                        return;
                    }
                    if (_hintInfo == null)
                    {
                        _hintInfo = MyAPIGateway.Utilities.CreateNotification("");
                    }
                    var sb = new StringBuilder();

                    if (!TargetIsStatic)
                    {
                        sb.AppendLine("Can't place node on dynamic physics");
                    }
                    else
                    {
                        using (new TemporaryVertex(this))
                            if (_vertices.Count >= 2)
                            {
                                var jointData = EdgePlacerSystem.ComputeEdgeParameters(_vertices[_vertices.Count - 2], _vertices[_vertices.Count - 1]);
                                sb.Append($"Length {jointData.Length:F1} m");
                                if (PlacedDefinition != null && jointData.Length > PlacedDefinition.Distance.Max)
                                {
                                    sb.AppendLine($" >= {PlacedDefinition.Distance.Max:F1} m");
                                }
                                else if (PlacedDefinition != null && jointData.Length < PlacedDefinition.Distance.Min)
                                {
                                    sb.AppendLine($" <= {PlacedDefinition.Distance.Min:F1} m");
                                }
                                else
                                {
                                    sb.AppendLine();
                                }
                                if (jointData.BendRadians.HasValue)
                                {
                                    var b = jointData.BendRadians.Value;
                                    sb.Append($"Curve {b * 180 / Math.PI:F0}º");
                                    if (PlacedDefinition != null && b > PlacedDefinition.MaxAngleRadians)
                                    {
                                        sb.AppendLine($" >= {PlacedDefinition.MaxAngleDegrees}º");
                                    }
                                    else
                                    {
                                        sb.AppendLine();
                                    }
                                }

                                if (jointData.Grade.HasValue)
                                {
                                    var g = jointData.Grade.Value;
                                    sb.Append($"Grade {g * 100:F0}%");
                                    if (PlacedDefinition != null && Math.Abs(g) > PlacedDefinition.MaxGradeRatio)
                                    {
                                        sb.AppendLine($" {(g < 0 ? "<= -" : ">= ")}{PlacedDefinition.MaxGradeRatio * 100:F0}%");
                                    }
                                    else
                                    {
                                        sb.AppendLine();
                                    }
                                }
                            }
                    }

                    _hintInfo.Text = sb.ToString();
                    _hintInfo.Show();     // resets alive time + adds to queue if it's not in it
                    return;
                }

                case MyHandItemActionEnum.Secondary:
                {
                    _vertices.Clear();
                    var entity = Target.Entity?.Components.Get <BendyShapeProxy>()?.Owner ?? Target.Entity;
                    var dynCon = entity?.Components.Get <BendyComponent>();
                    if (dynCon == null || dynCon.Graph != Graph)
                    {
                        return;
                    }
                    EdgePlacerSystem.RaiseRemoveEdge(Holder.EntityId, entity.EntityId);
                    return;
                }

                case MyHandItemActionEnum.Primary:
                {
                    _vertices.Add(CreateVertex(Target.Position));
                    if (_vertices.Count < 2)
                    {
                        return;
                    }
                    var pts = _vertices.Select(x => x.Position).ToArray();
                    _vertices.RemoveRange(0, _vertices.Count - 1);
                    EdgePlacerSystem.RaisePlaceEdge(new EdgePlacerSystem.EdgePlacerConfig()
                        {
                            EntityPlacing = Holder.EntityId,
                            Placed        = Definition.Placed
                        }, pts
                                                    );
                    return;
                }

                case MyHandItemActionEnum.None:
                default:
                    return;
                }
            }
            finally
            {
                Cleanup();
            }
        }
        protected override bool Start(MyHandItemActionEnum action)
        {
            if (!IsLocallyControlled)
            {
                return(false);
            }
            var player = MyAPIGateway.Players.GetPlayerControllingEntity(Holder);

            if (player == null)
            {
                return(false);
            }
            if (Target.Entity == null || Vector3D.DistanceSquared(Target.Position, Holder.GetPosition()) > ClickDistSq)
            {
                player.ShowNotification($"Too far away from the chosen point.  Click closer to yourself.", 2000, null, new Vector4(1, 0, 0, 1));
                return(false);
            }

            switch (action)
            {
            case MyHandItemActionEnum.Tertiary:
                return(_vertices.Count > 0);

            case MyHandItemActionEnum.Secondary:
                string err;
                if (ValidateDeconstruct(out err, true))
                {
                    return(true);
                }
                if (!string.IsNullOrEmpty(err))
                {
                    player.ShowNotification(err, 2000, null,
                                            new Vector4(1, 0, 0, 1));
                }
                if (_vertices.Count > 0 && Vector3D.DistanceSquared(_vertices[_vertices.Count - 1].Position, Target.Position) < 1)
                {
                    _vertices.RemoveAt(_vertices.Count - 1);
                }
                return(false);

            case MyHandItemActionEnum.Primary:
                if (_vertices.Count == 1 && MyAPIGateway.Input.IsKeyDown(MyKeys.Shift))
                {
                    var originalStart = _vertices[0];
                    var lastVertex    = CreateVertex(Target.Position);
                    _vertices.Add(lastVertex);
                    EdgePlacerSystem.AnnotateNodes(Graph, _vertices);
                    var curveData = new LongPlanData(this, _vertices[0], _vertices[1]);
                    var jointData = EdgePlacerSystem.ComputeEdgeParameters(_vertices[0], _vertices[_vertices.Count - 1]);
                    {
                        _tmpMessages.Clear();
                        if (jointData.BendRadians.HasValue)
                        {
                            var angle = jointData.BendRadians.Value / curveData.Count;
                            if (angle > RailConstants.LongToleranceFactor * PlacedDefinition.MaxAngleRadians)
                            {
                                _tmpMessages.Add(
                                    $"Too curvy {angle * 180 / Math.PI:F0}º >= {RailConstants.LongToleranceFactor * PlacedDefinition.MaxAngleDegrees:F0}º");
                            }
                        }

                        // ReSharper disable once InvertIf
                        if (jointData.Grade.HasValue)
                        {
                            var grade = jointData.Grade.Value;
                            // ReSharper disable once InvertIf
                            if (Math.Abs(grade) > RailConstants.LongToleranceFactor * PlacedDefinition.MaxGradeRatio)
                            {
                                _tmpMessages.Add(
                                    $"Too steep {grade * 100:F0}% {(grade < 0 ? "<= -" : ">= ")}{RailConstants.LongToleranceFactor * PlacedDefinition.MaxGradeRatio * 100:F0}%");
                            }
                        }

                        if (_tmpMessages.Count > 0)
                        {
                            player.ShowNotification(string.Join("\n", _tmpMessages), 2000, null,
                                                    new Vector4(1, 0, 0, 1));
                            _tmpMessages.Clear();
                            _vertices.Clear();
                            _vertices.Add(originalStart);
                            return(false);
                        }
                    }
                    ComputeLong(_vertices[0], _vertices[1], curveData);
                    _vertices.RemoveAt(_vertices.Count - 1);
                    _tmpMessages.Clear();
                    player.ShowNotification($"Dividing into {curveData.Count} segments");
                    if (!ValidatePlace(_tmpMessages, true))
                    {
                        player.ShowNotification(string.Join("\n", _tmpMessages), 2000, null,
                                                new Vector4(1, 0, 0, 1));
                        _vertices.Clear();
                        _vertices.Add(originalStart);
                        return(false);
                    }

                    return(false);
                }

                _tmpMessages.Clear();
                if (ValidatePlace(_tmpMessages, true))
                {
                    return(true);
                }
                if (_tmpMessages.Count > 0)
                {
                    player.ShowNotification(string.Join("\n", _tmpMessages), 2000, null,
                                            new Vector4(1, 0, 0, 1));
                }
                _tmpMessages.Clear();
                return(false);

            case MyHandItemActionEnum.None:
            default:
                return(false);
            }
        }
        private void Render()
        {
            // hackfix because OnTargetEntityChanged is only called when the actual entity changed.
            SetTarget();

            var cam = MyCameraComponent.ActiveCamera;

            if (Graph == null || cam == null)
            {
                return;
            }

            {
                // draw node markers
                var proj = cam.GetProjectionSetup();
                proj.FarPlane = 100;
                var frust = new BoundingFrustumD(cam.GetViewMatrix() * proj.ProjectionMatrix);
                Graph.Nodes.OverlapAllFrustum(ref frust, (Node node, bool intersects) =>
                {
                    var color = _nodeColor;
                    var p1    = node.Position;
                    var p2    = node.Position + _nodeMarkerSize * node.Up;
                    MySimpleObjectDraw.DrawLine(p1, p2, _squareMaterial, ref color, _nodeWidth);
                });
            }

            {
                foreach (var k in _vertices)
                {
                    if (Vector3D.DistanceSquared(cam.GetPosition(), k.Position) < 100 * 100)
                    {
                        var color = _nodeColor;
                        var p1    = k.Position;
                        var p2    = k.Position + _nodeMarkerSize * k.Up;
                        MySimpleObjectDraw.DrawLine(p1, p2, _squareMaterial, ref color, _nodeWidth);
                    }
                }
            }

            if (Holder == null)
            {
                return;
            }

            {
                using (new TemporaryVertex(this))
                {
                    for (var i = 1; i < _vertices.Count; i++)
                    {
                        var nextVert    = _vertices[i];
                        var currentVert = _vertices[i - 1];

                        var nextMatrix    = ComputeVertexMatrix(nextVert, i);
                        var currentMatrix = ComputeVertexMatrix(currentVert, i - 1);

                        var color = PlacedDefinition == null || EdgePlacerSystem.VerifyEdge(PlacedDefinition, currentVert, nextVert, null)
                            ? _edgeColor
                            : _edgeColorBad;
                        const float vertShift = .1f;
                        if (Vector3D.DistanceSquared(currentMatrix.Translation, nextMatrix.Translation) > 30 * 30)
                        {
                            var curve = PrepareSphericalBez(currentMatrix, nextMatrix);
                            curve.Draw(color, upZero: currentVert.Up * vertShift, upOne: nextVert.Up * vertShift);
                        }
                        else
                        {
                            if (Math.Min(Vector3D.DistanceSquared(cam.GetPosition(), nextVert.Position),
                                         Vector3D.DistanceSquared(cam.GetPosition(), currentVert.Position)) > 100 * 100)
                            {
                                continue;
                            }
                            var curve = PrepareNormalBez(currentMatrix, nextMatrix);
                            curve.Draw(color, upZero: currentVert.Up * vertShift, upOne: nextVert.Up * vertShift);
                        }
                    }
                }
            }
        }
 private void NodesChanged(Node obj)
 {
     EdgePlacerSystem.AnnotateNodes(Graph, _vertices);
 }