Warning: session_start(): open(/var/lib/php/sessions/sess_6fsv1fmpo8ht0hn2aound84gfc, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
sclass BaseBase {
S globalID = aGlobalID();
S text;
S textForRender() { ret text; }
}
sbool traits_multiLine = true;
sclass Base extends BaseBase {
new L traits;
bool hasTrait(S t) { ret containsIC(traits(), t); }
L traits() { if (nempty(text) && neq(first(traits), text)) traits.add(0, text); ret traits; }
void addTraits(L l) { setAddAll(traits(), l); }
void addTrait(S t) { if (nempty(t)) setAdd(traits(), t); }
S textForRender() {
L traits = traits();
if (traits_multiLine) ret lines(traits);
if (l(traits) <= 1) ret first(traits);
ret first(traits) + " [" + join(", ", dropFirst(traits)) + "]";
}
void setText(S text) {
this.text = text;
traits = ll(text);
}
}
sclass CirclesAndLines {
new L circles;
new L lines;
Class extends Arrow> arrowClass = Arrow;
Class extends Circle> circleClass = Circle;
S title;
S globalID = aGlobalID();
transient Lock lock = fairLock();
transient S defaultImageID = #1007372;
transient double imgZoom = 1; // zoom for the circle images
transient Pt translate;
Circle hoverCircle; // which one we are hovering over
transient O onUserMadeArrow, onUserMadeCircle, onLayoutChange;
transient O onFullLayoutChange, onDeleteCircle, onDeleteLine;
transient O onRenameCircle, onRenameLine, onStructureChange;
transient BufferedImage imageForUserMadeNodes;
static int maxDistanceToLine = 20; // for clicking
transient S backgroundImageID = defaultBackgroundImageID;
static S defaultBackgroundImageID = #1007195;
static Color defaultLineColor = Color.white;
static bool debugRender;
static O staticPopupExtender;
transient double scale = 1; // zoom whole image
transient bool recordHistory;
L history;
// auto-visualize
Circle circle_autoVis(S text, S visualizationText, double x, double y) {
ret addAndReturn(circles,
nu(circleClass, +x, +y, +text,
quickvis := visualizationText,
img := processImage(quickVisualizeOr(visualizationText, defaultImageID))));
}
S makeVisualizationText(S text) {
ret possibleGlobalID(text) ? "" : text;
}
Circle circle_autoVis(S text, double x, double y) {
ret circle_autoVis(text, makeVisualizationText(text), x, y);
}
Circle circle(BufferedImage img, double x, double y, S text) {
ret addAndReturn(circles, nu(circleClass, +x, +y, +text, img := processImage(img));
}
Circle circle(S text, BufferedImage img, double x, double y) {
ret circle(img, x, y, text);
}
Circle circle(S text, double x, double y) {
ret addAndReturn(circles, nu(circleClass, +x, +y, +text, img := processImage(imageForUserMadeNodes());
}
Circle addCircle(S imageID, double x, double y) {
ret addCircle(imageID, x, y, "");
}
Circle addCircle(S imageID, double x, double y, S text) {
ret addAndReturn(circles, nu(circleClass, +x, +y, +text, img := processImage(loadImage2(imageID))));
}
Arrow findArrow(Circle a, Circle b) {
for (Line l : getWhere(lines, +a, +b))
if (l instanceof Arrow)
ret (Arrow) l;
null;
}
Line addLine(Circle a, Circle b) {
Line line = findWhere(lines, +a, +b);
if (line == null)
lines.add(line = nu(Line, +a, +b));
ret line;
}
Arrow arrow(Circle a, S text, Circle b) {
ret addArrow(a, b, text);
}
Arrow addArrow(Circle a, Circle b) {
ret addArrow(a, b, "");
}
Arrow addArrow(Circle a, Circle b, S text) {
ret addAndReturn(lines, nu(arrowClass, +a, +b, +text));
}
BufferedImage makeImage(int w, int h) {
BufferedImage bg = renderTiledBackground(backgroundImageID, w, h, ptX(translate), ptY(translate));
if (!lock.tryLock()) null;
try {
if (scale != 1)
createGraphics_modulate(bg, voidfunc(Graphics2D g) {
g.scale(scale, scale);
});
// Lines
if (debugRender)
print("Have " + n(lines, "line"));
HashMap, Line> hasLine = new HashMap;
new HashMap flipMap;
for (Line l : lines) {
hasLine.put(pair(l.a, l.b), l);
Line x = hasLine.get(pair(l.b, l.a));
if (x != null) {
if (debugRender)
print("flipMap " + l.a.text + " / " + l.b.text);
flipMap.put(x, false);
flipMap.put(l, false);
}
}
for (Line l : lines) {
DoublePt a = translateDoublePt(translate, l.a.doublePt(w, h, this));
DoublePt b = translateDoublePt(translate, l.b.doublePt(w, h, this));
if (debugRender)
print("Line " + a + " " + b);
if (l << Arrow)
drawThoughtArrow(bg, l.a.img(this), iround(a.x), iround(a.y), l.b.img(this), iround(b.x), iround(b.y), l.color);
else
drawThoughtLine(bg, l.a.img(this), iround(a.x), iround(a.y), l.b.img(this), iround(b.x), iround(b.y), l.color);
S text = l.textForRender();
if (nempty(text)) {
drawOutlineTextAlongLine_flip.set(flipMap.get(l));
drawThoughtLineText(bg, l.a.img(this), iround(a.x), iround(a.y), l.b.img(this), iround(b.x), iround(b.y), text, Color.white /*l.color*/);
}
}
// Circles
for (Circle c : circles) {
DoublePt p = translateDoublePt(translate, c.doublePt(w, h, this));
drawThoughtCircle(bg, c.img(this), p.x, p.y);
}
for (Circle c : circles) {
DoublePt p = translateDoublePt(translate, c.doublePt(w, h, this));
S text = c.textForRender();
if (nempty(text))
drawThoughtCircleText(bg, c.img(this), p, text);
if (c == hoverCircle)
drawThoughtCirclePlus(bg, c.img(this), p.x, p.y);
}
} finally {
lock.unlock();
createGraphics_modulate(bg, null);
}
ret bg;
}
Canvas showAsFrame(int w, int h) {
Canvas canvas = showAsFrame();
frameInnerSize(canvas, w, h);
centerFrame(getFrame(canvas));
ret canvas;
}
Canvas showAsFrame() {
ret (Canvas) swing(func {
Canvas canvas = makeCanvas();
showCenterFrame(canvas);
ret canvas;
});
}
Canvas makeCanvas() {
fO makeImg = func(int w, int h) { makeImage(w, h) };
final Canvas canvas = jcanvas(makeImg);
disableImageSurfaceSelector(canvas);
new CircleDragger(this, canvas, r { updateCanvas(canvas, makeImg) });
componentPopupMenu(canvas, voidfunc(JPopupMenu menu) {
// POPUP MENU START
Pt p = pointFromEvent(canvas, componentPopupMenu_mouseEvent.get());
addMenuItem(menu, "New Circle...", r { newCircle(canvas) });
final Line l = findLine(canvas, p);
if (l != null) {
addMenuItem(menu, "Rename Relation...", r {
renameLine(canvas, l)
});
addMenuItem(menu, "Delete Relation", r {
deleteLine(l);
canvas.update();
});
}
final Circle c = findCircle(canvas, p);
if (c != null) {
addMenuItem(menu, "Rename Circle...", r { renameCircle(canvas, c) });
addMenuItem(menu, "Delete Circle", r {
deleteCircle(c);
canvas.update();
});
if (c.img != null || c.quickvis != null)
addMenuItem(menu, "Delete Image", r {
c.img = null;
c.quickvis = null;
canvas.update();
});
if (neqic(c.text, c.quickvis))
addMenuItem(menu, "Visualize", r {
thread "Visualizing" {
quickVisualize(c.text);
print("Quickvis done");
swing {
c.img = null;
c.quickvis = c.text;
canvas.update();
pcallF(onRenameCircle, c); schange();
}
}
});
}
addMenuItem(menu, "Copy structure to clipboard", r {
copyTextToClipboard(cal_simplifiedStructure(CirclesAndLines.this))
});
addMenuItem(menu, "Paste structure", r {
S text = getTextFromClipboard();
if (nempty(text)) {
copyCAL(cal_unstructure(text), CirclesAndLines.this);
canvas.update();
schange();
}
});
pcallF(staticPopupExtender, CirclesAndLines.this, canvas, menu);
// POPUP MENU END
});
ret canvas;
}
void newCircle {
final JTextField text = jtextfield();
showFormTitled("New Circle", "Text", text, r-thread { loading {
S theText = getTextTrim(text);
makeCircle(theText);
canvas.update();
}});
}
Canvas show() { ret showAsFrame(); }
Canvas show(int w, int h) { ret showAsFrame(w, h); }
Circle findCircle(S text) {
for (Circle c : circles) if (eq(c.text, text)) ret c;
for (Circle c : circles) if (eqic(c.text, text)) ret c;
null;
}
void renameCircle(final Canvas canvas, final Circle c) {
final JTextField tf = jtextfield(c.text);
showFormTitled("Rename circle",
"Old name", jlabel(c.text),
"New name", tf,
r {
c.setText(getTextTrim(tf));
canvas.update();
pcallF(onRenameCircle, c); schange();
});
}
void renameLine(final Canvas canvas, final Line l) {
final JTextField tf = jtextfield(l.text);
showFormTitled("Rename relation",
"Old name", jlabel(l.text),
"New name", tf,
r {
l.setText(getTextTrim(tf));
canvas.update();
pcallF(onRenameLine, l); schange();
});
}
void clear {
clearAll(circles, lines);
}
// only finds actually containing circles
Circle findCircle(ImageSurface canvas, Pt p) {
p = untranslatePt(translate, p);
new Lowest best;
for (Circle c : circles)
if (c.contains(this, canvas, p))
best.put(c, pointDistance(p, c.pt2(this, canvas)));
ret best!;
}
Circle findNearestCircle(ImageSurface canvas, Pt p) {
new Lowest best;
for (Circle c : circles)
if (c.contains(this, canvas, p))
best.put(c, pointDistance(p, c.pt2(this, canvas)));
ret best.get();
}
BufferedImage processImage(BufferedImage img) {
ret scaleImage(img, imgZoom);
}
void deleteCircle(Circle c) {
for (Line l : cloneList(lines))
if (l.a == c || l.b == c) deleteLine(l);
circles.remove(c);
pcallF(onDeleteCircle, c); schange();
}
void deleteLine(Line l) {
lines.remove(l);
pcallF(onDeleteLine, l); schange();
}
void openPlusDialog(final Circle c, final ImageSurface canvas) {
if (c == null) ret;
final JTextField tfFrom = jtextfield(c.text);
final JTextField tfRel = jtextfield(web_defaultRelationName());
final JComboBox tfTo = autoComboBox(collect(circles, 'text));
showFormTitled("Add connection",
"From node", tfFrom,
"Connection name", tfRel,
"To node", tfTo,
func {
S sA = getTextTrim(tfFrom);
Circle a = eq(sA, c.text) ? c : findOrMakeCircle(sA);
if (a == null) { messageBox("Not found: " + getTextTrim(tfFrom)); false; }
Circle b = findOrMakeCircle(getTextTrim(tfTo));
if (b == null) { messageBox("Not found: " + getTextTrim(tfTo)); false; }
if (a == b) { infoBox("Can't connect circle to itself for now"); false; }
Arrow arrow = arrow(a, getTextTrim(tfRel), b);
((Canvas) canvas).update();
pcallF(onUserMadeArrow, arrow); schange();
null;
});
awtLater(tfRel, 100, r { requestFocus(tfRel) });
}
void schange() {
pcallF(onStructureChange);
}
Circle findOrMakeCircle(S text) {
Circle c = findCircle(text);
if (c != null) ret c;
ret makeCircle(text);
}
Circle newCircle(S text) {
Circle c = circle(imageForUserMadeNodes(), random(0.1, 0.9), random(0.1, 0.9), text);
pcallF(onUserMadeCircle, c); schange();
ret c;
}
BufferedImage imageForUserMadeNodes() {
if (imageForUserMadeNodes == null)
imageForUserMadeNodes = whiteImage(20, 20);
ret imageForUserMadeNodes;
}
Line findLine(Canvas is, Pt p) {
p = untranslatePt(translate, p);
new Lowest best;
for (Line line : lines) {
double d = distancePointToLineSegment(line.a.pt(this, is), line.b.pt(this, is), p);
if (d <= maxDistanceToLine)
best.put(line, d);
}
ret best!;
}
Pt pointFromEvent(ImageSurface canvas, MouseEvent e) {
ret scalePt(canvas.pointFromEvent(e), 1/scale);
}
void addToHistory(O o) {
if (history == null) history = new L;
history.add(o);
}
} // end of class CirclesAndLines
sclass Circle extends Base {
transient BufferedImage img;
double x, y;
//static BufferedImage defaultImage;
S quickvis;
BufferedImage img(CirclesAndLines cal) {
if (img != null) ret img;
if (nempty(quickvis)) img = quickVisualize(quickvis);
//if (defaultImage == null) defaultImage = loadImage2(#1007372);
ret cal.imageForUserMadeNodes();
}
Pt pt2(CirclesAndLines cal, ImageSurface is) {
ret pt2(is.getWidth(), is.getHeight(), cal);
}
Pt pt(CirclesAndLines cal, ImageSurface is) {
ret pt(is.getWidth(), is.getHeight(), cal);
}
Pt pt(int w, int h, CirclesAndLines cal) {
ret new Pt(iround(x*w/cal.scale), iround(y*h/cal.scale));
}
Pt pt2(int w, int h, CirclesAndLines cal) {
ret new Pt(iround(x*w), iround(y*h));
}
DoublePt doublePt(int w, int h, CirclesAndLines cal) {
ret new DoublePt(x*w/cal.scale, y*h/cal.scale);
}
bool contains(CirclesAndLines cal, ImageSurface is, Pt p) {
ret pointDistance(p, pt2(cal, is)) <= iround(thoughtCircleSize(img(cal))*cal.scale)/2+1;
}
}
sclass Line extends Base {
Circle a, b;
transient Color color = CirclesAndLines.defaultLineColor;
Line setColor(Color color) {
this.color = color;
this;
}
}
Line > Arrow {}
sclass CircleDragger extends MouseAdapter {
CirclesAndLines cal;
ImageSurface is;
O update;
int dx, dy;
Circle circle;
Pt startPoint;
*(CirclesAndLines *cal, ImageSurface *is, O *update) {
if (containsInstance(is.tools, CircleDragger)) ret;
is.tools.add(this);
is.addMouseListener(this);
is.addMouseMotionListener(this);
}
public void mouseMoved(MouseEvent e) {
Pt p = is.pointFromEvent(e);
Circle c = cal.findCircle(is, p);
if (c != cal.hoverCircle) {
cal.hoverCircle = c;
callF(update);
}
}
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
Pt p = is.pointFromEvent(e);
startPoint = p;
circle = cal.findCircle(is, p);
if (circle != null) {
dx = p.x-iround(circle.x*is.getWidth());
dy = p.y-iround(circle.y*is.getHeight());
//printVars("mousePressed", +dx, +dy, +p, cx := circle.x, cy := circle.y, w := is.getWidth(), h := is.getHeight());
} else {
Pt t = unnull(cal.translate);
dx = p.x-t.x;
dy = p.y-t.y;
}
}
}
public void mouseDragged(MouseEvent e) {
if (startPoint == null) ret;
Pt p = is.pointFromEvent(e);
if (circle != null) {
circle.x = (p.x-dx)/(double) is.getWidth();
circle.y = (p.y-dy)/(double) is.getHeight();
//printVars("mouseDragged", +dx, +dy, +p, cx := circle.x, cy := circle.y, w := is.getWidth(), h := is.getHeight());
pcallF(cal.onLayoutChange, circle);
callF(update);
} else {
cal.translate = new Pt(p.x-dx, p.y-dy);
callF(update);
}
}
public void mouseReleased(MouseEvent e) {
mouseDragged(e);
if (eq(is.pointFromEvent(e), startPoint))
cal.openPlusDialog(circle, is);
circle = null;
startPoint = null;
}
}