/// <summary> /// Built an index for a specified objects. /// <para> /// This method creates a more optimal partitioning of the space /// than the one which is obtained by sequentially adding objects /// to the index. The resulting tree is unbalanced. /// </para> /// </summary> /// <remarks> /// <para> /// The algorithm is optimized for fast discarding the empty space. /// The criterion for the optimal partition is the minimum of /// (number_of_objects_in_1st_area * square_of_1st_area + number_of_objects_in_2nd_area * square_of_2nd_area). /// The number of possible partitions defined into <see cref="KDTree.DividingGridRowCount"/>. /// </para> /// </remarks> /// <param name="objects">Enumerator of objects for indexing</param> public void Build <T>(IEnumerable <T> objects) where T : IIndexable { Clear(); List <T> list = new List <T>(); foreach (T obj in objects) { if (IndexedSpace.ContainsRectangle(obj.BoundingRectangle)) { list.Add(obj); } else { throw new ArgumentException("At least one object goes beyond the indexed space", "objects"); } } _root.BuildUnbalanced(list, 1); }
/// <summary> /// Builds an index for the specified objects. /// </summary> internal void BuildUnbalanced <T>(List <T> objects, int currentDepth) where T : IIndexable { if (currentDepth > _parent.Depth) { _parent._depth = currentDepth; } // Возможно мы достигли: // 1. максимально допустимой глубины индекса // 2. порогового значения площади ячейки узла // или кол-во объектов переданных для индексирования // не превысило минимального. // При этом создавать дочерние узлы уже нельзя if (_parent.Depth >= _parent.MaxDepth || _parent._boxSquareThreshold > _boundingBox.Width * _boundingBox.Height || _parent.MinObjectCount >= objects.Count) { foreach (T obj in objects) { _objects.Add(obj); } objects.Clear(); return; } List <IIndexable> objects0 = new List <IIndexable>(); List <IIndexable> objects1 = new List <IIndexable>(); double minCost = double.MaxValue; BoundingRectangle box0, box1; BoundingRectangle minCostBox0 = _parent._indexedSpace, minCostBox1 = _parent._indexedSpace; #region Вычисление разбиения с минимальной стоимостью: стоимость = кол-во объекты * площадь for (int i = 1; i < KDTree.DividingGridRowCount; i++) { double x = _boundingBox.MinX + _boundingBox.Width / KDTree.DividingGridRowCount * i; splitX(_boundingBox, x, out box0, out box1); double cost = getSplitCostVertical(objects, box0, box1); if (cost < minCost) { minCost = cost; minCostBox0 = box0; minCostBox1 = box1; } double y = _boundingBox.MinY + _boundingBox.Height / KDTree.DividingGridRowCount * i; splitY(_boundingBox, y, out box0, out box1); cost = getSplitCostHorizontal(objects, ref box0, ref box1); if (cost < minCost) { minCost = cost; minCostBox0 = box0; minCostBox1 = box1; } } #endregion // разбиваем исходный список на 3 множества: // objects0 - объекты индексируемые в дочернем узле _child0 // objects1 - объекты индексируемые в дочернем узле _child1 // _objects - объекты этого узла foreach (T obj in objects) { if (minCostBox0.ContainsRectangle(obj.BoundingRectangle)) { objects0.Add(obj); } else { if (minCostBox1.ContainsRectangle(obj.BoundingRectangle)) { objects1.Add(obj); } else { _objects.Add(obj); } } } objects.Clear(); if (objects0.Count > 0 || objects1.Count > 0) { _child0 = new KDTreeNode(minCostBox0, _parent); _child1 = new KDTreeNode(minCostBox1, _parent); _child0.BuildUnbalanced(objects0, currentDepth + 1); _child1.BuildUnbalanced(objects1, currentDepth + 1); } }