Example #1
0
  /**
   * Run through a GSG expression looking at its leaves and return a list of the distinct leaves.
   * Note: leaf and leaf.complement() are not considered distinct. Recursive internal call.
   *
   * @param expression
   * @return
   */
  private void uniqueList_r(ArrayList<RrCSG> list) {
    switch (op) {
      case LEAF:
        RrCSG entry;
        for (int i = 0; i < list.size(); i++) {
          entry = list.get(i);
          if (this == entry || complement() == entry) return;
        }
        list.add(this);
        break;

      case NULL:
      case UNIVERSE:
        Debug.e("uniqueList_r: null or universe at a leaf.");
        break;

      case UNION:
      case INTERSECTION:
        c1.uniqueList_r(list);
        c2.uniqueList_r(list);
        break;

      default:
        Debug.e("uniqueList_r: invalid operator.");
    }

    return;
  }
  public BooleanGridList offset(LayerRules lc, boolean outline) {
    boolean foundation = lc.getLayingSupport();
    if (outline && foundation) Debug.e("Offsetting a foundation outline!");

    BooleanGridList result = new BooleanGridList();
    for (int i = 0; i < size(); i++) {
      Attributes att = attribute(i);
      if (att == null) Debug.e("BooleanGridList.offset(): null attribute!");
      else {
        Extruder[] es = lc.getPrinter().getExtruders();
        Extruder e;
        int shells;
        if (foundation) {
          e = es[0]; // By convention extruder 0 builds the foundation
          shells = 1;
        } else {
          e = att.getExtruder();
          shells = e.getShells();
        }
        if (outline) {
          for (int shell = 0; shell < shells; shell++)
            result.add(get(i).offset(-((double) shell + 0.5) * e.getExtrusionSize()), att);
        } else {
          // Must be a hatch.  Only do it if the gap is +ve or we're building the foundation
          double offSize;
          if (foundation) offSize = 3;
          else offSize = -((double) shells + 0.5) * e.getExtrusionSize() + e.getInfillOverlap();
          if (e.getExtrusionInfillWidth() > 0 || foundation) // Z valuesn't mattere here
          result.add(get(i).offset(offSize), att);
        }
      }
    }
    return result;
  }
Example #3
0
  /**
   * Prune the set to a box
   *
   * @param b
   * @return pruned box as new CSG object
   */
  public RrCSG prune(RrRectangle b) {
    RrCSG result = this;

    switch (op) {
      case LEAF:
        RrInterval i = hp.value(b);
        if (i.empty()) Debug.e("RrCSG.prune(RrBox): empty interval!");
        else if (i.neg()) result = universe();
        else if (i.pos()) result = nothing();
        break;

      case NULL:
      case UNIVERSE:
        break;

      case UNION:
        result = union(c1.prune(b), c2.prune(b));
        break;

      case INTERSECTION:
        result = intersection(c1.prune(b), c2.prune(b));
        break;

      default:
        Debug.e("RrCSG.prune(RrBox): dud op value!");
    }

    return result;
  }
Example #4
0
  /**
   * Convert to a string
   *
   * @param result
   * @param white
   * @return string representation
   */
  private String toString_r(String result, String white) {
    switch (op) {
      case LEAF:
        result = result + white + hp.toString() + "\n";
        break;

      case NULL:
        result = result + white + "0\n";
        break;

      case UNIVERSE:
        result = result + white + "U\n";
        break;

      case UNION:
        result = result + white + "+\n";
        white = white + " ";
        result = c1.toString_r(result, white);
        result = c2.toString_r(result, white);
        break;

      case INTERSECTION:
        result = result + white + "&\n";
        white = white + " ";
        result = c1.toString_r(result, white);
        result = c2.toString_r(result, white);
        break;

      default:
        Debug.e("toString_r(): invalid operator.");
    }
    return result;
  }
