001package conexp.fx.core.layout;
002/*
003 * #%L
004 * Concept Explorer FX
005 * %%
006 * Copyright (C) 2010 - 2023 Francesco Kriegel
007 * %%
008 * This program is free software: you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License as
010 * published by the Free Software Foundation, either version 3 of the
011 * License, or (at your option) any later version.
012 * 
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 * 
018 * You should have received a copy of the GNU General Public
019 * License along with this program.  If not, see
020 * <http://www.gnu.org/licenses/gpl-3.0.html>.
021 * #L%
022 */
023
024import java.util.Map.Entry;
025import java.util.function.Function;
026import java.util.stream.Stream;
027
028import com.google.common.collect.Maps;
029
030import conexp.fx.core.collections.ListIterators;
031import conexp.fx.core.collections.Pair;
032import conexp.fx.core.context.Concept;
033import conexp.fx.core.math.Points;
034import javafx.beans.binding.Binding;
035import javafx.geometry.Point3D;
036
037public abstract class QualityMeasure<G, M, V> implements Function<AdditiveConceptLayout<G, M>, V> {
038
039//    implements com.google.common.base.Function<AdditiveConceptLayout<G, M>, V>,
040//    java.util.function.Function<AdditiveConceptLayout<G, M>, V> {
041//
042//  private final Map<Pair<Map<G, Point3D>, Map<M, Point3D>>, V> cache = new ConcurrentHashMap<>();
043//
044//  public final V apply(final AdditiveConceptLayout<G, M> layout) {
045//    return cache.computeIfAbsent(Pair.of(layout._seedsG, layout._seedsM), __ -> compute(layout));
046//  }
047//
048  @Override
049  public V apply(AdditiveConceptLayout<G, M> t) {
050    return compute(t);
051  }
052
053  protected abstract V compute(AdditiveConceptLayout<G, M> layout);
054
055  public static final <G, M, V> QualityMeasure<G, M, V>
056      of(final java.util.function.Function<AdditiveConceptLayout<G, M>, V> f) {
057    return new QualityMeasure<G, M, V>() {
058
059      @Override
060      protected V compute(AdditiveConceptLayout<G, M> layout) {
061        return f.apply(layout);
062      }
063    };
064  }
065
066  @SafeVarargs
067  public static final <G, M> QualityMeasure<G, M, Double>
068      linearCombination(final Pair<Double, QualityMeasure<G, M, Double>>... weightedMeasures) {
069    return of(
070        layout -> Stream
071            .of(weightedMeasures)
072            .map(p -> p.first() * p.second().apply(layout))
073            .reduce(Double::sum)
074            .orElse(0d));
075  }
076
077  public static final <G, M> QualityMeasure<G, M, Double> topBottomXDistance() {
078    return of(
079        layout -> 1d - (Math.abs(
080            layout.getOrAddPosition(layout.lattice.context.topConcept()).getValue().getX()
081                - layout.getOrAddPosition(layout.lattice.context.bottomConcept()).getValue().getX())
082            / layout.getCurrentBoundingBox(false, false).getWidth()));
083  }
084
085  public static final <G, M> QualityMeasure<G, M, Double> heightWidthRatio() {
086    return of(layout -> {
087      final double h = layout.getCurrentBoundingBox(false, false).getHeight();
088      final double w = layout.getCurrentBoundingBox(false, false).getWidth();
089      final double min = Math.min(h, w);
090      final double max = Math.max(h, w);
091      return min / max;
092    });
093  }
094
095  public static final <G, M> QualityMeasure<G, M, Integer> edgeIntersections() {
096    return of(layout -> {
097      int intersections = 0;
098      for (Pair<Pair<Concept<G, M>, Concept<G, M>>, Pair<Concept<G, M>, Concept<G, M>>> e : ListIterators
099          .upperCartesianDiagonalStrict(layout.lattice))
100        if (!e.first().x().equals(e.second().y()) && !e.first().y().equals(e.second().x()))
101          if (Points.intersectingLineSegments(
102              layout.getOrAddPosition(e.first().x()).getValue(),
103              layout.getOrAddPosition(e.first().y()).getValue(),
104              layout.getOrAddPosition(e.second().x()).getValue(),
105              layout.getOrAddPosition(e.second().y()).getValue()))
106            intersections++;
107      return intersections;
108    });
109  }
110
111  public static final <G, M> QualityMeasure<G, M, Pair<Concept<G, M>, Double>> conflictDistance() {
112    return of(layout -> {
113      double num = 0d;
114      double minC = 1d;
115      double sumC = 0d;
116      double minL = Double.MAX_VALUE;
117      double sumL = 0d;
118      final Concept<G, M> bottom = layout.lattice.context.selection.bottomConcept();
119      Concept<G, M> faulty = bottom;
120      for (Entry<Concept<G, M>, Binding<Point3D>> entry : Maps
121          .asMap(layout.lattice.rowHeads(), layout::getOrAddPosition)
122          .entrySet()) {
123        // layout.positionBindings.entrySet())
124        // //
125        // {
126        if (!entry.getKey().equals(bottom)) {
127          final Point3D p = entry.getValue().getValue();
128          final Concept<G, M> c = entry.getKey();
129          for (Pair<Concept<G, M>, Concept<G, M>> edge : layout.lattice)
130            if (!edge.x().equals(bottom) && !c.equals(edge.x()) && !c.equals(edge.y())) {
131              final Point3D q1 = layout.getOrAddPosition(edge.x()).getValue();
132              final Point3D q2 = layout.getOrAddPosition(edge.y()).getValue();
133              final double length = q1.distance(q2);
134              final double conflictDistance = Math.min(Points.pointSegmentDistance(p, q1, q2) / length, 0.5d) * 2d;
135              num++;
136              minL = Math.min(minL, length);
137              sumL += length;
138              sumC += conflictDistance;
139              if (conflictDistance < minC) {
140                minC = conflictDistance;
141                faulty = c;
142              }
143            }
144        }
145      }
146      final double avgC = num == 0d ? 1d : (sumC / num);
147      final double avgL = num == 0d ? 1d : (sumL / num);
148      return Pair.of(faulty, Math.pow(minC * Math.pow(minC / avgC, 0.5d) * Math.pow(minL / avgL, 0.25d), 0.125d));
149    });
150  }
151
152  public static final <G, M> QualityMeasure<G, M, Integer> parallelEdges() {
153    return of(layout -> {
154      return 0;
155    });
156  }
157
158  public static final <G, M> QualityMeasure<G, M, Integer> distinctDirections() {
159    return of(layout -> {
160      return 0;
161    });
162  }
163
164  public static final <G, M> QualityMeasure<G, M, Integer> distinctAngles() {
165    return of(layout -> {
166      return 0;
167    });
168  }
169}