package org.geogebra.common.geogebra3D.kernel3D.geos;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.geogebra.common.awt.GColor;
import org.geogebra.common.geogebra3D.kernel3D.algos.AlgoJoinPoints3D;
import org.geogebra.common.geogebra3D.kernel3D.algos.AlgoPolygon3D;
import org.geogebra.common.geogebra3D.kernel3D.algos.AlgoPolyhedronPoints;
import org.geogebra.common.geogebra3D.kernel3D.transform.MirrorableAtPlane;
import org.geogebra.common.kernel.CircularDefinitionException;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.ConstructionElementCycle;
import org.geogebra.common.kernel.Path;
import org.geogebra.common.kernel.PathMover;
import org.geogebra.common.kernel.PathMoverGeneric;
import org.geogebra.common.kernel.PathParameter;
import org.geogebra.common.kernel.Region;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.algos.AlgoTransformation;
import org.geogebra.common.kernel.arithmetic.MyDouble;
import org.geogebra.common.kernel.arithmetic.NumberValue;
import org.geogebra.common.kernel.arithmetic.ValueType;
import org.geogebra.common.kernel.geos.Dilateable;
import org.geogebra.common.kernel.geos.GProperty;
import org.geogebra.common.kernel.geos.GeoBoolean;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoList;
import org.geogebra.common.kernel.geos.GeoNumberValue;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.geos.GeoPolygon;
import org.geogebra.common.kernel.geos.Traceable;
import org.geogebra.common.kernel.geos.Transformable;
import org.geogebra.common.kernel.geos.Translateable;
import org.geogebra.common.kernel.kernelND.GeoCoordSys2D;
import org.geogebra.common.kernel.kernelND.GeoDirectionND;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.kernelND.GeoLineND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.kernelND.GeoPolyhedronInterface;
import org.geogebra.common.kernel.kernelND.GeoSegmentND;
import org.geogebra.common.kernel.kernelND.HasHeight;
import org.geogebra.common.kernel.kernelND.HasSegments;
import org.geogebra.common.kernel.kernelND.HasVolume;
import org.geogebra.common.kernel.kernelND.RotateableND;
import org.geogebra.common.plugin.GeoClass;
import org.geogebra.common.util.debug.Log;
/**
* @author ggb3D
*
* Class describing a GeoPolyhedron
*
*/
public class GeoPolyhedron extends GeoElement3D
implements HasSegments, HasVolume, Traceable, RotateableND,
Translateable, MirrorableAtPlane, Transformable, Dilateable, HasHeight,
Path, GeoPolyhedronInterface, GeoNumberValue {
/** pyramid */
public static final int TYPE_PYRAMID = 1;
/** prism */
public static final int TYPE_PRISM = 3;
/** tetrahedron */
public static final int TYPE_TETRAHEDRON = 4;
/** cube */
public static final int TYPE_CUBE = 5;
/** octahedron */
public static final int TYPE_OCTAHEDRON = 6;
/** dodecahedron */
public static final int TYPE_DODECAHEDRON = 7;
/** icosahedron */
public static final int TYPE_ICOSAHEDRON = 8;
/** one of the TYPE_* constants */
int type;
/** vertices */
// protected ArrayList<GeoPoint3D> points;
/** edges index */
protected TreeMap<ConstructionElementCycle, Long> segmentsIndex;
/** max faces edges */
protected long segmentsIndexMax = 0;
/** edges */
protected TreeMap<Long, GeoSegment3D> segments;
/** edges linked (e.g basis of the prism -- WARNING: not always updated) */
private TreeMap<ConstructionElementCycle, GeoSegmentND> segmentsLinked;
/** faces index */
protected TreeMap<ConstructionElementCycle, Integer> polygonsIndex;
/** faces descriptions */
protected ArrayList<ConstructionElementCycle> polygonsDescriptions;
/** max faces index */
protected int polygonsIndexMax = 0;
/** faces */
protected TreeMap<Integer, GeoPolygon3D> polygons;
/** faces linked */
protected ArrayList<GeoPolygon> polygonsLinked;
/** points created by the algo */
protected ArrayList<GeoPoint3D> pointsCreated;
/** face currently constructed */
private ConstructionElementCycle currentFace;
/**
* constructor
*
* @param c
* construction
*/
public GeoPolyhedron(Construction c) {
super(c);
// moved from GeoElement's constructor
// must be called from the subclass, see
// http://benpryor.com/blog/2008/01/02/dont-call-subclass-methods-from-a-superclass-constructor/
setConstructionDefaults(); // init visual settings
polygonsIndex = new TreeMap<ConstructionElementCycle, Integer>();
polygonsDescriptions = new ArrayList<ConstructionElementCycle>();
polygons = new TreeMap<Integer, GeoPolygon3D>();
segmentsIndex = new TreeMap<ConstructionElementCycle, Long>();
segments = new TreeMap<Long, GeoSegment3D>();
segmentsLinked = new TreeMap<ConstructionElementCycle, GeoSegmentND>();
polygonsLinked = new ArrayList<GeoPolygon>();
pointsCreated = new ArrayList<GeoPoint3D>();
}
/**
* Update segments linked set with the polygon's segment
*
* @param polygon
* source polygon
*/
private void addSegmentsLinked(GeoPolygon polygon) {
if (polygon.getSegments() != null) {
for (GeoSegmentND segment : polygon.getSegments()) {
addSegmentLinked(segment);
}
}
}
/**
* update set of segments linked to this
*/
public void updateSegmentsLinked() {
segmentsLinked.clear();
for (GeoPolygon p : getPolygonsLinked()) {
addSegmentsLinked(p);
}
}
/**
*
* @return segments linked to the polyhedron (eg segments of the bottom)
*/
public Collection<GeoSegmentND> getSegmentsLinked() {
return segmentsLinked.values();
}
/**
*
* @return polygons linked to the polyhedron (eg the bottom)
*/
public Collection<GeoPolygon> getPolygonsLinked() {
return polygonsLinked;
}
/**
* Copy constructor
*
* @param polyhedron
* original
*/
public GeoPolyhedron(GeoPolyhedron polyhedron) {
this(polyhedron.getConstruction());
set(polyhedron);
}
/**
* set the type of polyhedron
*
* @param type
* one of the TYPE_* constants
*/
public void setType(int type) {
this.type = type;
}
/**
*
* @return the type of polyhedron
*/
public int getType() {
return type;
}
/**
* start a new face
*/
public void startNewFace() {
currentFace = new ConstructionElementCycle();
}
/**
* add the point to the current face and to the point list if it's a new one
*
* @param point
* vertex
*/
public void addPointToCurrentFace(GeoPointND point) {
currentFace.add(point);
}
/**
* ends the current face and store it in the faces list
*/
public void endCurrentFace() {
currentFace.setDirection();
// Application.debug(polygonsIndexMax);
// add to index
polygonsIndex.put(currentFace, Integer.valueOf(polygonsIndexMax));
polygonsDescriptions.add(currentFace);
polygonsIndexMax++;
}
/**
*
* @return index for polygon described by current constructing face (null if
* not exists)
*/
public Integer getCurrentFaceIndex() {
currentFace.setDirection();
Integer index = polygonsIndex.get(currentFace);
int ret;
if (index == null) {
ret = -1;
} else {
ret = index;
}
Log.debug(currentFace + ": " + ret);
return index;
}
/**
*
* @return current face descriptor
*/
public ConstructionElementCycle getCurrentFace() {
return currentFace;
}
/**
*
* @return constructed polygons indices
*/
public Collection<Integer> getPolygonsIndices() {
return polygonsIndex.values();
}
/**
* last face index (for pyramid/prism)
*/
private int topFaceIndex;
/**
* says that current face created is the last face (for pyramid/prism) (warn
* : call it AFTER endCurrentFace())
*/
public void setCurrentFaceIsTopFace() {
topFaceIndex = polygonsIndexMax - 1;
}
/**
* creates a polygon corresponding to the index
*
* @param index
* index of the polygon
* @return polygon corresponding
*/
public GeoPolygon3D createPolygon(int index) {
currentFace = polygonsDescriptions.get(index);
// vertices of the face
GeoPointND[] p = new GeoPointND[currentFace.size()];
// edges linked to the face
GeoSegmentND[] s = new GeoSegmentND[currentFace.size()];
GeoPointND endPoint = (GeoPointND) currentFace.get(0);
p[0] = endPoint; // first point for the polygon
GeoPointND firstPoint = endPoint;
int j;
for (j = 1; j < currentFace.size(); j++) {
// creates edges
GeoPointND startPoint = endPoint;
endPoint = (GeoPointND) currentFace.get(j);
s[j - 1] = createSegment(startPoint, endPoint);
// points for the polygon
p[j] = endPoint;
}
// last segment
s[j - 1] = createSegment(endPoint, firstPoint);
GeoPolygon3D polygon = createPolygon(p, index);
polygon.setSegments(s);
return polygon;
}
/**
* update the faces
*/
public void createFaces() {
for (int index = 0; index < polygonsDescriptions.size(); index++) {
createPolygon(index);
}
}
/**
* create a polygon joining the given points
*
* @param points
* vertices of the polygon
* @param index
* index in polygons map
* @return the polygon
*/
public GeoPolygon3D createPolygon(GeoPointND[] points, int index) {
AlgoPolygon3D algo = new AlgoPolygon3D(cons, points, false, this);
cons.removeFromConstructionList(algo);
GeoPolygon3D polygon = (GeoPolygon3D) algo.getPoly();
// refresh color to ensure polygons have same color as polyhedron:
polygon.setObjColor(getObjectColor());
polygon.setAlphaValue(getAlphaValue());
polygon.setLineThickness(getLineThickness());
polygon.setLineType(getLineType());
// force init labels called to avoid polygon to draw edges
polygon.setInitLabelsCalled(true);
if (condShowObject != null) {
try {
polygon.setShowObjectCondition(getShowObjectCondition());
} catch (Exception e) {
// circular definition
}
}
// put the polygon into the collection
polygons.put(index, polygon);
return polygon;
}
/**
* add the polygon as a polygon linked to this (e.g basis of a prism)
*
* @param polygon
* existing polygon
*/
public void addPolygonLinked(GeoPolygon polygon) {
polygonsLinked.add(polygon);
addSegmentsLinked(polygon);
polygon.addMeta(this);
}
/**
* add the point as created point (by algo)
*
* @param point
* vertex
*/
public void addPointCreated(GeoPoint3D point) {
pointsCreated.add(point);
}
/**
* return a segment joining startPoint and endPoint if this segment already
* exists in segments, return the already stored one
*
* @param startPoint
* the start point
* @param endPoint
* the end point
* @return the segment
*/
public GeoSegmentND createSegment(GeoPointND startPoint,
GeoPointND endPoint) {
// Application.debug(startPoint.getLabel() + endPoint.getLabel());
ConstructionElementCycle key = ConstructionElementCycle
.segmentDescription((GeoElement) startPoint,
(GeoElement) endPoint);
// check if this segment is not already created
if (segmentsIndex.containsKey(key)) {
// Application.debug(startPoint.getLabel() + endPoint.getLabel());
// App.error("segmentsIndex : "+key);
return segments.get(segmentsIndex.get(key));
}
// check if this segment is not a segment linked
if (segmentsLinked.containsKey(key)) {
// App.error("segmentsLinked : "+key);
return segmentsLinked.get(key);
}
return createNewSegment(startPoint, endPoint, key);
}
/**
* create new segment (if not already exists)
*
* @param startPoint
* start point
* @param endPoint
* end point
* @param key
* key for segment
* @return new segment
*/
protected GeoSegmentND createNewSegment(GeoPointND startPoint,
GeoPointND endPoint, ConstructionElementCycle key) {
// App.error("new segment : "+key);
GeoSegment3D segment;
AlgoJoinPoints3D algoSegment = new AlgoJoinPoints3D(cons, startPoint,
endPoint, this, GeoClass.SEGMENT3D);
cons.removeFromConstructionList(algoSegment);
segment = (GeoSegment3D) algoSegment.getCS();
// refresh color to ensure segments have same color as polyhedron:
segment.setObjColor(getObjectColor());
segment.setLineThickness(getLineThickness());
segment.setLineType(getLineType());
if (condShowObject != null) {
try {
segment.setShowObjectCondition(getShowObjectCondition());
} catch (Exception e) {
// circular definition
}
}
storeSegment(segment, key);
return segment;
}
/**
* store the segment with the given key
*
* @param segment
* segment
* @param key
* key
*/
protected void storeSegment(GeoSegment3D segment,
ConstructionElementCycle key) {
Long index = Long.valueOf(segmentsIndexMax);
segmentsIndex.put(key, index);
segments.put(index, segment);
segmentsIndexMax++;
}
/**
* @param startPoint
* segment start point
* @param endPoint
* segment end point
* @return segment with given endpoints or null if not found
*/
public GeoSegmentND getSegment(GeoPointND startPoint, GeoPointND endPoint) {
// Application.debug(startPoint.getLabel() + endPoint.getLabel());
ConstructionElementCycle key = ConstructionElementCycle
.segmentDescription((GeoElement) startPoint,
(GeoElement) endPoint);
// check if this segment is already created
if (segmentsIndex.containsKey(key)) {
return segments.get(segmentsIndex.get(key));
}
// check if this segment is a segment linked
if (segmentsLinked.containsKey(key)) {
return segmentsLinked.get(key);
}
return null;
}
/**
* @param segment
* existing segment
*/
public void addSegmentLinked(GeoSegmentND segment) {
ConstructionElementCycle key = ConstructionElementCycle
.segmentDescription(segment.getStartPointAsGeoElement(),
segment.getEndPointAsGeoElement());
// Log.debug("linked : "+key);
segmentsLinked.put(key, segment);
}
/**
* @param labels
* output labels, only first one is used
*/
public void defaultLabels(String[] labels) {
if (cons.isSuppressLabelsActive()) { // for redefine
return;
}
if (labels == null || labels.length == 0) {
setLabel(null);
} else {
setLabel(labels[0]);
}
defaultPolygonsLabels();
defaultSegmentLabels();
}
private boolean allLabelsAreSet = false;
/**
* Returns whether the method initLabels() was called for this polygon. This
* is important to know whether the segments have gotten labels.
*
* @return true iff all labels (of created polygons, segments, points) are
* set.
*/
final public boolean allLabelsAreSet() {
return allLabelsAreSet;
}
/**
* set init labels called
*
* @param flag
* flag for labels
*/
public void setAllLabelsAreSet(boolean flag) {
allLabelsAreSet = flag;
}
/**
* Inits the labels of this polyhedron, its faces and edges. labels[0] for
* polyhedron itself, labels[1..n] for faces and edges,
*
* @param labels
* labels for this, points, faces, edges
*/
public void initLabels(String[] labels) {
// Application.printStacktrace("");
if (cons.isSuppressLabelsActive()) { // for redefine
return;
}
if (labels == null || labels.length == 0) {
initLabels(new String[1]);
return;
}
setAllLabelsAreSet(true);
/*
* String s="labels:\n"; for (int i=0; i<labels.length; i++)
* s+=labels[i]+"\n"; s+="points: "+pointsCreated.size()+"\npolygons: "
* +polygons .size()+"\nsegments: "+segments.size();
* Application.debug(s);
*/
// first label for polyhedron itself
setLabel(labels[0]);
int index = 1;
// labels for created points
if (labels.length - index < pointsCreated.size()) {
defaultPointsLabels();
defaultPolygonsLabels();
defaultSegmentLabels();
return;
}
for (GeoPoint3D point : pointsCreated) {
point.setLabel(labels[index]);
index++;
}
// labels for polygons
if (labels.length - index < polygons.size()) {
defaultPolygonsLabels();
defaultSegmentLabels();
return;
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setLabel(labels[index]);
// Application.debug("labels["+index+"]="+labels[index]);
index++;
}
// labels for segments
if (labels.length - index < segments.size()) {
defaultSegmentLabels();
return;
}
// labels for segments
for (GeoSegment3D segment : segments.values()) {
segment.setLabel(labels[index]);
// Application.debug("labels["+index+"]="+labels[index]+",\nsegment:"+segment.getParentAlgorithm());
index++;
}
}
private void defaultPointsLabels() {
for (GeoPointND point : pointsCreated) {
point.setLabel(null);
}
}
private StringBuilder sb = new StringBuilder();
/**
*
* @param geo
* @return level of usability of the label
*/
private static int usableLabel(GeoElement geo) {
if (!geo.isLabelSet()) {
return 2; // not usable
} else if (geo.getLabel(StringTemplate.defaultTemplate).contains("_")) {
return 2; // not usable
}
else {
return 0; // usable
}
}
private void defaultPolygonsLabels() {
for (Entry<ConstructionElementCycle, Integer> entry : polygonsIndex
.entrySet()) {
ConstructionElementCycle key = entry.getKey();
// stores points names and find the first
String label1 = null;
int labelUsability = 0;
String[] points = new String[key.size()];
int indexFirstPointName = 0;
int i = 0;
for (Iterator<GeoElementND> it = key.iterator(); it.hasNext()
&& (labelUsability < 2);) {
GeoElement p = (GeoElement) it.next();
labelUsability += usableLabel(p);
if (labelUsability < 2) {
points[i] = p.getLabel(StringTemplate.defaultTemplate);
if (points[i].compareToIgnoreCase(
points[indexFirstPointName]) < 0) {
indexFirstPointName = i;
}
i++;
}
}
if (labelUsability < 2) {
sb.setLength(0);
sb.append(getLoc().getPlain("Name.face"));
// sets the direction to the next first name
int indexSecondPointPlus = indexFirstPointName + 1;
if (indexSecondPointPlus == points.length) {
indexSecondPointPlus = 0;
}
int indexSecondPointMinus = indexFirstPointName - 1;
if (indexSecondPointMinus == -1) {
indexSecondPointMinus = points.length - 1;
}
if (points[indexSecondPointPlus].compareToIgnoreCase(
points[indexSecondPointMinus]) < 0) {
for (int j = indexFirstPointName; j < points.length; j++) {
sb.append(points[j]);
}
for (int j = 0; j < indexFirstPointName; j++) {
sb.append(points[j]);
}
} else {
for (int j = indexFirstPointName; j >= 0; j--) {
sb.append(points[j]);
}
for (int j = points.length
- 1; j > indexFirstPointName; j--) {
sb.append(points[j]);
}
}
label1 = sb.toString();
}
polygons.get(entry.getValue()).setLabel(label1);
}
}
private void defaultSegmentLabels() {
for (Entry<ConstructionElementCycle, Long> entry : segmentsIndex
.entrySet()) {
ConstructionElementCycle key = entry.getKey();
int labelUsability = 0;
String label1 = null;
String[] points = new String[2];
int i = 0;
for (Iterator<GeoElementND> it = key.iterator(); it.hasNext()
&& (labelUsability < 2);) {
GeoElement p = (GeoElement) it.next();
labelUsability += usableLabel(p);
if (labelUsability < 2) {
points[i] = p.getLabel(StringTemplate.defaultTemplate);
i++;
}
}
if (labelUsability < 2) {
sb.setLength(0);
sb.append(getLoc().getPlain("Name.edge"));
// sets the points names in order
if (points[0].compareToIgnoreCase(points[1]) < 0) {
sb.append(points[0]);
sb.append(points[1]);
} else {
sb.append(points[1]);
sb.append(points[0]);
}
label1 = sb.toString();
}
segments.get(entry.getValue()).setLabel(label1);
}
}
@Override
public GeoSegmentND[] getSegments() {
GeoSegmentND[] ret = new GeoSegmentND[segments.size()];
int i = 0;
for (GeoSegment3D segment : segments.values()) {
ret[i] = segment;
i++;
}
return ret;
}
/**
* @return polyhedron's edges
*/
public GeoSegment3D[] getSegments3D() {
GeoSegment3D[] ret = new GeoSegment3D[segments.size()];
int i = 0;
for (GeoSegment3D segment : segments.values()) {
ret[i] = segment;
i++;
}
return ret;
}
/**
* @return polyhedron's faces
*/
public GeoPolygon[] getFaces() {
GeoPolygon[] polygonsArray = new GeoPolygon[polygonsLinked.size()
+ polygons.size()];
int index = 0;
for (GeoPolygon polygon : polygonsLinked) {
polygonsArray[index] = polygon;
index++;
}
for (GeoPolygon polygon : polygons.values()) {
polygonsArray[index] = polygon;
index++;
}
return polygonsArray;
}
/**
* @return polyhedron's faces
*/
public GeoPolygon3D[] getFaces3D() {
GeoPolygon3D[] polygonsArray = new GeoPolygon3D[polygons.size()];
int index = 0;
for (GeoPolygon3D polygon : polygons.values()) {
polygonsArray[index] = polygon;
index++;
}
return polygonsArray;
}
/**
* @return faces as collection
*/
public Collection<GeoPolygon3D> getFacesCollection() {
return polygons.values();
}
/**
* @param index
* face index
* @return face
*/
public GeoPolygon getFace(int index) {
int polygonsLinkedSize = polygonsLinked.size();
if (index < polygonsLinkedSize) {
return polygonsLinked.get(index);
}
return polygons.get(index - polygonsLinkedSize);
}
/**
* @return total number of faces
*/
public int getFacesSize() {
return polygonsLinked.size() + polygons.size();
}
/**
* @param index
* index
* @return face
*/
public GeoPolygon3D getFace3D(int index) {
return polygons.get(index);
}
/**
*
* @return collection of polygons created by this
*/
public Collection<GeoPolygon3D> getPolygons() {
return polygons.values();
}
/**
* set all polygons to reverse normals (for 3D drawing)
*
* @param flag
* flag
*/
public void setReverseNormalsForDrawing(boolean flag) {
for (GeoPolygon polygon : polygonsLinked) {
polygon.setReverseNormalForDrawing(flag);
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setReverseNormalForDrawing(flag);
}
}
/**
* set all polygons to reverse normals
*/
public void setReverseNormals() {
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setReverseNormal();
}
setReverseNormalsForDrawing(true);
}
@Override
public void setEuclidianVisible(boolean visible) {
super.setEuclidianVisible(visible);
if (cons.isFileLoading()) {
return;
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setEuclidianVisible(visible, false);
}
for (GeoPolygon polygon : polygonsLinked) {
polygon.setEuclidianVisible(visible, false);
}
for (GeoSegment3D segment : segments.values()) {
segment.setEuclidianVisible(visible);
}
for (GeoSegmentND segment : getSegmentsLinked()) {
segment.setEuclidianVisible(visible);
}
}
@Override
public void setObjColor(GColor color) {
super.setObjColor(color);
if (cons.isFileLoading()) {
return;
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setObjColor(color);
polygon.updateVisualStyle(GProperty.COLOR);
}
for (GeoPolygon polygon : polygonsLinked) {
polygon.setObjColor(color);
polygon.updateVisualStyle(GProperty.COLOR);
}
for (GeoSegment3D segment : segments.values()) {
segment.setObjColor(color);
segment.updateVisualStyle(GProperty.COLOR);
}
for (GeoSegmentND segment : getSegmentsLinked()) {
segment.setObjColor(color);
segment.updateVisualStyle(GProperty.COLOR);
}
getKernel().notifyRepaint();
}
@Override
public void removeColorFunction() {
if (getColorFunction() == null) {
return;
}
super.removeColorFunction();
if (polygons == null || cons.isFileLoading()) {
return;
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.removeColorFunction();
polygon.updateVisualStyle(GProperty.COLOR);
}
for (GeoPolygon polygon : polygonsLinked) {
polygon.removeColorFunction();
polygon.updateVisualStyle(GProperty.COLOR);
}
for (GeoSegment3D segment : segments.values()) {
segment.removeColorFunction();
segment.updateVisualStyle(GProperty.COLOR);
}
for (GeoSegmentND segment : getSegmentsLinked()) {
((GeoElement) segment).removeColorFunction();
segment.updateVisualStyle(GProperty.COLOR);
}
getKernel().notifyRepaint();
}
@Override
public void setColorFunction(final GeoList col) {
super.setColorFunction(col);
if (polygons == null || cons.isFileLoading()) {
return;
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setColorFunction(col);
polygon.updateVisualStyle(GProperty.COLOR);
}
for (GeoPolygon polygon : polygonsLinked) {
polygon.setColorFunction(col);
polygon.updateVisualStyle(GProperty.COLOR);
}
for (GeoSegment3D segment : segments.values()) {
segment.setColorFunction(col);
segment.updateVisualStyle(GProperty.COLOR);
}
for (GeoSegmentND segment : getSegmentsLinked()) {
((GeoElement) segment).setColorFunction(col);
segment.updateVisualStyle(GProperty.COLOR);
}
getKernel().notifyRepaint();
}
@Override
public void setLineType(int type) {
super.setLineType(type);
if (polygons == null || cons.isFileLoading()) {
return;
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setLineType(type, false);
polygon.updateVisualStyle(GProperty.LINE_STYLE);
}
for (GeoPolygon polygon : polygonsLinked) {
polygon.setLineType(type, false);
polygon.updateVisualStyle(GProperty.LINE_STYLE);
}
for (GeoSegment3D segment : segments.values()) {
segment.setLineType(type);
segment.updateVisualStyle(GProperty.LINE_STYLE);
}
for (GeoSegmentND segment : getSegmentsLinked()) {
((GeoElement) segment).setLineType(type);
segment.updateVisualStyle(GProperty.LINE_STYLE);
}
}
@Override
public void setLayer(int layer2) {
super.setLayer(layer2);
if (polygons == null || cons.isFileLoading()) {
return;
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setLayer(layer2);
polygon.updateVisualStyle(GProperty.LAYER);
}
for (GeoPolygon polygon : polygonsLinked) {
polygon.setLayer(layer2);
polygon.updateVisualStyle(GProperty.LAYER);
}
for (GeoSegment3D segment : segments.values()) {
segment.setLayer(layer2);
segment.updateVisualStyle(GProperty.LAYER);
}
for (GeoSegmentND segment : getSegmentsLinked()) {
((GeoElement) segment).setLayer(layer2);
segment.updateVisualStyle(GProperty.LAYER);
}
}
@Override
public void setLineTypeHidden(int type) {
super.setLineTypeHidden(type);
if (polygons == null || cons.isFileLoading()) {
return;
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setLineTypeHidden(type, false);
polygon.updateVisualStyle(GProperty.LINE_STYLE);
}
for (GeoPolygon polygon : polygonsLinked) {
polygon.setLineTypeHidden(type, false);
polygon.updateVisualStyle(GProperty.LINE_STYLE);
}
for (GeoSegment3D segment : segments.values()) {
segment.setLineTypeHidden(type);
segment.updateVisualStyle(GProperty.LINE_STYLE);
}
for (GeoSegmentND segment : getSegmentsLinked()) {
((GeoElement) segment).setLineTypeHidden(type);
segment.updateVisualStyle(GProperty.LINE_STYLE);
}
}
@Override
public void setLineThickness(int th) {
super.setLineThickness(th);
if (polygons == null || cons.isFileLoading()) {
return;
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setLineThickness(th, false);
polygon.update();
}
for (GeoPolygon polygon : polygonsLinked) {
polygon.setLineThickness(th, false);
polygon.updateVisualStyle(GProperty.LINE_STYLE);
}
for (GeoSegment3D segment : segments.values()) {
segment.setLineThickness(th);
segment.updateVisualStyle(GProperty.LINE_STYLE);
}
for (GeoSegmentND segment : getSegmentsLinked()) {
segment.setLineThickness(th);
segment.updateVisualStyle(GProperty.LINE_STYLE);
}
}
@Override
public void setLineThicknessOrVisibility(int th) {
super.setLineThickness(th);
if (polygons == null || cons.isFileLoading()) {
return;
}
GProperty prop = th > 0 ? GProperty.LINE_STYLE : GProperty.COMBINED;
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setLineThickness(th, false);
polygon.update();
}
for (GeoPolygon polygon : polygonsLinked) {
polygon.setLineThickness(th, false);
polygon.updateVisualStyle(prop);
}
for (GeoSegment3D segment : segments.values()) {
segment.setLineThicknessOrVisibility(th);
segment.updateVisualStyle(prop);
}
for (GeoSegmentND segment : getSegmentsLinked()) {
((GeoElement) segment).setLineThicknessOrVisibility(th);
segment.updateVisualStyle(prop);
}
}
@Override
public void setAlphaValue(double alpha) {
super.setAlphaValue(alpha);
if (cons.isFileLoading()) {
return;
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setAlphaValue(alpha);
polygon.updateVisualStyle(GProperty.COLOR);
}
for (GeoPolygon polygon : polygonsLinked) {
polygon.setAlphaValue(alpha);
polygon.updateVisualStyle(GProperty.COLOR);
}
getKernel().notifyRepaint();
}
@Override
public GeoElement copy() {
return new GeoPolyhedron(this);
}
@Override
public GeoClass getGeoClassType() {
return GeoClass.POLYHEDRON;
}
@Override
public String getTypeString() {
switch (type) {
case TYPE_PRISM:
return "Prism";
case TYPE_PYRAMID:
return "Pyramid";
case TYPE_TETRAHEDRON:
return "Tetrahedron";
case TYPE_CUBE:
return "Cube";
case TYPE_OCTAHEDRON:
return "Octahedron";
case TYPE_DODECAHEDRON:
return "Dodecahedron";
case TYPE_ICOSAHEDRON:
return "Icosahedron";
default:
return "Polyhedron";
}
}
@Override
public boolean isDefined() {
return isDefined;
}
@Override
public boolean isEqual(GeoElementND Geo) {
// TODO Auto-generated method stub
return false;
}
static private Comparator<GeoPointND> pointIdComparator = null;
@Override
public void set(GeoElementND geo) {
if (geo.isGeoPolyhedron()) {
GeoPolyhedron polyhedron = (GeoPolyhedron) geo;
isDefined = polyhedron.isDefined;
// global
type = polyhedron.type;
setVolume(polyhedron.getVolume());
setArea(polyhedron.getArea());
setOrientedHeight(polyhedron.getOrientedHeight());
topFaceIndex = polyhedron.topFaceIndex;
if (!polyhedron.polygonsLinked.isEmpty()) {
// in this case, polyhedron has a bottom face linked
topFaceIndex++;
}
// init copy points list
if (copyPoints == null) {
if (pointIdComparator == null) {
pointIdComparator = new Comparator<GeoPointND>() {
@Override
public int compare(GeoPointND o1, GeoPointND o2) {
if (o1.getID() < o2.getID()) {
return -1;
}
if (o1.getID() > o2.getID()) {
return 1;
}
return 0;
}
};
}
copyPoints = new TreeMap<GeoPointND, GeoPoint3D>(
pointIdComparator);
}
int index;
// set segments
index = 0;
for (GeoSegmentND s : polyhedron.segmentsLinked.values()) {
if (setSegment(index, s)) {
index++;
}
}
for (GeoSegment3D s : polyhedron.segments.values()) {
if (setSegment(index, s)) {
index++;
}
}
// set last segments undefined
if (!segments.isEmpty()) {
for (int i = index; i <= segments.lastKey(); i++) {
segments.get((long) i).setUndefined();
}
}
// set polygons
index = 0;
for (GeoPolygon p : polyhedron.polygonsLinked) {
if (setPolygon(index, p)) {
index++;
}
}
for (GeoPolygon p : polyhedron.polygons.values()) {
if (setPolygon(index, p)) {
index++;
}
}
// set points values
for (Map.Entry<GeoPointND, GeoPoint3D> entry : copyPoints
.entrySet()) {
// set copy point to original point
entry.getValue().set(entry.getKey());
}
AlgoElement algo = getParentAlgorithm();
if (!(algo instanceof AlgoTransformation)) {
// we need it e.g. for polyhedron0 = polyhedron, for lists
updatePolygonsAndSegmentsAlgos();
}
}
}
private TreeMap<GeoPointND, GeoPoint3D> copyPoints;
private GeoPoint3D getCopyPoint(GeoPointND point) {
GeoPoint3D copyPoint = copyPoints.get(point);
if (copyPoint == null) {
copyPoint = new GeoPoint3D(point);
copyPoints.put(point, copyPoint);
}
return copyPoint;
}
private boolean setPolygon(int index, GeoPolygon p) {
if (!p.isDefined()) {
return false;
}
int pointsLength = p.getPointsLength();
if (pointsLength == 0) {
return false;
}
// set list of points
GeoPoint3D[] pointsList = new GeoPoint3D[pointsLength];
int i = 0;
for (GeoPointND point : p.getPointsND()) {
GeoPoint3D copyPoint = getCopyPoint(point);
pointsList[i] = copyPoint;
i++;
}
// set polygon
GeoPolygon3D poly = polygons.get(index);
if (poly == null) {
startNewFace();
for (GeoPointND point : pointsList) {
addPointToCurrentFace(point);
}
endCurrentFace();
createPolygon(index);
} else {
poly.modifyInputPoints(pointsList);
}
return true;
}
private boolean setSegment(long index, GeoSegmentND s) {
if (!s.isDefined()) {
return false;
}
GeoPoint3D startPoint = getCopyPoint(s.getStartPoint());
GeoPoint3D endPoint = getCopyPoint(s.getEndPoint());
ConstructionElementCycle key = ConstructionElementCycle
.segmentDescription(startPoint, endPoint);
if (index >= segmentsIndexMax) {
// seg = (GeoSegment3D)
createNewSegment(startPoint, endPoint, key);
} else {
GeoSegment3D seg = segments.get(index);
seg.modifyInputPoints(startPoint, endPoint);
segmentsIndex.put(key, index);
}
return true;
}
private boolean isDefined = true;
@Override
public void setUndefined() {
isDefined = false;
volume = Double.NaN;
/*
* for (GeoPolygon3D polygon : polygons.values()){
* polygon.setEuclidianVisible(visible,false); }
*
* for (GeoPolygon polygon : polygonsLinked){
* polygon.setEuclidianVisible(visible,false); }
*
* for (GeoSegment3D segment : segments.values()){
* segment.setEuclidianVisible(visible); }
*
* for (GeoSegmentND segment : segmentsLinked.values()){
* segment.setEuclidianVisible(visible); }
*/
}
/**
* Set defined flag to true
*/
public void setDefined() {
isDefined = true;
}
@Override
public boolean showInAlgebraView() {
return true;
}
@Override
protected boolean showInEuclidianView() {
return isDefined();
}
@Override
public boolean isGeoPolyhedron() {
return true;
}
@Override
public String toValueString(StringTemplate tpl) {
return kernel.format(getVolume(), tpl);
}
private StringBuilder sbToString = new StringBuilder(50);
@Override
public String toString(StringTemplate tpl) {
sbToString.setLength(0);
sbToString.append(label);
sbToString.append(" = ");
sbToString.append(kernel.format(getVolume(), tpl));
return sbToString.toString();
}
@Override
public String toStringMinimal(StringTemplate tpl) {
sbToString.setLength(0);
sbToString.append(regrFormat(getVolume()));
return sbToString.toString();
}
/** to be able to fill it with an alpha value */
@Override
public boolean isFillable() {
return true;
}
@Override
protected void getXMLtags(StringBuilder sbXml) {
getLineStyleXML(sbXml);
super.getXMLtags(sbXml);
}
// /////////////////////////////////////////
// GeoElement3DInterface
@Override
public Coords getLabelPosition() {
return Coords.O; // TODO
}
@Override
public void remove() {
for (GeoPolygon polygon : polygonsLinked) {
polygon.removeMeta(this);
}
// prevent from removing this when redefine a prism (see
// AlgoJoinPoints3D and AlgoPolygon)
if (this != getConstruction().getKeepGeo()) {
super.remove();
}
}
// //////////////////////////
// VOLUME
// //////////////////////////
private double volume = Double.NaN;
/**
* sets the volume
*
* @param volume
* volume
*/
public void setVolume(double volume) {
this.volume = volume;
}
@Override
public double getVolume() {
return volume;
}
@Override
public boolean hasFiniteVolume() {
return isDefined();
}
// //////////////////////////
// AREA
// //////////////////////////
private double area = Double.NaN;
/**
* sets the area (total area of the faces)
*
* @param area
* area
*/
public void setArea(double area) {
this.area = area;
}
/**
* Note : recalc area when from pyramid/prism algo
*
* @return area
*/
public double getArea() {
// if parent algo is prism/pyramid, update area from faces
AlgoElement algo = getParentAlgorithm();
if (algo instanceof AlgoPolyhedronPoints) {
area = 0;
for (GeoPolygon p : polygonsLinked) {
area += p.getArea();
}
for (GeoPolygon p : polygons.values()) {
if (p.isDefined()) {
area += p.getArea();
}
}
}
return area;
}
/**
* @return true when defined
*/
public boolean hasFiniteArea() {
return isDefined();
}
// ////////////////
// TRACE
// ////////////////
private boolean trace;
@Override
public boolean isTraceable() {
return true;
}
@Override
public boolean getTrace() {
return trace;
}
@Override
public void setTrace(boolean trace) {
this.trace = trace;
if (polygons == null) {
return;
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setTrace(trace);
}
for (GeoPolygon polygon : polygonsLinked) {
polygon.setTrace(trace);
}
for (GeoSegment3D segment : segments.values()) {
segment.setTrace(trace);
}
for (GeoSegmentND segment : getSegmentsLinked()) {
((Traceable) segment).setTrace(trace);
}
getKernel().notifyRepaint();
}
// ////////////////////////////////
// TRANSFORM
// ////////////////////////////////
@Override
public void rotate(NumberValue r, GeoPointND S) {
for (GeoPoint3D point : copyPoints.values()) {
if (point.isDefined()) {
point.rotate(r, S);
}
}
updatePolygonsAndSegmentsAlgos();
}
@Override
public void rotate(NumberValue r) {
for (GeoPoint3D point : copyPoints.values()) {
if (point.isDefined()) {
point.rotate(r);
}
}
updatePolygonsAndSegmentsAlgos();
}
@Override
public void rotate(NumberValue r, GeoPointND S,
GeoDirectionND orientation) {
for (GeoPoint3D point : copyPoints.values()) {
if (point.isDefined()) {
point.rotate(r, S, orientation);
}
}
updatePolygonsAndSegmentsAlgos();
}
@Override
public void rotate(NumberValue r, GeoLineND line) {
for (GeoPoint3D point : copyPoints.values()) {
if (point.isDefined()) {
point.rotate(r, line);
}
}
updatePolygonsAndSegmentsAlgos();
}
@Override
final public boolean isTranslateable() {
return true;
}
@Override
public void translate(Coords v) {
for (GeoPoint3D point : copyPoints.values()) {
if (point.isDefined()) {
point.translate(v);
}
}
updatePolygonsAndSegmentsAlgos();
}
private void updatePolygonsAndSegmentsAlgos() {
for (GeoSegment3D seg : segments.values()) {
seg.getParentAlgorithm().update();
}
for (GeoPolygon3D p : polygons.values()) {
p.getParentAlgorithm().update();
}
}
// //////////////////////
// MIRROR
// //////////////////////
@Override
public void mirror(Coords Q) {
for (GeoPoint3D point : copyPoints.values()) {
if (point.isDefined()) {
point.mirror(Q);
}
}
updatePolygonsAndSegmentsAlgos();
}
@Override
public void mirror(GeoLineND g) {
for (GeoPoint3D point : copyPoints.values()) {
if (point.isDefined()) {
point.mirror(g);
}
}
updatePolygonsAndSegmentsAlgos();
}
@Override
public void mirror(GeoCoordSys2D plane) {
for (GeoPoint3D point : copyPoints.values()) {
if (point.isDefined()) {
point.mirror(plane);
}
}
updatePolygonsAndSegmentsAlgos();
}
// //////////////////////
// DILATE
// //////////////////////
@Override
public void dilate(NumberValue rval, Coords S) {
for (GeoPoint3D point : copyPoints.values()) {
if (point.isDefined()) {
point.dilate(rval, S);
}
}
updatePolygonsAndSegmentsAlgos();
double r = rval.getDouble();
double rAbs = Math.abs(r);
volume *= rAbs * rAbs * rAbs;
area *= rAbs * rAbs;
orientedHeight *= r;
}
/**
* oriented (positive or negative) height
*/
private double orientedHeight;
/**
* set oriented (positive or negative) height
*
* @param height
* height
*/
public void setOrientedHeight(double height) {
orientedHeight = height;
}
@Override
public double getOrientedHeight() {
return orientedHeight;
}
/**
*
* @return bottom face (for pyramid & prism)
*/
public GeoPolygon getBottomFace() {
if (polygonsLinked.isEmpty()) {
return polygons.get(0);
}
return polygonsLinked.get(0);
}
/**
*
* @return last face (for pyramid/prism)
*/
public GeoPolygon getTopFace() {
return polygons.get(topFaceIndex);
}
/**
*
* @return first side face (for prism)
*/
public GeoPolygon getFirstSideFace() {
if (polygonsLinked.isEmpty()) {
return polygons.get(1);
}
return polygons.get(0);
}
/**
*
* @return top point (for pyramid)
*/
public Coords getTopPoint() {
GeoPolygon p = getFirstSideFace();
return p.getPoint3D(p.getPointsLength() - 1);
}
// /////////////////////////////////
// Path interface
@Override
public boolean isPath() {
return true;
}
@Override
public PathMover createPathMover() {
return new PathMoverGeneric(this);
}
@Override
public double getMaxParameter() {
return segmentsLinked.size() + segments.size();
}
@Override
public double getMinParameter() {
return 0;
}
@Override
public boolean isClosedPath() {
return true;
}
@Override
public void pathChanged(GeoPointND PI) {
// if kernel doesn't use path/region parameters, do as if point changed
// its coords
if (!getKernel().usePathAndRegionParameters(PI)) {
pointChanged(PI);
return;
}
// TODO remove that
if (!(PI instanceof GeoPoint3D)) {
return;
}
GeoPoint3D P = (GeoPoint3D) PI;
PathParameter pp = P.getPathParameter();
// remember old parameter
double oldT = pp.getT();
// find the segment where the point lies
int index = (int) pp.getT();
GeoSegmentND seg;
if (index < segmentsLinked.size()) {
seg = (GeoSegmentND) segmentsLinked.values().toArray()[index];
} else {
seg = (GeoSegmentND) segments.values().toArray()[index
- segmentsLinked.size()];
}
// sets the path parameter for the segment, calc the new position of the
// point
pp.setT(pp.getT() - index);
seg.pathChanged(P);
// Log.debug(seg+" , "+oldT);
// recall the old parameter
pp.setT(oldT);
}
@Override
public void pointChanged(GeoPointND PI) {
// TODO remove that
if (!(PI instanceof GeoPoint3D)) {
return;
}
GeoPoint3D P = (GeoPoint3D) PI;
Coords coordsOld = P.getInhomCoords().copyVector();
// prevent from region bad coords calculations
Region region = P.getRegion();
P.setRegion(null);
double minDist = Double.POSITIVE_INFINITY;
Coords res = null;
double param = 0;
// find closest point on each segment
PathParameter pp = P.getPathParameter();
int i = 0;
for (GeoSegmentND segment : segmentsLinked.values()) {
P.setCoords(coordsOld, false); // prevent circular path.pointChanged
if (segment.isDefined()) {
segment.pointChanged(P);
}
double dist;// = P.getInhomCoords().sub(coordsOld).squareNorm();
// double dist = 0;
if (P.hasWillingCoords() && P.hasWillingDirection()) {
dist = P.getInhomCoords().distLine(P.getWillingCoords(),
P.getWillingDirection());
} else {
dist = P.getInhomCoords().sub(coordsOld).squareNorm();
}
if (dist < minDist) {
minDist = dist;
// remember closest point
res = P.getInhomCoords().copyVector();
param = i + pp.getT();
// Application.debug(i);
}
i++;
}
for (GeoSegmentND segment : segments.values()) {
P.setCoords(coordsOld, false); // prevent circular path.pointChanged
if (segment.isDefined()) {
segment.pointChanged(P);
}
double dist;// = P.getInhomCoords().sub(coordsOld).squareNorm();
// double dist = 0;
if (P.hasWillingCoords() && P.hasWillingDirection()) {
dist = P.getInhomCoords().distLine(P.getWillingCoords(),
P.getWillingDirection());
} else {
dist = P.getInhomCoords().sub(coordsOld).squareNorm();
}
if (dist < minDist) {
minDist = dist;
// remember closest point
res = P.getInhomCoords().copyVector();
param = i + pp.getT();
// Application.debug(i);
}
i++;
}
P.setCoords(res, false);
pp.setT(param);
P.setRegion(region);
}
@Override
public boolean isOnPath(GeoPointND PI, double eps) {
GeoPoint P = (GeoPoint) PI;
if (P.getPath() == this) {
return true;
}
// check if P is on one of the segments
for (GeoSegmentND segment : segmentsLinked.values()) {
if (segment.isDefined() && segment.isOnPath(P, eps)) {
return true;
}
}
for (GeoSegmentND segment : segments.values()) {
if (segment.isDefined() && segment.isOnPath(P, eps)) {
return true;
}
}
return false;
}
@Override
public void setShowObjectCondition(final GeoBoolean cond)
throws CircularDefinitionException {
super.setShowObjectCondition(cond);
if (cons.isFileLoading()) {
return;
}
for (GeoPoint3D point : pointsCreated) {
point.setShowObjectCondition(cond);
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.setShowObjectCondition(cond);
}
for (GeoPolygon polygon : polygonsLinked) {
polygon.setShowObjectCondition(cond);
}
for (GeoSegment3D segment : segments.values()) {
segment.setShowObjectCondition(cond);
}
for (GeoSegmentND segment : getSegmentsLinked()) {
((GeoElement) segment).setShowObjectCondition(cond);
}
}
@Override
public void updateVisualStyle(GProperty prop) {
super.updateVisualStyle(prop);
for (GeoPoint3D point : pointsCreated) {
point.updateVisualStyle(prop);
}
for (GeoPolygon3D polygon : polygons.values()) {
polygon.updateVisualStyle(prop);
}
for (GeoPolygon polygon : polygonsLinked) {
polygon.updateVisualStyle(prop);
}
for (GeoSegment3D segment : segments.values()) {
segment.updateVisualStyle(prop);
}
for (GeoSegmentND segment : getSegmentsLinked()) {
((GeoElement) segment).updateVisualStyle(prop);
}
}
@Override
public void setPointSizeOrVisibility(int size) {
for (GeoPoint3D point : pointsCreated) {
setPointSize(point, size);
}
if (getParentAlgorithm() != null) {
for (GeoElement point : getParentAlgorithm().getInput()) {
if (point.isGeoPoint()) {
setPointSize((GeoPointND) point, size);
}
}
}
for (GeoPolygon p : polygonsLinked) {
p.setPointSizeOrVisibility(size);
}
}
private static void setPointSize(GeoPointND point, int size) {
if (size > 0) {
point.setEuclidianVisibleIfNoConditionToShowObject(true);
point.setPointSize(size);
} else {
point.setEuclidianVisibleIfNoConditionToShowObject(false);
}
point.updateRepaint();
}
@Override
final public HitType getLastHitType() {
return HitType.ON_FILLING;
}
@Override
public MyDouble getNumber() {
return new MyDouble(kernel, getDouble());
}
@Override
public double getDouble() {
return getVolume();
}
@Override
public ValueType getValueType() {
return ValueType.NUMBER;
}
@Override
public void pseudoCentroid(Coords coords) {
coords.set(0, 0, 0);
int n = 0;
for (GeoSegment3D segment : segments.values()) {
pseudoCentroidAdd(coords, segment);
n++;
}
for (GeoSegmentND segment : segmentsLinked.values()) {
pseudoCentroidAdd(coords, segment);
n++;
}
coords.mulInside(0.5 / n);
}
final private static void pseudoCentroidAdd(Coords coords,
GeoSegmentND segment) {
coords.setAdd3(coords, segment.getStartInhomCoords());
coords.setAdd3(coords, segment.getEndInhomCoords());
}
}