Package com.cburch.draw.shapes

Source Code of com.cburch.draw.shapes.Poly

/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

package com.cburch.draw.shapes;

import java.awt.Graphics;
import java.awt.geom.GeneralPath;
import java.util.List;
import java.util.Random;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.cburch.draw.model.CanvasObject;
import com.cburch.draw.model.Handle;
import com.cburch.draw.model.HandleGesture;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.util.UnmodifiableList;

public class Poly extends FillableCanvasObject {
  private boolean closed;
  // "handles" should be immutable - create a new array and change using
  // setHandles rather than changing contents
  private Handle[] handles;
  private GeneralPath path;
  private double[] lens;
  private Bounds bounds;
 
  public Poly(boolean closed, List<Location> locations) {
    Handle[] hs = new Handle[locations.size()];
    int i = -1;
    for (Location loc : locations) {
      i++;
      hs[i] = new Handle(this, loc.getX(), loc.getY());
    }
   
    this.closed = closed;
    handles = hs;
    recomputeBounds();
  }
 
  @Override
  public boolean matches(CanvasObject other) {
    if (other instanceof Poly) {
      Poly that = (Poly) other;
      Handle[] a = this.handles;
      Handle[] b = that.handles;
      if (this.closed != that.closed || a.length != b.length) {
        return false;
      } else {
        for (int i = 0, n = a.length; i < n; i++) {
          if (!a[i].equals(b[i])) return false;
        }
        return super.matches(that);
      }
    } else {
      return false;
    }
  }
 
  @Override
  public int matchesHashCode() {
    int ret = super.matchesHashCode();
    ret = ret * 3 + (closed ? 1 : 0);
    Handle[] hs = handles;
    for (int i = 0, n = hs.length; i < n; i++) {
      ret = ret * 31 + hs[i].hashCode();
    }
    return ret;
  }
 
  @Override
  public String getDisplayName() {
    if (closed) {
      return Strings.get("shapePolygon");
    } else {
      return Strings.get("shapePolyline");
    }
  }
 
  @Override
  public Element toSvgElement(Document doc) {
    return SvgCreator.createPoly(doc, this);
  }

  @Override
  public List<Attribute<?>> getAttributes() {
    return DrawAttr.getFillAttributes(getPaintType());
  }
 
  @Override
  public final boolean contains(Location loc, boolean assumeFilled) {
    Object type = getPaintType();
    if (assumeFilled && type == DrawAttr.PAINT_STROKE) {
      type = DrawAttr.PAINT_STROKE_FILL;
    }
    if (type == DrawAttr.PAINT_STROKE) {
      int thresh = Math.max(Line.ON_LINE_THRESH, getStrokeWidth() / 2);
      PolyUtil.ClosestResult result = PolyUtil.getClosestPoint(loc,
          closed, handles);
      return result.getDistanceSq() < thresh * thresh;
    } else if (type == DrawAttr.PAINT_FILL) {
      GeneralPath path = getPath();
      return path.contains(loc.getX(), loc.getY());
    } else { // fill and stroke
      GeneralPath path = getPath();
      if (path.contains(loc.getX(), loc.getY())) return true;
      int width = getStrokeWidth();
      PolyUtil.ClosestResult result = PolyUtil.getClosestPoint(loc,
          closed, handles);
      return result.getDistanceSq() < (width * width) / 4;
    }
  }
 
  @Override
  public final Location getRandomPoint(Bounds bds, Random rand) {
    if (getPaintType() == DrawAttr.PAINT_STROKE) {
      Location ret = getRandomBoundaryPoint(bds, rand);
      int w = getStrokeWidth();
      if (w > 1) {
        int dx = rand.nextInt(w) - w / 2;
        int dy = rand.nextInt(w) - w / 2;
        ret = ret.translate(dx, dy);
      }
      return ret;
    } else {
      return super.getRandomPoint(bds, rand);
    }
  }

