All,

I'm working on getting the basics together for a typical graph-structure
editor. I've borrowed some code from the "Elastic Nodes" example to serve
as the basis of my editor. I have a couple of issues that I'm hoping to
get some help with...

Here's my code:

----8<----

package com.example.editor;

import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QGraphicsScene;
import com.trolltech.qt.gui.QGraphicsView;
import com.trolltech.qt.gui.QMainWindow;
import com.trolltech.qt.gui.QPainter.RenderHint;

public class Editor extends QMainWindow {
        public Editor() {
                scene = new QGraphicsScene();
                scene.setSceneRect(0, 0, 700, 700);

                Node previousNode = null;
                for(int i = 0; i < 3; i++) {
                        Node node = new Node((int) (25 + (Math.random() * 75)));
                        node.setPos(Math.random() * 600, Math.random() * 600);
                        scene.addItem(node);

                        if(previousNode != null) {
                                Edge edge = new Edge(previousNode, node);
                                previousNode.addEdge(edge);
                                node.addEdge(edge);
                                scene.addItem(edge);
                        }
                        previousNode = node;
                }

                view = new QGraphicsView(scene);
                view.setRenderHint(RenderHint.Antialiasing, true);

                setCentralWidget(view);
        }

        protected QGraphicsScene scene;
        protected QGraphicsView view;

        public static void main(String[] args) {
                QApplication.initialize(args);

                Editor editor = new Editor();
                editor.show();

                QApplication.exec();
        }
}

----8<----

package com.example.editor;

import java.util.ArrayList;
import java.util.List;

import com.trolltech.qt.core.QRectF;
import com.trolltech.qt.gui.QGraphicsEllipseItem;
import com.trolltech.qt.gui.QPen;
import com.trolltech.qt.gui.QGraphicsItem.GraphicsItemChange;
import com.trolltech.qt.gui.QGraphicsItem.GraphicsItemFlag;

public class Node extends QGraphicsEllipseItem {
        public Node(int diameter) {
                setPen(pen);
                setDiameter(diameter);
                setFlag(GraphicsItemFlag.ItemIsMovable, true);
                edges = new ArrayList<Edge>();
        }

        @Override
        public Object itemChange(GraphicsItemChange change, Object value) {
                switch(change) {
                        case ItemPositionChange:
                                for(Edge edge : edges) {
                                        edge.nodeChanged();
                                }
                }
                return super.itemChange(change, value);
        }

        public void addEdge(Edge edge) {
                edges.add(edge);
        }

        public void setDiameter(int diameter) {
                this.diameter = diameter;

                QRectF rect = new QRectF(-(diameter / 2), -(diameter / 2), 
diameter,
diameter);
                this.setRect(rect);
                this.update(rect);
        }
        public int getDiameter() {
                return diameter;
        }

        protected int diameter;
        protected List<Edge> edges;

        protected static QPen pen;
        static {
                pen = new QPen();
                pen.setWidth(6);
        }
}

----8<----

package com.example.editor;

import com.trolltech.qt.core.QPointF;
import com.trolltech.qt.core.QRectF;
import com.trolltech.qt.core.Qt.BrushStyle;
import com.trolltech.qt.gui.QBrush;
import com.trolltech.qt.gui.QColor;
import com.trolltech.qt.gui.QGraphicsItem;
import com.trolltech.qt.gui.QLineF;
import com.trolltech.qt.gui.QPainter;
import com.trolltech.qt.gui.QPen;
import com.trolltech.qt.gui.QPolygonF;
import com.trolltech.qt.gui.QStyleOptionGraphicsItem;
import com.trolltech.qt.gui.QWidget;

public class Edge extends QGraphicsItem {
        public Edge(Node sourceNode, Node destinationNode) {
                this.sourceNode = sourceNode;
                sourcePoint = new QPointF();
                sourceArrowPoint1 = new QPointF();
                sourceArrowPoint2 = new QPointF();
                sourceArrowPolygon = new QPolygonF();

                this.destinationNode = destinationNode;
                destinationPoint = new QPointF();

                boundingRect = new QRectF();
                extra = (PEN_WIDTH + ARROW_SIZE) / 2;

                nodeChanged();
        }

