Beispiel #1
0
        /// <summary>
        /// Inserts items via drag-and-drop</summary>
        /// <param name="e">DragEventArgs containing drag and drop event data</param>
        public void Insert(DragEventArgs e)
        {
            IEnumerable <ITimelineObject> itemCopies;

            if (m_timelineControl.DragDropObjects != null)
            {
                itemCopies = m_timelineControl.DragDropObjects;
                m_timelineControl.DragDropObjects = null;
            }
            else
            {
                itemCopies = ConvertDrop(e);
            }

            Point           clientPoint   = m_timelineControl.PointToClient(new Point(e.X, e.Y));
            PointF          mouseLocation = clientPoint;
            ITimelineObject dropTarget    = Pick(mouseLocation);

            List <TimelinePath> newSelection = new List <TimelinePath>();

            foreach (ITimelineObject droppedItem in itemCopies)
            {
                Insert(droppedItem, dropTarget);
                newSelection.Add(new TimelinePath(droppedItem));
            }
            Selection.SetRange(newSelection.AsIEnumerable <object>());
        }
Beispiel #2
0
        /// <summary>
        /// Performs custom actions on DragEnter events</summary>
        /// <param name="e">DragEventArgs containing event data</param>
        protected virtual void OnDragEnter(DragEventArgs e)
        {
            var converted = new List <ITimelineObject>(ConvertDrop(e));

            m_timelineControl.DragDropObjects = converted;

            Selection.Clear();
            foreach (IEvent draggedEvent in converted.AsIEnumerable <IEvent>())
            {
                Selection.Add(new TimelinePath(draggedEvent));
            }
        }
Beispiel #3
0
        /// <summary>
        /// Performs a picking operation and returns enumeration of Nodes intersecting the region</summary>
        /// <param name="pickRegion">Hit test region</param>
        /// <typeparam name="T">Type of objects intersecting pick region</typeparam>
        /// <returns>Objects of type T intersecting the pick region</returns>
        /// <remarks>The default implementation only returns intersecting Nodes, but derived
        /// classes can override this method to return Edges or EdgeRoutes as well.</remarks>
        public virtual IEnumerable <T> Pick <T>(Region pickRegion) where T : class
        {
            List <TNode> pickedGraphNodes = new List <TNode>();

            using (Graphics g = AdaptedControl.CreateGraphics())
            {
                RectangleF pickRect = pickRegion.GetBounds(g);
                pickRect = GdiUtil.InverseTransform(m_transformAdapter.Transform, pickRect);
                foreach (TNode node in m_graph.Nodes)
                {
                    RectangleF nodeBounds = m_renderer.GetBounds(node, g);
                    if (nodeBounds.IntersectsWith(pickRect))
                    {
                        pickedGraphNodes.Add(node);
                    }
                }
            }

            return(pickedGraphNodes.AsIEnumerable <T>());
        }