  private Location getRandomBoundaryPoint(Bounds bds, Random rand) {
    Handle[] hs = handles;
    double[] ls = lens;
    if (ls == null) {
      ls = new double[hs.length + (closed ? 1 : 0)];
      double total = 0.0;
      for (int i = 0; i < ls.length; i++) {
        int j = (i + 1) % hs.length;
        total += LineUtil.distance(hs[i].getX(), hs[i].getY(),
            hs[j].getX(), hs[j].getY());
        ls[i] = total;
      }
      lens = ls;
    }
    double pos = ls[ls.length - 1] * rand.nextDouble();
    for (int i = 0; true; i++) {
      if (pos < ls[i]) {
        Handle p = hs[i];
        Handle q = hs[(i + 1) % hs.length];
        double u = Math.random();
        int x = (int) Math.round(p.getX() + u * (q.getX() - p.getX()));
        int y = (int) Math.round(p.getY() + u * (q.getY() - p.getY()));
        return Location.create(x, y);
      }
    }
  }
 
  @Override
  public Bounds getBounds() {
    return bounds;
  }
 
  @Override
  public void translate(int dx, int dy) {
    Handle[] hs = handles;
    Handle[] is = new Handle[hs.length];
    for(int i = 0; i < hs.length; i++) {
      is[i] = new Handle(this, hs[i].getX() + dx, hs[i].getY() + dy);
    }
    setHandles(is);
  }
 
  public boolean isClosed() {
    return closed;
  }
 
  @Override
  public List<Handle> getHandles(HandleGesture gesture) {
    Handle[] hs = handles;
    if (gesture == null) {
      return UnmodifiableList.create(hs);
    } else {
      Handle g = gesture.getHandle();
      Handle[] ret = new Handle[hs.length];
      for (int i = 0, n = hs.length; i < n; i++) {
        Handle h = hs[i];
        if (h.equals(g)) {
          int x = h.getX() + gesture.getDeltaX();
          int y = h.getY() + gesture.getDeltaY();
          Location r;
          if (gesture.isShiftDown()) {
            Location prev = hs[(i + n - 1) % n].getLocation();
            Location next = hs[(i + 1) % n].getLocation();
            if (!closed) {
              if (i == 0) prev = null;
              if (i == n - 1) next = null;
            }
            if (prev == null) {
              r = LineUtil.snapTo8Cardinals(next, x, y);
            } else if (next == null) {
              r = LineUtil.snapTo8Cardinals(prev, x, y);
            } else {
              Location to = Location.create(x, y);
              Location a = LineUtil.snapTo8Cardinals(prev, x, y);
              Location b = LineUtil.snapTo8Cardinals(next, x, y);
              int ad = a.manhattanDistanceTo(to);
              int bd = b.manhattanDistanceTo(to);
              r = ad < bd ? a : b;
            }
          } else {
            r = Location.create(x, y);
          }
          ret[i] = new Handle(this, r);
        } else {
          ret[i] = h;
        }
      }
      return UnmodifiableList.create(ret);
    }
  }
 
  @Override
  public boolean canMoveHandle(Handle handle) {
    return true;
  }

  @Override
  public Handle moveHandle(HandleGesture gesture) {
    List<Handle> hs = getHandles(gesture);
    Handle[] is = new Handle[hs.size()];
    Handle ret = null;
    int i = -1;
    for (Handle h : hs) {
      i++;
      is[i] = h;
    }
    setHandles(is);
    return ret;
  }
 
  @Override
  public Handle canInsertHandle(Location loc) {
    PolyUtil.ClosestResult result = PolyUtil.getClosestPoint(loc, closed,
        handles);
    int thresh = Math.max(Line.ON_LINE_THRESH, getStrokeWidth() / 2);
    if (result.getDistanceSq() < thresh * thresh) {
      Location resLoc = result.getLocation();
      if (result.getPreviousHandle().isAt(resLoc)
          || result.getNextHandle().isAt(resLoc)) {
        return null;
      } else {
        return new Handle(this, result.getLocation());
      }
    } else {
      return null;
    }
  }
 
