From: Tuukka Lehtonen Date: Mon, 10 Oct 2016 12:04:10 +0000 (+0300) Subject: Sync git svn branch with SVN repository r33319. X-Git-Tag: v1.25.0~72^2 X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=commitdiff_plain;h=7684baeb8bc7963700676af20db6f4a860581e46 Sync git svn branch with SVN repository r33319. refs #6744 [PRIVATE-5991] refs #6745 refs #6748 --- diff --git a/bundles/org.simantics.diagram.ontology/graph.tg b/bundles/org.simantics.diagram.ontology/graph.tg index eda90ce84..72053ec5b 100644 Binary files a/bundles/org.simantics.diagram.ontology/graph.tg and b/bundles/org.simantics.diagram.ontology/graph.tg differ diff --git a/bundles/org.simantics.diagram.ontology/graph/DiagramProfiles.pgraph b/bundles/org.simantics.diagram.ontology/graph/DiagramProfiles.pgraph index 652ea695b..072bf49d8 100644 --- a/bundles/org.simantics.diagram.ontology/graph/DiagramProfiles.pgraph +++ b/bundles/org.simantics.diagram.ontology/graph/DiagramProfiles.pgraph @@ -79,4 +79,8 @@ DIA.Profile.defaultEnabled DIA.ConfigurableProfile object : builder.getContents()) { + TransferableGraph1 tg = ClipboardUtils.accept(graph, object, SimanticsKeys.KEY_TRANSFERABLE_GRAPH); + TransferableGraphs.importGraph1(graph, tg, advisor); + } + } return instance; } diff --git a/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/Content.java b/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/Content.java index ba933d71c..eb957c8f6 100644 --- a/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/Content.java +++ b/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/Content.java @@ -12,23 +12,27 @@ *******************************************************************************/ package org.simantics.document.server.io; +import java.io.InputStream; + public class Content { - private byte[] data; + private InputStream input; private String mimeType; private long lastModified; + private int length; - public Content(byte[] data, String mimeType, long lastModified) { - this.data = data; + public Content(InputStream input, String mimeType, long lastModified, int length) { + this.input = input; this.mimeType = mimeType; this.lastModified = lastModified; + this.length = length; } - public byte[] getData() { - return data; + public InputStream getInputStream() { + return input; } - public void setData(byte[] data) { - this.data = data; + public void setInputStream(InputStream input) { + this.input = input; } public String getMimeType() { @@ -46,4 +50,12 @@ public class Content { public void setLastModified(long lastModified) { this.lastModified = lastModified; } + + public int getLength() { + return length; + } + + public void setLength(int length) { + this.length = length; + } } diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncActiveModelTypicals.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncActiveModelTypicals.java index bbe8111e5..882e04f0d 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncActiveModelTypicals.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncActiveModelTypicals.java @@ -80,6 +80,7 @@ public class SyncActiveModelTypicals { RuleChooserDialog.RuleResult result = RuleChooserDialog.choose(shell, msg.toString(), activeModelTypicalTemplates); if(result == null) return; + session.markUndoPoint(); SyncTypicalTemplatesToInstances req = new SyncTypicalTemplatesToInstances(result.selectedRules, activeModelTypicalTemplates).logging(result.logging); session.syncRequest(req); if (result.logging) { diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalInstanceWithTemplate.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalInstanceWithTemplate.java index 86bfdc0bb..b0d52e355 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalInstanceWithTemplate.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalInstanceWithTemplate.java @@ -79,6 +79,7 @@ public class SyncCurrentTypicalInstanceWithTemplate { RuleChooserDialog.RuleResult result = RuleChooserDialog.choose(shell, "Synchronizing typical instance with its template.", new Resource[] { input.getResource() }); if(result == null) return; + session.markUndoPoint(); SyncTypicalTemplatesToInstances req = SyncTypicalTemplatesToInstances.syncSingleInstance(result.selectedRules, input.getResource()).logging(result.logging); session.syncRequest(req); if (result.logging) { diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalTemplateToInstances.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalTemplateToInstances.java index f262cd56e..d3d1de389 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalTemplateToInstances.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalTemplateToInstances.java @@ -77,6 +77,7 @@ public class SyncCurrentTypicalTemplateToInstances { RuleChooserDialog.RuleResult result = RuleChooserDialog.choose(shell, "Synchronizing typical template to all its instances.", new Resource[] { input.getResource() }); if(result == null) return; + session.markUndoPoint(); SyncTypicalTemplatesToInstances req = new SyncTypicalTemplatesToInstances(result.selectedRules, input.getResource()).logging(result.logging); session.syncRequest(req); if (result.logging) { diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/SyncActiveModelTypicals.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/SyncActiveModelTypicals.java index 5ba8b9f2d..441702dd5 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/SyncActiveModelTypicals.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/SyncActiveModelTypicals.java @@ -80,6 +80,7 @@ public class SyncActiveModelTypicals extends AbstractHandler { RuleChooserDialog.RuleResult result = RuleChooserDialog.choose(shell, msg.toString(), activeModelTypicalTemplates); if(result == null) return null; + session.markUndoPoint(); SyncTypicalTemplatesToInstances req = new SyncTypicalTemplatesToInstances(result.selectedRules, activeModelTypicalTemplates).logging(result.logging); session.syncRequest(req); if (result.logging) { diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/SyncCurrentTypicalInstanceWithTemplate.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/SyncCurrentTypicalInstanceWithTemplate.java index df049a671..3b1455f37 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/SyncCurrentTypicalInstanceWithTemplate.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/SyncCurrentTypicalInstanceWithTemplate.java @@ -44,6 +44,7 @@ public class SyncCurrentTypicalInstanceWithTemplate extends AbstractHandler { RuleChooserDialog.RuleResult result = RuleChooserDialog.choose(shell, "Synchronizing typical instance with its template.", new Resource[] { input.getResource() }); if(result == null) return null; + session.markUndoPoint(); SyncTypicalTemplatesToInstances req = SyncTypicalTemplatesToInstances.syncSingleInstance(result.selectedRules, input.getResource()).logging(result.logging); session.syncRequest(req); if (result.logging) { diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/SyncCurrentTypicalTemplateToInstances.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/SyncCurrentTypicalTemplateToInstances.java index 931e3f7fa..c4454831e 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/SyncCurrentTypicalTemplateToInstances.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/typicals/SyncCurrentTypicalTemplateToInstances.java @@ -43,6 +43,7 @@ public class SyncCurrentTypicalTemplateToInstances extends AbstractHandler { RuleChooserDialog.RuleResult result = RuleChooserDialog.choose(shell, "Synchronizing typical template to all its instances in the currently active model.", new Resource[] { input.getResource() }); if(result == null) return null; + session.markUndoPoint(); SyncTypicalTemplatesToInstances req = new SyncTypicalTemplatesToInstances(result.selectedRules, input.getResource()).logging(result.logging); session.syncRequest(req); if (result.logging) { diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/typicals/SyncTypicalTemplatesToInstances.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/typicals/SyncTypicalTemplatesToInstances.java index cd3260513..83407bb04 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/typicals/SyncTypicalTemplatesToInstances.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/typicals/SyncTypicalTemplatesToInstances.java @@ -50,6 +50,7 @@ import org.simantics.diagram.handler.CopyPasteStrategy; import org.simantics.diagram.handler.ElementObjectAssortment; import org.simantics.diagram.handler.PasteOperation; import org.simantics.diagram.handler.Paster; +import org.simantics.diagram.handler.Paster.RouteLine; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.diagram.synchronization.CollectingModificationQueue; import org.simantics.diagram.synchronization.CopyAdvisor; @@ -204,9 +205,9 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { */ protected Map copyMap; - final private Map> messageLogs = new HashMap>(); + final private Map> messageLogs = new HashMap<>(); - public List logs = new ArrayList(); + public List logs = new ArrayList<>(); private boolean writeLog; @@ -293,7 +294,7 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { if(indexRoot == null) throw new DatabaseException("FATAL: Diagram is not under any index root."); List log = messageLogs.get(indexRoot); if(log == null) { - log = new ArrayList(); + log = new ArrayList<>(); messageLogs.put(indexRoot, log); } return log; @@ -329,7 +330,7 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { this.syncCtx.set(ModelingSynchronizationHints.MODELING_RESOURCE, ModelingResources.getInstance(graph)); this.metadata = new TypicalSynchronizationMetadata(); - this.metadata.synchronizedTypicals = new ArrayList(); + this.metadata.synchronizedTypicals = new ArrayList<>(); this.temporaryDiagram = Diagram.spawnNew(DiagramClass.DEFAULT); this.temporaryDiagram.setHint(SynchronizationHints.CONTEXT, syncCtx); @@ -363,7 +364,7 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { Collection libs = graph.syncRequest(new ObjectsWithType(indexRoot, L0.ConsistsOf, DOC.DocumentLibrary)); if(libs.isEmpty()) continue; - List nrs = new ArrayList(); + List nrs = new ArrayList<>(); for(Resource lib : libs) nrs.add(new NamedResource(NameUtils.getSafeName(graph, lib), lib)); Collections.sort(nrs, AlphanumComparator.CASE_INSENSITIVE_COMPARATOR); Resource library = nrs.iterator().next().getResource(); @@ -414,7 +415,7 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { if (instances.isEmpty()) return; - Set templateElements = new THashSet( graph.syncRequest( + Set templateElements = new THashSet<>( graph.syncRequest( new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) ); try { @@ -435,7 +436,7 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { if (template == null) return; - Set templateElements = new THashSet( graph.syncRequest( + Set templateElements = new THashSet<>( graph.syncRequest( new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) ); try { @@ -498,7 +499,7 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { // therefore clone the query result. typicalInfoBean = (TypicalInfoBean) typicalInfoBean.clone(); typicalInfoBean.templateElements = currentTemplateElements; - typicalInfoBean.auxiliary = new HashMap(1); + typicalInfoBean.auxiliary = new HashMap<>(1); TypicalInfo info = new TypicalInfo(); info.monitor = monitor; @@ -522,12 +523,12 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { // instance elements that do not have a MOD.HasElementSource // relation but have a MOD.IsTemplatized tag. Set instanceElementsRemovedFromTemplate = findInstanceElementsRemovedFromTemplate( - graph, info, new THashSet(dSizeAbs)); + graph, info, new THashSet<>(dSizeAbs)); // Find elements in template that do not yet exist in the instance Set templateElementsAddedToTemplate = findTemplateElementsMissingFromInstance( graph, currentTemplateElements, info, - new THashSet(dSizeAbs)); + new THashSet<>(dSizeAbs)); Set changedTemplateElements = changedElementsByDiagram.removeValues(template); @@ -624,7 +625,7 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { ElementObjectAssortment assortment = new ElementObjectAssortment(graph, elementsAddedToTemplate); if (copyMap == null) - copyMap = new THashMap(); + copyMap = new THashMap<>(); else copyMap.clear(); @@ -665,7 +666,7 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { ModelingResources MOD = ModelingResources.getInstance(graph); Resource instanceComposite = graph.getPossibleObject(instance, MOD.DiagramToComposite); - List instanceComponents = new ArrayList(elementsAddedToTemplate.size()); + List instanceComponents = new ArrayList<>(elementsAddedToTemplate.size()); // Post-process added elements after typicalInfo has been updated and // template mapping statements are in place. @@ -871,6 +872,17 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { return changed; } + private static class Connector { + public final Resource attachmentRelation; + public final Resource connector; + public RouteLine attachedTo; + + public Connector(Resource attachmentRelation, Resource connector) { + this.attachmentRelation = attachmentRelation; + this.connector = connector; + } + } + /** * Synchronizes two route graph connection topologies if and only if the * destination connection is not attached to any node elements besides @@ -901,19 +913,28 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { cu = new ConnectionUtil(graph); // 0.1. find mappings between source and target connection connectors - Collection targetConnectors = graph.getObjects(targetConnection, DIA.HasConnector); - for (Resource targetConnector : targetConnectors) { + Collection toTargetConnectors = graph.getStatements(targetConnection, DIA.HasConnector); + Map targetConnectors = new THashMap<>(toTargetConnectors.size()); + for (Statement toTargetConnector : toTargetConnectors) { + Resource targetConnector = toTargetConnector.getObject(); + targetConnectors.put(targetConnector, new Connector(toTargetConnector.getPredicate(), targetConnector)); Statement toNode = cu.getConnectedComponentStatement(targetConnection, targetConnector); if (toNode == null) { // Corrupted target connection! - ErrorLogger.defaultLogError("Encountered corrupted typical template connection " + NameUtils.getSafeName(graph, targetConnection, true) + " with a stray DIA.Connector instance " + NameUtils.getSafeName(graph, targetConnector, true), new Exception("trace")); + ErrorLogger.defaultLogError("Encountered corrupted typical template connection " + + NameUtils.getSafeName(graph, targetConnection, true) + " with a stray DIA.Connector instance " + + NameUtils.getSafeName(graph, targetConnector, true) + " that is not attached to any element.", + new Exception("trace")); return false; } - - // Check that the target connections does not connect to - // non-templatized elements before syncing. - if (!graph.hasStatement(toNode.getObject(), MOD.IsTemplatized)) + if (!graph.hasStatement(targetConnector, DIA.AreConnected)) { + // Corrupted target connection! + ErrorLogger.defaultLogError("Encountered corrupted typical template connection " + + NameUtils.getSafeName(graph, targetConnection, true) + " with a stray DIA.Connector instance " + + NameUtils.getSafeName(graph, targetConnector, true) + " that is not connected to any other route node.", + new Exception("trace")); return false; + } //Resource templateNode = typicalInfo.instanceToTemplate.get(toNode.getObject()); Resource templateNode = graph.getPossibleObject(toNode.getObject(), MOD.HasElementSource); @@ -928,7 +949,7 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { t2s.put(targetConnector, templateConnector); if (DEBUG) - System.out.println("Mapping connector " + debug(typicalInfo, "Mapping connector " + NameUtils.getSafeName(graph, templateConnector, true) + " to " + NameUtils.getSafeName(graph, targetConnector, true)); } @@ -940,14 +961,17 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { // 0.2. find mapping between source and target route lines Collection sourceInteriorRouteNodes = graph.getObjects(sourceConnection, DIA.HasInteriorRouteNode); Collection targetInteriorRouteNodes = graph.getObjects(targetConnection, DIA.HasInteriorRouteNode); - Map sourceToRouteLine = new THashMap(); - Map targetToRouteLine = new THashMap(); + Map sourceToRouteLine = new THashMap<>(); + Map targetToRouteLine = new THashMap<>(); for (Resource source : sourceInteriorRouteNodes) sourceToRouteLine.put(source, Paster.readRouteLine(graph, source)); for (Resource target : targetInteriorRouteNodes) targetToRouteLine.put(target, Paster.readRouteLine(graph, target)); + Map originalSourceToRouteLine = new THashMap<>(sourceToRouteLine); + Map originalTargetToRouteLine = new THashMap<>(targetToRouteLine); + nextSourceLine: for (Iterator> sourceIt = sourceToRouteLine.entrySet().iterator(); !targetToRouteLine.isEmpty() && sourceIt.hasNext();) { Map.Entry sourceEntry = sourceIt.next(); @@ -961,7 +985,7 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { targetIt.remove(); if (DEBUG) - System.out.println("Mapping routeline " + debug(typicalInfo, "Mapping routeline " + NameUtils.getSafeName(graph, sourceEntry.getKey(), true) + " - " + sourceEntry.getValue() + " to " + NameUtils.getSafeName(graph, targetEntry.getKey(), true) @@ -973,20 +997,40 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { } if (DEBUG) { - System.out.println("Take 1: Source to target route nodes map : " + s2t); - System.out.println("Take 1: Target to source route nodes map : " + t2s); + debug(typicalInfo, "Take 1: Source to target route nodes map : " + s2t); + debug(typicalInfo, "Take 1: Target to source route nodes map : " + t2s); } - // 1.1 remove excess connectors - for (Resource targetConnector : targetConnectors) { - if (!t2s.containsKey(targetConnector)) { - typicalInfo.messageLog.add("\t\t\tremove excess connector from target connection: " + NameUtils.getSafeName(graph, targetConnector)); - cu.removeConnectionPart(targetConnector); - changed = true; + // 1.1. Temporarily disconnect instance-specific connectors from the the connection . + // They will be added back to the connection after the templatized parts of the + // connection have been synchronized. + + // Stores diagram connectors that are customizations in the synchronized instance. + List instanceOnlyConnectors = null; + + for (Connector connector : targetConnectors.values()) { + if (!t2s.containsKey(connector.connector)) { + typicalInfo.messageLog.add("\t\tencountered instance-specific diagram connector in target connection: " + NameUtils.getSafeName(graph, connector.connector)); + + // Find the RouteLine this connectors is connected to. + for (Resource rl : graph.getObjects(connector.connector, DIA.AreConnected)) { + connector.attachedTo = originalTargetToRouteLine.get(rl); + if (connector.attachedTo != null) + break; + } + + // Disconnect connector from connection + graph.deny(targetConnection, connector.attachmentRelation, connector.connector); + graph.deny(connector.connector, DIA.AreConnected); + + // Keep track of the disconnected connector + if (instanceOnlyConnectors == null) + instanceOnlyConnectors = new ArrayList<>(targetConnectors.size()); + instanceOnlyConnectors.add(connector); } } - // 1.2 add missing connectors to target + // 1.2. add missing connectors to target Collection sourceConnectors = graph.getObjects(sourceConnection, DIA.HasConnector); for (Resource sourceConnector : sourceConnectors) { if (!s2t.containsKey(sourceConnector)) { @@ -1016,7 +1060,7 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { // 2. sync route lines and their connectivity: // 2.1. assign correspondences in target for each source route line - // by reusing excess routelines in target and by creating new + // by reusing excess route lines in target and by creating new // route lines. Resource[] targetRouteLines = targetToRouteLine.keySet().toArray(Resource.NONE); @@ -1027,7 +1071,7 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { Resource source = sourceEntry.getKey(); Paster.RouteLine sourceLine = sourceEntry.getValue(); - typicalInfo.messageLog.add("\t\t\tassign an instance-side routeline complement for " + NameUtils.getSafeName(graph, source, true) + " - " + sourceLine); + typicalInfo.messageLog.add("\t\t\tassign an instance-side routeline counterpart for " + NameUtils.getSafeName(graph, source, true) + " - " + sourceLine); // Assign target route line for source Resource target = null; @@ -1051,16 +1095,16 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { } if (targetRouteLine >= 0) { - typicalInfo.messageLog.add("\t\t\tremove excess route lines (" + (targetRouteLine + 1) + ") from target connection"); + typicalInfo.messageLog.add("\t\t\tremove excess route lines (" + (targetRouteLine + 1) + ") from target connection"); for (; targetRouteLine >= 0; targetRouteLine--) { - typicalInfo.messageLog.add("\t\t\t\tremove excess route line: " + NameUtils.getSafeName(graph, targetRouteLines[targetRouteLine], true)); + typicalInfo.messageLog.add("\t\t\t\tremove excess route line: " + NameUtils.getSafeName(graph, targetRouteLines[targetRouteLine], true)); cu.removeConnectionPart(targetRouteLines[targetRouteLine]); } } if (DEBUG) { - System.out.println("Take 2: Source to target route nodes map : " + s2t); - System.out.println("Take 2: Target to source route nodes map : " + t2s); + debug(typicalInfo, "Take 2: Source to target route nodes map : " + s2t); + debug(typicalInfo, "Take 2: Target to source route nodes map : " + t2s); } // 2.2. Synchronize target connection topology (DIA.AreConnected) @@ -1071,9 +1115,100 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { changed |= cu.removeExtraInteriorRouteNodes(targetConnection) > 0; changed |= cu.removeUnusedConnectors(targetConnection) > 0; + // 3.1. Ensure that all mapped route nodes in the target connection + // are tagged with MOD.IsTemplatized. Future synchronization + // can then take advantage of this information to more easily + // decide which parts of the connection are originated from + // the template and which are not. + changed |= markMappedRouteNodesTemplatized(graph, s2t.values()); + + // 4. Add temporarily disconnected instance-specific connectors + // back to the synchronized connection. The route line to attach + // to is based on a simple heuristic. + if (instanceOnlyConnectors != null) { + if (originalSourceToRouteLine.isEmpty()) { + // If there are 0 route lines in the template connection, + // then one must be added to the instance connection. + // This can only happen if the template connection is + // simple, i.e. just between two terminals without any + // custom routing. + + // Attach all target connection connectors to the newly created route line + Resource rl = cu.newRouteLine(targetConnection, null, null); + for (Resource sourceConnector : sourceConnectors) { + Resource targetConnector = s2t.get(sourceConnector); + graph.deny(targetConnector, DIA.AreConnected); + graph.claim(targetConnector, DIA.AreConnected, DIA.AreConnected, rl); + } + + // Copy orientation and position for new route line from original target route lines. + // This is a simplification that will attach any amount of route lines in the original + // target connection into just one route line. There is room for improvement here + // but it will require a more elaborate algorithm to find and cut the non-templatized + // route lines as well as connectors out of the connection before synchronizing it. + // + // TODO: This implementation chooses the added route line position at random if + // there are multiple route lines in the target connection. + if (!originalTargetToRouteLine.isEmpty()) { + RouteLine originalRl = originalTargetToRouteLine.values().iterator().next(); + setRouteLine(graph, rl, originalRl); + } + + // Attach the instance specific connectors also to the only route line + for (Connector connector : instanceOnlyConnectors) { + graph.claim(targetConnection, connector.attachmentRelation, connector.connector); + graph.claim(connector.connector, DIA.AreConnected, DIA.AreConnected, rl); + } + + changed = true; + } else { + for (Connector connector : instanceOnlyConnectors) { + // Find the route line that most closely matches the original + // route line that the connector was connected to. + Resource closestMatch = null; + double closestDistance = Double.MAX_VALUE; + if (connector.attachedTo != null) { + for (Map.Entry sourceLine : originalSourceToRouteLine.entrySet()) { + double dist = distance(sourceLine.getValue(), connector.attachedTo); + if (dist < closestDistance) { + closestMatch = s2t.get(sourceLine.getKey()); + closestDistance = dist; + } + } + } else { + closestMatch = originalSourceToRouteLine.keySet().iterator().next(); + } + graph.claim(targetConnection, connector.attachmentRelation, connector.connector); + graph.claim(connector.connector, DIA.AreConnected, DIA.AreConnected, closestMatch); + if (closestDistance > 0) + changed = true; + typicalInfo.messageLog.add("\t\t\treattached instance-specific connector " + + NameUtils.getSafeName(graph, connector.connector) + " to nearest existing route line " + + NameUtils.getSafeName(graph, closestMatch) + " with distance " + closestDistance); + } + } + } + return changed; } + private boolean markMappedRouteNodesTemplatized(WriteGraph graph, Iterable routeNodes) throws DatabaseException { + boolean changed = false; + for (Resource rn : routeNodes) { + if (!graph.hasStatement(rn, MOD.IsTemplatized)) { + graph.claim(rn, MOD.IsTemplatized, MOD.IsTemplatized, rn); + changed = true; + } + } + return changed; + } + + private static double distance(RouteLine l1, RouteLine l2) { + double dist = Math.abs(l2.getPosition() - l1.getPosition()); + dist *= l2.isHorizontal() == l1.isHorizontal() ? 1 : 1000; + return dist; + } + private boolean connectRouteNodes(WriteGraph graph, TypicalInfo typicalInfo, Collection sourceRouteNodes) throws DatabaseException { boolean changed = false; for (Resource src : sourceRouteNodes) { @@ -1114,6 +1249,15 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { return changed; } + private void setRouteLine(WriteGraph graph, Resource line, double position, boolean horizontal) throws DatabaseException { + graph.claimLiteral(line, DIA.HasPosition, L0.Double, position, Bindings.DOUBLE); + graph.claimLiteral(line, DIA.IsHorizontal, L0.Boolean, horizontal, Bindings.BOOLEAN); + } + + private void setRouteLine(WriteGraph graph, Resource line, RouteLine rl) throws DatabaseException { + setRouteLine(graph, line, rl.getPosition(), rl.isHorizontal()); + } + private void copyRouteLine(WriteGraph graph, Resource src, Resource tgt) throws DatabaseException { Double pos = graph.getPossibleRelatedValue(src, DIA.HasPosition, Bindings.DOUBLE); Boolean hor = graph.getPossibleRelatedValue(src, DIA.IsHorizontal, Bindings.BOOLEAN); @@ -1142,9 +1286,16 @@ public class SyncTypicalTemplatesToInstances extends WriteRequest { private static Map newOrClear(Map current) { if (current == null) - return new THashMap(); + return new THashMap<>(); current.clear(); return current; } + private void debug(TypicalInfo typicalInfo, String message) { + if (DEBUG) { + System.out.println(message); + typicalInfo.messageLog.add(message); + } + } + } \ No newline at end of file diff --git a/bundles/org.simantics.scenegraph/.classpath b/bundles/org.simantics.scenegraph/.classpath index de670af55..47085403c 100644 --- a/bundles/org.simantics.scenegraph/.classpath +++ b/bundles/org.simantics.scenegraph/.classpath @@ -1,5 +1,8 @@ + + + diff --git a/bundles/org.simantics.scenegraph/META-INF/MANIFEST.MF b/bundles/org.simantics.scenegraph/META-INF/MANIFEST.MF index 80ef76d7c..dad71c00d 100644 --- a/bundles/org.simantics.scenegraph/META-INF/MANIFEST.MF +++ b/bundles/org.simantics.scenegraph/META-INF/MANIFEST.MF @@ -16,7 +16,10 @@ Require-Bundle: gnu.trove3;bundle-version="3.0.0", org.simantics.utils;bundle-version="1.1.0" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ClassPath: ., - lib/svgSalamander-tiny.jar + lib/svgSalamander-tiny.jar, + lib/batik-parser-1.8.jar, + lib/batik-awt-util-1.8.jar, + lib/batik-util-1.8.jar Export-Package: com.kitfox.svg, com.kitfox.svg.xml, org.simantics.scenegraph, diff --git a/bundles/org.simantics.scenegraph/build.properties b/bundles/org.simantics.scenegraph/build.properties index 03f349f6c..3dca05bdf 100644 --- a/bundles/org.simantics.scenegraph/build.properties +++ b/bundles/org.simantics.scenegraph/build.properties @@ -17,4 +17,8 @@ bin.includes = META-INF/,\ TROVE-README-license.txt,\ TROVE-LICENSE.txt,\ README.txt,\ - JSI-LICENSE.txt \ No newline at end of file + JSI-LICENSE.txt,\ + lib/batik-parser-1.8.jar,\ + lib/batik-awt-util-1.8.jar,\ + lib/batik-util-1.8.jar + diff --git a/bundles/org.simantics.scenegraph/lib/batik-awt-util-1.8.jar b/bundles/org.simantics.scenegraph/lib/batik-awt-util-1.8.jar new file mode 100644 index 000000000..ce0be3cd0 Binary files /dev/null and b/bundles/org.simantics.scenegraph/lib/batik-awt-util-1.8.jar differ diff --git a/bundles/org.simantics.scenegraph/lib/batik-parser-1.8.jar b/bundles/org.simantics.scenegraph/lib/batik-parser-1.8.jar new file mode 100644 index 000000000..01108ad7c Binary files /dev/null and b/bundles/org.simantics.scenegraph/lib/batik-parser-1.8.jar differ diff --git a/bundles/org.simantics.scenegraph/lib/batik-util-1.8.jar b/bundles/org.simantics.scenegraph/lib/batik-util-1.8.jar new file mode 100644 index 000000000..cb1c63f19 Binary files /dev/null and b/bundles/org.simantics.scenegraph/lib/batik-util-1.8.jar differ diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/ActionShapes.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/ActionShapes.java new file mode 100644 index 000000000..cc44d372a --- /dev/null +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/ActionShapes.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2016 Association for Decentralized Information Management in + * Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.scenegraph.g2d.nodes.connection; + +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.io.StringReader; + +import org.apache.batik.parser.AWTPathProducer; +import org.apache.batik.parser.ParseException; + +/** + * @author Tuukka Lehtonen + * @since 1.22.2, 1.25.0 + */ +class ActionShapes { + + private static final String SCISSOR_PATH = "M 1.0204323,-0.337727 C 0.92746851,-0.449273 0.76034565,-0.444851 0.6356318,-0.396443 l -0.78340687,0.296247 c -0.22844697,-0.124111 -0.45371804,-0.08771 -0.45389716,-0.148477 -1.3989e-4,-0.0475 0.0435015,-0.03722 0.0366086,-0.160853 -0.006619,-0.118717 -0.1308295,-0.206188 -0.24796642,-0.198065 -0.11720479,-3.56e-4 -0.23843025,0.08983 -0.23910595,0.213676 -0.00836,0.124786 0.096897,0.240343 0.221027,0.248145 0.14544563,0.02121 0.40264226,-0.06761 0.5240569,0.148471 -0.0894972,0.166266 -0.24914468,0.167198 -0.39351418,0.159315 -0.11985922,-0.0065 -0.26369532,0.02823 -0.32050912,0.146186 -0.0549,0.113051 -2.567e-4,0.27357 0.12665686,0.307747 0.12812523,0.04659 0.30371326,-0.01328 0.33371488,-0.160538 0.0231253,-0.113504 -0.0573489,-0.166568 -0.0266378,-0.207838 0.0231733,-0.03113 0.17097889,-0.01358 0.4338543,-0.13258 l 0.85013888,0.296862 c 0.10739722,0.02964 0.23851917,0.02826 0.33326488,-0.07755 L 0.14842838,0.004094 1.0204312,-0.337731 Z m -1.7489069,-0.176336 c 0.12383244,0.06866 0.11428878,0.255942 -0.0140755,0.292584 -0.11605716,0.0408 -0.2648432,-0.0717 -0.2281757,-0.197724 0.021388,-0.103377 0.15747907,-0.141864 0.24225133,-0.09486 z m 0.007633,0.765633 c 0.12914301,0.04727 0.1078809,0.265594 -0.0232155,0.295316 -0.0869168,0.03046 -0.2114303,-0.01258 -0.2205326,-0.115113 -0.017329,-0.124578 0.12880443,-0.237615 0.24374818,-0.180208 z"; + + private static final String CROSS_PATH = "M 0.82205219,-1.16919 0.00195748,-0.3491 -0.81813723,-1.16919 -1.1707871,-0.81654 -0.35069244,0.00355 -1.1707871,0.82364 -0.81813723,1.17629 0.00195748,0.3562 0.82205219,1.17629 1.1747021,0.82364 0.35460739,0.00355 l 0,0 0.82009471,-0.82009 z"; + + static Shape parsePath(String path, AffineTransform xform) { + try { + Shape s = AWTPathProducer.createShape(new StringReader(path), PathIterator.WIND_EVEN_ODD); + return xform != null ? xform.createTransformedShape(s) : s; + } catch (ParseException | IOException e) { + // Should not happen + throw new Error(e); + } + } + + static Shape transformShape(Shape shape, double scaleX, double scaleY, double offsetX, double offsetY, double rotate) { + AffineTransform tr = new AffineTransform(); + tr.translate(offsetX, offsetY); + tr.scale(scaleX, scaleY); + if (rotate != 0) + tr.rotate(rotate); + return tr.createTransformedShape(shape); + } + + public static final Shape SCISSOR_SHAPE = parsePath(SCISSOR_PATH, null); + public static final Shape CROSS_SHAPE = parsePath(CROSS_PATH, null); + + private static final Rectangle2D SCISSOR_BOUNDS = SCISSOR_SHAPE.getBounds2D(); + private static final Rectangle2D CROSS_BOUNDS = CROSS_SHAPE.getBounds2D(); + + public static final double SCISSOR_WIDTH = SCISSOR_BOUNDS.getWidth(); + public static final double SCISSOR_HEIGHT = SCISSOR_BOUNDS.getHeight(); + + public static final double CROSS_WIDTH = CROSS_BOUNDS.getWidth(); + public static final double CROSS_HEIGHT = CROSS_BOUNDS.getHeight(); + +} diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/HighlightActionPointsAction.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/HighlightActionPointsAction.java index 04d08e3ec..7f7bd3cd9 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/HighlightActionPointsAction.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/HighlightActionPointsAction.java @@ -13,23 +13,23 @@ package org.simantics.scenegraph.g2d.nodes.connection; import java.awt.AlphaComposite; import java.awt.BasicStroke; +import java.awt.Color; import java.awt.Composite; import java.awt.Graphics2D; +import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.net.URL; import java.util.ArrayList; import java.util.Collection; -import javax.imageio.ImageIO; - import org.simantics.diagram.connection.RouteGraph; import org.simantics.diagram.connection.RouteLineHalf; import org.simantics.diagram.connection.actions.IAction; import org.simantics.diagram.connection.rendering.IRouteGraphRenderer; +import org.simantics.scenegraph.utils.Quality; +import org.simantics.scenegraph.utils.QualityHints; /** * @author Tuukka Lehtonen @@ -67,26 +67,25 @@ public class HighlightActionPointsAction implements IAction { } } - static BufferedImage cross; - static BufferedImage cut; + private static final Shape CROSS_SHAPE = ActionShapes.CROSS_SHAPE; + private static final Shape SCISSOR_SHAPE = ActionShapes.transformShape(ActionShapes.SCISSOR_SHAPE, 1, 1, 0, 0, -Math.PI/2); - static { - cross = safeReadImage("cross.png"); - cut = safeReadImage("cut.png"); - } + private static final Color CROSS_COLOR = new Color(0xe4, 0x40, 0x61); + private static final Color SCISSOR_COLOR = new Color(20, 20, 20); public static final Stroke STROKE = new BasicStroke(0.1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); - public static final AlphaComposite COMPOSITE = AlphaComposite.SrcOver.derive(0.6f); + public static final AlphaComposite NO_HIT_COMPOSITE = AlphaComposite.SrcOver.derive(0.8f); + public static final AlphaComposite HIT_COMPOSITE = AlphaComposite.SrcOver.derive(0.2f); public static final double DEGENERATED_LINE_LENGTH = 1; - public static final double CUT_DIST_FROM_END = 0.5; + public static final double CUT_DIST_FROM_END = 0.75; RouteGraph rg; transient Collection lhs = new ArrayList(); transient AffineTransform transform = new AffineTransform(); - transient AffineTransform transform2 = new AffineTransform(); transient Rectangle2D rect = new Rectangle2D.Double(); + transient Point2D point = new Point2D.Double(); public HighlightActionPointsAction(RouteGraph rg) { this.rg = rg; @@ -98,94 +97,73 @@ public class HighlightActionPointsAction implements IAction { @Override public void render(Graphics2D g, IRouteGraphRenderer renderer, double mouseX, double mouseY) { - // Cannot perform cut or delete segment actions on connections between 2 - // terminals. + // Cannot perform cut or delete segment actions + // on connections between 2 terminals. boolean simpleConnection = (rg.isSimpleConnection() || rg.getTerminals().size() <= 2); boolean branchedConnection = rg.getTerminals().size() > 2; + if (!branchedConnection || simpleConnection) + return; + + AffineTransform preTr = g.getTransform(); + double realViewScale = 1.0 / getScale(preTr); + //System.out.println(realViewScale); + // Don't render any of the actions if they could not be seen anyway. + if (realViewScale > 0.7) + return; lhs.clear(); rg.getLineHalves(lhs); - AffineTransform preTr = g.getTransform(); - double viewScale = 1.0 / getScale(preTr); - transform2.setToScale(viewScale, viewScale); + Pick pick = pickAction(rg, lhs, preTr, mouseX, mouseY); + Composite originalComposite = g.getComposite(); - g.setComposite(COMPOSITE); + Composite basicComposite = pick.action != null ? HIT_COMPOSITE : NO_HIT_COMPOSITE; + g.setComposite(basicComposite); - Pick pick = pickAction(rg, lhs, preTr, mouseX, mouseY); + // Always render these in high quality because otherwise the shapes + // will render with ugly artifacts when zoom level is a bit higher. + QualityHints origQualityHints = QualityHints.getQuality(g); + QualityHints.getHints(Quality.HIGH).setQuality(g); + // Render line removal markers if (!simpleConnection) { - double crossW = cross.getWidth()*viewScale*.5; - double crossH = cross.getHeight()*viewScale*.5; - - // Render line removal markers + g.setPaint(CROSS_COLOR); for (RouteLineHalf lh : lhs) { -// if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH) -// continue; -// if (!lh.getLine().isTransient()) -// continue; - if (lh.getLine().getTerminal() == null) + if (removeLocation(lh, point) == null) continue; - double x = lh.getLink().getX(); - double y = lh.getLink().getY(); - if (lh.getLine().isHorizontal()) { - x = (lh.getLine().getBegin().getX() + lh.getLine().getEnd().getX()) * .5; - } else { - y = (lh.getLine().getBegin().getY() + lh.getLine().getEnd().getY()) * .5; - } - boolean hit = pick.matches(Action.REMOVE, lh); - if (hit) g.setComposite(originalComposite); - transform.setToTranslation(x-crossW, y-crossH); - g.transform(transform); - g.drawImage(cross, transform2, null); + g.translate(point.getX(), point.getY()); + g.fill(CROSS_SHAPE); g.setTransform(preTr); if (hit) - g.setComposite(COMPOSITE); + g.setComposite(basicComposite); } } // Render reconnection markers if the connection is branched. if (branchedConnection) { - double cutW = cut.getWidth()*viewScale*.5; - double cutH = cut.getHeight()*viewScale*.5; - - final double dist = CUT_DIST_FROM_END; + g.setPaint(SCISSOR_COLOR); for (RouteLineHalf lh : lhs) { - if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH*3) + if (reconnectLocation(lh, point) == null) continue; - double x = lh.getLink().getX(); - double y = lh.getLink().getY(); - if (lh.getLine().isHorizontal()) { - if (lh.getLink() == lh.getLine().getBegin()) - x += dist*2; - else - x -= dist*2; - } else { - if (lh.getLink() == lh.getLine().getBegin()) - y += dist*2; - else - y -= dist*2; - } - boolean hit = pick.matches(Action.RECONNECT, lh); - if (hit) g.setComposite(originalComposite); - transform.setToTranslation(x-cutW, y-cutH); - if (!lh.getLine().isHorizontal()) { - transform.rotate(Math.PI/2, cutW, cutH); - } + transform.setToTranslation(point.getX(), point.getY()); + if (!lh.getLine().isHorizontal()) + transform.rotate(Math.PI/2); + transform.translate(0, 0.35); g.transform(transform); - g.drawImage(cut, transform2, null); + g.fill(SCISSOR_SHAPE); g.setTransform(preTr); if (hit) - g.setComposite(COMPOSITE); + g.setComposite(basicComposite); } } + origQualityHints.setQuality(g); g.setComposite(originalComposite); } @@ -205,35 +183,23 @@ public class HighlightActionPointsAction implements IAction { if (!branchedConnection || simpleConnection || viewTr == null) return Pick.MISS; - lhs.clear(); - rg.getLineHalves(lhs); + double viewScale = 1.0 / getScale(viewTr); + if (viewScale > 0.7) + return Pick.MISS; + double nearest = Double.MAX_VALUE; RouteLineHalf selected = null; Action selectedAction = null; - double nearest = Double.MAX_VALUE; - double viewScale = 1.0 / getScale(viewTr); + // Pick line removal markers if (!simpleConnection) { - double crossW = cross.getWidth()*viewScale*.5; - double crossH = cross.getHeight()*viewScale*.5; - - // Render line removal markers + double s = ActionShapes.CROSS_WIDTH * 0.25; for (RouteLineHalf lh : lhs) { -// if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH) -// continue; -// if (!lh.getLine().isTransient()) -// continue; - if (lh.getLine().getTerminal() == null) + if (removeLocation(lh, point) == null) continue; - double x = lh.getLink().getX(); - double y = lh.getLink().getY(); - if (lh.getLine().isHorizontal()) { - x = (lh.getLine().getBegin().getX() + lh.getLine().getEnd().getX()) * .5; - } else { - y = (lh.getLine().getBegin().getY() + lh.getLine().getEnd().getY()) * .5; - } - - rect.setFrameFromCenter(x, y, x-crossW, y-crossH); + double x = point.getX(); + double y = point.getY(); + rect.setFrameFromCenter(x, y, x-s, y-s); boolean hit = rect.contains(mouseX, mouseY); if (hit) { double distSq = distSq(x, y, mouseX, mouseY); @@ -246,35 +212,21 @@ public class HighlightActionPointsAction implements IAction { } } - // Render reconnection markers if the connection is branched. + // Pick reconnection markers if the connection is branched. if (branchedConnection) { - double cutW = cut.getWidth()*viewScale*.5; - double cutH = cut.getHeight()*viewScale*.5; - - final double dist = CUT_DIST_FROM_END; + double w = ActionShapes.SCISSOR_HEIGHT * 0.4; + double h = ActionShapes.SCISSOR_WIDTH * 0.3; for (RouteLineHalf lh : lhs) { - if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH*3) + if (reconnectLocation(lh, point) == null) continue; - double x = lh.getLink().getX(); - double y = lh.getLink().getY(); - if (lh.getLine().isHorizontal()) { - if (lh.getLink() == lh.getLine().getBegin()) - x += dist*2; - else - x -= dist*2; - } else { - if (lh.getLink() == lh.getLine().getBegin()) - y += dist*2; - else - y -= dist*2; - } - - rect.setFrameFromCenter(x, y, x-cutW, y-cutH); + double x = point.getX(); + double y = point.getY(); + rect.setFrameFromCenter(x, y, x-w, y-h); boolean hit = rect.contains(mouseX, mouseY); if (hit) { double distSq = distSq(x, y, mouseX, mouseY); if (distSq < nearest) { - nearest = dist; + nearest = distSq; selected = lh; selectedAction = Action.RECONNECT; } @@ -285,6 +237,43 @@ public class HighlightActionPointsAction implements IAction { return selected == null ? Pick.MISS : new Pick(selectedAction, selected); } + private static Point2D removeLocation(RouteLineHalf lh, Point2D p) { + if (lh.getLine().getTerminal() == null) + return null; + + double x = lh.getLink().getX(); + double y = lh.getLink().getY(); + if (lh.getLine().isHorizontal()) { + x = (lh.getLine().getBegin().getX() + lh.getLine().getEnd().getX()) * .5; + } else { + y = (lh.getLine().getBegin().getY() + lh.getLine().getEnd().getY()) * .5; + } + p.setLocation(x, y); + return p; + } + + private static Point2D reconnectLocation(RouteLineHalf lh, Point2D p) { + if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH*3) + return null; + + final double dist = CUT_DIST_FROM_END; + double x = lh.getLink().getX(); + double y = lh.getLink().getY(); + if (lh.getLine().isHorizontal()) { + if (lh.getLink() == lh.getLine().getBegin()) + x += dist*2; + else + x -= dist*2; + } else { + if (lh.getLink() == lh.getLine().getBegin()) + y += dist*2; + else + y -= dist*2; + } + p.setLocation(x, y); + return p; + } + private static double distSq(double x1, double y1, double x2, double y2) { double dx = x2 - x1; double dy = y2 - y1; @@ -301,13 +290,4 @@ public class HighlightActionPointsAction implements IAction { return Math.sqrt(Math.abs(m00*m11 - m10*m01)); } - private static BufferedImage safeReadImage(String name) { - try { - URL url = HighlightActionPointsAction.class.getResource(name); - return ImageIO.read(url); - } catch (IOException e) { - return null; - } - } - }