d96ac78dc01133a55575481975194bfc1b8accc1
markd
  Tue Dec 12 13:11:04 2023 -0800
Make glyph drawing code from decorators to being available to all of hgTracks.  This is to allow for experimentation in using glyphs to draw SNPs.  There is no functional changes, it just moves code around.

diff --git src/hg/hgTracks/simpleTracks.c src/hg/hgTracks/simpleTracks.c
index 9975164..5e35448 100644
--- src/hg/hgTracks/simpleTracks.c
+++ src/hg/hgTracks/simpleTracks.c
@@ -1679,30 +1679,132 @@
     w = 1;
 hvGfxBox(hvg, x1, y, w, height, color);
 
 char *shortLabel = cloneString(label);
 /* calculate how many characters we can squeeze into the box */
 int charsInBox = w / mgFontCharWidth(font, 'm');
 if (charsInBox < strlen(label))
     if (charsInBox > 4)
         strcpy(shortLabel+charsInBox-3, "...");
 if (charsInBox < strlen(shortLabel))
     return;
 Color labelColor = hvGfxContrastingColor(hvg, color);
 hvGfxTextCentered(hvg, x1, y, w, height, labelColor, font, shortLabel);
 }
 
+struct xyPair {
+    double x, y;
+};
+
+struct glyphShape {
+    int nPoints;
+    struct xyPair* points;
+};
+
+/* An obtuse representation, but this is a list of the glyphs we know how to draw along with coordinates for
+ * the sequential points of each glyph on the unit square.  Those will then be scaled by whatever the current
+ * track height is for actual drawing. */
+static struct glyphShape glyphShapes[] = {
+    [GLYPH_CIRCLE] = (struct glyphShape) {0, NULL},
+
+    [GLYPH_TRIANGLE] = (struct glyphShape) {3, (struct xyPair[]) {{0.5,0},{1.07735,1},{-0.07735,1}}},
+    [GLYPH_INV_TRIANGLE] = (struct glyphShape) {3, (struct xyPair[]) {{0.5,1},{1.07735,0},{-0.07735,0}}},
+    [GLYPH_SQUARE] = (struct glyphShape) {4, (struct xyPair[]) {{0,0},{1,0},{1,1},{0,1}}},
+    [GLYPH_DIAMOND] = (struct glyphShape) {4, (struct xyPair[]) {{0.5,0},{1,0.5},{0.5,1},{0,0.5}}},
+    [GLYPH_PENTAGRAM] = (struct glyphShape) {5, (struct xyPair[]) {{0.5,0.0},{0.824920,1.0},{-0.025731,0.381966},
+            {1.02573,0.381966},{0.175080,1}}},
+    [GLYPH_OCTAGON] = (struct glyphShape) {8, (struct xyPair[]) {{0.292893,1},{0.707107,1},{1,0.707107},
+            {1,0.292893},{0.707107,0},{0.292893,0},{0,0.292893},{0,0.707107}}},
+    [GLYPH_STAR] = (struct glyphShape) {10, (struct xyPair[]) {{0.500000,0.000000},{0.624108,0.381966},{1.025731,0.381966},
+            {0.700811,0.618034},{0.824920,1.000000},{0.500000,0.763932},{0.175080,1.000000},{0.299189,0.618034},
+            {-0.025731,0.381966},{0.375892,0.381966}}}
+};
+
+
+
+void drawScalledGlyph(struct hvGfx *hvg, int chromStart, int chromEnd, double scale, int xOff, int y,
+                      int heightPer, glyphType glyph, boolean filled, Color outlineColor, Color fillColor)
+/* Draw a glyph as a circle/polygon.  If filled, draw as with fillColor,
+ * which may have transparency.
+ */
+{
+int glyphHeight = heightPer-1;
+int startX, endX;
+double middleX, middleY = y+heightPer/2.0;
+// A glyph might be defined on a wide range - find the center and draw specifically there
+// so we don't have a glyph shifting if only part of that window is in view.
+int centeredStart, centeredEnd;
+centeredStart = (chromStart + chromEnd)/2;
+centeredEnd = (chromStart + chromEnd+1)/2;
+int ptCount, i, x0, y0;
+if (!scaledBoxToPixelCoords(centeredStart, centeredEnd, scale, xOff, &startX, &endX))
+    return;  // apparently we don't intersect the window
+middleX = (startX+endX)/2.0;
+switch (glyph)
+    {
+    case GLYPH_CIRCLE:
+        hvGfxCircle(hvg, middleX, middleY, heightPer/2, fillColor, TRUE);
+        hvGfxCircle(hvg, middleX, middleY, heightPer/2, outlineColor, FALSE);
+        break;
+    default:
+        ptCount = glyphShapes[glyph].nPoints;
+        struct gfxPoly *poly = gfxPolyNew();
+        for (i=0; i<ptCount; i++)
+            {
+            x0 = middleX + (glyphShapes[glyph].points[i].x-0.5)*glyphHeight;
+            y0 = middleY + (glyphShapes[glyph].points[i].y-0.5)*glyphHeight;
+            gfxPolyAddPoint(poly, x0, y0);
+            }
+        hvGfxDrawPoly(hvg,poly,fillColor,TRUE);
+        hvGfxDrawPoly(hvg,poly,outlineColor,FALSE);
+        gfxPolyFree(&poly);
+        break;
+    }
+}
+
+#define GLYPH_STRING_CIRCLE "Circle"
+#define GLYPH_STRING_TRIANGLE "Triangle"
+#define GLYPH_STRING_INV_TRIANGLE "InvTriangle"
+#define GLYPH_STRING_SQUARE "Square"
+#define GLYPH_STRING_DIAMOND "Diamond"
+#define GLYPH_STRING_OCTAGON "Octagon"
+#define GLYPH_STRING_STAR "Star"
+#define GLYPH_STRING_PENTAGRAM "Pentagram"
+
+glyphType parseGlyphType(char *glyphStr)
+/* Return the enum glyph type for a string specifying a glyph.
+ * Defaults to GLYPH_CIRCLE if the string is unrecognized. */
+{
+if (sameWordOk(glyphStr, GLYPH_STRING_TRIANGLE))
+    return GLYPH_TRIANGLE;
+if (sameWordOk(glyphStr, GLYPH_STRING_INV_TRIANGLE))
+    return GLYPH_INV_TRIANGLE;
+if (sameWordOk(glyphStr, GLYPH_STRING_SQUARE))
+    return GLYPH_SQUARE;
+if (sameWordOk(glyphStr, GLYPH_STRING_DIAMOND))
+    return GLYPH_DIAMOND;
+if (sameWordOk(glyphStr, GLYPH_STRING_OCTAGON))
+    return GLYPH_OCTAGON;
+if (sameWordOk(glyphStr, GLYPH_STRING_STAR))
+    return GLYPH_STAR;
+if (sameWordOk(glyphStr, GLYPH_STRING_PENTAGRAM))
+    return GLYPH_PENTAGRAM;
+
+return GLYPH_CIRCLE;
+}
+
+
 void filterItems(struct track *tg, boolean (*filter)(struct track *tg, void *item),
                 char *filterType)
 /* Filter out items from track->itemList. */
 {
 struct slList *newList = NULL, *oldList = NULL, *el, *next;
 boolean exclude = FALSE;
 boolean color = FALSE;
 enum trackVisibility vis = tvHide;
 
 if (sameWord(filterType, "none"))
     return;
 
 if (sameWord(filterType, "include"))
     exclude = FALSE;
 else if (sameWord(filterType, "exclude"))