  @Override
  public Handle canDeleteHandle(Location loc) {
    int minHandles = closed ? 3 : 2;
    Handle[] hs = handles;
    if (hs.length <= minHandles) {
      return null;
    } else {
      int qx = loc.getX();
      int qy = loc.getY();
      int w = Math.max(Line.ON_LINE_THRESH, getStrokeWidth() / 2);
      for (Handle h : hs) {
        int hx = h.getX();
        int hy = h.getY();
        if (LineUtil.distance(qx, qy, hx, hy) < w * w) {
          return h;
        }
      }
      return null;
    }
  }
 
  @Override
  public void insertHandle(Handle desired, Handle previous) {
    Location loc = desired.getLocation();
    Handle[] hs = handles;
    Handle prev;
    if (previous == null) {
      PolyUtil.ClosestResult result = PolyUtil.getClosestPoint(loc,
          closed, hs);
      prev = result.getPreviousHandle();
    } else {
      prev = previous;
    }
    Handle[] is = new Handle[hs.length + 1];
    boolean inserted = false;
    for(int i = 0; i < hs.length; i++) {
      if (inserted) {
        is[i + 1] = hs[i];
      } else if (hs[i].equals(prev)) {
        inserted = true;
        is[i] = hs[i];
        is[i + 1] = desired;
      } else {
        is[i] = hs[i];
      }
    }
    if (!inserted) {
      throw new IllegalArgumentException("no such handle");
    }
    setHandles(is);
  }
 
  @Override
  public Handle deleteHandle(Handle handle) {
    Handle[] hs = handles;
    int n = hs.length;
    Handle[] is = new Handle[n - 1];
    Handle previous = null;
    boolean deleted = false;
    for (int i = 0; i < n; i++) {
      if (deleted) {
        is[i - 1] = hs[i];
      } else if (hs[i].equals(handle)) {
        if (previous == null) {
          previous = hs[n - 1];
        }
        deleted = true;
      } else {
        previous = hs[i];
        is[i] = hs[i];
      }
    }
    setHandles(is);
    return previous;
  }
 
  @Override
  public void paint(Graphics g, HandleGesture gesture) {
    List<Handle> hs = getHandles(gesture);
    int[] xs = new int[hs.size()];
    int[] ys = new int[hs.size()];
    int i = -1;
    for (Handle h : hs) {
      i++;
      xs[i] = h.getX();
      ys[i] = h.getY();
    }

    if (setForFill(g)) {
      g.fillPolygon(xs, ys, xs.length);
    }
    if (setForStroke(g)) {
      if (closed) g.drawPolygon(xs, ys, xs.length);
      else g.drawPolyline(xs, ys, xs.length);
    }
  }

  private void setHandles(Handle[] hs) {
    handles = hs;
    lens = null;
    path = null;
    recomputeBounds();
  }

  private void recomputeBounds() {
    Handle[] hs = handles;
    int x0 = hs[0].getX();
    int y0 = hs[0].getY();
    int x1 = x0;
    int y1 = y0;
    for(int i = 1; i < hs.length; i++) {
      int x = hs[i].getX();
      int y = hs[i].getY();
      if (x < x0) x0 = x;
      if (x > x1) x1 = x;
      if (y < y0) y0 = y;
      if (y > y1) y1 = y;
    }
    Bounds bds = Bounds.create(x0, y0, x1 - x0 + 1, y1 - y0 + 1);
    int stroke = getStrokeWidth();
    bounds = stroke < 2 ? bds : bds.expand(stroke / 2);
  }

  private GeneralPath getPath() {
    GeneralPath p = path;
    if (p == null) {
      p = new GeneralPath();
      Handle[] hs = handles;
      if (hs.length > 0) {
        boolean first = true;
        for (Handle h : hs) {
          if (first) {
            p.moveTo(h.getX(), h.getY());
            first = false;
          } else {
            p.lineTo(h.getX(), h.getY());
          }
        }
      }
      path = p;
    }
    return p;
  }
}
TOP

Related Classes of com.cburch.draw.shapes.Poly

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.