Example #5
0
  /**
   * The interval value of a box (analagous to point)
   *
   * @param b
   * @return value of a box
   */
  public RrInterval value(RrRectangle b) {
    RrInterval result;

    switch (op) {
      case LEAF:
        result = hp.value(b);
        break;

      case NULL:
        result = new RrInterval(1, 1.01); // Is this clever?  Or dumb?
        break;

      case UNIVERSE:
        result = new RrInterval(-1.01, -1); // Ditto.
        break;

      case UNION:
        result = RrInterval.min(c1.value(b), c2.value(b));
        break;

      case INTERSECTION:
        result = RrInterval.max(c1.value(b), c2.value(b));
        break;

      default:
        Debug.e("value(RrBox): invalid operator.");
        result = new RrInterval();
    }

    return result;
  }
Example #6
0
  /**
   * "Potential" value of a point; i.e. a membership test -ve means inside; 0 means on the surface;
   * +ve means outside
   *
   * @param p
   * @return potential value of a point
   */
  public double value(Rr2Point p) {
    double result = 1;
    //		RrCSG c = leaf(p);
    switch (op) {
      case LEAF:
        result = hp.value(p);
        break;

      case NULL:
        result = 1;
        break;

      case UNIVERSE:
        result = -1;
        break;

      case UNION:
        result = Math.min(c1.value(p), c2.value(p));
        break;

      case INTERSECTION:
        result = Math.max(c1.value(p), c2.value(p));
        break;

      default:
        Debug.e("RrCSG.value(): dud operator.");
    }
    return result;
  }
Example #7
0
  /**
   * leaf find the half-plane that generates the value for a point
   *
   * @param p
   * @return leaf?
   */
  public RrCSG leaf(Rr2Point p) {
    RrCSG result, r1, r2;

    switch (op) {
      case LEAF:
        result = this;
        break;

      case NULL:
        result = this;
        break;

      case UNIVERSE:
        result = this;
        break;

      case UNION:
        r1 = c1.leaf(p);
        r2 = c2.leaf(p);
        if (r1.value(p) < r2.value(p)) return r1;
        else return r2;

      case INTERSECTION:
        r1 = c1.leaf(p);
        r2 = c2.leaf(p);
        if (r1.value(p) > r2.value(p)) return r1;
        else return r2;

      default:
        Debug.e("leaf(Rr2Point): invalid operator.");
        result = nothing();
    }
    return result;
  }
Example #8
0
  /**
   * Offset by a distance (+ve or -ve) TODO: this should keep track of complements
   *
   * @param d
   * @return offset CSG object by distance d
   */
  public RrCSG offset(double d) {
    RrCSG result;

    switch (op) {
      case LEAF:
        result = new RrCSG(hp.offset(d));
        break;

      case NULL:
      case UNIVERSE:
        result = this;
        break;

      case UNION:
        result = union(c1.offset(d), c2.offset(d));
        break;

      case INTERSECTION:
        result = intersection(c1.offset(d), c2.offset(d));
        break;

      default:
        Debug.e("offset(): invalid operator.");
        result = nothing();
    }
    return result;
  }
Example #9
0
  /**
   * Replace duplicate of leaf with leaf itself TODO: this should also use known complements
   *
   * @param leaf
   * @param tolerance
   */
  private void replaceAllSameLeaves(RrCSG leaf, double tolerance) {
    int same;
    switch (op) {
      case LEAF:
      case NULL:
      case UNIVERSE:
        // System.out.println("replace_all_same_leaves(): at a leaf!");
        break;

      case UNION:
      case INTERSECTION:
        RrHalfPlane hp = leaf.hp;
        if (c1.op == RrCSGOp.LEAF) {
          same = RrHalfPlane.same(hp, c1.hp, tolerance);
          if (same == 0) c1 = leaf;
          if (same == -1) c1 = leaf.complement();
        } else c1.replaceAllSameLeaves(leaf, tolerance);

        if (c2.op == RrCSGOp.LEAF) {
          same = RrHalfPlane.same(hp, c2.hp, tolerance);
          if (same == 0) c2 = leaf;
          if (same == -1) c2 = leaf.complement();
        } else c2.replaceAllSameLeaves(leaf, tolerance);
        break;

      default:
        Debug.e("replace_all_same(): invalid operator.");
    }
  }
