/*
* Project Info: http://jcae.sourceforge.net
*
* 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 2.1 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, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* (C) Copyright 2012, by EADS France
*/
package org.jcae.mesh.amibe.metrics;
import gnu.trove.map.hash.TIntObjectHashMap;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jcae.mesh.amibe.ds.Mesh;
import org.jcae.mesh.amibe.ds.Triangle;
import org.jcae.mesh.amibe.ds.Vertex;
import org.jcae.mesh.xmldata.DoubleFileReader;
import org.jcae.mesh.xmldata.PrimitiveFileReaderFactory;
/**
*
* @author Jerome Robert
*/
public class MetricSupport {
private final static Logger LOGGER = Logger.getLogger(MetricSupport.class.getName());
private DoubleFileReader dfrMetrics;
private AnalyticMetricInterface analyticMetric;
private final Map<Vertex, EuclidianMetric3D> metrics;
private final Mesh mesh;
private final TIntObjectHashMap<AnalyticMetricInterface> metricsPartitionMap =
new TIntObjectHashMap<AnalyticMetricInterface>();
private final String sizeOptionKey;
private EuclidianMetric3D uniformMetric;
public interface AnalyticMetricInterface
{
/**
* Return the target size when topology information are available.
* This is used for exemple before inserting a point into the mesh.
*/
double getTargetSize(double x, double y, double z, int groupId);
/**
* Return the target size when topology information are available.
* This is only used at the initialization of algorithms
*/
double getTargetSizeTopo(Mesh mesh, Vertex v);
}
public static abstract class AnalyticMetric implements AnalyticMetricInterface
{
/**
* Return the target size when topology information are available.
* This is used for exemple before inserting a point into the mesh.
*/
public abstract double getTargetSize(double x, double y, double z, int groupId);
/**
* Return the target size when topology information are available.
* This is only used at the initialization of algorithms
*/
@Override
public double getTargetSizeTopo(Mesh mesh, Vertex v)
{
int groupId = -1;
if(v.isManifold())
groupId = ((Triangle)v.getLink()).getGroupId();
return getTargetSize(v.getX(), v.getY(), v.getZ(), groupId);
}
}
public MetricSupport(Mesh mesh, Map<String, String> options) {
this(mesh, options, "size");
}
public MetricSupport(Mesh mesh, Map<String, String> options, String sizeOption) {
this.sizeOptionKey = sizeOption;
this.mesh = mesh;
Collection<Vertex> nodeset = mesh.getNodes();
double size = 0.0;
for (final Map.Entry<String, String> opt: options.entrySet())
{
final String key = opt.getKey();
final String val = opt.getValue();
if (sizeOption.equals(key))
{
size = Double.parseDouble(val);
LOGGER.log(Level.FINE, "Size: {0}", size);
analyticMetric = null;
dfrMetrics = null;
}
else if ("metricsFile".equals(key))
{
PrimitiveFileReaderFactory pfrf = new PrimitiveFileReaderFactory();
try {
dfrMetrics = pfrf.getDoubleReader(new File(val));
} catch (FileNotFoundException ex) {
LOGGER.log(Level.SEVERE, null, ex);
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
analyticMetric = null;
}
}
// Arbitrary size: 2*initial number of nodes
metrics = new HashMap<Vertex, EuclidianMetric3D>(
nodeset == null ? 0 : 2*nodeset.size());
if (dfrMetrics != null)
{
try {
for (Vertex v : nodeset)
metrics.put(v, new EuclidianMetric3D(dfrMetrics.get(v.getLabel() - 1)));
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, null, ex);
throw new RuntimeException("Error when loading metrics map file", ex);
}
}
else if (size > 0.0)
{
// If targetSize is 0.0, metrics will be set by calling setAnalyticMetric()
// below.
uniformMetric = new EuclidianMetric3D(size);
}
}
public boolean isKnownOption(String key)
{
return "metricsFile".equals(key) || sizeOptionKey.equals(key);
}
public void setSize(double size)
{
uniformMetric = new EuclidianMetric3D(size);
}
public void setAnalyticMetric(AnalyticMetricInterface m)
{
analyticMetric = m;
}
public void setAnalyticMetric(int groupId, AnalyticMetricInterface m)
{
metricsPartitionMap.put(groupId, m);
}
public void compute()
{
if (analyticMetric != null || !metricsPartitionMap.isEmpty())
{
for (Triangle t : mesh.getTriangles())
{
if (!t.isReadable())
continue;
AnalyticMetricInterface metric = getAnalyticMetric(t.getGroupId());
if (metric == null)
throw new NullPointerException("Cannot determine metrics, either set 'size' or 'metricsMap' arguments, or call Remesh.setAnalyticMetric()");
for (int i = 0; i < 3; i++)
{
Vertex v = t.getV(i);
EuclidianMetric3D curMetric = metrics.get(v);
EuclidianMetric3D newMetric = new EuclidianMetric3D(
metric.getTargetSizeTopo(mesh, v));
if (curMetric == null ||
curMetric.getUnitBallBBox()[0] > newMetric.getUnitBallBBox()[0])
metrics.put(v, newMetric);
}
}
}
}
public void put(Vertex v)
{
if(analyticMetric != null)
metrics.put(v, new EuclidianMetric3D(
analyticMetric.getTargetSizeTopo(mesh, v)));
}
public void put(Vertex v, EuclidianMetric3D m)
{
metrics.put(v, m);
}
/**
* Get the metric of an unknown vertex which aims at being inserted in the
* given triangle
*/
public EuclidianMetric3D get(Vertex v, Triangle t)
{
return get(v, t.getGroupId());
}
/**
* Get the metric of an unknown vertex which aims at being inserted in the
* given triangle
*/
public EuclidianMetric3D get(Vertex v, int groupId)
{
AnalyticMetricInterface metric = getAnalyticMetric(groupId);
EuclidianMetric3D toReturn = null;
if (metric == null)
{
toReturn = uniformMetric;
}
else
{
toReturn = new EuclidianMetric3D(
metric.getTargetSize(v.getX(), v.getY(), v.getZ(), groupId));
}
return toReturn;
}
/** Get the metric of a known vertex */
public EuclidianMetric3D get(Vertex v)
{
EuclidianMetric3D r = metrics.get(v);
return r == null ? uniformMetric : r;
}
private AnalyticMetricInterface getAnalyticMetric(int groupId)
{
AnalyticMetricInterface metric = metricsPartitionMap.get(groupId);
if (metric == null)
metric = analyticMetric;
return metric;
}
public boolean isEmpty()
{
return metrics.isEmpty() && uniformMetric == null;
}
public double interpolatedDistance(Vertex pt1, Vertex pt2)
{
return interpolatedDistance(pt1, get(pt1), pt2, get(pt2));
}
public static double interpolatedDistance(Vertex pt1, Metric m1, Vertex pt2, Metric m2)
{
assert m1 != null : "Metric null at point "+pt1;
assert m2 != null : "Metric null at point "+pt2;
double a = Math.sqrt(m1.distance2(pt1, pt2));
double b = Math.sqrt(m2.distance2(pt1, pt2));
// Linear interpolation:
//double l = (2.0/3.0) * (a*a + a*b + b*b) / (a + b);
// Geometric interpolation
double l = Math.abs(a-b) < 1.e-6*(a+b) ? 0.5*(a+b) : (a - b)/Math.log(a/b);
return l;
}
}