Beispiel #4
0
        /// <summary>
        /// Performs custom actions on validation Ending events</summary>
        /// <param name="sender">Validation context</param>
        /// <param name="e">Event args</param>
        protected override void OnEnding(object sender, EventArgs e)
        {
            var referenceValidator = DomNode.Cast<ReferenceValidator>();
            referenceValidator.Suspended = false;

            if (m_undoingOrRedoing)
            {
                foreach (var subgraph in m_subGraphs.Where(x => x.Dirty).OrderByDescending(s => s.Level))
                {
                    subgraph.Dirty = false; // just reset Dirty flag
                    foreach (var wire in subgraph.Wires)
                    {
                        // reset pin target
                        wire.InputPinTarget = null;
                        wire.OutputPinTarget = null;
                    }
                   
                
                    subgraph.UpdateGroupPinInfo();    
                    subgraph.OnChanged(EventArgs.Empty); // but notify the change
                }
                    
                foreach (var circuit in m_circuits)
                {
                    foreach (var wire in circuit.Wires)
                    {
                        // reset pin target
                        wire.InputPinTarget = null;
                        wire.OutputPinTarget = null;
                    }
                    circuit.Dirty = false;
                }
                    
                return;
            }

            var containersToCheck = new List<ICircuitContainer>();
            containersToCheck.AddRange(m_subGraphs.Where(g => g.Dirty).AsIEnumerable<ICircuitContainer>());
            containersToCheck.AddRange(m_circuits.Where(g => g.Dirty).AsIEnumerable<ICircuitContainer>());

            while (m_subGraphs.Any(n => n.Dirty) || m_circuits.Any(n => n.Dirty))
            {
                // inner subgraphs updated first
                foreach (var subgraph in m_subGraphs.OrderByDescending(s => s.Level))
                {
                    subgraph.IgnoreFanInOut = MovingCrossContainer;
                    subgraph.Update();
                    if (subgraph.IgnoreFanInOut)
                    {
                        subgraph.IgnoreFanInOut = false;
                        subgraph.Dirty = true;
                    }
                }
                UpdateWires(containersToCheck);
                MovingCrossContainer = false;

                foreach (var circuit in m_circuits)
                {
                    circuit.Update();
                }
            }

            // update group pin connectivity and other info from bottom up, for display purpose only
            foreach (var group in containersToCheck.AsIEnumerable<Group>().OrderByDescending(s => s.Level))
            {
                group.UpdateGroupPinInfo();
            }
   
            foreach (var subgraph in m_nodesInserted.Keys)
            {
                IEnumerable<Element> nodes = m_nodesInserted[subgraph];
                if (nodes.Any())
                {
                    var viewingContext = subgraph.Cast<ViewingContext>();
                    if (viewingContext.Control != null)
                    {
                        var subGraphPinAdapter = viewingContext.Control.As<GroupPinEditor>();
                        if (subGraphPinAdapter != null)
                            subGraphPinAdapter.AdjustLayout(nodes, EmptyEnumerable<GroupPin>.Instance, new Point(0, 0));
                    }
                }
            } 
        }
Beispiel #5
0
        private IList <GroupPinData> GetGroupPinChainData(D2dCircuitRenderer <TElement, TWire, TPin> circuitRender, Point worldOffset, D2dGraphics g)
        {
            var connection = DomNode.Cast <Wire>();
            var dataPoints = new List <GroupPinData>();

            if (circuitRender != null)
            {
                var relativePath = new List <ICircuitGroupType <TElement, TWire, TPin> >();

                // --- edge starts from the output pin
                var group = connection.OutputElement.As <ICircuitGroupType <TElement, TWire, TPin> >();
                foreach (var groupPin in connection.OutputPinSinkChain)
                {
                    var pt = circuitRender.GetPinPositionCenterY(group.Cast <TElement>(), groupPin.Index, false, g);
                    pt.Offset(worldOffset);
                    pt.Offset(circuitRender.WorldOffset(relativePath.AsIEnumerable <TElement>()));
                    relativePath.Add(group);
                    dataPoints.Add(new GroupPinData {
                        Pos = pt, Group = group, GroupPin = groupPin
                    });
                    if (!group.Expanded)
                    {
                        break;
                    }
                    group = groupPin.InternalElement.As <ICircuitGroupType <TElement, TWire, TPin> >();
                }

                dataPoints.Reverse(); // upward
                if (relativePath.Any())
                {
                    var startGroup = relativePath.Last();
                    if (startGroup.Expanded) // the edge starts from an expanded group pin, need to draw the virtual link
                    {
                        // the first data starts from sub-node/pin
                        var   firstGroupPin = dataPoints[0].GroupPin;
                        Point p0            = circuitRender.GetPinPositionCenterY(firstGroupPin.InternalElement.Cast <TElement>(), firstGroupPin.InternalPinIndex, false, g);
                        p0.Offset(worldOffset);
                        p0.Offset(circuitRender.WorldOffset(relativePath.AsIEnumerable <TElement>()));
                        dataPoints.Insert(0, new GroupPinData {
                            Pos = p0
                        });
                    }
                }
                else // edge starts from a non-expanded node
                {
                    Point p0 = circuitRender.GetPinPositionCenterY(connection.OutputElement.Cast <TElement>(), connection.OutputPin.Index, false, g);
                    p0.Offset(worldOffset);
                    dataPoints.Add(new GroupPinData {
                        Pos = p0
                    });
                }

                int numInputPoints = dataPoints.Count;
                dataPoints[dataPoints.Count - 1].IsReal = true;

                // --- edge ends at the input pin
                relativePath.Clear();
                group = connection.InputElement.As <ICircuitGroupType <TElement, TWire, TPin> >();
                foreach (var groupPin in connection.InputPinSinkChain)
                {
                    var pt = circuitRender.GetPinPositionCenterY(group.Cast <TElement>(), groupPin.Index, true, g);
                    pt.Offset(worldOffset);
                    pt.Offset(circuitRender.WorldOffset(relativePath.AsIEnumerable <TElement>()));
                    relativePath.Add(group);
                    dataPoints.Add(new GroupPinData {
                        Pos = pt, Group = group, GroupPin = groupPin
                    });
                    if (!group.Expanded)
                    {
                        break;
                    }
                    group = groupPin.InternalElement.As <ICircuitGroupType <TElement, TWire, TPin> >();
                }

                if (relativePath.Any())
                {
                    var lastGroupPin = dataPoints[dataPoints.Count - 1].GroupPin;
                    var lastGroup    = dataPoints[dataPoints.Count - 1].Group;
                    if (lastGroup.Expanded) // the edge ends at an expanded group pin, need to draw the virtual link to subnode
                    {
                        // the last data ends at sub-node/pin
                        Point pn = circuitRender.GetPinPositionCenterY(lastGroupPin.InternalElement.Cast <TElement>(), lastGroupPin.InternalPinIndex, true, g);
                        pn.Offset(worldOffset);
                        pn.Offset(circuitRender.WorldOffset(relativePath.AsIEnumerable <TElement>()));
                        dataPoints.Add(new GroupPinData {
                            Pos = pn
                        });
                    }
                }
                else // edge ends at a non-expanded node
                {
                    Point pn = circuitRender.GetPinPositionCenterY(connection.InputElement.Cast <TElement>(), connection.InputPin.Index, true, g);
                    pn.Offset(worldOffset);
                    dataPoints.Add(new GroupPinData {
                        Pos = pn
                    });
                }

                dataPoints[numInputPoints].IsReal = true;
            }

            return(dataPoints);
        }