Example #10
0
  /**
   * Deep copy
   *
   * @param c
   */
  public RrCSG(RrCSG c) {
    if (c == u || c == n) Debug.e("RrCSG deep copy: copying null or universal set.");

    if (c.hp != null) hp = new RrHalfPlane(c.hp);
    else hp = null;

    if (c.c1 != null) c1 = new RrCSG(c.c1);
    else c1 = null;

    if (c.c2 != null) c2 = new RrCSG(c.c2);
    else c2 = null;

    comp = null; // This'll be built if it's needed

    op = c.op;
    complexity = c.complexity;
  }
Example #11
0
  /**
   * Replace duplicate of all leaves with the last instance of each; also link up complements.
   *
   * @param root
   * @param tolerance
   * @return simplified CSG object
   */
  private void simplify_r(RrCSG root, double tolerance) {
    switch (op) {
      case LEAF:
        root.replaceAllSameLeaves(this, tolerance);
        break;

      case NULL:
      case UNIVERSE:
        // System.out.println("simplify_r(): at a leaf!");
        break;

      case UNION:
      case INTERSECTION:
        c1.simplify_r(root, tolerance);
        c2.simplify_r(root, tolerance);
        break;

      default:
        Debug.e("simplify_r(): invalid operator.");
    }
  }
