Search in sources :

Example 16 with OrientedBoundingBox

use of com.ardor3d.bounding.OrientedBoundingBox in project energy3d by concord-consortium.

the class PrintController method computePageDimension.

private void computePageDimension() {
    spaceBetweenParts = Scene.getInstance().areAnnotationsVisible() ? 3.0 : 0;
    double fromPageToWorldCoord;
    if (!isScaleToFit) {
        fromPageToWorldCoord = exactFromPageToWorldCoord / (Scene.getInstance().getAnnotationScale() / 10.0);
    } else {
        double maxWidth = 0;
        double maxHeight = 0;
        for (final HousePart printPart : printParts) {
            if (printPart.isPrintable()) {
                if (printPart instanceof Roof) {
                    for (final Spatial roofPartNode : ((Roof) printPart).getRoofPartsRoot().getChildren()) {
                        if (roofPartNode.getSceneHints().getCullHint() != CullHint.Always) {
                            final OrientedBoundingBox boundingBox = (OrientedBoundingBox) ((Node) roofPartNode).getChild(0).getWorldBound().asType(Type.OBB);
                            final double width = Math.min(boundingBox.getExtent().getX(), boundingBox.getExtent().getZ());
                            final double height = Math.max(boundingBox.getExtent().getX(), boundingBox.getExtent().getZ());
                            if (width > maxWidth) {
                                maxWidth = width;
                            }
                            if (height > maxHeight) {
                                maxHeight = height;
                            }
                        }
                    }
                } else {
                    final OrientedBoundingBox boundingBox = (OrientedBoundingBox) printPart.getMesh().getWorldBound().asType(Type.OBB);
                    final double width = Math.min(boundingBox.getExtent().getX(), boundingBox.getExtent().getZ());
                    final double height = Math.max(boundingBox.getExtent().getX(), boundingBox.getExtent().getZ());
                    if (width > maxWidth) {
                        maxWidth = width;
                    }
                    if (height > maxHeight) {
                        maxHeight = height;
                    }
                }
            }
        }
        maxWidth *= 2;
        maxHeight *= 2;
        maxWidth += 2 * spaceBetweenParts;
        maxHeight += 2 * spaceBetweenParts;
        final double ratio = pageFormat.getImageableWidth() / pageFormat.getImageableHeight();
        if (maxWidth / maxHeight > ratio) {
            pageWidth = ratio < 1 ? Math.min(maxWidth, maxHeight) : Math.max(maxWidth, maxHeight);
            pageHeight = pageWidth / ratio;
        } else {
            pageHeight = ratio < 1 ? Math.max(maxWidth, maxHeight) : Math.min(maxWidth, maxHeight);
            pageWidth = pageHeight * ratio;
        }
        fromPageToWorldCoord = pageWidth / pageFormat.getImageableWidth();
    }
    pageLeft = pageFormat.getImageableX() * fromPageToWorldCoord + spaceBetweenParts / 2.0;
    pageRight = (pageFormat.getImageableX() + pageFormat.getImageableWidth()) * fromPageToWorldCoord - spaceBetweenParts / 2.0;
    pageTop = pageFormat.getImageableY() * fromPageToWorldCoord + spaceBetweenParts / 2.0;
    if (labelHeight == 0.0) {
        final BMText label = Annotation.makeNewLabel(1);
        label.setFontScale(0.5);
        labelHeight = label.getHeight();
    }
    pageBottom = (pageFormat.getImageableY() + pageFormat.getImageableHeight()) * fromPageToWorldCoord;
    pageWidth = pageFormat.getWidth() * fromPageToWorldCoord;
    pageHeight = pageFormat.getHeight() * fromPageToWorldCoord;
}
Also used : Roof(org.concord.energy3d.model.Roof) OrientedBoundingBox(com.ardor3d.bounding.OrientedBoundingBox) Spatial(com.ardor3d.scenegraph.Spatial) BMText(com.ardor3d.ui.text.BMText) HousePart(org.concord.energy3d.model.HousePart)

Example 17 with OrientedBoundingBox

use of com.ardor3d.bounding.OrientedBoundingBox in project energy3d by concord-consortium.

the class PrintController method fitInPage.