Beispiel #6
0
        /// <summary>
        /// Creates an array of property descriptors that are associated with the adapted DomNode's
        /// DomNodeType. No duplicates will be in the array (based on the property descriptor's Name
        /// property).</summary>
        /// <returns>Array of property descriptors</returns>
        protected override System.ComponentModel.PropertyDescriptor[] GetPropertyDescriptors()
        {
            // Initialize property desciptors with the ones from the base class
            // If this is not done, the new property descriptors would be used instead of
            // rather than in addition to the ones defined in the schema
            List<System.ComponentModel.PropertyDescriptor> descriptors =
                new List<System.ComponentModel.PropertyDescriptor>(base.GetPropertyDescriptors());

            // Add ITransformable properties:
            // Translation, Rotation, Scale, RotatePivot, ScalePivot (if supported by this object)

            ITransformable node = this.Cast<ITransformable>();
            TransformationTypes transformType = node.TransformationType;

            NumericTupleEditor tupleEditor =
                new NumericTupleEditor(typeof(float), new string[] { "x", "y", "z" });
            NumericTupleEditor rotationTupleEditor =
                new NumericTupleEditor(typeof(float), new string[] { "x", "y", "z" });
            rotationTupleEditor.ScaleFactor = 360 / (2 * Math.PI); // Radians to Degrees

            string category = "Transform".Localize();
            // Check for transform types
            if ((transformType & TransformationTypes.Translation) != 0)
                descriptors.Add(
                    new AttributePropertyDescriptor(
                        "Translation", Schema.transformObjectType.translateAttribute, category, "Translation of Game Object along X, Y, and Z axes".Localize(),
                        false, tupleEditor));

            if ((transformType & TransformationTypes.Rotation) != 0)
                descriptors.Add(new AttributePropertyDescriptor(
                        "Rotation".Localize(), Schema.transformObjectType.rotateAttribute, category,
                        "Origin of Rotation transform relative to Game Object Translation".Localize(),
                        false, rotationTupleEditor));

            if ((transformType & TransformationTypes.Scale) != 0)
            {
                if ((transformType & TransformationTypes.UniformScale) == 0)
                    descriptors.Add(
                        new AttributePropertyDescriptor(
                            "Scale".Localize(),
                            Schema.transformObjectType.scaleAttribute, category,
                            "Scale of Game Object along X, Y, and Z axes".Localize(),
                            false, tupleEditor));
                else
                    descriptors.Add(
                        new AttributePropertyDescriptor(
                            "Uniform Scale".Localize(), Schema.transformObjectType.scaleAttribute,
                            category,
                            "Scale of Game Object uniformly along X, Y, and Z axes".Localize(),
                            false, new UniformArrayEditor<Single>()));
            }

            if ((transformType & TransformationTypes.Pivot) != 0)
                descriptors.Add(
                    new AttributePropertyDescriptor(
                        "Pivot".Localize(), Schema.transformObjectType.pivotAttribute, category,
                        "Origin of Rotation and scale transform relative to Game Object Translation".Localize(),
                        false, tupleEditor));

            // remove hidden properties
            HashSet<string> hiddenProps = (HashSet<string>)this.DomNode.Type.GetTag(SchemaLoader.HiddenProperties);
            if (hiddenProps != null)
            {
                List<PropertyDescriptor> removeList = new List<PropertyDescriptor>();
                foreach (AttributePropertyDescriptor propdescr in descriptors.AsIEnumerable<AttributePropertyDescriptor>())
                {
                    if (hiddenProps.Contains(propdescr.AttributeInfo.Name))
                    {
                        removeList.Add(propdescr);
                    }
                }

                foreach (PropertyDescriptor propDescr in removeList)
                    descriptors.Remove(propDescr);
            }

            return descriptors.ToArray();
        }
        /// <summary>
        /// Creates an array of property descriptors that are associated with the adapted DomNode's
        /// DomNodeType. No duplicates will be in the array (based on the property descriptor's Name
        /// property).</summary>
        /// <returns>Array of property descriptors</returns>
        protected override System.ComponentModel.PropertyDescriptor[] GetPropertyDescriptors()
        {
            // Initialize property desciptors with the ones from the base class
            // If this is not done, the new property descriptors would be used instead of
            // rather than in addition to the ones defined in the schema
            List <System.ComponentModel.PropertyDescriptor> descriptors =
                new List <System.ComponentModel.PropertyDescriptor>(base.GetPropertyDescriptors());

            // Add ITransformable properties:
            // Translation, Rotation, Scale, RotatePivot, ScalePivot (if supported by this object)

            ITransformable      node          = this.Cast <ITransformable>();
            TransformationTypes transformType = node.TransformationType;

            NumericTupleEditor tupleEditor =
                new NumericTupleEditor(typeof(float), new string[] { "x", "y", "z" });
            NumericTupleEditor rotationTupleEditor =
                new NumericTupleEditor(typeof(float), new string[] { "x", "y", "z" });

            rotationTupleEditor.ScaleFactor = 360 / (2 * Math.PI); // Radians to Degrees

            FloatArrayConverter converter         = new FloatArrayConverter();
            FloatArrayConverter rotationConverter = new FloatArrayConverter();

            rotationConverter.ScaleFactor = 360 / (2 * Math.PI); // Radians to Degrees


            string category = "Transform".Localize();

            // Check for transform types
            if ((transformType & TransformationTypes.Translation) != 0)
            {
                descriptors.Add(
                    new AttributePropertyDescriptor(
                        "Translation", Schema.gameObjectType.translateAttribute, category, "Translation of Game Object along X, Y, and Z axes".Localize(),
                        false, tupleEditor, converter));
            }

            if ((transformType & TransformationTypes.Rotation) != 0)
            {
                descriptors.Add(new AttributePropertyDescriptor(
                                    "Rotation".Localize(), Schema.gameObjectType.rotateAttribute, category,
                                    "Origin of Rotation transform relative to Game Object Translation".Localize(),
                                    false, rotationTupleEditor, rotationConverter));
            }

            if ((transformType & TransformationTypes.Scale) != 0)
            {
                if ((transformType & TransformationTypes.UniformScale) == 0)
                {
                    descriptors.Add(
                        new AttributePropertyDescriptor(
                            "Scale".Localize(),
                            Schema.gameObjectType.scaleAttribute, category,
                            "Scale of Game Object along X, Y, and Z axes".Localize(),
                            false, tupleEditor, converter));
                }
                else
                {
                    descriptors.Add(
                        new AttributePropertyDescriptor(
                            "Scale".Localize(), Schema.gameObjectType.scaleAttribute,
                            category,
                            "Scale of Game Object along X, Y, and Z axes".Localize(),
                            false, new UniformArrayEditor <Single>(), new UniformFloatArrayConverter()));
                }
            }

            if ((transformType & TransformationTypes.Pivot) != 0)
            {
                descriptors.Add(
                    new AttributePropertyDescriptor(
                        "Pivot".Localize(), Schema.gameObjectType.pivotAttribute, category,
                        "Origin of Rotation and scale transform relative to Game Object Translation".Localize(),
                        false, tupleEditor, converter));
            }


            // remove hidden properties
            HashSet <string> hiddenProps = (HashSet <string>) this.DomNode.Type.GetTag(SchemaLoader.HiddenProperties);

            if (hiddenProps != null)
            {
                List <PropertyDescriptor> removeList = new List <PropertyDescriptor>();
                foreach (AttributePropertyDescriptor propdescr in descriptors.AsIEnumerable <AttributePropertyDescriptor>())
                {
                    if (hiddenProps.Contains(propdescr.AttributeInfo.Name))
                    {
                        removeList.Add(propdescr);
                    }
                }

                foreach (PropertyDescriptor propDescr in removeList)
                {
                    descriptors.Remove(propDescr);
                }
            }


            return(descriptors.ToArray());
        }
