org.bimserver.GeometryGenerator Maven / Gradle / Ivy
package org.bimserver;
/******************************************************************************
* Copyright (C) 2009-2016 BIMserver.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see {@literal }.
*****************************************************************************/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.NotImplementedException;
import org.bimserver.database.DatabaseSession;
import org.bimserver.database.OldQuery;
import org.bimserver.emf.IdEObject;
import org.bimserver.emf.IfcModelInterface;
import org.bimserver.emf.IfcModelInterfaceException;
import org.bimserver.emf.OidProvider;
import org.bimserver.emf.PackageMetaData;
import org.bimserver.emf.Schema;
import org.bimserver.geometry.Matrix;
import org.bimserver.geometry.Vector;
import org.bimserver.ifc.BasicIfcModel;
import org.bimserver.models.geometry.GeometryData;
import org.bimserver.models.geometry.GeometryFactory;
import org.bimserver.models.geometry.GeometryInfo;
import org.bimserver.models.geometry.GeometryPackage;
import org.bimserver.models.geometry.Vector3f;
import org.bimserver.models.ifc2x3tc1.IfcShapeRepresentation;
import org.bimserver.models.store.RenderEnginePluginConfiguration;
import org.bimserver.models.store.User;
import org.bimserver.models.store.UserSettings;
import org.bimserver.plugins.ModelHelper;
import org.bimserver.plugins.PluginConfiguration;
import org.bimserver.plugins.PluginManager;
import org.bimserver.plugins.objectidms.HideAllInversesObjectIDM;
import org.bimserver.plugins.renderengine.EntityNotFoundException;
import org.bimserver.plugins.renderengine.IndexFormat;
import org.bimserver.plugins.renderengine.Precision;
import org.bimserver.plugins.renderengine.RenderEngine;
import org.bimserver.plugins.renderengine.RenderEngineException;
import org.bimserver.plugins.renderengine.RenderEngineFilter;
import org.bimserver.plugins.renderengine.RenderEngineGeometry;
import org.bimserver.plugins.renderengine.RenderEngineInstance;
import org.bimserver.plugins.renderengine.RenderEngineModel;
import org.bimserver.plugins.renderengine.RenderEnginePlugin;
import org.bimserver.plugins.renderengine.RenderEngineSettings;
import org.bimserver.plugins.serializers.Serializer;
import org.bimserver.plugins.serializers.SerializerException;
import org.bimserver.plugins.serializers.SerializerPlugin;
import org.bimserver.shared.exceptions.UserException;
import org.bimserver.utils.CollectionUtils;
import org.bimserver.utils.Formatters;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GeometryGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(GeometryGenerator.class);
private final BimServer bimServer;
private final Map hashes = new ConcurrentHashMap();
private EClass productClass;
private EClass productRepresentationClass;
private EStructuralFeature geometryFeature;
private EStructuralFeature representationFeature;
private EStructuralFeature representationsFeature;
private PackageMetaData packageMetaData;
private AtomicLong bytesSaved = new AtomicLong();
private AtomicLong totalBytes = new AtomicLong();
public GeometryGenerator(final BimServer bimServer) {
this.bimServer = bimServer;
}
public class Runner implements Runnable {
private EClass eClass;
private RenderEnginePlugin renderEnginePlugin;
private DatabaseSession databaseSession;
private RenderEngineSettings renderEngineSettings;
private RenderEngineFilter renderEngineFilter;
private RenderEngineFilter renderEngineFilterTransformed = new RenderEngineFilter(true);
private boolean store;
private IfcModelInterface targetModel;
private SerializerPlugin ifcSerializerPlugin;
private IfcModelInterface model;
private int pid;
private int rid;
private Map bigMap;
private GenerateGeometryResult generateGeometryResult;
public Runner(EClass eClass, RenderEnginePlugin renderEnginePlugin, DatabaseSession databaseSession, RenderEngineSettings renderEngineSettings, boolean store, IfcModelInterface targetModel, SerializerPlugin ifcSerializerPlugin, IfcModelInterface model, int pid, int rid, Map bigMap, RenderEngineFilter renderEngineFilter, GenerateGeometryResult generateGeometryResult) {
this.eClass = eClass;
this.renderEnginePlugin = renderEnginePlugin;
this.databaseSession = databaseSession;
this.renderEngineSettings = renderEngineSettings;
this.store = store;
this.targetModel = targetModel;
this.ifcSerializerPlugin = ifcSerializerPlugin;
this.model = model;
this.pid = pid;
this.rid = rid;
this.bigMap = bigMap;
this.renderEngineFilter = renderEngineFilter;
this.generateGeometryResult = generateGeometryResult;
}
@Override
public void run() {
targetModel.generateMinimalExpressIds();
Serializer ifcSerializer = ifcSerializerPlugin.createSerializer(new PluginConfiguration());
RenderEngine renderEngine = null;
try {
renderEngine = renderEnginePlugin.createRenderEngine(new PluginConfiguration(), model.getPackageMetaData().getSchema().getEPackageName());
} catch (RenderEngineException e) {
LOGGER.error("", e);
}
try {
renderEngine.init();
ifcSerializer.init(targetModel, null, bimServer.getPluginManager(), model.getPackageMetaData(), true);
boolean debug = true;
InputStream in = null;
if (debug) {
File file = new File((eClass == null ? "all" : eClass.getName()) + ".ifc");
FileOutputStream fos = new FileOutputStream(file);
IOUtils.copy(ifcSerializer.getInputStream(), fos);
fos.close();
in = new FileInputStream(file);
} else {
in = ifcSerializer.getInputStream();
}
RenderEngineModel renderEngineModel = renderEngine.openModel(in);
try {
renderEngineModel.setSettings(renderEngineSettings);
renderEngineModel.setFilter(renderEngineFilter);
renderEngineModel.generateGeneralGeometry();
List allWithSubTypes = null;
if (eClass == null) {
allWithSubTypes = targetModel.getAllWithSubTypes(packageMetaData.getEClass("IfcProduct"));
} else {
allWithSubTypes = targetModel.getAll(eClass);
}
for (IdEObject ifcProduct : allWithSubTypes) {
IdEObject representation = (IdEObject) ifcProduct.eGet(representationFeature);
if (representation != null && ((List>) representation.eGet(representationsFeature)).size() > 0) {
List> representations = (List>) representation.eGet(representationsFeature);
try {
RenderEngineInstance renderEngineInstance = renderEngineModel.getInstanceFromExpressId(ifcProduct.getExpressId());
RenderEngineGeometry geometry = renderEngineInstance.generateGeometry();
boolean translate = true;
if (geometry == null || geometry.getIndices().length == 0) {
renderEngineModel.setFilter(renderEngineFilterTransformed);
geometry = renderEngineInstance.generateGeometry();
if (geometry != null) {
translate = false;
}
renderEngineModel.setFilter(renderEngineFilter);
}
if (geometry != null && geometry.getNrIndices() > 0) {
GeometryInfo geometryInfo = null;
if (store) {
geometryInfo = model.createAndAdd(GeometryPackage.eINSTANCE.getGeometryInfo(), databaseSession.newOid(GeometryPackage.eINSTANCE.getGeometryInfo()));
databaseSession.store(geometryInfo, pid, rid);
// geometryInfo = packageMetaData.create(GeometryInfo.class);
// Long newOid = databaseSession.newOid(GeometryPackage.eINSTANCE.getGeometryInfo());
// ((IdEObjectImpl) geometryInfo).setOid(newOid);
// model.add(newOid, geometryInfo);
} else {
geometryInfo = GeometryFactory.eINSTANCE.createGeometryInfo();
}
geometryInfo.setMinBounds(createVector3f(packageMetaData, model, Float.POSITIVE_INFINITY, databaseSession, store, pid, rid));
geometryInfo.setMaxBounds(createVector3f(packageMetaData, model, Float.NEGATIVE_INFINITY, databaseSession, store, pid, rid));
try {
double area = renderEngineInstance.getArea();
geometryInfo.setArea(area);
double volume = renderEngineInstance.getVolume();
if (volume < 0d) {
volume = -volume;
}
geometryInfo.setVolume(volume);
// EStructuralFeature guidFeature = ifcProduct.eClass().getEStructuralFeature("GlobalId");
// String guid = (String) ifcProduct.eGet(guidFeature);
// System.out.println(guid + ": " + "Area: " + area + ", Volume: " + volume);
} catch (UnsupportedOperationException e) {
}
GeometryData geometryData = null;
if (store) {
geometryData = model.createAndAdd(GeometryPackage.eINSTANCE.getGeometryData(), databaseSession.newOid(GeometryPackage.eINSTANCE.getGeometryData()));
databaseSession.store(geometryData, pid, rid);
} else {
geometryData = GeometryFactory.eINSTANCE.createGeometryData();
}
geometryData.setIndices(intArrayToByteArray(geometry.getIndices()));
geometryData.setVertices(floatArrayToByteArray(geometry.getVertices()));
geometryData.setMaterialIndices(intArrayToByteArray(geometry.getMaterialIndices()));
geometryData.setNormals(floatArrayToByteArray(geometry.getNormals()));
geometryInfo.setPrimitiveCount(geometry.getIndices().length / 3);
if (geometry.getMaterialIndices() != null && geometry.getMaterialIndices().length > 0) {
boolean hasMaterial = false;
float[] vertex_colors = new float[geometry.getVertices().length / 3 * 4];
for (int i = 0; i < geometry.getMaterialIndices().length; ++i) {
int c = geometry.getMaterialIndices()[i];
for (int j = 0; j < 3; ++j) {
int k = geometry.getIndices()[i * 3 + j];
if (c > -1) {
hasMaterial = true;
for (int l = 0; l < 4; ++l) {
vertex_colors[4 * k + l] = geometry.getMaterials()[4 * c + l];
}
}
}
}
if (hasMaterial) {
geometryData.setMaterials(floatArrayToByteArray(vertex_colors));
}
}
double[] tranformationMatrix = new double[16];
Matrix.setIdentityM(tranformationMatrix, 0);
if (translate && renderEngineInstance.getTransformationMatrix() != null) {
tranformationMatrix = renderEngineInstance.getTransformationMatrix();
}
for (int i = 0; i < geometry.getIndices().length; i++) {
processExtends(geometryInfo, tranformationMatrix, geometry.getVertices(), geometry.getIndices()[i] * 3, generateGeometryResult);
}
geometryInfo.setData(geometryData);
long length = (geometryData.getIndices() != null ? geometryData.getIndices().length : 0) +
(geometryData.getVertices() != null ? geometryData.getVertices().length : 0) +
(geometryData.getNormals() != null ? geometryData.getNormals().length : 0) +
(geometryData.getMaterials() != null ? geometryData.getMaterials().length : 0) +
(geometryData.getMaterialIndices() != null ? geometryData.getMaterialIndices().length : 0);
setTransformationMatrix(geometryInfo, tranformationMatrix);
if (bimServer.getServerSettingsCache().getServerSettings().isReuseGeometry()) {
int hash = hash(geometryData);
if (hashes.containsKey(hash)) {
databaseSession.removeFromCommit(geometryData);
geometryInfo.setData(hashes.get(hash));
bytesSaved.addAndGet(length);
} else {
hashes.put(hash, geometryData);
}
}
totalBytes.addAndGet(length);
if (bigMap == null) {
ifcProduct.eSet(geometryFeature, geometryInfo);
if (store) {
databaseSession.store(ifcProduct, pid, rid);
}
} else {
bigMap.get(ifcProduct).eSet(geometryFeature, geometryInfo);
ifcProduct.eSet(geometryFeature, geometryInfo); // ??
if (store) {
databaseSession.store(bigMap.get(ifcProduct), pid, rid);
}
}
}
} catch (EntityNotFoundException e) {
// As soon as we find a representation that is not Curve2D, then we should show a "INFO" message in the log to indicate there could be something wrong
boolean ignoreNotFound = true;
for (Object rep : representations) {
if (rep instanceof IfcShapeRepresentation) {
IfcShapeRepresentation ifcShapeRepresentation = (IfcShapeRepresentation)rep;
if (!"Curve2D".equals(ifcShapeRepresentation.getRepresentationType())) {
ignoreNotFound = false;
}
}
}
if (!ignoreNotFound) {
LOGGER.info("Entity not found " + ifcProduct.eClass().getName() + " " + ifcProduct.getExpressId() + "/" + ifcProduct.getOid());
}
} catch (BimserverDatabaseException | RenderEngineException e) {
LOGGER.error("", e);
} catch (IfcModelInterfaceException e) {
LOGGER.error("", e);
}
}
}
} finally {
in.close();
renderEngineModel.close();
}
} catch (SerializerException | RenderEngineException | IOException e) {
LOGGER.error("", e);
} finally {
try {
renderEngine.close();
} catch (RenderEngineException e) {
LOGGER.error("", e);
}
}
}
}
@SuppressWarnings("unchecked")
public GenerateGeometryResult generateGeometry(long uoid, final PluginManager pluginManager, final DatabaseSession databaseSession, final IfcModelInterface model, final int pid, final int rid,
final boolean store, GeometryCache geometryCache) throws BimserverDatabaseException, GeometryGeneratingException {
GenerateGeometryResult generateGeometryResult = new GenerateGeometryResult();
packageMetaData = model.getPackageMetaData();
productClass = packageMetaData.getEClass("IfcProduct");
productRepresentationClass = packageMetaData.getEClass("IfcProductRepresentation");
geometryFeature = productClass.getEStructuralFeature("geometry");
representationFeature = productClass.getEStructuralFeature("Representation");
representationsFeature = productRepresentationClass.getEStructuralFeature("Representations");
if (geometryCache != null && !geometryCache.isEmpty()) {
returnCachedData(model, geometryCache, databaseSession, pid, rid);
return null;
}
long start = System.nanoTime();
String pluginName = "";
if (model.getPackageMetaData().getSchema() == Schema.IFC4) {
pluginName = "org.bimserver.ifc.step.serializer.Ifc4StepSerializerPlugin";
} else if (model.getPackageMetaData().getSchema() == Schema.IFC2X3TC1) {
pluginName = "org.bimserver.ifc.step.serializer.Ifc2x3tc1StepSerializerPlugin";
}
try {
final SerializerPlugin ifcSerializerPlugin = (SerializerPlugin) pluginManager.getPlugin(pluginName, true);
if (ifcSerializerPlugin == null) {
throw new UserException("No IFC serializer found");
}
User user = (User) databaseSession.get(uoid, OldQuery.getDefault());
UserSettings userSettings = user.getUserSettings();
RenderEnginePluginConfiguration defaultRenderEngine = userSettings.getDefaultRenderEngine();
if (defaultRenderEngine == null) {
throw new UserException("No default render engine has been selected for this user");
}
final RenderEnginePlugin renderEnginePlugin = pluginManager.getRenderEngine(defaultRenderEngine.getPluginDescriptor().getPluginClassName(), true);
if (renderEnginePlugin == null) {
throw new UserException("No (enabled) render engine found of type " + defaultRenderEngine.getPluginDescriptor().getPluginClassName());
}
int maxSimultanousThreads = Math.min(bimServer.getServerSettingsCache().getServerSettings().getRenderEngineProcesses(), Runtime.getRuntime().availableProcessors());
if (maxSimultanousThreads < 1) {
maxSimultanousThreads = 1;
}
final RenderEngineSettings settings = new RenderEngineSettings();
settings.setPrecision(Precision.SINGLE);
settings.setIndexFormat(IndexFormat.AUTO_DETECT);
settings.setGenerateNormals(true);
settings.setGenerateTriangles(true);
settings.setGenerateWireFrame(false);
final RenderEngineFilter renderEngineFilter = new RenderEngineFilter();
if (maxSimultanousThreads == 1) {
Runner runner = new Runner(null, renderEnginePlugin, databaseSession, settings, store, model, ifcSerializerPlugin, model, pid, rid, null, renderEngineFilter, generateGeometryResult);
runner.run();
} else {
Set classes = new HashSet<>();
for (IdEObject object : model.getAllWithSubTypes(packageMetaData.getEClass("IfcProduct"))) {
IdEObject representation = (IdEObject)object.eGet(representationFeature);
if (representation != null && ((List>)representation.eGet(representationsFeature)).size() > 0) {
classes.add(object.eClass());
}
}
if (classes.size() == 0) {
return null;
}
classes.remove(packageMetaData.getEClass("IfcAnnotation"));
classes.remove(packageMetaData.getEClass("IfcOpeningElement"));
LOGGER.debug("Using " + maxSimultanousThreads + " processes for geometry generation");
ThreadPoolExecutor executor = new ThreadPoolExecutor(maxSimultanousThreads, maxSimultanousThreads, 24, TimeUnit.HOURS, new ArrayBlockingQueue(classes.size()));
final Map bigMap = new HashMap();
HideAllInversesObjectIDM idm = new HideAllInversesObjectIDM(CollectionUtils.singleSet(packageMetaData.getEPackage()), pluginManager.getMetaDataManager().getPackageMetaData("ifc2x3tc1"));
OidProvider oidProvider = new OidProvider(){
@Override
public long newOid(EClass eClass) {
return databaseSession.newOid(eClass);
}};
for (final EClass eClass : classes) {
final BasicIfcModel targetModel = new BasicIfcModel(pluginManager.getMetaDataManager().getPackageMetaData("ifc2x3tc1"), null);
ModelHelper modelHelper = new ModelHelper(bimServer.getMetaDataManager(), targetModel);
modelHelper.setOidProvider(oidProvider);
modelHelper.setObjectIDM(idm);
IdEObject newOwnerHistory = modelHelper.copyBasicObjects(model, bigMap);
for (IdEObject idEObject : model.getAll(eClass)) {
IdEObject newObject = modelHelper.copy(idEObject, false, ModelHelper.createObjectIdm(idEObject.eClass()));
modelHelper.copyDecomposes(idEObject, newOwnerHistory);
bigMap.put(newObject, idEObject);
if (eClass.getName().equals("IfcWallStandardCase")) {
EStructuralFeature hasOpeningsFeature = idEObject.eClass().getEStructuralFeature("HasOpenings");
for (IdEObject ifcRelVoidsElement : ((List)idEObject.eGet(hasOpeningsFeature))) {
bigMap.put(modelHelper.copy(ifcRelVoidsElement, false), ifcRelVoidsElement);
EStructuralFeature relatedOpeningElementFeature = ifcRelVoidsElement.eClass().getEStructuralFeature("RelatedOpeningElement");
IdEObject relatedOpeningElement = (IdEObject) ifcRelVoidsElement.eGet(relatedOpeningElementFeature);
if (relatedOpeningElement != null) {
bigMap.put(modelHelper.copy(relatedOpeningElement, false), relatedOpeningElement);
}
}
}
}
executor.submit(new Runner(eClass, renderEnginePlugin, databaseSession, settings, store, targetModel, ifcSerializerPlugin, model, pid, rid, bigMap, renderEngineFilter, generateGeometryResult));
}
executor.shutdown();
executor.awaitTermination(24, TimeUnit.HOURS);
}
long end = System.nanoTime();
LOGGER.info("Rendertime: " + ((end - start) / 1000000) + "ms, " + "Reused: " + Formatters.bytesToString(bytesSaved.get()) + ", Total: " + Formatters.bytesToString(totalBytes.get()) + ", Final: " + Formatters.bytesToString(totalBytes.get() - bytesSaved.get()));
} catch (Exception e) {
LOGGER.error("", e);
throw new GeometryGeneratingException(e);
}
return generateGeometryResult;
}
private int hash(GeometryData geometryData) {
int hashCode = 0;
if (geometryData.getIndices() != null) {
hashCode += Arrays.hashCode(geometryData.getIndices());
}
if (geometryData.getVertices() != null) {
hashCode += Arrays.hashCode(geometryData.getVertices());
}
if (geometryData.getNormals() != null) {
hashCode += Arrays.hashCode(geometryData.getNormals());
}
if (geometryData.getMaterialIndices() != null) {
hashCode += Arrays.hashCode(geometryData.getMaterialIndices());
}
if (geometryData.getMaterials() != null) {
hashCode += Arrays.hashCode(geometryData.getMaterials());
}
return hashCode;
}
private void processExtends(GeometryInfo geometryInfo, double[] transformationMatrix, float[] vertices, int index, GenerateGeometryResult generateGeometryResult) {
double x = vertices[index];
double y = vertices[index + 1];
double z = vertices[index + 2];
double[] result = new double[4];
Matrix.multiplyMV(result, 0, transformationMatrix, 0, new double[] { x, y, z, 1 }, 0);
x = result[0];
y = result[1];
z = result[2];
geometryInfo.getMinBounds().setX(Math.min(x, geometryInfo.getMinBounds().getX()));
geometryInfo.getMinBounds().setY(Math.min(y, geometryInfo.getMinBounds().getY()));
geometryInfo.getMinBounds().setZ(Math.min(z, geometryInfo.getMinBounds().getZ()));
geometryInfo.getMaxBounds().setX(Math.max(x, geometryInfo.getMaxBounds().getX()));
geometryInfo.getMaxBounds().setY(Math.max(y, geometryInfo.getMaxBounds().getY()));
geometryInfo.getMaxBounds().setZ(Math.max(z, geometryInfo.getMaxBounds().getZ()));
generateGeometryResult.getMinBounds().setX(Math.min(x, generateGeometryResult.getMinBounds().getX()));
generateGeometryResult.getMinBounds().setY(Math.min(y, generateGeometryResult.getMinBounds().getY()));
generateGeometryResult.getMinBounds().setZ(Math.min(z, generateGeometryResult.getMinBounds().getZ()));
generateGeometryResult.getMaxBounds().setX(Math.max(x, generateGeometryResult.getMaxBounds().getX()));
generateGeometryResult.getMaxBounds().setY(Math.max(y, generateGeometryResult.getMaxBounds().getY()));
generateGeometryResult.getMaxBounds().setZ(Math.max(z, generateGeometryResult.getMaxBounds().getZ()));
}
private byte[] floatArrayToByteArray(float[] vertices) {
if (vertices == null) {
return null;
}
ByteBuffer buffer = ByteBuffer.wrap(new byte[vertices.length * 4]);
buffer.order(ByteOrder.LITTLE_ENDIAN);
FloatBuffer asFloatBuffer = buffer.asFloatBuffer();
for (float f : vertices) {
asFloatBuffer.put(f);
}
return buffer.array();
}
private byte[] intArrayToByteArray(int[] indices) {
if (indices == null) {
return null;
}
ByteBuffer buffer = ByteBuffer.wrap(new byte[indices.length * 4]);
buffer.order(ByteOrder.LITTLE_ENDIAN);
IntBuffer asIntBuffer = buffer.asIntBuffer();
for (int i : indices) {
asIntBuffer.put(i);
}
return buffer.array();
}
private void setTransformationMatrix(GeometryInfo geometryInfo, double[] transformationMatrix) {
ByteBuffer byteBuffer = ByteBuffer.allocate(16 * 8);
byteBuffer.order(ByteOrder.nativeOrder());
DoubleBuffer asDoubleBuffer = byteBuffer.asDoubleBuffer();
for (double f : transformationMatrix) {
asDoubleBuffer.put(f);
}
geometryInfo.setTransformation(byteBuffer.array());
}
private static boolean almostTheSame(float f1, float f2, float maxDiff) {
return Math.abs(f1 - f2) < maxDiff;
}
/**
* This function should return a transformation matrix (with translation and
* rotation, no scaling) overlaying triangle V on U Assumed is that the
* triangles are indeed the same and the order of the vertices is also the
* same (shifts are not allowed) This function can probably be optimized for
* speed and also the LOC can probably be reduced
*
* @param originalV1
* @param originalV2
* @param originalV3
* @param u1
* @param u2
* @param u3
* @return
*/
private static float[] getTransformationMatrix(float[] originalV1, float[] originalV2, float[] originalV3, float[] u1, float[] u2, float[] u3, float maxDiff) {
float[] v1 = copy(originalV1);
float[] v2 = copy(originalV2);
float[] v3 = copy(originalV3);
u1 = copy(u1);
u2 = copy(u2);
u3 = copy(u3);
float transX = u1[0] - v1[0];
float transY = u1[1] - v1[1];
float transZ = u1[2] - v1[2];
float translation[] = new float[16];
Matrix.setIdentityM(translation, 0);
Matrix.translateM(translation, 0, u1[0], u1[1], u1[2]);
float[] toZeroTranslation = new float[16];
Matrix.setIdentityM(toZeroTranslation, 0);
Matrix.translateM(toZeroTranslation, 0, -v1[0], -v1[1], -v1[2]);
if (almostTheSame(v2[0] + transX, u2[0], maxDiff) && almostTheSame(v2[1] + transY, u2[1], maxDiff) && almostTheSame(v2[2] + transZ, u2[2], maxDiff)
&& almostTheSame(v3[0] + transX, u3[0], maxDiff) && almostTheSame(v3[1] + transY, u3[1], maxDiff) && almostTheSame(v3[2] + transZ, u3[2], maxDiff)) {
// The other two points are already the same, to there was no
// rotation
return translation;
}
// Normalize both triangles to their first vertex
subtract(u2, u1);
subtract(u3, u1);
subtract(u1, u1);
subtract(v2, v1);
subtract(v3, v1);
subtract(v1, v1);
float[] u2CrossV2 = Vector.crossProduct(u2, v2);
float[] r2 = new float[16];
Matrix.setIdentityM(r2, 0);
float[] r2v2 = new float[4];
if (!equalsAlmost(u2, v2, maxDiff)) {
float u2InV2 = Vector.dot(u2, v2);
float[] axis = u2CrossV2;
if (axis[0] == 0 && axis[1] == 0 && axis[2] == 0) {
axis = new float[] { u2[1], -u2[0], 0, 0 };
}
Matrix.rotateM(r2, 0, (float) Math.toDegrees(Math.atan2(Vector.length(u2CrossV2), u2InV2)), axis[0], axis[1], axis[2]);
Matrix.multiplyMV(r2v2, 0, r2, 0, new float[] { v2[0], v2[1], v2[2], 1 }, 0);
if (!equalsAlmost(r2v2, u2, maxDiff)) {
Matrix.setIdentityM(r2, 0);
Matrix.rotateM(r2, 0, -(float) Math.toDegrees(Math.atan2(Vector.length(u2CrossV2), u2InV2)), axis[0], axis[1], axis[2]);
Matrix.multiplyMV(r2v2, 0, r2, 0, new float[] { v2[0], v2[1], v2[2], 1 }, 0);
if (!equalsAlmost(r2v2, u2, maxDiff)) {
return null;
}
}
} else {
r2v2 = copy(v2);
}
float[] r2v3 = new float[4];
Matrix.multiplyMV(r2v3, 0, r2, 0, new float[] { v3[0], v3[1], v3[2], 1 }, 0);
float[] r3 = new float[16];
Matrix.setIdentityM(r3, 0);
float angleDegrees = (float) Math.toDegrees(getPlaneAngle(v1, r2v2, r2v3, u1, u2, u3));
Matrix.rotateM(r3, 0, angleDegrees, r2v2[0], r2v2[1], r2v2[2]);
float[] r3v3 = new float[4];
Matrix.multiplyMV(r3v3, 0, r3, 0, new float[] { r2v3[0], r2v3[1], r2v3[2], 1 }, 0);
float[] r2v1 = new float[4];
Matrix.multiplyMV(r2v1, 0, r2, 0, new float[] { v1[0], v1[1], v1[2], 1 }, 0);
float[] r3v1 = new float[4];
Matrix.multiplyMV(r3v1, 0, r3, 0, new float[] { r2v1[0], r2v1[1], r2v1[2], 1 }, 0);
if (!equalsAlmost(r3v3, u3, maxDiff)) {
Matrix.setIdentityM(r3, 0);
Matrix.rotateM(r3, 0, -angleDegrees, r2v2[0], r2v2[1], r2v2[2]);
Matrix.multiplyMV(r3v3, 0, r3, 0, new float[] { r2v3[0], r2v3[1], r2v3[2], 1 }, 0);
float[] r3v2 = new float[4];
Matrix.multiplyMV(r3v2, 0, r3, 0, new float[] { r2v2[0], r2v2[1], r2v2[2], 1 }, 0);
if (!equalsAlmost(r3v3, u3, maxDiff) || !equalsAlmost(r3v2, u2, maxDiff)) {
return null;
}
}
float[] subResult = new float[16];
float[] subResult2 = new float[16];
float[] subResult3 = new float[16];
float[] totalResult = new float[16];
float[] startMatrix = new float[16];
Matrix.setIdentityM(startMatrix, 0);
Matrix.multiplyMM(subResult, 0, toZeroTranslation, 0, startMatrix, 0);
Matrix.multiplyMM(subResult2, 0, r2, 0, subResult, 0);
Matrix.multiplyMM(subResult3, 0, r3, 0, subResult2, 0);
Matrix.multiplyMM(totalResult, 0, translation, 0, subResult3, 0);
return totalResult;
}
/**
* Get the angle in radians between two planes
*
* @param v1
* @param v2
* @param v3
* @param u1
* @param u2
* @param u3
* @return
*/
private static double getPlaneAngle(float[] v1, float[] v2, float[] v3, float[] u1, float[] u2, float[] u3) {
float[] cross1 = Vector.crossProduct(new float[] { v2[0] - v1[0], v2[1] - v1[1], v2[2] - v1[2] }, new float[] { v3[0] - v1[0], v3[1] - v1[1], v3[2] - v1[2] });
float[] cross2 = Vector.crossProduct(new float[] { u2[0] - u1[0], u2[1] - u1[1], u2[2] - u1[2] }, new float[] { u3[0] - u1[0], u3[1] - u1[1], u3[2] - u1[2] });
float num = Vector.dot(cross1, cross2);
float den = Vector.length(cross1) * Vector.length(cross2);
float a = num / den;
if (a > 1) {
a = 1;
}
if (a < -1) {
a = -1;
}
double result = Math.acos(a);
if (Double.isNaN(result)) {
System.out.println();
}
return result;
}
private static boolean equalsAlmost(float[] r2v2, float[] u2, float maxDiff) {
for (int i = 0; i < 3; i++) {
if (!almostTheSame(r2v2[i], u2[i], maxDiff)) {
return false;
}
}
return true;
}
private static float[] copy(float[] v1) {
float[] result = new float[v1.length];
System.arraycopy(v1, 0, result, 0, v1.length);
return result;
}
private static boolean test(float[] v1, float[] v2, float[] transformationMatrix, float maxDiff) {
float[] resultVector = new float[4];
Matrix.multiplyMV(resultVector, 0, transformationMatrix, 0, new float[] { v1[0], v1[1], v1[2], 1 }, 0);
normalize(resultVector);
boolean theSame = true;
for (int i = 0; i < 3; i++) {
if (!almostTheSame(resultVector[i], v2[i], maxDiff)) {
theSame = false;
}
}
if (!theSame) {
System.out.println("Difference");
Vector.dump("Was", v1);
Vector.dump("Became", resultVector);
Vector.dump("Should be", v2);
System.out.println();
return false;
}
return true;
}
private static void normalize(float[] resultVector) {
resultVector[0] = resultVector[0] * resultVector[3];
resultVector[1] = resultVector[1] * resultVector[3];
resultVector[2] = resultVector[2] * resultVector[3];
resultVector[3] = 1;
}
private static void subtract(float[] u2, float[] v1) {
u2[0] = u2[0] - v1[0];
u2[1] = u2[1] - v1[1];
u2[2] = u2[2] - v1[2];
}
public static void main(String[] args) {
float maxDiff = 0.1f;
test1(maxDiff);
test2(maxDiff);
Random random = new Random();
for (int i = 0; i < 10; i++) {
float[] matrix = new float[16];
Matrix.setIdentityM(matrix, 0);
for (int j = 0; j < 10; j++) {
Matrix.rotateM(matrix, 0, random.nextFloat() * 360, random.nextFloat(), random.nextFloat(), random.nextFloat());
}
float[] v1 = new float[] { random.nextFloat(), random.nextFloat(), random.nextFloat(), 1 };
float[] v2 = new float[] { random.nextFloat(), random.nextFloat(), random.nextFloat(), 1 };
float[] v3 = new float[] { random.nextFloat(), random.nextFloat(), random.nextFloat(), 1 };
float[] r1 = new float[4];
float[] r2 = new float[4];
float[] r3 = new float[4];
Matrix.multiplyMV(r1, 0, matrix, 0, v1, 0);
Matrix.multiplyMV(r2, 0, matrix, 0, v2, 0);
Matrix.multiplyMV(r3, 0, matrix, 0, v3, 0);
float[] calculatedMatrix = getTransformationMatrix(v1, v2, v3, r1, r2, r3, maxDiff);
test(v1, r1, calculatedMatrix, maxDiff);
test(v2, r2, calculatedMatrix, maxDiff);
test(v3, r3, calculatedMatrix, maxDiff);
for (int j = 0; j < 10; j++) {
float[] q1 = new float[] { random.nextFloat(), random.nextFloat(), random.nextFloat(), 1 };
float[] q2 = new float[] { random.nextFloat(), random.nextFloat(), random.nextFloat(), 1 };
float[] q3 = new float[] { random.nextFloat(), random.nextFloat(), random.nextFloat(), 1 };
float[] b1 = new float[4];
float[] b2 = new float[4];
float[] b3 = new float[4];
Matrix.multiplyMV(b1, 0, matrix, 0, q1, 0);
Matrix.multiplyMV(b2, 0, matrix, 0, q2, 0);
Matrix.multiplyMV(b3, 0, matrix, 0, q3, 0);
test(q1, b1, calculatedMatrix, maxDiff);
test(q2, b2, calculatedMatrix, maxDiff);
test(q3, b3, calculatedMatrix, maxDiff);
}
}
}
private static void test1(float maxDiff) {
float[] v1 = new float[] { 1, 2, 0 };
float[] v2 = new float[] { 1, 1, 0 };
float[] v3 = new float[] { 3, 2, 0 };
float[] u1 = new float[] { 0, 2, 0 };
float[] u2 = new float[] { -1, 2, 0 };
float[] u3 = new float[] { 0, 2, 2 };
float[] transformationMatrix = getTransformationMatrix(v1, v2, v3, u1, u2, u3, maxDiff);
test(v1, u1, transformationMatrix, maxDiff);
test(v2, u2, transformationMatrix, maxDiff);
test(v3, u3, transformationMatrix, maxDiff);
}
private static void test2(float maxDiff) {
float[] v1 = new float[] { 3, 0, 0 };
float[] v2 = new float[] { 4, 0, 0 };
float[] v3 = new float[] { 4, 1, 0 };
float[] u1 = new float[] { 1, 3, 0 };
float[] u2 = new float[] { 0, 3, 0 };
float[] u3 = new float[] { 0, 2, 0 };
float[] transformationMatrix = getTransformationMatrix(v1, v2, v3, u1, u2, u3, maxDiff);
test(v1, u1, transformationMatrix, maxDiff);
test(v2, u2, transformationMatrix, maxDiff);
test(v3, u3, transformationMatrix, maxDiff);
}
private void returnCachedData(IfcModelInterface model, GeometryCache geometryCache, DatabaseSession databaseSession, int pid, int rid) throws BimserverDatabaseException {
EClass productClass = model.getPackageMetaData().getEClass("IfcProduct");
List products = model.getAllWithSubTypes(productClass);
for (IdEObject ifcProduct : products) {
GeometryCacheEntry geometryCacheEntry = geometryCache.get(ifcProduct.getExpressId());
if (geometryCacheEntry != null) {
GeometryData geometryData = databaseSession.create(GeometryPackage.eINSTANCE.getGeometryData(), pid, rid);
geometryData.setVertices(geometryCacheEntry.getVertices().array());
geometryData.setNormals(geometryCacheEntry.getNormals().array());
GeometryInfo geometryInfo = databaseSession.create(GeometryPackage.eINSTANCE.getGeometryInfo(), pid, rid);
Vector3f min = databaseSession.create(GeometryPackage.eINSTANCE.getVector3f(), pid, rid);
min.setX(geometryCacheEntry.getGeometryInfo().getMinBounds().getX());
min.setY(geometryCacheEntry.getGeometryInfo().getMinBounds().getY());
min.setZ(geometryCacheEntry.getGeometryInfo().getMinBounds().getZ());
Vector3f max = databaseSession.create(GeometryPackage.eINSTANCE.getVector3f(), pid, rid);
max.setX(geometryCacheEntry.getGeometryInfo().getMaxBounds().getX());
max.setY(geometryCacheEntry.getGeometryInfo().getMaxBounds().getY());
max.setZ(geometryCacheEntry.getGeometryInfo().getMaxBounds().getZ());
geometryInfo.setMinBounds(min);
geometryInfo.setMaxBounds(max);
geometryInfo.setData(geometryData);
ifcProduct.eSet(ifcProduct.eClass().getEStructuralFeature("geometry"), geometryInfo);
}
}
}
private Vector3f createVector3f(PackageMetaData packageMetaData, IfcModelInterface model, float defaultValue, DatabaseSession session, boolean store, int pid, int rid) throws BimserverDatabaseException, IfcModelInterfaceException {
Vector3f vector3f = null;
if (store) {
vector3f = model.createAndAdd(GeometryPackage.eINSTANCE.getVector3f(), session.newOid(GeometryPackage.eINSTANCE.getVector3f()));
session.store(vector3f, pid, rid);
} else {
vector3f = GeometryFactory.eINSTANCE.createVector3f();
}
vector3f.setX(defaultValue);
vector3f.setY(defaultValue);
vector3f.setZ(defaultValue);
return vector3f;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy