internal override List <Target> GenerateTargets(UserSocket floating) { // copied from Lined.GenerateDefaultTargets and modified List <Target> targets = new List <Target>(); // add all of the vertices // note that using m_intDefinedVertices rather than Vertices.Count omits the last closing vertex if closed for (int index = 0; index <= m_DefinedVertices - 1; index++) { targets.Add(new Target(this, Vertices[index], Target.Types.Vertex, floating, Target.Priorities.Vertex, shapeIndex: index)); } // check the lines for (int index = 0; index <= Vertices.Count - 2; index++) { // it is relatively quick to check if the point is close enough to the bounding rectangle for the arc RectangleF bounds = new RectangleF(Vertices[index].X, Vertices[index].Y, 0, 0); Geometry.Extend(ref bounds, Vertices[index + 1]); Geometry.Extend(ref bounds, m_ControlPoints[index * 2]); Geometry.Extend(ref bounds, m_ControlPoints[index * 2 + 1]); if (Geometry.DirtyDistance(floating.Centre, bounds) <= Target.MaximumInterestDistance) { float maximumError = MaximumTError(index) * 3; // doesn't need to be so precise, because the target point release be on the Bezier // said the user probably can't tell if it is a little bit short or long Target create = GenerateLineTargetOnOneBezier(floating, GetSingleCurve(index), targets, maximumError, index); if (create != null) { targets.Add(create); } } } return(targets); }
internal override List <UserSocket> GetPointsWhichSnapWhenMoving() { if (!AutoTargets) { return(null); } List <UserSocket> list = (from pt in Vertices select UserSocket.CreateForPoint(pt)).ToList(); list.Add(UserSocket.CreateForPoint(Middle())); return(list); }
public override void CopyFrom(Datum other, CopyDepth depth, Mapping mapID) { base.CopyFrom(other, depth, mapID); UserSocket socket = (UserSocket)other; m_Centre = socket.m_Centre; m_Exit = socket.m_Exit; Options = socket.Options; Classification = socket.Classification; m_Bounds = RectangleF.Empty; }
// base does Centre and Middle internal override List <Target> GenerateTargets(UserSocket floating) { if (!AutoTargets) { return(null); } List <Target> targets = new List <Target>(); Lined.AddLineTargets(this, targets, floating, m_Bounds.GetPoints(), true); targets.Add(new Target(this, Centre, Target.Types.Centre, floating)); return(targets); }
internal override List <UserSocket> GetPointsWhichSnapWhenMoving() { if (!AutoTargets) { return(null); } List <UserSocket> list = new List <UserSocket>(); foreach (PointF pt in m_Bounds.GetPoints()) { list.Add(UserSocket.CreateForPoint(pt)); } list.Add(UserSocket.CreateForPoint(Middle())); return(list); }
public float RotationRequired(UserSocket other) { // rotation required of this socket to make it fit other socket. 0 means they match or one or both don't care about angle if (m_Exit.IsEmpty) { return(0); } if (other.ExitVector.IsEmpty) { return(0); } float angle = other.ExitVector.VectorAngle() - m_Exit.VectorAngle() - Geometry.ANGLE180; // angle180 on end, because they are expected to be OPPOSITE return(Geometry.NormaliseAnglePlusMinus180(angle)); }
public bool GenderCompatible(UserSocket other) { if ((Options & OptionsEnum._Gender) == OptionsEnum._Gender) { return(true); // this shape doesn't care } if ((other.Options & OptionsEnum._Gender) == OptionsEnum._Gender) { return(true); // other shape doesn't care } if ((Options & OptionsEnum._Gender) == (other.Options & OptionsEnum._Gender)) { return(false); // genders same- fails } return(true); }
public bool AngleCompatibile(UserSocket other) { // vectors must be opposite (apart from rounding error), if both defined if (m_Exit.IsEmpty) { return(true); } if (other.ExitVector.IsEmpty) { return(true); } float angle = Geometry.AbsoluteAngularDifference(m_Exit.VectorAngle(), other.ExitVector.VectorAngle()); if (angle > Geometry.ANGLE180 - Geometry.NEGLIGIBLEANGLESMALL) { return(true); } return(false); }
// a more convenient constructor for shapes when generating targets: // might also want to store the source socket eventually public Target(Shape shape, PointF position, Types type, UserSocket source, Priorities priority = Priorities.Standard, float intersectionCertainty = Geometry.PI / 2, int shapeIndex = -1) { // ObjSource if the moving item for which targets have been requested // -1 is used as the default shape index to make it obvious when it was not defined (0 is usually valid) Shape = shape; Position = position; Priority = priority; Type = type; Debug.Assert(priority == Priorities.Intersection || intersectionCertainty == Geometry.ANGLE90); // certainty has no effect unless it is an intersection if (type == Types.Intersection) { Uncertainty = Geometry.ANGLE90 - intersectionCertainty; } if (source != null) { Distance = Geometry.DistanceBetween(source.Centre, position); // otherwise Distance left as 0. Not used for real snapping targets, but can be used for some dummy ones } ShapeIndex = shapeIndex; if (type == Types.GrabSpot && !(this is ForGrabSpot)) { throw new ArgumentException(nameof(type)); } }
public const float MAXIMUMSNAPROTATION = Geometry.ANGLE45 / 2; // most to autorotate a moving shape internal override List <Target> GenerateTargets(UserSocket floating) { List <Target> list = new List <Target>(); EnsureSocketList(); foreach (UserSocket socket in m_Sockets) { if ((socket.Options & UserSocket.OptionsEnum.ShapeSnap) > 0) { // if they aren't compatible we still generate the shape, but mark it as failed Target newTarget = new Target(this, socket.Centre, Target.Types.Vertex, floating, Target.Priorities.Vertex); if (!floating.ClassCompatible(socket) || !floating.GenderCompatible(socket)) { newTarget.Mismatch = true; } else { float angle = floating.RotationRequired(socket); if (Math.Abs(angle) > MAXIMUMSNAPROTATION) { newTarget = null; // angle so far out it is not a match at all } else if (angle != 0) { // can autorotate newTarget.RotationRequired = angle; } } if (newTarget != null) { list.Add(newTarget); } } } return(list); }
internal override List <Target> GenerateTargets(UserSocket floating) { return(base.GenerateTargetsDefault(floating, false)); }
internal override List <Target> GenerateTargets(UserSocket floating) { return(MaskShape.GenerateTargets(floating)); }
internal override List <Target> GenerateTargets(UserSocket floating) => Element.GenerateTargets(floating);
internal override List <Target> GenerateTargets(UserSocket floating) => base.GenerateTargetsDefault(floating, Closed());
public bool ClassCompatible(UserSocket other) { return(string.IsNullOrEmpty(Classification) || string.IsNullOrEmpty(other.Classification) || Classification == other.Classification); }
internal override List <Target> GenerateTargets(UserSocket floating) { return(new List <Target> { new Target(this, m_Centre, Target.Types.Centre, floating) }); }