Beispiel #8
0
        /// <summary>
        /// Inserts the data object into the context.
        /// Generic insert via IInstancingContext. Called from, for example, the standard paste command.</summary>
        /// <param name="insertingObject">Data to insert; e.g., System.Windows.Forms.IDataObject</param>
        public void Insert(object insertingObject)
        {
            IDataObject dataObject = (IDataObject)insertingObject;

            // use current document control to center the elements
            object[] items = dataObject.GetData(typeof(object[])) as object[];
            if (items == null)
            {
                return;
            }
            IEnumerable <DomNode> sourceDomNodes = PathsToDomNodes(items);

            DomNode[] nodeCopies  = DomNode.Copy(sourceDomNodes);
            var       itemSources = new List <ITimelineObject>(sourceDomNodes.AsIEnumerable <ITimelineObject>());
            var       itemCopies  = new List <ITimelineObject>(nodeCopies.AsIEnumerable <ITimelineObject>());

            TimelineDocument   document        = DomNode.Cast <TimelineDocument>();
            D2dTimelineControl timelineControl = document.TimelineControl;

            Rectangle clientRect  = m_timelineControl.VisibleClientRectangle;
            Point     clientPoint = new Point(
                clientRect.Left + clientRect.Width / 2,
                clientRect.Top + clientRect.Height / 2);

            CenterEvents(itemCopies.AsIEnumerable <IEvent>(), clientPoint, timelineControl.Transform);

            var sourceTargetPairs = new List <Tuple <ITimelineObject, ITimelineObject> >(itemSources.Count);

            for (int i = 0; i < itemSources.Count; i++)
            {
                sourceTargetPairs.Add(new Tuple <ITimelineObject, ITimelineObject>(itemSources[i], itemCopies[i]));
            }

            // Prepare the mapping of source objects to their tracks. These may be known already (from
            //  a previous Copy operation), but we can't be sure, so let's augment m_copyObjToTrack if
            //  possible.
            if (m_copyObjToTrack == null)
            {
                m_copyObjToTrack = new Dictionary <ITimelineObject, ITrack>();
            }
            foreach (var source in itemSources)
            {
                IGroup group;
                ITrack track;
                GetTrackAndGroup(source, out track, out group);
                if (track != null)
                {
                    m_copyObjToTrack[source] = track;
                }
            }

            // Guess where the user wants to paste. Priority:
            // 1. The timeline control's target (currently selected) track.
            ITrack targetTrack = timelineControl.TargetTrack != null ? (ITrack)m_timelineControl.TargetTrack.Last : null;

            // But only if it's visible.
            if (targetTrack != null)
            {
                if (!IsTrackVisible(targetTrack))
                {
                    targetTrack = null;
                }
            }
            // 2. The track in the center of the view.
            if (targetTrack == null)
            {
                ITimelineObject centerObject = Pick(clientPoint);
                IGroup          centerGroup;
                GetTrackAndGroup(centerObject, out targetTrack, out centerGroup);
            }
            // 3. The first visible track
            if (targetTrack == null)
            {
                foreach (IGroup group in Timeline.Groups)
                {
                    foreach (ITrack track in group.Tracks)
                    {
                        if (IsTrackVisible(track))
                        {
                            targetTrack = track;
                            break;
                        }
                    }
                    if (targetTrack != null)
                    {
                        break;
                    }
                }
            }

            Dictionary <ITimelineObject, ITrack> copiesToTracks = CreateTrackMappings(
                m_timelineDocument.Timeline, sourceTargetPairs, targetTrack, m_copyObjToTrack);

            foreach (ITimelineObject item in itemCopies)
            {
                // Not all items will have a track. The item could be a group, for example.
                ITrack dropTarget;
                copiesToTracks.TryGetValue(item, out dropTarget);
                Insert(item, dropTarget);
            }

            List <TimelinePath> newSelection = new List <TimelinePath>();

            foreach (ITimelineObject copy in itemCopies)
            {
                // Would need a way to get the path of ITimelineReference objects....
                if (TimelineControl.GetOwningTimeline(copy) != Timeline)
                {
                    throw new NotImplementedException("We haven't implemented the ability to insert timeline objects into a sub-document");
                }
                newSelection.Add(new TimelinePath(copy));
            }

            Selection.SetRange(newSelection.AsIEnumerable <object>());
        }
Beispiel #9
0
        /// <summary>
        /// Performs custom actions on validation Ending events</summary>
        /// <param name="sender">Validation context</param>
        /// <param name="e">Event args</param>
        protected override void OnEnding(object sender, EventArgs e)
        {
            var referenceValidator = DomNode.As <ReferenceValidator>();

            if (referenceValidator != null)
            {
                referenceValidator.Suspended = false;
            }


            if (m_undoingOrRedoing)
            {
                foreach (var subgraph in m_subGraphs.Where(x => x.Dirty).OrderByDescending(s => s.Level))
                {
                    subgraph.Dirty = false; // just reset Dirty flag
                    foreach (var wire in subgraph.Wires)
                    {
                        // reset pin target
                        wire.InputPinTarget  = null;
                        wire.OutputPinTarget = null;
                    }


                    subgraph.UpdateGroupPinInfo();
                    subgraph.OnChanged(EventArgs.Empty); // but notify the change
                }

                foreach (var circuit in m_circuits)
                {
                    foreach (var wire in circuit.Wires)
                    {
                        // reset pin target
                        wire.InputPinTarget  = null;
                        wire.OutputPinTarget = null;
                    }
                    circuit.Dirty = false;
                }

                return;
            }

            var containersToCheck = new List <ICircuitContainer>();

            containersToCheck.AddRange(m_subGraphs.Where(g => g.Dirty).AsIEnumerable <ICircuitContainer>());
            containersToCheck.AddRange(m_circuits.Where(g => g.Dirty).AsIEnumerable <ICircuitContainer>());

            while (m_subGraphs.Any(n => n.Dirty) || m_circuits.Any(n => n.Dirty))
            {
                // inner subgraphs updated first
                foreach (var subgraph in m_subGraphs.OrderByDescending(s => s.Level))
                {
                    subgraph.IgnoreFanInOut = MovingCrossContainer;
                    subgraph.Update();
                    if (subgraph.IgnoreFanInOut)
                    {
                        subgraph.IgnoreFanInOut = false;
                        subgraph.Dirty          = true;
                    }
                }
                UpdateWires(containersToCheck);
                MovingCrossContainer = false;

                foreach (var circuit in m_circuits)
                {
                    circuit.Update();
                }
            }

            // update group pin connectivity and other info from bottom up, for display purpose only
            foreach (var group in containersToCheck.AsIEnumerable <Group>().OrderByDescending(s => s.Level))
            {
                group.UpdateGroupPinInfo();
            }

            foreach (var subgraph in m_nodesInserted.Keys)
            {
                IEnumerable <Element> nodes = m_nodesInserted[subgraph];
                if (nodes.Any())
                {
                    var viewingContext = subgraph.Cast <ViewingContext>();
                    if (viewingContext.Control != null)
                    {
                        var subGraphPinAdapter = viewingContext.Control.As <GroupPinEditor>();
                        if (subGraphPinAdapter != null)
                        {
                            subGraphPinAdapter.AdjustLayout(nodes, EmptyEnumerable <GroupPin> .Instance, new Point(0, 0));
                        }
                    }
                }
            }
        }
Beispiel #10
0
        /// <summary>
        /// Groups the specified GameObjects</summary>
        /// <param name="gobs">GameObjects to be grouped</param>
        /// <remarks>Creates a new GameObjectGroup and moves all
        /// the GameObjects into it.</remarks>
        public ITransformableGroup Group(IEnumerable <object> gobs)
        {
            // extra check.
            if (!CanGroup(gobs))
            {
                return(null);
            }

            IGameObjectFolder root = null;
            var gameObjects        = new List <ITransformable>();

            foreach (var gameObject in gobs)
            {
                ITransformable trans = gameObject.As <ITransformable>();
                if (trans == null)
                {
                    continue;
                }
                var node = gameObject.As <DomNode>();
                if (node == null)
                {
                    continue;
                }

                gameObjects.Add(trans);
                var root1 = GetObjectFolder(node);
                if (root != null && root != root1)
                {
                    return(null);
                }
                root = root1;
            }

            // sort from shallowest in the tree to deepest. Then remove any nodes that
            // are already children of other nodes in the list
            gameObjects.Sort(
                (lhs, rhs) => { return(lhs.As <DomNode>().Lineage.Count().CompareTo(rhs.As <DomNode>().Lineage.Count())); });

            // awkward iteration here... Maybe there's a better way to traverse this list..?
            for (int c = 0; c < gameObjects.Count;)
            {
                var  n      = gameObjects[c].As <DomNode>();
                bool remove = false;
                for (int c2 = 0; c2 < c; ++c2)
                {
                    if (n.IsDescendantOf(gameObjects[c2].As <DomNode>()))
                    {
                        remove = true;
                        break;
                    }
                }

                if (remove)
                {
                    gameObjects.RemoveAt(c);
                }
                else
                {
                    ++c;
                }
            }

            // finally, we must have at least 2 valid objects to perform the grouping operation
            if (gameObjects.Count < 2)
            {
                return(null);
            }

            AABB groupBox = new AABB();

            foreach (var gameObject in gameObjects.AsIEnumerable <IBoundable>())
            {
                groupBox.Extend(gameObject.BoundingBox);
            }

            var group = root.CreateGroup();

            if (group == null)
            {
                return(null);
            }

            group.As <DomNode>().InitializeExtensions();

            // try to add the group to the parent of the shallowest item
            var  groupParent        = gameObjects[0].As <DomNode>().Parent.AsAll <IHierarchical>();
            bool addedToGroupParent = false;

            foreach (var h in groupParent)
            {
                if (h.AddChild(group))
                {
                    addedToGroupParent = true; break;
                }
            }
            if (!addedToGroupParent)
            {
                return(null);
            }

            // arrange the transform for the group so that it's origin is in the center of the objects
            var groupParentTransform = new Matrix4F();

            if (groupParent.Is <ITransformable>())
            {
                groupParentTransform = groupParent.As <ITransformable>().LocalToWorld;
            }

            Matrix4F invgroupParentTransform = new Matrix4F();

            invgroupParentTransform.Invert(groupParentTransform);
            group.Transform = Matrix4F.Multiply(new Matrix4F(groupBox.Center), invgroupParentTransform);

            Matrix4F invWorld = new Matrix4F();

            invWorld.Invert(group.Transform);

            // now try to actually add the objects to the group
            var hier = group.AsAll <IHierarchical>();

            foreach (var gameObject in gameObjects)
            {
                Matrix4F oldWorld = gameObject.LocalToWorld;

                bool addedToGroup = false;
                foreach (var h in hier)
                {
                    if (h.AddChild(gameObject))
                    {
                        addedToGroup = true;  break;
                    }
                }

                if (addedToGroup)
                {
                    gameObject.Transform = Matrix4F.Multiply(oldWorld, invWorld);
                }
            }

            return(group);
        }