Commit b13c6e18 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

draw modification residues using new information

parent cd7156ce
......@@ -46,7 +46,7 @@ import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.common.geometry.ColorParser;
import lcsb.mapviewer.converter.ConverterException;
import lcsb.mapviewer.converter.annotation.XmlAnnotationParser;
import lcsb.mapviewer.converter.graphics.bioentity.element.species.SpeciesConverter;
import lcsb.mapviewer.converter.graphics.bioentity.element.species.ModificationResidueConverter;
import lcsb.mapviewer.converter.graphics.bioentity.reaction.ReactionConverter;
import lcsb.mapviewer.converter.model.celldesigner.CommonXmlParser;
import lcsb.mapviewer.model.LogMarker;
......@@ -586,8 +586,8 @@ public class SbgnmlXmlExporter {
glyph.setId(mr.getIdModificationResidue());
glyph.setClazz(GlyphClazz.STATE_VARIABLE.getClazz());
double width = SpeciesConverter.DEFAULT_MODIFICATION_DIAMETER;
double height = SpeciesConverter.DEFAULT_MODIFICATION_DIAMETER;
double width = ModificationResidueConverter.DEFAULT_MODIFICATION_DIAMETER;
double height = ModificationResidueConverter.DEFAULT_MODIFICATION_DIAMETER;
if (mr instanceof AbstractSiteModification) {
AbstractSiteModification modification = (AbstractSiteModification) mr;
Glyph.State state = new Glyph.State();
......
......@@ -18,6 +18,7 @@ import lcsb.mapviewer.commands.SemanticZoomLevelMatcher;
import lcsb.mapviewer.common.MimeType;
import lcsb.mapviewer.common.Pair;
import lcsb.mapviewer.converter.graphics.bioentity.BioEntityConverterImpl;
import lcsb.mapviewer.converter.graphics.bioentity.element.species.ModificationResidueConverter;
import lcsb.mapviewer.converter.graphics.bioentity.element.species.StructuralStateConverter;
import lcsb.mapviewer.converter.graphics.bioentity.element.species.StructuralStateSbgnConverter;
import lcsb.mapviewer.converter.graphics.layer.LayerLineConverter;
......@@ -37,6 +38,7 @@ import lcsb.mapviewer.model.map.reaction.Reaction;
import lcsb.mapviewer.model.map.species.Complex;
import lcsb.mapviewer.model.map.species.Element;
import lcsb.mapviewer.model.map.species.Species;
import lcsb.mapviewer.model.map.species.field.ModificationResidue;
import lcsb.mapviewer.model.map.species.field.StructuralState;
import lcsb.mapviewer.model.overlay.DataOverlayEntry;
......@@ -212,7 +214,7 @@ public abstract class AbstractImageGenerator {
// Get the SBGN display format option from the model
this.sbgnFormat = params.isSbgn();
List<Drawable> drawables = new ArrayList<>(params.getModel().getDrawables());
List<Drawable> drawables = new ArrayList<>(params.getModel().getDrawables(true));
drawables.sort(BioEntity.Z_INDEX_COMPARATOR);
// draw all elements
......@@ -233,6 +235,8 @@ public abstract class AbstractImageGenerator {
drawLine((PolylineData) element);
} else if (element instanceof StructuralState) {
drawStructuralState((StructuralState) element);
} else if (element instanceof ModificationResidue) {
drawModificationResidue((ModificationResidue) element);
} else {
throw new DrawingException("Unknown class type: " + element);
}
......@@ -241,6 +245,7 @@ public abstract class AbstractImageGenerator {
setDrawn(true);
}
private void drawText(final LayerText element) {
new LayerTextConverter().draw(element, graphics);
}
......@@ -258,13 +263,24 @@ public abstract class AbstractImageGenerator {
}
private void drawStructuralState(final StructuralState structuralState) {
ConverterParams stateParams = new ConverterParams().level(level).nested(params.nested);
if (this.sbgnFormat) {
new StructuralStateSbgnConverter().draw(structuralState, graphics);
new StructuralStateSbgnConverter().draw(structuralState, graphics, stateParams);
} else {
new StructuralStateConverter().draw(structuralState, graphics);
new StructuralStateConverter().draw(structuralState, graphics, stateParams);
}
}
protected void drawModificationResidue(ModificationResidue structuralState) {
ConverterParams stateParams = new ConverterParams().level(level).nested(params.nested);
if (this.sbgnFormat) {
new ModificationResidueConverter().drawModification(structuralState, graphics, stateParams);
} else {
new ModificationResidueConverter().drawModification(structuralState, graphics, stateParams);
}
}
/**
* Method called after drawing. It should close drawing canvas properly.
*/
......
package lcsb.mapviewer.converter.graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.List;
import lcsb.mapviewer.commands.SemanticZoomLevelMatcher;
import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.common.exception.InvalidStateException;
import lcsb.mapviewer.model.graphics.HorizontalAlign;
import lcsb.mapviewer.model.graphics.VerticalAlign;
import lcsb.mapviewer.model.map.BioEntity;
import lcsb.mapviewer.model.map.Drawable;
import lcsb.mapviewer.model.map.compartment.Compartment;
import lcsb.mapviewer.model.map.reaction.AbstractNode;
import lcsb.mapviewer.model.map.reaction.NodeOperator;
import lcsb.mapviewer.model.map.reaction.Product;
import lcsb.mapviewer.model.map.reaction.Reactant;
import lcsb.mapviewer.model.map.reaction.Reaction;
import lcsb.mapviewer.model.map.reaction.ReactionNode;
import lcsb.mapviewer.model.map.species.Complex;
import lcsb.mapviewer.model.map.species.Element;
import lcsb.mapviewer.model.map.species.Species;
public abstract class DrawableConverter<T extends Drawable> {
/**
* Class that allows to check if element is visible (or transparent) when
* drawing. It's used to filter out invisible elements when drawing
* semantic/hierarchy view.
*/
private SemanticZoomLevelMatcher zoomLevelMatcher = new SemanticZoomLevelMatcher();
/**
* This method draws a string on graphics using current font. The coordinates
* of the text is given as a point. Both parameters centered described if text
* should be automatically centered horizontally and vertically.
*
* @param border
* where the text should be drawn
* @param text
* text to draw
* @param graphics
* where we want to draw the object
* @param horizontalCentered
* should the text be horizontally centered
* @param verticalCentered
* should the text be vertically centered
*/
protected void drawText(final Rectangle2D border, final String text, final Graphics2D graphics,
final HorizontalAlign horizontalAlign, final VerticalAlign verticalAlign) {
double textHeight = getTextHeight(text, graphics);
double y = border.getY();
String[] lines = text.split("\n");
double lineHeight = graphics.getFontMetrics().getHeight();
switch (verticalAlign) {
case TOP:
y = border.getMinY() + graphics.getFontMetrics().getAscent();
break;
case MIDDLE:
y = border.getCenterY() - (textHeight / 2 - graphics.getFontMetrics().getAscent());
break;
case BOTTOM:
y = border.getMaxY() - (textHeight - graphics.getFontMetrics().getAscent());
break;
default:
throw new InvalidArgumentException("Don't know how to align text with: " + verticalAlign);
}
for (final String string : lines) {
double currX;
switch (horizontalAlign) {
case LEFT:
currX = border.getX();
break;
case CENTER:
currX = border.getCenterX() - getTextWidth(string, graphics) / 2;
break;
case RIGTH:
currX = border.getMaxX() - getTextWidth(string, graphics);
break;
default:
throw new InvalidArgumentException("Don't know how to align text with: " + horizontalAlign);
}
graphics.drawString(string, (int) currX, (int) y);
y += lineHeight;
}
}
/**
* Return width of the text.
*
* @param text
* width of this text will be computed
* @param graphics
* the width will be computed assuming using this graphics
* @return width of the text
*/
protected double getTextWidth(final String text, final Graphics2D graphics) {
if (text == null) {
return 0;
}
if (text.equals("")) {
return 0;
}
double result = 0;
String[] lines = text.split("\n");
for (final String string : lines) {
result = Math.max(result, graphics.getFontMetrics().stringWidth(string));
}
return result;
}
/**
* Returns text height.
*
* @param text
* height of this text will be computed
* @param graphics
* the height will be computed assuming using this graphics
* @return height of the text
*/
protected double getTextHeight(final String text, final Graphics2D graphics) {
if (text == null) {
return 0;
}
if (text.equals("")) {
return 0;
}
double result = 0;
int lines = text.split("\n").length;
result = graphics.getFontMetrics().getHeight() * lines;
return result;
}
/**
* Checks if {@link AbstractNode} is visible according to visualization given
* in params.
*
* @param node
* visibility of this object will be checked
* @param params
* visualization params
* @return true if object is visible
*/
protected boolean isVisible(final AbstractNode node, final ConverterParams params) {
if (node instanceof NodeOperator) {
return isVisible((NodeOperator) node, params);
} else if (node instanceof ReactionNode) {
return isVisible(((ReactionNode) node).getElement(), params);
} else {
throw new InvalidArgumentException("Unknown class type: " + node.getClass());
}
}
/**
* Checks if {@link NodeOperator} is visible according to visualization given
* in params.
*
* @param operator
* visibility of this object will be checked
* @param params
* visualization params
* @return true if object is visible
*/
protected boolean isVisible(final NodeOperator operator, final ConverterParams params) {
boolean result = false;
if (operator.isModifierOperator() || operator.isReactantOperator()) {
for (final AbstractNode input : operator.getInputs()) {
result |= isVisible(input, params);
}
} else if (operator.isProductOperator()) {
for (final AbstractNode output : operator.getOutputs()) {
result |= isVisible(output, params);
}
} else {
throw new InvalidStateException("Unknown class state: " + operator);
}
return result;
}
/**
* Checks if {@link BioEntity} is visible according to visualization given in
* params.
*
* @param bioEntity
* visibility of this object will be checked
* @param params
* visualization params
* @return true if object is visible
*/
protected boolean isVisible(final BioEntity bioEntity, final ConverterParams params) {
if (params.isNested()) {
boolean result = zoomLevelMatcher.isVisible(params.getLevel(), bioEntity.getVisibilityLevel());
if (bioEntity instanceof Element) {
Compartment compartment = ((Element) bioEntity).getCompartment();
if (compartment != null) {
result &= isVisible(compartment, params);
}
if (bioEntity instanceof Species) {
Complex complex = ((Species) bioEntity).getComplex();
if (complex != null) {
result &= isVisible(complex, params);
}
}
} else if (bioEntity instanceof Reaction) {
if (!isAnyProductVisible(((Reaction) bioEntity).getProducts(), params)) {
result = false;
} else if (!isAnyReactantVisible(((Reaction) bioEntity).getReactants(), params)) {
result = false;
}
} else {
throw new InvalidArgumentException("Unknown class type: " + bioEntity.getClass());
}
return result;
}
return true;
}
/**
* Checks if {@link Element} given in the argument is transparent according to
* the level given in the {@link ConverterParams}.
*
* @param bioEntity
* {@link BioEntity} to be checked
* @param params
* params against which check is run
* @return <code>true</code> if object is transparent, <code>false</code>
* otherwise
*/
protected boolean isTransparent(final Element bioEntity, final ConverterParams params) {
return zoomLevelMatcher.isTransparent(params.getLevel(), bioEntity.getTransparencyLevel()) || !params.isNested();
}
/**
* Checks if at least one reactant is visible.
*
* @param reactants
* list of reactants
* @param params
* params against which check is run
* @return true if at least one reactant is visible
*/
private boolean isAnyReactantVisible(final List<Reactant> reactants, final ConverterParams params) {
boolean result = false;
for (final Reactant reactant : reactants) {
if (isVisible(reactant, params)) {
result = true;
}
}
return result;
}
/**
* Checks if at least one product is visible.
*
* @param products
* list of products
* @param params
* params against which check is run
* @return true if at least one product is visible
*/
private boolean isAnyProductVisible(final List<Product> products, final ConverterParams params) {
boolean result = false;
for (final Product product : products) {
if (isVisible(product, params)) {
result = true;
}
}
return result;
}
}
......@@ -7,22 +7,10 @@ import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import lcsb.mapviewer.commands.SemanticZoomLevelMatcher;
import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.common.exception.InvalidStateException;
import lcsb.mapviewer.converter.graphics.ConverterParams;
import lcsb.mapviewer.converter.graphics.DrawableConverter;
import lcsb.mapviewer.converter.graphics.DrawingException;
import lcsb.mapviewer.model.map.BioEntity;
import lcsb.mapviewer.model.map.compartment.Compartment;
import lcsb.mapviewer.model.map.reaction.AbstractNode;
import lcsb.mapviewer.model.map.reaction.NodeOperator;
import lcsb.mapviewer.model.map.reaction.Product;
import lcsb.mapviewer.model.map.reaction.Reactant;
import lcsb.mapviewer.model.map.reaction.Reaction;
import lcsb.mapviewer.model.map.reaction.ReactionNode;
import lcsb.mapviewer.model.map.species.Complex;
import lcsb.mapviewer.model.map.species.Element;
import lcsb.mapviewer.model.map.species.Species;
import lcsb.mapviewer.model.overlay.DataOverlayEntry;
/**
......@@ -34,24 +22,19 @@ import lcsb.mapviewer.model.overlay.DataOverlayEntry;
* @param <T>
* class of alias to convert
*/
public abstract class BioEntityConverter<T extends BioEntity> {
public abstract class BioEntityConverter<T extends BioEntity> extends DrawableConverter<T> {
/**
* Alpha value (0..255) used for visualizing overlay data that are normally
* visualized in javascript.
* visualized in JavaScript.
*/
public static final int LAYOUT_ALPHA = 200;
/**
* Default class logger.
*/
@SuppressWarnings("unused")
private final Logger logger = LogManager.getLogger();
/**
* Class that allows to check if element is visible (or transparent) when
* drawing. It's used to filter out invisible elements when drawing
* semantic/hierarchy view.
*/
private SemanticZoomLevelMatcher zoomLevelMatcher = new SemanticZoomLevelMatcher();
/**
* This function draw {@link BioEntity} on the {@link Graphics2D} object.
......@@ -124,141 +107,4 @@ public abstract class BioEntityConverter<T extends BioEntity> {
*/
public abstract void drawText(final T bioEntity, final Graphics2D graphics, final ConverterParams params) throws DrawingException;
/**
* Checks if {@link AbstractNode} is visible according to visualization given in
* params.
*
* @param node
* visibility of this object will be checked
* @param params
* visualization params
* @return true if object is visible
*/
protected boolean isVisible(final AbstractNode node, final ConverterParams params) {
if (node instanceof NodeOperator) {
return isVisible((NodeOperator) node, params);
} else if (node instanceof ReactionNode) {
return isVisible(((ReactionNode) node).getElement(), params);
} else {
throw new InvalidArgumentException("Unknown class type: " + node.getClass());
}
}
/**
* Checks if {@link NodeOperator} is visible according to visualization given in
* params.
*
* @param operator
* visibility of this object will be checked
* @param params
* visualization params
* @return true if object is visible
*/
protected boolean isVisible(final NodeOperator operator, final ConverterParams params) {
boolean result = false;
if (operator.isModifierOperator() || operator.isReactantOperator()) {
for (final AbstractNode input : operator.getInputs()) {
result |= isVisible(input, params);
}
} else if (operator.isProductOperator()) {
for (final AbstractNode output : operator.getOutputs()) {
result |= isVisible(output, params);
}
} else {
throw new InvalidStateException("Unknown class state: " + operator);
}
return result;
}
/**
* Checks if {@link BioEntity} is visible according to visualization given in
* params.
*
* @param bioEntity
* visibility of this object will be checked
* @param params
* visualization params
* @return true if object is visible
*/
protected boolean isVisible(final BioEntity bioEntity, final ConverterParams params) {
if (params.isNested()) {
boolean result = zoomLevelMatcher.isVisible(params.getLevel(), bioEntity.getVisibilityLevel());
if (bioEntity instanceof Element) {
Compartment compartment = ((Element) bioEntity).getCompartment();
if (compartment != null) {
result &= isVisible(compartment, params);
}
if (bioEntity instanceof Species) {
Complex complex = ((Species) bioEntity).getComplex();
if (complex != null) {
result &= isVisible(complex, params);
}
}
} else if (bioEntity instanceof Reaction) {
if (!isAnyProductVisible(((Reaction) bioEntity).getProducts(), params)) {
result = false;
} else if (!isAnyReactantVisible(((Reaction) bioEntity).getReactants(), params)) {
result = false;
}
} else {
throw new InvalidArgumentException("Unknown class type: " + bioEntity.getClass());
}
return result;
}
return true;
}
/**
* Checks if {@link Element} given in the argument is transparent according to
* the level given in the {@link ConverterParams}.
*
* @param bioEntity
* {@link BioEntity} to be checked
* @param params
* params against which check is run
* @return <code>true</code> if object is transparent, <code>false</code>
* otherwise
*/
protected boolean isTransparent(final Element bioEntity, final ConverterParams params) {
return zoomLevelMatcher.isTransparent(params.getLevel(), bioEntity.getTransparencyLevel()) || !params.isNested();
}
/**
* Checks if at least one reactant is visible.
*
* @param reactants
* list of reactants
* @param params
* params against which check is run
* @return true if at least one reactant is visible
*/
private boolean isAnyReactantVisible(final List<Reactant> reactants, final ConverterParams params) {
boolean result = false;
for (final Reactant reactant : reactants) {
if (isVisible(reactant, params)) {
result = true;
}
}
return result;
}
/**
* Checks if at least one product is visible.
*
* @param products
* list of products
* @param params
* params against which check is run
* @return true if at least one product is visible
*/
private boolean isAnyProductVisible(final List<Product> products, final ConverterParams params) {
boolean result = false;
for (final Product product : products) {
if (isVisible(product, params)) {
result = true;
}
}
return result;
}
}
......@@ -15,7 +15,6 @@ import lcsb.mapviewer.commands.ColorExtractor;
import lcsb.mapviewer.converter.graphics.ConverterParams;
import lcsb.mapviewer.model.map.species.AntisenseRna;
import lcsb.mapviewer.model.map.species.Species;
import lcsb.mapviewer.model.map.species.field.ModificationResidue;