package hu.bme.mit.inf.dslreasoner.visualisation.pi2graphviz import guru.nidi.graphviz.attribute.Shape import guru.nidi.graphviz.attribute.Style import guru.nidi.graphviz.model.Graph import guru.nidi.graphviz.model.Label import guru.nidi.graphviz.model.Node import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.DefinedElement import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Relation import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Type import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.TypeDefinition import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.BinaryElementRelationLink import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.visualisation.PartialInterpretationVisualisation import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.visualisation.PartialInterpretationVisualiser import hu.bme.mit.inf.dslreasoner.workspace.ReasonerWorkspace import java.util.Collection import java.util.HashMap import java.util.HashSet import java.util.LinkedList import java.util.List import java.util.Random import java.util.Set import org.eclipse.xtext.xbase.lib.Functions.Function1 import static guru.nidi.graphviz.model.Factory.* import static extension hu.bme.mit.inf.dslreasoner.util.CollectionsUtil.* class GraphvizVisualiser implements PartialInterpretationVisualiser { val TypeColoringStyle typeColoringStyle = TypeColoringStyle::AVERAGE; val smallFontSize=9 val keywords = #{"class","reference","literal","enum"} override visualiseConcretization(PartialInterpretation partialInterpretation) { visualisePartialInterpretation(partialInterpretation,true) } override visualisePartialSolution(PartialInterpretation partialInterpretation) { visualisePartialInterpretation(partialInterpretation,false) } def private getElements(PartialInterpretation model) { return model.problem.elements + model.newElements + model.booleanelements+ model.integerelements+ model.stringelement+ model.realelements } def private visualisePartialInterpretation(PartialInterpretation partialInterpretation, boolean concretizationOnly) { val problem = partialInterpretation.problem // Elements of the partial solutions val oldElements = problem.elements val newElements = partialInterpretation.newElements //val prototypeElements = #[partialInterpretation.openWorldElementPrototype] val allElements = getElements(partialInterpretation) // Indexing types val mustTypes = new HashMap> val mayTypes = new HashMap> for(element : allElements) { mustTypes.put(element,new HashSet) mayTypes.put(element,new HashSet) } for(typeDefinition: problem.types.filter(TypeDefinition)) { for(element : typeDefinition.elements) { mustTypes.get(element)+=typeDefinition } } for(partialTypeInterpretations: partialInterpretation.partialtypeinterpratation) { for(element : partialTypeInterpretations.elements) { mustTypes.get(element)+=partialTypeInterpretations.interpretationOf } } // Indexing references // Drawing the nodes val elements2Node = new HashMap val elements2ID = new HashMap for(oldElemenetIndex : 0.. void drawDataTypes(Collection collection, Function1 namer, HashMap elements2Node, HashMap elements2ID) { for(booleanElementIndex: 0.. mustTypes, Set mayTypes) { var tableStyle = ''' CELLSPACING="0" BORDER="2" CELLBORDER="0" CELLPADDING="1" STYLE="ROUNDED"''' if(typeColoringStyle==TypeColoringStyle::AVERAGE) { tableStyle += ''' BGCOLOR="#«typePredicateColor(mustTypes).toBackgroundColorString»"''' } val mainLabel = if(element.name !== null) { val parts = element.name.split("\\s+") textWithSubSup(parts.getOrNull(0),parts.getOrNull(1),parts.getOrNull(2),null) } else { val parts = ID.split("\\s+") textWithSubSup(parts.get(0),parts.get(1),parts.getOrNull(2),null) } val label = Label.html( ''''''+ '''«mainLabel»'''+ '''«FOR mustTypeName : mustTypes.map[it.name].sort»«typePredicateDescription(mustTypeName,true)»«ENDFOR»'''+ '''«FOR mayTypeName : mayTypes.map[it.name].sort»«typePredicateDescription(mayTypeName,false)»«ENDFOR»'''+ '''''') val node = node(ID).with(label).with( Shape.NONE //, //Attributes::attr("margin","0") //new MapAttributes() => [add("margin", "0")] ) return node } protected def drawEdge(BinaryElementRelationLink link, Relation relation, Node to, boolean isContainemnt) { val nameSegments = relation.name.split("\\s+") var l = to(to).with(Label.html(textWithSubSup(nameSegments.getOrNull(0),nameSegments.getOrNull(1),nameSegments.getOrNull(2),null))) if(isContainemnt) { l = l.with(Style.BOLD) } return l } private def getOrNull(List list, int index) { if(list.size>index) return list.get(index) else return null } def private textWithSubSup(String text, String sub, String sup, String after) { val actualText = text val actualSub = if(sub===null) {" "} else {sub.checkAndHighlightKeyword} val actualSup = if(sup==null) {" "} else {sup.checkAndHighlightKeyword} val actualAfter = if(after === null) {""} else { '''«after»''' } return ''''''+ '''«actualAfter»'''+ ''''''+ '''
«actualText»«actualSup»
«actualSub»
''' } def checkAndHighlightKeyword(String word) { if(keywords.contains(word)) { return '''«word»''' } else { word } } def typePredicateDescription(String typeName, boolean must) { val value = if(must){'''1'''} else{'''½'''} val backgroundColor = if(this.typeColoringStyle == TypeColoringStyle.FLAG) { ''' BGCOLOR="#«typeName.typePredicateColor.toBackgroundColorString»"''' } else { "" } val typeNameSegments = typeName.split("\\s+") return '''«textWithSubSup(typeNameSegments.getOrNull(0),typeNameSegments.getOrNull(1),typeNameSegments.getOrNull(2),null)» = «value» ''' } def toBackgroundColorString(List backgroundColor) { '''«Integer.toHexString(backgroundColor.get(0))»«Integer.toHexString(backgroundColor.get(1))»«Integer.toHexString(backgroundColor.get(2))»''' } protected def typePredicateColor(Set types) { types.averageColor } protected def typePredicateColor(String name) { val Random random = new Random(name.hashCode) val rangePicker = [|random.nextInt(128)+128] return #[rangePicker.apply(), rangePicker.apply(), rangePicker.apply()] } private def averageColor(Set types) { if(types.empty) { return #[256,256,256] } else { val typeColors = types.filter[!it.isIsAbstract].map[typePredicateColor(it.name)] return #[ typeColors.map[get(0)].average, typeColors.map[get(1)].average, typeColors.map[get(2)].average ] } } private def average(Iterable doubles) { return doubles.reduce[p1, p2|p1+p2]/doubles.size } } enum TypeColoringStyle { FLAG, AVERAGE } class GraphvizVisualisation implements PartialInterpretationVisualisation { private static VisualisationQueque queue = new VisualisationQueque val private Graph graph public new(Graph graph) { this.graph = graph } override writeToFile(ReasonerWorkspace workspace, String name) { val file = workspace.getFile('''«name»''') queue.start val future = queue.addTask(graph,file) val message = future.get if(message===null) { workspace.refreshFile('''«name»''') } else { // Do nothing } } }