Commit db61fbc7 authored by gsavin's avatar gsavin

Add degree limitation. Separate 2D/3D NTree.

parent 117f00d6
...@@ -49,8 +49,8 @@ import org.miv.pherd.geom.Point3; ...@@ -49,8 +49,8 @@ import org.miv.pherd.geom.Point3;
* represents the boid in the forces system. The boid particle in turn contains * represents the boid in the forces system. The boid particle in turn contains
* a {@link BoidForces} object that represents all the forces exercising on the * a {@link BoidForces} object that represents all the forces exercising on the
* boid. Globally, the {@link Boid} class acts on the graph and updates its * boid. Globally, the {@link Boid} class acts on the graph and updates its
* position, creating links toward other boids/nodes that it sees, whereas * position, creating links toward other boids/nodes that it sees, whereas the
* the {@link BoidForces} are used to compute the boid position. * {@link BoidForces} are used to compute the boid position.
* </p> * </p>
* *
* @author Guilhelm Savin * @author Guilhelm Savin
...@@ -109,11 +109,11 @@ public class Boid extends AdjacencyListNode { ...@@ -109,11 +109,11 @@ public class Boid extends AdjacencyListNode {
} }
public void checkNeighborhood(Boid... boids) { public void checkNeighborhood(Boid... boids) {
if(getGraph().getNode(getId()) == null) { if (getGraph().getNode(getId()) == null) {
System.err.printf("I do not exist any more !!!%n"); System.err.printf("I do not exist any more !!!%n");
return; return;
} }
if (boids != null) { if (boids != null) {
Iterator<Boid> it = getNeighborNodeIterator(); Iterator<Boid> it = getNeighborNodeIterator();
LinkedList<Boid> toRemove = null; LinkedList<Boid> toRemove = null;
...@@ -146,11 +146,14 @@ public class Boid extends AdjacencyListNode { ...@@ -146,11 +146,14 @@ public class Boid extends AdjacencyListNode {
} }
for (Boid b2 : boids) { for (Boid b2 : boids) {
if (getEdgeBetween(b2) == null) { if (getEdgeBetween(b2) == null
if(getGraph().getNode(b2.getId())!= null) && getDegree() < species.maxNeighborhood) {
getGraph().addEdge(getEdgeId(this, b2), this, b2); if (getGraph().getNode(b2.getId()) != null)
else System.err.printf("%s does not exists !!%n", b2.getId()); getGraph().addEdge(getEdgeId(this, b2), this, b2);
} else
System.err
.printf("%s does not exists !!%n", b2.getId());
}
} }
} else { } else {
while (getDegree() > 0) while (getDegree() > 0)
......
...@@ -133,8 +133,8 @@ public abstract class BoidForces { ...@@ -133,8 +133,8 @@ public abstract class BoidForces {
direction.scalarDiv(countAtt); direction.scalarDiv(countAtt);
attraction attraction
.set(barycenter.x - boid.getPosition().x, barycenter.y .set(barycenter.x - boid.getPosition().x, barycenter.y
- boid.getPosition().y, barycenter.z - boid.getPosition().y,
- boid.getPosition().z); barycenter.z - boid.getPosition().z);
} }
if (countRep > 0) { if (countRep > 0) {
...@@ -198,7 +198,7 @@ public abstract class BoidForces { ...@@ -198,7 +198,7 @@ public abstract class BoidForces {
public void moveBarycenter(Point3 p) { public void moveBarycenter(Point3 p) {
barycenter.move(p); barycenter.move(p);
} }
/** /**
* A boid particle p2 that is visible by p1 as been found, integrate it in * A boid particle p2 that is visible by p1 as been found, integrate it in
* the forces that apply to the boid p1. * the forces that apply to the boid p1.
...@@ -262,12 +262,15 @@ public abstract class BoidForces { ...@@ -262,12 +262,15 @@ public abstract class BoidForces {
nextPos.y = hi.y - aarea; nextPos.y = hi.y - aarea;
dir.data[1] = -dir.data[1]; dir.data[1] = -dir.data[1];
} }
if (nextPos.z + dir.data[2] <= lo.z + aarea) {
nextPos.z = lo.z + aarea; if (is3D()) {
dir.data[2] = -dir.data[2]; if (nextPos.z + dir.data[2] <= lo.z + aarea) {
} else if (nextPos.z + dir.data[2] >= hi.z - aarea) { nextPos.z = lo.z + aarea;
nextPos.z = hi.z - aarea; dir.data[2] = -dir.data[2];
dir.data[2] = -dir.data[2]; } else if (nextPos.z + dir.data[2] >= hi.z - aarea) {
nextPos.z = hi.z - aarea;
dir.data[2] = -dir.data[2];
}
} }
} }
...@@ -345,4 +348,6 @@ public abstract class BoidForces { ...@@ -345,4 +348,6 @@ public abstract class BoidForces {
public abstract Point3 getNextPosition(); public abstract Point3 getNextPosition();
public abstract Collection<Boid> getNeighborhood(); public abstract Collection<Boid> getNeighborhood();
public abstract boolean is3D();
} }
\ No newline at end of file
...@@ -549,6 +549,16 @@ public class BoidGraph extends AdjacencyListGraph { ...@@ -549,6 +549,16 @@ public class BoidGraph extends AdjacencyListGraph {
public void step() { public void step() {
step++; step++;
for (BoidSpecies sp : boidSpecies.values()) {
sp.terminateStep(step);
}
for (BoidGraphListener listener : boidGraphListeners) {
listener.step(step);
}
forcesFactory.step();
stepBegins(step); stepBegins(step);
} }
...@@ -720,23 +730,6 @@ public class BoidGraph extends AdjacencyListGraph { ...@@ -720,23 +730,6 @@ public class BoidGraph extends AdjacencyListGraph {
set(key, null); set(key, null);
} }
} }
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.SinkAdapter#stepBegins(java.lang.String,
* long, double)
*/
public void stepBegins(String sourceId, long timeId, double step) {
for (BoidSpecies sp : boidSpecies.values()) {
sp.terminateStep(step);
}
for (BoidGraphListener listener : boidGraphListeners) {
listener.step(step);
}
forcesFactory.step();
}
} }
private class BoidFactory implements NodeFactory<Boid> { private class BoidFactory implements NodeFactory<Boid> {
...@@ -762,7 +755,7 @@ public class BoidGraph extends AdjacencyListGraph { ...@@ -762,7 +755,7 @@ public class BoidGraph extends AdjacencyListGraph {
BoidGraph ctx = new BoidGraph(); BoidGraph ctx = new BoidGraph();
try { try {
ctx.loadDGSConfiguration("configExampleWithTwoSpecies.dgs"); ctx.loadDGSConfiguration("configExample.dgs");
} catch (Exception e1) { } catch (Exception e1) {
e1.printStackTrace(); e1.printStackTrace();
} }
......
...@@ -46,7 +46,7 @@ public class BoidSpecies implements Iterable<Boid> { ...@@ -46,7 +46,7 @@ public class BoidSpecies implements Iterable<Boid> {
* Kinds of parameters. * Kinds of parameters.
*/ */
public static enum Parameter { public static enum Parameter {
COUNT, ANGLE_OF_VIEW, VIEW_ZONE, SPEED_FACTOR, MAX_SPEED, MIN_SPEED, DIRECTION_FACTOR, ATTRACTION_FACTOR, REPULSION_FACTOR, INERTIA, FEAR_FACTOR, ADD_SPECIES_NAME_IN_UI_CLASS COUNT, ANGLE_OF_VIEW, VIEW_ZONE, SPEED_FACTOR, MAX_SPEED, MIN_SPEED, DIRECTION_FACTOR, ATTRACTION_FACTOR, REPULSION_FACTOR, INERTIA, FEAR_FACTOR, ADD_SPECIES_NAME_IN_UI_CLASS, MAX_NEIGHBORHOOD
} }
/** /**
...@@ -158,6 +158,8 @@ public class BoidSpecies implements Iterable<Boid> { ...@@ -158,6 +158,8 @@ public class BoidSpecies implements Iterable<Boid> {
protected int initialCount = 0; protected int initialCount = 0;
int maxNeighborhood = 20;
/** /**
* New default species with a random color. * New default species with a random color.
* *
...@@ -270,6 +272,13 @@ public class BoidSpecies implements Iterable<Boid> { ...@@ -270,6 +272,13 @@ public class BoidSpecies implements Iterable<Boid> {
case ADD_SPECIES_NAME_IN_UI_CLASS: case ADD_SPECIES_NAME_IN_UI_CLASS:
addSpeciesNameInUIClass = Boolean.parseBoolean(val); addSpeciesNameInUIClass = Boolean.parseBoolean(val);
break; break;
case MAX_NEIGHBORHOOD:
maxNeighborhood = (int) Double.parseDouble(val);
if (maxNeighborhood <= 0)
maxNeighborhood = Integer.MAX_VALUE;
break;
} }
} }
......
...@@ -5,7 +5,7 @@ null 0 0 ...@@ -5,7 +5,7 @@ null 0 0
# GraphStream attributes # GraphStream attributes
# #
cg ui.quality ui.antialias #cg ui.quality ui.antialias
cg ui.stylesheet="node { size: 4px; } node.moustik { fill-color: #1d1d1d; } edge { fill-color: grey; }" cg ui.stylesheet="node { size: 4px; } node.moustik { fill-color: #1d1d1d; } edge { fill-color: grey; }"
# #
...@@ -13,7 +13,7 @@ cg ui.stylesheet="node { size: 4px; } node.moustik { fill-color: #1d1d1d; } edge ...@@ -13,7 +13,7 @@ cg ui.stylesheet="node { size: 4px; } node.moustik { fill-color: #1d1d1d; } edge
# #
cg boids.max_steps=8000 cg boids.max_steps=8000
cg boids.random_seed=2132134879 #cg boids.random_seed=2132134879
cg boids.area=1 cg boids.area=1
cg boids.sleep_time=30 cg boids.sleep_time=30
...@@ -22,7 +22,7 @@ cg boids.sleep_time=30 ...@@ -22,7 +22,7 @@ cg boids.sleep_time=30
# #
cg boids.species.moustik=org.graphstream.boids.BoidSpecies # Create a new species using class BoidSpecies cg boids.species.moustik=org.graphstream.boids.BoidSpecies # Create a new species using class BoidSpecies
cg boids.species.moustik.angle_of_view=0 # Set angleOfView of this species to 0 cg boids.species.moustik.angle_of_view=0.25 # Set angleOfView of this species to 0
cg boids.species.moustik.view_zone=0.15 # Set viewZone cg boids.species.moustik.view_zone=0.15 # Set viewZone
cg boids.species.moustik.speed_factor=0.3 # Set speedFactor cg boids.species.moustik.speed_factor=0.3 # Set speedFactor
cg boids.species.moustik.max_speed=1 # Set maxSpeed cg boids.species.moustik.max_speed=1 # Set maxSpeed
...@@ -31,10 +31,13 @@ cg boids.species.moustik.direction_factor=0.1 # Set directionFactor ...@@ -31,10 +31,13 @@ cg boids.species.moustik.direction_factor=0.1 # Set directionFactor
cg boids.species.moustik.attraction_factor=0.5 # Set attractionFactor cg boids.species.moustik.attraction_factor=0.5 # Set attractionFactor
cg boids.species.moustik.repulsion_factor=0.001 # Set repulsionFactor cg boids.species.moustik.repulsion_factor=0.001 # Set repulsionFactor
cg boids.species.moustik.inertia=1.1 # Set inertia cg boids.species.moustik.inertia=1.1 # Set inertia
cg boids.species.moustik.max_neighborhood=10
cg boids.species.moustik.add_species_name_in_ui_class=true # Tell species to add its name in the 'ui.class' attribute cg boids.species.moustik.add_species_name_in_ui_class=true # Tell species to add its name in the 'ui.class' attribute
# of node, so we can define a custom css for each species. # of node, so we can define a custom css for each species.
# #
# This last command create 100 boids of the moustik species # This last command create 100 boids of the moustik species
# #
cg boids.species.moustik.count=100 cg boids.species.moustik.count=1000
cg boids.forces_factory=org.graphstream.boids.forces.ntree.NTreeForcesFactory
\ No newline at end of file
...@@ -5,7 +5,7 @@ null 0 0 ...@@ -5,7 +5,7 @@ null 0 0
# GraphStream attributes # GraphStream attributes
# #
cg ui.quality ui.antialias #cg ui.quality ui.antialias
cg ui.stylesheet="node.bee { size: 10px; fill-color: orange; } node.moustik { size: 4px; fill-color: #1d1d1d; } edge { fill-color: grey; }" cg ui.stylesheet="node.bee { size: 10px; fill-color: orange; } node.moustik { size: 4px; fill-color: #1d1d1d; } edge { fill-color: grey; }"
# #
...@@ -22,7 +22,7 @@ cg boids.sleep_time=30 ...@@ -22,7 +22,7 @@ cg boids.sleep_time=30
# #
cg boids.species.moustik=org.graphstream.boids.BoidSpecies cg boids.species.moustik=org.graphstream.boids.BoidSpecies
cg boids.species.moustik.angle_of_view=0 cg boids.species.moustik.angle_of_view=0.1
cg boids.species.moustik.view_zone=0.15 cg boids.species.moustik.view_zone=0.15
cg boids.species.moustik.speed_factor=0.3 cg boids.species.moustik.speed_factor=0.3
cg boids.species.moustik.max_speed=0.9 cg boids.species.moustik.max_speed=0.9
...@@ -44,7 +44,7 @@ cg boids.species.bee.speed_factor=0.3 ...@@ -44,7 +44,7 @@ cg boids.species.bee.speed_factor=0.3
cg boids.species.bee.max_speed=1 cg boids.species.bee.max_speed=1
cg boids.species.bee.min_speed=0.1 cg boids.species.bee.min_speed=0.1
cg boids.species.bee.direction_factor=0.2 cg boids.species.bee.direction_factor=0.2
cg boids.species.bee.attraction_factor=0.3 cg boids.species.bee.attraction_factor=0.4
cg boids.species.bee.repulsion_factor=0.005 cg boids.species.bee.repulsion_factor=0.005
cg boids.species.bee.inertia=1.1 cg boids.species.bee.inertia=1.1
cg boids.species.bee.add_species_name_in_ui_class=true cg boids.species.bee.add_species_name_in_ui_class=true
...@@ -53,7 +53,7 @@ cg boids.species.bee.add_species_name_in_ui_class=true ...@@ -53,7 +53,7 @@ cg boids.species.bee.add_species_name_in_ui_class=true
# This last command create 100 boids of the moustik species # This last command create 100 boids of the moustik species
# #
cg boids.species.moustik.count=100 cg boids.species.moustik.count=1000
cg boids.species.bee.count=100 cg boids.species.bee.count=100
cg boids.forces_factory=org.graphstream.boids.forces.greedy.GreedyForcesFactory cg boids.forces_factory=org.graphstream.boids.forces.ntree.NTreeForcesFactory
...@@ -56,7 +56,7 @@ public class GreedyForces extends BoidForces { ...@@ -56,7 +56,7 @@ public class GreedyForces extends BoidForces {
position.x = r.nextDouble() * (hi.x - lo.x) + lo.x; position.x = r.nextDouble() * (hi.x - lo.x) + lo.x;
position.y = r.nextDouble() * (hi.y - lo.y) + lo.y; position.y = r.nextDouble() * (hi.y - lo.y) + lo.y;
position.z = 0; position.z = 0;
nextPosition.copy(position); nextPosition.copy(position);
} }
...@@ -91,7 +91,7 @@ public class GreedyForces extends BoidForces { ...@@ -91,7 +91,7 @@ public class GreedyForces extends BoidForces {
if (isVisible(boid, b.getPosition())) if (isVisible(boid, b.getPosition()))
contacts.add(b); contacts.add(b);
} }
return contacts; return contacts;
} }
...@@ -103,4 +103,13 @@ public class GreedyForces extends BoidForces { ...@@ -103,4 +103,13 @@ public class GreedyForces extends BoidForces {
public Point3 getNextPosition() { public Point3 getNextPosition() {
return nextPosition; return nextPosition;
} }
/*
* (non-Javadoc)
*
* @see org.graphstream.boids.BoidForces#is3D()
*/
public boolean is3D() {
return false;
}
} }
...@@ -123,7 +123,7 @@ public class NTreeForces extends BoidForces { ...@@ -123,7 +123,7 @@ public class NTreeForces extends BoidForces {
*/ */
protected void forcesFromCell(Cell cell, Set<Boid> contacts) { protected void forcesFromCell(Cell cell, Set<Boid> contacts) {
Iterator<? extends Particle> particles = cell.getParticles(); Iterator<? extends Particle> particles = cell.getParticles();
while (particles.hasNext()) { while (particles.hasNext()) {
Particle particle = particles.next(); Particle particle = particles.next();
...@@ -150,17 +150,13 @@ public class NTreeForces extends BoidForces { ...@@ -150,17 +150,13 @@ public class NTreeForces extends BoidForces {
double x1 = cell.getSpace().getLoAnchor().x; double x1 = cell.getSpace().getLoAnchor().x;
double y1 = cell.getSpace().getLoAnchor().y; double y1 = cell.getSpace().getLoAnchor().y;
double z1 = cell.getSpace().getLoAnchor().z;
double x2 = cell.getSpace().getHiAnchor().x; double x2 = cell.getSpace().getHiAnchor().x;
double y2 = cell.getSpace().getHiAnchor().y; double y2 = cell.getSpace().getHiAnchor().y;
double z2 = cell.getSpace().getHiAnchor().z;
double X1 = source.getPosition().x - vz; double X1 = source.getPosition().x - vz;
double Y1 = source.getPosition().y - vz; double Y1 = source.getPosition().y - vz;
double Z1 = source.getPosition().z - vz;
double X2 = source.getPosition().x + vz; double X2 = source.getPosition().x + vz;
double Y2 = source.getPosition().y + vz; double Y2 = source.getPosition().y + vz;
double Z2 = source.getPosition().z + vz;
// Only when the area is before or after the cell there cannot // Only when the area is before or after the cell there cannot
// exist an intersection (case a and b). Else there must be an // exist an intersection (case a and b). Else there must be an
...@@ -182,9 +178,6 @@ public class NTreeForces extends BoidForces { ...@@ -182,9 +178,6 @@ public class NTreeForces extends BoidForces {
if (Y2 < y1 || Y1 > y2) if (Y2 < y1 || Y1 > y2)
return false; return false;
if (Z2 < z1 || Z1 > z2)
return false;
return true; return true;
} }
...@@ -202,4 +195,13 @@ public class NTreeForces extends BoidForces { ...@@ -202,4 +195,13 @@ public class NTreeForces extends BoidForces {
return neigh; return neigh;
} }
/*
* (non-Javadoc)
*
* @see org.graphstream.boids.BoidForces#is3D()
*/
public boolean is3D() {
return false;
}
} }
\ No newline at end of file
/*
* Copyright 2006 - 2012
* Antoine Dutot <antoine.dutot@graphstream-project.org>
* Guilhelm Savin <guilhelm.savin@graphstream-project.org>
*
* This file is part of gs-boids <http://graphstream-project.org>.
*
* gs-boids is a library whose purpose is to provide a boid behavior to a set of
* particles.
*
* This program is free software distributed under the terms of two licenses, the
* CeCILL-C license that fits European law, and the GNU Lesser General Public
* License. You can use, modify and/ or redistribute the software under the terms
* of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
* URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
*/
package org.graphstream.boids.forces.ntree;
import org.graphstream.boids.Boid;
import org.miv.pherd.ntree.Cell;
/**
* A basic definition of forces for a boid.
*
* <p>
* The kind of forces exercising on a boid can be changed to either use a n-tree
* or not, or to account for other kind of forces or another force model. This
* is the default force system that matches the basic boid definition as defined
* by Craig Reynolds.
* </p>
*
* @author Guilhelm Savin
* @author Antoine Dutot
*/
public class NTreeForces3D extends NTreeForces {
public NTreeForces3D(BoidParticle p) {
super(p);
}
/**
* A rectangular intersection function, is the boid view area intersecting
* the given cell?. This provides a quick lookup function to test if a cell
* must be explored or not. Later, a better test according to a spherical
* view zone will be done.
*
* @param cell
* The cell to test for intersection with the boid rectangular
* view area.
* @return True if there is an intersection.
*/
protected boolean intersection(Boid source, Cell cell) {
double vz = source.getSpecies().getViewZone();
double x1 = cell.getSpace().getLoAnchor().x;
double y1 = cell.getSpace().getLoAnchor().y;
double z1 = cell.getSpace().getLoAnchor().z;
double x2 = cell.getSpace().getHiAnchor().x;
double y2 = cell.getSpace().getHiAnchor().y;
double z2 = cell.getSpace().getHiAnchor().z;
double X1 = source.getPosition().x - vz;
double Y1 = source.getPosition().y - vz;
double Z1 = source.getPosition().z - vz;
double X2 = source.getPosition().x + vz;
double Y2 = source.getPosition().y + vz;
double Z2 = source.getPosition().z + vz;
// Only when the area is before or after the cell there cannot
// exist an intersection (case a and b). Else there must be an
// intersection (cases c, d, e and f).
//
// |-a-| +---------+ |-b-|
// | |
// |-c-| |-d-|
// | |
// | |-e-| |
// | |
// |-+----f----+-|
// | |
// +---------+
if (X2 < x1 || X1 > x2)
return false;
if (Y2 < y1 || Y1 > y2)
return false;
if (Z2 < z1 || Z1 > z2)
return false;
return true;
}
/*
* (non-Javadoc)
*
* @see org.graphstream.boids.BoidForces#is3D()
*/
public boolean is3D() {
return true;
}
}
\ No newline at end of file
...@@ -39,6 +39,7 @@ import org.miv.pherd.geom.Point3; ...@@ -39,6 +39,7 @@ import org.miv.pherd.geom.Point3;
import org.miv.pherd.ntree.Anchor; import org.miv.pherd.ntree.Anchor;
import org.miv.pherd.ntree.CellSpace; import org.miv.pherd.ntree.CellSpace;
import org.miv.pherd.ntree.OctreeCellSpace; import org.miv.pherd.ntree.OctreeCellSpace;
import org.miv.pherd.ntree.QuadtreeCellSpace;
public class NTreeForcesFactory implements BoidForcesFactory, ElementSink { public class NTreeForcesFactory implements BoidForcesFactory, ElementSink {
...@@ -50,12 +51,25 @@ public class NTreeForcesFactory implements BoidForcesFactory, ElementSink { ...@@ -50,12 +51,25 @@ public class NTreeForcesFactory implements BoidForcesFactory, ElementSink {
protected BoidGraph ctx; protected BoidGraph ctx;
protected boolean is3D;
public NTreeForcesFactory(BoidGraph ctx) { public NTreeForcesFactory(BoidGraph ctx) {
this(ctx, false);
}
public NTreeForcesFactory(BoidGraph ctx, boolean is3D) {
double area = ctx.getArea(); double area = ctx.getArea();
int maxParticlesPerCell = 10; int maxParticlesPerCell = 10;
this.space = new OctreeCellSpace(new Anchor(-area, -area, -area), this.is3D = is3D;
new Anchor(area, area, area));
if (is3D)
this.space = new OctreeCellSpace(new Anchor(-area, -area, -area),
new Anchor(area, area, area));
else
this.space = new QuadtreeCellSpace(new Anchor(-area, -area, 0),
new Anchor(area, area, 0));
this.pbox = new ParticleBox(maxParticlesPerCell, space, this.pbox = new ParticleBox(maxParticlesPerCell, space,
new BoidCellData()); new BoidCellData());
this.ctx = ctx; this.ctx = ctx;
...@@ -84,7 +98,7 @@ public class NTreeForcesFactory implements BoidForcesFactory, ElementSink { ...@@ -84,7 +98,7 @@ public class NTreeForcesFactory implements BoidForcesFactory, ElementSink {
*/ */
public BoidForces createNewForces(Boid b) { public BoidForces createNewForces(Boid b) {
BoidParticle p = new BoidParticle(ctx, b); BoidParticle p = new BoidParticle(ctx, b);
NTreeForces f = new NTreeForces(p); NTreeForces f = is3D ? new NTreeForces3D(p) : new NTreeForces(p);
return f; return f;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment