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

io.lacuna.artifex.utils.regions.Split Maven / Gradle / Ivy

package io.lacuna.artifex.utils.regions;

import io.lacuna.artifex.*;
import io.lacuna.artifex.utils.DoubleAccumulator;
import io.lacuna.artifex.utils.Scalars;
import io.lacuna.artifex.utils.SweepQueue;
import io.lacuna.bifurcan.*;

import java.util.Arrays;
import java.util.Objects;

import static io.lacuna.artifex.utils.Intersections.PARAMETRIC_EPSILON;
import static io.lacuna.artifex.utils.Intersections.SPATIAL_EPSILON;
import static java.lang.Math.max;

public class Split {

  static class VertexUnion {

    private final IMap parent = new LinearMap<>();
    private final ISet roots = new LinearSet<>();

    public void join(Vec2 a, Vec2 b) {
      a = adjust(a);
      b = adjust(b);
      int cmp = a.compareTo(b);
      if (cmp < 0) {
        parent.put(b, a);
        roots.add(a);
      } else if (cmp > 0) {
        parent.put(a, b);
        roots.add(b);
      } else {
        roots.add(b);
      }
    }

    public Vec2 adjust(Vec2 p) {
      Vec2 curr = p;
      for (; ; ) {
        Vec2 next = parent.get(curr, null);
        if (next == null) {
          if (!curr.equals(p)) {
            parent.put(p, curr);
          }
          return curr;
        }
        curr = next;
      }
    }

    public Curve2 adjust(Curve2 c) {
      Vec2 start = adjust(c.start());
      Vec2 end = adjust(c.end());

      return start.equals(end)
        ? null
        : c.endpoints(start, end);
    }

    public ISet roots() {
      return roots.difference(parent.keys());
    }

  }

  public static class Result {
    public final Region2 a, b;
    public final ISet splits;

    public Result(Region2 a, Region2 b, ISet splits) {
      this.a = a;
      this.b = b;
      this.splits = splits;
    }
  }

  public static Result split(Region2 a, Region2 b) {

    SweepQueue[] queues = new SweepQueue[]{new SweepQueue(), new SweepQueue()};

    add(a, queues[0]);
    add(b, queues[1]);

    VertexUnion union = new VertexUnion();
    IMap intersections = new LinearMap<>();

    Curve2[] cs = new Curve2[2];
    for (; ; ) {
      int idx = SweepQueue.next(queues);
      cs[idx] = queues[idx].take();

      if (cs[idx] == null) {
        break;
      }

      intersections.put(cs[idx], new DoubleAccumulator());

      for (Curve2 c : queues[1 - idx].active()) {
        cs[1 - idx] = c;
        Vec2[] ts = cs[0].intersections(cs[1]);

        for (int i = 0; i < ts.length; i++) {
          //System.out.println(ts.length + " " + ts[i] + " " + cs[0].position(ts[i].x) + " " + cs[0].position(ts[i].x).sub(cs[1].position(ts[i].y)).length() + " " + ts[i].sub(ts[max(0, i - 1)]));
          double t0 = ts[i].x;
          double t1 = ts[i].y;

          intersections.get(cs[0]).get().add(t0);
          intersections.get(cs[1]).get().add(t1);

          Vec2 p0 = cs[0].position(t0);
          Vec2 p1 = cs[1].position(t1);
          union.join(p0, p1);
        }
      }
    }

    IMap deduped = intersections.mapValues((c, acc) -> dedupe(c, acc, union));

    return new Result(
      split(a, deduped, union),
      split(b, deduped, union),
      union.roots());
  }

  private static Region2 split(Region2 region, IMap splits, VertexUnion union) {
    return new Region2(
      Arrays.stream(region.rings)
        .map(ring -> split(ring, splits, union))
        .filter(Objects::nonNull)
        .toArray(Ring2[]::new));
  }

  private static DoubleAccumulator dedupe(Curve2 c, DoubleAccumulator acc, VertexUnion union) {

    double[] ts = acc.toArray();
    Arrays.sort(ts);

    DoubleAccumulator result = new DoubleAccumulator();
    for (int i = 0; i < ts.length; i++) {
      double t0 = result.size() == 0 ? 0 : result.last();
      double t1 = ts[i];
      if (Scalars.equals(t0, t1, PARAMETRIC_EPSILON)
        || Vec.equals(c.position(t0), c.position(t1), SPATIAL_EPSILON)) {
        union.join(c.position(t0), c.position(t1));
      } else if (Scalars.equals(t1, 1, PARAMETRIC_EPSILON)
        || Vec.equals(c.position(t1), c.end(), SPATIAL_EPSILON)) {
        union.join(c.position(t1), c.end());
      } else {
        result.add(t1);
      }
    }

    return result;
  }

  private static Ring2 split(Ring2 r, IMap splits, VertexUnion union) {
    IList curves = new LinearList<>();
    for (Curve2 c : r.curves) {
      DoubleAccumulator acc = splits.get(c).get();
      for (Curve2 cp : c.split(acc.toArray())) {
        cp = union.adjust(cp);
        if (cp != null) {
          curves.addLast(cp);
        }
      }
    }

    return curves.size() == 0 ? null : new Ring2(curves);
  }

  private static void add(Region2 region, SweepQueue queue) {
    for (Ring2 r : region.rings()) {
      for (Curve2 c : r.curves) {
        queue.add(c, c.start().x, c.end().x);
      }
    }
  }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy