All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rinde.sim.pdptw.common.TimeLinePanel Maven / Gradle / Ivy

The newest version!
/**
 * 
 */
package rinde.sim.pdptw.common;

import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Lists.newArrayList;

import java.util.List;

import javax.annotation.Nullable;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.ScrollBar;

import rinde.sim.core.TickListener;
import rinde.sim.core.TimeLapse;
import rinde.sim.core.model.ModelProvider;
import rinde.sim.core.model.ModelReceiver;
import rinde.sim.core.model.pdp.PDPModel;
import rinde.sim.core.model.pdp.PDPModel.PDPModelEventType;
import rinde.sim.core.model.pdp.PDPModelEvent;
import rinde.sim.core.model.pdp.Parcel;
import rinde.sim.event.Event;
import rinde.sim.event.Listener;
import rinde.sim.ui.renderers.PanelRenderer;
import rinde.sim.util.TimeFormatter;

/**
 * Time line panel is an UI element that shows a real time visualization of
 * parcels and their time windows in a simulation.
 * @author Rinde van Lon 
 */
public final class TimeLinePanel implements ModelReceiver, PanelRenderer,
    TickListener {

  Canvas canvas;
  Canvas barCanvas;
  Point origin = new Point(0, 0);
  ScrollBar hBar;
  ScrollBar vBar;
  long currentTime = 0;
  Timeline timeline;

  public TimeLinePanel() {}

  @Override
  public void registerModelProvider(ModelProvider mp) {
    final PDPModel pdp = mp.getModel(PDPModel.class);
    checkState(pdp != null, "PDPModel is required.");
    pdp.getEventAPI().addListener(new Listener() {
      @Override
      public void handleEvent(Event e) {
        if (e.getEventType() == PDPModelEventType.NEW_PARCEL) {
          final PDPModelEvent event = (PDPModelEvent) e;
          timeline.addParcel(new ParcelInfo(event.time, event.parcel));
        }
      }
    }, PDPModelEventType.NEW_PARCEL);
  }

  @Override
  public void initializePanel(Composite parent) {
    final TimelineBar timelineBar = new TimelineBar(parent.getDisplay());
    timeline = new Timeline(parent.getDisplay());

    final GridLayout layout = new GridLayout(1, false);
    layout.marginHeight = 2;
    layout.marginWidth = 2;
    layout.verticalSpacing = 0;
    parent.setLayout(layout);
    barCanvas = new Canvas(parent, SWT.NONE);
    final GridData barData = new GridData(SWT.FILL, SWT.TOP, true, false);
    barData.minimumHeight = 22;
    barData.heightHint = 22;
    barCanvas.setLayoutData(barData);
    barCanvas.setSize(20, 20);
    barCanvas.addPaintListener(new PaintListener() {
      @Override
      public void paintControl(PaintEvent e) {
        timelineBar.update(timeline.getWidth());
        e.gc.drawImage(timelineBar.contents, origin.x, 0);
        e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_RED));
        e.gc.drawLine(origin.x + (int) (currentTime / timeline.timePerPixel),
            10, origin.x + (int) (currentTime / timeline.timePerPixel),
            barCanvas.getClientArea().height);
      }
    });
    canvas = new Canvas(parent, SWT.DOUBLE_BUFFERED | SWT.NONE | SWT.V_SCROLL
        | SWT.H_SCROLL);
    canvas.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    canvas.addPaintListener(new PaintListener() {

      @Override
      public void paintControl(PaintEvent e) {
        timeline.update();

        e.gc.drawImage(timeline.contents, origin.x, origin.y);

        e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_RED));
        e.gc.drawLine(origin.x + (int) (currentTime / timeline.timePerPixel),
            0, origin.x + (int) (currentTime / timeline.timePerPixel),
            canvas.getClientArea().height);

        hBar.setMaximum(timeline.getWidth() == 0 ? 1 : timeline.getWidth() + 20);
        vBar.setMaximum(timeline.getHeight() + 5);
        hBar.setThumb(Math.min(timeline.getWidth() + 20,
            canvas.getClientArea().width));
        vBar.setThumb(Math.min(timeline.getHeight() + 5,
            canvas.getClientArea().height));
      }
    });

    hBar = canvas.getHorizontalBar();
    hBar.setIncrement(2);
    hBar.setPageIncrement(20);
    hBar.addListener(SWT.Selection, new org.eclipse.swt.widgets.Listener() {
      @Override
      public void handleEvent(org.eclipse.swt.widgets.Event e) {
        final int hSelection = hBar.getSelection();
        final int destX = -hSelection - origin.x;
        canvas.scroll(destX, 0, 0, 0, timeline.getWidth(),
            timeline.getHeight(), false);
        barCanvas.scroll(destX, 0, 0, 0,
            timelineBar.contents.getBounds().width,
            timelineBar.contents.getBounds().height, false);
        origin.x = -hSelection;
      }
    });
    vBar = canvas.getVerticalBar();
    vBar.setIncrement(2);
    vBar.setPageIncrement(20);
    vBar.addListener(SWT.Selection, new org.eclipse.swt.widgets.Listener() {
      @Override
      public void handleEvent(@Nullable org.eclipse.swt.widgets.Event e) {
        final int vSelection = vBar.getSelection();
        final int destY = -vSelection - origin.y;
        canvas.scroll(0, destY, 0, 0, timeline.getWidth(),
            timeline.getHeight(), false);
        origin.y = -vSelection;
      }
    });
    canvas.redraw();
    barCanvas.redraw();
  }

  @Override
  public int preferredSize() {
    return 200;
  }

  @Override
  public int getPreferredPosition() {
    return SWT.TOP;
  }

  @Override
  public String getName() {
    return "Timeline";
  }

  @Override
  public void tick(TimeLapse timeLapse) {}

  @Override
  public void afterTick(TimeLapse timeLapse) {
    currentTime = timeLapse.getStartTime();
    if (canvas.isDisposed()) {
      return;
    }
    canvas.getDisplay().syncExec(new Runnable() {
      @Override
      public void run() {
        if (!canvas.isDisposed()) {
          canvas.redraw();
          barCanvas.redraw();
        }
      }
    });
  }

  static Image createNewTransparentImg(Display d, int w, int h) {
    final Color white = d.getSystemColor(SWT.COLOR_WHITE);
    final Color black = d.getSystemColor(SWT.COLOR_BLACK);
    final PaletteData palette = new PaletteData(new RGB[] { white.getRGB(),
        black.getRGB() });
    final ImageData sourceData = new ImageData(w, h, 1, palette);
    sourceData.transparentPixel = 0;
    return new Image(d, sourceData);
  }

  class ParcelInfo {
    final long eventTime;
    final Parcel parcel;

    ParcelInfo(long time, Parcel p) {
      eventTime = time;
      parcel = p;
    }
  }

  class TimelineBar {
    protected final Display display;
    protected Image contents;
    protected Font font;

    TimelineBar(Display d) {
      display = d;
      contents = createNewTransparentImg(display, 700, 20);
      font = new Font(display, "arial", 10, SWT.NORMAL);
      drawTimeline();
    }

    void update(int width) {
      if (contents.getBounds().width < width) {
        contents.dispose();
        contents = createNewTransparentImg(display, width + 30, 20);
        drawTimeline();
      }
    }

    void drawTimeline() {
      final GC gc = new GC(contents);
      gc.setAdvanced(true);
      // gc.setAntialias(SWT.ON);
      gc.setTextAntialias(SWT.OFF);

      final int large = (600000 / 15000);
      final int small = large / 5;

      for (int i = 0; i < contents.getBounds().width; i += small) {

        final int height = i % large == 0 ? 10 : 5;
        if (i % large == 0) {
          String time = TimeFormatter.format(15000 * i);
          time = time.substring(0, time.length() - 3);
          gc.setFont(font);
          final Point size = gc.textExtent(time);
          gc.drawText(time, i - (size.x / 2), 0);
        }

        gc.drawLine(i, 20 - height, i, 20);

      }
      gc.dispose();
    }
  }

  class Timeline {
    Display display;
    Image contents;
    List parcels;
    List newParcels;
    long timePerPixel = 15000;

    int rowHeight = 15;
    int height;
    int width;

    Color lineColor;
    Color pickupColor;
    Color deliveryColor;

    Timeline(Display d) {
      display = d;
      parcels = newArrayList();
      newParcels = newArrayList();
      lineColor = d.getSystemColor(SWT.COLOR_WIDGET_BORDER);
      pickupColor = d.getSystemColor(SWT.COLOR_BLUE);
      deliveryColor = d.getSystemColor(SWT.COLOR_DARK_RED);
    }

    void ensureImg() {
      if (contents == null) {
        contents = createNewTransparentImg(display, 700, 100);
      } else {
        final boolean widthViolation = width > contents.getBounds().width;
        final boolean heightViolation = height > contents.getBounds().height;
        if (widthViolation || heightViolation) {
          final int newWidth = Math.max(width, contents.getBounds().width)
              + (widthViolation ? (int) ((60 * 60000) / timePerPixel) : 0);
          final int newHeight = contents.getBounds().height
              + (heightViolation ? 10 * rowHeight : 0);

          final Image newContents = createNewTransparentImg(display, newWidth,
              newHeight);
          // copy previous image to new image
          final GC gc = new GC(newContents);
          gc.drawImage(contents, 0, 0);
          gc.dispose();
          contents.dispose();
          contents = newContents;
        }
      }
    }

    void addParcel(final ParcelInfo p) {
      newParcels.add(p);

      width = Math.max(width,
          (int) (p.parcel.getDeliveryTimeWindow().end / timePerPixel));
    }

    void update() {
      if (display == null) {
        return;
      }
      final int oldHeight = height;
      parcels.addAll(newParcels);
      height = parcels.size() * rowHeight;
      ensureImg();
      for (int i = 0; i < newParcels.size(); i++) {
        drawParcel(newParcels.get(i), oldHeight + (i * rowHeight));
      }
      newParcels.clear();
    }

    void drawParcel(ParcelInfo p, int y) {
      final int startX = (int) (p.eventTime / timePerPixel);
      final int startPickX = (int) (p.parcel.getPickupTimeWindow().begin / timePerPixel);
      final int endPickX = (int) (p.parcel.getPickupTimeWindow().end / timePerPixel);
      final int startDelX = (int) (p.parcel.getDeliveryTimeWindow().begin / timePerPixel);
      final int endDelX = (int) (p.parcel.getDeliveryTimeWindow().end / timePerPixel);

      final GC gc = new GC(contents);
      gc.setForeground(lineColor);
      gc.drawLine(startX, y + 1, startX, y + 13);
      gc.drawLine(startX, y + 8, startPickX, y + 8);

      gc.setBackground(pickupColor);
      gc.fillRectangle(startPickX, y + 2, endPickX - startPickX, 6);
      gc.drawRectangle(startPickX, y + 2, endPickX - startPickX, 6);

      gc.drawLine(endPickX, y + 8, startDelX, y + 8);

      gc.setBackground(deliveryColor);
      gc.fillRectangle(startDelX, y + 8, endDelX - startDelX, 6);
      gc.drawRectangle(startDelX, y + 8, endDelX - startDelX, 6);

      gc.dispose();
    }

    int getHeight() {
      return height;
    }

    int getWidth() {
      return width;
    }

    void dispose() {
      contents.dispose();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy