Print

Print


Commit in GeomConverter on MAIN
src/org/lcsim/geometry/compact/converter/svg/Main.java+89added 1.1
                                            /SvgConverter.java+527added 1.1
resources/META-INF/services/org.lcsim.geometry.compact.converter.Converter+2-11.5 -> 1.6
+618-1
2 added + 1 modified, total 3 files
check in new SVG converter code; work in progress

GeomConverter/src/org/lcsim/geometry/compact/converter/svg
Main.java added at 1.1
diff -N Main.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Main.java	26 Jan 2011 01:19:39 -0000	1.1
@@ -0,0 +1,89 @@
+/**
+ * @author Jeremy McCormick <[log in to unmask]>
+ * @version $Id: Main.java,v 1.1 2011/01/26 01:19:39 jeremy Exp $
+ */
+package org.lcsim.geometry.compact.converter.svg;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.swing.filechooser.FileFilter;
+
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
+import org.lcsim.geometry.Detector;
+import org.lcsim.geometry.GeometryReader;
+import org.lcsim.geometry.compact.converter.Converter;
+
+public class Main implements Converter
+{
+    /**
+     * @param args the command line arguments
+     */
+    public static void main( String[] args ) throws Exception
+    {
+        if ( args.length < 1 || args.length > 2 )
+            usage();
+        InputStream in = new BufferedInputStream( new FileInputStream( args[ 0 ] ) );
+        OutputStream out = args.length == 1 ? System.out : new BufferedOutputStream( new FileOutputStream( args[ 1 ] ) );
+        new Main().convert( args[ 0 ], in, out );
+    }
+
+    public Main()
+    {}
+
+    public void convert( String inputFileName, InputStream in, OutputStream out ) throws Exception
+    {
+        // Read in detector.
+        GeometryReader reader = new GeometryReader();
+        Detector detector = reader.read( in );
+
+        // Create the HTML.
+        Element root = SvgConverter.convert( detector );
+
+        // Create the document.
+        Document doc = new Document();
+        doc.setRootElement( root );                
+
+        // Write out the document.
+        XMLOutputter outputter = new XMLOutputter();
+        outputter.setFormat( Format.getPrettyFormat() );
+        outputter.output( doc, out );
+        out.close();
+    }
+
+    private static void usage()
+    {
+        System.out.println( "java " + Main.class.getName() + " <compact> [<svg>]" );
+        System.exit( 0 );
+    }
+
+    public FileFilter getFileFilter()
+    {
+        return new HepRepFileFilter();
+    }
+
+    public String getOutputFormat()
+    {
+        return "svg";
+    }
+
+    private static class HepRepFileFilter extends FileFilter
+    {
+        public boolean accept( java.io.File file )
+        {
+            return file.isDirectory() || file.getName().endsWith( ".svg" );
+        }
+
+        public String getDescription()
+        {
+            return "HTML file (*.svg)";
+        }
+    }
+}
\ No newline at end of file

