/*
* $Id$
* This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc
*
* Copyright (c) 2000-2012 Stephane GALLAND.
* Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
* Universite de Technologie de Belfort-Montbeliard.
* Copyright (c) 2013-2016 The original authors, and other authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.arakhne.afc.math.tree.node;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Collection;
import java.util.List;
import org.eclipse.xtext.xbase.lib.Pure;
import org.arakhne.afc.math.MathUtil;
import org.arakhne.afc.math.tree.TreeNode;
/**
* This is the generic implementation of a
* tree for which each node has eight children.
*
* <p><h3>moveTo</h3>
* According to its definition in
* {@link TreeNode#moveTo(TreeNode, int)}, the binary
* tree node implementation of <code>moveTo</code>
* replaces any existing node at the position given as
* parameter of <code>moveTo</code>..
*
* @param <D> is the type of the data inside the tree
* @param <N> is the type of the tree nodes.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
public abstract class OctTreeNode<D, N extends OctTreeNode<D, N>> extends AbstractTreeNode<D, N> {
private static final long serialVersionUID = -6184547894654511018L;
private N child1;
private N child2;
private N child3;
private N child4;
private N child5;
private N child6;
private N child7;
private N child8;
/**
* Empty node.
*/
public OctTreeNode() {
this(DEFAULT_LINK_LIST_USE);
}
/** Construct a node.
* @param data are the initial user data
*/
public OctTreeNode(Collection<D> data) {
super(DEFAULT_LINK_LIST_USE, data);
this.child1 = null;
this.child2 = null;
this.child3 = null;
this.child4 = null;
this.child5 = null;
this.child6 = null;
this.child7 = null;
this.child8 = null;
}
/** Construct a node.
* @param data are the initial user data
*/
public OctTreeNode(D data) {
this(DEFAULT_LINK_LIST_USE, data);
}
/** Construct a node.
* @param useLinkedList indicates if a linked list must be used to store the data.
* If <code>false</code>, an ArrayList will be used.
*/
public OctTreeNode(boolean useLinkedList) {
super(useLinkedList);
this.child1 = null;
this.child2 = null;
this.child3 = null;
this.child4 = null;
this.child5 = null;
this.child6 = null;
this.child7 = null;
this.child8 = null;
}
/** Construct a node.
* @param useLinkedList indicates if a linked list must be used to store the data.
* If <code>false</code>, an ArrayList will be used.
* @param copyDataCollection indicates if the given data collection is copied
* if <code>true</code> or the inner data collection will be the given
* collection itself if <code>false</code>.
* @param data are the initial user data
*/
public OctTreeNode(boolean useLinkedList, boolean copyDataCollection, List<D> data) {
super(useLinkedList, copyDataCollection, data);
this.child1 = null;
this.child2 = null;
this.child3 = null;
this.child4 = null;
this.child5 = null;
this.child6 = null;
this.child7 = null;
this.child8 = null;
}
/** Construct a node.
* @param useLinkedList indicates if a linked list must be used to store the data.
* If <code>false</code>, an ArrayList will be used.
* @param data are the initial user data
*/
public OctTreeNode(boolean useLinkedList, D data) {
super(useLinkedList, data);
this.child1 = null;
this.child2 = null;
this.child3 = null;
this.child4 = null;
this.child5 = null;
this.child6 = null;
this.child7 = null;
this.child8 = null;
}
@Pure
@Override
public Class<? extends Enum<?>> getPartitionEnumeration() {
return OctTreeZone.class;
}
/** Invoked when this object must be deserialized.
*
* @param in is the input stream.
* @throws IOException in case of input stream access error.
* @throws ClassNotFoundException if some class was not found.
*/
@SuppressWarnings("checkstyle:npathcomplexity")
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
final N me = toN();
if (this.child1 != null) {
this.child1.setParentNodeReference(me, false);
}
if (this.child2 != null) {
this.child2.setParentNodeReference(me, false);
}
if (this.child3 != null) {
this.child3.setParentNodeReference(me, false);
}
if (this.child4 != null) {
this.child4.setParentNodeReference(me, false);
}
if (this.child5 != null) {
this.child5.setParentNodeReference(me, false);
}
if (this.child6 != null) {
this.child6.setParentNodeReference(me, false);
}
if (this.child7 != null) {
this.child7.setParentNodeReference(me, false);
}
if (this.child8 != null) {
this.child8.setParentNodeReference(me, false);
}
}
/** Clear the tree.
*
* <p>Caution: this method also destroyes the
* links between the child nodes inside the tree.
* If you want to unlink the first-level
* child node with
* this node but leave the rest of the tree
* unchanged, please call <code>setChildAt(i,null)</code>.
*/
@Override
@SuppressWarnings({"checkstyle:npathcomplexity", "checkstyle:magicnumber"})
public void clear() {
if (this.child1 != null) {
final N child = this.child1;
setChildAt(0, null);
child.clear();
}
if (this.child2 != null) {
final N child = this.child2;
setChildAt(1, null);
child.clear();
}
if (this.child3 != null) {
final N child = this.child3;
setChildAt(2, null);
child.clear();
}
if (this.child4 != null) {
final N child = this.child4;
setChildAt(3, null);
child.clear();
}
if (this.child5 != null) {
final N child = this.child5;
setChildAt(4, null);
child.clear();
}
if (this.child6 != null) {
final N child = this.child6;
setChildAt(5, null);
child.clear();
}
if (this.child7 != null) {
final N child = this.child7;
setChildAt(6, null);
child.clear();
}
if (this.child8 != null) {
final N child = this.child8;
setChildAt(7, null);
child.clear();
}
removeAllUserData();
}
@Pure
@Override
@SuppressWarnings("checkstyle:magicnumber")
public int getChildCount() {
return 8;
}
@Pure
@Override
public int getNotNullChildCount() {
return this.notNullChildCount;
}
@Pure
@Override
@SuppressWarnings({"checkstyle:npathcomplexity", "checkstyle:returncount", "checkstyle:magicnumber"})
public int indexOf(N child) {
if (child == this.child1) {
return 0;
}
if (child == this.child2) {
return 1;
}
if (child == this.child3) {
return 2;
}
if (child == this.child4) {
return 3;
}
if (child == this.child5) {
return 4;
}
if (child == this.child6) {
return 5;
}
if (child == this.child7) {
return 6;
}
if (child == this.child8) {
return 7;
}
return -1;
}
/** Replies the zone of the specified child.
*
* @param child the child.
* @return the zone or <code>null</code>.
*/
@Pure
public final OctTreeZone zoneOf(N child) {
final int idx = indexOf(child);
final OctTreeZone[] zones = OctTreeZone.values();
if (idx < 0 || idx >= zones.length) {
return zones[idx];
}
return null;
}
@Pure
@Override
@SuppressWarnings({"checkstyle:returncount", "checkstyle:magicnumber"})
public N getChildAt(int index) throws IndexOutOfBoundsException {
switch (index) {
case 0:
return this.child1;
case 1:
return this.child2;
case 2:
return this.child3;
case 3:
return this.child4;
case 4:
return this.child5;
case 5:
return this.child6;
case 6:
return this.child7;
case 7:
return this.child8;
default:
}
throw new IndexOutOfBoundsException();
}
/** Replies count of children in this node.
*
* @param zone is the position of the node to reply
* @return the node at the given index
*/
@Pure
public final N getChildAt(OctTreeZone zone) {
if (zone != null) {
return getChildAt(zone.ordinal());
}
return null;
}
@Override
public boolean moveTo(N newParent, int index) {
return moveTo(newParent, index, false);
}
/** Move this node in the given new node.
*
* <p>This function is preferred to a sequence of calls
* to {@link #removeFromParent()} and {@link #setChildAt(int, OctTreeNode)}
* because it fires a limited set of events dedicated to the move
* the node.
*
* @param newParent is the new parent for this node.
* @param zone is the position of this node in the new parent.
* @return <code>true</code> on success, otherwise <code>false</code>.
*/
public boolean moveTo(N newParent, OctTreeZone zone) {
if (zone != null) {
return moveTo(newParent, zone.ordinal());
}
return false;
}
@Override
@SuppressWarnings({"checkstyle:returncount", "checkstyle:magicnumber"})
public boolean setChildAt(int index, N newChild) throws IndexOutOfBoundsException {
switch (index) {
case 0:
return setChild1(newChild);
case 1:
return setChild2(newChild);
case 2:
return setChild3(newChild);
case 3:
return setChild4(newChild);
case 4:
return setChild5(newChild);
case 5:
return setChild6(newChild);
case 6:
return setChild7(newChild);
case 7:
return setChild8(newChild);
default:
}
throw new IndexOutOfBoundsException();
}
/** Set the child at the specified zone.
*
* @param zone is the zone where to put the child
* @param newChild is the new child
* @return <code>true</code> on success, otherwise <code>false</code>
*/
public final boolean setChildAt(OctTreeZone zone, N newChild) {
return setChildAt(zone.ordinal(), newChild);
}
@Override
@SuppressWarnings({"checkstyle:npathcomplexity", "checkstyle:returncount", "checkstyle:cyclomaticcomplexity",
"checkstyle:magicnumber"})
protected void setChildAtWithoutEventFiring(int index, N newChild) throws IndexOutOfBoundsException {
switch (index) {
case 0:
if (this.child1 != null) {
--this.notNullChildCount;
}
this.child1 = newChild;
if (this.child1 != null) {
++this.notNullChildCount;
}
break;
case 1:
if (this.child2 != null) {
--this.notNullChildCount;
}
this.child2 = newChild;
if (this.child2 != null) {
++this.notNullChildCount;
}
break;
case 2:
if (this.child3 != null) {
--this.notNullChildCount;
}
this.child3 = newChild;
if (this.child3 != null) {
++this.notNullChildCount;
}
break;
case 3:
if (this.child4 != null) {
--this.notNullChildCount;
}
this.child4 = newChild;
if (this.child4 != null) {
++this.notNullChildCount;
}
break;
case 4:
if (this.child5 != null) {
--this.notNullChildCount;
}
this.child5 = newChild;
if (this.child5 != null) {
++this.notNullChildCount;
}
break;
case 5:
if (this.child6 != null) {
--this.notNullChildCount;
}
this.child6 = newChild;
if (this.child6 != null) {
++this.notNullChildCount;
}
break;
case 6:
if (this.child7 != null) {
--this.notNullChildCount;
}
this.child7 = newChild;
if (this.child7 != null) {
++this.notNullChildCount;
}
break;
case 7:
if (this.child8 != null) {
--this.notNullChildCount;
}
this.child8 = newChild;
if (this.child8 != null) {
++this.notNullChildCount;
}
break;
default:
throw new IndexOutOfBoundsException();
}
}
@Override
@SuppressWarnings({"checkstyle:npathcomplexity", "checkstyle:returncount", "checkstyle:cyclomaticcomplexity"})
public boolean removeChild(N child) {
if (child != null) {
if (child == this.child1) {
return setChild1(null);
} else if (child == this.child2) {
return setChild2(null);
} else if (child == this.child3) {
return setChild3(null);
} else if (child == this.child4) {
return setChild4(null);
} else if (child == this.child5) {
return setChild5(null);
} else if (child == this.child6) {
return setChild6(null);
} else if (child == this.child7) {
return setChild7(null);
} else if (child == this.child8) {
return setChild8(null);
}
}
return false;
}
/** Set the child 1.
*
* @param newChild is the new child
* @return <code>true</code> on success, otherwise <code>false</code>
*/
private boolean setChild1(N newChild) {
if (this.child1 == newChild) {
return false;
}
if (this.child1 != null) {
this.child1.setParentNodeReference(null, true);
--this.notNullChildCount;
firePropertyChildRemoved(0, this.child1);
}
if (newChild != null) {
final N oldParent = newChild.getParentNode();
if (oldParent != this) {
newChild.removeFromParent();
}
}
this.child1 = newChild;
if (newChild != null) {
newChild.setParentNodeReference(toN(), true);
++this.notNullChildCount;
firePropertyChildAdded(0, newChild);
}
return true;
}
/** Set the child 2.
*
* @param newChild is the new child
* @return <code>true</code> on success, otherwise <code>false</code>
*/
private boolean setChild2(N newChild) {
if (this.child2 == newChild) {
return false;
}
if (this.child2 != null) {
this.child2.setParentNodeReference(null, true);
--this.notNullChildCount;
firePropertyChildRemoved(1, this.child2);
}
if (newChild != null) {
final N oldParent = newChild.getParentNode();
if (oldParent != this) {
newChild.removeFromParent();
}
}
this.child2 = newChild;
if (newChild != null) {
newChild.setParentNodeReference(toN(), true);
++this.notNullChildCount;
firePropertyChildAdded(1, newChild);
}
return true;
}
/** Set the child 3.
*
* @param newChild is the new child
* @return <code>true</code> on success, otherwise <code>false</code>
*/
private boolean setChild3(N newChild) {
if (this.child3 == newChild) {
return false;
}
if (this.child3 != null) {
this.child3.setParentNodeReference(null, true);
--this.notNullChildCount;
firePropertyChildRemoved(2, this.child3);
}
if (newChild != null) {
final N oldParent = newChild.getParentNode();
if (oldParent != this) {
newChild.removeFromParent();
}
}
this.child3 = newChild;
if (newChild != null) {
newChild.setParentNodeReference(toN(), true);
++this.notNullChildCount;
firePropertyChildAdded(2, newChild);
}
return true;
}
/** Set the child 4.
*
* @param newChild is the new child
* @return <code>true</code> on success, otherwise <code>false</code>
*/
private boolean setChild4(N newChild) {
if (this.child4 == newChild) {
return false;
}
if (this.child4 != null) {
this.child4.setParentNodeReference(null, true);
--this.notNullChildCount;
firePropertyChildRemoved(3, this.child4);
}
if (newChild != null) {
final N oldParent = newChild.getParentNode();
if (oldParent != this) {
newChild.removeFromParent();
}
}
this.child4 = newChild;
if (newChild != null) {
newChild.setParentNodeReference(toN(), true);
++this.notNullChildCount;
firePropertyChildAdded(3, newChild);
}
return true;
}
/** Set the child 5.
*
* @param newChild is the new child
* @return <code>true</code> on success, otherwise <code>false</code>
*/
@SuppressWarnings("checkstyle:magicnumber")
private boolean setChild5(N newChild) {
if (this.child5 == newChild) {
return false;
}
if (this.child5 != null) {
this.child5.setParentNodeReference(null, true);
--this.notNullChildCount;
firePropertyChildRemoved(4, this.child5);
}
if (newChild != null) {
final N oldParent = newChild.getParentNode();
if (oldParent != this) {
newChild.removeFromParent();
}
}
this.child5 = newChild;
if (newChild != null) {
newChild.setParentNodeReference(toN(), true);
++this.notNullChildCount;
firePropertyChildAdded(4, newChild);
}
return true;
}
/** Set the child 6.
*
* @param newChild is the new child
* @return <code>true</code> on success, otherwise <code>false</code>
*/
@SuppressWarnings("checkstyle:magicnumber")
private boolean setChild6(N newChild) {
if (this.child6 == newChild) {
return false;
}
if (this.child6 != null) {
this.child6.setParentNodeReference(null, true);
--this.notNullChildCount;
firePropertyChildRemoved(5, this.child5);
}
if (newChild != null) {
final N oldParent = newChild.getParentNode();
if (oldParent != this) {
newChild.removeFromParent();
}
}
this.child6 = newChild;
if (newChild != null) {
newChild.setParentNodeReference(toN(), true);
++this.notNullChildCount;
firePropertyChildAdded(5, newChild);
}
return true;
}
/** Set the child 7.
*
* @param newChild is the new child
* @return <code>true</code> on success, otherwise <code>false</code>
*/
@SuppressWarnings("checkstyle:magicnumber")
private boolean setChild7(N newChild) {
if (this.child7 == newChild) {
return false;
}
if (this.child7 != null) {
this.child7.setParentNodeReference(null, true);
--this.notNullChildCount;
firePropertyChildRemoved(6, this.child7);
}
if (newChild != null) {
final N oldParent = newChild.getParentNode();
if (oldParent != this) {
newChild.removeFromParent();
}
}
this.child7 = newChild;
if (newChild != null) {
newChild.setParentNodeReference(toN(), true);
++this.notNullChildCount;
firePropertyChildAdded(6, newChild);
}
return true;
}
/** Set the child 8.
*
* @param newChild is the new child
* @return <code>true</code> on success, otherwise <code>false</code>
*/
@SuppressWarnings("checkstyle:magicnumber")
private boolean setChild8(N newChild) {
if (this.child8 == newChild) {
return false;
}
if (this.child8 != null) {
this.child8.setParentNodeReference(null, true);
--this.notNullChildCount;
firePropertyChildRemoved(7, this.child8);
}
if (newChild != null) {
final N oldParent = newChild.getParentNode();
if (oldParent != this) {
newChild.removeFromParent();
}
}
this.child8 = newChild;
if (newChild != null) {
newChild.setParentNodeReference(toN(), true);
++this.notNullChildCount;
firePropertyChildAdded(7, newChild);
}
return true;
}
@Pure
@Override
@SuppressWarnings("checkstyle:booleanexpressioncomplexity")
public boolean isLeaf() {
return this.child1 == null && this.child2 == null && this.child3 == null && this.child4 == null
&& this.child5 == null && this.child6 == null && this.child7 == null && this.child8 == null;
}
@Override
@SuppressWarnings({"checkstyle:magicnumber", "checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity"})
public void getChildren(Object[] array) {
if (array != null) {
if (array.length > 0) {
array[0] = this.child1;
}
if (array.length > 1) {
array[1] = this.child2;
}
if (array.length > 2) {
array[2] = this.child3;
}
if (array.length > 3) {
array[3] = this.child4;
}
if (array.length > 4) {
array[4] = this.child5;
}
if (array.length > 5) {
array[5] = this.child6;
}
if (array.length > 6) {
array[6] = this.child7;
}
if (array.length > 7) {
array[7] = this.child8;
}
}
}
@Override
@SuppressWarnings({"checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity"})
public int getMinHeight() {
return 1 + MathUtil.min(
this.child1 != null ? this.child1.getMinHeight() : 0,
this.child2 != null ? this.child2.getMinHeight() : 0,
this.child3 != null ? this.child3.getMinHeight() : 0,
this.child4 != null ? this.child4.getMinHeight() : 0,
this.child5 != null ? this.child5.getMinHeight() : 0,
this.child6 != null ? this.child6.getMinHeight() : 0,
this.child7 != null ? this.child7.getMinHeight() : 0,
this.child8 != null ? this.child8.getMinHeight() : 0);
}
@Pure
@Override
@SuppressWarnings({"checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity"})
public int getMaxHeight() {
return 1 + MathUtil.max(
this.child1 != null ? this.child1.getMaxHeight() : 0,
this.child2 != null ? this.child2.getMaxHeight() : 0,
this.child3 != null ? this.child3.getMaxHeight() : 0,
this.child4 != null ? this.child4.getMaxHeight() : 0,
this.child5 != null ? this.child5.getMaxHeight() : 0,
this.child6 != null ? this.child6.getMaxHeight() : 0,
this.child7 != null ? this.child7.getMaxHeight() : 0,
this.child8 != null ? this.child8.getMaxHeight() : 0);
}
@Override
@SuppressWarnings({"checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity"})
protected void getHeights(int currentHeight, List<Integer> heights) {
if (isLeaf()) {
heights.add(new Integer(currentHeight));
} else {
if (this.child1 != null) {
this.child1.getHeights(currentHeight + 1, heights);
}
if (this.child2 != null) {
this.child2.getHeights(currentHeight + 1, heights);
}
if (this.child3 != null) {
this.child3.getHeights(currentHeight + 1, heights);
}
if (this.child4 != null) {
this.child4.getHeights(currentHeight + 1, heights);
}
if (this.child5 != null) {
this.child5.getHeights(currentHeight + 1, heights);
}
if (this.child6 != null) {
this.child6.getHeights(currentHeight + 1, heights);
}
if (this.child7 != null) {
this.child7.getHeights(currentHeight + 1, heights);
}
if (this.child8 != null) {
this.child8.getHeights(currentHeight + 1, heights);
}
}
}
/**
* This is the generic implementation of a
* tree for which each node has eight children.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
public enum OctTreeZone {
/** This is the index of the child that correspond to
* the voxel at north-west and front position.
*/
NORTH_WEST_FRONT,
/** This is the index of the child that correspond to
* the voxel at north-west and back position.
*/
NORTH_WEST_BACK,
/** This is the index of the child that correspond to
* the voxel at north-east and front position.
*/
NORTH_EAST_FRONT,
/** This is the index of the child that correspond to
* the voxel at north-east and back position.
*/
NORTH_EAST_BACK,
/** This is the index of the child that correspond to
* the voxel at south-west and front position.
*/
SOUTH_WEST_FRONT,
/** This is the index of the child that correspond to
* the voxel at south-west and back position.
*/
SOUTH_WEST_BACK,
/** This is the index of the child that correspond to
* the voxel at south-east and front position.
*/
SOUTH_EAST_FRONT,
/** This is the index of the child that correspond to
* the voxel at south-east and back position.
*/
SOUTH_EAST_BACK;
/** Replies the zone corresponding to the given index.
* The index is the same as the ordinal value of the
* enumeration. If the given index does not correspond
* to an ordinal value, <code>null</code> is replied.
*
* @param index the index.
* @return the zone or <code>null</code>
*/
@Pure
public static OctTreeZone fromInteger(int index) {
if (index < 0) {
return null;
}
final OctTreeZone[] nodes = values();
if (index >= nodes.length) {
return null;
}
return nodes[index];
}
}
/**
* This is the generic implementation of a oct
* tree.
*
* @param <D> is the type of the data inside the tree
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
public static class DefaultOctTreeNode<D> extends OctTreeNode<D, DefaultOctTreeNode<D>> {
private static final long serialVersionUID = 3732643480212763103L;
/**
* Empty node.
*/
public DefaultOctTreeNode() {
super();
}
/** Construct a node.
* @param data are the initial user data
*/
public DefaultOctTreeNode(Collection<D> data) {
super(data);
}
/** Construct a node.
* @param data are the initial user data
*/
public DefaultOctTreeNode(D data) {
super(data);
}
}
}