private boolean fitInPage(final Spatial printPart, final ArrayList<Spatial> page) {
    for (final Spatial neighborPart : page) {
        final Vector3 neighborPartCenter = ((UserData) neighborPart.getUserData()).getPrintCenter();
        final OrientedBoundingBox neighborBound = (OrientedBoundingBox) neighborPart.getWorldBound().asType(Type.OBB);
        final OrientedBoundingBox printPartBound = (OrientedBoundingBox) printPart.getWorldBound().asType(Type.OBB);
        final double xExtend = neighborBound.getExtent().getX() + printPartBound.getExtent().getX() + spaceBetweenParts;
        final double zExtend = neighborBound.getExtent().getZ() + printPartBound.getExtent().getZ() + spaceBetweenParts;
        for (double angleQuarter = 0; angleQuarter < 4; angleQuarter++) {
            final boolean isHorizontal = angleQuarter % 2 == 0;
            final Vector3 tryCenter = new Matrix3().fromAngles(0, angleQuarter * Math.PI / 2.0, 0).applyPost(new Vector3(isHorizontal ? xExtend : zExtend, 0, 0), null);
            tryCenter.addLocal(neighborPartCenter);
            if (!isHorizontal) {
                tryCenter.setX(pageLeft + printPartBound.getExtent().getX());
            }
            if (!isHorizontal) {
                tryCenter.setX(MathUtils.clamp(tryCenter.getX(), pageLeft + printPartBound.getExtent().getX(), pageRight - printPartBound.getExtent().getX()));
            } else {
                tryCenter.setZ(MathUtils.clamp(tryCenter.getZ(), -pageBottom + printPartBound.getExtent().getZ(), -pageTop - printPartBound.getExtent().getZ()));
            }
            tryCenter.setY(Scene.getOriginalHouseRoot().getWorldBound().getCenter().getY());
            boolean collision = false;
            if (tryCenter.getX() - printPartBound.getExtent().getX() < pageLeft - MathUtils.ZERO_TOLERANCE || tryCenter.getX() + printPartBound.getExtent().getX() > pageRight + MathUtils.ZERO_TOLERANCE || tryCenter.getZ() + printPartBound.getExtent().getZ() > -pageTop + MathUtils.ZERO_TOLERANCE || tryCenter.getZ() - printPartBound.getExtent().getZ() < -pageBottom - MathUtils.ZERO_TOLERANCE) {
                collision = true;
            } else {
                for (final Spatial otherPart : page) {
                    printPartBound.setCenter(tryCenter);
                    final OrientedBoundingBox otherPartBound = (OrientedBoundingBox) otherPart.getWorldBound().asType(Type.OBB);
                    otherPartBound.setCenter(((UserData) otherPart.getUserData()).getPrintCenter());
                    if (printPartBound.getExtent().getX() + otherPartBound.getExtent().getX() > Math.abs(printPartBound.getCenter().getX() - otherPartBound.getCenter().getX()) - spaceBetweenParts + MathUtils.ZERO_TOLERANCE && printPartBound.getExtent().getZ() + otherPartBound.getExtent().getZ() > Math.abs(printPartBound.getCenter().getZ() - otherPartBound.getCenter().getZ()) - spaceBetweenParts + MathUtils.ZERO_TOLERANCE) {
                        collision = true;
                        break;
                    }
                }
            }
            if (!collision) {
                ((UserData) printPart.getUserData()).setPrintCenter(tryCenter);
                page.add(printPart);
                return true;
            }
        }
    }
    return false;
}
Also used : OrientedBoundingBox(com.ardor3d.bounding.OrientedBoundingBox) Spatial(com.ardor3d.scenegraph.Spatial) UserData(org.concord.energy3d.model.UserData) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) Vector3(com.ardor3d.math.Vector3) Matrix3(com.ardor3d.math.Matrix3)

Example 18 with OrientedBoundingBox

use of com.ardor3d.bounding.OrientedBoundingBox in project energy3d by concord-consortium.

the class Util method getOrientedBoundingBox.

