!7 sclass Circle { BufferedImage img; double x, y; } sclass Line { Circle a, b; } static new L circles; static new L lines; static Circle addCircle(S imageID, double x, double y) { ret addAndReturn(circles, nu(Circle, +x, +y, img := loadImage2(imageID))); } static Line addLine(Circle a, Circle b) { ret addAndReturn(lines, nu(Line, +a, +b)); } p-pretty /* although that's debatable given the detailed person */ { Circle a = addCircle(#1007291, 1/3.0, 0.5); Circle b = addCircle(#1007292, 2/3.0, 0.5); addLine(a, b); fO makeImg = func(int w, int h) { BufferedImage bg = renderTiledBackground(#1007195, w, h); for (Line l : lines) drawThoughtLine(bg, l.a.img, iround(l.a.x*w), iround(l.a.y*h), l.b.img, iround(l.b.x*w), iround(l.b.y*h)); for (Circle c : circles) drawThoughtCircle(bg, c.img, iround(c.x*w), iround(c.y*h)); ret bg; }; final ImageSurface canvas = jcanvas(makeImg); disableImageSurfaceSelector(canvas); new CircleDragger(canvas, r { canvas.setImage(asBufferedImage(callF(makeImg))) }); showFrame(canvas); } sclass CircleDragger extends MouseAdapter { ImageSurface is; O update; int dx, dy; Circle circle; *(ImageSurface *is, O *update) { if (containsInstance(is.tools, CircleDragger)) ret; is.tools.add(this); is.addMouseListener(this); is.addMouseMotionListener(this); } public void mousePressed(MouseEvent e) { if (evt.getButton() == MouseEvent.BUTTON1) { Pt p = is.pointFromEvent(e); circle = findCircle(p); if (circle != null) { dx = p.x-iround(circle.x*is.getWidth()); dy = p.y-iround(circle.y*is.getHeight()); } } } public void mouseDragged(MouseEvent e) { if (circle != null) { Pt p = is.pointFromEvent(e); circle.x = (p.x-dx)/is.getWidth(); circle.y = (p.y-dy)/is.getHeight(); callF(update); } } public void mouseReleased(MouseEvent e) { mouseDragged(e); circle = null; } }