package context.apps.demos.homeactivity; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Polygon; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import context.arch.intelligibility.Explanation; import context.arch.intelligibility.expression.DNF; import context.arch.intelligibility.expression.Parameter; import context.arch.intelligibility.expression.Reason; import context.arch.intelligibility.hmm.HmmExplainer; import context.arch.intelligibility.presenters.Presenter; import context.arch.intelligibility.query.AltQuery; import context.arch.intelligibility.query.Query; import context.arch.intelligibility.query.WhatIfQuery; public class TimelineStatesPanel extends JPanel { private static final long serialVersionUID = 8300845514081563499L; public final DecimalFormat nf = (DecimalFormat)DecimalFormat.getInstance(); { nf.applyPattern("###########0"); } private StatePanel[] statePanels; public static int NUM_ACTIVITIES = 7; public static int SEQUENCE_LENGTH = 5; // T private ActivityEnactor enactor; private HmmExplainer explainer; private boolean editable; private JLabel averageLabel; Presenter<Void> presenter; @SuppressWarnings("serial") public TimelineStatesPanel(ActivityEnactor enactor, boolean editable) { this.enactor = enactor; this.explainer = (HmmExplainer)enactor.getExplainer(); this.editable = editable; setBorder(BorderFactory.createTitledBorder("Activities Timeline")); statePanels = new StatePanel[SEQUENCE_LENGTH]; for (int t = 0; t < SEQUENCE_LENGTH; t++) { statePanels[t] = new StatePanel(t); add(statePanels[t]); } setStates( new ArrayList<String>() {{ add("Undefined"); add("Undefined"); add("Undefined"); add("Undefined"); add("Undefined"); }}); setEvidences( new double[] { 0, 0, 0, 0, 0 } ); averageLabel = new JLabel("0.00%"); add(averageLabel); presenter = new Presenter<Void>(enactor) { public final DecimalFormat nf = (DecimalFormat)DecimalFormat.getInstance(); { nf.applyPattern("##0.000########"); } @SuppressWarnings("unchecked") @Override public Void render(Explanation explanation) { Query query = explanation.getQuery(); String question = query.getQuestion(); List<String> stateSeq = TimelineStatesPanel.this.enactor.getOutcomeValueSequence(); if (question == AltQuery.QUESTION_WHY_NOT) { String altOutcome = ((AltQuery)query).getAltOutcomeValue(); stateSeq = Arrays.asList(altOutcome.split(" ")); } else if (question == WhatIfQuery.QUESTION_WHAT_IF) { String whatifOutcome = (String) explanation.getContent().getFirstLiteral().getValue(); stateSeq = Arrays.asList(whatifOutcome.substring(1, whatifOutcome.length()-1).split(", ")); } System.out.println("stateSeq = " + stateSeq); setStates(stateSeq); // always show this setEvidences(new double[HomeModel.SEQUENCE_LENGTH]); if (question == Query.QUESTION_WHAT) { // really just add certainty double certainty = (Double) explainer.getCertaintyExplanation().getFirstLiteral().getValue(); System.out.println("Certainty = " + (certainty*100) + "%"); averageLabel.setText("Certainty = " + nf.format(certainty*100) + "%"); } else if (question == Query.QUESTION_WHY || question == AltQuery.QUESTION_WHY_NOT) { // why or why not DNF exp = explanation.getContent(); Reason whyExpl = exp.get(0); double[] transEvidences = new double[stateSeq.size()]; // System.out.println("whyExp = " + whyExp); for (int i = 0; i < transEvidences.length; i++) { transEvidences[i] = ((Parameter<Double>)whyExpl.get(i + 1)).getValue(); // skip first which is average } setEvidences(transEvidences); double average = ((Parameter<Double>)whyExpl.get(0)).getValue(); // first element is average averageLabel.setText("Average = " + nf.format(average)); } return null; } }; } public void setEvidences(double[] transitionEvidences) { for (int t = 0; t < statePanels.length; t++) { statePanels[t].setEvidence(transitionEvidences[t]); } } private List<String> states; public void setStates(List<String> states) { this.states = states; for (int t = 0; t < statePanels.length; t++) { String state = states.get(t); // System.out.println("state = " + state); statePanels[t].setState(state); } repaint(); } public List<String> getStates() { if (states == null) { // if not yet set, then generate What presenter.render( explainer.getExplanation( new Query(Query.QUESTION_WHAT, ActivityWidget.ACTIVITY, System.currentTimeMillis()))); } repaint(); return states; } private class StatePanel extends JPanel { private static final long serialVersionUID = -5318260705701330914L; public final Map<String, Icon> stateIcons = new LinkedHashMap<String, Icon>(); { stateIcons.put("LeaveHouse", new ImageIcon("demos/home-hmm/img/exit.png")); stateIcons.put("UseToilet", new ImageIcon("demos/home-hmm/img/toilet.png")); stateIcons.put("TakeShower", new ImageIcon("demos/home-hmm/img/shower.png")); stateIcons.put("GotoBed", new ImageIcon("demos/home-hmm/img/sleep.png")); stateIcons.put("Breakfast", new ImageIcon("demos/home-hmm/img/breakfast.png")); stateIcons.put("Dinner", new ImageIcon("demos/home-hmm/img/dinner.png")); stateIcons.put("GetDrink", new ImageIcon("demos/home-hmm/img/cup.png")); stateIcons.put("Undefined", new ImageIcon("demos/home-hmm/img/unknown.png")); }; public final Map<Icon, String> iconStates = new LinkedHashMap<Icon, String>(); { for (String key : stateIcons.keySet()) { iconStates.put(stateIcons.get(key), key); } } private JLabel transitionArrow; private JLabel stateLabel; private JComboBox stateComboBox; @SuppressWarnings("unused") Image arrowImg = new ImageIcon("demos/home-hmm/img/arrow-prob.png").getImage(); private double evidence = .4; // private String state; private boolean first; // if first, then it represents transition from start; i.e. prior probability private int t; public StatePanel(int t) { this.t = t; this.first = t == 0; transitionArrow = new JLabel() { private static final long serialVersionUID = -6006798250322379965L; public void paint(Graphics g) { int w = getWidth(), h = getHeight(); int h_2 = h/2; int thickness; int arrowThickness; if (evidence == 0) { arrowThickness = thickness = 0; } else { thickness = (int)(Math.log(evidence/3e7)*20 * h_2 * 2/3); // manually calibrated if (thickness <= 0) { arrowThickness = thickness = 1; } arrowThickness = thickness + 4; } Shape arrow = new Polygon( new int[] { 0, w*2/3, w*2/3, w, w*2/3, w*2/3, 0 }, new int[] { h_2 - thickness, h_2 - thickness, h_2 - arrowThickness, h_2, h_2 + arrowThickness, h_2 + thickness, h_2 + thickness, }, 7); Graphics2D g2 = (Graphics2D)g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); // to smooth images when scaled if (first) { g.setColor(FloorplanPanel.SENSOR_GRAY_LESS); } else { g.setColor(FloorplanPanel.SENSOR_BLUE_LESS); } g2.fill(arrow); g.setColor(FloorplanPanel.SENSOR_BLUE); g2.draw(arrow); // g2.drawImage(arrowImg, 0, 0, w, (int)(probability * h), this); } }; transitionArrow.setPreferredSize(new Dimension(40, 30)); stateLabel = new JLabel(); if (editable) { stateLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } stateComboBox = new JComboBox(stateIcons.values().toArray()); stateComboBox.setVisible(false); stateLabel.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent evt) { if (editable) { stateLabel.setVisible(false); stateComboBox.setVisible(true); } } }); stateComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (editable) { stateLabel.setVisible(true); stateLabel.setIcon((Icon)stateComboBox.getSelectedItem()); stateComboBox.setVisible(false); // also set for global array // System.out.println("stateComboBox.getSelectedIndex() = " + stateComboBox.getSelectedIndex()); states.set(StatePanel.this.t, ""+stateComboBox.getSelectedIndex()); } } }); add(transitionArrow); add(stateLabel); add(stateComboBox); } public void setEvidence(double transitionEvidence) { this.evidence = transitionEvidence; transitionArrow.setToolTipText(nf.format(evidence)); transitionArrow.repaint(); } public void setState(String activity) { try { int i = Integer.parseInt(activity); // sometimes it is numeric activity = enactor.getOutcomeValues().get(i); } catch (NumberFormatException e) { // leave activity = activity } stateLabel.setIcon(stateIcons.get(activity)); stateComboBox.setSelectedItem(stateLabel.getIcon()); stateLabel.setToolTipText(activity); // this.state = activity; } // public String getState() { // return state; // } } }