1 package org.lcsim.geometry.compact.converter.svg;
2
3 import java.text.DecimalFormat;
4 import java.util.ArrayList;
5 import java.util.Collections;
6 import java.util.Comparator;
7 import java.util.List;
8
9 import org.jdom.Element;
10 import org.jdom.Namespace;
11 import org.lcsim.detector.IDetectorElement;
12 import org.lcsim.detector.solids.Tube;
13 import org.lcsim.geometry.Calorimeter;
14 import org.lcsim.geometry.Calorimeter.CalorimeterType;
15 import org.lcsim.geometry.Tracker;
16 import org.lcsim.geometry.compact.Detector;
17 import org.lcsim.geometry.compact.Subdetector;
18 import org.lcsim.geometry.compact.VisAttributes;
19 import org.lcsim.geometry.subdetector.DiskTracker;
20 import org.lcsim.geometry.subdetector.MultiLayerTracker;
21 import org.lcsim.geometry.subdetector.PolyconeSupport;
22 import org.lcsim.geometry.subdetector.SiTrackerBarrel;
23 import org.lcsim.geometry.subdetector.TubeSegment;
24 import org.lcsim.geometry.subdetector.PolyconeSupport.ZPlane;
25
26
27
28
29
30
31
32
33
34
35
36 class SvgConverter
37 {
38
39 private static final Namespace ns = Namespace.getNamespace("http://www.w3.org/2000/svg");
40 private static final Namespace xlink = Namespace.getNamespace("xlink", "http://www.w3.org/1999/xlink");
41
42
43 private static final double scale = 0.1;
44
45
46
47 private static final double xmargin = 1500;
48 private static final double ymargin = 1000;
49
50
51
52 private static final double viewportX = 1200;
53
54
55
56 private static final double viewportY = 1000;
57
58
59 private static final DecimalFormat df = new DecimalFormat("#.##");
60
61
62 private static Element labelsY;
63 private static Element labelsX;
64
65
66
67
68
69
70 private static double yLabelTolerance = 200.;
71
72
73 private static double yLabelMarginX = 10;
74
75
76
77 private static double zLabelOffsetY = 25;
78
79
80
81 private static double minTrackerLayerThickness = 50;
82
83 public static Element convert(Detector d)
84 {
85
86 Detector detector = (org.lcsim.geometry.compact.Detector)d;
87
88
89 double[] zy = findMaxZY(detector);
90
91
92
93 Element root = new Element("svg");
94 root.setNamespace(ns);
95 root.addNamespaceDeclaration(xlink);
96 root.setAttribute("version", "1.2");
97
98
99 root.setAttribute("width", viewportX + "px");
100 root.setAttribute("height", viewportY + "px");
101
102
103 Element g = new Element("g", ns);
104 root.addContent(g);
105
106
107 Element header = text(g, detector.getName(), viewportX / 2, 50);
108 header.setAttribute("font-family", "Arial");
109 header.setAttribute("font-size", "32");
110
111
112 Element gs = new Element("g", ns);
113 g.addContent(gs);
114 gs.setAttribute("transform", "scale(" + scale + ") " + "translate(" + xmargin + ", " + ymargin + ")");
115
116
117 Element xaxis = line(gs, 0., zy[1], zy[0], zy[1]);
118 xaxis.setAttribute("stroke-width", "1");
119 xaxis.setAttribute("stroke", "black");
120 xaxis.setAttribute("id", "xaxis");
121
122
123 Element yaxis = line(gs, 0., 0., 0., zy[1]);
124 yaxis.setAttribute("stroke-width", "1");
125 yaxis.setAttribute("stroke", "black");
126 yaxis.setAttribute("id", "yaxis");
127
128
129 labelsY = new Element("g", ns);
130 g.addContent(labelsY);
131 labelsY.setAttribute("font-size", "12");
132 labelsY.setAttribute("id", "ylabels");
133 labelsY.setAttribute("transform", "translate(" + 0 + ", " + ymargin * scale + ")");
134
135
136 labelsX = new Element("g", ns);
137 g.addContent(labelsX);
138 labelsX.setAttribute("font-size", "12");
139 labelsX.setAttribute("id", "xlabels");
140 labelsX.setAttribute("transform", "translate(" + (xmargin * scale) + ", " + ((ymargin + zy[1]) * scale) + ")");
141
142
143 convertSubdetectors(gs, detector, zy[0], zy[1]);
144
145
146 return root;
147 }
148
149 private static String convertColor(VisAttributes vis)
150 {
151 float[] rgba = vis.getRGBA();
152 return "rgb(" + rgba[0] * 100 + "%, " + rgba[1] * 100 + "%, " + rgba[2] * 100 + "%)";
153 }
154
155 private static class InnerRadiusCompare implements Comparator<Calorimeter>
156 {
157 public int compare(Calorimeter subdet1, Calorimeter subdet2)
158 {
159 double ir1 = subdet1.getInnerRadius();
160 double ir2 = subdet2.getInnerRadius();
161 if (ir1 > ir2)
162 {
163 return 1;
164 }
165 else if (ir1 < ir2)
166 {
167 return -1;
168 }
169 else
170 {
171 return 0;
172 }
173 }
174 }
175
176 private static class InnerZCompare implements Comparator<Calorimeter>
177 {
178 public int compare(Calorimeter subdet1, Calorimeter subdet2)
179 {
180 double ir1 = subdet1.getInnerZ();
181 double ir2 = subdet2.getInnerZ();
182 if (ir1 > ir2)
183 {
184 return 1;
185 }
186 else if (ir1 < ir2)
187 {
188 return -1;
189 }
190 else
191 {
192 return 0;
193 }
194 }
195 }
196
197 private static void convertSubdetectors(Element parent, Detector detector, double maxZ, double maxY)
198 {
199 List<Subdetector> subdetectors = detector.getSubdetectorList();
200
201
202 List<Calorimeter> calorimeters = new ArrayList<Calorimeter>();
203 List<Calorimeter> barrelCalorimeters = new ArrayList<Calorimeter>();
204 List<Calorimeter> endcapCalorimeters = new ArrayList<Calorimeter>();
205 for (Subdetector subdet : subdetectors)
206 {
207 if (subdet instanceof Calorimeter)
208 {
209 if (subdet.isBarrel())
210 {
211 barrelCalorimeters.add((Calorimeter)subdet);
212 }
213 else if (subdet.isEndcap())
214 {
215 endcapCalorimeters.add((Calorimeter)subdet);
216 }
217 }
218 }
219
220
221 Collections.sort(barrelCalorimeters, new InnerRadiusCompare());
222
223
224 calorimeters.addAll(barrelCalorimeters);
225
226
227 Collections.sort(endcapCalorimeters, new InnerZCompare());
228
229
230 calorimeters.addAll(endcapCalorimeters);
231
232
233 for (Calorimeter cal : calorimeters)
234 {
235 SvgConverter.convertSubdetector(parent, (Subdetector)cal, maxZ, maxY);
236 }
237
238
239 for (org.lcsim.geometry.Subdetector subdet : detector.getSubdetectors().values())
240 {
241 if (!(subdet instanceof Calorimeter))
242 {
243 SvgConverter.convertSubdetector(parent, (Subdetector)subdet, maxZ, maxY);
244 }
245 }
246 }
247
248
249
250
251
252
253
254 private static class ZPlaneCompare implements Comparator<ZPlane>
255 {
256 public int compare(ZPlane zp1, ZPlane zp2)
257 {
258 double z1 = zp1.getZ();
259 double z2 = zp2.getZ();
260 if (z1 > z2)
261 return 1;
262 else if (z1 < z2)
263 return -1;
264 else
265 return 0;
266 }
267 }
268
269 private static void convertSubdetector(Element parent, Subdetector subdet, double maxZ, double maxY)
270 {
271
272 System.out.println(">> " + subdet.getName());
273
274
275 VisAttributes vis = subdet.getVisAttributes();
276
277
278 if (!vis.getVisible())
279 {
280 System.out.println(" *not visible* ... skipping");
281 return;
282 }
283
284
285 String color = convertColor(vis);
286 float alpha = vis.getRGBA()[3];
287
288
289 Element g = new Element("g", ns);
290 g.setAttribute("id", subdet.getName());
291 parent.addContent(g);
292
293
294 g.setAttribute("stroke", color);
295 g.setAttribute("fill", color);
296 g.setAttribute("stroke-width", "3");
297 g.setAttribute("opacity", Float.toString(alpha));
298
299
300 if (subdet instanceof Calorimeter)
301 {
302
303 g.setAttribute("stroke-width", "0");
304
305
306 Calorimeter cal = (Calorimeter)subdet;
307 double innerR = cal.getInnerRadius();
308 double outerR = cal.getOuterRadius();
309 double halfZ = cal.getZLength() / 2;
310 double zlength = cal.getZLength();
311
312
313 Element labelGroup = labelsY;
314
315
316 if (subdet.isBarrel())
317 {
318
319 rect(g, 0., maxY - outerR, outerR - innerR, halfZ);
320
321
322 Element lineInner =
323 line(labelGroup, 75., ((maxY - innerR) * scale), xmargin * scale, (maxY - innerR) * scale);
324 lineInner.setAttribute("stroke-dasharray", "6,3");
325 lineInner.setAttribute("stroke", "gray");
326 lineInner.setAttribute("stroke-width", "1");
327
328
329 text(labelGroup, df.format(innerR), yLabelMarginX, ((maxY - innerR) * scale) + 5);
330
331
332 if (outerR - innerR > yLabelTolerance)
333 {
334
335 Element lineOuter =
336 line(labelGroup, 75., ((maxY - outerR) * scale), xmargin * scale, (maxY - outerR) * scale);
337 lineOuter.setAttribute("stroke-dasharray", "6,3");
338 lineOuter.setAttribute("stroke", "gray");
339 lineOuter.setAttribute("stroke-width", "1");
340
341
342 text(labelGroup, df.format(outerR), yLabelMarginX, ((maxY - outerR) * scale) + 5);
343 }
344
345
346 labelGroup = labelsX;
347
348
349 Element lineBottom = line(labelGroup, 0, zLabelOffsetY, halfZ * scale, zLabelOffsetY);
350 lineBottom.setAttribute("stroke-dasharray", "6,3");
351 lineBottom.setAttribute("stroke", "gray");
352 lineBottom.setAttribute("stroke-width", "1");
353
354
355 text(labelGroup, df.format(halfZ), ((halfZ * scale) / 2), zLabelOffsetY - 5);
356
357
358 zLabelOffsetY += 25;
359
360 Calorimeter.CalorimeterType calType = cal.getCalorimeterType();
361 System.out.println(calType.toString());
362 if (!calType.equals(CalorimeterType.UNKNOWN))
363 {
364 String calLabel = calType.toString().replace("_BARREL", "");
365 double calThickness = outerR - innerR;
366 double y = (maxY - outerR) + calThickness / 2;
367 y *= scale;
368 Element calText = text(labelsY, calLabel, 80, y + 5);
369 calText.setAttribute("font-size", "12");
370 }
371 }
372
373 else if (subdet.isEndcap() && subdet.getReflect())
374 {
375
376 double innerZ = cal.getInnerZ();
377 double outerZ = cal.getOuterZ();
378
379
380 rect(g, innerZ, maxY - outerR, outerR - innerR, halfZ * 2);
381
382 double thickness = outerZ - innerZ;
383
384
385 labelGroup = labelsX;
386
387
388 Element lineEndcap = line(labelGroup, innerZ * scale, zLabelOffsetY, outerZ * scale, zLabelOffsetY);
389 lineEndcap.setAttribute("stroke-dasharray", "6,3");
390 lineEndcap.setAttribute("stroke", "gray");
391 lineEndcap.setAttribute("stroke-width", "1");
392
393
394
395 text(labelGroup, df.format(zlength), ((innerZ + thickness / 2) * scale) - 10, zLabelOffsetY - 5);
396
397
398 zLabelOffsetY += 25;
399 }
400 }
401
402 else if (subdet instanceof Tracker)
403 {
404
405 if (subdet instanceof SiTrackerBarrel || subdet instanceof MultiLayerTracker)
406 {
407 double minR = 9999999.;
408 double maxR = 0;
409
410 IDetectorElement de = subdet.getDetectorElement();
411 for (IDetectorElement layer : de.getChildren())
412 {
413
414 Tube tube = (Tube)layer.getGeometry().getLogicalVolume().getSolid();
415 double thickness = tube.getOuterRadius() - tube.getInnerRadius();
416 double r = tube.getInnerRadius() + thickness / 2;
417 double halfZ = tube.getZHalfLength();
418 double outerR = tube.getOuterRadius();
419 double innerR = tube.getInnerRadius();
420
421 if (innerR < minR)
422 {
423 minR = innerR;
424 }
425
426 if (outerR > maxR)
427 {
428 maxR = outerR;
429 }
430
431
432 if (thickness < minTrackerLayerThickness)
433 {
434
435 line(g, 0, maxY - r, halfZ, maxY - r);
436 }
437
438 else
439 {
440
441 g.setAttribute("stroke", "black");
442
443
444 rect(g, 0., maxY - outerR, outerR - innerR, halfZ);
445 }
446 }
447
448 System.out.println("maxR = " + maxR);
449 System.out.println("minR = " + minR);
450 }
451
452
453
454 else if (subdet instanceof DiskTracker)
455 {
456 DiskTracker diskTracker = (DiskTracker)subdet;
457 int nlayers = diskTracker.getInnerR().length;
458 for (int i = 0, n = nlayers; i < n; i++ )
459 {
460 double innerR = diskTracker.getInnerR()[i];
461 double outerR = diskTracker.getOuterR()[i];
462 double z = diskTracker.getThickness()[i];
463 double innerZ = diskTracker.getInnerZ()[i];
464 double midZ = innerZ + z / 2;
465
466
467 if (z < minTrackerLayerThickness)
468 {
469
470 line(g, midZ, maxY - outerR, midZ, maxY - innerR);
471 }
472
473 else
474 {
475
476 g.setAttribute("stroke", "black");
477
478
479 rect(g, innerZ, maxY - outerR, outerR - innerR, z);
480 }
481 }
482 }
483 }
484 else if (subdet instanceof PolyconeSupport)
485 {
486 PolyconeSupport support = (PolyconeSupport)subdet;
487
488
489 List<ZPlane> zplanes = new ArrayList<ZPlane>(support.getZPlanes());
490 Collections.sort(zplanes, new ZPlaneCompare());
491
492
493 List<ZPlane> zplanesUse = new ArrayList<ZPlane>();
494 for (int i = 0, n = zplanes.size(); i < n; i++ )
495 {
496 ZPlane zplane = zplanes.get(i);
497
498
499 if (zplane.getZ() > 0)
500 {
501
502 if (i > 0 && zplanesUse.size() == 0)
503 {
504
505 ZPlane lastNegZPlane = zplanes.get(i - 1);
506
507
508 if (lastNegZPlane.getRMin() == zplane.getRMin() && lastNegZPlane.getRMax() == zplane.getRMax())
509 {
510 ZPlane borderZPlane = new ZPlane(lastNegZPlane.getRMin(), lastNegZPlane.getRMax(), 0);
511 zplanesUse.add(borderZPlane);
512 }
513
514 }
515
516 else
517 {
518 zplanesUse.add(zplane);
519 }
520 }
521 }
522
523 if (zplanesUse.size() > 0)
524 {
525
526 StringBuffer buff = new StringBuffer();
527
528
529 for (ZPlane zplane : zplanesUse)
530 {
531 double outerR = zplane.getRMax();
532 double z = zplane.getZ();
533 buff.append(z + "," + (maxY - outerR) + " ");
534 }
535
536
537 List<ZPlane> reverseZPlanes = new ArrayList<ZPlane>(zplanesUse);
538 Collections.reverse(reverseZPlanes);
539
540
541 for (ZPlane zplane : reverseZPlanes)
542 {
543 double innerR = zplane.getRMin();
544 double z = zplane.getZ();
545 buff.append(z + "," + (maxY - innerR) + " ");
546 }
547
548 String points = buff.toString();
549 points.trim();
550
551
552 Element polygon = new Element("polygon", ns);
553 polygon.setAttribute("points", points);
554 g.addContent(polygon);
555 }
556 }
557 else if (subdet instanceof TubeSegment)
558 {
559 TubeSegment tube = (TubeSegment)subdet;
560
561 if (tube.getTransform().getTranslation().z() > 0)
562 {
563 double innerR = tube.getInnerRadius();
564 double outerR = tube.getOuterRadius();
565 double halfZ = tube.getZHalfLength();
566 double zmin = tube.getTransform().getTranslation().z() - halfZ;
567
568
569 if (zmin > 0)
570 {
571 rect(g, zmin, maxY - outerR, outerR - innerR, halfZ);
572 }
573
574
575
576 }
577 }
578
579
580
581 }
582
583 private static Element line(Element parent, double x1, double y1, double x2, double y2)
584 {
585 Element line = new Element("line", ns);
586 parent.addContent(line);
587 line.setAttribute("x1", df.format(x1));
588 line.setAttribute("y1", df.format(y1));
589 line.setAttribute("x2", df.format(x2));
590 line.setAttribute("y2", df.format(y2));
591 return line;
592 }
593
594 private static Element rect(Element parent, double x, double y, double height, double width)
595 {
596 Element rect = new Element("rect", ns);
597 parent.addContent(rect);
598 rect.setAttribute("x", df.format(x));
599 rect.setAttribute("y", df.format(y));
600 rect.setAttribute("height", df.format(height));
601 rect.setAttribute("width", df.format(width));
602 return rect;
603 }
604
605 private static Element text(Element parent, String text, double x, double y)
606 {
607 Element t = new Element("text", ns);
608 parent.addContent(t);
609 t.setText(text);
610 t.setAttribute("x", df.format(x));
611 t.setAttribute("y", df.format(y));
612 return t;
613 }
614
615 private static double[] findMaxZY(Detector detector)
616 {
617 double[] zy = new double[2];
618 double z = 0;
619 double y = 0;
620
621
622 for (Subdetector subdet : detector.getSubdetectors().values())
623 {
624 if (subdet instanceof Calorimeter)
625 {
626 Calorimeter cal = (Calorimeter)subdet;
627 if (cal.getOuterRadius() > y)
628 {
629 y = cal.getOuterRadius();
630 }
631 if (cal.getOuterZ() > z)
632 {
633 z = cal.getOuterZ();
634 }
635 }
636 }
637
638 if (z == 0 || y == 0)
639 {
640 throw new RuntimeException("Could not find ZY extent of this Detector!");
641 }
642
643 zy[0] = z;
644 zy[1] = y;
645
646 return zy;
647 }
648 }