+ Map<Object, AtomSpecModel> colourMap = buildColoursMap(ssm, files,
+ sequence, sr, viewPanel);
+
+ List<String> colourCommands = buildColourCommands(colourMap);
+
+ StructureMappingcommandSet cs = new StructureMappingcommandSet(
+ ChimeraCommands.class, null,
+ colourCommands.toArray(new String[colourCommands.size()]));
+
+ return new StructureMappingcommandSet[] { cs };
+ }
+
+ /**
+ * Traverse the map of colours/models/chains/positions to construct a list of
+ * 'color' commands (one per distinct colour used). The format of each command
+ * is
+ *
+ * <pre>
+ * <blockquote>
+ * color colorname #modelnumber:range.chain
+ * e.g. color #00ff00 #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,...
+ * </blockquote>
+ * </pre>
+ *
+ * @param colourMap
+ * @return
+ */
+ protected static List<String> buildColourCommands(
+ Map<Object, AtomSpecModel> colourMap)
+ {
+ /*
+ * This version concatenates all commands into a single String (semi-colon
+ * delimited). If length limit issues arise, refactor to return one color
+ * command per colour.
+ */
+ List<String> commands = new ArrayList<String>();
+ StringBuilder sb = new StringBuilder(256);
+ boolean firstColour = true;
+ for (Object key : colourMap.keySet())
+ {
+ Color colour = (Color) key;
+ String colourCode = ColorUtils.toTkCode(colour);
+ if (!firstColour)
+ {
+ sb.append("; ");
+ }
+ sb.append("color ").append(colourCode).append(" ");
+ firstColour = false;
+ final AtomSpecModel colourData = colourMap.get(colour);
+ sb.append(colourData.getAtomSpec());
+ }
+ commands.add(sb.toString());
+ return commands;
+ }
+
+ /**
+ * Traverses a map of { modelNumber, {chain, {list of from-to ranges} } } and
+ * builds a Chimera format atom spec
+ *
+ * @param modelAndChainRanges
+ */
+ protected static String getAtomSpec(
+ Map<Integer, Map<String, List<int[]>>> modelAndChainRanges)
+ {
+ StringBuilder sb = new StringBuilder(128);
+ boolean firstModelForColour = true;
+ for (Integer model : modelAndChainRanges.keySet())
+ {
+ boolean firstPositionForModel = true;
+ if (!firstModelForColour)
+ {
+ sb.append("|");
+ }
+ firstModelForColour = false;
+ sb.append("#").append(model).append(":");
+
+ final Map<String, List<int[]>> modelData = modelAndChainRanges
+ .get(model);
+ for (String chain : modelData.keySet())
+ {
+ boolean hasChain = !"".equals(chain.trim());
+ for (int[] range : modelData.get(chain))
+ {
+ if (!firstPositionForModel)
+ {
+ sb.append(",");
+ }
+ if (range[0] == range[1])
+ {
+ sb.append(range[0]);
+ }
+ else
+ {
+ sb.append(range[0]).append("-").append(range[1]);
+ }
+ if (hasChain)
+ {
+ sb.append(".").append(chain);
+ }
+ firstPositionForModel = false;
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * <pre>
+ * Build a data structure which records contiguous subsequences for each colour.
+ * From this we can easily generate the Chimera command for colour by sequence.
+ * Color
+ * Model number
+ * Chain
+ * list of start/end ranges
+ * Ordering is by order of addition (for colours and positions), natural ordering (for models and chains)
+ * </pre>
+ */
+ protected static Map<Object, AtomSpecModel> buildColoursMap(
+ StructureSelectionManager ssm, String[] files,
+ SequenceI[][] sequence, SequenceRenderer sr,
+ AlignmentViewPanel viewPanel)
+ {
+ FeatureRenderer fr = viewPanel.getFeatureRenderer();
+ FeatureColourFinder finder = new FeatureColourFinder(fr);
+ AlignViewportI viewport = viewPanel.getAlignViewport();
+ HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
+ AlignmentI al = viewport.getAlignment();
+ Map<Object, AtomSpecModel> colourMap = new LinkedHashMap<Object, AtomSpecModel>();
+ Color lastColour = null;