Search in sources :

Example 16 with UserData

use of org.concord.energy3d.model.UserData in project energy3d by concord-consortium.

the class EnergyPanel method updateProperties.

// As this method may be called from a non-Event-Queue thread, updating GUI must be done through invokeLater.
public void updateProperties() {
    // update part properties
    final HousePart selectedPart = SceneManager.getInstance().getSelectedPart();
    final boolean energyViewShown = MainPanel.getInstance().getEnergyButton().isSelected();
    final double meterToFoot;
    final String lengthUnit;
    switch(Scene.getInstance().getUnit()) {
        case USCustomaryUnits:
            meterToFoot = 3.28084;
            lengthUnit = "ft";
            break;
        default:
            meterToFoot = 1;
            lengthUnit = "m";
    }
    final double scale = Scene.getInstance().getAnnotationScale() * meterToFoot;
    final TitledBorder partPanelBorder = (TitledBorder) partPanel.getBorder();
    if (selectedPart != null) {
        final ReadOnlyVector3 v = selectedPart.getAbsPoint(0);
        if (selectedPart instanceof Tree) {
            final Tree tree = (Tree) selectedPart;
            if (tree.isDrawable()) {
                EventQueue.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        partPanelBorder.setTitle("Tree (" + tree.getId() + "): " + tree.getTreeName());
                        partProperty1Label.setText("  Spread & Height:");
                        partProperty2Label.setText("  Type:");
                        partProperty3Label.setText("  Position:");
                        final double l = v.length();
                        double a = 90 + Math.toDegrees(Math.asin(-v.getY() / l));
                        if (v.getX() < 0) {
                            a = 360 - a;
                        }
                        if (Util.isZero(a - 360)) {
                            a = 0;
                        }
                        partProperty1TextField.setText(ONE_DECIMAL.format(tree.getWidth() * scale) + lengthUnit + ", " + ONE_DECIMAL.format(tree.getHeight() * scale) + lengthUnit);
                        partProperty2TextField.setText(tree.getTreeName() + " (" + (tree.getTreeType() == Tree.PINE ? "Evergreen" : "Deciduous") + ")");
                        partProperty3TextField.setText("(" + ONE_DECIMAL.format(v.getX() * scale) + ", " + ONE_DECIMAL.format(v.getY() * scale) + ")" + lengthUnit + " or (" + ONE_DECIMAL.format(l * scale) + lengthUnit + ", " + ONE_DECIMAL.format(a) + "\u00B0)");
                        partProperty1TextField.putClientProperty("tooltip", "The spread and height of the tree");
                        partProperty2TextField.putClientProperty("tooltip", "The type of the tree");
                        partProperty3TextField.putClientProperty("tooltip", "The (x, y) or polar coordinates on the land");
                    }
                });
            }
        } else if (selectedPart instanceof Human) {
            final Human human = (Human) selectedPart;
            if (human.isDrawable()) {
                EventQueue.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        partPanelBorder.setTitle("Human (" + human.getId() + "): " + human.getHumanName());
                        partProperty1Label.setText("  X:");
                        partProperty2Label.setText("  Y:");
                        partProperty3Label.setText("  Z:");
                        partProperty1TextField.setText(ONE_DECIMAL.format(v.getX() * scale) + lengthUnit);
                        partProperty2TextField.setText(ONE_DECIMAL.format(v.getY() * scale) + lengthUnit);
                        partProperty3TextField.setText(ONE_DECIMAL.format(v.getZ() * scale) + lengthUnit);
                        partProperty1TextField.putClientProperty("tooltip", "X coordinate");
                        partProperty2TextField.putClientProperty("tooltip", "Y coordinate");
                        partProperty3TextField.putClientProperty("tooltip", "Z coordinate");
                    }
                });
            }
        } else if (selectedPart instanceof SolarPanel) {
            final SolarPanel sp = (SolarPanel) selectedPart;
            if (sp.isDrawable()) {
                final Foundation f = sp.getTopContainer();
                if (f != null) {
                    double a = sp.getRelativeAzimuth() + f.getAzimuth();
                    if (a >= 360) {
                        a -= 360;
                    }
                    final double az = a;
                    final boolean flat = (sp.getContainer() instanceof Roof && Util.isZero(sp.getContainer().getHeight())) || (sp.getContainer() instanceof Foundation);
                    EventQueue.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            String title = "Solar Panel (" + sp.getId() + "), " + (sp.getModelName() == null ? "" : "Model: " + sp.getModelName());
                            final String trackerName = sp.getTrackerName();
                            if (trackerName != null) {
                                title += ", Tracker: " + trackerName;
                            }
                            partPanelBorder.setTitle(title);
                            partProperty1Label.setText("  Size & Position:");
                            partProperty1TextField.setText(TWO_DECIMALS.format(sp.getPanelWidth() * meterToFoot) + "\u00d7" + TWO_DECIMALS.format(sp.getPanelHeight() * meterToFoot) + lengthUnit + ", (" + ONE_DECIMAL.format(v.getX() * scale) + ", " + ONE_DECIMAL.format(v.getY() * scale) + ", " + ONE_DECIMAL.format(v.getZ() * scale) + ")" + lengthUnit);
                            partProperty1TextField.putClientProperty("tooltip", "The length, width, and center coordinates of the solar panel");
                            partProperty2Label.setText("  Angles:");
                            partProperty2TextField.setText(flat ? "tilt: " + ONE_DECIMAL.format(Util.isZero(sp.getTiltAngle()) ? Math.toDegrees(Math.asin(sp.getNormal().getY())) : sp.getTiltAngle()) + "\u00B0, azimuth: " + ONE_DECIMAL.format(az) + "\u00B0" : " --- ");
                            partProperty2TextField.putClientProperty("tooltip", "The angles of the solar panel");
                            final String eff = ONE_DECIMAL.format(sp.getCellEfficiency() * 100) + "%";
                            if (energyViewShown) {
                                partProperty3Label.setText("  Efficiency & Yield:");
                                partProperty3TextField.setText(eff + ", " + TWO_DECIMALS.format(sp.getSolarPotentialToday()) + " kWh");
                                partProperty3TextField.putClientProperty("tooltip", "The solar cell efficiency and daily yield of the solar panel");
                            } else {
                                partProperty3Label.setText("  Efficiency:");
                                partProperty3TextField.setText(eff);
                                partProperty3TextField.putClientProperty("tooltip", "The solar cell efficiency of the solar panel");
                            }
                        }
                    });
                }
            }
        } else if (selectedPart instanceof Rack) {
            final Rack rack = (Rack) selectedPart;
            if (rack.isDrawable()) {
                final Foundation f = rack.getTopContainer();
                if (f != null) {
                    double a = rack.getRelativeAzimuth() + f.getAzimuth();
                    if (a >= 360) {
                        a -= 360;
                    }
                    final double az = a;
                    final int n = rack.isMonolithic() ? rack.getNumberOfSolarPanels() : rack.getChildren().size();
                    EventQueue.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            String title = "Rack (" + rack.getId() + ")";
                            final SolarPanel s = rack.getSolarPanel();
                            if (s.getModelName() != null) {
                                title += ", Model: " + s.getModelName();
                            }
                            final String trackerName = rack.getTrackerName();
                            if (trackerName != null) {
                                title += ", Tracker: " + trackerName;
                            }
                            partPanelBorder.setTitle(title);
                            partProperty1Label.setText("  Size & Center:");
                            partProperty1TextField.setText(TWO_DECIMALS.format(rack.getRackWidth() * meterToFoot) + "\u00d7" + TWO_DECIMALS.format(rack.getRackHeight() * meterToFoot) + lengthUnit + ", (" + ONE_DECIMAL.format(v.getX() * scale) + ", " + ONE_DECIMAL.format(v.getY() * scale) + ", " + ONE_DECIMAL.format(v.getZ() * scale) + ")" + lengthUnit);
                            partProperty1TextField.putClientProperty("tooltip", "The length, width, and center coordinates of the rack");
                            partProperty2Label.setText("  Angles:");
                            partProperty2TextField.setText("tilt: " + ONE_DECIMAL.format(Util.isZero(rack.getTiltAngle()) ? Math.toDegrees(Math.asin(rack.getNormal().getY())) : rack.getTiltAngle()) + "\u00B0, azimuth: " + ONE_DECIMAL.format(az) + "\u00B0");
                            partProperty2TextField.putClientProperty("tooltip", "The angles of the rack");
                            partProperty3Label.setText("  Solar Panels:");
                            final SolarPanel sp = rack.getSolarPanel();
                            final String eff = ONE_DECIMAL.format(sp.getCellEfficiency() * 100) + "%";
                            if (energyViewShown) {
                                partProperty3Label.setText("  Efficiency & Yield:");
                                partProperty3TextField.setText(eff + ", " + TWO_DECIMALS.format(rack.getSolarPotentialToday()) + " kWh");
                                partProperty3TextField.putClientProperty("tooltip", "The solar cell efficiency and daily yield of the solar panel array on the rack");
                            } else {
                                if (rack.isMonolithic()) {
                                    final int[] rnc = rack.getSolarPanelRowAndColumnNumbers();
                                    partProperty3TextField.setText("" + n + " (" + rnc[0] + "\u00D7" + rnc[1] + "), " + s.getPanelWidth() + "\u00D7" + s.getPanelHeight() + lengthUnit + ", " + eff);
                                } else {
                                    partProperty3TextField.setText("" + n);
                                }
                                partProperty3TextField.putClientProperty("tooltip", "Number and type of solar panels on this rack");
                            }
                        }
                    });
                }
            }
        } else if (selectedPart instanceof Mirror) {
            final Mirror m = (Mirror) selectedPart;
            if (m.isDrawable()) {
                final Foundation f = m.getTopContainer();
                if (f != null) {
                    double a = m.getRelativeAzimuth() + f.getAzimuth();
                    if (a >= 360) {
                        a -= 360;
                    }
                    final double az = a;
                    final boolean flat = m.getContainer() instanceof Foundation;
                    EventQueue.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            partPanelBorder.setTitle("Heliostat (" + m.getId() + ")");
                            partProperty1Label.setText("  Size & Center:");
                            partProperty1TextField.setText(TWO_DECIMALS.format(m.getMirrorWidth() * meterToFoot) + "\u00d7" + TWO_DECIMALS.format(m.getMirrorHeight() * meterToFoot) + lengthUnit + ", (" + ONE_DECIMAL.format(v.getX() * scale) + ", " + ONE_DECIMAL.format(v.getY() * scale) + ", " + ONE_DECIMAL.format(v.getZ() * scale) + ")" + lengthUnit);
                            partProperty1TextField.putClientProperty("tooltip", "The length, width, and center coordinates of the heliostat");
                            partProperty2Label.setText("  Angles:");
                            partProperty2TextField.setText(flat ? "tilt: " + ONE_DECIMAL.format(m.getTiltAngle()) + "\u00B0, azimuth: " + ONE_DECIMAL.format(az) + "\u00B0" : " --- ");
                            partProperty2TextField.putClientProperty("tooltip", "The angles of the heliostat");
                            final Foundation receiver = m.getReceiver();
                            final String s = "R=" + ONE_DECIMAL.format(m.getReflectance() * 100) + "%" + (receiver == null ? "" : ", \u03B7=" + ONE_DECIMAL.format(receiver.getSolarReceiverEfficiency() * 100) + "%");
                            if (energyViewShown) {
                                partProperty3Label.setText("  Properties & Yield:");
                                partProperty3TextField.setText(s + ", " + ONE_DECIMAL.format(m.getSolarPotentialToday() * m.getSystemEfficiency()) + " kWh");
                                partProperty3TextField.putClientProperty("tooltip", "The physical properties and electric yield of this heliostat");
                            } else {
                                partProperty3Label.setText("  Properties:");
                                partProperty3TextField.setText(s);
                                partProperty3TextField.putClientProperty("tooltip", "The physical properties of this heliostat");
                            }
                        }
                    });
                }
            }
        } else if (selectedPart instanceof ParabolicTrough) {
            final ParabolicTrough t = (ParabolicTrough) selectedPart;
            if (t.isDrawable()) {
                final Foundation f = t.getTopContainer();
                if (f != null) {
                    double a = t.getRelativeAzimuth() + f.getAzimuth();
                    if (a >= 360) {
                        a -= 360;
                    }
                    final double az = a;
                    // http://www.powerfromthesun.net/Book/chapter08/chapter08.html
                    final double focalLength = t.getSemilatusRectum() * 0.5;
                    final double d = t.getApertureWidth();
                    final double h = d * d / (16 * focalLength);
                    final double rimAngle = Math.toDegrees(Math.atan(1.0 / (d / (8 * h) - (2 * h) / d)));
                    final double b = 4 * h / d;
                    final double c = Math.sqrt(b * b + 1);
                    final double s = 0.5 * d * c + 2 * focalLength * Math.log(b + c);
                    EventQueue.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            partPanelBorder.setTitle("Parabolic Trough (" + t.getId() + ")");
                            partProperty1Label.setText("  Length & Center:");
                            partProperty1TextField.setText(TWO_DECIMALS.format(t.getTroughLength() * meterToFoot) + lengthUnit + ", module:" + TWO_DECIMALS.format(t.getModuleLength() * meterToFoot) + lengthUnit + ", (" + ONE_DECIMAL.format(v.getX() * scale) + ", " + ONE_DECIMAL.format(v.getY() * scale) + ", " + ONE_DECIMAL.format(v.getZ() * scale) + ")" + lengthUnit + ", azimuth:" + ONE_DECIMAL.format(az) + "\u00B0");
                            partProperty1TextField.putClientProperty("tooltip", "Assembly length, module length, center coordinates, and azimith of the parabolic trough");
                            partProperty2Label.setText("  Parabola Shape:");
                            partProperty2TextField.setText("f=" + ONE_DECIMAL.format(focalLength * meterToFoot) + lengthUnit + ", d=" + ONE_DECIMAL.format(t.getApertureWidth() * meterToFoot) + lengthUnit + ", h=" + ONE_DECIMAL.format(h * meterToFoot) + lengthUnit + ", \u03C6=" + ONE_DECIMAL.format(rimAngle >= 0 ? rimAngle : 180 + rimAngle) + "\u00B0");
                            partProperty2TextField.putClientProperty("tooltip", "Parameters of the parabolic shape");
                            final String str = "R=" + ONE_DECIMAL.format(t.getReflectance() * 100) + "%, s=" + ONE_DECIMAL.format(s * t.getTroughLength() * meterToFoot * meterToFoot) + lengthUnit + "\u00B2, a=" + ONE_DECIMAL.format(d * t.getTroughLength() * meterToFoot * meterToFoot) + lengthUnit + "\u00B2, \u03B1=" + ONE_DECIMAL.format(t.getAbsorptance() * 100) + "%";
                            if (energyViewShown) {
                                partProperty3Label.setText("  Properties & Yield:");
                                partProperty3TextField.setText(str + ", " + ONE_DECIMAL.format(t.getSolarPotentialToday() * t.getSystemEfficiency()) + " kWh");
                                partProperty3TextField.putClientProperty("tooltip", "The properties and yield of this parabolic trough");
                            } else {
                                partProperty3Label.setText("  Properties:");
                                partProperty3TextField.setText(str);
                                partProperty3TextField.putClientProperty("tooltip", "The properties of this parabolic trough");
                            }
                        }
                    });
                }
            }
        } else if (selectedPart instanceof ParabolicDish) {
            final ParabolicDish d = (ParabolicDish) selectedPart;
            if (d.isDrawable()) {
                final Foundation f = d.getTopContainer();
                if (f != null) {
                    double a = d.getRelativeAzimuth() + f.getAzimuth();
                    if (a >= 360) {
                        a -= 360;
                    }
                    final double focalLength = d.getFocalLength();
                    final double rimRadius = d.getRimRadius();
                    EventQueue.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            partPanelBorder.setTitle("Parabolic Dish (" + d.getId() + ")");
                            partProperty1Label.setText("  Size & Center:");
                            partProperty1TextField.setText("Rim radius=" + TWO_DECIMALS.format(rimRadius * meterToFoot) + lengthUnit + ", (" + ONE_DECIMAL.format(v.getX() * scale) + ", " + ONE_DECIMAL.format(v.getY() * scale) + ", " + ONE_DECIMAL.format(v.getZ() * scale) + ")" + lengthUnit);
                            partProperty1TextField.putClientProperty("tooltip", "Rim radius and center coordinates of the parabolic dish");
                            partProperty2Label.setText("  Parabola Shape:");
                            partProperty2TextField.setText("Focal length=" + ONE_DECIMAL.format(focalLength * meterToFoot) + lengthUnit);
                            partProperty2TextField.putClientProperty("tooltip", "Parameters of the parabolic shape");
                            final String str = "R=" + ONE_DECIMAL.format(d.getReflectance() * 100) + "%, \u03B1=" + ONE_DECIMAL.format(d.getAbsorptance() * 100) + "%";
                            if (energyViewShown) {
                                partProperty3Label.setText("  Properties & Yield:");
                                partProperty3TextField.setText(str + ", " + ONE_DECIMAL.format(d.getSolarPotentialToday() * d.getSystemEfficiency()) + " kWh");
                                partProperty3TextField.putClientProperty("tooltip", "The properties and yield of this parabolic dish");
                            } else {
                                partProperty3Label.setText("  Properties:");
                                partProperty3TextField.setText(str);
                                partProperty3TextField.putClientProperty("tooltip", "The properties of this parabolic dish");
                            }
                        }
                    });
                }
            }
        } else if (selectedPart instanceof FresnelReflector) {
            final FresnelReflector r = (FresnelReflector) selectedPart;
            if (r.isDrawable()) {
                final Foundation f = r.getTopContainer();
                if (f != null) {
                    double a = r.getRelativeAzimuth() + f.getAzimuth();
                    if (a >= 360) {
                        a -= 360;
                    }
                    final double az = a;
                    EventQueue.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            partPanelBorder.setTitle("Fresnel Reflector (" + r.getId() + ")");
                            partProperty1Label.setText("  Center & Azimuth:");
                            partProperty1TextField.setText("(" + ONE_DECIMAL.format(v.getX() * scale) + ", " + ONE_DECIMAL.format(v.getY() * scale) + ", " + ONE_DECIMAL.format(v.getZ() * scale) + ")" + lengthUnit + ", azimuth:" + ONE_DECIMAL.format(az) + "\u00B0");
                            partProperty1TextField.putClientProperty("tooltip", "Center coordinates and azimuth of the Fresnel reflector");
                            partProperty2Label.setText("  Length & Width:");
                            partProperty2TextField.setText(TWO_DECIMALS.format(r.getLength() * meterToFoot) + lengthUnit + ", module:" + TWO_DECIMALS.format(r.getModuleLength() * meterToFoot) + lengthUnit + ", " + ONE_DECIMAL.format(r.getModuleWidth() * meterToFoot) + lengthUnit);
                            partProperty2TextField.putClientProperty("tooltip", "Assembly length, module length, and width of the Fresnel reflector");
                            final Foundation receiver = r.getReceiver();
                            final String str = "R=" + ONE_DECIMAL.format(r.getReflectance() * 100) + "%, a=" + ONE_DECIMAL.format(r.getModuleWidth() * r.getLength() * meterToFoot * meterToFoot) + lengthUnit + "\u00B2" + (receiver == null ? "" : ", \u03B7=" + ONE_DECIMAL.format(receiver.getSolarReceiverEfficiency() * 100) + "%");
                            if (energyViewShown) {
                                partProperty3Label.setText("  Properties & Yield:");
                                partProperty3TextField.setText(str + ", " + ONE_DECIMAL.format(r.getSolarPotentialToday() * r.getSystemEfficiency()) + " kWh");
                                partProperty3TextField.putClientProperty("tooltip", "The properties and yield of this Fresnel reflector");
                            } else {
                                partProperty3Label.setText("  Properties:");
                                partProperty3TextField.setText(str);
                                partProperty3TextField.putClientProperty("tooltip", "The properties of this Fresnel reflector");
                            }
                        }
                    });
                }
            }
        } else if (selectedPart instanceof Sensor) {
            final Sensor sensor = (Sensor) selectedPart;
            if (sensor.isDrawable()) {
                EventQueue.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        partPanelBorder.setTitle("Sensor (" + sensor.getId() + ")");
                        partProperty1Label.setText("  Position:");
                        partProperty2Label.setText("  Thermal:");
                        partProperty3Label.setText("  Light:");
                        partProperty1TextField.setText("(" + ONE_DECIMAL.format(v.getX() * scale) + ", " + ONE_DECIMAL.format(v.getY() * scale) + ", " + ONE_DECIMAL.format(v.getZ() * scale) + ")" + lengthUnit);
                        partProperty2TextField.setText(TWO_DECIMALS.format(-sensor.getTotalHeatLoss() / sensor.getArea()) + " kWh/day/m\u00B2");
                        partProperty3TextField.setText(TWO_DECIMALS.format(sensor.getSolarPotentialToday() / sensor.getArea()) + " kWh/day/m\u00B2");
                        partProperty1TextField.putClientProperty("tooltip", "The (x, y, z) coordinates of the sensor");
                        partProperty2TextField.putClientProperty("tooltip", "The heat flux measured by the sensor");
                        partProperty3TextField.putClientProperty("tooltip", "The light intensity measured by the sensor");
                    }
                });
            }
        } else if (selectedPart instanceof Foundation) {
            final Foundation foundation = (Foundation) selectedPart;
            if (foundation.isDrawable()) {
                final Vector3 v1 = foundation.getAbsPoint(1);
                final Vector3 v2 = foundation.getAbsPoint(2);
                final Vector3 v3 = foundation.getAbsPoint(3);
                final double cx = 0.25 * (v.getX() + v1.getX() + v2.getX() + v3.getX());
                final double cy = 0.25 * (v.getY() + v1.getY() + v2.getY() + v3.getY());
                final double lx = v.distance(v2);
                final double ly = v.distance(v1);
                final double lz = foundation.getHeight();
                final double az = foundation.getAzimuth();
                final String landArea;
                final FoundationPolygon polygon = foundation.getPolygon();
                if (polygon != null && polygon.isVisible()) {
                    landArea = " (inset:" + ONE_DECIMAL.format(polygon.getArea()) + ")";
                } else {
                    landArea = "";
                }
                final Mesh selectedMesh;
                final Node selectedNode;
                final OrientedBoundingBox nodeBox, meshBox;
                final List<Node> nodes = foundation.getImportedNodes();
                if (nodes != null) {
                    selectedMesh = foundation.getSelectedMesh();
                    if (selectedMesh != null) {
                        selectedNode = selectedMesh.getParent();
                        nodeBox = Util.getOrientedBoundingBox(selectedNode);
                        meshBox = Util.getOrientedBoundingBox(selectedMesh);
                    } else {
                        selectedNode = null;
                        nodeBox = null;
                        meshBox = null;
                    }
                } else {
                    selectedMesh = null;
                    selectedNode = null;
                    nodeBox = null;
                    meshBox = null;
                }
                EventQueue.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        if (selectedNode != null) {
                            final double xNodeBox = 2 * nodeBox.getExtent().getX() * scale;
                            final double yNodeBox = 2 * nodeBox.getExtent().getY() * scale;
                            final double zNodeBox = 2 * nodeBox.getExtent().getZ() * scale;
                            final double xMeshBox = 2 * meshBox.getExtent().getX() * scale;
                            final double yMeshBox = 2 * meshBox.getExtent().getY() * scale;
                            final double zMeshBox = 2 * meshBox.getExtent().getZ() * scale;
                            final ReadOnlyVector3 meshBoxCenter = meshBox.getCenter();
                            final NodeState ns = foundation.getNodeState(selectedNode);
                            final Vector3 position = ns.getRelativePosition().add(foundation.getAbsCenter(), null);
                            Vector3 meshNormal = null;
                            int meshIndex = -1;
                            if (selectedMesh.getUserData() instanceof UserData) {
                                final UserData ud = (UserData) selectedMesh.getUserData();
                                meshIndex = ud.getMeshIndex();
                                meshNormal = (Vector3) ud.getNormal();
                                if (!Util.isZero(az)) {
                                    selectedNode.getRotation().applyPost(meshNormal, meshNormal);
                                }
                            }
                            // System.out.println(">>>" + Util.computeFirstNormal(selectedMesh) + ", " + Util.getFirstNormalFromBuffer(selectedMesh));
                            final String meshBoxString = TWO_DECIMALS.format(xMeshBox) + "\u00d7" + (TWO_DECIMALS.format(yMeshBox)) + "\u00d7" + (TWO_DECIMALS.format(zMeshBox)) + lengthUnit;
                            final String meshCenterString = "(" + ONE_DECIMAL.format(meshBoxCenter.getX() * scale) + ", " + ONE_DECIMAL.format(meshBoxCenter.getY() * scale) + ", " + ONE_DECIMAL.format(meshBoxCenter.getZ() * scale) + ")" + lengthUnit;
                            final String meshNormalString = meshNormal != null ? "(" + TWO_DECIMALS.format(meshNormal.getX()) + ", " + TWO_DECIMALS.format(meshNormal.getY()) + ", " + TWO_DECIMALS.format(meshNormal.getZ()) + ")" : "";
                            partPanelBorder.setTitle("Node #" + foundation.getImportedNodes().indexOf(selectedNode) + " (" + Util.getFileName(ns.getSourceURL().getPath()).replace("%20", " ") + "), Mesh #" + meshIndex + ", Base #" + foundation.getId());
                            partProperty1Label.setText("  Node:");
                            partProperty2Label.setText("  Mesh:");
                            partProperty1TextField.setText(TWO_DECIMALS.format(xNodeBox) + "\u00d7" + (TWO_DECIMALS.format(yNodeBox)) + "\u00d7" + (TWO_DECIMALS.format(zNodeBox)) + lengthUnit + ", (" + TWO_DECIMALS.format(position.getX() * scale) + ", " + TWO_DECIMALS.format(position.getY() * scale) + ")" + lengthUnit);
                            partProperty2TextField.setText(meshBoxString + ", " + meshCenterString);
                            partProperty1TextField.putClientProperty("tooltip", "Dimension and location of the bounding box of the selected node<br>File:" + ns.getSourceURL().getFile());
                            partProperty2TextField.putClientProperty("tooltip", "Dimension and location of the bounding box of the selected mesh");
                            if (energyViewShown) {
                                double dailyMeshSolarPotential = 0;
                                final double[] meshSolarPotential = SolarRadiation.getInstance().getSolarPotential(selectedMesh);
                                for (final double x : meshSolarPotential) {
                                    dailyMeshSolarPotential += x;
                                }
                                partProperty3Label.setText("  Solar:");
                                partProperty3TextField.setText("\u2191" + meshNormalString + ", " + TWO_DECIMALS.format(dailyMeshSolarPotential) + " kWh");
                                partProperty3TextField.putClientProperty("tooltip", "Normal vector and solar potential of the selected mesh");
                            } else {
                                partProperty3Label.setText("  Normal:");
                                partProperty3TextField.setText("\u2191" + meshNormalString + ", " + selectedMesh.getMeshData().getVertexCount() + " vertices");
                                partProperty3TextField.putClientProperty("tooltip", "Normal vector and vertex count of the selected mesh");
                            }
                        } else {
                            partPanelBorder.setTitle("Foundation (" + foundation.getId() + ")");
                            partProperty1Label.setText("  Size:");
                            partProperty1TextField.setText(TWO_DECIMALS.format(lx * scale) + "\u00d7" + (TWO_DECIMALS.format(ly * scale)) + "\u00d7" + (TWO_DECIMALS.format(lz * scale)) + lengthUnit + ", Area\u2248" + ONE_DECIMAL.format(lx * ly * scale * scale) + landArea + lengthUnit + "\u00B2");
                            partProperty1TextField.putClientProperty("tooltip", "The length and width of the foundation");
                            partProperty2Label.setText("  Position:");
                            partProperty2TextField.setText("(" + TWO_DECIMALS.format(cx * scale) + ", " + TWO_DECIMALS.format(cy * scale) + ")" + lengthUnit);
                            partProperty2TextField.putClientProperty("tooltip", "The (x, y) coordinate of the center of the foundation");
                            partProperty3Label.setText("  Azimuth:");
                            partProperty3TextField.setText(TWO_DECIMALS.format(az) + "\u00B0");
                            partProperty3TextField.putClientProperty("tooltip", "The azimuth of the reference edge");
                        }
                    }
                });
            }
        } else if (selectedPart instanceof Roof) {
            final Roof roof = (Roof) selectedPart;
            if (roof.isDrawable()) {
                final double area = roof.getArea();
                EventQueue.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        partPanelBorder.setTitle("Roof (" + roof.getId() + ")");
                        partProperty1Label.setText("  Area & Rise:");
                        partProperty1TextField.setText("Area = " + TWO_DECIMALS.format(area) + lengthUnit + "\u00B2, Rise = " + TWO_DECIMALS.format(roof.getHeight() * scale) + lengthUnit);
                        partProperty1TextField.putClientProperty("tooltip", "The total area and the rise of the roof<br>(The rise is the highest point of the roof to the top of the walls.)");
                        partProperty2Label.setText("  Thermal:");
                        partProperty3Label.setText("  Solar:");
                        final String rval = ONE_DECIMAL.format(Util.toUsRValue(roof.getUValue()));
                        final boolean isBuildingProject = Scene.getInstance().getProjectType() == Foundation.TYPE_BUILDING;
                        final float absorptance = 1 - roof.getAlbedo();
                        if (energyViewShown) {
                            partProperty2TextField.setText("R-value = " + rval + ", Gain = " + TWO_DECIMALS.format(-roof.getTotalHeatLoss()) + " kWh");
                            partProperty2TextField.putClientProperty("tooltip", "The R-value and daily thermal gain of the roof");
                            if (isBuildingProject) {
                                partProperty3TextField.setText("Absorptance = " + TWO_DECIMALS.format(absorptance) + ", Absorption = " + TWO_DECIMALS.format(roof.getSolarPotentialToday() * absorptance) + " kWh");
                                partProperty3TextField.putClientProperty("tooltip", "The absorptance and daily solar heat gain of the roof surface");
                            } else {
                                partProperty3TextField.setText("Radiation energy = " + TWO_DECIMALS.format(roof.getSolarPotentialToday()) + " kWh");
                                partProperty3TextField.putClientProperty("tooltip", "The solar radiation energy onto this roof surface");
                            }
                        } else {
                            partProperty2TextField.setText("R-value = " + rval + " (US system)");
                            partProperty2TextField.putClientProperty("tooltip", "The R-value of the roof");
                            partProperty3TextField.setText("Absorptance = " + TWO_DECIMALS.format(absorptance));
                            partProperty3TextField.putClientProperty("tooltip", "The absorptance of the roof surface");
                        }
                    }
                });
            }
        } else if (selectedPart instanceof Window) {
            final Window window = (Window) selectedPart;
            if (window.isDrawable()) {
                final double lx = window.getWindowWidth();
                final double ly = window.getWindowHeight();
                final Vector3 v1 = window.getAbsPoint(1);
                final Vector3 v2 = window.getAbsPoint(2);
                final Vector3 v3 = window.getAbsPoint(3);
                final double cx = 0.25 * (v.getX() + v1.getX() + v2.getX() + v3.getX());
                final double cy = 0.25 * (v.getY() + v1.getY() + v2.getY() + v3.getY());
                final double cz = 0.25 * (v.getZ() + v1.getZ() + v2.getZ() + v3.getZ());
                EventQueue.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        partPanelBorder.setTitle("Window (" + window.getId() + ")");
                        partProperty1Label.setText("  Size & Center:");
                        partProperty1TextField.setText(TWO_DECIMALS.format(lx) + "\u00d7" + (TWO_DECIMALS.format(ly)) + lengthUnit + " \u2248 " + TWO_DECIMALS.format(lx * ly) + lengthUnit + "\u00B2, (" + TWO_DECIMALS.format(cx * scale) + ", " + TWO_DECIMALS.format(cy * scale) + ", " + TWO_DECIMALS.format(cz * scale) + ")" + lengthUnit);
                        partProperty1TextField.putClientProperty("tooltip", "The width, height, and center of the window");
                        partProperty2Label.setText("  Thermal:");
                        partProperty3Label.setText("  Solar:");
                        final String shgc = TWO_DECIMALS.format(window.getSolarHeatGainCoefficient());
                        if (energyViewShown) {
                            partProperty2TextField.setText("U-Value = " + TWO_DECIMALS.format(Util.toUsUValue(window.getUValue())) + ", Gain = " + TWO_DECIMALS.format(-window.getTotalHeatLoss()) + " kWh");
                            partProperty2TextField.putClientProperty("tooltip", "The U-value and daily thermal gain of the window");
                            partProperty3TextField.setText("SHGC = " + shgc + ", Gain = " + TWO_DECIMALS.format(window.getSolarPotentialToday() * window.getSolarHeatGainCoefficient()) + " kWh");
                            partProperty3TextField.putClientProperty("tooltip", "The SHGC value and daily solar gain of the window");
                        } else {
                            partProperty2TextField.setText("U-Value = " + TWO_DECIMALS.format(Util.toUsUValue(window.getUValue())) + " (US system)");
                            partProperty2TextField.putClientProperty("tooltip", "The U-value of the window");
                            partProperty3TextField.setText("SHGC = " + shgc);
                            partProperty3TextField.putClientProperty("tooltip", "The solar heat gain coefficient (SHGC) of the window");
                        }
                    }
                });
            }
        } else if (selectedPart instanceof Wall) {
            final Wall wall = (Wall) selectedPart;
            if (wall.isDrawable()) {
                final Vector3 v1 = wall.getAbsPoint(1);
                final Vector3 v2 = wall.getAbsPoint(2);
                final Vector3 v3 = wall.getAbsPoint(3);
                final double cx = 0.25 * (v.getX() + v1.getX() + v2.getX() + v3.getX());
                final double cy = 0.25 * (v.getY() + v1.getY() + v2.getY() + v3.getY());
                final double cz = 0.25 * (v.getZ() + v1.getZ() + v2.getZ() + v3.getZ());
                final double lx = v.distance(v2);
                final double ly = v.distance(v1);
                EventQueue.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        partPanelBorder.setTitle("Wall (" + wall.getId() + ")");
                        partProperty1Label.setText("  Size & Center:");
                        partProperty1TextField.setText(TWO_DECIMALS.format(lx * scale) + "\u00d7" + (TWO_DECIMALS.format(ly * scale)) + lengthUnit + " \u2248 " + TWO_DECIMALS.format(lx * ly * scale * scale) + lengthUnit + " \u00B2, " + "(" + TWO_DECIMALS.format(cx * scale) + ", " + TWO_DECIMALS.format(cy * scale) + ", " + TWO_DECIMALS.format(cz * scale) + ")" + lengthUnit);
                        partProperty1TextField.putClientProperty("tooltip", "The width, height, and center of the wall");
                        partProperty2Label.setText("  Thermal:");
                        partProperty3Label.setText("  Solar:");
                        final String rval = ONE_DECIMAL.format(Util.toUsRValue(wall.getUValue()));
                        final float absorptance = 1 - wall.getAlbedo();
                        if (energyViewShown) {
                            partProperty2TextField.setText("R-Value = " + rval + ", Gain = " + TWO_DECIMALS.format(-wall.getTotalHeatLoss()) + " kWh");
                            partProperty2TextField.putClientProperty("tooltip", "The R-value and daily thermal gain of the wall");
                            partProperty3TextField.setText("Absorptance = " + TWO_DECIMALS.format(absorptance) + ", Absorption = " + TWO_DECIMALS.format(wall.getSolarPotentialToday() * absorptance) + " kWh");
                            partProperty3TextField.putClientProperty("tooltip", "The absorptance and daily solar heat gain of the wall surface");
                        } else {
                            partProperty2TextField.setText("R-Value = " + rval + " (US system)");
                            partProperty2TextField.putClientProperty("tooltip", "The R-value of the wall");
                            partProperty3TextField.setText("Absorptance = " + TWO_DECIMALS.format(absorptance));
                            partProperty3TextField.putClientProperty("tooltip", "The absorptance of the wall surface");
                        }
                    }
                });
            }
        } else if (selectedPart instanceof Door) {
            final Door door = (Door) selectedPart;
            if (door.isDrawable()) {
                final Vector3 v1 = door.getAbsPoint(1);
                final Vector3 v2 = door.getAbsPoint(2);
                final Vector3 v3 = door.getAbsPoint(3);
                final double cx = 0.25 * (v.getX() + v1.getX() + v2.getX() + v3.getX());
                final double cy = 0.25 * (v.getY() + v1.getY() + v2.getY() + v3.getY());
                final double cz = 0.25 * (v.getZ() + v1.getZ() + v2.getZ() + v3.getZ());
                final double lx = v.distance(v2);
                final double ly = v.distance(v1);
                EventQueue.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        partPanelBorder.setTitle("Door (" + door.getId() + ")");
                        partProperty1Label.setText("  Size & Center:");
                        partProperty1TextField.setText(TWO_DECIMALS.format(lx * scale) + "\u00d7" + (TWO_DECIMALS.format(ly * scale)) + lengthUnit + " \u2248 " + TWO_DECIMALS.format(lx * ly * scale * scale) + lengthUnit + "\u00B2, (" + TWO_DECIMALS.format(cx * scale) + ", " + TWO_DECIMALS.format(cy * scale) + ", " + TWO_DECIMALS.format(cz * scale) + ")" + lengthUnit);
                        partProperty1TextField.putClientProperty("tooltip", "The width, height, and center of the door");
                        partProperty2Label.setText("  Thermal:");
                        partProperty3Label.setText("  Solar:");
                        final String uval = TWO_DECIMALS.format(Util.toUsUValue(door.getUValue()));
                        final float absorptance = 1 - door.getAlbedo();
                        if (energyViewShown) {
                            partProperty2TextField.setText("U-Value = " + uval + ", Gain = " + TWO_DECIMALS.format(-door.getTotalHeatLoss()) + " kWh");
                            partProperty2TextField.putClientProperty("tooltip", "The R-value and daily thermal gain of the door");
                            partProperty3TextField.setText("Absorptance = " + TWO_DECIMALS.format(absorptance) + ", Absorption = " + TWO_DECIMALS.format(door.getSolarPotentialToday() * absorptance) + " kWh");
                            partProperty3TextField.putClientProperty("tooltip", "The absorptance and daily solar heat gain of the door surface");
                        } else {
                            partProperty2TextField.setText("U-Value = " + uval + " (US system)");
                            partProperty2TextField.putClientProperty("tooltip", "The U-value of the door");
                            partProperty3TextField.setText("Absorptance = " + TWO_DECIMALS.format(absorptance));
                            partProperty3TextField.putClientProperty("tooltip", "The absorptance of the door surface");
                        }
                    }
                });
            }
        } else if (selectedPart instanceof Floor) {
            final Floor floor = (Floor) selectedPart;
            if (floor.isDrawable()) {
                final double cx, cy;
                final double cz = v.getZ();
                if (floor.getPoints().size() > 1) {
                    final Vector3 v1 = floor.getAbsPoint(1);
                    final Vector3 v2 = floor.getAbsPoint(2);
                    final Vector3 v3 = floor.getAbsPoint(3);
                    cx = 0.25 * (v.getX() + v1.getX() + v2.getX() + v3.getX());
                    cy = 0.25 * (v.getY() + v1.getY() + v2.getY() + v3.getY());
                } else {
                    cx = v.getX();
                    cy = v.getY();
                }
                EventQueue.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        partPanelBorder.setTitle("Floor (" + floor.getId() + ")");
                        partProperty1Label.setText("  Area & Center");
                        partProperty1TextField.setText(ONE_DECIMAL.format(floor.getArea()) + lengthUnit + "\u00B2, (" + ONE_DECIMAL.format(cx * scale) + ", " + ONE_DECIMAL.format(cy * scale) + ", " + ONE_DECIMAL.format(cz * scale) + ")" + lengthUnit);
                        partProperty1TextField.putClientProperty("tooltip", "The area and center of the floor");
                        partProperty2Label.setText("  Thermal:");
                        partProperty2TextField.setText("N.A.");
                        partProperty2TextField.putClientProperty("tooltip", "Not applicable to thermal analysis");
                        partProperty3Label.setText("  Solar:");
                        partProperty3TextField.setText("N.A.");
                        partProperty3TextField.putClientProperty("tooltip", "Not applicable to solar analysis");
                    }
                });
            }
        }
    } else {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                final int numberOfSolarPanels = Scene.getInstance().countSolarPanels();
                if (numberOfSolarPanels > 0) {
                    partPanelBorder.setTitle("Solar Panels");
                    partProperty1Label.setText("  Total Number:");
                    partProperty1TextField.setText("" + numberOfSolarPanels);
                    partProperty1TextField.putClientProperty("tooltip", "Total number of solar panels");
                    partProperty2Label.setText("  Total Cost:");
                    partProperty2TextField.setText("$" + TWO_DECIMALS.format(PvProjectCost.getInstance().getTotalCost()));
                    partProperty2TextField.putClientProperty("tooltip", "Total cost of solar panels");
                    partProperty3Label.setText("  -");
                    partProperty3TextField.setText("");
                    partProperty3TextField.putClientProperty("tooltip", null);
                } else {
                    final int numberOfHeliostats = Scene.getInstance().countParts(Mirror.class);
                    if (numberOfHeliostats > 0) {
                        partPanelBorder.setTitle("Heliostats");
                        partProperty1Label.setText("  Total Number:");
                        partProperty1TextField.setText("" + numberOfHeliostats);
                        partProperty1TextField.putClientProperty("tooltip", "Total number of heliostats");
                        partProperty2Label.setText("  Total Cost:");
                        partProperty2TextField.setText("$" + TWO_DECIMALS.format(CspProjectCost.getInstance().getTotalCost()));
                        partProperty2TextField.putClientProperty("tooltip", "Total cost of heliostats");
                        partProperty3Label.setText("  -");
                        partProperty3TextField.setText("");
                        partProperty3TextField.putClientProperty("tooltip", null);
                    } else {
                        final int numberOfParabolicTroughs = Scene.getInstance().countParts(ParabolicTrough.class);
                        if (numberOfParabolicTroughs > 0) {
                            partPanelBorder.setTitle("Parabolic Troughs");
                            partProperty1Label.setText("  Total Number:");
                            partProperty1TextField.setText("" + numberOfParabolicTroughs);
                            partProperty1TextField.putClientProperty("tooltip", "Total number of parabolic troughs");
                            partProperty2Label.setText("  Total Cost:");
                            partProperty2TextField.setText("$" + TWO_DECIMALS.format(CspProjectCost.getInstance().getTotalCost()));
                            partProperty2TextField.putClientProperty("tooltip", "Total cost of parabolic troughs");
                            partProperty3Label.setText("  -");
                            partProperty3TextField.setText("");
                            partProperty3TextField.putClientProperty("tooltip", null);
                        } else {
                            final int numberOfParabolicDishes = Scene.getInstance().countParts(ParabolicDish.class);
                            if (numberOfParabolicDishes > 0) {
                                partPanelBorder.setTitle("Parabolic Dishes");
                                partProperty1Label.setText("  Total Number:");
                                partProperty1TextField.setText("" + numberOfParabolicDishes);
                                partProperty1TextField.putClientProperty("tooltip", "Total number of parabolic dishes");
                                partProperty2Label.setText("  Total Cost:");
                                partProperty2TextField.setText("$" + TWO_DECIMALS.format(CspProjectCost.getInstance().getTotalCost()));
                                partProperty2TextField.putClientProperty("tooltip", "Total cost of parabolic dishes");
                                partProperty3Label.setText("  -");
                                partProperty3TextField.setText("");
                                partProperty3TextField.putClientProperty("tooltip", null);
                            } else {
                                final int numberOfFresnelReflectors = Scene.getInstance().countParts(FresnelReflector.class);
                                if (numberOfFresnelReflectors > 0) {
                                    partPanelBorder.setTitle("Fresnel Reflectors");
                                    partProperty1Label.setText("  Total Number:");
                                    partProperty1TextField.setText("" + numberOfFresnelReflectors);
                                    partProperty1TextField.putClientProperty("tooltip", "Total number of Fresnel reflectors");
                                    partProperty2Label.setText("  Total Cost:");
                                    partProperty2TextField.setText("$" + TWO_DECIMALS.format(CspProjectCost.getInstance().getTotalCost()));
                                    partProperty2TextField.putClientProperty("tooltip", "Total cost of Fresnel reflectors");
                                    partProperty3Label.setText("  -");
                                    partProperty3TextField.setText("");
                                    partProperty3TextField.putClientProperty("tooltip", null);
                                } else {
                                    final int numberOfNodes = Scene.getInstance().countNodes();
                                    if (numberOfNodes > 0) {
                                        partPanelBorder.setTitle("Structures");
                                        partProperty1Label.setText("  Total Nodes:");
                                        partProperty1TextField.setText("" + numberOfNodes);
                                        partProperty1TextField.putClientProperty("tooltip", "Total number of structure nodes");
                                        partProperty2Label.setText("  Total Meshes:");
                                        partProperty2TextField.setText("" + Scene.getInstance().countMeshes());
                                        partProperty2TextField.putClientProperty("tooltip", "Total number of structure meshes");
                                        partProperty3Label.setText("  -");
                                        partProperty3TextField.setText("");
                                        partProperty3TextField.putClientProperty("tooltip", null);
                                    } else {
                                        partPanelBorder.setTitle(" -");
                                        partProperty1Label.setText("  -");
                                        partProperty1TextField.setText("");
                                        partProperty1TextField.putClientProperty("tooltip", null);
                                        partProperty2Label.setText("  -");
                                        partProperty2TextField.setText("");
                                        partProperty2TextField.putClientProperty("tooltip", null);
                                        partProperty3Label.setText("  -");
                                        partProperty3TextField.setText("");
                                        partProperty3TextField.putClientProperty("tooltip", null);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        });
    }
    partPanel.repaint();
    // update building properties
    final Foundation selectedFoundation;
    if (selectedPart == null) {
        selectedFoundation = null;
    } else if (selectedPart instanceof Foundation) {
        selectedFoundation = (Foundation) selectedPart;
    } else {
        selectedFoundation = selectedPart.getTopContainer();
    }
    EventQueue.invokeLater(new Runnable() {

        @Override
        public void run() {
            if (selectedFoundation != null) {
                switch(selectedFoundation.getProjectType()) {
                    case Foundation.TYPE_BUILDING:
                        dataPanel.remove(instructionPanel);
                        dataPanel.remove(pvProjectPanel);
                        dataPanel.remove(cspProjectPanel);
                        dataPanel.add(buildingPanel, 2);
                        final Calendar c = Heliodon.getInstance().getCalendar();
                        final int temp = selectedFoundation.getThermostat().getTemperature(c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY, c.get(Calendar.HOUR_OF_DAY));
                        switch(Scene.getInstance().getUnit()) {
                            case InternationalSystemOfUnits:
                                thermostatTemperatureField.setText(temp + " \u00B0C");
                                break;
                            case USCustomaryUnits:
                                thermostatTemperatureField.setText(Math.round(32.0 + 9.0 * temp / 5.0) + " \u00B0F");
                                break;
                        }
                        thermostatPanel.add(adjustThermostatButton, BorderLayout.EAST);
                        String s2 = selectedFoundation.toString();
                        s2 = s2.substring(0, s2.indexOf(')') + 1);
                        final int i1 = s2.indexOf('(');
                        final int i2 = s2.indexOf(')');
                        ((TitledBorder) buildingPanel.getBorder()).setTitle("Building #" + s2.substring(i1 + 1, i2));
                        buildingInfoPanel.update(selectedFoundation);
                        break;
                    case Foundation.TYPE_PV_PROJECT:
                        dataPanel.remove(instructionPanel);
                        dataPanel.remove(buildingPanel);
                        dataPanel.remove(cspProjectPanel);
                        dataPanel.add(pvProjectPanel, 2);
                        pvProjectInfoPanel.update(selectedFoundation);
                        break;
                    case Foundation.TYPE_CSP_PROJECT:
                        dataPanel.remove(instructionPanel);
                        dataPanel.remove(buildingPanel);
                        dataPanel.remove(pvProjectPanel);
                        dataPanel.add(cspProjectPanel, 2);
                        cspProjectInfoPanel.update(selectedFoundation);
                        break;
                    case -1:
                        dataPanel.remove(instructionPanel);
                        dataPanel.remove(buildingPanel);
                        dataPanel.remove(pvProjectPanel);
                        dataPanel.remove(cspProjectPanel);
                        break;
                }
            } else {
                dataPanel.remove(buildingPanel);
                dataPanel.remove(pvProjectPanel);
                dataPanel.remove(cspProjectPanel);
                dataPanel.add(instructionPanel, 2);
                for (int i = 0; i < instructionSheets.length; i++) {
                    final String contentType = Scene.getInstance().getInstructionSheetTextType(i);
                    instructionSheets[i].setContentType(contentType == null ? "text/plain" : contentType);
                    if (!instructionSheets[i].getText().equals(Scene.getInstance().getInstructionSheetText(i))) {
                        instructionSheets[i].setText(Scene.getInstance().getInstructionSheetText(i));
                    }
                }
            }
            dataPanel.validate();
            dataPanel.repaint();
        }
    });
}
Also used : ParabolicTrough(org.concord.energy3d.model.ParabolicTrough) NodeState(org.concord.energy3d.model.NodeState) Wall(org.concord.energy3d.model.Wall) UserData(org.concord.energy3d.model.UserData) Node(com.ardor3d.scenegraph.Node) TitledBorder(javax.swing.border.TitledBorder) Rack(org.concord.energy3d.model.Rack) Roof(org.concord.energy3d.model.Roof) Tree(org.concord.energy3d.model.Tree) Foundation(org.concord.energy3d.model.Foundation) List(java.util.List) HousePart(org.concord.energy3d.model.HousePart) FoundationPolygon(org.concord.energy3d.model.FoundationPolygon) Human(org.concord.energy3d.model.Human) Window(org.concord.energy3d.model.Window) Floor(org.concord.energy3d.model.Floor) FresnelReflector(org.concord.energy3d.model.FresnelReflector) GregorianCalendar(java.util.GregorianCalendar) Calendar(java.util.Calendar) Mesh(com.ardor3d.scenegraph.Mesh) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) Vector3(com.ardor3d.math.Vector3) Door(org.concord.energy3d.model.Door) ParabolicDish(org.concord.energy3d.model.ParabolicDish) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) OrientedBoundingBox(com.ardor3d.bounding.OrientedBoundingBox) SolarPanel(org.concord.energy3d.model.SolarPanel) Mirror(org.concord.energy3d.model.Mirror) Sensor(org.concord.energy3d.model.Sensor)

Example 17 with UserData

use of org.concord.energy3d.model.UserData in project energy3d by concord-consortium.

the class PopupMenuForMesh method getPopupMenu.

static JPopupMenu getPopupMenu() {
    if (popupMenuForMesh == null) {
        final JMenuItem miInfo = new JMenuItem("Mesh");
        miInfo.setEnabled(false);
        miInfo.setOpaque(true);
        miInfo.setBackground(Config.isMac() ? Color.BLACK : Color.GRAY);
        miInfo.setForeground(Color.WHITE);
        final JMenuItem miMessThickness = new JMenuItem("Thickness...");
        miMessThickness.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                final HousePart selectedPart = SceneManager.getInstance().getSelectedPart();
                if (selectedPart instanceof Foundation) {
                    final Foundation f = (Foundation) selectedPart;
                    final Mesh m = f.getSelectedMesh();
                    if (m != null) {
                        final Node n = m.getParent();
                        final String title = "<html>Adjust the distance between two mesh faces to create some thickness<br>A larger thickness also mitigates the z-fighting effect.</html>";
                        while (true) {
                            final String newValue = JOptionPane.showInputDialog(MainFrame.getInstance(), title, f.getMeshThickness(n) * Scene.getInstance().getAnnotationScale());
                            if (newValue == null) {
                                break;
                            } else {
                                try {
                                    final double val = Double.parseDouble(newValue);
                                    if (val < 0 || val > 1) {
                                        JOptionPane.showMessageDialog(MainFrame.getInstance(), "Thickness must be between 0 and 1 meter.", "Range Error", JOptionPane.ERROR_MESSAGE);
                                    } else {
                                        SceneManager.getTaskManager().update(new Callable<Object>() {

                                            @Override
                                            public Object call() throws Exception {
                                                f.setMeshThickness(n, val / Scene.getInstance().getAnnotationScale());
                                                f.draw();
                                                return null;
                                            }
                                        });
                                        break;
                                    }
                                } catch (final NumberFormatException exception) {
                                    JOptionPane.showMessageDialog(MainFrame.getInstance(), newValue + " is an invalid value!", "Error", JOptionPane.ERROR_MESSAGE);
                                }
                            }
                        }
                    }
                }
            }
        });
        final JMenuItem miReverseNormalVector = new JMenuItem("Reverse Mesh Normal Vector");
        miReverseNormalVector.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                final HousePart selectedPart = SceneManager.getInstance().getSelectedPart();
                if (selectedPart instanceof Foundation) {
                    final Foundation f = (Foundation) selectedPart;
                    SceneManager.getTaskManager().update(new Callable<Object>() {

                        @Override
                        public Object call() throws Exception {
                            final Mesh m = f.getSelectedMesh();
                            if (m != null) {
                                Util.reverseFace(m);
                                f.getNodeState(m.getParent()).reverseNormalOfMesh(((UserData) m.getUserData()).getMeshIndex());
                                f.draw();
                                updateAfterEdit();
                            }
                            return null;
                        }
                    });
                }
            }
        });
        final JMenuItem miAlignBottom = new JMenuItem("Align Node Bottom with Ground Level");
        miAlignBottom.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                final HousePart selectedPart = SceneManager.getInstance().getSelectedPart();
                if (selectedPart instanceof Foundation) {
                    final Foundation f = (Foundation) selectedPart;
                    final Mesh m = f.getSelectedMesh();
                    if (m != null) {
                        SceneManager.getTaskManager().update(new Callable<Object>() {

                            @Override
                            public Object call() throws Exception {
                                final Node n = m.getParent();
                                if (n != null) {
                                    final OrientedBoundingBox boundingBox = Util.getOrientedBoundingBox(n);
                                    final double zBottom = boundingBox.getCenter().getZ() - boundingBox.getZAxis().getZ() * boundingBox.getExtent().getZ() - f.getHeight();
                                    f.translateImportedNode(n, 0, 0, -zBottom);
                                    f.draw();
                                    updateAfterEdit();
                                }
                                return null;
                            }
                        });
                    }
                }
            }
        });
        final JMenuItem miAlignCenter = new JMenuItem("Align Node Center with Foundation Center");
        miAlignCenter.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                final HousePart selectedPart = SceneManager.getInstance().getSelectedPart();
                if (selectedPart instanceof Foundation) {
                    final Foundation f = (Foundation) selectedPart;
                    final Mesh m = f.getSelectedMesh();
                    if (m != null) {
                        SceneManager.getTaskManager().update(new Callable<Object>() {

                            @Override
                            public Object call() throws Exception {
                                final Node n = m.getParent();
                                if (n != null) {
                                    final OrientedBoundingBox boundingBox = Util.getOrientedBoundingBox(n);
                                    final ReadOnlyVector3 shift = boundingBox.getCenter().subtract(f.getAbsCenter(), null);
                                    f.translateImportedNode(n, shift.getX(), shift.getY(), 0);
                                    f.setMeshSelectionVisible(false);
                                    f.draw();
                                    updateAfterEdit();
                                }
                                return null;
                            }
                        });
                    }
                }
            }
        });
        final JMenuItem miCopyNode = new JMenuItem("Copy Node");
        miCopyNode.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Config.isMac() ? KeyEvent.META_MASK : InputEvent.CTRL_MASK));
        miCopyNode.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                SceneManager.getTaskManager().update(new Callable<Object>() {

                    @Override
                    public Object call() throws Exception {
                        final HousePart selectedPart = SceneManager.getInstance().getSelectedPart();
                        if (selectedPart instanceof Foundation) {
                            final Foundation f = (Foundation) selectedPart;
                            final Mesh m = f.getSelectedMesh();
                            if (m != null) {
                                final Node n = m.getParent();
                                Scene.getInstance().setCopyNode(n, f.getNodeState(n));
                            }
                        }
                        return null;
                    }
                });
            }
        });
        final JMenuItem miPaste = new JMenuItem("Paste");
        miPaste.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, Config.isMac() ? KeyEvent.META_MASK : InputEvent.CTRL_MASK));
        miPaste.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                SceneManager.getTaskManager().update(new Callable<Object>() {

                    @Override
                    public Object call() throws Exception {
                        final HousePart selectedPart = SceneManager.getInstance().getSelectedPart();
                        if (selectedPart instanceof Foundation) {
                            final Foundation f = (Foundation) selectedPart;
                            final Mesh m = f.getSelectedMesh();
                            if (m != null) {
                                Scene.getInstance().pasteToPickedLocationOnMesh(m);
                                Scene.getInstance().setEdited(true);
                                updateAfterEdit();
                            }
                        }
                        return null;
                    }
                });
            }
        });
        popupMenuForMesh = new JPopupMenu();
        popupMenuForMesh.setInvoker(MainPanel.getInstance().getCanvasPanel());
        popupMenuForMesh.addPopupMenuListener(new PopupMenuListener() {

            @Override
            public void popupMenuWillBecomeVisible(final PopupMenuEvent e) {
                final HousePart selectedPart = SceneManager.getInstance().getSelectedPart();
                if (selectedPart instanceof Foundation) {
                    final Foundation f = (Foundation) selectedPart;
                    final Mesh m = f.getSelectedMesh();
                    if (m != null) {
                        String name = f.getNodeState(m.getParent()).getName();
                        if (name == null) {
                            name = "Undefined";
                        }
                        miInfo.setText(m.getName() + " (" + name + ")");
                        final OrientedBoundingBox boundingBox = Util.getOrientedBoundingBox(m.getParent());
                        final ReadOnlyVector3 center = boundingBox.getCenter();
                        final double zBottom = center.getZ() - boundingBox.getZAxis().getZ() * boundingBox.getExtent().getZ();
                        miAlignBottom.setEnabled(!Util.isZero(zBottom - f.getHeight()));
                        final Vector3 foundationCenter = f.getAbsCenter();
                        miAlignCenter.setEnabled(!Util.isEqual(new Vector2(foundationCenter.getX(), foundationCenter.getY()), new Vector2(center.getX(), center.getY())));
                        final HousePart copyBuffer = Scene.getInstance().getCopyBuffer();
                        miPaste.setEnabled(copyBuffer instanceof SolarPanel || copyBuffer instanceof Rack);
                    }
                }
            }

            @Override
            public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) {
                miAlignBottom.setEnabled(true);
                miAlignCenter.setEnabled(true);
            }

            @Override
            public void popupMenuCanceled(final PopupMenuEvent e) {
                miAlignBottom.setEnabled(true);
                miAlignCenter.setEnabled(true);
            }
        });
        final JMenuItem miDeleteMesh = new JMenuItem("Delete Mesh");
        miDeleteMesh.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                final HousePart selectedPart = SceneManager.getInstance().getSelectedPart();
                if (selectedPart instanceof Foundation) {
                    final Foundation f = (Foundation) selectedPart;
                    final Mesh m = f.getSelectedMesh();
                    if (m != null) {
                        SceneManager.getTaskManager().update(new Callable<Object>() {

                            @Override
                            public Object call() throws Exception {
                                f.deleteMesh(m);
                                updateAfterEdit();
                                return null;
                            }
                        });
                    }
                }
            }
        });
        final JMenuItem miRestoreDeletedMeshes = new JMenuItem("Restore Deleted Meshes (Reload Required)");
        miRestoreDeletedMeshes.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                final HousePart selectedPart = SceneManager.getInstance().getSelectedPart();
                if (selectedPart instanceof Foundation) {
                    final Foundation f = (Foundation) selectedPart;
                    final Mesh m = f.getSelectedMesh();
                    if (m != null) {
                        SceneManager.getTaskManager().update(new Callable<Object>() {

                            @Override
                            public Object call() throws Exception {
                                f.restoreDeletedMeshes(m.getParent());
                                updateAfterEdit();
                                return null;
                            }
                        });
                    }
                }
            }
        });
        final JMenuItem miCutNode = new JMenuItem("Cut Node");
        miCutNode.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, Config.isMac() ? KeyEvent.META_MASK : InputEvent.CTRL_MASK));
        miCutNode.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                final HousePart selectedPart = SceneManager.getInstance().getSelectedPart();
                if (selectedPart instanceof Foundation) {
                    final Foundation f = (Foundation) selectedPart;
                    final Mesh m = f.getSelectedMesh();
                    if (m != null) {
                        SceneManager.getTaskManager().update(new Callable<Object>() {

                            @Override
                            public Object call() throws Exception {
                                final Node n = m.getParent();
                                Scene.getInstance().setCopyNode(n, f.getNodeState(n));
                                f.deleteNode(n);
                                updateAfterEdit();
                                return null;
                            }
                        });
                    }
                }
            }
        });
        final JMenuItem miMeshProperties = new JMenuItem("Mesh Properties...");
        miMeshProperties.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                final HousePart selectedPart = SceneManager.getInstance().getSelectedPart();
                if (selectedPart instanceof Foundation) {
                    final Foundation f = (Foundation) selectedPart;
                    final Mesh m = f.getSelectedMesh();
                    if (m != null) {
                        final UserData ud = (UserData) m.getUserData();
                        final JPanel gui = new JPanel(new BorderLayout());
                        final String title = "<html>A mesh is a basic unit (e.g., a triangle or a line) of geometry of a structure.</html>";
                        gui.add(new JLabel(title), BorderLayout.NORTH);
                        final JPanel propertiesPanel = new JPanel(new SpringLayout());
                        propertiesPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
                        gui.add(propertiesPanel, BorderLayout.CENTER);
                        // index mode
                        JLabel label = new JLabel("Index Mode: ", JLabel.TRAILING);
                        propertiesPanel.add(label);
                        JTextField textField = new JTextField(m.getMeshData().getIndexMode(0) + "", 5);
                        textField.setEditable(false);
                        label.setLabelFor(textField);
                        propertiesPanel.add(textField);
                        // vertex count
                        label = new JLabel("Vertex Count: ", JLabel.TRAILING);
                        propertiesPanel.add(label);
                        textField = new JTextField(m.getMeshData().getVertexCount() + "", 5);
                        textField.setEditable(false);
                        label.setLabelFor(textField);
                        propertiesPanel.add(textField);
                        // normal
                        label = new JLabel("Normal Vector: ", JLabel.TRAILING);
                        propertiesPanel.add(label);
                        final ReadOnlyVector3 normal = ((UserData) m.getUserData()).getNormal();
                        textField = new JTextField("(" + threeDecimalsFormat.format(normal.getX()) + ", " + threeDecimalsFormat.format(normal.getY()) + ", " + threeDecimalsFormat.format(normal.getZ()) + "), relative", 5);
                        textField.setEditable(false);
                        label.setLabelFor(textField);
                        propertiesPanel.add(textField);
                        // color
                        label = new JLabel("Color: ", JLabel.TRAILING);
                        propertiesPanel.add(label);
                        final ReadOnlyColorRGBA rgb = m.getDefaultColor();
                        colorChooser.setColor(new Color(Math.round(rgb.getRed() * 255), Math.round(rgb.getGreen() * 255), Math.round(rgb.getBlue() * 255)));
                        label.setLabelFor(colorChooser);
                        propertiesPanel.add(colorChooser);
                        SpringUtilities.makeCompactGrid(propertiesPanel, 4, 2, 6, 6, 6, 6);
                        if (JOptionPane.showConfirmDialog(MainFrame.getInstance(), gui, "Mesh Properties: " + miInfo.getText(), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) {
                            final Color color = colorChooser.getColor();
                            m.clearRenderState(StateType.Texture);
                            m.setDefaultColor(new ColorRGBA(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, 1));
                            final NodeState ns = f.getNodeState(m.getParent());
                            ns.setMeshColor(ud.getMeshIndex(), m.getDefaultColor());
                            f.draw();
                        }
                    }
                }
            }
        });
        final JMenuItem miNodeProperties = new JMenuItem("Node Properties...");
        miNodeProperties.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                final HousePart selectedPart = SceneManager.getInstance().getSelectedPart();
                if (selectedPart instanceof Foundation) {
                    final Foundation f = (Foundation) selectedPart;
                    final Mesh m = f.getSelectedMesh();
                    if (m != null) {
                        final Node n = m.getParent();
                        if (n != null) {
                            final NodeState ns = f.getNodeState(n);
                            final JPanel gui = new JPanel(new BorderLayout());
                            final String title = "<html>A node contains a set of meshes that represent<br>the geometry of the structure.</html>";
                            gui.add(new JLabel(title), BorderLayout.NORTH);
                            final JPanel propertiesPanel = new JPanel(new SpringLayout());
                            propertiesPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
                            gui.add(propertiesPanel, BorderLayout.CENTER);
                            // name
                            JLabel label = new JLabel("Name: ", JLabel.TRAILING);
                            propertiesPanel.add(label);
                            final JTextField nameField = new JTextField(ns.getName(), 5);
                            label.setLabelFor(nameField);
                            propertiesPanel.add(nameField);
                            // name
                            label = new JLabel("File: ", JLabel.TRAILING);
                            propertiesPanel.add(label);
                            final JTextField fileField = new JTextField(Util.getFileName(ns.getSourceURL().getPath()), 5);
                            label.setLabelFor(fileField);
                            propertiesPanel.add(fileField);
                            // children count
                            label = new JLabel("Children: ", JLabel.TRAILING);
                            propertiesPanel.add(label);
                            final JTextField textField = new JTextField(n.getNumberOfChildren() + "", 5);
                            textField.setEditable(false);
                            label.setLabelFor(textField);
                            propertiesPanel.add(textField);
                            SpringUtilities.makeCompactGrid(propertiesPanel, 3, 2, 6, 6, 6, 6);
                            if (JOptionPane.showConfirmDialog(MainFrame.getInstance(), gui, "Node Properties", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) {
                                final String nodeName = nameField.getText();
                                if (nodeName != null && !nodeName.trim().equals("")) {
                                    n.setName(nodeName);
                                    f.getNodeState(n).setName(nodeName);
                                } else {
                                    JOptionPane.showMessageDialog(MainFrame.getInstance(), "Node must have a name!", "Name Error", JOptionPane.ERROR_MESSAGE);
                                }
                            }
                        }
                    }
                }
            }
        });
        popupMenuForMesh.add(miInfo);
        popupMenuForMesh.add(miCutNode);
        popupMenuForMesh.add(miPaste);
        popupMenuForMesh.add(miCopyNode);
        popupMenuForMesh.addSeparator();
        popupMenuForMesh.add(miAlignBottom);
        popupMenuForMesh.add(miAlignCenter);
        popupMenuForMesh.add(miMessThickness);
        popupMenuForMesh.add(miNodeProperties);
        popupMenuForMesh.addSeparator();
        popupMenuForMesh.add(miDeleteMesh);
        popupMenuForMesh.add(miReverseNormalVector);
        popupMenuForMesh.add(miRestoreDeletedMeshes);
        popupMenuForMesh.add(miMeshProperties);
    }
    return popupMenuForMesh;
}
Also used : JPanel(javax.swing.JPanel) ReadOnlyColorRGBA(com.ardor3d.math.type.ReadOnlyColorRGBA) NodeState(org.concord.energy3d.model.NodeState) UserData(org.concord.energy3d.model.UserData) ActionEvent(java.awt.event.ActionEvent) Node(com.ardor3d.scenegraph.Node) PopupMenuListener(javax.swing.event.PopupMenuListener) JTextField(javax.swing.JTextField) PopupMenuEvent(javax.swing.event.PopupMenuEvent) Callable(java.util.concurrent.Callable) Rack(org.concord.energy3d.model.Rack) BorderLayout(java.awt.BorderLayout) Foundation(org.concord.energy3d.model.Foundation) JMenuItem(javax.swing.JMenuItem) HousePart(org.concord.energy3d.model.HousePart) Color(java.awt.Color) Mesh(com.ardor3d.scenegraph.Mesh) JLabel(javax.swing.JLabel) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) Vector3(com.ardor3d.math.Vector3) JPopupMenu(javax.swing.JPopupMenu) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) OrientedBoundingBox(com.ardor3d.bounding.OrientedBoundingBox) ActionListener(java.awt.event.ActionListener) ReadOnlyColorRGBA(com.ardor3d.math.type.ReadOnlyColorRGBA) ColorRGBA(com.ardor3d.math.ColorRGBA) Vector2(com.ardor3d.math.Vector2) SolarPanel(org.concord.energy3d.model.SolarPanel) SpringLayout(javax.swing.SpringLayout)

Aggregations

UserData (org.concord.energy3d.model.UserData)17 ReadOnlyVector3 (com.ardor3d.math.type.ReadOnlyVector3)10 Foundation (org.concord.energy3d.model.Foundation)9 HousePart (org.concord.energy3d.model.HousePart)9 Vector3 (com.ardor3d.math.Vector3)8 Mesh (com.ardor3d.scenegraph.Mesh)8 Spatial (com.ardor3d.scenegraph.Spatial)6 Rack (org.concord.energy3d.model.Rack)6 SolarPanel (org.concord.energy3d.model.SolarPanel)6 Node (com.ardor3d.scenegraph.Node)5 PickedHousePart (org.concord.energy3d.model.PickedHousePart)5 OrientedBoundingBox (com.ardor3d.bounding.OrientedBoundingBox)4 CullHint (com.ardor3d.scenegraph.hint.CullHint)4 ArrayList (java.util.ArrayList)4 Roof (org.concord.energy3d.model.Roof)4 Window (org.concord.energy3d.model.Window)4 List (java.util.List)3 NodeState (org.concord.energy3d.model.NodeState)3 ParabolicTrough (org.concord.energy3d.model.ParabolicTrough)3 Wall (org.concord.energy3d.model.Wall)3