GeomConverter/src/org/lcsim/geometry/compact/converter/svg
diff -u -r1.1 -r1.2
--- SvgConverter.java 26 Jan 2011 01:19:39 -0000 1.1
+++ SvgConverter.java 27 Jan 2011 00:52:48 -0000 1.2
@@ -2,13 +2,14 @@
import java.text.DecimalFormat;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.jdom.Element;
import org.jdom.Namespace;
import org.lcsim.detector.IDetectorElement;
+import org.lcsim.detector.ITranslation3D;
import org.lcsim.detector.solids.Tube;
import org.lcsim.geometry.Calorimeter;
import org.lcsim.geometry.Tracker;
@@ -17,7 +18,10 @@
import org.lcsim.geometry.compact.VisAttributes;
import org.lcsim.geometry.subdetector.DiskTracker;
import org.lcsim.geometry.subdetector.MultiLayerTracker;
+import org.lcsim.geometry.subdetector.PolyconeSupport;
import org.lcsim.geometry.subdetector.SiTrackerBarrel;
+import org.lcsim.geometry.subdetector.TubeSegment;
+import org.lcsim.geometry.subdetector.PolyconeSupport.ZPlane;
/**
* Convert from a compact detector description to an SVG view. This will only work correctly for full ILC physics
@@ -27,11 +31,8 @@
*/
// TODO Fix manual scaling where possible (primarily in text and associated lines).
// TODO Add groups for label lines so user units (from Detector) can be used instead of scaling.
-// TODO Adjust label lines so they lie exactly on detector edges.
// TODO For Calorimeters that are large enough (Hcal, Muon) add layering instead of fill.
-// TODO Implement output for trackers.
-// TODO Implement additional types besides tracker/calorimeter (supports, etc.).
-// TODO Vertical bottom label lines?
+// TODO Add lefthand labels of Calorimeter types, Tracker types, and Coil/Solenoid.
class SvgConverter
{
// Namespaces.
@@ -44,9 +45,14 @@
// These margins are in USER not absolute units.
private static final double xmargin = 1500;
private static final double ymargin = 1000;
-
+
+ // FIXME This needs to be determined dynamically from size of
+ // left margin + left labels + detector area.
private static final double viewportX = 1200;
- private static final double viewportY = 1200;
+
+ // FIXME This needs to be determined dynamically from size of
+ // upper margin + detector area + bottom label area.
+ private static final double viewportY = 1000;
// Formatting for length measurements.
private static final DecimalFormat df = new DecimalFormat("#.##");
@@ -54,9 +60,9 @@
// Static access to important grouping elements.
private static Element labelsY;
private static Element labelsX;
- //private static Element labelLinesX;
- //private static Element labelLinesY;
- private static Element subdetGroup;
+ // private static Element labelLinesX;
+ // private static Element labelLinesY;
+ // private static Element subdetGroup;
// If barrel detector is less than this thick,
// its outer radius label will not be printed.
@@ -68,7 +74,7 @@
// Starting offset for text as bottom labels are created.
// This incremented as Z labels are added.
private static double zLabelOffsetY = 25;
-
+
// If a layer is less than this size in LCSim units (mm),
// then it will be drawn as a line rather than a rectangle.
private static double minTrackerLayerThickness = 50;
@@ -105,18 +111,18 @@
// Subdetector group.
Element gs = new Element("g", ns);
- subdetGroup = gs;
+ // subdetGroup = gs;
g.addContent(gs);
gs.setAttribute("transform", "scale(" + scale + ") " + "translate(" + xmargin + ", " + ymargin + ")");
// SVG X axis.
- Element xaxis = line(gs, 0., zy[ 1 ], zy[ 0 ], zy[ 1 ]);
+ Element xaxis = line(gs, 0., zy[1], zy[0], zy[1]);
xaxis.setAttribute("stroke-width", "1");
xaxis.setAttribute("stroke", "black");
xaxis.setAttribute("id", "xaxis");
// SVG Y axis.
- Element yaxis = line(gs, 0., 0., 0., zy[ 1 ]);
+ Element yaxis = line(gs, 0., 0., 0., zy[1]);
yaxis.setAttribute("stroke-width", "1");
yaxis.setAttribute("stroke", "black");
yaxis.setAttribute("id", "yaxis");
@@ -133,10 +139,10 @@
g.addContent(labelsX);
labelsX.setAttribute("font-size", "12");
labelsX.setAttribute("id", "xlabels");
- labelsX.setAttribute("transform", "translate(" + (xmargin * scale) + ", " + ((ymargin + zy[ 1 ]) * scale) + ")");
+ labelsX.setAttribute("transform", "translate(" + (xmargin * scale) + ", " + ((ymargin + zy[1]) * scale) + ")");
// Make ZY view of Detector.
- convertSubdetectors(gs, detector, zy[ 0 ], zy[ 1 ]);
+ convertSubdetectors(gs, detector, zy[0], zy[1]);
// Return the created Element to be written out by Main.
return root;
@@ -145,7 +151,7 @@
private static String convertColor(VisAttributes vis)
{
float[] rgba = vis.getRGBA();
- return "rgb(" + rgba[ 0 ] * 100 + "%, " + rgba[ 1 ] * 100 + "%, " + rgba[ 2 ] * 100 + "%)";
+ return "rgb(" + rgba[0] * 100 + "%, " + rgba[1] * 100 + "%, " + rgba[2] * 100 + "%)";
}
public static class InnerRadiusCompare implements Comparator<Calorimeter>
@@ -169,7 +175,7 @@
}
}
- public static class InnerZCompare implements Comparator<Calorimeter>
+ private static class InnerZCompare implements Comparator<Calorimeter>
{
public int compare(Calorimeter subdet1, Calorimeter subdet2)
{
@@ -190,10 +196,13 @@
}
}
+ // TODO Cleanup this messy method. Make and use one list of Subdetectors rather than several.
private static void convertSubdetectors(Element parent, Detector detector, double maxZ, double maxY)
- {
- // Make a list of Calorimeters.
+ {
List<Subdetector> subdetectors = detector.getSubdetectorList();
+
+ // Look at Calorimeters first.
+ List<Calorimeter> calorimeters = new ArrayList<Calorimeter>();
List<Calorimeter> barrelCalorimeters = new ArrayList<Calorimeter>();
List<Calorimeter> endcapCalorimeters = new ArrayList<Calorimeter>();
for (Subdetector subdet : subdetectors)
@@ -209,70 +218,79 @@
endcapCalorimeters.add((Calorimeter)subdet);
}
}
-
- }
-
- // HACK Manual array copy. Arrays method loses type info.
- Calorimeter[] barrelCalorimeterArray = new Calorimeter[barrelCalorimeters.size()];
- for (int i = 0; i < barrelCalorimeterArray.length; i++ )
- {
- barrelCalorimeterArray[ i ] = barrelCalorimeters.get(i);
- }
-
+ }
+
// Sort Calorimeter barrels on innerR.
- Arrays.sort(barrelCalorimeterArray, new InnerRadiusCompare());
-
- // Sort Calorimeter endcaps on innerZ.
- Calorimeter[] endcapCalorimeterArray = new Calorimeter[endcapCalorimeters.size()];
- for (int i = 0; i < endcapCalorimeterArray.length; i++ )
- {
- endcapCalorimeterArray[ i ] = endcapCalorimeters.get(i);
- }
- Arrays.sort(endcapCalorimeterArray, new InnerZCompare());
+ Collections.sort(barrelCalorimeters, new InnerRadiusCompare());
+
+ // Add sorted barrel calorimeters to list.
+ calorimeters.addAll(barrelCalorimeters);
- // Draw barrel calorimeters from inner radius to outer radius.
- for (Calorimeter subdet : barrelCalorimeterArray)
- {
- SvgConverter.convertSubdetector(parent, (Subdetector)subdet, maxZ, maxY);
- }
+ // Sort Calorimeter endcaps on innerZ.
+ Collections.sort(endcapCalorimeters, new InnerZCompare());
+
+ // Add sorted endcap calorimeters to list.
+ calorimeters.addAll(endcapCalorimeters);
- // Draw endcap calorimeters from inner Z to outer Z.
- for (Calorimeter subdet : endcapCalorimeterArray)
+ // Make the SVG for ordered Calorimeters.
+ for (Calorimeter cal : calorimeters)
{
- SvgConverter.convertSubdetector(parent, (Subdetector)subdet, maxZ, maxY);
+ SvgConverter.convertSubdetector(parent, (Subdetector)cal, maxZ, maxY);
}
- // Now draw the Trackers.
+ // Now draw the Trackers, supports, and dead material.
for (org.lcsim.geometry.Subdetector subdet : detector.getSubdetectors().values())
{
- // TODO Remove if statement here when all types are handled.
- if (subdet instanceof SiTrackerBarrel ||
- subdet instanceof MultiLayerTracker ||
- subdet instanceof DiskTracker)
+ // TODO Remove if statement here when all types are handled above.
+ //if (subdet instanceof SiTrackerBarrel || subdet instanceof MultiLayerTracker
+ // || subdet instanceof DiskTracker || subdet instanceof PolyconeSupport
+ // || subdet instanceof TubeSegment)
+ if (!(subdet instanceof Calorimeter))
{
SvgConverter.convertSubdetector(parent, (Subdetector)subdet, maxZ, maxY);
}
}
-
- // process Trackers and others here
+ }
+
+ /**
+ * Order a List of ZPlanes by Z value.
+ *
+ * @author jeremym
+ *
+ */
+ private static class ZPlaneCompare implements Comparator<ZPlane>
+ {
+ public int compare(ZPlane zp1, ZPlane zp2)
+ {
+ double z1 = zp1.getZ();
+ double z2 = zp2.getZ();
+ if (z1 > z2)
+ return 1;
+ else if (z1 < z2)
+ return -1;
+ else
+ return 0;
+ }
}
private static void convertSubdetector(Element parent, Subdetector subdet, double maxZ, double maxY)
{
+ // Debug print.
System.out.println(">> " + subdet.getName());
// visualization
VisAttributes vis = subdet.getVisAttributes();
-
- // If not visible then immediately return.
+
+ // If not visible then immediately return.
if (!vis.getVisible())
{
- System.out.println("not visible");
+ System.out.println(" *not visible* ... skipping");
return;
}
-
+
+ // Convert color parameters to SVG format.
String color = convertColor(vis);
- float alpha = vis.getRGBA()[ 3 ];
+ float alpha = vis.getRGBA()[3];
// Make a group for this Subdetector.
Element g = new Element("g", ns);
@@ -280,7 +298,7 @@
parent.addContent(g);
// Set Subdetector's line and fill colors.
- g.setAttribute("stroke", color);
+ g.setAttribute("stroke", color);
g.setAttribute("fill", color);
g.setAttribute("stroke-width", "3"); // Default stroke-width.
g.setAttribute("opacity", Float.toString(alpha));
@@ -290,12 +308,9 @@
if (subdet instanceof Calorimeter)
{
- // Use a black outline.
- //g.setAttribute("stroke", "black");
-
- // Turn off rectangle outline.
+ // Turn off shape outline.
g.setAttribute("stroke-width", "0");
-
+
// Get Calorimeter generic parameters.
Calorimeter cal = (Calorimeter)subdet;
double innerR = cal.getInnerRadius();
@@ -313,7 +328,8 @@
rect(g, 0., maxY - outerR, outerR - innerR, halfZ);
// Line at inner radius of barrel calorimeter.
- Element lineInner = line(labelGroup, 75., ((maxY - innerR) * scale), xmargin * scale, (maxY - innerR) * scale);
+ Element lineInner =
+ line(labelGroup, 75., ((maxY - innerR) * scale), xmargin * scale, (maxY - innerR) * scale);
lineInner.setAttribute("stroke-dasharray", "6,3");
lineInner.setAttribute("stroke", "gray");
lineInner.setAttribute("stroke-width", "1");
@@ -325,7 +341,8 @@
if (outerR - innerR > yLabelTolerance)
{
// Line at outer radius of barrel calorimeter.
- Element lineOuter = line(labelGroup, 75., ((maxY - outerR) * scale), xmargin * scale, (maxY - outerR) * scale);
+ Element lineOuter =
+ line(labelGroup, 75., ((maxY - outerR) * scale), xmargin * scale, (maxY - outerR) * scale);
lineOuter.setAttribute("stroke-dasharray", "6,3");
lineOuter.setAttribute("stroke", "gray");
lineOuter.setAttribute("stroke-width", "1");
@@ -383,7 +400,7 @@
{
// Draw barrel trackers.
if (subdet instanceof SiTrackerBarrel || subdet instanceof MultiLayerTracker)
- {
+ {
// Loop over SiTrackerBarrel layers.
IDetectorElement de = subdet.getDetectorElement();
for (IDetectorElement layer : de.getChildren())
@@ -394,7 +411,7 @@
double r = tube.getInnerRadius() + thickness / 2;
double halfZ = tube.getZHalfLength();
double outerR = tube.getOuterRadius();
- double innerR = tube.getInnerRadius();
+ double innerR = tube.getInnerRadius();
// Draw a line for this tracker layer.
if (thickness < minTrackerLayerThickness)
@@ -407,26 +424,26 @@
{
// Use a black outline to separate nearby layers.
g.setAttribute("stroke", "black");
-
+
// Make a rectangle for the layer.
rect(g, 0., maxY - outerR, outerR - innerR, halfZ);
}
}
}
// TODO Replace compact based code with IDetectorElement, but DiskTracker
- // layers need their own DetectorElements.
+ // layers need their own DetectorElements.
else if (subdet instanceof DiskTracker)
- {
+ {
DiskTracker diskTracker = (DiskTracker)subdet;
int nlayers = diskTracker.getInnerR().length;
- for (int i=0, n=nlayers; i<n; i++)
+ for (int i = 0, n = nlayers; i < n; i++ )
{
double innerR = diskTracker.getInnerR()[i];
double outerR = diskTracker.getOuterR()[i];
double z = diskTracker.getThickness()[i];
double innerZ = diskTracker.getInnerZ()[i];
- double midZ = innerZ + z / 2;
-
+ double midZ = innerZ + z / 2;
+
// Draw a line for this tracker layer.
if (z < minTrackerLayerThickness)
{
@@ -438,18 +455,111 @@
{
// Use a black outline to separate nearby layers.
g.setAttribute("stroke", "black");
-
+
// Make a rectangle for the layer.
rect(g, innerZ, maxY - outerR, outerR - innerR, z);
- }
+ }
+ }
+ }
+ }
+ else if (subdet instanceof PolyconeSupport)
+ {
+ PolyconeSupport support = (PolyconeSupport)subdet;
+
+ // Sort zplanes by z, from negative to positive.
+ List<ZPlane> zplanes = new ArrayList<ZPlane>(support.getZPlanes());
+ Collections.sort(zplanes, new ZPlaneCompare());
+
+ // Make a list of usable ZPlanes.
+ List<ZPlane> zplanesUse = new ArrayList<ZPlane>();
+ for (int i = 0, n = zplanes.size(); i < n; i++ )
+ {
+ ZPlane zplane = zplanes.get(i);
+
+ // Only use ZPlanes in positive Z.
+ if (zplane.getZ() > 0)
+ {
+ // Make a modified ZPlane for components that cross the xaxis.
+ if (i > 0 && zplanesUse.size() == 0)
+ {
+ ZPlane lastNegZPlane = zplanes.get(i - 1);
+
+ // Check if last point had same radius values, so that
+ // drawing across the X axis can be done simply by starting
+ // from x==0.
+ if (lastNegZPlane.getRMin() == zplane.getRMin() && lastNegZPlane.getRMax() == zplane.getRMax())
+ {
+ ZPlane borderZPlane = new ZPlane(lastNegZPlane.getRMin(), lastNegZPlane.getRMax(), 0);
+ zplanesUse.add(borderZPlane);
+ }
+ // FIXME Handle ZPlanes crossing xaxis that have different radii from the next ZPlane.
+ }
+ // Add positive ZPlane to usable list.
+ else
+ {
+ zplanesUse.add(zplane);
+ }
+ }
+ }
+
+ if (zplanesUse.size() > 0)
+ {
+ StringBuffer buff = new StringBuffer();
+
+ // Start by making outer radii points in positive X direction.
+ for (ZPlane zplane : zplanesUse)
+ {
+ double outerR = zplane.getRMax();
+ double z = zplane.getZ();
+ buff.append(z + "," + (maxY - outerR) + " ");
+ }
+
+ // Make a reverse list.
+ List<ZPlane> reverseZPlanes = new ArrayList<ZPlane>(zplanesUse);
+ Collections.reverse(reverseZPlanes);
+
+ // Now make inner radius points going in the negative X direction.
+ for (ZPlane zplane : reverseZPlanes)
+ {
+ double innerR = zplane.getRMin();
+ double z = zplane.getZ();
+ buff.append(z + "," + (maxY - innerR) + " ");
}
+ String points = buff.toString();
+ points.trim();
+
+ // System.out.println(points);
+
+ // Make the polygon using the list of points.
+ Element polygon = new Element("polygon", ns);
+ polygon.setAttribute("points", points);
+ g.addContent(polygon);
}
}
- // TODO Handle additional types here...
- // PolyconeSupport
- // SiTrackerEndcap
- // SiTrackerEndcap2 (difficult...no mother vols for layers)
- // TubeSegment
+ else if (subdet instanceof TubeSegment)
+ {
+ TubeSegment tube = (TubeSegment)subdet;
+
+ if (tube.getTransform().getTranslation().z() > 0)
+ {
+ double innerR = tube.getInnerRadius();
+ double outerR = tube.getOuterRadius();
+ double halfZ = tube.getZHalfLength();
+ double zmin = tube.getTransform().getTranslation().z() - halfZ;
+
+ // Only draw components with a positive z position.
+ if (zmin > 0)
+ {
+ rect(g, zmin, maxY - outerR, outerR - innerR, halfZ);
+ }
+
+ // FIXME: Rotation is ignored.
+ // FIXME: TubeSegments going across Y axis are ignored.
+ }
+ }
+ // TODO Handle these additional types...
+ // SiTrackerEndcap
+ // SiTrackerEndcap2
}
private static Element line(Element parent, double x1, double y1, double x2, double y2)
@@ -512,16 +622,14 @@
throw new RuntimeException("Could not find ZY extent of this Detector!");
}
- zy[ 0 ] = z;
- zy[ 1 ] = y;
+ zy[0] = z;
+ zy[1] = y;
return zy;
}
-
+
/*
- interface SubdetectorConverter
- {
- void convert(Element parent, Detector detector, double maxZ, double maxY);
- }
- */
+ * This would be nice. interface SubdetectorConverter { void convert(Element parent, Detector detector, double maxZ,
+ * double maxY); }
+ */
}
\ No newline at end of file