GeomConverter/src/org/lcsim/geometry/compact/converter/svg
SvgConverter.java added at 1.1
diff -N SvgConverter.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ SvgConverter.java	26 Jan 2011 01:19:39 -0000	1.1
@@ -0,0 +1,527 @@
+package org.lcsim.geometry.compact.converter.svg;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+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.solids.Tube;
+import org.lcsim.geometry.Calorimeter;
+import org.lcsim.geometry.Tracker;
+import org.lcsim.geometry.compact.Detector;
+import org.lcsim.geometry.compact.Subdetector;
+import org.lcsim.geometry.compact.VisAttributes;
+import org.lcsim.geometry.subdetector.DiskTracker;
+import org.lcsim.geometry.subdetector.MultiLayerTracker;
+import org.lcsim.geometry.subdetector.SiTrackerBarrel;
+
+/**
+ * Convert from a compact detector description to an SVG view. This will only work correctly for full ILC physics
+ * detectors.
+ * 
+ * @author jeremym
+ */
+// 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?
+class SvgConverter
+{
+    // Namespaces.
+    private static final Namespace ns = Namespace.getNamespace("http://www.w3.org/2000/svg");
+    private static final Namespace xlink = Namespace.getNamespace("xlink", "http://www.w3.org/1999/xlink");
+
+    // Scaling of Subdetector drawing from LCSim natural units (mm).
+    private static final double scale = 0.1;
+
+    // These margins are in USER not absolute units.
+    private static final double xmargin = 1500;
+    private static final double ymargin = 1000;
+    
+    private static final double viewportX = 1200;
+    private static final double viewportY = 1200;
+
+    // Formatting for length measurements.
+    private static final DecimalFormat df = new DecimalFormat("#.##");
+
+    // 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;
+
+    // If barrel detector is less than this thick,
+    // its outer radius label will not be printed.
+    private static double yLabelTolerance = 200.;
+
+    // Label text margin from leftmost edge.
+    private static double yLabelMarginX = 10;
+
+    // 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;
+
+    public static Element convert(Detector d)
+    {
+        // Convert to specific subclass for more method access.
+        Detector detector = (org.lcsim.geometry.compact.Detector)d;
+
+        // Compute max ZY measurements of detector.
+        double[] zy = findMaxZY(detector);
+        // System.out.println("max xy = " + zy[0] + ", " + zy[1]);
+
+        // SVG root element.
+        Element root = new Element("svg");
+        root.setNamespace(ns);
+        root.addNamespaceDeclaration(xlink);
+        root.setAttribute("version", "1.2");
+
+        // Set viewport window.
+        root.setAttribute("width", viewportX + "px");
+        root.setAttribute("height", viewportY + "px");
+
+        // All elements go into this group.
+        Element g = new Element("g", ns);
+        root.addContent(g);
+        // g.setAttribute("transform",
+        // "scale(" + scale + ") " + "translate(" + xmargin + ", " + 0 + ")");
+
+        // Header with name.
+        Element header = text(g, detector.getName(), viewportX / 2, 50);
+        header.setAttribute("font-family", "Arial");
+        header.setAttribute("font-size", "32");
+
+        // Subdetector group.
+        Element gs = new Element("g", ns);
+        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 ]);
+        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 ]);
+        yaxis.setAttribute("stroke-width", "1");
+        yaxis.setAttribute("stroke", "black");
+        yaxis.setAttribute("id", "yaxis");
+
+        // SVG Y labels group.
+        labelsY = new Element("g", ns);
+        g.addContent(labelsY);
+        labelsY.setAttribute("font-size", "12");
+        labelsY.setAttribute("id", "ylabels");
+        labelsY.setAttribute("transform", "translate(" + 0 + ", " + ymargin * scale + ")");
+
+        // SVG X labels group.
+        labelsX = new Element("g", ns);
+        g.addContent(labelsX);
+        labelsX.setAttribute("font-size", "12");
+        labelsX.setAttribute("id", "xlabels");
+        labelsX.setAttribute("transform", "translate(" + (xmargin * scale) + ", " + ((ymargin + zy[ 1 ]) * scale) + ")");
+
+        // Make ZY view of Detector.
+        convertSubdetectors(gs, detector, zy[ 0 ], zy[ 1 ]);
+
+        // Return the created Element to be written out by Main.
+        return root;
+    }
+
+    private static String convertColor(VisAttributes vis)
+    {
+        float[] rgba = vis.getRGBA();
+        return "rgb(" + rgba[ 0 ] * 100 + "%, " + rgba[ 1 ] * 100 + "%, " + rgba[ 2 ] * 100 + "%)";
+    }
+
+    public static class InnerRadiusCompare implements Comparator<Calorimeter>
+    {
+        public int compare(Calorimeter subdet1, Calorimeter subdet2)
+        {
+            double ir1 = subdet1.getInnerRadius();
+            double ir2 = subdet2.getInnerRadius();
+            if (ir1 > ir2)
+            {
+                return 1;
+            }
+            else if (ir1 < ir2)
+            {
+                return -1;
+            }
+            else
+            {
+                return 0;
+            }
+        }
+    }
+
+    public static class InnerZCompare implements Comparator<Calorimeter>    
+    {
+        public int compare(Calorimeter subdet1, Calorimeter subdet2)
+        {
+            double ir1 = subdet1.getInnerZ();
+            double ir2 = subdet2.getInnerZ();
+            if (ir1 > ir2)
+            {
+                return 1;
+            }
+            else if (ir1 < ir2)
+            {
+                return -1;
+            }
+            else
+            {
+                return 0;
+            }
+        }
+    }
+
+    private static void convertSubdetectors(Element parent, Detector detector, double maxZ, double maxY)
+    {
+        // Make a list of Calorimeters.
+        List<Subdetector> subdetectors = detector.getSubdetectorList();
+        List<Calorimeter> barrelCalorimeters = new ArrayList<Calorimeter>();
+        List<Calorimeter> endcapCalorimeters = new ArrayList<Calorimeter>();
+        for (Subdetector subdet : subdetectors)
+        {
+            if (subdet instanceof Calorimeter)
+            {
+                if (subdet.isBarrel())
+                {
+                    barrelCalorimeters.add((Calorimeter)subdet);
+                }
+                else if (subdet.isEndcap())
+                {
+                    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());     
+
+        // Draw barrel calorimeters from inner radius to outer radius.        
+        for (Calorimeter subdet : barrelCalorimeterArray)
+        {
+            SvgConverter.convertSubdetector(parent, (Subdetector)subdet, maxZ, maxY);
+        }
+
+        // Draw endcap calorimeters from inner Z to outer Z.
+        for (Calorimeter subdet : endcapCalorimeterArray)
+        {
+            SvgConverter.convertSubdetector(parent, (Subdetector)subdet, maxZ, maxY);
+        }
+        
+        // Now draw the Trackers.
+        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)
+            {
+                SvgConverter.convertSubdetector(parent, (Subdetector)subdet, maxZ, maxY);
+            }
+        }
+        
+        // process Trackers and others here
+    }
+
+    private static void convertSubdetector(Element parent, Subdetector subdet, double maxZ, double maxY)
+    {
+        System.out.println(">> " + subdet.getName());
+
+        // visualization
+        VisAttributes vis = subdet.getVisAttributes();
+        
+        // If not visible then immediately return. 
+        if (!vis.getVisible())
+        {
+            System.out.println("not visible");
+            return;
+        }
+        
+        String color = convertColor(vis);
+        float alpha = vis.getRGBA()[ 3 ];
+
+        // Make a group for this Subdetector.
+        Element g = new Element("g", ns);
+        g.setAttribute("id", subdet.getName());
+        parent.addContent(g);
+
+        // Set Subdetector's line and fill colors.
+        g.setAttribute("stroke", color);        
+        g.setAttribute("fill", color);
+        g.setAttribute("stroke-width", "3"); // Default stroke-width.
+        g.setAttribute("opacity", Float.toString(alpha));
+
+        // Margin group.
+        // g.setAttribute("transform", "translate(" + margin + ", " + margin + ")");
+
+        if (subdet instanceof Calorimeter)
+        {
+            // Use a black outline.
+            //g.setAttribute("stroke", "black"); 
+            
+            // Turn off rectangle outline.
+            g.setAttribute("stroke-width", "0");
+            
+            // Get Calorimeter generic parameters.
+            Calorimeter cal = (Calorimeter)subdet;
+            double innerR = cal.getInnerRadius();
+            double outerR = cal.getOuterRadius();
+            double halfZ = cal.getZLength() / 2;
+            double zlength = cal.getZLength();
+
+            // The labels group is accessed statically to avoid having to pass it down.
+            Element labelGroup = labelsY;
+
+            // Draw barrel calorimeters.
+            if (subdet.isBarrel())
+            {
+                // Make a rectangular outline of calorimeter barrel.
+                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);
+                lineInner.setAttribute("stroke-dasharray", "6,3");
+                lineInner.setAttribute("stroke", "gray");
+                lineInner.setAttribute("stroke-width", "1");
+
+                // Label inner radius measurement.
+                text(labelGroup, df.format(innerR), yLabelMarginX, ((maxY - innerR) * scale) + 5);
+
+                // Outer R is only labeled if there is enough space for it.
+                if (outerR - innerR > yLabelTolerance)
+                {
+                    // Line at outer radius of barrel calorimeter.
+                    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");
+
+                    // Label outer radius measurement.
+                    text(labelGroup, df.format(outerR), yLabelMarginX, ((maxY - outerR) * scale) + 5);
+                }
+
+                // Do bottom labels now. Switch variable reference.
+                labelGroup = labelsX;
+
+                // Dashed lines at bottom along z direction indicating barrel z measurements.
+                Element lineBottom = line(labelGroup, 0, zLabelOffsetY, halfZ * scale, zLabelOffsetY);
+                lineBottom.setAttribute("stroke-dasharray", "6,3");
+                lineBottom.setAttribute("stroke", "gray");
+                lineBottom.setAttribute("stroke-width", "1");
+
+                // Label measurement for barrel z.
+                text(labelGroup, df.format(halfZ), ((halfZ * scale) / 2), zLabelOffsetY - 5);
+
+                // Increment for next label.
+                zLabelOffsetY += 25;
+            }
+            // Draw endcap calorimeters that are reflected.
+            else if (subdet.isEndcap() && subdet.getReflect())
+            {
+                // Get geometry parameters.
+                double innerZ = cal.getInnerZ();
+                double outerZ = cal.getOuterZ();
+
+                // Make a rectangle for the endpca.
+                rect(g, innerZ, maxY - outerR, outerR - innerR, halfZ * 2);
+
+                double thickness = outerZ - innerZ;
+
+                // Setup reference to make a label and line on the xaxis.
+                labelGroup = labelsX;
+
+                // Line indicating endcap extent in X (or Z in LCSim coordinates).
+                Element lineEndcap = line(labelGroup, innerZ * scale, zLabelOffsetY, outerZ * scale, zLabelOffsetY);
+                lineEndcap.setAttribute("stroke-dasharray", "6,3");
+                lineEndcap.setAttribute("stroke", "gray");
+                lineEndcap.setAttribute("stroke-width", "1");
+
+                // Label measurement of endcap z length.
+                // FIXME Manual 10 pix adjustment.
+                text(labelGroup, df.format(zlength), ((innerZ + thickness / 2) * scale) - 10, zLabelOffsetY - 5);
+
+                // Increment labeling offset.
+                zLabelOffsetY += 25;
+            }
+        }
+        // Draw trackers.
+        else if (subdet instanceof Tracker)
+        {
+            // Draw barrel trackers.
+            if (subdet instanceof SiTrackerBarrel || subdet instanceof MultiLayerTracker)
+            {                
+                // Loop over SiTrackerBarrel layers.
+                IDetectorElement de = subdet.getDetectorElement();
+                for (IDetectorElement layer : de.getChildren())
+                {
+                    // Get parameters from layer's tube shape.
+                    Tube tube = (Tube)layer.getGeometry().getLogicalVolume().getSolid();
+                    double thickness = tube.getOuterRadius() - tube.getInnerRadius();
+                    double r = tube.getInnerRadius() + thickness / 2;
+                    double halfZ = tube.getZHalfLength();
+                    double outerR = tube.getOuterRadius();
+                    double innerR = tube.getInnerRadius();                    
+
+                    // Draw a line for this tracker layer.
+                    if (thickness < minTrackerLayerThickness)
+                    {
+                        // Draw a line with Subdetector's color.
+                        line(g, 0, maxY - r, halfZ, maxY - r);
+                    }
+                    // Draw a layer rectangle if component is thick enough.
+                    else
+                    {
+                        // 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.
+            else if (subdet instanceof DiskTracker)
+            {                                
+                DiskTracker diskTracker = (DiskTracker)subdet;
+                int nlayers = diskTracker.getInnerR().length;
+                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; 
+                                        
+                    // Draw a line for this tracker layer.
+                    if (z < minTrackerLayerThickness)
+                    {
+                        // Draw a line with Subdetector's color.
+                        line(g, midZ, maxY - outerR, midZ, maxY - innerR);
+                    }
+                    // Draw a layer rectangle if component is thick enough.
+                    else
+                    {
+                        // 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);
+                    }    
+                }
+            }
+        }
+        // TODO Handle additional types here...       
+        // PolyconeSupport
+        // SiTrackerEndcap 
+        // SiTrackerEndcap2 (difficult...no mother vols for layers)
+        // TubeSegment
+    }
+
+    private static Element line(Element parent, double x1, double y1, double x2, double y2)
+    {
+        Element line = new Element("line", ns);
+        parent.addContent(line);
+        line.setAttribute("x1", df.format(x1));
+        line.setAttribute("y1", df.format(y1));
+        line.setAttribute("x2", df.format(x2));
+        line.setAttribute("y2", df.format(y2));
+        return line;
+    }
+
+    private static Element rect(Element parent, double x, double y, double height, double width)
+    {
+        Element rect = new Element("rect", ns);
+        parent.addContent(rect);
+        rect.setAttribute("x", df.format(x));
+        rect.setAttribute("y", df.format(y));
+        rect.setAttribute("height", df.format(height));
+        rect.setAttribute("width", df.format(width));
+        return rect;
+    }
+
+    private static Element text(Element parent, String text, double x, double y)
+    {
+        Element t = new Element("text", ns);
+        parent.addContent(t);
+        t.setText(text);
+        t.setAttribute("x", df.format(x));
+        t.setAttribute("y", df.format(y));
+        return t;
+    }
+
+    private static double[] findMaxZY(Detector detector)
+    {
+        double[] zy = new double[2];
+        double z = 0;
+        double y = 0;
+
+        // Assume calorimeter with largest extent defines max ZX.
+        for (Subdetector subdet : detector.getSubdetectors().values())
+        {
+            if (subdet instanceof Calorimeter)
+            {
+                Calorimeter cal = (Calorimeter)subdet;
+                if (cal.getOuterRadius() > y)
+                {
+                    y = cal.getOuterRadius();
+                }
+                if (cal.getOuterZ() > z)
+                {
+                    z = cal.getOuterZ();
+                }
+            }
+        }
+
+        if (z == 0 || y == 0)
+        {
+            throw new RuntimeException("Could not find ZY extent of this Detector!");
+        }
+
+        zy[ 0 ] = z;
+        zy[ 1 ] = y;
+
+        return zy;
+    }
+    
+    /*
+    interface SubdetectorConverter
+    {
+       void convert(Element parent, Detector detector, double maxZ, double maxY);
+    }
+    */
+}
\ No newline at end of file

GeomConverter/resources/META-INF/services
org.lcsim.geometry.compact.converter.Converter 1.5 -> 1.6
diff -u -r1.5 -r1.6
--- org.lcsim.geometry.compact.converter.Converter	4 Jan 2011 21:59:12 -0000	1.5
+++ org.lcsim.geometry.compact.converter.Converter	26 Jan 2011 01:19:39 -0000	1.6
@@ -2,4 +2,5 @@
 org.lcsim.geometry.compact.converter.lcdd.Main
 org.lcsim.geometry.compact.converter.GODL.Main
 org.lcsim.geometry.compact.converter.pandora.Main
-org.lcsim.geometry.compact.converter.html.Main
\ No newline at end of file
+org.lcsim.geometry.compact.converter.html.Main
+org.lcsim.geometry.compact.converter.svg.Main
\ No newline at end of file
CVSspam 0.2.8