        public void nodeChanged() {
                double dx = sourceNode.pos().x() - destinationNode.pos().x();
                double dy = sourceNode.pos().y() - destinationNode.pos().y();

                double length = Math.sqrt(dx * dx + dy * dy);
                if (length == 0.0) return;

                double sourcePaddingX = dx / length * (sourceNode.diameter / 2 
+ 2);
                double sourcePaddingY = dy / length * (sourceNode.diameter / 2 
+ 2);
                double destinationPaddingX = dx / length * 
(destinationNode.diameter / 2
+ 2);
                double destinationPaddingY = dy / length * 
(destinationNode.diameter / 2
+ 2);

                removeFromIndex();
                sourcePoint.setX(sourceNode.pos().x() - sourcePaddingX);
                sourcePoint.setY(sourceNode.pos().y() - sourcePaddingY);

                destinationPoint.setX(destinationNode.pos().x() + 
destinationPaddingX);
                destinationPoint.setY(destinationNode.pos().y() + 
destinationPaddingY);

                boundingRect.setBottomLeft(sourceNode.pos());
                boundingRect.setTopRight(destinationNode.pos());

                boundingRect.adjust(-extra, -extra, extra, extra);

                boundingRect = boundingRect.normalized();
                addToIndex();
        }

        public void paint(QPainter painter, QStyleOptionGraphicsItem option,
QWidget widget) {
                painter.setPen(pen);

                QLineF line = new QLineF(sourcePoint, destinationPoint);
                painter.drawLine(line);

                double angle;
                if (line.length() > 0) {
                        angle = Math.acos(line.dx() / line.length());
                } else {
                        angle = 0;
                }
                if (line.dy() >= 0) {
                        angle = (Math.PI * 2) - angle;
                }

                sourceArrowPoint1.setX(sourcePoint.x() + Math.sin(angle + 
Math.PI / 3) *
ARROW_SIZE);
                sourceArrowPoint1.setY(sourcePoint.y() + Math.cos(angle + 
Math.PI / 3) *
ARROW_SIZE);

                sourceArrowPoint2.setX(sourcePoint.x() + Math.sin(angle + 
Math.PI -
Math.PI / 3) * ARROW_SIZE);
                sourceArrowPoint2.setY(sourcePoint.y() + Math.cos(angle + 
Math.PI -
Math.PI / 3) * ARROW_SIZE);

                sourceArrowPolygon.clear();
                sourceArrowPolygon.append(line.p1());
                sourceArrowPolygon.append(sourceArrowPoint1);
                sourceArrowPolygon.append(sourceArrowPoint2);

                painter.setBrush(brush);
                painter.drawPolygon(sourceArrowPolygon);
        }

        public QRectF boundingRect() {
                return boundingRect;
        }

        protected Node sourceNode;
        protected QPointF sourcePoint;
        protected QPointF sourceArrowPoint1;
        protected QPointF sourceArrowPoint2;
        protected QPolygonF sourceArrowPolygon;

        protected Node destinationNode;
        protected QPointF destinationPoint;

        protected QRectF boundingRect;
        protected double extra;

        protected static QPen pen;
        protected static QBrush brush;
        protected static int ARROW_SIZE = 10;
        protected static int PEN_WIDTH = 1;
        static {
                pen = new QPen();
                pen.setWidth(PEN_WIDTH);

                brush = new QBrush();
                brush.setStyle(BrushStyle.SolidPattern);
                brush.setColor(new QColor(0, 0, 0, 255));
        }
}

----8<----

The first problem I'm having is (what I believe to be) a remedial
trigonometry problem. When I drag the three "nodes" around the scene, the
bounding rectangles for the edges aren't always calculated correctly, and
I end up with lots of "smearing" and "artifacts" when the edges are
re-drawn. Is there a better technique for calculating the bounding
rectangles of my edges?

Second (and related to the first issue), is there a more appropriate
technique for representing the edges in my scene? The position of the
edges are all "0, 0" and the bounding rectangles are all represented
relative to the scene positions of the center points of the nodes. This is
how the "elastic nodes" example worked. In general, is there a better way
to deal with graphics items that depend on the position of two other
graphics items, but there isn't a clear parent/child relationship?

Third, is using the "itemChanged()" method on the node, and then calling a
method on the edge to recalculate its geometry, which in turn calls
Edge.update(), the right way to handle scene updates?

Thanks in advance for any guidance anyone can offer!

Michael

_______________________________________________
Qt-jambi-interest mailing list
[email protected]
http://lists.trolltech.com/mailman/listinfo/qt-jambi-interest

Reply via email to