Example #12
0
  /**
   * Lazy evaluation for complement.
   *
   * @return complement
   */
  public RrCSG complement() {
    if (comp != null) return comp;

    RrCSG result;

    switch (op) {
      case LEAF:
        result = new RrCSG(hp.complement());
        break;

      case NULL:
        return universe();

      case UNIVERSE:
        return nothing();

      case UNION:
        result = intersection(c1.complement(), c2.complement());
        break;

      case INTERSECTION:
        result = union(c1.complement(), c2.complement());
        break;

      default:
        Debug.e("complement(): invalid operator.");
        return nothing();
    }

    // Remember, so we don't have to do it again.
    // (I do hope that the Java garbage collector is up to
    // spotting this deadly embrace, or we - I mean it - has
    // a memory leak.)
    // It turned out it was dumb.  Hence addition of destroy() above...

    comp = result;
    result.comp = this;

    return comp;
  }
  /**
   * Make a list with a single entry: the union of all the entries. Set its attributes to that of
   * extruder 0 in the extruder list.
   *
   * @param a
   * @return
   */
  public BooleanGridList union(Extruder[] es) {
    BooleanGridList result = new BooleanGridList();
    if (size() <= 0) return result;

    BooleanGrid contents = get(0);

    Attributes att = attribute(0);
    Boolean foundAttribute0 = false;
    if (att.getExtruder() == es[0]) foundAttribute0 = true;
    for (int i = 1; i < size(); i++) {
      if (!foundAttribute0) {
        if (attribute(i).getExtruder() == es[0]) {
          att = attribute(i);
          foundAttribute0 = true;
        }
      }
      contents = BooleanGrid.union(contents, get(i));
    }
    if (!foundAttribute0) Debug.e("RrCSGPolygonList.union(): Attribute of extruder 0 not found.");
    result.add(contents, att);
    return result;
  }
  /**
   * Plot a polygon
   *
   * @throws IOException
   * @throws ReprapException
   * @return
   */
  private void plot(RrPolygon p, boolean firstOneInLayer) throws ReprapException, IOException {
    Attributes att = p.getAttributes();
    Printer printer = layerConditions.getPrinter();
    double outlineFeedrate = att.getExtruder().getOutlineFeedrate();
    double infillFeedrate = att.getExtruder().getInfillFeedrate();

    boolean acc =
        att.getExtruder().getMaxAcceleration() > 0; // Preferences.loadGlobalBool("Accelerating");

    // int leng = p.size();

    if (p.size() <= 1) {
      startNearHere = null;
      return;
    }

    // If the length of the plot is <0.05mm, don't bother with it.
    // This will not spot an attempt to plot 10,000 points in 1mm.
    double plotDist = 0;
    Rr2Point lastPoint = p.point(0);
    for (int i = 1; i < p.size(); i++) {
      Rr2Point n = p.point(i);
      plotDist += Rr2Point.d(lastPoint, n);
      lastPoint = n;
    }
    if (plotDist < Preferences.machineResolution() * 0.5) {
      Debug.d("Rejected line with " + p.size() + " points, length: " + plotDist);
      startNearHere = null;
      return;
    }

    printer.selectExtruder(att);

    // Don't do these with mid-point starting

    //		if(p.isClosed() && att.getExtruder().incrementedStart())
    //			p = p.incrementedStart(layerConditions);
    //		else if(p.isClosed() && att.getExtruder().randomStart())
    //			p = p.randomStart();

    int stopExtruding = p.size() + 10;
    int stopValve = stopExtruding;
    double extrudeBackLength = att.getExtruder().getExtrusionOverRun();
    double valveBackLength = att.getExtruder().getValveOverRun();
    if (extrudeBackLength >= valveBackLength) {
      if (extrudeBackLength > 0) stopExtruding = p.backStep(extrudeBackLength);
      if (valveBackLength > 0) stopValve = p.backStep(valveBackLength);
    } else {
      if (valveBackLength > 0) stopValve = p.backStep(valveBackLength);
      if (extrudeBackLength > 0) stopExtruding = p.backStep(extrudeBackLength);
    }

    if (printer.isCancelled()) return;

    // If getMinLiftedZ() is negative, never lift the head

    Boolean lift = att.getExtruder().getMinLiftedZ() >= 0;

    if (acc)
      p.setSpeeds(
          att.getExtruder().getSlowXYFeedrate(),
          p.isClosed() ? outlineFeedrate : infillFeedrate,
          att.getExtruder().getMaxAcceleration());

    if (extrudeBackLength <= 0) stopExtruding = Integer.MAX_VALUE;
    else if (acc) stopExtruding = p.findBackPoint(extrudeBackLength);

    if (valveBackLength <= 0) stopValve = Integer.MAX_VALUE;
    else if (acc) stopValve = p.findBackPoint(valveBackLength);

    currentFeedrate = att.getExtruder().getFastXYFeedrate();
    singleMove(p.point(0));

    if (acc) currentFeedrate = p.speed(0);
    else {
      if (p.isClosed()) {
        currentFeedrate = outlineFeedrate;
      } else {
        currentFeedrate = infillFeedrate;
      }
    }

    plot(p.point(0), p.point(1), false, false);

    // Print any lead-in.
    printer.printStartDelay(firstOneInLayer);

    boolean extrudeOff = false;
    boolean valveOff = false;
    boolean oldexoff;

    //		if(p.isClosed())
    //		{
    //			for(int j = 1; j <= p.size(); j++)
    //			{
    //				int i = j%p.size();
    //				Rr2Point next = p.point((j+1)%p.size());
    //
    //				if (printer.isCancelled())
    //				{
    //					printer.stopMotor();
    //					singleMove(posNow());
    //					move(posNow(), posNow(), lift, true, true);
    //					return;
    //				}
    //				if(acc)
    //					currentFeedrate = p.speed(i);
    //
    //				oldexoff = extrudeOff;
    //				extrudeOff = j > stopExtruding || j == p.size();
    //				valveOff = j > stopValve || j == p.size();
    //
    //				plot(p.point(i), next, extrudeOff, valveOff);
    //				if(oldexoff ^ extrudeOff)
    //					printer.printEndReverse();
    //			}
    //		} else

    //		{
    for (int i = 1; i < p.size(); i++) {
      Rr2Point next = p.point((i + 1) % p.size());

      if (printer.isCancelled()) {
        printer.stopMotor();
        singleMove(posNow());
        move(posNow(), posNow(), lift, lift, true);
        return;
      }

      if (acc) currentFeedrate = p.speed(i);

      oldexoff = extrudeOff;
      extrudeOff = i > stopExtruding || i == p.size() - 1;
      valveOff = i > stopValve || i == p.size() - 1;
      //				if(oldexoff ^ extrudeOff)
      //					printer.printEndReverse();
      plot(p.point(i), next, extrudeOff, valveOff);
      if (oldexoff ^ extrudeOff) printer.printEndReverse();
    }
    //		}

    if (p.isClosed()) move(p.point(0), p.point(0), false, false, true);

    move(posNow(), posNow(), lift, lift, true);

    // The last point is near where we want to start next
    if (p.isClosed()) startNearHere = p.point(0);
    else startNearHere = p.point(p.size() - 1);

    if (simulationPlot != null) {
      RrPolygonList pgl = new RrPolygonList();
      pgl.add(p);
      simulationPlot.add(pgl);
    }
  }
  /** @throws Exception */
  public void produce() throws Exception {
    int movementSpeedZ;
    boolean subtractive;
    boolean interLayerCooling;

    try {
      subtractive = Preferences.loadGlobalBool("Subtractive");
      movementSpeedZ = Preferences.loadGlobalInt("MovementSpeedZ(0..255)");
    } catch (Exception ex) {
      movementSpeedZ = 212;
      subtractive = false;
      System.err.println(
          "Warning: could not load Z MovementSpeed and/or subtractive flag, using default");
    }

    try {
      interLayerCooling = Preferences.loadGlobalBool("InterLayerCooling");
    } catch (Exception ex) {
      interLayerCooling = true;
      System.err.println("Warning: could not load InterLayerCooling flag, using default");
    }

    reprap.setSpeedZ(movementSpeedZ);
    Debug.d("Intialising reprap");
    reprap.initialise();
    Debug.d("Selecting material 0");
    reprap.selectExtruder(0);
    Debug.d("Setting temperature");
    reprap.getExtruder().heatOn();

    // A "warmup" segment to get things in working order
    if (!subtractive) {
      reprap.setSpeed(reprap.getExtruder().getXYSpeed());
      reprap.moveTo(1, 1, 0, false, false);

      // Workaround to get the thing to start heating up
      reprap.printTo(1, 1, 0, true);

      if (reprap.getExtruder().getNozzleClearTime() <= 0) {
        Debug.d("Printing warmup segments, moving to (1,1)");
        // Take it slow and easy.
        Debug.d("Printing warmup segments, printing to (1,60)");
        reprap.moveTo(1, 25, 0, false, false);
        reprap.setSpeed(
            LinePrinter.speedFix(
                reprap.getExtruder().getXYSpeed(), reprap.getExtruder().getOutlineSpeed()));
        reprap.printTo(1, 60, 0, false);
        Debug.d("Printing warmup segments, printing to (3,60)");
        reprap.printTo(3, 60, 0, false);
        Debug.d("Printing warmup segments, printing to (3,25)");
        reprap.printTo(3, 25, 0, true);
        Debug.d("Warmup complete");
      }
      reprap.setSpeed(reprap.getFastSpeed());
    }

    // This should now split off layers one at a time
    // and pass them to the LayerProducer.

    boolean isEvenLayer = true;
    STLSlice stlc;
    double zMax;

    bld.mouseToWorld();
    stlc = new STLSlice(bld.getSTLs());
    zMax = stlc.maxZ();

    double startZ;
    double endZ;
    double stepZ;
    if (subtractive) {
      // Subtractive construction works from the top, downwards
      startZ = zMax;
      endZ = 0;
      stepZ = -reprap.getExtruder().getExtrusionHeight();
      reprap.setZManual(startZ);
    } else {
      // Normal constructive fabrication, start at the bottom and work up.

      startZ = 0;
      endZ = zMax;

      stepZ = reprap.getExtruder().getExtrusionHeight();
    }

    int layerNumber = 0;

    for (double z = startZ; subtractive ? z > endZ : z < endZ; z += stepZ) {

      if (reprap.isCancelled()) break;
      Debug.d("Commencing layer at " + z);

      // Change Z height
      reprap.moveTo(reprap.getX(), reprap.getY(), z, false, false);

      if (reprap.isCancelled()) break;

      // Pretend we've just finished a layer first time;
      // All other times we really will have.

      if (layerNumber == 0 || interLayerCooling) {
        reprap.finishedLayer(layerNumber);
        reprap.betweenLayers(layerNumber);
      }

      RrCSGPolygonList slice = stlc.slice(z + reprap.getExtruder().getExtrusionHeight() * 0.5);
      BranchGroup lowerShell = stlc.getBelow();

      LayerProducer layer = null;
      if (slice.size() > 0)
        layer =
            new LayerProducer(
                reprap,
                z,
                slice,
                lowerShell,
                isEvenLayer ? evenHatchDirection : oddHatchDirection,
                layerNumber,
                endZ);

      if (layerNumber == 0 || interLayerCooling) {
        reprap.startingLayer(layerNumber);
      }

      if (reprap.isCancelled()) break;

      if (layer != null) {
        layer.plot();
        layer.destroy();
      }
      layer = null;

      slice.destroy();
      stlc.destroyLayer();

      isEvenLayer = !isEvenLayer;

      layerNumber++;
    }

    if (subtractive) reprap.moveTo(0, 0, startZ, true, true);
    else reprap.moveTo(0, 0, reprap.getZ(), true, true);

    reprap.terminate();
  }