public static OrientedBoundingBox getOrientedBoundingBox(final Node node) {
    int count = 0;
    for (final Spatial s : node.getChildren()) {
        if (s instanceof Mesh) {
            final Mesh m = (Mesh) s;
            count += m.getMeshData().getVertexBuffer().limit();
        }
    }
    final FloatBuffer newbuf = BufferUtils.createFloatBuffer(count);
    for (final Spatial s : node.getChildren()) {
        if (s instanceof Mesh) {
            final Mesh m = (Mesh) s;
            final FloatBuffer buf = m.getMeshData().getVertexBuffer();
            buf.rewind();
            while (buf.hasRemaining()) {
                final Vector3 v = new Vector3(buf.get(), buf.get(), buf.get());
                m.getWorldTransform().applyForward(v);
                newbuf.put(v.getXf()).put(v.getYf()).put(v.getZf());
            }
        }
    }
    final OrientedBoundingBox boundingBox = new OrientedBoundingBox();
    boundingBox.computeFromPoints(newbuf);
    boundingBox.transform(node.getWorldTransform().invert(null), node.getWorldBound());
    // node.updateWorldBound(true);
    return boundingBox;
}
Also used : OrientedBoundingBox(com.ardor3d.bounding.OrientedBoundingBox) Spatial(com.ardor3d.scenegraph.Spatial) Mesh(com.ardor3d.scenegraph.Mesh) FloatBuffer(java.nio.FloatBuffer) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) Vector3(com.ardor3d.math.Vector3) PickingHint(com.ardor3d.scenegraph.hint.PickingHint) Point(org.poly2tri.geometry.primitives.Point)

Example 19 with OrientedBoundingBox

use of com.ardor3d.bounding.OrientedBoundingBox in project energy3d by concord-consortium.

the class Util method getOrientedBoundingBox.

public static OrientedBoundingBox getOrientedBoundingBox(final Mesh mesh) {
    final FloatBuffer buf = mesh.getMeshData().getVertexBuffer();
    buf.rewind();
    final FloatBuffer newbuf = BufferUtils.createFloatBuffer(buf.limit());
    while (buf.hasRemaining()) {
        final Vector3 v = new Vector3(buf.get(), buf.get(), buf.get());
        mesh.getWorldTransform().applyForward(v);
        newbuf.put(v.getXf()).put(v.getYf()).put(v.getZf());
    }
    final OrientedBoundingBox boundingBox = new OrientedBoundingBox();
    boundingBox.computeFromPoints(newbuf);
    boundingBox.transform(mesh.getWorldTransform().invert(null), mesh.getModelBound());
    mesh.updateWorldBound(true);
    return boundingBox;
}
Also used : OrientedBoundingBox(com.ardor3d.bounding.OrientedBoundingBox) FloatBuffer(java.nio.FloatBuffer) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) Vector3(com.ardor3d.math.Vector3)

Example 20 with OrientedBoundingBox

use of com.ardor3d.bounding.OrientedBoundingBox 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)

Aggregations

OrientedBoundingBox (com.ardor3d.bounding.OrientedBoundingBox)24 Mesh (com.ardor3d.scenegraph.Mesh)9 Node (com.ardor3d.scenegraph.Node)8 BMText (com.ardor3d.ui.text.BMText)8 ColorRGBA (com.ardor3d.math.ColorRGBA)7 ReadOnlyVector3 (com.ardor3d.math.type.ReadOnlyVector3)7 Line (com.ardor3d.scenegraph.Line)7 Vector3 (com.ardor3d.math.Vector3)6 BoundingBox (com.ardor3d.bounding.BoundingBox)5 OffsetState (com.ardor3d.renderer.state.OffsetState)5 Box (com.ardor3d.scenegraph.shape.Box)5 ReadOnlyColorRGBA (com.ardor3d.math.type.ReadOnlyColorRGBA)4 Spatial (com.ardor3d.scenegraph.Spatial)4 CullHint (com.ardor3d.scenegraph.hint.CullHint)4 UserData (org.concord.energy3d.model.UserData)4 Cylinder (com.ardor3d.scenegraph.shape.Cylinder)3 FloatBuffer (java.nio.FloatBuffer)3 HousePart (org.concord.energy3d.model.HousePart)3 CullState (com.ardor3d.renderer.state.CullState)2 Foundation (org.concord.energy3d.model.Foundation)2