Java tutorial
/******************************************************************************* * Caleydo - Visualization for Molecular Biology - http://caleydo.org * Copyright (c) The Caleydo Team. All rights reserved. * Licensed under the new BSD license, available at http://caleydo.org/license *******************************************************************************/ package org.caleydo.view.domino.internal.band; import static org.caleydo.view.domino.internal.band.IBandHost.SourceTarget.SOURCE; import static org.caleydo.view.domino.internal.band.IBandHost.SourceTarget.TARGET; import gleem.linalg.Vec2f; import gleem.linalg.Vec4f; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.caleydo.core.data.selection.SelectionType; import org.caleydo.core.id.IDType; import org.caleydo.core.util.collection.Pair; import org.caleydo.core.util.color.Color; import org.caleydo.core.view.opengl.layout2.GLGraphics; import org.caleydo.core.view.opengl.layout2.geom.Rect; import org.caleydo.core.view.opengl.layout2.manage.GLLocation; import org.caleydo.view.domino.api.model.EDirection; import org.caleydo.view.domino.api.model.typed.MultiTypedSet; import org.caleydo.view.domino.api.model.typed.TypedCollections; import org.caleydo.view.domino.api.model.typed.TypedGroupList; import org.caleydo.view.domino.api.model.typed.TypedList; import org.caleydo.view.domino.api.model.typed.TypedListGroup; import org.caleydo.view.domino.api.model.typed.TypedSet; import org.caleydo.view.domino.api.model.typed.TypedSets; import org.caleydo.view.domino.internal.Constants; import org.caleydo.view.domino.internal.INodeLocator; import org.caleydo.view.domino.internal.band.IBandHost.SourceTarget; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap.Builder; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; /** * @author Samuel Gratzl * */ public class CrossBand extends ABand { private Disc overview; private final List<IBandRenderAble> overviewRoutes = new ArrayList<>(3); private Vec2f sLoc, tLoc; public CrossBand(Pair<String, String> labels, MultiTypedSet shared, TypedGroupList sData, TypedGroupList tData, INodeLocator sLocator, INodeLocator tLocator, Vec2f s, Vec2f t, EDirection sDir, EDirection tDir, ABandIdentifier identifier) { super(shared, sData, tData, sLocator, tLocator, sDir, tDir, identifier, labels); assert sDir.asDim().isHorizontal(); this.sLoc = s; this.tLoc = t; { TypedSet sShared = shared.slice(sData.getIdType()); TypedSet tShared = shared.slice(tData.getIdType()); float sr = ((float) sShared.size()) / sData.size(); float tr = ((float) tShared.size()) / tData.size(); boolean s_top = this.tDir == EDirection.SOUTH; boolean t_left = this.sDir == EDirection.EAST; final float wtotal = (float) locT(EBandMode.OVERVIEW, 0).getSize(); final float htotal = (float) locS(EBandMode.OVERVIEW, 0).getSize(); Rect bounds = new Rect(tLoc.x() + (t_left ? ((1 - tr) * wtotal) : 0), sLoc.y() + (!s_top ? 0 : (1 - sr) * htotal), tr * wtotal, sr * htotal); String label = toIntersectionLabel(sLabel, sData.size(), tLabel, tData.size(), sShared, tShared); this.overview = new Disc(label, bounds, sShared, tShared, sLoc.x(), tLoc.y()); overviewRoutes.add(this.overview); if (sr < 1) { // add a non-mapped indicator TypedSet sNotMapped = sData.asSet().difference(sShared); Pair<Vec4f, Vec4f> r = notMappedConnectors(SOURCE, sr); label = toNotMappedLabel(sLabel, sData.size(), tLabel, tData.size(), sNotMapped); overviewRoutes.add(new NotMapped(label, sNotMapped, TypedCollections.empty(tData.getIdType()), SOURCE, r.getFirst(), r.getSecond(), sDir, EBandMode.OVERVIEW)); } if (tr < 1) { TypedSet tNotMapped = tData.asSet().difference(tShared); Pair<Vec4f, Vec4f> r = notMappedConnectors(TARGET, tr); label = toNotMappedLabel(tLabel, tData.size(), sLabel, sData.size(), tNotMapped); overviewRoutes.add(new NotMapped(label, TypedCollections.empty(sData.getIdType()), tNotMapped, TARGET, r.getFirst(), r.getSecond(), tDir, EBandMode.OVERVIEW)); } } } /** * @param source * @param ratio * @return */ private Pair<Vec4f, Vec4f> notMappedConnectors(SourceTarget source, float ratio) { final float wtotal = (float) locT(EBandMode.OVERVIEW, 0).getSize(); final float htotal = (float) locS(EBandMode.OVERVIEW, 0).getSize(); if (source == SourceTarget.SOURCE) { return notMappedConnectors(source, (1 - ratio) * htotal, htotal, 0, wtotal); } else { return notMappedConnectors(source, (1 - ratio) * wtotal, wtotal, 0, htotal); } } private Pair<Vec4f, Vec4f> notMappedConnectors(SourceTarget source, float notMappedSize, float totalSize, float shift, float otherTotal) { boolean s_top = this.tDir == EDirection.SOUTH; boolean t_left = this.sDir == EDirection.EAST; Vec4f sv, tv; if (source == SourceTarget.SOURCE) { final float s_y = sLoc.y() + shift + (s_top ? 0 : (totalSize - notMappedSize)); if (this.sDir == EDirection.WEST) { sv = new Vec4f(tLoc.x(), s_y, notMappedSize, 0); tv = new Vec4f(tLoc.x() + otherTotal, s_y, 0, 0); } else { sv = new Vec4f(tLoc.x() + otherTotal, s_y, notMappedSize, 0); tv = new Vec4f(tLoc.x(), s_y, 0, 0); } } else { final float t_x = tLoc.x() + shift + (t_left ? 0 : (totalSize - notMappedSize)); if (this.tDir == EDirection.NORTH) { sv = new Vec4f(t_x, sLoc.y() + otherTotal, 0, 0); tv = new Vec4f(t_x, sLoc.y(), notMappedSize, 0); } else { sv = new Vec4f(t_x, sLoc.y(), 0, 0); tv = new Vec4f(t_x, sLoc.y() + otherTotal, notMappedSize, 0); } } return Pair.make(sv, tv); } @Override public void setLocators(INodeLocator sLocator, INodeLocator tLocator) { super.setLocators(sLocator, tLocator); { float sr = ((float) overview.sShared.size()) / sData.size(); float tr = ((float) overview.tShared.size()) / tData.size(); final float wtotal = (float) locT(EBandMode.OVERVIEW, 0).getSize(); final float htotal = (float) locS(EBandMode.OVERVIEW, 0).getSize(); boolean s_top = this.tDir == EDirection.SOUTH; boolean t_left = this.sDir == EDirection.EAST; Rect bounds = new Rect(tLoc.x() + (t_left ? ((1 - tr) * wtotal) : 0), sLoc.y() + (!s_top ? 0 : (1 - sr) * htotal), tr * wtotal, sr * htotal); this.overview = new Disc(overview.getLabel(), bounds, overview.sShared, overview.tShared, sLoc.x(), tLoc.y()); overviewRoutes.set(0, this.overview); if (sr < 1) { NotMapped m = (NotMapped) overviewRoutes.get(1); // add a non-mapped indicator Pair<Vec4f, Vec4f> r = notMappedConnectors(SourceTarget.SOURCE, sr); overviewRoutes.add(new NotMapped(m.getLabel(), m.sShared, m.tShared, SourceTarget.SOURCE, r.getFirst(), r.getSecond(), sDir, EBandMode.OVERVIEW)); } if (tr < 1) { int index = sr < 1 ? 2 : 1; NotMapped m = (NotMapped) overviewRoutes.get(index); Pair<Vec4f, Vec4f> r = notMappedConnectors(SourceTarget.TARGET, tr); overviewRoutes.add(new NotMapped(m.getLabel(), m.sShared, m.tShared, SourceTarget.TARGET, r.getFirst(), r.getSecond(), tDir, EBandMode.OVERVIEW)); } } } @Override public boolean stubify() { // TODO Auto-generated method stub return false; } @Override public void renderMiniMap(GLGraphics g) { overview.renderMiniMap(g); } @Override protected IBandRenderAble overviewRoute() { return overview; } @Override protected List<? extends IBandRenderAble> overviewRoutes() { return overviewRoutes; } @Override public void render(GLGraphics g, float w, float h, IBandHost host) { renderAdapter(g); super.render(g, w, h, host); } @Override public boolean intersects(Rectangle2D bounds) { if (super.intersects(bounds)) return true; // TODO check adapter final float hs = (float) locS(EBandMode.OVERVIEW, 0).getSize(); final float wt = (float) locT(EBandMode.OVERVIEW, 0).getSize(); { float x; float w; if (sDir == EDirection.WEST) { x = sLoc.x(); w = tLoc.x() - sLoc.x(); } else { x = tLoc.x() + wt; w = sLoc.x() - tLoc.x() - wt; } if (bounds.intersects(x, sLoc.y(), w, hs)) return true; } { float y; float h; if (tDir == EDirection.NORTH) { y = tLoc.y(); h = sLoc.y() - tLoc.y(); } else { y = sLoc.y() + hs; h = tLoc.y() - sLoc.y() - hs; } if (bounds.intersects(tLoc.x(), y, wt, h)) return true; } return false; // return overviewRoute().intersects(bounds); } private void renderAdapter(GLGraphics g) { final boolean showAll = mode.compareTo(EBandMode.GROUPED_DETAIL) >= 0; if (showAll) g.color(0, 0, 0, 0.04f); else g.color(EBandMode.OVERVIEW.getColor()); final float hs = (float) locS(EBandMode.OVERVIEW, 0).getSize(); final float wt = (float) locT(EBandMode.OVERVIEW, 0).getSize(); if (showAll) { g.fillRect(tLoc.x(), sLoc.y(), wt, hs); } { float x; float w; final int sGroups = sData.getGroups().size(); if (sDir == EDirection.WEST) { x = sLoc.x(); w = tLoc.x() - sLoc.x(); } else { x = tLoc.x() + wt; w = sLoc.x() - tLoc.x() - wt; } if (showAll || sGroups <= 1) g.fillRect(x, sLoc.y(), w, hs); else { for (int i = 0; i < sGroups; ++i) { GLLocation gi = locS(EBandMode.GROUPS, i); g.fillRect(x, sLoc.y() + (float) gi.getOffset(), w, (float) gi.getSize()); } } } { float y; float h; final int tGroups = tData.getGroups().size(); if (tDir == EDirection.NORTH) { y = tLoc.y(); h = sLoc.y() - tLoc.y(); } else { y = sLoc.y() + hs; h = tLoc.y() - sLoc.y() - hs; } if (showAll || tGroups <= 1) g.fillRect(tLoc.x(), y, wt, h); else { for (int i = 0; i < tGroups; ++i) { GLLocation gi = locT(EBandMode.GROUPS, i); g.fillRect(tLoc.x() + (float) gi.getOffset(), y, (float) gi.getSize(), h); } } } } void renderConnector(GLGraphics g, Rect bounds) { final Color c = SelectionType.MOUSE_OVER.getColor(); g.color(c.r, c.g, c.b, 0.5f); float xStart = this.sLoc.x(); float yStart = this.tLoc.y(); if (sDir == EDirection.WEST) smallRect(g, xStart, bounds.y(), bounds.x() - xStart, bounds.height()); else { smallRect(g, bounds.x2(), bounds.y(), xStart - bounds.x2(), bounds.height()); } if (tDir == EDirection.NORTH) smallRect(g, bounds.x(), yStart, bounds.width(), bounds.y() - yStart); else { smallRect(g, bounds.x(), bounds.y2(), bounds.width(), yStart - bounds.y2()); } } private static void smallRect(GLGraphics g, float x, float y, float w, float h) { if (w <= 2) g.lineWidth(2).drawLine(x, y, x, y + h).lineWidth(1); else if (h <= 2) g.lineWidth(2).drawLine(x, y, x + w, y).lineWidth(1); else g.fillRect(x, y, w, h); } @Override protected List<? extends IBandRenderAble> computeGroupRoutes() { List<IBandRenderAble> groupRoutes = new ArrayList<>(); List<TypedSet> sSets = new ArrayList<>(); List<TypedSet> tSets = new ArrayList<>(); // convert all to the subset of the shared set final List<TypedListGroup> sgroups = sData.getGroups(); for (TypedListGroup sGroup : sgroups) sSets.add(overview.sShared.intersect(sGroup.asSet())); final List<TypedListGroup> tgroups = tData.getGroups(); for (TypedListGroup tGroup : tgroups) tSets.add(overview.tShared.intersect(tGroup.asSet())); // starting points for right side groups int[] tinneracc = new int[tgroups.size()]; Arrays.fill(tinneracc, 0); List<List<MosaicRect>> tgroupss = new ArrayList<>(tgroups.size()); for (int i = 0; i < tinneracc.length; ++i) tgroupss.add(new ArrayList<MosaicRect>()); final TypedSet tEmpty = TypedCollections.empty(tData.getIdType()); final TypedSet sEmpty = TypedCollections.empty(sData.getIdType()); final float wtotal = (float) locT(EBandMode.OVERVIEW, 0).getSize(); final float htotal = (float) locS(EBandMode.OVERVIEW, 0).getSize(); // for each left groups check all right groups for (int i = 0; i < sgroups.size(); ++i) { TypedListGroup sgroup = sgroups.get(i); TypedSet sset = sSets.get(i); if (sset.isEmpty()) continue; GLLocation sgroupLocation = locS(EBandMode.GROUPS, i); int sinneracc = 0; final double sFactor = sgroupLocation.getSize() / sgroup.size(); double y = sLoc.y() + sgroupLocation.getOffset(); int sGroupStart = groupRoutes.size(); for (int j = 0; j < tgroups.size(); ++j) { TypedListGroup tgroup = tgroups.get(j); TypedSet tset = tSets.get(j); if (tset.isEmpty()) continue; MultiTypedSet shared = TypedSets.intersect(sset, tset); if (shared.isEmpty()) // nothing shared continue; GLLocation tgroupLocation = locT(EBandMode.GROUPS, j); double x = tLoc.x() + tgroupLocation.getOffset(); TypedSet sShared = shared.slice(sData.getIdType()); TypedSet tShared = shared.slice(tData.getIdType()); String label = toIntersectionLabel(sgroup.getLabel(), sgroup.size(), tgroup.getLabel(), tgroup.size(), sShared, tShared); final double tFactor = tgroupLocation.getSize() / tgroup.size(); double h = sShared.size() * sFactor; double w = tShared.size() * tFactor; Rect bounds = new Rect((float) (x + tinneracc[j] * tFactor), (float) (y + sinneracc * sFactor), (float) w, (float) h); final MosaicRect m = new MosaicRect(label, bounds, sShared, tShared, EBandMode.GROUPS); groupRoutes.add(m); tgroupss.get(j).add(m); sinneracc += sShared.size(); tinneracc[j] += tShared.size(); } final int notMapped = sgroup.size() - sinneracc; if (notMapped > 0) { TypedSet notMappedIds = sgroup.asSet().difference(overview.sShared); final float notMappedSize = (float) (notMapped * sFactor); Pair<Vec4f, Vec4f> r = notMappedConnectors(SourceTarget.SOURCE, notMappedSize, (float) sgroupLocation.getSize(), (float) sgroupLocation.getOffset(), wtotal); String label = toNotMappedLabel(sgroup.getLabel(), sgroup.size(), tLabel, tData.size(), notMappedIds); groupRoutes.add(new NotMapped(label, notMappedIds, tEmpty, SourceTarget.SOURCE, r.getFirst(), r.getSecond(), sDir, EBandMode.GROUPS)); boolean s_top = this.tDir == EDirection.SOUTH; if (s_top) { for (MosaicRect bb : Iterables.filter(groupRoutes.subList(sGroupStart, groupRoutes.size() - 1), MosaicRect.class)) { bb.bounds.y(bb.bounds.y() + notMappedSize); } } } } for (int i = 0; i < tgroups.size(); ++i) { TypedListGroup tgroup = tgroups.get(i); final int notMapped = tgroup.size() - tinneracc[i]; if (notMapped <= 0) continue; GLLocation tgroupLocation = locT(EBandMode.GROUPS, i); final double tFactor = tgroupLocation.getSize() / tgroup.size(); TypedSet notMappedIds = tgroup.asSet().difference(overview.tShared); final float notMappedSize = (float) (notMapped * tFactor); Pair<Vec4f, Vec4f> r = notMappedConnectors(SourceTarget.TARGET, notMappedSize, (float) (tgroupLocation.getSize()), (float) (tgroupLocation.getOffset()), htotal); String label = toNotMappedLabel(tgroup.getLabel(), tgroup.size(), sLabel, sData.size(), notMappedIds); groupRoutes.add(new NotMapped(label, sEmpty, notMappedIds, SourceTarget.TARGET, r.getFirst(), r.getSecond(), tDir, EBandMode.GROUPS)); boolean t_left = this.sDir == EDirection.EAST; if (t_left) { for (MosaicRect bb : tgroupss.get(i)) bb.bounds.x(bb.bounds.x() + notMappedSize); } } return groupRoutes; } @Override protected List<? extends IBandRenderAble> computeDetailRoutes() { List<IBandRenderAble> detailRoutes = new ArrayList<>(); TypedList s = shared.sliceList(sData.getIdType()); final Multimap<Integer, Integer> slookup = computeLookup(s); TypedList t = shared.sliceList(tData.getIdType()); final Multimap<Integer, Integer> tlookup = computeLookup(tData); Set<PointB> points = new HashSet<>(); for (int i = 0; i < sData.size(); ++i) { Integer sId = sData.get(i); if (sId.intValue() < 0) { continue; } Collection<Integer> indices = slookup.get(sId); if (indices.isEmpty()) { continue; } GLLocation slocation = locS(EBandMode.DETAIL, i); if (!slocation.isDefined()) { continue; } for (int sindex : indices) { Integer tId = t.get(sindex); Collection<Integer> tindices = tlookup.get(tId); if (tindices.isEmpty()) continue; for (int tindex : tindices) { GLLocation tlocation = locT(EBandMode.DETAIL, tindex); if (!tlocation.isDefined()) continue; boolean merged = false; for (PointB point : points) { if (point.merge(slocation, tlocation, sId, tId)) { merged = true; break; } } if (!merged) { points.add(new PointB(slocation, tlocation, sId, tId)); } } } } flushPoints(detailRoutes, points, EBandMode.DETAIL); return detailRoutes; } @Override protected List<? extends IBandRenderAble> computeGroupDetailRoutes() { return detailRoutes(); } private void flushPoints(List<IBandRenderAble> detailRoutes, Set<PointB> lines, EBandMode mode) { if (lines.isEmpty()) return; final IDType s = this.overview.sShared.getIdType(); final IDType t = this.overview.tShared.getIdType(); for (PointB line : lines) detailRoutes.add(line.create(s, t, mode)); lines.clear(); } class PointB { protected GLLocation sloc; protected GLLocation tloc; private final Set<Integer> sIds = new HashSet<>(2); private final Set<Integer> tIds = new HashSet<>(2); public PointB(GLLocation s, GLLocation t, Integer sId, Integer tId) { this.sloc = s; this.tloc = t; this.sIds.add(sId); this.tIds.add(tId); } public boolean merge(GLLocation sloc, GLLocation tloc, Integer sId, Integer tId) { if (!combineAble(this.sloc, sloc) || !combineAble(this.tloc, tloc)) return false; this.sloc = mergeImpl(this.sloc, sloc); this.tloc = mergeImpl(this.tloc, tloc); this.sIds.add(sId); this.tIds.add(tId); return true; } private boolean combineAble(GLLocation a, GLLocation b) { // two lines can be combined if double aStart = a.getOffset(); double aEnd = a.getOffset2(); double bStart = b.getOffset(); double bEnd = b.getOffset2(); if (c(aStart, bStart) && c(aEnd, bEnd)) // same return true; return false; } /** * @param sloc2 * @param sloc3 * @return */ private GLLocation mergeImpl(GLLocation a, GLLocation b) { double start = Math.min(a.getOffset(), b.getOffset()); double end = Math.max(a.getOffset2(), b.getOffset2()); return new GLLocation(start, end - start); } private boolean c(double a, double b) { return Math.abs(a - b) < 0.01; } public MosaicRect create(IDType s, IDType t, EBandMode mode) { double x = tLoc.x() + tloc.getOffset(); double y = sLoc.y() + sloc.getOffset(); double w = tloc.getSize(); double h = sloc.getSize(); Rect bounds = new Rect((float) x, (float) y, (float) w, (float) h); final TypedSet sIds2 = new TypedSet(sIds, s); final TypedSet tIds2 = new TypedSet(tIds, t); String label = toItemLabel(sIds2, tIds2); return new MosaicRect(label, bounds, sIds2, tIds2, mode); } } private static Multimap<Integer, Integer> computeLookup(List<Integer> s) { Builder<Integer, Integer> b = ImmutableMultimap.builder(); for (int i = 0; i < s.size(); ++i) { b.put(s.get(i), i); } return b.build(); } private class Disc extends MosaicRect { private final float xStart, yStart; public Disc(String label, Rect bounds, TypedSet sIds, TypedSet tIds, float xStart, float yStart) { super(label, bounds, sIds, tIds, EBandMode.OVERVIEW); this.xStart = xStart; this.yStart = yStart; } /** * @param g */ public void renderMiniMap(GLGraphics g) { final Color color = EBandMode.OVERVIEW.getColor(); g.color(color.r, color.g, color.b, color.a); renderBox(g, true, 1.f, 1.f); } private void renderBox(GLGraphics g, boolean fill, float wFactor, float hFactor) { final Rect b = bounds; if (fill) { if (sDir == EDirection.WEST) { if (tDir == EDirection.NORTH) { g.fillRect(xStart, b.y(), b.x() - xStart, b.height() * hFactor); g.fillRect(b.x(), yStart, b.width() * wFactor, b.y() - yStart + b.height() * hFactor); } else { g.fillRect(xStart, b.y() + b.height() * (1 - hFactor), b.x() - xStart, b.height() * hFactor); g.fillRect(b.x(), b.y() + b.height() * (1 - hFactor), b.width() * wFactor, yStart - b.y2() + b.height() * hFactor); } } else { if (tDir == EDirection.NORTH) { g.fillRect(b.x2(), b.y(), xStart - b.x2(), b.height() * hFactor); g.fillRect(b.x() + (b.width() * (1 - wFactor)), yStart, b.width() * wFactor, b.y() - yStart + b.height() * hFactor); } else { g.fillRect(b.x2(), b.y() + b.height() * (1 - hFactor), xStart - b.x2(), b.height() * hFactor); g.fillRect(b.x() + (b.width() * (1 - wFactor)), b.y() + b.height() * (1 - hFactor), b.width() * wFactor, yStart - b.y2() + b.height() * hFactor); } } } else { if (sDir == EDirection.WEST) { if (tDir == EDirection.NORTH) { g.drawPath(true, v(xStart, b.y()), b.xy(), v(b.x(), yStart), v(b.x2(), yStart), b.x2y2(), v(xStart, b.y2())); } else { g.drawPath(true, v(xStart, b.y()), b.x2y(), v(b.x2(), yStart), v(b.x(), yStart), b.xy2(), v(xStart, b.y2())); } } else { if (tDir == EDirection.NORTH) { g.drawPath(true, v(b.x(), yStart), v(b.x2(), yStart), b.x2y(), v(xStart, b.y()), v(xStart, b.y2()), b.xy2()); } else { g.drawPath(true, b.xy(), v(xStart, b.y()), v(xStart, b.y2()), b.x2y2(), v(b.x2(), yStart), v(b.x(), yStart)); } } } } /** * @param bound * @return */ @Override public boolean intersects(Rectangle2D bound) { if (super.intersects(bound)) return true; final Rect b = bounds; // FIXME wrong // if (sDim == EDirection.WEST) { // if (new Rect(xStart, b.y(), b.x() - xStart, b.height()).asRectangle2D().intersects(bound)) // return true; // } else { // if (new Rect(b.x2(), b.y(), xStart - b.x2(), b.height()).asRectangle2D().intersects(bound)) // return true; // } // // if (tDim == EDirection.NORTH) { // if (new Rect(b.x(), yStart, b.width(), b.y() - yStart).asRectangle2D().intersects(bound)) // return true; // } else { // if (new Rect(b.x(), b.y2(), b.width(), yStart - b.y2()).asRectangle2D().intersects(bound)) // return true; // } return false; } @Override public void renderRoute(GLGraphics g, IBandHost host, int nrItems, boolean withSelection) { super.renderRoute(g, host, nrItems, withSelection); // final Color color = EBandMode.OVERVIEW.getColor(); // g.color(color); // renderBox(g, true, 1.f, 1.f); // // // if (g.isPickingPass()) // return; // for (SelectionType type : SELECTION_TYPES) { // int sS = host.getSelected(sShared, type).size(); // int tS = host.getSelected(tShared, type).size(); // if (sS > 0 && tS > 0) { // final Color c = type.getColor(); // g.color(c.r, c.g, c.b, 0.5f); // renderBox(g, true, (tS / (float) tShared.size()), (sS / (float) sShared.size())); // } // } // // g.color(color.darker()); // renderBox(g, false, 1.f, 1.f); } } static Vec2f v(float x, float y) { return new Vec2f(x, y); } private class MosaicRect extends ARelation { protected final Rect bounds; public MosaicRect(String label, Rect bounds, TypedSet sIds, TypedSet tIds, EBandMode mode) { super(label, sIds, tIds, mode); this.bounds = bounds; } @Override public void renderRoute(GLGraphics g, IBandHost host, int nrItems, boolean withSelection) { if (mode.compareTo(EBandMode.GROUPED_DETAIL) >= 0 || (bounds.width() <= Constants.SCATTER_POINT_SIZE && bounds.height() <= Constants.SCATTER_POINT_SIZE)) { renderPoint(g, host); return; } Color color = mode.getColor(); g.color(color); g.fillRect(bounds); if (g.isPickingPass()) return; for (SelectionType type : SELECTION_TYPES) { int sS = host.getSelected(sShared, type).size(); int tS = host.getSelected(tShared, type).size(); if (sS > 0 && tS > 0) { final Color c = type.getColor(); g.color(c.r, c.g, c.b, 0.5f); Rect b2 = new Rect(bounds.x(), bounds.y(), bounds.width() * (tS / (float) tShared.size()), bounds.height() * (sS / (float) sShared.size())); g.fillRect(b2); if (type == SelectionType.MOUSE_OVER) renderConnector(g, b2); } } if (mode.ordinal() <= EBandMode.GROUPS.ordinal()) { g.color(color.darker()); g.drawRect(bounds.x(), bounds.y(), bounds.width(), bounds.height()); } } /** * @param g * @param host */ private void renderPoint(GLGraphics g, IBandHost host) { Color c = mode.getColor(); boolean hovered = false; if (!g.isPickingPass()) { for (SelectionType type : Lists.reverse(SELECTION_TYPES)) { int sS = host.getSelected(sShared, type).size(); int tS = host.getSelected(tShared, type).size(); if (sS > 0 && tS > 0) { c = type.getColor(); if (type == SelectionType.MOUSE_OVER) hovered = true; break; } } } g.color(c.r, c.g, c.b, 1.f); g.drawPoint(bounds.x() + bounds.width() * 0.5f, bounds.y() + bounds.height() * 0.5f); if (hovered) renderConnector(g, bounds); } @Override public boolean intersects(Rectangle2D bounds) { return this.bounds.asRectangle2D().intersects(bounds); } @Override public Rect getBoundingBox() { return bounds; } } }