001package conexp.fx.gui.dataset; 002 003/* 004 * #%L 005 * Concept Explorer FX 006 * %% 007 * Copyright (C) 2010 - 2023 Francesco Kriegel 008 * %% 009 * This program is free software: you can redistribute it and/or modify 010 * it under the terms of the GNU General Public License as 011 * published by the Free Software Foundation, either version 3 of the 012 * License, or (at your option) any later version. 013 * 014 * This program is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public 020 * License along with this program. If not, see 021 * <http://www.gnu.org/licenses/gpl-3.0.html>. 022 * #L% 023 */ 024import java.io.File; 025import java.io.IOException; 026import java.util.Collections; 027import java.util.List; 028import java.util.stream.Collectors; 029 030import conexp.fx.core.algorithm.exploration.AttributeExploration; 031import conexp.fx.core.algorithm.exploration.ParallelAttributeExploration; 032import conexp.fx.core.algorithm.lattice.IFox2; 033import conexp.fx.core.algorithm.lattice.IPred; 034import conexp.fx.core.algorithm.nextclosures.NextClosures2Bit; 035import conexp.fx.core.builder.FileRequest; 036import conexp.fx.core.builder.Request; 037import conexp.fx.core.builder.Requests.Source; 038import conexp.fx.core.builder.StringRequest; 039import conexp.fx.core.collections.Pair; 040import conexp.fx.core.collections.relation.RelationEvent; 041import conexp.fx.core.context.Concept; 042import conexp.fx.core.context.ConceptLattice; 043import conexp.fx.core.context.Context; 044import conexp.fx.core.context.Implication; 045import conexp.fx.core.context.MatrixContext; 046import conexp.fx.core.context.MatrixContext.AutomaticMode; 047import conexp.fx.core.exporter.CFXExporter; 048import conexp.fx.core.exporter.CXTExporter; 049import conexp.fx.core.exporter.HTMLExporter; 050import conexp.fx.core.exporter.PDFExporter; 051import conexp.fx.core.exporter.PNGExporter; 052import conexp.fx.core.exporter.SVGExporter; 053import conexp.fx.core.exporter.TeXExporter; 054import conexp.fx.core.exporter.TeXExporter.ContextTeXPackage; 055import conexp.fx.core.exporter.TeXExporter.DiagramTeXPackage; 056import conexp.fx.core.exporter.TeXExporter.FitScale; 057import conexp.fx.core.exporter.TeXExporter.TeXOptions; 058import conexp.fx.core.layout.AdditiveConceptLayout; 059import conexp.fx.core.layout.AdditiveConceptLayout.Type; 060import conexp.fx.core.layout.ConceptMovement; 061import conexp.fx.core.layout.GeneticLayouter; 062import conexp.fx.core.layout.LayoutEvolution; 063import conexp.fx.core.layout.QualityMeasure; 064import conexp.fx.core.util.Constants; 065import conexp.fx.core.util.FileFormat; 066import conexp.fx.gui.ConExpFX; 067import conexp.fx.gui.context.ConceptWidget; 068import conexp.fx.gui.context.ImplicationWidget; 069import conexp.fx.gui.context.MatrixContextWidget; 070import conexp.fx.gui.dialog.FXDialog.Answer; 071import conexp.fx.gui.dialog.FXDialog.Return; 072import conexp.fx.gui.dialog.TeXDialog; 073import conexp.fx.gui.graph.CircularGraph; 074import conexp.fx.gui.graph.ConceptGraph; 075import conexp.fx.gui.graph.PolarGraph; 076import conexp.fx.gui.task.TimeTask; 077import conexp.fx.gui.util.Platform2; 078import javafx.application.Platform; 079import javafx.collections.FXCollections; 080import javafx.collections.ObservableList; 081import javafx.stage.FileChooser; 082import javafx.stage.FileChooser.ExtensionFilter; 083 084public final class FCADataset<G, M> extends Dataset { 085 086 public final Request<G, M> request; 087 public final MatrixContext<G, M> context; 088 public final ConceptLattice<G, M> lattice; 089 public final AdditiveConceptLayout<G, M> layout; 090// private final SimpleConceptLayout<G, M> l; 091 public final ObservableList<Concept<G, M>> concepts = 092 FXCollections.<Concept<G, M>> observableArrayList(); 093 public final ObservableList<Implication<G, M>> implications = 094 FXCollections.<Implication<G, M>> observableArrayList(); 095 public final ObservableList<Implication<G, M>> partialImplications = 096 FXCollections.observableArrayList(); 097 098 public final QualityMeasure<G, M, Pair<Concept<G, M>, Double>> conflictDistance = 099 QualityMeasure.conflictDistance(); 100// public final EdgeIntersections<G, M> edgeIntersections = new EdgeIntersections<G, M>(); 101 public boolean editable = false; 102 103 public final MatrixContextWidget<G, M> contextWidget; 104 public final ConceptGraph<G, M> conceptGraph; 105 public final ConceptWidget<G, M> conceptWidget; 106 public final ImplicationWidget<G, M> implicationWidget; 107 108 public FCADataset(final Dataset parentDataset, final Request<G, M> request) { 109 super(parentDataset); 110 this.request = request; 111 this.context = request.createContext(AutomaticMode.REDUCE); 112 this.context.id.bind(id); 113 this.lattice = new ConceptLattice<G, M>(context); 114 this.layout = new AdditiveConceptLayout<G, M>(lattice, null, null, Type.HYBRID); 115 this.layout.observe(); 116 this.id.set(request.getId()); 117 if (request.src != Source.FILE) 118 unsavedChanges.set(true); 119 else if (request instanceof FileRequest) 120 file = ((FileRequest) request).file; 121// this.layout.observe(); 122// this.setContent(pane); 123// this.setGraphic(ImageViewBuilder 124// .create() 125// .image(new Image(GUI.class.getResourceAsStream("image/context.gif"))) 126// .build()); 127// this.textProperty().bind(new StringBinding() { 128// 129// { 130// bind(fca.id, fca.unsavedChanges); 131// } 132// 133// protected final String computeValue() { 134// return fca.id.get() + (fca.unsavedChanges.get() ? " *" : ""); 135// } 136// }); 137// this.setOnClosed(new EventHandler<Event>() { 138// 139// public final void handle(final Event event) { 140// if (fca.unsavedChanges.get()) 141// if (new FXDialog(conExp.primaryStage, Style.QUESTION, "Unsaved Changes", fca.id.get() 142// + " has unsaved changes. Do you want to save?", null).showAndWait().equals(Result.YES)) 143// save(); 144// } 145// }); 146// final ExecutorStatusBar conExp.executorStatusBar = new ExecutorStatusBar(); 147// conExp.executorStatusBar.bindTo(conExp.executor); 148// this.statusWidget = conExp.executorStatusBar.statusBar; 149 if (request instanceof FileRequest) { 150 this.editable = true; 151// this.contextWidget = (MatrixContextWidget<G, M>) new StringMatrixContextWidget((FCADataset<String, String>) this); 152 } else if (request instanceof StringRequest) { 153 this.editable = true; 154// this.contextWidget = (MatrixContextWidget<G, M>) new StringMatrixContextWidget((FCADataset<String, String>) this); 155 } // else 156 this.contextWidget = new MatrixContextWidget<G, M>(this); 157 this.conceptGraph = new ConceptGraph<G, M>(this); 158 this.conceptWidget = new ConceptWidget<G, M>(this); 159 this.implicationWidget = new ImplicationWidget<G, M>(this); 160 lattice.addEventHandler(event -> { 161 concepts.clear(); 162 concepts.addAll(lattice.colHeads()); 163 } , RelationEvent.ALL_CHANGED); 164 views.add(new DatasetView<Context<G, M>>("Context", contextWidget, context)); 165 views.add(new DatasetView<AdditiveConceptLayout<G, M>>("Lattice", conceptGraph, layout)); 166// this.l = new SimpleConceptLayout<G, M>(lattice); 167// views.add(new DatasetView<SimpleConceptLayout<G, M>>("Force Lattice", new ConceptGraph<G, M>(this, l), l)); 168 views.add(new DatasetView<List<Concept<G, M>>>("Concepts", conceptWidget, concepts)); 169 views.add(new DatasetView<List<Implication<G, M>>>("Implications", implicationWidget, implications)); 170 defaultActiveViews.add("Lattice"); 171 actions.add(new DatasetAction("Polar Layout", () -> polarLayout())); 172 actions.add(new DatasetAction("Circular Layout", () -> circularLayout())); 173 if (editable) { 174 actions.add(new DatasetAction("Explore...", () -> { 175 implications.clear(); 176 try { 177 AttributeExploration.withHumanExpert(((MatrixContext<String, String>) context).getSelection()).start(); 178 } catch (InterruptedException __) {} 179 })); 180 actions.add(new DatasetAction("Explore (parallel)...", () -> { 181 implications.clear(); 182 ConExpFX.execute(ParallelAttributeExploration.createExplorationTask((FCADataset<String, String>) this)); 183 })); 184 } 185 actions.add(new DatasetAction("Refresh", () -> reinitializeWithNextClosures())); 186 Platform2.runOnFXThread(() -> initializeWithNextClosures()); 187 } 188 189 private final void polarLayout() { 190 new PolarGraph<G, M>(lattice).show(); 191 } 192 193 private final void circularLayout() { 194 new CircularGraph<G, M>(lattice).show(); 195 } 196 197 public final void initializeWithIFox() { 198 ConExpFX.execute(TimeTask.create(this, "Importing Formal Context", () -> { 199 request.setContent(); 200 context.addEventHandler( 201 __ -> Platform.runLater(() -> unsavedChanges.set(true)), 202 RelationEvent.ROWS, 203 RelationEvent.COLUMNS, 204 RelationEvent.ENTRIES); 205 })); 206 ConExpFX.execute(new TimeTask<Void>("Initialization") { 207 208 @Override 209 protected Void call() { 210 updateProgress(0d, 1d); 211 if (isCancelled()) 212 return null; 213 updateProgress(0.1d, 1d); 214 context.deselectAllAttributes(); 215 final Concept<G, M> top = new Concept<G, M>(context.rowHeads(), Collections.<M> emptySet()); 216 lattice.rowHeads().add(top); 217 updateProgress(0.2d, 1d); 218 updateMessage("Computing Object Labels..."); 219 synchronized (lattice.objectConcepts) { 220 for (G g : context.rowHeads()) 221 lattice.objectConcepts.put(g, top); 222 } 223 updateProgress(0.4d, 1d); 224 layout.invalidate(); 225 for (int col = 0; col < context.colHeads().size(); col++) { 226 final int _col = col; 227 ConExpFX.execute(new TimeTask<Void>("Selecting " + _col) { 228 229 @Override 230 protected Void call() { 231 updateProgress(0d, 1d); 232 if (isCancelled()) 233 return null; 234 selectAttribute(context.colHeads().get(_col)); 235 updateProgress(1d, 1d); 236 return null; 237 } 238 }); 239 } 240 updateProgress(1d, 1d); 241 return null; 242 } 243 }); 244 } 245 246 public final void initializeWithNextClosures() { 247 ConExpFX.execute(TimeTask.create(this, "Importing Formal Context", () -> { 248 request.setContent(); 249// context.addEventHandler(__ -> reinitializeWithNextClosures(), RelationEvent.ROWS); 250 context.addEventHandler( 251 __ -> Platform.runLater(() -> unsavedChanges.set(true)), 252 RelationEvent.ROWS, 253 RelationEvent.COLUMNS, 254 RelationEvent.ENTRIES); 255 })); 256 _reinitializeWithNextClosures(); 257 } 258 259 private final void reinitializeWithNextClosures() { 260 ConExpFX.execute(TimeTask.create(this, "Reinit", () -> { 261 layout.seedsG.clear(); 262 layout.seedsM.clear(); 263 lattice.objectConcepts.clear(); 264 lattice.attributeConcepts.clear(); 265 conceptGraph.removeContent(); 266 lattice.rowHeads().clear(); 267 concepts.clear(); 268 implications.clear(); 269 partialImplications.clear(); 270 } , true)); 271 _reinitializeWithNextClosures(); 272 } 273 274 private final void _reinitializeWithNextClosures() { 275 ConExpFX.execute(new TimeTask<Void>(this, "Locking Graph") { 276 277 protected final Void call() { 278 updateProgress(0d, 1d); 279 if (isCancelled()) 280 return null; 281 updateProgress(0.5d, 1d); 282 conceptGraph.controller.graphLock = true; 283 conceptGraph.highlightLock = true; 284 updateProgress(1d, 1d); 285 return null; 286 } 287 }); 288 ConExpFX.execute(TimeTask.create(this, "Reduce Formal Context", () -> context.pushAllChangedEvent() 289// context.initHandlers(true, MatrixContext.AutomaticMode.REDUCE) 290// final int rows = this.fcaInstance.context.rowHeads().size(); 291// final int cols = this.fcaInstance.context.colHeads().size(); 292// this.fcaInstance.context.initHandlers(true, MatrixContext.AutomaticMode.fromSize(rows,cols)); 293 )); 294 ConExpFX.execute(NextClosures2Bit.createTask(this)); 295 ConExpFX.execute( 296 TimeTask.create( 297 this, 298 "Sorting Formal Concepts", 299 () -> lattice.rowHeads().addAll( 300 concepts 301 .parallelStream() 302 .sorted((c1, c2) -> (int) Math.signum(c1.intent().size() - c2.intent().size())) 303 .collect(Collectors.toList())))); 304 ConExpFX.execute(GeneticLayouter.initialSeeds(this)); 305 ConExpFX.execute(IPred.neighborhoodP(this)); 306 ConExpFX.execute(new TimeTask<Void>(this, "Unlocking Graph") { 307 308 protected final Void call() { 309 updateProgress(0d, 1d); 310 if (isCancelled()) 311 return null; 312 updateProgress(0.5d, 1d); 313 conceptGraph.controller.graphLock = false; 314 conceptGraph.highlightLock = false; 315 updateProgress(1d, 1d); 316 return null; 317 } 318 }); 319 ConExpFX.execute(TimeTask.create(this, "Initialize Concept Lattice Graph", () -> { 320// Platform2.runOnFXThread(() -> layout.invalidate()); 321// new Thread(() -> lattice.pushAllChangedEvent()).start(); 322 lattice.pushAllChangedEvent(); 323// Platform2.runOnFXThread(() -> layout.invalidate()); 324 })); 325 ConExpFX.execute( 326 TimeTask.create( 327 this, 328 "Computing Partial Implications", 329 () -> implications.addAll(lattice.luxenburgerBase(0d, true)))); 330//ConExpFX.execute(new TimeTask<Void>(this, "Computing Partial Implications") { 331// 332//@Override 333//protected Void call() { 334// updateProgress(0d, 1d); 335// if (isCancelled()) 336// return null; 337// conceptGraph.controller.graphLock = true; 338// conceptGraph.highlightLock = true; 339// updateProgress(0.25d, 1d); 340// implications.addAll(lattice.luxenburgerBase(0d, true)); 341// updateProgress(0.75d, 1d); 342// conceptGraph.controller.graphLock = false; 343// conceptGraph.highlightLock = false; 344// updateProgress(1d, 1d); 345// return null; 346//} 347//}); 348 relayout(0, Constants.POPULATION); 349 ConExpFX.execute(TimeTask.create(this, "Initialize Concept Lattice Graph", layout::invalidate, true)); 350// ConExpFX.execute(TimeTask.create(this, "Force Layouter", () -> { 351// for (Concept<G, M> c : lattice.rowHeads()) 352// l.getOrAddPosition(c).setValue(layout.getPosition(c).getValue()); 353// l.start(); 354// })); 355 } 356 357 public final void addObject(final G object, final int index) { 358 ConExpFX.execute(new TimeTask<Void>(this, "New Object") { 359 360 protected final Void call() { 361 updateProgress(0d, 1d); 362 if (isCancelled()) 363 return null; 364 updateProgress(0.5d, 1d); 365// conceptGraph.controller.graphLock = true; 366// conceptGraph.highlightLock = true; 367// context.pushAllChangedEvent(); 368 if (index == -1) 369 context.rowHeads().add(object); 370 else 371 context.rowHeads().add(index, object); 372 Platform2.runOnFXThread(() -> { 373 unsavedChanges.set(true); 374 }); 375// lattice.rowHeads().clear(); 376 updateProgress(1d, 1d); 377 return null; 378 } 379 }); 380 this.reinitializeWithNextClosures(); 381 } 382 383 public final void addAttribute(final M attribute, final int index) { 384 ConExpFX.execute(new TimeTask<Void>(this, "New Attribute") { 385 386 protected final Void call() { 387 updateProgress(0d, 1d); 388 if (isCancelled()) 389 return null; 390 updateProgress(0.5d, 1d); 391 conceptGraph.controller.graphLock = true; 392 conceptGraph.highlightLock = true; 393 Platform2.runOnFXThread(() -> unsavedChanges.set(true)); 394 if (index == -1) 395 context.colHeads().add(attribute); 396 else 397 context.colHeads().add(index, attribute); 398 context.deselectAttribute(attribute); 399 context.pushAllChangedEvent(); 400 // select(attribute); 401 updateProgress(1d, 1d); 402 return null; 403 } 404 }); 405 ConExpFX.execute(IFox2.select(id.get(), layout, attribute, conflictDistance, ConExpFX.getThreadPool())); 406 ConExpFX.execute(new TimeTask<Void>(this, "New Attribute") { 407 408 protected final Void call() { 409 updateProgress(0d, 1d); 410 if (isCancelled()) 411 return null; 412 updateProgress(0.5d, 1d); 413 conceptGraph.controller.graphLock = false; 414 conceptGraph.highlightLock = false; 415 updateProgress(1d, 1d); 416 return null; 417 } 418 }); 419 } 420 421 public final void removeObject(final G object) { 422 ConExpFX.execute(new TimeTask<Void>(this, "Removing Object") { 423 424 protected final Void call() { 425 updateProgress(0d, 1d); 426 if (isCancelled()) 427 return null; 428 updateProgress(0.5d, 1d); 429 // conceptGraph.controller.graphLock = true; 430 // conceptGraph.highlightLock = true; 431 // context.pushAllChangedEvent(); 432 context.rowHeads().remove(object); 433 Platform2.runOnFXThread(() -> unsavedChanges.set(true)); 434 // lattice.rowHeads().clear(); 435 updateProgress(1d, 1d); 436 return null; 437 } 438 }); 439 this.reinitializeWithNextClosures(); 440 } 441 442 public final void removeAttribute(final M attribute) { 443 ConExpFX.execute(TimeTask.create(this, "Removing Attribute", () -> { 444 conceptGraph.controller.graphLock = true; 445 conceptGraph.highlightLock = true; 446 Platform2.runOnFXThread(() -> unsavedChanges.set(true)); 447// context.deselectAttribute(attribute); 448// context.pushAllChangedEvent(); 449 })); 450 if (context.selectedAttributes().contains(attribute)) 451 ConExpFX.execute(IFox2.ignore(id.get(), layout, attribute, conflictDistance, ConExpFX.getThreadPool())); 452 ConExpFX.execute(TimeTask.create(this, "Removing Attribute", () -> { 453 context.colHeads().remove(attribute); 454 context.pushAllChangedEvent(); 455 conceptGraph.controller.graphLock = false; 456 conceptGraph.highlightLock = false; 457 })); 458 } 459 460 public final void renameObject(final G oldName, final G newName) { 461 if (oldName.equals(newName)) 462 return; 463 ConExpFX.execute(TimeTask.create(this, "Renaming Object", () -> { 464 context.rowHeads().set(oldName, newName); 465 Platform2.runOnFXThread(() -> unsavedChanges.set(true)); 466 })); 467 this.reinitializeWithNextClosures(); 468 } 469 470 public final void renameAttribute(final M oldName, final M newName) { 471 if (oldName.equals(newName)) 472 return; 473 ConExpFX.execute(TimeTask.create(this, "Renaming Attribute", () -> { 474 context.colHeads().set(oldName, newName); 475 Platform2.runOnFXThread(() -> unsavedChanges.set(true)); 476 })); 477 this.reinitializeWithNextClosures(); 478 } 479 480 public final void flip(final G object, final M attribute) { 481 if (!context.selectedAttributes().contains(attribute) || !context.selectedObjects().contains(object)) { 482 if (context.contains(object, attribute)) 483 context.remove(object, attribute); 484 else 485 context.addFast(object, attribute); 486 } else { 487 ConExpFX.execute( 488 TimeTask.compose( 489 FCADataset.this, 490 "Flipping Incidence", 491 new TimeTask<Void>(FCADataset.this, "Flip Init") { 492 493 protected final Void call() { 494 updateProgress(0d, 1d); 495 // if (isCancelled()) 496 // return null; 497 updateProgress(0.5d, 1d); 498 conceptGraph.controller.graphLock = true; 499 conceptGraph.highlightLock = true; 500 updateProgress(1d, 1d); 501 return null; 502 } 503 }, 504 IFox2.ignore(id.get(), layout, attribute, conflictDistance, ConExpFX.getThreadPool()), 505 new TimeTask<Void>(FCADataset.this, "Context Flip") { 506 507 protected final Void call() { 508 updateProgress(0d, 1d); 509 // if (isCancelled()) 510 // return null; 511 updateProgress(0.5d, 1d); 512 conceptGraph.controller.graphLock = false; 513 conceptGraph.highlightLock = false; 514 if (layout.lattice.context.contains(object, attribute)) 515 layout.lattice.context.remove(object, attribute); 516 else 517 layout.lattice.context.addFast(object, attribute); 518 conceptGraph.controller.graphLock = true; 519 conceptGraph.highlightLock = true; 520 updateProgress(1d, 1d); 521 return null; 522 } 523 }, 524 IFox2.select(id.get(), layout, attribute, conflictDistance, ConExpFX.getThreadPool()), 525 new TimeTask<Void>(FCADataset.this, "Flip Finish") { 526 527 protected final Void call() { 528 updateProgress(0d, 1d); 529 // if (isCancelled()) 530 // return null; 531 updateProgress(0.5d, 1d); 532 conceptGraph.controller.graphLock = false; 533 conceptGraph.highlightLock = false; 534 updateProgress(1d, 1d); 535 return null; 536 } 537 })); 538 relayout(2, 2); 539 } 540 } 541 542 public final void selectObject(final G object) { 543 ConExpFX.execute(TimeTask.create(this, "Selecting Object", () -> context.selectObject(object))); 544 this.reinitializeWithNextClosures(); 545 } 546 547 public final void ignoreObject(final G object) { 548 ConExpFX.execute(TimeTask.create(this, "Unselecting Object", () -> context.deselectObject(object))); 549 this.reinitializeWithNextClosures(); 550 } 551 552 public final void selectAttribute(final M attribute) { 553 ConExpFX.execute(TimeTask.compose( 554 FCADataset.this, 555 "Selecting Attribute", 556 new TimeTask<Void>(this, "Select Init") { 557 558 protected final Void call() { 559 updateProgress(0d, 1d); 560 if (isCancelled()) 561 return null; 562 updateProgress(0.5d, 1d); 563 conceptGraph.controller.graphLock = true; 564 conceptGraph.highlightLock = true; 565 updateProgress(1d, 1d); 566 return null; 567 } 568 }, 569 IFox2.select(id.get(), layout, attribute, conflictDistance, ConExpFX.getThreadPool()), 570 new TimeTask<Void>(this, "Select Finish") { 571 572 protected final Void call() { 573 updateProgress(0d, 1d); 574 if (isCancelled()) 575 return null; 576 updateProgress(0.5d, 1d); 577 conceptGraph.controller.graphLock = false; 578 conceptGraph.highlightLock = false; 579 updateProgress(1d, 1d); 580 return null; 581 } 582 })); 583 relayout(2, 2); 584 } 585 586 public final void ignoreAttribute(final M attribute) { 587 ConExpFX 588 .execute(TimeTask.compose( 589 FCADataset.this, 590 "Unselecting attribute", 591 new TimeTask<Void>(this, "Ignore Init") { 592 593 protected final Void call() { 594 updateProgress(0d, 1d); 595 if (isCancelled()) 596 return null; 597 updateProgress(0.5d, 1d); 598 conceptGraph.controller.graphLock = true; 599 conceptGraph.highlightLock = true; 600 updateProgress(1d, 1d); 601 return null; 602 } 603 }, 604 IFox2.ignore(id.get(), layout, attribute, conflictDistance, ConExpFX.getThreadPool()), 605 new TimeTask<Void>(this, "Ignore Finish") { 606 607 protected final Void call() { 608 updateProgress(0d, 1d); 609 if (isCancelled()) 610 return null; 611 updateProgress(0.5d, 1d); 612 conceptGraph.controller.graphLock = false; 613 conceptGraph.highlightLock = false; 614 updateProgress(1d, 1d); 615 return null; 616 } 617 })); 618 relayout(2, 2); 619 } 620 621 public final void relayout(final int generationCount, final int populationSize) { 622 ConExpFX.execute(GeneticLayouter.seeds(this, false, generationCount, populationSize)); 623 } 624 625 public final void refine(final int generationCount) { 626 ConExpFX.execute(GeneticLayouter.seeds(this, true, generationCount, 1)); 627 } 628 629 public final LayoutEvolution<G, M> qualityChart(final Concept<G, M> concept, final ConceptMovement movement) { 630 final LayoutEvolution<G, M> qualityEvolution = new LayoutEvolution<G, M>( 631 layout, 632 concept, 633 movement, 634 2d, 635 2d, 636 32, 637 1, 638 16, 639 conflictDistance, 640 ConExpFX.instance.executor.tpe); 641 ConExpFX.execute(LayoutEvolution.calculate(qualityEvolution)); 642 return qualityEvolution; 643 } 644 645 public final void storeToFile() { 646 ConExpFX.execute(new TimeTask<Void>(this, "Store") { 647 648 @SuppressWarnings("incomplete-switch") 649 protected final Void call() { 650 updateProgress(0d, 1d); 651 if (isCancelled()) 652 return null; 653 updateProgress(0.5d, 1d); 654 switch (FileFormat.of(file, FileFormat.CFX, FileFormat.CXT).second()) { 655 case CXT: 656 CXTExporter.export(context, contextWidget.rowHeaderPane.rowMap, contextWidget.colHeaderPane.columnMap, file); 657 break; 658 case CFX: 659 CFXExporter 660 .export(context, contextWidget.rowHeaderPane.rowMap, contextWidget.colHeaderPane.columnMap, layout, file); 661 break; 662 } 663 Platform2.runOnFXThread(() -> { 664 id.set(file.getName()); 665 unsavedChanges.set(false); 666 ConExpFX.instance.fileHistory.remove(file); 667 ConExpFX.instance.fileHistory.add(0, file); 668 }); 669 updateProgress(1d, 1d); 670 return null; 671 } 672 }); 673 } 674 675 public void export(FileFormat svg, File file2) { 676 exportToFile(file2); 677 } 678 679 public void exportTeX(TeXOptions options) { 680 try { 681 new TeXExporter<G, M>( 682 context, 683 contextWidget.rowHeaderPane.rowMap, 684 contextWidget.colHeaderPane.columnMap, 685 layout, 686 options).export(); 687 } catch (IOException e) { 688 e.printStackTrace(); 689 } 690 } 691 692 public final void exportToFile(final File file) { 693 ConExpFX.execute(new TimeTask<Void>(this, "Export") { 694 695 @SuppressWarnings("incomplete-switch") 696 protected final Void call() { 697 updateProgress(0d, 1d); 698 if (isCancelled()) 699 return null; 700 updateProgress(0.5d, 1d); 701 switch (FileFormat 702 .of(file, FileFormat.TEX, FileFormat.PNG, FileFormat.SVG, FileFormat.PDF, FileFormat.HTML) 703 .second()) { 704 case TEX: 705 exportTeX( 706 new TeXOptions( 707 file, 708 false, 709 true, 710 false, 711 ContextTeXPackage.Ganter, 712 DiagramTeXPackage.Ganter, 713 new FitScale(80, 120))); 714 break; 715 case PNG: 716 PNGExporter.export( 717 context, 718 contextWidget.rowHeaderPane.rowMap, 719 contextWidget.colHeaderPane.columnMap, 720 layout, 721 true, 722 true, 723 file); 724 break; 725 case SVG: 726 SVGExporter.export( 727 context, 728 contextWidget.rowHeaderPane.rowMap, 729 contextWidget.colHeaderPane.columnMap, 730 layout, 731 true, 732 true, 733 file); 734 break; 735 case PDF: 736 PDFExporter.export( 737 context, 738 contextWidget.rowHeaderPane.rowMap, 739 contextWidget.colHeaderPane.columnMap, 740 layout, 741 true, 742 true, 743 file); 744 break; 745 case HTML: 746 HTMLExporter.export( 747 context, 748 contextWidget.rowHeaderPane.rowMap, 749 contextWidget.colHeaderPane.columnMap, 750 layout, 751 true, 752 true, 753 file); 754 break; 755 } 756 updateProgress(1d, 1d); 757 return null; 758 } 759 }); 760 } 761 762 public final void save() { 763 if (file == null) 764 saveAs(); 765 else 766 storeToFile(); 767 } 768 769 public final void saveAs() { 770 final FileChooser fileChooser = new FileChooser(); 771 fileChooser.setTitle("Save Formal Context File"); 772 if (ConExpFX.instance.lastDirectory != null) 773 fileChooser.setInitialDirectory(ConExpFX.instance.lastDirectory); 774 fileChooser.getExtensionFilters().add(new ExtensionFilter("Context & Lattice (ConExpFX Format, *.cfx)", "*.cfx")); 775 fileChooser.getExtensionFilters().add(new ExtensionFilter("Only Context (Burmeister Format, *.cxt)", "*.cxt")); 776 final File file = fileChooser.showSaveDialog(ConExpFX.instance.primaryStage); 777 if (file != null) { 778 this.file = file; 779 ConExpFX.instance.lastDirectory = file.getParentFile(); 780 storeToFile(); 781 } 782 } 783 784 public final void export() { 785 final FileChooser fileChooser = new FileChooser(); 786 if (ConExpFX.instance.lastDirectory != null) 787 fileChooser.setInitialDirectory(ConExpFX.instance.lastDirectory); 788 fileChooser 789 .getExtensionFilters() 790 .add(new ExtensionFilter("Context & Lattice (TeX - Ganter's fca.sty, *.tex)", "*.tex")); 791 fileChooser 792 .getExtensionFilters() 793 .add(new ExtensionFilter("Only Lattice (Portable Network Graphics, *.png)", "*.png")); 794 fileChooser 795 .getExtensionFilters() 796 .add(new ExtensionFilter("Only Lattice (Scalable Vector Graphics, *.svg)", "*.svg")); 797 fileChooser 798 .getExtensionFilters() 799 .add(new ExtensionFilter("Only Lattice (Portable Document Format, *.pdf)", "*.pdf")); 800 fileChooser 801 .getExtensionFilters() 802 .add(new ExtensionFilter("Only Context (Hypertext Markup Language, *.html)", "*.html")); 803 final File file = fileChooser.showSaveDialog(ConExpFX.instance.primaryStage); 804 if (file != null) { 805 ConExpFX.instance.lastDirectory = file.getParentFile(); 806 exportToFile(file); 807 } 808 } 809 810 @Deprecated 811 public void exportTeX() { 812 final Return<TeXOptions> ret = new TeXDialog<G, M>(ConExpFX.instance.primaryStage).showAndWait(); 813 if (ret.result().equals(Answer.OK)) { 814 final FileChooser chooser = new FileChooser(); 815 chooser.getExtensionFilters().add(new ExtensionFilter("LaTeX File (*.tex)", "*.tex")); 816 chooser.setInitialDirectory(ConExpFX.instance.lastDirectory); 817 final File file = chooser.showSaveDialog(ConExpFX.instance.primaryStage); 818 if (file == null) 819 return; 820 final TeXOptions value = ret.value(); 821 value.file = file; 822 exportTeX(value); 823 } 824 } 825 826 public final void shutdown() { 827 828 } 829 830 @Override 831 public void close() { 832 // TODO Auto-generated method stub 833 834 } 835 836}