S3DB layer (#475)
This commit is contained in:
parent
85342ddd84
commit
ca9786dca3
@ -41,7 +41,7 @@ import java.util.Set;
|
|||||||
|
|
||||||
public class BuildingLayer extends Layer implements TileLoaderThemeHook {
|
public class BuildingLayer extends Layer implements TileLoaderThemeHook {
|
||||||
|
|
||||||
private final static int BUILDING_LEVEL_HEIGHT = 280; // cm
|
protected final static int BUILDING_LEVEL_HEIGHT = 280; // cm
|
||||||
|
|
||||||
private final static int MIN_ZOOM = 17;
|
private final static int MIN_ZOOM = 17;
|
||||||
private final static int MAX_ZOOM = 17;
|
private final static int MAX_ZOOM = 17;
|
||||||
@ -52,7 +52,7 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
|
|||||||
private static final Object BUILDING_DATA = BuildingLayer.class.getName();
|
private static final Object BUILDING_DATA = BuildingLayer.class.getName();
|
||||||
|
|
||||||
// Can be replaced with Multimap in Java 8
|
// Can be replaced with Multimap in Java 8
|
||||||
private HashMap<Integer, List<BuildingElement>> mBuildings = new HashMap<>();
|
protected HashMap<Integer, List<BuildingElement>> mBuildings = new HashMap<>();
|
||||||
|
|
||||||
class BuildingElement {
|
class BuildingElement {
|
||||||
MapElement element;
|
MapElement element;
|
||||||
@ -93,6 +93,8 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
|
|||||||
@Override
|
@Override
|
||||||
public boolean process(MapTile tile, RenderBuckets buckets, MapElement element,
|
public boolean process(MapTile tile, RenderBuckets buckets, MapElement element,
|
||||||
RenderStyle style, int level) {
|
RenderStyle style, int level) {
|
||||||
|
// FIXME check why some buildings are processed up to 4 times (should avoid overhead)
|
||||||
|
// FIXME fix artifacts at tile borders
|
||||||
|
|
||||||
if (!(style instanceof ExtrusionStyle))
|
if (!(style instanceof ExtrusionStyle))
|
||||||
return false;
|
return false;
|
||||||
@ -128,7 +130,7 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
|
|||||||
* @param extrusion the style of map element
|
* @param extrusion the style of map element
|
||||||
* @param tile the tile which contains map element
|
* @param tile the tile which contains map element
|
||||||
*/
|
*/
|
||||||
private void processElement(MapElement element, ExtrusionStyle extrusion, MapTile tile) {
|
protected void processElement(MapElement element, ExtrusionStyle extrusion, MapTile tile) {
|
||||||
int height = 0; // cm
|
int height = 0; // cm
|
||||||
int minHeight = 0; // cm
|
int minHeight = 0; // cm
|
||||||
|
|
||||||
@ -162,7 +164,7 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
|
|||||||
*
|
*
|
||||||
* @param tile the tile which contains stored map elements
|
* @param tile the tile which contains stored map elements
|
||||||
*/
|
*/
|
||||||
private void processElements(MapTile tile) {
|
protected void processElements(MapTile tile) {
|
||||||
if (!mBuildings.containsKey(tile.hashCode()))
|
if (!mBuildings.containsKey(tile.hashCode()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
283
vtm/src/org/oscim/layers/tile/buildings/S3DBLayer.java
Normal file
283
vtm/src/org/oscim/layers/tile/buildings/S3DBLayer.java
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Gustl22
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License 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/>.
|
||||||
|
*/
|
||||||
|
package org.oscim.layers.tile.buildings;
|
||||||
|
|
||||||
|
import org.oscim.core.Box;
|
||||||
|
import org.oscim.core.GeometryBuffer;
|
||||||
|
import org.oscim.core.MapElement;
|
||||||
|
import org.oscim.core.Tag;
|
||||||
|
import org.oscim.core.TagSet;
|
||||||
|
import org.oscim.core.Tile;
|
||||||
|
import org.oscim.layers.tile.MapTile;
|
||||||
|
import org.oscim.layers.tile.vector.VectorTileLayer;
|
||||||
|
import org.oscim.map.Map;
|
||||||
|
import org.oscim.theme.styles.ExtrusionStyle;
|
||||||
|
import org.oscim.utils.ExtrusionUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.oscim.renderer.MapRenderer.COORD_SCALE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An experimental layer to display S3DB elements.
|
||||||
|
*/
|
||||||
|
public class S3DBLayer extends BuildingLayer {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(S3DBLayer.class);
|
||||||
|
|
||||||
|
private final float TILE_SCALE = (ExtrusionUtils.REF_TILE_SIZE / (Tile.SIZE * COORD_SCALE));
|
||||||
|
|
||||||
|
private boolean mColored = true;
|
||||||
|
|
||||||
|
public S3DBLayer(Map map, VectorTileLayer tileLayer) {
|
||||||
|
super(map, tileLayer, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isColored() {
|
||||||
|
return mColored;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColored(boolean colored) {
|
||||||
|
mColored = colored;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void complete(MapTile tile, boolean success) {
|
||||||
|
super.complete(tile, success);
|
||||||
|
// Do stuff here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processElement(MapElement element, ExtrusionStyle extrusion, MapTile tile) {
|
||||||
|
float groundScale = tile.getGroundScale();
|
||||||
|
|
||||||
|
int maxHeight = 0; // cm
|
||||||
|
int minHeight = 0; // cm
|
||||||
|
int roofHeight = 0;
|
||||||
|
|
||||||
|
// Get roof height
|
||||||
|
String v = element.tags.getValue(Tag.KEY_ROOF_HEIGHT);
|
||||||
|
if (v != null) {
|
||||||
|
roofHeight = (int) (Float.parseFloat(v) * 100);
|
||||||
|
} else if ((v = element.tags.getValue(Tag.KEY_ROOF_LEVELS)) != null) {
|
||||||
|
roofHeight = (int) (Float.parseFloat(v) * BUILDING_LEVEL_HEIGHT);
|
||||||
|
} else if ((v = element.tags.getValue(Tag.KEY_ROOF_ANGLE)) != null) {
|
||||||
|
Box bb = null;
|
||||||
|
for (int k = 0; k < element.index[0]; k += 2) {
|
||||||
|
float p1 = element.points[k];
|
||||||
|
float p2 = element.points[k + 1];
|
||||||
|
if (bb == null)
|
||||||
|
bb = new Box(p1, p2, p1, p2);
|
||||||
|
else {
|
||||||
|
bb.add(p1, p2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bb != null) {
|
||||||
|
float maxSize = (int) Math.max(bb.getHeight(), bb.getWidth()) * TILE_SCALE;
|
||||||
|
roofHeight = (int) ((Float.parseFloat(v) / 45.f) * (maxSize * 15)); // Angle is simplified, 15 is some constant, may depend on lat
|
||||||
|
}
|
||||||
|
} else if ((v = element.tags.getValue(Tag.KEY_ROOF_SHAPE)) != null && !v.equals(Tag.VALUE_FLAT)) {
|
||||||
|
roofHeight = (2 * BUILDING_LEVEL_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get building height
|
||||||
|
v = element.tags.getValue(Tag.KEY_HEIGHT);
|
||||||
|
if (v != null) {
|
||||||
|
maxHeight = (int) (Float.parseFloat(v) * 100);
|
||||||
|
} else {
|
||||||
|
// #TagFromTheme: generalize level/height tags
|
||||||
|
if ((v = element.tags.getValue(Tag.KEY_BUILDING_LEVELS)) != null) {
|
||||||
|
maxHeight = (int) (Float.parseFloat(v) * BUILDING_LEVEL_HEIGHT);
|
||||||
|
maxHeight += roofHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maxHeight == 0)
|
||||||
|
maxHeight = extrusion.defaultHeight * 100;
|
||||||
|
|
||||||
|
v = element.tags.getValue(Tag.KEY_MIN_HEIGHT);
|
||||||
|
if (v != null)
|
||||||
|
minHeight = (int) (Float.parseFloat(v) * 100);
|
||||||
|
else {
|
||||||
|
// #TagFromTheme: level/height tags
|
||||||
|
if ((v = element.tags.getValue(Tag.KEY_BUILDING_MIN_LEVEL)) != null)
|
||||||
|
minHeight = (int) (Float.parseFloat(v) * BUILDING_LEVEL_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get building color
|
||||||
|
Integer bColor = null;
|
||||||
|
if (mColored) {
|
||||||
|
if (element.tags.containsKey(Tag.KEY_BUILDING_COLOR)) {
|
||||||
|
bColor = S3DBUtils.getColor(element.tags.getValue(Tag.KEY_BUILDING_COLOR), false);
|
||||||
|
} else if (element.tags.containsKey(Tag.KEY_BUILDING_MATERIAL)) {
|
||||||
|
bColor = S3DBUtils.getMaterialColor(element.tags.getValue(Tag.KEY_BUILDING_MATERIAL), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bColor == null) {
|
||||||
|
bColor = extrusion.colorTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale x, y and z axis
|
||||||
|
ExtrusionUtils.mapPolyCoordScale(element);
|
||||||
|
float minHeightS = ExtrusionUtils.mapGroundScale(minHeight, groundScale) * TILE_SCALE;
|
||||||
|
float maxHeightS = ExtrusionUtils.mapGroundScale(maxHeight, groundScale) * TILE_SCALE;
|
||||||
|
float minRoofHeightS = ExtrusionUtils.mapGroundScale(maxHeight - roofHeight, groundScale) * TILE_SCALE;
|
||||||
|
|
||||||
|
// Process building and roof
|
||||||
|
processRoof(element, tile, minRoofHeightS, maxHeightS, bColor);
|
||||||
|
if (S3DBUtils.calcOutlines(element, minHeightS, minRoofHeightS)) {
|
||||||
|
get(tile).addMeshElement(element, groundScale, bColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void processElements(MapTile tile) {
|
||||||
|
if (!mBuildings.containsKey(tile.hashCode()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
List<BuildingElement> tileBuildings = mBuildings.get(tile.hashCode());
|
||||||
|
Set<BuildingElement> rootBuildings = new HashSet<>();
|
||||||
|
for (BuildingElement partBuilding : tileBuildings) {
|
||||||
|
if (!partBuilding.isPart)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
TagSet partTags = partBuilding.element.tags;
|
||||||
|
String refId = partTags.getValue(Tag.KEY_REF); // #TagFromTheme
|
||||||
|
refId = refId == null ? partTags.getValue("root_id") : refId; // Mapzen
|
||||||
|
if (refId == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Search buildings which inherit parts
|
||||||
|
for (BuildingElement rootBuilding : tileBuildings) {
|
||||||
|
if (rootBuilding.isPart
|
||||||
|
|| !(refId.equals(rootBuilding.element.tags.getValue(Tag.KEY_ID))))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (mColored) {
|
||||||
|
TagSet rootTags = rootBuilding.element.tags;
|
||||||
|
for (int i = 0; i < rootTags.size(); i++) {
|
||||||
|
Tag rTag = rootTags.get(i);
|
||||||
|
if ((rTag.key.equals(Tag.KEY_COLOR) && !partTags.containsKey(Tag.KEY_MATERIAL)
|
||||||
|
|| rTag.key.equals(Tag.KEY_ROOF_COLOR) && !partTags.containsKey(Tag.KEY_ROOF_MATERIAL)
|
||||||
|
|| rTag.key.equals(Tag.KEY_ROOF_SHAPE))
|
||||||
|
&& !partTags.containsKey(rTag.key)) {
|
||||||
|
partTags.add(rTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rootBuildings.add(rootBuilding);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tileBuildings.removeAll(rootBuildings); // root buildings aren't rendered
|
||||||
|
|
||||||
|
for (BuildingElement buildingElement : tileBuildings) {
|
||||||
|
processElement(buildingElement.element, buildingElement.style, tile);
|
||||||
|
}
|
||||||
|
mBuildings.remove(tile.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the roof parts of building.
|
||||||
|
*
|
||||||
|
* @param element the MapElement which needs a roof
|
||||||
|
* @param tile the tile which contains map element
|
||||||
|
* @param minHeight the height of the underlying building
|
||||||
|
* @param maxHeight the height of the roof + minHeight (whole building)
|
||||||
|
* @param buildingColor the color of main building
|
||||||
|
*/
|
||||||
|
private void processRoof(MapElement element, MapTile tile, float minHeight, float maxHeight, int buildingColor) {
|
||||||
|
Integer roofColor = null;
|
||||||
|
String v;
|
||||||
|
|
||||||
|
if (mColored) {
|
||||||
|
v = element.tags.getValue(Tag.KEY_ROOF_COLOR);
|
||||||
|
if (v != null)
|
||||||
|
roofColor = S3DBUtils.getColor(v, true);
|
||||||
|
else if ((v = element.tags.getValue(Tag.KEY_ROOF_MATERIAL)) != null)
|
||||||
|
roofColor = S3DBUtils.getMaterialColor(v, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean roofOrientationAcross = false;
|
||||||
|
if ((v = element.tags.getValue(Tag.KEY_ROOF_ORIENTATION)) != null) {
|
||||||
|
if (v.equals(Tag.VALUE_ACROSS)) {
|
||||||
|
roofOrientationAcross = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calc roof shape
|
||||||
|
v = element.tags.getValue(Tag.KEY_ROOF_SHAPE);
|
||||||
|
if (v == null) {
|
||||||
|
v = Tag.VALUE_FLAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
float groundScale = tile.getGroundScale();
|
||||||
|
|
||||||
|
GeometryBuffer gElement = new GeometryBuffer(element);
|
||||||
|
GeometryBuffer specialParts = null;
|
||||||
|
if (roofColor == null) roofColor = buildingColor;
|
||||||
|
boolean success = false;
|
||||||
|
|
||||||
|
switch (v) {
|
||||||
|
case Tag.VALUE_DOME:
|
||||||
|
case Tag.VALUE_ONION:
|
||||||
|
success = S3DBUtils.calcCircleMesh(gElement, minHeight, maxHeight, v);
|
||||||
|
break;
|
||||||
|
case Tag.VALUE_ROUND:
|
||||||
|
case Tag.VALUE_SALTBOX:
|
||||||
|
case Tag.VALUE_GABLED:
|
||||||
|
case Tag.VALUE_GAMBREL:
|
||||||
|
specialParts = new GeometryBuffer(0, 0); // No data in GeometryBuffer needed
|
||||||
|
success = S3DBUtils.calcRidgeMesh(gElement, minHeight, maxHeight, roofOrientationAcross, true, specialParts);
|
||||||
|
break;
|
||||||
|
case Tag.VALUE_MANSARD:
|
||||||
|
case Tag.VALUE_HALF_HIPPED:
|
||||||
|
case Tag.VALUE_HIPPED:
|
||||||
|
success = S3DBUtils.calcRidgeMesh(gElement, minHeight, maxHeight, roofOrientationAcross, false, null);
|
||||||
|
break;
|
||||||
|
case Tag.VALUE_SKILLION:
|
||||||
|
// ROOF_SLOPE_DIRECTION is not supported yet
|
||||||
|
String roofDirection = element.tags.getValue(Tag.KEY_ROOF_DIRECTION);
|
||||||
|
float roofDegree = 0;
|
||||||
|
if (roofDirection != null) {
|
||||||
|
roofDegree = Float.parseFloat(roofDirection);
|
||||||
|
}
|
||||||
|
specialParts = new GeometryBuffer(element);
|
||||||
|
success = S3DBUtils.calcSkillionMesh(gElement, minHeight, maxHeight, roofDegree, specialParts);
|
||||||
|
break;
|
||||||
|
case Tag.VALUE_PYRAMIDAL:
|
||||||
|
success = S3DBUtils.calcPyramidalMesh(gElement, minHeight, maxHeight);
|
||||||
|
break;
|
||||||
|
case Tag.VALUE_FLAT:
|
||||||
|
default:
|
||||||
|
success = S3DBUtils.calcFlatMesh(gElement, minHeight);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
get(tile).addMeshElement(gElement, groundScale, roofColor);
|
||||||
|
if (specialParts != null) {
|
||||||
|
get(tile).addMeshElement(specialParts, groundScale, buildingColor);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("Roof calculation failed: " + element.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,7 @@ package org.oscim.renderer.bucket;
|
|||||||
import org.oscim.backend.canvas.Color;
|
import org.oscim.backend.canvas.Color;
|
||||||
import org.oscim.core.GeometryBuffer;
|
import org.oscim.core.GeometryBuffer;
|
||||||
import org.oscim.core.Tile;
|
import org.oscim.core.Tile;
|
||||||
|
import org.oscim.utils.ExtrusionUtils;
|
||||||
import org.oscim.utils.FastMath;
|
import org.oscim.utils.FastMath;
|
||||||
import org.oscim.utils.KeyMap;
|
import org.oscim.utils.KeyMap;
|
||||||
import org.oscim.utils.KeyMap.HashItem;
|
import org.oscim.utils.KeyMap.HashItem;
|
||||||
@ -361,13 +362,9 @@ public class ExtrusionBucket extends RenderBucket {
|
|||||||
float[] points = element.points;
|
float[] points = element.points;
|
||||||
|
|
||||||
/* 10 cm steps */
|
/* 10 cm steps */
|
||||||
float sfactor = 1 / 10f;
|
/* match height with ground resolution (meter per pixel) */
|
||||||
height *= sfactor;
|
height = ExtrusionUtils.mapGroundScale(height, mGroundResolution);
|
||||||
minHeight *= sfactor;
|
minHeight = ExtrusionUtils.mapGroundScale(minHeight, mGroundResolution);
|
||||||
|
|
||||||
/* match height with ground resultion (meter per pixel) */
|
|
||||||
height /= mGroundResolution;
|
|
||||||
minHeight /= mGroundResolution;
|
|
||||||
|
|
||||||
boolean complexOutline = false;
|
boolean complexOutline = false;
|
||||||
boolean simpleOutline = true;
|
boolean simpleOutline = true;
|
||||||
|
|||||||
51
vtm/src/org/oscim/utils/ExtrusionUtils.java
Normal file
51
vtm/src/org/oscim/utils/ExtrusionUtils.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012, 2013 Hannes Janetzek
|
||||||
|
* Copyright 2018 Gustl22
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License 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/>.
|
||||||
|
*/
|
||||||
|
package org.oscim.utils;
|
||||||
|
|
||||||
|
import org.oscim.core.GeometryBuffer;
|
||||||
|
import org.oscim.core.Tile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class with extrusion helper methods.
|
||||||
|
*/
|
||||||
|
public final class ExtrusionUtils {
|
||||||
|
|
||||||
|
public static final float REF_TILE_SIZE = 4096.0f; // Standard ref tile size
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param value the height value
|
||||||
|
* @param groundScale the ground scale of tile
|
||||||
|
* @return the corresponding height in the specific tile
|
||||||
|
*/
|
||||||
|
public static float mapGroundScale(float value, float groundScale) {
|
||||||
|
/* match height with ground resolution (meter per pixel) */
|
||||||
|
return value / (groundScale * 10); // 10 cm steps
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map the raw buffer scale to scale of coordinates
|
||||||
|
*/
|
||||||
|
public static void mapPolyCoordScale(GeometryBuffer buffer) {
|
||||||
|
float tileScale = REF_TILE_SIZE / Tile.SIZE;
|
||||||
|
float[] points = buffer.points;
|
||||||
|
for (int pPos = 0; pPos < buffer.pointNextPos; pPos++) {
|
||||||
|
points[pPos] = points[pPos] * tileScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExtrusionUtils() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -20,6 +20,7 @@ package org.oscim.utils.geom;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
// TODO Utils can be improved e.g. by avoiding object creations
|
||||||
public final class GeometryUtils {
|
public final class GeometryUtils {
|
||||||
|
|
||||||
private GeometryUtils() {
|
private GeometryUtils() {
|
||||||
@ -101,19 +102,19 @@ public final class GeometryUtils {
|
|||||||
if (bisection[0] == 0 && bisection[1] == 0) {
|
if (bisection[0] == 0 && bisection[1] == 0) {
|
||||||
// 90 degree to v1
|
// 90 degree to v1
|
||||||
bisection[0] = v1[1];
|
bisection[0] = v1[1];
|
||||||
bisection[1] = v1[0];
|
bisection[1] = -v1[0];
|
||||||
}
|
}
|
||||||
return bisection;
|
return bisection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param a first vector
|
* @param a first vector
|
||||||
* @param b second vector (same length as a)
|
* @param b second vector
|
||||||
* @return a - b
|
* @return a - b
|
||||||
*/
|
*/
|
||||||
public static float[] diffVec(float[] a, float[] b) {
|
public static float[] diffVec(float[] a, float[] b) {
|
||||||
float[] diff = new float[a.length];
|
float[] diff = new float[Math.min(a.length, b.length)];
|
||||||
for (int i = 0; i < a.length; i++) {
|
for (int i = 0; i < diff.length; i++) {
|
||||||
diff[i] = a[i] - b[i];
|
diff[i] = a[i] - b[i];
|
||||||
}
|
}
|
||||||
return diff;
|
return diff;
|
||||||
@ -121,12 +122,12 @@ public final class GeometryUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param a first vector
|
* @param a first vector
|
||||||
* @param b second vector (same length as a)
|
* @param b second vector
|
||||||
* @return a + b
|
* @return a + b
|
||||||
*/
|
*/
|
||||||
public static float[] sumVec(float[] a, float[] b) {
|
public static float[] sumVec(float[] a, float[] b) {
|
||||||
float[] add = new float[a.length];
|
float[] add = new float[Math.min(a.length, b.length)];
|
||||||
for (int i = 0; i < a.length; i++) {
|
for (int i = 0; i < add.length; i++) {
|
||||||
add[i] = b[i] + a[i];
|
add[i] = b[i] + a[i];
|
||||||
}
|
}
|
||||||
return add;
|
return add;
|
||||||
@ -182,6 +183,18 @@ public final class GeometryUtils {
|
|||||||
return Math.sqrt(dx * dx + dy * dy);
|
return Math.sqrt(dx * dx + dy * dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param pP point
|
||||||
|
* @param pL point of line
|
||||||
|
* @param vL vector of line
|
||||||
|
* @return the minimum distance between line and point
|
||||||
|
*/
|
||||||
|
public static float distancePointLine2D(float[] pP, float[] pL, float[] vL) {
|
||||||
|
float[] vPL = diffVec(pL, pP);
|
||||||
|
float[] vPS = diffVec(vPL, scale(vL, dotProduct(vPL, vL)));
|
||||||
|
return (float) Math.sqrt(dotProduct(vPS, vPS));
|
||||||
|
}
|
||||||
|
|
||||||
public static double dotProduct(float[] p, int a, int b, int c) {
|
public static double dotProduct(float[] p, int a, int b, int c) {
|
||||||
|
|
||||||
double ux = (p[b] - p[a]);
|
double ux = (p[b] - p[a]);
|
||||||
@ -260,14 +273,11 @@ public final class GeometryUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return more than 0 if points of triangle are clockwise, 0 if triangle is a line,
|
* @return a positive value, if pA-pB-pC makes a counter-clockwise turn,
|
||||||
* else less than 0.
|
* negative for clockwise turn, and zero if the points are collinear.
|
||||||
*/
|
*/
|
||||||
public static float isTrisClockwise(float[] pA, float[] pB, float[] pC) {
|
public static float isTrisClockwise(float[] pA, float[] pB, float[] pC) {
|
||||||
float v = (pB[0] - pA[0]) * (pB[1] + pA[1]);
|
return (pB[0] - pA[0]) * (pC[1] - pA[1]) - (pB[1] - pA[1]) * (pC[0] - pA[0]);
|
||||||
v += (pC[0] - pB[0]) * (pC[1] + pB[1]);
|
|
||||||
v += (pA[0] - pC[0]) * (pA[1] + pC[1]);
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user