public static List <Layer.Group> findItemsToUngroup(List <Layer.LayerItem> itemsToUngroup) { // create a search list that we will expend and to keep the original selection intact List <Layer.LayerItem> searchList = new List <Layer.LayerItem>(itemsToUngroup); List <Layer.Group> result = new List <Layer.Group>(itemsToUngroup.Count); // Search the top group of the tree, because this action only ungroup the top of the tree // The list of items to ungroup can also be a forest, so keep all the top of the trees for (int i = 0; i < searchList.Count; ++i) { Layer.LayerItem item = searchList[i]; // check if this item as a father group or is single (top item) if (item.Group == null) { // check if this item is a group that can be ungrouped or a simple item by casting it Layer.Group group = item as Layer.Group; // if it is a group that can be ungrouped and not already in the list, add it to the list if ((group != null) && group.CanUngroup && !result.Contains(group)) { result.Add(group); } } else if (!searchList.Contains(item.Group)) { // add it to the search list even if this father group is cannot be ungroup because a grand father // could be ungroupable above, so we need to do the exhaustive forest search searchList.Add(item.Group); } } return(result); }
private void replaceOneItem(LayerBrick layer, Layer.LayerItem itemToRemove, Layer.LayerItem itemToAdd) { // memorise the brick index order and the group of the old brick int oldItemIndex = -1; Layer.Group oldItemGroup = itemToRemove.Group; // first remove the item from its group, in case it belongs to a group // (so that later if someone ask all item of this group, it won't get this limbo item) if (itemToRemove.Group != null) { itemToRemove.Group.removeItem(itemToRemove); } // check if the item to remove is a group or a simple brick if (itemToRemove.IsAGroup) { List <Layer.LayerItem> bricksToRemove = (itemToRemove as Layer.Group).getAllLeafItems(); foreach (Layer.LayerItem item in bricksToRemove) { oldItemIndex = layer.removeBrick(item as LayerBrick.Brick); } // we alsways take the last index to be sure we don't keep an index bigger than the brick list // after removing all the parts of the group } else { oldItemIndex = layer.removeBrick(itemToRemove as LayerBrick.Brick); } // check if the item to add is a group or a simple brick if (itemToAdd.IsAGroup) { List <Layer.LayerItem> bricksToAdd = (itemToAdd as Layer.Group).getAllLeafItems(); // since we will add all the brick at the same index, iterating in the normal order // the insertion order will be reversed. So we reverse the list to make the insertion in correct order bricksToAdd.Reverse(); foreach (Layer.LayerItem item in bricksToAdd) { layer.addBrick(item as LayerBrick.Brick, oldItemIndex); } } else { layer.addBrick(itemToAdd as LayerBrick.Brick, oldItemIndex); } // then once the item has been added to the layer, add it also to the group if the old item had a group if (oldItemGroup != null) { oldItemGroup.addItem(itemToAdd); } // notify the part list view (after actually adding and deleting the bricks because the total map size need to be recomputed) MainForm.Instance.NotifyPartListForBrickRemoved(layer, itemToRemove, false); MainForm.Instance.NotifyPartListForBrickAdded(layer, itemToAdd, false); }
private int mNextPreferedActiveConnectionIndex = 0; // the prefered active connection index according to the brick library #endregion Fields #region Constructors public AddConnectGroup(LayerBrick layer, string partNumber, int wantedConnexion) { // save the layer and construct the group mBrickLayer = layer; mGroup = new Layer.Group(partNumber); LayerBrick.Brick selectedBrick = layer.getConnectableBrick(); // get the flat list of bricks from the hierarchical group mBricksInTheGroup = mGroup.getAllLeafItems(); // check if we can attach the group to the unique selected object if (selectedBrick != null) { // find the brick of the group that will be connected to the selected brick LayerBrick.Brick brickToConnect = null; if (selectedBrick.HasConnectionPoint) brickToConnect = findBrickToConnectAdnSetBestConnectionPointIndex(selectedBrick, ref wantedConnexion); // check if the selected brick has connection point if (brickToConnect != null && brickToConnect.HasConnectionPoint) { // after setting the active connection point index from which this brick will be attached, // get the prefered index from the library mNextPreferedActiveConnectionIndex = BrickLibrary.Instance.getConnectionNextPreferedIndex(partNumber, wantedConnexion); // Compute the orientation of the bricks float newOrientation = AddConnectBrick.sGetOrientationOfConnectedBrick(selectedBrick, brickToConnect); newOrientation -= brickToConnect.Orientation; // Rotate all the bricks of the group first before translating RotateBrickOnPivotBrick rotateBricksAction = new RotateBrickOnPivotBrick(layer, mBricksInTheGroup, newOrientation, brickToConnect); rotateBricksAction.MustUpdateBrickConnectivity = false; rotateBricksAction.redo(); // compute the translation to add to all the bricks PointF translation = new PointF(selectedBrick.ActiveConnectionPosition.X - brickToConnect.ActiveConnectionPosition.X, selectedBrick.ActiveConnectionPosition.Y - brickToConnect.ActiveConnectionPosition.Y); mGroup.translate(translation); } else { PointF position = selectedBrick.Position; position.X += selectedBrick.DisplayArea.Width; mGroup.Position = position; } // set the index of the brick in the list just after the selected brick mInsertIndex = layer.BrickList.IndexOf(selectedBrick) + 1; } }
private Layer.LayerItem createReplacementBrick(Layer.LayerItem brick, string newPartNumber) { // compute the altitude of the brick we want to replace float altitude = 0.0f; if (brick.IsAGroup) { // get the average altitude of all the children List <Layer.LayerItem> children = (brick as Layer.Group).getAllLeafItems(); if (children.Count > 0) { foreach (Layer.LayerItem child in children) { altitude += (child as LayerBrick.Brick).Altitude; } altitude /= children.Count; } } else { altitude = (brick as LayerBrick.Brick).Altitude; } // create a new brick and copy all the parameters of the old one Layer.LayerItem newBrick = null; if (BrickLibrary.Instance.isAGroup(newPartNumber)) { newBrick = new Layer.Group(newPartNumber); // set the altitude to all children List <Layer.LayerItem> children = (newBrick as Layer.Group).getAllLeafItems(); foreach (Layer.LayerItem child in children) { (child as LayerBrick.Brick).Altitude = altitude; } } else { newBrick = new LayerBrick.Brick(newPartNumber); (newBrick as LayerBrick.Brick).Altitude = altitude; } newBrick.Orientation = brick.Orientation; newBrick.Center = brick.Center; // return the new brick return(newBrick); }
/// <summary> /// Call this method when you want to notify the budget counter that a new brick has been added /// </summary> /// <param name="layer">The layer on which the brick is added</param> /// <param name="brick">the brick that was added</param> /// <param name="isDueToRegroup">tells if the brick is a group that was regrouped by an undo</param> public void addBrickNotification(LayerBrick layer, Layer.LayerItem brickOrGroup, bool isDueToRegroup) { string partID = brickOrGroup.PartNumber; if (partID != string.Empty) { // get the current count in the count dictionary int currentCount = 0; mCount.TryGetValue(partID, out currentCount); // and update the value in the global dictionnary mCount.Remove(partID); mCount.Add(partID, currentCount + 1); // also update in the layered dictionary Dictionary <string, int> layeredCount = null; if (mCountPerLayer.TryGetValue(layer, out layeredCount)) { int currentCountInLayer = 0; layeredCount.TryGetValue(partID, out currentCountInLayer); layeredCount.Remove(partID); layeredCount.Add(partID, currentCountInLayer + 1); } else { // this is a new layer (this layer didn't exist before), so add the layer and also the count layeredCount = new Dictionary <string, int>(); layeredCount.Add(partID, 1); mCountPerLayer.Add(layer, layeredCount); } } // add also all the named children if the brick is a group // (unless it is a regroup in that case the children are already counted) if (!isDueToRegroup) { Layer.Group group = brickOrGroup as Layer.Group; if (group != null) { foreach (Layer.LayerItem item in group.Items) { addBrickNotification(layer, item, isDueToRegroup); } } } }
/// <summary> /// Call this method when you want to notify the budget counter that a brick has been removed /// </summary> /// <param name="layer">The layer on which the brick is removed</param> /// <param name="brick">the brick that was removed</param> /// <param name="isDueToUngroup">tell if the brick is a group that is ungrouped</param> public void removeBrickNotification(LayerBrick layer, Layer.LayerItem brickOrGroup, bool isDueToUngroup) { string partID = brickOrGroup.PartNumber; if (partID != string.Empty) { // get the current count int currentCount = 0; mCount.TryGetValue(partID, out currentCount); if (currentCount > 0) { // update the value mCount.Remove(partID); mCount.Add(partID, currentCount - 1); } // also update in the layered dictionary Dictionary <string, int> layeredCount = null; if (mCountPerLayer.TryGetValue(layer, out layeredCount)) { int currentCountInLayer = 0; layeredCount.TryGetValue(partID, out currentCountInLayer); if (currentCountInLayer > 0) { layeredCount.Remove(partID); layeredCount.Add(partID, currentCountInLayer - 1); } } // if the layer doesn't exist, we have nothing to remove } // remove also all the named children if the brick is a group // (unless it is a ungroup in that case we leave the children and just remove the one we ungrouped) if (!isDueToUngroup) { Layer.Group group = brickOrGroup as Layer.Group; if (group != null) { foreach (Layer.LayerItem item in group.Items) { removeBrickNotification(layer, item, isDueToUngroup); } } } }
/// <summary> /// This method check that the specified partNumber is not used inside the hierrachy of the group /// in order to avoid saving a group with cyclic reference (that will generate an error at loading). /// This is a recursive function on the specified group /// </summary> /// <param name="partNumber">the name of the group the user wants to use to save his group</param> /// <returns>true if some cyclic reference is detected</returns> private bool isCyclicReferenceDetected(Layer.Group group, string partNumber) { foreach (Layer.LayerItem item in group.Items) { // if we find a match, return true immediatly if (item.PartNumber.Equals(partNumber)) { return(true); } // if it's a group, call recursively if (item.IsAGroup) { bool isCyclic = isCyclicReferenceDetected(item as Layer.Group, partNumber); // only return true if we found a match, if false continue to iterate on next item if (isCyclic) { return(true); } } } // nothing detected, return false return(false); }
/// <summary> /// This tool method clones all the item of the specified list into a new list. /// This method also clone the groups that may belong to this list of bricks. /// The cloned items are in the same order as the original list /// </summary> /// <param name="listToClone">The original list of brick to copy</param> /// <param name="addGroupsInItemList">if this parameter is true, the groups are also added in the Items list</param> /// <returns>A clone list of cloned brick with there cloned groups</returns> protected List <Layer.LayerItem> cloneItemList(List <Layer.LayerItem> listToClone, bool addGroupsInItemList) { // the resulting list List <Layer.LayerItem> result = new List <Layer.LayerItem>(listToClone.Count); // use a dictionnary to recreate the groups that may be inside the list of brick to duplicate // this dictionnary makes an association between the group to duplicate and the new duplicated one Dictionary <Layer.Group, Layer.Group> groupsToCreate = new Dictionary <Layer.Group, Layer.Group>(); // also use a list of item that we will make grow to create all the groups List <Layer.LayerItem> fullOriginalItemList = new List <Layer.LayerItem>(listToClone); // use a for instead of a foreach because the list will grow for (int i = 0; i < fullOriginalItemList.Count; ++i) { // get the current item Layer.LayerItem originalItem = fullOriginalItemList[i]; Layer.LayerItem duplicatedItem = null; // check if the item is a group or a brick if (originalItem.IsAGroup) { // if the item is a group that means the list already grown, and that means we also have it in the dictionnary Layer.Group associatedGroup = null; groupsToCreate.TryGetValue(originalItem as Layer.Group, out associatedGroup); duplicatedItem = associatedGroup; // check if we also need to add the group if (addGroupsInItemList) { result.Add(duplicatedItem); } } else { // if the item is a brick, just clone it and add it to the result // clone the item (because the same list of text to add can be paste several times) duplicatedItem = originalItem.Clone(); // add the duplicated item in the list result.Add(duplicatedItem); } // check if the item to clone belongs to a group then also duplicate the group if (originalItem.Group != null) { // get the duplicated group if already created otherwise create it and add it in the dictionary Layer.Group duplicatedGroup = null; groupsToCreate.TryGetValue(originalItem.Group, out duplicatedGroup); if (duplicatedGroup == null) { duplicatedGroup = new Layer.Group(originalItem.Group); groupsToCreate.Add(originalItem.Group, duplicatedGroup); fullOriginalItemList.Add(originalItem.Group); } // assign the group to the brick duplicatedGroup.addItem(duplicatedItem); // check if we need to also assign the brick that hold the connection point if (originalItem.Group.BrickThatHoldsActiveConnection == originalItem) { duplicatedGroup.BrickThatHoldsActiveConnection = (duplicatedItem as LayerBrick.Brick); } } } // delete the dictionary groupsToCreate.Clear(); fullOriginalItemList.Clear(); // return the cloned list return(result); }
protected PointF mCenter = new PointF(0, 0); // in Stud coord /// <summary> /// A common method that can be called by the constructor to initialize many stuff /// </summary> /// <param name="layer"></param> /// <param name="originalItems"></param> /// <param name="angle"></param> /// <param name="forceKeepLastCenter"></param> protected virtual void commonConstructor(Layer layer, List <Layer.LayerItem> originalItems, float angle, bool forceKeepLastCenter) { mLayer = layer; mRotateCW = (angle < 0.0f); mRotationStep = Math.Abs(angle); // we must invalidate the last center if the last action in the undo stack is not a rotation if (!forceKeepLastCenter && !ActionManager.Instance.getUndoableActionType().IsInstanceOfType(this)) { sLastCenterIsValid = false; } // fill the brick list with the one provided and set the center of rotation for this action if (originalItems.Count > 0) { // copy the list, because the pointer may change (specially if it is the selection) // also compute the center of all the bricks PointF minCenter = new PointF(originalItems[0].DisplayArea.Left, originalItems[0].DisplayArea.Top); PointF maxCenter = new PointF(originalItems[0].DisplayArea.Right, originalItems[0].DisplayArea.Bottom); mItems = new List <Layer.LayerItem>(originalItems.Count); mNamedGroupsOfTheItems = new List <Layer.Group>(originalItems.Count); mTopGroupsOfTheItems = new List <Layer.Group>(originalItems.Count); foreach (Layer.LayerItem obj in originalItems) { mItems.Add(obj); //compute the new center if the static one is not valid if (!sLastCenterIsValid) { if (obj.DisplayArea.Left < minCenter.X) { minCenter.X = obj.DisplayArea.Left; } if (obj.DisplayArea.Top < minCenter.Y) { minCenter.Y = obj.DisplayArea.Top; } if (obj.DisplayArea.Right > maxCenter.X) { maxCenter.X = obj.DisplayArea.Right; } if (obj.DisplayArea.Bottom > maxCenter.Y) { maxCenter.Y = obj.DisplayArea.Bottom; } } // Also collect the groups amongs all the items Layer.Group parentGroup = obj.Group; if (parentGroup != null) { // we found a group, add it to the list if it is a named group and if it is not already in if (parentGroup.IsANamedGroup && !mNamedGroupsOfTheItems.Contains(parentGroup)) { mNamedGroupsOfTheItems.Add(parentGroup); } // also if that group doesn't have a parent group, this is a top group, so add it to the top group list if not already in if ((parentGroup.Group == null) && !mTopGroupsOfTheItems.Contains(parentGroup)) { mTopGroupsOfTheItems.Add(parentGroup); } } } // set the center for this rotation action (keep the previous one or compute a new one if (sLastCenterIsValid) { mCenter = sLastCenter; } else { // recompute a new center mCenter.X = (maxCenter.X + minCenter.X) * 0.5f; mCenter.Y = (maxCenter.Y + minCenter.Y) * 0.5f; // and assign it to the static one sLastCenter = mCenter; sLastCenterIsValid = true; } } }
public SaveGroupNameForm() { InitializeComponent(); // create an array of forbidden char for the group name (before setting the name of the group) List<char> charList = new List<char>(System.IO.Path.GetInvalidFileNameChars()); foreach (char character in System.IO.Path.GetInvalidPathChars()) if (!charList.Contains(character)) charList.Add(character); charList.Add('&'); // the ampersome is authorized in file name, but brings trouble in xml, since it is the escape char. mForbiddenChar = charList.ToArray(); // save the error list from the text field char[] separator = { '|' }; mErrorHint = this.nameErrorLabel.Text.Split(separator); // fill the language combo fillAndSelectLanguageComboBox(); // set the author (could be overriden later) this.authorTextBox.Text = this.Author; // get the list of the top items List<Layer.LayerItem> topItems = Layer.sGetTopItemListFromList(Map.Instance.SelectedLayer.SelectedObjects); // fill the name if there's only one group selected if (topItems.Count == 1) { // if this window is called with one object, it should be normally a group // otherwise the save group cannot be called mGroupToSave = (topItems[0]) as Layer.Group; mWasGroupToSaveCreated = false; if (mGroupToSave.IsANamedGroup) { string partNumber = mGroupToSave.PartNumber; // set the name here and init the rest in the function nameTextBox.Text = partNumber; initControlWithPartInfo(partNumber); } } else { // sort the top items as on the layer topItems.Sort(Map.Instance.SelectedLayer.compareItemOrderOnLayer); // create a group temporally for the export purpose mGroupToSave = new Layer.Group(); mGroupToSave.addItem(topItems); mWasGroupToSaveCreated = true; } // call explicitly the event to set the correct color and error message // after setting all the data members used to check the validity of the name this.nameTextBox_TextChanged(nameTextBox, null); // configure the xmlSetting for writing mXmlSettings.CheckCharacters = false; mXmlSettings.CloseOutput = true; mXmlSettings.ConformanceLevel = System.Xml.ConformanceLevel.Document; mXmlSettings.Encoding = new UTF8Encoding(false); mXmlSettings.Indent = true; mXmlSettings.IndentChars = "\t"; mXmlSettings.NewLineChars = "\r\n"; mXmlSettings.NewLineOnAttributes = false; mXmlSettings.OmitXmlDeclaration = false; }
public SaveGroupNameForm() { InitializeComponent(); // create an array of forbidden char for the group name (before setting the name of the group) List <char> charList = new List <char>(System.IO.Path.GetInvalidFileNameChars()); foreach (char character in System.IO.Path.GetInvalidPathChars()) { if (!charList.Contains(character)) { charList.Add(character); } } charList.Add('&'); // the ampersome is authorized in file name, but brings trouble in xml, since it is the escape char. mForbiddenChar = charList.ToArray(); // save the error list from the text field char[] separator = { '|' }; mErrorHint = this.nameErrorLabel.Text.Split(separator); // fill the language combo fillAndSelectLanguageComboBox(); // set the author (could be overriden later) this.authorTextBox.Text = this.Author; // get the list of the top items List <Layer.LayerItem> topItems = Layer.sGetTopItemListFromList(Map.Instance.SelectedLayer.SelectedObjects); // fill the name if there's only one group selected if (topItems.Count == 1) { // if this window is called with one object, it should be normally a group // otherwise the save group cannot be called mGroupToSave = (topItems[0]) as Layer.Group; mWasGroupToSaveCreated = false; if (mGroupToSave.IsANamedGroup) { string partNumber = mGroupToSave.PartNumber; // set the name here and init the rest in the function nameTextBox.Text = partNumber; initControlWithPartInfo(partNumber); } } else { // sort the top items as on the layer topItems.Sort(Map.Instance.SelectedLayer.compareItemOrderOnLayer); // create a group temporally for the export purpose mGroupToSave = new Layer.Group(); mGroupToSave.addItem(topItems); mWasGroupToSaveCreated = true; } // call explicitly the event to set the correct color and error message // after setting all the data members used to check the validity of the name this.nameTextBox_TextChanged(nameTextBox, null); // configure the xmlSetting for writing mXmlSettings.CheckCharacters = false; mXmlSettings.CloseOutput = true; mXmlSettings.ConformanceLevel = System.Xml.ConformanceLevel.Document; mXmlSettings.Encoding = new UTF8Encoding(false); mXmlSettings.Indent = true; mXmlSettings.IndentChars = "\t"; mXmlSettings.NewLineChars = "\r\n"; mXmlSettings.NewLineOnAttributes = false; mXmlSettings.OmitXmlDeclaration = false; }
/// <summary> /// Write a list of prefered connections. The list is computed as follow: /// First we get all the free connections in the whole group, then we split that /// list into sub-list per connection type, and then for each sub-list we create /// a loop of next connection preferences. /// For example if the group contains 4 track and 3 road connections, and the /// two track parts are connected, then we have only 2 free track connections /// and 3 free road connection. We will generate a first cycle for the track /// and a second cycle for the road. /// </summary> /// <param name="xmlWriter">The xmlWritter where to write the list</param> /// <param name="group">The group for which we are writting the list</param> private void writeGroupConnectionPreferenceList(XmlWriter xmlWriter, Layer.Group group) { // get all the bricks in a flat list (removing intermediate group) List <Layer.LayerItem> allBricks = group.getAllLeafItems(); // if the group doesn't contains brick, just early exit if ((allBricks.Count == 0) || !(allBricks[0] is LayerBrick.Brick)) { return; } // declare a dictionary of list of connections, to store all the connections separated by connection type (which is the key of the dictionnary) Dictionary <int, List <ConnectionAndIndex> > connectionsPerType = new Dictionary <int, List <ConnectionAndIndex> >(); // declare a global counter to count the connection index inside the group int connectionCounter = 0; // now, iterate on all the connections of all the bricks, and create separated list of free connection foreach (Layer.LayerItem item in allBricks) { LayerBrick.Brick brick = item as LayerBrick.Brick; if (brick.HasConnectionPoint) { foreach (LayerBrick.Brick.ConnectionPoint connection in brick.ConnectionPoints) { if (connection.IsFree) { // we found a free connection, check if the list for its type was already added in the dictionary if (!connectionsPerType.ContainsKey(connection.Type)) { connectionsPerType.Add(connection.Type, new List <ConnectionAndIndex>()); } // now get the list corresponding to the type List <ConnectionAndIndex> freeConnectionList = connectionsPerType[connection.Type]; // then add the connection in the list freeConnectionList.Add(new ConnectionAndIndex(connection, connectionCounter)); } // increase the connection counter, no matter if the current connection is free or not connectionCounter++; } } } // check if we found something if (connectionsPerType.Count > 0) { // write the header of the list xmlWriter.WriteStartElement("GroupConnectionPreferenceList"); // now iterate on the dictionary, and for each list, create a cycle foreach (KeyValuePair <int, List <ConnectionAndIndex> > connectionsOfOneType in connectionsPerType) { for (int i = 0; i < connectionsOfOneType.Value.Count; ++i) { // get the current and the next connection ConnectionAndIndex currentConnection = connectionsOfOneType.Value[i]; ConnectionAndIndex nextConnection = (i == connectionsOfOneType.Value.Count - 1) ? connectionsOfOneType.Value[0] : connectionsOfOneType.Value[i + 1]; // write the current prefered link xmlWriter.WriteStartElement("nextIndex"); xmlWriter.WriteAttributeString("from", currentConnection.mIndexInTheGroup.ToString()); xmlWriter.WriteValue(nextConnection.mIndexInTheGroup); xmlWriter.WriteEndElement(); // nextIndex } } xmlWriter.WriteEndElement(); // GroupConnectionPreferenceList } }
/// <summary> /// Recursive function that save one xml file for the specified group in the custom library /// </summary> /// <param name="group">The group to save</param> /// <param name="groupName">the name of the group that should be used to save the file</param> /// <param name="groupNumber">The sequential number of the group, starting with 0 for the top group</param> private void saveGroup(Layer.Group group, string groupName, int groupNumber) { // use a counter for the sub-groups of this group, starting from this group number + 1 int subGroupNumber = groupNumber + 1; // get the position of the first item to make it the origin PointF origin = new PointF(); if (group.ItemsCount > 0) { origin = group.Items[0].Center; } // trim and uppercase the group name and save it in the array groupName = groupName.Trim().ToUpper(); mNewGroupName.Add(groupName); // get the full filename and save it in the array string filename = getFullFileNameFromGroupName(groupName); mNewXmlFilesToLoad.Add(new FileInfo(filename)); // open the stream XmlWriter xmlWriter = XmlWriter.Create(filename, mXmlSettings); // start to write the header and the top node xmlWriter.WriteStartDocument(); xmlWriter.WriteStartElement("group"); // author xmlWriter.WriteElementString("Author", this.authorTextBox.Text); // description xmlWriter.WriteStartElement("Description"); foreach (KeyValuePair <string, string> keyValue in mDescription) { xmlWriter.WriteElementString(keyValue.Key, keyValue.Value); } xmlWriter.WriteEndElement(); // Description // sorting key xmlWriter.WriteElementString("SortingKey", this.sortingKeyTextBox.Text.Trim()); // in library? Only the top group is in library, the other one are hidden (normal behavior) if (groupNumber != 0) { xmlWriter.WriteElementString("NotListedInLibrary", "true"); } // image URL if it exists if (this.imageURLTextBox.Text != string.Empty) { xmlWriter.WriteElementString("ImageURL", this.imageURLTextBox.Text); } // can ungroup? if (this.canUngroupCheckBox.Checked) { xmlWriter.WriteElementString("CanUngroup", "true"); } else { xmlWriter.WriteElementString("CanUngroup", "false"); } // sub part list xmlWriter.WriteStartElement("SubPartList"); foreach (Layer.LayerItem item in group.Items) { xmlWriter.WriteStartElement("SubPart"); if (item.PartNumber != string.Empty) { xmlWriter.WriteAttributeString("id", item.PartNumber); } else { xmlWriter.WriteAttributeString("id", getSubGroupName(groupName, subGroupNumber++)); } // position and angle PointF center = item.Center; center.X -= origin.X; center.Y -= origin.Y; XmlReadWrite.writePointFLowerCase(xmlWriter, "position", center); XmlReadWrite.writeFloat(xmlWriter, "angle", item.Orientation); // end of subpart xmlWriter.WriteEndElement(); // SubPart } xmlWriter.WriteEndElement(); // SubPartList // for the top group we should also write cyclic prefered connection lists (one cycle per type of connection among the free connections) if (groupNumber == 0) { writeGroupConnectionPreferenceList(xmlWriter, group); } // write the end element and close the stream xmlWriter.WriteEndElement(); // group xmlWriter.Close(); // now iterate on all the unnamed group recursively // we do two iteration on the group list because we don't like to open several files at the same time subGroupNumber = groupNumber + 1; // reinit the counter foreach (Layer.LayerItem item in group.Items) { if (item.PartNumber == string.Empty) { saveGroup(item as Layer.Group, getSubGroupName(groupName, subGroupNumber), subGroupNumber); subGroupNumber++; } } }
private int mNextPreferedActiveConnectionIndex = 0; // the prefered active connection index according to the brick library /// <summary> /// Add a new named group which has the specified partNumber on the specifier layer, and connected it to /// the specified selectedItem (part or group) using the specified wanted connection for that new part. /// </summary> /// <param name="layer">The layer on which to add the group, and in which the selectedItem is selected</param> /// <param name="selectedItem">The single selected item (this can be a single part or a single group). This parameter cannot be null.</param> /// <param name="partNumber">The number of the named group to add</param> /// <param name="wantedConnexion">The connection index of the group to add that should be used to connect to the selected item, or -1 if you don't care.</param> public AddConnectGroup(LayerBrick layer, Layer.LayerItem selectedItem, string partNumber, int wantedConnexion) { // the selected item should not be null System.Diagnostics.Debug.Assert(selectedItem != null); // save the layer and construct the group mBrickLayer = layer; mGroup = new Layer.Group(partNumber); // get the flat list of bricks from the hierarchical group mBricksInTheGroup = mGroup.getAllLeafItems(); // get the connectable brick among the selection, and also the selected item (in case the selected item is a single group without connections points) LayerBrick.Brick selectedBrick = layer.getConnectableBrick(); LayerBrick.Brick brickToConnectInAddedGroup = null; // check if we can attach the group to the unique selected object if ((selectedBrick != null) && selectedBrick.HasConnectionPoint) { // find the brick of the group that will be connected to the selected brick brickToConnectInAddedGroup = findBrickToConnectAndSetBestConnectionPointIndex(selectedItem, ref wantedConnexion); } // check if the brick to connect is valid and has connection point if ((brickToConnectInAddedGroup != null) && brickToConnectInAddedGroup.HasConnectionPoint) { // after setting the active connection point index from which this brick will be attached, // get the prefered index from the library mNextPreferedActiveConnectionIndex = BrickLibrary.Instance.getConnectionNextPreferedIndex(partNumber, wantedConnexion); // Compute the orientation of the bricks float newOrientation = AddConnectBrick.sGetOrientationOfConnectedBrick(selectedBrick, brickToConnectInAddedGroup); newOrientation -= brickToConnectInAddedGroup.Orientation; // Rotate all the bricks of the group first before translating RotateBrickOnPivotBrick rotateBricksAction = new RotateBrickOnPivotBrick(layer, mBricksInTheGroup, newOrientation, brickToConnectInAddedGroup); rotateBricksAction.MustUpdateBrickConnectivity = false; rotateBricksAction.redo(); // compute the translation to add to all the bricks PointF translation = new PointF(selectedBrick.ActiveConnectionPosition.X - brickToConnectInAddedGroup.ActiveConnectionPosition.X, selectedBrick.ActiveConnectionPosition.Y - brickToConnectInAddedGroup.ActiveConnectionPosition.Y); mGroup.translate(translation); } else { // and just compute the position to the right of the selected item PointF position = selectedItem.Position; position.X += selectedItem.DisplayArea.Width; mGroup.Position = position; // the reassing the selected brick with the first brick of the group if the selected item is a group // so that the brick index can correctly be set if (selectedItem.IsAGroup) { selectedBrick = (selectedItem as Layer.Group).getAllLeafItems()[0] as LayerBrick.Brick; } else { selectedBrick = selectedItem as LayerBrick.Brick; } } // set the index of the group in the list just after the selected brick if (selectedBrick != null) { mInsertIndex = layer.BrickList.IndexOf(selectedBrick) + 1; } }
private LayerBrick.Brick findBrickToConnectAndSetBestConnectionPointIndex(Layer.LayerItem fixedItem, ref int wantedConnexion) { // the brick that will be used to attach the group LayerBrick.Brick brickToAttach = null; int originalWantedConnexion = wantedConnexion; // memorize the original wanted connection in case we need to reset it // get the type of the active connexion of the selected brick Layer.Group fixedGroup = fixedItem as Layer.Group; int fixedConnexionType = (fixedGroup != null) ? fixedGroup.ActiveConnectionPoint.Type : (fixedItem as LayerBrick.Brick).ActiveConnectionPoint.Type; // first connect all the connection inside the group. Normally the connection of the bricks of the group // are done directly on the layer, afer that the brick has been added. But here we need to connect the bricks // inisde the group, in order to check the free remaining connections LayerBrick.updateBrickConnectivityForASpareSetOfBrickAmongThemselve(mBricksInTheGroup); // try to give the correct connexion point, either the specified wanted one, or if // we add the same brick do a special case bool isActiveConnectionPointChosen = false; if (wantedConnexion >= 0) { // set the active connexion point with the wanted one brickToAttach = setActiveConnectionIndex(wantedConnexion); // check that the wanted connection type is the same as the selected brick isActiveConnectionPointChosen = (brickToAttach != null) && (brickToAttach.ActiveConnectionPoint.Type == fixedConnexionType) && brickToAttach.ActiveConnectionPoint.IsFree; } else if ((fixedGroup != null) && (fixedGroup.PartNumber == mGroup.PartNumber)) { // check if the new added brick is the same kind of the selected one, if so, // then we choose the previous connection point, but check if it is the same type wantedConnexion = BrickLibrary.Instance.getConnectionNextPreferedIndex(fixedGroup.PartNumber, fixedGroup.ActiveConnectionIndex); brickToAttach = setActiveConnectionIndex(wantedConnexion); // check that the connection type is the same isActiveConnectionPointChosen = (brickToAttach != null) && (brickToAttach.ActiveConnectionPoint.Type == fixedConnexionType) && brickToAttach.ActiveConnectionPoint.IsFree; } // if we didn't find any valid active connexion point, set the active connection // with the first connexion of the same type that we can find (if the brick as any connection point) if (!isActiveConnectionPointChosen) { wantedConnexion = 0; foreach (Layer.LayerItem item in mBricksInTheGroup) { LayerBrick.Brick brick = item as LayerBrick.Brick; if (brick.HasConnectionPoint) { for (int i = 0; i < brick.ConnectionPoints.Count; ++i) { if ((brick.ConnectionPoints[i].Type == fixedConnexionType) && brick.ConnectionPoints[i].IsFree) { brick.ActiveConnectionPointIndex = i; brickToAttach = brick; isActiveConnectionPointChosen = true; break; } // increase the connection count ++wantedConnexion; } } // break again if we found it if (isActiveConnectionPointChosen) { break; } } } // if we still didn't find a compatible brick, reset the wanted connection that we have modified if (!isActiveConnectionPointChosen) { wantedConnexion = originalWantedConnexion; } // finally return the brick selected for the attachement return(brickToAttach); }
/// <summary> /// This tool method clones all the item of the specified list into a new list. /// This method also clone the groups that may belong to this list of bricks. /// The cloned items are in the same order as the original list /// </summary> /// <param name="listToClone">The original list of brick to copy</param> /// <param name="addGroupsInItemList">if this parameter is true, the groups are also added in the Items list</param> /// <returns>A clone list of cloned brick with there cloned groups</returns> protected List<Layer.LayerItem> cloneItemList(List<Layer.LayerItem> listToClone, bool addGroupsInItemList) { // the resulting list List<Layer.LayerItem> result = new List<Layer.LayerItem>(listToClone.Count); // use a dictionnary to recreate the groups that may be inside the list of brick to duplicate // this dictionnary makes an association between the group to duplicate and the new duplicated one Dictionary<Layer.Group, Layer.Group> groupsToCreate = new Dictionary<Layer.Group, Layer.Group>(); // also use a list of item that we will make grow to create all the groups List<Layer.LayerItem> fullOriginalItemList = new List<Layer.LayerItem>(listToClone); // use a for instead of a foreach because the list will grow for (int i = 0; i < fullOriginalItemList.Count; ++i) { // get the current item Layer.LayerItem originalItem = fullOriginalItemList[i]; Layer.LayerItem duplicatedItem = null; // check if the item is a group or a brick if (originalItem.IsAGroup) { // if the item is a group that means the list already grown, and that means we also have it in the dictionnary Layer.Group associatedGroup = null; groupsToCreate.TryGetValue(originalItem as Layer.Group, out associatedGroup); duplicatedItem = associatedGroup; // check if we also need to add the group if (addGroupsInItemList) result.Add(duplicatedItem); } else { // if the item is a brick, just clone it and add it to the result // clone the item (because the same list of text to add can be paste several times) duplicatedItem = originalItem.Clone(); // add the duplicated item in the list result.Add(duplicatedItem); } // check if the item to clone belongs to a group then also duplicate the group if (originalItem.Group != null) { // get the duplicated group if already created otherwise create it and add it in the dictionary Layer.Group duplicatedGroup = null; groupsToCreate.TryGetValue(originalItem.Group, out duplicatedGroup); if (duplicatedGroup == null) { duplicatedGroup = new Layer.Group(originalItem.Group); groupsToCreate.Add(originalItem.Group, duplicatedGroup); fullOriginalItemList.Add(originalItem.Group); } // assign the group to the brick duplicatedGroup.addItem(duplicatedItem); // check if we need to also assign the brick that hold the connection point if (originalItem.Group.BrickThatHoldsActiveConnection == originalItem) duplicatedGroup.BrickThatHoldsActiveConnection = (duplicatedItem as LayerBrick.Brick); } } // delete the dictionary groupsToCreate.Clear(); fullOriginalItemList.Clear(); // return the cloned list return result; }