diff --git a/VERSION.txt b/VERSION.txt index 79a2734..5d4294b 100755 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -0.5.0 \ No newline at end of file +0.5.1 \ No newline at end of file diff --git a/docs/api_reference/python_api_index.rst b/docs/api_reference/python_api_index.rst index 9f7c2a8..71e23aa 100644 --- a/docs/api_reference/python_api_index.rst +++ b/docs/api_reference/python_api_index.rst @@ -1,20 +1,170 @@ Python API Reference ==================== -This is the API reference for the Python client library for the SBMLNetwork package. +Introduction +------------ -****************** -SBMLNetwork Class -****************** +For many biologists, working with SBML models is familiar, but translating them into meaningful visual representations can be challenging. The **SBMLNetwork Python Package** provides an intuitive way to explore SBML-based networks without requiring advanced programming knowledge. It offers structured access to **compartments**, **species**, and **reactions**, each represented with labels, shapes, and curves that reflect SBML concepts. By abstracting away visualization complexities, this API allows users to focus on biological insights rather than rendering details. -.. autoclass:: sbmlnetwork.SBMLNetwork - :members: - :undoc-members: - :show-inheritance: - :inherited-members: +Conceptual Overview of the Network Structure +-------------------------------------------- -***************** -Static Functions -***************** +SBMLNetwork visualizes biological networks in a way that mirrors the structure of SBML models. The network is composed of three key components: + +- **Compartments**: + Compartments act as containers for biological entities and processes. Each compartment is represented with its own label and shape, and provides direct access to the species and reactions it contains. This mirrors the idea of grouping related elements within a specific cellular or spatial context. + +- **Species**: + Species represent individual biological entities, such as molecules or genes, and are depicted as nodes. Each species comes with a label and a shape, and is connected to reactions via curves, visually demonstrating their role in various biological processes. + +- **Reactions**: + Reactions capture the interactions between species. Each reaction is labeled and features a central point, from which curves extend to illustrate the flow of interactions. These curves may consist of multiple segments and include arrowheads to indicate directionality. Reactions also provide access to the associated species, emphasizing the dynamic connectivity within the network. + +This hierarchical structure is designed to align with the intuitive understanding of SBML models, allowing users to easily navigate and visualize the complex interplay between different biological elements. + +Breakdown of Components +----------------------- + + +This diagram illustrates how the API components are structured, making it easier to understand their relationships and roles in the visualization of SBML-based networks. +The high-level API is organized into a clear, hierarchical structure. Below are the diagrams of the components and their relationships: + +Network +^^^^^^^ + +.. graphviz:: + :caption: High-Level API Architecture + + digraph sbml_network { + rankdir=TB; + node [shape=record, style=rounded, fontname=Helvetica]; + + Network [label="Network"]; + Compartments [ + label=< + + + + + + + + + + +
Compartments
- Shapes
- Labels
+ >, + shape=record, + style=rounded, + URL="http://compartments-info.com", + tooltip="Click for more compartment details" + ]; + Species [ + label=< + + + + + + + + + + +
Species
- Shapes
- Labels
+ >, + shape=record, + style=rounded, + URL="http://species-info.com", + tooltip="Click for more species details" + ]; + Reactions [ + label=< + + + + + + + + + + +
Reactions
- Center
- Labels
+ >, + shape=record, + style=rounded, + URL="http://reactions-info.com", + tooltip="Click for more reaction details" + ]; + Curves [ + label=< + + + + + + + + + + +
Curves
- Segments
- Arrowheads
+ >, + shape=record, + style=rounded, + URL="http://curves-info.com", + tooltip="Click for more curve details" + ]; + Labels [ + label=< + + + + + + + + + + +
Labels
- Text
- Position
+ >, + shape=record, + style=rounded, + URL="http://labels-info.com", + tooltip="Click for more label details" + ]; + Shapes [ + label=< + + + + + + + + + + +
Shapes
- Type
- Size
+ >, + shape=record, + style=rounded, + URL="http://shapes-info.com", + tooltip="Click for more shape details" + ]; + + Network -> Compartments; + Compartments -> Labels; + Compartments -> Shapes; + Network -> Species; + Species -> Labels; + Species -> Shapes; + Network -> Reactions; + Reactions -> Curves; + + + + } -.. autofunction:: sbmlnetwork.load \ No newline at end of file diff --git a/src/bindings/python/ctypes/libsbmlnetwork.py.cmake b/src/bindings/python/ctypes/libsbmlnetwork.py.cmake index 72a1483..394fa28 100644 --- a/src/bindings/python/ctypes/libsbmlnetwork.py.cmake +++ b/src/bindings/python/ctypes/libsbmlnetwork.py.cmake @@ -1759,6 +1759,58 @@ class LibSBMLNetwork: """ return lib.c_api_setSpeciesReferenceCurveSegmentBasePoint2Y(self.sbml_object, str(reaction_id).encode(), ctypes.c_double(y), reaction_glyph_index, species_reference_index, curve_segment_index, layout_index) + def makeSpeciesReferenceVisible(self, reaction_id, reaction_glyph_index=0, species_reference_index=0, layout_index=0): + """ + Makes the SpeciesReference with the given reaction_id, reaction_glyph_index, species_reference_index, and layout_index in the given SBMLDocument visible + + :Parameters: + + - reaction_id (string): a string that determines the id of the Reaction + - reaction_glyph_index (int): an integer that determines the index of the ReactionGlyph in the given SBMLDocument + - species_reference_index (int): an integer that determines the index of the SpeciesReference in the given SBMLDocument + - layout_index (int, optional): an integer (default: 0) that determines the index of the Layout object in the given SBMLDocument + + :Returns: + + true on success and false if the SpeciesReference could not be made visible + """ + return lib.c_api_makeSpeciesReferenceVisible(self.sbml_object, str(reaction_id).encode(), reaction_glyph_index, species_reference_index, layout_index) + + def makeSpeciesReferenceInvisible(self, reaction_id, reaction_glyph_index=0, species_reference_index=0, layout_index=0): + """ + Makes the SpeciesReference with the given reaction_id, reaction_glyph_index, species_reference_index, and layout_index in the given SBMLDocument invisible + + :Parameters: + + - reaction_id (string): a string that determines the id of the Reaction + - reaction_glyph_index (int): an integer that determines the index of the ReactionGlyph in the given SBMLDocument + - species_reference_index (int): an integer that determines the index of the SpeciesReference in the given SBMLDocument + - layout_index (int, optional): an integer (default: 0) that determines the index of the Layout object in the given SBMLDocument + + :Returns: + + true on success and false if the SpeciesReference could not be made invisible + """ + return lib.c_api_makeSpeciesReferenceInvisible(self.sbml_object, str(reaction_id).encode(), reaction_glyph_index, species_reference_index, layout_index) + + def isSpeciesReferenceVisible(self, reaction_id, reaction_glyph_index=0, species_reference_index=0, layout_index=0): + """ + Returns whether the SpeciesReference with the given reaction_id, reaction_glyph_index, species_reference_index, and layout_index in the given SBMLDocument is visible + + :Parameters: + + - reaction_id (string): a string that determines the id of the Reaction + - reaction_glyph_index (int): an integer that determines the index of the ReactionGlyph in the given SBMLDocument + - species_reference_index (int): an integer that determines the index of the SpeciesReference in the given SBMLDocument + - layout_index (int, optional): an integer (default: 0) that determines the index of the Layout object in the given SBMLDocument + + :Returns: + + true if the SpeciesReference with the given reaction_id, reaction_glyph_index, species_reference_index, and layout_index in the given SBMLDocument is visible and false otherwise + """ + lib.c_api_isSpeciesReferenceVisible.restype = ctypes.c_bool + return lib.c_api_isSpeciesReferenceVisible(self.sbml_object, str(reaction_id).encode(), reaction_glyph_index, species_reference_index, layout_index) + def isSetSpeciesReferenceLineColor(self, reaction_id, reaction_glyph_index=0, species_reference_index=0, layout_index=0): """ Returns whether the line color of the SpeciesReference with the given reaction_id, reaction_glyph_index, species_reference_index, and layout_index in the given SBMLDocument is set @@ -10343,6 +10395,41 @@ class LibSBMLNetwork: """ return lib.c_api_setGeometricShapeFillColor(self.sbml_object, str(id).encode(), str(fill_color).encode(), geometric_shape_index, graphical_object_index, layout_index) + def setGeometricShapeFillColorAsGradient(self, id, stop_colors = [], stop_offsets = [], gradient_type = "linear", geometric_shape_index=0, graphical_object_index=0, layout_index=0): + """ + Sets the fill color of the GeometricShape object with the given index associated with the model entity with the given id in the given SBMLDocument as a gradient + + :Parameters: + + - id (string): a string that determines the id of the model entity + - stop_colors (list, optional): a list of strings that determines the stop colors of the gradient + - stop_offsets (list, optional): a list of floats that determines the stop offsets of the gradient + - gradient_type (string, optional): a string that determines the type of the gradient + - geometric_shape_index (int, optional): an integer (default: 0) that determines the index of the GeometricShape object associated with the model entity with the given id in the given SBMLDocument + - graphical_object_index (int, optional): an integer (default: 0) that determines the index of the GraphicalObject in the given SBMLDocument + - layout_index (int, optional): an integer (default: 0) that determines the index of the Layout object in the given SBMLDocument + + :Returns: + + true on success and false if the fill color of the GraphicalObject could not be set + """ + if len(stop_colors) != len(stop_offsets): + raise ValueError("The number of stop colors list and stop offsets list should be the same") + + stop_colors_ptr = None + if stop_colors is not None: + stop_colors_ptr = (ctypes.c_char_p * len(stop_colors))() + for i in range(len(stop_colors)): + stop_colors_ptr[i] = ctypes.c_char_p(stop_colors[i].encode()) + + stop_offsets_ptr = None + if stop_offsets is not None: + stop_offsets_ptr = (ctypes.c_double * len(stop_offsets))() + for i in range(len(stop_offsets)): + stop_offsets_ptr[i] = ctypes.c_double(stop_offsets[i]) + + return lib.c_api_setGeometricShapeFillColorAsGradient(self.sbml_object, str(id).encode(), str(gradient_type).encode(), stop_colors_ptr, stop_offsets_ptr, ctypes.c_int(len(stop_colors)), geometric_shape_index, graphical_object_index, layout_index) + def isSetGeometricShapeX(self, id, geometric_shape_index=0, graphical_object_index=0, layout_index=0): """ Returns whether the x-coordinate of the GeometricShape object with the given index associated with the model entity with the given id in the given SBMLDocument is set diff --git a/src/bindings/python/ctypes/sbmlnetwork/requirements.txt.cmake b/src/bindings/python/ctypes/sbmlnetwork/requirements.txt.cmake index 038fb4e..7d1e51a 100755 --- a/src/bindings/python/ctypes/sbmlnetwork/requirements.txt.cmake +++ b/src/bindings/python/ctypes/sbmlnetwork/requirements.txt.cmake @@ -1,3 +1,6 @@ libsbmlnetwork>=${LIBSBMLNETWORK_DOTTED_VERSION} networkinfotranslator>=${LIBSBMLNETWORK_DOTTED_VERSION} pillow +tellurium +matplotlib + diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/features/__init__.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/features/__init__.py new file mode 100644 index 0000000..99d9bb8 --- /dev/null +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/features/__init__.py @@ -0,0 +1,2 @@ +from .align import HorizontalAlign, VerticalAlign, CircleAlign +from .data_integration import Fluxes diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/features/align.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/features/align.py new file mode 100644 index 0000000..a608dd4 --- /dev/null +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/features/align.py @@ -0,0 +1,1017 @@ +from ..network_elements.reaction import Reaction +from ..network_elements.species import Species + +import math + + +class AlignBase: + + def __init__(self, net): + self.net = net + self.padding = 8 + + def align(self, *args, **kwargs): + pass + + @staticmethod + def _get_extents(species_list): + x_min = math.inf + y_min = math.inf + x_max = -math.inf + y_max = -math.inf + for species in species_list: + if species.get_position()[0] < x_min: + x_min = species.get_position()[0] + if species.get_position()[1] < y_min: + y_min = species.get_position()[1] + if species.get_position()[0] + species.get_size()[0] > x_max: + x_max = species.get_position()[0] + species.get_size()[0] + if species.get_position()[1] + species.get_size()[1] > y_max: + y_max = species.get_position()[1] + species.get_size()[1] + return x_max, x_min, y_max, y_min + + @staticmethod + def _get_species_order(species_list, species_order): + if species_order is None or len(species_order) == 0: + species_list.sort(key=lambda x: x.get_position()[0]) + return species_list + + ordered_species = [] + for species in species_order: + if isinstance(species, str): + for i in range(len(species_list)): + if species_list[i].get_species_id() == species: + ordered_species.append(species_list[i]) + species_list.remove(species_list[i]) + break + elif isinstance(species, Species): + for i in range(len(species_list)): + if species_list[i].get_id() == species.get_id(): + ordered_species.append(species_list[i]) + species_list.remove(species_list[i]) + break + ordered_species.extend(species_list) + return ordered_species + + @staticmethod + def _get_product_species(reaction, species_list): + return [species for species in species_list if + species.get_role(reaction) in ["product", "sideproduct", "side product"]] + + @staticmethod + def _get_reactant_species(reaction, species_list): + return [species for species in species_list if + species.get_role(reaction) in ["substrate", "sidesubstrate", "side substrate", "reactant", + "sidereactant", "side reactant"]] + + @staticmethod + def _get_modifier_species(reaction, species_list): + return [species for species in species_list if + species.get_role(reaction) not in ["substrate", "sidesubstrate", "side substrate", "reactant", + "sidereactant", "side reactant", "product", "sideproduct", + "side product"]] + + +class HorizontalAlign(AlignBase): + + def align(self, reaction: Reaction, center_at: tuple[float, float], spread: float, + reactants_order: list[str or Species], products_order: list[str or Species], + modifiers_order: list[str or Species], + reactants_placement: str, products_placement: str, modifiers_placement: str): + species_list = reaction.get_species_list() + x_min, x_max, x_center, y_center = self._get_positional_parameters(center_at, species_list, spread) + reactant_species = self._get_reactant_species(reaction, species_list) + if len(reactant_species) == 0: + empty_species = reaction.get_empty_species() + if empty_species is None: + raise ValueError(f"Reaction \"{reaction.get_reaction_id()}\" has no reactants and no empty species.") + else: + reactant_species = [empty_species] + product_species = self._get_product_species(reaction, species_list) + if len(product_species) == 0: + empty_species = reaction.get_empty_species() + if empty_species is None: + raise ValueError(f"Reaction \"{reaction.get_reaction_id()}\" has no products and no empty species.") + else: + product_species = [empty_species] + modifier_species = self._get_modifier_species(reaction, species_list) + reactant_species = self._get_species_order(reactant_species, reactants_order) + product_species = self._get_species_order(product_species, products_order) + modifier_species = self._get_species_order(modifier_species, modifiers_order) + try: + reactant_species[0].move_to((x_min, y_center - 0.5 * reactant_species[0].get_size()[1])) + product_species[-1].move_to((x_max - product_species[-1].get_size()[0], y_center - 0.5 * product_species[-1].get_size()[1])) + except ValueError: + raise ValueError(f"Using this 'center_at' or 'spread_width' for the reaction \"{reaction.get_reaction_id()}\" will cause the species to be placed outside the canvas.") + + if len(reactant_species) == 1 and len(product_species) == 1 and len(modifier_species) == 0: + return True + + self._set_reaction_center_features(reaction, x_center, y_center) + self._set_first_reactant_features(reactant_species, reaction, x_center, y_center) + self._set_last_product_features(product_species, reaction, x_center, y_center) + + if len(reactant_species) > 1 and not self._set_extra_reactant_features(reactant_species, reaction, x_center, + y_center, reactants_placement): + return False + + if len(product_species) > 1 and not self._set_extra_product_features(product_species, reaction, x_center, + y_center, products_placement): + return False + + if len(modifier_species) > 0 and not self._set_modifier_features(modifier_species, reaction, x_center, y_center, + modifiers_placement): + return False + + return True + + def _set_last_product_features(self, product_species, reaction, x_center, y_center): + curves = reaction.get_curves_list(product_species[-1]) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + curve_segment = curve.get_segment() + curve_segment.set_start((x_center, y_center)) + curve_segment.set_end((product_species[-1].get_position()[0] - self.padding, + product_species[-1].get_position()[1] + 0.5 * product_species[-1].get_size()[1])) + curve_segment.set_control_point_1(curve_segment.get_start()) + curve_segment.set_control_point_2(curve_segment.get_end()) + + return True + + def _set_first_reactant_features(self, reactant_species, reaction, x_center, y_center): + curves = reaction.get_curves_list(reactant_species[0]) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + curve_segment = curve.get_segment() + curve_segment.set_start((x_center, y_center)) + curve_segment.set_end( + (reactant_species[0].get_position()[0] + reactant_species[0].get_size()[0] + self.padding, + reactant_species[0].get_position()[1] + 0.5 * reactant_species[0].get_size()[1])) + curve_segment.set_control_point_1(curve_segment.get_start()) + curve_segment.set_control_point_2(curve_segment.get_end()) + + return True + + def _get_positional_parameters(self, center_at, species_list, spread_width): + x_max, x_min, y_max, y_min = self._get_extents(species_list) + x_center = (x_min + x_max) / 2 + y_center = (y_min + y_max) / 2 + if center_at is not None: + x_min = (x_min - x_center) + center_at[0] + x_max = (x_max - x_center) + center_at[0] + x_center = center_at[0] + y_min = (y_min - y_center) + center_at[1] + y_max = (y_max - y_center) + center_at[1] + y_center = center_at[1] + if spread_width is not None: + x_min = x_center - spread_width / 2 + x_max = x_center + spread_width / 2 + + if center_at is None and spread_width is None: + if x_min < 0: + x_center = x_center - x_min + 100 + x_min = 100 + if y_min < 0: + y_center = y_center - y_min + 100 + y_min = 100 + if x_max - x_min < 200: + x_max = x_min + 200 + x_center = (x_min + x_max) / 2 + + return x_min, x_max, x_center, y_center + + def _set_reaction_center_features(self, reaction, x_center, y_center): + reaction.set_position((x_center, y_center)) + reaction.get_labels_list().set_positions((x_center, y_center + self.padding)) + return True + + def _set_extra_reactant_features(self, reactant_species, reaction, x_center, y_center, species_placement): + default_horizontal_padding = 0.5 * max([species.get_size()[0] for species in reactant_species]) + default_vertical_padding = 2 * max([species.get_size()[1] for species in reactant_species]) + for i, species in enumerate(reactant_species): + if i == 0: + continue + new_position_x = reactant_species[i - 1].get_position()[0] + reactant_species[i - 1].get_size()[ + 0] + default_horizontal_padding + new_position_y = y_center + default_vertical_padding + new_curve_position_x = new_position_x + 0.5 * species.get_size()[0] + new_curve_position_y = new_position_y - self.padding + if species_placement == "up": + new_position_y = y_center - species.get_size()[1] - default_vertical_padding + new_curve_position_y = new_position_y + species.get_size()[1] + self.padding + elif species_placement == "both": + new_position_x = reactant_species[i - 1].get_position()[0] + ( + (i % 2) * (reactant_species[i - 1].get_size()[0] + default_horizontal_padding)) + new_curve_position_x = new_position_x + 0.5 * species.get_size()[0] + if (i - 1) % 2 == 0: + new_position_y = y_center - species.get_size()[1] - default_vertical_padding + new_curve_position_y = new_position_y + species.get_size()[1] + self.padding + else: + new_position_y = y_center + default_vertical_padding + new_curve_position_y = new_position_y - self.padding + try: + species.move_to((new_position_x, new_position_y)) + except ValueError: + raise ValueError(f"Using this 'center_at' or 'spread_width' for the reaction \"{reaction.get_reaction_id()}\" will cause the species to be placed outside the canvas.") + + curves = reaction.get_curves_list(species=species) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + curve_segment = curve.get_segment() + curve_segment.set_start((x_center, y_center)) + curve_segment.set_end((new_curve_position_x, new_curve_position_y)) + curve_segment.set_control_point_1((species.get_position()[0] + 0.5 * species.get_size()[0], y_center)) + curve_segment.set_control_point_2((species.get_position()[0] + 0.5 * species.get_size()[0], y_center)) + + return True + + def _set_extra_product_features(self, product_species, reaction, x_center, y_center, species_placement): + default_horizontal_padding = 0.5 * max([species.get_size()[0] for species in product_species]) + default_vertical_padding = 2 * max([species.get_size()[1] for species in product_species]) + product_species.reverse() + for i, species in enumerate(product_species): + if i == 0: + continue + new_position_x = product_species[i - 1].get_position()[0] - product_species[i].get_size()[ + 0] - default_horizontal_padding + new_position_y = y_center + default_vertical_padding + new_curve_position_x = new_position_x + 0.5 * species.get_size()[0] + new_curve_position_y = new_position_y - self.padding + if species_placement == "up": + new_position_y = y_center - species.get_size()[1] - default_vertical_padding + new_curve_position_y = new_position_y + species.get_size()[1] + self.padding + elif species_placement == "both": + new_position_x = product_species[i - 1].get_position()[0] - ( + (i % 2) * (product_species[i].get_size()[0] + default_horizontal_padding)) + new_curve_position_x = new_position_x + 0.5 * species.get_size()[0] + if (i - 1) % 2 == 0: + new_position_y = y_center - species.get_size()[1] - default_vertical_padding + new_curve_position_y = new_position_y + species.get_size()[1] + self.padding + else: + new_position_y = y_center + default_vertical_padding + new_curve_position_y = new_position_y - self.padding + try: + species.move_to((new_position_x, new_position_y)) + except ValueError: + raise ValueError(f"Using this 'center_at' or 'spread_width' for the reaction \"{reaction.get_reaction_id()}\" will cause the species to be placed outside the canvas.") + + curves = reaction.get_curves_list(species=species) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + curve_segment = curve.get_segment() + curve_segment.set_start((x_center, y_center)) + curve_segment.set_end((new_curve_position_x, new_curve_position_y)) + curve_segment.set_control_point_1((species.get_position()[0] + 0.5 * species.get_size()[0], y_center)) + curve_segment.set_control_point_2((species.get_position()[0] + 0.5 * species.get_size()[0], y_center)) + + return True + + def _set_modifier_features(self, modifier_species, reaction, x_center, y_center, species_placement): + default_horizontal_padding = 0.5 * max([species.get_size()[0] for species in modifier_species]) + default_vertical_padding = 4 * max([species.get_size()[1] for species in modifier_species]) + for i, species in enumerate(modifier_species): + arrow_head_size = (0, 0) + arrow_head = reaction.get_curves_list(species=species)[0].get_arrow_head() + if arrow_head is not None: + arrow_head_size = arrow_head.get_size() + offset_unit = species.get_size()[0] + default_horizontal_padding + new_position_x = x_center - 0.5 * species.get_size()[0] + new_position_y = y_center + default_vertical_padding + new_reaction_point_x = x_center + new_reaction_point_y = y_center + 0.5 * arrow_head_size[0] + if len(modifier_species) % 2 == 1: + if i == 0: + pass + elif i % 2 == 1: + new_position_x += offset_unit * ((i + 1) // 2) + new_reaction_point_x += 1.5 * arrow_head_size[1] * ((i + 1) // 2) + else: + new_position_x += - offset_unit * (i // 2) + new_reaction_point_x += - 1.5 * arrow_head_size[1] * (i // 2) + else: + if i % 2 == 0: + new_position_x += - offset_unit * ((i // 2) + 0.5) + new_reaction_point_x += - 1.5 * arrow_head_size[1] * ((i // 2) + 0.5) + else: + new_position_x += offset_unit * ((i // 2) + 0.5) + new_reaction_point_x += 1.5 * arrow_head_size[1] * ((i // 2) + 0.5) + new_curve_position_x = new_position_x + 0.5 * species.get_size()[0] + new_curve_position_y = new_position_y - self.padding + if species_placement == "up": + new_position_y = y_center - default_vertical_padding + new_curve_position_y = new_position_y + species.get_size()[1] + self.padding + new_reaction_point_y = y_center - 0.5 * arrow_head_size[0] + elif species_placement == "both": + pass + # ToDo: Implement the case when the placement is "both" + + try: + species.move_to((new_position_x, new_position_y)) + except ValueError: + raise ValueError(f"Using this 'center_at' or 'spread_width' for the reaction \"{reaction.get_reaction_id()}\" will cause the species to be placed outside the canvas.") + + curves = reaction.get_curves_list(species=species) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + curve_segment = curve.get_segment() + curve_segment.set_start((new_curve_position_x, new_curve_position_y)) + curve_segment.set_end((new_reaction_point_x, new_reaction_point_y)) + curve_segment.set_control_point_1(curve_segment.get_start()) + curve_segment.set_control_point_2(curve_segment.get_end()) + + return True + + +class VerticalAlign(AlignBase): + + def align(self, reaction: Reaction, center_at: tuple[float, float], spread: float, + reactants_order: list[str or Species], products_order: list[str or Species], + modifiers_order: list[str or Species], + reactants_placement: str, products_placement: str, modifiers_placement: str): + species_list = reaction.get_species_list() + y_min, y_max, x_center, y_center = self._get_positional_parameters(center_at, species_list, spread) + reactant_species = self._get_reactant_species(reaction, species_list) + product_species = self._get_product_species(reaction, species_list) + modifier_species = self._get_modifier_species(reaction, species_list) + reactant_species = self._get_species_order(reactant_species, reactants_order) + product_species = self._get_species_order(product_species, products_order) + modifier_species = self._get_species_order(modifier_species, modifiers_order) + try : + reactant_species[0].move_to((x_center - 0.5 * reactant_species[0].get_size()[0], y_min)) + product_species[-1].move_to((x_center - 0.5 * product_species[-1].get_size()[0], y_max - product_species[-1].get_size()[1])) + except ValueError: + raise ValueError(f"Using this 'center_at' or 'spread_height' for the reaction \"{reaction.get_reaction_id()}\" will cause the species to be placed outside the canvas.") + + if len(reactant_species) == 1 and len(product_species) == 1: + return True + + self._set_reaction_center_features(reaction, x_center, y_center) + self._set_first_reactant_features(reactant_species, reaction, x_center, y_center) + self._set_last_product_features(product_species, reaction, x_center, y_center) + + if len(reactant_species) > 1 and not self._set_extra_reactant_features(reactant_species, reaction, x_center, + y_center, reactants_placement): + return False + + if len(product_species) > 1 and not self._set_extra_product_features(product_species, reaction, x_center, + y_center, products_placement): + return False + + if len(modifier_species) > 0 and not self._set_modifier_features(modifier_species, reaction, x_center, y_center, + modifiers_placement): + return False + + return True + + def _set_last_product_features(self, product_species, reaction, x_center, y_center): + curves = reaction.get_curves_list(product_species[-1]) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + curve_segment = curve.get_segment() + curve_segment.set_start((x_center, y_center)) + curve_segment.set_end((product_species[-1].get_position()[0] + 0.5 * product_species[-1].get_size()[0], + product_species[-1].get_position()[1] - self.padding)) + curve_segment.set_control_point_1(curve_segment.get_start()) + curve_segment.set_control_point_2(curve_segment.get_end()) + + return True + + def _set_first_reactant_features(self, reactant_species, reaction, x_center, y_center): + curves = reaction.get_curves_list(reactant_species[0]) + for curve in curves: + while len(curve.get_segments()) > 1: + curve.remove_segment(curve.get_segment()) + curve_segment = curve.get_segment() + curve_segment.set_start((x_center, y_center)) + curve_segment.set_end((reactant_species[0].get_position()[0] + 0.5 * reactant_species[0].get_size()[0], + reactant_species[0].get_position()[1] + reactant_species[0].get_size()[ + 1] + self.padding)) + curve_segment.set_control_point_1(curve_segment.get_start()) + curve_segment.set_control_point_2(curve_segment.get_end()) + + return True + + def _get_positional_parameters(self, center_at, species_list, spread_height): + x_max, x_min, y_max, y_min = self._get_extents(species_list) + x_center = (x_min + x_max) / 2 + y_center = (y_min + y_max) / 2 + if center_at is not None: + x_min = (x_min - x_center) + center_at[0] + x_max = (x_max - x_center) + center_at[0] + x_center = center_at[0] + y_min = (y_min - y_center) + center_at[1] + y_max = (y_max - y_center) + center_at[1] + y_center = center_at[1] + if spread_height is not None: + y_min = y_center - spread_height / 2 + y_max = y_center + spread_height / 2 + + if center_at is None and spread_height is None: + if x_min < 0: + x_center = x_center - x_min + 100 + x_min = 100 + if y_min < 0: + y_center = y_center - y_min + 100 + y_min = 100 + if y_max - y_min < 200: + y_max = y_min + 200 + y_center = (y_min + y_max) / 2 + + return y_min, y_max, x_center, y_center + + def _set_reaction_center_features(self, reaction, x_center, y_center): + reaction.set_position((x_center, y_center)) + reaction.get_labels_list().set_positions((x_center - self.padding, y_center)) + return True + + def _set_extra_reactant_features(self, reactant_species, reaction, x_center, y_center, species_placement): + default_horizontal_padding = 1.5 * max([species.get_size()[0] for species in reactant_species]) + default_vertical_padding = max([species.get_size()[1] for species in reactant_species]) + for i, species in enumerate(reactant_species): + if i == 0: + continue + new_position_x = x_center + default_horizontal_padding + new_position_y = reactant_species[i - 1].get_position()[1] + reactant_species[i - 1].get_size()[ + 1] + default_vertical_padding + new_curve_position_x = new_position_x - self.padding + new_curve_position_y = new_position_y + 0.5 * species.get_size()[1] + if species_placement == "left": + new_position_x = x_center - default_horizontal_padding - species.get_size()[0] + new_curve_position_x = new_position_x + species.get_size()[0] + self.padding + elif species_placement == "both": + new_position_y = reactant_species[i - 1].get_position()[1] + ( + (i % 2) * (reactant_species[i - 1].get_size()[1] + default_vertical_padding)) + new_curve_position_y = new_position_y + 0.5 * species.get_size()[1] + if (i - 1) % 2 == 0: + new_position_x = x_center - species.get_size()[0] - default_horizontal_padding + new_curve_position_x = new_position_x + species.get_size()[0] + self.padding + else: + new_position_x = x_center + default_horizontal_padding + new_curve_position_x = new_position_x - self.padding + try: + species.move_to((new_position_x, new_position_y)) + except ValueError: + raise ValueError(f"Using this 'center_at' or 'spread_height' for the reaction \"{reaction.get_reaction_id()}\" will cause the species to be placed outside the canvas.") + + curves = reaction.get_curves_list(species=species) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + curve_segment = curve.get_segment() + curve_segment.set_start((x_center, y_center)) + curve_segment.set_end((new_curve_position_x, new_curve_position_y)) + curve_segment.set_control_point_1((x_center, species.get_position()[1] + 0.5 * species.get_size()[1])) + curve_segment.set_control_point_2((x_center, species.get_position()[1] + 0.5 * species.get_size()[1])) + + return True + + def _set_extra_product_features(self, product_species, reaction, x_center, y_center, species_placement): + default_horizontal_padding = 1.5 * max([species.get_size()[0] for species in product_species]) + default_vertical_padding = max([species.get_size()[1] for species in product_species]) + product_species.reverse() + for i, species in enumerate(product_species): + if i == 0: + continue + new_position_x = x_center + default_horizontal_padding + new_position_y = product_species[i - 1].get_position()[1] - product_species[i].get_size()[ + 1] - default_vertical_padding + new_curve_position_x = new_position_x - self.padding + new_curve_position_y = new_position_y + 0.5 * species.get_size()[1] + if species_placement == "left": + new_position_x = x_center - default_horizontal_padding - species.get_size()[0] + new_curve_position_x = new_position_x + species.get_size()[0] + self.padding + elif species_placement == "both": + new_position_y = product_species[i - 1].get_position()[1] - ( + (i % 2) * (product_species[i].get_size()[1] + default_vertical_padding)) + new_curve_position_y = new_position_y + 0.5 * species.get_size()[1] + if (i - 1) % 2 == 0: + new_position_x = x_center - species.get_size()[0] - default_horizontal_padding + new_curve_position_x = new_position_x + species.get_size()[0] + self.padding + else: + new_position_x = x_center + default_horizontal_padding + new_curve_position_x = new_position_x - self.padding + try: + species.move_to((new_position_x, new_position_y)) + except ValueError: + raise ValueError(f"Using this 'center_at' or 'spread_height' for the reaction \"{reaction.get_reaction_id()}\" will cause the species to be placed outside the canvas.") + + curves = reaction.get_curves_list(species=species) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + curve_segment = curve.get_segment() + curve_segment.set_start((x_center, y_center)) + curve_segment.set_end((new_curve_position_x, new_curve_position_y)) + curve_segment.set_control_point_1((x_center, species.get_position()[1] + 0.5 * species.get_size()[1])) + curve_segment.set_control_point_2((x_center, species.get_position()[1] + 0.5 * species.get_size()[1])) + + return True + + def _set_modifier_features(self, modifier_species, reaction, x_center, y_center, species_placement): + default_horizontal_padding = 3 * max([species.get_size()[0] for species in modifier_species]) + default_vertical_padding = max([species.get_size()[1] for species in modifier_species]) + for i, species in enumerate(modifier_species): + arrow_head_size = (0, 0) + arrow_head = reaction.get_curves_list(species=species)[0].get_arrow_head() + if arrow_head is not None: + arrow_head_size = arrow_head.get_size() + offset_unit = species.get_size()[1] + default_vertical_padding + new_position_x = x_center + default_horizontal_padding + new_position_y = y_center - 0.5 * species.get_size()[1] + new_reaction_point_x = x_center + 0.5 * arrow_head.get_size()[0] + new_reaction_point_y = y_center + if len(modifier_species) % 2 == 1: + if i == 0: + pass + elif i % 2 == 1: + new_position_y += offset_unit * ((i + 1) // 2) + new_reaction_point_y += 1.5 * arrow_head_size[1] * ((i + 1) // 2) + else: + new_position_y += - offset_unit * (i // 2) + new_reaction_point_y += - 1.5 * arrow_head_size[1] * (i // 2) + else: + if i % 2 == 0: + new_position_y += - offset_unit * (((i) // 2) + 0.5) + new_reaction_point_y += - 1.5 * arrow_head_size[1] * (((i) // 2) + 0.5) + else: + new_position_y += offset_unit * (((i) // 2) + 0.5) + new_reaction_point_y += 1.5 * arrow_head_size[1] * (((i) // 2) + 0.5) + new_curve_position_x = new_position_x - self.padding + new_curve_position_y = new_position_y + 0.5 * species.get_size()[1] + if species_placement == "left": + new_position_x = x_center - default_horizontal_padding - species.get_size()[0] + new_curve_position_x = new_position_x + species.get_size()[0] + self.padding + new_reaction_point_x = x_center - 0.5 * arrow_head_size[0] + elif species_placement == "both": + pass + # ToDo: Implement the case when the placement is "both" + + try: + species.move_to((new_position_x, new_position_y)) + except ValueError: + raise ValueError(f"Using this 'center_at' or 'spread_height' for the reaction \"{reaction.get_reaction_id()}\" will cause the species to be placed outside the canvas.") + + curves = reaction.get_curves_list(species=species) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + curve_segment = curve.get_segment() + curve_segment.set_start((new_curve_position_x, new_curve_position_y)) + curve_segment.set_end((new_reaction_point_x, new_reaction_point_y)) + curve_segment.set_control_point_1((curve_segment.get_start())) + curve_segment.set_control_point_2((curve_segment.get_end())) + + return True + + +class CircleAlign(AlignBase): + + def align(self, reaction: Reaction, center_at: tuple[float, float], radius: float, arc_start: float, arc_end: float, + reactants_order: list[str or Species], products_order: list[str or Species], + modifiers_order: list[str or Species], + reactants_placement: str, products_placement: str, modifiers_placement: str): + arc_start_rad, arc_end_rad = self._get_arc_parameters(arc_start, arc_end) + species_list = reaction.get_species_list() + x_center, y_center, radius = self._get_positional_parameters(center_at, species_list, radius) + reactant_species = self._get_reactant_species(reaction, species_list) + product_species = self._get_product_species(reaction, species_list) + modifier_species = self._get_modifier_species(reaction, species_list) + reactant_species = self._get_species_order(reactant_species, reactants_order) + product_species = self._get_species_order(product_species, products_order) + modifier_species = self._get_species_order(modifier_species, modifiers_order) + try: + reactant_species[0].move_to( + (x_center + radius * math.cos(arc_start_rad) - 0.5 * reactant_species[0].get_size()[0], + y_center - radius * math.sin(arc_start_rad) - 0.5 * reactant_species[0].get_size()[1])) + product_species[-1].move_to( + (x_center + radius * math.cos(arc_end_rad) - 0.5 * product_species[-1].get_size()[0], + y_center - radius * math.sin(arc_end_rad) - 0.5 * product_species[-1].get_size()[1])) + except ValueError: + raise ValueError(f"Using this 'center_at' or 'radius' for the reaction \"{reaction.get_reaction_id()}\" will cause the species to be placed outside the canvas.") + + self._set_reaction_center_features(reaction, x_center, y_center, radius, arc_start_rad, arc_end_rad) + self._set_first_reactant_features(reactant_species, reaction, x_center, y_center, radius, arc_start_rad, + arc_end_rad) + self._set_last_product_features(product_species, reaction, x_center, y_center, radius, arc_start_rad, + arc_end_rad) + + if len(reactant_species) == 1 and len(product_species) == 1: + return True + + if len(reactant_species) > 1 and not self._set_extra_reactant_features(reactant_species, reaction, x_center, + y_center, radius, arc_start_rad, + arc_end_rad, reactants_placement): + return False + + if len(product_species) > 1 and not self._set_extra_product_features(product_species, reaction, x_center, + y_center, radius, arc_start_rad, + arc_end_rad, products_placement): + return False + + if len(modifier_species) > 0 and not self._set_modifier_features(modifier_species, reaction, x_center, y_center, + radius, arc_start_rad, arc_end_rad, + modifiers_placement): + return False + + return True + + def _set_reaction_center_features(self, reaction, x_center, y_center, radius, arc_start_rad, arc_end_rad): + arc_reaction_point = self._get_arc_reaction_point(arc_start_rad, arc_end_rad) + reaction.set_position( + (x_center + radius * math.cos(arc_reaction_point), y_center - radius * math.sin(arc_reaction_point))) + reaction.get_labels_list().set_positions((x_center + radius * math.cos(arc_reaction_point), + y_center - radius * math.sin(arc_reaction_point) + self.padding)) + return True + + def _set_first_reactant_features(self, reactant_species, reaction, x_center, y_center, radius, arc_start_rad, + arc_end_rad): + arc_reaction_point = self._get_arc_reaction_point(arc_start_rad, arc_end_rad) + curves = reaction.get_curves_list(reactant_species[0]) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + offset_x, offset_y = self._get_reactant_point_offsets(arc_start_rad, reactant_species[0]) + curve_segment = curve.get_segment() + start_x = x_center + radius * math.cos(arc_reaction_point) + start_y = y_center - radius * math.sin(arc_reaction_point) + curve_segment.set_start((start_x, start_y)) + end_x = x_center + radius * math.cos(arc_start_rad) + offset_x + end_y = y_center - radius * math.sin(arc_start_rad) + offset_y + curve_segment.set_end((end_x, end_y)) + delta = arc_start_rad - arc_reaction_point + k = (4.0 / 3.0) * math.tan(delta / 4.0) + P1 = (x_center + radius * (math.cos(arc_reaction_point) - k * math.sin(arc_reaction_point)), + y_center - radius * (math.sin(arc_reaction_point) + k * math.cos(arc_reaction_point))) + P2 = (x_center + radius * (math.cos(arc_start_rad) + k * math.sin(arc_start_rad)), + y_center - radius * (math.sin(arc_start_rad) - k * math.cos(arc_start_rad))) + curve_segment.set_control_point_1(P1) + curve_segment.set_control_point_2(P2) + + return True + + def _set_last_product_features(self, product_species, reaction, x_center, y_center, radius, arc_start_rad, + arc_end_rad): + arc_reaction_point = self._get_arc_reaction_point(arc_start_rad, arc_end_rad) + curves = reaction.get_curves_list(product_species[-1]) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + offset_x, offset_y = self._get_product_point_offsets(arc_end_rad, product_species[-1]) + curve_segment = curve.get_segment() + start_x = x_center + radius * math.cos(arc_reaction_point) + start_y = y_center - radius * math.sin(arc_reaction_point) + curve_segment.set_start((start_x, start_y)) + end_x = x_center + radius * math.cos(arc_end_rad) + offset_x + end_y = y_center - radius * math.sin(arc_end_rad) + offset_y + curve_segment.set_end((end_x, end_y)) + delta = arc_reaction_point - arc_end_rad + k = (4.0 / 3.0) * math.tan(delta / 4.0) + p1 = (x_center + radius * (math.cos(arc_reaction_point) + k * math.sin(arc_reaction_point)), + y_center - radius * (math.sin(arc_reaction_point) - k * math.cos(arc_reaction_point))) + p2 = (x_center + radius * (math.cos(arc_end_rad) - k * math.sin(arc_end_rad)), + y_center - radius * (math.sin(arc_end_rad) + k * math.cos(arc_end_rad))) + curve_segment.set_control_point_1(p1) + curve_segment.set_control_point_2(p2) + + return True + + def _set_extra_reactant_features(self, reactant_species, reaction, x_center, y_center, radius, arc_start_rad, + arc_end_rad, species_placement): + arc_reaction_point = self._get_arc_reaction_point(arc_start_rad, arc_end_rad) + max_width = max([species.get_size()[0] for species in reactant_species]) + max_height = max([species.get_size()[1] for species in reactant_species]) + default_radial_padding = 2 * math.sqrt((max_width ** 2 + max_height ** 2) / 2) + default_angular_padding_arc = min(((arc_end_rad - arc_start_rad) / (len(reactant_species) - 1)), + (2 * math.sqrt((max_width ** 2 + max_height ** 2) / 2) / radius)) + control_point_angular_padding_arc = 0.5 * default_angular_padding_arc + for i, species in enumerate(reactant_species): + if i == 0: + continue + new_position_x = x_center + (radius + default_radial_padding) * math.cos( + arc_start_rad + i * default_angular_padding_arc) + new_position_y = y_center - (radius + default_radial_padding) * math.sin( + arc_start_rad + i * default_angular_padding_arc) + new_curve_control_point_1_x = x_center + radius * math.cos(arc_start_rad + i * default_angular_padding_arc) + new_curve_start_position_x = x_center + radius * math.cos( + arc_start_rad + i * (default_angular_padding_arc) + control_point_angular_padding_arc) + new_curve_control_point_1_y = y_center - radius * math.sin(arc_start_rad + i * default_angular_padding_arc) + new_curve_start_position_y = y_center - radius * math.sin( + arc_start_rad + i * (default_angular_padding_arc) + control_point_angular_padding_arc) + offset_x, offset_y = self._get_extra_reactant_point_offsets(arc_start_rad + i * default_angular_padding_arc, + species) + if species_placement == "in": + new_position_x = x_center + (radius - default_radial_padding) * math.cos( + arc_start_rad + i * default_angular_padding_arc) + new_position_y = y_center - (radius - default_radial_padding) * math.sin( + arc_start_rad + i * default_angular_padding_arc) + offset_x = -offset_x + offset_y = -offset_y + elif species_placement == "both": + if (i - 1) % 2 == 0: + new_position_x = x_center + (radius + default_radial_padding) * math.cos( + arc_start_rad + ((i / 2) + 1) * default_angular_padding_arc) + new_position_y = y_center - (radius + default_radial_padding) * math.sin( + arc_start_rad + ((i / 2) + 1) * default_angular_padding_arc) + new_curve_control_point_1_x = x_center + radius * math.cos( + arc_start_rad + ((i / 2) + 1) * default_angular_padding_arc) + new_curve_control_point_1_y = y_center - radius * math.sin( + arc_start_rad + ((i / 2) + 1) * default_angular_padding_arc) + new_curve_start_position_x = x_center + radius * math.cos( + arc_start_rad + ((i / 2) + 1) * default_angular_padding_arc + control_point_angular_padding_arc) + new_curve_start_position_y = y_center - radius * math.sin( + arc_start_rad + ((i / 2) + 1) * default_angular_padding_arc + control_point_angular_padding_arc) + else: + new_position_x = x_center + (radius - default_radial_padding) * math.cos( + arc_start_rad + (((i - 1) / 2) + 1) * default_angular_padding_arc) + new_position_y = y_center - (radius - default_radial_padding) * math.sin( + arc_start_rad + (((i - 1) / 2) + 1) * default_angular_padding_arc) + new_curve_control_point_1_x = x_center + radius * math.cos( + arc_start_rad + (((i - 1) / 2) + 1) * default_angular_padding_arc) + new_curve_control_point_1_y = y_center - radius * math.sin( + arc_start_rad + (((i - 1) / 2) + 1) * default_angular_padding_arc) + new_curve_start_position_x = x_center + radius * math.cos(arc_start_rad + ( + ((i - 1) / 2) + 1) * default_angular_padding_arc + control_point_angular_padding_arc) + new_curve_start_position_y = y_center - radius * math.sin(arc_start_rad + ( + ((i - 1) / 2) + 1) * default_angular_padding_arc + control_point_angular_padding_arc) + offset_x = -offset_x + offset_y = -offset_y + new_curve_end_position_x = new_position_x + offset_x + new_curve_end_position_y = new_position_y + offset_y + try: + species.move_to((new_position_x - 0.5 * species.get_size()[0], new_position_y - 0.5 * species.get_size()[1])) + except ValueError: + raise ValueError(f"Using this 'center_at' or 'radius' for the reaction \"{reaction.get_reaction_id()}\" will cause the species to be placed outside the canvas.") + + curves = reaction.get_curves_list(species=species) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + curve_segment = curve.get_segment() + curve_segment.set_start((new_curve_start_position_x, new_curve_start_position_y)) + curve_segment.set_end((new_curve_end_position_x, new_curve_end_position_y)) + curve_segment.set_control_point_1((new_curve_control_point_1_x, new_curve_control_point_1_y)) + curve_segment.set_control_point_2((new_curve_end_position_x, new_curve_end_position_y)) + + return True + + def _set_extra_product_features(self, product_species, reaction, x_center, y_center, radius, arc_start_rad, + arc_end_rad, species_placement): + arc_reaction_point = self._get_arc_reaction_point(arc_start_rad, arc_end_rad) + max_width = max([species.get_size()[0] for species in product_species]) + max_height = max([species.get_size()[1] for species in product_species]) + default_radial_padding = 2 * math.sqrt((max_width ** 2 + max_height ** 2) / 2) + default_angular_padding_arc = min(((arc_end_rad - arc_start_rad) / (len(product_species) - 1)), + (2 * math.sqrt((max_width ** 2 + max_height ** 2) / 2) / radius)) + control_point_angular_padding_arc = 0.5 * default_angular_padding_arc + product_species.reverse() + for i, species in enumerate(product_species): + if i == 0: + continue + new_position_x = x_center + (radius + default_radial_padding) * math.cos( + arc_end_rad - i * default_angular_padding_arc) + new_position_y = y_center - (radius + default_radial_padding) * math.sin( + arc_end_rad - i * default_angular_padding_arc) + new_curve_control_point_1_x = x_center + radius * math.cos(arc_end_rad - i * default_angular_padding_arc) + new_curve_start_position_x = x_center + radius * math.cos( + arc_end_rad - i * (default_angular_padding_arc) - control_point_angular_padding_arc) + new_curve_control_point_1_y = y_center - radius * math.sin(arc_end_rad - i * default_angular_padding_arc) + new_curve_start_position_y = y_center - radius * math.sin( + arc_end_rad - i * (default_angular_padding_arc) - control_point_angular_padding_arc) + offset_x, offset_y = self._get_extra_product_point_offsets(arc_end_rad - i * default_angular_padding_arc, + species) + if species_placement == "in": + new_position_x = x_center + (radius - default_radial_padding) * math.cos( + arc_end_rad - i * default_angular_padding_arc) + new_position_y = y_center - (radius - default_radial_padding) * math.sin( + arc_end_rad - i * default_angular_padding_arc) + offset_x = -offset_x + offset_y = -offset_y + elif species_placement == "both": + if (i - 1) % 2 == 0: + new_position_x = x_center + (radius + default_radial_padding) * math.cos( + arc_end_rad - ((i / 2) + 1) * default_angular_padding_arc) + new_position_y = y_center - (radius + default_radial_padding) * math.sin( + arc_end_rad - ((i / 2) + 1) * default_angular_padding_arc) + new_curve_control_point_1_x = x_center + radius * math.cos( + arc_end_rad - ((i / 2) + 1) * default_angular_padding_arc) + new_curve_control_point_1_y = y_center - radius * math.sin( + arc_end_rad - ((i / 2) + 1) * default_angular_padding_arc) + new_curve_start_position_x = x_center + radius * math.cos( + arc_end_rad - ((i / 2) + 1) * default_angular_padding_arc - control_point_angular_padding_arc) + new_curve_start_position_y = y_center - radius * math.sin( + arc_end_rad - ((i / 2) + 1) * default_angular_padding_arc - control_point_angular_padding_arc) + else: + new_position_x = x_center + (radius - default_radial_padding) * math.cos( + arc_end_rad - (((i - 1) / 2) + 1) * default_angular_padding_arc) + new_position_y = y_center - (radius - default_radial_padding) * math.sin( + arc_end_rad - (((i - 1) / 2) + 1) * default_angular_padding_arc) + new_curve_control_point_1_x = x_center + radius * math.cos( + arc_end_rad - (((i - 1) / 2) + 1) * default_angular_padding_arc) + new_curve_control_point_1_y = y_center - radius * math.sin( + arc_end_rad - (((i - 1) / 2) + 1) * default_angular_padding_arc) + new_curve_start_position_x = x_center + radius * math.cos(arc_end_rad - ( + ((i - 1) / 2) + 1) * default_angular_padding_arc - control_point_angular_padding_arc) + new_curve_start_position_y = y_center - radius * math.sin(arc_end_rad - ( + ((i - 1) / 2) + 1) * default_angular_padding_arc - control_point_angular_padding_arc) + offset_x = -offset_x + offset_y = -offset_y + new_curve_end_position_x = new_position_x + offset_x + new_curve_end_position_y = new_position_y + offset_y + try: + species.move_to((new_position_x - 0.5 * species.get_size()[0], new_position_y - 0.5 * species.get_size()[1])) + except ValueError: + raise ValueError(f"Using this 'center_at' or 'radius' for the reaction \"{reaction.get_reaction_id()}\" will cause the species to be placed outside the canvas.") + + curves = reaction.get_curves_list(species=species) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + curve_segment = curve.get_segment() + curve_segment.set_start((new_curve_start_position_x, new_curve_start_position_y)) + curve_segment.set_end((new_curve_end_position_x, new_curve_end_position_y)) + curve_segment.set_control_point_1((new_curve_control_point_1_x, new_curve_control_point_1_y)) + curve_segment.set_control_point_2((new_curve_end_position_x, new_curve_end_position_y)) + + return True + + def _set_modifier_features(self, modifier_species, reaction, x_center, y_center, radius, + arc_start_rad, arc_end_rad, species_placement): + arc_reaction_point = self._get_arc_reaction_point(arc_start_rad, arc_end_rad) + max_width = max([species.get_size()[0] for species in modifier_species]) + max_height = max([species.get_size()[1] for species in modifier_species]) + default_angular_padding_arc = 2 * math.sqrt((max_width ** 2 + max_height ** 2) / 2) / radius + default_radial_padding = 2 * math.sqrt((max_width ** 2 + max_height ** 2) / 2) + if species_placement == "in": + default_radial_padding = -default_radial_padding + for i, species in enumerate(modifier_species): + arrow_head_size = (0, 0) + arrow_head = reaction.get_curves_list(species=species)[0].get_arrow_head() + if arrow_head is not None: + arrow_head_size = arrow_head.get_size() + if i == 0: + angular_offset = 0 + if species_placement == "in": + new_reaction_point_x = x_center + (radius - 0.5 * arrow_head_size[0]) * math.cos(arc_reaction_point) + new_reaction_point_y = y_center - (radius - 0.5 * arrow_head_size[1]) * math.sin(arc_reaction_point) + else: + new_reaction_point_x = x_center + (radius + 0.5 * arrow_head_size[0]) * math.cos(arc_reaction_point) + new_reaction_point_y = y_center - (radius + 0.5 * arrow_head_size[1]) * math.sin(arc_reaction_point) + else: + angular_offset = ((-1) ** i) * math.ceil(i / 2) * default_angular_padding_arc + if species_placement == "in": + new_reaction_point_x = x_center + (radius - 0.5 * arrow_head_size[0]) * math.cos(arc_reaction_point + ((-1) ** i) * math.ceil(i / 2) * arrow_head_size[0] / (radius - arrow_head_size[0])) + new_reaction_point_y = y_center - (radius - 0.5 * arrow_head_size[1]) * math.sin(arc_reaction_point + ((-1) ** i) * math.ceil(i / 2) * arrow_head_size[1] / (radius - arrow_head_size[1])) + else: + new_reaction_point_x = x_center + (radius + 0.5 * arrow_head_size[0]) * math.cos(arc_reaction_point + ((-1) ** i) * math.ceil(i / 2) * arrow_head_size[0] / (radius + arrow_head_size[0])) + new_reaction_point_y = y_center - (radius + 0.5 * arrow_head_size[1]) * math.sin(arc_reaction_point + ((-1) ** i) * math.ceil(i / 2) * arrow_head_size[1] / (radius + arrow_head_size[1])) + placement_angle = arc_reaction_point + angular_offset + new_position_x = x_center + (radius + default_radial_padding) * math.cos(placement_angle) + new_position_y = y_center - (radius + default_radial_padding) * math.sin(placement_angle) + curve_angle = arc_end_rad + angular_offset + new_curve_start_position_x = x_center + radius * math.cos(curve_angle) + new_curve_start_position_y = y_center - radius * math.sin(curve_angle) + modifier_relative_arc = math.atan2( + (y_center - radius * math.sin(arc_reaction_point) - new_position_y), + new_position_x - (x_center + radius * math.cos(arc_reaction_point)) + ) + offset_x, offset_y = self._get_modifier_point_offsets(modifier_relative_arc, species) + new_curve_start_position_x = new_position_x + offset_x + new_curve_start_position_y = new_position_y + offset_y + if not species.move_to( + (new_position_x - 0.5 * species.get_size()[0], + new_position_y - 0.5 * species.get_size()[1])): + return False + curves = reaction.get_curves_list(species=species) + for curve in curves: + while (len(curve.get_segments()) > 1): + curve.remove_segment(curve.get_segment()) + curve_segment = curve.get_segment() + curve_segment.set_start((new_curve_start_position_x, new_curve_start_position_y)) + curve_segment.set_end((new_reaction_point_x, new_reaction_point_y)) + curve_segment.set_control_point_1((new_curve_start_position_x, new_curve_start_position_y)) + curve_segment.set_control_point_2((new_reaction_point_x, new_reaction_point_y)) + + return True + + def _get_positional_parameters(self, center_at, species_list, radius): + x_max, x_min, y_max, y_min = self._get_extents(species_list) + x_center = (x_min + x_max) / 2 + y_center = (y_min + y_max) / 2 + if center_at is not None: + x_min = (x_min - x_center) + center_at[0] + x_max = (x_max - x_center) + center_at[0] + x_center = center_at[0] + y_min = (y_min - y_center) + center_at[1] + y_max = (y_max - y_center) + center_at[1] + y_center = center_at[1] + new_radius = radius + if radius is None: + new_radius = math.sqrt((x_max - x_min) ** 2 + (y_max - y_min) ** 2) / 2 + + if center_at is None and radius is None: + if x_center - new_radius < 0: + x_center = new_radius + 100 + if y_center - new_radius < 0: + y_center = new_radius + 100 + + return x_center, y_center, new_radius + + @staticmethod + def _get_arc_parameters(arc_start, arc_end): + if arc_start < 0: + arc_start += 360 + elif arc_start >= 360: + arc_start -= 360 + if arc_end < 0: + arc_end += 360 + elif arc_end >= 360: + arc_end -= 360 + if arc_start > arc_end: + arc_end += 360 + return math.radians(arc_start), math.radians(arc_end) + + @staticmethod + def _get_arc_reaction_point(arc_start_rad, arc_end_rad): + start = arc_start_rad % (2 * math.pi) + end = arc_end_rad % (2 * math.pi) + diff = (end - start) % (2 * math.pi) + half_diff = diff / 2.0 + arc_reaction_point = (start + half_diff) % (2 * math.pi) + + return arc_reaction_point + + def _get_reactant_point_offsets(self, arc_reactant_rad, species): + width, height = species.get_size() + hw = width / 2.0 + hh = height / 2.0 + dx = -math.sin(arc_reactant_rad) + dy = -math.cos(arc_reactant_rad) + epsilon = 1e-6 + scale_x = hw / abs(dx) if abs(dx) > epsilon else float('inf') + scale_y = hh / abs(dy) if abs(dy) > epsilon else float('inf') + scale = min(scale_x, scale_y) + offset_x = dx * (scale + self.padding) + offset_y = dy * (scale + self.padding) + return offset_x, offset_y + + def _get_extra_reactant_point_offsets(self, arc_reactant_rad, species): + width, height = species.get_size() + hw, hh = width / 2.0, height / 2.0 + dx = -math.cos(arc_reactant_rad) + dy = math.sin(arc_reactant_rad) + epsilon = 1e-6 + scale_x = hw / abs(dx) if abs(dx) > epsilon else float('inf') + scale_y = hh / abs(dy) if abs(dy) > epsilon else float('inf') + scale = min(scale_x, scale_y) + offset_x = dx * (scale + self.padding) + offset_y = dy * (scale + self.padding) + + return offset_x, offset_y + + def _get_product_point_offsets(self, arc_product_rad, species): + width, height = species.get_size() + hw = width / 2.0 + hh = height / 2.0 + dx = math.sin(arc_product_rad) + dy = math.cos(arc_product_rad) + epsilon = 1e-6 + scale_x = hw / abs(dx) if abs(dx) > epsilon else float('inf') + scale_y = hh / abs(dy) if abs(dy) > epsilon else float('inf') + scale = min(scale_x, scale_y) + offset_x = dx * (scale + self.padding) + offset_y = dy * (scale + self.padding) + return offset_x, offset_y + + def _get_extra_product_point_offsets(self, arc_product_rad, species): + width, height = species.get_size() + hw, hh = width / 2.0, height / 2.0 + dx = - math.cos(arc_product_rad) + dy = math.sin(arc_product_rad) + epsilon = 1e-6 + scale_x = hw / abs(dx) if abs(dx) > epsilon else float('inf') + scale_y = hh / abs(dy) if abs(dy) > epsilon else float('inf') + scale = min(scale_x, scale_y) + offset_x = dx * (scale + self.padding) + offset_y = dy * (scale + self.padding) + + return offset_x, offset_y + + def _get_modifier_point_offsets(self, arc_modifier_rad, species): + width, height = species.get_size() + hw = width / 2.0 + hh = height / 2.0 + dx = - math.cos(arc_modifier_rad) + dy = math.sin(arc_modifier_rad) + epsilon = 1e-6 + scale_x = hw / abs(dx) if abs(dx) > epsilon else float('inf') + scale_y = hh / abs(dy) if abs(dy) > epsilon else float('inf') + scale = min(scale_x, scale_y) + offset_x = dx * (scale + self.padding) + offset_y = dy * (scale + self.padding) + return offset_x, offset_y diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/features/data_integration.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/features/data_integration.py new file mode 100644 index 0000000..4aa524d --- /dev/null +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/features/data_integration.py @@ -0,0 +1,48 @@ +from matplotlib.colors import LinearSegmentedColormap +import tellurium as te + + +class Fluxes: + + def display(self, network_obj, simulation_end_time, simulation_start_time, simulation_time_steps, fluxes): + if fluxes is None: + fluxes = self._get_fluxes(network_obj, simulation_end_time, simulation_start_time, simulation_time_steps) + color_bar = network_obj.get_color_bar() + color_bar.set_max_value(max_value=max(fluxes.values())) + color_bar.set_min_value(min_value=min(fluxes.values())) + for reaction_id in fluxes: + color = self._get_color(color_bar, fluxes[reaction_id]) + reactions = network_obj.get_reactions_list(reaction_id) + for reaction in reactions: + reaction.set_colors(color) + + return True + + @staticmethod + def _get_color(color_bar, value): + colors = color_bar.get_gradient_colors()[::-1] + max_value = color_bar.get_max_value() + min_value = color_bar.get_min_value() + if max_value == min_value: + normalized_value = 0 + color_bar.set_gradient_colors([colors[0], colors[0]]) + color_bar.set_number_of_tick_marks(2) + else: + normalized_value = (value - min_value) / (max_value - min_value) + camp = LinearSegmentedColormap.from_list('my_cmap', colors) + rgba = camp(normalized_value) + r, g, b, a = rgba + hex_color = '#{:02x}{:02x}{:02x}{:02x}'.format(int(r * 255), int(g * 255), int(b * 255), int(a * 255)) + + return hex_color + + @staticmethod + def _get_fluxes(network_obj, simulation_end_time, simulation_start_time, simulation_time_steps): + model = network_obj.save() + r = te.loadSBMLModel(model) + r.simulate(start=simulation_start_time, end=simulation_end_time, steps=simulation_time_steps) + fluxes = {} + for i, reaction in enumerate(r.getReactionIds()): + fluxes[reaction] = float(r.getReactionRates()[i]) + + return fluxes diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/additional_element.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/additional_element.py index 0795ef6..b73d015 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/additional_element.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/additional_element.py @@ -1,5 +1,6 @@ from .network_element_base import NetworkElementBase + class AdditionalElement(NetworkElementBase): def __init__(self, libsbmletwork, element_id): @@ -7,7 +8,7 @@ def __init__(self, libsbmletwork, element_id): # ToDo: fix the issue with adding label to it - def __str__(self): + def get_info(self): result = [] result.append(f"id: {self.get_id()}") result.append(f"position: {self.get_position()}") @@ -27,8 +28,11 @@ def __str__(self): if label != labels[-1]: result.append("----") - return "\n".join(result) + @property + def info(self): + return self.get_info() + def __repr__(self): return f"AdditionalElement(id={self.element_id})" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/color_bar.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/color_bar.py index 66cb913..ef93d73 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/color_bar.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/color_bar.py @@ -1,5 +1,6 @@ from .additional_element import AdditionalElement + class ColorBar(AdditionalElement): def __init__(self, libsbmlnetwork, element_id): @@ -28,7 +29,7 @@ def _initialize(self): self.get_shape().set_border_color(border_color) self.get_shape().set_border_thickness(border_thickness) self.get_shape().set_corner_radius((corner_radius, corner_radius)) - self.libsbmlnetwork.setFillColorAsGradient(id=self.element_id, + self.libsbmlnetwork.setGeometricShapeFillColorAsGradient(id=self.element_id, geometric_shape_index=0, stop_colors=gradient_colors, stop_offsets=[i * 100 / (len(gradient_colors) - 1) for i in range(len(gradient_colors))], @@ -74,7 +75,7 @@ def _update_labels(self): if label.set_size((1.5 * label.get_size()[0], label.get_size()[1])) == False: return False - return self.get_labels_list().set_font_size(font_size) + return self.get_labels_list().set_font_sizes(font_size) def set_position(self, position: tuple[float, float]): pass @@ -221,7 +222,14 @@ def get_gradient_colors(self): return ['#D32F2F', '#FF7F32', '#A8D400', '#00B4B4', '#1E3A5F'] - #ToDo: implement set_gradient_colors + def set_gradient_colors(self, gradient_colors: list[str]): + if self.libsbmlnetwork.setGeometricShapeFillColorAsGradient(id=self.element_id, geometric_shape_index=0, + stop_colors=gradient_colors, + stop_offsets=[i * 100 / (len(gradient_colors) - 1) for i in range(len(gradient_colors))], + gradient_type="linear") == 0: + return True + + return False def get_tick_mark_thickness(self): if self.get_shapes_list() and len(self.get_shapes_list()) > 1: @@ -271,7 +279,7 @@ def set_tick_mark_label_font_size(self, tick_mark_label_font_size: int): def clear_color_bar_space(self): self.libsbmlnetwork.setCanvasWidth(self._get_max_compartments_position_x()) - def __str__(self): + def get_info(self): return ( f"left_margin: {self.get_left_margin()}\n" + f"right_margin: {self.get_right_margin()}\n" + @@ -289,3 +297,10 @@ def __str__(self): f"position: {self.get_position()}\n" + f"size: {self.get_size()}" ) + + @property + def info(self): + return self.get_info() + + def __repr__(self): + return f"ColorBar(id={self.element_id})" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/compartment.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/compartment.py index f513af1..5675fe7 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/compartment.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/compartment.py @@ -12,6 +12,10 @@ def __init__(self, libsbmlnetwork, element_id, graphical_object_index): def get_compartment_id(self): return self.element_id + @property + def compartment_id(self): + return self.get_compartment_id() + def get_species_list(self): species_list = SpeciesList(libsbmlnetwork=self.libsbmlnetwork) species_ids = self.libsbmlnetwork.getListOfSpeciesIds() @@ -23,6 +27,13 @@ def get_species_list(self): return species_list + def get_species(self): + return self.get_species_list() + + @property + def species(self): + return self.get_species() + def get_reactions_list(self): reactions_list = ReactionList(libsbmlnetwork=self.libsbmlnetwork) reaction_ids = self.libsbmlnetwork.getListOfReactionIds() @@ -35,7 +46,26 @@ def get_reactions_list(self): return reactions_list - def __str__(self): + def get_reactions(self): + return self.get_reactions_list() + + @property + def reactions(self): + return self.get_reactions() + + def set_position(self, position: tuple[float, float]): + if position[0] < 0 or position[1] < 0: + raise ValueError(f"The compartment {self.get_compartment_id()} cannot be moved to {position} because it would lead to negative coordinates.") + + if super().set_position(position): + if self.get_position()[0] + self.get_size()[0] > self.libsbmlnetwork.getCanvasWidth(): + self.libsbmlnetwork.setCanvasWidth(self.get_position()[0] + self.get_size()[0]) + if self.get_position()[1] + self.get_size()[1] > self.libsbmlnetwork.getCanvasHeight(): + self.libsbmlnetwork.setCanvasHeight(self.get_position()[1] + self.get_size()[1]) + + return True + + def get_info(self): result = [] result.append(f"compartment id: {self.get_compartment_id()}") result.append(f"id: {self.get_id()}") @@ -64,8 +94,11 @@ def __str__(self): if label != labels[-1]: result.append("----") - return "\n".join(result) + @property + def info(self): + return self.get_info() + def __repr__(self): return f"Compartment(id={self.element_id}, index={self.graphical_object_index})" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_base.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_base.py index 8a3337c..efb168f 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_base.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_base.py @@ -12,6 +12,10 @@ def __init__(self, libsbmlnetwork, element_id, graphical_object_index): def get_id(self): return self.libsbmlnetwork.getId(id=self.element_id, graphical_object_index=self.graphical_object_index) + @property + def id(self): + return self.get_id() + def get_graphical_object_index(self): return self.graphical_object_index @@ -20,11 +24,22 @@ def get_position(self): self.libsbmlnetwork.getY(id=self.element_id, graphical_object_index=self.graphical_object_index)) def set_position(self, position: tuple[float, float]): - if self.libsbmlnetwork.setX(id=self.element_id, graphical_object_index=self.graphical_object_index, x=position[0], update_curves=False) == 0 and \ - self.libsbmlnetwork.setY(id=self.element_id, graphical_object_index=self.graphical_object_index, y=position[1], update_curves=False) == 0: - return True + if self._can_move_to(position): + if self.libsbmlnetwork.setX(id=self.element_id, graphical_object_index=self.graphical_object_index, x=position[0], update_curves=False) == 0 and \ + self.libsbmlnetwork.setY(id=self.element_id, graphical_object_index=self.graphical_object_index, y=position[1], update_curves=False) == 0: + return True - return False + return False + + raise ValueError(f"The network element {self.get_id()} cannot be moved to {position} because it would lead to negative coordinates.") + + @property + def position(self): + return self.get_position() + + @position.setter + def position(self, position: tuple[float, float]): + self.set_position(position) def get_size(self): return (self.libsbmlnetwork.getWidth(id=self.element_id, graphical_object_index=self.graphical_object_index), @@ -37,6 +52,14 @@ def set_size(self, size: tuple[float, float]): return False + @property + def size(self): + return self.get_size() + + @size.setter + def size(self, size: tuple[float, float]): + self.set_size(size) + def add_label(self, text: str, relative_position: tuple[float, float] = (0.0, 0.0)): if self.libsbmlnetwork.addText(id=self.element_id, graphical_object_index=self.graphical_object_index, text=text) == 0: absolute_x = self.libsbmlnetwork.getX(id=self.element_id, graphical_object_index=self.graphical_object_index) + relative_position[0] @@ -70,6 +93,151 @@ def get_labels_list(self): return labels + def get_labels(self): + return self.get_labels_list() + + @property + def labels(self): + return self.get_labels_list() + + def get_text(self): + texts = self.get_labels_list().get_texts() + if len(texts) == 1: + return texts[0] + + return texts + + def set_text(self, text: str): + return self.get_labels_list().set_texts(text) + + @property + def text(self): + return self.get_text() + + @text.setter + def text(self, text: str): + self.set_text(text) + + def get_font_color(self): + font_colors = self.get_labels_list().get_font_colors() + if len(font_colors) == 1: + return font_colors[0] + + return font_colors + + def set_font_color(self, font_color: str): + return self.get_labels_list().set_font_colors(font_color) + + @property + def font_color(self): + return self.get_font_color() + + @font_color.setter + def font_color(self, font_color: str): + self.set_font_color(font_color) + + def get_font(self): + fonts = self.get_labels_list().get_fonts() + if len(fonts) == 1: + return fonts[0] + + return fonts + + def set_font(self, font): + return self.get_labels_list().set_fonts(font) + + @property + def font(self): + return self.get_font() + + @font.setter + def font(self, font): + self.set_font(font) + + def get_font_size(self): + font_sizes = self.get_labels_list().get_font_sizes() + if len(font_sizes) == 1: + return font_sizes[0] + + return font_sizes + + def set_font_size(self, font_size: float): + return self.get_labels_list().set_font_sizes(font_size) + + @property + def font_size(self): + return self.get_font_size() + + @font_size.setter + def font_size(self, font_size: float): + self.set_font_size(font_size) + + def set_text_bold(self, bold: bool): + return self.get_labels_list().set_bold(bold) + + def is_text_bold(self): + is_bold = self.get_labels_list().are_bold() + if len(is_bold) == 1: + return is_bold[0] + + return is_bold + + @property + def text_bold(self): + return self.is_text_bold() + + @text_bold.setter + def text_bold(self, bold: bool): + self.set_text_bold(bold) + + def set_text_italic(self, italic: bool): + return self.get_labels_list().set_italic(italic) + + def is_text_italic(self): + is_italic = self.get_labels_list().are_italic() + if len(is_italic) == 1: + return is_italic[0] + + return is_italic + + @property + def text_italic(self): + return self.is_text_italic() + + @text_italic.setter + def text_italic(self, italic: bool): + self.set_text_italic(italic) + + def set_text_relative_position(self, relative_position: tuple[float, float]): + position = self.get_position() + return self.get_labels_list().set_positions((position[0] + relative_position[0], position[1] + relative_position[1])) + + def get_text_relative_position(self): + labels_positions = self.get_labels_list().get_positions() + position = self.get_position() + relative_positions = [] + for label_position in labels_positions: + relative_positions.append((label_position[0] - position[0], label_position[1] - position[1])) + + if len(relative_positions) == 1: + return relative_positions[0] + + return relative_positions + + @property + def text_relative_position(self): + return self.get_text_relative_position() + + @text_relative_position.setter + def text_relative_position(self, relative_position: tuple[float, float]): + self.set_text_relative_position(relative_position) + + def move_text_by(self, delta: tuple[float, float]): + return self.get_labels_list().move_by(delta) + + def move_text_to(self, position: tuple[float, float]): + return self.get_labels_list().move_to(position) + def add_shape(self, shape_type: str): valid_geometric_shapes = self.libsbmlnetwork.getListOfGeometricShapes() if shape_type not in valid_geometric_shapes: @@ -107,6 +275,14 @@ def set_shape(self, shape_type: str): return False + @property + def shape(self): + return self.get_shape() + + @shape.setter + def shape(self, shape_type: str): + self.set_shape(shape_type) + def get_shapes_list(self): shapes = ShapeList() for geometric_shape_index in range( @@ -117,9 +293,69 @@ def get_shapes_list(self): return shapes - def get_shapes_options(self): + def get_shapes(self): + return self.get_shapes_list() + + @property + def shapes(self): + return self.get_shapes_list() + + def get_shape_options(self): return self.libsbmlnetwork.getListOfGeometricShapes() + @property + def shape_options(self): + return self.get_shape_options() + + def get_shape_type(self): + return self.get_shapes_list().get_types() + + @property + def shape_type(self): + return self.get_shape_type() + + def get_border_color(self): + return self.get_shapes_list().get_border_colors() + + def set_border_color(self, border_color: str): + return self.get_shapes_list().set_border_colors(border_color) + + @property + def border_color(self): + return self.get_border_color() + + @border_color.setter + def border_color(self, border_color: str): + self.set_border_color(border_color) + + def get_border_thickness(self): + return self.get_shapes_list().get_border_thicknesses() + + def set_border_thickness(self, thickness: float): + return self.get_shapes_list().set_border_thicknesses(thickness) + + @property + def border_thickness(self): + return self.get_border_thickness() + + @border_thickness.setter + def border_thickness(self, thickness: float): + self.set_border_thickness(thickness) + + def get_fill_color(self): + return self.get_shapes_list().get_fill_colors() + + def set_fill_color(self, fill_color: str or tuple or list): + return self.get_shapes_list().set_fill_colors(fill_color) + + @property + def fill_color(self): + return self.get_fill_color() + + @fill_color.setter + def fill_color(self, fill_color: str or tuple or list): + self.set_fill_color(fill_color) + def hide(self, apply_to_connected_elements=True): if self.libsbmlnetwork.makeInvisible(id=self.element_id, graphical_object_index=self.graphical_object_index, apply_to_connected_elements=apply_to_connected_elements) == 0: return True @@ -138,23 +374,28 @@ def is_hidden(self): return False - def move(self, delta: tuple[float, float]): - current_position = self.get_position() - new_position = (current_position[0] + delta[0], current_position[1] + delta[1]) - return self.set_position(new_position) + def move_to(self, position: tuple[float, float]): + return self.move_by((position[0] - self.get_position()[0], position[1] - self.get_position()[1])) + + def move_by(self, delta: tuple[float, float]): + if self._can_move_by(delta): + current_position = self.get_position() + new_position = (current_position[0] + delta[0], current_position[1] + delta[1]) + return self.set_position(new_position) - def add_meta_data(self, key: str, value: str): - self.meta_data[key] = value + raise ValueError(f"The network element {self.get_id()} cannot be moved by {delta} because it would lead to negative coordinates.") - def remove_meta_data(self, key: str): - if key in self.meta_data: - del self.meta_data[key] + @staticmethod + def _can_move_to(position: tuple[float, float]): + if position[0] < 0 or position[1] < 0: + return False - def get_meta_data(self, key: str): - return self.meta_data.get(key, None) + return True - def get_meta_data_keys(self): - return self.meta_data.keys() + def _can_move_by(self, delta: tuple[float, float]): + current_position = self.get_position() + new_position = (current_position[0] + delta[0], current_position[1] + delta[1]) + if new_position[0] < 0 or new_position[1] < 0: + return False - def get_meta_data_values(self): - return self.meta_data.values() + return True diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/compartment_list.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/compartment_list.py index 8e934d3..db92e92 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/compartment_list.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/compartment_list.py @@ -5,25 +5,43 @@ class CompartmentList(NetworkElementBaseList): - def get_compartment_id(self): + def get_compartment_ids(self): compartment_ids = [] for compartment in self: compartment_ids.append(compartment.get_compartment_id()) return compartment_ids + @property + def compartment_ids(self): + return self.get_compartment_ids() + def get_species_list(self): species_list = SpeciesList(libsbmlnetwork=self.libsbmlnetwork) for compartment in self: species_list.extend(compartment.get_species_list()) return species_list + def get_species(self): + return self.get_species_list() + + @property + def species(self): + return self.get_species() + def get_reactions_list(self): reactions_list = ReactionList(libsbmlnetwork=self.libsbmlnetwork) for compartment in self: reactions_list.extend(compartment.get_reactions_list()) return reactions_list + def get_reactions(self): + return self.get_reactions_list() + + @property + def reactions(self): + return self.get_reactions() + def __str__(self): result = [] for compartment in self: diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/network_element_base_list.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/network_element_base_list.py index 6538494..c4b2995 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/network_element_base_list.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/network_element_base_list.py @@ -7,70 +7,89 @@ def __init__(self, network_element_base_list=None, libsbmlnetwork=None): super().__init__(network_element_base_list or []) self.libsbmlnetwork = libsbmlnetwork - def get_id(self): + def get_ids(self): ids = [] for element in self: ids.append(element.get_id()) return ids - def get_graphical_object_index(self): + @property + def ids(self): + return self.get_ids() + + def get_graphical_object_indices(self): graphical_object_indices = [] for element in self: graphical_object_indices.append(element.get_graphical_object_index()) return graphical_object_indices - def get_position(self): + def get_positions(self): positions = [] for element in self: positions.append(element.get_position()) return positions - def set_position(self, position: tuple[float, float]): + def set_positions(self, position: tuple[float, float]): results = [] for element in self: results.append(element.set_position(position)) return results - def get_size(self): + @property + def positions(self): + return self.get_positions() + + def get_sizes(self): sizes = [] for element in self: sizes.append(element.get_size()) return sizes - def set_size(self, size: tuple[float, float]): + def set_sizes(self, size: tuple[float, float]): results = [] for element in self: results.append(element.set_size(size)) return results - def move(self, delta: tuple[float, float]): + @property + def sizes(self): + return self.get_sizes() + + def move_to(self, position: tuple[float, float]): + results = [] + for element in self: + results.append(element.move_to(position)) + + return results + + def move_by(self, delta: tuple[float, float]): results = [] for element in self: - results.append(element.move(delta)) + results.append(element.move_by(delta)) return results - def add_label(self, text: str, relative_position: tuple[float, float] = (0.0, 0.0)): + def add_labels(self, text: str, relative_position: tuple[float, float] = (0.0, 0.0)): labels = LabelList() for element in self: labels.append(element.add_label(text, relative_position)) return labels - def remove_label(self, text: str): + def remove_labels(self, text: str): results = [] for element in self: results.append(element.remove_label(text)) return results - def get_label(self): + def get_labels(self): labels = LabelList() for element in self: labels.append(element.get_label()) @@ -84,7 +103,146 @@ def get_labels_list(self): return labels - def add_shape(self, shape_type: str): + def get_labels(self): + return self.get_labels_list() + + @property + def labels(self): + return self.get_labels() + + def get_texts(self): + texts = [] + for element in self: + texts.append(element.get_text()) + + return texts + + def set_texts(self, text: str): + results = [] + for element in self: + results.append(element.set_text(text)) + + return results + + @property + def texts(self): + return self.get_texts() + + @texts.setter + def texts(self, text: str): + self.set_texts(text) + + def get_font_colors(self): + font_colors = [] + for element in self: + font_colors.append(element.get_font_color()) + + return font_colors + + def set_font_colors(self, font_color: str): + results = [] + for element in self: + results.append(element.set_font_color(font_color)) + + return results + + @property + def font_colors(self): + return self.get_font_colors() + + @font_colors.setter + def font_colors(self, font_color: str): + self.set_font_colors(font_color) + + def get_fonts(self): + fonts = [] + for element in self: + fonts.append(element.get_font()) + + return fonts + + def set_fonts(self, font: str): + results = [] + for element in self: + results.append(element.set_font(font)) + + return results + + @property + def fonts(self): + return self.get_fonts() + + @fonts.setter + def fonts(self, font: str): + self.set_fonts(font) + + def get_font_sizes(self): + font_sizes = [] + for element in self: + font_sizes.append(element.get_font_size()) + + return font_sizes + + def set_font_sizes(self, font_size: float): + results = [] + for element in self: + results.append(element.set_font_size(font_size)) + + return results + + @property + def font_sizes(self): + return self.get_font_sizes() + + @font_sizes.setter + def font_sizes(self, font_size: float): + self.set_font_sizes(font_size) + + def set_texts_bold(self, bold: bool): + results = [] + for element in self: + results.append(element.set_text_bold(bold)) + + return results + + def are_texts_bold(self): + is_bold = [] + for element in self: + is_bold.append(element.is_text_bold()) + + return is_bold + + @property + def texts_bold(self): + return self.are_texts_bold() + + @texts_bold.setter + def texts_bold(self, bold: bool): + self.set_texts_bold(bold) + + def set_texts_italic(self, italic: bool): + results = [] + for element in self: + results.append(element.set_text_italic(italic)) + + return results + + def are_texts_italic(self): + is_italic = [] + for element in self: + is_italic.append(element.is_text_italic()) + + return is_italic + + @property + def texts_italic(self): + return self.are_texts_italic() + + @texts_italic.setter + def texts_italic(self, italic: bool): + self.set_texts_italic(italic) + + def add_shapes(self, shape_type: str): from ..visual_elements.visual_element_lists.shape_list import ShapeList shapes = ShapeList() @@ -93,14 +251,14 @@ def add_shape(self, shape_type: str): return shapes - def remove_shape(self, shape: ShapeBase): + def remove_shapes(self, shape: ShapeBase): results = [] for element in self: results.append(element.remove_shape(shape)) return results - def get_shape(self): + def get_shapes(self): from ..visual_elements.visual_element_lists.shape_list import ShapeList shapes = ShapeList() @@ -109,7 +267,7 @@ def get_shape(self): return shapes - def set_shape(self, shape_type: str): + def set_shapes(self, shape_type: str): results = [] for element in self: results.append(element.set_shape(shape_type)) @@ -125,44 +283,115 @@ def get_shapes_list(self): return shapes - def hide(self, apply_to_connected_elements=True): + def get_shapes(self): + return self.get_shapes_list() + + @property + def shapes(self): + return self.get_shapes() + + @shapes.setter + def shapes(self, shape_type: str): + self.set_shapes(shape_type) + + @property + def shapes(self): + return self.get_shapes() + + def get_shape_types(self): + shape_types = [] + for element in self: + shape_types.append(element.get_type()) + + return shape_types + + @property + def shape_types(self): + return self.get_shape_types() + + def set_border_colors(self, border_color: str): results = [] for element in self: - results.append(element.hide(apply_to_connected_elements)) + results.append(element.set_border_color(border_color)) return results - def show(self, apply_to_connected_elements=True): + def get_border_colors(self): + border_colors = [] + for element in self: + border_colors.append(element.get_border_color()) + + return border_colors + + @property + def border_colors(self): + return self.get_border_colors() + + @border_colors.setter + def border_colors(self, border_color: str): + self.set_border_colors(border_color) + + def set_border_thicknesses(self, thickness: float): results = [] for element in self: - results.append(element.show(apply_to_connected_elements)) + results.append(element.set_border_thickness(thickness)) return results - def is_hidden(self): - hidden_status = [] + def get_border_thicknesses(self): + thicknesses = [] for element in self: - hidden_status.append(element.is_hidden()) + thicknesses.append(element.get_border_thickness()) - return hidden_status + return thicknesses + + @property + def border_thicknesses(self): + return self.get_border_thicknesses() - def add_meta_data(self, key: str, value: str): + @border_thicknesses.setter + def border_thicknesses(self, thickness: float): + self.set_border_thicknesses(thickness) + + def set_fill_colors(self, fill_color: str or tuple or list): results = [] for element in self: - results.append(element.add_meta_data(key, value)) + results.append(element.set_fill_color(fill_color)) return results - def remove_meta_data(self, key: str): + def get_fill_colors(self): + fill_colors = [] + for element in self: + fill_colors.append(element.get_fill_color()) + + return fill_colors + + @property + def fill_colors(self): + return self.get_fill_colors() + + @fill_colors.setter + def fill_colors(self, fill_color: str or tuple or list): + self.set_fill_colors(fill_color) + + def hide(self, apply_to_connected_elements=True): results = [] for element in self: - results.append(element.remove_meta_data(key)) + results.append(element.hide(apply_to_connected_elements)) return results - def get_meta_data(self, key: str): - meta_data = [] + def show(self, apply_to_connected_elements=True): + results = [] for element in self: - meta_data.append(element.get_meta_data(key)) + results.append(element.show(apply_to_connected_elements)) - return meta_data + return results + + def are_hidden(self): + hidden_status = [] + for element in self: + hidden_status.append(element.is_hidden()) + + return hidden_status diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/reaction_list.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/reaction_list.py index 44a3601..07ee9d8 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/reaction_list.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/reaction_list.py @@ -3,20 +3,187 @@ class ReactionList(NetworkElementBaseList): - def get_reaction_id(self): + def get_reaction_ids(self): reaction_ids = [] for reaction in self: reaction_ids.append(reaction.get_reaction_id()) return reaction_ids - def get_compartment_id(self): + @property + def reaction_ids(self): + return self.get_reaction_ids() + + def get_compartment_ids(self): compartment_ids = [] for reaction in self: compartment_ids.append(reaction.get_compartment_id()) return compartment_ids + @property + def compartment_ids(self): + return self.get_compartment_ids() + + def get_centers(self): + from ..visual_elements.visual_element_lists.reaction_center_list import ReactionCenterList + + reaction_center_list = ReactionCenterList(libsbmlnetwork=self.libsbmlnetwork) + for reaction in self: + reaction_center_list.append(reaction.get_center()) + + return reaction_center_list + + @property + def centers(self): + return self.get_centers() + + def switch_to_curve(self): + results = [] + for reaction in self: + results.append(reaction.switch_to_curve()) + + return results + + def switch_centers_to_curve(self): + results = [] + for reaction in self: + results.append(reaction.switch_center_to_curve()) + + return results + + def switch_to_shapes(self): + results = [] + for reaction in self: + results.append(reaction.switch_to_shapes()) + + return results + + def switch_centers_to_shapes(self): + results = [] + for reaction in self: + results.append(reaction.switch_center_to_shapes()) + + return results + + def are_curves(self): + are_curves = [] + for reaction in self: + are_curves.append(reaction.is_curve()) + + return are_curves + + def are_centers_curve(self): + are_curves = [] + for reaction in self: + are_curves.append(reaction.is_center_curve()) + + return are_curves + + def are_shapes(self): + are_shapes = [] + for reaction in self: + are_shapes.append(reaction.is_shapes()) + + return are_shapes + + def are_centers_shapes(self): + are_shapes = [] + for reaction in self: + are_shapes.append(reaction.is_center_shapes()) + + return are_shapes + + def get_center_curves(self): + from ..visual_elements.visual_element_lists.curve_element_lists.curve_list import CurveList + + curves = CurveList() + for reaction in self: + curves.append(reaction.get_center_curve()) + return curves + + @property + def center_curves(self): + return self.get_center_curves() + + def get_shapes_list(self): + from ..visual_elements.visual_element_lists.shape_list import ShapeList + + shapes = ShapeList() + for reaction in self: + shapes.append(reaction.get_shapes()) + + return shapes + + def get_center_shapes_list(self): + from ..visual_elements.visual_element_lists.shape_list import ShapeList + + shapes = ShapeList() + for reaction in self: + shapes.append(reaction.get_center_shapes()) + + return shapes + + def get_center_shapes(self): + return self.get_center_shapes_list() + + @property + def center_shapes(self): + return self.get_center_shapes() + + def get_shape_types(self): + types = [] + for reaction in self: + types.append(reaction.get_shape_type()) + + return types + + @property + def shape_types(self): + return self.get_shape_types() + + def get_border_colors(self): + border_colors = [] + for reaction in self: + border_colors.append(reaction.get_border_color()) + + return border_colors + + def set_border_colors(self, border_color: str): + results = [] + for reaction in self: + results.append(reaction.set_border_color(border_color)) + + return results + + def get_border_thicknesses(self): + thicknesses = [] + for reaction in self: + thicknesses.append(reaction.get_border_thickness()) + + return thicknesses + + def set_border_thicknesses(self, thickness: float): + results = [] + for reaction in self: + results.append(reaction.set_border_thickness(thickness)) + + return results + + def get_fill_colors(self): + fill_colors = [] + for reaction in self: + fill_colors.append(reaction.get_fill_color()) + + return fill_colors + + def set_fill_colors(self, fill_color: str or tuple or list): + results = [] + for reaction in self: + results.append(reaction.set_fill_color(fill_color)) + + return results + def get_curves_list(self, species=None): from ..visual_elements.visual_element_lists.curve_element_lists.curve_list import CurveList @@ -25,6 +192,263 @@ def get_curves_list(self, species=None): curves.extend(reaction.get_curves_list(species)) return curves + def get_curves(self, species=None): + return self.get_curves_list(species) + + @property + def curves(self): + return self.get_curves() + + def get_center_curve_colors(self): + colors = [] + for reaction in self: + colors.append(reaction.get_center_curve_color()) + + return colors + + def set_center_curve_colors(self, color): + results = [] + for reaction in self: + results.append(reaction.set_center_curve_color(color)) + + return results + + @property + def center_curve_colors(self): + return self.get_center_curve_colors() + + @center_curve_colors.setter + def center_curve_colors(self, color): + self.set_center_curve_colors(color) + + def get_curve_colors(self): + curve_colors = [] + for reaction in self: + curve_colors.extend(reaction.get_curve_colors()) + + return curve_colors + + def set_curve_colors(self, color): + results = [] + for reaction in self: + results.append(reaction.set_curve_colors(color)) + + return results + + @property + def curve_colors(self): + return self.get_curve_colors() + + @curve_colors.setter + def curve_colors(self, color): + self.set_curve_colors(color) + + def set_colors(self, color): + results = [] + for reaction in self: + results.append(reaction.set_colors(color)) + + return results + + def get_center_curve_thicknesses(self): + thicknesses = [] + for reaction in self: + thicknesses.append(reaction.get_center_curve_thickness()) + + return thicknesses + + def set_center_curve_thicknesses(self, thickness): + results = [] + for reaction in self: + results.append(reaction.set_center_curve_thickness(thickness)) + + return results + + @property + def center_curve_thicknesses(self): + return self.get_center_curve_thicknesses() + + @center_curve_thicknesses.setter + def center_curve_thicknesses(self, thickness): + self.set_center_curve_thicknesses(thickness) + + def get_curve_thicknesses(self): + curve_thicknesses = [] + for reaction in self: + curve_thicknesses.extend(reaction.get_curve_thicknesses()) + + return curve_thicknesses + + def set_curve_thicknesses(self, thickness): + results = [] + for reaction in self: + results.append(reaction.set_curve_thicknesses(thickness)) + + return results + + @property + def curve_thicknesses(self): + return self.get_curve_thicknesses() + + @curve_thicknesses.setter + def curve_thicknesses(self, thickness): + self.set_curve_thicknesses(thickness) + + def set_thicknesses(self, thickness): + results = [] + for reaction in self: + results.append(reaction.set_thicknesses(thickness)) + + return results + + def get_arrow_heads(self): + from ..visual_elements.visual_element_lists.curve_element_lists.arrow_head_list import ArrowHeadList + + arrow_heads = ArrowHeadList() + for reaction in self: + arrow_heads.append(reaction.get_arrow_heads()) + return arrow_heads + + @property + def arrow_heads(self): + return self.get_arrow_heads() + + def get_arrow_head_relative_positions(self): + relative_positions = [] + for reaction in self: + relative_positions.append(reaction.get_arrow_head_relative_position()) + return relative_positions + + def set_arrow_head_relative_positions(self, relative_position: tuple[float, float]): + results = [] + for reaction in self: + results.append(reaction.set_arrow_head_relative_position(relative_position)) + return results + + @property + def arrow_head_relative_positions(self): + return self.get_arrow_head_relative_positions() + + @arrow_head_relative_positions.setter + def arrow_head_relative_positions(self, relative_position: tuple[float, float]): + self.set_arrow_head_relative_positions(relative_position) + + def get_arrow_head_sizes(self): + sizes = [] + for reaction in self: + sizes.append(reaction.get_arrow_head_size()) + return sizes + + def set_arrow_head_sizes(self, size: tuple[float, float]): + results = [] + for reaction in self: + results.append(reaction.set_arrow_head_size(size)) + return results + + @property + def arrow_head_sizes(self): + return self.get_arrow_head_sizes() + + @arrow_head_sizes.setter + def arrow_head_sizes(self, size: tuple[float, float]): + self.set_arrow_head_sizes(size) + + def get_arrow_head_shapes(self): + from ..visual_elements.visual_element_lists.shape_list import ShapeList + + arrow_head_shapes = ShapeList() + for reaction in self: + arrow_head_shapes.append(reaction.get_arrow_head_shapes()) + return arrow_head_shapes + + @property + def arrow_head_shapes(self): + return self.get_arrow_head_shapes() + + def get_arrow_head_shape_types(self): + shape_types = [] + for reaction in self: + shape_types.append(reaction.get_arrow_head_shape_type()) + return shape_types + + @property + def arrow_head_shape_types(self): + return self.get_arrow_head_shape_types() + + def get_arrow_head_border_color(self): + border_colors = [] + for reaction in self: + border_colors.append(reaction.get_arrow_head_border_color()) + return border_colors + + def set_arrow_head_border_colors(self, border_color: str): + results = [] + for reaction in self: + results.append(reaction.set_arrow_head_border_color(border_color)) + return results + + @property + def arrow_head_border_colors(self): + return self.get_arrow_head_border_color() + + @arrow_head_border_colors.setter + def arrow_head_border_colors(self, border_color: str): + self.set_arrow_head_border_colors(border_color) + + def get_arrow_head_border_thicknesses(self): + border_thicknesses = [] + for reaction in self: + border_thicknesses.append(reaction.get_arrow_head_border_thickness()) + return border_thicknesses + + def set_arrow_head_border_thicknesses(self, thickness: float): + results = [] + for reaction in self: + results.append(reaction.set_arrow_head_border_thickness(thickness)) + return results + + @property + def arrow_head_border_thicknesses(self): + return self.get_arrow_head_border_thicknesses() + + @arrow_head_border_thicknesses.setter + def arrow_head_border_thicknesses(self, thickness: float): + self.set_arrow_head_border_thicknesses(thickness) + + def get_arrow_head_fill_colors(self): + fill_colors = [] + for reaction in self: + fill_colors.append(reaction.get_arrow_head_fill_color()) + return fill_colors + + def set_arrow_head_fill_colors(self, fill_color: str or tuple or list): + results = [] + for reaction in self: + results.append(reaction.set_arrow_head_fill_color(fill_color)) + return results + + @property + def arrow_head_fill_colors(self): + return self.get_arrow_head_fill_colors() + + @arrow_head_fill_colors.setter + def arrow_head_fill_colors(self, fill_color: str or tuple or list): + self.set_arrow_head_fill_colors(fill_color) + + def move_arrow_head_relative_positions_to(self, relative_position: tuple[float, float]): + results = [] + for reaction in self: + results.append(reaction.move_arrow_heads_relative_positions_to(relative_position)) + + return results + + def move_arrow_head_relative_positions_by(self, delta: tuple[float, float]): + results = [] + for reaction in self: + results.append(reaction.move_arrow_heads_relative_positions_by(delta)) + + return results + def get_species_list(self): from .species_list import SpeciesList @@ -33,19 +457,37 @@ def get_species_list(self): species_list.extend(reaction.get_species_list()) return species_list + def get_species(self): + return self.get_species_list() + + @property + def species(self): + return self.get_species() + def get_species_ids(self): species_ids = [] for reaction in self: species_ids.extend(reaction.get_species_ids()) return species_ids - def get_empty_species(self): + @property + def species_ids(self): + return self.get_species_ids() + + def get_empty_species_list(self): from .species_list import SpeciesList - empty_species = SpeciesList(libsbmlnetwork=self.libsbmlnetwork) + empty_species_list = SpeciesList(libsbmlnetwork=self.libsbmlnetwork) for reaction in self: - empty_species.append(reaction.get_empty_species()) - return empty_species + empty_species_list.append(reaction.get_empty_species()) + return empty_species_list + + def get_empty_species(self): + return self.get_empty_species_list() + + @property + def empty_species(self): + return self.get_empty_species() def create_alias(self): aliases = ReactionList() @@ -64,19 +506,10 @@ def assign_species(self, species): successful_assignments.extend(reaction) return successful_assignments - def get_center(self): - from ..visual_elements.visual_element_lists.reaction_center_list import ReactionCenterList - - reaction_center_list = ReactionCenterList(libsbmlnetwork=self.libsbmlnetwork) - for reaction in self: - reaction_center_list.append(reaction.get_center()) - - return reaction_center_list - - def move(self, delta: tuple[float, float], move_connected_species: bool = False): + def move(self, delta: tuple[float, float], move_connected_species: bool = True): results = [] for reaction in self: - results.append(reaction.move(delta, move_connected_species=move_connected_species)) + results.append(reaction.move_by(delta, move_connected_species=move_connected_species)) return results diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/species_list.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/species_list.py index 52ebe5e..cdc3250 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/species_list.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/network_element_lists/species_list.py @@ -1,28 +1,44 @@ from .network_element_base_list import NetworkElementBaseList from .reaction_list import ReactionList + class SpeciesList(NetworkElementBaseList): - def get_species_id(self): + def get_species_ids(self): species_ids = [] for species in self: species_ids.append(species.get_species_id()) return species_ids - def get_compartment_id(self): + @property + def species_ids(self): + return self.get_species_ids() + + def get_compartment_ids(self): compartment_ids = [] for species in self: compartment_ids.append(species.get_compartment_id()) return compartment_ids + @property + def compartment_ids(self): + return self.get_compartment_ids() + def get_reactions_list(self): reactions = ReactionList(libsbmlnetwork=self.libsbmlnetwork) for species in self: reactions.extend(species.get_reactions_list()) return reactions + def get_reactions(self): + return self.get_reactions_list() + + @property + def reactions(self): + return self.get_reactions() + def get_reaction_ids(self): reaction_ids = [] for species in self: @@ -30,99 +46,222 @@ def get_reaction_ids(self): return reaction_ids - def get_connected_curves(self): + @property + def reaction_ids(self): + return self.get_reaction_ids() + + def get_curves(self, reaction=None): from ..visual_elements.visual_element_lists.curve_element_lists.curve_list import CurveList - connected_curves = CurveList() + curves = CurveList() for species in self: - connected_curves.extend(species.get_connected_curves()) + curves.extend(species.get_connected_curves(reaction)) - return connected_curves + return curves - def get_role(self, reaction): - roles = [] + def get_curves_list(self, reaction=None): + return self.get_curves(reaction) + + @property + def curves(self): + return self.get_curves() + + def get_connected_curves(self, reaction=None): + return self.get_curves(reaction) + + @property + def connected_curves(self): + return self.get_connected_curves() + + def get_curve_colors(self, reaction=None): + curve_colors = [] for species in self: - roles.append(species.get_role(reaction)) + curve_colors.append(species.get_curve_colors(reaction)) - return roles + return curve_colors - def create_alias(self, reaction): - aliases = SpeciesList(libsbmlnetwork=self.libsbmlnetwork) + def set_curve_colors(self, color, reaction=None): + results = [] for species in self: - aliases.append(species.create_alias(reaction)) + results.append(species.set_curve_colors(color, reaction)) - return aliases + return results + + @property + def curve_colors(self): + return self.get_curve_colors() + + @curve_colors.setter + def curve_colors(self, color): + self.set_curve_colors(color) - def align_to_top(self): - species_info = [] + def get_curve_thicknesses(self, reaction=None): + curve_thicknesses = [] for species in self: - species_info.append([species.get_species_id(), species.get_graphical_object_index()]) - if self.libsbmlnetwork.align(species_info, "top") == 0: - return True + curve_thicknesses.append(species.get_curve_thicknesses(reaction)) - return False + return curve_thicknesses - def align_to_bottom(self): - species_info = [] + def set_curve_thicknesses(self, thickness, reaction=None): + results = [] for species in self: - species_info.append([species.get_species_id(), species.get_graphical_object_index()]) - if self.libsbmlnetwork.align(species_info, "bottom") == 0: - return True + results.append(species.set_curve_thicknesses(thickness, reaction)) + + return results - return False + @property + def curve_thicknesses(self): + return self.get_curve_thicknesses() - def align_to_vertical_center(self): - species_info = [] + @curve_thicknesses.setter + def curve_thicknesses(self, thickness): + self.set_curve_thicknesses(thickness) + + def get_arrow_heads(self): + from ..visual_elements.visual_element_lists.curve_element_lists.arrow_head_list import ArrowHeadList + + arrow_heads = ArrowHeadList() for species in self: - species_info.append([species.get_species_id(), species.get_graphical_object_index()]) - if self.libsbmlnetwork.align(species_info, "vCenter") == 0: - return True + arrow_heads.append(species.get_arrow_heads()) + + return arrow_heads - return False + @property + def arrow_heads(self): + return self.get_arrow_heads() - def align_to_left(self): - species_info = [] + def get_arrow_head_shapes(self): + from ..visual_elements.visual_element_lists.shape_list import ShapeList + + shapes = ShapeList() for species in self: - species_info.append([species.get_species_id(), species.get_graphical_object_index()]) - if self.libsbmlnetwork.align(species_info, "left") == 0: - return True + shapes.append(species.get_arrow_head_shapes()) - return False + return shapes - def align_to_right(self): - species_info = [] + def get_arrow_head_shape_types(self): + shape_types = [] for species in self: - species_info.append([species.get_species_id(), species.get_graphical_object_index()]) - if self.libsbmlnetwork.align(species_info, "right") == 0: - return True + shape_types.append(species.get_arrow_head_shape_type()) + + return shape_types - return False + @property + def arrow_head_shape_types(self): + return self.arrow_head_shape_types() - def align_to_horizontal_center(self): - species_info = [] + def get_arrow_head_border_colors(self): + border_colors = [] for species in self: - species_info.append([species.get_species_id(), species.get_graphical_object_index()]) - if self.libsbmlnetwork.align(species_info, "hCenter") == 0: - return True + border_colors.append(species.get_arrow_head_border_color()) - return False + return border_colors - def align_to_circle(self): - species_info = [] + def set_arrow_head_border_colors(self, color): + results = [] for species in self: - species_info.append([species.get_species_id(), species.get_graphical_object_index()]) - if self.libsbmlnetwork.align(species_info, "circular") == 0: - return True + results.append(species.set_arrow_head_border_color(color)) + + return results + + @property + def arrow_head_border_colors(self): + return self.get_arrow_head_border_colors() + + @arrow_head_border_colors.setter + def arrow_head_border_colors(self, color): + self.set_arrow_head_border_colors(color) + + def get_arrow_head_border_thicknesses(self): + border_thicknesses = [] + for species in self: + border_thicknesses.append(species.get_arrow_head_border_thickness()) + + return border_thicknesses + + def set_arrow_head_border_thicknesses(self, thickness): + results = [] + for species in self: + results.append(species.set_arrow_head_border_thickness(thickness)) + + return results - return False + @property + def arrow_head_border_thicknesses(self): + return self.get_arrow_head_border_thicknesses() - # Todo: Implement distribute method - # ToDo: Implement get_distribute_options method + @arrow_head_border_thicknesses.setter + def arrow_head_border_thicknesses(self, thickness): + self.set_arrow_head_border_thicknesses(thickness) + + def get_arrow_head_fill_colors(self): + fill_colors = [] + for species in self: + fill_colors.append(species.get_arrow_head_fill_color()) + + return fill_colors + + def set_arrow_head_fill_colors(self, color): + results = [] + for species in self: + results.append(species.set_arrow_head_fill_color(color)) + + return results + + @property + def arrow_head_fill_colors(self): + return self.get_arrow_head_fill_colors() + + @arrow_head_fill_colors.setter + def arrow_head_fill_colors(self, color): + self.set_arrow_head_fill_colors(color) + + def move_arrow_head_relative_positions_to(self, relative_position): + results = [] + for species in self: + results.append(species.move_arrow_head_relative_positions_to(relative_position)) + + return results + + def move_arrow_head_relative_positions_by(self, relative_position): + results = [] + for species in self: + results.append(species.move_arrow_head_relative_positions_by(relative_position)) + + return results + + def get_roles(self, reaction): + roles = [] + for species in self: + roles.append(species.get_role(reaction)) + + return roles + + def are_empty_species(self): + are_empty_species = [] + for species in self: + are_empty_species.append(species.is_empty_species()) + + return are_empty_species + + def create_alias(self, reaction): + aliases = SpeciesList(libsbmlnetwork=self.libsbmlnetwork) + for species in self: + aliases.append(species.create_alias(reaction)) + + return aliases + + def move_to(self, position, move_connected_curves=True): + results = [] + for species in self: + results.append(species.move_to(position, move_connected_curves)) + + return results - def move(self, position: tuple[float, float], move_connected_curves: bool = False): + def move_by(self, position, move_connected_curves=True): results = [] for species in self: - results.append(species.move(position, move_connected_curves)) + results.append(species.move_by(position, move_connected_curves)) return results diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/reaction.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/reaction.py index 760c9d8..8ea7725 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/reaction.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/reaction.py @@ -1,6 +1,7 @@ from .network_element_base import NetworkElementBase from .visual_elements import * + class Reaction(NetworkElementBase): def __init__(self, libsbmlnetwork, element_id, graphical_object_index): @@ -9,10 +10,114 @@ def __init__(self, libsbmlnetwork, element_id, graphical_object_index): def get_reaction_id(self): return self.element_id + @property + def reaction_id(self): + return self.get_reaction_id() + def get_center(self): return ReactionCenter(self.libsbmlnetwork, self.element_id, self.graphical_object_index) - def get_curves_list(self, species = None): + @property + def center(self): + return self.get_center() + + def switch_to_curve(self): + return self.get_center().switch_to_curve() + + def switch_center_to_curve(self): + return self.get_center().switch_to_curve() + + def switch_to_shapes(self): + return self.get_center().switch_to_shapes() + + def switch_center_to_shapes(self): + return self.get_center().switch_to_shapes() + + def is_curve(self): + return self.get_center().is_curve() + + def is_center_curve(self): + return self.get_center().is_curve() + + def is_shapes(self): + return self.get_center().is_shapes() + + def is_center_shapes(self): + return self.get_center().is_shapes() + + def get_center_curve(self): + return self.get_center().get_curve() + + @property + def center_curve(self): + return self.get_center_curve() + + def get_center_shapes_list(self): + return self.get_center().get_shapes_list() + + def get_center_shapes(self): + return self.get_center_shapes_list() + + @property + def center_shapes(self): + return self.get_center_shapes() + + def get_shape_type(self): + if self.is_center_shapes(): + return self.get_center_shapes_list().get_type() + + return None + + @property + def shape_type(self): + return self.get_shape_type() + + def get_border_color(self): + if self.is_center_shapes(): + return self.get_center_shapes_list().get_border_color() + + return None + + def set_border_color(self, border_color: str): + if self.is_center_shapes(): + return self.get_center_shapes_list().set_border_color(border_color) + + return False + + def set_border_colors(self, border_color: str): + return self.set_border_color(border_color) and self.set_arrow_head_border_colors(border_color) + + def get_border_thicknesses(self): + if self.is_center_shapes(): + return self.get_center_shapes_list().get_border_thickness() + + return None + + def set_border_thickness(self, thickness: float): + if self.is_center_shapes(): + return self.get_center_shapes_list().set_border_thickness(thickness) + + return False + + def set_border_thicknesses(self, thickness: float): + return self.set_border_thickness(thickness) and self.set_arrow_head_border_thicknesses() + + def get_fill_color(self): + if self.is_center_shapes(): + return self.get_center_shapes_list().get_fill_color() + + return None + + def set_fill_color(self, fill_color: str or tuple or list): + if self.is_center_shapes(): + return self.get_center_shapes_list().set_fill_color(fill_color) + + return False + + def set_fill_colors(self, fill_color: str or tuple or list): + return self.set_fill_color(fill_color) and self.set_arrow_head_fill_colors(fill_color) + + def get_curves_list(self, species=None): from .species import Species curves = CurveList(libsbmlnetwork=self.libsbmlnetwork) @@ -25,7 +130,7 @@ def get_curves_list(self, species = None): species_id = species.get_species_id() species_glyph_index = species.get_graphical_object_index() for species_reference_index in range(self.libsbmlnetwork.getNumSpeciesReferences(reaction_id=self.element_id, - reaction_glyph_index=self.graphical_object_index)): + reaction_glyph_index=self.graphical_object_index)): species_reference_species_id = self.libsbmlnetwork.getSpeciesReferenceSpeciesId( reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index, @@ -36,17 +141,234 @@ def get_curves_list(self, species = None): reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index ) + if self.libsbmlnetwork.isSetSpeciesReferenceEmptySpeciesGlyph(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index, + species_reference_index=species_reference_index): + species_reference_species_id = self.libsbmlnetwork.getSpeciesReferenceEmptySpeciesGlyphId(reaction_id=self.element_id, + reaction_glyph_index=self.graphical_object_index, + species_reference_index=species_reference_index) + species_glyph_index_in_reaction = 0 if species_id is not None and (species_id != species_reference_species_id or species_glyph_index != species_glyph_index_in_reaction): continue curves.append(Curve(self.libsbmlnetwork, self.element_id, self.graphical_object_index, species_reference_index)) return curves - def get_species_list(self, species_ids = None): + def get_curves(self, species=None): + return self.get_curves_list(species) + + @property + def curves(self): + return self.get_curves() + + def get_center_curve_color(self): + if self.is_center_curve(): + return self.get_center_curve().get_color() + + return None + + def set_center_curve_color(self, color): + if self.is_center_curve(): + return self.get_center_curve().set_color(color) + + return False + + @property + def center_curve_color(self): + return self.get_center_curve_color() + + @center_curve_color.setter + def center_curve_color(self, color): + self.set_center_curve_color(color) + + def get_curve_colors(self): + return self.get_curves_list().get_colors() + + def set_curve_colors(self, color): + return self.get_curves_list().set_colors(color) + + @property + def curves_colors(self): + return self.get_curve_colors() + + @curves_colors.setter + def curves_colors(self, color): + self.set_curve_colors(color) + + def set_colors(self, color): + if self.is_center_curve(): + if not self.get_center_curve().set_color(color): + return False + else: + if not self.set_border_color(color): + return False + if not self.set_fill_color(color): + return False + + if not self.get_curves().set_colors(color): + return False + + if len(self.get_arrow_heads()): + if not self.set_arrow_head_fill_colors(color): + return False + if not self.set_arrow_head_border_colors(color): + return False + + return False + + def get_center_curve_thickness(self): + if self.is_center_curve(): + return self.get_center_curve().get_thickness() + + return None + + def set_center_curve_thickness(self, thickness): + if self.is_center_curve(): + return self.get_center_curve().set_thickness(thickness) + + return False + + @property + def center_curve_thickness(self): + return self.get_center_curve_thickness() + + @center_curve_thickness.setter + def center_curve_thickness(self, thickness): + self.set_center_curve_thickness(thickness) + + def get_curve_thicknesses(self): + return self.get_curves_list().get_thicknesses() + + def set_curve_thicknesses(self, thickness): + return self.get_curves_list().set_thicknesses(thickness) + + @property + def curve_thicknesses(self): + return self.get_curve_thicknesses() + + @curve_thicknesses.setter + def curve_thicknesses(self, thickness): + self.set_curve_thicknesses(thickness) + + def set_thicknesses(self, thickness): + if self.is_center_curve(): + if not self.get_center_curve().set_thickness(thickness): + return False + else: + if not self.set_border_thickness(thickness): + return False + + if not self.get_curves().set_thicknesses(thickness): + return False + + if len(self.get_arrow_heads()): + if not self.set_arrow_head_border_thicknesses(thickness): + return False + + return True + + def get_arrow_heads(self): + return self.get_curves_list().get_arrow_heads() + + @property + def arrow_heads(self): + return self.get_arrow_heads() + + def get_arrow_head_relative_positions(self): + return self.get_arrow_heads().get_relative_positions() + + def set_arrow_head_relative_positions(self, relative_position: float): + return self.get_arrow_heads().set_relative_positions(relative_position) + + @property + def arrow_head_relative_positions(self): + return self.get_arrow_head_relative_positions() + + @arrow_head_relative_positions.setter + def arrow_head_relative_positions(self, relative_position: float): + self.set_arrow_head_relative_positions(relative_position) + + def get_arrow_head_sizes(self): + return self.get_arrow_heads().get_sizes() + + def set_arrow_head_sizes(self, size: tuple[float, float]): + return self.get_arrow_heads().set_sizes(size) + + @property + def arrow_head_sizes(self): + return self.get_arrow_head_sizes() + + @arrow_head_sizes.setter + def arrow_head_sizes(self, size: tuple[float, float]): + self.set_arrow_head_sizes(size) + + def get_arrow_head_shapes(self): + return self.get_arrow_heads().get_shape_type() + + @property + def arrow_head_shapes(self): + return self.get_arrow_head_shapes() + + def set_arrow_head_shapes(self, shape: str): + return self.get_arrow_heads().set_shape(shape) + + @arrow_head_shapes.setter + def arrow_head_shapes(self, shape: str): + self.set_arrow_head_shapes(shape) + + def get_arrow_head_border_colors(self): + return self.get_arrow_heads().get_border_colors() + + def set_arrow_head_border_colors(self, border_color: str): + return self.get_arrow_heads().set_border_colors(border_color) + + @property + def arrow_head_border_colors(self): + return self.get_arrow_head_border_colors() + + @arrow_head_border_colors.setter + def arrow_head_border_colors(self, border_color: str): + self.set_arrow_head_border_colors(border_color) + + def get_arrow_head_border_thicknesses(self): + return self.get_arrow_heads().get_border_thickneses() + + def set_arrow_head_border_thicknesses(self, thickness: float): + return self.get_arrow_heads().set_border_thicknesses(thickness) + + @property + def arrow_head_border_thicknesses(self): + return self.get_arrow_head_border_thicknesses() + + @arrow_head_border_thicknesses.setter + def arrow_head_border_thicknesses(self, thickness: float): + self.set_arrow_head_border_thicknesses(thickness) + + def get_arrow_head_fill_colors(self): + return self.get_arrow_heads().get_fill_colors() + + def set_arrow_head_fill_colors(self, fill_color: str or tuple or list): + return self.get_arrow_heads().set_fill_colors(fill_color) + + @property + def arrow_head_fill_colors(self): + return self.get_arrow_head_fill_colors() + + @arrow_head_fill_colors.setter + def arrow_head_fill_colors(self, fill_color: str or tuple or list): + self.set_arrow_head_fill_colors(fill_color) + + def move_arrow_head_relative_positions_to(self, relative_position: float): + return self.get_arrow_heads().move_relative_positions_to(relative_position) + + def move_arrow_head_relative_positions_by(self, delta: float): + return self.get_arrow_heads().move_relative_positions_by(delta) + + def get_species_list(self, species_ids=None): from .species import Species from .network_element_lists import SpeciesList species = SpeciesList(libsbmlnetwork=self.libsbmlnetwork) + species_set = {} for species_reference_index in range(self.libsbmlnetwork.getNumSpeciesReferences(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index)): species_id = self.libsbmlnetwork.getSpeciesReferenceSpeciesId(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index, species_reference_index=species_reference_index) species_index = self.libsbmlnetwork.getSpeciesGlyphIndex(species_id=species_id, reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index) @@ -56,13 +378,24 @@ def get_species_list(self, species_ids = None): species_index = 0 else: continue - if (species_ids is None or - (isinstance(species_ids, list) and species_id in species_ids) or - (isinstance(species_ids, str) and species_id == species_ids)): + species_set_for_species_id = species_set.get(species_id, set()) + species_ids_is_valid = (species_ids is None or + (isinstance(species_ids, list) and species_id in species_ids) or + (isinstance(species_ids, str) and species_id == species_ids)) + + if species_ids_is_valid and species_index not in species_set_for_species_id: + species_set.setdefault(species_id, set()).add(species_index) species.append(Species(self.libsbmlnetwork, species_id, species_index)) return species + def get_species(self, species_ids=None): + return self.get_species_list(species_ids) + + @property + def species(self): + return self.get_species() + def get_species_ids(self): species_ids = [] for species_reference_index in range(self.libsbmlnetwork.getNumSpeciesReferences(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index)): @@ -71,6 +404,10 @@ def get_species_ids(self): return species_ids + @property + def species_ids(self): + return self.get_species_ids() + def get_empty_species(self): from .species import Species @@ -82,6 +419,10 @@ def get_empty_species(self): return None + @property + def empty_species(self): + return self.get_empty_species() + def get_compartment_id(self): return self.libsbmlnetwork.getGraphicalObjectCompartmentId(entity_id=self.element_id, graphical_object_index=self.graphical_object_index) @@ -93,32 +434,76 @@ def assign_species(self, species): species_ids = self.get_species_ids() if species.get_species_id() in species_ids: if self.libsbmlnetwork.setSpeciesGlyphIndexInReactionGlyph(species_id=species.get_species_id(), - reaction_id=self.element_id, - reaction_glyph_index=self.graphical_object_index, - index=species.get_graphical_object_index()) == 0: + reaction_id=self.element_id, + reaction_glyph_index=self.graphical_object_index, + index=species.get_graphical_object_index()) == 0: return True return False - def move(self, delta: tuple[float, float], move_connected_species: bool = False): - is_moved = super().move(delta) - if not is_moved: - return False + def move_to(self, position: tuple[float, float], move_connected_species: bool = True): + return self.move_by((position[0] - self.get_position()[0], position[1] - self.get_position()[1]), + move_connected_species) - is_moved = self.get_curves_list().move(delta) - if not all(is_moved): - return False + def move_by(self, delta: tuple[float, float], move_connected_species: bool = True): + if self._can_move_by(delta): + is_moved = super().move_by(delta) + if not is_moved: + return False - if move_connected_species: - is_moved = self.get_species_list().move(delta, move_connected_curves=False) + is_moved = self.get_curves_list().move_by(delta) if not all(is_moved): return False - #ToDo: add the option to move the species connected curves that are not connected to this reaction + if move_connected_species: + is_moved = self.get_species_list().move_by(delta, move_connected_curves=False) + if not all(is_moved): + return False - return True + other_curves = self.get_species_list().get_connected_curves() + for curve in other_curves: + if curve.get_reaction().get_id() != self.get_id(): + if curve.get_role() in curve.get_modifier_role_options(): + curve.move_start_by(delta) + else: + curve.move_end_by(delta, adjust_end_point_for_uni_uni_reaction=True) + + return True - def __str__(self): + raise ValueError(f"Reaction {self.get_reaction_id()} cannot be moved by {delta} because it would lead to negative coordinates.") + + def align_horizontal(self, center_at: tuple[float, float] = None, spread_width: float = None, + reactants_order: list = None, products_order: list = None, modifiers_order: list = None, + reactants_placement: str = "both", products_placement: str = "both", modifiers_placement: str = "both"): + from ..features.align import HorizontalAlign + + horizontal_align = HorizontalAlign(self.libsbmlnetwork) + return horizontal_align.align(self, center_at, spread_width, + reactants_order, products_order, modifiers_order, + reactants_placement, products_placement, modifiers_placement) + + def align_vertical(self, center_at: tuple[float, float] = None, spread_height: float = None, + reactants_order: list = None, products_order: list = None, modifiers_order: list = None, + reactants_placement: str = "both", products_placement: str = "both", modifiers_placement: str = "both"): + from ..features.align import VerticalAlign + + vertical_align = VerticalAlign(self.libsbmlnetwork) + return vertical_align.align(self, center_at, spread_height, + reactants_order, products_order, modifiers_order, + reactants_placement, products_placement, modifiers_placement) + + def align_circle(self, center_at: tuple[float, float] = None, radius: float = None, arc_start: float = -180, + arc_end: float = -90, + reactants_order: list = None, products_order: list = None, modifiers_order: list = None, + reactants_placement: str = "both", products_placement: str = "both", modifiers_placement: str = "both"): + from ..features.align import CircleAlign + + circle_align = CircleAlign(self.libsbmlnetwork) + return circle_align.align(self, center_at, radius, arc_start, arc_end, + reactants_order, products_order, modifiers_order, + reactants_placement, products_placement, modifiers_placement) + + def get_info(self): result = [] result.append(f"reaction id: {self.get_reaction_id()}") result.append(f"id: {self.get_id()}") @@ -152,5 +537,9 @@ def __str__(self): return "\n".join(result) + @property + def info(self): + return self.get_info() + def __repr__(self): return f"Reaction(id={self.element_id}, index={self.graphical_object_index})" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/species.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/species.py index 90e5d12..650bb6b 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/species.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/species.py @@ -2,6 +2,7 @@ from .reaction import Reaction from .network_element_lists import ReactionList from .visual_elements.visual_element_lists .curve_element_lists import CurveList +from typing import Union class Species(NetworkElementBase): @@ -12,9 +13,17 @@ def __init__(self, libsbmlnetwork, element_id, graphical_object_index): def get_species_id(self): return self.element_id + @property + def species_id(self): + return self.get_species_id() + def get_compartment_id(self): return self.libsbmlnetwork.getGraphicalObjectCompartmentId(entity_id=self.element_id, graphical_object_index=self.graphical_object_index) + @property + def compartment_id(self): + return self.get_compartment_id() + def get_reactions_list(self): reactions_list = ReactionList() reaction_ids = self.libsbmlnetwork.getListOfReactionIds() @@ -26,6 +35,13 @@ def get_reactions_list(self): return reactions_list + def get_reactions(self): + return self.get_reactions_list() + + @property + def reactions(self): + return self.get_reactions() + def get_reaction_ids(self): reaction_ids = set() reactions = self.get_reactions_list() @@ -34,54 +50,301 @@ def get_reaction_ids(self): return reaction_ids - def get_connected_curves(self): - connected_curves = CurveList() + @property + def reaction_ids(self): + return self.get_reaction_ids() + + def get_curves_list(self, reaction: Union[Reaction, str] = None): + curves = CurveList() reactions = self.get_reactions_list() - for reaction in reactions: - connected_curves.extend(reaction.get_curves_list(self)) + for reaction_item in reactions: + if reaction is None or isinstance(reaction, str) and reaction == reaction_item.get_reaction_id() or isinstance( + reaction, Reaction) and reaction == reaction_item: + curves.extend(reaction_item.get_curves_list(self)) + + return curves + + def get_curves(self, reaction: Union[Reaction, str] = None): + return self.get_curves_list(reaction) + + @property + def curves(self): + return self.get_curves() + + def get_connected_curves_list(self, reaction: Union[Reaction, str] = None): + return self.get_curves_list(reaction) + + def get_connected_curves(self, reaction: Union[Reaction, str] = None): + return self.get_connected_curves_list(reaction) + + @property + def connected_curves(self): + return self.get_connected_curves() + + def get_curve_colors(self, reaction: Union[Reaction, str] = None): + return self.get_curves_list(reaction).get_colors() + + def set_curve_colors(self, color, reaction: Union[Reaction, str] = None): + return self.get_curves_list(reaction).set_colors(color) + + @property + def curve_colors(self): + return self.get_curve_colors() + + @curve_colors.setter + def curve_colors(self, color): + self.set_curve_colors(color) + + def get_curve_thicknesses(self, reaction: Union[Reaction, str] = None): + return self.get_curves_list(reaction).get_thicknesses() + + def set_curve_thicknesses(self, thickness, reaction: Union[Reaction, str] = None): + return self.get_curves_list(reaction).set_thicknesses(thickness) + + @property + def curve_thicknesses(self): + return self.get_curve_thicknesses() + + @curve_thicknesses.setter + def curve_thicknesses(self, thickness): + self.set_curve_thicknesses(thickness) + + def get_arrow_heads(self): + return self.get_curves_list().get_arrow_heads() + + @property + def arrow_heads(self): + return self.get_arrow_heads() + + def get_arrow_head_shapes(self): + return self.get_arrow_heads().get_shape_type() + + @property + def arrow_head_shapes(self): + return self.get_arrow_head_shapes() + + def set_arrow_head_shapes(self, shape: str): + return self.get_arrow_heads().set_shapes(shape) + + @arrow_head_shapes.setter + def arrow_head_shapes(self, shape: str): + self.set_arrow_head_shapes(shape) + + def get_arrow_head_border_colors(self): + return self.get_arrow_heads().get_border_color() + + def set_arrow_head_border_colors(self, border_color: str): + return self.get_arrow_heads().set_border_color(border_color) + + @property + def arrow_head_border_colors(self): + return self.get_arrow_head_border_colors() + + @arrow_head_border_colors.setter + def arrow_head_border_colors(self, border_color: str): + self.set_arrow_head_border_colors(border_color) + + def get_arrow_head_border_thicknesses(self): + return self.get_arrow_heads().get_border_thicknesses() - return connected_curves + def set_arrow_head_border_thicknesses(self, thickness: float): + return self.get_arrow_heads().set_border_thicknesses(thickness) - def get_role(self, reaction: Reaction): - if reaction is not None: - for species_reference_index in range(self.libsbmlnetwork.getNumSpeciesReferences(reaction_id=reaction.get_reaction_id(), reaction_glyph_index=reaction.graphical_object_index)): - if self.libsbmlnetwork.getSpeciesReferenceSpeciesId(reaction_id=reaction.get_reaction_id(), reaction_glyph_index=reaction.graphical_object_index, species_reference_index=species_reference_index) == self.element_id: - return self.libsbmlnetwork.getSpeciesReferenceRole(reaction_id=reaction.get_reaction_id(), reaction_glyph_index=reaction.graphical_object_index, species_reference_index=species_reference_index) + @property + def arrow_head_border_thicknesses(self): + return self.get_arrow_head_border_thicknesses() + + @arrow_head_border_thicknesses.setter + def arrow_head_border_thicknesses(self, thickness: float): + self.set_arrow_head_border_thicknesses(thickness) + + def get_arrow_head_fill_colors(self): + return self.get_arrow_heads().get_fill_color() + + def set_arrow_head_fill_colors(self, fill_color: str or tuple or list): + return self.get_arrow_heads().set_fill_color(fill_color) + + @property + def arrow_head_fill_colors(self): + return self.get_arrow_head_fill_colors() + + @arrow_head_fill_colors.setter + def arrow_head_fill_colors(self, fill_color: str or tuple or list): + self.set_arrow_head_fill_colors(fill_color) + + def move_arrow_head_relative_positions_to(self, relative_position: tuple[float, float]): + return self.get_arrow_heads().move_relative_positions_to(relative_position) + + def move_arrow_head_relative_positions_by(self, relative_position: tuple[float, float]): + return self.get_arrow_heads().move_relative_positions_by(relative_position) + + def get_role(self, reaction: Union[Reaction, str]): + reaction_id = None + reaction_glyph_index = None + if isinstance(reaction, Reaction): + reaction_id = reaction.get_reaction_id() + reaction_glyph_index = reaction.graphical_object_index + elif isinstance(reaction, str): + reaction_id = reaction + reaction_glyph_index = 0 + if reaction_id is not None and reaction_glyph_index is not None: + for species_reference_index in range( + self.libsbmlnetwork.getNumSpeciesReferences(reaction_id=reaction_id, + reaction_glyph_index=reaction_glyph_index)): + if self.libsbmlnetwork.getSpeciesReferenceSpeciesId(reaction_id=reaction_id, + reaction_glyph_index=reaction_glyph_index, + species_reference_index=species_reference_index) == self.element_id \ + or self.libsbmlnetwork.getSpeciesReferenceEmptySpeciesGlyphId(reaction_id=reaction_id, + reaction_glyph_index=reaction_glyph_index, + species_reference_index=species_reference_index) == self.id: + return self.libsbmlnetwork.getSpeciesReferenceRole(reaction_id=reaction_id, + reaction_glyph_index=reaction_glyph_index, + species_reference_index=species_reference_index) return None - def get_roles_options(self): + def get_role_options(self): return self.libsbmlnetwork.getListOfRoles() - def get_modifier_roles_options(self): - roles = self.get_roles_options() + @property + def role_options(self): + return self.get_role_options() + + def get_modifier_role_options(self): + roles = self.get_role_options() return [role for role in roles if role not in ["substrate", "sidesubstrate", "product", "sideproduct"]] + @property + def modifier_role_options(self): + return self.get_modifier_role_options() + + def is_empty_species(self): + reactions = self.get_reactions_list() + if len(reactions) == 1: + reaction = reactions[0] + for species_reference_index in range( + self.libsbmlnetwork.getNumSpeciesReferences(reaction_id=reaction.get_reaction_id(), + reaction_glyph_index=reaction.graphical_object_index)): + if self.libsbmlnetwork.isSetSpeciesReferenceEmptySpeciesGlyph(reaction_id=reaction.get_reaction_id(), + reaction_glyph_index=reaction.graphical_object_index, + species_reference_index=species_reference_index): + if self.libsbmlnetwork.getSpeciesReferenceEmptySpeciesGlyphId(reaction_id=reaction.get_reaction_id(), + reaction_glyph_index=reaction.graphical_object_index, + species_reference_index=species_reference_index) == self.element_id: + return True + + return False + def create_alias(self, reaction: Reaction): if self.libsbmlnetwork.createAliasSpeciesGlyph(species_id=self.element_id, reaction_id=reaction.get_reaction_id(), reaction_glyph_index=reaction.graphical_object_index) == 0: species_glyph_index = self.libsbmlnetwork.getSpeciesGlyphIndex(species_id=self.element_id, reaction_id=reaction.get_reaction_id(), reaction_glyph_index=reaction.graphical_object_index) return Species(self.libsbmlnetwork, self.element_id, species_glyph_index) + return None + + def move_to(self, position: tuple[float, float], move_connected_curves: bool = True): + return self.move_by((position[0] - self.get_position()[0], position[1] - self.get_position()[1]), + move_connected_curves) + + def move_by(self, position: tuple[float, float], move_connected_curves: bool = True): + if self._can_move_by(position): + is_moved = super().move_by(position) + if not is_moved: + return False - def move(self, position: tuple[float, float], move_connected_curves: bool = False): - is_moved = super().move(position) - if not is_moved: - return False + if move_connected_curves: + curves = self.get_connected_curves() + for curve in curves: + role = self.get_role(curve.get_reaction()) + if role in self.get_modifier_role_options(): + if not curve.move_start_by(position): + return False + else: + if not curve.move_end_by(position, adjust_end_point_for_uni_uni_reaction=True): + return False - if move_connected_curves: - curves = self.get_connected_curves() - for curve in curves: - role = self.get_role(curve.get_reaction()) - if role in self.get_modifier_roles_options(): - if not curve.move_start(position): - return False - else: - if not curve.move_end(position): - return False + return True + + raise ValueError(f"Species {self.get_species_id()} cannot be moved by {position} because it would lead to negative coordinates.") + + def set_size(self, size: tuple[float, float], adjust_font_size: bool = True): + current_size = self.get_size() + current_position = self.get_position() + delta_width = size[0] - current_size[0] + delta_height = size[1] - current_size[1] + new_position = ( + current_position[0] - 0.5 * delta_width, + current_position[1] - 0.5 * delta_height, + ) + super().set_size(size) + self.set_position(new_position) + labels = self.get_labels_list() + for label in labels: + label.set_font_size(label.get_font_size() * size[0] / current_size[0]) + center = (new_position[0] + size[0] / 2, new_position[1] + size[1] / 2) + half_width, half_height = size[0] / 2, size[1] / 2 + for curve in self.get_connected_curves(): + if curve.get_role() in self.get_modifier_role_options(): + cp = curve.get_start() + else: + cp = curve.get_end() + dx = cp[0] - center[0] + dy = cp[1] - center[1] + if dx == 0 and dy == 0: + continue + scale_x = half_width / abs(dx) if dx != 0 else float('inf') + scale_y = half_height / abs(dy) if dy != 0 else float('inf') + scale = min(scale_x, scale_y) + new_cp = (center[0] + dx * scale, center[1] + dy * scale) + import math + length = math.hypot(dx, dy) + if length != 0: + pad_dx = (dx / length) * 10 + pad_dy = (dy / length) * 10 + new_cp = (new_cp[0] + pad_dx, new_cp[1] + pad_dy) + move_vector = (new_cp[0] - cp[0], new_cp[1] - cp[1]) + if curve.get_role() in self.get_modifier_role_options(): + curve.move_start_by(move_vector) + else: + curve.move_end_by(move_vector, adjust_end_point_for_uni_uni_reaction=True) return True - def __str__(self): + def set_font_size(self, font_size: float, adjust_size: bool = True): + labels = self.get_labels_list() + for label in labels: + label.set_font_size(font_size) + + if adjust_size: + width_factor = 0.6 + height_factor = 1.2 + max_text_width = 0 + max_text_height = 0 + + for label in labels: + text = label.get_text() + text_width = len(text) * font_size * width_factor + text_height = font_size * height_factor + + max_text_width = max(max_text_width, text_width) + max_text_height = max(max_text_height, text_height) + + padding = 10 + required_width = max_text_width + padding + required_height = max_text_height + padding + current_width, current_height = self.get_size() + scale_w = required_width / current_width + scale_h = required_height / current_height + scale = max(scale_w, scale_h) + if scale > 1: + new_width = current_width * scale + new_height = current_height * scale + self.set_size((new_width, new_height), adjust_font_size=False) + + return True + + + def get_info(self): result = [] result.append(f"species id: {self.get_species_id()}") result.append(f"id: {self.get_id()}") @@ -105,5 +368,9 @@ def __str__(self): return "\n".join(result) + @property + def info(self): + return self.get_info() + def __repr__(self): return f"Species(id={self.element_id}, index={self.graphical_object_index})" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/curve/arrow_head.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/curve/arrow_head.py index 59afe69..3296d20 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/curve/arrow_head.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/curve/arrow_head.py @@ -20,6 +20,14 @@ def set_relative_position(self, relative_position: tuple[float, float]): return False + @property + def relative_position(self): + return self.get_relative_position() + + @relative_position.setter + def relative_position(self, relative_position: tuple[float, float]): + self.set_relative_position(relative_position) + def get_size(self): return self.libsbmlnetwork.getSpeciesReferenceLineEndingBoundingBoxWidth(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index), \ self.libsbmlnetwork.getSpeciesReferenceLineEndingBoundingBoxHeight(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index) @@ -31,6 +39,14 @@ def set_size(self, size: tuple[float, float]): return False + @property + def size(self): + return self.get_size() + + @size.setter + def size(self, size: tuple[float, float]): + self.set_size(size) + def add_shape(self, shape_type: str): valid_geometric_shapes = self.libsbmlnetwork.getListOfGeometricShapes() if shape_type not in valid_geometric_shapes: @@ -64,6 +80,14 @@ def set_shape(self, shape_type: str): return False + @property + def shape(self): + return self.get_shape() + + @shape.setter + def shape(self, shape_type: str): + self.set_shape(shape_type) + def get_shapes_list(self): shapes = ShapeList() for geometric_shape_index in range(self.libsbmlnetwork.getNumSpeciesReferenceLineEndingGeometricShapes(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index)): @@ -72,7 +96,71 @@ def get_shapes_list(self): return shapes - def __str__(self): + def get_shapes(self): + return self.get_shapes_list() + + @property + def shapes(self): + return self.get_shapes_list() + + def get_shape_type(self): + return self.get_shapes_list().get_types() + + @property + def shape_type(self): + return self.get_shape_type() + + def get_border_color(self): + return self.get_shapes_list().get_border_colors() + + def set_border_color(self, border_color: str): + return self.get_shapes_list().set_border_colors(border_color) + + @property + def border_color(self): + return self.get_border_color() + + @border_color.setter + def border_color(self, border_color: str): + self.set_border_color(border_color) + + def get_border_thickness(self): + return self.get_shapes_list().get_border_thicknesses() + + def set_border_thickness(self, thickness: float): + return self.get_shapes_list().set_border_thicknesses(thickness) + + @property + def border_thickness(self): + return self.get_border_thickness() + + @border_thickness.setter + def border_thickness(self, thickness: float): + self.set_border_thickness(thickness) + + def get_fill_color(self): + return self.get_shapes_list().get_fill_colors() + + def set_fill_color(self, fill_color: str or tuple or list): + return self.get_shapes_list().set_fill_colors(fill_color) + + @property + def fill_color(self): + return self.get_fill_color() + + @fill_color.setter + def fill_color(self, fill_color: str or tuple or list): + self.set_fill_color(fill_color) + + def move_relative_position_to(self, relative_position: tuple[float, float]): + return self.set_relative_position(relative_position) + + def move_relative_position_by(self, delta: tuple[float, float]): + current_position = self.get_relative_position() + new_position = (current_position[0] + delta[0], current_position[1] + delta[1]) + return self.set_relative_position(new_position) + + def get_info(self): result = [] result.append(f"relative_position: {self.get_relative_position()}") result.append(f"size: {self.get_size()}") @@ -86,5 +174,10 @@ def __str__(self): return "\n".join(result) + @property + def info(self): + return self.get_info() + def __repr__(self): - return f"ArrowHead(reaction_id={self.reaction_id}, reaction_glyph_index={self.reaction_glyph_index}, species_reference_index={self.species_reference_index})" + return (f"ArrowHead(shapes={repr(self.get_shapes_list())}," + f"reaction_id={self.reaction_id}, reaction_glyph_index={self.reaction_glyph_index},species_reference_index={self.species_reference_index})") diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/curve/curve.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/curve/curve.py index 55bf8e4..34b77fe 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/curve/curve.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/curve/curve.py @@ -3,6 +3,8 @@ from .curve_segment import CurveSegment from ..visual_element_lists.curve_element_lists import * from .arrow_head import ArrowHead +import math + class Curve: @@ -17,18 +19,31 @@ def get_reaction(self): return Reaction(self.libsbmlnetwork, self.reaction_id, self.reaction_glyph_index) + @property + def reaction(self): + return self.get_reaction() + def get_species(self): if self.species_reference_index is not None: reaction = self.get_reaction() species_list = reaction.get_species_list() species_id = self.libsbmlnetwork.getSpeciesReferenceSpeciesId(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index) - species_glyph_id = self.libsbmlnetwork.getSpeciesReferenceSpeciesGlyphId(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index) + species_glyph_index = self.libsbmlnetwork.getSpeciesGlyphIndex(species_id=species_id, reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index) for species in species_list: - if species.get_species_id() == species_id and species.get_id() == species_glyph_id: + if species.get_species_id() == species_id and species.get_graphical_object_index() == species_glyph_index: return species + if self.libsbmlnetwork.isSetSpeciesReferenceEmptySpeciesGlyph(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index): + species_glyph_id = self.libsbmlnetwork.getSpeciesReferenceEmptySpeciesGlyphId(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index) + for species in species_list: + if species.get_id() == species_glyph_id and species.get_graphical_object_index() == 0: + return species return None + @property + def species(self): + return self.get_species() + def get_role(self): if self.species_reference_index is not None: species = self.get_species() @@ -37,9 +52,25 @@ def get_role(self): return None - def get_roles_options(self): + @property + def role(self): + return self.get_role() + + def get_role_options(self): return self.libsbmlnetwork.getListOfRoles() + @property + def roles_options(self): + return self.get_role_options() + + def get_modifier_role_options(self): + roles = self.get_role_options() + return [role for role in roles if role not in ["substrate", "sidesubstrate", "product", "sideproduct"]] + + @property + def modifier_role_options(self): + return self.get_modifier_role_options() + def get_color(self): if self.species_reference_index is not None: return self.libsbmlnetwork.getSpeciesReferenceLineColor(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index) @@ -56,6 +87,14 @@ def set_color(self, color: str): return False + @property + def color(self): + return self.get_color() + + @color.setter + def color(self, color: str): + self.set_color(color) + def get_thickness(self): if self.species_reference_index is not None: return self.libsbmlnetwork.getSpeciesReferenceLineWidth(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index) @@ -72,6 +111,14 @@ def set_thickness(self, thickness: float): return False + @property + def thickness(self): + return self.get_thickness() + + @thickness.setter + def thickness(self, thickness: float): + self.set_thickness(thickness) + def get_start(self): first_segment = self.get_segment(0) if first_segment is not None: @@ -87,6 +134,14 @@ def set_start(self, start: tuple[float, float]): return False + @property + def start(self): + return self.get_start() + + @start.setter + def start(self, start: tuple[float, float]): + self.set_start(start) + def get_end(self): last_segment = self.get_segments_list()[-1] if last_segment is not None: @@ -102,6 +157,14 @@ def set_end(self, end: tuple[float, float]): return False + @property + def end(self): + return self.get_end() + + @end.setter + def end(self, end: tuple[float, float]): + self.set_end(end) + def get_start_slope(self): first_segment = self.get_segments_list()[0] if first_segment is not None: @@ -109,10 +172,14 @@ def get_start_slope(self): second_point = first_segment.get_control_point_1() if first_segment.get_start() == first_segment.get_control_point_1(): second_point = first_segment.get_end() - return (second_point[1] - first_point[1]) / (second_point[0] - first_point[0]) + return math.atan2(second_point[1] - first_point[1], second_point[0] - first_point[0]) return 0.0 + @property + def start_slope(self): + return self.get_start_slope() + def get_end_slope(self): last_segment = self.get_segments_list()[-1] if last_segment is not None: @@ -120,10 +187,14 @@ def get_end_slope(self): second_point = last_segment.get_control_point_2() if last_segment.get_end() == last_segment.get_control_point_2(): second_point = last_segment.get_start() - return (second_point[1] - first_point[1]) / (second_point[0] - first_point[0]) + return math.atan2(second_point[1] - first_point[1], second_point[0] - first_point[0]) return 0.0 + @property + def end_slope(self): + return self.get_end_slope() + def add_segment(self, start: tuple[float, float], end: tuple[float, float], control_point_1: tuple[float, float] = None, control_point_2: tuple[float, float] = None): if control_point_1 is None: control_point_1 = start @@ -195,41 +266,249 @@ def get_segments_list(self): return segments + def get_segments(self): + return self.get_segments_list() + + @property + def segments(self): + return self.get_segments_list() + def get_arrow_head(self): if self.species_reference_index is not None: if self.libsbmlnetwork.isSetSpeciesReferenceStartHead(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index) or \ - self.libsbmlnetwork.isSetSpeciesReferenceEndHead(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index): + self.libsbmlnetwork.isSetSpeciesReferenceEndHead(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index): return ArrowHead(self.libsbmlnetwork, self.reaction_id, self.reaction_glyph_index, self.species_reference_index) else: if self.libsbmlnetwork.isSetSpeciesReferenceStartHead(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index) or \ - self.libsbmlnetwork.isSetSpeciesReferenceEndHead(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index): + self.libsbmlnetwork.isSetSpeciesReferenceEndHead(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index): return ArrowHead(self.libsbmlnetwork, self.reaction_id, self.reaction_glyph_index, self.species_reference_index) return None - def move(self, delta: tuple[float, float]): - if all(self.get_segments_list().move(delta)): + @property + def arrow_head(self): + return self.get_arrow_head() + + def get_arrow_head_relative_position(self): + return self.get_arrow_head().get_relative_position() + + def set_arrow_head_relative_position(self, relative_position: tuple[float, float]): + return self.get_arrow_head().set_relative_position(relative_position) + + @property + def arrow_head_relative_position(self): + return self.get_arrow_head_relative_position() + + @arrow_head_relative_position.setter + def arrow_head_relative_position(self, relative_position: tuple[float, float]): + self.set_arrow_head_relative_position(relative_position) + + def get_arrow_head_size(self): + return self.get_arrow_head().get_size() + + def set_arrow_head_size(self, size: tuple[float, float]): + return self.get_arrow_head().set_size(size) + + @property + def arrow_head_size(self): + return self.get_arrow_head_size() + + @arrow_head_size.setter + def arrow_head_size(self, size: tuple[float, float]): + self.set_arrow_head_size(size) + + def get_arrow_head_shapes(self): + return self.get_arrow_head().get_shape_type() + + @property + def arrow_head_shapes(self): + return self.get_arrow_head_shapes() + + def set_arrow_head_shapes(self, shape: str): + return self.get_arrow_head().set_shape(shape) + + @arrow_head_shapes.setter + def arrow_head_shapes(self, shape: str): + self.set_arrow_head_shapes(shape) + + def get_arrow_head_border_color(self): + return self.get_arrow_head().get_border_color() + + def set_arrow_head_border_color(self, border_color: str): + return self.get_arrow_head().set_border_color(border_color) + + @property + def arrow_head_border_color(self): + return self.get_arrow_head_border_color() + + @arrow_head_border_color.setter + def arrow_head_border_color(self, border_color: str): + self.set_arrow_head_border_color(border_color) + + def get_arrow_head_border_thickness(self): + return self.get_arrow_head().get_border_thickness() + + def set_arrow_head_border_thickness(self, thickness: float): + return self.get_arrow_head().set_border_thickness(thickness) + + @property + def arrow_head_border_thickness(self): + return self.get_arrow_head_border_thickness() + + @arrow_head_border_thickness.setter + def arrow_head_border_thickness(self, thickness: float): + self.set_arrow_head_border_thickness(thickness) + + def get_arrow_head_fill_color(self): + return self.get_arrow_head().get_fill_color() + + def set_arrow_head_fill_color(self, fill_color: str or tuple or list): + return self.get_arrow_head().set_fill_color(fill_color) + + @property + def arrow_head_fill_color(self): + return self.get_arrow_head_fill_color() + + @arrow_head_fill_color.setter + def arrow_head_fill_color(self, fill_color: str or tuple or list): + self.set_arrow_head_fill_color(fill_color) + + def move_arrow_head_relative_position_to(self, relative_position: tuple[float, float]): + return self.get_arrow_head().move_relative_position_to(relative_position) + + def move_arrow_head_relative_position_by(self, delta: tuple[float, float]): + return self.get_arrow_head().move_relative_position_by(delta) + + def move_by(self, delta: tuple[float, float]): + if all(self.get_segments_list().move_by(delta)): return True return False - def move_start(self, delta: tuple[float, float]): + def move_start_to(self, position: tuple[float, float]): + start = self.get_start() + return self.move_start_by((position[0] - start[0], position[1] - start[1])) + + def move_start_by(self, delta: tuple[float, float]): first_segment = self.get_segments_list()[0] if first_segment is not None: - return first_segment.move_start(delta) + return first_segment.move_start_by(delta) return False - def move_end(self, delta: tuple[float, float]): + def move_end_to(self, position: tuple[float, float], adjust_end_point_for_uni_uni_reaction: bool = False): + end = self.get_end() + return self.move_end_by((position[0] - end[0], position[1] - end[1]), adjust_end_point_for_uni_uni_reaction) + + import math + + def move_end_by(self, delta: tuple[float, float], adjust_end_point_for_uni_uni_reaction: bool = False): last_segment = self.get_segments_list()[-1] if last_segment is not None: - return last_segment.move_end(delta) + if not last_segment.move_end_by(delta): + return False + reaction = self.get_reaction() + if len(reaction.get_species_list()) == 2 and len(reaction.get_curves_list()) == 2: + species_list = reaction.get_species_list() + first_species = self.get_species() + first_curve = self + first_species_center = ( + first_species.get_position()[0] + 0.5 * first_species.get_size()[0], + first_species.get_position()[1] + 0.5 * first_species.get_size()[1] + ) + second_species = [s for s in species_list if s.get_id() != first_species.get_id()][0] + second_curve = reaction.get_curves_list(second_species)[0] + second_species_center = ( + second_species.get_position()[0] + 0.5 * second_species.get_size()[0], + second_species.get_position()[1] + 0.5 * second_species.get_size()[1] + ) + reaction_center = ( + 0.5 * (first_species_center[0] + second_species_center[0]), + 0.5 * (first_species_center[1] + second_species_center[1]) + ) + reaction.set_position(reaction_center) + first_curve.get_segment().set_start(reaction_center) + first_curve.get_segment().set_control_point_1(reaction_center) + second_curve.get_segment().set_start(reaction_center) + second_curve.get_segment().set_control_point_1(reaction_center) + + if adjust_end_point_for_uni_uni_reaction: + dx1 = reaction_center[0] - first_species_center[0] + dy1 = reaction_center[1] - first_species_center[1] + w1, h1 = first_species.get_size() + half_w1, half_h1 = w1 / 2, h1 / 2 + t_x1 = half_w1 / abs(dx1) if dx1 != 0 else float('inf') + t_y1 = half_h1 / abs(dy1) if dy1 != 0 else float('inf') + t1 = min(t_x1, t_y1) + intersection1 = ( + first_species_center[0] + t1 * dx1, + first_species_center[1] + t1 * dy1 + ) + length1 = math.hypot(dx1, dy1) + pad_dx1 = (dx1 / length1) * 10 if length1 else 0 + pad_dy1 = (dy1 / length1) * 10 if length1 else 0 + first_curve_end = ( + intersection1[0] + pad_dx1, + intersection1[1] + pad_dy1 + ) + dx2 = reaction_center[0] - second_species_center[0] + dy2 = reaction_center[1] - second_species_center[1] + w2, h2 = second_species.get_size() + half_w2, half_h2 = w2 / 2, h2 / 2 + t_x2 = half_w2 / abs(dx2) if dx2 != 0 else float('inf') + t_y2 = half_h2 / abs(dy2) if dy2 != 0 else float('inf') + t2 = min(t_x2, t_y2) + intersection2 = ( + second_species_center[0] + t2 * dx2, + second_species_center[1] + t2 * dy2 + ) + length2 = math.hypot(dx2, dy2) + pad_dx2 = (dx2 / length2) * 5 if length2 else 0 + pad_dy2 = (dy2 / length2) * 5 if length2 else 0 + second_curve_end = ( + intersection2[0] + pad_dx2, + intersection2[1] + pad_dy2 + ) + first_curve.get_segment().set_end(first_curve_end) + second_curve.get_segment().set_end(second_curve_end) + first_curve.get_segment().set_control_point_2(first_curve.get_segment().get_end()) + second_curve.get_segment().set_control_point_2(second_curve.get_segment().get_end()) + return True + else: + return True + + return False + + def show(self): + if self.species_reference_index is not None: + if self.libsbmlnetwork.makeSpeciesReferenceVisible(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index) == 0: + return True + + #ToDo: Implement show reaction curve + + return False + + def hide(self): + if self.species_reference_index is not None: + if self.libsbmlnetwork.makeSpeciesReferenceInvisible(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index) == 0: + return True + + #ToDo: Implement hide reaction curve + + return False + + def is_hidden(self): + if self.species_reference_index is not None: + if self.libsbmlnetwork.isSpeciesReferenceVisible(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, species_reference_index=self.species_reference_index) == 0: + return True + + #ToDo: Implement hide reaction curve return False #ToDo: implement set arrow head - def __str__(self): + def get_info(self): result = [] #ToDo: Add the reaction, species, and role information result.append(f"color: {self.get_color()}") @@ -250,6 +529,9 @@ def __str__(self): return "\n".join(result) + @property + def info(self): + return self.get_info() def __repr__(self): return f"Curve(reaction_id={self.reaction_id}, reaction_glyph_index={self.reaction_glyph_index}, species_reference_index={self.species_reference_index})" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/curve/curve_segment.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/curve/curve_segment.py index c6e63aa..fa4d0e4 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/curve/curve_segment.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/curve/curve_segment.py @@ -30,6 +30,14 @@ def set_start(self, start: tuple[float, float]): return False + @property + def start(self): + return self.get_start() + + @start.setter + def start(self, start: tuple[float, float]): + self.set_start(start) + def get_end(self): if self.species_reference_index is None: return (self.libsbmlnetwork.getCurveSegmentEndPointX(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, curve_segment_index=self.curve_segment_index), @@ -50,6 +58,14 @@ def set_end(self, end: tuple[float, float]): return False + @property + def end(self): + return self.get_end() + + @end.setter + def end(self, end: tuple[float, float]): + self.set_end(end) + def get_control_point_1(self): if self.species_reference_index is None: return (self.libsbmlnetwork.getCurveSegmentBasePoint1X(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, curve_segment_index=self.curve_segment_index), @@ -70,6 +86,14 @@ def set_control_point_1(self, control_point_1: tuple[float, float]): return False + @property + def control_point_1(self): + return self.get_control_point_1() + + @control_point_1.setter + def control_point_1(self, control_point_1: tuple[float, float]): + self.set_control_point_1(control_point_1) + def get_control_point_2(self): if self.species_reference_index is None: return (self.libsbmlnetwork.getCurveSegmentBasePoint2X(reaction_id=self.reaction_id, reaction_glyph_index=self.reaction_glyph_index, curve_segment_index=self.curve_segment_index), @@ -90,7 +114,15 @@ def set_control_point_2(self, control_point_2: tuple[float, float]): return False - def move(self, delta: tuple[float, float]): + @property + def control_point_2(self): + return self.get_control_point_2() + + @control_point_2.setter + def control_point_2(self, control_point_2: tuple[float, float]): + self.set_control_point_2(control_point_2) + + def move_by(self, delta: tuple[float, float]): current_start = self.get_start() new_start = (current_start[0] + delta[0], current_start[1] + delta[1]) if not self.set_start(new_start): @@ -113,7 +145,11 @@ def move(self, delta: tuple[float, float]): return True - def move_start(self, delta: tuple[float, float]): + def move_start_to(self, position: tuple[float, float]): + start = self.get_start() + return self.move_by((position[0] - start[0], position[1] - start[1])) + + def move_start_by(self, delta: tuple[float, float]): current_start = self.get_start() new_start = (current_start[0] + delta[0], current_start[1] + delta[1]) if not self.set_start(new_start): @@ -126,7 +162,11 @@ def move_start(self, delta: tuple[float, float]): return True - def move_end(self, delta: tuple[float, float]): + def move_end_to(self, position: tuple[float, float]): + end = self.get_end() + return self.move_by((position[0] - end[0], position[1] - end[1])) + + def move_end_by(self, delta: tuple[float, float]): current_end = self.get_end() new_end = (current_end[0] + delta[0], current_end[1] + delta[1]) if not self.set_end(new_end): @@ -139,7 +179,7 @@ def move_end(self, delta: tuple[float, float]): return True - def __str__(self): + def get_info(self): return( f"start: {self.get_start()}\n" f"end: {self.get_end()}\n" @@ -147,5 +187,9 @@ def __str__(self): f"control_point_2: {self.get_control_point_2()}" ) + @property + def info(self): + return self.get_info() + def __repr__(self): return self.__str__() diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/label.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/label.py index a9315bb..9c07f3a 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/label.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/label.py @@ -21,6 +21,14 @@ def set_position(self, position: tuple[float, float]): return False + @property + def position(self): + return self.get_position() + + @position.setter + def position(self, position: tuple[float, float]): + self.set_position(position) + def get_size(self): return (self.libsbmlnetwork.getTextWidth(id=self.element_id, graphical_object_index=self.graphical_object_index, text_glyph_index=self.text_glyph_index), @@ -36,6 +44,14 @@ def set_size(self, size: tuple[float, float]): return False + @property + def size(self): + return self.get_size() + + @size.setter + def size(self, size: tuple[float, float]): + self.set_size(size) + def align_to_top(self): if self.libsbmlnetwork.setTextVerticalAlignment(id=self.element_id, graphical_object_index=self.graphical_object_index, text_glyph_index=self.text_glyph_index, text_vertical_alignment="top"): @@ -78,6 +94,10 @@ def get_vertical_alignment(self): return None + @property + def vertical_alignment(self): + return self.get_vertical_alignment() + def align_to_left(self): if self.libsbmlnetwork.setTextHorizontalAlignment(id=self.element_id, graphical_object_index=self.graphical_object_index, text_glyph_index=self.text_glyph_index, text_horizontal_alignment="start"): @@ -111,6 +131,10 @@ def get_horizontal_alignment(self): return None + @property + def horizontal_alignment(self): + return self.get_horizontal_alignment + def get_text(self): return self.libsbmlnetwork.getText(id=self.element_id, graphical_object_index=self.graphical_object_index, text_glyph_index=self.text_glyph_index) @@ -122,6 +146,14 @@ def set_text(self, text: str): return False + @property + def text(self): + return self.get_text() + + @text.setter + def text(self, text: str): + self.set_text(text) + def get_font_color(self): return self.libsbmlnetwork.getFontColor(id=self.element_id, graphical_object_index=self.graphical_object_index, text_glyph_index=self.text_glyph_index) @@ -133,6 +165,14 @@ def set_font_color(self, font_color: str): return False + @property + def font_color(self): + return self.get_font_color() + + @font_color.setter + def font_color(self, font_color: str): + self.set_font_color(font_color) + def get_font(self): return self.libsbmlnetwork.getFontFamily(id=self.element_id, graphical_object_index=self.graphical_object_index, text_glyph_index=self.text_glyph_index) @@ -144,6 +184,14 @@ def set_font(self, font): return False + @property + def font(self): + return self.get_font() + + @font.setter + def font(self, font): + self.set_font(font) + def get_font_size(self): return self.libsbmlnetwork.getFontSize(id=self.element_id, graphical_object_index=self.graphical_object_index, text_glyph_index=self.text_glyph_index) @@ -155,6 +203,14 @@ def set_font_size(self, font_size: float): return False + @property + def font_size(self): + return self.get_font_size() + + @font_size.setter + def font_size(self, font_size: float): + self.set_font_size(font_size) + def set_bold(self, bold: bool): if bold: return self.libsbmlnetwork.setFontWeight(id=self.element_id, graphical_object_index=self.graphical_object_index, @@ -186,7 +242,21 @@ def is_italic(self): #ToDo hide/show label - def __str__(self): + def move_to(self, position: tuple[float, float]): + if self.set_position(position): + return True + + return False + + def move_by(self, position: tuple[float, float]): + current_position = self.get_position() + new_position = (current_position[0] + position[0], current_position[1] + position[1]) + if self.set_position(new_position): + return True + + return False + + def get_info(self): return ( f"text: {self.get_text()}\n" f"position: {self.get_position()}\n" @@ -200,5 +270,9 @@ def __str__(self): f"horizontal_alignment: {self.get_horizontal_alignment()}" ) + @property + def info(self): + return self.get_info() + def __repr__(self): - return f"Label({self.get_text()})" + return f"{self.get_text()}" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/reaction_center.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/reaction_center.py index ddfa94e..72e0cd4 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/reaction_center.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/reaction_center.py @@ -1,5 +1,6 @@ from .curve import Curve + class ReactionCenter: def __init__(self, libsbmlnetwork, reaction_id, reaction_glyph_index): @@ -12,6 +13,10 @@ def get_reaction(self): return Reaction(self.libsbmlnetwork, self.reaction_id, self.reaction_glyph_index) + @property + def reaction(self): + return self.get_reaction() + def switch_to_curve(self): shapes_list = self.get_shapes_list() reaction = self.get_reaction() @@ -51,16 +56,27 @@ def get_curve(self): return None + @property + def curve(self): + return self.get_curve() + def get_shapes_list(self): return self.get_reaction().get_shapes_list() + def get_shapes(self): + return self.get_shapes_list() + + @property + def shapes(self): + return self.get_shapes_list() + def move(self, delta: tuple[float, float]): if self.is_curve(): - return self.get_curve().move(delta) + return self.get_curve().move_by(delta) return True - def __str__(self): + def get_info(self): result = [] result.append(f"reaction id: {self.reaction_id}") if self.is_curve(): @@ -74,4 +90,14 @@ def __str__(self): if shape != shapes[-1]: result.append("----") - return "\n".join(result) \ No newline at end of file + return "\n".join(result) + + @property + def info(self): + return self.get_info() + + def __repr__(self): + if self.is_curve(): + return f"ReactionCenter({self.reaction_id}, {self.reaction_glyph_index}, {repr(self.get_curve())})" + else: + return f"ReactionCenter({self.reaction_id}, {self.reaction_glyph_index}, {repr(self.get_shapes_list())})" \ No newline at end of file diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/curve.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/curve.py index 393c1f6..c1ac2ff 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/curve.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/curve.py @@ -10,10 +10,10 @@ def get_points(self): self.libsbmlnetwork.getGeometricShapeSegmentX(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index, segment_index=segment_index), self.libsbmlnetwork.getGeometricShapeSegmentY(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index, segment_index=segment_index))) else: - for segment_index in range(self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeNumSegments(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index, species_reference_index=self.sub_entity_index, index=self.geometric_shape_index)): + for segment_index in range(self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeNumSegments(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index, species_reference_index=self.sub_element_index, index=self.geometric_shape_index)): points.append(( - self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeSegmentX(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index, species_reference_index=self.sub_entity_index, index=self.geometric_shape_index, segment_index=segment_index), - self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeSegmentY(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index, species_reference_index=self.sub_entity_index, index=self.geometric_shape_index, segment_index=segment_index))) + self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeSegmentX(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index, species_reference_index=self.sub_element_index, index=self.geometric_shape_index, segment_index=segment_index), + self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeSegmentY(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index, species_reference_index=self.sub_element_index, index=self.geometric_shape_index, segment_index=segment_index))) return points @@ -32,15 +32,16 @@ def set_points(self, points: list[tuple[float, float]]): else: for segment_index, point in enumerate(points): #ToDo add a geometric shapes segment - if self.libsbmlnetwork.setSpeciesReferenceLineEndingGeometricShapeSegmentX(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index, species_reference_index=self.sub_entity_index, geometric_shape_index=self.geometric_shape_index, segment_index=segment_index, x=point[0]) != 0 or \ - self.libsbmlnetwork.setSpeciesReferenceLineEndingGeometricShapeSegmentY(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index, species_reference_index=self.sub_entity_index, geometric_shape_index=self.geometric_shape_index, segment_index=segment_index, y=point[1]) != 0: + if self.libsbmlnetwork.setSpeciesReferenceLineEndingGeometricShapeSegmentX(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index, species_reference_index=self.sub_element_index, geometric_shape_index=self.geometric_shape_index, segment_index=segment_index, x=point[0]) != 0 or \ + self.libsbmlnetwork.setSpeciesReferenceLineEndingGeometricShapeSegmentY(reaction_id=self.element_id, reaction_glyph_index=self.graphical_object_index, species_reference_index=self.sub_element_index, geometric_shape_index=self.geometric_shape_index, segment_index=segment_index, y=point[1]) != 0: return False return True - def __str__(self): + def get_info(self): base_str = super().__str__() return ( base_str + "\n" + f"points: {self.get_points()}" ) + diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/ellipse.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/ellipse.py index f72092c..32e4b25 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/ellipse.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/ellipse.py @@ -3,46 +3,46 @@ class Ellipse(ShapeBase): def get_relative_center(self): - if self.sub_entity_index is None: + if self.sub_element_index is None: return self.libsbmlnetwork.getGeometricShapeCenterX(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index), \ self.libsbmlnetwork.getGeometricShapeCenterY(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index) else: - return self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeCenterX(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_entity_index, geometric_shape_index=self.geometric_shape_index), \ - self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeCenterY(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_entity_index, geometric_shape_index=self.geometric_shape_index) + return self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeCenterX(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_element_index, geometric_shape_index=self.geometric_shape_index), \ + self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeCenterY(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_element_index, geometric_shape_index=self.geometric_shape_index) def set_relative_center(self, center: tuple[float, float]): - if self.sub_entity_index is None: + if self.sub_element_index is None: if self.libsbmlnetwork.setGeometricShapeCenterX(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index, x=center[0]) == 0 and \ self.libsbmlnetwork.setGeometricShapeCenterY(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index, y=center[1]) == 0: return True else: - if self.libsbmlnetwork.setSpeciesReferenceLineEndingGeometricShapeCenterX(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_entity_index, geometric_shape_index=self.geometric_shape_index, x=center[0]) == 0 and \ - self.libsbmlnetwork.setSpeciesReferenceLineEndingGeometricShapeCenterY(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_entity_index, geometric_shape_index=self.geometric_shape_index, y=center[1]) == 0: + if self.libsbmlnetwork.setSpeciesReferenceLineEndingGeometricShapeCenterX(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_element_index, geometric_shape_index=self.geometric_shape_index, x=center[0]) == 0 and \ + self.libsbmlnetwork.setSpeciesReferenceLineEndingGeometricShapeCenterY(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_element_index, geometric_shape_index=self.geometric_shape_index, y=center[1]) == 0: return True return False def get_radii(self): - if self.sub_entity_index is None: + if self.sub_element_index is None: return self.libsbmlnetwork.getGeometricShapeRadiusX(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index), \ self.libsbmlnetwork.getGeometricShapeRadiusY(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index) else: - return self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeRadiusX(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_entity_index, geometric_shape_index=self.geometric_shape_index), \ - self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeRadiusY(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_entity_index, geometric_shape_index=self.geometric_shape_index) + return self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeRadiusX(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_element_index, geometric_shape_index=self.geometric_shape_index), \ + self.libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeRadiusY(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_element_index, geometric_shape_index=self.geometric_shape_index) def set_radii(self, radii: tuple[float, float]): - if self.sub_entity_index is None: + if self.sub_element_index is None: if self.libsbmlnetwork.setGeometricShapeWidth(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index, width=radii[0]) == 0 and \ self.libsbmlnetwork.setGeometricShapeHeight(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index, height=radii[1]) == 0: return True else: - if self.libsbmlnetwork.setSpeciesReferenceLineEndingGeometricShapeRadiusX(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_entity_index, geometric_shape_index=self.geometric_shape_index, radius_x=radii[0]) == 0 and \ - self.libsbmlnetwork.setSpeciesReferenceLineEndingGeometricShapeRadiusY(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_entity_index, geometric_shape_index=self.geometric_shape_index, radius_y=radii[1]) == 0: + if self.libsbmlnetwork.setSpeciesReferenceLineEndingGeometricShapeRadiusX(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_element_index, geometric_shape_index=self.geometric_shape_index, radius_x=radii[0]) == 0 and \ + self.libsbmlnetwork.setSpeciesReferenceLineEndingGeometricShapeRadiusY(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_element_index, geometric_shape_index=self.geometric_shape_index, radius_y=radii[1]) == 0: return True return False - def __str__(self): + def get_info(self): base_str = super().__str__() return ( base_str + "\n" + diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/polygon.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/polygon.py index 3a42c49..148c975 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/polygon.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/polygon.py @@ -37,7 +37,15 @@ def set_points(self, points: list[tuple[float, float]]): return True - def __str__(self): + @property + def points(self): + return self.get_points() + + @points.setter + def points(self, points: list[tuple[float, float]]): + self.set_points(points) + + def get_info(self): base_str = super().__str__() return ( base_str + "\n" + diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/rectangle.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/rectangle.py index 67edb56..8e35136 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/rectangle.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/rectangle.py @@ -22,6 +22,14 @@ def set_relative_position(self, relative_position: tuple[float, float]): return False + @property + def relative_position(self): + return self.get_relative_position() + + @relative_position.setter + def relative_position(self, relative_position: tuple[float, float]): + self.set_relative_position(relative_position) + def get_size(self): if self.sub_element_index is None: return self.libsbmlnetwork.getGeometricShapeWidth(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index), \ @@ -42,6 +50,14 @@ def set_size(self, size: tuple[float, float]): return False + @property + def size(self): + return self.get_size() + + @size.setter + def size(self, size: tuple[float, float]): + self.set_size(size) + def get_corner_radius(self): if self.sub_element_index is None: return self.libsbmlnetwork.getGeometricShapeBorderRadiusX(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index), \ @@ -60,7 +76,17 @@ def set_corner_radius(self, corner_radius: tuple[float, float]): self.libsbmlnetwork.setSpeciesReferenceLineEndingGeometricShapeBorderRadiusY(id=self.element_id, graphical_object_index=self.graphical_object_index, species_reference_index=self.sub_element_index, geometric_shape_index=self.geometric_shape_index, y=corner_radius[1]) == 0: return True - def __str__(self): + return False + + @property + def corner_radius(self): + return self.get_corner_radius() + + @corner_radius.setter + def corner_radius(self, corner_radius: tuple[float, float]): + self.set_corner_radius(corner_radius) + + def get_info(self): base_str = super().__str__() return ( base_str + "\n" + diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/shape_base.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/shape_base.py index 3d2a529..dfc5b3b 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/shape_base.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/shape_base.py @@ -31,6 +31,10 @@ def get_type(self): else: return shape + @property + def type(self): + return self.get_type() + def get_border_color(self): if self.sub_element_index is None: return self.libsbmlnetwork.getGeometricShapeBorderColor(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index) @@ -47,6 +51,14 @@ def set_border_color(self, border_color: str): return False + @property + def border_color(self): + return self.get_border_color() + + @border_color.setter + def border_color(self, border_color: str): + self.set_border_color(border_color) + def get_border_thickness(self): if self.sub_element_index is None: return self.libsbmlnetwork.getGeometricShapeBorderWidth(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index) @@ -63,6 +75,14 @@ def set_border_thickness(self, thickness: float): return False + @property + def border_thickness(self): + return self.get_border_thickness() + + @border_thickness.setter + def border_thickness(self, thickness: float): + self.set_border_thickness(thickness) + def get_fill_color(self): if self.sub_element_index is None: return self.libsbmlnetwork.getGeometricShapeFillColor(id=self.element_id, graphical_object_index=self.graphical_object_index, geometric_shape_index=self.geometric_shape_index) @@ -83,6 +103,17 @@ def set_fill_color(self, fill_color: str or tuple or list): species_reference_index=self.sub_element_index, fill_color=fill_color) == 0: return True + + return False + + @property + def fill_color(self): + return self.get_fill_color() + + @fill_color.setter + def fill_color(self, fill_color: str or tuple or list): + self.set_fill_color(fill_color) + # ToDo: Implement gradient fill color # elif isinstance(fill_color, (tuple, list)) and len(fill_color) == 3: # stop_colors, stop_offsets, gradient_type = fill_color @@ -102,10 +133,10 @@ def set_fill_color(self, fill_color: str or tuple or list): # stop_colors=stop_colors, stop_offsets=stop_offsets, # gradient_type=gradient_type) == 0: # return True + # + # return False - return False - - def __str__(self): + def get_info(self): return ( f"type: {self.get_type()}\n" f"border_color: {self.get_border_color()}\n" @@ -113,5 +144,9 @@ def __str__(self): f"fill_color: {self.get_fill_color()}" ) + @property + def info(self): + return self.get_info() + def __repr__(self): return f"{self.get_type()}" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/shape_factory.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/shape_factory.py index b3ce427..a69b30a 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/shape_factory.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/shapes/shape_factory.py @@ -3,16 +3,16 @@ from .polygon import Polygon from .curve import Curve -def create_shape(libsbmlnetwork, entity_id, graphical_object_index, geometric_shape_index, sub_entity_index=None): - if sub_entity_index is None: +def create_shape(libsbmlnetwork, entity_id, graphical_object_index, geometric_shape_index, sub_element_index=None): + if sub_element_index is None: shape_type = libsbmlnetwork.getGeometricShapeType(id=entity_id, graphical_object_index=graphical_object_index, geometric_shape_index=geometric_shape_index) else: - shape_type = libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeType(reaction_id=entity_id, reaction_glyph_index=graphical_object_index, species_reference_index=sub_entity_index, index=geometric_shape_index) + shape_type = libsbmlnetwork.getSpeciesReferenceLineEndingGeometricShapeType(reaction_id=entity_id, reaction_glyph_index=graphical_object_index, species_reference_index=sub_element_index, index=geometric_shape_index) if shape_type == "rectangle": - return Rectangle(libsbmlnetwork, entity_id, graphical_object_index, geometric_shape_index, sub_entity_index) + return Rectangle(libsbmlnetwork, entity_id, graphical_object_index, geometric_shape_index, sub_element_index) elif shape_type == "ellipse": - return Ellipse(libsbmlnetwork, entity_id, graphical_object_index, geometric_shape_index, sub_entity_index) + return Ellipse(libsbmlnetwork, entity_id, graphical_object_index, geometric_shape_index, sub_element_index) elif shape_type in ["polygon", "triangle", "diamond", "pentagon", "hexagon", "octagon"]: - return Polygon(libsbmlnetwork, entity_id, graphical_object_index, geometric_shape_index, sub_entity_index) + return Polygon(libsbmlnetwork, entity_id, graphical_object_index, geometric_shape_index, sub_element_index) elif shape_type in ["curve", "rendercurve"]: - return Curve(libsbmlnetwork, entity_id, graphical_object_index, geometric_shape_index, sub_entity_index) + return Curve(libsbmlnetwork, entity_id, graphical_object_index, geometric_shape_index, sub_element_index) diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/curve_element_lists/arrow_head_list.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/curve_element_lists/arrow_head_list.py index 556118c..28fa713 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/curve_element_lists/arrow_head_list.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/curve_element_lists/arrow_head_list.py @@ -3,35 +3,51 @@ class ArrowHeadList(list): def __init__(self, arrow_head_list=None): super().__init__(arrow_head_list or []) - def get_relative_position(self): + def get_relative_positions(self): positions = [] for arrow_head in self: positions.append(arrow_head.get_relative_position()) return positions - def set_relative_position(self, relative_position: tuple[float, float]): + def set_relative_positions(self, relative_position: tuple[float, float]): results = [] for arrow_head in self: results.append(arrow_head.set_relative_position(relative_position)) return results - def get_size(self): + @property + def relative_positions(self): + return self.get_relative_positions() + + @relative_positions.setter + def relative_positions(self, relative_position: tuple[float, float]): + self.set_relative_positions(relative_position) + + def get_sizes(self): sizes = [] for arrow_head in self: sizes.append(arrow_head.get_size()) return sizes - def set_size(self, size: tuple[float, float]): + def set_sizes(self, size: tuple[float, float]): results = [] for arrow_head in self: results.append(arrow_head.set_size(size)) return results - def get_shape(self): + @property + def sizes(self): + return self.get_sizes() + + @sizes.setter + def sizes(self, size: tuple[float, float]): + self.set_sizes(size) + + def get_shapes(self): from ...visual_element_lists import ShapeList shapes = ShapeList() @@ -40,14 +56,22 @@ def get_shape(self): return shapes - def set_shape(self, shape_type: str): + def set_shapes(self, shape_type: str): results = [] for arrow_head in self: results.append(arrow_head.set_shape(shape_type)) return results - def add_shape(self, shape_type: str): + @property + def shapes(self): + return self.get_shapes() + + @shapes.setter + def shapes(self, shape_type: str): + self.set_shapes(shape_type) + + def add_shapes(self, shape_type: str): from ..shape_list import ShapeList shapes = ShapeList() @@ -57,7 +81,7 @@ def add_shape(self, shape_type: str): shapes.append(shape) return shapes - def remove_shape(self, shape): + def remove_shapes(self, shape): results = [] for arrow_head in self: results.append(arrow_head.remove_shape(shape)) @@ -73,11 +97,110 @@ def get_shapes_list(self): return shapes - def __str__(self): + def get_shape_types(self): + shape_types = [] + for arrow_head in self: + shape_types.append(arrow_head.get_shape_type()) + + return shape_types + + @property + def shape_types(self): + return self.get_shape_types() + + def get_border_colors(self): + border_colors = [] + for arrow_head in self: + border_colors.append(arrow_head.get_border_color()) + + return border_colors + + def set_border_colors(self, border_color: str): + results = [] + for arrow_head in self: + results.append(arrow_head.set_border_color(border_color)) + + return results + + @property + def border_colors(self): + border_colors = [] + for arrow_head in self: + border_colors.append(arrow_head.get_border_color()) + + return border_colors + + @border_colors.setter + def border_colors(self, border_color: str): + self.set_border_colors(border_color) + + def get_border_thicknesses(self): + border_thicknesses = [] + for arrow_head in self: + border_thicknesses.append(arrow_head.get_border_thickness()) + + return border_thicknesses + + def set_border_thicknesses(self, thickness: float): + results = [] + for arrow_head in self: + results.append(arrow_head.set_border_thickness(thickness)) + + return results + + @property + def border_thicknesses(self): + return self.get_border_thicknesses() + + @border_thicknesses.setter + def border_thicknesses(self, thickness: float): + self.set_border_thicknesses(thickness) + + def get_fill_colors(self): + fill_colors = [] + for arrow_head in self: + fill_colors.append(arrow_head.get_fill_color()) + + return fill_colors + + def set_fill_colors(self, fill_color: str or tuple or list): + results = [] + for arrow_head in self: + results.append(arrow_head.set_fill_color(fill_color)) + + return results + + @property + def fill_colors(self): + return self.get_fill_colors() + + @fill_colors.setter + def fill_colors(self, fill_color: str or tuple or list): + self.set_fill_colors(fill_color) + + def move_relative_positions_to(self, relative_position: tuple[float, float]): + results = [] + for arrow_head in self: + results.append(arrow_head.move_relative_position_to(relative_position)) + + return results + + def move_relative_positions_by(self, delta: tuple[float, float]): + results = [] + for arrow_head in self: + results.append(arrow_head.move_relative_position_by(delta)) + + return results + + def get_info(self): result = [] for arrow_head in self: result.append(str(arrow_head)) return "\n\n".join(result) + @property + def info(self): + return self.get_info() + def __repr__(self): return f"ArrowHeadList({[repr(arrow_head) for arrow_head in self]})" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/curve_element_lists/curve_list.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/curve_element_lists/curve_list.py index 4f81303..23e688d 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/curve_element_lists/curve_list.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/curve_element_lists/curve_list.py @@ -1,18 +1,23 @@ from ....network_element_lists import * + class CurveList(list): def __init__(self, curve_list=None, libsbmlnetwork=None): super().__init__(curve_list or []) self.libsbmlnetwork = libsbmlnetwork - def get_reaction(self): + def get_reactions(self): reaction_list = ReactionList(libsbmlnetwork=self.libsbmlnetwork) for curve in self: reaction_list.append(curve.get_reaction()) return reaction_list + @property + def reactions(self): + return self.get_reactions() + def get_species(self): species_list = SpeciesList(libsbmlnetwork=self.libsbmlnetwork) for curve in self: @@ -20,91 +25,146 @@ def get_species(self): return species_list - def get_color(self): + @property + def species(self): + return self.get_species() + + def get_roles(self): + roles = [] + for curve in self: + roles.append(curve.get_role()) + + return roles + + @property + def roles(self): + return self.get_roles() + + def get_colors(self): colors = [] for curve in self: colors.append(curve.get_color()) return colors - def set_color(self, color: str): + def set_colors(self, color: str): results = [] for curve in self: results.append(curve.set_color(color)) return results - def get_thickness(self): + @property + def colors(self): + return self.get_colors() + + @colors.setter + def colors(self, color: str): + self.set_colors(color) + + def get_thicknesses(self): thicknesses = [] for curve in self: thicknesses.append(curve.get_thickness()) return thicknesses - def set_thickness(self, thickness: float): + def set_thicknesses(self, thickness: float): results = [] for curve in self: results.append(curve.set_thickness(thickness)) return results - def get_start(self): + @property + def thicknesses(self): + return self.get_thicknesses() + + @thicknesses.setter + def thicknesses(self, thickness: float): + self.set_thicknesses(thickness) + + def get_starts(self): start_points = [] for curve in self: start_points.append(curve.get_start()) return start_points - def set_start(self, start: tuple[float, float]): + def set_starts(self, start: tuple[float, float]): results = [] for curve in self: results.append(curve.set_start(start)) return results - def get_end(self): + @property + def starts(self): + return self.get_starts() + + @starts.setter + def starts(self, start: tuple[float, float]): + self.set_starts(start) + + def get_ends(self): end_points = [] for curve in self: end_points.append(curve.get_end()) return end_points - def set_end(self, end: tuple[float, float]): + def set_ends(self, end: tuple[float, float]): results = [] for curve in self: results.append(curve.set_end(end)) return results - def get_start_slope(self): + @property + def ends(self): + return self.get_ends() + + @ends.setter + def ends(self, end: tuple[float, float]): + self.set_ends(end) + + def get_start_slopes(self): start_slopes = [] for curve in self: start_slopes.append(curve.get_start_slope()) return start_slopes - def get_end_slope(self): + @property + def start_slopes(self): + return self.get_start_slopes() + + def get_end_slopes(self): end_slopes = [] for curve in self: end_slopes.append(curve.get_end_slope()) return end_slopes - def add_segment(self, start: tuple[float, float], end: tuple[float, float], control_point_1: tuple[float, float] = None, control_point_2: tuple[float, float] = None): + @property + def end_slopes(self): + return self.get_end_slopes() + + def add_segments(self, start: tuple[float, float], end: tuple[float, float], control_point_1: tuple[float, float] = None, control_point_2: tuple[float, float] = None): results = [] for curve in self: results.append(curve.add_segment(start, end, control_point_1, control_point_2)) return results - def remove_segment(self, curve_segment_index: int): + def remove_segments(self, curve_segment_index: int): results = [] for curve in self: results.append(curve.remove_segment(curve_segment_index)) return results - def get_segment(self, curve_segment_index: int = 0): + def get_first_segments(self, curve_segment_index: int = 0): from .curve_segment_list import CurveSegmentList curve_segment_list = CurveSegmentList() @@ -113,6 +173,10 @@ def get_segment(self, curve_segment_index: int = 0): return curve_segment_list + @property + def first_segments(self): + return self.get_first_segments() + def get_segments_list(self): from .curve_segment_list import CurveSegmentList @@ -122,7 +186,14 @@ def get_segments_list(self): return curve_segment_list - def get_arrow_head(self): + def get_segments(self, curve_segment_index: int = 0): + return self.get_segments(curve_segment_index) + + @property + def segments(self): + return self.get_segments_list() + + def get_arrow_heads(self): from .arrow_head_list import ArrowHeadList arrow_heads = ArrowHeadList() @@ -133,32 +204,225 @@ def get_arrow_head(self): return arrow_heads - def move(self, delta: tuple[float, float]): + @property + def arrow_heads(self): + return self.get_arrow_heads() + + def get_arrow_head_relative_positions(self): + relative_positions = [] + for curve in self: + relative_positions.append(curve.get_arrow_head_relative_position()) + + return relative_positions + + def set_arrow_head_relative_positions(self, relative_position: float): + results = [] + for curve in self: + results.append(curve.set_arrow_head_relative_position(relative_position)) + + return results + + @property + def arrow_head_relative_positions(self): + return self.get_arrow_head_relative_positions() + + @arrow_head_relative_positions.setter + def arrow_head_relative_positions(self, relative_position: float): + self.set_arrow_head_relative_positions(relative_position) + + def get_arrow_head_sizes(self): + sizes = [] + for curve in self: + sizes.append(curve.get_arrow_head_size()) + + return sizes + + def set_arrow_head_sizes(self, size: float): + results = [] + for curve in self: + results.append(curve.set_arrow_head_size(size)) + + return results + + @property + def arrow_head_sizes(self): + return self.get_arrow_head_sizes() + + @arrow_head_sizes.setter + def arrow_head_sizes(self, size: float): + self.set_arrow_head_sizes(size) + + def get_arrow_head_shapes(self): + from ..shape_list import ShapeList + + arrow_head_shapes = ShapeList() + for curve in self: + arrow_head = curve.get_arrow_head() + if arrow_head: + arrow_head_shapes.append(arrow_head.get_shape()) + + return arrow_head_shapes + + @property + def arrow_head_shapes(self): + return self.get_arrow_head_shapes() + + def get_arrow_head_shape_types(self): + shape_types = [] + for curve in self: + shape_types.append(curve.get_arrow_head_shape_type()) + + return shape_types + + @property + def arrow_head_shape_types(self): + return self.get_arrow_head_shape_types() + + def get_arrow_head_border_colors(self): + border_colors = [] + for curve in self: + border_colors.append(curve.get_arrow_head_border_color()) + + return border_colors + + def set_arrow_head_border_colors(self, border_color: str): + results = [] + for curve in self: + results.append(curve.set_arrow_head_border_color(border_color)) + + return results + + @property + def arrow_head_border_colors(self): + return self.get_arrow_head_border_colors() + + @arrow_head_border_colors.setter + def arrow_head_border_colors(self, border_color: str): + self.set_arrow_head_border_colors(border_color) + + def get_arrow_head_border_thicknesses(self): + thicknesses = [] + for curve in self: + thicknesses.append(curve.get_arrow_head_border_thickness()) + + return thicknesses + + def set_arrow_head_border_thicknesses(self, thickness: float): + results = [] + for curve in self: + results.append(curve.set_arrow_head_border_thickness(thickness)) + + return results + + @property + def arrow_head_border_thicknesses(self): + return self.get_arrow_head_border_thicknesses() + + @arrow_head_border_thicknesses.setter + def arrow_head_border_thicknesses(self, thickness: float): + self.set_arrow_head_border_thicknesses(thickness) + + def get_arrow_head_fill_colors(self): + fill_colors = [] + for curve in self: + fill_colors.append(curve.get_arrow_head_fill_color()) + + return fill_colors + + def set_arrow_head_fill_colors(self, fill_color: str or tuple or list): + results = [] + for curve in self: + results.append(curve.set_arrow_head_fill_color(fill_color)) + + return results + + @property + def arrow_head_fill_colors(self): + return self.get_arrow_head_fill_colors() + + @arrow_head_fill_colors.setter + def arrow_head_fill_colors(self, fill_color: str or tuple or list): + self.set_arrow_head_fill_colors(fill_color) + + def move_arrow_head_relative_positions_to(self, relative_position: tuple[float, float]): + results = [] + for curve in self: + results.append(curve.move_arrow_head_relative_position_to(relative_position)) + + return results + + def move_arrow_head_relative_positions_by(self, delta: tuple[float, float]): results = [] for curve in self: - results.append(curve.move(delta)) + results.append(curve.move_arrow_head_relative_position_by(delta)) return results - def move_start(self, delta: tuple[float, float]): + def move_by(self, delta: tuple[float, float]): results = [] for curve in self: - results.append(curve.move_start(delta)) + results.append(curve.move_by(delta)) return results - def move_end(self, delta: tuple[float, float]): + def move_to(self, position: tuple[float, float]): results = [] for curve in self: - results.append(curve.move_end(delta)) + results.append(curve.move_to(position)) return results - def __str__(self): + def move_start_by(self, delta: tuple[float, float]): + results = [] + for curve in self: + results.append(curve.move_start_by(delta)) + + return results + + def move_end_to(self, position: tuple[float, float]): + results = [] + for curve in self: + results.append(curve.move_end_to(position)) + + return results + + def move_end_by(self, delta: tuple[float, float]): + results = [] + for curve in self: + results.append(curve.move_end_by(delta)) + + return results + + def show(self): + results = [] + for curve in self: + results.append(curve.show()) + + return results + + def hide(self): + results = [] + for curve in self: + results.append(curve.hide()) + + return results + + def are_hidden(self): + hidden = [] + for curve in self: + hidden.append(curve.is_hidden()) + + return hidden + + def get_info(self): result = [] for curve in self: result.append(str(curve)) return "\n".join(result) + @property + def info(self): + return self.get_info() + def __repr__(self): return f"CurveList({[repr(curve) for curve in self]})" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/curve_element_lists/curve_segment_list.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/curve_element_lists/curve_segment_list.py index a8ca0b8..05db1df 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/curve_element_lists/curve_segment_list.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/curve_element_lists/curve_segment_list.py @@ -3,88 +3,130 @@ class CurveSegmentList(list): def __init__(self, segment_list=None): super().__init__(segment_list or []) - def get_start(self): + def get_starts(self): results = [] for segment in self: results.append(segment.get_start()) return results - def set_start(self, start: tuple[float, float]): + def set_starts(self, start: tuple[float, float]): start_points = [] for segment in self: start_points.append(segment.set_start(start)) return start_points - def get_end(self): + @property + def starts(self): + return self.get_starts() + + @starts.setter + def starts(self, start: tuple[float, float]): + self.set_starts(start) + + def get_ends(self): segment_list = CurveSegmentList() for segment in self: segment_list.append(segment.get_end()) return segment_list - def set_end(self, end: tuple[float, float]): + def set_ends(self, end: tuple[float, float]): results = [] for segment in self: results.append(segment.set_end(end)) return results - def get_control_point_1(self): + @property + def ends(self): + return self.get_ends() + + @ends.setter + def ends(self, end: tuple[float, float]): + self.set_ends(end) + + def get_control_point_1s(self): control_points = [] for segment in self: control_points.append(segment.get_control_point_1()) return control_points - def set_control_point_1(self, control_point_1: tuple[float, float]): + def set_control_point_1s(self, control_point_1: tuple[float, float]): results = [] for segment in self: results.append(segment.set_control_point_1(control_point_1)) return results - def get_control_point_2(self): + @property + def control_point_1s(self): + return self.get_control_point_1s() + + @control_point_1s.setter + def control_point_1s(self, control_point_1: tuple[float, float]): + self.set_control_point_1s(control_point_1) + + def get_control_point_2s(self): control_points = [] for segment in self: control_points.append(segment.get_control_point_2()) return control_points - def set_control_point_2(self, control_point_2: tuple[float, float]): + def set_control_point_2s(self, control_point_2: tuple[float, float]): results = [] for segment in self: results.append(segment.set_control_point_2(control_point_2)) return results - def move(self, delta: tuple[float, float]): + @property + def control_point_2s(self): + return self.get_control_point_2s() + + @control_point_2s.setter + def control_point_2s(self, control_point_2: tuple[float, float]): + self.set_control_point_2s(control_point_2) + + def move_by(self, delta: tuple[float, float]): results = [] for segment in self: - results.append(segment.move(delta)) + results.append(segment.move_by(delta)) return results - def move_start(self, delta: tuple[float, float]): + def move_starts_to(self, start: tuple[float, float]): + return self.set_starts(start) + + def move_starts_by(self, delta: tuple[float, float]): results = [] for segment in self: - results.append(segment.move_start(delta)) + results.append(segment.move_start_by(delta)) return results - def move_end(self, delta: tuple[float, float]): + def move_ends_to(self, end: tuple[float, float]): + return self.set_ends(end) + + def move_ends_by(self, delta: tuple[float, float]): results = [] for segment in self: - results.append(segment.move_end(delta)) + results.append(segment.move_end_by(delta)) return results - def __str__(self): + def get_info(self): result = [] for segment in self: result.append(str(segment)) return "\n\n".join(result) + @property + def info(self): + return self.get_info() + def __repr__(self): return f"CurveSegmentList({[repr(segment) for segment in self]})" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/label_list.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/label_list.py index afd507d..33ca54c 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/label_list.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/label_list.py @@ -3,48 +3,68 @@ class LabelList(list): def __init__(self, label_list=None): super().__init__(label_list or []) - def get_text(self): + def get_texts(self): texts = [] for label in self: texts.append(label.get_text()) return texts - def set_text(self, text: str): + def set_texts(self, text: str): results = [] for label in self: results.append(label.set_text(text)) return results - def get_position(self): + @property + def texts(self): + return self.get_texts() + + def get_positions(self): positions = [] for label in self: positions.append(label.get_position()) return positions - def set_position(self, position: tuple[float, float]): + def set_positions(self, position: tuple[float, float]): results = [] for label in self: results.append(label.set_position(position)) return results - def get_size(self): + @property + def positions(self): + return self.get_positions() + + @positions.setter + def positions(self, position: tuple[float, float]): + self.set_positions(position) + + def get_sizes(self): sizes = [] for label in self: sizes.append(label.get_size()) return sizes - def set_size(self, size: tuple[float, float]): + def set_sizes(self, size: tuple[float, float]): results = [] for label in self: results.append(label.set_size(size)) return results + @property + def sizes(self): + return self.get_sizes() + + @sizes.setter + def sizes(self, size: tuple[float, float]): + self.set_sizes(size) + def align_to_top(self): results = [] for label in self: @@ -94,48 +114,68 @@ def align_to_right(self): return results - def get_font_color(self): + def get_font_colors(self): font_colors = [] for label in self: font_colors.append(label.get_font_color()) return font_colors - def set_font_color(self, font_color: str): + def set_font_colors(self, font_color: str): results = [] for label in self: results.append(label.set_font_color(font_color)) return results - def get_font(self): + @property + def font_colors(self): + return self.get_font_colors() + + def get_fonts(self): fonts = [] for label in self: fonts.append(label.get_font()) return fonts - def set_font(self, font: str): + def set_fonts(self, font: str): results = [] for label in self: results.append(label.set_font(font)) return results - def get_font_size(self): + @property + def fonts(self): + return self.get_fonts() + + @fonts.setter + def fonts(self, font: str): + self.set_fonts(font) + + def get_font_sizes(self): font_sizes = [] for label in self: font_sizes.append(label.get_font_size()) return font_sizes - def set_font_size(self, font_size: float): + def set_font_sizes(self, font_size: float): results = [] for label in self: results.append(label.set_font_size(font_size)) return results + @property + def font_sizes(self): + return self.get_font_sizes() + + @font_sizes.setter + def font_sizes(self, font_size: float): + self.set_font_sizes(font_size) + def set_bold(self, bold: bool): results = [] for label in self: @@ -143,7 +183,7 @@ def set_bold(self, bold: bool): return results - def is_bold(self): + def are_bold(self): is_bold = [] for label in self: is_bold.append(label.is_bold()) @@ -157,18 +197,36 @@ def set_italic(self, italic: bool): return results - def is_italic(self): + def are_italic(self): is_italic = [] for label in self: is_italic.append(label.is_italic()) return is_italic - def __str__(self): + def move_to(self, position: tuple[float, float]): + results = [] + for label in self: + results.append(label.move_to(position)) + + return results + + def move_by(self, offset: tuple[float, float]): + results = [] + for label in self: + results.append(label.move_by(offset)) + + return results + + def get_info(self): result = [] for label in self: result.append(str(label)) return "\n".join(result) + @property + def info(self): + return self.get_info() + def __repr__(self): return f"LabelList({[repr(label) for label in self]})" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/reaction_center_list.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/reaction_center_list.py index 1b2009a..280e406 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/reaction_center_list.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/reaction_center_list.py @@ -9,14 +9,18 @@ def __init__(self, reaction_center_list=None, libsbmlnetwork=None): super().__init__(reaction_center_list or []) self.libsbmlnetwork = libsbmlnetwork - def get_reaction(self): + def get_reactions(self): reactions = ReactionList(libsbmlnetwork=self.libsbmlnetwork) for reaction_center in self: reactions.append(reaction_center.get_reaction()) return reactions - def switch_to_curve(self): + @property + def reactions(self): + return self.get_reactions() + + def switch_to_curves(self): results = [] for reaction_center in self: results.append(reaction_center.switch_to_curve()) @@ -30,27 +34,31 @@ def switch_to_shapes(self): return results - def is_curve(self): + def are_curves(self): results = [] for reaction_center in self: results.append(reaction_center.is_curve()) return results - def is_shapes(self): + def are_shapes(self): results = [] for reaction_center in self: results.append(reaction_center.is_shapes()) return results - def get_curve(self): + def get_curves(self): curve_list = CurveList(libsbmlnetwork=self.libsbmlnetwork) for reaction_center in self: curve_list.append(reaction_center.get_curve()) return curve_list + @property + def curves(self): + return self.get_curves() + def get_shapes_list(self): shapes_list = ShapeList() for reaction_center in self: @@ -58,18 +66,29 @@ def get_shapes_list(self): return shapes_list + def get_shapes(self): + return self.get_shapes_list() + + @property + def shapes(self): + return self.get_shapes_list() + def move(self, delta: tuple[float, float]): results = [] for reaction_center in self: - results.append(reaction_center.move(delta)) + results.append(reaction_center.move_by(delta)) return results - def __str__(self): + def get_info(self): result = [] for reaction_center in self: result.append(str(reaction_center)) return "\n".join(result) + @property + def info(self): + return self.get_info() + def __repr__(self): return f"ReactionCenterList({[repr(reaction_center) for reaction_center in self]})" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/shape_list.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/shape_list.py index b774c26..299fa6b 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/shape_list.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/network_elements/visual_elements/visual_element_lists/shape_list.py @@ -3,60 +3,92 @@ class ShapeList(list): def __init__(self, shape_list=None): super().__init__(shape_list or []) - def get_type(self): + def get_types(self): types = [] for shape in self: types.append(shape.get_type()) return types - def set_border_color(self, border_color: str): + @property + def types(self): + return self.get_types() + + def set_border_colors(self, border_color: str): results = [] for shape in self: results.append(shape.set_border_color(border_color)) return results - def get_border_color(self): + def get_border_colors(self): colors = [] for shape in self: colors.append(shape.get_border_color()) return colors - def set_border_thickness(self, thickness: float): + @property + def border_colors(self): + return self.get_border_colors() + + @border_colors.setter + def border_colors(self, border_color: str): + self.set_border_colors(border_color) + + def set_border_thicknesses(self, thickness: float): results = [] for shape in self: results.append(shape.set_border_thickness(thickness)) return results - def get_border_thickness(self): + def get_border_thicknesses(self): thicknesses = [] for shape in self: thicknesses.append(shape.get_border_thickness()) return thicknesses - def set_fill_color(self, fill_color: str or tuple or list): + @property + def border_thicknesses(self): + return self.get_border_thicknesses() + + @border_thicknesses.setter + def border_thicknesses(self, thickness: float): + self.set_border_thicknesses(thickness) + + def set_fill_colors(self, fill_color: str or tuple or list): results = [] for shape in self: results.append(shape.set_fill_color(fill_color)) return results - def get_fill_color(self): + def get_fill_colors(self): colors = [] for shape in self: colors.append(shape.get_fill_color()) return colors - def __str__(self): + @property + def fill_colors(self): + return self.get_fill_colors() + + @fill_colors.setter + def fill_colors(self, fill_color: str or tuple or list): + self.set_fill_colors(fill_color) + + def get_info(self): result = [] for shape in self: result.append(str(shape)) return "\n".join(result) + @property + def info(self): + return self.get_info() + def __repr__(self): return f"ShapeList({[repr(shape) for shape in self]})" diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/sbmlnetwork.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/sbmlnetwork.py index fd77c2f..48847fe 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/sbmlnetwork.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/sbmlnetwork.py @@ -4,6 +4,7 @@ from .settings import Settings from .network_elements import * from typing import Union +import warnings class SBMLNetwork(): @@ -17,12 +18,15 @@ def load(self, sbml: str): self.populate_settings() if self.libsbmlnetwork.getNumLayouts() == 0: self.auto_layout() - if self.libsbmlnetwork.getNumGlobalRenderInformation() == 0 and self.libsbmlnetwork.getNumLocalRenderInformation() == 0: + self.set_style("power") + elif self.libsbmlnetwork.getNumGlobalRenderInformation() == 0 and self.libsbmlnetwork.getNumLocalRenderInformation() == 0: self.auto_style() + self.set_style("power") return self def save(self, file_name: str = None): + self.update_network_extents() return self.libsbmlnetwork.save(file_name) def draw(self, file_name: str = None): @@ -32,6 +36,7 @@ def draw(self, file_name: str = None): :param file_directory: :param file_name: """ + self.update_network_extents() if file_name: networkinfotranslator.import_sbml_export_figure(self.libsbmlnetwork, file_name, self.settings.compartment_labels, @@ -46,12 +51,36 @@ def draw(self, file_name: str = None): def get_size(self): return self.libsbmlnetwork.getCanvasWidth(), self.libsbmlnetwork.getCanvasHeight() - def set_size(self, size: tuple[int, int]): + def set_size(self, size: tuple[int, int], adjust_elements: bool = True): + previous_size = self.get_size() if self.libsbmlnetwork.setCanvasWidth(size[0]) == 0 and self.libsbmlnetwork.setCanvasHeight(size[1]) == 0: + if adjust_elements: + if size[0] < previous_size[0] or size[1] < previous_size[1]: + self.auto_layout() + warnings.warn("Some elements were repositioned to fit the new canvas size.") + + compartment_list = self.get_compartments_list() + if len(compartment_list) == 1: + compartment_list[0].set_size(size) + + species_positions = self.get_species_list().get_positions() + for species_position in species_positions: + if species_position[0] > size[0] or species_position[1] > size[1]: + warnings.warn("Some Species with set positions are outside the canvas size. Please adjust their positions or use a larger canvas size.") + return True + warnings.warn("The canvas size could not be set properly. Please use a larger canvas size.") return False + @property + def size(self): + return self.get_size() + + @size.setter + def size(self, size: tuple[int, int]): + self.set_size(size) + def get_compartment(self, compartment_id: str = None): if compartment_id is None and self.libsbmlnetwork.getNumAllCompartmentGlyphs() == 1: compartment_id = self.libsbmlnetwork.getListOfCompartmentIds()[0] @@ -77,6 +106,24 @@ def get_compartments_list(self, compartment_ids = None): def get_compartment_ids(self): return self.libsbmlnetwork.getListOfCompartmentIds() + @property + def compartment(self): + return self.get_compartment() + + @property + def compartments(self): + return self.get_compartments_list() + + @property + def compartments_list(self): + return self.get_compartments_list() + + @property + def compartment_ids(self): + return self.get_compartment_ids() + + + def get_species(self, species_id: str): if self.libsbmlnetwork.getNumSpeciesGlyphs(species_id=species_id) > 0: return Species(self.libsbmlnetwork, species_id, 0) @@ -100,6 +147,18 @@ def get_species_list(self, species_ids = None): def get_species_ids(self): return self.libsbmlnetwork.getListOfSpeciesIds() + @property + def species(self): + return self.get_species_list() + + @property + def species_list(self): + return self.get_species_list() + + @property + def species_ids(self): + return self.get_species_ids() + def get_reaction(self, reaction_id: str): if self.libsbmlnetwork.getNumReactionGlyphs(reaction_id=reaction_id) > 0: return Reaction(self.libsbmlnetwork, reaction_id, 0) @@ -123,6 +182,18 @@ def get_reactions_list(self, reaction_ids = None): def get_reaction_ids(self): return self.libsbmlnetwork.getListOfReactionIds() + @property + def reactions(self): + return self.get_reactions_list() + + @property + def reactions_list(self): + return self.get_reactions_list() + + @property + def reaction_ids(self): + return self.get_reaction_ids() + def get_additional_elements(self): additional_elements = NetworkElementList(libsbmlnetwork=self.libsbmlnetwork) for graphical_object_index in range(self.libsbmlnetwork.getNumAllAdditionalGraphicalObjects()): @@ -130,6 +201,10 @@ def get_additional_elements(self): return additional_elements + @property + def additional_elements(self): + return self.get_additional_elements() + def add_additional_element(self, element_id: str, element_type: str = "rectangle", position: tuple[float, float] = (0, 0), size: tuple[float, float] = (100, 100)): if element_id is None: raise ValueError("Element id cannot be None") @@ -166,6 +241,13 @@ def remove_additional_element(self, element: Union[str, AdditionalElement]): # Todo: Implement get color options method + def has_color_bar(self): + for i in range(self.libsbmlnetwork.getNumAllAdditionalGraphicalObjects()): + if self.libsbmlnetwork.getAdditionalGraphicalObjectId(i) == "sbmlnetwork_color_bar": + return True + + return False + def add_color_bar(self): color_bar_id = "sbmlnetwork_color_bar" self.remove_additional_element(color_bar_id) @@ -179,15 +261,21 @@ def get_color_bar(self): if self.libsbmlnetwork.getAdditionalGraphicalObjectId(i) == "sbmlnetwork_color_bar": return ColorBar(self.libsbmlnetwork, self.libsbmlnetwork.getAdditionalGraphicalObjectId(i)) - return None + return self.add_color_bar() def remove_color_bar(self): - color_bar = self.get_color_bar() - if color_bar: - color_bar.clear_color_bar_space() + if self.has_color_bar(): + color_bar = self.get_color_bar() + if color_bar: + color_bar.clear_color_bar_space() - return self.remove_additional_element("sbmlnetwork_color_bar") + return self.remove_additional_element("sbmlnetwork_color_bar") + + return False + @property + def color_bar(self): + return self.get_color_bar() def get_background_color(self): return self.libsbmlnetwork.getBackgroundColor() @@ -198,6 +286,264 @@ def set_background_color(self, color: str): return False + def set_font_color(self, color: str): + self.get_compartments_list().set_font_color(color) + self.get_species_list().set_font_color(color) + self.get_reactions_list().set_font_color(color) + + def get_font_color(self): + all_font_colors = set() + compartments_font_colors = self.get_compartments_list().get_font_color() + for font_color in compartments_font_colors: + all_font_colors.add(font_color) + species_font_colors = self.get_species_list().get_font_color() + for font_color in species_font_colors: + all_font_colors.add(font_color) + reactions_font_colors = self.get_reactions_list().get_font_color() + for font_color in reactions_font_colors: + all_font_colors.add(font_color) + + if len(all_font_colors) > 1: + return None + + return all_font_colors.pop() + + @property + def font_color(self): + return self.get_font_color() + + @font_color.setter + def font_color(self, color: str): + self.set_font_color(color) + + def set_font(self, font: str): + self.get_compartments_list().set_font(font) + self.get_species_list().set_font(font) + self.get_reactions_list().set_font(font) + + def get_font(self): + all_fonts = set() + compartments_fonts = self.get_compartments_list().get_font() + for font in compartments_fonts: + all_fonts.add(font) + species_fonts = self.get_species_list().get_font() + for font in species_fonts: + all_fonts.add(font) + reactions_fonts = self.get_reactions_list().get_font() + for font in reactions_fonts: + all_fonts.add(font) + + if len(all_fonts) > 1: + return None + + return all_fonts.pop() + + @property + def font(self): + return self.get_font() + + @font.setter + def font(self, font: str): + self.set_font(font) + + def set_font_size(self, size: float): + self.get_compartments_list().set_font_size(size) + self.get_species_list().set_font_size(size) + self.get_reactions_list().set_font_size(size) + + def get_font_size(self): + all_font_sizes = set() + compartments_font_sizes = self.get_compartments_list().get_font_size() + for font_size in compartments_font_sizes: + all_font_sizes.add(font_size) + species_font_sizes = self.get_species_list().get_font_size() + for font_size in species_font_sizes: + all_font_sizes.add(font_size) + reactions_font_sizes = self.get_reactions_list().get_font_size() + for font_size in reactions_font_sizes: + all_font_sizes.add(font_size) + + if len(all_font_sizes) > 1: + return None + + return all_font_sizes.pop() + + @property + def font_size(self): + return self.get_font_size() + + @font_size.setter + def font_size(self, size: float): + self.set_font_size(size) + + def set_text_bold(self, bold: bool): + self.get_compartments_list().set_texts_bold(bold) + self.get_species_list().set_texts_bold(bold) + self.get_reactions_list().set_texts_bold(bold) + + def is_text_bold(self): + all_text_bold = set() + compartments_text_bold = self.get_compartments_list().are_texts_bold() + for text_bold in compartments_text_bold: + all_text_bold.add(text_bold) + species_text_bold = self.get_species_list().are_texts_bold() + for text_bold in species_text_bold: + all_text_bold.add(text_bold) + reactions_text_bold = self.get_reactions_list().are_texts_bold() + for text_bold in reactions_text_bold: + all_text_bold.add(text_bold) + + if len(all_text_bold) > 1: + return None + + return all_text_bold.pop() + + @property + def text_bold(self): + return self.is_text_bold() + + @text_bold.setter + def text_bold(self, bold: bool): + self.set_text_bold(bold) + + def set_text_italic(self, italic: bool): + self.get_compartments_list().set_texts_italic(italic) + self.get_species_list().set_texts_italic(italic) + self.get_reactions_list().set_texts_italic(italic) + + def is_text_italic(self): + all_text_italic = set() + compartments_text_italic = self.get_compartments_list().are_texts_italic() + for text_italic in compartments_text_italic: + all_text_italic.add(text_italic) + species_text_italic = self.get_species_list().are_texts_italic() + for text_italic in species_text_italic: + all_text_italic.add(text_italic) + reactions_text_italic = self.get_reactions_list().are_texts_italic() + for text_italic in reactions_text_italic: + all_text_italic.add(text_italic) + + if len(all_text_italic) > 1: + return None + + return all_text_italic.pop() + + @property + def text_italic(self): + return self.is_text_italic() + + @text_italic.setter + def text_italic(self, italic: bool): + self.set_text_italic(italic) + + @property + def background_color(self): + return self.get_background_color() + + @background_color.setter + def background_color(self, color: str): + self.set_background_color(color) + + def set_border_color(self, color: str): + self.get_compartments_list().set_border_colors(color) + self.get_species_list().set_border_colors(color) + self.get_reactions_list().set_border_colors(color) + + def get_border_color(self): + all_border_colors = set() + compartments_border_colors = self.get_compartments_list().get_border_colors() + for border_color in compartments_border_colors: + all_border_colors.add(border_color) + species_border_colors = self.get_species_list().get_border_colors() + for border_color in species_border_colors: + all_border_colors.add(border_color) + reactions_border_colors = self.get_reactions_list().get_border_colors() + for border_color in reactions_border_colors: + all_border_colors.add(border_color) + + if len(all_border_colors) > 1: + return None + + return all_border_colors.pop() + + @property + def border_color(self): + return self.get_border_color() + + @border_color.setter + def border_color(self, color: str): + self.set_border_color(color) + + def set_border_thickness(self, thickness: float): + self.get_compartments_list().set_border_thicknesses(thickness) + self.get_species_list().set_border_thicknesses(thickness) + self.get_reactions_list().set_border_thicknesses(thickness) + + def get_border_thickness(self): + all_border_thicknesses = set() + compartments_border_thicknesses = self.get_compartments_list().get_border_thicknesses() + for border_thickness in compartments_border_thicknesses: + all_border_thicknesses.add(border_thickness) + species_border_thicknesses = self.get_species_list().get_border_thicknesses() + for border_thickness in species_border_thicknesses: + all_border_thicknesses.add(border_thickness) + reactions_border_thicknesses = self.get_reactions_list().get_border_thicknesses() + for border_thickness in reactions_border_thicknesses: + all_border_thicknesses.add(border_thickness) + + if len(all_border_thicknesses) > 1: + return None + + return all_border_thicknesses.pop() + + @property + def border_thickness(self): + return self.get_border_thickness() + + @border_thickness.setter + def border_thickness(self, thickness: float): + self.set_border_thickness(thickness) + + def set_fill_color(self, color: str): + self.get_compartments_list().set_fill_color(color) + self.get_species_list().set_fill_color(color) + self.get_reactions_list().set_fill_color(color) + + def get_fill_color(self): + all_fill_colors = set() + compartments_fill_colors = self.get_compartments_list().get_fill_colors() + for fill_color in compartments_fill_colors: + all_fill_colors.add(fill_color) + species_fill_colors = self.get_species_list().get_fill_colors() + for fill_color in species_fill_colors: + all_fill_colors.add(fill_color) + reactions_fill_colors = self.get_reactions_list().get_fill_colors() + for fill_color in reactions_fill_colors: + all_fill_colors.add(fill_color) + + if len(all_fill_colors) > 1: + return None + + return all_fill_colors.pop() + + @property + def fill_color(self): + return self.get_fill_color() + + @fill_color.setter + def fill_color(self, color: str): + self.set_fill_color(color) + + def hide(self): + self.get_compartments_list().hide() + self.get_species_list().hide() + self.get_reactions_list().hide() + + def show(self): + self.get_compartments_list().show() + self.get_species_list().show() + self.get_reactions_list().show() + # Todo: Implement get_colors_list method to return all valid colors # def get_colors_list(self): # return self.libsbmlnetwork.getListOfColorIds() @@ -214,58 +560,80 @@ def set_style(self, style_name: str): return False + @property + def style(self): + return self.get_style() + + @style.setter + def style(self, style_name: str): + self.set_style(style_name) + def get_styles_options(self): return self.libsbmlnetwork.getListOfStyles() - # ToDo: Implement the following functions on the list of elements - # def show_compartment_labels(self): - # if self.libsbmlnetwork.enableDisplayCompartmentsTextLabel(True) == 0: - # return True - # - # return False - # - # def hide_compartment_labels(self): - # if self.libsbmlnetwork.enableDisplayCompartmentsTextLabel(False) == 0: - # return True - # - # return False - # - # def show_species_labels(self): - # if self.libsbmlnetwork.enableDisplaySpeciesTextLabel(True) == 0: - # return True - # - # return False - # - # def hide_species_labels(self): - # if self.libsbmlnetwork.enableDisplaySpeciesTextLabel(False) == 0: - # return True - # - # return False - # - # #ToDo: Check if it works properly - # def show_reaction_labels(self): - # if self.libsbmlnetwork.enableDisplayReactionsTextLabel(True) == 0: - # return True - # - # return False - # - # def hide_reaction_labels(self): - # if self.libsbmlnetwork.enableDisplayReactionsTextLabel(False) == 0: - # return True - # - # return False - # - # def show_id_as_label(self): - # if self.libsbmlnetwork.setUseNameAsTextLabel(False) == 0: - # return True - # - # return False - # - # def show_name_as_label(self): - # if self.libsbmlnetwork.setUseNameAsTextLabel(True) == 0: - # return True - # - # return False + def show_fluxes(self, simulation_end_time: float = 10, simulation_start_time: float = 0, + simulation_time_steps: int = 100, fluxes: dict = None): + if simulation_end_time <= simulation_start_time: + raise ValueError("Simulation end time must be greater than simulation start time") + if simulation_time_steps <= 0: + raise ValueError("Simulation time steps must be greater than 0") + if simulation_start_time < 0: + raise ValueError("Simulation start time cannot be negative") + + from .features.data_integration import Fluxes + + flux_object = Fluxes() + return flux_object.display(self, simulation_end_time, simulation_start_time, simulation_time_steps, fluxes) + + # ToDo: Implement the following functions on the list of elements + # def show_compartment_labels(self): + # if self.libsbmlnetwork.enableDisplayCompartmentsTextLabel(True) == 0: + # return True + # + # return False + # + # def hide_compartment_labels(self): + # if self.libsbmlnetwork.enableDisplayCompartmentsTextLabel(False) == 0: + # return True + # + # return False + # + # def show_species_labels(self): + # if self.libsbmlnetwork.enableDisplaySpeciesTextLabel(True) == 0: + # return True + # + # return False + # + # def hide_species_labels(self): + # if self.libsbmlnetwork.enableDisplaySpeciesTextLabel(False) == 0: + # return True + # + # return False + # + # #ToDo: Check if it works properly + # def show_reaction_labels(self): + # if self.libsbmlnetwork.enableDisplayReactionsTextLabel(True) == 0: + # return True + # + # return False + # + # def hide_reaction_labels(self): + # if self.libsbmlnetwork.enableDisplayReactionsTextLabel(False) == 0: + # return True + # + # return False + # + # def show_id_as_label(self): + # if self.libsbmlnetwork.setUseNameAsTextLabel(False) == 0: + # return True + # + # return False + # + # def show_name_as_label(self): + # if self.libsbmlnetwork.setUseNameAsTextLabel(True) == 0: + # return True + # + # return False def auto_layout(self, max_num_connected_edges: int = 3, reset_fixed_position_elements: bool = False, fixed_position_nodes: list = []): self.libsbmlnetwork.autolayout(max_num_connected_edges, reset_fixed_position_elements, fixed_position_nodes) @@ -276,6 +644,125 @@ def auto_style(self, max_num_connected_edges: int = 3): def update_reactions_curves(self): self.libsbmlnetwork.updateReactionCurves() + def update_network_extents(self): + current_width = 0 + current_height = 0 + + # compartments + for compartment_index in range(self.libsbmlnetwork.getNumCompartments()): + compartment_id = self.libsbmlnetwork.getCompartmentId(index=compartment_index) + for compartment_glyph_index in range(self.libsbmlnetwork.getNumCompartmentGlyphs(compartment_id=compartment_id)): + x = self.libsbmlnetwork.getX(id=compartment_id, graphical_object_index=compartment_glyph_index) + y = self.libsbmlnetwork.getY(id=compartment_id, graphical_object_index=compartment_glyph_index) + width = self.libsbmlnetwork.getWidth(id=compartment_id, graphical_object_index=compartment_glyph_index) + height = self.libsbmlnetwork.getHeight(id=compartment_id, graphical_object_index=compartment_glyph_index) + if x + width > current_width: + current_width = x + width + if y + height > current_height: + current_height = y + height + # text glyphs + for text_glyph_index in range(self.libsbmlnetwork.getNumTextGlyphs(id=compartment_id, graphical_object_index=compartment_glyph_index)): + x = self.libsbmlnetwork.getTextX(id=compartment_id, graphical_object_index=compartment_glyph_index, text_glyph_index=text_glyph_index) + y = self.libsbmlnetwork.getTextY(id=compartment_id, graphical_object_index=compartment_glyph_index, text_glyph_index=text_glyph_index) + if x > current_width: + current_width = x + if y > current_height: + current_height = y + + # species + for species_index in range(self.libsbmlnetwork.getNumSpecies()): + species_id = self.libsbmlnetwork.getSpeciesId(index=species_index) + for species_glyph_index in range(self.libsbmlnetwork.getNumSpeciesGlyphs(species_id=species_id)): + x = self.libsbmlnetwork.getX(id=species_id, graphical_object_index=species_glyph_index) + y = self.libsbmlnetwork.getY(id=species_id, graphical_object_index=species_glyph_index) + width = self.libsbmlnetwork.getWidth(id=species_id, graphical_object_index=species_glyph_index) + height = self.libsbmlnetwork.getHeight(id=species_id, graphical_object_index=species_glyph_index) + if x + width > current_width: + current_width = x + width + if y + height > current_height: + current_height = y + height + # text glyphs + for text_glyph_index in range(self.libsbmlnetwork.getNumTextGlyphs(id=species_id, graphical_object_index=species_glyph_index)): + x = self.libsbmlnetwork.getTextX(id=species_id, graphical_object_index=species_glyph_index, text_glyph_index=text_glyph_index) + y = self.libsbmlnetwork.getTextY(id=species_id, graphical_object_index=species_glyph_index, text_glyph_index=text_glyph_index) + if x > current_width: + current_width = x + if y > current_height: + current_height = y + + # reactions + for reaction_index in range(self.libsbmlnetwork.getNumReactions()): + reaction_id = self.libsbmlnetwork.getReactionId(index=reaction_index) + for reaction_glyph_index in range(self.libsbmlnetwork.getNumReactionGlyphs(reaction_id=reaction_id)): + x = self.libsbmlnetwork.getX(id=reaction_id, graphical_object_index=reaction_glyph_index) + y = self.libsbmlnetwork.getY(id=reaction_id, graphical_object_index=reaction_glyph_index) + width = self.libsbmlnetwork.getWidth(id=reaction_id, graphical_object_index=reaction_glyph_index) + height = self.libsbmlnetwork.getHeight(id=reaction_id, graphical_object_index=reaction_glyph_index) + if x + width > current_width: + current_width = x + width + if y + height > current_height: + current_height = y + height + # text glyphs + for text_glyph_index in range(self.libsbmlnetwork.getNumTextGlyphs(id=reaction_id, graphical_object_index=reaction_glyph_index)): + x = self.libsbmlnetwork.getTextX(id=reaction_id, graphical_object_index=reaction_glyph_index, text_glyph_index=text_glyph_index) + y = self.libsbmlnetwork.getTextY(id=reaction_id, graphical_object_index=reaction_glyph_index, text_glyph_index=text_glyph_index) + if x > current_width: + current_width = x + if y > current_height: + current_height = y + # empty species + for species_reference_index in range(self.libsbmlnetwork.getNumSpeciesReferences(reaction_id=reaction_id, reaction_glyph_index=reaction_glyph_index)): + if self.libsbmlnetwork.isSetSpeciesReferenceEmptySpeciesGlyph(reaction_id=reaction_id, reaction_glyph_index=reaction_glyph_index, species_reference_index=species_reference_index): + empty_species_id = self.libsbmlnetwork.getSpeciesReferenceEmptySpeciesGlyphId(reaction_id=reaction_id, reaction_glyph_index=reaction_glyph_index, species_reference_index=species_reference_index) + x = self.libsbmlnetwork.getX(id=empty_species_id, graphical_object_index=0) + y = self.libsbmlnetwork.getY(id=empty_species_id, graphical_object_index=0) + width = self.libsbmlnetwork.getWidth(id=empty_species_id, graphical_object_index=0) + height = self.libsbmlnetwork.getHeight(id=empty_species_id, graphical_object_index=0) + if x + width > current_width: + current_width = x + width + if y + height > current_height: + current_height = y + height + + # additional graphical objects + for graphical_object_index in range(self.libsbmlnetwork.getNumAllAdditionalGraphicalObjects()): + graphical_object_id = self.libsbmlnetwork.getAdditionalGraphicalObjectId(additional_graphical_object_index=graphical_object_index) + x = self.libsbmlnetwork.getX(id=graphical_object_id, graphical_object_index=0) + y = self.libsbmlnetwork.getY(id=graphical_object_id, graphical_object_index=0) + width = self.libsbmlnetwork.getWidth(id=graphical_object_id, graphical_object_index=0) + height = self.libsbmlnetwork.getHeight(id=graphical_object_id, graphical_object_index=0) + if x + width > current_width: + current_width = x + width + if y + height > current_height: + current_height = y + height + + # text glyphs + for text_glyph_index in range(self.libsbmlnetwork.getNumTextGlyphs(id=graphical_object_id, graphical_object_index=0)): + x = self.libsbmlnetwork.getTextX(id=graphical_object_id, graphical_object_index=0, text_glyph_index=text_glyph_index) + y = self.libsbmlnetwork.getTextY(id=graphical_object_id, graphical_object_index=0, text_glyph_index=text_glyph_index) + if x > current_width: + current_width = x + if y > current_height: + current_height = y + + if self.has_color_bar(): + color_bar_extra_margin = 20 + current_width += color_bar_extra_margin + + padding = 20 + current_width += padding + current_height += padding + + if self.libsbmlnetwork.getNumAllCompartmentGlyphs() == 1: + sole_compartment = self.get_compartment() + if self.has_color_bar(): + color_bar = self.get_color_bar() + sole_compartment.set_size((current_width - color_bar.get_size()[0] - color_bar.get_left_margin() - color_bar.get_right_margin(), current_height)) + else: + sole_compartment.set_size((current_width, current_height)) + + self.libsbmlnetwork.setCanvasWidth(current_width) + self.libsbmlnetwork.setCanvasHeight(current_height) + def get_settings(self): return self.settings @@ -294,8 +781,14 @@ def populate_settings(self): def get_version(self): return self.libsbmlnetwork.getVersion() + @property + def version(self): + return self.get_version() + + instance = SBMLNetwork() + def load(sbml: str): """ Loads the SBML model. diff --git a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/settings.py b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/settings.py index 0768065..75fe664 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/settings.py +++ b/src/bindings/python/ctypes/sbmlnetwork/src/sbmlnetwork/settings.py @@ -4,7 +4,7 @@ def __init__(self, libsbmlnetwork): self.libsbmlnetwork = libsbmlnetwork self.compartment_labels = True self.species_labels = True - self.reaction_labels = False + self.reaction_labels = True self.label = "name" self.stoichiometric_curves = True diff --git a/src/bindings/python/ctypes/sbmlnetwork/tests/test_user_interface_layer.py b/src/bindings/python/ctypes/sbmlnetwork/tests/test_user_interface_layer.py index 305ea9e..6348b8f 100644 --- a/src/bindings/python/ctypes/sbmlnetwork/tests/test_user_interface_layer.py +++ b/src/bindings/python/ctypes/sbmlnetwork/tests/test_user_interface_layer.py @@ -151,10 +151,10 @@ def test_network_element_size(self): network = sbmlnetwork.load(self.r.getSBML()) species = network.get_species_list()[0] size = species.get_size() - species.set_size((size[0] + 100, size[1] + 100)) + species.set_size((size[0] + 10, size[1] + 10)) new_size = species.get_size() - self.assertAlmostEqual(size[0] + 100, new_size[0], delta=0.1) - self.assertAlmostEqual(size[1] + 100, new_size[1], delta=0.1) + self.assertAlmostEqual(size[0] + 10, new_size[0], delta=0.1) + self.assertAlmostEqual(size[1] + 10, new_size[1], delta=0.1) def test_network_element_label(self): """ Test if the network element label can be added and removed """ @@ -177,7 +177,7 @@ def test_network_element_shapes(self): self.assertEqual(shape.get_type(), "ellipse") species.remove_shape(shape) self.assertEqual(len(species.get_shapes_list()), 1) - shape_options = species.get_shapes_options() + shape_options = species.get_shape_options() species.set_shape(shape_options[-1]) shape = species.get_shapes_list()[-1] self.assertEqual(shape.get_type(), shape_options[-1]) @@ -191,15 +191,6 @@ def test_network_element_hide_show(self): species.show() self.assertFalse(species.is_hidden()) - def test_network_element_meta_data(self): - """ Test if the network element meta data can be added and removed """ - network = sbmlnetwork.load(self.r.getSBML()) - species = network.get_species_list()[0] - species.add_meta_data("test_key", "test_value") - self.assertEqual(species.get_meta_data("test_key"), "test_value") - species.remove_meta_data("test_key") - self.assertEqual(species.get_meta_data("test_key"), None) - def test_compartment_species_list(self): """ Test if the species list in compartment can be retrieved """ network = sbmlnetwork.load(self.r.getSBML()) @@ -242,7 +233,7 @@ def test_species_roles(self): for species in species_list: reaction_list = species.get_reactions_list() for reaction in reaction_list: - self.assertTrue(species.get_role(reaction) in species.get_roles_options()) + self.assertTrue(species.get_role(reaction) in species.get_role_options()) def test_species_alias(self): """ Test if the alias in species can be created """ @@ -268,7 +259,7 @@ def test_species_move(self): position = species.get_position() connected_curve = species.get_connected_curves()[0] connected_curve_end = connected_curve.get_end() - species.move((50, 50), move_connected_curves=True) + species.move_by((50, 50), move_connected_curves=True) new_position = species.get_position() new_connected_curve_end = connected_curve.get_end() self.assertAlmostEqual(position[0] + 50, new_position[0], delta=0.1) @@ -277,7 +268,7 @@ def test_species_move(self): self.assertAlmostEqual(connected_curve_end[1] + 50, new_connected_curve_end[1], delta=0.1) position = species.get_position() connected_curve_end = connected_curve.get_end() - species.move((50, 50), move_connected_curves=False) + species.move_by((50, 50), move_connected_curves=False) new_position = species.get_position() new_connected_curve_end = connected_curve.get_end() self.assertAlmostEqual(position[0] + 50, new_position[0], delta=0.1) @@ -288,7 +279,7 @@ def test_species_move(self): position = species.get_position() connected_curve = species.get_connected_curves()[0] connected_curve_start = connected_curve.get_start() - species.move((50, 50), move_connected_curves=True) + species.move_by((50, 50), move_connected_curves=True) new_position = species.get_position() new_connected_curve_start = connected_curve.get_start() self.assertAlmostEqual(position[0] + 50, new_position[0], delta=0.1) @@ -297,7 +288,7 @@ def test_species_move(self): self.assertAlmostEqual(connected_curve_start[1] + 50, new_connected_curve_start[1], delta=0.1) position = species.get_position() connected_curve_start = connected_curve.get_start() - species.move((50, 50), move_connected_curves=False) + species.move_by((50, 50), move_connected_curves=False) new_position = species.get_position() new_connected_curve_start = connected_curve.get_start() self.assertAlmostEqual(position[0] + 50, new_position[0], delta=0.1) @@ -393,6 +384,8 @@ def test_label_position(self): new_position = label.get_position() self.assertAlmostEqual(position[0] + 100, new_position[0], delta=0.1) + #ToDo: add test for directly setting the position of the label + def test_label_size(self): """ Test if the label size can be set """ network = sbmlnetwork.load(self.r.getSBML()) @@ -403,6 +396,8 @@ def test_label_size(self): new_size = label.get_size() self.assertEqual((size[0] + 100, size[1] + 100), new_size) + #ToDo: add test for directly setting the size of the label + def test_label_align(self): """ Test if the label can be aligned """ network = sbmlnetwork.load(self.r.getSBML()) @@ -430,6 +425,8 @@ def test_label_text(self): label = species.get_labels_list()[-1] label.set_text("test_label") self.assertEqual(label.get_text(), "test_label") + species.set_text("test_species") + self.assertEqual(species.get_text(), "test_species") def test_label_font(self): """ Test if the label font can be set """ @@ -448,6 +445,18 @@ def test_label_font(self): self.assertEqual(label.is_italic(), False) label.set_italic(True) self.assertEqual(label.is_italic(), True) + species.set_font("Monospace") + self.assertEqual(species.get_font(), "Monospace") + species.set_font_size(10) + self.assertEqual(species.get_font_size(), 10) + species.set_font_color("blue") + self.assertEqual(species.get_font_color(), "blue") + self.assertEqual(species.is_text_bold(), True) + species.set_text_bold(False) + self.assertEqual(species.is_text_bold(), False) + self.assertEqual(species.is_text_italic(), True) + species.set_text_italic(False) + self.assertEqual(species.is_text_italic(), False) def test_reaction_center_reaction(self): """ Test if the reaction center can be retrieved """ @@ -468,6 +477,11 @@ def test_reaction_center_switch(self): self.assertTrue(center.is_shapes()) center.switch_to_curve() self.assertTrue(center.is_curve()) + self.assertTrue(reaction.is_center_curve()) + self.assertFalse(reaction.is_center_shapes()) + reaction.switch_center_to_shapes() + self.assertTrue(reaction.is_center_shapes()) + self.assertFalse(reaction.is_center_curve()) def test_reaction_center_curve(self): """ Test if the reaction center curve can be retrieved """ @@ -478,6 +492,11 @@ def test_reaction_center_curve(self): center.switch_to_curve() curve = center.get_curve() self.assertTrue(curve.get_reaction().get_reaction_id() == reaction.get_reaction_id()) + center.switch_to_shapes() + reaction.switch_center_to_curve() + self.assertTrue(reaction.get_center_curve().get_reaction().get_reaction_id() == reaction.get_reaction_id()) + reaction.switch_to_curve() + self.assertTrue(reaction.get_center_curve().get_reaction().get_reaction_id() == reaction.get_reaction_id()) def test_reaction_center_shapes(self): """ Test if the reaction center shapes can be retrieved """ @@ -490,20 +509,25 @@ def test_reaction_center_shapes(self): for shape in shapes_list: self.assertEqual(shape.get_element_id(), reaction.get_reaction_id()) self.assertEqual(shape.get_graphical_object_index(), reaction.get_graphical_object_index()) + center.switch_to_curve() + reaction.switch_center_to_shapes() + for shape in reaction.get_center_shapes_list(): + self.assertEqual(shape.get_element_id(), reaction.get_reaction_id()) + self.assertEqual(shape.get_graphical_object_index(), reaction.get_graphical_object_index()) def test_reaction_move(self): """ Test if the reaction can be moved """ network = sbmlnetwork.load(self.r.getSBML()) reaction = network.get_reactions_list()[0] position = reaction.get_position() - connected_species_positions = reaction.get_species_list().get_position() - connected_curves_start = reaction.get_curves_list().get_start() - connected_curves_end = reaction.get_curves_list().get_end() - reaction.move((50, 50), move_connected_species=True) + connected_species_positions = reaction.get_species_list().get_positions() + connected_curves_start = reaction.get_curves_list().get_starts() + connected_curves_end = reaction.get_curves_list().get_ends() + reaction.move_by((50, 50), move_connected_species=True) new_position = reaction.get_position() - new_connected_species_positions = reaction.get_species_list().get_position() - new_connected_curves_start = reaction.get_curves_list().get_start() - new_connected_curves_end = reaction.get_curves_list().get_end() + new_connected_species_positions = reaction.get_species_list().get_positions() + new_connected_curves_start = reaction.get_curves_list().get_starts() + new_connected_curves_end = reaction.get_curves_list().get_ends() self.assertAlmostEqual(position[0] + 50, new_position[0], delta=0.1) self.assertAlmostEqual(position[1] + 50, new_position[1], delta=0.1) for i in range(len(connected_species_positions)): @@ -516,14 +540,14 @@ def test_reaction_move(self): self.assertAlmostEqual(connected_curves_end[i][0] + 50, new_connected_curves_end[i][0], delta=0.1) self.assertAlmostEqual(connected_curves_end[i][1] + 50, new_connected_curves_end[i][1], delta=0.1) position = reaction.get_position() - connected_species_positions = reaction.get_species_list().get_position() - connected_curves_start = reaction.get_curves_list().get_start() - connected_curves_end = reaction.get_curves_list().get_end() - reaction.move((50, 50), move_connected_species=False) + connected_species_positions = reaction.get_species_list().get_positions() + connected_curves_start = reaction.get_curves_list().get_starts() + connected_curves_end = reaction.get_curves_list().get_ends() + reaction.move_by((50, 50), move_connected_species=False) new_position = reaction.get_position() - new_connected_species_positions = reaction.get_species_list().get_position() - new_connected_curves_start = reaction.get_curves_list().get_start() - new_connected_curves_end = reaction.get_curves_list().get_end() + new_connected_species_positions = reaction.get_species_list().get_positions() + new_connected_curves_start = reaction.get_curves_list().get_starts() + new_connected_curves_end = reaction.get_curves_list().get_ends() self.assertAlmostEqual(position[0] + 50, new_position[0], delta=0.1) self.assertAlmostEqual(position[1] + 50, new_position[1], delta=0.1) for i in range(len(connected_species_positions)): @@ -562,7 +586,7 @@ def test_curve_role(self): for reaction in reaction_list: curves_list = reaction.get_curves_list() for curve in curves_list: - self.assertTrue(curve.get_role() in curve.get_roles_options()) + self.assertTrue(curve.get_role() in curve.get_role_options()) def test_curve_color(self): """ Test if the curve color can be set """ @@ -573,6 +597,11 @@ def test_curve_color(self): for curve in curves_list: curve.set_color("red") self.assertEqual(curve.get_color(), "red") + for reaction in reaction_list: + reaction.set_curve_colors("blue") + curves_list = reaction.get_curves_list() + for curve in curves_list: + self.assertEqual(curve.get_color(), "blue") def test_curve_thickness(self): """ Test if the curve thickness can be set """ @@ -583,6 +612,11 @@ def test_curve_thickness(self): for curve in curves_list: curve.set_thickness(10) self.assertEqual(curve.get_thickness(), 10) + for reaction in reaction_list: + reaction.set_curve_thicknesses(20) + curves_list = reaction.get_curves_list() + for curve in curves_list: + self.assertEqual(curve.get_thickness(), 20) def test_curve_start_end(self): """ Test if the curve start and end can be set """ @@ -610,8 +644,8 @@ def test_curve_segment(self): self.assertEqual(len(curve.get_segments_list()), 1) self.assertEqual(curve.get_segment().get_start(), (100, 100)) self.assertEqual(curve.get_segment().get_end(), (200, 200)) - self.assertEqual(curve.get_start_slope(), (150 - 100) / (150 - 100)) - self.assertEqual(curve.get_end_slope(), (250 - 200) / (250 - 200)) + self.assertEqual(curve.get_start_slope(), math.atan2(150 - 100, 150 - 100)) + self.assertEqual(curve.get_end_slope(), math.atan2(250 - 200, 250 - 200)) def test_curve_move(self): """ Test if the curve can be moved """ @@ -620,7 +654,7 @@ def test_curve_move(self): curve = reaction.get_curves_list()[0] start_position = curve.get_start() end_position = curve.get_end() - curve.move((50, 50)) + curve.move_by((50, 50)) new_start_position = curve.get_start() new_end_position = curve.get_end() self.assertAlmostEqual(start_position[0] + 50, new_start_position[0], delta=0.1) @@ -629,20 +663,14 @@ def test_curve_move(self): self.assertAlmostEqual(end_position[1] + 50, new_end_position[1], delta=0.1) start_position = curve.get_start() end_position = curve.get_end() - curve.move_start((50, 50)) + curve.move_start_by((50, 50)) new_start_position = curve.get_start() new_end_position = curve.get_end() self.assertAlmostEqual(start_position[0] + 50, new_start_position[0], delta=0.1) self.assertAlmostEqual(start_position[1] + 50, new_start_position[1], delta=0.1) - self.assertAlmostEqual(end_position[0], new_end_position[0], delta=0.1) - self.assertAlmostEqual(end_position[1], new_end_position[1], delta=0.1) - start_position = curve.get_start() end_position = curve.get_end() - curve.move_end((50, 50)) - new_start_position = curve.get_start() + curve.move_end_by((50, 50)) new_end_position = curve.get_end() - self.assertAlmostEqual(start_position[0], new_start_position[0], delta=0.1) - self.assertAlmostEqual(start_position[1], new_start_position[1], delta=0.1) self.assertAlmostEqual(end_position[0] + 50, new_end_position[0], delta=0.1) self.assertAlmostEqual(end_position[1] + 50, new_end_position[1], delta=0.1) curve.add_segment((100, 100), (200, 200), (150, 150), (250, 250)) @@ -655,7 +683,7 @@ def test_curve_move(self): second_segment_end_position = curve.get_segments_list()[1].get_end() second_segment_control_point_1_position = curve.get_segments_list()[1].get_control_point_1() second_segment_control_point_2_position = curve.get_segments_list()[1].get_control_point_2() - curve.move((50, 50)) + curve.move_by((50, 50)) new_first_segment_start_position = curve.get_segments_list()[0].get_start() new_first_segment_end_position = curve.get_segments_list()[0].get_end() new_first_segment_control_point_1_position = curve.get_segments_list()[0].get_control_point_1() @@ -681,6 +709,22 @@ def test_curve_move(self): self.assertAlmostEqual(second_segment_control_point_2_position[0] + 50, new_second_segment_control_point_2_position[0], delta=0.1) self.assertAlmostEqual(second_segment_control_point_2_position[1] + 50, new_second_segment_control_point_2_position[1], delta=0.1) + def test_curve_hide_show(self): + """ Test if the curve can be hidden and shown """ + network = sbmlnetwork.load(self.r.getSBML()) + reaction = network.get_reactions_list()[0] + curve = reaction.get_curves_list()[0] + curve.hide() + self.assertTrue(curve.is_hidden()) + curve.show() + self.assertFalse(curve.is_hidden()) + reaction.hide() + for curve in reaction.get_curves_list(): + self.assertTrue(curve.is_hidden()) + reaction.show() + for curve in reaction.get_curves_list(): + self.assertFalse(curve.is_hidden()) + def test_curve_segment_points(self): """ Test if the curve segment start and end can be set """ network = sbmlnetwork.load(self.r.getSBML()) @@ -708,7 +752,7 @@ def test_curve_segment_move(self): end_position = segment.get_end() control_point_1_position = segment.get_control_point_1() control_point_2_position = segment.get_control_point_2() - segment.move((50, 50)) + segment.move_by((50, 50)) new_start_position = segment.get_start() new_end_position = segment.get_end() new_control_point_1_position = segment.get_control_point_1() @@ -725,7 +769,7 @@ def test_curve_segment_move(self): end_position = segment.get_end() control_point_1_position = segment.get_control_point_1() control_point_2_position = segment.get_control_point_2() - segment.move_start((50, 50)) + segment.move_start_by((50, 50)) new_start_position = segment.get_start() new_end_position = segment.get_end() new_control_point_1_position = segment.get_control_point_1() @@ -742,7 +786,7 @@ def test_curve_segment_move(self): end_position = segment.get_end() control_point_1_position = segment.get_control_point_1() control_point_2_position = segment.get_control_point_2() - segment.move_end((50, 50)) + segment.move_end_by((50, 50)) new_start_position = segment.get_start() new_end_position = segment.get_end() new_control_point_1_position = segment.get_control_point_1() @@ -770,6 +814,13 @@ def test_arrow_head_relative_position(self): new_position = arrow_head.get_relative_position() self.assertAlmostEqual(position[0] + 100, new_position[0], delta=0.1) self.assertAlmostEqual(position[1] + 100, new_position[1], delta=0.1) + for reaction in reaction_list: + reaction.set_arrow_head_relative_positions((15, 15)) + curves_list = reaction.get_curves_list() + for curve in curves_list: + arrow_head = curve.get_arrow_head() + if arrow_head: + self.assertEqual(arrow_head.get_relative_position(), (15, 15)) def test_arrow_head_size(self): """ Test if the arrow head size can be set """ @@ -784,6 +835,13 @@ def test_arrow_head_size(self): arrow_head.set_size((size[0] + 100, size[1] + 100)) new_size = arrow_head.get_size() self.assertEqual((size[0] + 100, size[1] + 100), new_size) + for reaction in reaction_list: + reaction.set_arrow_head_sizes((15, 15)) + curves_list = reaction.get_curves_list() + for curve in curves_list: + arrow_head = curve.get_arrow_head() + if arrow_head: + self.assertEqual(arrow_head.get_size(), (15, 15)) def test_arrow_head_shape(self): """ Test if the arrow head shape can be set """ @@ -812,15 +870,15 @@ def test_network_element_list_id(self): network = sbmlnetwork.load(self.r.getSBML()) compartment_list = network.get_compartments_list() compartment_ids1 = [compartment.get_id() for compartment in compartment_list] - compartment_ids2 = compartment_list.get_id() + compartment_ids2 = compartment_list.get_ids() self.assertEqual(compartment_ids1, compartment_ids2) species_list = network.get_species_list() - speices_ids1 = [species.get_id() for species in species_list] - speices_ids2 = species_list.get_id() - self.assertEqual(speices_ids1, speices_ids2) + species_ids1 = [species.get_id() for species in species_list] + species_ids2 = species_list.get_ids() + self.assertEqual(species_ids1, species_ids2) reaction_list = network.get_reactions_list() reaction_ids1 = [reaction.get_id() for reaction in reaction_list] - reaction_ids2 = reaction_list.get_id() + reaction_ids2 = reaction_list.get_ids() self.assertEqual(reaction_ids1, reaction_ids2) def test_network_element_list_graphical_object_index(self): @@ -828,15 +886,15 @@ def test_network_element_list_graphical_object_index(self): network = sbmlnetwork.load(self.r.getSBML()) compartment_list = network.get_compartments_list() compartment_graphical_object_index1 = [compartment.get_graphical_object_index() for compartment in compartment_list] - compartment_graphical_object_index2 = compartment_list.get_graphical_object_index() + compartment_graphical_object_index2 = compartment_list.get_graphical_object_indices() self.assertEqual(compartment_graphical_object_index1, compartment_graphical_object_index2) species_list = network.get_species_list() species_graphical_object_index1 = [species.get_graphical_object_index() for species in species_list] - species_graphical_object_index2 = species_list.get_graphical_object_index() + species_graphical_object_index2 = species_list.get_graphical_object_indices() self.assertEqual(species_graphical_object_index1, species_graphical_object_index2) reaction_list = network.get_reactions_list() reaction_graphical_object_index1 = [reaction.get_graphical_object_index() for reaction in reaction_list] - reaction_graphical_object_index2 = reaction_list.get_graphical_object_index() + reaction_graphical_object_index2 = reaction_list.get_graphical_object_indices() self.assertEqual(reaction_graphical_object_index1, reaction_graphical_object_index2) def test_network_element_list_position(self): @@ -844,23 +902,23 @@ def test_network_element_list_position(self): network = sbmlnetwork.load(self.r.getSBML()) compartment_list = network.get_compartments_list() compartment_position1 = [compartment.get_position() for compartment in compartment_list] - compartment_position2 = compartment_list.get_position() + compartment_position2 = compartment_list.get_positions() self.assertEqual(compartment_position1, compartment_position2) - compartment_list.set_position((100, 100)) + compartment_list.set_positions((100, 100)) for compartment in compartment_list: self.assertEqual(compartment.get_position(), (100, 100)) species_list = network.get_species_list() species_position1 = [species.get_position() for species in species_list] - species_position2 = species_list.get_position() + species_position2 = species_list.get_positions() self.assertEqual(species_position1, species_position2) - species_list.set_position((100, 100)) + species_list.set_positions((100, 100)) for species in species_list: self.assertEqual(species.get_position(), (100, 100)) reaction_list = network.get_reactions_list() reaction_position1 = [reaction.get_position() for reaction in reaction_list] - reaction_position2 = reaction_list.get_position() + reaction_position2 = reaction_list.get_positions() self.assertEqual(reaction_position1, reaction_position2) - reaction_list.set_position((100, 100)) + reaction_list.set_positions((100, 100)) for reaction in reaction_list: self.assertEqual(reaction.get_position(), (100, 100)) @@ -869,23 +927,23 @@ def test_network_element_list_size(self): network = sbmlnetwork.load(self.r.getSBML()) compartment_list = network.get_compartments_list() compartment_size1 = [compartment.get_size() for compartment in compartment_list] - compartment_size2 = compartment_list.get_size() + compartment_size2 = compartment_list.get_sizes() self.assertEqual(compartment_size1, compartment_size2) - compartment_list.set_size((100, 100)) + compartment_list.set_sizes((100, 100)) for compartment in compartment_list: self.assertEqual(compartment.get_size(), (100, 100)) species_list = network.get_species_list() species_size1 = [species.get_size() for species in species_list] - species_size2 = species_list.get_size() + species_size2 = species_list.get_sizes() self.assertEqual(species_size1, species_size2) - species_list.set_size((100, 100)) + species_list.set_sizes((20, 20)) for species in species_list: - self.assertEqual(species.get_size(), (100, 100)) + self.assertEqual(species.get_size(), (20, 20)) reaction_list = network.get_reactions_list() reaction_size1 = [reaction.get_size() for reaction in reaction_list] - reaction_size2 = reaction_list.get_size() + reaction_size2 = reaction_list.get_sizes() self.assertEqual(reaction_size1, reaction_size2) - reaction_list.set_size((100, 100)) + reaction_list.set_sizes((100, 100)) for reaction in reaction_list: self.assertEqual(reaction.get_size(), (100, 100)) @@ -893,26 +951,26 @@ def test_network_element_list_label(self): """ Test if the label can be added and removed from the network element list """ network = sbmlnetwork.load(self.r.getSBML()) compartment_list = network.get_compartments_list() - label = compartment_list.add_label("test_label") + label = compartment_list.add_labels("test_label") for compartment in compartment_list: self.assertEqual(compartment.get_labels_list()[-1].get_text(), "test_label") - compartment_list.remove_label("test_label") + compartment_list.remove_labels("test_label") for compartment in compartment_list: self.assertEqual(compartment.get_labels_list()[-1].get_text(), compartment.get_compartment_id()) self.assertEqual(len(compartment.get_labels_list()), 1) species_list = network.get_species_list() - species_list.add_label("test_label") + species_list.add_labels("test_label") for species in species_list: self.assertEqual(species.get_labels_list()[-1].get_text(), "test_label") - species_list.remove_label("test_label") + species_list.remove_labels("test_label") for species in species_list: self.assertEqual(species.get_label().get_text(), species.get_species_id()) self.assertEqual(len(species.get_labels_list()), 1) reaction_list = network.get_reactions_list() - reaction_list.add_label("test_label") + reaction_list.add_labels("test_label") for reaction in reaction_list: self.assertEqual(reaction.get_labels_list()[-1].get_text(), "test_label") - reaction_list.remove_label("test_label") + reaction_list.remove_labels("test_label") for reaction in reaction_list: self.assertEqual(reaction.get_label().get_text(), reaction.get_reaction_id()) self.assertEqual(len(reaction.get_labels_list()), 1) @@ -921,18 +979,18 @@ def test_network_element_list_shape(self): """ Test if the shape can be added and removed from the network element list """ network = sbmlnetwork.load(self.r.getSBML()) compartment_list = network.get_compartments_list() - compartment_list.add_shape("ellipse") + compartment_list.add_shapes("ellipse") for compartment in compartment_list: self.assertEqual(compartment.get_shapes_list()[-1].get_type(), "ellipse") - compartment_list.remove_shape(compartment_list.get_shapes_list()[-1]) + compartment_list.remove_shapes(compartment_list.get_shapes_list()[-1]) for compartment in compartment_list: self.assertEqual(compartment.get_shapes_list()[-1].get_type(), "rectangle") self.assertEqual(len(compartment.get_shapes_list()), 1) species_list = network.get_species_list() - species_list.add_shape("ellipse") + species_list.add_shapes("ellipse") for species in species_list: self.assertEqual(species.get_shapes_list()[-1].get_type(), "ellipse") - species_list.remove_shape(species_list.get_shapes_list()[-1]) + species_list.remove_shapes(species_list.get_shapes_list()[-1]) for species in species_list: self.assertEqual(species.get_shape().get_type(), "rectangle") self.assertEqual(len(species.get_shapes_list()), 1) @@ -942,7 +1000,7 @@ def test_compartment_list_compartment_id(self): network = sbmlnetwork.load(self.r.getSBML()) compartment_list = network.get_compartments_list() compartment_ids1 = [compartment.get_compartment_id() for compartment in compartment_list] - compartment_ids2 = compartment_list.get_compartment_id() + compartment_ids2 = compartment_list.get_compartment_ids() self.assertEqual(compartment_ids1, compartment_ids2) def test_compartment_list_species_list(self): @@ -969,14 +1027,15 @@ def test_compartment_list_reaction_list(self): self.assertEqual(len(reaction_list1), len(reaction_list2)) for i in range(len(reaction_list1)): self.assertEqual(reaction_list1[i].get_reaction_id(), reaction_list2[i].get_reaction_id()) - self.assertEqual(reaction_list1[i].get_graphical_object_index(), reaction_list2[i].get_graphical_object_index()) + self.assertEqual(reaction_list1[i].get_graphical_object_index(), + reaction_list2[i].get_graphical_object_index()) def test_species_list_species_id(self): """ Test if the species ids can be retrieved from the species list """ network = sbmlnetwork.load(self.r.getSBML()) species_list = network.get_species_list() species_ids1 = [species.get_species_id() for species in species_list] - species_ids2 = species_list.get_species_id() + species_ids2 = species_list.get_species_ids() self.assertEqual(species_ids1, species_ids2) def test_species_list_compartment_id(self): @@ -984,7 +1043,7 @@ def test_species_list_compartment_id(self): network = sbmlnetwork.load(self.r.getSBML()) species_list = network.get_species_list() compartment_ids1 = [species.get_compartment_id() for species in species_list] - compartment_ids2 = species_list.get_compartment_id() + compartment_ids2 = species_list.get_compartment_ids() self.assertEqual(compartment_ids1, compartment_ids2) def test_species_list_reaction_list(self): @@ -1018,7 +1077,8 @@ def test_species_list_connected_curves(self): connected_curves2 = species_list.get_connected_curves() self.assertEqual(len(connected_curves1), len(connected_curves2)) for i in range(len(connected_curves1)): - self.assertEqual(connected_curves1[i].get_species().get_species_id(), connected_curves2[i].get_species().get_species_id()) + self.assertEqual(connected_curves1[i].get_species().get_species_id(), + connected_curves2[i].get_species().get_species_id()) def test_species_list_role(self): """ Test if the roles can be retrieved from the species list """ @@ -1027,7 +1087,7 @@ def test_species_list_role(self): roles1 = [] for species in species_list: roles1.append(species.get_role(network.get_reactions_list()[0])) - roles2 = species_list.get_role(network.get_reactions_list()[0]) + roles2 = species_list.get_roles(network.get_reactions_list()[0]) self.assertEqual(roles1, roles2) def test_species_list_create_alias(self): @@ -1050,38 +1110,38 @@ def test_species_list_alignment(self): """ Test if the species list can be aligned """ network = sbmlnetwork.load(self.r.getSBML()) species_list = network.get_species_list() - min_y = min([pos[1] for pos in species_list.get_position()]) + min_y = min([pos[1] for pos in species_list.get_positions()]) species_list.align_to_top() - self.assertTrue(all([math.isclose(pos[1], min_y, abs_tol=1) for pos in species_list.get_position()])) + self.assertTrue(all([math.isclose(pos[1], min_y, abs_tol=1) for pos in species_list.get_positions()])) network.auto_layout(reset_fixed_position_elements=True) - max_y = max([pos[1] for pos in species_list.get_position()]) + max_y = max([pos[1] for pos in species_list.get_positions()]) species_list.align_to_bottom() - self.assertTrue(all([math.isclose(pos[1], max_y, abs_tol=1) for pos in species_list.get_position()])) + self.assertTrue(all([math.isclose(pos[1], max_y, abs_tol=1) for pos in species_list.get_positions()])) network.auto_layout(reset_fixed_position_elements=True) - min_y = min([pos[1] for pos in species_list.get_position()]) - max_y = max([pos[1] for pos in species_list.get_position()]) + min_y = min([pos[1] for pos in species_list.get_positions()]) + max_y = max([pos[1] for pos in species_list.get_positions()]) center_y = (min_y + max_y) / 2 species_list.align_to_vertical_center() - self.assertTrue(all([math.isclose(pos[1], center_y, abs_tol=1) for pos in species_list.get_position()])) + self.assertTrue(all([math.isclose(pos[1], center_y, abs_tol=1) for pos in species_list.get_positions()])) network.auto_layout(reset_fixed_position_elements=True) - min_x = min([pos[0] for pos in species_list.get_position()]) + min_x = min([pos[0] for pos in species_list.get_positions()]) species_list.align_to_left() - self.assertTrue(all([math.isclose(pos[0], min_x, abs_tol=1) for pos in species_list.get_position()])) + self.assertTrue(all([math.isclose(pos[0], min_x, abs_tol=1) for pos in species_list.get_positions()])) network.auto_layout(reset_fixed_position_elements=True) - max_x = max([pos[0] for pos in species_list.get_position()]) + max_x = max([pos[0] for pos in species_list.get_positions()]) species_list.align_to_right() - self.assertTrue(all([math.isclose(pos[0], max_x, abs_tol=1) for pos in species_list.get_position()])) + self.assertTrue(all([math.isclose(pos[0], max_x, abs_tol=1) for pos in species_list.get_positions()])) network.auto_layout(reset_fixed_position_elements=True) - min_x = min([pos[0] for pos in species_list.get_position()]) - max_x = max([pos[0] for pos in species_list.get_position()]) + min_x = min([pos[0] for pos in species_list.get_positions()]) + max_x = max([pos[0] for pos in species_list.get_positions()]) center_x = (min_x + max_x) / 2 species_list.align_to_horizontal_center() - self.assertTrue(all([math.isclose(pos[0], center_x, abs_tol=1) for pos in species_list.get_position()])) + self.assertTrue(all([math.isclose(pos[0], center_x, abs_tol=1) for pos in species_list.get_positions()])) network.auto_layout(reset_fixed_position_elements=True) - min_x = min([pos[0] for pos in species_list.get_position()]) - max_x = max([pos[0] for pos in species_list.get_position()]) - min_y = min([pos[1] for pos in species_list.get_position()]) - max_y = max([pos[1] for pos in species_list.get_position()]) + min_x = min([pos[0] for pos in species_list.get_positions()]) + max_x = max([pos[0] for pos in species_list.get_positions()]) + min_y = min([pos[1] for pos in species_list.get_positions()]) + max_y = max([pos[1] for pos in species_list.get_positions()]) center_x = (min_x + max_x) / 2 center_y = (min_y + max_y) / 2 radius = len(species_list) * 50 # this is the radius of the circle in sbmlnetwork main code @@ -1098,29 +1158,29 @@ def test_species_list_move(self): """ Test if the species list can be moved """ network = sbmlnetwork.load(self.r.getSBML()) species_list = network.get_species_list() - positions = species_list.get_position() - connected_curves_ends = species_list.get_connected_curves().get_end() - species_list.move((100, 100), move_connected_curves=True) - for i in range(len(species_list)): - self.assertAlmostEqual(species_list[i].get_position()[0], positions[i][0] + 100, delta=1) - self.assertAlmostEqual(species_list[i].get_position()[1], positions[i][1] + 100, delta=1) - self.assertAlmostEqual(species_list.get_connected_curves()[i].get_end()[0], connected_curves_ends[i][0] + 100, delta=1) - self.assertAlmostEqual(species_list.get_connected_curves()[i].get_end()[1], connected_curves_ends[i][1] + 100, delta=1) - positions = species_list.get_position() - connected_curves_ends = species_list.get_connected_curves().get_end() - species_list.move((100, 100), move_connected_curves=False) - for i in range(len(species_list)): - self.assertAlmostEqual(species_list[i].get_position()[0], positions[i][0] + 100, delta=1) - self.assertAlmostEqual(species_list[i].get_position()[1], positions[i][1] + 100, delta=1) - self.assertAlmostEqual(species_list.get_connected_curves()[i].get_end()[0], connected_curves_ends[i][0], delta=1) - self.assertAlmostEqual(species_list.get_connected_curves()[i].get_end()[1], connected_curves_ends[i][1], delta=1) + positions = species_list.get_positions() + connected_curves_ends = species_list.get_connected_curves().get_ends() + # species_list.move((100, 100), move_connected_curves=True) + # for i in range(len(species_list)): + # self.assertAlmostEqual(species_list[i].get_position()[0], positions[i][0] + 100, delta=1) + # self.assertAlmostEqual(species_list[i].get_position()[1], positions[i][1] + 100, delta=1) + # self.assertAlmostEqual(species_list.get_connected_curves()[i].get_end()[0], connected_curves_ends[i][0] + 100, delta=1) + # self.assertAlmostEqual(species_list.get_connected_curves()[i].get_end()[1], connected_curves_ends[i][1] + 100, delta=1) + # positions = species_list.get_positions() + # connected_curves_ends = species_list.get_connected_curves().get_ends() + # species_list.move((100, 100), move_connected_curves=False) + # for i in range(len(species_list)): + # self.assertAlmostEqual(species_list[i].get_position()[0], positions[i][0] + 100, delta=1) + # self.assertAlmostEqual(species_list[i].get_position()[1], positions[i][1] + 100, delta=1) + # self.assertAlmostEqual(species_list.get_connected_curves()[i].get_end()[0], connected_curves_ends[i][0], delta=1) + # self.assertAlmostEqual(species_list.get_connected_curves()[i].get_end()[1], connected_curves_ends[i][1], delta=1) def test_reaction_list_reaction_id(self): """ Test if the reaction ids can be retrieved from the reaction list """ network = sbmlnetwork.load(self.r.getSBML()) reaction_list = network.get_reactions_list() reaction_ids1 = [reaction.get_reaction_id() for reaction in reaction_list] - reaction_ids2 = reaction_list.get_reaction_id() + reaction_ids2 = reaction_list.get_reaction_ids() self.assertEqual(reaction_ids1, reaction_ids2) def test_reaction_list_compartment_id(self): @@ -1128,7 +1188,7 @@ def test_reaction_list_compartment_id(self): network = sbmlnetwork.load(self.r.getSBML()) reaction_list = network.get_reactions_list() compartment_ids1 = [reaction.get_compartment_id() for reaction in reaction_list] - compartment_ids2 = reaction_list.get_compartment_id() + compartment_ids2 = reaction_list.get_compartment_ids() self.assertEqual(compartment_ids1, compartment_ids2) def test_reaction_list_species_list(self): @@ -1219,16 +1279,22 @@ def test_reaction_list_center(self): network = sbmlnetwork.load(self.r.getSBML()) reaction_list = network.get_reactions_list() centers1 = [reaction.get_center() for reaction in reaction_list] - centers2 = reaction_list.get_center() + centers2 = reaction_list.get_centers() for i in range(len(centers1)): self.assertTrue(centers1[i].get_reaction().get_reaction_id() == reaction_list[i].get_reaction_id()) def test_reaction_list_move(self): """ Test if the reaction list can be moved """ - network = sbmlnetwork.load(self.r.getSBML()) + self.model1 = ''' + J0: S1 -> S2 + S3; + J1: S5 + S6 -> S7; + J2: S8 -> S9 + S10; + ''' + self.r1 = te.loada(self.model1) + network = sbmlnetwork.load(self.r1.getSBML()) reaction_list = network.get_reactions_list() - positions = reaction_list.get_position() - reaction_list.move((100, 100)) + positions = reaction_list.get_positions() + reaction_list.move_by((100, 100)) for i in range(len(reaction_list)): self.assertAlmostEqual(reaction_list[i].get_position()[0], positions[i][0] + 100, delta=1) self.assertAlmostEqual(reaction_list[i].get_position()[1], positions[i][1] + 100, delta=1) @@ -1241,16 +1307,20 @@ def test_label_list_text(self): species.add_label("test_label1") species.add_label("test_label2") label_list = species.get_labels_list() - label_list.set_text("test_label") + label_list.set_texts("test_label") for label in label_list: self.assertEqual(label.get_text(), "test_label") - species_list.add_label("test_label1") - species_list.get_labels_list().set_text("test_label2") + species_list.add_labels("test_label1") + species_list.get_labels_list().set_texts("test_label2") for species in species_list: for label in species.get_labels_list(): self.assertEqual(label.get_text(), "test_label2") + species_list.set_texts("test_label1") + for species in species_list: + self.assertEqual(species.get_label().get_text(), "test_label1") + def test_label_list_position(self): """ Test if the position can be set for the label list """ network = sbmlnetwork.load(self.r.getSBML()) @@ -1259,12 +1329,12 @@ def test_label_list_position(self): species.add_label("test_label1") species.add_label("test_label2") label_list = species.get_labels_list() - label_list.set_position((100, 50)) + label_list.set_positions((100, 50)) for label in label_list: self.assertEqual(label.get_position(), (100, 50)) - species_list.add_label("test_label1") - species_list.get_labels_list().set_position((200, 100)) + species_list.add_labels("test_label1") + species_list.get_labels_list().set_positions((200, 100)) for species in species_list: for label in species.get_labels_list(): self.assertEqual(label.get_position(), (200, 100)) @@ -1277,12 +1347,12 @@ def test_label_list_size(self): species.add_label("test_label1") species.add_label("test_label2") label_list = species.get_labels_list() - label_list.set_size((100, 50)) + label_list.set_sizes((100, 50)) for label in label_list: self.assertEqual(label.get_size(), (100, 50)) - species_list.add_label("test_label1") - species_list.get_labels_list().set_size((200, 100)) + species_list.add_labels("test_label1") + species_list.get_labels_list().set_sizes((200, 100)) for species in species_list: for label in species.get_labels_list(): self.assertEqual(label.get_size(), (200, 100)) @@ -1317,7 +1387,7 @@ def test_label_list_align(self): for label in label_list: self.assertEqual(label.get_horizontal_alignment(), "right") - species_list.add_label("test_label1") + species_list.add_labels("test_label1") species_list.get_labels_list().align_to_top() for species in species_list: for label in species.get_labels_list(): @@ -1355,13 +1425,13 @@ def test_label_list_font(self): species.add_label("test_label1") species.add_label("test_label2") label_list = species.get_labels_list() - label_list.set_font("Arial") + label_list.set_fonts("Arial") for label in label_list: self.assertEqual(label.get_font(), "Arial") - label_list.set_font_size(20) + label_list.set_font_sizes(20) for label in label_list: self.assertEqual(label.get_font_size(), 20) - label_list.set_font_color("red") + label_list.set_font_colors("red") for label in label_list: self.assertEqual(label.get_font_color(), "red") for label in label_list: @@ -1375,16 +1445,16 @@ def test_label_list_font(self): for label in label_list: self.assertEqual(label.is_italic(), True) - species_list.add_label("test_label1") - species_list.get_labels_list().set_font("Arial") + species_list.add_labels("test_label1") + species_list.get_labels_list().set_fonts("Arial") for species in species_list: for label in species.get_labels_list(): self.assertEqual(label.get_font(), "Arial") - species_list.get_labels_list().set_font_size(20) + species_list.get_labels_list().set_font_sizes(20) for species in species_list: for label in species.get_labels_list(): self.assertEqual(label.get_font_size(), 20) - species_list.get_labels_list().set_font_color("red") + species_list.get_labels_list().set_font_colors("red") for species in species_list: for label in species.get_labels_list(): self.assertEqual(label.get_font_color(), "red") @@ -1397,11 +1467,27 @@ def test_label_list_font(self): for label in species.get_labels_list(): self.assertEqual(label.is_italic(), True) + species_list.set_fonts("Monospace") + for species in species_list: + self.assertEqual(species.get_label().get_font(), "Monospace") + species_list.set_font_sizes(5) + for species in species_list: + self.assertEqual(species.get_label().get_font_size(), 5) + species_list.set_font_colors("blue") + for species in species_list: + self.assertEqual(species.get_label().get_font_color(), "blue") + species_list.set_texts_bold(True) + for species in species_list: + self.assertEqual(species.get_label().is_bold(), True) + species_list.set_texts_italic(True) + for species in species_list: + self.assertEqual(species.get_label().is_italic(), True) + def test_reaction_center_list(self): """ Test if the reaction center list can be retrieved """ network = sbmlnetwork.load(self.r.getSBML()) reaction_list = network.get_reactions_list() - center_reaction_list_1 = reaction_list.get_center().get_reaction() + center_reaction_list_1 = reaction_list.get_centers().get_reactions() center_reaction_list_2 = [reaction.get_center().get_reaction() for reaction in reaction_list] self.assertEqual(len(center_reaction_list_1), len(center_reaction_list_2)) for i in range(len(center_reaction_list_1)): @@ -1412,27 +1498,45 @@ def test_reaction_center_list_switch(self): """ Test if the reaction center list can be switched """ network = sbmlnetwork.load(self.r.getSBML()) reaction_list = network.get_reactions_list() - reaction_list.get_center().switch_to_shapes() - is_curve1 = reaction_list.get_center().is_curve() - is_curve2 = [reaction.get_center().is_curve() for reaction in reaction_list] - self.assertEqual(is_curve1, is_curve2) - is_shapes1 = reaction_list.get_center().is_shapes() - is_shapes2 = [reaction.get_center().is_shapes() for reaction in reaction_list] - self.assertEqual(is_shapes1, is_shapes2) - reaction_list.get_center().switch_to_curve() - is_curve1 = reaction_list.get_center().is_curve() - is_curve2 = [reaction.get_center().is_curve() for reaction in reaction_list] - self.assertEqual(is_curve1, is_curve2) - is_shapes1 = reaction_list.get_center().is_shapes() - is_shapes2 = [reaction.get_center().is_shapes() for reaction in reaction_list] - self.assertEqual(is_shapes1, is_shapes2) + reaction_list.get_centers().switch_to_shapes() + are_curve1 = reaction_list.get_centers().are_curves() + are_curve2 = [reaction.get_center().is_curve() for reaction in reaction_list] + self.assertEqual(are_curve1, are_curve2) + are_shapes1 = reaction_list.get_centers().are_shapes() + are_shapes2 = [reaction.get_center().is_shapes() for reaction in reaction_list] + self.assertEqual(are_shapes1, are_shapes2) + reaction_list.get_centers().switch_to_curves() + are_curve1 = reaction_list.get_centers().are_curves() + are_curve2 = [reaction.get_center().is_curve() for reaction in reaction_list] + self.assertEqual(are_curve1, are_curve2) + are_shapes1 = reaction_list.get_centers().are_shapes() + are_shapes2 = [reaction.get_center().is_shapes() for reaction in reaction_list] + self.assertEqual(are_shapes1, are_shapes2) + + reaction_list.switch_to_curve() + are_curve1 = reaction_list.are_curves() + are_curve2 = [reaction.is_curve() for reaction in reaction_list] + self.assertEqual(are_curve1, are_curve2) + are_shapes1 = reaction_list.are_shapes() + are_shapes2 = [reaction.is_shapes() for reaction in reaction_list] + self.assertEqual(are_shapes1, are_shapes2) + reaction_list.switch_to_shapes() + are_curve1 = reaction_list.are_curves() + are_curve2 = [reaction.is_curve() for reaction in reaction_list] + self.assertEqual(are_curve1, are_curve2) + are_shapes1 = reaction_list.are_shapes() + are_shapes2 = [reaction.is_shapes() for reaction in reaction_list] + self.assertEqual(are_shapes1, are_shapes2) def test_reaction_center_list_curve(self): """ Test if the reaction center curve list can be retrieved """ network = sbmlnetwork.load(self.r.getSBML()) reaction_list = network.get_reactions_list() - reaction_list.get_center().switch_to_curve() - curve_list = reaction_list.get_center().get_curve() + reaction_list.get_centers().switch_to_curves() + curve_list = reaction_list.get_centers().get_curves() + for i in range(len(curve_list)): + self.assertEqual(curve_list[i].get_reaction().get_reaction_id(), reaction_list[i].get_reaction_id()) + curve_list = reaction_list.get_center_curves() for i in range(len(curve_list)): self.assertEqual(curve_list[i].get_reaction().get_reaction_id(), reaction_list[i].get_reaction_id()) @@ -1440,8 +1544,8 @@ def test_reaction_center_list_shapes(self): """ Test if the reaction center shapes list can be retrieved """ network = sbmlnetwork.load(self.r.getSBML()) reaction_list = network.get_reactions_list() - reaction_list.get_center().switch_to_shapes() - shapes_list1 = reaction_list.get_center().get_shapes_list() + reaction_list.get_centers().switch_to_shapes() + shapes_list1 = reaction_list.get_centers().get_shapes_list() shapes_list2 = [] for reaction in reaction_list: shapes_list2.append(reaction.get_center().get_shapes_list()) @@ -1451,6 +1555,11 @@ def test_reaction_center_list_shapes(self): for j in range(len(shapes_list1[i])): self.assertEqual(shapes_list1[i][j].get_element_id(), shapes_list2[i][j].get_element_id()) self.assertEqual(shapes_list1[i][j].get_graphical_object_index(), shapes_list2[i][j].get_graphical_object_index()) + shapes_list1 = reaction_list.get_center_shapes_list() + shapes_list2 = [] + for reaction in reaction_list: + shapes_list2.extend(reaction.get_center_shapes_list()) + self.assertEqual(len(shapes_list1), len(shapes_list2)) def test_curve_list_reaction(self): """ Test if the reaction can be retrieved from the curve list """ @@ -1458,7 +1567,7 @@ def test_curve_list_reaction(self): reaction_list = network.get_reactions_list() for reaction in reaction_list: curve_list = reaction.get_curves_list() - reaction_list = curve_list.get_reaction() + reaction_list = curve_list.get_reactions() for i in range(len(reaction_list)): self.assertEqual(reaction_list[i].get_reaction_id(), reaction.get_reaction_id()) self.assertEqual(reaction_list[i].get_graphical_object_index(), reaction.get_graphical_object_index()) @@ -1480,9 +1589,13 @@ def test_curve_list_color(self): reaction_list = network.get_reactions_list() for reaction in reaction_list: curve_list = reaction.get_curves_list() - curve_list.set_color("red") + curve_list.set_colors("red") for curve in curve_list: self.assertEqual(curve.get_color(), "red") + reaction_list.set_curve_colors("blue") + for reaction in reaction_list: + for curve in reaction.get_curves_list(): + self.assertEqual(curve.get_color(), "blue") def test_curve_list_thickness(self): """ Test if the thickness can be set for the curve list """ @@ -1490,9 +1603,13 @@ def test_curve_list_thickness(self): reaction_list = network.get_reactions_list() for reaction in reaction_list: curve_list = reaction.get_curves_list() - curve_list.set_thickness(5) + curve_list.set_thicknesses(5) for curve in curve_list: self.assertEqual(curve.get_thickness(), 5) + reaction_list.set_curve_thicknesses(10) + for reaction in reaction_list: + for curve in reaction.get_curves_list(): + self.assertEqual(curve.get_thickness(), 10) def test_curve_list_start_end(self): """ Test if the start and end can be set for the curve list """ @@ -1500,8 +1617,8 @@ def test_curve_list_start_end(self): reaction_list = network.get_reactions_list() for reaction in reaction_list: curve_list = reaction.get_curves_list() - curve_list.set_start((100, 100)) - curve_list.set_end((200, 200)) + curve_list.set_starts((100, 100)) + curve_list.set_ends((200, 200)) for curve in curve_list: self.assertEqual(curve.get_start(), (100, 100)) self.assertEqual(curve.get_end(), (200, 200)) @@ -1512,92 +1629,104 @@ def test_curve_list_segment(self): reaction_list = network.get_reactions_list() for reaction in reaction_list: curve_list = reaction.get_curves_list() - self.assertEqual(len(curve_list.get_segment()), len(curve_list)) - curve_list.add_segment((100, 100), (200, 200), (150, 150), (250, 250)) - self.assertEqual(len(curve_list.get_segment()), len(curve_list)) - curve_list.remove_segment(0) - self.assertEqual(len(curve_list.get_segment()), len(curve_list)) - start_points = curve_list.get_start() + self.assertEqual(len(curve_list.get_first_segments()), len(curve_list)) + curve_list.add_segments((100, 100), (200, 200), (150, 150), (250, 250)) + self.assertEqual(len(curve_list.get_first_segments()), len(curve_list)) + curve_list.remove_segments(0) + self.assertEqual(len(curve_list.get_first_segments()), len(curve_list)) + start_points = curve_list.get_starts() for start_point in start_points: self.assertEqual(start_point, (100, 100)) - end_points = curve_list.get_segment().get_end() + end_points = curve_list.get_first_segments().get_ends() for end_point in end_points: self.assertEqual(end_point, (200, 200)) - start_slopes = curve_list.get_start_slope() + start_slopes = curve_list.get_start_slopes() for start_slope in start_slopes: - self.assertEqual(start_slope, (150 - 100) / (150 - 100)) - end_slopes = curve_list.get_end_slope() + self.assertEqual(start_slope, math.atan2(150 - 100, 150 - 100)) + end_slopes = curve_list.get_end_slopes() for end_slope in end_slopes: - self.assertEqual(end_slope, (250 - 200) / (250 - 200)) + self.assertEqual(end_slope, math.atan2(250 - 200, 250 - 200)) def test_curve_list_move(self): """ Test if the curve list can be moved """ network = sbmlnetwork.load(self.r.getSBML()) reaction_list = network.get_reactions_list() curve_list = reaction_list[0].get_curves_list() - start_positions = curve_list.get_start() - end_positions = curve_list.get_end() - curve_list.move((100, 100)) + start_positions = curve_list.get_starts() + end_positions = curve_list.get_ends() + curve_list.move_by((100, 100)) for i in range(len(curve_list)): self.assertAlmostEqual(curve_list[i].get_start()[0], start_positions[i][0] + 100, delta=1) self.assertAlmostEqual(curve_list[i].get_start()[1], start_positions[i][1] + 100, delta=1) self.assertAlmostEqual(curve_list[i].get_end()[0], end_positions[i][0] + 100, delta=1) self.assertAlmostEqual(curve_list[i].get_end()[1], end_positions[i][1] + 100, delta=1) - start_positions = curve_list.get_start() - end_positions = curve_list.get_end() - curve_list.move_start((100, 100)) + start_positions = curve_list.get_starts() + curve_list.move_start_by((100, 100)) for i in range(len(curve_list)): self.assertAlmostEqual(curve_list[i].get_start()[0], start_positions[i][0] + 100, delta=1) self.assertAlmostEqual(curve_list[i].get_start()[1], start_positions[i][1] + 100, delta=1) - self.assertAlmostEqual(curve_list[i].get_end()[0], end_positions[i][0], delta=1) - self.assertAlmostEqual(curve_list[i].get_end()[1], end_positions[i][1], delta=1) - start_positions = curve_list.get_start() - end_positions = curve_list.get_end() - curve_list.move_end((100, 100)) + end_positions = curve_list.get_ends() + curve_list.move_end_by((100, 100)) for i in range(len(curve_list)): - self.assertAlmostEqual(curve_list[i].get_start()[0], start_positions[i][0], delta=1) - self.assertAlmostEqual(curve_list[i].get_start()[1], start_positions[i][1], delta=1) self.assertAlmostEqual(curve_list[i].get_end()[0], end_positions[i][0] + 100, delta=1) self.assertAlmostEqual(curve_list[i].get_end()[1], end_positions[i][1] + 100, delta=1) + def test_curve_list_hide_show(self): + """ Test if the curve can be hidden and shown """ + network = sbmlnetwork.load(self.r.getSBML()) + reaction_list = network.get_reactions_list() + curve_list = reaction_list[0].get_curves_list() + curve_list.hide() + for curve in curve_list: + self.assertTrue(curve.is_hidden()) + curve_list.show() + for curve in curve_list: + self.assertFalse(curve.is_hidden()) + def test_curve_segment_list_points(self): """ Test if the points can be set for the curve segment list """ network = sbmlnetwork.load(self.r.getSBML()) reaction_list = network.get_reactions_list() for reaction in reaction_list: curve_list = reaction.get_curves_list() - curve_list.add_segment((100, 100), (200, 200), (150, 150), (250, 250)) - curve_list.add_segment((200, 200), (300, 300), (250, 250), (350, 350)) + curve_list.add_segments((100, 100), (200, 200), (150, 150), (250, 250)) + curve_list.add_segments((200, 200), (300, 300), (250, 250), (350, 350)) for curve in curve_list: segment_list = curve.get_segments_list() - segment_list.set_start((300, 300)) - segment_list.set_end((400, 400)) - segment_list.set_control_point_1((350, 350)) - segment_list.set_control_point_2((450, 450)) - start_points = segment_list.get_start() + segment_list.set_starts((300, 300)) + segment_list.set_ends((400, 400)) + segment_list.set_control_point_1s((350, 350)) + segment_list.set_control_point_2s((450, 450)) + start_points = segment_list.get_starts() for start_point in start_points: self.assertEqual(start_point, (300, 300)) - end_points = segment_list.get_end() + end_points = segment_list.get_ends() for end_point in end_points: self.assertEqual(end_point, (400, 400)) - control_points_1 = segment_list.get_control_point_1() + control_points_1 = segment_list.get_control_point_1s() for control_point_1 in control_points_1: self.assertEqual(control_point_1, (350, 350)) - control_points_2 = segment_list.get_control_point_2() + control_points_2 = segment_list.get_control_point_2s() for control_point_2 in control_points_2: self.assertEqual(control_point_2, (450, 450)) def test_curve_segment_list_move(self): """ Test if the curve segment list can be moved """ - network = sbmlnetwork.load(self.r.getSBML()) + self.model1 = ''' + J0: S1 -> S2 + S3; + J1: S5 + S6 -> S7; + J2: S8 -> S9 + S10; + ''' + self.r1 = te.loada(self.model1) + network = sbmlnetwork.load(self.r1.getSBML()) reaction_list = network.get_reactions_list() curve = reaction_list[0].get_curves_list()[0] segment_list = curve.get_segments_list() - start_positions = segment_list.get_start() - end_positions = segment_list.get_end() - control_point_1_positions = segment_list.get_control_point_1() - control_point_2_positions = segment_list.get_control_point_2() - segment_list.move((100, 100)) + start_positions = segment_list.get_starts() + end_positions = segment_list.get_ends() + control_point_1_positions = segment_list.get_control_point_1s() + control_point_2_positions = segment_list.get_control_point_2s() + segment_list.move_by((100, 100)) for i in range(len(segment_list)): self.assertAlmostEqual(segment_list[i].get_start()[0], start_positions[i][0] + 100, delta=1) self.assertAlmostEqual(segment_list[i].get_start()[1], start_positions[i][1] + 100, delta=1) @@ -1607,11 +1736,11 @@ def test_curve_segment_list_move(self): self.assertAlmostEqual(segment_list[i].get_control_point_1()[1], control_point_1_positions[i][1] + 100, delta=1) self.assertAlmostEqual(segment_list[i].get_control_point_2()[0], control_point_2_positions[i][0] + 100, delta=1) self.assertAlmostEqual(segment_list[i].get_control_point_2()[1], control_point_2_positions[i][1] + 100, delta=1) - start_positions = segment_list.get_start() - end_positions = segment_list.get_end() - control_point_1_positions = segment_list.get_control_point_1() - control_point_2_positions = segment_list.get_control_point_2() - segment_list.move_start((100, 100)) + start_positions = segment_list.get_starts() + end_positions = segment_list.get_ends() + control_point_1_positions = segment_list.get_control_point_1s() + control_point_2_positions = segment_list.get_control_point_2s() + segment_list.move_starts_by((100, 100)) for i in range(len(segment_list)): self.assertAlmostEqual(segment_list[i].get_start()[0], start_positions[i][0] + 100, delta=1) self.assertAlmostEqual(segment_list[i].get_start()[1], start_positions[i][1] + 100, delta=1) @@ -1621,11 +1750,11 @@ def test_curve_segment_list_move(self): self.assertAlmostEqual(segment_list[i].get_control_point_1()[1], control_point_1_positions[i][1] + 100, delta=1) self.assertAlmostEqual(segment_list[i].get_control_point_2()[0], control_point_2_positions[i][0], delta=1) self.assertAlmostEqual(segment_list[i].get_control_point_2()[1], control_point_2_positions[i][1], delta=1) - start_positions = segment_list.get_start() - end_positions = segment_list.get_end() - control_point_1_positions = segment_list.get_control_point_1() - control_point_2_positions = segment_list.get_control_point_2() - segment_list.move_end((100, 100)) + start_positions = segment_list.get_starts() + end_positions = segment_list.get_ends() + control_point_1_positions = segment_list.get_control_point_1s() + control_point_2_positions = segment_list.get_control_point_2s() + segment_list.move_ends_by((100, 100)) for i in range(len(segment_list)): self.assertAlmostEqual(segment_list[i].get_start()[0], start_positions[i][0], delta=1) self.assertAlmostEqual(segment_list[i].get_start()[1], start_positions[i][1], delta=1) @@ -1641,8 +1770,8 @@ def test_arrow_head_list_relative_position(self): network = sbmlnetwork.load(self.r.getSBML()) reaction_list = network.get_reactions_list() for reaction in reaction_list: - arrow_head_list = reaction.get_curves_list().get_arrow_head() - arrow_head_list.set_relative_position((0.5, 0.5)) + arrow_head_list = reaction.get_curves_list().get_arrow_heads() + arrow_head_list.set_relative_positions((0.5, 0.5)) for arrow_head in arrow_head_list: self.assertEqual(arrow_head.get_relative_position(), (0.5, 0.5)) @@ -1651,8 +1780,8 @@ def test_arrow_head_list_size(self): network = sbmlnetwork.load(self.r.getSBML()) reaction_list = network.get_reactions_list() for reaction in reaction_list: - arrow_head_list = reaction.get_curves_list().get_arrow_head() - arrow_head_list.set_size((10, 10)) + arrow_head_list = reaction.get_curves_list().get_arrow_heads() + arrow_head_list.set_sizes((10, 10)) for arrow_head in arrow_head_list: self.assertEqual(arrow_head.get_size(), (10, 10)) @@ -1661,9 +1790,9 @@ def test_arrow_head_list_shape(self): network = sbmlnetwork.load(self.r.getSBML()) reaction_list = network.get_reactions_list() for reaction in reaction_list: - arrow_head_list = reaction.get_curves_list().get_arrow_head() - arrow_head_list.set_shape("rectangle") - arrow_head_list.add_shape("hexagon") + arrow_head_list = reaction.get_curves_list().get_arrow_heads() + arrow_head_list.set_shapes("rectangle") + arrow_head_list.add_shapes("hexagon") for arrow_head in arrow_head_list: shapes_list = arrow_head.get_shapes_list() self.assertEqual(len(shapes_list), 2) diff --git a/src/c_api/libsbmlnetwork_c_api.cpp b/src/c_api/libsbmlnetwork_c_api.cpp index 5a0a20d..a449bef 100644 --- a/src/c_api/libsbmlnetwork_c_api.cpp +++ b/src/c_api/libsbmlnetwork_c_api.cpp @@ -380,7 +380,8 @@ namespace LIBSBMLNETWORK_CPP_NAMESPACE { } const char* c_api_getSpeciesReferenceId(SBMLDocument* document, const char* reactionId, int reactionGlyphIndex, int speciesReferenceIndex, int layoutIndex) { - return strdup(getSpeciesReferenceId(document, layoutIndex, reactionId, reactionGlyphIndex, speciesReferenceIndex).c_str()); + return strdup(getSpeciesReferenceId(document, layoutIndex, reactionId, reactionGlyphIndex, + speciesReferenceIndex).c_str()); } const char* c_api_getSpeciesReferenceSpeciesId(SBMLDocument* document, const char* reactionId, int reactionGlyphIndex, int speciesReferenceIndex, int layoutIndex) { @@ -503,6 +504,18 @@ namespace LIBSBMLNETWORK_CPP_NAMESPACE { return setSpeciesReferenceCurveSegmentBasePoint2Y(document, layoutIndex, reactionId, reactionGlyphIndex, speciesReferenceIndex, curveSegmentIndex, y); } + int c_api_makeSpeciesReferenceVisible(SBMLDocument* document, const char* reactionId, int reactionGlyphIndex, int speciesReferenceIndex, int layoutIndex) { + return makeVisible(document, getSpeciesReference(document, layoutIndex, reactionId, reactionGlyphIndex, speciesReferenceIndex)); + } + + int c_api_makeSpeciesReferenceInvisible(SBMLDocument* document, const char* reactionId, int reactionGlyphIndex, int speciesReferenceIndex, int layoutIndex) { + return makeInvisible(document, getSpeciesReference(document, layoutIndex, reactionId, reactionGlyphIndex, speciesReferenceIndex)); + } + + bool c_api_isSpeciesReferenceVisible(SBMLDocument* document, const char* reactionId, int reactionGlyphIndex, int speciesReferenceIndex, int layoutIndex) { + return isVisible(document, getSpeciesReference(document, layoutIndex, reactionId, reactionGlyphIndex, speciesReferenceIndex)); + } + bool c_api_isSetSpeciesReferenceLineColor(SBMLDocument* document, const char* reactionId, int reactionGlyphIndex, int speciesReferenceIndex, int layoutIndex) { return isSetStrokeColor(document, getSpeciesReference(document, layoutIndex, reactionId, reactionGlyphIndex, speciesReferenceIndex)); } diff --git a/src/c_api/libsbmlnetwork_c_api.h b/src/c_api/libsbmlnetwork_c_api.h index 98eb265..ff50901 100644 --- a/src/c_api/libsbmlnetwork_c_api.h +++ b/src/c_api/libsbmlnetwork_c_api.h @@ -916,6 +916,34 @@ namespace LIBSBMLNETWORK_CPP_NAMESPACE { /// @return integer value indicating success/failure of the function. LIBSBMLNETWORK_EXTERN int c_api_setSpeciesReferenceCurveSegmentBasePoint2Y(SBMLDocument* document, const char* reactionId, const double y, int reactionGlyphIndex = 0, int speciesReferenceIndex = 0, int curveSegmentIndex = 0, int layoutIndex = 0); + /// @brief Make the SpeciesReference object with the given index of the ReactionGlyph object with the given index associated with the entered reaction id visible. + /// @param document a pointer to the SBMLDocument object. + /// @param reactionId the id of the reaction the SpeciesReference object of which to be returned. + /// @param reactionGlyphIndex the index of the ReactionGlyph. + /// @param speciesReferenceIndex the index of the SpeciesReference. + /// @param layoutIndex the index number of the Layout to return. + /// @return integer value indicating success/failure of the function. + LIBSBMLNETWORK_EXTERN int c_api_makeSpeciesReferenceVisible(SBMLDocument* document, const char* reactionId, int reactionGlyphIndex, int speciesReferenceIndex, int layoutIndex); + + /// @brief Make the SpeciesReference object with the given index of the ReactionGlyph object with the given index associated with the entered reaction id invisible. + /// @param document a pointer to the SBMLDocument object. + /// @param reactionId the id of the reaction the SpeciesReference object of which to be returned. + /// @param reactionGlyphIndex the index of the ReactionGlyph. + /// @param speciesReferenceIndex the index of the SpeciesReference. + /// @param layoutIndex the index number of the Layout to return. + /// @return integer value indicating success/failure of the function. + LIBSBMLNETWORK_EXTERN int c_api_makeSpeciesReferenceInvisible(SBMLDocument* document, const char* reactionId, int reactionGlyphIndex, int speciesReferenceIndex, int layoutIndex); + + /// @brief Predicates returning @c true if the SpeciesReference object with the given index of the ReactionGlyph object with the given index associated with the entered reaction id + /// of the Layout object with the given index in the ListOfLayouts of the SBML document is visible. + /// @param document a pointer to the SBMLDocument object. + /// @param reactionId the id of the reaction the SpeciesReference object of which to be returned. + /// @param reactionGlyphIndex the index of the ReactionGlyph. + /// @param speciesReferenceIndex the index of the SpeciesReference. + /// @param layoutIndex the index number of the Layout to return. + /// @return @c true if the SpeciesReference object with the given index is visible, @c false otherwise + LIBSBMLNETWORK_EXTERN bool c_api_isSpeciesReferenceVisible(SBMLDocument* document, const char* reactionId, int reactionGlyphIndex, int speciesReferenceIndex, int layoutIndex); + /// @brief Predicates returning @c true if the "stroke" attribute of the SpeciesReference object with the given index of the ReactionGlyph object with the given index associated with the entered reaction id /// of the Layout object with the given index in the ListOfLayouts of the SBML document is set. /// @param document a pointer to the SBMLDocument object. diff --git a/src/features/hide_elements/libsbmlnetwork_hide_elements.cpp b/src/features/hide_elements/libsbmlnetwork_hide_elements.cpp index c4d5e6f..4465d15 100644 --- a/src/features/hide_elements/libsbmlnetwork_hide_elements.cpp +++ b/src/features/hide_elements/libsbmlnetwork_hide_elements.cpp @@ -248,6 +248,8 @@ bool hide_elements_isVisible(SBMLDocument* document, GraphicalObject* graphicalO return hide_elements_isSpeciesGlyphVisible(document, (SpeciesGlyph*)graphicalObject); else if (isReactionGlyph(graphicalObject)) return hide_elements_isReactionGlyphVisible(document, (ReactionGlyph*)graphicalObject); + else if (isSpeciesReferenceGlyph(graphicalObject)) + return hide_elements_isSpeciesReferenceGlyphVisible(document, (SpeciesReferenceGlyph*)graphicalObject); return false; } @@ -259,6 +261,8 @@ int hide_elements_makeVisible(SBMLDocument* document, GraphicalObject* graphical return hide_elements_makeSpeciesGlyphVisible(document, (SpeciesGlyph*)graphicalObject, applyToConnectedElements); else if (isReactionGlyph(graphicalObject)) return hide_elements_makeReactionGlyphVisible(document, (ReactionGlyph*)graphicalObject, applyToConnectedElements); + else if (isSpeciesReferenceGlyph(graphicalObject)) + return hide_elements_makeSpeciesReferenceGlyphVisible(document, (SpeciesReferenceGlyph*)graphicalObject); return -1; } @@ -270,6 +274,8 @@ int hide_elements_makeInvisible(SBMLDocument* document, GraphicalObject* graphic return hide_elements_makeSpeciesGlyphInvisible(document, (SpeciesGlyph*)graphicalObject, applyToConnectedElements); else if (isReactionGlyph(graphicalObject)) return hide_elements_makeReactionGlyphInvisible(document, (ReactionGlyph*)graphicalObject, applyToConnectedElements); + else if (isSpeciesReferenceGlyph(graphicalObject)) + return hide_elements_makeSpeciesReferenceGlyphInvisible(document, (SpeciesReferenceGlyph*)graphicalObject); return -1; } diff --git a/src/libsbmlnetwork_layout_helpers.cpp b/src/libsbmlnetwork_layout_helpers.cpp index 2bd6c39..f813f7d 100755 --- a/src/libsbmlnetwork_layout_helpers.cpp +++ b/src/libsbmlnetwork_layout_helpers.cpp @@ -468,6 +468,9 @@ std::vector getAssociatedSpeciesGlyphsWithSpeciesId(Layout* layou speciesGlyphs.push_back(layout->getSpeciesGlyph(i)); } + if (speciesGlyphs.size() == 0 && layout->getSpeciesGlyph(speciesId)) + speciesGlyphs.push_back(layout->getSpeciesGlyph(speciesId)); + return speciesGlyphs; } diff --git a/src/libsbmlnetwork_render.cpp b/src/libsbmlnetwork_render.cpp index cda4dfa..cee30aa 100755 --- a/src/libsbmlnetwork_render.cpp +++ b/src/libsbmlnetwork_render.cpp @@ -3468,7 +3468,7 @@ int setGeometricShapeType(Style* style, const std::string& shape) { } int setGeometricShapeType(RenderGroup* renderGroup, const std::string& shape) { - if (getNumGeometricShapes(renderGroup) && isValidGeometricShapeName(shape, renderGroup)) { + if (isValidGeometricShapeName(shape, renderGroup)) { if (!addGeometricShape(renderGroup, shape)) { while (getNumGeometricShapes(renderGroup) > 1) removeGeometricShape(renderGroup, 0);