在 GraphStream 中鼠标悬停时获取 GraphicEdge
Get GraphicEdge at Mouse hovering in GraphStream
我想在鼠标悬停在边上时显示边的权重。
所以我在实现的 MouseManager 中使用了 MouseEvent.MOUSE_MOVED
。对于节点,我只需调用 view.findGraphicElementAt(getManagedTypes(), event.getX(), event.getY())
即可获取 GraphicNode 对象。不幸的是,边没有 one x 和 y 值,因此无法通过此方法找到。是的,我知道 getX()
和 getY()
是为 GraphicEdge 实现的,但只是指向边缘的中心。
我需要边缘对象来获取存储在边缘的更多信息(例如重量)。那么如何使用 x、y 或我可以从接收到的 MouseEvent 中检索的其他一些值来获取边缘对象?
实际上,MouseOverMouseManager
或 FxMouseOverMouseManager
中已经实现了边缘选择、mouseOver 和 mouseLeft 功能(其中包括悬停在边缘上)。调用 view.enableMouseOptions()
时会自动设置此管理器,但由于某些其他原因我已经实现了一个单独的 MouseManager 并且仅当将鼠标悬停在边缘中心时才检测到悬停在边缘上。所以我的解决方案是将代码从 MouseOverMouseManager
复制到 MyMousemanager 并修改它。
编辑:
public class CompoundListNetworkMouseManager extends FxMouseManager {
private GraphicElement hoveredElement;
private long hoveredElementLastChanged;
private ReentrantLock hoverLock = new ReentrantLock();
private Timer hoverTimer = new Timer(true);
private HoverTimerTask latestHoverTimerTask;
/**
*(copied from the GraphsStream Library)
* The mouse needs to stay on an element for at least this amount of milliseconds,
* until the element gets the attribute "ui.mouseOver" assigned.
* A value smaller or equal to zero indicates, that the attribute is assigned without delay.
* */
private final long delayHover;
public CompoundListNetworkMouseManager(){
super(EnumSet.of(InteractiveElement.NODE, InteractiveElement.EDGE));
this.delayHover = 100;
}
@Override
public void init(GraphicGraph graph, View view) {
this.graph = graph;
this.view = view;
view.addListener(MouseEvent.MOUSE_MOVED, mouseMoved);
}
@Override
public void release() {
view.removeListener(MouseEvent.MOUSE_MOVED, mouseMoved);
}
@Override
public EnumSet<InteractiveElement> getManagedTypes() {
return super.getManagedTypes();
}
protected void mouseOverElement(GraphicElement element){
element.setAttribute("ui.mouseOver", true);
element.setAttribute("ui.class", "mouseOver"); //I defined a class/type for edges in the CSS styling sheet that is calles "mouseOver"
if(element instanceof GraphicEdge) {
mouseOverEdge((GraphicEdge) element);
}
else if(element instanceof GraphicNode){
mouseOverNode((GraphicNode) element);
}
protected void mouseOverEdge(GraphicEdge graphicEdge) {
view.freezeElement(graphicEdge, true);
Edge edge = graph.getEdge(graphicEdge.getId());
System.out.println("Mouse over edge " + edge.getId());
}
protected void mouseLeftElement(GraphicElement element) {
this.hoveredElement = null;
element.removeAttribute("ui.mouseOver");
element.removeAttribute("ui.class");
}
EventHandler<MouseEvent> mouseMoved = new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
try {
hoverLock.lockInterruptibly();
boolean stayedOnElement = false;
curElement = view.findGraphicElementAt(getManagedTypes(), event.getX(), event.getY());
//adjusted implementation of search for edges
if(curElement == null && getManagedTypes().contains(InteractiveElement.EDGE)){
curElement = (GraphicElement) findEdgeAt(event.getX(), event.getY());
}
if(hoveredElement != null) {
//check if mouse stayed on the same element to avoid the mouseOverEvent being processed multiple times
stayedOnElement = curElement == null ? false : curElement.equals(hoveredElement);
if (!stayedOnElement && hoveredElement.hasAttribute("ui.mouseOver")) {
mouseLeftElement(hoveredElement);
}
}
if (!stayedOnElement && curElement != null) {
if (delayHover <= 0) {
mouseOverElement(curElement);
} else {
hoveredElement = curElement;
hoveredElementLastChanged = Calendar.getInstance().getTimeInMillis();
if (latestHoverTimerTask != null) {
latestHoverTimerTask.cancel();
}
latestHoverTimerTask = new HoverTimerTask(hoveredElementLastChanged, hoveredElement);
hoverTimer.schedule(latestHoverTimerTask, delayHover);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
hoverLock.unlock();
}
}
};
//copied from GraphStream Library
private final class HoverTimerTask extends TimerTask {
private final long lastChanged;
private final GraphicElement element;
public HoverTimerTask(long lastChanged, GraphicElement element) {
this.lastChanged = lastChanged;
this.element = element;
}
@Override
public void run() {
try {
hoverLock.lock();
if (hoveredElementLastChanged == lastChanged) {
mouseOverElement(element);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
hoverLock.unlock();
}
}
}
//findGraphicElement could be used but I wanted to implement the edgeContains method myself
private Edge findEdgeAt(double x, double y){
Camera cam = view.getCamera();
GraphMetrics metrics = cam.getMetrics();
//transform x and y
double xT = x + metrics.viewport[0];
double yT = y + metrics.viewport[0];
Edge edgeFound = null;
if (getManagedTypes().contains(InteractiveElement.EDGE)) {
Optional<Edge> edge = graph.edges().filter(e -> edgeContains((GraphicEdge) e, xT, yT)).findFirst();
if (edge.isPresent()) {
if (cam.isVisible((GraphicElement) edge.get())) {
edgeFound = edge.get();
}
}
}
return edgeFound;
}
//new edgeContains() method that finds edge at hovering not only when hovered over edge center
private boolean edgeContains(GraphicEdge edge, double x, double y) {
Camera cam = view.getCamera();
GraphMetrics metrics = cam.getMetrics();
Values size = edge.getStyle().getSize();
double deviation = metrics.lengthToPx(size, 0);
Point3 edgeNode0 = cam.transformGuToPx(edge.from.x, edge.from.y, 0);
Point3 edgeNode1 = cam.transformGuToPx(edge.to.x, edge.to.y, 0);
//check of point x,y is between nodes of the edge
boolean edgeContains = false;
//check x,y range
if(x > Math.min(edgeNode0.x, edgeNode1.x) - deviation
&& x < Math.max(edgeNode0.x, edgeNode1.x) + deviation
&& y > Math.min(edgeNode0.y, edgeNode1.y) - deviation
&& y < Math.max(edgeNode0.y, edgeNode1.y) + deviation){
//check deviation from edge
Vector2 vectorNode0To1 = new Vector2(edgeNode0, edgeNode1);
Point2 point = new Point2(x, y);
Vector2 vectorNode0ToPoint = new Vector2(edgeNode0, point);
//cross product of vectorNode0ToPoint and vectorNode0to1
double crossProduct = vectorNode0ToPoint.x() * vectorNode0To1.y() - vectorNode0To1.x() * vectorNode0ToPoint.y();
//distance of point to the line extending the edge
double d = Math.abs(crossProduct) / vectorNode0To1.length();
if(d <= deviation){
edgeContains = true;
}
}
return edgeContains;
}
}
CSS 样式表:
...
edge{
fill-color: black;
size: 2px;
arrow-shape: none;
shape: line;
text-mode: hidden;
}
edge.mouseOver {
fill-color: red;
stroke-color: red;
text-mode: normal;
text-background-mode: plain; /*plain or none*/
text-background-color: rgba(255, 255, 255, 200);
text-alignment: along;
}
我想在鼠标悬停在边上时显示边的权重。
所以我在实现的 MouseManager 中使用了 MouseEvent.MOUSE_MOVED
。对于节点,我只需调用 view.findGraphicElementAt(getManagedTypes(), event.getX(), event.getY())
即可获取 GraphicNode 对象。不幸的是,边没有 one x 和 y 值,因此无法通过此方法找到。是的,我知道 getX()
和 getY()
是为 GraphicEdge 实现的,但只是指向边缘的中心。
我需要边缘对象来获取存储在边缘的更多信息(例如重量)。那么如何使用 x、y 或我可以从接收到的 MouseEvent 中检索的其他一些值来获取边缘对象?
实际上,MouseOverMouseManager
或 FxMouseOverMouseManager
中已经实现了边缘选择、mouseOver 和 mouseLeft 功能(其中包括悬停在边缘上)。调用 view.enableMouseOptions()
时会自动设置此管理器,但由于某些其他原因我已经实现了一个单独的 MouseManager 并且仅当将鼠标悬停在边缘中心时才检测到悬停在边缘上。所以我的解决方案是将代码从 MouseOverMouseManager
复制到 MyMousemanager 并修改它。
编辑:
public class CompoundListNetworkMouseManager extends FxMouseManager {
private GraphicElement hoveredElement;
private long hoveredElementLastChanged;
private ReentrantLock hoverLock = new ReentrantLock();
private Timer hoverTimer = new Timer(true);
private HoverTimerTask latestHoverTimerTask;
/**
*(copied from the GraphsStream Library)
* The mouse needs to stay on an element for at least this amount of milliseconds,
* until the element gets the attribute "ui.mouseOver" assigned.
* A value smaller or equal to zero indicates, that the attribute is assigned without delay.
* */
private final long delayHover;
public CompoundListNetworkMouseManager(){
super(EnumSet.of(InteractiveElement.NODE, InteractiveElement.EDGE));
this.delayHover = 100;
}
@Override
public void init(GraphicGraph graph, View view) {
this.graph = graph;
this.view = view;
view.addListener(MouseEvent.MOUSE_MOVED, mouseMoved);
}
@Override
public void release() {
view.removeListener(MouseEvent.MOUSE_MOVED, mouseMoved);
}
@Override
public EnumSet<InteractiveElement> getManagedTypes() {
return super.getManagedTypes();
}
protected void mouseOverElement(GraphicElement element){
element.setAttribute("ui.mouseOver", true);
element.setAttribute("ui.class", "mouseOver"); //I defined a class/type for edges in the CSS styling sheet that is calles "mouseOver"
if(element instanceof GraphicEdge) {
mouseOverEdge((GraphicEdge) element);
}
else if(element instanceof GraphicNode){
mouseOverNode((GraphicNode) element);
}
protected void mouseOverEdge(GraphicEdge graphicEdge) {
view.freezeElement(graphicEdge, true);
Edge edge = graph.getEdge(graphicEdge.getId());
System.out.println("Mouse over edge " + edge.getId());
}
protected void mouseLeftElement(GraphicElement element) {
this.hoveredElement = null;
element.removeAttribute("ui.mouseOver");
element.removeAttribute("ui.class");
}
EventHandler<MouseEvent> mouseMoved = new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
try {
hoverLock.lockInterruptibly();
boolean stayedOnElement = false;
curElement = view.findGraphicElementAt(getManagedTypes(), event.getX(), event.getY());
//adjusted implementation of search for edges
if(curElement == null && getManagedTypes().contains(InteractiveElement.EDGE)){
curElement = (GraphicElement) findEdgeAt(event.getX(), event.getY());
}
if(hoveredElement != null) {
//check if mouse stayed on the same element to avoid the mouseOverEvent being processed multiple times
stayedOnElement = curElement == null ? false : curElement.equals(hoveredElement);
if (!stayedOnElement && hoveredElement.hasAttribute("ui.mouseOver")) {
mouseLeftElement(hoveredElement);
}
}
if (!stayedOnElement && curElement != null) {
if (delayHover <= 0) {
mouseOverElement(curElement);
} else {
hoveredElement = curElement;
hoveredElementLastChanged = Calendar.getInstance().getTimeInMillis();
if (latestHoverTimerTask != null) {
latestHoverTimerTask.cancel();
}
latestHoverTimerTask = new HoverTimerTask(hoveredElementLastChanged, hoveredElement);
hoverTimer.schedule(latestHoverTimerTask, delayHover);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
hoverLock.unlock();
}
}
};
//copied from GraphStream Library
private final class HoverTimerTask extends TimerTask {
private final long lastChanged;
private final GraphicElement element;
public HoverTimerTask(long lastChanged, GraphicElement element) {
this.lastChanged = lastChanged;
this.element = element;
}
@Override
public void run() {
try {
hoverLock.lock();
if (hoveredElementLastChanged == lastChanged) {
mouseOverElement(element);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
hoverLock.unlock();
}
}
}
//findGraphicElement could be used but I wanted to implement the edgeContains method myself
private Edge findEdgeAt(double x, double y){
Camera cam = view.getCamera();
GraphMetrics metrics = cam.getMetrics();
//transform x and y
double xT = x + metrics.viewport[0];
double yT = y + metrics.viewport[0];
Edge edgeFound = null;
if (getManagedTypes().contains(InteractiveElement.EDGE)) {
Optional<Edge> edge = graph.edges().filter(e -> edgeContains((GraphicEdge) e, xT, yT)).findFirst();
if (edge.isPresent()) {
if (cam.isVisible((GraphicElement) edge.get())) {
edgeFound = edge.get();
}
}
}
return edgeFound;
}
//new edgeContains() method that finds edge at hovering not only when hovered over edge center
private boolean edgeContains(GraphicEdge edge, double x, double y) {
Camera cam = view.getCamera();
GraphMetrics metrics = cam.getMetrics();
Values size = edge.getStyle().getSize();
double deviation = metrics.lengthToPx(size, 0);
Point3 edgeNode0 = cam.transformGuToPx(edge.from.x, edge.from.y, 0);
Point3 edgeNode1 = cam.transformGuToPx(edge.to.x, edge.to.y, 0);
//check of point x,y is between nodes of the edge
boolean edgeContains = false;
//check x,y range
if(x > Math.min(edgeNode0.x, edgeNode1.x) - deviation
&& x < Math.max(edgeNode0.x, edgeNode1.x) + deviation
&& y > Math.min(edgeNode0.y, edgeNode1.y) - deviation
&& y < Math.max(edgeNode0.y, edgeNode1.y) + deviation){
//check deviation from edge
Vector2 vectorNode0To1 = new Vector2(edgeNode0, edgeNode1);
Point2 point = new Point2(x, y);
Vector2 vectorNode0ToPoint = new Vector2(edgeNode0, point);
//cross product of vectorNode0ToPoint and vectorNode0to1
double crossProduct = vectorNode0ToPoint.x() * vectorNode0To1.y() - vectorNode0To1.x() * vectorNode0ToPoint.y();
//distance of point to the line extending the edge
double d = Math.abs(crossProduct) / vectorNode0To1.length();
if(d <= deviation){
edgeContains = true;
}
}
return edgeContains;
}
}
CSS 样式表:
...
edge{
fill-color: black;
size: 2px;
arrow-shape: none;
shape: line;
text-mode: hidden;
}
edge.mouseOver {
fill-color: red;
stroke-color: red;
text-mode: normal;
text-background-mode: plain; /*plain or none*/
text-background-color: rgba(255, 255, 255, 200);
text-alignment: along;
}