Example #16
0
  /**
   * This takes a complicated expression assumed to contain multiple instances of leafA and leafB
   * and returns the equivalent CSG expression involving at most leafA and leafB once (except for
   * non-manifold shapes).
   *
   * @param leafA
   * @param leafB
   * @return equivalent CSG expression involving at most leafA and leafB once
   */
  private RrCSG crossCategorise(RrCSG leafA, RrCSG leafB) {
    RrHalfPlane a = leafA.plane();
    RrHalfPlane b = leafB.plane();
    Rr2Point an = a.normal();
    Rr2Point bn = b.normal();
    Rr2Point v02 = Rr2Point.add(an, bn);
    Rr2Point v31 = Rr2Point.sub(bn, an);
    Rr2Point x, x0, x1, x2, x3;
    int category = 0;
    try {
      x = a.cross_point(b);
      v02 = v02.norm();
      v31 = v31.norm();
      x2 = Rr2Point.add(x, v02);
      x0 = Rr2Point.sub(x, v02);
      x1 = Rr2Point.add(x, v31);
      x3 = Rr2Point.sub(x, v31);
      if (value(x0) <= 0) category |= 1;
      if (value(x1) <= 0) category |= 2;
      if (value(x2) <= 0) category |= 4;
      if (value(x3) <= 0) category |= 8;

      switch (category) {
        case 0:
          return nothing();
        case 1:
          return intersection(leafA, leafB);
        case 2:
          return intersection(leafA, leafB.complement());
        case 3:
          return leafA;
        case 4:
          return intersection(leafA.complement(), leafB.complement());
        case 5:
          Debug.e("RrCSG crossCategorise: non-manifold shape (case 0101)!");
          return union(
              intersection(leafA, leafB), intersection(leafA.complement(), leafB.complement()));
        case 6:
          return leafB.complement();
        case 7:
          return union(leafA, leafB.complement());
        case 8:
          return intersection(leafA.complement(), leafB);
        case 9:
          return leafB;
        case 10:
          Debug.e("RrCSG crossCategorise: non-manifold shape (case 1010)!");
          return union(
              RrCSG.intersection(leafA.complement(), leafB),
              intersection(leafA, leafB.complement()));
        case 11:
          return union(leafA, leafB);
        case 12:
          return leafA.complement();
        case 13:
          return union(leafA.complement(), leafB);
        case 14:
          return union(leafA.complement(), leafB.complement());
        case 15:
          return universe();
        default:
          Debug.e("RrCSG crossCategorise: bitwise | doesn't seem to work...");
          return this;
      }
    } catch (Exception e) {
      // leafA and leafB are parallel

      x0 = Rr2Point.mul(Rr2Point.add(a.pLine().origin(), b.pLine().origin()), 0.5);
      x1 = Rr2Point.mul(Rr2Point.sub(a.pLine().origin(), b.pLine().origin()), 3);
      x2 = Rr2Point.add(x0, x1);
      x1 = Rr2Point.sub(x0, x1);
      if (value(x0) <= 0) category |= 1;
      if (value(x1) <= 0) category |= 2;
      if (value(x2) <= 0) category |= 4;

      if (leafA.value(x0) <= 0) leafA = leafA.complement();

      if (leafB.value(x0) <= 0) leafB = leafB.complement();

      switch (category) {
        case 0:
          return nothing();
        case 1:
          return intersection(leafA.complement(), leafB.complement());
        case 2:
          return leafB;
        case 3:
          return leafA.complement();
        case 4:
          return leafA;
        case 5:
          return union(leafA, leafB);
        case 6:
          return leafB.complement();
        case 7:
          return universe();
        default:
          Debug.e("RrCSG crossCategorise: bitwise | doesn't seem to work...");
          return this;
      }
    }
  }