\r
DIA.ConfigurableProfile <T L0.Entity\r
\r
+DIA.HasTemplate <R L0.ConsistsOf\r
+ L0.HasDomain DIA.ProfileEntry\r
+ L0.HasRange DIA.ProfileEntry\r
+\r
\r
public final Resource HasSymbolContributionFilter;\r
public final Resource HasSymbol_Inverse;\r
public final Resource HasTailConnector;\r
+ public final Resource HasTemplate;\r
+ public final Resource HasTemplate_Inverse;\r
public final Resource HasText;\r
public final Resource HasText_Inverse;\r
public final Resource HasTransform;\r
public static final String HasSymbolContributionFilter = "http://www.simantics.org/Diagram-2.2/HasSymbolContributionFilter";\r
public static final String HasSymbol_Inverse = "http://www.simantics.org/Diagram-2.2/HasSymbol/Inverse";\r
public static final String HasTailConnector = "http://www.simantics.org/Diagram-2.2/HasTailConnector";\r
+ public static final String HasTemplate = "http://www.simantics.org/Diagram-2.2/HasTemplate";\r
+ public static final String HasTemplate_Inverse = "http://www.simantics.org/Diagram-2.2/HasTemplate/Inverse";\r
public static final String HasText = "http://www.simantics.org/Diagram-2.2/HasText";\r
public static final String HasText_Inverse = "http://www.simantics.org/Diagram-2.2/HasText/Inverse";\r
public static final String HasTransform = "http://www.simantics.org/Diagram-2.2/HasTransform";\r
HasSymbolContributionFilter = getResourceOrNull(graph, URIs.HasSymbolContributionFilter);\r
HasSymbol_Inverse = getResourceOrNull(graph, URIs.HasSymbol_Inverse);\r
HasTailConnector = getResourceOrNull(graph, URIs.HasTailConnector);\r
+ HasTemplate = getResourceOrNull(graph, URIs.HasTemplate);\r
+ HasTemplate_Inverse = getResourceOrNull(graph, URIs.HasTemplate_Inverse);\r
HasText = getResourceOrNull(graph, URIs.HasText);\r
HasText_Inverse = getResourceOrNull(graph, URIs.HasText_Inverse);\r
HasTransform = getResourceOrNull(graph, URIs.HasTransform);\r
import java.util.HashMap;\r
import java.util.List;\r
import java.util.Map;\r
+import java.util.Set;\r
\r
import org.simantics.databoard.Bindings;\r
import org.simantics.db.ReadGraph;\r
import org.simantics.db.Resource;\r
import org.simantics.db.WriteGraph;\r
+import org.simantics.db.WriteOnlyGraph;\r
import org.simantics.db.common.request.WriteRequest;\r
import org.simantics.db.common.utils.ListUtils;\r
import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.adapter.impl.DefaultCopyHandler;\r
+import org.simantics.db.layer0.adapter.impl.DefaultPasteImportAdvisor;\r
+import org.simantics.db.layer0.util.ClipboardUtils;\r
+import org.simantics.db.layer0.util.SimanticsClipboard;\r
+import org.simantics.db.layer0.util.SimanticsClipboardImpl;\r
+import org.simantics.db.layer0.util.SimanticsKeys;\r
import org.simantics.db.layer0.variable.Variable;\r
import org.simantics.db.service.VirtualGraphSupport;\r
import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.graph.db.TransferableGraphs;\r
+import org.simantics.graph.representation.Root;\r
+import org.simantics.graph.representation.TransferableGraph1;\r
import org.simantics.layer0.Layer0;\r
import org.simantics.modeling.ModelingResources;\r
import org.simantics.simulation.ontology.SimulationResource;\r
for (Resource r : enabled) {\r
graph.claim(p, SIM.IsActive, r);\r
}\r
-\r
}\r
});\r
}\r
Double priority = graph.getPossibleRelatedValue(entry, DIA.ProfileEntry_HasPriority, Bindings.DOUBLE);\r
if (priority != null) {\r
graph.claimLiteral(instance, DIA.ProfileEntry_HasPriority, priority, Bindings.DOUBLE);\r
+ }\r
+ for (Resource template : graph.getObjects(entry, DIA.HasTemplate)) {\r
+ SimanticsClipboardImpl builder = new SimanticsClipboardImpl();\r
+ DefaultCopyHandler handler = new DefaultCopyHandler(template);\r
+ DefaultPasteImportAdvisor advisor = new DefaultPasteImportAdvisor(instance) {\r
+ @Override\r
+ public Resource createRoot(WriteOnlyGraph graph, Root root, Resource resource)\r
+ throws DatabaseException {\r
+ Layer0 l0 = graph.getService(Layer0.class);\r
+ DiagramResource DIA = graph.getService(DiagramResource.class); \r
+ \r
+ if(resource == null) resource = graph.newResource();\r
+ \r
+ graph.claim(library, DIA.HasTemplate, DIA.HasTemplate_Inverse, resource);\r
+ \r
+ String newName = getName(root);\r
+ graph.addLiteral(resource, l0.HasName, l0.NameOf, l0.String, newName, Bindings.STRING);\r
+ \r
+ addRootInfo(root, newName, resource);\r
+ \r
+ return resource;\r
+ }\r
+ };\r
+ handler.copyToClipboard(graph, builder);\r
+ for(Set<SimanticsClipboard.Representation> object : builder.getContents()) {\r
+ TransferableGraph1 tg = ClipboardUtils.accept(graph, object, SimanticsKeys.KEY_TRANSFERABLE_GRAPH);\r
+ TransferableGraphs.importGraph1(graph, tg, advisor);\r
+ }\r
+ \r
}\r
return instance;\r
}\r
*******************************************************************************/\r
package org.simantics.document.server.io;\r
\r
+import java.io.InputStream;\r
+\r
public class Content {\r
- private byte[] data;\r
+ private InputStream input;\r
private String mimeType;\r
private long lastModified;\r
+ private int length;\r
\r
- public Content(byte[] data, String mimeType, long lastModified) {\r
- this.data = data;\r
+ public Content(InputStream input, String mimeType, long lastModified, int length) {\r
+ this.input = input;\r
this.mimeType = mimeType;\r
this.lastModified = lastModified;\r
+ this.length = length;\r
}\r
\r
- public byte[] getData() {\r
- return data;\r
+ public InputStream getInputStream() {\r
+ return input;\r
}\r
\r
- public void setData(byte[] data) {\r
- this.data = data;\r
+ public void setInputStream(InputStream input) {\r
+ this.input = input;\r
}\r
\r
public String getMimeType() {\r
public void setLastModified(long lastModified) {\r
this.lastModified = lastModified;\r
}\r
+\r
+ public int getLength() {\r
+ return length;\r
+ }\r
+\r
+ public void setLength(int length) {\r
+ this.length = length;\r
+ }\r
}\r
RuleChooserDialog.RuleResult result = RuleChooserDialog.choose(shell, msg.toString(), activeModelTypicalTemplates);\r
if(result == null) return;\r
\r
+ session.markUndoPoint();\r
SyncTypicalTemplatesToInstances req = new SyncTypicalTemplatesToInstances(result.selectedRules, activeModelTypicalTemplates).logging(result.logging); \r
session.syncRequest(req);\r
if (result.logging) {\r
RuleChooserDialog.RuleResult result = RuleChooserDialog.choose(shell, "Synchronizing typical instance with its template.", new Resource[] { input.getResource() });\r
if(result == null) return;\r
\r
+ session.markUndoPoint();\r
SyncTypicalTemplatesToInstances req = SyncTypicalTemplatesToInstances.syncSingleInstance(result.selectedRules, input.getResource()).logging(result.logging); \r
session.syncRequest(req);\r
if (result.logging) {\r
RuleChooserDialog.RuleResult result = RuleChooserDialog.choose(shell, "Synchronizing typical template to all its instances.", new Resource[] { input.getResource() });\r
if(result == null) return;\r
\r
+ session.markUndoPoint();\r
SyncTypicalTemplatesToInstances req = new SyncTypicalTemplatesToInstances(result.selectedRules, input.getResource()).logging(result.logging); \r
session.syncRequest(req);\r
if (result.logging) {\r
RuleChooserDialog.RuleResult result = RuleChooserDialog.choose(shell, msg.toString(), activeModelTypicalTemplates);\r
if(result == null) return null;\r
\r
+ session.markUndoPoint();\r
SyncTypicalTemplatesToInstances req = new SyncTypicalTemplatesToInstances(result.selectedRules, activeModelTypicalTemplates).logging(result.logging); \r
session.syncRequest(req);\r
if (result.logging) {\r
RuleChooserDialog.RuleResult result = RuleChooserDialog.choose(shell, "Synchronizing typical instance with its template.", new Resource[] { input.getResource() });\r
if(result == null) return null;\r
\r
+ session.markUndoPoint();\r
SyncTypicalTemplatesToInstances req = SyncTypicalTemplatesToInstances.syncSingleInstance(result.selectedRules, input.getResource()).logging(result.logging); \r
session.syncRequest(req);\r
if (result.logging) {\r
RuleChooserDialog.RuleResult result = RuleChooserDialog.choose(shell, "Synchronizing typical template to all its instances in the currently active model.", new Resource[] { input.getResource() });\r
if(result == null) return null;\r
\r
+ session.markUndoPoint();\r
SyncTypicalTemplatesToInstances req = new SyncTypicalTemplatesToInstances(result.selectedRules, input.getResource()).logging(result.logging); \r
session.syncRequest(req);\r
if (result.logging) {\r
import org.simantics.diagram.handler.ElementObjectAssortment;\r
import org.simantics.diagram.handler.PasteOperation;\r
import org.simantics.diagram.handler.Paster;\r
+import org.simantics.diagram.handler.Paster.RouteLine;\r
import org.simantics.diagram.stubs.DiagramResource;\r
import org.simantics.diagram.synchronization.CollectingModificationQueue;\r
import org.simantics.diagram.synchronization.CopyAdvisor;\r
*/\r
protected Map<Object, Object> copyMap;\r
\r
- final private Map<Resource, List<String>> messageLogs = new HashMap<Resource, List<String>>();\r
+ final private Map<Resource, List<String>> messageLogs = new HashMap<>();\r
\r
- public List<Resource> logs = new ArrayList<Resource>();\r
+ public List<Resource> logs = new ArrayList<>();\r
\r
private boolean writeLog;\r
\r
if(indexRoot == null) throw new DatabaseException("FATAL: Diagram is not under any index root.");\r
List<String> log = messageLogs.get(indexRoot);\r
if(log == null) {\r
- log = new ArrayList<String>();\r
+ log = new ArrayList<>();\r
messageLogs.put(indexRoot, log);\r
}\r
return log;\r
this.syncCtx.set(ModelingSynchronizationHints.MODELING_RESOURCE, ModelingResources.getInstance(graph));\r
\r
this.metadata = new TypicalSynchronizationMetadata();\r
- this.metadata.synchronizedTypicals = new ArrayList<Resource>();\r
+ this.metadata.synchronizedTypicals = new ArrayList<>();\r
\r
this.temporaryDiagram = Diagram.spawnNew(DiagramClass.DEFAULT);\r
this.temporaryDiagram.setHint(SynchronizationHints.CONTEXT, syncCtx);\r
Collection<Resource> libs = graph.syncRequest(new ObjectsWithType(indexRoot, L0.ConsistsOf, DOC.DocumentLibrary));\r
if(libs.isEmpty()) continue;\r
\r
- List<NamedResource> nrs = new ArrayList<NamedResource>();\r
+ List<NamedResource> nrs = new ArrayList<>();\r
for(Resource lib : libs) nrs.add(new NamedResource(NameUtils.getSafeName(graph, lib), lib));\r
Collections.sort(nrs, AlphanumComparator.CASE_INSENSITIVE_COMPARATOR);\r
Resource library = nrs.iterator().next().getResource();\r
if (instances.isEmpty())\r
return;\r
\r
- Set<Resource> templateElements = new THashSet<Resource>( graph.syncRequest(\r
+ Set<Resource> templateElements = new THashSet<>( graph.syncRequest(\r
new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) );\r
\r
try {\r
if (template == null)\r
return;\r
\r
- Set<Resource> templateElements = new THashSet<Resource>( graph.syncRequest(\r
+ Set<Resource> templateElements = new THashSet<>( graph.syncRequest(\r
new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) );\r
\r
try {\r
// therefore clone the query result.\r
typicalInfoBean = (TypicalInfoBean) typicalInfoBean.clone();\r
typicalInfoBean.templateElements = currentTemplateElements;\r
- typicalInfoBean.auxiliary = new HashMap<Object, Object>(1);\r
+ typicalInfoBean.auxiliary = new HashMap<>(1);\r
\r
TypicalInfo info = new TypicalInfo();\r
info.monitor = monitor;\r
// instance elements that do not have a MOD.HasElementSource\r
// relation but have a MOD.IsTemplatized tag.\r
Set<Resource> instanceElementsRemovedFromTemplate = findInstanceElementsRemovedFromTemplate(\r
- graph, info, new THashSet<Resource>(dSizeAbs));\r
+ graph, info, new THashSet<>(dSizeAbs));\r
\r
// Find elements in template that do not yet exist in the instance\r
Set<Resource> templateElementsAddedToTemplate = findTemplateElementsMissingFromInstance(\r
graph, currentTemplateElements, info,\r
- new THashSet<Resource>(dSizeAbs));\r
+ new THashSet<>(dSizeAbs));\r
\r
Set<Resource> changedTemplateElements = changedElementsByDiagram.removeValues(template);\r
\r
\r
ElementObjectAssortment assortment = new ElementObjectAssortment(graph, elementsAddedToTemplate);\r
if (copyMap == null)\r
- copyMap = new THashMap<Object, Object>();\r
+ copyMap = new THashMap<>();\r
else\r
copyMap.clear();\r
\r
\r
ModelingResources MOD = ModelingResources.getInstance(graph);\r
Resource instanceComposite = graph.getPossibleObject(instance, MOD.DiagramToComposite);\r
- List<Resource> instanceComponents = new ArrayList<Resource>(elementsAddedToTemplate.size());\r
+ List<Resource> instanceComponents = new ArrayList<>(elementsAddedToTemplate.size());\r
\r
// Post-process added elements after typicalInfo has been updated and\r
// template mapping statements are in place.\r
return changed;\r
}\r
\r
+ private static class Connector {\r
+ public final Resource attachmentRelation;\r
+ public final Resource connector;\r
+ public RouteLine attachedTo;\r
+\r
+ public Connector(Resource attachmentRelation, Resource connector) {\r
+ this.attachmentRelation = attachmentRelation;\r
+ this.connector = connector;\r
+ }\r
+ }\r
+\r
/**\r
* Synchronizes two route graph connection topologies if and only if the\r
* destination connection is not attached to any node elements besides\r
cu = new ConnectionUtil(graph);\r
\r
// 0.1. find mappings between source and target connection connectors\r
- Collection<Resource> targetConnectors = graph.getObjects(targetConnection, DIA.HasConnector);\r
- for (Resource targetConnector : targetConnectors) {\r
+ Collection<Statement> toTargetConnectors = graph.getStatements(targetConnection, DIA.HasConnector);\r
+ Map<Resource, Connector> targetConnectors = new THashMap<>(toTargetConnectors.size());\r
+ for (Statement toTargetConnector : toTargetConnectors) {\r
+ Resource targetConnector = toTargetConnector.getObject();\r
+ targetConnectors.put(targetConnector, new Connector(toTargetConnector.getPredicate(), targetConnector));\r
Statement toNode = cu.getConnectedComponentStatement(targetConnection, targetConnector);\r
if (toNode == null) {\r
// Corrupted target connection!\r
- 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"));\r
+ ErrorLogger.defaultLogError("Encountered corrupted typical template connection "\r
+ + NameUtils.getSafeName(graph, targetConnection, true) + " with a stray DIA.Connector instance "\r
+ + NameUtils.getSafeName(graph, targetConnector, true) + " that is not attached to any element.",\r
+ new Exception("trace"));\r
return false;\r
}\r
-\r
- // Check that the target connections does not connect to\r
- // non-templatized elements before syncing.\r
- if (!graph.hasStatement(toNode.getObject(), MOD.IsTemplatized))\r
+ if (!graph.hasStatement(targetConnector, DIA.AreConnected)) {\r
+ // Corrupted target connection!\r
+ ErrorLogger.defaultLogError("Encountered corrupted typical template connection "\r
+ + NameUtils.getSafeName(graph, targetConnection, true) + " with a stray DIA.Connector instance "\r
+ + NameUtils.getSafeName(graph, targetConnector, true) + " that is not connected to any other route node.",\r
+ new Exception("trace"));\r
return false;\r
+ }\r
\r
//Resource templateNode = typicalInfo.instanceToTemplate.get(toNode.getObject());\r
Resource templateNode = graph.getPossibleObject(toNode.getObject(), MOD.HasElementSource);\r
t2s.put(targetConnector, templateConnector);\r
\r
if (DEBUG)\r
- System.out.println("Mapping connector "\r
+ debug(typicalInfo, "Mapping connector "\r
+ NameUtils.getSafeName(graph, templateConnector, true)\r
+ " to " + NameUtils.getSafeName(graph, targetConnector, true));\r
}\r
// 0.2. find mapping between source and target route lines\r
Collection<Resource> sourceInteriorRouteNodes = graph.getObjects(sourceConnection, DIA.HasInteriorRouteNode);\r
Collection<Resource> targetInteriorRouteNodes = graph.getObjects(targetConnection, DIA.HasInteriorRouteNode);\r
- Map<Resource, Paster.RouteLine> sourceToRouteLine = new THashMap<Resource, Paster.RouteLine>();\r
- Map<Resource, Paster.RouteLine> targetToRouteLine = new THashMap<Resource, Paster.RouteLine>();\r
+ Map<Resource, Paster.RouteLine> sourceToRouteLine = new THashMap<>();\r
+ Map<Resource, Paster.RouteLine> targetToRouteLine = new THashMap<>();\r
\r
for (Resource source : sourceInteriorRouteNodes)\r
sourceToRouteLine.put(source, Paster.readRouteLine(graph, source));\r
for (Resource target : targetInteriorRouteNodes)\r
targetToRouteLine.put(target, Paster.readRouteLine(graph, target));\r
\r
+ Map<Resource, Paster.RouteLine> originalSourceToRouteLine = new THashMap<>(sourceToRouteLine);\r
+ Map<Resource, Paster.RouteLine> originalTargetToRouteLine = new THashMap<>(targetToRouteLine);\r
+\r
nextSourceLine:\r
for (Iterator<Map.Entry<Resource, Paster.RouteLine>> sourceIt = sourceToRouteLine.entrySet().iterator(); !targetToRouteLine.isEmpty() && sourceIt.hasNext();) {\r
Map.Entry<Resource, Paster.RouteLine> sourceEntry = sourceIt.next();\r
targetIt.remove();\r
\r
if (DEBUG)\r
- System.out.println("Mapping routeline "\r
+ debug(typicalInfo, "Mapping routeline "\r
+ NameUtils.getSafeName(graph, sourceEntry.getKey(), true)\r
+ " - " + sourceEntry.getValue()\r
+ " to " + NameUtils.getSafeName(graph, targetEntry.getKey(), true)\r
}\r
\r
if (DEBUG) {\r
- System.out.println("Take 1: Source to target route nodes map : " + s2t);\r
- System.out.println("Take 1: Target to source route nodes map : " + t2s);\r
+ debug(typicalInfo, "Take 1: Source to target route nodes map : " + s2t);\r
+ debug(typicalInfo, "Take 1: Target to source route nodes map : " + t2s);\r
}\r
\r
- // 1.1 remove excess connectors\r
- for (Resource targetConnector : targetConnectors) {\r
- if (!t2s.containsKey(targetConnector)) {\r
- typicalInfo.messageLog.add("\t\t\tremove excess connector from target connection: " + NameUtils.getSafeName(graph, targetConnector));\r
- cu.removeConnectionPart(targetConnector);\r
- changed = true;\r
+ // 1.1. Temporarily disconnect instance-specific connectors from the the connection .\r
+ // They will be added back to the connection after the templatized parts of the\r
+ // connection have been synchronized.\r
+\r
+ // Stores diagram connectors that are customizations in the synchronized instance.\r
+ List<Connector> instanceOnlyConnectors = null;\r
+\r
+ for (Connector connector : targetConnectors.values()) {\r
+ if (!t2s.containsKey(connector.connector)) {\r
+ typicalInfo.messageLog.add("\t\tencountered instance-specific diagram connector in target connection: " + NameUtils.getSafeName(graph, connector.connector));\r
+\r
+ // Find the RouteLine this connectors is connected to.\r
+ for (Resource rl : graph.getObjects(connector.connector, DIA.AreConnected)) {\r
+ connector.attachedTo = originalTargetToRouteLine.get(rl);\r
+ if (connector.attachedTo != null)\r
+ break;\r
+ }\r
+\r
+ // Disconnect connector from connection\r
+ graph.deny(targetConnection, connector.attachmentRelation, connector.connector);\r
+ graph.deny(connector.connector, DIA.AreConnected);\r
+\r
+ // Keep track of the disconnected connector\r
+ if (instanceOnlyConnectors == null)\r
+ instanceOnlyConnectors = new ArrayList<>(targetConnectors.size());\r
+ instanceOnlyConnectors.add(connector);\r
}\r
}\r
\r
- // 1.2 add missing connectors to target\r
+ // 1.2. add missing connectors to target\r
Collection<Resource> sourceConnectors = graph.getObjects(sourceConnection, DIA.HasConnector);\r
for (Resource sourceConnector : sourceConnectors) {\r
if (!s2t.containsKey(sourceConnector)) {\r
\r
// 2. sync route lines and their connectivity:\r
// 2.1. assign correspondences in target for each source route line\r
- // by reusing excess routelines in target and by creating new\r
+ // by reusing excess route lines in target and by creating new\r
// route lines.\r
\r
Resource[] targetRouteLines = targetToRouteLine.keySet().toArray(Resource.NONE);\r
Resource source = sourceEntry.getKey();\r
Paster.RouteLine sourceLine = sourceEntry.getValue();\r
\r
- typicalInfo.messageLog.add("\t\t\tassign an instance-side routeline complement for " + NameUtils.getSafeName(graph, source, true) + " - " + sourceLine);\r
+ typicalInfo.messageLog.add("\t\t\tassign an instance-side routeline counterpart for " + NameUtils.getSafeName(graph, source, true) + " - " + sourceLine);\r
\r
// Assign target route line for source\r
Resource target = null;\r
}\r
\r
if (targetRouteLine >= 0) {\r
- typicalInfo.messageLog.add("\t\t\tremove excess route lines (" + (targetRouteLine + 1) + ") from target connection");\r
+ typicalInfo.messageLog.add("\t\t\tremove excess route lines (" + (targetRouteLine + 1) + ") from target connection");\r
for (; targetRouteLine >= 0; targetRouteLine--) {\r
- typicalInfo.messageLog.add("\t\t\t\tremove excess route line: " + NameUtils.getSafeName(graph, targetRouteLines[targetRouteLine], true));\r
+ typicalInfo.messageLog.add("\t\t\t\tremove excess route line: " + NameUtils.getSafeName(graph, targetRouteLines[targetRouteLine], true));\r
cu.removeConnectionPart(targetRouteLines[targetRouteLine]);\r
}\r
}\r
\r
if (DEBUG) {\r
- System.out.println("Take 2: Source to target route nodes map : " + s2t);\r
- System.out.println("Take 2: Target to source route nodes map : " + t2s);\r
+ debug(typicalInfo, "Take 2: Source to target route nodes map : " + s2t);\r
+ debug(typicalInfo, "Take 2: Target to source route nodes map : " + t2s);\r
}\r
\r
// 2.2. Synchronize target connection topology (DIA.AreConnected)\r
changed |= cu.removeExtraInteriorRouteNodes(targetConnection) > 0;\r
changed |= cu.removeUnusedConnectors(targetConnection) > 0;\r
\r
+ // 3.1. Ensure that all mapped route nodes in the target connection\r
+ // are tagged with MOD.IsTemplatized. Future synchronization\r
+ // can then take advantage of this information to more easily\r
+ // decide which parts of the connection are originated from\r
+ // the template and which are not.\r
+ changed |= markMappedRouteNodesTemplatized(graph, s2t.values());\r
+\r
+ // 4. Add temporarily disconnected instance-specific connectors\r
+ // back to the synchronized connection. The route line to attach\r
+ // to is based on a simple heuristic.\r
+ if (instanceOnlyConnectors != null) {\r
+ if (originalSourceToRouteLine.isEmpty()) {\r
+ // If there are 0 route lines in the template connection,\r
+ // then one must be added to the instance connection.\r
+ // This can only happen if the template connection is\r
+ // simple, i.e. just between two terminals without any\r
+ // custom routing.\r
+\r
+ // Attach all target connection connectors to the newly created route line\r
+ Resource rl = cu.newRouteLine(targetConnection, null, null);\r
+ for (Resource sourceConnector : sourceConnectors) {\r
+ Resource targetConnector = s2t.get(sourceConnector);\r
+ graph.deny(targetConnector, DIA.AreConnected);\r
+ graph.claim(targetConnector, DIA.AreConnected, DIA.AreConnected, rl);\r
+ }\r
+\r
+ // Copy orientation and position for new route line from original target route lines.\r
+ // This is a simplification that will attach any amount of route lines in the original\r
+ // target connection into just one route line. There is room for improvement here\r
+ // but it will require a more elaborate algorithm to find and cut the non-templatized\r
+ // route lines as well as connectors out of the connection before synchronizing it.\r
+ //\r
+ // TODO: This implementation chooses the added route line position at random if\r
+ // there are multiple route lines in the target connection.\r
+ if (!originalTargetToRouteLine.isEmpty()) {\r
+ RouteLine originalRl = originalTargetToRouteLine.values().iterator().next();\r
+ setRouteLine(graph, rl, originalRl);\r
+ }\r
+\r
+ // Attach the instance specific connectors also to the only route line\r
+ for (Connector connector : instanceOnlyConnectors) {\r
+ graph.claim(targetConnection, connector.attachmentRelation, connector.connector);\r
+ graph.claim(connector.connector, DIA.AreConnected, DIA.AreConnected, rl);\r
+ }\r
+\r
+ changed = true;\r
+ } else {\r
+ for (Connector connector : instanceOnlyConnectors) {\r
+ // Find the route line that most closely matches the original\r
+ // route line that the connector was connected to.\r
+ Resource closestMatch = null;\r
+ double closestDistance = Double.MAX_VALUE;\r
+ if (connector.attachedTo != null) {\r
+ for (Map.Entry<Resource, Paster.RouteLine> sourceLine : originalSourceToRouteLine.entrySet()) {\r
+ double dist = distance(sourceLine.getValue(), connector.attachedTo);\r
+ if (dist < closestDistance) {\r
+ closestMatch = s2t.get(sourceLine.getKey());\r
+ closestDistance = dist;\r
+ }\r
+ }\r
+ } else {\r
+ closestMatch = originalSourceToRouteLine.keySet().iterator().next();\r
+ }\r
+ graph.claim(targetConnection, connector.attachmentRelation, connector.connector);\r
+ graph.claim(connector.connector, DIA.AreConnected, DIA.AreConnected, closestMatch);\r
+ if (closestDistance > 0)\r
+ changed = true;\r
+ typicalInfo.messageLog.add("\t\t\treattached instance-specific connector "\r
+ + NameUtils.getSafeName(graph, connector.connector) + " to nearest existing route line "\r
+ + NameUtils.getSafeName(graph, closestMatch) + " with distance " + closestDistance);\r
+ }\r
+ }\r
+ }\r
+\r
return changed;\r
}\r
\r
+ private boolean markMappedRouteNodesTemplatized(WriteGraph graph, Iterable<Resource> routeNodes) throws DatabaseException {\r
+ boolean changed = false;\r
+ for (Resource rn : routeNodes) {\r
+ if (!graph.hasStatement(rn, MOD.IsTemplatized)) {\r
+ graph.claim(rn, MOD.IsTemplatized, MOD.IsTemplatized, rn);\r
+ changed = true;\r
+ }\r
+ }\r
+ return changed;\r
+ }\r
+\r
+ private static double distance(RouteLine l1, RouteLine l2) {\r
+ double dist = Math.abs(l2.getPosition() - l1.getPosition());\r
+ dist *= l2.isHorizontal() == l1.isHorizontal() ? 1 : 1000;\r
+ return dist;\r
+ }\r
+\r
private boolean connectRouteNodes(WriteGraph graph, TypicalInfo typicalInfo, Collection<Resource> sourceRouteNodes) throws DatabaseException {\r
boolean changed = false;\r
for (Resource src : sourceRouteNodes) {\r
return changed;\r
}\r
\r
+ private void setRouteLine(WriteGraph graph, Resource line, double position, boolean horizontal) throws DatabaseException {\r
+ graph.claimLiteral(line, DIA.HasPosition, L0.Double, position, Bindings.DOUBLE);\r
+ graph.claimLiteral(line, DIA.IsHorizontal, L0.Boolean, horizontal, Bindings.BOOLEAN);\r
+ }\r
+\r
+ private void setRouteLine(WriteGraph graph, Resource line, RouteLine rl) throws DatabaseException {\r
+ setRouteLine(graph, line, rl.getPosition(), rl.isHorizontal());\r
+ }\r
+\r
private void copyRouteLine(WriteGraph graph, Resource src, Resource tgt) throws DatabaseException {\r
Double pos = graph.getPossibleRelatedValue(src, DIA.HasPosition, Bindings.DOUBLE);\r
Boolean hor = graph.getPossibleRelatedValue(src, DIA.IsHorizontal, Bindings.BOOLEAN);\r
\r
private static <K, V> Map<K, V> newOrClear(Map<K, V> current) {\r
if (current == null)\r
- return new THashMap<K, V>();\r
+ return new THashMap<>();\r
current.clear();\r
return current;\r
}\r
\r
+ private void debug(TypicalInfo typicalInfo, String message) {\r
+ if (DEBUG) {\r
+ System.out.println(message);\r
+ typicalInfo.messageLog.add(message);\r
+ }\r
+ }\r
+\r
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>\r
<classpath>\r
+ <classpathentry exported="true" kind="lib" path="lib/batik-awt-util-1.8.jar"/>\r
+ <classpathentry exported="true" kind="lib" path="lib/batik-util-1.8.jar"/>\r
+ <classpathentry exported="true" kind="lib" path="lib/batik-parser-1.8.jar"/>\r
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>\r
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
<classpathentry kind="src" path="src"/>\r
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,
TROVE-README-license.txt,\\r
TROVE-LICENSE.txt,\\r
README.txt,\\r
- JSI-LICENSE.txt
\ No newline at end of file
+ JSI-LICENSE.txt,\\r
+ lib/batik-parser-1.8.jar,\\r
+ lib/batik-awt-util-1.8.jar,\\r
+ lib/batik-util-1.8.jar\r
+\r
--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2016 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * Semantum Oy - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.scenegraph.g2d.nodes.connection;\r
+\r
+import java.awt.Shape;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.PathIterator;\r
+import java.awt.geom.Rectangle2D;\r
+import java.io.IOException;\r
+import java.io.StringReader;\r
+\r
+import org.apache.batik.parser.AWTPathProducer;\r
+import org.apache.batik.parser.ParseException;\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ * @since 1.22.2, 1.25.0\r
+ */\r
+class ActionShapes {\r
+\r
+ 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";\r
+\r
+ 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";\r
+\r
+ static Shape parsePath(String path, AffineTransform xform) {\r
+ try {\r
+ Shape s = AWTPathProducer.createShape(new StringReader(path), PathIterator.WIND_EVEN_ODD);\r
+ return xform != null ? xform.createTransformedShape(s) : s;\r
+ } catch (ParseException | IOException e) {\r
+ // Should not happen\r
+ throw new Error(e);\r
+ }\r
+ }\r
+\r
+ static Shape transformShape(Shape shape, double scaleX, double scaleY, double offsetX, double offsetY, double rotate) {\r
+ AffineTransform tr = new AffineTransform();\r
+ tr.translate(offsetX, offsetY);\r
+ tr.scale(scaleX, scaleY);\r
+ if (rotate != 0)\r
+ tr.rotate(rotate);\r
+ return tr.createTransformedShape(shape);\r
+ }\r
+\r
+ public static final Shape SCISSOR_SHAPE = parsePath(SCISSOR_PATH, null);\r
+ public static final Shape CROSS_SHAPE = parsePath(CROSS_PATH, null);\r
+\r
+ private static final Rectangle2D SCISSOR_BOUNDS = SCISSOR_SHAPE.getBounds2D();\r
+ private static final Rectangle2D CROSS_BOUNDS = CROSS_SHAPE.getBounds2D();\r
+\r
+ public static final double SCISSOR_WIDTH = SCISSOR_BOUNDS.getWidth();\r
+ public static final double SCISSOR_HEIGHT = SCISSOR_BOUNDS.getHeight();\r
+\r
+ public static final double CROSS_WIDTH = CROSS_BOUNDS.getWidth();\r
+ public static final double CROSS_HEIGHT = CROSS_BOUNDS.getHeight();\r
+\r
+}\r
\r
import java.awt.AlphaComposite;\r
import java.awt.BasicStroke;\r
+import java.awt.Color;\r
import java.awt.Composite;\r
import java.awt.Graphics2D;\r
+import java.awt.Shape;\r
import java.awt.Stroke;\r
import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Point2D;\r
import java.awt.geom.Rectangle2D;\r
-import java.awt.image.BufferedImage;\r
-import java.io.IOException;\r
-import java.net.URL;\r
import java.util.ArrayList;\r
import java.util.Collection;\r
\r
-import javax.imageio.ImageIO;\r
-\r
import org.simantics.diagram.connection.RouteGraph;\r
import org.simantics.diagram.connection.RouteLineHalf;\r
import org.simantics.diagram.connection.actions.IAction;\r
import org.simantics.diagram.connection.rendering.IRouteGraphRenderer;\r
+import org.simantics.scenegraph.utils.Quality;\r
+import org.simantics.scenegraph.utils.QualityHints;\r
\r
/**\r
* @author Tuukka Lehtonen\r
}\r
}\r
\r
- static BufferedImage cross;\r
- static BufferedImage cut;\r
+ private static final Shape CROSS_SHAPE = ActionShapes.CROSS_SHAPE;\r
+ private static final Shape SCISSOR_SHAPE = ActionShapes.transformShape(ActionShapes.SCISSOR_SHAPE, 1, 1, 0, 0, -Math.PI/2);\r
\r
- static {\r
- cross = safeReadImage("cross.png");\r
- cut = safeReadImage("cut.png");\r
- }\r
+ private static final Color CROSS_COLOR = new Color(0xe4, 0x40, 0x61);\r
+ private static final Color SCISSOR_COLOR = new Color(20, 20, 20);\r
\r
public static final Stroke STROKE = new BasicStroke(0.1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);\r
- public static final AlphaComposite COMPOSITE = AlphaComposite.SrcOver.derive(0.6f);\r
+ public static final AlphaComposite NO_HIT_COMPOSITE = AlphaComposite.SrcOver.derive(0.8f);\r
+ public static final AlphaComposite HIT_COMPOSITE = AlphaComposite.SrcOver.derive(0.2f);\r
\r
public static final double DEGENERATED_LINE_LENGTH = 1;\r
- public static final double CUT_DIST_FROM_END = 0.5;\r
+ public static final double CUT_DIST_FROM_END = 0.75;\r
\r
RouteGraph rg;\r
\r
transient Collection<RouteLineHalf> lhs = new ArrayList<RouteLineHalf>();\r
transient AffineTransform transform = new AffineTransform();\r
- transient AffineTransform transform2 = new AffineTransform();\r
transient Rectangle2D rect = new Rectangle2D.Double();\r
+ transient Point2D point = new Point2D.Double();\r
\r
public HighlightActionPointsAction(RouteGraph rg) {\r
this.rg = rg;\r
\r
@Override\r
public void render(Graphics2D g, IRouteGraphRenderer renderer, double mouseX, double mouseY) {\r
- // Cannot perform cut or delete segment actions on connections between 2\r
- // terminals.\r
+ // Cannot perform cut or delete segment actions\r
+ // on connections between 2 terminals.\r
boolean simpleConnection = (rg.isSimpleConnection() || rg.getTerminals().size() <= 2);\r
boolean branchedConnection = rg.getTerminals().size() > 2;\r
+ if (!branchedConnection || simpleConnection)\r
+ return;\r
+\r
+ AffineTransform preTr = g.getTransform();\r
+ double realViewScale = 1.0 / getScale(preTr);\r
+ //System.out.println(realViewScale);\r
+ // Don't render any of the actions if they could not be seen anyway.\r
+ if (realViewScale > 0.7)\r
+ return;\r
\r
lhs.clear();\r
rg.getLineHalves(lhs);\r
\r
- AffineTransform preTr = g.getTransform();\r
- double viewScale = 1.0 / getScale(preTr);\r
- transform2.setToScale(viewScale, viewScale);\r
+ Pick pick = pickAction(rg, lhs, preTr, mouseX, mouseY);\r
+\r
Composite originalComposite = g.getComposite();\r
- g.setComposite(COMPOSITE);\r
+ Composite basicComposite = pick.action != null ? HIT_COMPOSITE : NO_HIT_COMPOSITE;\r
+ g.setComposite(basicComposite);\r
\r
- Pick pick = pickAction(rg, lhs, preTr, mouseX, mouseY);\r
+ // Always render these in high quality because otherwise the shapes\r
+ // will render with ugly artifacts when zoom level is a bit higher.\r
+ QualityHints origQualityHints = QualityHints.getQuality(g);\r
+ QualityHints.getHints(Quality.HIGH).setQuality(g);\r
\r
+ // Render line removal markers\r
if (!simpleConnection) {\r
- double crossW = cross.getWidth()*viewScale*.5;\r
- double crossH = cross.getHeight()*viewScale*.5;\r
-\r
- // Render line removal markers\r
+ g.setPaint(CROSS_COLOR);\r
for (RouteLineHalf lh : lhs) {\r
-// if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH)\r
-// continue;\r
-// if (!lh.getLine().isTransient())\r
-// continue;\r
- if (lh.getLine().getTerminal() == null)\r
+ if (removeLocation(lh, point) == null)\r
continue;\r
- double x = lh.getLink().getX();\r
- double y = lh.getLink().getY();\r
- if (lh.getLine().isHorizontal()) {\r
- x = (lh.getLine().getBegin().getX() + lh.getLine().getEnd().getX()) * .5;\r
- } else {\r
- y = (lh.getLine().getBegin().getY() + lh.getLine().getEnd().getY()) * .5;\r
- }\r
-\r
boolean hit = pick.matches(Action.REMOVE, lh);\r
-\r
if (hit)\r
g.setComposite(originalComposite);\r
- transform.setToTranslation(x-crossW, y-crossH);\r
- g.transform(transform);\r
- g.drawImage(cross, transform2, null);\r
+ g.translate(point.getX(), point.getY());\r
+ g.fill(CROSS_SHAPE);\r
g.setTransform(preTr);\r
if (hit)\r
- g.setComposite(COMPOSITE);\r
+ g.setComposite(basicComposite);\r
}\r
}\r
\r
// Render reconnection markers if the connection is branched.\r
if (branchedConnection) {\r
- double cutW = cut.getWidth()*viewScale*.5;\r
- double cutH = cut.getHeight()*viewScale*.5;\r
-\r
- final double dist = CUT_DIST_FROM_END;\r
+ g.setPaint(SCISSOR_COLOR);\r
for (RouteLineHalf lh : lhs) {\r
- if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH*3)\r
+ if (reconnectLocation(lh, point) == null)\r
continue;\r
- double x = lh.getLink().getX();\r
- double y = lh.getLink().getY();\r
- if (lh.getLine().isHorizontal()) {\r
- if (lh.getLink() == lh.getLine().getBegin())\r
- x += dist*2;\r
- else\r
- x -= dist*2;\r
- } else {\r
- if (lh.getLink() == lh.getLine().getBegin())\r
- y += dist*2;\r
- else\r
- y -= dist*2;\r
- }\r
-\r
boolean hit = pick.matches(Action.RECONNECT, lh);\r
-\r
if (hit)\r
g.setComposite(originalComposite);\r
- transform.setToTranslation(x-cutW, y-cutH);\r
- if (!lh.getLine().isHorizontal()) {\r
- transform.rotate(Math.PI/2, cutW, cutH);\r
- }\r
+ transform.setToTranslation(point.getX(), point.getY());\r
+ if (!lh.getLine().isHorizontal())\r
+ transform.rotate(Math.PI/2);\r
+ transform.translate(0, 0.35);\r
g.transform(transform);\r
- g.drawImage(cut, transform2, null);\r
+ g.fill(SCISSOR_SHAPE);\r
g.setTransform(preTr);\r
if (hit)\r
- g.setComposite(COMPOSITE);\r
+ g.setComposite(basicComposite);\r
}\r
}\r
\r
+ origQualityHints.setQuality(g);\r
g.setComposite(originalComposite);\r
}\r
\r
if (!branchedConnection || simpleConnection || viewTr == null)\r
return Pick.MISS;\r
\r
- lhs.clear();\r
- rg.getLineHalves(lhs);\r
+ double viewScale = 1.0 / getScale(viewTr);\r
+ if (viewScale > 0.7)\r
+ return Pick.MISS;\r
\r
+ double nearest = Double.MAX_VALUE;\r
RouteLineHalf selected = null;\r
Action selectedAction = null;\r
- double nearest = Double.MAX_VALUE;\r
- double viewScale = 1.0 / getScale(viewTr);\r
\r
+ // Pick line removal markers\r
if (!simpleConnection) {\r
- double crossW = cross.getWidth()*viewScale*.5;\r
- double crossH = cross.getHeight()*viewScale*.5;\r
-\r
- // Render line removal markers\r
+ double s = ActionShapes.CROSS_WIDTH * 0.25;\r
for (RouteLineHalf lh : lhs) {\r
-// if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH)\r
-// continue;\r
-// if (!lh.getLine().isTransient())\r
-// continue;\r
- if (lh.getLine().getTerminal() == null)\r
+ if (removeLocation(lh, point) == null)\r
continue;\r
- double x = lh.getLink().getX();\r
- double y = lh.getLink().getY();\r
- if (lh.getLine().isHorizontal()) {\r
- x = (lh.getLine().getBegin().getX() + lh.getLine().getEnd().getX()) * .5;\r
- } else {\r
- y = (lh.getLine().getBegin().getY() + lh.getLine().getEnd().getY()) * .5;\r
- }\r
-\r
- rect.setFrameFromCenter(x, y, x-crossW, y-crossH);\r
+ double x = point.getX();\r
+ double y = point.getY();\r
+ rect.setFrameFromCenter(x, y, x-s, y-s);\r
boolean hit = rect.contains(mouseX, mouseY);\r
if (hit) {\r
double distSq = distSq(x, y, mouseX, mouseY);\r
}\r
}\r
\r
- // Render reconnection markers if the connection is branched.\r
+ // Pick reconnection markers if the connection is branched.\r
if (branchedConnection) {\r
- double cutW = cut.getWidth()*viewScale*.5;\r
- double cutH = cut.getHeight()*viewScale*.5;\r
-\r
- final double dist = CUT_DIST_FROM_END;\r
+ double w = ActionShapes.SCISSOR_HEIGHT * 0.4;\r
+ double h = ActionShapes.SCISSOR_WIDTH * 0.3;\r
for (RouteLineHalf lh : lhs) {\r
- if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH*3)\r
+ if (reconnectLocation(lh, point) == null)\r
continue;\r
- double x = lh.getLink().getX();\r
- double y = lh.getLink().getY();\r
- if (lh.getLine().isHorizontal()) {\r
- if (lh.getLink() == lh.getLine().getBegin())\r
- x += dist*2;\r
- else\r
- x -= dist*2;\r
- } else {\r
- if (lh.getLink() == lh.getLine().getBegin())\r
- y += dist*2;\r
- else\r
- y -= dist*2;\r
- }\r
-\r
- rect.setFrameFromCenter(x, y, x-cutW, y-cutH);\r
+ double x = point.getX();\r
+ double y = point.getY();\r
+ rect.setFrameFromCenter(x, y, x-w, y-h);\r
boolean hit = rect.contains(mouseX, mouseY);\r
if (hit) {\r
double distSq = distSq(x, y, mouseX, mouseY);\r
if (distSq < nearest) {\r
- nearest = dist;\r
+ nearest = distSq;\r
selected = lh;\r
selectedAction = Action.RECONNECT;\r
}\r
return selected == null ? Pick.MISS : new Pick(selectedAction, selected);\r
}\r
\r
+ private static Point2D removeLocation(RouteLineHalf lh, Point2D p) {\r
+ if (lh.getLine().getTerminal() == null)\r
+ return null;\r
+\r
+ double x = lh.getLink().getX();\r
+ double y = lh.getLink().getY();\r
+ if (lh.getLine().isHorizontal()) {\r
+ x = (lh.getLine().getBegin().getX() + lh.getLine().getEnd().getX()) * .5;\r
+ } else {\r
+ y = (lh.getLine().getBegin().getY() + lh.getLine().getEnd().getY()) * .5;\r
+ }\r
+ p.setLocation(x, y);\r
+ return p;\r
+ }\r
+\r
+ private static Point2D reconnectLocation(RouteLineHalf lh, Point2D p) {\r
+ if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH*3)\r
+ return null;\r
+\r
+ final double dist = CUT_DIST_FROM_END;\r
+ double x = lh.getLink().getX();\r
+ double y = lh.getLink().getY();\r
+ if (lh.getLine().isHorizontal()) {\r
+ if (lh.getLink() == lh.getLine().getBegin())\r
+ x += dist*2;\r
+ else\r
+ x -= dist*2;\r
+ } else {\r
+ if (lh.getLink() == lh.getLine().getBegin())\r
+ y += dist*2;\r
+ else\r
+ y -= dist*2;\r
+ }\r
+ p.setLocation(x, y);\r
+ return p;\r
+ }\r
+\r
private static double distSq(double x1, double y1, double x2, double y2) {\r
double dx = x2 - x1;\r
double dy = y2 - y1;\r
return Math.sqrt(Math.abs(m00*m11 - m10*m01));\r
}\r
\r
- private static BufferedImage safeReadImage(String name) {\r
- try {\r
- URL url = HighlightActionPointsAction.class.getResource(name);\r
- return ImageIO.read(url);\r
- } catch (IOException e) {\r
- return null;\r
- }\r
- }\r
-\r
}\r