Source code for cls_shapes


#!/usr/bin/python
# FLC 2013

import os
import math
import numbers
import tikzpy.obj_data as obj_data
import copy

def log(txt):

    print(txt)

[docs]class _shapes(object): """**Shapes class:** .. _shapes_cls: :platform: Unix, Windows :synopsis: Allows to add 2D shapes using points :properties: * Available shapes * Line between two points. See :ref:`shp.line <shapes_line>` * Path of multiple points. See :ref:`shp.path <shapes_path>` * Circle given by center and radius. See :ref:`shp.circle <shapes_circle>` * Arc given by starting point, radius and angles. See :ref:`shp.arc <shapes_arc>` * Arc given by center, radius and angles. See :ref:`shp.arc_by_center <shapes_arc_by_center>` * Text label. See :ref:`shp.text <shapes_text>` * Add bitmap. See :ref:`shp.bitmap <shapes_bitmap>` * Shapes allow to set labels (see labels section). See :ref:`labels <labels_cls>` **Chracteristics of a shape (shp) object** * Each shape object has different properties depending on the nature of the shape """ """ For the furture: * Add scale group of shapes shps * .add_arc(angle_in, angle_out, radius ) -- arc (90:-90:.5) in a line * add_cycle in a line * Shade options * Add opacity/opacity, section 21 * Add opacity as optional variable karg * Add zorder and list of labels to shapes functions * Zorder divided in two: internal (to use in 3D & render, valid from the second decimal) & external (only valid until the second decimal) * Add render of surfaces * Path limit of 500 points -- Sub divide in two paths -------------- Done ------------------ * Check arrows and example -- July 2016 * Add mapbits, images -- July 2016 * Add decorations (review in type) -- April 2016 * Check fill examples -- April 2016 * Add patterns into fill -- April 2016 * Add translate of shps -- April 2016 * Add rotation of shps -- April 2016 * Add copy of shps -- April 2016 * Review labels -- May 2016 * Grid rectangle -- May 2016 (should be done as an assembly of lines, to be rotable) * Rectangle given by two corners -- (should be done as an assembly of lines, to be rotable) * Parabola given by three points -- Discarded * arc_by_center -- August 2017 """ def __init__(self, parent): self.parent = parent ### Counters self.counters = obj_data._clsdata(type = int(0)) ### Dictionary of lines self.shapes = obj_data._clsdata(type = {}) self.counters["shapes"] = 0 def log(self, txt, ref = ""): self.parent.log(txt, ref = ref) def error(self, txt, ref = ""): self.parent.error(txt, ref = ref) def __getitem__(self, key): return self.getitem(key) def keys(self): return self.shapes.keys def getitem(self, key): ### Give a point _key = str(key) if self.shapes[_key]: return _shape(self,_key) else: return None
[docs] def line(self, p1, p2, layer = 0, thick = "", type = "", color = "", fill = ""): """ .. _shapes_line: **Synopsis:** * Add a line shape or segment between two points **Args:** * p1: segment start point (given by id, alias or point object) * p2: segment end point (given by id, alias or point object) **Optional parameters:** * layer = 0: layer member where the shape belongs * thick = "": line thickness (see :ref:`thick examples <ex_shapes_thick>`) * type = "": type of line (see :ref:`type examples <ex_shapes_type>`) * color = "": color of the line (see :ref:`colors examples <ex_shapes_color>`) * fill = "": fill texture of the line (see :ref:`fill examples <ex_shapes_fill>`) **Returns:** * A line shape object **Chracteristics of a shape line object** :ivar id: get unique id of the shape object :ivar action: get type of shape object :ivar zorder: set/get z position respect the drawing plane and viewer :ivar labels: get labels list that the shape is associated to :ivar addlabel: set/get add a label or list of labels to the shape :ivar dellabel: set/get delete a shape label :ivar comment: multifunctional text field :ivar arrow: set/get arrow parameters (see :ref:`arrow examples <ex_shapes_arrow>`) :ivar arrow_build(start,end,scale): arrow parameter (see :ref:`arrow examples <ex_shapes_arrow>`) :ivar thick: set/get line thickness (see :ref:`thick examples <ex_shapes_thick>`) :ivar type: set/get type of line (see :ref:`type examples <ex_shapes_type>`) :ivar color: set/get color of the line (see :ref:`colors examples <ex_shapes_color>`) :ivar fill: set/get fill texture of the line (see :ref:`fill examples <ex_shapes_fill>`) :ivar addpto: set or add a new points to the line. Get a list of points that form the line. See :ref:`addpto examples <ex_shapes_addpto>` .. note:: * See example of lines in :doc:`shapes examples </_examples/tikzpy_shapes/test_gen>` """ item = self._additem("line", layer = layer) item.addpto = p1 item.addpto = p2 if thick != "": item.thick = thick if type != "": item.type = type if color != "": item.color = color if fill != "": item.fill = fill return item
[docs] def path(self, ptos, layer = 0, thick = "", type = "", color = "", fill = ""): """ .. _shapes_path: **Synopsis:** * Add a path of multiple points connected by a line **Args:** * ptos: multiple points. Can be set as a point, a list o points, an alias or a list of alias. See :ref:`addpto examples <ex_shapes_addpto>` **Optional parameters:** * layer = 0: layer member where the shape belongs * thick = "": path thickness (see :ref:`thick examples <ex_shapes_thick>`) * type = "": type of path (see :ref:`type examples <ex_shapes_type>`) * color = "": color of the path (see :ref:`colors examples <ex_shapes_color>`) * fill = "": fill texture of the path (see :ref:`fill examples <ex_shapes_fill>`) **Returns:** * A path shape object **Chracteristics of a shape line object** :ivar id: get unique id of the shape object :ivar action: get type of shape object :ivar zorder: set/get z position respect the drawing plane and viewer :ivar labels: get labels list that the shape is associated to :ivar addlabel: set/get add a label or list of labels to the shape :ivar dellabel: set/get delete a shape label :ivar comment: multifunctional text field :ivar arrow: set/get arrow parameters (see :ref:`arrow examples <ex_shapes_arrow>`) :ivar arrow_build(start,end,scale): arrow parameter (see :ref:`arrow examples <ex_shapes_arrow>`) :ivar thick: set/get line thickness (see :ref:`thick examples <ex_shapes_thick>`) :ivar type: set/get type of line (see :ref:`type examples <ex_shapes_type>`) :ivar color: set/get color of the line (see :ref:`colors examples <ex_shapes_color>`) :ivar fill: set/get fill texture of the line (see :ref:`fill examples <ex_shapes_fill>`) :ivar addpto: set or add a new points to the line. Get a list of points that form the line. See :ref:`addpto examples <ex_shapes_addpto>` .. note:: * See example of paths in :doc:`shapes examples </_examples/tikzpy_shapes/test_gen>` """ item = self._additem("path", layer = layer) item.addpto = ptos if thick != "": item.thick = thick if type != "": item.type = type if color != "": item.color = color if fill != "": item.fill = fill return item
def rectangle(self, layer = 0): return self._additem("rectangle", layer = layer)
[docs] def circle(self, pto, radius, layer = 0, thick = "", type = "", color = "", fill = ""): """ .. _shapes_circle: **Synopsis:** * Add a circle defined by the centre and the radius **Args:** * pto: point of the centre of the circle (given by id, alias or point object) * radius: radius of the circle **Optional parameters:** * layer = 0: layer member where the shape belongs * thick = "": line thickness (see :ref:`thick examples <ex_shapes_thick>`) * type = "": type of line (see :ref:`type examples <ex_shapes_type>`) * color = "": color of the line (see :ref:`colors examples <ex_shapes_color>`) * fill = "": fill texture of the line (see :ref:`fill examples <ex_shapes_fill>`) **Returns:** * A circle shape object **Chracteristics of a shape circle object** :ivar id: get unique id of the shape object :ivar action: get type of shape object :ivar zorder: set/get z position respect the drawing plane and viewer :ivar labels: get labels list that the shape is associated to :ivar addlabel: set/get add a label or list of labels to the shape :ivar dellabel: set/get delete a shape label :ivar comment: multifunctional text field :ivar arrow: set/get arrow parameters (see :ref:`arrow examples <ex_shapes_arrow>`) :ivar arrow_build(start,end,scale): arrow parameter (see :ref:`arrow examples <ex_shapes_arrow>`) :ivar thick: set/get line thickness (see :ref:`thick examples <ex_shapes_thick>`) :ivar type: set/get type of line (see :ref:`type examples <ex_shapes_type>`) :ivar color: set/get color of the line (see :ref:`colors examples <ex_shapes_color>`) :ivar fill: set/get fill texture of the line (see :ref:`fill examples <ex_shapes_fill>`) .. note:: * See example of circles in :doc:`shapes examples </_examples/tikzpy_shapes/test_gen>` """ item = self._additem("circle", layer = layer) item.radius = float(radius) item.addpto = pto if thick != "": item.thick = thick if type != "": item.type = type if color != "": item.color = color if fill != "": item.fill = fill return item
[docs] def arc(self, start_point, radius, start_angle, end_angle, layer = 0, thick = "", type = "", color = "", fill = ""): """ .. _shapes_arc: **Synopsis:** * Add an arc defined by the starting point, radius, start angle and end angle **Args:** * start_point: starting point of the arc (given by id, alias or point object) * radius: radius of the arc * start_angle: start angle in degrees * end_angle: end angle in degrees **Optional parameters:** * layer = 0: layer member where the shape belongs * thick = "": line thickness (see :ref:`thick examples <ex_shapes_thick>`) * type = "": type of line (see :ref:`type examples <ex_shapes_type>`) * color = "": color of the line (see :ref:`colors examples <ex_shapes_color>`) * fill = "": fill texture of the line (see :ref:`fill examples <ex_shapes_fill>`) **Returns:** * An arc shape object **Chracteristics of a shape arc object** :ivar id: get unique id of the shape object :ivar action: get type of shape object :ivar zorder: set/get z position respect the drawing plane and viewer :ivar labels: get labels list that the shape is associated to :ivar addlabel: set/get add a label or list of labels to the shape :ivar dellabel: set/get delete a shape label :ivar comment: multifunctional text field :ivar arrow: set/get arrow parameters (see :ref:`arrow examples <ex_shapes_arrow>`) :ivar arrow_build(start,end,scale): arrow parameter (see :ref:`arrow examples <ex_shapes_arrow>`) :ivar thick: set/get line thickness (see :ref:`thick examples <ex_shapes_thick>`) :ivar type: set/get type of line (see :ref:`type examples <ex_shapes_type>`) :ivar color: set/get color of the line (see :ref:`colors examples <ex_shapes_color>`) :ivar fill: set/get fill texture of the line (see fill examples) .. note:: * See example of arcs in :doc:`shapes examples </_examples/tikzpy_shapes/test_gen>` """ # Angles in degrees item = self._additem("arc", layer = layer) item.radius = float(radius) item.start_angle = float(start_angle) item.end_angle = float(end_angle) item.addpto = start_point if thick != "": item.thick = thick if type != "": item.type = type if color != "": item.color = color if fill != "": item.fill = fill return item
[docs] def arc_by_center(self, center_point, radius, start_angle, end_angle, layer = 0, thick = "", type = "", color = "", fill = ""): """ .. _shapes_arc_by_center: **Synopsis:** * Add an arc defined by the center point, radius, start angle and end angle **Args:** * center_point: starting point of the arc (given by id, alias or point object) * radius: radius of the arc * start_angle: start angle in degrees * end_angle: end angle in degrees **Optional parameters:** * layer = 0: layer member where the shape belongs * thick = "": line thickness (see :ref:`thick examples <ex_shapes_thick>`) * type = "": type of line (see :ref:`type examples <ex_shapes_type>`) * color = "": color of the line (see :ref:`colors examples <ex_shapes_color>`) * fill = "": fill texture of the line (see :ref:`fill examples <ex_shapes_fill>`) **Returns:** * An arc shape object **Chracteristics of a shape arc object** :ivar id: get unique id of the shape object :ivar action: get type of shape object :ivar zorder: set/get z position respect the drawing plane and viewer :ivar labels: get labels list that the shape is associated to :ivar addlabel: set/get add a label or list of labels to the shape :ivar dellabel: set/get delete a shape label :ivar comment: multifunctional text field :ivar arrow: set/get arrow parameters (see :ref:`arrow examples <ex_shapes_arrow>`) :ivar arrow_build(start,end,scale): arrow parameter (see :ref:`arrow examples <ex_shapes_arrow>`) :ivar thick: set/get line thickness (see :ref:`thick examples <ex_shapes_thick>`) :ivar type: set/get type of line (see :ref:`type examples <ex_shapes_type>`) :ivar color: set/get color of the line (see :ref:`colors examples <ex_shapes_color>`) :ivar fill: set/get fill texture of the line (see fill examples) .. note:: * See example of arcs in :doc:`shapes examples </_examples/tikzpy_shapes/test_gen>` """ # Angles in degrees x = center_point.x + (radius * math.cos(math.radians(start_angle))) y = center_point.y + (radius * math.sin(math.radians(start_angle))) z = center_point.z start_point = self.parent.pto.pto(x=x, y=y, z=z, layer=layer, alias='') return self.arc(start_point, radius, start_angle, end_angle, layer = layer, thick = thick, type = type, color = color, fill = fill)
[docs] def text(self, pto, text, layer = 0, color = "", fill = "", rotate_text = 0, position = "", align = ""): """ .. _shapes_text: **Synopsis:** * Add a text. Referenced by a point position. **Args:** * pto: point reference for the text node * text: text to be added **Optional parameters:** * layer = 0: layer member where the shape belongs * color = "": color of the line (see :ref:`colors examples <ex_shapes_color>`) * fill = "": fill texture of the line (see :ref:`fill examples <ex_shapes_fill>`) * rotate_text = 0.: float number in degrees of text rotation * position = "": "above" , "below" , "left" , "right", "above left" , "below left" , "above right" , "below right" * align = "": "left" , "center" , "right" **Returns:** * An text shape object **Chracteristics of a shape text object** :ivar id: get unique id of the shape object :ivar action: get type of shape object :ivar zorder: set/get z position respect the drawing plane and viewer :ivar labels: get labels list that the shape is associated to :ivar addlabel: set/get add a label or list of labels to the shape :ivar dellabel: set/get delete a shape label :ivar comment: multifunctional text field :ivar color: set/get color of the text (see :ref:`colors examples <ex_shapes_color>`) :ivar fill: set/get fill texture of the text (see :ref:`fill examples <ex_shapes_fill>`) :ivar text: set/get text label :ivar rotate_text: float number in degrees of text rotation :ivar position: options: "above" , "below" , "left" , "right", "above left" , "below left" , "above right" , "below right" :ivar align: options: "left" , "center" , "right" .. note:: * See :ref:`text examples <ex_shapes_text>` """ item = self._additem("text", layer = layer) item.text = text item.addpto = pto if color != "": item.color = color if fill != "": item.fill = fill if rotate_text != 0.: item.rotate_text = rotate_text if position != "": item.position = position if align != "": item.align = align return item
[docs] def bitmap(self, pto, bitmap_path, width = None, height = None, layer = 0, color = "", fill = "", rotate_text = 0, position = "", align = ""): """ .. _shapes_bitmap: **Synopsis:** * Add a bitmap or image file. Referenced by a point position using a node wrapper. **Args:** * pto: point reference for the bitmap node * bitmap_path: path to bitmap or image file (jpg, png, pdf) **Optional parameters:** * width = None: image width, if just width or height is supply the aspect ratio is mantain * height = None: image height, if just width or height is supply the aspect ratio is mantain * layer = 0: layer member where the shape belongs * color = "": color of the line (see :ref:`colors examples <ex_shapes_color>`) * fill = "": fill texture of the line (see :ref:`fill examples <ex_shapes_fill>`) * rotate_text = 0.: float number in degrees of image rotation * position = "": "above" , "below" , "left" , "right", "above left" , "below left" , "above right" , "below right" * align = "": "left" , "center" , "right" **Returns:** * An bitmap shape object **Chracteristics of a shape bitmap object** :ivar id: get unique id of the shape object :ivar action: get type of shape object :ivar zorder: set/get z position respect the drawing plane and viewer :ivar labels: get labels list that the shape is associated to :ivar addlabel: set/get add a label or list of labels to the shape :ivar dellabel: set/get delete a shape label :ivar comment: multifunctional bitmap field :ivar color: set/get color of the bitmap (see :ref:`colors examples <ex_shapes_color>`) :ivar fill: set/get fill texture of the bitmap (see :ref:`fill examples <ex_shapes_fill>`) :ivar text: set/get bitmap path (windows backslash is replace by slash) :ivar rotate_text: float number in degrees of bitmap rotation :ivar position: options: "above" , "below" , "left" , "right", "above left" , "below left" , "above right" , "below right" :ivar align: options: "left" , "center" , "right" .. note:: * See :ref:`bitmap examples <ex_shapes_bitmap>` """ item = self._additem("bitmap", layer = layer) item.text = self._build_bitmap_text(bitmap_path, width, height) item.addpto = pto if color != "": item.color = color if fill != "": item.fill = fill if rotate_text != 0.: item.rotate_text = rotate_text if position != "": item.position = position if align != "": item.align = align return item
def _build_bitmap_text(self, bitmap_path, width, height): ### Build a bitmap text opt = "" if width is not None: opt = "width=%.4f##units##," % width if height is not None: opt = "height=%.4f##units##" % height if not os.path.isfile(bitmap_path): self.error("Bitmap file does not exist", ref = "_build_bitmap_text") txt = r"\includegraphics[%s]{%s}" % (opt, bitmap_path.replace("\\", r"/")) return txt
[docs] def grid(self, corner1_pto, corner2_pto, xstep = 1, ystep = 1, layer = 0, thick = "help lines", type = "", color = ""): """ .. _shapes_grid: **Synopsis:** * Add a renctangular grid defined by the two opposite corner points **Args:** * corner1_pto: point of corner 1 * corner2_pto: point of corner 2 **Optional parameters:** * xstep = 1: grid step in x direction * ystep = 1: grid step in y direction * layer = 0: layer member where the shape belongs * thick = "help lines": line thickness (see :ref:`thick examples <ex_shapes_thick>`) * type = "": type of line (see :ref:`type examples <ex_shapes_type>`) * color = "": color of the line (see :ref:`colors examples <ex_shapes_color>`) **Returns:** * A grid shape object **Chracteristics of a shape line object** :ivar id: get unique id of the shape object :ivar action: get type of shape object :ivar zorder: set/get z position respect the drawing plane and viewer :ivar labels: get labels list that the shape is associated to :ivar addlabel: set/get add a label or list of labels to the shape :ivar dellabel: set/get delete a shape label :ivar comment: multifunctional text field :ivar thick: set/get line thickness (see :ref:`thick examples <ex_shapes_thick>`) :ivar type: set/get type of line (see :ref:`type examples <ex_shapes_type>`) :ivar color: set/get color of the line (see :ref:`colors examples <ex_shapes_color>`) .. note:: * The grid cannot be rotated * See :ref:`grid examples <ex_shapes_grid>` """ item = self._additem("grid", layer = layer) item.step = [1, xstep, ystep] item.addpto = corner1_pto item.addpto = corner2_pto if thick != "": item.thick = thick if type != "": item.type = type if color != "": item.color = color return item
def parabola(self, pto1, ptobend, pto2, layer = 0): item = self._additem("parabola", layer = layer) item.addpto = pto1 item.addpto = ptobend item.addpto = pto2 return item ######################### Translation, copy, rotate
[docs] def copy(self, shps): """ .. _shapes_copy_list: **Synopsis:** * For a list of shapes (give by id or object), it return a list of ids copied shapes **Args:** * shps: list of shapes or single shape. Given by id or shape object. **Optional parameters:** * None **Returns:** * List of unique ids of the copied shapes .. note:: * See example :ref:`copy shapes <ex_groups_ex2>`. """ lst_out = [] if type(shps) is type([]): for shp in shps: id = self._check_shp(shp) _shp = self.getitem(id).copy() lst_out.append( _shp.id ) else: id = self._check_shp(shps) _shp = self.getitem(id).copy() lst_out.append( _shp.id ) return lst_out
[docs] def translate(self, shps, x = 0., y = 0., z = 0.): """ .. _shapes_translate: **Synopsis:** * Translate a shapes of list in a 3D space **Args:** * shps: list of shapes or single shape. Given by id or shape object. **Optional parameters:** * x = 0: increment in x coordinate to tranlate * y = 0: increment in y coordinate to tranlate * z = 0: increment in z coordinate to tranlate **Returns:** * None .. note:: * See example :ref:`translate shapes <ex_groups_ex2>`. """ ### Create auxiliary group grp = self.parent.grp._auxgroup() grp.add = shps ### Translate self.parent.pto.translate(grp.ptos_of_shapes, x = x, y = y, z = z)
[docs] def translate_to(self, shps, pto_ref, pto_ref_final): """ .. _shapes_translate_to: **Synopsis:** * Given a reference point and a final position for such point. Translate a shape or list of shapes in a 3D space in a similar manner. **Args:** * shps: list of shapes or single shape. Given by id or shape object. **Optional parameters:** * pto_ref: reference point * pto_ref_final: final refence point position **Returns:** * None .. note:: * See example :ref:`translate shapes <ex_groups_ex2>`. """ ### Create auxiliary group grp = self.parent.grp._auxgroup() grp.add = shps ### Translate self.parent.pto.translate_to(grp.ptos_of_shapes, pto_ref, pto_ref_final)
[docs] def rotate(self, shps, pto_rotation, Ax = 0., Ay = 0., Az = 0.): """ .. _shapes_rotate: **Synopsis:** * Rotate a shape of list of shapes in a 3D space respect an origin point **Args:** * shps: list of shapes or single shape. Given by id or shape object. * pto_rotation: center point of rotation **Optional parameters:** * Ax = 0.: yaw angle in degrees to turn respect axis X * Ay = 0.: pitch angle in degrees to turn respect axis Y * Az = 0.: roll angle in degrees to turn respect axis Z **Returns:** * None .. note:: * See example :ref:`rotation shapes <ex_groups_ex3>`. """ ### Create auxiliary group grp = self.parent.grp._auxgroup() grp.add = shps ### Translate self.parent.pto.rotate(grp.ptos_of_shapes, pto_rotation, Ax = Ax, Ay = Ay, Az = Az)
######################### def _check_shp(self, val): ### Check type of shape entry if type(val) is self._type_of_shape(): id = val.id else: id = self.getitem(val).id if id is None: self.error("Error key.", ref = "shapes_copy_list") return id def _additem(self, type, layer = 0): # Create auto new _key = "#s%i" % self.counters["shapes"] self.counters["shapes"] = self.counters["shapes"] + 1 self.shapes[_key] = {} lline = _shape(self,_key) lline.layer = layer lline.action = type lline.addlabel = "default" return lline def _type_of_shape(self): ### Returns the type of shape return type(_shape(self,None)) ######################### Mod properties in block
[docs] def zorder_to_shapes(self, shps, value): """ .. _shapes_zorder_to_shapes: **Synopsis:** * Modify zorder value for a shape or list of shapes **Args:** * shps: list of shapes or single shape. Given by id or shape object. * value: zorder value **Returns:** * None .. note:: * zorder: z position respect the drawing plane and viewer """ self._mod_properties(shps, value, "zorder")
[docs] def thick_to_shapes(self, shps, value): """ .. _shapes_thick_to_shapes: **Synopsis:** * Modify thick value for a shape or list of shapes **Args:** * shps: list of shapes or single shape. Given by id or shape object. * value: thick value **Returns:** * None .. note:: * thick: shape thickness (see :ref:`thick examples <ex_shapes_thick>`) """ self._mod_properties(shps, value, "thick")
[docs] def type_to_shapes(self, shps, value): """ .. _shapes_type_to_shapes: **Synopsis:** * Modify type value for a shape or list of shapes **Args:** * shps: list of shapes or single shape. Given by id or shape object. * value: type value **Returns:** * None .. note:: * type: type of shape (see :ref:`type examples <ex_shapes_type>`) """ self._mod_properties(shps, value, "thick")
[docs] def color_to_shapes(self, shps, value): """ .. _shapes_color_to_shapes: **Synopsis:** * Modify color value for a shape or list of shapes **Args:** * shps: list of shapes or single shape. Given by id or shape object. * value: color value **Returns:** * None .. note:: * color: color of the shape (see :ref:`colors examples <ex_shapes_color>`) """ self._mod_properties(shps, value, "color")
[docs] def fill_to_shapes(self, shps, value): """ .. _shapes_fill_to_shapes: **Synopsis:** * Modify fill value for a shape or list of shapes **Args:** * shps: list of shapes or single shape. Given by id or shape object. * value: fill value **Returns:** * None .. note:: * fill: fill texture of the shape (see :ref:`fill examples <ex_shapes_fill>`) """ self._mod_properties(shps, value, "fill")
[docs] def arrow_to_shapes(self, shps, start="latex", end="latex", scale=1.): """ .. _shapes_arrow_to_shapes: **Synopsis:** * Modify arrow tips value for a shape or list of shapes **Args:** * shps: list of shapes or single shape. Given by id or shape object. **Optional parameters:** * start="latex": arrow tip type, at starting side * end="latex": arrow tip type, at end side * scale=1.: arrow scale **Returns:** * None .. note:: * arrow: arrow tips types of the shape (see :ref:`arrow examples <ex_shapes_arrow>`) """ value = [start, end, scale] self._mod_properties(shps, value, "arrow")
def _mod_properties(self, shps, value, txt_property): ### Modifies properties by name def _mode_prop(_shp, txt_property, val, islist): if txt_property == "addlabel": _shp.addlabel = val elif txt_property == "dellabel": _shp.dellabel = val elif txt_property == "zorder": if islist: self.error("Not accept lists of zorder") _shp.zorder = val elif txt_property == "thick": if islist: self.error("Not accept lists of thick") _shp.thick = val elif txt_property == "type": if islist: self.error("Not accept lists of type") _shp.type = val elif txt_property == "color": if islist: self.error("Not accept lists of color") _shp.color = val elif txt_property == "fill": if islist: self.error("Not accept lists of fill") _shp.fill = val elif txt_property == "arrow": if islist: self.error("Not accept lists of arrow") [start, end, scale] = val _shp.arrow_build(start=start, end=end, scale=scale) else: self.error("Wrong labeling ") ### Create auxiliary group grp = self.parent.grp._auxgroup() grp.add = shps ### Iterate for _shp in grp.shps: if type(value) == type([]) and txt_property != "arrow" and txt_property != "fill": for _value in value: _mode_prop(_shp, txt_property, _value, True) else: _mode_prop(_shp, txt_property, value, False) ######################### Labels def _shapes_by_label(self, value): ### Return list of shapes by label or list of labels lst = [] if type(value) == type([]): for _value in value: for key in self.keys(): shp = self.getitem(key) if _value in self.shapes[key]["labels"]: if shp.id not in lst: lst.append(shp.id) else: for key in self.keys(): shp = self.getitem(key) if value in self.shapes[key]["labels"]: if shp.id not in lst: lst.append(shp.id) return lst def _dellabel(self, value): ### Delete a label from all the shapes if value in self.parent.lbl.labels: ### Delete from all the shapes for key in self.keys(): shp = self.getitem(key) shp.dellabel = value ### Remake list self.parent.lbl._dellabel(value) return True else: #log("The label %s has not been previously declared" % value) return False def _rename_label(self, name_old, name_new): ### Rename a label from all the shapes if name_old != name_new: ### Add new name self.parent.lbl.addlabel = name_new ### Delete from all the shapes for key in self.keys(): shp = self.getitem(key) if name_old in self.shapes[key]["labels"]: shp.dellabel = name_old shp.addlabel = name_new ### Remake list self.parent.lbl._dellabel(name_old) return True else: #log("Inconsistent") return False ######################### Builder def _types_patterns(self): v = ["horizontal lines", "vertical lines", "north east lines", "north west lines", "grid", \ "crosshatch", "dots", "crosshatch dots", "fivepointed stars", "sixpointed stars", "bricks", "checkerboard"] return v
[docs] def pattern_build(self, type_pattern, color = ""): """ .. _shapes_pattern_build: **Synopsis:** * Builds a type of pattern fill object that can be set to the fill property **Args:** * type_pattern: type of pattern **Optional parameters:** * color="": color of pattern (see :ref:`colors examples <ex_shapes_color>`) **Returns:** * A pattern object that can be set to the fill property .. note:: * Possible types: horizontal lines, vertical lines, north east lines, north west lines, grid, crosshatch, dots, crosshatch dots, fivepointed stars, sixpointed stars, bricks, checkerboard * See :ref:`fill examples <ex_shapes_fill>` """ _type_pattern = str(type_pattern).lower().strip() if _type_pattern not in self._types_patterns(): self.error("Type of pattern not found") _color = str(color).lower().strip() self.parent.col[_color] = _color lst = ["#pat#", _type_pattern, _color] return lst
class _shape(object): def __init__(self, parent, key): self.parent = parent self._key = key self._draw = False if key is None: return if not self.parent.shapes[key]: self.parent.shapes[self._key]["action"] = "" self.parent.shapes[self._key]["layer"] = 0 self.parent.shapes[self._key]["z-order"] = 0. self.parent.shapes[self._key]["labels"] = [] self.parent.shapes[self._key]["group_label"] = None self.parent.shapes[self._key]["comment"] = "" self.parent.shapes[self._key]["ptos"] = [] self.parent.shapes[self._key]["arrow"] = "" self.parent.shapes[self._key]["thick"] = "" self.parent.shapes[self._key]["type"] = "" self.parent.shapes[self._key]["color"] = "" self.parent.shapes[self._key]["fill"] = "" self.parent.shapes[self._key]["radius"] = 1. self.parent.shapes[self._key]["start_angle"] = 0. self.parent.shapes[self._key]["end_angle"] = 90. self.parent.shapes[self._key]["step"] = [] self.parent.shapes[self._key]["text"] = "" self.parent.shapes[self._key]["position"] = "" self.parent.shapes[self._key]["align"] = "" self.parent.shapes[self._key]["textsize"] = "" self.parent.shapes[self._key]["rotate_text"] = 0. ############################################# def copy(self): shp = self.parent._additem(self.action, layer = self.layer) for key in self.parent.shapes[self.id].keys(): self.parent.shapes[shp.id][key] = copy.deepcopy(self.parent.shapes[self.id][key]) return shp def build_tik_string(self, units = ""): str = "" if self.action == "line" or self.action == "path": opt = "" if self.arrow != "": opt += self.arrow.replace("##units##",units) + " ," if self.thick != "": opt += self.thick.replace("##units##",units) + " ," if self.type != "": opt += self.type.replace("##units##",units) + " ," if self.color != "": opt += self._color_build() + " ," if self.fill != "": opt += self._construct_fill() + " ," if len(opt) > 0: opt = "[" + opt[:-2] + "] " ptos = "" if len(self.addpto) >= 2: for ii in range(0,len(self.addpto)): pto = self.addpto[ii] ptos += "(%.4f%s,%.4f%s,%.4f%s) -- " % (pto.x, units, pto.y, units, pto.z, units) if len(ptos) > 0: ptos = ptos[:-4] else: log("Less than two point for the line %s" % self.id) str = r"\draw %s %s;" % (opt, ptos) #str = r"\path %s %s;" % (opt, ptos) ### Invisible lines, intersection if self.action == "rectangle": opt = "" #if self.arrow != "": opt += self.arrow.replace("##units##",units) + " ," if self.thick != "": opt += self.thick.replace("##units##",units) + " ," if self.type != "": opt += self.type.replace("##units##",units) + " ," if self.color != "": opt += self._color_build() + " ," if self.fill != "": opt += self._construct_fill() + " ," if len(opt) > 0: opt = "[" + opt[:-2] + "] " ptos = "" if len(self.addpto) >= 2: for ii in range(0,2): pto = self.addpto[ii] ptos += "(%.4f%s,%.4f%s,%.4f%s) rectangle " % (pto.x, units, pto.y, units, pto.z, units) if len(ptos) > 0: ptos = ptos[:-11] else: log("Less than two point for the rectangle %s" % self.id) str = r"\draw %s %s;" % (opt, ptos) if self.action == "circle": opt = "" #if self.arrow != "": opt += self.arrow.replace("##units##",units) + " ," if self.thick != "": opt += self.thick.replace("##units##",units) + " ," if self.type != "": opt += self.type.replace("##units##",units) + " ," if self.color != "": opt += self._color_build() + " ," if self.fill != "": opt += self._construct_fill() + " ," if len(opt) > 0: opt = "[" + opt[:-2] + "] " ptos = "" if len(self.addpto) > 0: pto = self.addpto[0] ptos = "(%.4f%s,%.4f%s,%.4f%s) circle [radius=%.4f%s]" % (pto.x, units, pto.y, units, pto.z, units, float(self.radius), units) else: log("Less than one point for the circle %s" % self.id) str = r"\draw %s %s;" % (opt, ptos) if self.action == "arc": opt = "" if self.arrow != "": opt += self.arrow.replace("##units##",units) + " ," if self.thick != "": opt += self.thick.replace("##units##",units) + " ," if self.type != "": opt += self.type.replace("##units##",units) + " ," if self.color != "": opt += self._color_build() + " ," if self.fill != "": opt += self._construct_fill() + " ," if len(opt) > 0: opt = "[" + opt[:-2] + "] " ptos = "" if len(self.addpto) > 0: pto = self.addpto[0] ptos = "(%.4f%s,%.4f%s,%.4f%s) arc [radius=%.4f%s, start angle=%.4f%s, end angle=%.4f%s]" % \ (pto.x, units, pto.y, units, pto.z, units, float(self.radius), units, float(self.start_angle), units, float(self.end_angle), units) else: log("Less than one point for the arc %s" % self.id) str = r"\draw %s %s;" % (opt, ptos) if self.action == "grid": opt = "" #if self.arrow != "": opt += self.arrow.replace("##units##",units) + " ," if self.thick != "": opt += self.thick.replace("##units##",units) + " ," if self.type != "": opt += self.type.replace("##units##",units) + " ," if self.color != "": opt += self._color_build() + " ," #if self.fill != "": opt += self._construct_fill() + " ," if self.step[2] != 0: opt += "ystep=%.4f ," % self.step[2] if self.step[1] != 0: opt += "xstep=%.4f ," % self.step[1] #if self.step[0] != 0: opt += "step=%.4f ," % self.step[0] if len(opt) > 0: opt = "[" + opt[:-2] + "] " ptos = "" if len(self.addpto) >= 2: for ii in range(0,2): pto = self.addpto[ii] ptos += "(%.4f%s,%.4f%s,%.4f%s) grid " % (pto.x, units, pto.y, units, pto.z, units) if len(ptos) > 0: ptos = ptos[:-6] else: log("Less than two point for the grid %s" % self.id) str = r"\draw %s %s;" % (opt, ptos) print(str) if self.action == "text": opt = "" #if self.arrow != "": opt += self.arrow.replace("##units##",units) + " ," #if self.thick != "": opt += self.thick.replace("##units##",units) + " ," #if self.type != "": opt += self.type.replace("##units##",units) + " ," if self.color != "": opt += self._color_build() + " ," if self.fill != "": opt += self._construct_fill() + " ," if self.position != "": opt += self.position + " ," if self.align != "": opt += "align=" + self.align + " ," if self.rotate_text != 0.: opt += "rotate=%.3f" % self.rotate_text+ " ," if len(opt) > 0: opt = "[" + opt[:-2] + "] " ptos = "" if len(self.addpto) >= 1: for ii in range(0,1): pto = self.addpto[ii] ptos += r"(%.4f%s,%.4f%s,%.4f%s) " % (pto.x, units, pto.y, units, pto.z, units) #if len(ptos) > 0: ptos = ptos[:] else: log("Less than one point for the node %s" % self.id) str = r"\node %s at %s {%s};" % (opt, ptos,self.text) if self.action == "bitmap": opt = "" #if self.arrow != "": opt += self.arrow.replace("##units##",units) + " ," #if self.thick != "": opt += self.thick.replace("##units##",units) + " ," #if self.type != "": opt += self.type.replace("##units##",units) + " ," if self.color != "": opt += self._color_build() + " ," if self.fill != "": opt += self._construct_fill() + " ," if self.position != "": opt += self.position + " ," if self.align != "": opt += "align=" + self.align + " ," if self.rotate_text != 0.: opt += "rotate=%.3f" % self.rotate_text+ " ," if len(opt) > 0: opt = "[" + opt[:-2] + "] " ptos = "" if len(self.addpto) >= 1: for ii in range(0,1): pto = self.addpto[ii] ptos += r"(%.4f%s,%.4f%s,%.4f%s) " % (pto.x, units, pto.y, units, pto.z, units) #if len(ptos) > 0: ptos = ptos[:] else: log("Less than one point for the node %s" % self.id) if units == "": txtt = self.text.replace("##units##","cm") else: txtt = self.text.replace("##units##",units) str = r"\node %s at %s {%s};" % (opt, ptos, txtt) if self.action == "parabola": opt = "" if self.arrow != "": opt += self.arrow.replace("##units##",units) + " ," if self.thick != "": opt += self.thick.replace("##units##",units) + " ," if self.type != "": opt += self.type.replace("##units##",units) + " ," if self.color != "": opt += self._color_build() + " ," if self.fill != "": opt += self._construct_fill() + " ," if len(opt) > 0: opt = "[" + opt[:-2] + "] " ptos = "" if len(self.addpto) == 3: pto = self.addpto[0] ptos += "(%.4f%s,%.4f%s,%.4f%s) parabola " % (pto.x, units, pto.y, units, pto.z, units) pto = self.addpto[1] ptos += " bend (%.4f%s,%.4f%s,%.4f%s) " % (pto.x, units, pto.y, units, pto.z, units) pto = self.addpto[2] ptos += "(%.4f%s,%.4f%s,%.4f%s)" % (pto.x, units, pto.y, units, pto.z, units) else: log("Only three points allowed in the parabla %s" % self.id) str = r"\draw%s %s;" % (opt,ptos) return str ############################################# """ id addpto arrow arrow_build(self, start, end, scale) thick type color fill radius start_angle end_angle step text rotate_text position align action layer zorder labels addlabel comment """ @property def id(self): return self._key @property def addpto(self): return self.parent.shapes[self._key]["ptos"] @addpto.setter def addpto(self, value): ### Can be set as a point, a list o points, an alias, a list of alias or ### a list of alias and points objects. Also id of points can be used. ### Add points if type(value) is type([]): for v in value: _pto = self.parent.parent.pto._choices(v) self.parent.shapes[self._key]["ptos"].append(_pto) else: _pto = self.parent.parent.pto._choices(value) self.parent.shapes[self._key]["ptos"].append(_pto) @property def arrow(self): # \usetikzlibrary{arrows} # -> <-> <- |-| <-| |-> <<->> o-stealth [-] (-) # arrow head = 4mm # page 311 pgfmanualCVS2012-11-04 return self.parent.shapes[self._key]["arrow"] @arrow.setter def arrow(self, value): val = str(value).lower().strip() self.parent.shapes[self._key]["arrow"] = val def arrow_build(self, start="latex", end="latex", scale=1.): txt,s,e = "","","" if start != "" and not start is None: s = r"{%s[scale=%.4f##units##]}" % (start, scale) if end != "" and not end is None: e = r"{%s[scale=%.4f##units##]}" % (end, scale) if s != "" or e != "": txt = s + r"-" + e self.parent.shapes[self._key]["arrow"] = txt def line_thick_options(self): lst = ["ultra thin" , "very thin" , "thin" , "semithick" , "thick" , "very thick", "ultra thick", "help lines"] return lst @property def thick(self): # \usetikzlibrary{arrows} # ultra thin , very thin , thin , semithick , thick , very thick and ultra thick # line width=0.2cm # page 311 pgfmanualCVS2012-11-04 return self.parent.shapes[self._key]["thick"] @thick.setter def thick(self, value): lst = self.line_thick_options() val = str(value).lower().strip() if val in lst: self.parent.shapes[self._key]["thick"] = val elif val == "": pass else: self.parent.shapes[self._key]["thick"] = "line width = %.4f##units##" % float(val) def line_type_options(self): lst = ["solid", "dashed" , "dotted" , "dashdotted" , "dashdotdotted"] lst = lst + ["densely dashed" , "densely dotted" , "densely dashdotted" , "densely dashdotdotted"] lst = lst + ["loosely dashed" , "loosely dotted" , "loosely dashdotted" , "loosely dashdotdotted"] lst2 = ["zigzag", "random", "saw", "snake"] return lst, lst2 @property def type(self): # \usetikzlibrary{decorations, decorations.pathmorphing} # page 159 pgfmanualCVS2012-11-04 # page 377 pgfmanualCVS2012-11-04 return self.parent.shapes[self._key]["type"] @type.setter def type(self, value): lst,lst2 = self.line_type_options() val = str(value).lower().strip() if val in lst: self.parent.shapes[self._key]["type"] = val elif val in lst2: if val == "zigzag": val2 = "decorate, decoration={straight zigzag}" if val == "random": val2 = "decorate, decoration={random steps}" if val == "saw": val2 = "decorate, decoration={saw}" if val == "snake": val2 = "decorate, decoration={snake}" self.parent.shapes[self._key]["type"] = val2 elif (val.split("_")[0] in lst2) and len(val.split("_")) == 2: if val.split("_")[0] == "zigzag": val2 = "decorate, decoration={straight zigzag, segment length=%s##units##}" % val.split("_")[1] if val.split("_")[0] == "random": val2 = "decorate, decoration={random steps, segment length=%s##units##}" % val.split("_")[1] if val.split("_")[0] == "saw": val2 = "decorate, decoration={saw, segment length=%s##units##}" % val.split("_")[1] if val.split("_")[0] == "snake": val2 = "decorate, decoration={snake, segment length=%s##units##}" % val.split("_")[1] self.parent.shapes[self._key]["type"] = val2 elif (val.split("_")[0] in lst2) and len(val.split("_")) == 3: if val.split("_")[0] == "zigzag": val2 = "decorate, decoration={straight zigzag, segment length=%s##units##, amplitude=%s##units##}" % (val.split("_")[1], val.split("_")[2]) if val.split("_")[0] == "random": val2 = "decorate, decoration={random steps, segment length=%s##units##, amplitude=%s##units##}" % (val.split("_")[1], val.split("_")[2]) if val.split("_")[0] == "saw": val2 = "decorate, decoration={saw, segment length=%s##units##, amplitude=%s##units##}" % (val.split("_")[1], val.split("_")[2]) if val.split("_")[0] == "snake": val2 = "decorate, decoration={snake, segment length=%s##units##, amplitude=%s##units##}" % (val.split("_")[1], val.split("_")[2]) self.parent.shapes[self._key]["type"] = val2 else: self.parent.shapes[self._key]["type"] = val @property def color(self): # name, black!30, green!20!white, 255_255_255, 255_255_255_0, custom defined in colors return self.parent.shapes[self._key]["color"] def _color_build(self): return self.parent.parent.col[self.color] @color.setter def color(self, value): val = str(value).lower().strip() self.parent.parent.col[val] = val self.parent.shapes[self._key]["color"] = val @property def fill(self): # name, black!30, green!20!white # return self.parent.shapes[self._key]["fill"] @fill.setter def fill(self, value): if type(value) == type([]): if value[0] == "#pat#": ### Pattern case self.parent.shapes[self._key]["fill"] = value elif value[0] == "#sha#": ### Shade case self.parent.shapes[self._key]["fill"] = value else: self.parent.error("Wrong fill type, %s" % value) else: ### Color type val = str(value).lower().strip() self.parent.parent.col[val] = val self.parent.shapes[self._key]["fill"] = val def _construct_fill(self): value = self.parent.shapes[self._key]["fill"] _out = "" if type(value) == type([]): if value[0] == "#pat#": ### Pattern case [nn, _type_pattern, _color] = value if _color != "": _out = "pattern color=%s ," % self.parent.parent.col[_color] _out += "pattern=%s" % _type_pattern elif value[0] == "#sha#": ### Shade case pass else: self.parent.error("Wrong fill type, %s" % value) else: _out = " fill= " + self.parent.parent.col[value] + " ," return _out @property def action(self): return self.parent.shapes[self._key]["action"] @action.setter def action(self, value): self.parent.shapes[self._key]["action"] = value @property def layer(self): return self.parent.shapes[self._key]["layer"] @layer.setter def layer(self, value): self.parent.shapes[self._key]["layer"] = float(value) @property def zorder(self): return self.parent.shapes[self._key]["z-order"] @zorder.setter def zorder(self, value): self.parent.shapes[self._key]["z-order"] = float(value) @property def comment(self): return self.parent.shapes[self._key]["comment"] @comment.setter def comment(self, value): self.parent.shapes[self._key]["comment"] = str(value) @property def radius(self): return float(self.parent.shapes[self._key]["radius"]) @radius.setter def radius(self, value): self.parent.shapes[self._key]["radius"] = float(value) @property def start_angle(self): return float(self.parent.shapes[self._key]["start_angle"]) @start_angle.setter def start_angle(self, value): self.parent.shapes[self._key]["start_angle"] = float(value) @property def end_angle(self): return float(self.parent.shapes[self._key]["end_angle"]) @end_angle.setter def end_angle(self, value): self.parent.shapes[self._key]["end_angle"] = float(value) @property def step(self): return self.parent.shapes[self._key]["step"] @step.setter def step(self, value): if type(value) == type([]): self.parent.shapes[self._key]["step"] = value else: log("Step is not a list") ############### Labels @property def _labels(self): return self.parent.shapes[self._key]["labels"] @property def labels(self): return copy.deepcopy(self.parent.shapes[self._key]["labels"]) @property def addlabel(self): return self.labels @addlabel.setter def addlabel(self, value): if type(value) is type([]): for val in value: self.addlabel = val else: if value in self.parent.parent.lbl.labels: if not value in self.parent.shapes[self._key]["labels"]: self.parent.shapes[self._key]["labels"].append(value) return True else: self.parent.parent.lbl.addlabel = value if not value in self.parent.shapes[self._key]["labels"]: self.parent.shapes[self._key]["labels"].append(value) return True @property def dellabel(self): return self.labels @dellabel.setter def dellabel(self, value): if value in self.parent.parent.lbl.labels: if value in self.parent.shapes[self._key]["labels"]: newlst = [x for x in self.labels if x != value] self.parent.shapes[self._key]["labels"] = newlst return True else: #log("The label %s has not been added before" % value) return False else: #log("The label %s has not been previously declared" % value) return False ############### Text @property def text(self): return self.parent.shapes[self._key]["text"] @text.setter def text(self, value): self.parent.shapes[self._key]["text"] = str(value) @property def rotate_text(self): return self.parent.shapes[self._key]["rotate_text"] @rotate_text.setter def rotate_text(self, value): ### float number in degrees self.parent.shapes[self._key]["rotate_text"] = float(value) def positions_options(self): lst = ["above" , "below" , "left" , "right", "above left" , "below left" , "above right" , "below right"] return lst @property def position(self): return self.parent.shapes[self._key]["position"] @position.setter def position(self, value): lst = self.positions_options() if value in lst: self.parent.shapes[self._key]["position"] = value else: if isinstance(value, numbers.Real): if int(value) >= 0 and int(value) < len(lst): self.parent.shapes[self._key]["position"] = lst[int(value)] else: self.parent.error("Wrong position type, %s" % value) else: self.parent.error("Wrong position type, %s" % value) def align_options(self): lst = ["left" , "center" , "right"] return lst @property def align(self): return self.parent.shapes[self._key]["align"] @align.setter def align(self, value): lst = self.align_options() if value in lst: self.parent.shapes[self._key]["align"] = value else: if isinstance(value, numbers.Real): if int(value) >= 0 and int(value) < len(lst): self.parent.shapes[self._key]["align"] = lst[int(value)] else: self.parent.error("Wrong align type, %s" % value) else: self.parent.error("Wrong align type, %s" % value) def __repr__(self): return self.id def __str__(self): return "Line key:%s layer=%i points=%i NumLines:%i" % (self.id, self.layer, len(self.addpto), len(self.parent.shapes))