static class Point { float mx, my; Point(float imx, float imy) { mx = imx; my = imy; } float squaredDistanceTo(Point p) { return pow(p.mx - this.mx,2.0) + pow(p.my - this.my,2.0); } void print() { if (false) { processing.core.PApplet.print("["); processing.core.PApplet.print(mx); processing.core.PApplet.print(","); processing.core.PApplet.print(my); processing.core.PApplet.print("]"); } } static float pointLineDistance(Point p, Point l1, Point l2) { p.print(); l1.print(); l2.print(); float u = ((p.mx - l1.mx)*(l2.mx - l1.mx) + (p.my - l1.my)*(l2.my - l1.my)) / (l1.squaredDistanceTo(l2)); u = constrain(u, 0.0, 1.0); //processing.core.PApplet.print("u="); //processing.core.PApplet.print(u); Point closest = new Point(l1.mx + u * (l2.mx - l1.mx), l1.my + u*(l2.my - l1.my)); float distance = sqrt(p.squaredDistanceTo(closest)); //processing.core.PApplet.print(" dist="); //processing.core.PApplet.print(distance); //processing.core.PApplet.print("\n"); return distance; } static float minLineSegmentDistance(Point p1, Point p2, Point p3, Point p4) { float denom = (p4.my - p3.my) * (p2.mx - p1.mx) - (p4.mx - p3.mx) * (p2.my - p1.my); float ua = ((p4.mx - p3.mx) * (p1.my - p3.my) - (p4.my - p3.my) * (p1.mx - p3.mx)) / denom; float ub = ((p2.mx - p1.mx) * (p1.my - p3.my) - (p2.my - p1.my) * (p1.mx - p3.mx)) / denom; ua = constrain(ua, 0.0, 1.0); ub = constrain(ub, 0.0, 1.0); Point x1, x2; x1 = new Point(p1.mx + ua * (p2.mx - p1.mx), p1.my + ua * (p2.my - p1.my)); x2 = new Point(p3.mx + ub * (p4.mx - p3.mx), p3.my + ub * (p4.my - p3.my)); return sqrt(x1.squaredDistanceTo(x2)); } } class Caret { Point mp; Point mp1; Point mp2; Caret(Point p, float len, float angle1, float angle2) { mp = p; mp1 = new Point(mp.mx+len*cos(angle1), mp.my+len*sin(angle1)); mp2 = new Point(mp.mx+len*cos(angle1+angle2), mp.my+len*sin(angle1+angle2)); } boolean tooCloseTo(Caret otherCaret) { if (Point.minLineSegmentDistance(mp, mp1, otherCaret.mp, otherCaret.mp1) < minDist) return true; if (Point.minLineSegmentDistance(mp, mp1, otherCaret.mp, otherCaret.mp2) < minDist) return true; if (Point.minLineSegmentDistance(mp, mp2, otherCaret.mp, otherCaret.mp1) < minDist) return true; if (Point.minLineSegmentDistance(mp, mp2, otherCaret.mp, otherCaret.mp2) < minDist) return true; return false; } void draw() { line(mp.mx, mp.my, mp1.mx, mp1.my); line(mp.mx, mp.my, mp2.mx, mp2.my); //ellipse(mp.mx, mp.my, minDist, minDist); //ellipse(mp1.mx, mp1.my, minDist, minDist); //ellipse(mp2.mx, mp2.my, minDist, minDist); } } Vector carets; float radius = 5.0; float minDist = 3.0; void setup() { size(320,240); smooth(); } void draw() { if (mousePressed) return; carets = new Vector(); int retries; float threshold = pow(radius*2.1,2); do { boolean acceptable; Caret c; retries = 0; do { Point p = new Point(random(0.0,width), random(0.0,height)); c = new Caret(p, radius, random(0.0,TWO_PI),radians(random(40.0,80.0))); acceptable = true; for (Iterator iter = carets.iterator(); iter.hasNext();) { if (c.tooCloseTo((Caret)iter.next())) { retries++; acceptable = false; break; } } } while (!acceptable && retries < 100 && carets.size() < 1000); if (acceptable) { carets.add(c); retries = 0; } } while (retries == 0); background(255); for (Iterator iter = carets.iterator(); iter.hasNext(); ) { ((Caret)iter.next()).draw(); } }