diff --git a/MANIFEST.in b/MANIFEST.in index 64da3539690..1476d7ca4d0 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -11,7 +11,6 @@ recursive-include Orange/widgets *.png *.svg *.js *.css *.html recursive-include Orange/widgets/tests *.tab recursive-include Orange/widgets/data/tests *.tab recursive-include Orange/widgets/tests/workflows *.ows -recursive-include Orange/widgets/utils/plot *.fs *.vs *.gs *.obj recursive-include distribute *.svg *.desktop diff --git a/Orange/widgets/gui.py b/Orange/widgets/gui.py index b4f6834fde1..4bcac5b2f77 100644 --- a/Orange/widgets/gui.py +++ b/Orange/widgets/gui.py @@ -9,12 +9,12 @@ import warnings import logging from types import LambdaType -from collections import defaultdict +from collections import defaultdict, Sequence import pkg_resources from AnyQt import QtWidgets, QtCore, QtGui -from AnyQt.QtCore import Qt, QSize, pyqtSignal as Signal +from AnyQt.QtCore import Qt, QSize, QItemSelection, pyqtSignal as Signal from AnyQt.QtGui import QCursor, QColor from AnyQt.QtWidgets import ( QApplication, QStyle, QSizePolicy, QWidget, QLabel, QGroupBox, QSlider, @@ -1084,7 +1084,7 @@ def listView(widget, master, value=None, model=None, box=None, callback=None, connectControl(master, value, callback, view.selectionModel().selectionChanged, CallFrontListView(view), - CallBackListView(model, master, value)) + CallBackListView(model, view, master, value)) misc.setdefault('addSpace', True) miscellanea(view, bg, widget, **misc) return view @@ -2270,20 +2270,25 @@ def __call__(self, *value): class CallBackListView(ControlledCallback): - def __init__(self, model, widget, attribute): + def __init__(self, model, view, widget, attribute): super().__init__(widget, attribute) self.model = model + self.view = view # triggered by selectionModel().selectionChanged() - def __call__(self, newSelection, _): + def __call__(self, *_): # This must be imported locally to avoid circular imports from Orange.widgets.utils.itemmodels import PyListModel - indexes = newSelection.indexes() - if indexes: - value = newSelection.indexes()[0].row() + values = [i.row() + for i in self.view.selectionModel().selection().indexes()] + if values: + # FIXME: irrespective of PyListModel check, this might/should always + # callback with values! if isinstance(self.model, PyListModel): - value = self.model[value] - self.acyclic_setattr(value) + values = [self.model[i] for i in values] + if self.view.selectionMode() == self.view.SingleSelection: + values = values[0] + self.acyclic_setattr(values) class CallBackListBox: @@ -2467,22 +2472,28 @@ def action(self, value): class CallFrontListView(ControlledCallFront): - def action(self, value): - model = self.control.model() - if not isinstance(value, int): - if isinstance(value, str): - search_role = Qt.DisplayRole - elif isinstance(value, Variable): - search_role = TableVariable - else: - search_role = Qt.DisplayRole - value = str(value) - for i in range(model.rowCount()): - if model.data(model.index(i), search_role) == value: - value = i - break - sel_model = self.control.selectionModel() - sel_model.select(model.index(value), sel_model.ClearAndSelect) + def action(self, values): + view = self.control + model = view.model() + sel_model = view.selectionModel() + + if not isinstance(values, Sequence): + values = [values] + + selection = QItemSelection() + for value in values: + if not isinstance(value, int): + if isinstance(value, Variable): + search_role = TableVariable + else: + search_role = Qt.DisplayRole + value = str(value) + for i in range(model.rowCount()): + if model.data(model.index(i), search_role) == value: + value = i + break + selection.select(model.index(value), model.index(value)) + sel_model.select(selection, sel_model.ClearAndSelect) class CallFrontListBox(ControlledCallFront): diff --git a/Orange/widgets/tests/test_gui.py b/Orange/widgets/tests/test_gui.py index ff8e17ff075..a0616440086 100644 --- a/Orange/widgets/tests/test_gui.py +++ b/Orange/widgets/tests/test_gui.py @@ -17,25 +17,62 @@ def test_checked_extension(self): class TestListModel(GuiTest): - def test_select(self): - widget = OWWidget() - widget.foo = None + def setUp(self): + self.widget = OWWidget() + self.widget.foo = None self.attrs = VariableListModel() - view = gui.listView(widget.controlArea, widget, "foo", model=self.attrs) + self.view = gui.listView( + self.widget.controlArea, self.widget, "foo", model=self.attrs) + + def test_select_callback(self): + widget = self.widget + view = self.view + self.assertIsNone(widget.foo) + a, b, c = (ContinuousVariable(x) for x in "abc") self.attrs[:] = [a, b, c] + view.setCurrentIndex(self.attrs.index(0, 0)) self.assertIs(widget.foo, a) view.setCurrentIndex(self.attrs.index(2, 0)) self.assertIs(widget.foo, c) + view.setSelectionMode(view.MultiSelection) + sel_model = view.selectionModel() + sel_model.clear() + view.setCurrentIndex(self.attrs.index(1, 0)) + self.assertEqual(widget.foo, [b]) + + def test_select_callfront(self): + widget = self.widget + view = self.view + + a, b, c = (ContinuousVariable(x) for x in "abc") + self.attrs[:] = [a, b, c] + widget.foo = b selection = view.selectedIndexes() self.assertEqual(len(selection), 1) self.assertEqual(selection[0].row(), 1) -class ComboBoxText(GuiTest): + view.setSelectionMode(view.MultiSelection) + widget.foo = [a, c] + selection = view.selectedIndexes() + self.assertEqual(len(selection), 2) + self.assertEqual({selection[0].row(), selection[1].row()}, {0, 2}) + + widget.foo = [] + selection = view.selectedIndexes() + self.assertEqual(len(selection), 0) + + widget.foo = [2, "b"] + selection = view.selectedIndexes() + self.assertEqual(len(selection), 2) + self.assertEqual({selection[0].row(), selection[1].row()}, {1, 2}) + + +class ComboBoxTest(GuiTest): def test_set_initial_value(self): widget = OWWidget() variables = [ContinuousVariable(x) for x in "abc"] diff --git a/Orange/widgets/utils/__init__.py b/Orange/widgets/utils/__init__.py index 71068075980..95d44b9719f 100644 --- a/Orange/widgets/utils/__init__.py +++ b/Orange/widgets/utils/__init__.py @@ -37,15 +37,6 @@ def to_html(str): getHtmlCompatibleString = to_html -def checksum(x): - if x is None: - return None - try: - return x.checksum() - except: - return float('nan') - - def get_variable_values_sorted(variable): """ Return a list of sorted values for given attribute, if all its values can be diff --git a/Orange/widgets/utils/plot/__init__.py b/Orange/widgets/utils/plot/__init__.py index d152dcbf5b7..090c6f3f3bb 100644 --- a/Orange/widgets/utils/plot/__init__.py +++ b/Orange/widgets/utils/plot/__init__.py @@ -1,27 +1,10 @@ """ +Plot classes and tools that were once used in Orange widgets -************************* -Plot classes and tools for use in Orange widgets -************************* - -The main class of this module is :obj:`.OWPlot`, from which all plots -in visualization widgets should inherit. - -This module also contains plot elements, which are normally used by the :obj:`.OWPlot`, -but can also be used directly or subclassed - +Due to lack of maintenance (non-functioning), the majority of it has been +stripped in this commit """ from .owplotgui import * from .owpalette import * from .owconstants import * - -try: - from .owcurve import * - from .owpoint import * - from .owlegend import * - from .owaxis import * - from .owplot import * - from .owtools import * -except (ImportError, RuntimeError): - pass diff --git a/Orange/widgets/utils/plot/generator.gs b/Orange/widgets/utils/plot/generator.gs deleted file mode 100644 index e0e21fa637d..00000000000 --- a/Orange/widgets/utils/plot/generator.gs +++ /dev/null @@ -1,96 +0,0 @@ -#version 150 - -layout(points) in; -layout(triangle_strip, max_vertices=144) out; - -uniform int x_index; -uniform int y_index; -uniform int z_index; -uniform int color_index; -uniform int symbol_index; -uniform int size_index; - -uniform bool use_2d_symbols; - -uniform float jitter_size; -uniform bool jitter_continuous; -uniform bool x_discrete; -uniform bool y_discrete; -uniform bool z_discrete; - -uniform samplerBuffer symbol_buffer; -uniform samplerBuffer data_buffer; - -uniform int num_symbols_used; -uniform int[20] symbols_indices; -uniform int[20] symbols_sizes; -uniform int example_size; - -// Colors are specified in case of a discrete attribute. -uniform int num_colors; -uniform vec3[50] colors; - -out vec3 out_position; -out vec3 out_offset; -out vec3 out_color; -out vec3 out_normal; -out float out_index; - -// http://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl -// Should return pseudo-random value [-0.5, 0.5] -float rand(vec3 co){ - return fract(sin(dot(co.xyz, vec3(12.9898, 78.233, 42.42))) * 43758.5453) - 0.5; -} - -void main() -{ - vec4 position = gl_in[0].gl_Position; - const float scale = 0.001; - - out_index = position.x; - int index = int(out_index * example_size); - - out_position = vec3(texelFetch(data_buffer, index+x_index).x, - texelFetch(data_buffer, index+y_index).x, - texelFetch(data_buffer, index+z_index).x); - - if (x_discrete || jitter_continuous) - out_position.x += rand(out_position * out_index) * jitter_size / 100.; - if (y_discrete || jitter_continuous) - out_position.y += rand(out_position * out_index) * jitter_size / 100.; - if (z_discrete || jitter_continuous) - out_position.z += rand(out_position * out_index) * jitter_size / 100.; - - int symbol = 0; - if (num_symbols_used > 1 && symbol_index > -1) - symbol = clamp(int(texelFetch(data_buffer, index+symbol_index).x * num_symbols_used), 0, 9); - if (!use_2d_symbols) - symbol += 10; - - float size = texelFetch(data_buffer, index+size_index).x; - if (size_index < 0 || size < 0.) - size = 1.; - - float color = texelFetch(data_buffer, index+color_index).x; - if (num_colors > 0) - out_color = colors[int(color*num_colors)]; - else if (color_index > -1) - out_color = vec3(0., 0., color); - else - out_color = vec3(0., 0., 0.8); - - for (int i = 0; i < symbols_sizes[symbol]; ++i) - { - out_offset = texelFetch(symbol_buffer, symbols_indices[symbol]+i*6+0).xyz * size * scale; - out_normal = texelFetch(symbol_buffer, symbols_indices[symbol]+i*6+3).xyz; - EmitVertex(); - out_offset = texelFetch(symbol_buffer, symbols_indices[symbol]+i*6+1).xyz * size * scale; - out_normal = texelFetch(symbol_buffer, symbols_indices[symbol]+i*6+4).xyz; - EmitVertex(); - out_offset = texelFetch(symbol_buffer, symbols_indices[symbol]+i*6+2).xyz * size * scale; - out_normal = texelFetch(symbol_buffer, symbols_indices[symbol]+i*6+5).xyz; - EmitVertex(); - - EndPrimitive(); - } -} diff --git a/Orange/widgets/utils/plot/generator.vs b/Orange/widgets/utils/plot/generator.vs deleted file mode 100644 index 36dd599aa98..00000000000 --- a/Orange/widgets/utils/plot/generator.vs +++ /dev/null @@ -1,8 +0,0 @@ -#version 150 - -in float index; - -void main() -{ - gl_Position = vec4(index, 0., 0., 0.); -} diff --git a/Orange/widgets/utils/plot/owaxis.py b/Orange/widgets/utils/plot/owaxis.py deleted file mode 100644 index c17eb8bae62..00000000000 --- a/Orange/widgets/utils/plot/owaxis.py +++ /dev/null @@ -1,372 +0,0 @@ -""" - The Axis class display an axis on a graph - - The axis contains a line with configurable style, possible arrows, and a title - - .. attribute:: line_style - The LineStyle with which the axis line is drawn - - .. attribute:: title - The string to be displayed alongside the axis - - .. attribute:: title_above - A boolean which specifies whether the title should be placed above or below the axis - Normally the title would be above for top and left axes. - - .. attribute:: title_location - can either be AxisStart, AxisEnd or AxisMiddle. The default is AxisMiddle - - .. attribute:: arrows - A bitfield containing AxisEnd if an arrow should be drawn at the line's end (line.p2()) - and AxisStart if there should be an arrows at the first point. - - By default, there's an arrow at the end of the line - - .. attribute:: zoomable - If this is set to True, the axis line will zoom together with the rest of the graph. - Otherwise, the line will remain in place and only tick marks will zoom. - - .. method:: make_title - Makes a pretty title, with the quantity title in italics and the unit in normal text - - .. method:: label_pos - Controls where the axis title and tick marks are placed relative to the axis -""" - -from math import * - -from AnyQt.QtWidgets import ( - QGraphicsItem, QGraphicsLineItem, QGraphicsTextItem, QGraphicsPathItem, - QGraphicsRectItem, ) -from AnyQt.QtGui import QPainterPath, QTransform, QPen, QFontMetrics -from AnyQt.QtCore import QLineF, QPointF, QRectF, Qt - -from .owconstants import * -from .owtools import resize_plot_item_list -from .owpalette import OWPalette - - -class OWAxis(QGraphicsItem): - Role = OWPalette.Axis - - def __init__(self, id, title='', title_above=False, title_location=AxisMiddle, - line=None, arrows=0, plot=None, bounds=None): - QGraphicsItem.__init__(self) - self.setFlag(QGraphicsItem.ItemHasNoContents) - self.setZValue(AxisZValue) - self.id = id - self.title = title - self.title_location = title_location - self.data_line = line - self.plot = plot - self.graph_line = None - self.size = None - self.scale = None - self.tick_length = (10, 5, 0) - self.arrows = arrows - self.title_above = title_above - self.line_item = QGraphicsLineItem(self) - self.title_item = QGraphicsTextItem(self) - self.end_arrow_item = None - self.start_arrow_item = None - self.show_title = False - self.scale = None - path = QPainterPath() - path.setFillRule(Qt.WindingFill) - path.moveTo(0, 3.09) - path.lineTo(0, -3.09) - path.lineTo(9.51, 0) - path.closeSubpath() - self.arrow_path = path - self.label_items = [] - self.label_bg_items = [] - self.tick_items = [] - self._ticks = [] - self.zoom_transform = QTransform() - self.labels = None - self.values = None - self._bounds = bounds - self.auto_range = None - self.auto_scale = True - - self.zoomable = False - self.update_callback = None - self.max_text_width = 50 - self.text_margin = 5 - self.always_horizontal_text = False - - @staticmethod - def compute_scale(min, max): - magnitude = int(3 * log10(abs(max - min)) + 1) - if magnitude % 3 == 0: - first_place = 1 - elif magnitude % 3 == 1: - first_place = 2 - else: - first_place = 5 - magnitude = magnitude // 3 - 1 - step = first_place * pow(10, magnitude) - first_val = ceil(min / step) * step - return first_val, step - - def update_ticks(self): - self._ticks = [] - major, medium, minor = self.tick_length - if self.labels is not None and not self.auto_scale: - values = self.values or range(len(self.labels)) - for i, text in zip(values, self.labels): - self._ticks.append((i, text, medium, 1)) - else: - if self.scale and not self.auto_scale: - min, max, step = self.scale - elif self.auto_range: - min, max = self.auto_range - if min is not None and max is not None: - step = (max - min) / 10 - else: - return - else: - return - - if max == min: - return - - val, step = self.compute_scale(min, max) - while val <= max: - self._ticks.append((val, "%.4g" % val, medium, step)) - val += step - - def update_graph(self): - if self.update_callback: - self.update_callback() - - def update(self, zoom_only=False): - self.update_ticks() - line_color = self.plot.color(OWPalette.Axis) - text_color = self.plot.color(OWPalette.Text) - if not self.graph_line or not self.scene(): - return - self.line_item.setLine(self.graph_line) - self.line_item.setPen(line_color) - if self.title: - self.title_item.setHtml('' + self.title + '') - self.title_item.setDefaultTextColor(text_color) - if self.title_location == AxisMiddle: - title_p = 0.5 - elif self.title_location == AxisEnd: - title_p = 0.95 - else: - title_p = 0.05 - title_pos = self.graph_line.pointAt(title_p) - v = self.graph_line.normalVector().unitVector() - - dense_text = False - if hasattr(self, 'title_margin'): - offset = self.title_margin - elif self._ticks: - if self.should_be_expanded(): - offset = 55 - dense_text = True - else: - offset = 35 - else: - offset = 10 - - if self.title_above: - title_pos += (v.p2() - v.p1()) * (offset + QFontMetrics(self.title_item.font()).height()) - else: - title_pos -= (v.p2() - v.p1()) * offset - ## TODO: Move it according to self.label_pos - self.title_item.setVisible(self.show_title) - self.title_item.setRotation(-self.graph_line.angle()) - c = self.title_item.mapToParent(self.title_item.boundingRect().center()) - tl = self.title_item.mapToParent(self.title_item.boundingRect().topLeft()) - self.title_item.setPos(title_pos - c + tl) - - ## Arrows - if not zoom_only: - if self.start_arrow_item: - self.scene().removeItem(self.start_arrow_item) - self.start_arrow_item = None - if self.end_arrow_item: - self.scene().removeItem(self.end_arrow_item) - self.end_arrow_item = None - - if self.arrows & AxisStart: - if not zoom_only or not self.start_arrow_item: - self.start_arrow_item = QGraphicsPathItem(self.arrow_path, self) - self.start_arrow_item.setPos(self.graph_line.p1()) - self.start_arrow_item.setRotation(-self.graph_line.angle() + 180) - self.start_arrow_item.setBrush(line_color) - self.start_arrow_item.setPen(line_color) - if self.arrows & AxisEnd: - if not zoom_only or not self.end_arrow_item: - self.end_arrow_item = QGraphicsPathItem(self.arrow_path, self) - self.end_arrow_item.setPos(self.graph_line.p2()) - self.end_arrow_item.setRotation(-self.graph_line.angle()) - self.end_arrow_item.setBrush(line_color) - self.end_arrow_item.setPen(line_color) - - ## Labels - - n = len(self._ticks) - resize_plot_item_list(self.label_items, n, QGraphicsTextItem, self) - resize_plot_item_list(self.label_bg_items, n, QGraphicsRectItem, self) - resize_plot_item_list(self.tick_items, n, QGraphicsLineItem, self) - - test_rect = QRectF(self.graph_line.p1(), self.graph_line.p2()).normalized() - test_rect.adjust(-1, -1, 1, 1) - - n_v = self.graph_line.normalVector().unitVector() - if self.title_above: - n_p = n_v.p2() - n_v.p1() - else: - n_p = n_v.p1() - n_v.p2() - l_v = self.graph_line.unitVector() - l_p = l_v.p2() - l_v.p1() - for i in range(n): - pos, text, size, step = self._ticks[i] - hs = 0.5 * step - tick_pos = self.map_to_graph(pos) - if not test_rect.contains(tick_pos): - self.tick_items[i].setVisible(False) - self.label_items[i].setVisible(False) - continue - item = self.label_items[i] - item.setVisible(True) - if not zoom_only: - if self.id in XAxes or getattr(self, 'is_horizontal', False): - item.setHtml('
' + Qt.escape(text.strip()) + '
') - else: - item.setHtml(Qt.escape(text.strip())) - - item.setTextWidth(-1) - text_angle = 0 - if dense_text: - w = min(item.boundingRect().width(), self.max_text_width) - item.setTextWidth(w) - if self.title_above: - label_pos = tick_pos + n_p * (w + self.text_margin) + l_p * item.boundingRect().height() / 2 - else: - label_pos = tick_pos + n_p * self.text_margin + l_p * item.boundingRect().height() / 2 - text_angle = -90 if self.title_above else 90 - else: - w = min(item.boundingRect().width(), - QLineF(self.map_to_graph(pos - hs), self.map_to_graph(pos + hs)).length()) - label_pos = tick_pos + n_p * self.text_margin + l_p * item.boundingRect().height() / 2 - item.setTextWidth(w) - - if not self.always_horizontal_text: - if self.title_above: - item.setRotation(-self.graph_line.angle() - text_angle) - else: - item.setRotation(self.graph_line.angle() - text_angle) - - item.setPos(label_pos) - item.setDefaultTextColor(text_color) - - self.label_bg_items[i].setRect(item.boundingRect()) - self.label_bg_items[i].setPen(QPen(Qt.NoPen)) - self.label_bg_items[i].setBrush(self.plot.color(OWPalette.Canvas)) - - item = self.tick_items[i] - item.setVisible(True) - tick_line = QLineF(v) - tick_line.translate(-tick_line.p1()) - tick_line.setLength(size) - if self.title_above: - tick_line.setAngle(tick_line.angle() + 180) - item.setLine(tick_line) - item.setPen(line_color) - item.setPos(self.map_to_graph(pos)) - - @staticmethod - def make_title(label, unit=None): - lab = '' + label + '' - if unit: - lab = lab + ' [' + unit + ']' - return lab - - def set_line(self, line): - self.graph_line = line - self.update() - - def set_title(self, title): - self.title = title - self.update() - - def set_show_title(self, b): - self.show_title = b - self.update() - - def set_labels(self, labels, values): - self.labels = labels - self.values = values - self.graph_line = None - self.auto_scale = False - self.update_ticks() - self.update_graph() - - def set_scale(self, min, max, step_size): - self.scale = (min, max, step_size) - self.graph_line = None - self.auto_scale = False - self.update_ticks() - self.update_graph() - - def set_tick_length(self, minor, medium, major): - self.tick_length = (minor, medium, major) - self.update() - - def map_to_graph(self, x): - min, max = self.plot.bounds_for_axis(self.id) - if min == max: - return QPointF() - line_point = self.graph_line.pointAt((x - min) / (max - min)) - end_point = line_point * self.zoom_transform - return self.projection(end_point, self.graph_line) - - @staticmethod - def projection(point, line): - norm = line.normalVector() - norm.translate(point - norm.p1()) - p = QPointF() - type = line.intersect(norm, p) - return p - - def continuous_labels(self): - min, max, step = self.scale - magnitude = log10(abs(max - min)) - - def paint(self, painter, option, widget): - pass - - def boundingRect(self): - return QRectF() - - def ticks(self): - if not self._ticks: - self.update_ticks() - return self._ticks - - def bounds(self): - if self._bounds: - return self._bounds - if self.labels: - return -0.2, len(self.labels) - 0.8 - elif self.scale: - min, max, _step = self.scale - return min, max - elif self.auto_range: - return self.auto_range - else: - return 0, 1 - - def set_bounds(self, value): - self._bounds = value - - def should_be_expanded(self): - self.update_ticks() - return self.id in YAxes or self.always_horizontal_text or sum( - len(t[1]) for t in self._ticks) * 12 > self.plot.width() - diff --git a/Orange/widgets/utils/plot/owcurve.py b/Orange/widgets/utils/plot/owcurve.py deleted file mode 100644 index 4ee9adbec1c..00000000000 --- a/Orange/widgets/utils/plot/owcurve.py +++ /dev/null @@ -1,267 +0,0 @@ -''' -############################## -Curve (``owcurve``) -############################## - -.. class:: OWPlotItem - - This class represents a base for any item than can be added to a plot. - - .. method:: attach(plot) - - Attaches this item to ``plot``. The Plot takes the ownership of this item. - - :param plot: the plot to which to add this item - :type plot: :obj:`.OWPlot` - - :seealso: :meth:`.OWPlot.add_item`. - - .. method:: detach() - - Removes this item from its plot. The item's ownership is returned to Python. - - .. method:: plot() - - :returns: The plot this item is attached to. If the item is not attached to any plot, ``None`` is returned. - :rtype: :obj:`.OWPlot` - - .. method:: data_rect() - - Returns the bounding rectangle of this item in data coordinates. This method is used in autoscale calculations. - - .. method:: set_data_rect(rect) - - :param rect: The new bounding rectangle in data coordinates - :type rect: :obj:`.QRectF` - - .. method:: set_graph_transform(transform) - - Sets the graph transform (the transformation that maps from data to plot coordinates) for this item. - - .. method:: graph_transform() - - :returns: The current graph transformation. - :rtype: QTransform - - .. method:: set_zoom_transform(transform) - - Sets the zoom transform (the transformation that maps from plot to scene coordinates) for this item. - - .. method:: zoom_transform() - - :returns: The current zoom transformation. - :rtype: QTransform - - .. method:: set_axes(x_axis, y_axis) - - Sets the pair of axes used for positioning this item. - - .. method:: axes() - - :returns: The item's pair of axes - :rtype: tuple of int int - - .. method:: update_properties() - - Called by the plot, this function is supposed to updates the item's internal state to match its settings. - - The default implementation does nothing and shold be reimplemented by subclasses. - - .. method:: register_points() - - If this item constains any points (of type :obj:`.OWPoint`), add them to the plot in this function. - - The default implementation does nothing. - - .. method:: set_in_background(background) - - If ``background`` is ``True``, the item is moved to be background of this plot, behind other items and axes. - Otherwise, it's brought to the front, in front of axes. - - The default in ``False``, so that items apper in front of axes. - - .. method:: is_in_background() - - Returns if item is in the background, set with :meth:`set_in_background`. - - **Subclassing** - - Often you will want to create a custom curve class that inherits from OWCurve. - For this purpose, OWPlotItem provides two virtual methods: :meth:`paint` and :meth:`update_properties`. - - * ``update_properties()`` is called whenever a curve or the plot is changed and needs to be updated. - In this method, child items and other members should be recalculated and updated. - The OWCurve even provides a number of methods for asynchronous (threaded) updating. - - * `paint()` is called whenever the item needs to be painted on the scene. - This method is called more often, so it's advisable to avoid long operation in the method. - - Most provided plot items, including :obj:`OWCurve`, :obj:`OWMultiCurve` and utility curves in :mod:`.owtools` - only reimplement the first method, because they are optimized for performance with large data sets. - -.. autoclass:: OWCurve - -.. class:: OWMultiCurve - - A multi-curve is a curve in which each point can have its own properties. - The point coordinates can be set by calling :meth:`.OWCurve.set_data`, just like in a normal curve. - - In addition, OWMultiCurve provides methods for setting properties for individual points. - Each of this methods take a list as a parameter. If the list has less elements that the curve's data, - the first element is used for all points. - - .. method:: set_point_colors(lst) - - :param lst: The list of colors to assign to points - :type lst: list of QColor - - .. seealso:: :meth:`.OWPoint.set_color` - - .. method:: set_point_sizes(lst) - - :param lst: The list of sizes to assign to points - :type lst: list of int - - .. seealso:: :meth:`.OWPoint.set_size` - - .. method:: set_point_symbols(lst) - - :param lst: The list of symbols to assign to points - :type lst: list of int - - .. seealso:: :meth:`.OWPoint.set_symbol` - -''' - -from .owconstants import * -import orangeqt - -OWPlotItem = orangeqt.PlotItem - -#@deprecated_members({ -# "setYAxis" : "set_y_axis", -# "setData" : "set_data" -#}) - -class OWCurve(orangeqt.Curve): - """ - This class represents a curve on a plot. - It is essentially a plot item with a series of data points or a continuous line. - - :param xData: list of x coordinates - :type xData: list of float - - :param yData: list of y coordinates - :type yData: list of float - - :param x_axis_key: The x axis of this curve - :type x_axis_key: int - - :param y_axis_key: The y axis of this curve - :type y_axis_key: int - - :param tooltip: The curve's tooltip - :type tooltip: str - - .. note:: - - All the points or line segments in an OWCurve have the same properties. - Different points in one curve are supported by the :obj:`.OWMultiCurve` class. - - - .. method:: point_item(x, y, size=0, parent=None) - - Returns a single point with this curve's properties. - It is useful for representing the curve, for example in the legend. - - :param x: The x coordinate of the point. - :type x: float - - :param y: The y coordinate of the point. - :type y: float - - :param size: If nonzero, this argument determines the size of the resulting point. - Otherwise, the point is created with the curve's :meth:`OWCurve.point_size` - :type size: int - - :param parent: An optional parent for the returned item. - :type parent: :obj:`.QGraphicsItem` - - .. attribute:: name - - The name of the curve, used in the legend or in tooltips. - - .. method:: set_data(x_data, y_data) - - Sets the curve's data to a list of coordinates specified by ``x_data`` and ``y_data``. - - .. method:: data() - - :returns: The curve's data as a list of data points. - :rtype: list of tuple of float float - - .. method:: set_style(style) - - Sets the curve's style to ``style``. - - The following values are recognized by OWCurve: - - =================== =============================================== - Value Result - =================== =============================================== - OWCurve.Points Only points are shown, no lines - OWCurve.Lines A continuous line is shown, no points - OWCurve.LinesPoints Both points and lines between them are shown - OWCurve.Dots A dotted line is shown, no points - OWCurve.NoCurve Deprecated, same as ``OWCurve.Points`` - =================== =============================================== - - Curve subclasses can use this value for different drawing modes. - Values up to OWCurve.UserCurve are reserved, so use only higher numbers, like the following example:: - - class MyCurve(OWCurve): - PonyStyle = OWCurve.UserCurve + 42 - - def draw_ponies() - # Draw type-specific things here - - def update_properties(self): - if self.style() == PonyStyle: - self.draw_ponies() - else: - OWCurve.update_properties(self) - - .. method:: style() - - :return: The curve's style, set with :meth:`set_style` - :rtype: int - - .. method:: cancel_all_updates() - - Cancel all pending threaded updates and block until they are finished. - This is usually called before starting a new round of updates. - - .. method:: update_number_of_items() - - Resizes the point list so that it matches the number of data points in :meth:`data` - - .. method:: update_point_coordinates() - - Sets the coordinates of each point to match :meth:`data`. - - .. method:: update_point_positions() - - Sets the scene positions of the points to match their data coordinates. - """ - NoCurve = orangeqt.Curve.Points - - def __init__(self, xData=[], yData=[], x_axis_key=xBottom, y_axis_key=yLeft, tooltip=None): - orangeqt.Curve.__init__(self, xData, yData) - self.set_axes(x_axis_key, y_axis_key) - if tooltip: - self.setToolTip(tooltip) - self.name = '' - -OWMultiCurve = orangeqt.MultiCurve - - diff --git a/Orange/widgets/utils/plot/owlegend.py b/Orange/widgets/utils/plot/owlegend.py deleted file mode 100644 index b9265c4a914..00000000000 --- a/Orange/widgets/utils/plot/owlegend.py +++ /dev/null @@ -1,363 +0,0 @@ -""" -########################## -Plot legend (``owlegend``) -########################## - -.. autoclass:: OWLegendItem - -.. autoclass:: OWLegendTitle - -.. autoclass:: OWLegend - :members: - -""" - -from AnyQt.QtWidgets import QGraphicsTextItem, QGraphicsRectItem, QGraphicsObject -from AnyQt.QtGui import QPen, QLinearGradient -from AnyQt.QtCore import QPointF, QRectF, Qt, QPropertyAnimation, QSizeF - -from .owpoint import * -from .owcurve import OWCurve -from .owtools import move_item_xy -from .owpalette import OWPalette - -PointColor = 1 -PointSize = 2 -PointSymbol = 4 - -class OWLegendItem(QGraphicsObject): - """ - Represents a legend item with a title and a point symbol. - - :param name: The text to display - :type name: str - - :param point: The point symbol - :type point: :obj:`.OWPoint` - - :param parent: The parent item, passed to QGraphicsItem - :type parent: :obj:`QGraphicsItem` - - .. seealso:: :meth:`.OWLegend.add_item`, :meth:`.OWLegend.add_curve`. - """ - def __init__(self, name, point, parent): - QGraphicsObject.__init__(self, parent) - self.text_item = QGraphicsTextItem(name, self) - if point: - s = point.size() - height = max(2*s, self.text_item.boundingRect().height()) - else: - height = self.text_item.boundingRect().height() - p = 0.5 * height - self.text_item.setPos(height, 0) - self.point_item = point - if point: - self.point_item.setParentItem(self) - self.point_item.setPos(p, p) - self._rect = QRectF(0, 0, height + self.text_item.boundingRect().width(), height ) - self.rect_item = QGraphicsRectItem(self._rect, self) - self.rect_item.setPen(QPen(Qt.NoPen)) - self.rect_item.stackBefore(self.text_item) - if self.point_item: - self.rect_item.stackBefore(self.point_item) - - def boundingRect(self): - return self._rect - - def paint(self, painter, option, widget): - pass - -class OWLegendTitle(QGraphicsObject): - """ - A legend item that shows ``text`` with a bold font and no symbol. - """ - def __init__(self, text, parent): - QGraphicsObject.__init__(self, parent) - self.text_item = QGraphicsTextItem(text + ':', self) - f = self.text_item.font() - f.setBold(True) - self.text_item.setFont(f) - self.rect_item = QGraphicsRectItem(self.text_item.boundingRect(), self) - self.rect_item.setPen(QPen(Qt.NoPen)) - self.rect_item.stackBefore(self.text_item) - - def boundingRect(self): - return self.text_item.boundingRect() - - def paint(self, painter, option, widget): - pass - -class OWLegendGradient(QGraphicsObject): - - gradient_width = 20 - - def __init__(self, palette, values, parent): - QGraphicsObject.__init__(self, parent) - self.parent = parent - self.palette = palette - self.values = values - self.legend = parent - self.label_items = [QGraphicsTextItem(text, self) for text in values] - for i in self.label_items: - i.setTextWidth(50) - - self.rect = QRectF() - - self.gradient_item = QGraphicsRectItem(self) - self.gradient = QLinearGradient() - self.gradient.setStops([(v*0.1, self.palette[v*0.1]) for v in range(11) ]) - self.orientation = Qt.Horizontal - self.set_orientation(Qt.Vertical) - - def set_orientation(self, orientation): - if self.orientation == orientation: - return - - self.orientation = orientation - - if self.orientation == Qt.Vertical: - height = max([item.boundingRect().height() for item in self.label_items]) - total_height = height * max(5, len(self.label_items)) - interval = (total_height - self.label_items[-1].boundingRect().height()) / (len(self.label_items) -1) - self.gradient_item.setRect(10, 0, self.gradient_width, total_height) - self.gradient.setStart(10, 0) - self.gradient.setFinalStop(10, total_height) - self.gradient_item.setBrush(QBrush(self.gradient)) - self.gradient_item.setPen(QPen(Qt.NoPen)) - y = 0 - x = 30 - for item in self.label_items: - move_item_xy(item, x, y, self.parent.graph.animate_plot) - y += interval - self.rect = QRectF(10, 0, self.gradient_width + max([item.boundingRect().width() for item in self.label_items]), self.label_items[0].boundingRect().height() * max(5, len(self.label_items))) - else: - width = 50 - height = max([item.boundingRect().height() for item in self.label_items]) - total_width = width * max(5, len(self.label_items)) - interval = (total_width - self.label_items[-1].boundingRect().width()) / (len(self.label_items) -1) - - self.gradient_item.setRect(0, 0, total_width, self.gradient_width) - self.gradient.setStart(0, 0) - self.gradient.setFinalStop(total_width, 0) - self.gradient_item.setBrush(QBrush(self.gradient)) - self.gradient_item.setPen(QPen(Qt.NoPen)) - x = 0 - y = 30 - for item in self.label_items: - move_item_xy(item, x, y, self.parent.graph.animate_plot) - x += interval - self.rect = QRectF(0, 0, total_width, self.gradient_width + height) - - def boundingRect(self): - return getattr(self, 'rect', QRectF()) - - def paint(self, painter, option, widget): - pass - - -class OWLegend(QGraphicsObject): - """ - A legend for :obj:`.OWPlot`. - - Its items are arranged into a hierarchy by `category`. This is useful when points differ in more than one attribute. - In such a case, there can be one category for point color and one for point shape. Usually the category name - will be the name of the attribute, while the item's title will be the value. - - Arbitrary categories can be created, for an example see :meth:`.OWPlot.update_axes`, which creates a special category - for unused axes. - decimals - .. image:: files/legend-categories.png - - In the image above, `type` and `milk` are categories with 7 and 2 possible values, respectively. - - """ - def __init__(self, graph, scene): - QGraphicsObject.__init__(self) - if scene: - scene.addItem(self) - self.graph = graph - self.curves = [] - self.items = {} - self.attributes = [] - self.point_attrs = {} - self.point_vals = {} - self.default_values = { - PointColor : Qt.black, - PointSize : 8, - PointSymbol : OWPoint.Ellipse - } - self.box_rect = QRectF() - self.setFiltersChildEvents(True) - self.setFlag(self.ItemHasNoContents, True) - self.mouse_down = False - self._orientation = Qt.Vertical - self.max_size = QSizeF() - self._floating = True - self._floating_animation = None - self._mouse_down_pos = QPointF() - - def clear(self): - """ - Removes all items from the legend - """ - for lst in self.items.values(): - for i in lst: - i.setParentItem(None) - if self.scene(): - self.scene().removeItem(i) - self.items = {} - self.update_items() - - - def add_curve(self, curve): - """ - Adds a legend item with the same point symbol and name as ``curve``. - - If the curve's name contains the equal sign (=), it is split at that sign. The first part of the curve - is a used as the category, and the second part as the value. - """ - i = curve.name.find('=') - if i == -1: - cat = '' - name = curve.name - else: - cat = curve.name[:i] - name = curve.name[i+1:] - self.add_item(cat, name, curve.point_item(0, 0, 0)) - - def add_item(self, category, value, point): - """ - Adds an item with title ``value`` and point symbol ``point`` to the specified ``category``. - """ - if category not in self.items: - self.items[category] = [OWLegendTitle(category, self)] - self.items[category].append(OWLegendItem(str(value), point, self)) - self.update_items() - - def add_color_gradient(self, title, values): - if len(values) < 2: - # No point in showing a gradient with less that two values - return - if title in self.items: - self.remove_category(title) - item = OWLegendGradient(self.graph.contPalette, [str(v) for v in values], self) - self.items[title] = [OWLegendTitle(title, self), item] - self.update_items() - - def remove_category(self, category): - """ - Removes ``category`` and all items that belong to it. - """ - if category not in self.items: - return - if self.scene(): - for item in self.items[category]: - self.scene().removeItem(item) - del self.items[category] - - def update_items(self): - """ - Updates the legend, repositioning the items according to the legend's orientation. - """ - self.box_rect = QRectF() - x = y = 0 - - for lst in self.items.values(): - for item in lst: - if hasattr(item, 'text_item'): - item.text_item.setDefaultTextColor(self.graph.color(OWPalette.Text)) - if hasattr(item, 'rect_item'): - item.rect_item.setBrush(self.graph.color(OWPalette.Canvas)) - if hasattr(item, 'set_orientation'): - item.set_orientation(self._orientation) - - if self._orientation == Qt.Vertical: - for lst in self.items.values(): - for item in lst: - if self.max_size.height() and y and y + item.boundingRect().height() > self.max_size.height(): - y = 0 - x = x + item.boundingRect().width() - self.box_rect = self.box_rect | item.boundingRect().translated(x, y) - move_item_xy(item, x, y, self.graph.animate_plot) - y = y + item.boundingRect().height() - elif self._orientation == Qt.Horizontal: - for lst in self.items.values(): - max_h = max(item.boundingRect().height() for item in lst) - for item in lst: - if self.max_size.width() and x and x + item.boundingRect().width() > self.max_size.width(): - x = 0 - y = y + max_h - self.box_rect = self.box_rect | item.boundingRect().translated(x, y) - move_item_xy(item, x, y, self.graph.animate_plot) - x = x + item.boundingRect().width() - if lst: - x = 0 - y = y + max_h - - def mouseMoveEvent(self, event): - self.graph.notify_legend_moved(event.scenePos()) - if self._floating: - p = event.scenePos() - self._mouse_down_pos - if self._floating_animation and self._floating_animation.state() == QPropertyAnimation.Running: - self.set_pos_animated(p) - else: - self.setPos(p) - event.accept() - - def mousePressEvent(self, event): - self.setCursor(Qt.ClosedHandCursor) - self.mouse_down = True - self._mouse_down_pos = event.scenePos() - self.pos() - event.accept() - - def mouseReleaseEvent(self, event): - self.unsetCursor() - self.mouse_down = False - self._mouse_down_pos = QPointF() - event.accept() - - def boundingRect(self): - return self.box_rect - - def paint(self, painter, option, widget=None): - pass - - def set_orientation(self, orientation): - """ - Sets the legend's orientation to ``orientation``. - """ - self._orientation = orientation - self.update_items() - - def orientation(self): - return self._orientation - - def set_pos_animated(self, pos): - if (self.pos() - pos).manhattanLength() < 6 or not self.graph.animate_plot: - self.setPos(pos) - else: - t = 250 - if self._floating_animation and self._floating_animation.state() == QPropertyAnimation.Running: - t = t - self._floating_animation.currentTime() - self._floating_animation.stop() - self._floating_animation = QPropertyAnimation(self, 'pos') - self._floating_animation.setEndValue(pos) - self._floating_animation.setDuration(t) - self._floating_animation.start(QPropertyAnimation.KeepWhenStopped) - - def set_floating(self, floating, pos=None): - """ - If floating is ``True``, the legend can be dragged with the mouse. - Otherwise, it's fixed in its position. - - If ``pos`` is specified, the legend is moved there. - """ - if floating == self._floating: - return - self._floating = floating - if pos: - if floating: - self.set_pos_animated(pos - self._mouse_down_pos) - else: - self.set_pos_animated(pos) - diff --git a/Orange/widgets/utils/plot/owopenglrenderer.py b/Orange/widgets/utils/plot/owopenglrenderer.py deleted file mode 100644 index 71bda2e4290..00000000000 --- a/Orange/widgets/utils/plot/owopenglrenderer.py +++ /dev/null @@ -1,391 +0,0 @@ -''' - -################# -OpenGL Renderer (``owopenglrenderer``) -################# - -.. autoclass:: VertexBuffer - -.. autoclass:: OWOpenGLRenderer - -''' - -from ctypes import c_void_p - -from AnyQt.QtGui import QColor, QMatrix4x4 -from AnyQt import QtOpenGL - -import OpenGL -OpenGL.ERROR_CHECKING = False -OpenGL.ERROR_LOGGING = False -OpenGL.FULL_LOGGING = False -OpenGL.ERROR_ON_COPY = False -from OpenGL.GL import * -from OpenGL.GL.ARB.vertex_array_object import * -from OpenGL.GL.ARB.vertex_buffer_object import * -import numpy - -class VertexBuffer: - ''' - A thin abstraction simplifying the usage of Vertex Buffer Objects (VBO). Warning: understanding what this code does - requires basic knowledge of OpenGL. - - VBOs are necessary in OpenGL 2+ world, since immediate mode (glBegin/glVertex/glEnd paradigm) has been deprecated - and is slow (even more so using it through PyOpenGL). Vertex Array Objects (VAO) were introduced in OpenGL version 3.0; they - reduce the amount of function calls that need to be made by storing the set of bindings between vertex attributes - and vertex data. VAOs are used only if the underlying hardware supports them. This class provides a simple usage pattern - which is suitable for many applications:: - - data = numpy.array([1.3, 5.1, 42., 0., - 3.3, 4.3, 5.1, 1.], dtype=numpy.float32) - buffer = VertexBuffer(data, [(3, GL_FLOAT), (1, GL_FLOAT)]) - # ... Later when drawing: - buffer.draw(GL_LINES) - # Possible setup in a vertex shader: - # - # attribute vec3 position; - # attribute float index; - # ... - - What the example above does depends on the rest of the vertex shader (left to your imagination). VertexBuffer constructor - takes data and data format description as its parameters. Format specifies mapping between data values and vertex attributes. - See the constructor for more info. - ''' - - def __init__(self, data, format_description, usage=GL_STATIC_DRAW): - ''' - Constructs VBO and prepares vertex attribute bindings. OpenGL context must be up already (initializeGL - has been called). - - :param data: Data array (vertices) to be sent to the GPU. - :type numpy.array - - :param format_description: Describes vertex attribute bindings. This parameter must be an iterable - of tuples. Each tuple specifies a generic vertex attribute (the order is important!) by specifying - the number of components (must be 1,2,3 or 4) and data type (e.g. GL_FLOAT, GL_INT). See the example - above. Normalization for fixed-point values is turned off. - :type an iterable of tuples - - :param usage: Specifies the expected usage pattern. The symbolic constant must be GL_STREAM_DRAW, - GL_STREAM_READ, GL_STREAM_COPY, GL_STATIC_DRAW, GL_STATIC_READ, GL_STATIC_COPY, - GL_DYNAMIC_DRAW, GL_DYNAMIC_READ, or GL_DYNAMIC_COPY. Default is GL_STATIC_DRAW. - :type GLenum - ''' - - self._format_description = format_description - - if glGenVertexArrays: - self._vao = GLuint(42) - glGenVertexArrays(1, self._vao) - glBindVertexArray(self._vao) - vertex_buffer_id = glGenBuffers(1) - glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id) - glBufferData(GL_ARRAY_BUFFER, data, usage) - - vertex_size = sum(attribute[0]*4 for attribute in format_description) # TODO: sizeof(type) - self._num_vertices = len(data) / (vertex_size / 4) - current_size = 0 - for i, (num_components, type) in enumerate(format_description): - glVertexAttribPointer(i, num_components, type, GL_FALSE, vertex_size, c_void_p(current_size)) - glEnableVertexAttribArray(i) - current_size += num_components*4 - - glBindVertexArray(0) - glBindBuffer(GL_ARRAY_BUFFER, 0) - else: - self._vbo_id = glGenBuffers(1) - glBindBuffer(GL_ARRAY_BUFFER, self._vbo_id) - glBufferData(GL_ARRAY_BUFFER, data, usage) - self._data_length = len(data) - glBindBuffer(GL_ARRAY_BUFFER, 0) - - def __del__(self): - # TODO - pass - - def draw(self, primitives=GL_TRIANGLES, first=0, count=-1): - ''' - Renders primitives from data array. By default it renders triangles using all the data. Consult - OpenGL documentation (specifically ``glDrawArrays``) for detail info. - - :param primitives: What kind of primitives to render. Symbolic constants GL_POINTS, GL_LINE_STRIP, - GL_LINE_LOOP, GL_LINES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, GL_QUAD_STRIP, - GL_QUADS, and GL_POLYGON are accepted. - :type GLenum - - :param first: Specifies the starting index into data. - :type int - - :param count: The number of indices (not primitives) to be rendered. - :type int - ''' - if hasattr(self, '_vao'): - glBindVertexArray(self._vao) - glDrawArrays(primitives, first, - count if count != -1 else self._num_vertices - first) - glBindVertexArray(0) - else: - glBindBuffer(GL_ARRAY_BUFFER, self._vbo_id) - - vertex_size = sum(attribute[0]*4 for attribute in self._format_description) - current_size = 0 - for i, (num_components, type) in enumerate(self._format_description): - glVertexAttribPointer(i, num_components, type, GL_FALSE, vertex_size, c_void_p(current_size)) - glEnableVertexAttribArray(i) - current_size += num_components*4 - - glDrawArrays(primitives, first, - count if count != -1 else self._data_length / (vertex_size / 4) - first) - - for i in range(len(self._format_description)): - glDisableVertexAttribArray(i) - glBindBuffer(GL_ARRAY_BUFFER, 0) - -class OWOpenGLRenderer: - ''' - OpenGL 3 deprecated a lot of old (1.x) functions, particulary, it removed - immediate mode (glBegin, glEnd, glVertex paradigm). Vertex buffer objects and similar - (through glDrawArrays for example) should be used instead. This class simplifies - the usage of that functionality by providing methods which resemble immediate mode. - - Example usage:: - - renderer = OWOpenGLRenderer() - # Setup vanilla transforms - projection = QMatrix4x4() - projection.perspective(45., 1., 1., 50.) - model = view = QMatrix4x4() - renderer.set_transform(model, view, projection) - renderer.draw_line(QVector3D(0, 0, 0), QVector3D(1, 1, 1), color=QColor(0, 0, 255)) - - Note that usually you don't have to create an instance of the renderer, since it's available in Plot3D: ``OWPlot3D.renderer``. - Also, current projection and modelview transforms can be accessed (``OWPlot3D.projection`` and ``OWPlot3D.modelview``) in - OWPlot3D callbacks. - - **2D drawing** - - The API of this class is general enough to support drawing 2D shapes. An example setup:: - - projection = QMatrix4x4() - # Use ortho projection, specify width and height of the window - projection.ortho(0, self.width(), self.height(), 0, -1, 1) - model = view = QMatrix4x4() - renderer.set_transform(model, view, projection) - # Red triangle. Z-value is not important. - renderer.draw_triangle(QVector3D(1, 2, 0), - QVector3D(3, 2, 0), - QVector3D(2, 3, 0), - color=QColor(255, 0, 0)) - ''' - - def __init__(self): - self._projection = QMatrix4x4() - self._model = QMatrix4x4() - self._view = QMatrix4x4() - - ## Shader used to draw primitives. Position and color of vertices specified through uniforms. Nothing fancy. - vertex_shader_source = ''' - attribute float index; - varying vec4 color; - - uniform vec3 positions[6]; // 6 vertices for quad - uniform vec4 colors[6]; - - uniform mat4 projection, model, view; - - void main(void) - { - int i = int(index); - gl_Position = projection * view * model * vec4(positions[i], 1.); - color = colors[i]; - } - ''' - - fragment_shader_source = ''' - varying vec4 color; - - void main(void) - { - gl_FragColor = color; - } - ''' - - self._shader = QtOpenGL.QGLShaderProgram() - self._shader.addShaderFromSourceCode(QtOpenGL.QGLShader.Vertex, vertex_shader_source) - self._shader.addShaderFromSourceCode(QtOpenGL.QGLShader.Fragment, fragment_shader_source) - - self._shader.bindAttributeLocation('index', 0) - - if not self._shader.link(): - print('Failed to link dummy renderer shader!') - - indices = numpy.array(range(6), dtype=numpy.float32) - self._vertex_buffer = VertexBuffer(indices, [(1, GL_FLOAT)]) - - def set_transform(self, model, view=None, projection=None, viewport=None): - ''' - Sets current projection, model, view and viewport transforms. ``model`` parameter - is most often changed. Passing None as a parameter value keeps the internal value - unmodified. - - :param model: Model transform (usually translation, rotation, scale or a combination of these). - :type QMatrix4x4 - - :param view: View transform (camera transformation). - :type QMatrix4x4 - - :param projection: Projection transform (e.g. ortho, perspective). - :type QMatrix4x4 - - :param viewport: Viewport transform. A list of 4 ints specifying viewport rectangle (lower left corner + width and height). - :type list of 4 int - ''' - if model != None: - self._model = model - if view != None: - self._view = view - if projection != None: - self._projection = projection - if viewport: - glViewport(*viewport) - self._shader.bind() - self._shader.setUniformValue('projection', self._projection) - self._shader.setUniformValue('model', self._model) - self._shader.setUniformValue('view', self._view) - self._shader.release() - - def draw_line(self, position0, position1, color0=QColor(0, 0, 0), color1=QColor(0, 0 ,0), color=None): - ''' - Draws a line using current transform. - - :param position0: Beginning of line. - :type QVector3D - - :param position1: End of line. - :type QVector3D - - :param color0: Color of the beginning of the line. - :type QColor - - :param color1: Color of the end of the line. - :type QColor - - :param color: A convenience parameter. Overrides ``color0`` and ``color1``. - :type QColor - ''' - - if color: - colors = [color.redF(), color.greenF(), color.blueF(), color.alphaF()] * 2 - else: - colors = [color0.redF(), color0.greenF(), color0.blueF(), color0.alphaF(), - color1.redF(), color1.greenF(), color1.blueF(), color1.alphaF()] - - positions = [position0.x(), position0.y(), position0.z(), - position1.x(), position1.y(), position1.z()] - - self._shader.bind() - glUniform4fv(glGetUniformLocation(self._shader.programId(), 'colors'), len(colors)/4, numpy.array(colors, numpy.float32)) - glUniform3fv(glGetUniformLocation(self._shader.programId(), 'positions'), len(positions)/3, numpy.array(positions, numpy.float32)) - self._vertex_buffer.draw(GL_LINES, 0, 2) - self._shader.release() - - def draw_rectangle(self, position0, position1, position2, position3, - color0=QColor(0, 0, 0), color1=QColor(0, 0, 0), color2=QColor(0, 0, 0), color3=QColor(0, 0, 0), color=None): - ''' - Draws a rectangle using current transform. Vertices must specified in clockwise or counter-clockwise order (otherwise nothing - might be drawn). - - :param position0: First vertex position. - :type QVector3D - - :param position1: Second vertex position. - :type QVector3D - - :param position2: Third vertex position. - :type QVector3D - - :param position3: Fourth vertex position. - :type QVector3D - - :param color0: First vertex color. - :type QColor - - :param color1: Second vertex color. - :type QColor - - :param color2: Third vertex color. - :type QColor - - :param color3: Fourth vertex color. - :type QColor - - :param color: A convenience parameter: Overrides color values. - :type QColor - ''' - if color: - colors = [color.redF(), color.greenF(), color.blueF(), color.alphaF()] * 6 - else: - colors = [color0.redF(), color0.greenF(), color0.blueF(), color0.alphaF(), - color1.redF(), color1.greenF(), color1.blueF(), color1.alphaF(), - color3.redF(), color3.greenF(), color3.blueF(), color3.alphaF(), - - color3.redF(), color3.greenF(), color3.blueF(), color3.alphaF(), - color1.redF(), color1.greenF(), color1.blueF(), color1.alphaF(), - color2.redF(), color2.greenF(), color2.blueF(), color2.alphaF()] - - positions = [position0.x(), position0.y(), position0.z(), - position1.x(), position1.y(), position1.z(), - position3.x(), position3.y(), position3.z(), - - position3.x(), position3.y(), position3.z(), - position1.x(), position1.y(), position1.z(), - position2.x(), position2.y(), position2.z()] - - self._shader.bind() - glUniform4fv(glGetUniformLocation(self._shader.programId(), 'colors'), len(colors)/4, numpy.array(colors, numpy.float32)) - glUniform3fv(glGetUniformLocation(self._shader.programId(), 'positions'), len(positions)/3, numpy.array(positions, numpy.float32)) - self._vertex_buffer.draw(GL_TRIANGLES, 0, 6) - self._shader.release() - - def draw_triangle(self, position0, position1, position2, - color0=QColor(0, 0, 0), color1=QColor(0, 0, 0), color2=QColor(0, 0, 0), color=None): - ''' - Draws a triangle using current transform. - - :param position0: First vertex position. - :type QVector3D - - :param position1: Second vertex position. - :type QVector3D - - :param position2: Third vertex position. - :type QVector3D - - :param color0: First vertex color. - :type QColor - - :param color1: Second vertex color. - :type QColor - - :param color2: Third vertex color. - :type QColor - - :param color: A convenience parameter: Overrides color values. - :type QColor - ''' - if color: - colors = [color.redF(), color.greenF(), color.blueF(), color.alphaF()] * 3 - else: - colors = [color0.redF(), color0.greenF(), color0.blueF(), color0.alphaF(), - color1.redF(), color1.greenF(), color1.blueF(), color1.alphaF(), - color2.redF(), color2.greenF(), color2.blueF(), color2.alphaF()] - - positions = [position0.x(), position0.y(), position0.z(), - position1.x(), position1.y(), position1.z(), - position2.x(), position2.y(), position2.z()] - - self._shader.bind() - glUniform4fv(glGetUniformLocation(self._shader.programId(), 'colors'), len(colors)/4, numpy.array(colors, numpy.float32)) - glUniform3fv(glGetUniformLocation(self._shader.programId(), 'positions'), len(positions)/3, numpy.array(positions, numpy.float32)) - self._vertex_buffer.draw(GL_TRIANGLES, 0, 3) - self._shader.release() diff --git a/Orange/widgets/utils/plot/owplot.py b/Orange/widgets/utils/plot/owplot.py deleted file mode 100644 index 1dfa7109f02..00000000000 --- a/Orange/widgets/utils/plot/owplot.py +++ /dev/null @@ -1,1878 +0,0 @@ -''' - -################# -Plot (``owplot``) -################# - -.. autoclass:: OrangeWidgets.plot.OWPlot - -''' - -from AnyQt.QtWidgets import \ - QGraphicsView, QGraphicsScene, QGraphicsRectItem, QGraphicsTextItem,\ - QToolTip, QApplication -from AnyQt.QtGui import QPen, QBrush, QColor, QPainter, QTransform, QPolygonF -from AnyQt.QtCore import \ - QPointF, QRectF, QLineF, QPoint, QRect, QPropertyAnimation, Qt, QEvent, \ - pyqtProperty - -from Orange.widgets.gui import OWComponent -from Orange.widgets.settings import Setting - -LeftLegend = 0 -RightLegend = 1 -BottomLegend = 2 -TopLegend = 3 -ExternalLegend = 4 - -UNUSED_ATTRIBUTES_STR = 'unused attributes' - -from .owaxis import * -from .owcurve import * -from .owlegend import * -from .owplotgui import OWPlotGUI -from .owtools import * - -from ..colorpalette import ColorPaletteGenerator - -## Color values copied from orngView.SchemaView for consistency -SelectionPen = QPen(QBrush(QColor(51, 153, 255, 192)), - 1, Qt.SolidLine, Qt.RoundCap) -SelectionBrush = QBrush(QColor(168, 202, 236, 192)) - -#from OWDlgs import OWChooseImageSizeDlg -#from OWColorPalette import * # color palletes, ... -#from Orange.utils import deprecated_members, deprecated_attribute - -import orangeqt - -def n_min(*args): - lst = args[0] if len(args) == 1 else args - a = [i for i in lst if i is not None] - return min(a) if a else None - -def n_max(*args): - lst = args[0] if len(args) == 1 else args - a = [i for i in lst if i is not None] - return max(a) if a else None - -name_map = { - "saveToFileDirect": "save_to_file_direct", - "saveToFile" : "save_to_file", - "addCurve" : "add_curve", - "addMarker" : "add_marker", - "updateLayout" : "update_layout", - "activateZooming" : "activate_zooming", - "activateSelection" : "activate_selection", - "activateRectangleSelection" : "activate_rectangle_selection", - "activatePolygonSelection" : "activate_polygon_selection", - "activatePanning" : "activate_panning", - "getSelectedPoints" : "get_selected_points", - "setAxisScale" : "set_axis_scale", - "setAxisLabels" : "set_axis_labels", - "setAxisAutoScale" : "set_axis_autoscale", - "setTickLength" : "set_axis_tick_length", - "updateCurves" : "update_curves", - "itemList" : "plot_items", - "setShowMainTitle" : "set_show_main_title", - "setMainTitle" : "set_main_title", - "invTransform" : "inv_transform", - "setAxisTitle" : "set_axis_title", - "setShowAxisTitle" : "set_show_axis_title" -} - -#@deprecated_members(name_map, wrap_methods=list(name_map.keys())) -class OWPlot(orangeqt.Plot, OWComponent): - """ - The base class for all plots in Orange. It uses the Qt Graphics View Framework - to draw elements on a graph. - - **Plot layout** - - .. attribute:: show_legend - - A boolean controlling whether the legend is displayed or not - - .. attribute:: show_main_title - - Controls whether or not the main plot title is displayed - - .. attribute:: main_title - - The plot title, usually show on top of the plot - - .. automethod:: set_main_title - - .. automethod:: set_show_main_title - - .. attribute:: axis_margin - - How much space (in pixels) should be left on each side for the axis, its label and its title. - - .. attribute:: title_margin - - How much space (in pixels) should be left at the top of the plot for the title, if the title is shown. - - .. seealso:: attribute :attr:`show_main_title` - - .. attribute:: plot_margin - - How much space (in pixels) should be left at each side of the plot as whitespace. - - - **Coordinate transformation** - - There are several coordinate systems used by OWPlot: - - * `widget` coordinates. - - This is the coordinate system of the position returned by :meth:`.QEvent.pos()`. - No calculations or positions is done with this coordinates, they must first be converted - to scene coordinates with :meth:`mapToScene`. - - * `data` coordinates. - - The value used internally in Orange to specify the values of attributes. - For example, this can be age in years, the number of legs, or any other numeric value. - - * `plot` coordinates. - - These coordinates specify where the plot items are placed on the graph, but doesn't account for zoom. - They can be retrieved for a particular plot item with :meth:`.PlotItem.pos()`. - - * `scene` or `zoom` coordinates. - - Like plot coordinates, except that they take the :attr:`zoom_transform` into account. They represent the - actual position of an item on the scene. - - These are the coordinates returned by :meth:`.PlotItem.scenePos()` and :meth:`mapToScene`. - - For example, they can be used to determine what is under the cursor. - - In most cases, you will use data coordinates for interacting with the actual data, and scene coordinates for - interacting with the plot items. The other two sets are mostly used for converting. - - .. automethod:: map_to_graph - - .. automethod:: map_from_graph - - .. automethod:: transform - - .. automethod:: inv_transform - - .. method:: nearest_point(pos) - - Returns the point nearest to ``pos``, or ``None`` if no point is close enough. - - :param pos: The position in scene coordinates - :type pos: QPointF - - :rtype: :obj:`.OWPoint` - - .. method:: point_at(pos) - - If there is a point with data coordinates equal to ``pos``, if is returned. - Otherwise, this function returns None. - - :param pos: The position in data coordinates - :type pos: tuple of float float - - :rtype: :obj:`.OWPoint` - - - **Data curves** - The preferred method for showing a series of data points is :meth:`set_main_curve_data`. - It allows you to specify point positions, colors, labels, sizes and shapes. - - .. automethod:: set_main_curve_data - - .. automethod:: add_curve - - .. automethod:: add_custom_curve - - .. automethod:: add_marker - - .. method:: add_item(item) - - Adds any PlotItem ``item`` to this plot. - Calling this function directly is useful for adding a :obj:`.Marker` or another object that does not have to appear in the legend. - For data curves, consider using :meth:`add_custom_curve` instead. - - .. method:: plot_items() - - Returns the list of all plot items added to this graph with :meth:`add_item` or :meth:`.PlotItem.attach`. - - **Axes** - - .. automethod:: add_axis - - .. automethod:: add_custom_axis - - .. automethod:: set_axis_enabled - - .. automethod:: set_axis_labels - - .. automethod:: set_axis_scale - - **Settings** - - .. attribute:: gui - - An :obj:`.OWPlotGUI` object associated with this graph - - **Point Selection and Marking** - - There are four possible selection behaviors used for selecting or marking points in OWPlot. - They are used in :meth:`select_points` and :meth:`mark_points` and are the same for both operations. - - .. data:: AddSelection - - The points are added to the selection, without affected the currently selected points - - .. data:: RemoveSelection - - The points are removed from the selection, without affected the currently selected points - - .. data:: ToggleSelection - - The points' selection state is toggled - - .. data:: ReplaceSelection - - The current selection is replaced with the new one - - .. note:: There are exactly the same functions for point selection and marking. - For simplicity, they are only documented once. - - .. method:: select_points(area, behavior) - .. method:: mark_points(area, behavior) - - Selects or marks all points inside the ``area`` - - :param area: The newly selected/marked area - :type area: QRectF or QPolygonF - - :param behavior: :data:`AddSelection`, :data:`RemoveSelection`, :data:`ToggleSelection` or :data:`ReplaceSelection` - :type behavior: int - - .. method:: unselect_all_points() - .. method:: unmark_all_points() - - Unselects or unmarks all the points in the plot - - .. method:: selected_points() - .. method:: marked_points() - - Returns a list of all selected or marked points - - :rtype: list of OWPoint - - .. method:: selected_points(xData, yData) - - For each of the point specified by ``xData`` and ``yData``, the point's selection state is returned. - - :param xData: The list of x coordinates - :type xData: list of float - - :param yData: The list of y coordinates - :type yData: list of float - - :rtype: list of int - - **Color schemes** - - By default, OWPlot uses the application's system palette for drawing everything - except data curves and points. This way, it maintains consistency with other application - with regards to the user interface. - - If data is plotted with no color specified, it will use a system color as well, - so that a good contrast with the background in guaranteed. - - OWPlot uses the :meth:`.OWidget.palette` to determine its color scheme, so it can be - changed using :meth:`.QWidget.setPalette`. There are also two predefined color schemes: - ``OWPalette.Dark`` and ``OWPalette.Light``, which provides a dark and a light scheme - respectively. - - .. attribute:: theme_name - - A string attribute with three possible values: - ============== =========================== - Value Meaning - -------------- --------------------------- - "default" The system palette is used - "dark" The dark theme is used - "light" The light theme is used - ============== =========================== - - To apply the settings, first set this attribute's value, and then call :meth:`update_theme` - - .. automethod:: update_theme - - On the other hand, curves with a specified color will use colors from Orange's palette, - which can be configured within Orange. Each plot contains two separate palettes: - one for continuous attributes, and one for discrete ones. Both are created by - :obj:`.OWColorPalette.ColorPaletteGenerator` - - .. attribute:: continuous_palette - - The palette used when point color represents a continuous attribute - - .. attribute:: discrete_palette - - The palette used when point color represents a discrete attribute - - """ - - point_settings = ["point_width", "alpha_value"] - plot_settings = ["show_legend", "show_grid"] - - alpha_value = Setting(255) - - show_legend = Setting(False) - show_grid = Setting(False) - - - appearance_settings = ["antialias_plot", "animate_plot", "animate_points", "disable_animations_threshold", "auto_adjust_performance"] - - def settings_list(self, graph_name, settings): - return [graph_name + '.' + setting for setting in settings] - - def __init__(self, parent = None, name = "None", show_legend = 1, axes = [xBottom, yLeft], widget = None): - """ - Creates a new graph - - If your visualization uses axes other than ``xBottom`` and ``yLeft``, specify them in the - ``axes`` parameter. To use non-cartesian axes, set ``axes`` to an empty list - and add custom axes with :meth:`add_axis` or :meth:`add_custom_axis` - """ - orangeqt.Plot.__init__(self, parent) - OWComponent.__init__(self, widget) - self.widget = widget - self.parent_name = name - self.title_item = None - - self.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing) - - self._legend = OWLegend(self, self.scene()) - self._legend.setZValue(LegendZValue) - self._legend_margin = QRectF(0, 0, 100, 0) - self._legend_moved = False - self.axes = dict() - - self.axis_margin = 50 - self.y_axis_extra_margin = 30 - self.title_margin = 40 - self.graph_margin = 10 - - self.mainTitle = None - self.showMainTitle = False - self.XaxisTitle = None - self.YLaxisTitle = None - self.YRaxisTitle = None - - # Method aliases, because there are some methods with different names but same functions - self.setCanvasBackground = self.setCanvasColor - self.map_from_widget = self.mapToScene - - # OWScatterPlot needs these: - self.point_width = 5 - self.show_filled_symbols = True - self.show_grid = True - - self.curveSymbols = list(range(13)) - self.tips = TooltipManager(self) - self.setMouseTracking(True) - self.grabGesture(Qt.PinchGesture) - self.grabGesture(Qt.PanGesture) - - self.state = NOTHING - self._pressed_mouse_button = Qt.NoButton - self._pressed_point = None - self.selection_items = [] - self._current_rs_item = None - self._current_ps_item = None - self.polygon_close_treshold = 10 - self.sendSelectionOnUpdate = False - self.auto_send_selection_callback = None - - self.data_range = {} - self.map_transform = QTransform() - self.graph_area = QRectF() - - ## Performance optimization - self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) - self.scene().setItemIndexMethod(QGraphicsScene.NoIndex) - - self.animate_plot = True - self.animate_points = True - self.antialias_plot = True - self.antialias_points = True - self.antialias_lines = True - - self.auto_adjust_performance = True - self.disable_animations_threshold = 5000 - # self.setInteractive(False) - - self.warn_unused_attributes = False - - self._bounds_cache = {} - self._transform_cache = {} - self.block_update = False - - self.use_animations = True - self._animations = [] - - ## Mouse event handlers - self.mousePressEventHandler = None - self.mouseMoveEventHandler = None - self.mouseReleaseEventHandler = None - self.mouseStaticClickHandler = self.mouseStaticClick - self.static_click = False - - self._marker_items = [] - self.grid_curve = PlotGrid(self) - - self._zoom_rect = None - self._zoom_transform = QTransform() - self.zoom_stack = [] - self.old_legend_margin = None - self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - - ## Add specified axes: - - for key in axes: - if key in [yLeft, xTop]: - self.add_axis(key, title_above=1) - else: - self.add_axis(key) - - self.continuous_palette = ColorPaletteGenerator(number_of_colors= -1) - self.discrete_palette = ColorPaletteGenerator() - - self.gui = OWPlotGUI(self) - """ - An :obj:`.OWPlotGUI` object associated with this plot - """ - self.activate_zooming() - self.selection_behavior = self.AddSelection - - self.main_curve = None - - self.replot() - -# selectionCurveList = deprecated_attribute("selectionCurveList", "selection_items") -# autoSendSelectionCallback = deprecated_attribute("autoSendSelectionCallback", "auto_send_selection_callback") -# showLegend = deprecated_attribute("showLegend", "show_legend") -# pointWidth = deprecated_attribute("pointWidth", "point_width") -# alphaValue = deprecated_attribute("alphaValue", "alpha_value") -# useAntialiasing = deprecated_attribute("useAntialiasing", "use_antialiasing") -# showFilledSymbols = deprecated_attribute("showFilledSymbols", "show_filled_symbols") -# mainTitle = deprecated_attribute("mainTitle", "main_title") -# showMainTitle = deprecated_attribute("showMainTitle", "show_main_title") -# gridCurve = deprecated_attribute("gridCurve", "grid_curve") -# contPalette = deprecated_attribute("contPalette", "continuous_palette") -# discPalette = deprecated_attribute("discPalette", "discrete_palette") - - def scrollContentsBy(self, dx, dy): - # This is overriden here to prevent scrolling with mouse and keyboard - # Instead of moving the contents, we simply do nothing - pass - - def graph_area_rect(self): - return self.graph_area - - def map_to_graph(self, point, axes = None, zoom = False): - ''' - Maps ``point``, which can be ether a tuple of (x,y), a QPoint or a QPointF, from data coordinates - to plot coordinates. - - :param point: The point in data coordinates - :type point: tuple or QPointF - - :param axes: The pair of axes along which to transform the point. - If none are specified, (xBottom, yLeft) will be used. - :type axes: tuple of float float - - :param zoom: if ``True``, the current :attr:`zoom_transform` will be considered in the transformation, and the result will be in scene coordinates instead. - :type zoom: int - - :return: The transformed point in scene coordinates - :type: tuple of float float - ''' - if type(point) == tuple: - (x, y) = point - point = QPointF(x, y) - if axes: - x_id, y_id = axes - point = point * self.transform_for_axes(x_id, y_id) - else: - point = point * self.map_transform - if zoom: - point = point * self._zoom_transform - return (point.x(), point.y()) - - def map_from_graph(self, point, axes = None, zoom = False): - ''' - Maps ``point``, which can be ether a tuple of (x,y), a QPoint or a QPointF, from plot coordinates - to data coordinates. - - :param point: The point in data coordinates - :type point: tuple or QPointF - - :param axes: The pair of axes along which to transform the point. If none are specified, (xBottom, yLeft) will be used. - :type axes: tuple of float float - - :param zoom: if ``True``, the current :attr:`zoom_transform` will be considered in the transformation, and the ``point`` should be in scene coordinates instead. - :type zoom: int - - :returns: The transformed point in data coordinates - :rtype: tuple of float float - ''' - if type(point) == tuple: - (x, y) = point - point = QPointF(x,y) - if zoom: - t, ok = self._zoom_transform.inverted() - point = point * t - if axes: - x_id, y_id = axes - t, ok = self.transform_for_axes(x_id, y_id).inverted() - else: - t, ok = self.map_transform.inverted() - ret = point * t - return (ret.x(), ret.y()) - - def save_to_file(self, extraButtons = []): - sizeDlg = OWChooseImageSizeDlg(self, extraButtons, parent=self) - sizeDlg.exec_() - - def save_to_file_direct(self, fileName, size = None): - sizeDlg = OWChooseImageSizeDlg(self) - sizeDlg.saveImage(fileName, size) - - def activate_zooming(self): - ''' - Activates the zooming mode, where the user can zoom in and out with a single mouse click - or by dragging the mouse to form a rectangular area - ''' - self.state = ZOOMING - - def activate_rectangle_selection(self): - ''' - Activates the rectangle selection mode, where the user can select points in a rectangular area - by dragging the mouse over them - ''' - self.state = SELECT_RECTANGLE - - def activate_selection(self): - ''' - Activates the point selection mode, where the user can select points by clicking on them - ''' - self.state = SELECT - - def activate_polygon_selection(self): - ''' - Activates the polygon selection mode, where the user can select points by drawing a polygon around them - ''' - self.state = SELECT_POLYGON - - def activate_panning(self): - ''' - Activates the panning mode, where the user can move the zoom projection by dragging the mouse - ''' - self.state = PANNING - - def set_show_main_title(self, b): - ''' - Shows the main title if ``b`` is ``True``, and hides it otherwise. - ''' - self.showMainTitle = b - self.replot() - - def set_main_title(self, t): - ''' - Sets the main title to ``t`` - ''' - self.mainTitle = t - self.replot() - - def setShowXaxisTitle(self, b = -1): - if b == -1 and hasattr(self, 'showXaxisTitle'): - b = self.showXaxisTitle - self.set_show_axis_title(xBottom, b) - - def setXaxisTitle(self, title): - self.set_axis_title(xBottom, title) - - def setShowYLaxisTitle(self, b = -1): - if b == -1 and hasattr(self, 'showYLaxisTitle'): - b = self.showYLaxisTitle - self.set_show_axis_title(yLeft, b) - - def setYLaxisTitle(self, title): - self.set_axis_title(yLeft, title) - - def setShowYRaxisTitle(self, b = -1): - if b == -1 and hasattr(self, 'showYRaxisTitle'): - b = self.showYRaxisTitle - self.set_show_axis_title(yRight, b) - - def setYRaxisTitle(self, title): - self.set_axis_title(yRight, title) - - def enableGridXB(self, b): - self.grid_curve.set_x_enabled(b) - self.replot() - - def enableGridYL(self, b): - self.grid_curve.set_y_enabled(b) - self.replot() - - def setGridColor(self, c): - self.grid_curve.set_pen(QPen(c)) - self.replot() - - def setCanvasColor(self, c): - p = self.palette() - p.setColor(OWPalette.Canvas, c) - self.set_palette(p) - - def setData(self, data): - self.clear() - self.replot() - - def setXlabels(self, labels): - if xBottom in self.axes: - self.set_axis_labels(xBottom, labels) - elif xTop in self.axes: - self.set_axis_labels(xTop, labels) - - def set_axis_autoscale(self, axis_id): - if axis_id in self.axes: - self.axes[axis_id].auto_scale = True - elif axis_id in self.data_range: - del self.data_range[axis_id] - - def set_axis_labels(self, axis_id, labels, values=None): - ''' - Sets the labels of axis ``axis_id`` to ``labels``. This is used for axes displaying a discrete data type. - - :param labels: The ID of the axis to change - :type labels: int - - :param labels: The list of labels to be displayed along the axis - :type labels: A list of strings - - .. note:: This changes the axis scale and removes any previous scale set with :meth:`set_axis_scale`. - ''' - if axis_id in self._bounds_cache: - del self._bounds_cache[axis_id] - self._transform_cache = {} - self.axes[axis_id].set_labels(labels, values) - - def set_axis_scale(self, axis_id, min, max, step_size=0): - ''' - Sets the scale of axis ``axis_id`` to show an interval between ``min`` and ``max``. - If ``step`` is specified and non-zero, it determines the steps between label on the axis. - Otherwise, they are calculated automatically. - - .. note:: This changes the axis scale and removes any previous labels set with :meth:`set_axis_labels`. - ''' - if axis_id in self._bounds_cache: - del self._bounds_cache[axis_id] - self._transform_cache = {} - if axis_id in self.axes: - self.axes[axis_id].set_scale(min, max, step_size) - else: - self.data_range[axis_id] = (min, max) - - def set_axis_title(self, axis_id, title): - if axis_id in self.axes: - self.axes[axis_id].set_title(title) - - def set_show_axis_title(self, axis_id, b): - if axis_id in self.axes: - if b == -1: - b = not self.axes[axis_id].show_title - self.axes[axis_id].set_show_title(b) - self.replot() - - def set_axis_tick_length(self, axis_id, minor, medium, major): - if axis_id in self.axes: - self.axes[axis_id].set_tick_legth(minor, medium, major) - - def setYLlabels(self, labels): - self.set_axis_labels(yLeft, labels) - - def setYRlabels(self, labels): - self.set_axis_labels(yRight, labels) - - def add_custom_curve(self, curve, enableLegend = False): - ''' - Adds a custom PlotItem ``curve`` to the plot. - If ``enableLegend`` is ``True``, a curve symbol defined by - :meth:`.OWCurve.point_item` and the ``curve``'s name - :obj:`.OWCurve.name` is added to the legend. - - This function recalculates axis bounds and replots the plot if needed. - - :param curve: The curve to add - :type curve: :obj:`.OWCurve` - ''' - self.add_item(curve) - if enableLegend: - self.legend().add_curve(curve) - for key in [curve.axes()]: - if key in self._bounds_cache: - del self._bounds_cache[key] - self._transform_cache = {} - if hasattr(curve, 'tooltip'): - curve.setToolTip(curve.tooltip) - x,y = curve.axes() - if curve.is_auto_scale() and (self.is_axis_auto_scale(x) or self.is_axis_auto_scale(y)): - self.set_dirty() - self.replot() - else: - curve.set_graph_transform(self.transform_for_axes(x,y)) - curve.update_properties() - return curve - - def add_curve(self, name, brushColor = None, penColor = None, size = 5, style = Qt.NoPen, - symbol = OWPoint.Ellipse, enableLegend = False, xData = [], yData = [], showFilledSymbols = None, - lineWidth = 1, pen = None, autoScale = 0, antiAlias = None, penAlpha = 255, brushAlpha = 255, - x_axis_key = xBottom, y_axis_key = yLeft): - ''' - Creates a new :obj:`.OWCurve` with the specified parameters and adds it to the graph. - If ``enableLegend`` is ``True``, a curve symbol is added to the legend. - ''' - c = OWCurve(xData, yData, x_axis_key, y_axis_key, tooltip=name) - c.set_zoom_transform(self._zoom_transform) - c.name = name - c.set_style(style) - - if not brushColor: - brushColor = self.color(OWPalette.Data) - if not penColor: - penColor = self.color(OWPalette.Data) - - c.set_color(penColor) - - if pen: - p = pen - else: - p = QPen() - p.setColor(penColor) - p.setWidth(lineWidth) - c.set_pen(p) - - c.set_brush(brushColor) - - c.set_symbol(symbol) - c.set_point_size(size) - c.set_data(xData, yData) - - c.set_auto_scale(autoScale) - - return self.add_custom_curve(c, enableLegend) - - def set_main_curve_data(self, x_data, y_data, color_data, label_data, size_data, shape_data, marked_data = [], valid_data = [], x_axis_key=xBottom, y_axis_key=yLeft): - """ - Creates a single curve that can have points of different colors, shapes and sizes. - This is the preferred method for visualization that show a series of different points. - - :param x_data: The list of X coordinates of the points - :type x_data: list of float - - :param y_data: The list of Y coordinates of the points - :type y_data: list of float - - :param color_data: The list of point colors - :type color_data: list of QColor - - :param label_data: The list of point labels - :type label_data: list of str - - :param size_data: The list of point sizes - :type size_data: list of int - - :param shape_data: The list of point symbols - :type shape_data: list of int - - The number of points in the curve will be equal to min(len(x_data), len(y_data)). - The other four list can be empty, in which case a default value will be used. - If they contain only one element, its value will be used for all points. - - .. note:: This function does not add items to the legend automatically. - You will have to add them yourself with :meth:`.OWLegend.add_item`. - - .. seealso:: :obj:`.OWMultiCurve`, :obj:`.OWPoint` - """ - if not self.main_curve: - self.main_curve = OWMultiCurve([], []) - self.add_item(self.main_curve) - - self.update_performance(len(x_data)) - - if len(valid_data): - import numpy - x_data = numpy.compress(valid_data, x_data) - y_data = numpy.compress(valid_data, y_data) - if len(color_data) > 1: - color_data = numpy.compress(valid_data, color_data) - if len(size_data) > 1: - size_data = numpy.compress(valid_data, size_data) - if len(shape_data) > 1: - shape_data = numpy.compress(valid_data, shape_data) - if len(label_data) > 1: - label_data = numpy.compress(valid_data, label_data) - if len(marked_data) > 1: - marked_data = numpy.compress(valid_data, marked_data).tolist() - - c = self.main_curve - c.set_data(x_data, y_data) - c.set_axes(x_axis_key, y_axis_key) - c.set_point_colors(color_data) - c.set_point_labels(label_data) - c.set_point_sizes(size_data) - c.set_point_symbols(shape_data) - if len(marked_data): - c.set_points_marked(marked_data) - self.marked_points_changed.emit() - c.name = 'Main Curve' - - self.replot() - - def remove_curve(self, item): - ''' - Removes ``item`` from the plot - ''' - self.remove_item(item) - self.legend().remove_curve(item) - - def plot_data(self, xData, yData, colors, labels, shapes, sizes): - pass - - def add_axis(self, axis_id, title='', title_above=False, title_location=AxisMiddle, - line=None, arrows=0, zoomable=False, bounds=None): - ''' - Creates an :obj:`OrangeWidgets.plot.OWAxis` with the specified ``axis_id`` and ``title``. - ''' - a = OWAxis(axis_id, title, title_above, title_location, line, arrows, self, bounds=bounds) - self.scene().addItem(a) - a.zoomable = zoomable - a.update_callback = self.replot - if axis_id in self._bounds_cache: - del self._bounds_cache[axis_id] - self._transform_cache = {} - self.axes[axis_id] = a - if not axis_id in CartesianAxes: - self.set_show_axis_title(axis_id, True) - return a - - def remove_all_axes(self, user_only = True): - ''' - Removes all axes from the plot - ''' - ids = [] - for id,item in self.axes.items(): - if not user_only or id >= UserAxis: - ids.append(id) - self.scene().removeItem(item) - for id in ids: - del self.axes[id] - - def add_custom_axis(self, axis_id, axis): - ''' - Adds a custom ``axis`` with id ``axis_id`` to the plot - ''' - self.axes[axis_id] = axis - self.replot() - - def add_marker(self, name, x, y, alignment = -1, bold = 0, color = None, brushColor = None, size=None, antiAlias = None, - x_axis_key = xBottom, y_axis_key = yLeft): - m = Marker(name, x, y, alignment, bold, color, brushColor) - self._marker_items.append((m, x, y, x_axis_key, y_axis_key)) - self.add_custom_curve(m) - - return m - - def removeAllSelections(self): - ## TODO - pass - - def clear(self): - """ - Clears the plot, removing all curves, markers and tooltips. - Axes and the grid are not removed - """ - for i in self.plot_items(): - if i is not self.grid_curve: - self.remove_item(i) - self.main_curve = None - self._bounds_cache = {} - self._transform_cache = {} - self.clear_markers() - self.tips.removeAll() - self.legend().clear() - self.old_legend_margin = None - self.update_grid() - - def clear_markers(self): - """ - Removes all markers added with :meth:`add_marker` from the plot - """ - for item,x,y,x_axis,y_axis in self._marker_items: - item.detach() - self._marker_items = [] - - def update_layout(self): - ''' - Updates the plot layout. - - This function recalculates the position of titles, axes, the legend and the main plot area. - It does not update the curve or the other plot items. - ''' - if not self.isVisible(): - # No point in updating the graph if it's still hidden - return - graph_rect = QRectF(self.contentsRect()) - self.centerOn(graph_rect.center()) - m = self.graph_margin - graph_rect.adjust(m, m, -m, -m) - - if self.showMainTitle and self.mainTitle: - if self.title_item: - self.scene().remove_item(self.title_item) - del self.title_item - self.title_item = QGraphicsTextItem(self.mainTitle, scene=self.scene()) - title_size = self.title_item.boundingRect().size() - ## TODO: Check if the title is too big - self.title_item.setPos( graph_rect.width()/2 - title_size.width()/2, self.title_margin/2 - title_size.height()/2 ) - graph_rect.setTop(graph_rect.top() + self.title_margin) - - if self.show_legend: - self._legend_outside_area = QRectF(graph_rect) - self._legend.max_size = self._legend_outside_area.size() - r = self._legend_margin - graph_rect.adjust(r.left(), r.top(), -r.right(), -r.bottom()) - - self._legend.update_items() - - axis_rects = dict() - base_margin = min(self.axis_margin, graph_rect.height()/4, graph_rect.height()/4) - if xBottom in self.axes and self.axes[xBottom].isVisible(): - margin = base_margin - if self.axes[xBottom].should_be_expanded(): - margin += min(20, graph_rect.height()/8, graph_rect.width() / 8) - bottom_rect = QRectF(graph_rect) - bottom_rect.setTop( bottom_rect.bottom() - margin) - axis_rects[xBottom] = bottom_rect - graph_rect.setBottom( graph_rect.bottom() - margin) - if xTop in self.axes and self.axes[xTop].isVisible(): - margin = base_margin - if self.axes[xTop].should_be_expanded(): - margin += min(20, graph_rect.height()/8, graph_rect.width() / 8) - top_rect = QRectF(graph_rect) - top_rect.setBottom(top_rect.top() + margin) - axis_rects[xTop] = top_rect - graph_rect.setTop(graph_rect.top() + margin) - if yLeft in self.axes and self.axes[yLeft].isVisible(): - margin = base_margin - if self.axes[yLeft].should_be_expanded(): - margin += min(20, graph_rect.height()/8, graph_rect.width() / 8) - left_rect = QRectF(graph_rect) - left = graph_rect.left() + margin + self.y_axis_extra_margin - left_rect.setRight(left) - graph_rect.setLeft(left) - axis_rects[yLeft] = left_rect - if xBottom in axis_rects: - axis_rects[xBottom].setLeft(left) - if xTop in axis_rects: - axis_rects[xTop].setLeft(left) - if yRight in self.axes and self.axes[yRight].isVisible(): - margin = base_margin - if self.axes[yRight].should_be_expanded(): - margin += min(20, graph_rect.height()/8, graph_rect.width() / 8) - right_rect = QRectF(graph_rect) - right = graph_rect.right() - margin - self.y_axis_extra_margin - right_rect.setLeft(right) - graph_rect.setRight(right) - axis_rects[yRight] = right_rect - if xBottom in axis_rects: - axis_rects[xBottom].setRight(right) - if xTop in axis_rects: - axis_rects[xTop].setRight(right) - - if self.graph_area != graph_rect: - self.graph_area = QRectF(graph_rect) - self.set_graph_rect(self.graph_area) - self._transform_cache = {} - - if self._zoom_rect: - data_zoom_rect = self.map_transform.inverted()[0].mapRect(self._zoom_rect) - self.map_transform = self.transform_for_axes() - self.set_zoom_rect(self.map_transform.mapRect(data_zoom_rect)) - - self.map_transform = self.transform_for_axes() - - for c in self.plot_items(): - x,y = c.axes() - c.set_graph_transform(self.transform_for_axes(x,y)) - c.update_properties() - - def update_zoom(self): - ''' - Updates the zoom transformation of the plot items. - ''' - zt = self.zoom_transform() - self._zoom_transform = zt - self.set_zoom_transform(zt) - - self.update_axes(zoom_only=True) - self.viewport().update() - - def update_axes(self, zoom_only=False): - """ - Updates the axes. - - If ``zoom_only`` is ``True``, only the positions of the axes and their labels are recalculated. - Otherwise, all their labels are updated. - """ - if self.warn_unused_attributes and not zoom_only: - self._legend.remove_category(UNUSED_ATTRIBUTES_STR) - - for id, item in self.axes.items(): - if item.scale is None and item.labels is None: - item.auto_range = self.bounds_for_axis(id) - - if id in XAxes: - (x,y) = (id, yLeft) - elif id in YAxes: - (x,y) = (xBottom, id) - else: - (x,y) = (xBottom, yLeft) - - if id in CartesianAxes: - ## This class only sets the lines for these four axes, widgets are responsible for the rest - if x in self.axes and y in self.axes: - item.data_line = self.axis_line(self.data_rect_for_axes(x,y), id) - if id in CartesianAxes: - item.graph_line = self.axis_line(self.graph_area, id, invert_y = True) - elif item.data_line: - t = self.transform_for_axes(x, y) - item.graph_line = t.map(item.data_line) - - if item.graph_line and item.zoomable: - item.graph_line = self._zoom_transform.map(item.graph_line) - - if not zoom_only: - if item.graph_line: - item.show() - else: - item.hide() - if self.warn_unused_attributes: - self._legend.add_item(UNUSED_ATTRIBUTES_STR, item.title, None) - item.zoom_transform = self._zoom_transform - item.update(zoom_only) - - def replot(self): - ''' - Replot the entire graph. - - This functions redraws everything on the graph, so it can be very slow - ''' - #self.setBackgroundBrush(self.color(OWPalette.Canvas)) - self._bounds_cache = {} - self._transform_cache = {} - self.set_clean() - self.update_antialiasing() - self.update_legend() - self.update_layout() - self.update_zoom() - self.update_axes() - self.update_grid() - self.update_filled_symbols() - self.setSceneRect(QRectF(self.contentsRect())) - self.viewport().update() - - def update_legend(self): - if self.show_legend and not self._legend_moved: - ## If the legend hasn't been moved it, we set it outside, in the top right corner - m = self.graph_margin - r = QRectF(self.contentsRect()) - r.adjust(m, m, -m, -m) - self._legend.max_size = r.size() - self._legend.update_items() - w = self._legend.boundingRect().width() - self._legend_margin = QRectF(0, 0, w, 0) - self._legend.set_floating(False) - self._legend.set_orientation(Qt.Vertical) - self._legend.setPos(QRectF(self.contentsRect()).topRight() + QPointF(-w, 0)) - - - if (self._legend.isVisible() == self.show_legend): - return - - self._legend.setVisible(self.show_legend) - if self.show_legend: - if self.old_legend_margin is not None: - self.animate(self, 'legend_margin', self.old_legend_margin, duration = 100) - else: - r = self.legend_rect() - self.ensure_inside(r, self.contentsRect()) - self._legend.setPos(r.topLeft()) - self.notify_legend_moved(r.topLeft()) - else: - self.old_legend_margin = self.legend_margin - self.animate(self, 'legend_margin', QRectF(), duration=100) - - def update_filled_symbols(self): - ## TODO: Implement this in Curve.cpp - pass - - def update_grid(self): - self.grid_curve.set_x_enabled(self.show_grid) - self.grid_curve.set_y_enabled(self.show_grid) - self.grid_curve.update_properties() - - def legend(self): - ''' - Returns the plot's legend, which is a :obj:`OrangeWidgets.plot.OWLegend` - ''' - return self._legend - - def legend_rect(self): - if self.show_legend: - return self._legend.mapRectToScene(self._legend.boundingRect()) - else: - return QRectF() - - def isLegendEvent(self, event, function): - if self.show_legend and self.legend_rect().contains(self.mapToScene(event.pos())): - function(self, event) - return True - else: - return False - - def mouse_action(self, event): - b = event.buttons() | event.button() - m = event.modifiers() - if b == Qt.LeftButton | Qt.RightButton: - b = Qt.MidButton - if m & Qt.AltModifier and b == Qt.LeftButton: - m = m & ~Qt.AltModifier - b = Qt.MidButton - - if b == Qt.LeftButton and not m: - return self.state - - if b == Qt.RightButton and not m and self.state == SELECT: - return SELECT_RIGHTCLICK - - if b == Qt.MidButton: - return PANNING - - if b in [Qt.LeftButton, Qt.RightButton] and (self.state == ZOOMING or m == Qt.ControlModifier): - return ZOOMING - - if b == Qt.LeftButton and m == Qt.ShiftModifier: - return SELECT - - ## Event handling - - def event(self, event): - if event.type() == QEvent.Gesture: - return self.gestureEvent(event) - else: - return orangeqt.Plot.event(self, event) - - def gestureEvent(self, event): - for gesture in event.gestures(): - if gesture.state() == Qt.GestureStarted: - self.current_gesture_scale = 1. - event.accept(gesture) - continue - elif gesture.gestureType() == Qt.PinchGesture: - old_animate_plot = self.animate_plot - self.animate_plot = False - self.zoom(gesture.centerPoint(), gesture.scaleFactor()/self.current_gesture_scale ) - self.current_gesture_scale = gesture.scaleFactor() - self.animate_plot = old_animate_plot - elif gesture.gestureType() == Qt.PanGesture: - self.pan(gesture.delta()) - return True - - def resizeEvent(self, event): - self.replot() - s = event.size() - event.oldSize() - if self.legend_margin.right() > 0: - self._legend.setPos(self._legend.pos() + QPointF(s.width(), 0)) - if self.legend_margin.bottom() > 0: - self._legend.setPos(self._legend.pos() + QPointF(0, s.height())) - - def showEvent(self, event): - self.replot() - - def mousePressEvent(self, event): - self.static_click = True - self._pressed_mouse_button = event.button() - self._pressed_mouse_pos = event.pos() - - if self.mousePressEventHandler and self.mousePressEventHandler(event): - event.accept() - return - - if self.isLegendEvent(event, QGraphicsView.mousePressEvent): - return - - point = self.mapToScene(event.pos()) - a = self.mouse_action(event) - - if a == SELECT and hasattr(self, 'move_selected_points'): - self._pressed_point = self.nearest_point(point) - self._pressed_point_coor = None - if self._pressed_point is not None: - self._pressed_point_coor = self._pressed_point.coordinates() - - if a == PANNING: - self._last_pan_pos = point - event.accept() - else: - orangeqt.Plot.mousePressEvent(self, event) - - def mouseMoveEvent(self, event): - if event.buttons() and (self._pressed_mouse_pos - event.pos()).manhattanLength() > QApplication.instance().startDragDistance(): - self.static_click = False - - if self.mouseMoveEventHandler and self.mouseMoveEventHandler(event): - event.accept() - return - - if self.isLegendEvent(event, QGraphicsView.mouseMoveEvent): - return - - point = self.mapToScene(event.pos()) - if not self._pressed_mouse_button: - if self.receivers(self.point_hovered) > 0: - self.point_hovered.emit(self.nearest_point(point)) - - ## We implement a workaround here, because sometimes mouseMoveEvents are not fast enough - ## so the moving legend gets left behind while dragging, and it's left in a pressed state - if self._legend.mouse_down: - QGraphicsView.mouseMoveEvent(self, event) - return - - a = self.mouse_action(event) - - if a == SELECT and self._pressed_point is not None and self._pressed_point.is_selected() and hasattr(self, 'move_selected_points'): - animate_points = self.animate_points - self.animate_points = False - x1, y1 = self._pressed_point_coor - x2, y2 = self.map_from_graph(point, zoom=True) - self.move_selected_points((x2 - x1, y2 - y1)) - self.replot() - if self._pressed_point is not None: - self._pressed_point_coor = self._pressed_point.coordinates() - - self.animate_points = animate_points - - elif a in [SELECT, ZOOMING] and self.graph_area.contains(point): - if not self._current_rs_item: - self._selection_start_point = self.mapToScene(self._pressed_mouse_pos) - self._current_rs_item = QGraphicsRectItem(scene=self.scene()) - self._current_rs_item.setPen(SelectionPen) - self._current_rs_item.setBrush(SelectionBrush) - self._current_rs_item.setZValue(SelectionZValue) - self._current_rs_item.setRect(QRectF(self._selection_start_point, point).normalized()) - elif a == PANNING: - if not self._last_pan_pos: - self._last_pan_pos = self.mapToScene(self._pressed_mouse_pos) - self.pan(point - self._last_pan_pos) - self._last_pan_pos = point - else: - x, y = self.map_from_graph(point, zoom=True) - text, x, y = self.tips.maybeTip(x, y) - if type(text) == int: - text = self.buildTooltip(text) - if text and x is not None and y is not None: - tp = self.mapFromScene(QPointF(x,y) * self.map_transform * self._zoom_transform) - self.showTip(tp.x(), tp.y(), text) - else: - orangeqt.Plot.mouseMoveEvent(self, event) - - - def mouseReleaseEvent(self, event): - self._pressed_mouse_button = Qt.NoButton - - if self.mouseReleaseEventHandler and self.mouseReleaseEventHandler(event): - event.accept() - return - if self.static_click and self.mouseStaticClickHandler and self.mouseStaticClickHandler(event): - event.accept() - return - - if self.isLegendEvent(event, QGraphicsView.mouseReleaseEvent): - return - - a = self.mouse_action(event) - if a == SELECT and self._pressed_point is not None: - self._pressed_point = None - if a in [ZOOMING, SELECT] and self._current_rs_item: - rect = self._current_rs_item.rect() - if a == ZOOMING: - self.zoom_to_rect(self._zoom_transform.inverted()[0].mapRect(rect)) - else: - self.add_selection(rect) - self.scene().removeItem(self._current_rs_item) - self._current_rs_item = None - return - orangeqt.Plot.mouseReleaseEvent(self, event) - - def mouseStaticClick(self, event): - point = self.mapToScene(event.pos()) - if point not in self.graph_area: - return False - - a = self.mouse_action(event) - b = event.buttons() | event.button() - - if a == ZOOMING: - if event.button() == Qt.LeftButton: - self.zoom_in(point) - elif event.button() == Qt.RightButton: - self.zoom_back() - else: - return False - return True - elif a == SELECT and b == Qt.LeftButton: - point_item = self.nearest_point(point) - b = self.selection_behavior - - if b == self.ReplaceSelection: - self.unselect_all_points() - b = self.AddSelection - - if point_item: - point_item.set_selected(b == self.AddSelection or (b == self.ToggleSelection and not point_item.is_selected())) - self.selection_changed.emit() - elif a == SELECT and b == Qt.RightButton: - point_item = self.nearest_point(point) - if point_item: - self.point_rightclicked.emit(self.nearest_point(point)) - else: - self.unselect_all_points() - else: - return False - - def wheelEvent(self, event): - point = self.mapToScene(event.pos()) - d = event.delta() / 120.0 - self.zoom(point, pow(2,d)) - - @staticmethod - def transform_from_rects(r1, r2): - """ - Returns a QTransform that maps from rectangle ``r1`` to ``r2``. - """ - if r1 is None or r2 is None: - return QTransform() - if r1.width() == 0 or r1.height() == 0 or r2.width() == 0 or r2.height() == 0: - return QTransform() - tr1 = QTransform().translate(-r1.left(), -r1.top()) - ts = QTransform().scale(r2.width()/r1.width(), r2.height()/r1.height()) - tr2 = QTransform().translate(r2.left(), r2.top()) - return tr1 * ts * tr2 - - def transform_for_zoom(self, factor, point, rect): - if factor == 1: - return QTransform() - - dp = point - - t = QTransform() - t.translate(dp.x(), dp.y()) - t.scale(factor, factor) - t.translate(-dp.x(), -dp.y()) - return t - - def rect_for_zoom(self, point, old_rect, scale = 2): - r = QRectF() - r.setWidth(old_rect.width() / scale) - r.setHeight(old_rect.height() / scale) - r.moveCenter(point) - - self.ensure_inside(r, self.graph_area) - - return r - - def set_state(self, state): - self.state = state - if state != SELECT_RECTANGLE: - self._current_rs_item = None - if state != SELECT_POLYGON: - self._current_ps_item = None - - def get_selected_points(self, xData, yData, validData): - if self.main_curve: - selected = [] - points = self.main_curve.points() - i = 0 - for d in validData: - if d: - selected.append(points[i].is_selected()) - i += 1 - else: - selected.append(False) - else: - selected = self.selected_points(xData, yData) - unselected = [not i for i in selected] - return selected, unselected - - def add_selection(self, reg): - """ - Selects all points in the region ``reg`` using the current :attr: `selection_behavior`. - """ - self.select_points(reg, self.selection_behavior) - self.viewport().update() - if self.auto_send_selection_callback: - self.auto_send_selection_callback() - - def points_equal(self, p1, p2): - if type(p1) == tuple: - (x, y) = p1 - p1 = QPointF(x, y) - if type(p2) == tuple: - (x, y) = p2 - p2 = QPointF(x, y) - return (QPointF(p1)-QPointF(p2)).manhattanLength() < self.polygon_close_treshold - - def data_rect_for_axes(self, x_axis = xBottom, y_axis = yLeft): - """ - Calculates the bounding rectangle in data coordinates for the axes ``x_axis`` and ``y_axis``. - """ - if x_axis in self.axes and y_axis in self.axes: - x_min, x_max = self.bounds_for_axis(x_axis, try_auto_scale=True) - y_min, y_max = self.bounds_for_axis(y_axis, try_auto_scale=True) - if (x_min or x_max) and (y_min or y_max): - r = QRectF(x_min, y_min, x_max-x_min, y_max-y_min) - return r - r = orangeqt.Plot.data_rect_for_axes(self, x_axis, y_axis) - for id, axis in self.axes.items(): - if id not in CartesianAxes and axis.data_line: - r |= QRectF(axis.data_line.p1(), axis.data_line.p2()) - ## We leave a 5% margin on each side so the graph doesn't look overcrowded - ## TODO: Perhaps change this from a fixed percentage to always round to a round number - dx = r.width() / 20.0 - dy = r.height() / 20.0 - r.adjust(-dx, -dy, dx, dy) - return r - - def transform_for_axes(self, x_axis = xBottom, y_axis = yLeft): - """ - Returns the graph transform that maps from data to scene coordinates using axes ``x_axis`` and ``y_axis``. - """ - if not (x_axis, y_axis) in self._transform_cache: - # We must flip the graph area, becase Qt coordinates start from top left, while graph coordinates start from bottom left - a = QRectF(self.graph_area) - t = a.top() - a.setTop(a.bottom()) - a.setBottom(t) - self._transform_cache[(x_axis, y_axis)] = self.transform_from_rects(self.data_rect_for_axes(x_axis, y_axis), a) - return self._transform_cache[(x_axis, y_axis)] - - def transform(self, axis_id, value): - """ - Transforms the ``value`` from data to plot coordinates along the axis ``axis_id``. - - This function always ignores zoom. If you need to account for zooming, use :meth:`map_to_graph`. - """ - if axis_id in XAxes: - size = self.graph_area.width() - margin = self.graph_area.left() - else: - size = self.graph_area.height() - margin = self.graph_area.top() - m, M = self.bounds_for_axis(axis_id) - if m is None or M is None or M == m: - return 0 - else: - return margin + (value-m)/(M-m) * size - - def inv_transform(self, axis_id, value): - """ - Transforms the ``value`` from plot to data coordinates along the axis ``axis_id``. - - This function always ignores zoom. If you need to account for zooming, use :meth:`map_from_graph`. - """ - if axis_id in XAxes: - size = self.graph_area.width() - margin = self.graph_area.left() - else: - size = self.graph_area.height() - margin = self.graph_area.top() - m, M = self.bounds_for_axis(axis_id) - if m is not None and M is not None: - return m + (value-margin)/size * (M-m) - else: - return 0 - - def bounds_for_axis(self, axis_id, try_auto_scale=True): - if axis_id in self.axes and not self.axes[axis_id].auto_scale: - return self.axes[axis_id].bounds() - if try_auto_scale: - lower, upper = orangeqt.Plot.bounds_for_axis(self, axis_id) - if lower != upper: - lower = lower - (upper-lower)/20.0 - upper = upper + (upper-lower)/20.0 - return lower, upper - else: - return None, None - - def enableYRaxis(self, enable=1): - self.set_axis_enabled(yRight, enable) - - def enableLRaxis(self, enable=1): - self.set_axis_enabled(yLeft, enable) - - def enableXaxis(self, enable=1): - self.set_axis_enabled(xBottom, enable) - - def set_axis_enabled(self, axis, enable): - if axis not in self.axes: - self.add_axis(axis) - self.axes[axis].setVisible(enable) - self.replot() - - @staticmethod - def axis_coordinate(point, axis_id): - if axis_id in XAxes: - return point.x() - elif axis_id in YAxes: - return point.y() - else: - return None - - # #################################################################### - # return string with attribute names and their values for example example - def getExampleTooltipText(self, example, indices=None, maxIndices=20): - if indices and type(indices[0]) == str: - indices = [self.attributeNameIndex[i] for i in indices] - if not indices: - indices = list(range(len(self.dataDomain.attributes))) - - # don't show the class value twice - if example.domain.classVar: - classIndex = self.attributeNameIndex[example.domain.classVar.name] - while classIndex in indices: - indices.remove(classIndex) - - text = "Attributes:
" - for index in indices[:maxIndices]: - attr = self.attributeNames[index] - if attr not in example.domain: text += " "*4 + "%s = ?
" % (Qt.escape(attr)) - elif example[attr].isSpecial(): text += " "*4 + "%s = ?
" % (Qt.escape(attr)) - else: text += " "*4 + "%s = %s
" % (Qt.escape(attr), Qt.escape(str(example[attr]))) - if len(indices) > maxIndices: - text += " "*4 + " ...
" - - if example.domain.classVar: - text = text[:-4] - text += "
Class:
" - if example.getclass().isSpecial(): text += " "*4 + "%s = ?
" % (Qt.escape(example.domain.classVar.name)) - else: text += " "*4 + "%s = %s
" % (Qt.escape(example.domain.classVar.name), Qt.escape(str(example.getclass()))) - - if len(example.domain.getmetas()) != 0: - text = text[:-4] - text += "
Meta attributes:
" - # show values of meta attributes - for key in example.domain.getmetas(): - try: text += " "*4 + "%s = %s
" % (Qt.escape(example.domain[key].name), Qt.escape(str(example[key]))) - except: pass - return text[:-4] # remove the last
- - # show a tooltip at x,y with text. if the mouse will move for more than 2 pixels it will be removed - def showTip(self, x, y, text): - QToolTip.showText(self.mapToGlobal(QPoint(x, y)), text, self, QRect(x-3,y-3,6,6)) - - def notify_legend_moved(self, pos): - self._legend_moved = True - l = self.legend_rect() - g = getattr(self, '_legend_outside_area', QRectF()) - p = QPointF() - rect = QRectF() - offset = 20 - if pos.x() > g.right() - offset: - self._legend.set_orientation(Qt.Vertical) - rect.setRight(self._legend.boundingRect().width()) - p = g.topRight() - self._legend.boundingRect().topRight() - elif pos.x() < g.left() + offset: - self._legend.set_orientation(Qt.Vertical) - rect.setLeft(self._legend.boundingRect().width()) - p = g.topLeft() - elif pos.y() < g.top() + offset: - self._legend.set_orientation(Qt.Horizontal) - rect.setTop(self._legend.boundingRect().height()) - p = g.topLeft() - elif pos.y() > g.bottom() - offset: - self._legend.set_orientation(Qt.Horizontal) - rect.setBottom(self._legend.boundingRect().height()) - p = g.bottomLeft() - self._legend.boundingRect().bottomLeft() - - if p.isNull(): - self._legend.set_floating(True, pos) - else: - self._legend.set_floating(False, p) - - if rect != self._legend_margin: - orientation = Qt.Horizontal if rect.top() or rect.bottom() else Qt.Vertical - self._legend.set_orientation(orientation) - self.animate(self, 'legend_margin', rect, duration=100) - - def get_legend_margin(self): - return self._legend_margin - - def set_legend_margin(self, value): - self._legend_margin = value - self.update_layout() - self.update_axes() - - legend_margin = pyqtProperty(QRectF, get_legend_margin, set_legend_margin) - - def update_curves(self): - if self.main_curve: - self.main_curve.set_alpha_value(self.alpha_value) - else: - for c in self.plot_items(): - if isinstance(c, orangeqt.Curve) and not getattr(c, 'ignore_alpha', False): - au = c.auto_update() - c.set_auto_update(False) - c.set_point_size(self.point_width) - color = c.color() - color.setAlpha(self.alpha_value) - c.set_color(color) - c.set_auto_update(au) - c.update_properties() - self.viewport().update() - - update_point_size = update_curves - update_alpha_value = update_curves - - def update_antialiasing(self, use_antialiasing=None): - if use_antialiasing is not None: - self.antialias_plot = use_antialiasing - - self.setRenderHint(QPainter.Antialiasing, self.antialias_plot) - - def update_animations(self, use_animations=None): - if use_animations is not None: - self.animate_plot = use_animations - self.animate_points = use_animations - - def update_performance(self, num_points = None): - if self.auto_adjust_performance: - if not num_points: - if self.main_curve: - num_points = len(self.main_curve.points()) - else: - num_points = sum( len(c.points()) for c in self.curves ) - if num_points > self.disable_animations_threshold: - self.disabled_animate_points = self.animate_points - self.animate_points = False - - self.disabled_animate_plot = self.animate_plot - self.animate_plot = False - - self.disabled_antialias_lines = self.animate_points - self.antialias_lines = True - - elif hasattr(self, 'disabled_animate_points'): - self.animate_points = self.disabled_animate_points - del self.disabled_animate_points - - self.animate_plot = self.disabled_animate_plot - del self.disabled_animate_plot - - self.antialias_lines = True # self.disabled_antialias_lines - del self.disabled_antialias_lines - - def animate(self, target, prop_name, end_val, duration = None, start_val = None): - for a in self._animations: - if a.state() == QPropertyAnimation.Stopped: - self._animations.remove(a) - if self.animate_plot: - a = QPropertyAnimation(target, prop_name) - a.setEndValue(end_val) - if start_val is not None: - a.setStartValue(start_val) - if duration: - a.setDuration(duration) - self._animations.append(a) - a.start(QPropertyAnimation.KeepWhenStopped) - else: - target.setProperty(prop_name, end_val) - - def clear_selection(self): - self.unselect_all_points() - - def send_selection(self): - if self.auto_send_selection_callback: - self.auto_send_selection_callback() - - def pan(self, delta): - if type(delta) == tuple: - x, y = delta - else: - x, y = delta.x(), delta.y() - t = self.zoom_transform() - x = x / t.m11() - y = y / t.m22() - r = QRectF(self.zoom_rect) - r.translate(-QPointF(x,y)) - self.ensure_inside(r, self.graph_area) - self.zoom_rect = r - - def zoom_to_rect(self, rect): - self.ensure_inside(rect, self.graph_area) - - # add to zoom_stack if zoom_rect is larger - if self.zoom_rect.width() > rect.width() or self.zoom_rect.height() > rect.height(): - self.zoom_stack.append(self.zoom_rect) - - self.animate(self, 'zoom_rect', rect, start_val = self.get_zoom_rect()) - - def zoom_back(self): - if self.zoom_stack: - rect = self.zoom_stack.pop() - self.animate(self, 'zoom_rect', rect, start_val = self.get_zoom_rect()) - - def reset_zoom(self): - self._zoom_rect = None - self.update_zoom() - - def zoom_transform(self): - return self.transform_from_rects(self.zoom_rect, self.graph_area) - - def zoom_in(self, point): - self.zoom(point, scale = 2) - - def zoom_out(self, point): - self.zoom(point, scale = 0.5) - - def zoom(self, point, scale): - print(len(self.zoom_stack)) - t, ok = self._zoom_transform.inverted() - point = point * t - r = QRectF(self.zoom_rect) - i = 1.0/scale - r.setTopLeft(point*(1-i) + r.topLeft()*i) - r.setBottomRight(point*(1-i) + r.bottomRight()*i) - - self.ensure_inside(r, self.graph_area) - - # remove smaller zoom rects from stack - while len(self.zoom_stack) > 0 and r.width() >= self.zoom_stack[-1].width() and r.height() >= self.zoom_stack[-1].height(): - self.zoom_stack.pop() - - self.zoom_to_rect(r) - - def get_zoom_rect(self): - if self._zoom_rect: - return self._zoom_rect - else: - return self.graph_area - - def set_zoom_rect(self, rect): - self._zoom_rect = rect - self._zoom_transform = self.transform_from_rects(rect, self.graph_area) - self.update_zoom() - - zoom_rect = pyqtProperty(QRectF, get_zoom_rect, set_zoom_rect) - - @staticmethod - def ensure_inside(small_rect, big_rect): - if small_rect.width() > big_rect.width(): - small_rect.setWidth(big_rect.width()) - if small_rect.height() > big_rect.height(): - small_rect.setHeight(big_rect.height()) - - if small_rect.right() > big_rect.right(): - small_rect.moveRight(big_rect.right()) - elif small_rect.left() < big_rect.left(): - small_rect.moveLeft(big_rect.left()) - - if small_rect.bottom() > big_rect.bottom(): - small_rect.moveBottom(big_rect.bottom()) - elif small_rect.top() < big_rect.top(): - small_rect.moveTop(big_rect.top()) - - def shuffle_points(self): - if self.main_curve: - self.main_curve.shuffle_points() - - def set_progress(self, done, total): - if not self.widget: - return - - if done == total: - self.widget.progressBarFinished() - else: - self.widget.progressBarSet(100.0 * done / total) - - def start_progress(self): - if self.widget: - self.widget.progressBarInit() - - def end_progress(self): - if self.widget: - self.widget.progressBarFinished() - - def is_axis_auto_scale(self, axis_id): - if axis_id not in self.axes: - return axis_id not in self.data_range - return self.axes[axis_id].auto_scale - - def axis_line(self, rect, id, invert_y = False): - if invert_y: - r = QRectF(rect) - r.setTop(rect.bottom()) - r.setBottom(rect.top()) - rect = r - if id == xBottom: - line = QLineF(rect.topLeft(), rect.topRight()) - elif id == xTop: - line = QLineF(rect.bottomLeft(), rect.bottomRight()) - elif id == yLeft: - line = QLineF(rect.topLeft(), rect.bottomLeft()) - elif id == yRight: - line = QLineF(rect.topRight(), rect.bottomRight()) - else: - line = None - return line - - def color(self, role, group = None): - if group: - return self.palette().color(group, role) - else: - return self.palette().color(role) - - def set_palette(self, p): - ''' - Sets the plot palette to ``p``. - - :param p: The new color palette - :type p: :obj:`.QPalette` - ''' - self.setPalette(p) - self.replot() - - def update_theme(self): - ''' - Updates the current color theme, depending on the value of :attr:`theme_name`. - ''' - if self.theme_name.lower() == 'default': - self.set_palette(OWPalette.System) - elif self.theme_name.lower() == 'light': - self.set_palette(OWPalette.Light) - elif self.theme_name.lower() == 'dark': - self.set_palette(OWPalette.Dark) diff --git a/Orange/widgets/utils/plot/owplot3d.py b/Orange/widgets/utils/plot/owplot3d.py deleted file mode 100644 index 35c65832771..00000000000 --- a/Orange/widgets/utils/plot/owplot3d.py +++ /dev/null @@ -1,1248 +0,0 @@ -''' - -################# -Plot3D (``owplot3D``) -################# - -.. autoclass:: OrangeWidgets.plot.OWPlot3D - -''' - -import os -import time -from math import sin, cos, pi -import struct - -from PyQt4.QtCore import * -from PyQt4.QtGui import * -from PyQt4 import QtOpenGL - -from OWDlgs import OWChooseImageSizeDlg -from Orange.utils import deprecated_attribute, deprecated_members - -import orangeqt -from .owplotgui import OWPlotGUI -from .owtheme import PlotTheme -from .Orange.widgets.utils.plot.owplot import OWPlot -from .owlegend import OWLegend, OWLegendItem, OWLegendTitle, OWLegendGradient -from .Orange.widgets.utils.plot.owopenglrenderer import OWOpenGLRenderer -from .owconstants import ZOOMING, PANNING, ROTATING - -from OWColorPalette import ColorPaletteGenerator - -import OpenGL -OpenGL.ERROR_CHECKING = False -OpenGL.ERROR_LOGGING = False -OpenGL.FULL_LOGGING = False -OpenGL.ERROR_ON_COPY = False -from OpenGL.GL import * -from OpenGL.GL.ARB.vertex_array_object import * -from OpenGL.GL.ARB.vertex_buffer_object import * -from ctypes import c_void_p, c_char, c_char_p, POINTER - -import numpy - -try: - from itertools import chain - -except: - pass - -def vec_div(v1, v2): - return QVector3D(v1.x() / v2.x(), - v1.y() / v2.y(), - v1.z() / v2.z()) - -def lower_bound(value, vec): - if vec.x() < value: - vec.setX(value) - if vec.y() < value: - vec.setY(value) - if vec.z() < value: - vec.setZ(value) - return vec - -def enum(*sequential): - enums = dict(zip(sequential, range(len(sequential)))) - enums['is_valid'] = lambda self, enum_value: enum_value < len(sequential) - enums['to_str'] = lambda self, enum_value: sequential[enum_value] - enums['__len__'] = lambda self: len(sequential) - return type('Enum', (), enums)() - -PlotState = enum('IDLE', 'DRAGGING_LEGEND', 'ROTATING', 'SCALING', 'SELECTING', 'PANNING') - -Symbol = enum('RECT', 'TRIANGLE', 'DTRIANGLE', 'CIRCLE', 'LTRIANGLE', - 'DIAMOND', 'WEDGE', 'LWEDGE', 'CROSS', 'XCROSS') - -from plot.primitives import get_symbol_geometry, clamp, GeometryType - -class OWLegend3D(OWLegend): - def set_symbol_geometry(self, symbol, geometry): - if not hasattr(self, '_symbol_geometry'): - self._symbol_geometry = {} - self._symbol_geometry[symbol] = geometry - - def _draw_item_background(self, pos, item, color): - rect = item.rect().normalized().adjusted(pos.x(), pos.y(), pos.x(), pos.y()) - self.widget.renderer.draw_rectangle( - QVector3D(rect.left(), rect.top(), 0), - QVector3D(rect.left(), rect.bottom(), 0), - QVector3D(rect.right(), rect.bottom(), 0), - QVector3D(rect.right(), rect.top(), 0), - color=color) - - def _draw_symbol(self, pos, symbol): - edges = self._symbol_geometry[symbol.symbol()] - color = symbol.color() - size = symbol.size() / 2 - for v0, v1 in zip(edges[::2], edges[1::2]): - x0, y0 = v0.x(), v0.y() - x1, y1 = v1.x(), v1.y() - self.widget.renderer.draw_line( - QVector3D(x0*size + pos.x(), -y0*size + pos.y(), 0), - QVector3D(x1*size + pos.x(), -y1*size + pos.y(), 0), - color=color) - - def _paint(self, widget): - self.widget = widget - glDisable(GL_DEPTH_TEST) - glDisable(GL_BLEND) - offset = QPointF(0, 15) # TODO - - for category in self.items: - items = self.items[category] - for item in items: - if isinstance(item, OWLegendTitle): - pos = self.pos() + item.pos() - self._draw_item_background(pos, item.rect_item, widget._theme.background_color) - - widget.qglColor(widget._theme.labels_color) - pos = self.pos() + item.pos() + item.text_item.pos() + offset - widget.renderText(pos.x(), pos.y(), item.text_item.toPlainText(), item.text_item.font()) - elif isinstance(item, OWLegendItem): - pos = self.pos() + item.pos() - self._draw_item_background(pos, item.rect_item, widget._theme.background_color) - - widget.qglColor(widget._theme.labels_color) - pos = self.pos() + item.pos() + item.text_item.pos() + offset - widget.renderText(pos.x(), pos.y(), item.text_item.toPlainText(), item.text_item.font()) - - symbol = item.point_item - pos = self.pos() + item.pos() + symbol.pos() - self._draw_symbol(pos, symbol) - elif isinstance(item, OWLegendGradient): - pos = self.pos() + item.pos() - proxy = lambda: None - proxy.rect = lambda: item.rect - self._draw_item_background(pos, proxy, widget._theme.background_color) - - widget.qglColor(widget._theme.labels_color) - for label in item.label_items: - pos = self.pos() + item.pos() + label.pos() + offset + QPointF(5, 0) - widget.renderText(pos.x(), pos.y(), label.toPlainText(), label.font()) - - pos = self.pos() + item.pos() + item.gradient_item.pos() - rect = item.gradient_item.rect().normalized().adjusted(pos.x(), pos.y(), pos.x(), pos.y()) - widget.renderer.draw_rectangle( - QVector3D(rect.left(), rect.top(), 0), - QVector3D(rect.left(), rect.bottom(), 0), - QVector3D(rect.right(), rect.bottom(), 0), - QVector3D(rect.right(), rect.top(), 0), - QColor(0, 0, 0), - QColor(0, 0, 255), - QColor(0, 0, 255), - QColor(0, 0, 0)) - -name_map = { - "saveToFileDirect": "save_to_file_direct", - "saveToFile" : "save_to_file", -} - -@deprecated_members(name_map, wrap_methods=list(name_map.keys())) -class OWPlot3D(orangeqt.Plot3D): - ''' - The base class behind 3D plots in Orange. Uses OpenGL as its rendering platform. - - **Settings** - - .. attribute:: show_legend - - A boolean controlling whether the legend is displayed or not - - .. attribute:: gui - - An :obj:`.OWPlotGUI` object associated with this graph - - **Data** - This is the most important part of the Plot3D API. :meth:`set_plot_data` is - used to (not surprisingly) set the data to draw. - :meth:`set_features` tells Plot3D how to interpret the data (this method must - be called after :meth:`set_plot_data` and can be called multiple times). - :meth:`set_valid_data` optionally informs the plot which examples are invalid and - should not be drawn. It should be called after set_plot_data, but before set_features. - This separation permits certain optimizations, e.g. ScatterPlot3D sets data once only (at - the beginning), later on it calls solely set_features and set_valid_data. - - .. automethod:: set_plot_data - - .. automethod:: set_valid_data - - .. automethod:: set_features - - **Selections** - There are four possible selection behaviors used for selecting points in OWPlot3D. - - .. data:: AddSelection - - Points are added to the current selection, without affecting currently selected points. - - .. data:: RemoveSelection - - Points are removed from the current selection. - - .. data:: ToggleSelection - - The points' selection state is toggled. - - .. data:: ReplaceSelection - - The current selection is replaced with new one. - - .. automethod:: select_points - - .. automethod:: unselect_all_points - - .. automethod:: get_selected_indices - - .. automethod:: get_min_max_selected - - .. automethod:: set_selection_behavior - - **Callbacks** - - Plot3D provides several callbacks which can be used to perform additional tasks ( - such as drawing custom geometry before/after the data is drawn). For example usage - see ``OWScatterPlot3D``. Callbacks provided: - - ============== ================================================== - Callback Event - -------------- -------------------------------------------------- - auto_send_selection_callback Selection changed. - mouseover_callback Mouse cursor moved over a point. Also send example's index. - before_draw_callback Right before drawing points (but after - current camera transformations have been computed, - so it's safe to use ``projection``, ``view`` and - ``model``). - after_draw_callback Right after points have been drawn. - ============== ================================================== - - **Coordinate transformations** - - Data set by ``set_plot_data`` is in what Plot3D calls - data coordinate system. Plot coordinate system takes into account plot translation - and scale (together effectively being zoom as well). - - .. automethod:: map_to_plot - - .. automethod:: map_to_data - - **Themes** - - Colors used for data points are specified with two palettes, one for continuous attributes, and one for - discrete ones. Both are created by - :obj:`.OWColorPalette.ColorPaletteGenerator` - - .. attribute:: continuous_palette - - The palette used when point color represents a continuous attribute - - .. attribute:: discrete_palette - - The palette used when point color represents a discrete attribute - ''' - def __init__(self, parent=None): - orangeqt.Plot3D.__init__(self, parent) - - # Don't clear background when using QPainter - self.setAutoFillBackground(False) - - self.camera_distance = 6. - self.scale_factor = 0.30 - self.rotation_factor = 0.3 - self.zoom_factor = 2000. - self.yaw = self.pitch = -pi / 4. - self.panning_factor = 0.8 - self.perspective_near = 0.5 - self.perspective_far = 10. - self.camera_fov = 14. - self.update_camera() - - self.show_legend = True - self._legend = OWLegend3D(self, None) - self._legend_margin = QRectF(0, 0, 100, 0) - self._legend_moved = False - self._legend.set_floating(True) - self._legend.set_orientation(Qt.Vertical) - - self.use_2d_symbols = False - self.symbol_scale = 1. - self.alpha_value = 255 - - self._state = PlotState.IDLE - self._selection = None - self.selection_behavior = OWPlot.AddSelection - - ## Callbacks - self.auto_send_selection_callback = None - self.mouseover_callback = None - self.before_draw_callback = None - self.after_draw_callback = None - - self.setMouseTracking(True) - self._mouse_position = QPoint(0, 0) - self.invert_mouse_x = False - self.mouse_sensitivity = 5 - - self.clear_plot_transformations() - - self._theme = PlotTheme() - self._tooltip_fbo_dirty = True - self._tooltip_win_center = [0, 0] - self._use_fbos = True - - # If True, do drawing using instancing + geometry shader processing, - # if False, build VBO every time set_plot_data is called. - self._use_opengl_3 = False - self.hide_outside = False - self.fade_outside = True - - self.data = self.data_array = None - self.x_index = -1 - self.y_index = -1 - self.z_index = -1 - self.label_index = -1 - self.color_index = -1 - self.symbol_index = -1 - self.size_index = -1 - self.colors = [] - self.num_symbols_used = -1 - self.x_discrete = False - self.y_discrete = False - self.z_discrete = False - - self.continuous_palette = ColorPaletteGenerator(numberOfColors=-1) - self.discrete_palette = ColorPaletteGenerator() - - # An :obj:`.OWPlotGUI` object associated with this plot - self.gui = OWPlotGUI(self) - - def legend(self): - ''' - Returns the plot's legend, which is a :obj:`OrangeWidgets.plot.OWLegend` - ''' - return self._legend - - def initializeGL(self): - if hasattr(self, '_init_done'): - return - self.makeCurrent() - glClearColor(1.0, 1.0, 1.0, 1.0) - glClearDepth(1.0) - glDepthFunc(GL_LESS) - glEnable(GL_DEPTH_TEST) - glEnable(GL_LINE_SMOOTH) # TODO - glDisable(GL_CULL_FACE) - glEnable(GL_MULTISAMPLE) - - # TODO: check hardware for OpenGL 3.x+ support - - self.renderer = OWOpenGLRenderer() - - if self._use_opengl_3: - self._feedback_generated = False - - self.generating_program = QtOpenGL.QGLShaderProgram() - self.generating_program.addShaderFromSourceFile(QtOpenGL.QGLShader.Geometry, - os.path.join(os.path.dirname(__file__), 'generator.gs')) - self.generating_program.addShaderFromSourceFile(QtOpenGL.QGLShader.Vertex, - os.path.join(os.path.dirname(__file__), 'generator.vs')) - varyings = (c_char_p * 5)() - varyings[:] = ['out_position', 'out_offset', 'out_color', 'out_normal', 'out_index'] - glTransformFeedbackVaryings(self.generating_program.programId(), 5, - ctypes.cast(varyings, POINTER(POINTER(c_char))), GL_INTERLEAVED_ATTRIBS) - - self.generating_program.bindAttributeLocation('index', 0) - - if not self.generating_program.link(): - print('Failed to link generating shader! Attribute changes may be slow.') - else: - print('Generating shader linked.') - - # Upload all symbol geometry into a TBO (texture buffer object), so that generating - # geometry shader will have access to it. (TBO is easier to use than a texture in this use case). - geometry_data = [] - symbols_indices = [] - symbols_sizes = [] - for symbol in range(len(Symbol)): - triangles = get_symbol_geometry(symbol, GeometryType.SOLID_3D) - symbols_indices.append(len(geometry_data) / 3) - symbols_sizes.append(len(triangles)) - for tri in triangles: - geometry_data.extend(chain(*tri)) - - for symbol in range(len(Symbol)): - triangles = get_symbol_geometry(symbol, GeometryType.SOLID_2D) - symbols_indices.append(len(geometry_data) / 3) - symbols_sizes.append(len(triangles)) - for tri in triangles: - geometry_data.extend(chain(*tri)) - - self.symbols_indices = symbols_indices - self.symbols_sizes = symbols_sizes - - tbo = glGenBuffers(1) - glBindBuffer(GL_TEXTURE_BUFFER, tbo) - glBufferData(GL_TEXTURE_BUFFER, len(geometry_data)*4, numpy.array(geometry_data, 'f'), GL_STATIC_DRAW) - glBindBuffer(GL_TEXTURE_BUFFER, 0) - self.symbol_buffer = glGenTextures(1) - glBindTexture(GL_TEXTURE_BUFFER, self.symbol_buffer) - glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32F, tbo) # 3 floating-point components - glBindTexture(GL_TEXTURE_BUFFER, 0) - - # Generate dummy vertex buffer (points which will be fed to the geometry shader). - self.dummy_vao = GLuint(0) - glGenVertexArrays(1, self.dummy_vao) - glBindVertexArray(self.dummy_vao) - vertex_buffer_id = glGenBuffers(1) - glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id) - glBufferData(GL_ARRAY_BUFFER, numpy.arange(50*1000, dtype=numpy.float32), GL_STATIC_DRAW) - glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 4, c_void_p(0)) - glEnableVertexAttribArray(0) - glBindVertexArray(0) - glBindBuffer(GL_ARRAY_BUFFER, 0) - - # Specify an output VBO (and VAO) - self.feedback_vao = feedback_vao = GLuint(0) - glGenVertexArrays(1, feedback_vao) - glBindVertexArray(feedback_vao) - self.feedback_bid = feedback_bid = glGenBuffers(1) - glBindBuffer(GL_ARRAY_BUFFER, feedback_bid) - vertex_size = (3+3+3+3+1)*4 - glBufferData(GL_ARRAY_BUFFER, 20*1000*144*vertex_size, c_void_p(0), GL_STATIC_DRAW) - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vertex_size, c_void_p(0)) - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertex_size, c_void_p(3*4)) - glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, vertex_size, c_void_p(6*4)) - glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, vertex_size, c_void_p(9*4)) - glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, vertex_size, c_void_p(12*4)) - glEnableVertexAttribArray(0) - glEnableVertexAttribArray(1) - glEnableVertexAttribArray(2) - glEnableVertexAttribArray(3) - glEnableVertexAttribArray(4) - glBindVertexArray(0) - glBindBuffer(GL_ARRAY_BUFFER, 0) - else: - # Load symbol geometry and send it to the C++ parent. - geometry_data = [] - for symbol in range(len(Symbol)): - triangles = get_symbol_geometry(symbol, GeometryType.SOLID_2D) - triangles = [QVector3D(*v) for triangle in triangles for v in triangle] - orangeqt.Plot3D.set_symbol_geometry(self, symbol, 0, triangles) - - triangles = get_symbol_geometry(symbol, GeometryType.SOLID_3D) - triangles = [QVector3D(*v) for triangle in triangles for v in triangle] - orangeqt.Plot3D.set_symbol_geometry(self, symbol, 1, triangles) - - edges = get_symbol_geometry(symbol, GeometryType.EDGE_2D) - edges = [QVector3D(*v) for edge in edges for v in edge] - orangeqt.Plot3D.set_symbol_geometry(self, symbol, 2, edges) - self._legend.set_symbol_geometry(symbol, edges) - - edges = get_symbol_geometry(symbol, GeometryType.EDGE_3D) - edges = [QVector3D(*v) for edge in edges for v in edge] - orangeqt.Plot3D.set_symbol_geometry(self, symbol, 3, edges) - - self.symbol_program = QtOpenGL.QGLShaderProgram() - self.symbol_program.addShaderFromSourceFile(QtOpenGL.QGLShader.Vertex, - os.path.join(os.path.dirname(__file__), 'symbol.vs')) - self.symbol_program.addShaderFromSourceFile(QtOpenGL.QGLShader.Fragment, - os.path.join(os.path.dirname(__file__), 'symbol.fs')) - - self.symbol_program.bindAttributeLocation('position', 0) - self.symbol_program.bindAttributeLocation('offset', 1) - self.symbol_program.bindAttributeLocation('color', 2) - self.symbol_program.bindAttributeLocation('normal', 3) - - if not self.symbol_program.link(): - print('Failed to link symbol shader!') - else: - print('Symbol shader linked.') - - self.symbol_program_use_2d_symbols = self.symbol_program.uniformLocation('use_2d_symbols') - self.symbol_program_symbol_scale = self.symbol_program.uniformLocation('symbol_scale') - self.symbol_program_alpha_value = self.symbol_program.uniformLocation('alpha_value') - self.symbol_program_scale = self.symbol_program.uniformLocation('scale') - self.symbol_program_translation = self.symbol_program.uniformLocation('translation') - self.symbol_program_hide_outside = self.symbol_program.uniformLocation('hide_outside') - self.symbol_program_fade_outside = self.symbol_program.uniformLocation('fade_outside') - self.symbol_program_force_color = self.symbol_program.uniformLocation('force_color') - self.symbol_program_encode_color = self.symbol_program.uniformLocation('encode_color') - - format = QtOpenGL.QGLFramebufferObjectFormat() - format.setAttachment(QtOpenGL.QGLFramebufferObject.Depth) - self._tooltip_fbo = QtOpenGL.QGLFramebufferObject(256, 256, format) - if self._tooltip_fbo.isValid(): - print('Tooltip FBO created.') - else: - print('Failed to create tooltip FBO! Tooltips disabled.') - self._use_fbos = False - - self._init_done = True - - def resizeGL(self, width, height): - pass - - def update_camera(self): - self.pitch = clamp(self.pitch, -3., -0.1) - self.camera = QVector3D( - sin(self.pitch)*cos(self.yaw), - cos(self.pitch), - sin(self.pitch)*sin(self.yaw)) - - def get_mvp(self): - ''' - Return current model, view and projection transforms. - ''' - projection = QMatrix4x4() - width, height = self.width(), self.height() - aspect = float(width) / height if height != 0 else 1 - projection.perspective(self.camera_fov, aspect, self.perspective_near, self.perspective_far) - - view = QMatrix4x4() - view.lookAt( - self.camera * self.camera_distance, - QVector3D(0,-0.1, 0), - QVector3D(0, 1, 0)) - - model = QMatrix4x4() - return model, view, projection - - def set_2D_mode(self): - ''' - Sets ortho projection and identity modelview transform. A convenience method which - can be called before doing 2D drawing. - ''' - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - glOrtho(0, self.width(), self.height(), 0, -1, 1) - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - - self.projection = QMatrix4x4() - self.projection.ortho(0, self.width(), self.height(), 0, -1, 1) - self.model = QMatrix4x4() - self.view = QMatrix4x4() - - def paintEvent(self, event): - glViewport(0, 0, self.width(), self.height()) - self.qglClearColor(self._theme.background_color) - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - - model, view, projection = self.get_mvp() - self.model = model - self.view = view - self.projection = projection - - if self.before_draw_callback: - self.before_draw_callback() - - plot_scale = lower_bound(1e-5, self.plot_scale+self.additional_scale) - - self.symbol_program.bind() - self.symbol_program.setUniformValue('modelview', self.view * self.model) - self.symbol_program.setUniformValue('projection', self.projection) - self.symbol_program.setUniformValue(self.symbol_program_use_2d_symbols, self.use_2d_symbols) - self.symbol_program.setUniformValue(self.symbol_program_fade_outside, self.fade_outside) - self.symbol_program.setUniformValue(self.symbol_program_hide_outside, self.hide_outside) - self.symbol_program.setUniformValue(self.symbol_program_encode_color, False) - self.symbol_program.setUniformValue(self.symbol_program_symbol_scale, self.symbol_scale, self.symbol_scale) - self.symbol_program.setUniformValue(self.symbol_program_alpha_value, self.alpha_value / 255., self.alpha_value / 255.) - self.symbol_program.setUniformValue(self.symbol_program_scale, plot_scale) - self.symbol_program.setUniformValue(self.symbol_program_translation, self.plot_translation) - self.symbol_program.setUniformValue(self.symbol_program_force_color, 0., 0., 0., 0.) - - if self._use_opengl_3 and self._feedback_generated: - glEnable(GL_DEPTH_TEST) - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glBindVertexArray(self.feedback_vao) - glDrawArrays(GL_TRIANGLES, 0, self.num_primitives_generated*3) - glBindVertexArray(0) - elif not self._use_opengl_3: - glDisable(GL_CULL_FACE) - glEnable(GL_DEPTH_TEST) - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - orangeqt.Plot3D.draw_data(self, self.symbol_program.programId(), self.alpha_value / 255.) - - self.symbol_program.release() - - self._draw_labels() - - if self.after_draw_callback: - self.after_draw_callback() - - if self._tooltip_fbo_dirty: - self._tooltip_fbo.bind() - glClearColor(1, 1, 1, 1) - glClearDepth(1) - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glDisable(GL_BLEND) - glEnable(GL_DEPTH_TEST) - - glViewport(-self._mouse_position.x()+128, -(self.height()-self._mouse_position.y())+128, self.width(), self.height()) - self._tooltip_win_center = [self._mouse_position.x(), self._mouse_position.y()] - - self.symbol_program.bind() - self.symbol_program.setUniformValue(self.symbol_program_encode_color, True) - - if self._use_opengl_3 and self._feedback_generated: - glBindVertexArray(self.feedback_vao) - glDrawArrays(GL_TRIANGLES, 0, self.num_primitives_generated*3) - glBindVertexArray(0) - elif not self._use_opengl_3: - orangeqt.Plot3D.draw_data_solid(self) - self.symbol_program.release() - self._tooltip_fbo.release() - self._tooltip_fbo_dirty = False - glViewport(0, 0, self.width(), self.height()) - - self._draw_helpers() - - if self.show_legend: - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - glOrtho(0, self.width(), self.height(), 0, -1, 1) - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - glDisable(GL_BLEND) - - self._legend._paint(self) - - self.swapBuffers() - - def _draw_labels(self): - if self.label_index < 0: - return - - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - glMultMatrixd(numpy.array(self.projection.data(), dtype=float)) - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - glMultMatrixd(numpy.array(self.view.data(), dtype=float)) - glMultMatrixd(numpy.array(self.model.data(), dtype=float)) - - self.qglColor(self._theme.labels_color) - for example in self.data.transpose(): - x = example[self.x_index] - y = example[self.y_index] - z = example[self.z_index] - label = example[self.label_index] - x, y, z = self.map_to_plot(QVector3D(x, y, z)) - # TODO - #if isinstance(label, str): - #self.renderText(x,y,z, label, font=self._theme.labels_font) - #else: - self.renderText(x,y,z, ('%.3f' % label).rstrip('0').rstrip('.'), - font=self._theme.labels_font) - - def _draw_helpers(self): - glEnable(GL_BLEND) - glDisable(GL_DEPTH_TEST) - - projection = QMatrix4x4() - projection.ortho(0, self.width(), self.height(), 0, -1, 1) - model = view = QMatrix4x4() - - self.renderer.set_transform(model, view, projection) - - if self._state == PlotState.SCALING: - x, y = self._mouse_position.x(), self._mouse_position.y() - self.renderer.draw_triangle(QVector3D(x-5, y-30, 0), - QVector3D(x+5, y-30, 0), - QVector3D(x, y-40, 0), - color=self._theme.helpers_color) - self.renderer.draw_line(QVector3D(x, y, 0), - QVector3D(x, y-30, 0), - color=self._theme.helpers_color) - self.renderer.draw_triangle(QVector3D(x-5, y-10, 0), - QVector3D(x+5, y-10, 0), - QVector3D(x, y, 0), - color=self._theme.helpers_color) - - self.renderer.draw_triangle(QVector3D(x+10, y, 0), - QVector3D(x+20, y-5, 0), - QVector3D(x+20, y+5, 0), - color=self._theme.helpers_color) - self.renderer.draw_line(QVector3D(x+10, y, 0), - QVector3D(x+40, y, 0), - color=self._theme.helpers_color) - self.renderer.draw_triangle(QVector3D(x+50, y, 0), - QVector3D(x+40, y-5, 0), - QVector3D(x+40, y+5, 0), - color=self._theme.helpers_color) - - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - glOrtho(0, self.width(), self.height(), 0, -1, 1) - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - - self.renderText(x, y-50, 'Scale y axis', font=self._theme.labels_font) - self.renderText(x+60, y+3, 'Scale x and z axes', font=self._theme.labels_font) - elif self._state == PlotState.SELECTING: - internal_color = QColor(168, 202, 236, 50) - self.renderer.draw_rectangle(QVector3D(self._selection.left(), self._selection.top(), 0), - QVector3D(self._selection.right(), self._selection.top(), 0), - QVector3D(self._selection.right(), self._selection.bottom(), 0), - QVector3D(self._selection.left(), self._selection.bottom(), 0), - color=internal_color) - - border_color = QColor(51, 153, 255, 192) - self.renderer.draw_line(QVector3D(self._selection.left(), self._selection.top(), 0), - QVector3D(self._selection.right(), self._selection.top(), 0), - border_color, border_color) - self.renderer.draw_line(QVector3D(self._selection.right(), self._selection.top(), 0), - QVector3D(self._selection.right(), self._selection.bottom(), 0), - border_color, border_color) - self.renderer.draw_line(QVector3D(self._selection.right(), self._selection.bottom(), 0), - QVector3D(self._selection.left(), self._selection.bottom(), 0), - border_color, border_color) - self.renderer.draw_line(QVector3D(self._selection.left(), self._selection.bottom(), 0), - QVector3D(self._selection.left(), self._selection.top(), 0), - border_color, border_color) - - def set_features(self, - x_index, y_index, z_index, - color_index, symbol_index, size_index, label_index, - colors, num_symbols_used, - x_discrete, y_discrete, z_discrete): - ''' - Explains to the plot how to interpret the data set by :meth:`set_plot_data`. Its arguments - are indices (must be less than the size of an example) into the dataset (each one - specifies a column). Additionally, it accepts a list of colors (when color is a discrete - attribute), a value specifying how many different symbols are needed to display the data and - information whether or not positional data is discrete. - - .. note:: This function does not add items to the legend automatically. - You will have to add them yourself with :meth:`.OWLegend.add_item`. - - :param x_index: Index (column) of the x coordinate. - :type int - - :param y_index: Index (column) of the y coordinate. - :type int - - :param z_index: Index (column) of the z coordinate. - :type int - - :param color_index: Index (column) of the color attribute. - :type int - - :param symbol_index: Index (column) of the symbol attribute. - :type int - - :param size_index: Index (column) of the size attribute. - :type int - - :param label_index: Index (column) of the label attribute. - :type int - - :param colors: List of colors used for symbols. When color is a discrete attribute, - this list should be empty. You should make sure the number of colors in this list - equals the number of unique values in the color attribute. - :type list of QColor - - :param num_symbols_used: Specifies the number of unique values in the symbol attribute. - Must be -1 if all points are to use the same symbol. - :type int - - :param x_discrete: Specifies whether or not x coordinate is discrete. - :type bool - - :param y_discrete: Specifies whether or not y coordinate is discrete. - :type bool - - :param z_discrete: Specifies whether or not z coordinate is discrete. - :type bool - ''' - if self.data == None: - print('Error: set_plot_data has not been called yet!') - return - start = time.time() - self.makeCurrent() - self.x_index = x_index - self.y_index = y_index - self.z_index = z_index - self.color_index = color_index - self.symbol_index = symbol_index - self.size_index = size_index - self.colors = colors - self.num_symbols_used = num_symbols_used - self.x_discrete = x_discrete - self.y_discrete = y_discrete - self.z_discrete = z_discrete - self.label_index = label_index - - if self.num_examples > 10*1000: - self.use_2d_symbols = True - - if self._use_opengl_3: - # Re-run generating program (geometry shader), store - # results through transform feedback into a VBO on the GPU. - self.generating_program.bind() - self.generating_program.setUniformValue('x_index', x_index) - self.generating_program.setUniformValue('y_index', y_index) - self.generating_program.setUniformValue('z_index', z_index) - self.generating_program.setUniformValue('x_discrete', x_discrete) - self.generating_program.setUniformValue('y_discrete', y_discrete) - self.generating_program.setUniformValue('z_discrete', z_discrete) - self.generating_program.setUniformValue('color_index', color_index) - self.generating_program.setUniformValue('symbol_index', symbol_index) - self.generating_program.setUniformValue('size_index', size_index) - self.generating_program.setUniformValue('use_2d_symbols', self.use_2d_symbols) - self.generating_program.setUniformValue('example_size', self.example_size) - self.generating_program.setUniformValue('num_colors', len(colors)) - self.generating_program.setUniformValue('num_symbols_used', num_symbols_used) - # TODO: colors is list of QColor - glUniform3fv(glGetUniformLocation(self.generating_program.programId(), 'colors'), - len(colors), numpy.array(colors, 'f').ravel()) - glUniform1iv(glGetUniformLocation(self.generating_program.programId(), 'symbols_sizes'), - len(Symbol)*2, numpy.array(self.symbols_sizes, dtype='i')) - glUniform1iv(glGetUniformLocation(self.generating_program.programId(), 'symbols_indices'), - len(Symbol)*2, numpy.array(self.symbols_indices, dtype='i')) - - glActiveTexture(GL_TEXTURE0) - glBindTexture(GL_TEXTURE_BUFFER, self.symbol_buffer) - self.generating_program.setUniformValue('symbol_buffer', 0) - glActiveTexture(GL_TEXTURE1) - glBindTexture(GL_TEXTURE_BUFFER, self.data_buffer) - self.generating_program.setUniformValue('data_buffer', 1) - - qid = glGenQueries(1) - glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, qid) - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, self.feedback_bid) - glEnable(GL_RASTERIZER_DISCARD) - glBeginTransformFeedback(GL_TRIANGLES) - - glBindVertexArray(self.dummy_vao) - glDrawArrays(GL_POINTS, 0, self.num_examples) - - glEndTransformFeedback() - glDisable(GL_RASTERIZER_DISCARD) - - glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) - self.num_primitives_generated = glGetQueryObjectuiv(qid, GL_QUERY_RESULT) - glBindVertexArray(0) - self._feedback_generated = True - print(('Num generated primitives: ' + str(self.num_primitives_generated))) - - self.generating_program.release() - glActiveTexture(GL_TEXTURE0) - print(('Generation took ' + str(time.time()-start) + ' seconds')) - else: - orangeqt.Plot3D.update_data(self, x_index, y_index, z_index, - color_index, symbol_index, size_index, label_index, - colors, num_symbols_used, - x_discrete, y_discrete, z_discrete, self.use_2d_symbols) - print(('Data processing took ' + str(time.time() - start) + ' seconds')) - - self.update() - - def set_plot_data(self, data, subset_data=None): - ''' - Sets the data to be drawn. Data is expected to be scaled already (see ``OWScatterPlot3D`` for example). - - :param data: Data - :type data: numpy array - ''' - self.makeCurrent() - self.data = data - self.data_array = numpy.array(data.transpose().flatten(), dtype=numpy.float32) - self.example_size = len(data) - self.num_examples = len(data[0]) - - if self._use_opengl_3: - tbo = glGenBuffers(1) - glBindBuffer(GL_TEXTURE_BUFFER, tbo) - glBufferData(GL_TEXTURE_BUFFER, len(self.data_array)*4, self.data_array, GL_STATIC_DRAW) - glBindBuffer(GL_TEXTURE_BUFFER, 0) - - self.data_buffer = glGenTextures(1) - glBindTexture(GL_TEXTURE_BUFFER, self.data_buffer) - GL_R32F = 0x822E - glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, tbo) - glBindTexture(GL_TEXTURE_BUFFER, 0) - else: - orangeqt.Plot3D.set_data(self, self.data_array, - self.num_examples, - self.example_size) - - def set_valid_data(self, valid_data): - ''' - Specifies which examples are invalid and should not be displayed. - - :param valid_data: List of booleans: true for valid indices, false otherwise - :type valid_data: list of bool - ''' - self.valid_data = numpy.array(valid_data, dtype=bool) - orangeqt.Plot3D.set_valid_data(self, self.valid_data) - - def set_new_zoom(self, min, max): - ''' - Specifies new zoom in data coordinates. Zoom is provided in form of plot translation - and scale, not camera transformation. This might not be what you want (``OWLinProj3D`` - disables this behavior for example). Plot3D remembers translation and scale. - ``zoom_out`` can be use to restore the previous zoom level. - - :param min: Lower left corner of the new zoom volume. - :type QVector3D - - :param max: Upper right corner of the new zoom volume. - :type QVector3D - ''' - self._zoom_stack.append((self.plot_scale, self.plot_translation)) - center = (max + min) / 2. - new_translation = -center - self._zoomed_size = max-min + QVector3D(1e-5, 1e-5, 1e-5) - new_scale = vec_div(QVector3D(1, 1, 1), self._zoomed_size) - self._animate_new_scale_translation(new_scale, new_translation) - - def zoom_out(self): - ''' - Restores previous zoom level. - ''' - if len(self._zoom_stack) < 1: - new_translation = QVector3D(-0.5, -0.5, -0.5) - new_scale = QVector3D(1, 1, 1) - else: - new_scale, new_translation = self._zoom_stack.pop() - self._animate_new_scale_translation(new_scale, new_translation) - self._zoomed_size = vec_div(QVector3D(1, 1, 1), new_scale) - - def _animate_new_scale_translation(self, new_scale, new_translation, num_steps=10): - translation_step = (new_translation - self.plot_translation) / float(num_steps) - scale_step = (new_scale - self.plot_scale) / float(num_steps) - # Animate zooming: translate first for a number of steps, - # then scale. Make sure it doesn't take too long. - start = time.time() - for i in range(num_steps): - if time.time() - start > 1.: - self.plot_translation = new_translation - break - self.plot_translation = self.plot_translation + translation_step - self.repaint() - for i in range(num_steps): - if time.time() - start > 1.: - self.plot_scale = new_scale - break - self.plot_scale = self.plot_scale + scale_step - self.repaint() - - def save_to_file(self, extraButtons=[]): - print('Save to file called!') - sizeDlg = OWChooseImageSizeDlg(self, extraButtons, parent=self) - sizeDlg.exec_() - - def save_to_file_direct(self, fileName, size=None): - sizeDlg = OWChooseImageSizeDlg(self) - sizeDlg.saveImage(fileName, size) - - def map_to_plot(self, point): - ''' - Maps ``point`` to plot coordinates (applies current translation and scale). - ``point`` is assumed to be in data coordinates. - - :param point: Location in space - :type QVector3D - ''' - plot_scale = lower_bound(1e-5, self.plot_scale+self.additional_scale) - point = (point + self.plot_translation) * plot_scale - return point - - def map_to_data(self, point): - ''' - Maps ``point`` to data coordinates (applies inverse of the current translation and scale). - ``point`` is assumed to be in plot coordinates. - - :param point: Location in space - :type QVector3D - ''' - plot_scale = lower_bound(1e-5, self.plot_scale+self.additional_scale) - point = vec_div(point, plot_scale) - self.plot_translation - return point - - def get_min_max_selected(self, area): - ''' - Returns min/max x/y/z coordinate values of currently selected points. - - :param area: Rectangular area. - :type QRect - ''' - viewport = [0, 0, self.width(), self.height()] - area = [min(area.left(), area.right()), min(area.top(), area.bottom()), abs(area.width()), abs(area.height())] - min_max = orangeqt.Plot3D.get_min_max_selected(self, area, self.projection * self.view * self.model, - viewport, self.plot_scale, self.plot_translation) - return min_max - - def get_selected_indices(self): - ''' - Returns indices of currently selected points (examples). - ''' - return orangeqt.Plot3D.get_selected_indices(self) - - def select_points(self, area, behavior): - ''' - Selects all points inside volume specified by rectangular area and current camera transform - using selection ``behavior``. - - :param area: Rectangular area. - :type QRect - - :param behavior: :data:`AddSelection`, :data:`RemoveSelection`, :data:`ToggleSelection` or :data:`ReplaceSelection` - :type behavior: int - ''' - viewport = [0, 0, self.width(), self.height()] - area = [min(area.left(), area.right()), min(area.top(), area.bottom()), abs(area.width()), abs(area.height())] - orangeqt.Plot3D.select_points(self, area, self.projection * self.view * self.model, - viewport, self.plot_scale, self.plot_translation, - behavior) - orangeqt.Plot3D.update_data(self, self.x_index, self.y_index, self.z_index, - self.color_index, self.symbol_index, self.size_index, self.label_index, - self.colors, self.num_symbols_used, - self.x_discrete, self.y_discrete, self.z_discrete, self.use_2d_symbols) - - def unselect_all_points(self): - ''' - Unselects everything. - ''' - orangeqt.Plot3D.unselect_all_points(self) - orangeqt.Plot3D.update_data(self, self.x_index, self.y_index, self.z_index, - self.color_index, self.symbol_index, self.size_index, self.label_index, - self.colors, self.num_symbols_used, - self.x_discrete, self.y_discrete, self.z_discrete, self.use_2d_symbols) - self.update() - - def set_selection_behavior(self, behavior): - ''' - Sets selection behavior. - - :param behavior: :data:`AddSelection`, :data:`RemoveSelection`, :data:`ToggleSelection` or :data:`ReplaceSelection` - :type behavior: int - ''' - self.selection_behavior = behavior - - def send_selection(self): - if self.auto_send_selection_callback: - self.auto_send_selection_callback() - - def mousePressEvent(self, event): - pos = self._mouse_position = event.pos() - buttons = event.buttons() - - self._selection = None - - if buttons & Qt.LeftButton: - legend_pos = self._legend.pos() - lx, ly = legend_pos.x(), legend_pos.y() - if self.show_legend and self._legend.boundingRect().adjusted(lx, ly, lx, ly).contains(pos.x(), pos.y()): - event.scenePos = lambda: QPointF(pos) - self._legend.mousePressEvent(event) - self.setCursor(Qt.ClosedHandCursor) - self._state = PlotState.DRAGGING_LEGEND - elif self.state == PANNING: - self._state = PlotState.PANNING - elif self.state == ROTATING or QApplication.keyboardModifiers() & Qt.ShiftModifier: - self._state = PlotState.ROTATING - else: - self._state = PlotState.SELECTING - self._selection = QRect(pos.x(), pos.y(), 0, 0) - elif buttons & Qt.RightButton: - if QApplication.keyboardModifiers() & Qt.ShiftModifier: - self._state = PlotState.SCALING - self.scaling_init_pos = self._mouse_position - self.additional_scale = QVector3D(0, 0, 0) - else: - self.zoom_out() - self.update() - elif buttons & Qt.MiddleButton: - if QApplication.keyboardModifiers() & Qt.ShiftModifier: - self._state = PlotState.PANNING - else: - self._state = PlotState.ROTATING - - def _check_mouseover(self, pos): - if self.mouseover_callback != None and self._state == PlotState.IDLE: - if abs(pos.x() - self._tooltip_win_center[0]) > 100 or\ - abs(pos.y() - self._tooltip_win_center[1]) > 100: - self._tooltip_fbo_dirty = True - self.update() - # Use pixel-color-picking to read example index under mouse cursor (also called ID rendering). - self._tooltip_fbo.bind() - value = glReadPixels(pos.x() - self._tooltip_win_center[0] + 128, - self._tooltip_win_center[1] - pos.y() + 128, - 1, 1, - GL_RGBA, - GL_UNSIGNED_BYTE) - self._tooltip_fbo.release() - value = struct.unpack('I', value)[0] - # Check if value is less than 4294967295 ( - # the highest 32-bit unsigned integer) which - # corresponds to white background in color-picking buffer. - if value < 4294967295: - self.mouseover_callback(value) - - def mouseMoveEvent(self, event): - pos = event.pos() - - self._check_mouseover(pos) - - dx = pos.x() - self._mouse_position.x() - dy = pos.y() - self._mouse_position.y() - - if self.invert_mouse_x: - dx = -dx - - if self._state == PlotState.SELECTING: - self._selection.setBottomRight(pos) - elif self._state == PlotState.DRAGGING_LEGEND: - event.scenePos = lambda: QPointF(pos) - self._legend.mouseMoveEvent(event) - elif self._state == PlotState.ROTATING: - self.yaw += (self.mouse_sensitivity / 5.) * dx / (self.rotation_factor*self.width()) - self.pitch += (self.mouse_sensitivity / 5.) * dy / (self.rotation_factor*self.height()) - self.update_camera() - elif self._state == PlotState.PANNING: - right_vec = QVector3D.crossProduct(self.camera, QVector3D(0, 1, 0)).normalized() - up_vec = QVector3D.crossProduct(right_vec, self.camera).normalized() - right_vec.setX(right_vec.x() * dx / (self.width() * self.plot_scale.x() * self.panning_factor)) - right_vec.setZ(right_vec.z() * dx / (self.width() * self.plot_scale.z() * self.panning_factor)) - right_vec.setX(right_vec.x() * (self.mouse_sensitivity / 5.)) - right_vec.setZ(right_vec.z() * (self.mouse_sensitivity / 5.)) - up_scale = self.height() * self.plot_scale.y() * self.panning_factor - self.plot_translation -= right_vec + up_vec * (dy / up_scale) * (self.mouse_sensitivity / 5.) - elif self._state == PlotState.SCALING: - dx = pos.x() - self.scaling_init_pos.x() - dy = pos.y() - self.scaling_init_pos.y() - dx /= self.scale_factor * self.width() - dy /= self.scale_factor * self.height() - dy /= float(self._zoomed_size.y()) - dx *= self.mouse_sensitivity / 5. - dy *= self.mouse_sensitivity / 5. - right_vec = QVector3D.crossProduct(self.camera, QVector3D(0, 1, 0)).normalized() - self.additional_scale = QVector3D(-dx * abs(right_vec.x()) / float(self._zoomed_size.x()), - dy, - -dx * abs(right_vec.z()) / float(self._zoomed_size.z())) - elif self._state == PlotState.IDLE: - legend_pos = self._legend.pos() - lx, ly = legend_pos.x(), legend_pos.y() - if self.show_legend and self._legend.boundingRect().adjusted(lx, ly, lx, ly).contains(pos.x(), pos.y()): - self.setCursor(Qt.PointingHandCursor) - else: - self.unsetCursor() - - self._mouse_position = pos - self.update() - - def mouseReleaseEvent(self, event): - if self._state == PlotState.DRAGGING_LEGEND: - self._legend.mouseReleaseEvent(event) - if self._state == PlotState.SCALING: - self.plot_scale = lower_bound(1e-5, self.plot_scale+self.additional_scale) - self.additional_scale = QVector3D(0, 0, 0) - self._state = PlotState.IDLE - elif self._state == PlotState.SELECTING: - self._selection.setBottomRight(event.pos()) - if self.state == ZOOMING: # self.state is actually set by OWPlotGUI (different from self._state!) - min_max = self.get_min_max_selected(self._selection) - x_min, x_max, y_min, y_max, z_min, z_max = min_max - min, max = QVector3D(x_min, y_min, z_min), QVector3D(x_max, y_max, z_max) - self.set_new_zoom(min, max) - else: - self.select_points(self._selection, self.selection_behavior) - if self.auto_send_selection_callback: - self.auto_send_selection_callback() - - self._tooltip_fbo_dirty = True - self.unsetCursor() - self._state = PlotState.IDLE - self.update() - - def wheelEvent(self, event): - if event.orientation() == Qt.Vertical: - delta = 1 + event.delta() / self.zoom_factor - self.plot_scale *= delta - self._tooltip_fbo_dirty = True - self.update() - - def notify_legend_moved(self, pos): - self._legend.set_floating(True, pos) - self._legend.set_orientation(Qt.Vertical) - - def get_theme(self): - return self._theme - - def set_theme(self, value): - self._theme = value - self.update() - - theme = pyqtProperty(PlotTheme, get_theme, set_theme) - - def color(self, role, group = None): - if group: - return self.palette().color(group, role) - else: - return self.palette().color(role) - - def set_palette(self, p): - ''' - Sets the plot palette to ``p``. - - :param p: The new color palette - :type p: :obj:`.QPalette` - ''' - self.setPalette(p) - self.update() - - def show_tooltip(self, text): - x, y = self._mouse_position.x(), self._mouse_position.y() - QToolTip.showText(self.mapToGlobal(QPoint(x, y)), text, self, QRect(x-3, y-3, 6, 6)) - - def clear(self): - ''' - Clears the plot (legend) but remembers plot transformations (zoom, scale, translation). - ''' - self._legend.clear() - self._tooltip_fbo_dirty = True - self._feedback_generated = False - - def clear_plot_transformations(self): - ''' - Forgets plot transformations (zoom, scale, translation). - ''' - self._zoom_stack = [] - self._zoomed_size = QVector3D(1, 1, 1) - self.plot_translation = QVector3D(-0.5, -0.5, -0.5) - self.plot_scale = QVector3D(1, 1, 1) - self.additional_scale = QVector3D(0, 0, 0) - - contPalette = deprecated_attribute('contPalette', 'continuous_palette') - discPalette = deprecated_attribute('discPalette', 'discrete_palette') - showLegend = deprecated_attribute('showLegend', 'show_legend') - -if __name__ == "__main__": - # TODO - pass diff --git a/Orange/widgets/utils/plot/owpoint.py b/Orange/widgets/utils/plot/owpoint.py deleted file mode 100644 index 3976e44e8f2..00000000000 --- a/Orange/widgets/utils/plot/owpoint.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -##################### -Point (``owpoint``) -##################### - -.. class:: OWPoint - - Represents a point on the plot, usually a part of a curve, or in a legend item. - - The point is identified with its symbol, color, size, label, and state, where the state can be ether unselected (default), - marker, or selected. All these attributes can be changed and retrieved after the point is constructed. - For example, color can be set with :meth:`set_color`, while the current color is returned by :meth:`color`. - There are similarily named function for the other attributes. - - .. method:: __init__(symbol, color, size) - - :param symbol: The point symbol. - :type symbol: int - - :param color: The point color. - :type color: QColor - - :param size: The point size. - :type size: int - - .. method:: set_color(color) - - Sets the point's color to ``color`` - - .. method:: color() - - :returns: the point's color - - .. method:: set_size(size) - - Sets the point's size to ``size`` - - .. method:: size() - - :returns: the point's size - - .. method:: set_symbol(symbol) - - Sets the point's symbol to ``symbol`` - - .. method:: symbol() - - :returns: the point's symbol - - .. method:: set_selected(selected) - - Sets the point's selected state to ``selected`` - - .. method:: is_selected() - - :returns: ``True`` if the point is selected, ``False`` otherwise. - - .. method:: set_marked(marked) - - Sets the point's marked state to ``marked`` - - .. method:: is_marked() - - :returns: ``True`` if the point is marked, ``False`` otherwise. - - .. method:: set_label(label) - - Sets the point's label to ``label``. - The label is displayed under the symbol. - - .. method:: label() - - :returns: The point`s label, set with :meth:`set_label` - :rtype: str -""" - -import orangeqt - -OWPoint = orangeqt.Point diff --git a/Orange/widgets/utils/plot/owtheme.py b/Orange/widgets/utils/plot/owtheme.py deleted file mode 100644 index c5994e45739..00000000000 --- a/Orange/widgets/utils/plot/owtheme.py +++ /dev/null @@ -1,48 +0,0 @@ -from AnyQt.QtGui import QFont, QColor - -class PlotTheme(object): - '''Collection of color and font settings.''' - - def __init__(self): - self.labels_font = QFont('Verdana', 10) - self.helper_font = self.labels_font - self.helpers_color = QColor(0, 0, 0, 255) - self.background_color = QColor(255, 255, 255, 255) - self.axis_title_font = QFont('Verdana', 11, QFont.Bold) - self.axis_font = QFont('Verdana', 10) - self.labels_color = QColor(0, 0, 0, 255) - self.axis_color = QColor(30, 30, 30, 255) - self.axis_values_color = QColor(30, 30, 30, 255) - -class ScatterPlotTheme(PlotTheme): - def __init__(self): - super(ScatterPlotTheme, self).__init__() - self.grid_color = QColor(200, 200, 200, 255) - -class ScatterLightTheme(ScatterPlotTheme): - pass - -class ScatterDarkTheme(ScatterPlotTheme): - def __init__(self): - super(ScatterDarkTheme, self).__init__() - self.grid_color = QColor(80, 80, 80, 255) - self.labels_color = QColor(230, 230, 230, 255) - self.helpers_color = QColor(230, 230, 230, 255) - self.axis_values_color = QColor(180, 180, 180, 255) - self.axis_color = QColor(200, 200, 200, 255) - self.background_color = QColor(0, 0, 0, 255) - -class LinProjTheme(PlotTheme): - def __init__(self): - super(LinProjTheme, self).__init__() - -class LinProjLightTheme(LinProjTheme): - pass - -class LinProjDarkTheme(LinProjTheme): - def __init__(self): - super(LinProjDarkTheme, self).__init__() - self.labels_color = QColor(230, 230, 230, 255) - self.axis_values_color = QColor(170, 170, 170, 255) - self.axis_color = QColor(230, 230, 230, 255) - self.background_color = QColor(0, 0, 0, 255) diff --git a/Orange/widgets/utils/plot/owtools.py b/Orange/widgets/utils/plot/owtools.py deleted file mode 100644 index a7e23a82dbe..00000000000 --- a/Orange/widgets/utils/plot/owtools.py +++ /dev/null @@ -1,522 +0,0 @@ -''' -############################## -Plot tools (``owtools``) -############################## - -.. autofunction:: resize_plot_item_list - -.. autofunction:: move_item - -.. autofunction:: move_item_xy - -.. autoclass:: TooltipManager - :members: - -.. autoclass:: PolygonCurve - :members: - :show-inheritance: - -.. autoclass:: RectangleCurve - :members: - :show-inheritance: - -.. autoclass:: CircleCurve - :members: - :show-inheritance: - -.. autoclass:: UnconnectedLinesCurve - :members: - :show-inheritance: - -.. autoclass:: Marker - :members: - :show-inheritance: - -''' - -from AnyQt.QtWidgets import ( - QGraphicsItem, QGraphicsRectItem, QGraphicsTextItem, QGraphicsPolygonItem, - QGraphicsEllipseItem, QGraphicsPixmapItem, QGraphicsPathItem -) -from AnyQt.QtGui import QPolygonF, QPen, QBrush, QPainterPath, QImage, \ - qRgb, QPixmap -from AnyQt.QtCore import Qt, QRectF, QPointF, QPropertyAnimation - -from Orange.widgets.utils.colorpalette import ColorPaletteDlg -from Orange.widgets.utils import get_variable_values_sorted - -from .owcurve import * -from .owpalette import OWPalette -import orangeqt -import Orange - -#from Orange.preprocess.scaling import get_variable_values_sorted -#from Orange import orangeom -#import ColorPalette - -def resize_plot_item_list(lst, size, item_type, parent): - """ - Efficiently resizes a list of QGraphicsItems (PlotItems, Curves, etc.). - If the list is to be reduced, i.e. if len(lst) > size, then the extra items are first removed from the scene. - If items have to be added to the scene, new items will be of type ``item_type`` and will have ``parent`` - as their parent item. - - The list is resized in place, this function returns nothing. - - :param lst: The list to be resized - :type lst: list of QGraphicsItem - - :param size: The needed size of the list - :type size: int - - :param item_type: The type of items that should be added if the list has to be increased - :type item_type: type - - :param parent: Any new items will have this as their parent item - :type parent: QGraphicsItem - """ - n = len(lst) - if n > size: - for i in lst[size:]: - i.setParentItem(None) - if i.scene(): - i.scene().removeItem(i) - del lst[size:] - elif n < size: - lst.extend(item_type(parent) for i in range(size - n)) - -def move_item(item, pos, animate = True, duration = None): - ''' - Animates ``item`` to move to position ``pos``. - If animations are turned off globally, the item is instead move immediately, without any animation. - - :param item: The item to move - :type item: QGraphicsItem - - :param pos: The final position of the item - :type pos: QPointF - - :param duration: The duration of the animation. If unspecified, Qt's default value of 250 miliseconds is used. - :type duration: int - ''' - if not duration: - duration = 250 - orangeqt.PlotItem.move_item(item, pos, animate, duration) - -def move_item_xy(item, x, y, animate = True, duration = None): - ''' - Same as - move_item(item, QPointF(x, y), duration) - ''' - move_item(item, QPointF(x, y), animate, duration) - -class TooltipManager: - """ - A dynamic tool tip manager. - - :param plot: The plot used for transforming the coordinates - :type plot: :obj:`.OWPlot` - """ - def __init__(self, plot): - self.graph = plot - self.positions=[] - self.texts=[] - - def addToolTip(self, x, y, text, customX = 0, customY = 0): - """ - Adds a tool tip. If a tooltip with the same name already exists, it updates it instead of adding a new one. - - :param x: The x coordinate of the tip, in data coordinates. - :type x: float - - :param y: The y coordinate of the tip, in data coordinates. - :type y: float - - :param text: The text to show in the tip. - :type text: str or int - - :param customX: The maximum horizontal distance in pixels from the point (x,y) at which to show the tooltip. - :type customX: float - - :param customY: The maximum vertical distance in pixels from the point (x,y) at which to show the tooltip. - :type customY: float - - If ``customX`` and ``customY`` are omitted, a default of 6 pixels is used. - """ - self.positions.append((x,y, customX, customY)) - self.texts.append(text) - - #Decides whether to pop up a tool tip and which text to pop up - def maybeTip(self, x, y): - """ - Decides whether to pop up a tool tip and which text to show in it. - - :param x: the x coordinate of the mouse in data coordinates. - :type x: float - - :param y: the y coordinate of the mouse in data coordinates. - :type y: float - - :returns: A tuple consisting of the ``text``, ``x`` and ``y`` arguments to :meth:`addToolTip` of the - closest point. - :rtype: tuple of (int or str), float, float - """ - if len(self.positions) == 0: return ("", -1, -1) - dists = [max(abs(x-position[0])- position[2],0) + max(abs(y-position[1])-position[3], 0) for position in self.positions] - nearestIndex = dists.index(min(dists)) - - intX = abs(self.graph.transform(xBottom, x) - self.graph.transform(xBottom, self.positions[nearestIndex][0])) - intY = abs(self.graph.transform(yLeft, y) - self.graph.transform(yLeft, self.positions[nearestIndex][1])) - if self.positions[nearestIndex][2] == 0 and self.positions[nearestIndex][3] == 0: # if we specified no custom range then assume 6 pixels - if intX + intY < 6: return (self.texts[nearestIndex], self.positions[nearestIndex][0], self.positions[nearestIndex][1]) - else: return ("", None, None) - else: - if abs(self.positions[nearestIndex][0] - x) <= self.positions[nearestIndex][2] and abs(self.positions[nearestIndex][1] - y) <= self.positions[nearestIndex][3]: - return (self.texts[nearestIndex], x, y) - else: - return ("", None, None) - - def removeAll(self): - """ - Removes all tips - """ - self.positions = [] - self.texts = [] - -class PolygonCurve(OWCurve): - """ - A plot item that shows a filled or empty polygon. - - :param pen: The pen used to draw the polygon's outline - :type pen: :obj:`.QPen` - - :param brush: The brush used to paint the polygon's inside - :type brush: :obj:`.QBrush` - - :param xData: The list of x coordinates - :type xData: list of float - - :param yData: The list of y coordinates - :type yData: list of float - - :param tooltip: The tool tip shown when hovering over this curve - :type tooltip: str - """ - def __init__(self, pen = QPen(Qt.black), brush = QBrush(Qt.white), xData = [], yData = [], tooltip = None): - OWCurve.__init__(self, xData, yData, tooltip=tooltip) - self._data_polygon = self.polygon_from_data(xData, yData) - self._polygon_item = QGraphicsPolygonItem(self) - self.set_pen(pen) - self.set_brush(brush) - - def update_properties(self): - self._polygon_item.setPolygon(self.graph_transform().map(self._data_polygon)) - self._polygon_item.setPen(self.pen()) - self._polygon_item.setBrush(self.brush()) - - @staticmethod - def polygon_from_data(xData, yData): - """ - Creates a polygon from a list of x and y coordinates. - - :returns: A polygon with point corresponding to ``xData`` and ``yData``. - :rtype: QPolygonF - """ - if xData and yData: - n = min(len(xData), len(yData)) - p = QPolygonF(n+1) - for i in range(n): - p[i] = QPointF(xData[i], yData[i]) - p[n] = QPointF(xData[0], yData[0]) - return p - else: - return QPolygonF() - - def set_data(self, xData, yData): - self._data_polygon = self.polygon_from_data(xData, yData) - OWCurve.set_data(self, xData, yData) - -class RectangleCurve(OWCurve): - """ - A plot item that shows a rectangle. - - This class accepts the same options as :obj:`.PolygonCurve`. - The rectangle is calculated as the smallest rectangle that contains all points in ``xData`` and ``yData``. - """ - def __init__(self, pen = QPen(Qt.black), brush = QBrush(Qt.white), xData = None, yData = None, tooltip = None): - OWCurve.__init__(self, xData, yData, tooltip=tooltip) - self.set_pen(pen) - self.set_brush(brush) - self._item = QGraphicsRectItem(self) - - def update_properties(self): - self._item.setRect(self.graph_transform().mapRect(self.data_rect())) - self._item.setPen(self.pen()) - self._item.setBrush(self.brush()) - -class UnconnectedLinesCurve(orangeqt.UnconnectedLinesCurve): - """ - A plot item that shows a series of unconnected straight lines. - - :param name: The name of this curve. :seealso: :attr:`.OWCurve.name` - :type name: str - - :param pen: The pen used to draw the lines - :type pen: QPen - - :param xData: The list of x coordinates - :type xData: list of float - - :param yData: The list of y coordinates - :type yData: list of float - - The data should contain an even number of elements. Lines are drawn between the `n`-th and - `(n+1)`-th point for each even `n`. - """ - def __init__(self, name, pen = QPen(Qt.black), xData = None, yData = None): - orangeqt.UnconnectedLinesCurve.__init__(self) - self.set_data(xData, yData) - if pen: - self.set_pen(pen) - self.name = name - -class CircleCurve(OWCurve): - """ - Displays a circle on the plot - - :param pen: The pen used to draw the outline of the circle - :type pen: QPen - - :param brush: The brush used to paint the inside of the circle - :type brush: QBrush - - :param xCenter: The x coordinate of the circle's center - :type xCenter: float - - :param yCenter: The y coordinate of the circle's center - :type yCenter: float - - :param radius: The circle's radius - :type radius: float - """ - def __init__(self, pen = QPen(Qt.black), brush = QBrush(Qt.NoBrush), xCenter = 0.0, yCenter = 0.0, radius = 1.0): - OWCurve.__init__(self) - self._item = QGraphicsEllipseItem(self) - self.center = xCenter, yCenter - self.radius = radius - self._rect = QRectF(xCenter-radius, yCenter-radius, 2*radius, 2*radius) - self.set_pen(pen) - self.set_brush(brush) - - def update_properties(self): - self._item.setRect(self.graph_transform().mapRect(self.data_rect())) - self._item.setPen(self.pen()) - self._item.setBrush(self.brush()) - - def data_rect(self): - x, y = self.center - r = self.radius - return QRectF(x-r, y-r, 2*r, 2*r) - -class Marker(orangeqt.PlotItem): - """ - Displays a text marker on the plot. - - :param text: The text to display. It can be HTML-formatted - :type tex: str - - :param x: The x coordinate of the marker's position - :type x: float - - :param y: The y coordinate of the marker's position - :type y: float - - :param align: The text alignment - :type align: - - :param bold: If ``True``, the text will be show bold. - :type bold: int - - :param color: The text color - :type color: QColor - - :param brushColor: The color of the brush user to paint the background - :type color: QColor - - :param size: Font size - :type size: int - - Markers have the QGraphicsItem.ItemIgnoresTransformations flag set by default, - so text remains the same size when zooming. There is no need to scale the manually. - """ - def __init__(self, text, x, y, align, bold = 0, color = None, brushColor = None, size=None): - orangeqt.PlotItem.__init__(self) - self.setFlag(QGraphicsItem.ItemIgnoresTransformations, True) - self._item = QGraphicsTextItem(text, parent=self) - self._data_point = QPointF(x,y) - f = self._item.font() - f.setBold(bold) - if size: - f.setPointSize(size) - self._item.setFont(f) - self._item.setPos(x, y) - - def update_properties(self): - self._item.setPos(self.graph_transform().map(self._data_point)) - - -class ProbabilitiesItem(orangeqt.PlotItem): - """ - Displays class probabilities in the background - - :param classifier: The classifier for which the probabilities are calculated - :type classifier: orange.P2NN - - :param granularity: The size of individual cells - :type granularity: int - - :param scale: The data scale factor - :type scale: float - - :param spacing: The space between cells - :param spacing: int - - :param rect: The rectangle into which to draw the probabilities. If unspecified, the entire plot is used. - :type rect: QRectF - """ - def __init__(self, classifier, granularity, scale, spacing, rect=None): - orangeqt.PlotItem.__init__(self) - self.classifier = classifier - self.rect = rect - self.granularity = granularity - self.scale = scale - self.spacing = spacing - self.pixmap_item = QGraphicsPixmapItem(self) - self.set_in_background(True) - self.setZValue(ProbabilitiesZValue) - - def update_properties(self): - ## Mostly copied from OWScatterPlotGraph - if not self.plot(): - return - - if not self.rect: - x,y = self.axes() - self.rect = self.plot().data_rect_for_axes(x,y) - s = self.graph_transform().mapRect(self.rect).size().toSize() - if not s.isValid(): - return - rx = s.width() - ry = s.height() - - rx -= rx % self.granularity - ry -= ry % self.granularity - - p = self.graph_transform().map(QPointF(0, 0)) - self.graph_transform().map(self.rect.topLeft()) - p = p.toPoint() - - ox = p.x() - oy = -p.y() - - if self.classifier.classVar.is_continuous: - imagebmp = orangeom.potentialsBitmap(self.classifier, rx, ry, ox, oy, self.granularity, self.scale) - palette = [qRgb(255.*i/255., 255.*i/255., 255-(255.*i/255.)) for i in range(255)] + [qRgb(255, 255, 255)] - else: - imagebmp, nShades = orangeom.potentialsBitmap(self.classifier, rx, ry, ox, oy, self.granularity, self.scale, self.spacing) - palette = [] - sortedClasses = get_variable_values_sorted(self.classifier.domain.classVar) - for cls in self.classifier.classVar.values: - color = self.plot().discPalette.getRGB(sortedClasses.index(cls)) - towhite = [255-c for c in color] - for s in range(nShades): - si = 1-float(s)/nShades - palette.append(qRgb(*tuple([color[i]+towhite[i]*si for i in (0, 1, 2)]))) - palette.extend([qRgb(255, 255, 255) for i in range(256-len(palette))]) - - self.potentialsImage = QImage(imagebmp, rx, ry, QImage.Format_Indexed8) - self.potentialsImage.setColorTable(palette) - self.potentialsImage.setNumColors(256) - self.pixmap_item.setPixmap(QPixmap.fromImage(self.potentialsImage)) - self.pixmap_item.setPos(self.graph_transform().map(self.rect.bottomLeft())) - - def data_rect(self): - return self.rect if self.rect else QRectF() - - -#@deprecated_members({ -# 'enableX' : 'set_x_enabled', -# 'enableY' : 'set_y_enabled', -# 'xEnabled' : 'is_x_enabled', -# 'yEnabled' : 'is_y_enabled', -# 'setPen' : 'set_pen' -# }) -class PlotGrid(orangeqt.PlotItem): - """ - Draws a grid onto the plot - - :param plot: If specified, the grid will be attached to the ``plot``. - :type plot: :obj:`.OWPlot` - """ - def __init__(self, plot = None): - orangeqt.PlotItem.__init__(self) - self._x_enabled = True - self._y_enabled = True - self._path_item = QGraphicsPathItem(self) - self.set_in_background(True) - if plot: - self.attach(plot) - self._path_item.setPen(plot.color(OWPalette.Grid)) - - def set_x_enabled(self, b): - """ - Enables or disabled vertial grid lines - """ - if b < 0: - b = not self._x_enabled - self._x_enabled = b - self.update_properties() - - def is_x_enabled(self): - """ - Returns whether vertical grid lines are enabled - """ - return self._x_enabled - - def set_y_enabled(self, b): - """ - Enables or disabled horizontal grid lines - """ - if b < 0: - b = not self._y_enabled - self._y_enabled = b - self.update_properties() - - def is_y_enabled(self): - """ - Returns whether horizontal grid lines are enabled - """ - return self._y_enabled - - def set_pen(self, pen): - """ - Sets the pen used for drawing the grid lines - """ - self._path_item.setPen(pen) - - def update_properties(self): - p = self.plot() - if p is None: - return - x_id, y_id = self.axes() - rect = p.data_rect_for_axes(x_id, y_id) - path = QPainterPath() - if self._x_enabled and x_id in p.axes: - for pos, label, size, _w in p.axes[x_id].ticks(): - path.moveTo(pos, rect.bottom()) - path.lineTo(pos, rect.top()) - if self._y_enabled and y_id in p.axes: - for pos, label, size, _w in p.axes[y_id].ticks(): - path.moveTo(rect.left(), pos) - path.lineTo(rect.right(), pos) - self._path_item.setPath(self.graph_transform().map(path)) - diff --git a/Orange/widgets/utils/plot/primitives/__init__.py b/Orange/widgets/utils/plot/primitives/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/Orange/widgets/utils/plot/primitives/circle.obj b/Orange/widgets/utils/plot/primitives/circle.obj deleted file mode 100644 index b039d9155aa..00000000000 --- a/Orange/widgets/utils/plot/primitives/circle.obj +++ /dev/null @@ -1,37 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v 0.000000 1.000000 -0.000000 -v 0.382683 0.923880 -0.000000 -v 0.707107 0.707107 -0.000000 -v 0.923880 0.382683 -0.000000 -v 1.000000 0.000000 0.000000 -v 0.923880 -0.382684 0.000000 -v 0.707107 -0.707107 0.000000 -v 0.382683 -0.923880 0.000000 -v -0.382683 0.923880 -0.000000 -v -0.707107 0.707107 -0.000000 -v -0.923880 0.382683 -0.000000 -v -1.000000 0.000000 0.000000 -v -0.923880 -0.382684 0.000000 -v -0.707107 -0.707107 0.000000 -v -0.382684 -0.923880 0.000000 -v 0.000000 -1.000000 0.000000 -v 0.000000 0.000000 0.000000 -usemtl (null) -s off -f 1 2 17 -f 2 3 17 -f 3 4 17 -f 4 5 17 -f 5 6 17 -f 6 7 17 -f 7 8 17 -f 8 16 17 -f 15 16 17 -f 14 15 17 -f 13 14 17 -f 12 13 17 -f 11 12 17 -f 10 11 17 -f 9 10 17 -f 1 9 17 diff --git a/Orange/widgets/utils/plot/primitives/circle_edges.obj b/Orange/widgets/utils/plot/primitives/circle_edges.obj deleted file mode 100644 index 9f0fc5b878e..00000000000 --- a/Orange/widgets/utils/plot/primitives/circle_edges.obj +++ /dev/null @@ -1,35 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -o Mesh_circle -v 0.000000 1.000000 -0.000000 -v 0.382683 0.923880 -0.000000 -v 0.707107 0.707107 -0.000000 -v 0.923880 0.382683 -0.000000 -v 1.000000 0.000000 0.000000 -v 0.923880 -0.382684 0.000000 -v 0.707107 -0.707107 0.000000 -v 0.382683 -0.923880 0.000000 -v 0.000000 -1.000000 0.000000 -v -0.382684 -0.923880 0.000000 -v -0.707107 -0.707107 0.000000 -v -0.923880 -0.382684 0.000000 -v -1.000000 0.000000 0.000000 -v -0.923880 0.382683 -0.000000 -v -0.707107 0.707107 -0.000000 -v -0.382683 0.923880 -0.000000 -f 5 6 -f 1 16 -f 2 3 -f 10 11 -f 8 9 -f 12 13 -f 7 8 -f 9 10 -f 15 16 -f 13 14 -f 4 5 -f 6 7 -f 11 12 -f 3 4 -f 1 2 -f 14 15 diff --git a/Orange/widgets/utils/plot/primitives/cone_hq.obj b/Orange/widgets/utils/plot/primitives/cone_hq.obj deleted file mode 100644 index 279ccd09dcb..00000000000 --- a/Orange/widgets/utils/plot/primitives/cone_hq.obj +++ /dev/null @@ -1,136 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v 0.707107 -1.000000 -0.707107 -v 0.923880 -1.000000 -0.382683 -v 1.000000 -1.000000 0.000000 -v 0.923880 -1.000000 0.382684 -v 0.707107 -1.000000 0.707107 -v 0.382683 -1.000000 0.923880 -v 0.000000 -1.000000 1.000000 -v -0.382683 -1.000000 0.923880 -v -0.707107 -1.000000 0.707107 -v -0.923880 -1.000000 0.382684 -v -1.000000 -1.000000 0.000000 -v -0.923879 -1.000000 -0.382684 -v -0.707107 -1.000000 -0.707107 -v -0.382683 -1.000000 -0.923880 -v 0.000001 -1.000000 -1.000000 -v 0.382684 -1.000000 -0.923879 -v 0.000000 3.000000 -0.000000 -v 0.707107 -1.100000 -0.707107 -v 0.382684 -1.100000 -0.923879 -v 0.000001 -1.100000 -1.000000 -v -0.382683 -1.100000 -0.923880 -v -0.707107 -1.100000 -0.707107 -v -0.923879 -1.100000 -0.382684 -v -1.000000 -1.100000 0.000000 -v -0.923880 -1.100000 0.382684 -v -0.707107 -1.100000 0.707107 -v -0.382683 -1.100000 0.923880 -v 0.000000 -1.100000 1.000000 -v 0.382683 -1.100000 0.923880 -v 0.707107 -1.100000 0.707107 -v 0.923880 -1.100000 0.382684 -v 1.000000 -1.100000 0.000000 -v 0.923880 -1.100000 -0.382683 -v 0.000000 -1.100000 0.000000 -vn 0.934019 0.097690 -0.343547 -vn 0.731437 0.097690 -0.674825 -vn 0.000000 1.000000 0.000000 -vn 0.994385 0.097690 0.040010 -vn 0.903378 0.097690 0.417493 -vn 0.674825 0.097690 0.731437 -vn 0.343547 0.097690 0.934019 -vn -0.040010 0.097690 0.994385 -vn -0.417493 0.097690 0.903378 -vn -0.731437 0.097690 0.674825 -vn -0.934019 0.097690 0.343547 -vn -0.994385 0.097690 -0.040010 -vn -0.903378 0.097690 -0.417493 -vn -0.674825 0.097690 -0.731437 -vn -0.343547 0.097690 -0.934019 -vn 0.040010 0.097690 -0.994385 -vn 0.417493 0.097690 -0.903378 -vn 0.265419 -0.561296 -0.783868 -vn 0.545183 -0.561296 -0.622608 -vn 0.000000 -1.000000 0.000000 -vn -0.054750 -0.561296 -0.825770 -vn -0.366588 -0.561296 -0.741966 -vn -0.622608 -0.561296 -0.545183 -vn -0.783868 -0.561296 -0.265419 -vn -0.825770 -0.561296 0.054750 -vn -0.741966 -0.561296 0.366588 -vn -0.545183 -0.561296 0.622639 -vn -0.265419 -0.561296 0.783868 -vn 0.054750 -0.561296 0.825770 -vn 0.366588 -0.561296 0.741966 -vn 0.622608 -0.561296 0.545183 -vn 0.783868 -0.561296 0.265419 -vn 0.825770 -0.561296 -0.054750 -vn 0.741966 -0.561296 -0.366588 -usemtl (null) -s 1 -f 2//1 1//2 17//3 -f 17//3 3//4 2//1 -f 17//3 4//5 3//4 -f 17//3 5//6 4//5 -f 17//3 6//7 5//6 -f 17//3 7//8 6//7 -f 17//3 8//9 7//8 -f 17//3 9//10 8//9 -f 17//3 10//11 9//10 -f 17//3 11//12 10//11 -f 17//3 12//13 11//12 -f 17//3 13//14 12//13 -f 17//3 14//15 13//14 -f 17//3 15//16 14//15 -f 17//3 16//17 15//16 -f 17//3 1//2 16//17 -f 19//18 18//19 34//20 -f 34//20 20//21 19//18 -f 34//20 21//22 20//21 -f 34//20 22//23 21//22 -f 34//20 23//24 22//23 -f 34//20 24//25 23//24 -f 34//20 25//26 24//25 -f 34//20 26//27 25//26 -f 34//20 27//28 26//27 -f 34//20 28//29 27//28 -f 34//20 29//30 28//29 -f 34//20 30//31 29//30 -f 34//20 31//32 30//31 -f 34//20 32//33 31//32 -f 34//20 33//34 32//33 -f 34//20 18//19 33//34 -f 1//2 2//1 33//34 -f 1//2 33//34 18//19 -f 2//1 3//4 32//33 -f 2//1 32//33 33//34 -f 3//4 4//5 31//32 -f 3//4 31//32 32//33 -f 4//5 5//6 30//31 -f 4//5 30//31 31//32 -f 5//6 6//7 29//30 -f 5//6 29//30 30//31 -f 6//7 7//8 28//29 -f 6//7 28//29 29//30 -f 7//8 8//9 27//28 -f 7//8 27//28 28//29 -f 8//9 9//10 26//27 -f 8//9 26//27 27//28 -f 9//10 10//11 25//26 -f 9//10 25//26 26//27 -f 10//11 11//12 24//25 -f 10//11 24//25 25//26 -f 11//12 12//13 23//24 -f 11//12 23//24 24//25 -f 12//13 13//14 22//23 -f 12//13 22//23 23//24 -f 13//14 14//15 21//22 -f 13//14 21//22 22//23 -f 14//15 15//16 20//21 -f 14//15 20//21 21//22 -f 15//16 16//17 19//18 -f 15//16 19//18 20//21 -f 16//17 1//2 18//19 -f 16//17 18//19 19//18 diff --git a/Orange/widgets/utils/plot/primitives/cross.obj b/Orange/widgets/utils/plot/primitives/cross.obj deleted file mode 100644 index 28f1fc94015..00000000000 --- a/Orange/widgets/utils/plot/primitives/cross.obj +++ /dev/null @@ -1,72 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -0.300000 -1.000000 -0.446899 -v -0.300000 -1.000000 0.446899 -v -0.300000 1.000000 -0.446899 -v -0.300000 1.000000 0.446899 -v 0.297500 -1.000000 -0.446899 -v 0.297500 -1.000000 0.446899 -v 0.297500 1.000000 -0.446899 -v 0.297499 1.000000 0.446900 -v 1.000000 0.300000 -0.446899 -v 1.000000 0.300000 0.446900 -v -1.000000 0.300000 0.446899 -v -1.000000 0.300000 -0.446899 -v -0.300000 0.300000 0.446899 -v -0.300000 0.300000 -0.446899 -v 0.297500 0.300000 0.446899 -v 0.297500 0.300000 -0.446899 -v 1.000000 -0.297500 -0.446899 -v 1.000000 -0.297500 0.446899 -v -1.000000 -0.297500 0.446899 -v -1.000000 -0.297500 -0.446899 -v -0.300000 -0.297500 0.446899 -v -0.300000 -0.297500 -0.446899 -v 0.297500 -0.297500 0.446899 -v 0.297500 -0.297500 -0.446899 -usemtl Material -s off -f 17 18 24 -f 18 23 24 -f 5 24 23 -f 5 23 6 -f 1 2 22 -f 2 21 22 -f 19 20 21 -f 20 22 21 -f 3 14 13 -f 3 13 4 -f 11 13 12 -f 13 14 12 -f 7 8 15 -f 7 15 16 -f 9 16 10 -f 16 15 10 -f 22 14 24 -f 14 16 24 -f 1 22 24 -f 1 24 5 -f 24 16 17 -f 16 9 17 -f 21 2 23 -f 2 6 23 -f 13 21 15 -f 21 23 15 -f 15 23 10 -f 23 18 10 -f 20 12 14 -f 20 14 22 -f 11 19 13 -f 19 21 13 -f 19 11 12 -f 19 12 20 -f 17 9 18 -f 9 10 18 -f 14 3 7 -f 14 7 16 -f 4 13 8 -f 13 15 8 -f 7 3 8 -f 3 4 8 -f 1 5 6 -f 1 6 2 diff --git a/Orange/widgets/utils/plot/primitives/cross_2d.obj b/Orange/widgets/utils/plot/primitives/cross_2d.obj deleted file mode 100644 index 93d2783a245..00000000000 --- a/Orange/widgets/utils/plot/primitives/cross_2d.obj +++ /dev/null @@ -1,26 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -0.000000 -1.000000 0.300000 -v -0.000000 1.000000 0.300000 -v -0.000000 -1.000000 -0.297500 -v 0.000001 1.000000 -0.297499 -v 0.000001 0.300000 -1.000000 -v -0.000000 0.300000 1.000000 -v -0.000000 0.300000 0.300000 -v -0.000000 0.300000 -0.297500 -v -0.000000 -0.297500 -1.000000 -v -0.000000 -0.297500 1.000000 -v -0.000000 -0.297500 0.300000 -v -0.000000 -0.297500 -0.297500 -usemtl Material.001 -s off -f 11 1 12 -f 1 3 12 -f 7 11 8 -f 11 12 8 -f 8 12 5 -f 12 9 5 -f 6 10 7 -f 10 11 7 -f 2 7 4 -f 7 8 4 diff --git a/Orange/widgets/utils/plot/primitives/cross_2d_edges.obj b/Orange/widgets/utils/plot/primitives/cross_2d_edges.obj deleted file mode 100644 index a05d5eec4f9..00000000000 --- a/Orange/widgets/utils/plot/primitives/cross_2d_edges.obj +++ /dev/null @@ -1,27 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -o Mesh_cross_2d -v 0.000000 -0.297500 0.300000 -v 0.000000 -1.000000 0.300000 -v 0.000000 -0.297500 -0.297500 -v 0.000000 -1.000000 -0.297500 -v 0.000000 0.300000 0.300000 -v 0.000000 0.300000 -0.297500 -v 0.000001 0.300000 -1.000000 -v 0.000000 -0.297500 -1.000000 -v 0.000000 0.300000 1.000000 -v 0.000000 -0.297500 1.000000 -v 0.000000 1.000000 0.300000 -v 0.000001 1.000000 -0.297499 -f 6 7 -f 2 4 -f 5 9 -f 11 12 -f 9 10 -f 1 10 -f 7 8 -f 5 11 -f 3 4 -f 3 8 -f 1 2 -f 6 12 diff --git a/Orange/widgets/utils/plot/primitives/cross_edges.obj b/Orange/widgets/utils/plot/primitives/cross_edges.obj deleted file mode 100644 index c32d2f48f95..00000000000 --- a/Orange/widgets/utils/plot/primitives/cross_edges.obj +++ /dev/null @@ -1,26 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -0.000000 -1.000000 0.300000 -v -0.000000 1.000000 0.300000 -v -0.000000 -1.000000 -0.297500 -v 0.000001 1.000000 -0.297499 -v 0.000001 0.300000 -1.000000 -v -0.000000 0.300000 1.000000 -v -0.000000 0.300000 0.300000 -v -0.000000 0.300000 -0.297500 -v -0.000000 -0.297500 -1.000000 -v -0.000000 -0.297500 1.000000 -v -0.000000 -0.297500 0.300000 -v -0.000000 -0.297500 -0.297500 -f 9 12 -f 3 12 -f 1 11 -f 10 11 -f 2 7 -f 6 7 -f 4 8 -f 5 8 -f 1 3 -f 5 9 -f 6 10 -f 2 4 diff --git a/Orange/widgets/utils/plot/primitives/cube.obj b/Orange/widgets/utils/plot/primitives/cube.obj deleted file mode 100644 index 22f804cfc6b..00000000000 --- a/Orange/widgets/utils/plot/primitives/cube.obj +++ /dev/null @@ -1,24 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v 1.000000 -1.000000 -1.000000 -v 1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 -1.000000 -v 1.000000 1.000000 -1.000000 -v 0.999999 1.000000 1.000001 -v -1.000000 1.000000 1.000000 -v -1.000000 1.000000 -1.000000 -usemtl Material -s off -f 5 1 4 -f 5 4 8 -f 3 7 8 -f 3 8 4 -f 2 6 3 -f 6 7 3 -f 1 5 2 -f 5 6 2 -f 5 8 6 -f 8 7 6 -f 1 2 3 -f 1 3 4 diff --git a/Orange/widgets/utils/plot/primitives/cube_edges.obj b/Orange/widgets/utils/plot/primitives/cube_edges.obj deleted file mode 100644 index 2ec9ce0fff8..00000000000 --- a/Orange/widgets/utils/plot/primitives/cube_edges.obj +++ /dev/null @@ -1,23 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -o Mesh_cube -v 1.000000 1.000000 -1.000000 -v 1.000000 -1.000000 -1.000000 -v -1.000000 -1.000000 -1.000000 -v -1.000000 1.000000 -1.000000 -v -1.000000 -1.000000 1.000000 -v -1.000000 1.000000 1.000000 -v 1.000000 -1.000000 1.000000 -v 0.999999 1.000000 1.000001 -f 2 7 -f 2 3 -f 6 8 -f 1 4 -f 5 7 -f 3 5 -f 4 6 -f 5 6 -f 7 8 -f 3 4 -f 1 8 -f 1 2 diff --git a/Orange/widgets/utils/plot/primitives/diamond.obj b/Orange/widgets/utils/plot/primitives/diamond.obj deleted file mode 100644 index 9df2e22a78f..00000000000 --- a/Orange/widgets/utils/plot/primitives/diamond.obj +++ /dev/null @@ -1,10 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v 1.000000 0.000000 0.000000 -v -1.000000 0.000000 0.000000 -v 0.000000 -1.000000 0.000000 -v 0.000000 1.000000 -0.000000 -usemtl Material.001 -s off -f 4 2 3 -f 1 4 3 diff --git a/Orange/widgets/utils/plot/primitives/diamond_edges.obj b/Orange/widgets/utils/plot/primitives/diamond_edges.obj deleted file mode 100644 index 6f497dd79e1..00000000000 --- a/Orange/widgets/utils/plot/primitives/diamond_edges.obj +++ /dev/null @@ -1,11 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -o Mesh_diamond -v 0.000000 1.000000 -0.000000 -v -1.000000 0.000000 0.000000 -v 0.000000 -1.000000 0.000000 -v 1.000000 0.000000 0.000000 -f 1 4 -f 3 4 -f 1 2 -f 2 3 diff --git a/Orange/widgets/utils/plot/primitives/dpyramid.obj b/Orange/widgets/utils/plot/primitives/dpyramid.obj deleted file mode 100644 index 2165e540adb..00000000000 --- a/Orange/widgets/utils/plot/primitives/dpyramid.obj +++ /dev/null @@ -1,15 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -1.000000 1.000000 -1.000000 -v -1.000000 1.000000 1.000000 -v 1.000000 1.000000 1.000000 -v 1.000000 1.000000 -1.000000 -v 0.000000 -1.000000 0.000000 -usemtl Material -s off -f 1 5 2 -f 2 5 3 -f 3 5 4 -f 5 1 4 -f 1 2 3 -f 1 3 4 diff --git a/Orange/widgets/utils/plot/primitives/dpyramid_edges.obj b/Orange/widgets/utils/plot/primitives/dpyramid_edges.obj deleted file mode 100644 index a2b1ecf4e58..00000000000 --- a/Orange/widgets/utils/plot/primitives/dpyramid_edges.obj +++ /dev/null @@ -1,16 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -o Mesh_dpyramid -v -1.000000 1.000000 -1.000000 -v 0.000000 -1.000000 0.000000 -v -1.000000 1.000000 1.000000 -v 1.000000 1.000000 1.000000 -v 1.000000 1.000000 -1.000000 -f 4 5 -f 1 5 -f 2 5 -f 3 4 -f 1 2 -f 2 3 -f 1 3 -f 2 4 diff --git a/Orange/widgets/utils/plot/primitives/dtriangle.obj b/Orange/widgets/utils/plot/primitives/dtriangle.obj deleted file mode 100644 index 3fd63519de9..00000000000 --- a/Orange/widgets/utils/plot/primitives/dtriangle.obj +++ /dev/null @@ -1,8 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -1.000000 1.000000 -0.000000 -v 1.000000 1.000000 -0.000000 -v 0.000000 -1.000000 0.000000 -usemtl Material.001 -s off -f 1 3 2 diff --git a/Orange/widgets/utils/plot/primitives/dtriangle_edges.obj b/Orange/widgets/utils/plot/primitives/dtriangle_edges.obj deleted file mode 100644 index bcd8701935b..00000000000 --- a/Orange/widgets/utils/plot/primitives/dtriangle_edges.obj +++ /dev/null @@ -1,8 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -1.000000 1.000000 0.000000 -v 1.000000 1.000000 -0.000000 -v 0.000000 -1.000000 0.000000 -f 1 2 -f 2 3 -f 1 3 diff --git a/Orange/widgets/utils/plot/primitives/lpyramid.obj b/Orange/widgets/utils/plot/primitives/lpyramid.obj deleted file mode 100644 index 06758f2f6ed..00000000000 --- a/Orange/widgets/utils/plot/primitives/lpyramid.obj +++ /dev/null @@ -1,15 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v 1.000000 -1.000000 -1.000000 -v 1.000000 -1.000000 1.000000 -v -1.000000 -0.000000 -0.000000 -v 1.000000 1.000000 -1.000000 -v 0.999999 1.000000 1.000001 -usemtl Material -s off -f 1 2 3 -f 4 3 5 -f 2 5 3 -f 4 1 3 -f 1 4 2 -f 4 5 2 diff --git a/Orange/widgets/utils/plot/primitives/lpyramid_edges.obj b/Orange/widgets/utils/plot/primitives/lpyramid_edges.obj deleted file mode 100644 index 11bb34599c0..00000000000 --- a/Orange/widgets/utils/plot/primitives/lpyramid_edges.obj +++ /dev/null @@ -1,16 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -o Mesh_lpyramid -v 1.000000 -1.000000 -1.000000 -v 1.000000 -1.000000 1.000000 -v -1.000000 0.000000 0.000000 -v 1.000000 1.000000 -1.000000 -v 0.999999 1.000000 1.000001 -f 1 4 -f 4 5 -f 2 5 -f 1 2 -f 2 3 -f 3 4 -f 3 5 -f 1 3 diff --git a/Orange/widgets/utils/plot/primitives/ltriangle.obj b/Orange/widgets/utils/plot/primitives/ltriangle.obj deleted file mode 100644 index e8ab1633d28..00000000000 --- a/Orange/widgets/utils/plot/primitives/ltriangle.obj +++ /dev/null @@ -1,8 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -1.000000 -1.000000 0.000001 -v 1.000000 -0.000000 -0.000000 -v -0.999999 1.000000 -0.000000 -usemtl Material.001 -s off -f 1 3 2 diff --git a/Orange/widgets/utils/plot/primitives/ltriangle_edges.obj b/Orange/widgets/utils/plot/primitives/ltriangle_edges.obj deleted file mode 100644 index 391a314ec89..00000000000 --- a/Orange/widgets/utils/plot/primitives/ltriangle_edges.obj +++ /dev/null @@ -1,8 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -1.000000 -1.000000 0.000001 -v 1.000000 -0.000000 -0.000000 -v -0.999999 1.000000 -0.000000 -f 1 3 -f 2 3 -f 1 2 diff --git a/Orange/widgets/utils/plot/primitives/lwedge.obj b/Orange/widgets/utils/plot/primitives/lwedge.obj deleted file mode 100644 index 6e44db07b2e..00000000000 --- a/Orange/widgets/utils/plot/primitives/lwedge.obj +++ /dev/null @@ -1,18 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v 1.000000 -1.000000 -1.000000 -v 1.000000 -1.000000 1.000000 -v 1.000000 1.000000 -1.000000 -v 0.999999 1.000000 1.000001 -v -1.000000 1.000000 1.000000 -v -1.000000 1.000000 -1.000000 -usemtl Material -s off -f 2 4 5 -f 1 6 3 -f 1 2 5 -f 1 5 6 -f 1 3 2 -f 3 4 2 -f 3 6 4 -f 6 5 4 diff --git a/Orange/widgets/utils/plot/primitives/lwedge_2d.obj b/Orange/widgets/utils/plot/primitives/lwedge_2d.obj deleted file mode 100644 index 61d62b0f4f2..00000000000 --- a/Orange/widgets/utils/plot/primitives/lwedge_2d.obj +++ /dev/null @@ -1,8 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -1.000000 -1.000000 0.000000 -v -0.999999 1.000000 -0.000001 -v 1.000000 1.000000 -0.000000 -usemtl Material.001 -s off -f 1 2 3 diff --git a/Orange/widgets/utils/plot/primitives/lwedge_2d_edges.obj b/Orange/widgets/utils/plot/primitives/lwedge_2d_edges.obj deleted file mode 100644 index 8a5f04f89c9..00000000000 --- a/Orange/widgets/utils/plot/primitives/lwedge_2d_edges.obj +++ /dev/null @@ -1,9 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -o Mesh_lwedge_2d -v -1.000000 -1.000000 0.000000 -v -0.999999 1.000000 -0.000001 -v 1.000000 1.000000 -0.000000 -f 1 3 -f 2 3 -f 1 2 diff --git a/Orange/widgets/utils/plot/primitives/lwedge_edges.obj b/Orange/widgets/utils/plot/primitives/lwedge_edges.obj deleted file mode 100644 index c6aa6a7e8c8..00000000000 --- a/Orange/widgets/utils/plot/primitives/lwedge_edges.obj +++ /dev/null @@ -1,19 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -mtllib lwedge_edges.mtl -o Mesh_lwedge -v 1.000000 -1.000000 1.000000 -v 0.999999 1.000000 1.000001 -v -1.000000 1.000000 1.000000 -v 1.000000 -1.000000 -1.000000 -v -1.000000 1.000000 -1.000000 -v 1.000000 1.000000 -1.000000 -f 3 5 -f 2 3 -f 1 2 -f 1 3 -f 5 6 -f 2 6 -f 4 6 -f 1 4 -f 4 5 diff --git a/Orange/widgets/utils/plot/primitives/octahedron.obj b/Orange/widgets/utils/plot/primitives/octahedron.obj deleted file mode 100644 index 05314cdd63e..00000000000 --- a/Orange/widgets/utils/plot/primitives/octahedron.obj +++ /dev/null @@ -1,26 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v 1.000000 0.000000 0.000000 -v -1.000000 0.000000 0.000000 -v 0.000000 -0.800000 -0.800000 -v 0.000000 -0.800000 0.800000 -v 0.000000 0.800000 -0.800000 -v 0.000000 0.800000 0.800000 -vn -0.624695 -0.000000 -0.780869 -vn 0.624695 -0.000000 -0.780869 -vn -0.624695 0.000000 0.780869 -vn 0.624695 0.000000 0.780869 -vn -0.624695 0.780869 -0.000000 -vn 0.624695 0.780869 -0.000000 -vn -0.624695 -0.780869 0.000000 -vn 0.624695 -0.780869 0.000000 -usemtl Material.001 -s off -f 3//1 2//1 5//1 -f 1//2 3//2 5//2 -f 6//3 2//3 4//3 -f 1//4 6//4 4//4 -f 5//5 2//5 6//5 -f 6//6 1//6 5//6 -f 4//7 2//7 3//7 -f 3//8 1//8 4//8 diff --git a/Orange/widgets/utils/plot/primitives/octahedron_edges.obj b/Orange/widgets/utils/plot/primitives/octahedron_edges.obj deleted file mode 100644 index 864713ad0b6..00000000000 --- a/Orange/widgets/utils/plot/primitives/octahedron_edges.obj +++ /dev/null @@ -1,20 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v 1.000000 0.000000 0.000000 -v -1.000000 0.000000 0.000000 -v 0.000000 -0.800000 -0.800000 -v 0.000000 -0.800000 0.800000 -v 0.000000 0.800000 -0.800000 -v 0.000000 0.800000 0.800000 -f 2 3 -f 2 5 -f 3 5 -f 1 3 -f 1 5 -f 2 6 -f 2 4 -f 4 6 -f 1 6 -f 1 4 -f 5 6 -f 3 4 diff --git a/Orange/widgets/utils/plot/primitives/pyramid.obj b/Orange/widgets/utils/plot/primitives/pyramid.obj deleted file mode 100644 index e6fc37de95f..00000000000 --- a/Orange/widgets/utils/plot/primitives/pyramid.obj +++ /dev/null @@ -1,15 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v 1.000000 -1.000000 -1.000000 -v 1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 -1.000000 -v -0.000000 1.000000 0.000000 -usemtl Material -s off -f 1 5 2 -f 2 5 3 -f 3 5 4 -f 5 1 4 -f 1 2 3 -f 1 3 4 diff --git a/Orange/widgets/utils/plot/primitives/pyramid_edges.obj b/Orange/widgets/utils/plot/primitives/pyramid_edges.obj deleted file mode 100644 index f2a634be9b8..00000000000 --- a/Orange/widgets/utils/plot/primitives/pyramid_edges.obj +++ /dev/null @@ -1,16 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -o Mesh_pyramid -v 1.000000 -1.000000 -1.000000 -v 0.000000 1.000000 -0.000000 -v 1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 -1.000000 -f 4 5 -f 1 5 -f 2 5 -f 3 4 -f 1 2 -f 2 3 -f 1 3 -f 2 4 diff --git a/Orange/widgets/utils/plot/primitives/rect.obj b/Orange/widgets/utils/plot/primitives/rect.obj deleted file mode 100644 index 4f8014ac704..00000000000 --- a/Orange/widgets/utils/plot/primitives/rect.obj +++ /dev/null @@ -1,10 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v 1.000000 -1.000000 0.000000 -v -1.000000 -1.000000 -0.000000 -v 1.000000 1.000000 0.000001 -v -1.000000 1.000000 -0.000000 -usemtl Material -s off -f 1 3 2 -f 3 4 2 diff --git a/Orange/widgets/utils/plot/primitives/rect_edges.obj b/Orange/widgets/utils/plot/primitives/rect_edges.obj deleted file mode 100644 index 440a49dd8bd..00000000000 --- a/Orange/widgets/utils/plot/primitives/rect_edges.obj +++ /dev/null @@ -1,10 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -1.000000 -1.000000 0.000000 -v -1.000000 1.000000 -0.000000 -v 1.000000 -1.000000 0.000000 -v 1.000000 1.000000 -0.000000 -f 3 4 -f 2 4 -f 1 3 -f 1 2 diff --git a/Orange/widgets/utils/plot/primitives/sphere.obj b/Orange/widgets/utils/plot/primitives/sphere.obj deleted file mode 100644 index 285fefeee62..00000000000 --- a/Orange/widgets/utils/plot/primitives/sphere.obj +++ /dev/null @@ -1,104 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v 0.000000 1.000000 -0.000000 -v 0.587785 0.809017 -0.000000 -v 0.951057 0.309017 -0.000000 -v 0.951056 -0.309017 0.000000 -v 0.587785 -0.809017 0.000000 -v 0.000000 -1.000000 0.000000 -v 0.293893 -0.809017 -0.509037 -v 0.475528 -0.309017 -0.823639 -v 0.475528 0.309017 -0.823639 -v 0.293893 0.809017 -0.509037 -v -0.293893 0.809017 -0.509037 -v -0.475528 0.309017 -0.823639 -v -0.475528 -0.309017 -0.823639 -v -0.293893 -0.809017 -0.509037 -v -0.587785 -0.809017 0.000000 -v -0.951057 -0.309017 0.000000 -v -0.951057 0.309017 -0.000000 -v -0.587785 0.809017 -0.000000 -v -0.293893 0.809017 0.509037 -v -0.475528 0.309017 0.823639 -v -0.475528 -0.309017 0.823639 -v -0.293893 -0.809017 0.509037 -v 0.293893 -0.809017 0.509037 -v 0.475528 -0.309017 0.823639 -v 0.475528 0.309017 0.823639 -v 0.293893 0.809017 0.509037 -vn 0.000000 -1.000000 0.000000 -vn 0.231330 -0.773949 -0.589435 -vn 0.626118 -0.773949 -0.094363 -vn 0.394787 0.773949 -0.495041 -vn 0.000000 1.000000 0.000000 -vn 0.626118 0.773949 0.094363 -vn -0.231330 0.773949 -0.589435 -vn -0.394787 -0.773949 -0.495041 -vn -0.576800 -0.816858 0.000000 -vn -0.626118 0.773949 -0.094363 -vn -0.394787 0.773949 0.495041 -vn -0.331248 -0.749016 0.573748 -vn 0.394787 -0.773949 0.495041 -vn 0.231330 0.773949 0.589435 -vn 0.948637 -0.315928 -0.015168 -vn 0.487472 -0.315928 0.813959 -vn 0.948637 0.315928 0.015168 -vn 0.461165 0.315928 0.829127 -vn -0.374950 0.374645 0.847926 -vn -0.488723 -0.211158 0.846492 -vn -0.909604 -0.415448 0.000000 -vn -0.959929 0.270791 0.071718 -vn -0.461165 0.315928 -0.829127 -vn -0.487472 -0.315928 -0.813959 -vn 0.461165 -0.315928 -0.829127 -vn 0.487472 0.315928 -0.813959 -usemtl (null) -s 1 -f 6//1 7//2 5//3 -f 10//4 1//5 2//6 -f 11//7 1//5 10//4 -f 6//1 14//8 7//2 -f 6//1 15//9 14//8 -f 18//10 1//5 11//7 -f 19//11 1//5 18//10 -f 6//1 22//12 15//9 -f 6//1 23//13 22//12 -f 26//14 1//5 19//11 -f 2//6 1//5 26//14 -f 6//1 5//3 23//13 -f 23//13 5//3 4//15 -f 23//13 4//15 24//16 -f 24//16 4//15 3//17 -f 24//16 3//17 25//18 -f 25//18 3//17 2//6 -f 25//18 2//6 26//14 -f 20//19 25//18 26//14 -f 20//19 26//14 19//11 -f 21//20 24//16 25//18 -f 21//20 25//18 20//19 -f 22//12 23//13 24//16 -f 22//12 24//16 21//20 -f 15//9 22//12 16//21 -f 22//12 21//20 16//21 -f 16//21 21//20 17//22 -f 21//20 20//19 17//22 -f 17//22 20//19 19//11 -f 17//22 19//11 18//10 -f 12//23 17//22 18//10 -f 12//23 18//10 11//7 -f 13//24 16//21 17//22 -f 13//24 17//22 12//23 -f 14//8 15//9 16//21 -f 14//8 16//21 13//24 -f 7//2 14//8 13//24 -f 7//2 13//24 8//25 -f 8//25 13//24 12//23 -f 8//25 12//23 9//26 -f 9//26 12//23 11//7 -f 9//26 11//7 10//4 -f 3//17 9//26 10//4 -f 3//17 10//4 2//6 -f 4//15 8//25 9//26 -f 4//15 9//26 3//17 -f 5//3 7//2 8//25 -f 5//3 8//25 4//15 diff --git a/Orange/widgets/utils/plot/primitives/sphere_edges.obj b/Orange/widgets/utils/plot/primitives/sphere_edges.obj deleted file mode 100644 index 47a0735d700..00000000000 --- a/Orange/widgets/utils/plot/primitives/sphere_edges.obj +++ /dev/null @@ -1,83 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -o Mesh_sphere -v 0.000000 -1.000000 0.000000 -v 0.293893 -0.809017 -0.509037 -v 0.587785 -0.809017 0.000000 -v 0.293893 0.809017 -0.509037 -v 0.000000 1.000000 -0.000000 -v 0.587785 0.809017 -0.000000 -v -0.293893 0.809017 -0.509037 -v -0.293893 -0.809017 -0.509037 -v -0.587785 -0.809017 0.000000 -v -0.587785 0.809017 -0.000000 -v -0.293893 0.809017 0.509037 -v -0.293893 -0.809017 0.509037 -v 0.293893 -0.809017 0.509037 -v 0.293893 0.809017 0.509037 -v 0.951056 -0.309017 0.000000 -v 0.475528 -0.309017 0.823639 -v 0.951057 0.309017 -0.000000 -v 0.475528 0.309017 0.823639 -v -0.475528 0.309017 0.823639 -v -0.475528 -0.309017 0.823639 -v -0.951057 -0.309017 0.000000 -v -0.951057 0.309017 -0.000000 -v -0.475528 0.309017 -0.823639 -v -0.475528 -0.309017 -0.823639 -v 0.475528 -0.309017 -0.823639 -v 0.475528 0.309017 -0.823639 -f 18 19 -f 5 7 -f 9 21 -f 1 13 -f 22 23 -f 6 14 -f 16 20 -f 17 26 -f 13 16 -f 5 6 -f 8 9 -f 1 12 -f 21 24 -f 4 5 -f 6 17 -f 11 19 -f 5 14 -f 15 16 -f 14 18 -f 2 25 -f 15 17 -f 17 18 -f 19 22 -f 4 6 -f 15 25 -f 10 22 -f 3 13 -f 23 26 -f 5 11 -f 7 23 -f 19 20 -f 11 14 -f 21 22 -f 8 24 -f 4 7 -f 1 3 -f 2 3 -f 5 10 -f 1 9 -f 20 21 -f 25 26 -f 3 15 -f 24 25 -f 12 20 -f 4 26 -f 1 2 -f 10 11 -f 2 8 -f 12 13 -f 1 8 -f 23 24 -f 16 18 -f 9 12 -f 7 10 diff --git a/Orange/widgets/utils/plot/primitives/sphere_hq.obj b/Orange/widgets/utils/plot/primitives/sphere_hq.obj deleted file mode 100644 index 27d8c87d480..00000000000 --- a/Orange/widgets/utils/plot/primitives/sphere_hq.obj +++ /dev/null @@ -1,3976 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v 0.000000 1.000000 -0.000000 -v 0.098017 0.995185 -0.000000 -v 0.773010 0.634393 -0.000000 -v 0.096133 -0.995185 -0.019122 -v 0.191341 -0.980785 -0.038060 -v 0.284706 -0.956941 -0.056632 -v 0.375330 -0.923880 -0.074658 -v 0.462338 -0.881922 -0.091965 -v 0.544895 -0.831470 -0.108386 -v 0.622203 -0.773011 -0.123764 -v 0.693520 -0.707107 -0.137950 -v 0.758157 -0.634394 -0.150807 -v 0.815493 -0.555571 -0.162212 -v 0.864975 -0.471397 -0.172054 -v 0.906127 -0.382684 -0.180240 -v 0.938553 -0.290285 -0.186690 -v 0.961940 -0.195091 -0.191342 -v 0.976062 -0.098017 -0.194151 -v 0.980785 -0.000000 -0.195090 -v 0.976062 0.098017 -0.194151 -v 0.961940 0.195090 -0.191342 -v 0.938553 0.290285 -0.186690 -v 0.906127 0.382683 -0.180240 -v 0.864975 0.471397 -0.172054 -v 0.815493 0.555570 -0.162212 -v 0.758157 0.634393 -0.150807 -v 0.693520 0.707107 -0.137950 -v 0.622204 0.773010 -0.123764 -v 0.544895 0.831470 -0.108386 -v 0.462339 0.881921 -0.091965 -v 0.375330 0.923880 -0.074658 -v 0.284707 0.956940 -0.056632 -v 0.191342 0.980785 -0.038060 -v 0.096134 0.995185 -0.019122 -v 0.090556 0.995185 -0.037510 -v 0.180240 0.980785 -0.074658 -v 0.268188 0.956940 -0.111087 -v 0.353553 0.923880 -0.146447 -v 0.435514 0.881921 -0.180396 -v 0.513280 0.831470 -0.212608 -v 0.586103 0.773010 -0.242772 -v 0.653281 0.707107 -0.270598 -v 0.714168 0.634393 -0.295818 -v 0.768178 0.555570 -0.318190 -v 0.814789 0.471397 -0.337497 -v 0.853553 0.382683 -0.353553 -v 0.884098 0.290285 -0.366205 -v 0.906127 0.195090 -0.375330 -v 0.919431 0.098017 -0.380841 -v 0.923879 -0.000000 -0.382683 -v 0.919431 -0.098017 -0.380841 -v 0.906127 -0.195091 -0.375330 -v 0.884097 -0.290285 -0.366205 -v 0.853553 -0.382684 -0.353553 -v 0.814789 -0.471397 -0.337497 -v 0.768178 -0.555571 -0.318190 -v 0.714168 -0.634394 -0.295818 -v 0.653281 -0.707107 -0.270598 -v 0.586103 -0.773011 -0.242772 -v 0.513279 -0.831470 -0.212607 -v 0.435513 -0.881922 -0.180395 -v 0.353553 -0.923880 -0.146446 -v 0.268187 -0.956941 -0.111087 -v 0.180239 -0.980785 -0.074658 -v 0.090555 -0.995185 -0.037509 -v 0.081498 -0.995185 -0.054455 -v 0.162211 -0.980785 -0.108386 -v 0.241362 -0.956941 -0.161273 -v 0.318189 -0.923880 -0.212607 -v 0.391952 -0.881922 -0.261894 -v 0.461939 -0.831470 -0.308658 -v 0.527478 -0.773011 -0.352450 -v 0.587937 -0.707107 -0.392847 -v 0.642734 -0.634394 -0.429461 -v 0.691342 -0.555571 -0.461940 -v 0.733291 -0.471397 -0.489969 -v 0.768178 -0.382684 -0.513280 -v 0.795667 -0.290285 -0.531647 -v 0.815493 -0.195091 -0.544895 -v 0.827466 -0.098017 -0.552895 -v 0.831469 -0.000000 -0.555570 -v 0.827466 0.098017 -0.552895 -v 0.815493 0.195090 -0.544895 -v 0.795667 0.290285 -0.531648 -v 0.768178 0.382683 -0.513280 -v 0.733291 0.471397 -0.489969 -v 0.691342 0.555570 -0.461940 -v 0.642735 0.634393 -0.429462 -v 0.587938 0.707107 -0.392848 -v 0.527479 0.773010 -0.352450 -v 0.461940 0.831470 -0.308658 -v 0.391952 0.881921 -0.261894 -v 0.318190 0.923880 -0.212608 -v 0.241363 0.956940 -0.161274 -v 0.162212 0.980785 -0.108386 -v 0.081498 0.995185 -0.054455 -v 0.069309 0.995185 -0.069309 -v 0.137950 0.980785 -0.137950 -v 0.205262 0.956940 -0.205262 -v 0.270598 0.923880 -0.270598 -v 0.333328 0.881921 -0.333328 -v 0.392847 0.831470 -0.392847 -v 0.448584 0.773010 -0.448584 -v 0.500000 0.707107 -0.500000 -v 0.546601 0.634393 -0.546601 -v 0.587938 0.555570 -0.587938 -v 0.623612 0.471397 -0.623612 -v 0.653281 0.382683 -0.653281 -v 0.676659 0.290285 -0.676659 -v 0.693520 0.195090 -0.693520 -v 0.703702 0.098017 -0.703702 -v 0.707107 -0.000000 -0.707107 -v 0.703702 -0.098017 -0.703702 -v 0.693520 -0.195091 -0.693520 -v 0.676659 -0.290285 -0.676659 -v 0.653281 -0.382684 -0.653281 -v 0.623612 -0.471397 -0.623612 -v 0.587938 -0.555571 -0.587938 -v 0.546601 -0.634394 -0.546601 -v 0.500000 -0.707107 -0.500000 -v 0.448583 -0.773011 -0.448583 -v 0.392847 -0.831470 -0.392847 -v 0.333327 -0.881922 -0.333327 -v 0.270598 -0.923880 -0.270598 -v 0.205262 -0.956941 -0.205262 -v 0.137949 -0.980785 -0.137949 -v 0.069308 -0.995185 -0.069308 -v -0.000001 -1.000000 0.000001 -v 0.054455 -0.995185 -0.081498 -v 0.108386 -0.980785 -0.162211 -v 0.161273 -0.956941 -0.241362 -v 0.212607 -0.923880 -0.318189 -v 0.261894 -0.881922 -0.391952 -v 0.308658 -0.831470 -0.461939 -v 0.352450 -0.773011 -0.527478 -v 0.392847 -0.707107 -0.587937 -v 0.429461 -0.634394 -0.642734 -v 0.461940 -0.555571 -0.691341 -v 0.489969 -0.471397 -0.733290 -v 0.513280 -0.382684 -0.768178 -v 0.531647 -0.290285 -0.795667 -v 0.544895 -0.195091 -0.815493 -v 0.552895 -0.098017 -0.827466 -v 0.555570 -0.000000 -0.831470 -v 0.552895 0.098017 -0.827466 -v 0.544895 0.195090 -0.815493 -v 0.531647 0.290285 -0.795667 -v 0.513280 0.382683 -0.768178 -v 0.489969 0.471397 -0.733291 -v 0.461940 0.555570 -0.691342 -v 0.429462 0.634393 -0.642735 -v 0.392847 0.707107 -0.587938 -v 0.352450 0.773010 -0.527479 -v 0.308658 0.831470 -0.461940 -v 0.261894 0.881921 -0.391952 -v 0.212608 0.923880 -0.318190 -v 0.161273 0.956940 -0.241363 -v 0.108386 0.980785 -0.162212 -v 0.054455 0.995185 -0.081498 -v 0.037510 0.995185 -0.090556 -v 0.074658 0.980785 -0.180240 -v 0.111087 0.956940 -0.268188 -v 0.146447 0.923880 -0.353553 -v 0.180396 0.881921 -0.435514 -v 0.212607 0.831470 -0.513280 -v 0.242772 0.773010 -0.586103 -v 0.270598 0.707107 -0.653282 -v 0.295818 0.634393 -0.714168 -v 0.318190 0.555570 -0.768178 -v 0.337497 0.471397 -0.814789 -v 0.353553 0.382683 -0.853553 -v 0.366205 0.290285 -0.884098 -v 0.375330 0.195090 -0.906127 -v 0.380841 0.098017 -0.919431 -v 0.382683 -0.000000 -0.923879 -v 0.380841 -0.098017 -0.919431 -v 0.375330 -0.195091 -0.906127 -v 0.366205 -0.290285 -0.884097 -v 0.353553 -0.382684 -0.853553 -v 0.337497 -0.471397 -0.814789 -v 0.318190 -0.555571 -0.768177 -v 0.295818 -0.634394 -0.714168 -v 0.270598 -0.707107 -0.653281 -v 0.242772 -0.773011 -0.586102 -v 0.212607 -0.831470 -0.513279 -v 0.180395 -0.881922 -0.435513 -v 0.146446 -0.923880 -0.353553 -v 0.111087 -0.956941 -0.268187 -v 0.074658 -0.980785 -0.180239 -v 0.037509 -0.995185 -0.090555 -v 0.019122 -0.995185 -0.096133 -v 0.038060 -0.980785 -0.191341 -v 0.056632 -0.956941 -0.284706 -v 0.074658 -0.923880 -0.375330 -v 0.091965 -0.881922 -0.462338 -v 0.108386 -0.831470 -0.544894 -v 0.123764 -0.773011 -0.622203 -v 0.137950 -0.707107 -0.693519 -v 0.150807 -0.634394 -0.758157 -v 0.162212 -0.555571 -0.815493 -v 0.172054 -0.471397 -0.864975 -v 0.180240 -0.382684 -0.906127 -v 0.186690 -0.290285 -0.938553 -v 0.191342 -0.195091 -0.961940 -v 0.194151 -0.098017 -0.976062 -v 0.195090 -0.000000 -0.980785 -v 0.194151 0.098017 -0.976062 -v 0.191342 0.195090 -0.961940 -v 0.186690 0.290285 -0.938553 -v 0.180240 0.382683 -0.906127 -v 0.172054 0.471397 -0.864975 -v 0.162212 0.555570 -0.815493 -v 0.150807 0.634393 -0.758157 -v 0.137950 0.707107 -0.693520 -v 0.123764 0.773010 -0.622204 -v 0.108386 0.831470 -0.544895 -v 0.091965 0.881921 -0.462339 -v 0.074658 0.923880 -0.375330 -v 0.056632 0.956940 -0.284707 -v 0.038060 0.980785 -0.191342 -v 0.019122 0.995185 -0.096134 -v 0.000000 0.995185 -0.098017 -v -0.000000 0.980785 -0.195090 -v -0.000000 0.956940 -0.290285 -v -0.000000 0.923880 -0.382683 -v -0.000000 0.881921 -0.471397 -v -0.000000 0.831470 -0.555570 -v -0.000000 0.773010 -0.634393 -v 0.000000 0.707107 -0.707107 -v -0.000000 0.634393 -0.773010 -v -0.000000 0.555570 -0.831469 -v -0.000000 0.471397 -0.881921 -v 0.000000 0.382683 -0.923879 -v -0.000000 0.290285 -0.956940 -v -0.000000 0.195090 -0.980785 -v 0.000000 0.098017 -0.995184 -v -0.000000 -0.000000 -1.000000 -v 0.000000 -0.098017 -0.995184 -v -0.000000 -0.195091 -0.980785 -v -0.000000 -0.290285 -0.956940 -v -0.000000 -0.382684 -0.923879 -v -0.000000 -0.471397 -0.881921 -v -0.000000 -0.555571 -0.831469 -v -0.000000 -0.634394 -0.773010 -v -0.000000 -0.707107 -0.707106 -v -0.000000 -0.773011 -0.634393 -v -0.000000 -0.831470 -0.555570 -v -0.000000 -0.881922 -0.471396 -v 0.000000 -0.923880 -0.382683 -v -0.000000 -0.956941 -0.290284 -v -0.000000 -0.980785 -0.195090 -v -0.000000 -0.995185 -0.098016 -v -0.019122 -0.995185 -0.096133 -v -0.038060 -0.980785 -0.191341 -v -0.056632 -0.956941 -0.284706 -v -0.074658 -0.923880 -0.375330 -v -0.091965 -0.881922 -0.462338 -v -0.108386 -0.831470 -0.544894 -v -0.123764 -0.773011 -0.622203 -v -0.137950 -0.707107 -0.693519 -v -0.150807 -0.634394 -0.758157 -v -0.162212 -0.555571 -0.815493 -v -0.172054 -0.471397 -0.864975 -v -0.180240 -0.382684 -0.906127 -v -0.186690 -0.290285 -0.938553 -v -0.191342 -0.195091 -0.961940 -v -0.194151 -0.098017 -0.976062 -v -0.195090 -0.000000 -0.980785 -v -0.194151 0.098017 -0.976062 -v -0.191342 0.195090 -0.961940 -v -0.186690 0.290285 -0.938553 -v -0.180240 0.382683 -0.906127 -v -0.172054 0.471397 -0.864975 -v -0.162212 0.555570 -0.815493 -v -0.150807 0.634393 -0.758157 -v -0.137950 0.707107 -0.693520 -v -0.123764 0.773010 -0.622203 -v -0.108386 0.831470 -0.544895 -v -0.091965 0.881921 -0.462339 -v -0.074658 0.923880 -0.375330 -v -0.056632 0.956940 -0.284707 -v -0.038060 0.980785 -0.191342 -v -0.019122 0.995185 -0.096134 -v -0.037510 0.995185 -0.090556 -v -0.074658 0.980785 -0.180240 -v -0.111087 0.956940 -0.268188 -v -0.146447 0.923880 -0.353553 -v -0.180396 0.881921 -0.435514 -v -0.212608 0.831470 -0.513280 -v -0.242772 0.773010 -0.586103 -v -0.270598 0.707107 -0.653281 -v -0.295818 0.634393 -0.714168 -v -0.318190 0.555570 -0.768178 -v -0.337497 0.471397 -0.814789 -v -0.353553 0.382683 -0.853553 -v -0.366205 0.290285 -0.884097 -v -0.375330 0.195090 -0.906127 -v -0.380841 0.098017 -0.919430 -v -0.382683 -0.000000 -0.923879 -v -0.380841 -0.098017 -0.919430 -v -0.375330 -0.195091 -0.906127 -v -0.366205 -0.290285 -0.884097 -v -0.353553 -0.382684 -0.853553 -v -0.337497 -0.471397 -0.814789 -v -0.318190 -0.555571 -0.768177 -v -0.295818 -0.634394 -0.714168 -v -0.270598 -0.707107 -0.653281 -v -0.242772 -0.773011 -0.586102 -v -0.212607 -0.831470 -0.513279 -v -0.180396 -0.881922 -0.435513 -v -0.146446 -0.923880 -0.353553 -v -0.111087 -0.956941 -0.268187 -v -0.074658 -0.980785 -0.180239 -v -0.037509 -0.995185 -0.090555 -v -0.054455 -0.995185 -0.081498 -v -0.108386 -0.980785 -0.162211 -v -0.161273 -0.956941 -0.241362 -v -0.212607 -0.923880 -0.318189 -v -0.261894 -0.881922 -0.391951 -v -0.308658 -0.831470 -0.461939 -v -0.352450 -0.773011 -0.527478 -v -0.392847 -0.707107 -0.587937 -v -0.429461 -0.634394 -0.642734 -v -0.461940 -0.555571 -0.691341 -v -0.489969 -0.471397 -0.733290 -v -0.513280 -0.382684 -0.768177 -v -0.531647 -0.290285 -0.795667 -v -0.544895 -0.195091 -0.815493 -v -0.552895 -0.098017 -0.827466 -v -0.555570 -0.000000 -0.831469 -v -0.552895 0.098017 -0.827466 -v -0.544895 0.195090 -0.815493 -v -0.531648 0.290285 -0.795667 -v -0.513280 0.382683 -0.768178 -v -0.489969 0.471397 -0.733290 -v -0.461940 0.555570 -0.691341 -v -0.429462 0.634393 -0.642735 -v -0.392847 0.707107 -0.587938 -v -0.352450 0.773010 -0.527479 -v -0.308658 0.831470 -0.461940 -v -0.261894 0.881921 -0.391952 -v -0.212608 0.923880 -0.318190 -v -0.161273 0.956940 -0.241363 -v -0.108386 0.980785 -0.162212 -v -0.054455 0.995185 -0.081498 -v -0.069309 0.995185 -0.069309 -v -0.137950 0.980785 -0.137950 -v -0.205262 0.956940 -0.205262 -v -0.270598 0.923880 -0.270598 -v -0.333328 0.881921 -0.333328 -v -0.392847 0.831470 -0.392847 -v -0.448584 0.773010 -0.448584 -v -0.500000 0.707107 -0.500000 -v -0.546601 0.634393 -0.546601 -v -0.587938 0.555570 -0.587938 -v -0.623612 0.471397 -0.623612 -v -0.653281 0.382683 -0.653281 -v -0.676659 0.290285 -0.676659 -v -0.693520 0.195090 -0.693520 -v -0.703702 0.098017 -0.703702 -v -0.707107 -0.000000 -0.707106 -v -0.703702 -0.098017 -0.703702 -v -0.693520 -0.195091 -0.693520 -v -0.676659 -0.290285 -0.676659 -v -0.653281 -0.382684 -0.653281 -v -0.623612 -0.471397 -0.623612 -v -0.587938 -0.555571 -0.587937 -v -0.546601 -0.634394 -0.546601 -v -0.500000 -0.707107 -0.500000 -v -0.448583 -0.773011 -0.448583 -v -0.392847 -0.831470 -0.392847 -v -0.333327 -0.881922 -0.333327 -v -0.270598 -0.923880 -0.270597 -v -0.205262 -0.956941 -0.205262 -v -0.137949 -0.980785 -0.137949 -v -0.069308 -0.995185 -0.069308 -v -0.081498 -0.995185 -0.054455 -v -0.162211 -0.980785 -0.108386 -v -0.241362 -0.956941 -0.161273 -v -0.318189 -0.923880 -0.212607 -v -0.391952 -0.881922 -0.261894 -v -0.461939 -0.831470 -0.308658 -v -0.527478 -0.773011 -0.352450 -v -0.587937 -0.707107 -0.392847 -v -0.642734 -0.634394 -0.429461 -v -0.691341 -0.555571 -0.461939 -v -0.733290 -0.471397 -0.489969 -v -0.768177 -0.382684 -0.513280 -v -0.795667 -0.290285 -0.531647 -v -0.815493 -0.195091 -0.544895 -v -0.827466 -0.098017 -0.552895 -v -0.831469 -0.000000 -0.555570 -v -0.827466 0.098017 -0.552895 -v -0.815493 0.195090 -0.544895 -v -0.795667 0.290285 -0.531647 -v -0.768178 0.382683 -0.513280 -v -0.733290 0.471397 -0.489969 -v -0.691342 0.555570 -0.461940 -v -0.642735 0.634393 -0.429461 -v -0.587938 0.707107 -0.392847 -v -0.527479 0.773010 -0.352450 -v -0.461940 0.831470 -0.308658 -v -0.391952 0.881921 -0.261894 -v -0.318190 0.923880 -0.212607 -v -0.241363 0.956940 -0.161273 -v -0.162212 0.980785 -0.108386 -v -0.081498 0.995185 -0.054455 -v -0.090556 0.995185 -0.037510 -v -0.180240 0.980785 -0.074658 -v -0.268188 0.956940 -0.111087 -v -0.353553 0.923880 -0.146447 -v -0.435514 0.881921 -0.180396 -v -0.513280 0.831470 -0.212607 -v -0.586103 0.773010 -0.242772 -v -0.653281 0.707107 -0.270598 -v -0.714168 0.634393 -0.295818 -v -0.768178 0.555570 -0.318189 -v -0.814789 0.471397 -0.337497 -v -0.853553 0.382683 -0.353553 -v -0.884097 0.290285 -0.366205 -v -0.906127 0.195090 -0.375330 -v -0.919430 0.098017 -0.380840 -v -0.923879 -0.000000 -0.382683 -v -0.919430 -0.098017 -0.380840 -v -0.906127 -0.195091 -0.375330 -v -0.884097 -0.290285 -0.366205 -v -0.853553 -0.382684 -0.353553 -v -0.814789 -0.471397 -0.337496 -v -0.768177 -0.555571 -0.318189 -v -0.714168 -0.634394 -0.295818 -v -0.653281 -0.707107 -0.270598 -v -0.586102 -0.773011 -0.242771 -v -0.513279 -0.831470 -0.212607 -v -0.435513 -0.881922 -0.180395 -v -0.353553 -0.923880 -0.146446 -v -0.268187 -0.956941 -0.111087 -v -0.180239 -0.980785 -0.074657 -v -0.090555 -0.995185 -0.037509 -v -0.096133 -0.995185 -0.019122 -v -0.191341 -0.980785 -0.038060 -v -0.284706 -0.956941 -0.056632 -v -0.375330 -0.923880 -0.074658 -v -0.462338 -0.881922 -0.091965 -v -0.544894 -0.831470 -0.108386 -v -0.622203 -0.773011 -0.123764 -v -0.693519 -0.707107 -0.137950 -v -0.758157 -0.634394 -0.150807 -v -0.815493 -0.555571 -0.162212 -v -0.864975 -0.471397 -0.172054 -v -0.906127 -0.382684 -0.180240 -v -0.938553 -0.290285 -0.186690 -v -0.961939 -0.195091 -0.191342 -v -0.976062 -0.098017 -0.194151 -v -0.980785 -0.000000 -0.195090 -v -0.976062 0.098017 -0.194151 -v -0.961939 0.195090 -0.191342 -v -0.938553 0.290285 -0.186690 -v -0.906127 0.382683 -0.180240 -v -0.864975 0.471397 -0.172054 -v -0.815493 0.555570 -0.162212 -v -0.758157 0.634393 -0.150807 -v -0.693520 0.707107 -0.137950 -v -0.622203 0.773010 -0.123764 -v -0.544895 0.831470 -0.108386 -v -0.462339 0.881921 -0.091965 -v -0.375330 0.923880 -0.074658 -v -0.284707 0.956940 -0.056632 -v -0.191342 0.980785 -0.038060 -v -0.096134 0.995185 -0.019122 -v -0.098017 0.995185 -0.000000 -v -0.195090 0.980785 -0.000000 -v -0.290285 0.956940 -0.000000 -v -0.382683 0.923880 0.000000 -v -0.471397 0.881921 0.000000 -v -0.555570 0.831470 0.000000 -v -0.634393 0.773010 -0.000000 -v -0.707107 0.707107 -0.000000 -v -0.773010 0.634393 0.000000 -v -0.831469 0.555570 0.000000 -v -0.881921 0.471397 -0.000000 -v -0.923879 0.382683 -0.000000 -v -0.956940 0.290285 0.000000 -v -0.980785 0.195090 0.000000 -v -0.995184 0.098017 0.000000 -v -0.999999 -0.000000 0.000000 -v -0.995184 -0.098017 0.000000 -v -0.980785 -0.195091 0.000000 -v -0.956940 -0.290285 -0.000000 -v -0.923879 -0.382684 0.000000 -v -0.881921 -0.471397 0.000000 -v -0.831469 -0.555571 0.000000 -v -0.773010 -0.634394 0.000000 -v -0.707106 -0.707107 0.000000 -v -0.634393 -0.773011 0.000000 -v -0.555569 -0.831470 0.000000 -v -0.471396 -0.881922 0.000000 -v -0.382683 -0.923880 0.000000 -v -0.290284 -0.956941 0.000000 -v -0.195090 -0.980785 0.000000 -v -0.098016 -0.995185 0.000000 -v -0.096133 -0.995185 0.019122 -v -0.191341 -0.980785 0.038060 -v -0.284706 -0.956941 0.056632 -v -0.375329 -0.923880 0.074658 -v -0.462338 -0.881922 0.091965 -v -0.544894 -0.831470 0.108386 -v -0.622203 -0.773011 0.123764 -v -0.693519 -0.707107 0.137950 -v -0.758157 -0.634394 0.150807 -v -0.815493 -0.555571 0.162212 -v -0.864975 -0.471397 0.172054 -v -0.906127 -0.382684 0.180240 -v -0.938553 -0.290285 0.186690 -v -0.961939 -0.195091 0.191342 -v -0.976062 -0.098017 0.194151 -v -0.980785 -0.000000 0.195091 -v -0.976062 0.098017 0.194151 -v -0.961939 0.195090 0.191342 -v -0.938553 0.290285 0.186690 -v -0.906127 0.382683 0.180240 -v -0.864975 0.471397 0.172054 -v -0.815493 0.555570 0.162212 -v -0.758157 0.634393 0.150807 -v -0.693520 0.707107 0.137950 -v -0.622203 0.773010 0.123764 -v -0.544895 0.831470 0.108386 -v -0.462339 0.881921 0.091965 -v -0.375330 0.923880 0.074658 -v -0.284707 0.956940 0.056632 -v -0.191342 0.980785 0.038060 -v -0.096134 0.995185 0.019122 -v -0.090556 0.995185 0.037509 -v -0.180240 0.980785 0.074658 -v -0.268188 0.956940 0.111087 -v -0.353553 0.923880 0.146447 -v -0.435514 0.881921 0.180396 -v -0.513280 0.831470 0.212607 -v -0.586103 0.773010 0.242772 -v -0.653281 0.707107 0.270598 -v -0.714168 0.634393 0.295818 -v -0.768177 0.555570 0.318190 -v -0.814789 0.471397 0.337496 -v -0.853553 0.382683 0.353553 -v -0.884097 0.290285 0.366205 -v -0.906127 0.195090 0.375330 -v -0.919430 0.098017 0.380841 -v -0.923879 -0.000000 0.382683 -v -0.919430 -0.098017 0.380841 -v -0.906127 -0.195091 0.375330 -v -0.884097 -0.290285 0.366205 -v -0.853553 -0.382684 0.353553 -v -0.814788 -0.471397 0.337497 -v -0.768177 -0.555571 0.318190 -v -0.714168 -0.634394 0.295818 -v -0.653281 -0.707107 0.270598 -v -0.586102 -0.773011 0.242772 -v -0.513279 -0.831470 0.212607 -v -0.435513 -0.881922 0.180396 -v -0.353553 -0.923880 0.146446 -v -0.268187 -0.956941 0.111087 -v -0.180239 -0.980785 0.074658 -v -0.090555 -0.995185 0.037509 -v -0.081498 -0.995185 0.054455 -v -0.162211 -0.980785 0.108386 -v -0.241362 -0.956941 0.161273 -v -0.318189 -0.923880 0.212607 -v -0.391951 -0.881922 0.261894 -v -0.461939 -0.831470 0.308658 -v -0.527478 -0.773011 0.352450 -v -0.587937 -0.707107 0.392847 -v -0.642734 -0.634394 0.429461 -v -0.691341 -0.555571 0.461940 -v -0.733290 -0.471397 0.489969 -v -0.768177 -0.382684 0.513280 -v -0.795666 -0.290285 0.531647 -v -0.815493 -0.195091 0.544895 -v -0.827465 -0.098017 0.552895 -v -0.831469 -0.000000 0.555570 -v -0.827465 0.098017 0.552895 -v -0.815493 0.195090 0.544895 -v -0.795666 0.290285 0.531648 -v -0.768178 0.382683 0.513280 -v -0.733290 0.471397 0.489969 -v -0.691341 0.555570 0.461940 -v -0.642734 0.634393 0.429462 -v -0.587938 0.707107 0.392847 -v -0.527478 0.773010 0.352450 -v -0.461939 0.831470 0.308658 -v -0.391952 0.881921 0.261894 -v -0.318190 0.923880 0.212607 -v -0.241363 0.956940 0.161273 -v -0.162212 0.980785 0.108386 -v -0.081498 0.995185 0.054455 -v -0.069309 0.995185 0.069309 -v -0.137950 0.980785 0.137950 -v -0.205262 0.956940 0.205262 -v -0.270598 0.923880 0.270598 -v -0.333328 0.881921 0.333328 -v -0.392847 0.831470 0.392847 -v -0.448583 0.773010 0.448583 -v -0.500000 0.707107 0.500000 -v -0.546601 0.634393 0.546601 -v -0.587937 0.555570 0.587938 -v -0.623612 0.471397 0.623612 -v -0.653281 0.382683 0.653281 -v -0.676659 0.290285 0.676659 -v -0.693520 0.195090 0.693520 -v -0.703701 0.098017 0.703701 -v -0.707106 -0.000000 0.707106 -v -0.703701 -0.098017 0.703701 -v -0.693520 -0.195091 0.693520 -v -0.676659 -0.290285 0.676659 -v -0.653281 -0.382684 0.653281 -v -0.623612 -0.471397 0.623612 -v -0.587937 -0.555571 0.587937 -v -0.546600 -0.634394 0.546601 -v -0.499999 -0.707107 0.500000 -v -0.448583 -0.773011 0.448583 -v -0.392847 -0.831470 0.392847 -v -0.333327 -0.881922 0.333327 -v -0.270597 -0.923880 0.270597 -v -0.205262 -0.956941 0.205262 -v -0.137949 -0.980785 0.137949 -v -0.069308 -0.995185 0.069308 -v -0.054455 -0.995185 0.081498 -v -0.108386 -0.980785 0.162211 -v -0.161273 -0.956941 0.241362 -v -0.212607 -0.923880 0.318189 -v -0.261894 -0.881922 0.391951 -v -0.308658 -0.831470 0.461939 -v -0.352449 -0.773011 0.527478 -v -0.392847 -0.707107 0.587937 -v -0.429461 -0.634394 0.642734 -v -0.461939 -0.555571 0.691341 -v -0.489969 -0.471397 0.733290 -v -0.513280 -0.382684 0.768177 -v -0.531647 -0.290285 0.795666 -v -0.544895 -0.195091 0.815493 -v -0.552894 -0.098017 0.827465 -v -0.555569 -0.000000 0.831469 -v -0.552894 0.098017 0.827465 -v -0.544895 0.195090 0.815493 -v -0.531647 0.290285 0.795667 -v -0.513280 0.382683 0.768177 -v -0.489969 0.471397 0.733290 -v -0.461939 0.555570 0.691341 -v -0.429461 0.634393 0.642735 -v -0.392847 0.707107 0.587938 -v -0.352450 0.773010 0.527478 -v -0.308658 0.831470 0.461939 -v -0.261894 0.881921 0.391952 -v -0.212607 0.923880 0.318190 -v -0.161273 0.956940 0.241363 -v -0.108386 0.980785 0.162212 -v -0.054455 0.995185 0.081498 -v -0.037510 0.995185 0.090556 -v -0.074658 0.980785 0.180240 -v -0.111087 0.956940 0.268188 -v -0.146447 0.923880 0.353553 -v -0.180396 0.881921 0.435514 -v -0.212607 0.831470 0.513280 -v -0.242772 0.773010 0.586103 -v -0.270598 0.707107 0.653281 -v -0.295818 0.634393 0.714168 -v -0.318189 0.555570 0.768177 -v -0.337496 0.471397 0.814788 -v -0.353553 0.382683 0.853553 -v -0.366205 0.290285 0.884097 -v -0.375330 0.195090 0.906127 -v -0.380840 0.098017 0.919430 -v -0.382683 -0.000000 0.923879 -v -0.380840 -0.098017 0.919430 -v -0.375330 -0.195090 0.906127 -v -0.366205 -0.290285 0.884097 -v -0.353553 -0.382684 0.853553 -v -0.337496 -0.471397 0.814788 -v -0.318189 -0.555571 0.768177 -v -0.295818 -0.634394 0.714168 -v -0.270598 -0.707107 0.653281 -v -0.242771 -0.773011 0.586102 -v -0.212607 -0.831470 0.513279 -v -0.180395 -0.881922 0.435513 -v -0.146446 -0.923880 0.353553 -v -0.111087 -0.956941 0.268187 -v -0.074657 -0.980785 0.180239 -v -0.037509 -0.995185 0.090555 -v -0.019122 -0.995185 0.096133 -v -0.038060 -0.980785 0.191341 -v -0.056632 -0.956941 0.284706 -v -0.074658 -0.923880 0.375329 -v -0.091965 -0.881922 0.462338 -v -0.108386 -0.831470 0.544894 -v -0.123764 -0.773011 0.622203 -v -0.137949 -0.707107 0.693519 -v -0.150807 -0.634394 0.758157 -v -0.162211 -0.555571 0.815493 -v -0.172054 -0.471397 0.864975 -v -0.180240 -0.382684 0.906127 -v -0.186690 -0.290285 0.938552 -v -0.191341 -0.195090 0.961939 -v -0.194151 -0.098017 0.976062 -v -0.195090 -0.000000 0.980784 -v -0.194151 0.098017 0.976062 -v -0.191341 0.195090 0.961939 -v -0.186690 0.290285 0.938553 -v -0.180240 0.382683 0.906127 -v -0.172054 0.471397 0.864975 -v -0.162211 0.555570 0.815493 -v -0.150807 0.634393 0.758157 -v -0.137950 0.707107 0.693520 -v -0.123764 0.773010 0.622203 -v -0.108386 0.831470 0.544895 -v -0.091965 0.881921 0.462339 -v -0.074658 0.923880 0.375330 -v -0.056632 0.956940 0.284707 -v -0.038060 0.980785 0.191342 -v -0.019122 0.995185 0.096134 -v 0.000000 0.995185 0.098017 -v 0.000000 0.980785 0.195090 -v 0.000000 0.956940 0.290284 -v 0.000000 0.923880 0.382683 -v 0.000000 0.881921 0.471396 -v 0.000000 0.831470 0.555570 -v 0.000000 0.773010 0.634393 -v 0.000000 0.707107 0.707107 -v 0.000000 0.634393 0.773010 -v 0.000000 0.555570 0.831469 -v 0.000000 0.471397 0.881921 -v 0.000000 0.382683 0.923879 -v 0.000000 0.290285 0.956940 -v 0.000000 0.195090 0.980785 -v 0.000000 0.098017 0.995184 -v 0.000000 -0.000000 0.999999 -v 0.000000 -0.098017 0.995184 -v 0.000000 -0.195090 0.980785 -v 0.000000 -0.290285 0.956940 -v 0.000000 -0.382684 0.923879 -v 0.000000 -0.471397 0.881921 -v 0.000000 -0.555571 0.831469 -v 0.000000 -0.634394 0.773010 -v 0.000000 -0.707107 0.707106 -v 0.000000 -0.773011 0.634392 -v 0.000000 -0.831470 0.555569 -v 0.000000 -0.881922 0.471396 -v 0.000000 -0.923880 0.382683 -v 0.000000 -0.956941 0.290284 -v 0.000000 -0.980785 0.195090 -v 0.000000 -0.995185 0.098016 -v 0.019122 -0.995185 0.096133 -v 0.038060 -0.980785 0.191341 -v 0.056632 -0.956941 0.284706 -v 0.074658 -0.923880 0.375329 -v 0.091965 -0.881922 0.462338 -v 0.108386 -0.831470 0.544894 -v 0.123764 -0.773011 0.622203 -v 0.137950 -0.707107 0.693519 -v 0.150807 -0.634394 0.758157 -v 0.162212 -0.555571 0.815493 -v 0.172054 -0.471397 0.864975 -v 0.180240 -0.382684 0.906127 -v 0.186690 -0.290285 0.938552 -v 0.191342 -0.195090 0.961939 -v 0.194151 -0.098017 0.976062 -v 0.195091 -0.000000 0.980784 -v 0.194151 0.098017 0.976062 -v 0.191342 0.195090 0.961939 -v 0.186690 0.290285 0.938552 -v 0.180240 0.382683 0.906127 -v 0.172054 0.471397 0.864975 -v 0.162212 0.555570 0.815493 -v 0.150807 0.634393 0.758157 -v 0.137950 0.707107 0.693520 -v 0.123764 0.773010 0.622203 -v 0.108386 0.831470 0.544895 -v 0.091965 0.881921 0.462339 -v 0.074658 0.923880 0.375330 -v 0.056632 0.956940 0.284707 -v 0.038060 0.980785 0.191341 -v 0.019122 0.995185 0.096134 -v 0.037510 0.995185 0.090556 -v 0.074658 0.980785 0.180240 -v 0.111087 0.956940 0.268188 -v 0.146447 0.923880 0.353553 -v 0.180396 0.881921 0.435513 -v 0.212607 0.831470 0.513279 -v 0.242772 0.773010 0.586102 -v 0.270598 0.707107 0.653281 -v 0.295818 0.634393 0.714168 -v 0.318190 0.555570 0.768177 -v 0.337496 0.471397 0.814788 -v 0.353553 0.382683 0.853553 -v 0.366205 0.290285 0.884097 -v 0.375330 0.195090 0.906127 -v 0.380840 0.098017 0.919430 -v 0.382683 -0.000000 0.923878 -v 0.380840 -0.098017 0.919430 -v 0.375330 -0.195090 0.906127 -v 0.366205 -0.290285 0.884097 -v 0.353553 -0.382684 0.853553 -v 0.337496 -0.471397 0.814788 -v 0.318190 -0.555571 0.768177 -v 0.295818 -0.634394 0.714168 -v 0.270598 -0.707107 0.653281 -v 0.242772 -0.773011 0.586102 -v 0.212607 -0.831470 0.513279 -v 0.180395 -0.881922 0.435513 -v 0.146446 -0.923880 0.353553 -v 0.111087 -0.956941 0.268187 -v 0.074658 -0.980785 0.180239 -v 0.037509 -0.995185 0.090555 -v 0.054455 -0.995185 0.081498 -v 0.108386 -0.980785 0.162211 -v 0.161273 -0.956941 0.241362 -v 0.212607 -0.923880 0.318189 -v 0.261894 -0.881922 0.391951 -v 0.308658 -0.831470 0.461939 -v 0.352450 -0.773011 0.527478 -v 0.392847 -0.707107 0.587937 -v 0.429461 -0.634394 0.642734 -v 0.461940 -0.555571 0.691341 -v 0.489969 -0.471397 0.733290 -v 0.513280 -0.382684 0.768177 -v 0.531647 -0.290285 0.795666 -v 0.544895 -0.195091 0.815493 -v 0.552895 -0.098017 0.827465 -v 0.555570 -0.000000 0.831468 -v 0.552895 0.098017 0.827465 -v 0.544895 0.195090 0.815493 -v 0.531647 0.290285 0.795666 -v 0.513280 0.382683 0.768177 -v 0.489969 0.471397 0.733290 -v 0.461940 0.555570 0.691341 -v 0.429462 0.634393 0.642734 -v 0.392847 0.707107 0.587938 -v 0.352450 0.773010 0.527478 -v 0.308658 0.831470 0.461939 -v 0.261894 0.881921 0.391952 -v 0.212607 0.923880 0.318189 -v 0.161273 0.956940 0.241363 -v 0.108386 0.980785 0.162211 -v 0.054455 0.995185 0.081498 -v 0.069309 0.995185 0.069308 -v 0.137950 0.980785 0.137949 -v 0.205262 0.956940 0.205262 -v 0.270598 0.923880 0.270598 -v 0.333328 0.881921 0.333327 -v 0.392847 0.831470 0.392847 -v 0.448583 0.773010 0.448583 -v 0.500000 0.707107 0.500000 -v 0.546601 0.634393 0.546601 -v 0.587938 0.555570 0.587937 -v 0.623612 0.471397 0.623612 -v 0.653281 0.382683 0.653281 -v 0.676659 0.290285 0.676658 -v 0.693520 0.195090 0.693519 -v 0.703701 0.098017 0.703701 -v 0.707106 -0.000000 0.707106 -v 0.703701 -0.098017 0.703701 -v 0.693520 -0.195091 0.693519 -v 0.676659 -0.290285 0.676659 -v 0.653281 -0.382684 0.653281 -v 0.623612 -0.471397 0.623612 -v 0.587937 -0.555571 0.587937 -v 0.546601 -0.634394 0.546600 -v 0.500000 -0.707107 0.499999 -v 0.448583 -0.773011 0.448583 -v 0.392847 -0.831470 0.392847 -v 0.333327 -0.881922 0.333327 -v 0.270597 -0.923880 0.270597 -v 0.205262 -0.956941 0.205262 -v 0.137949 -0.980785 0.137949 -v 0.069308 -0.995185 0.069308 -v 0.081498 -0.995185 0.054455 -v 0.162211 -0.980785 0.108386 -v 0.241362 -0.956941 0.161273 -v 0.318189 -0.923880 0.212607 -v 0.391951 -0.881922 0.261893 -v 0.461939 -0.831470 0.308658 -v 0.527478 -0.773011 0.352449 -v 0.587937 -0.707107 0.392847 -v 0.642734 -0.634394 0.429461 -v 0.691341 -0.555571 0.461939 -v 0.733290 -0.471397 0.489969 -v 0.768177 -0.382684 0.513279 -v 0.795666 -0.290285 0.531647 -v 0.815493 -0.195091 0.544895 -v 0.827465 -0.098017 0.552894 -v 0.831469 -0.000000 0.555569 -v 0.827465 0.098017 0.552894 -v 0.815493 0.195090 0.544895 -v 0.795666 0.290285 0.531647 -v 0.768177 0.382683 0.513280 -v 0.733290 0.471397 0.489969 -v 0.691341 0.555570 0.461939 -v 0.642734 0.634393 0.429461 -v 0.587938 0.707107 0.392847 -v 0.527478 0.773010 0.352450 -v 0.461939 0.831470 0.308658 -v 0.391952 0.881921 0.261894 -v 0.318190 0.923880 0.212607 -v 0.241363 0.956940 0.161273 -v 0.162212 0.980785 0.108386 -v 0.081498 0.995185 0.054455 -v 0.090556 0.995185 0.037509 -v 0.180240 0.980785 0.074658 -v 0.268188 0.956940 0.111087 -v 0.353553 0.923880 0.146446 -v 0.435513 0.881921 0.180395 -v 0.513279 0.831470 0.212607 -v 0.586102 0.773010 0.242771 -v 0.653281 0.707107 0.270598 -v 0.714168 0.634393 0.295818 -v 0.768177 0.555570 0.318189 -v 0.814788 0.471397 0.337496 -v 0.853553 0.382683 0.353553 -v 0.884097 0.290285 0.366205 -v 0.906127 0.195090 0.375330 -v 0.919430 0.098017 0.380840 -v 0.923879 -0.000000 0.382683 -v 0.919430 -0.098017 0.380840 -v 0.906127 -0.195091 0.375330 -v 0.884097 -0.290285 0.366205 -v 0.853553 -0.382684 0.353553 -v 0.814788 -0.471397 0.337496 -v 0.768177 -0.555571 0.318189 -v 0.714168 -0.634394 0.295818 -v 0.653281 -0.707107 0.270598 -v 0.586102 -0.773011 0.242771 -v 0.513279 -0.831470 0.212607 -v 0.435513 -0.881922 0.180395 -v 0.353552 -0.923880 0.146446 -v 0.268187 -0.956941 0.111087 -v 0.180239 -0.980785 0.074657 -v 0.090555 -0.995185 0.037509 -v 0.096133 -0.995185 0.019122 -v 0.191341 -0.980785 0.038060 -v 0.284706 -0.956941 0.056632 -v 0.375329 -0.923880 0.074658 -v 0.462338 -0.881922 0.091965 -v 0.544894 -0.831470 0.108386 -v 0.622203 -0.773011 0.123764 -v 0.693519 -0.707107 0.137949 -v 0.758157 -0.634394 0.150806 -v 0.815492 -0.555571 0.162211 -v 0.864975 -0.471397 0.172054 -v 0.906127 -0.382684 0.180240 -v 0.938552 -0.290285 0.186690 -v 0.961939 -0.195091 0.191341 -v 0.976061 -0.098017 0.194151 -v 0.980784 -0.000000 0.195090 -v 0.976061 0.098017 0.194151 -v 0.961939 0.195090 0.191341 -v 0.938552 0.290285 0.186689 -v 0.906127 0.382683 0.180240 -v 0.864975 0.471397 0.172054 -v 0.815493 0.555570 0.162211 -v 0.758157 0.634393 0.150807 -v 0.693520 0.707107 0.137950 -v 0.622203 0.773010 0.123764 -v 0.544895 0.831470 0.108386 -v 0.462339 0.881921 0.091965 -v 0.375330 0.923880 0.074658 -v 0.284707 0.956940 0.056632 -v 0.191342 0.980785 0.038060 -v 0.096134 0.995185 0.019122 -v 0.195090 0.980785 -0.000000 -v 0.290284 0.956940 -0.000000 -v 0.382683 0.923880 -0.000000 -v 0.471396 0.881921 -0.000000 -v 0.555570 0.831470 -0.000000 -v 0.634393 0.773010 -0.000000 -v 0.707107 0.707107 -0.000000 -v 0.831469 0.555570 -0.000000 -v 0.881920 0.471397 -0.000000 -v 0.923879 0.382683 -0.000000 -v 0.956940 0.290285 -0.000000 -v 0.980785 0.195090 -0.000000 -v 0.995184 0.098017 -0.000000 -v 0.999999 -0.000000 -0.000000 -v 0.995184 -0.098017 -0.000000 -v 0.980785 -0.195091 -0.000000 -v 0.956940 -0.290285 -0.000000 -v 0.923879 -0.382684 -0.000000 -v 0.881920 -0.471397 -0.000000 -v 0.831469 -0.555571 -0.000000 -v 0.773010 -0.634394 -0.000000 -v 0.707106 -0.707107 -0.000000 -v 0.634392 -0.773011 -0.000000 -v 0.555569 -0.831470 -0.000000 -v 0.471396 -0.881922 -0.000000 -v 0.382682 -0.923880 -0.000000 -v 0.290284 -0.956941 -0.000000 -v 0.195089 -0.980785 -0.000000 -v 0.098016 -0.995185 0.000000 -vn 0.000000 -1.000000 0.000000 -vn 0.112094 -0.993439 -0.022279 -vn 0.097995 -0.995178 0.000000 -vn 0.106265 0.994140 -0.018189 -vn 0.000000 1.000000 0.000000 -vn 0.107761 0.994140 0.002869 -vn 0.100681 0.994140 -0.038545 -vn 0.098453 -0.994140 -0.043916 -vn 0.087985 -0.994140 -0.062288 -vn 0.091220 0.994140 -0.057466 -vn 0.078249 0.994140 -0.074160 -vn 0.074160 -0.994140 -0.078249 -vn 0.057466 -0.994140 -0.091220 -vn 0.062288 0.994140 -0.087985 -vn 0.043916 0.994140 -0.098453 -vn 0.038545 -0.994140 -0.100681 -vn 0.018189 -0.994140 -0.106265 -vn 0.023865 0.994140 -0.105136 -vn 0.002869 0.994140 -0.107761 -vn -0.002869 -0.994140 -0.107761 -vn -0.023865 -0.994140 -0.105136 -vn -0.018189 0.994140 -0.106265 -vn -0.038545 0.994140 -0.100681 -vn -0.043916 -0.994140 -0.098453 -vn -0.062288 -0.994140 -0.087985 -vn -0.057466 0.994140 -0.091220 -vn -0.074160 0.994140 -0.078249 -vn -0.078249 -0.994140 -0.074160 -vn -0.091220 -0.994140 -0.057466 -vn -0.087985 0.994140 -0.062288 -vn -0.098453 0.994140 -0.043916 -vn -0.100681 -0.994140 -0.038545 -vn -0.106265 -0.994140 -0.018189 -vn -0.105136 0.994140 -0.023865 -vn -0.107761 0.994140 -0.002869 -vn -0.107761 -0.994140 0.002869 -vn -0.105136 -0.994140 0.023865 -vn -0.106265 0.994140 0.018189 -vn -0.100681 0.994140 0.038545 -vn -0.098453 -0.994140 0.043916 -vn -0.087985 -0.994140 0.062288 -vn -0.091220 0.994140 0.057466 -vn -0.078249 0.994140 0.074160 -vn -0.074160 -0.994140 0.078249 -vn -0.057466 -0.994140 0.091220 -vn -0.062288 0.994140 0.087985 -vn -0.043916 0.994140 0.098453 -vn -0.038545 -0.994140 0.100681 -vn -0.018189 -0.994140 0.106265 -vn -0.023865 0.994140 0.105136 -vn -0.002869 0.994140 0.107761 -vn 0.002869 -0.994140 0.107761 -vn 0.023865 -0.994140 0.105136 -vn 0.018189 0.994140 0.106265 -vn 0.038545 0.994140 0.100681 -vn 0.043916 -0.994140 0.098453 -vn 0.062288 -0.994140 0.087985 -vn 0.057466 0.994140 0.091220 -vn 0.074160 0.994140 0.078249 -vn 0.078249 -0.994140 0.074160 -vn 0.091220 -0.994140 0.057466 -vn 0.087985 0.994140 0.062288 -vn 0.098453 0.994140 0.043916 -vn 0.100681 -0.994140 0.038545 -vn 0.106265 -0.994140 0.018189 -vn 0.105136 0.994140 0.023865 -vn 0.178991 -0.983825 0.000000 -vn 0.191626 -0.980773 0.036500 -vn 0.274545 -0.961547 0.000000 -vn 0.284982 -0.956938 0.055116 -vn 0.367473 -0.930021 0.000000 -vn 0.375591 -0.923856 0.073183 -vn 0.456862 -0.889523 0.000000 -vn 0.462600 -0.881924 0.090548 -vn 0.541856 -0.840449 0.000000 -vn 0.545122 -0.831446 0.107059 -vn 0.621632 -0.783288 0.000000 -vn 0.622425 -0.773003 0.122532 -vn 0.702078 -0.711997 0.010407 -vn 0.688772 -0.706900 0.160649 -vn 0.762841 -0.634175 0.125858 -vn 0.766625 -0.641896 -0.014588 -vn 0.822230 -0.569109 0.000000 -vn 0.815638 -0.555559 0.161321 -vn 0.878506 -0.477523 0.012726 -vn 0.858943 -0.471206 0.200354 -vn 0.911618 -0.382488 0.150395 -vn 0.923429 -0.382488 -0.030305 -vn 0.956450 -0.290139 0.031373 -vn 0.931944 -0.290139 0.217383 -vn 0.967711 -0.194983 0.159673 -vn 0.980255 -0.194983 -0.032167 -vn 0.994629 -0.097934 0.032655 -vn 0.969146 -0.097934 0.226051 -vn 0.986633 0.000000 0.162786 -vn 0.999451 0.000000 -0.032807 -vn 0.994354 0.104984 0.014069 -vn 0.969146 0.097934 0.226051 -vn 0.967711 0.194983 0.159673 -vn 0.978637 0.204657 -0.019105 -vn 0.956938 0.290262 0.000458 -vn 0.938444 0.290262 0.187139 -vn 0.921079 0.389111 0.013245 -vn 0.899777 0.382488 0.209876 -vn 0.865108 0.471389 0.171300 -vn 0.874050 0.485763 0.000000 -vn 0.820490 0.555345 0.135380 -vn 0.825861 0.563646 -0.015839 -vn 0.773003 0.634388 0.001007 -vn 0.757927 0.634388 0.151799 -vn 0.707083 0.707114 0.001129 -vn 0.693289 0.707114 0.139042 -vn 0.628895 0.777398 0.009430 -vn 0.617969 0.772851 0.144139 -vn 0.548326 0.831324 0.090457 -vn 0.547319 0.836848 -0.010132 -vn 0.471358 0.881924 0.001404 -vn 0.462050 0.881924 0.093326 -vn 0.382672 0.923856 0.001465 -vn 0.375011 0.923856 0.076113 -vn 0.290262 0.956938 0.001526 -vn 0.284371 0.956938 0.058138 -vn 0.195074 0.980773 0.001556 -vn 0.191015 0.980773 0.039583 -vn 0.179601 0.980773 0.076113 -vn 0.267586 0.956938 0.112491 -vn 0.352947 0.923856 0.147801 -vn 0.434950 0.881924 0.181677 -vn 0.520157 0.831324 0.195715 -vn 0.577990 0.772851 0.261940 -vn 0.652821 0.707114 0.271615 -vn 0.713767 0.634388 0.296731 -vn 0.778314 0.555345 0.292856 -vn 0.815058 0.471389 0.336772 -vn 0.841548 0.382488 0.381390 -vn 0.883908 0.290262 0.366619 -vn 0.917966 0.194983 0.345378 -vn 0.906430 0.097934 0.410779 -vn 0.935911 0.000000 0.352153 -vn 0.906430 -0.097934 0.410779 -vn 0.917966 -0.194983 0.345378 -vn 0.871639 -0.290139 0.395032 -vn 0.864742 -0.382488 0.325358 -vn 0.803339 -0.471206 0.364086 -vn 0.768487 -0.555559 0.317362 -vn 0.723624 -0.634175 0.272256 -vn 0.644215 -0.706900 0.291940 -vn 0.586566 -0.773003 0.241615 -vn 0.513779 -0.831446 0.211341 -vn 0.436048 -0.881924 0.179052 -vn 0.354106 -0.923856 0.145054 -vn 0.268746 -0.956938 0.109653 -vn 0.180822 -0.980773 0.073183 -vn 0.163060 -0.980773 0.107059 -vn 0.242195 -0.956938 0.159978 -vn 0.318979 -0.923856 0.211341 -vn 0.392712 -0.881924 0.260689 -vn 0.462661 -0.831446 0.307535 -vn 0.528153 -0.773003 0.351390 -vn 0.574694 -0.713950 0.399915 -vn 0.645253 -0.639760 0.417524 -vn 0.691824 -0.555559 0.461196 -vn 0.719871 -0.479965 0.501358 -vn 0.773217 -0.389111 0.500687 -vn 0.777825 -0.290139 0.557482 -vn 0.832942 -0.194983 0.517838 -vn 0.808863 -0.097964 0.579730 -vn 0.849239 0.000000 0.527970 -vn 0.808863 0.097964 0.579730 -vn 0.832942 0.194983 0.517838 -vn 0.795404 0.290262 0.532029 -vn 0.750969 0.382488 0.538224 -vn 0.733696 0.471389 0.489334 -vn 0.706229 0.555345 0.439070 -vn 0.642140 0.634388 0.430280 -vn 0.587298 0.707114 0.393780 -vn 0.515763 0.772851 0.369671 -vn 0.471969 0.831324 0.293435 -vn 0.391156 0.881924 0.263039 -vn 0.317331 0.923856 0.213813 -vn 0.240486 0.956938 0.162542 -vn 0.161321 0.980773 0.109684 -vn 0.136814 0.980773 0.139042 -vn 0.204138 0.956938 0.206336 -vn 0.269539 0.923856 0.271615 -vn 0.332316 0.881924 0.334300 -vn 0.405652 0.831324 0.379864 -vn 0.433729 0.772851 0.463179 -vn 0.499161 0.707114 0.500778 -vn 0.545854 0.634388 0.547288 -vn 0.606983 0.555345 0.568407 -vn 0.624134 0.471389 0.623066 -vn 0.631519 0.382488 0.674398 -vn 0.676321 0.290262 0.676962 -vn 0.715903 0.194983 0.670400 -vn 0.680227 0.097964 0.726402 -vn 0.729911 0.000000 0.683523 -vn 0.680227 -0.097964 0.726402 -vn 0.715903 -0.194983 0.670400 -vn 0.654103 -0.290139 0.698508 -vn 0.653706 -0.382672 0.652821 -vn 0.624134 -0.471389 0.623066 -vn 0.588549 -0.555559 0.587298 -vn 0.562090 -0.626698 0.539689 -vn 0.496750 -0.702078 0.510147 -vn 0.463240 -0.766656 0.444502 -vn 0.391797 -0.827509 0.402051 -vn 0.334300 -0.881924 0.332316 -vn 0.271615 -0.923856 0.269539 -vn 0.206336 -0.956938 0.204138 -vn 0.139042 -0.980773 0.136814 -vn 0.109684 -0.980773 0.161321 -vn 0.162542 -0.956938 0.240486 -vn 0.213813 -0.923856 0.317331 -vn 0.263039 -0.881924 0.391156 -vn 0.295633 -0.836848 0.460707 -vn 0.357250 -0.777398 0.517655 -vn 0.373424 -0.706900 0.600665 -vn 0.450392 -0.634175 0.628407 -vn 0.462661 -0.555559 0.690817 -vn 0.481704 -0.465163 0.742668 -vn 0.530686 -0.373516 0.760796 -vn 0.505264 -0.290139 0.812708 -vn 0.571368 -0.194983 0.797174 -vn 0.525437 -0.097964 0.845149 -vn 0.582537 0.000000 0.812769 -vn 0.525437 0.097964 0.845149 -vn 0.571368 0.194983 0.797174 -vn 0.531236 0.290262 0.795923 -vn 0.487808 0.382488 0.784661 -vn 0.490585 0.471389 0.732841 -vn 0.484451 0.555345 0.675893 -vn 0.428602 0.634388 0.643269 -vn 0.391888 0.707114 0.588549 -vn 0.335032 0.772851 0.538896 -vn 0.323740 0.831324 0.451704 -vn 0.260689 0.881924 0.392712 -vn 0.211341 0.923856 0.319010 -vn 0.159978 0.956938 0.242195 -vn 0.107059 0.980773 0.163060 -vn 0.073183 0.980773 0.180822 -vn 0.109653 0.956938 0.268746 -vn 0.145054 0.923856 0.354106 -vn 0.179052 0.881924 0.436048 -vn 0.229408 0.831324 0.506180 -vn 0.223457 0.772851 0.593921 -vn 0.269539 0.707114 0.653706 -vn 0.294870 0.634388 0.714530 -vn 0.343272 0.555345 0.757439 -vn 0.338176 0.471389 0.814478 -vn 0.325358 0.382488 0.864742 -vn 0.365764 0.290262 0.884243 -vn 0.404859 0.194983 0.893338 -vn 0.350444 0.097964 0.931425 -vn 0.412763 0.000000 0.910794 -vn 0.350444 -0.097964 0.931425 -vn 0.404859 -0.194983 0.893338 -vn 0.336985 -0.290139 0.895657 -vn 0.364727 -0.389111 0.845882 -vn 0.320017 -0.479965 0.816797 -vn 0.319010 -0.555559 0.767815 -vn 0.319132 -0.634175 0.704215 -vn 0.249062 -0.706900 0.661977 -vn 0.257851 -0.766656 0.587939 -vn 0.208106 -0.827509 0.521378 -vn 0.181677 -0.881924 0.434950 -vn 0.147801 -0.923856 0.352947 -vn 0.112491 -0.956938 0.267586 -vn 0.076113 -0.980773 0.179601 -vn 0.039583 -0.980773 0.191015 -vn 0.058138 -0.956938 0.284371 -vn 0.076113 -0.923856 0.375011 -vn 0.093326 -0.881924 0.462050 -vn 0.096835 -0.836848 0.538774 -vn 0.131962 -0.777398 0.614978 -vn 0.115146 -0.706900 0.697836 -vn 0.175634 -0.634175 0.752953 -vn 0.163060 -0.555559 0.815302 -vn 0.172765 -0.471389 0.864803 -vn 0.180822 -0.382672 0.906003 -vn 0.155797 -0.290139 0.944212 -vn 0.222785 -0.194983 0.955138 -vn 0.161992 -0.097964 0.981903 -vn 0.227149 0.000000 0.973846 -vn 0.161992 0.097964 0.981903 -vn 0.222785 0.194983 0.955138 -vn 0.186224 0.290262 0.938627 -vn 0.150395 0.382488 0.911618 -vn 0.172765 0.471389 0.864803 -vn 0.188910 0.555345 0.809839 -vn 0.149785 0.634388 0.758324 -vn 0.136814 0.707114 0.693716 -vn 0.103305 0.772851 0.626118 -vn 0.126225 0.831324 0.541215 -vn 0.090548 0.881924 0.462600 -vn 0.073183 0.923856 0.375591 -vn 0.055116 0.956938 0.284982 -vn 0.036500 0.980773 0.191626 -vn -0.001556 0.980773 0.195074 -vn -0.001526 0.956938 0.290262 -vn -0.001465 0.923856 0.382672 -vn -0.001404 0.881924 0.471358 -vn 0.018220 0.831324 0.555467 -vn -0.020814 0.772851 0.634236 -vn -0.001129 0.707114 0.707083 -vn -0.001007 0.634388 0.772973 -vn 0.027284 0.555345 0.831141 -vn 0.000732 0.471389 0.881893 -vn -0.030305 0.382488 0.923429 -vn -0.000458 0.290262 0.956938 -vn 0.032167 0.194983 0.980255 -vn -0.032624 0.097964 0.994629 -vn 0.032807 0.000000 0.999451 -vn -0.032624 -0.097964 0.994629 -vn 0.032167 -0.194983 0.980255 -vn -0.031373 -0.290139 0.956450 -vn 0.000610 -0.382672 0.923856 -vn 0.000732 -0.471389 0.881893 -vn 0.000885 -0.555559 0.831446 -vn 0.025361 -0.634175 0.772729 -vn -0.023194 -0.706900 0.706900 -vn 0.001221 -0.773003 0.634358 -vn 0.001312 -0.831446 0.555559 -vn 0.001404 -0.881924 0.471358 -vn 0.001465 -0.923856 0.382672 -vn 0.001526 -0.956938 0.290262 -vn 0.001556 -0.980773 0.195074 -vn -0.036500 -0.980773 0.191626 -vn -0.055116 -0.956938 0.284982 -vn -0.073183 -0.923856 0.375591 -vn -0.090548 -0.881924 0.462600 -vn -0.107059 -0.831446 0.545122 -vn -0.122532 -0.773003 0.622425 -vn -0.160649 -0.706900 0.688772 -vn -0.125858 -0.634175 0.762841 -vn -0.161321 -0.555559 0.815638 -vn -0.171300 -0.471389 0.865108 -vn -0.179632 -0.382672 0.906217 -vn -0.217383 -0.290139 0.931944 -vn -0.159673 -0.194983 0.967711 -vn -0.226051 -0.097964 0.969146 -vn -0.162786 0.000000 0.986633 -vn -0.226051 0.097964 0.969146 -vn -0.159673 0.194983 0.967711 -vn -0.187139 0.290262 0.938444 -vn -0.209876 0.382488 0.899777 -vn -0.171300 0.471389 0.865108 -vn -0.135380 0.555345 0.820490 -vn -0.151799 0.634388 0.757927 -vn -0.139042 0.707114 0.693289 -vn -0.144139 0.772851 0.617969 -vn -0.090457 0.831324 0.548326 -vn -0.093326 0.881924 0.462050 -vn -0.076113 0.923856 0.375011 -vn -0.058138 0.956938 0.284371 -vn -0.039583 0.980773 0.191015 -vn -0.076113 0.980773 0.179601 -vn -0.112491 0.956938 0.267586 -vn -0.147801 0.923856 0.352947 -vn -0.181677 0.881924 0.434950 -vn -0.195715 0.831324 0.520157 -vn -0.261940 0.772851 0.577990 -vn -0.271615 0.707114 0.652821 -vn -0.296731 0.634388 0.713767 -vn -0.292856 0.555345 0.778314 -vn -0.336772 0.471389 0.815058 -vn -0.381390 0.382488 0.841548 -vn -0.366619 0.290262 0.883908 -vn -0.345378 0.194983 0.917966 -vn -0.410779 0.097964 0.906430 -vn -0.352153 0.000000 0.935911 -vn -0.410779 -0.097964 0.906430 -vn -0.345378 -0.194983 0.917966 -vn -0.395032 -0.290139 0.871639 -vn -0.352977 -0.382672 0.853755 -vn -0.336772 -0.471389 0.815058 -vn -0.317362 -0.555559 0.768487 -vn -0.272256 -0.634175 0.723624 -vn -0.291940 -0.706900 0.644215 -vn -0.241615 -0.773003 0.586566 -vn -0.211341 -0.831446 0.513779 -vn -0.179052 -0.881924 0.436048 -vn -0.145054 -0.923856 0.354106 -vn -0.109653 -0.956938 0.268746 -vn -0.073183 -0.980773 0.180822 -vn -0.107059 -0.980773 0.163060 -vn -0.159978 -0.956938 0.242195 -vn -0.211341 -0.923856 0.318979 -vn -0.260689 -0.881924 0.392712 -vn -0.307535 -0.831446 0.462661 -vn -0.351390 -0.773003 0.528153 -vn -0.399915 -0.713950 0.574694 -vn -0.417524 -0.639760 0.645253 -vn -0.461196 -0.555559 0.691824 -vn -0.489334 -0.471389 0.733696 -vn -0.512742 -0.382672 0.768487 -vn -0.557482 -0.290139 0.777825 -vn -0.517838 -0.194983 0.832942 -vn -0.579730 -0.097964 0.808863 -vn -0.527970 0.000000 0.849239 -vn -0.579730 0.097964 0.808863 -vn -0.517838 0.194983 0.832942 -vn -0.532029 0.290262 0.795404 -vn -0.538224 0.382488 0.750969 -vn -0.489334 0.471389 0.733696 -vn -0.439070 0.555345 0.706229 -vn -0.430280 0.634388 0.642140 -vn -0.393780 0.707114 0.587298 -vn -0.369671 0.772851 0.515763 -vn -0.293435 0.831324 0.471969 -vn -0.263039 0.881924 0.391156 -vn -0.213813 0.923856 0.317331 -vn -0.162542 0.956938 0.240486 -vn -0.109684 0.980773 0.161321 -vn -0.139042 0.980773 0.136814 -vn -0.206336 0.956938 0.204138 -vn -0.271615 0.923856 0.269539 -vn -0.334300 0.881924 0.332316 -vn -0.379864 0.831324 0.405652 -vn -0.463179 0.772851 0.433729 -vn -0.500778 0.707114 0.499161 -vn -0.547288 0.634388 0.545854 -vn -0.568407 0.555345 0.606983 -vn -0.623066 0.471389 0.624134 -vn -0.674398 0.382488 0.631519 -vn -0.676962 0.290262 0.676321 -vn -0.670400 0.194983 0.715903 -vn -0.726402 0.097964 0.680227 -vn -0.683523 0.000000 0.729911 -vn -0.726402 -0.097964 0.680227 -vn -0.670400 -0.194983 0.715903 -vn -0.698508 -0.290139 0.654103 -vn -0.652821 -0.382672 0.653706 -vn -0.623066 -0.471389 0.624134 -vn -0.587298 -0.555559 0.588549 -vn -0.539689 -0.626698 0.562090 -vn -0.510147 -0.702078 0.496750 -vn -0.447676 -0.773003 0.449446 -vn -0.391888 -0.831446 0.393780 -vn -0.332316 -0.881924 0.334300 -vn -0.269539 -0.923856 0.271615 -vn -0.204138 -0.956938 0.206336 -vn -0.136814 -0.980773 0.139042 -vn -0.161321 -0.980773 0.109684 -vn -0.240486 -0.956938 0.162542 -vn -0.317331 -0.923856 0.213813 -vn -0.391156 -0.881924 0.263039 -vn -0.461165 -0.831446 0.309763 -vn -0.526780 -0.773003 0.353465 -vn -0.600665 -0.706900 0.373424 -vn -0.628407 -0.634175 0.450392 -vn -0.690817 -0.555559 0.462661 -vn -0.732841 -0.471389 0.490585 -vn -0.767815 -0.382672 0.513779 -vn -0.812708 -0.290139 0.505264 -vn -0.797174 -0.194983 0.571368 -vn -0.845149 -0.097964 0.525437 -vn -0.812769 0.000000 0.582537 -vn -0.838984 0.088198 0.536912 -vn -0.808924 0.188177 0.556932 -vn -0.795923 0.290262 0.531236 -vn -0.784661 0.382488 0.487808 -vn -0.732841 0.471389 0.490585 -vn -0.675893 0.555345 0.484451 -vn -0.643269 0.634388 0.428602 -vn -0.588549 0.707114 0.391888 -vn -0.538896 0.772820 0.335032 -vn -0.451704 0.831324 0.323740 -vn -0.392712 0.881924 0.260689 -vn -0.318979 0.923856 0.211341 -vn -0.242195 0.956938 0.159978 -vn -0.163060 0.980773 0.107059 -vn -0.180822 0.980773 0.073183 -vn -0.268746 0.956938 0.109653 -vn -0.354106 0.923856 0.145054 -vn -0.436048 0.881924 0.179052 -vn -0.506180 0.831324 0.229408 -vn -0.593921 0.772820 0.223457 -vn -0.653706 0.707114 0.269539 -vn -0.714530 0.634388 0.294870 -vn -0.757439 0.555345 0.343272 -vn -0.814478 0.471389 0.338176 -vn -0.864742 0.382488 0.325358 -vn -0.884243 0.290262 0.365764 -vn -0.906217 0.195074 0.375042 -vn -0.919462 0.097995 0.380688 -vn -0.910794 0.000000 0.412763 -vn -0.931425 -0.097964 0.350444 -vn -0.893338 -0.194983 0.404859 -vn -0.895657 -0.290139 0.336985 -vn -0.853298 -0.382672 0.354106 -vn -0.814478 -0.471389 0.338176 -vn -0.767815 -0.555559 0.318979 -vn -0.704215 -0.634175 0.319132 -vn -0.661977 -0.706900 0.249062 -vn -0.585589 -0.773003 0.243904 -vn -0.512742 -0.831446 0.213813 -vn -0.434950 -0.881924 0.181677 -vn -0.352947 -0.923856 0.147801 -vn -0.267586 -0.956938 0.112491 -vn -0.179601 -0.980773 0.076113 -vn -0.191015 -0.980773 0.039583 -vn -0.284371 -0.956938 0.058138 -vn -0.375011 -0.923856 0.076113 -vn -0.462050 -0.881924 0.093326 -vn -0.544603 -0.831446 0.109684 -vn -0.621937 -0.773003 0.124973 -vn -0.697836 -0.706900 0.115146 -vn -0.752953 -0.634175 0.175634 -vn -0.815302 -0.555559 0.163060 -vn -0.864803 -0.471389 0.172765 -vn -0.906003 -0.382672 0.180822 -vn -0.944212 -0.290139 0.155797 -vn -0.955138 -0.194983 0.222785 -vn -0.981903 -0.097964 0.161992 -vn -0.973846 0.000000 0.227149 -vn -0.976074 0.097995 0.193976 -vn -0.961974 0.195074 0.191015 -vn -0.938627 0.290262 0.186224 -vn -0.911618 0.382488 0.150395 -vn -0.864803 0.471389 0.172765 -vn -0.809839 0.555345 0.188910 -vn -0.758324 0.634388 0.149785 -vn -0.693716 0.707114 0.136814 -vn -0.626118 0.772820 0.103305 -vn -0.541215 0.831324 0.126225 -vn -0.462600 0.881924 0.090548 -vn -0.375591 0.923856 0.073183 -vn -0.284982 0.956938 0.055116 -vn -0.191626 0.980773 0.036500 -vn -0.195074 0.980773 -0.001556 -vn -0.290262 0.956938 -0.001526 -vn -0.382672 0.923856 -0.001465 -vn -0.471358 0.881924 -0.001404 -vn -0.555467 0.831324 0.018220 -vn -0.634236 0.772820 -0.020814 -vn -0.707083 0.707114 -0.001129 -vn -0.772973 0.634388 -0.001007 -vn -0.831141 0.555345 0.027284 -vn -0.885128 0.465163 -0.012055 -vn -0.927427 0.373516 -0.018555 -vn -0.956938 0.290262 -0.000458 -vn -0.980773 0.195074 -0.000305 -vn -0.995178 0.097995 -0.000153 -vn -0.999451 0.000000 0.032807 -vn -0.994629 -0.097964 -0.032624 -vn -0.980255 -0.194983 0.032167 -vn -0.956450 -0.290139 -0.031373 -vn -0.923856 -0.382672 0.000610 -vn -0.881893 -0.471389 0.000732 -vn -0.831446 -0.555559 0.000885 -vn -0.772729 -0.634175 0.025361 -vn -0.706900 -0.706900 -0.023194 -vn -0.634358 -0.773003 0.001221 -vn -0.555559 -0.831446 0.001312 -vn -0.471358 -0.881924 0.001404 -vn -0.382672 -0.923856 0.001465 -vn -0.290262 -0.956938 0.001526 -vn -0.195074 -0.980773 0.001556 -vn -0.191626 -0.980773 -0.036500 -vn -0.284982 -0.956938 -0.055116 -vn -0.375591 -0.923856 -0.073183 -vn -0.462600 -0.881924 -0.090548 -vn -0.545122 -0.831446 -0.107059 -vn -0.622425 -0.773003 -0.122532 -vn -0.688772 -0.706900 -0.160649 -vn -0.762841 -0.634175 -0.125858 -vn -0.815638 -0.555559 -0.161321 -vn -0.865108 -0.471389 -0.171300 -vn -0.906217 -0.382672 -0.179632 -vn -0.931944 -0.290139 -0.217383 -vn -0.967711 -0.194983 -0.159673 -vn -0.969146 -0.097964 -0.226051 -vn -0.986633 0.000000 -0.162786 -vn -0.976012 0.097995 -0.194281 -vn -0.961852 0.195074 -0.191626 -vn -0.938444 0.290262 -0.187139 -vn -0.906003 0.382672 -0.180822 -vn -0.858943 0.471206 -0.200354 -vn -0.820490 0.555345 -0.135380 -vn -0.757927 0.634388 -0.151799 -vn -0.693289 0.707114 -0.139042 -vn -0.617969 0.772820 -0.144139 -vn -0.548326 0.831324 -0.090457 -vn -0.462050 0.881924 -0.093326 -vn -0.375011 0.923856 -0.076113 -vn -0.284371 0.956938 -0.058138 -vn -0.191015 0.980773 -0.039583 -vn -0.179601 0.980773 -0.076113 -vn -0.267586 0.956938 -0.112491 -vn -0.352947 0.923856 -0.147801 -vn -0.434950 0.881924 -0.181677 -vn -0.520157 0.831324 -0.195715 -vn -0.577990 0.772820 -0.261940 -vn -0.652821 0.707114 -0.271615 -vn -0.713767 0.634388 -0.296731 -vn -0.778314 0.555345 -0.292856 -vn -0.803339 0.471206 -0.364086 -vn -0.853298 0.382672 -0.354106 -vn -0.883908 0.290262 -0.366619 -vn -0.906003 0.195074 -0.375591 -vn -0.919370 0.097995 -0.380963 -vn -0.935911 0.000000 -0.352153 -vn -0.906430 -0.097964 -0.410779 -vn -0.917966 -0.194983 -0.345378 -vn -0.871639 -0.290139 -0.395032 -vn -0.853755 -0.382672 -0.352977 -vn -0.815058 -0.471389 -0.336772 -vn -0.768487 -0.555559 -0.317362 -vn -0.723624 -0.634175 -0.272256 -vn -0.644215 -0.706900 -0.291940 -vn -0.586566 -0.773003 -0.241615 -vn -0.513779 -0.831446 -0.211341 -vn -0.436048 -0.881924 -0.179052 -vn -0.354106 -0.923856 -0.145054 -vn -0.268746 -0.956938 -0.109653 -vn -0.180822 -0.980773 -0.073183 -vn -0.163060 -0.980773 -0.107059 -vn -0.242195 -0.956938 -0.159978 -vn -0.318979 -0.923856 -0.211341 -vn -0.392712 -0.881924 -0.260689 -vn -0.462661 -0.831446 -0.307535 -vn -0.528153 -0.773003 -0.351390 -vn -0.574694 -0.713950 -0.399915 -vn -0.645253 -0.639760 -0.417524 -vn -0.691824 -0.555559 -0.461196 -vn -0.733696 -0.471389 -0.489334 -vn -0.768487 -0.382672 -0.512742 -vn -0.777825 -0.290139 -0.557482 -vn -0.832942 -0.194983 -0.517838 -vn -0.808863 -0.097964 -0.579730 -vn -0.849239 0.000000 -0.527970 -vn -0.827357 0.097995 -0.552995 -vn -0.815302 0.195074 -0.545152 -vn -0.795404 0.290262 -0.532029 -vn -0.767815 0.382672 -0.513779 -vn -0.716880 0.471206 -0.513810 -vn -0.706229 0.555345 -0.439070 -vn -0.642140 0.634388 -0.430280 -vn -0.587298 0.707114 -0.393780 -vn -0.515763 0.772820 -0.369671 -vn -0.471969 0.831324 -0.293435 -vn -0.391156 0.881924 -0.263039 -vn -0.317331 0.923856 -0.213813 -vn -0.240486 0.956938 -0.162542 -vn -0.161321 0.980773 -0.109684 -vn -0.136814 0.980773 -0.139042 -vn -0.204138 0.956938 -0.206336 -vn -0.269539 0.923856 -0.271615 -vn -0.332316 0.881924 -0.334300 -vn -0.405652 0.831324 -0.379864 -vn -0.433729 0.772820 -0.463179 -vn -0.499161 0.707114 -0.500778 -vn -0.545854 0.634388 -0.547288 -vn -0.606983 0.555345 -0.568407 -vn -0.602863 0.471206 -0.643788 -vn -0.652821 0.382672 -0.653706 -vn -0.676321 0.290262 -0.676962 -vn -0.693289 0.195074 -0.693716 -vn -0.703574 0.097995 -0.703787 -vn -0.729911 0.000000 -0.683523 -vn -0.680227 -0.097964 -0.726402 -vn -0.715903 -0.194983 -0.670400 -vn -0.654103 -0.290139 -0.698508 -vn -0.653706 -0.382672 -0.652821 -vn -0.624134 -0.471389 -0.623066 -vn -0.588549 -0.555559 -0.587298 -vn -0.547288 -0.634388 -0.545854 -vn -0.500778 -0.707114 -0.499161 -vn -0.449446 -0.773003 -0.447676 -vn -0.393780 -0.831446 -0.391888 -vn -0.334300 -0.881924 -0.332316 -vn -0.271615 -0.923856 -0.269539 -vn -0.206336 -0.956938 -0.204138 -vn -0.139042 -0.980773 -0.136814 -vn -0.109684 -0.980773 -0.161321 -vn -0.162542 -0.956938 -0.240486 -vn -0.213813 -0.923856 -0.317331 -vn -0.263039 -0.881924 -0.391125 -vn -0.309763 -0.831446 -0.461165 -vn -0.353465 -0.773003 -0.526780 -vn -0.393780 -0.707114 -0.587298 -vn -0.430280 -0.634388 -0.642140 -vn -0.462661 -0.555559 -0.690817 -vn -0.490585 -0.471389 -0.732841 -vn -0.513779 -0.382672 -0.767815 -vn -0.505264 -0.290139 -0.812708 -vn -0.571368 -0.194983 -0.797174 -vn -0.525437 -0.097964 -0.845149 -vn -0.582537 0.000000 -0.812769 -vn -0.540727 0.104984 -0.834590 -vn -0.559587 0.204657 -0.803064 -vn -0.531236 0.290262 -0.795923 -vn -0.512742 0.382672 -0.768487 -vn -0.465682 0.471206 -0.749046 -vn -0.484451 0.555345 -0.675893 -vn -0.428602 0.634388 -0.643269 -vn -0.391888 0.707114 -0.588549 -vn -0.345592 0.766656 -0.541063 -vn -0.317881 0.827509 -0.462722 -vn -0.260689 0.881924 -0.392712 -vn -0.211341 0.923856 -0.318979 -vn -0.159978 0.956938 -0.242195 -vn -0.107059 0.980773 -0.163060 -vn -0.073183 0.980773 -0.180822 -vn -0.109653 0.956938 -0.268746 -vn -0.145054 0.923856 -0.354106 -vn -0.179052 0.881924 -0.436048 -vn -0.211341 0.831446 -0.513779 -vn -0.241615 0.773003 -0.586566 -vn -0.269539 0.707114 -0.653706 -vn -0.294870 0.634388 -0.714530 -vn -0.343272 0.555345 -0.757439 -vn -0.320017 0.479965 -0.816797 -vn -0.340220 0.389111 -0.856044 -vn -0.365764 0.290262 -0.884243 -vn -0.404859 0.194983 -0.893338 -vn -0.350444 0.097964 -0.931425 -vn -0.412763 0.000000 -0.910825 -vn -0.350444 -0.097964 -0.931425 -vn -0.404859 -0.194983 -0.893338 -vn -0.336985 -0.290139 -0.895657 -vn -0.354106 -0.382672 -0.853298 -vn -0.338176 -0.471389 -0.814478 -vn -0.318979 -0.555559 -0.767815 -vn -0.296731 -0.634388 -0.713767 -vn -0.271615 -0.707114 -0.652821 -vn -0.243904 -0.773003 -0.585589 -vn -0.213813 -0.831446 -0.512742 -vn -0.181677 -0.881924 -0.434950 -vn -0.147801 -0.923856 -0.352947 -vn -0.112491 -0.956938 -0.267586 -vn -0.076113 -0.980773 -0.179601 -vn -0.039583 -0.980773 -0.191015 -vn -0.058138 -0.956938 -0.284371 -vn -0.076113 -0.923856 -0.375011 -vn -0.093326 -0.881924 -0.462050 -vn -0.109684 -0.831446 -0.544603 -vn -0.124973 -0.773003 -0.621937 -vn -0.139042 -0.707114 -0.693258 -vn -0.151799 -0.634388 -0.757927 -vn -0.163060 -0.555559 -0.815302 -vn -0.172765 -0.471389 -0.864803 -vn -0.180822 -0.382672 -0.906003 -vn -0.155797 -0.290139 -0.944212 -vn -0.222785 -0.194983 -0.955138 -vn -0.161992 -0.097964 -0.981903 -vn -0.227149 0.000000 -0.973846 -vn -0.161992 0.097964 -0.981903 -vn -0.222785 0.194983 -0.955138 -vn -0.186224 0.290262 -0.938627 -vn -0.150395 0.382488 -0.911618 -vn -0.172765 0.471389 -0.864803 -vn -0.188910 0.555345 -0.809839 -vn -0.149785 0.634388 -0.758324 -vn -0.136814 0.707114 -0.693716 -vn -0.122532 0.773003 -0.622425 -vn -0.107059 0.831446 -0.545122 -vn -0.090548 0.881924 -0.462600 -vn -0.073183 0.923856 -0.375591 -vn -0.055116 0.956938 -0.284982 -vn -0.036500 0.980773 -0.191626 -vn 0.001556 0.980773 -0.195074 -vn 0.001526 0.956938 -0.290262 -vn 0.001465 0.923856 -0.382672 -vn 0.001404 0.881924 -0.471358 -vn 0.001312 0.831446 -0.555559 -vn 0.001221 0.773003 -0.634358 -vn 0.001129 0.707114 -0.707083 -vn 0.001007 0.634388 -0.772973 -vn -0.011292 0.549669 -0.835261 -vn -0.017792 0.462630 -0.886349 -vn 0.030305 0.382488 -0.923429 -vn 0.000458 0.290262 -0.956938 -vn -0.032167 0.194983 -0.980255 -vn 0.032624 0.097964 -0.994629 -vn -0.032807 0.000000 -0.999451 -vn 0.032624 -0.097964 -0.994629 -vn -0.032167 -0.194983 -0.980255 -vn 0.031373 -0.290139 -0.956450 -vn -0.000610 -0.382672 -0.923856 -vn -0.000732 -0.471389 -0.881893 -vn -0.000885 -0.555559 -0.831446 -vn -0.001007 -0.634388 -0.772973 -vn -0.001129 -0.707114 -0.707083 -vn -0.001221 -0.773003 -0.634358 -vn -0.001312 -0.831446 -0.555559 -vn -0.001404 -0.881924 -0.471358 -vn -0.001465 -0.923856 -0.382672 -vn -0.001526 -0.956938 -0.290262 -vn -0.001556 -0.980773 -0.195074 -vn 0.036500 -0.980773 -0.191626 -vn 0.055116 -0.956938 -0.284982 -vn 0.073183 -0.923856 -0.375591 -vn 0.090548 -0.881924 -0.462600 -vn 0.107059 -0.831446 -0.545122 -vn 0.122532 -0.773003 -0.622425 -vn 0.136814 -0.707114 -0.693716 -vn 0.149785 -0.634388 -0.758324 -vn 0.161321 -0.555559 -0.815638 -vn 0.171300 -0.471389 -0.865108 -vn 0.179632 -0.382672 -0.906217 -vn 0.217383 -0.290139 -0.931944 -vn 0.159673 -0.194983 -0.967711 -vn 0.226051 -0.097964 -0.969146 -vn 0.162786 0.000000 -0.986633 -vn 0.226051 0.097964 -0.969146 -vn 0.159673 0.194983 -0.967711 -vn 0.187139 0.290262 -0.938444 -vn 0.199133 0.373516 -0.905972 -vn 0.172033 0.471389 -0.864956 -vn 0.145573 0.563646 -0.813074 -vn 0.151799 0.634388 -0.757927 -vn 0.139042 0.707114 -0.693289 -vn 0.124973 0.773003 -0.621937 -vn 0.109684 0.831446 -0.544603 -vn 0.093326 0.881924 -0.462050 -vn 0.076113 0.923856 -0.375011 -vn 0.058138 0.956938 -0.284371 -vn 0.039583 0.980773 -0.191015 -vn 0.076113 0.980773 -0.179601 -vn 0.112491 0.956938 -0.267586 -vn 0.147801 0.923856 -0.352947 -vn 0.181677 0.881924 -0.434950 -vn 0.213813 0.831446 -0.512742 -vn 0.243904 0.773003 -0.585589 -vn 0.271615 0.707114 -0.652821 -vn 0.296731 0.634388 -0.713767 -vn 0.292856 0.555345 -0.778314 -vn 0.351268 0.479965 -0.803858 -vn 0.364727 0.389111 -0.845882 -vn 0.366619 0.290262 -0.883908 -vn 0.345378 0.194983 -0.917966 -vn 0.410779 0.097964 -0.906430 -vn 0.352153 0.000000 -0.935911 -vn 0.410779 -0.097964 -0.906430 -vn 0.345378 -0.194983 -0.917966 -vn 0.395032 -0.290139 -0.871639 -vn 0.352977 -0.382672 -0.853755 -vn 0.336772 -0.471389 -0.815058 -vn 0.317362 -0.555559 -0.768487 -vn 0.294870 -0.634388 -0.714530 -vn 0.269539 -0.707114 -0.653706 -vn 0.241615 -0.773003 -0.586566 -vn 0.211341 -0.831446 -0.513779 -vn 0.179052 -0.881924 -0.436018 -vn 0.145054 -0.923856 -0.354106 -vn 0.109653 -0.956938 -0.268746 -vn 0.073183 -0.980773 -0.180822 -vn 0.107059 -0.980773 -0.163060 -vn 0.159978 -0.956938 -0.242195 -vn 0.211341 -0.923856 -0.318979 -vn 0.260689 -0.881924 -0.392712 -vn 0.307535 -0.831446 -0.462661 -vn 0.351390 -0.773003 -0.528153 -vn 0.391888 -0.707114 -0.588549 -vn 0.428602 -0.634388 -0.643269 -vn 0.461196 -0.555559 -0.691824 -vn 0.489334 -0.471389 -0.733696 -vn 0.512742 -0.382672 -0.768487 -vn 0.557482 -0.290139 -0.777825 -vn 0.517838 -0.194983 -0.832942 -vn 0.579730 -0.097964 -0.808863 -vn 0.527970 0.000000 -0.849239 -vn 0.579730 0.097964 -0.808863 -vn 0.517838 0.194983 -0.832942 -vn 0.532029 0.290262 -0.795404 -vn 0.538224 0.382488 -0.750969 -vn 0.477615 0.462630 -0.746879 -vn 0.454634 0.549669 -0.700797 -vn 0.430280 0.634388 -0.642140 -vn 0.393780 0.707114 -0.587298 -vn 0.353465 0.773003 -0.526780 -vn 0.309763 0.831446 -0.461165 -vn 0.263039 0.881924 -0.391156 -vn 0.213813 0.923856 -0.317331 -vn 0.162542 0.956938 -0.240486 -vn 0.109684 0.980773 -0.161321 -vn 0.139042 0.980773 -0.136814 -vn 0.206336 0.956938 -0.204138 -vn 0.271615 0.923856 -0.269539 -vn 0.334300 0.881924 -0.332316 -vn 0.393780 0.831446 -0.391888 -vn 0.449446 0.773003 -0.447676 -vn 0.500778 0.707114 -0.499161 -vn 0.547288 0.634388 -0.545854 -vn 0.588549 0.555559 -0.587298 -vn 0.617328 0.465163 -0.634419 -vn 0.668905 0.373516 -0.642659 -vn 0.676962 0.290262 -0.676321 -vn 0.684713 0.188177 -0.704062 -vn 0.718131 0.088198 -0.690268 -vn 0.683523 0.000000 -0.729911 -vn 0.726402 -0.097964 -0.680227 -vn 0.682607 -0.201941 -0.702292 -vn 0.687613 -0.299600 -0.661336 -vn 0.652821 -0.382672 -0.653706 -vn 0.623066 -0.471389 -0.624134 -vn 0.587298 -0.555559 -0.588549 -vn 0.545854 -0.634388 -0.547288 -vn 0.499161 -0.707114 -0.500778 -vn 0.447676 -0.773003 -0.449446 -vn 0.391888 -0.831446 -0.393780 -vn 0.332316 -0.881924 -0.334300 -vn 0.269539 -0.923856 -0.271615 -vn 0.204138 -0.956938 -0.206336 -vn 0.136814 -0.980773 -0.139042 -vn 0.161321 -0.980773 -0.109684 -vn 0.240486 -0.956938 -0.162542 -vn 0.317331 -0.923856 -0.213813 -vn 0.391125 -0.881924 -0.263039 -vn 0.461165 -0.831446 -0.309763 -vn 0.526780 -0.773003 -0.353465 -vn 0.587298 -0.707114 -0.393780 -vn 0.642140 -0.634388 -0.430280 -vn 0.690817 -0.555559 -0.462661 -vn 0.732841 -0.471389 -0.490585 -vn 0.767815 -0.382672 -0.513779 -vn 0.795404 -0.290262 -0.532029 -vn 0.815302 -0.195074 -0.545152 -vn 0.837275 -0.107761 -0.535997 -vn 0.823542 -0.006989 -0.567186 -vn 0.827540 0.097995 -0.552751 -vn 0.815638 0.195074 -0.544633 -vn 0.795923 0.290262 -0.531236 -vn 0.768487 0.382672 -0.512742 -vn 0.733696 0.471389 -0.489334 -vn 0.691824 0.555559 -0.461196 -vn 0.643269 0.634388 -0.428602 -vn 0.588549 0.707114 -0.391888 -vn 0.528153 0.773003 -0.351390 -vn 0.462661 0.831446 -0.307535 -vn 0.392712 0.881924 -0.260689 -vn 0.318979 0.923856 -0.211341 -vn 0.242195 0.956938 -0.159978 -vn 0.163060 0.980773 -0.107059 -vn 0.180822 0.980773 -0.073183 -vn 0.268746 0.956938 -0.109653 -vn 0.354106 0.923856 -0.145054 -vn 0.436048 0.881924 -0.179052 -vn 0.513779 0.831446 -0.211341 -vn 0.586566 0.773003 -0.241615 -vn 0.653706 0.707114 -0.269539 -vn 0.714530 0.634388 -0.294870 -vn 0.768487 0.555559 -0.317362 -vn 0.815058 0.471389 -0.336772 -vn 0.853755 0.382672 -0.352977 -vn 0.884243 0.290262 -0.365764 -vn 0.906217 0.195074 -0.375042 -vn 0.919462 0.097995 -0.380688 -vn 0.923856 0.000000 -0.382672 -vn 0.919370 -0.097995 -0.380963 -vn 0.906003 -0.195074 -0.375591 -vn 0.883908 -0.290262 -0.366619 -vn 0.853298 -0.382672 -0.354106 -vn 0.814478 -0.471389 -0.338176 -vn 0.767815 -0.555559 -0.318979 -vn 0.713767 -0.634388 -0.296731 -vn 0.652821 -0.707114 -0.271615 -vn 0.585589 -0.773003 -0.243904 -vn 0.512742 -0.831446 -0.213813 -vn 0.434950 -0.881924 -0.181677 -vn 0.352947 -0.923856 -0.147801 -vn 0.267586 -0.956938 -0.112491 -vn 0.179601 -0.980773 -0.076113 -vn 0.207038 -0.977447 -0.041169 -vn 0.300027 -0.952055 -0.059664 -vn 0.390088 -0.917478 -0.077578 -vn 0.476424 -0.874081 -0.094760 -vn 0.558153 -0.822260 -0.110996 -vn 0.634510 -0.762505 -0.126194 -vn 0.704764 -0.695425 -0.140172 -vn 0.768212 -0.621632 -0.152806 -vn 0.824274 -0.541887 -0.163945 -vn 0.872402 -0.456893 -0.173528 -vn 0.905972 -0.373516 -0.199133 -vn 0.943022 -0.283517 -0.174047 -vn 0.959777 -0.185400 -0.210791 -vn 0.979308 -0.091006 -0.180578 -vn 0.976684 0.009796 -0.214331 -vn 0.976074 0.097995 -0.193976 -vn 0.961974 0.195074 -0.191015 -vn 0.938627 0.290262 -0.186224 -vn 0.906217 0.382672 -0.179632 -vn 0.865108 0.471389 -0.171300 -vn 0.815638 0.555559 -0.161321 -vn 0.758324 0.634388 -0.149785 -vn 0.693716 0.707114 -0.136814 -vn 0.622425 0.773003 -0.122532 -vn 0.545122 0.831446 -0.107059 -vn 0.462600 0.881924 -0.090548 -vn 0.375591 0.923856 -0.073183 -vn 0.284982 0.956938 -0.055116 -vn 0.191626 0.980773 -0.036500 -usemtl (null) -s 1 -f 128//1 4//2 994//3 -f 34//4 1//5 2//6 -f 35//7 1//5 34//4 -f 128//1 65//8 4//2 -f 128//1 66//9 65//8 -f 96//10 1//5 35//7 -f 97//11 1//5 96//10 -f 128//1 127//12 66//9 -f 128//1 129//13 127//12 -f 159//14 1//5 97//11 -f 160//15 1//5 159//14 -f 128//1 190//16 129//13 -f 128//1 191//17 190//16 -f 221//18 1//5 160//15 -f 222//19 1//5 221//18 -f 128//1 252//20 191//17 -f 128//1 253//21 252//20 -f 283//22 1//5 222//19 -f 284//23 1//5 283//22 -f 128//1 314//24 253//21 -f 128//1 315//25 314//24 -f 345//26 1//5 284//23 -f 346//27 1//5 345//26 -f 128//1 376//28 315//25 -f 128//1 377//29 376//28 -f 407//30 1//5 346//27 -f 408//31 1//5 407//30 -f 128//1 438//32 377//29 -f 128//1 439//33 438//32 -f 469//34 1//5 408//31 -f 470//35 1//5 469//34 -f 128//1 500//36 439//33 -f 128//1 501//37 500//36 -f 531//38 1//5 470//35 -f 532//39 1//5 531//38 -f 128//1 562//40 501//37 -f 128//1 563//41 562//40 -f 593//42 1//5 532//39 -f 594//43 1//5 593//42 -f 128//1 624//44 563//41 -f 128//1 625//45 624//44 -f 655//46 1//5 594//43 -f 656//47 1//5 655//46 -f 128//1 686//48 625//45 -f 128//1 687//49 686//48 -f 717//50 1//5 656//47 -f 718//51 1//5 717//50 -f 128//1 748//52 687//49 -f 128//1 749//53 748//52 -f 779//54 1//5 718//51 -f 780//55 1//5 779//54 -f 128//1 810//56 749//53 -f 128//1 811//57 810//56 -f 841//58 1//5 780//55 -f 842//59 1//5 841//58 -f 128//1 872//60 811//57 -f 128//1 873//61 872//60 -f 903//62 1//5 842//59 -f 904//63 1//5 903//62 -f 128//1 934//64 873//61 -f 128//1 935//65 934//64 -f 965//66 1//5 904//63 -f 2//6 1//5 965//66 -f 128//1 994//3 935//65 -f 935//65 994//3 993//67 -f 935//65 993//67 936//68 -f 936//68 993//67 992//69 -f 936//68 992//69 937//70 -f 937//70 992//69 991//71 -f 937//70 991//71 938//72 -f 938//72 991//71 990//73 -f 938//72 990//73 939//74 -f 939//74 990//73 989//75 -f 939//74 989//75 940//76 -f 940//76 989//75 988//77 -f 940//76 988//77 941//78 -f 941//78 988//77 987//79 -f 941//78 987//79 942//80 -f 942//80 987//79 943//81 -f 987//79 986//82 943//81 -f 943//81 986//82 985//83 -f 943//81 985//83 944//84 -f 944//84 985//83 984//85 -f 944//84 984//85 945//86 -f 945//86 984//85 946//87 -f 984//85 983//88 946//87 -f 946//87 983//88 982//89 -f 946//87 982//89 947//90 -f 947//90 982//89 948//91 -f 982//89 981//92 948//91 -f 948//91 981//92 980//93 -f 948//91 980//93 949//94 -f 949//94 980//93 950//95 -f 980//93 979//96 950//95 -f 950//95 979//96 978//97 -f 950//95 978//97 951//98 -f 951//98 978//97 952//99 -f 978//97 977//100 952//99 -f 952//99 977//100 976//101 -f 952//99 976//101 953//102 -f 953//102 976//101 975//103 -f 953//102 975//103 954//104 -f 954//104 975//103 955//105 -f 975//103 974//106 955//105 -f 955//105 974//106 956//107 -f 974//106 973//108 956//107 -f 956//107 973//108 3//109 -f 956//107 3//109 957//110 -f 957//110 3//109 972//111 -f 957//110 972//111 958//112 -f 958//112 972//111 971//113 -f 958//112 971//113 959//114 -f 959//114 971//113 960//115 -f 971//113 970//116 960//115 -f 960//115 970//116 969//117 -f 960//115 969//117 961//118 -f 961//118 969//117 968//119 -f 961//118 968//119 962//120 -f 962//120 968//119 967//121 -f 962//120 967//121 963//122 -f 963//122 967//121 966//123 -f 963//122 966//123 964//124 -f 964//124 966//123 2//6 -f 964//124 2//6 965//66 -f 905//125 964//124 965//66 -f 905//125 965//66 904//63 -f 906//126 963//122 964//124 -f 906//126 964//124 905//125 -f 907//127 962//120 963//122 -f 907//127 963//122 906//126 -f 908//128 961//118 962//120 -f 908//128 962//120 907//127 -f 909//129 960//115 961//118 -f 909//129 961//118 908//128 -f 910//130 959//114 909//129 -f 959//114 960//115 909//129 -f 911//131 958//112 959//114 -f 911//131 959//114 910//130 -f 912//132 957//110 958//112 -f 912//132 958//112 911//131 -f 913//133 956//107 957//110 -f 913//133 957//110 912//132 -f 914//134 955//105 913//133 -f 955//105 956//107 913//133 -f 915//135 954//104 914//134 -f 954//104 955//105 914//134 -f 916//136 953//102 954//104 -f 916//136 954//104 915//135 -f 917//137 952//99 953//102 -f 917//137 953//102 916//136 -f 918//138 951//98 917//137 -f 951//98 952//99 917//137 -f 919//139 950//95 951//98 -f 919//139 951//98 918//138 -f 920//140 949//94 919//139 -f 949//94 950//95 919//139 -f 921//141 948//91 949//94 -f 921//141 949//94 920//140 -f 922//142 947//90 921//141 -f 947//90 948//91 921//141 -f 923//143 946//87 947//90 -f 923//143 947//90 922//142 -f 924//144 945//86 923//143 -f 945//86 946//87 923//143 -f 925//145 944//84 945//86 -f 925//145 945//86 924//144 -f 926//146 943//81 944//84 -f 926//146 944//84 925//145 -f 927//147 942//80 926//146 -f 942//80 943//81 926//146 -f 928//148 941//78 942//80 -f 928//148 942//80 927//147 -f 929//149 940//76 941//78 -f 929//149 941//78 928//148 -f 930//150 939//74 940//76 -f 930//150 940//76 929//149 -f 931//151 938//72 939//74 -f 931//151 939//74 930//150 -f 932//152 937//70 938//72 -f 932//152 938//72 931//151 -f 933//153 936//68 937//70 -f 933//153 937//70 932//152 -f 934//64 935//65 936//68 -f 934//64 936//68 933//153 -f 873//61 934//64 933//153 -f 873//61 933//153 874//154 -f 874//154 933//153 932//152 -f 874//154 932//152 875//155 -f 875//155 932//152 931//151 -f 875//155 931//151 876//156 -f 876//156 931//151 930//150 -f 876//156 930//150 877//157 -f 877//157 930//150 929//149 -f 877//157 929//149 878//158 -f 878//158 929//149 928//148 -f 878//158 928//148 879//159 -f 879//159 928//148 927//147 -f 879//159 927//147 880//160 -f 880//160 927//147 881//161 -f 927//147 926//146 881//161 -f 881//161 926//146 925//145 -f 881//161 925//145 882//162 -f 882//162 925//145 924//144 -f 882//162 924//144 883//163 -f 883//163 924//144 884//164 -f 924//144 923//143 884//164 -f 884//164 923//143 922//142 -f 884//164 922//142 885//165 -f 885//165 922//142 886//166 -f 922//142 921//141 886//166 -f 886//166 921//141 920//140 -f 886//166 920//140 887//167 -f 887//167 920//140 888//168 -f 920//140 919//139 888//168 -f 888//168 919//139 918//138 -f 888//168 918//138 889//169 -f 889//169 918//138 890//170 -f 918//138 917//137 890//170 -f 890//170 917//137 916//136 -f 890//170 916//136 891//171 -f 891//171 916//136 915//135 -f 891//171 915//135 892//172 -f 892//172 915//135 893//173 -f 915//135 914//134 893//173 -f 893//173 914//134 894//174 -f 914//134 913//133 894//174 -f 894//174 913//133 912//132 -f 894//174 912//132 895//175 -f 895//175 912//132 911//131 -f 895//175 911//131 896//176 -f 896//176 911//131 910//130 -f 896//176 910//130 897//177 -f 897//177 910//130 898//178 -f 910//130 909//129 898//178 -f 898//178 909//129 908//128 -f 898//178 908//128 899//179 -f 899//179 908//128 907//127 -f 899//179 907//127 900//180 -f 900//180 907//127 906//126 -f 900//180 906//126 901//181 -f 901//181 906//126 905//125 -f 901//181 905//125 902//182 -f 902//182 905//125 904//63 -f 902//182 904//63 903//62 -f 843//183 902//182 903//62 -f 843//183 903//62 842//59 -f 844//184 901//181 902//182 -f 844//184 902//182 843//183 -f 845//185 900//180 901//181 -f 845//185 901//181 844//184 -f 846//186 899//179 900//180 -f 846//186 900//180 845//185 -f 847//187 898//178 899//179 -f 847//187 899//179 846//186 -f 848//188 897//177 847//187 -f 897//177 898//178 847//187 -f 849//189 896//176 897//177 -f 849//189 897//177 848//188 -f 850//190 895//175 896//176 -f 850//190 896//176 849//189 -f 851//191 894//174 895//175 -f 851//191 895//175 850//190 -f 852//192 893//173 851//191 -f 893//173 894//174 851//191 -f 853//193 892//172 852//192 -f 892//172 893//173 852//192 -f 854//194 891//171 892//172 -f 854//194 892//172 853//193 -f 855//195 890//170 891//171 -f 855//195 891//171 854//194 -f 856//196 889//169 855//195 -f 889//169 890//170 855//195 -f 857//197 888//168 889//169 -f 857//197 889//169 856//196 -f 858//198 887//167 857//197 -f 887//167 888//168 857//197 -f 859//199 886//166 887//167 -f 859//199 887//167 858//198 -f 860//200 885//165 859//199 -f 885//165 886//166 859//199 -f 861//201 884//164 885//165 -f 861//201 885//165 860//200 -f 862//202 883//163 884//164 -f 862//202 884//164 861//201 -f 863//203 882//162 883//163 -f 863//203 883//163 862//202 -f 864//204 881//161 882//162 -f 864//204 882//162 863//203 -f 865//205 880//160 881//161 -f 865//205 881//161 864//204 -f 866//206 879//159 880//160 -f 866//206 880//160 865//205 -f 867//207 878//158 879//159 -f 867//207 879//159 866//206 -f 868//208 877//157 878//158 -f 868//208 878//158 867//207 -f 869//209 876//156 877//157 -f 869//209 877//157 868//208 -f 870//210 875//155 876//156 -f 870//210 876//156 869//209 -f 871//211 874//154 875//155 -f 871//211 875//155 870//210 -f 872//60 873//61 874//154 -f 872//60 874//154 871//211 -f 811//57 872//60 871//211 -f 811//57 871//211 812//212 -f 812//212 871//211 870//210 -f 812//212 870//210 813//213 -f 813//213 870//210 869//209 -f 813//213 869//209 814//214 -f 814//214 869//209 868//208 -f 814//214 868//208 815//215 -f 815//215 868//208 867//207 -f 815//215 867//207 816//216 -f 816//216 867//207 817//217 -f 867//207 866//206 817//217 -f 817//217 866//206 865//205 -f 817//217 865//205 818//218 -f 818//218 865//205 819//219 -f 865//205 864//204 819//219 -f 819//219 864//204 863//203 -f 819//219 863//203 820//220 -f 820//220 863//203 862//202 -f 820//220 862//202 821//221 -f 821//221 862//202 861//201 -f 821//221 861//201 822//222 -f 822//222 861//201 860//200 -f 822//222 860//200 823//223 -f 823//223 860//200 824//224 -f 860//200 859//199 824//224 -f 824//224 859//199 858//198 -f 824//224 858//198 825//225 -f 825//225 858//198 826//226 -f 858//198 857//197 826//226 -f 826//226 857//197 856//196 -f 826//226 856//196 827//227 -f 827//227 856//196 828//228 -f 856//196 855//195 828//228 -f 828//228 855//195 854//194 -f 828//228 854//194 829//229 -f 829//229 854//194 853//193 -f 829//229 853//193 830//230 -f 830//230 853//193 831//231 -f 853//193 852//192 831//231 -f 831//231 852//192 832//232 -f 852//192 851//191 832//232 -f 832//232 851//191 850//190 -f 832//232 850//190 833//233 -f 833//233 850//190 849//189 -f 833//233 849//189 834//234 -f 834//234 849//189 848//188 -f 834//234 848//188 835//235 -f 835//235 848//188 836//236 -f 848//188 847//187 836//236 -f 836//236 847//187 846//186 -f 836//236 846//186 837//237 -f 837//237 846//186 845//185 -f 837//237 845//185 838//238 -f 838//238 845//185 844//184 -f 838//238 844//184 839//239 -f 839//239 844//184 843//183 -f 839//239 843//183 840//240 -f 840//240 843//183 842//59 -f 840//240 842//59 841//58 -f 781//241 840//240 841//58 -f 781//241 841//58 780//55 -f 782//242 839//239 840//240 -f 782//242 840//240 781//241 -f 783//243 838//238 839//239 -f 783//243 839//239 782//242 -f 784//244 837//237 838//238 -f 784//244 838//238 783//243 -f 785//245 836//236 837//237 -f 785//245 837//237 784//244 -f 786//246 835//235 785//245 -f 835//235 836//236 785//245 -f 787//247 834//234 835//235 -f 787//247 835//235 786//246 -f 788//248 833//233 834//234 -f 788//248 834//234 787//247 -f 789//249 832//232 833//233 -f 789//249 833//233 788//248 -f 790//250 831//231 789//249 -f 831//231 832//232 789//249 -f 791//251 830//230 790//250 -f 830//230 831//231 790//250 -f 792//252 829//229 830//230 -f 792//252 830//230 791//251 -f 793//253 828//228 829//229 -f 793//253 829//229 792//252 -f 794//254 827//227 793//253 -f 827//227 828//228 793//253 -f 795//255 826//226 827//227 -f 795//255 827//227 794//254 -f 796//256 825//225 795//255 -f 825//225 826//226 795//255 -f 797//257 824//224 825//225 -f 797//257 825//225 796//256 -f 798//258 823//223 797//257 -f 823//223 824//224 797//257 -f 799//259 822//222 823//223 -f 799//259 823//223 798//258 -f 800//260 821//221 799//259 -f 821//221 822//222 799//259 -f 801//261 820//220 821//221 -f 801//261 821//221 800//260 -f 802//262 819//219 820//220 -f 802//262 820//220 801//261 -f 803//263 818//218 802//262 -f 818//218 819//219 802//262 -f 804//264 817//217 818//218 -f 804//264 818//218 803//263 -f 805//265 816//216 817//217 -f 805//265 817//217 804//264 -f 806//266 815//215 816//216 -f 806//266 816//216 805//265 -f 807//267 814//214 815//215 -f 807//267 815//215 806//266 -f 808//268 813//213 814//214 -f 808//268 814//214 807//267 -f 809//269 812//212 813//213 -f 809//269 813//213 808//268 -f 810//56 811//57 812//212 -f 810//56 812//212 809//269 -f 749//53 810//56 809//269 -f 749//53 809//269 750//270 -f 750//270 809//269 808//268 -f 750//270 808//268 751//271 -f 751//271 808//268 807//267 -f 751//271 807//267 752//272 -f 752//272 807//267 806//266 -f 752//272 806//266 753//273 -f 753//273 806//266 805//265 -f 753//273 805//265 754//274 -f 754//274 805//265 755//275 -f 805//265 804//264 755//275 -f 755//275 804//264 803//263 -f 755//275 803//263 756//276 -f 756//276 803//263 757//277 -f 803//263 802//262 757//277 -f 757//277 802//262 801//261 -f 757//277 801//261 758//278 -f 758//278 801//261 800//260 -f 758//278 800//260 759//279 -f 759//279 800//260 799//259 -f 759//279 799//259 760//280 -f 760//280 799//259 798//258 -f 760//280 798//258 761//281 -f 761//281 798//258 762//282 -f 798//258 797//257 762//282 -f 762//282 797//257 796//256 -f 762//282 796//256 763//283 -f 763//283 796//256 764//284 -f 796//256 795//255 764//284 -f 764//284 795//255 794//254 -f 764//284 794//254 765//285 -f 765//285 794//254 766//286 -f 794//254 793//253 766//286 -f 766//286 793//253 792//252 -f 766//286 792//252 767//287 -f 767//287 792//252 791//251 -f 767//287 791//251 768//288 -f 768//288 791//251 769//289 -f 791//251 790//250 769//289 -f 769//289 790//250 770//290 -f 790//250 789//249 770//290 -f 770//290 789//249 788//248 -f 770//290 788//248 771//291 -f 771//291 788//248 787//247 -f 771//291 787//247 772//292 -f 772//292 787//247 786//246 -f 772//292 786//246 773//293 -f 773//293 786//246 774//294 -f 786//246 785//245 774//294 -f 774//294 785//245 784//244 -f 774//294 784//244 775//295 -f 775//295 784//244 783//243 -f 775//295 783//243 776//296 -f 776//296 783//243 782//242 -f 776//296 782//242 777//297 -f 777//297 782//242 781//241 -f 777//297 781//241 778//298 -f 778//298 781//241 780//55 -f 778//298 780//55 779//54 -f 719//299 778//298 779//54 -f 719//299 779//54 718//51 -f 720//300 777//297 778//298 -f 720//300 778//298 719//299 -f 721//301 776//296 777//297 -f 721//301 777//297 720//300 -f 722//302 775//295 776//296 -f 722//302 776//296 721//301 -f 723//303 774//294 775//295 -f 723//303 775//295 722//302 -f 724//304 773//293 723//303 -f 773//293 774//294 723//303 -f 725//305 772//292 773//293 -f 725//305 773//293 724//304 -f 726//306 771//291 772//292 -f 726//306 772//292 725//305 -f 727//307 770//290 771//291 -f 727//307 771//291 726//306 -f 728//308 769//289 727//307 -f 769//289 770//290 727//307 -f 729//309 768//288 728//308 -f 768//288 769//289 728//308 -f 730//310 767//287 768//288 -f 730//310 768//288 729//309 -f 731//311 766//286 767//287 -f 731//311 767//287 730//310 -f 732//312 765//285 731//311 -f 765//285 766//286 731//311 -f 733//313 764//284 765//285 -f 733//313 765//285 732//312 -f 734//314 763//283 733//313 -f 763//283 764//284 733//313 -f 735//315 762//282 763//283 -f 735//315 763//283 734//314 -f 736//316 761//281 735//315 -f 761//281 762//282 735//315 -f 737//317 760//280 761//281 -f 737//317 761//281 736//316 -f 738//318 759//279 760//280 -f 738//318 760//280 737//317 -f 739//319 758//278 759//279 -f 739//319 759//279 738//318 -f 740//320 757//277 758//278 -f 740//320 758//278 739//319 -f 741//321 756//276 740//320 -f 756//276 757//277 740//320 -f 742//322 755//275 756//276 -f 742//322 756//276 741//321 -f 743//323 754//274 755//275 -f 743//323 755//275 742//322 -f 744//324 753//273 754//274 -f 744//324 754//274 743//323 -f 745//325 752//272 753//273 -f 745//325 753//273 744//324 -f 746//326 751//271 752//272 -f 746//326 752//272 745//325 -f 747//327 750//270 751//271 -f 747//327 751//271 746//326 -f 748//52 749//53 750//270 -f 748//52 750//270 747//327 -f 687//49 748//52 747//327 -f 687//49 747//327 688//328 -f 688//328 747//327 746//326 -f 688//328 746//326 689//329 -f 689//329 746//326 745//325 -f 689//329 745//325 690//330 -f 690//330 745//325 744//324 -f 690//330 744//324 691//331 -f 691//331 744//324 743//323 -f 691//331 743//323 692//332 -f 692//332 743//323 742//322 -f 692//332 742//322 693//333 -f 693//333 742//322 741//321 -f 693//333 741//321 694//334 -f 694//334 741//321 695//335 -f 741//321 740//320 695//335 -f 695//335 740//320 739//319 -f 695//335 739//319 696//336 -f 696//336 739//319 738//318 -f 696//336 738//318 697//337 -f 697//337 738//318 737//317 -f 697//337 737//317 698//338 -f 698//338 737//317 736//316 -f 698//338 736//316 699//339 -f 699//339 736//316 700//340 -f 736//316 735//315 700//340 -f 700//340 735//315 734//314 -f 700//340 734//314 701//341 -f 701//341 734//314 702//342 -f 734//314 733//313 702//342 -f 702//342 733//313 732//312 -f 702//342 732//312 703//343 -f 703//343 732//312 704//344 -f 732//312 731//311 704//344 -f 704//344 731//311 730//310 -f 704//344 730//310 705//345 -f 705//345 730//310 729//309 -f 705//345 729//309 706//346 -f 706//346 729//309 707//347 -f 729//309 728//308 707//347 -f 707//347 728//308 708//348 -f 728//308 727//307 708//348 -f 708//348 727//307 726//306 -f 708//348 726//306 709//349 -f 709//349 726//306 725//305 -f 709//349 725//305 710//350 -f 710//350 725//305 724//304 -f 710//350 724//304 711//351 -f 711//351 724//304 712//352 -f 724//304 723//303 712//352 -f 712//352 723//303 722//302 -f 712//352 722//302 713//353 -f 713//353 722//302 721//301 -f 713//353 721//301 714//354 -f 714//354 721//301 720//300 -f 714//354 720//300 715//355 -f 715//355 720//300 719//299 -f 715//355 719//299 716//356 -f 716//356 719//299 718//51 -f 716//356 718//51 717//50 -f 657//357 716//356 717//50 -f 657//357 717//50 656//47 -f 658//358 715//355 716//356 -f 658//358 716//356 657//357 -f 659//359 714//354 715//355 -f 659//359 715//355 658//358 -f 660//360 713//353 714//354 -f 660//360 714//354 659//359 -f 661//361 712//352 713//353 -f 661//361 713//353 660//360 -f 662//362 711//351 661//361 -f 711//351 712//352 661//361 -f 663//363 710//350 711//351 -f 663//363 711//351 662//362 -f 664//364 709//349 710//350 -f 664//364 710//350 663//363 -f 665//365 708//348 709//349 -f 665//365 709//349 664//364 -f 666//366 707//347 665//365 -f 707//347 708//348 665//365 -f 667//367 706//346 666//366 -f 706//346 707//347 666//366 -f 668//368 705//345 706//346 -f 668//368 706//346 667//367 -f 669//369 704//344 705//345 -f 669//369 705//345 668//368 -f 670//370 703//343 669//369 -f 703//343 704//344 669//369 -f 671//371 702//342 703//343 -f 671//371 703//343 670//370 -f 672//372 701//341 671//371 -f 701//341 702//342 671//371 -f 673//373 700//340 701//341 -f 673//373 701//341 672//372 -f 674//374 699//339 673//373 -f 699//339 700//340 673//373 -f 675//375 698//338 699//339 -f 675//375 699//339 674//374 -f 676//376 697//337 698//338 -f 676//376 698//338 675//375 -f 677//377 696//336 697//337 -f 677//377 697//337 676//376 -f 678//378 695//335 696//336 -f 678//378 696//336 677//377 -f 679//379 694//334 678//378 -f 694//334 695//335 678//378 -f 680//380 693//333 694//334 -f 680//380 694//334 679//379 -f 681//381 692//332 693//333 -f 681//381 693//333 680//380 -f 682//382 691//331 692//332 -f 682//382 692//332 681//381 -f 683//383 690//330 691//331 -f 683//383 691//331 682//382 -f 684//384 689//329 690//330 -f 684//384 690//330 683//383 -f 685//385 688//328 689//329 -f 685//385 689//329 684//384 -f 686//48 687//49 688//328 -f 686//48 688//328 685//385 -f 625//45 686//48 685//385 -f 625//45 685//385 626//386 -f 626//386 685//385 684//384 -f 626//386 684//384 627//387 -f 627//387 684//384 683//383 -f 627//387 683//383 628//388 -f 628//388 683//383 682//382 -f 628//388 682//382 629//389 -f 629//389 682//382 681//381 -f 629//389 681//381 630//390 -f 630//390 681//381 680//380 -f 630//390 680//380 631//391 -f 631//391 680//380 679//379 -f 631//391 679//379 632//392 -f 632//392 679//379 633//393 -f 679//379 678//378 633//393 -f 633//393 678//378 677//377 -f 633//393 677//377 634//394 -f 634//394 677//377 676//376 -f 634//394 676//376 635//395 -f 635//395 676//376 675//375 -f 635//395 675//375 636//396 -f 636//396 675//375 674//374 -f 636//396 674//374 637//397 -f 637//397 674//374 638//398 -f 674//374 673//373 638//398 -f 638//398 673//373 672//372 -f 638//398 672//372 639//399 -f 639//399 672//372 640//400 -f 672//372 671//371 640//400 -f 640//400 671//371 670//370 -f 640//400 670//370 641//401 -f 641//401 670//370 642//402 -f 670//370 669//369 642//402 -f 642//402 669//369 668//368 -f 642//402 668//368 643//403 -f 643//403 668//368 667//367 -f 643//403 667//367 644//404 -f 644//404 667//367 645//405 -f 667//367 666//366 645//405 -f 645//405 666//366 646//406 -f 666//366 665//365 646//406 -f 646//406 665//365 664//364 -f 646//406 664//364 647//407 -f 647//407 664//364 663//363 -f 647//407 663//363 648//408 -f 648//408 663//363 662//362 -f 648//408 662//362 649//409 -f 649//409 662//362 650//410 -f 662//362 661//361 650//410 -f 650//410 661//361 660//360 -f 650//410 660//360 651//411 -f 651//411 660//360 659//359 -f 651//411 659//359 652//412 -f 652//412 659//359 658//358 -f 652//412 658//358 653//413 -f 653//413 658//358 657//357 -f 653//413 657//357 654//414 -f 654//414 657//357 656//47 -f 654//414 656//47 655//46 -f 595//415 654//414 655//46 -f 595//415 655//46 594//43 -f 596//416 653//413 654//414 -f 596//416 654//414 595//415 -f 597//417 652//412 653//413 -f 597//417 653//413 596//416 -f 598//418 651//411 652//412 -f 598//418 652//412 597//417 -f 599//419 650//410 651//411 -f 599//419 651//411 598//418 -f 600//420 649//409 599//419 -f 649//409 650//410 599//419 -f 601//421 648//408 649//409 -f 601//421 649//409 600//420 -f 602//422 647//407 648//408 -f 602//422 648//408 601//421 -f 603//423 646//406 647//407 -f 603//423 647//407 602//422 -f 604//424 645//405 603//423 -f 645//405 646//406 603//423 -f 605//425 644//404 604//424 -f 644//404 645//405 604//424 -f 606//426 643//403 644//404 -f 606//426 644//404 605//425 -f 607//427 642//402 643//403 -f 607//427 643//403 606//426 -f 608//428 641//401 607//427 -f 641//401 642//402 607//427 -f 609//429 640//400 641//401 -f 609//429 641//401 608//428 -f 610//430 639//399 609//429 -f 639//399 640//400 609//429 -f 611//431 638//398 639//399 -f 611//431 639//399 610//430 -f 612//432 637//397 611//431 -f 637//397 638//398 611//431 -f 613//433 636//396 637//397 -f 613//433 637//397 612//432 -f 614//434 635//395 636//396 -f 614//434 636//396 613//433 -f 615//435 634//394 635//395 -f 615//435 635//395 614//434 -f 616//436 633//393 634//394 -f 616//436 634//394 615//435 -f 617//437 632//392 633//393 -f 617//437 633//393 616//436 -f 618//438 631//391 632//392 -f 618//438 632//392 617//437 -f 619//439 630//390 631//391 -f 619//439 631//391 618//438 -f 620//440 629//389 630//390 -f 620//440 630//390 619//439 -f 621//441 628//388 629//389 -f 621//441 629//389 620//440 -f 622//442 627//387 628//388 -f 622//442 628//388 621//441 -f 623//443 626//386 627//387 -f 623//443 627//387 622//442 -f 624//44 625//45 626//386 -f 624//44 626//386 623//443 -f 563//41 624//44 623//443 -f 563//41 623//443 564//444 -f 564//444 623//443 622//442 -f 564//444 622//442 565//445 -f 565//445 622//442 621//441 -f 565//445 621//441 566//446 -f 566//446 621//441 620//440 -f 566//446 620//440 567//447 -f 567//447 620//440 619//439 -f 567//447 619//439 568//448 -f 568//448 619//439 618//438 -f 568//448 618//438 569//449 -f 569//449 618//438 617//437 -f 569//449 617//437 570//450 -f 570//450 617//437 571//451 -f 617//437 616//436 571//451 -f 571//451 616//436 615//435 -f 571//451 615//435 572//452 -f 572//452 615//435 614//434 -f 572//452 614//434 573//453 -f 573//453 614//434 613//433 -f 573//453 613//433 574//454 -f 574//454 613//433 612//432 -f 574//454 612//432 575//455 -f 575//455 612//432 576//456 -f 612//432 611//431 576//456 -f 576//456 611//431 610//430 -f 576//456 610//430 577//457 -f 577//457 610//430 578//458 -f 610//430 609//429 578//458 -f 578//458 609//429 608//428 -f 578//458 608//428 579//459 -f 579//459 608//428 580//460 -f 608//428 607//427 580//460 -f 580//460 607//427 606//426 -f 580//460 606//426 581//461 -f 581//461 606//426 605//425 -f 581//461 605//425 582//462 -f 582//462 605//425 583//463 -f 605//425 604//424 583//463 -f 583//463 604//424 584//464 -f 604//424 603//423 584//464 -f 584//464 603//423 602//422 -f 584//464 602//422 585//465 -f 585//465 602//422 601//421 -f 585//465 601//421 586//466 -f 586//466 601//421 600//420 -f 586//466 600//420 587//467 -f 587//467 600//420 588//468 -f 600//420 599//419 588//468 -f 588//468 599//419 598//418 -f 588//468 598//418 589//469 -f 589//469 598//418 597//417 -f 589//469 597//417 590//470 -f 590//470 597//417 596//416 -f 590//470 596//416 591//471 -f 591//471 596//416 595//415 -f 591//471 595//415 592//472 -f 592//472 595//415 594//43 -f 592//472 594//43 593//42 -f 533//473 592//472 593//42 -f 533//473 593//42 532//39 -f 534//474 591//471 592//472 -f 534//474 592//472 533//473 -f 535//475 590//470 591//471 -f 535//475 591//471 534//474 -f 536//476 589//469 590//470 -f 536//476 590//470 535//475 -f 537//477 588//468 589//469 -f 537//477 589//469 536//476 -f 538//478 587//467 537//477 -f 587//467 588//468 537//477 -f 539//479 586//466 587//467 -f 539//479 587//467 538//478 -f 540//480 585//465 586//466 -f 540//480 586//466 539//479 -f 541//481 584//464 585//465 -f 541//481 585//465 540//480 -f 542//482 583//463 541//481 -f 583//463 584//464 541//481 -f 543//483 582//462 542//482 -f 582//462 583//463 542//482 -f 544//484 581//461 582//462 -f 544//484 582//462 543//483 -f 545//485 580//460 581//461 -f 545//485 581//461 544//484 -f 546//486 579//459 580//460 -f 546//486 580//460 545//485 -f 547//487 578//458 579//459 -f 547//487 579//459 546//486 -f 548//488 577//457 547//487 -f 577//457 578//458 547//487 -f 549//489 576//456 577//457 -f 549//489 577//457 548//488 -f 550//490 575//455 549//489 -f 575//455 576//456 549//489 -f 551//491 574//454 575//455 -f 551//491 575//455 550//490 -f 552//492 573//453 574//454 -f 552//492 574//454 551//491 -f 553//493 572//452 573//453 -f 553//493 573//453 552//492 -f 554//494 571//451 572//452 -f 554//494 572//452 553//493 -f 555//495 570//450 554//494 -f 570//450 571//451 554//494 -f 556//496 569//449 570//450 -f 556//496 570//450 555//495 -f 557//497 568//448 569//449 -f 557//497 569//449 556//496 -f 558//498 567//447 568//448 -f 558//498 568//448 557//497 -f 559//499 566//446 567//447 -f 559//499 567//447 558//498 -f 560//500 565//445 566//446 -f 560//500 566//446 559//499 -f 561//501 564//444 565//445 -f 561//501 565//445 560//500 -f 562//40 563//41 564//444 -f 562//40 564//444 561//501 -f 501//37 562//40 561//501 -f 501//37 561//501 502//502 -f 502//502 561//501 560//500 -f 502//502 560//500 503//503 -f 503//503 560//500 559//499 -f 503//503 559//499 504//504 -f 504//504 559//499 558//498 -f 504//504 558//498 505//505 -f 505//505 558//498 557//497 -f 505//505 557//497 506//506 -f 506//506 557//497 556//496 -f 506//506 556//496 507//507 -f 507//507 556//496 555//495 -f 507//507 555//495 508//508 -f 508//508 555//495 509//509 -f 555//495 554//494 509//509 -f 509//509 554//494 553//493 -f 509//509 553//493 510//510 -f 510//510 553//493 552//492 -f 510//510 552//492 511//511 -f 511//511 552//492 551//491 -f 511//511 551//491 512//512 -f 512//512 551//491 550//490 -f 512//512 550//490 513//513 -f 513//513 550//490 514//514 -f 550//490 549//489 514//514 -f 514//514 549//489 548//488 -f 514//514 548//488 515//515 -f 515//515 548//488 516//516 -f 548//488 547//487 516//516 -f 516//516 547//487 546//486 -f 516//516 546//486 517//517 -f 517//517 546//486 545//485 -f 517//517 545//485 518//518 -f 518//518 545//485 544//484 -f 518//518 544//484 519//519 -f 519//519 544//484 543//483 -f 519//519 543//483 520//520 -f 520//520 543//483 521//521 -f 543//483 542//482 521//521 -f 521//521 542//482 522//522 -f 542//482 541//481 522//522 -f 522//522 541//481 540//480 -f 522//522 540//480 523//523 -f 523//523 540//480 539//479 -f 523//523 539//479 524//524 -f 524//524 539//479 538//478 -f 524//524 538//478 525//525 -f 525//525 538//478 526//526 -f 538//478 537//477 526//526 -f 526//526 537//477 536//476 -f 526//526 536//476 527//527 -f 527//527 536//476 535//475 -f 527//527 535//475 528//528 -f 528//528 535//475 534//474 -f 528//528 534//474 529//529 -f 529//529 534//474 533//473 -f 529//529 533//473 530//530 -f 530//530 533//473 532//39 -f 530//530 532//39 531//38 -f 471//531 530//530 531//38 -f 471//531 531//38 470//35 -f 472//532 529//529 530//530 -f 472//532 530//530 471//531 -f 473//533 528//528 529//529 -f 473//533 529//529 472//532 -f 474//534 527//527 528//528 -f 474//534 528//528 473//533 -f 475//535 526//526 527//527 -f 475//535 527//527 474//534 -f 476//536 525//525 475//535 -f 525//525 526//526 475//535 -f 477//537 524//524 525//525 -f 477//537 525//525 476//536 -f 478//538 523//523 524//524 -f 478//538 524//524 477//537 -f 479//539 522//522 523//523 -f 479//539 523//523 478//538 -f 480//540 521//521 479//539 -f 521//521 522//522 479//539 -f 481//541 520//520 480//540 -f 520//520 521//521 480//540 -f 482//542 519//519 520//520 -f 482//542 520//520 481//541 -f 483//543 518//518 519//519 -f 483//543 519//519 482//542 -f 484//544 517//517 518//518 -f 484//544 518//518 483//543 -f 485//545 516//516 517//517 -f 485//545 517//517 484//544 -f 486//546 515//515 485//545 -f 515//515 516//516 485//545 -f 487//547 514//514 515//515 -f 487//547 515//515 486//546 -f 488//548 513//513 487//547 -f 513//513 514//514 487//547 -f 489//549 512//512 513//513 -f 489//549 513//513 488//548 -f 490//550 511//511 512//512 -f 490//550 512//512 489//549 -f 491//551 510//510 511//511 -f 491//551 511//511 490//550 -f 492//552 509//509 510//510 -f 492//552 510//510 491//551 -f 493//553 508//508 492//552 -f 508//508 509//509 492//552 -f 494//554 507//507 508//508 -f 494//554 508//508 493//553 -f 495//555 506//506 507//507 -f 495//555 507//507 494//554 -f 496//556 505//505 506//506 -f 496//556 506//506 495//555 -f 497//557 504//504 505//505 -f 497//557 505//505 496//556 -f 498//558 503//503 504//504 -f 498//558 504//504 497//557 -f 499//559 502//502 503//503 -f 499//559 503//503 498//558 -f 500//36 501//37 502//502 -f 500//36 502//502 499//559 -f 439//33 500//36 499//559 -f 439//33 499//559 440//560 -f 440//560 499//559 498//558 -f 440//560 498//558 441//561 -f 441//561 498//558 497//557 -f 441//561 497//557 442//562 -f 442//562 497//557 496//556 -f 442//562 496//556 443//563 -f 443//563 496//556 495//555 -f 443//563 495//555 444//564 -f 444//564 495//555 494//554 -f 444//564 494//554 445//565 -f 445//565 494//554 493//553 -f 445//565 493//553 446//566 -f 446//566 493//553 447//567 -f 493//553 492//552 447//567 -f 447//567 492//552 491//551 -f 447//567 491//551 448//568 -f 448//568 491//551 490//550 -f 448//568 490//550 449//569 -f 449//569 490//550 489//549 -f 449//569 489//549 450//570 -f 450//570 489//549 488//548 -f 450//570 488//548 451//571 -f 451//571 488//548 452//572 -f 488//548 487//547 452//572 -f 452//572 487//547 486//546 -f 452//572 486//546 453//573 -f 453//573 486//546 454//574 -f 486//546 485//545 454//574 -f 454//574 485//545 484//544 -f 454//574 484//544 455//575 -f 455//575 484//544 483//543 -f 455//575 483//543 456//576 -f 456//576 483//543 482//542 -f 456//576 482//542 457//577 -f 457//577 482//542 481//541 -f 457//577 481//541 458//578 -f 458//578 481//541 480//540 -f 458//578 480//540 459//579 -f 459//579 480//540 460//580 -f 480//540 479//539 460//580 -f 460//580 479//539 478//538 -f 460//580 478//538 461//581 -f 461//581 478//538 477//537 -f 461//581 477//537 462//582 -f 462//582 477//537 476//536 -f 462//582 476//536 463//583 -f 463//583 476//536 464//584 -f 476//536 475//535 464//584 -f 464//584 475//535 474//534 -f 464//584 474//534 465//585 -f 465//585 474//534 473//533 -f 465//585 473//533 466//586 -f 466//586 473//533 472//532 -f 466//586 472//532 467//587 -f 467//587 472//532 471//531 -f 467//587 471//531 468//588 -f 468//588 471//531 470//35 -f 468//588 470//35 469//34 -f 409//589 468//588 469//34 -f 409//589 469//34 408//31 -f 410//590 467//587 468//588 -f 410//590 468//588 409//589 -f 411//591 466//586 467//587 -f 411//591 467//587 410//590 -f 412//592 465//585 466//586 -f 412//592 466//586 411//591 -f 413//593 464//584 465//585 -f 413//593 465//585 412//592 -f 414//594 463//583 413//593 -f 463//583 464//584 413//593 -f 415//595 462//582 463//583 -f 415//595 463//583 414//594 -f 416//596 461//581 462//582 -f 416//596 462//582 415//595 -f 417//597 460//580 461//581 -f 417//597 461//581 416//596 -f 418//598 459//579 417//597 -f 459//579 460//580 417//597 -f 419//599 458//578 459//579 -f 419//599 459//579 418//598 -f 420//600 457//577 458//578 -f 420//600 458//578 419//599 -f 421//601 456//576 457//577 -f 421//601 457//577 420//600 -f 422//602 455//575 456//576 -f 422//602 456//576 421//601 -f 423//603 454//574 455//575 -f 423//603 455//575 422//602 -f 424//604 453//573 423//603 -f 453//573 454//574 423//603 -f 425//605 452//572 453//573 -f 425//605 453//573 424//604 -f 426//606 451//571 425//605 -f 451//571 452//572 425//605 -f 427//607 450//570 451//571 -f 427//607 451//571 426//606 -f 428//608 449//569 450//570 -f 428//608 450//570 427//607 -f 429//609 448//568 449//569 -f 429//609 449//569 428//608 -f 430//610 447//567 448//568 -f 430//610 448//568 429//609 -f 431//611 446//566 430//610 -f 446//566 447//567 430//610 -f 432//612 445//565 446//566 -f 432//612 446//566 431//611 -f 433//613 444//564 445//565 -f 433//613 445//565 432//612 -f 434//614 443//563 444//564 -f 434//614 444//564 433//613 -f 435//615 442//562 443//563 -f 435//615 443//563 434//614 -f 436//616 441//561 442//562 -f 436//616 442//562 435//615 -f 437//617 440//560 441//561 -f 437//617 441//561 436//616 -f 438//32 439//33 440//560 -f 438//32 440//560 437//617 -f 377//29 438//32 437//617 -f 377//29 437//617 378//618 -f 378//618 437//617 436//616 -f 378//618 436//616 379//619 -f 379//619 436//616 435//615 -f 379//619 435//615 380//620 -f 380//620 435//615 434//614 -f 380//620 434//614 381//621 -f 381//621 434//614 433//613 -f 381//621 433//613 382//622 -f 382//622 433//613 432//612 -f 382//622 432//612 383//623 -f 383//623 432//612 431//611 -f 383//623 431//611 384//624 -f 384//624 431//611 385//625 -f 431//611 430//610 385//625 -f 385//625 430//610 429//609 -f 385//625 429//609 386//626 -f 386//626 429//609 428//608 -f 386//626 428//608 387//627 -f 387//627 428//608 427//607 -f 387//627 427//607 388//628 -f 388//628 427//607 426//606 -f 388//628 426//606 389//629 -f 389//629 426//606 390//630 -f 426//606 425//605 390//630 -f 390//630 425//605 424//604 -f 390//630 424//604 391//631 -f 391//631 424//604 392//632 -f 424//604 423//603 392//632 -f 392//632 423//603 422//602 -f 392//632 422//602 393//633 -f 393//633 422//602 421//601 -f 393//633 421//601 394//634 -f 394//634 421//601 420//600 -f 394//634 420//600 395//635 -f 395//635 420//600 419//599 -f 395//635 419//599 396//636 -f 396//636 419//599 418//598 -f 396//636 418//598 397//637 -f 397//637 418//598 398//638 -f 418//598 417//597 398//638 -f 398//638 417//597 416//596 -f 398//638 416//596 399//639 -f 399//639 416//596 415//595 -f 399//639 415//595 400//640 -f 400//640 415//595 414//594 -f 400//640 414//594 401//641 -f 401//641 414//594 402//642 -f 414//594 413//593 402//642 -f 402//642 413//593 412//592 -f 402//642 412//592 403//643 -f 403//643 412//592 411//591 -f 403//643 411//591 404//644 -f 404//644 411//591 410//590 -f 404//644 410//590 405//645 -f 405//645 410//590 409//589 -f 405//645 409//589 406//646 -f 406//646 409//589 408//31 -f 406//646 408//31 407//30 -f 347//647 406//646 407//30 -f 347//647 407//30 346//27 -f 348//648 405//645 406//646 -f 348//648 406//646 347//647 -f 349//649 404//644 405//645 -f 349//649 405//645 348//648 -f 350//650 403//643 404//644 -f 350//650 404//644 349//649 -f 351//651 402//642 403//643 -f 351//651 403//643 350//650 -f 352//652 401//641 351//651 -f 401//641 402//642 351//651 -f 353//653 400//640 401//641 -f 353//653 401//641 352//652 -f 354//654 399//639 400//640 -f 354//654 400//640 353//653 -f 355//655 398//638 399//639 -f 355//655 399//639 354//654 -f 356//656 397//637 355//655 -f 397//637 398//638 355//655 -f 357//657 396//636 397//637 -f 357//657 397//637 356//656 -f 358//658 395//635 396//636 -f 358//658 396//636 357//657 -f 359//659 394//634 395//635 -f 359//659 395//635 358//658 -f 360//660 393//633 394//634 -f 360//660 394//634 359//659 -f 361//661 392//632 393//633 -f 361//661 393//633 360//660 -f 362//662 391//631 361//661 -f 391//631 392//632 361//661 -f 363//663 390//630 391//631 -f 363//663 391//631 362//662 -f 364//664 389//629 363//663 -f 389//629 390//630 363//663 -f 365//665 388//628 389//629 -f 365//665 389//629 364//664 -f 366//666 387//627 388//628 -f 366//666 388//628 365//665 -f 367//667 386//626 387//627 -f 367//667 387//627 366//666 -f 368//668 385//625 386//626 -f 368//668 386//626 367//667 -f 369//669 384//624 385//625 -f 369//669 385//625 368//668 -f 370//670 383//623 384//624 -f 370//670 384//624 369//669 -f 371//671 382//622 383//623 -f 371//671 383//623 370//670 -f 372//672 381//621 382//622 -f 372//672 382//622 371//671 -f 373//673 380//620 381//621 -f 373//673 381//621 372//672 -f 374//674 379//619 380//620 -f 374//674 380//620 373//673 -f 375//675 378//618 379//619 -f 375//675 379//619 374//674 -f 376//28 377//29 378//618 -f 376//28 378//618 375//675 -f 315//25 376//28 375//675 -f 315//25 375//675 316//676 -f 316//676 375//675 374//674 -f 316//676 374//674 317//677 -f 317//677 374//674 373//673 -f 317//677 373//673 318//678 -f 318//678 373//673 372//672 -f 318//678 372//672 319//679 -f 319//679 372//672 371//671 -f 319//679 371//671 320//680 -f 320//680 371//671 370//670 -f 320//680 370//670 321//681 -f 321//681 370//670 369//669 -f 321//681 369//669 322//682 -f 322//682 369//669 368//668 -f 322//682 368//668 323//683 -f 323//683 368//668 367//667 -f 323//683 367//667 324//684 -f 324//684 367//667 366//666 -f 324//684 366//666 325//685 -f 325//685 366//666 365//665 -f 325//685 365//665 326//686 -f 326//686 365//665 364//664 -f 326//686 364//664 327//687 -f 327//687 364//664 328//688 -f 364//664 363//663 328//688 -f 328//688 363//663 362//662 -f 328//688 362//662 329//689 -f 329//689 362//662 330//690 -f 362//662 361//661 330//690 -f 330//690 361//661 360//660 -f 330//690 360//660 331//691 -f 331//691 360//660 359//659 -f 331//691 359//659 332//692 -f 332//692 359//659 358//658 -f 332//692 358//658 333//693 -f 333//693 358//658 357//657 -f 333//693 357//657 334//694 -f 334//694 357//657 356//656 -f 334//694 356//656 335//695 -f 335//695 356//656 336//696 -f 356//656 355//655 336//696 -f 336//696 355//655 354//654 -f 336//696 354//654 337//697 -f 337//697 354//654 353//653 -f 337//697 353//653 338//698 -f 338//698 353//653 352//652 -f 338//698 352//652 339//699 -f 339//699 352//652 340//700 -f 352//652 351//651 340//700 -f 340//700 351//651 350//650 -f 340//700 350//650 341//701 -f 341//701 350//650 349//649 -f 341//701 349//649 342//702 -f 342//702 349//649 348//648 -f 342//702 348//648 343//703 -f 343//703 348//648 347//647 -f 343//703 347//647 344//704 -f 344//704 347//647 346//27 -f 344//704 346//27 345//26 -f 285//705 344//704 345//26 -f 285//705 345//26 284//23 -f 286//706 343//703 344//704 -f 286//706 344//704 285//705 -f 287//707 342//702 343//703 -f 287//707 343//703 286//706 -f 288//708 341//701 342//702 -f 288//708 342//702 287//707 -f 289//709 340//700 341//701 -f 289//709 341//701 288//708 -f 290//710 339//699 340//700 -f 290//710 340//700 289//709 -f 291//711 338//698 339//699 -f 291//711 339//699 290//710 -f 292//712 337//697 338//698 -f 292//712 338//698 291//711 -f 293//713 336//696 337//697 -f 293//713 337//697 292//712 -f 294//714 335//695 293//713 -f 335//695 336//696 293//713 -f 295//715 334//694 335//695 -f 295//715 335//695 294//714 -f 296//716 333//693 334//694 -f 296//716 334//694 295//715 -f 297//717 332//692 333//693 -f 297//717 333//693 296//716 -f 298//718 331//691 297//717 -f 331//691 332//692 297//717 -f 299//719 330//690 331//691 -f 299//719 331//691 298//718 -f 300//720 329//689 299//719 -f 329//689 330//690 299//719 -f 301//721 328//688 329//689 -f 301//721 329//689 300//720 -f 302//722 327//687 301//721 -f 327//687 328//688 301//721 -f 303//723 326//686 327//687 -f 303//723 327//687 302//722 -f 304//724 325//685 326//686 -f 304//724 326//686 303//723 -f 305//725 324//684 325//685 -f 305//725 325//685 304//724 -f 306//726 323//683 324//684 -f 306//726 324//684 305//725 -f 307//727 322//682 323//683 -f 307//727 323//683 306//726 -f 308//728 321//681 322//682 -f 308//728 322//682 307//727 -f 309//729 320//680 321//681 -f 309//729 321//681 308//728 -f 310//730 319//679 320//680 -f 310//730 320//680 309//729 -f 311//731 318//678 319//679 -f 311//731 319//679 310//730 -f 312//732 317//677 318//678 -f 312//732 318//678 311//731 -f 313//733 316//676 317//677 -f 313//733 317//677 312//732 -f 314//24 315//25 316//676 -f 314//24 316//676 313//733 -f 253//21 314//24 313//733 -f 253//21 313//733 254//734 -f 254//734 313//733 312//732 -f 254//734 312//732 255//735 -f 255//735 312//732 311//731 -f 255//735 311//731 256//736 -f 256//736 311//731 310//730 -f 256//736 310//730 257//737 -f 257//737 310//730 309//729 -f 257//737 309//729 258//738 -f 258//738 309//729 308//728 -f 258//738 308//728 259//739 -f 259//739 308//728 307//727 -f 259//739 307//727 260//740 -f 260//740 307//727 306//726 -f 260//740 306//726 261//741 -f 261//741 306//726 305//725 -f 261//741 305//725 262//742 -f 262//742 305//725 304//724 -f 262//742 304//724 263//743 -f 263//743 304//724 303//723 -f 263//743 303//723 264//744 -f 264//744 303//723 302//722 -f 264//744 302//722 265//745 -f 265//745 302//722 266//746 -f 302//722 301//721 266//746 -f 266//746 301//721 300//720 -f 266//746 300//720 267//747 -f 267//747 300//720 268//748 -f 300//720 299//719 268//748 -f 268//748 299//719 298//718 -f 268//748 298//718 269//749 -f 269//749 298//718 270//750 -f 298//718 297//717 270//750 -f 270//750 297//717 296//716 -f 270//750 296//716 271//751 -f 271//751 296//716 295//715 -f 271//751 295//715 272//752 -f 272//752 295//715 273//753 -f 295//715 294//714 273//753 -f 273//753 294//714 274//754 -f 294//714 293//713 274//754 -f 274//754 293//713 292//712 -f 274//754 292//712 275//755 -f 275//755 292//712 291//711 -f 275//755 291//711 276//756 -f 276//756 291//711 290//710 -f 276//756 290//710 277//757 -f 277//757 290//710 289//709 -f 277//757 289//709 278//758 -f 278//758 289//709 288//708 -f 278//758 288//708 279//759 -f 279//759 288//708 287//707 -f 279//759 287//707 280//760 -f 280//760 287//707 286//706 -f 280//760 286//706 281//761 -f 281//761 286//706 285//705 -f 281//761 285//705 282//762 -f 282//762 285//705 284//23 -f 282//762 284//23 283//22 -f 223//763 282//762 283//22 -f 223//763 283//22 222//19 -f 224//764 281//761 282//762 -f 224//764 282//762 223//763 -f 225//765 280//760 281//761 -f 225//765 281//761 224//764 -f 226//766 279//759 280//760 -f 226//766 280//760 225//765 -f 227//767 278//758 279//759 -f 227//767 279//759 226//766 -f 228//768 277//757 278//758 -f 228//768 278//758 227//767 -f 229//769 276//756 277//757 -f 229//769 277//757 228//768 -f 230//770 275//755 276//756 -f 230//770 276//756 229//769 -f 231//771 274//754 275//755 -f 231//771 275//755 230//770 -f 232//772 273//753 231//771 -f 273//753 274//754 231//771 -f 233//773 272//752 232//772 -f 272//752 273//753 232//772 -f 234//774 271//751 272//752 -f 234//774 272//752 233//773 -f 235//775 270//750 271//751 -f 235//775 271//751 234//774 -f 236//776 269//749 235//775 -f 269//749 270//750 235//775 -f 237//777 268//748 269//749 -f 237//777 269//749 236//776 -f 238//778 267//747 237//777 -f 267//747 268//748 237//777 -f 239//779 266//746 267//747 -f 239//779 267//747 238//778 -f 240//780 265//745 239//779 -f 265//745 266//746 239//779 -f 241//781 264//744 265//745 -f 241//781 265//745 240//780 -f 242//782 263//743 264//744 -f 242//782 264//744 241//781 -f 243//783 262//742 263//743 -f 243//783 263//743 242//782 -f 244//784 261//741 262//742 -f 244//784 262//742 243//783 -f 245//785 260//740 261//741 -f 245//785 261//741 244//784 -f 246//786 259//739 260//740 -f 246//786 260//740 245//785 -f 247//787 258//738 259//739 -f 247//787 259//739 246//786 -f 248//788 257//737 258//738 -f 248//788 258//738 247//787 -f 249//789 256//736 257//737 -f 249//789 257//737 248//788 -f 250//790 255//735 256//736 -f 250//790 256//736 249//789 -f 251//791 254//734 255//735 -f 251//791 255//735 250//790 -f 252//20 253//21 254//734 -f 252//20 254//734 251//791 -f 191//17 252//20 251//791 -f 191//17 251//791 192//792 -f 192//792 251//791 250//790 -f 192//792 250//790 193//793 -f 193//793 250//790 249//789 -f 193//793 249//789 194//794 -f 194//794 249//789 248//788 -f 194//794 248//788 195//795 -f 195//795 248//788 247//787 -f 195//795 247//787 196//796 -f 196//796 247//787 246//786 -f 196//796 246//786 197//797 -f 197//797 246//786 245//785 -f 197//797 245//785 198//798 -f 198//798 245//785 244//784 -f 198//798 244//784 199//799 -f 199//799 244//784 243//783 -f 199//799 243//783 200//800 -f 200//800 243//783 242//782 -f 200//800 242//782 201//801 -f 201//801 242//782 241//781 -f 201//801 241//781 202//802 -f 202//802 241//781 240//780 -f 202//802 240//780 203//803 -f 203//803 240//780 204//804 -f 240//780 239//779 204//804 -f 204//804 239//779 238//778 -f 204//804 238//778 205//805 -f 205//805 238//778 206//806 -f 238//778 237//777 206//806 -f 206//806 237//777 236//776 -f 206//806 236//776 207//807 -f 207//807 236//776 208//808 -f 236//776 235//775 208//808 -f 208//808 235//775 234//774 -f 208//808 234//774 209//809 -f 209//809 234//774 233//773 -f 209//809 233//773 210//810 -f 210//810 233//773 211//811 -f 233//773 232//772 211//811 -f 211//811 232//772 231//771 -f 211//811 231//771 212//812 -f 212//812 231//771 230//770 -f 212//812 230//770 213//813 -f 213//813 230//770 229//769 -f 213//813 229//769 214//814 -f 214//814 229//769 228//768 -f 214//814 228//768 215//815 -f 215//815 228//768 227//767 -f 215//815 227//767 216//816 -f 216//816 227//767 226//766 -f 216//816 226//766 217//817 -f 217//817 226//766 225//765 -f 217//817 225//765 218//818 -f 218//818 225//765 224//764 -f 218//818 224//764 219//819 -f 219//819 224//764 223//763 -f 219//819 223//763 220//820 -f 220//820 223//763 222//19 -f 220//820 222//19 221//18 -f 161//821 220//820 221//18 -f 161//821 221//18 160//15 -f 162//822 219//819 220//820 -f 162//822 220//820 161//821 -f 163//823 218//818 219//819 -f 163//823 219//819 162//822 -f 164//824 217//817 218//818 -f 164//824 218//818 163//823 -f 165//825 216//816 217//817 -f 165//825 217//817 164//824 -f 166//826 215//815 216//816 -f 166//826 216//816 165//825 -f 167//827 214//814 215//815 -f 167//827 215//815 166//826 -f 168//828 213//813 214//814 -f 168//828 214//814 167//827 -f 169//829 212//812 213//813 -f 169//829 213//813 168//828 -f 170//830 211//811 169//829 -f 211//811 212//812 169//829 -f 171//831 210//810 211//811 -f 171//831 211//811 170//830 -f 172//832 209//809 210//810 -f 172//832 210//810 171//831 -f 173//833 208//808 209//809 -f 173//833 209//809 172//832 -f 174//834 207//807 173//833 -f 207//807 208//808 173//833 -f 175//835 206//806 207//807 -f 175//835 207//807 174//834 -f 176//836 205//805 175//835 -f 205//805 206//806 175//835 -f 177//837 204//804 205//805 -f 177//837 205//805 176//836 -f 178//838 203//803 177//837 -f 203//803 204//804 177//837 -f 179//839 202//802 203//803 -f 179//839 203//803 178//838 -f 180//840 201//801 202//802 -f 180//840 202//802 179//839 -f 181//841 200//800 201//801 -f 181//841 201//801 180//840 -f 182//842 199//799 200//800 -f 182//842 200//800 181//841 -f 183//843 198//798 199//799 -f 183//843 199//799 182//842 -f 184//844 197//797 198//798 -f 184//844 198//798 183//843 -f 185//845 196//796 197//797 -f 185//845 197//797 184//844 -f 186//846 195//795 196//796 -f 186//846 196//796 185//845 -f 187//847 194//794 195//795 -f 187//847 195//795 186//846 -f 188//848 193//793 194//794 -f 188//848 194//794 187//847 -f 189//849 192//792 193//793 -f 189//849 193//793 188//848 -f 190//16 191//17 192//792 -f 190//16 192//792 189//849 -f 129//13 190//16 189//849 -f 129//13 189//849 130//850 -f 130//850 189//849 188//848 -f 130//850 188//848 131//851 -f 131//851 188//848 187//847 -f 131//851 187//847 132//852 -f 132//852 187//847 186//846 -f 132//852 186//846 133//853 -f 133//853 186//846 185//845 -f 133//853 185//845 134//854 -f 134//854 185//845 184//844 -f 134//854 184//844 135//855 -f 135//855 184//844 183//843 -f 135//855 183//843 136//856 -f 136//856 183//843 182//842 -f 136//856 182//842 137//857 -f 137//857 182//842 181//841 -f 137//857 181//841 138//858 -f 138//858 181//841 180//840 -f 138//858 180//840 139//859 -f 139//859 180//840 179//839 -f 139//859 179//839 140//860 -f 140//860 179//839 178//838 -f 140//860 178//838 141//861 -f 141//861 178//838 142//862 -f 178//838 177//837 142//862 -f 142//862 177//837 176//836 -f 142//862 176//836 143//863 -f 143//863 176//836 144//864 -f 176//836 175//835 144//864 -f 144//864 175//835 174//834 -f 144//864 174//834 145//865 -f 145//865 174//834 146//866 -f 174//834 173//833 146//866 -f 146//866 173//833 172//832 -f 146//866 172//832 147//867 -f 147//867 172//832 171//831 -f 147//867 171//831 148//868 -f 148//868 171//831 149//869 -f 171//831 170//830 149//869 -f 149//869 170//830 150//870 -f 170//830 169//829 150//870 -f 150//870 169//829 168//828 -f 150//870 168//828 151//871 -f 151//871 168//828 167//827 -f 151//871 167//827 152//872 -f 152//872 167//827 166//826 -f 152//872 166//826 153//873 -f 153//873 166//826 165//825 -f 153//873 165//825 154//874 -f 154//874 165//825 164//824 -f 154//874 164//824 155//875 -f 155//875 164//824 163//823 -f 155//875 163//823 156//876 -f 156//876 163//823 162//822 -f 156//876 162//822 157//877 -f 157//877 162//822 161//821 -f 157//877 161//821 158//878 -f 158//878 161//821 160//15 -f 158//878 160//15 159//14 -f 98//879 158//878 159//14 -f 98//879 159//14 97//11 -f 99//880 157//877 158//878 -f 99//880 158//878 98//879 -f 100//881 156//876 157//877 -f 100//881 157//877 99//880 -f 101//882 155//875 156//876 -f 101//882 156//876 100//881 -f 102//883 154//874 155//875 -f 102//883 155//875 101//882 -f 103//884 153//873 154//874 -f 103//884 154//874 102//883 -f 104//885 152//872 153//873 -f 104//885 153//873 103//884 -f 105//886 151//871 152//872 -f 105//886 152//872 104//885 -f 106//887 150//870 151//871 -f 106//887 151//871 105//886 -f 107//888 149//869 150//870 -f 107//888 150//870 106//887 -f 108//889 148//868 107//888 -f 148//868 149//869 107//888 -f 109//890 147//867 148//868 -f 109//890 148//868 108//889 -f 110//891 146//866 147//867 -f 110//891 147//867 109//890 -f 111//892 145//865 110//891 -f 145//865 146//866 110//891 -f 112//893 144//864 145//865 -f 112//893 145//865 111//892 -f 113//894 143//863 112//893 -f 143//863 144//864 112//893 -f 114//895 142//862 143//863 -f 114//895 143//863 113//894 -f 115//896 141//861 114//895 -f 141//861 142//862 114//895 -f 116//897 140//860 141//861 -f 116//897 141//861 115//896 -f 117//898 139//859 140//860 -f 117//898 140//860 116//897 -f 118//899 138//858 139//859 -f 118//899 139//859 117//898 -f 119//900 137//857 138//858 -f 119//900 138//858 118//899 -f 120//901 136//856 137//857 -f 120//901 137//857 119//900 -f 121//902 135//855 136//856 -f 121//902 136//856 120//901 -f 122//903 134//854 135//855 -f 122//903 135//855 121//902 -f 123//904 133//853 134//854 -f 123//904 134//854 122//903 -f 124//905 132//852 133//853 -f 124//905 133//853 123//904 -f 125//906 131//851 132//852 -f 125//906 132//852 124//905 -f 126//907 130//850 131//851 -f 126//907 131//851 125//906 -f 127//12 129//13 130//850 -f 127//12 130//850 126//907 -f 66//9 127//12 126//907 -f 66//9 126//907 67//908 -f 67//908 126//907 125//906 -f 67//908 125//906 68//909 -f 68//909 125//906 124//905 -f 68//909 124//905 69//910 -f 69//910 124//905 123//904 -f 69//910 123//904 70//911 -f 70//911 123//904 122//903 -f 70//911 122//903 71//912 -f 71//912 122//903 121//902 -f 71//912 121//902 72//913 -f 72//913 121//902 120//901 -f 72//913 120//901 73//914 -f 73//914 120//901 119//900 -f 73//914 119//900 74//915 -f 74//915 119//900 118//899 -f 74//915 118//899 75//916 -f 75//916 118//899 117//898 -f 75//916 117//898 76//917 -f 76//917 117//898 116//897 -f 76//917 116//897 77//918 -f 77//918 116//897 115//896 -f 77//918 115//896 78//919 -f 78//919 115//896 114//895 -f 78//919 114//895 79//920 -f 79//920 114//895 113//894 -f 79//920 113//894 80//921 -f 80//921 113//894 81//922 -f 113//894 112//893 81//922 -f 81//922 112//893 111//892 -f 81//922 111//892 82//923 -f 82//923 111//892 110//891 -f 82//923 110//891 83//924 -f 83//924 110//891 109//890 -f 83//924 109//890 84//925 -f 84//925 109//890 108//889 -f 84//925 108//889 85//926 -f 85//926 108//889 107//888 -f 85//926 107//888 86//927 -f 86//927 107//888 106//887 -f 86//927 106//887 87//928 -f 87//928 106//887 105//886 -f 87//928 105//886 88//929 -f 88//929 105//886 104//885 -f 88//929 104//885 89//930 -f 89//930 104//885 103//884 -f 89//930 103//884 90//931 -f 90//931 103//884 102//883 -f 90//931 102//883 91//932 -f 91//932 102//883 101//882 -f 91//932 101//882 92//933 -f 92//933 101//882 100//881 -f 92//933 100//881 93//934 -f 93//934 100//881 99//880 -f 93//934 99//880 94//935 -f 94//935 99//880 98//879 -f 94//935 98//879 95//936 -f 95//936 98//879 97//11 -f 95//936 97//11 96//10 -f 36//937 95//936 96//10 -f 36//937 96//10 35//7 -f 37//938 94//935 95//936 -f 37//938 95//936 36//937 -f 38//939 93//934 94//935 -f 38//939 94//935 37//938 -f 39//940 92//933 93//934 -f 39//940 93//934 38//939 -f 40//941 91//932 92//933 -f 40//941 92//933 39//940 -f 41//942 90//931 91//932 -f 41//942 91//932 40//941 -f 42//943 89//930 90//931 -f 42//943 90//931 41//942 -f 43//944 88//929 89//930 -f 43//944 89//930 42//943 -f 44//945 87//928 88//929 -f 44//945 88//929 43//944 -f 45//946 86//927 87//928 -f 45//946 87//928 44//945 -f 46//947 85//926 86//927 -f 46//947 86//927 45//946 -f 47//948 84//925 85//926 -f 47//948 85//926 46//947 -f 48//949 83//924 84//925 -f 48//949 84//925 47//948 -f 49//950 82//923 83//924 -f 49//950 83//924 48//949 -f 50//951 81//922 82//923 -f 50//951 82//923 49//950 -f 51//952 80//921 81//922 -f 51//952 81//922 50//951 -f 52//953 79//920 80//921 -f 52//953 80//921 51//952 -f 53//954 78//919 79//920 -f 53//954 79//920 52//953 -f 54//955 77//918 78//919 -f 54//955 78//919 53//954 -f 55//956 76//917 77//918 -f 55//956 77//918 54//955 -f 56//957 75//916 76//917 -f 56//957 76//917 55//956 -f 57//958 74//915 75//916 -f 57//958 75//916 56//957 -f 58//959 73//914 74//915 -f 58//959 74//915 57//958 -f 59//960 72//913 73//914 -f 59//960 73//914 58//959 -f 60//961 71//912 72//913 -f 60//961 72//913 59//960 -f 61//962 70//911 71//912 -f 61//962 71//912 60//961 -f 62//963 69//910 70//911 -f 62//963 70//911 61//962 -f 63//964 68//909 69//910 -f 63//964 69//910 62//963 -f 64//965 67//908 68//909 -f 64//965 68//909 63//964 -f 65//8 66//9 67//908 -f 65//8 67//908 64//965 -f 4//2 65//8 64//965 -f 4//2 64//965 5//966 -f 5//966 64//965 63//964 -f 5//966 63//964 6//967 -f 6//967 63//964 62//963 -f 6//967 62//963 7//968 -f 7//968 62//963 61//962 -f 7//968 61//962 8//969 -f 8//969 61//962 60//961 -f 8//969 60//961 9//970 -f 9//970 60//961 59//960 -f 9//970 59//960 10//971 -f 10//971 59//960 58//959 -f 10//971 58//959 11//972 -f 11//972 58//959 57//958 -f 11//972 57//958 12//973 -f 12//973 57//958 56//957 -f 12//973 56//957 13//974 -f 13//974 56//957 55//956 -f 13//974 55//956 14//975 -f 14//975 55//956 54//955 -f 14//975 54//955 15//976 -f 15//976 54//955 53//954 -f 15//976 53//954 16//977 -f 16//977 53//954 52//953 -f 16//977 52//953 17//978 -f 17//978 52//953 51//952 -f 17//978 51//952 18//979 -f 18//979 51//952 50//951 -f 18//979 50//951 19//980 -f 19//980 50//951 49//950 -f 19//980 49//950 20//981 -f 20//981 49//950 48//949 -f 20//981 48//949 21//982 -f 21//982 48//949 47//948 -f 21//982 47//948 22//983 -f 22//983 47//948 46//947 -f 22//983 46//947 23//984 -f 23//984 46//947 45//946 -f 23//984 45//946 24//985 -f 24//985 45//946 44//945 -f 24//985 44//945 25//986 -f 25//986 44//945 43//944 -f 25//986 43//944 26//987 -f 26//987 43//944 42//943 -f 26//987 42//943 27//988 -f 27//988 42//943 41//942 -f 27//988 41//942 28//989 -f 28//989 41//942 40//941 -f 28//989 40//941 29//990 -f 29//990 40//941 39//940 -f 29//990 39//940 30//991 -f 30//991 39//940 38//939 -f 30//991 38//939 31//992 -f 31//992 38//939 37//938 -f 31//992 37//938 32//993 -f 32//993 37//938 36//937 -f 32//993 36//937 33//994 -f 33//994 36//937 35//7 -f 33//994 35//7 34//4 -f 966//123 33//994 34//4 -f 966//123 34//4 2//6 -f 967//121 32//993 33//994 -f 967//121 33//994 966//123 -f 968//119 31//992 32//993 -f 968//119 32//993 967//121 -f 969//117 30//991 31//992 -f 969//117 31//992 968//119 -f 970//116 29//990 30//991 -f 970//116 30//991 969//117 -f 971//113 28//989 29//990 -f 971//113 29//990 970//116 -f 972//111 27//988 28//989 -f 972//111 28//989 971//113 -f 3//109 26//987 27//988 -f 3//109 27//988 972//111 -f 973//108 25//986 26//987 -f 973//108 26//987 3//109 -f 974//106 24//985 25//986 -f 974//106 25//986 973//108 -f 975//103 23//984 24//985 -f 975//103 24//985 974//106 -f 976//101 22//983 23//984 -f 976//101 23//984 975//103 -f 977//100 21//982 22//983 -f 977//100 22//983 976//101 -f 978//97 20//981 21//982 -f 978//97 21//982 977//100 -f 979//96 19//980 20//981 -f 979//96 20//981 978//97 -f 980//93 18//979 979//96 -f 18//979 19//980 979//96 -f 981//92 17//978 18//979 -f 981//92 18//979 980//93 -f 982//89 16//977 981//92 -f 16//977 17//978 981//92 -f 983//88 15//976 16//977 -f 983//88 16//977 982//89 -f 984//85 14//975 983//88 -f 14//975 15//976 983//88 -f 985//83 13//974 984//85 -f 13//974 14//975 984//85 -f 986//82 12//973 985//83 -f 12//973 13//974 985//83 -f 987//79 11//972 986//82 -f 11//972 12//973 986//82 -f 988//77 10//971 987//79 -f 10//971 11//972 987//79 -f 989//75 9//970 988//77 -f 9//970 10//971 988//77 -f 990//73 8//969 989//75 -f 8//969 9//970 989//75 -f 991//71 7//968 990//73 -f 7//968 8//969 990//73 -f 992//69 6//967 991//71 -f 6//967 7//968 991//71 -f 993//67 5//966 992//69 -f 5//966 6//967 992//69 -f 994//3 4//2 993//67 -f 4//2 5//966 993//67 diff --git a/Orange/widgets/utils/plot/primitives/triangle.obj b/Orange/widgets/utils/plot/primitives/triangle.obj deleted file mode 100644 index 4acd157f1c4..00000000000 --- a/Orange/widgets/utils/plot/primitives/triangle.obj +++ /dev/null @@ -1,8 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -1.000000 -1.000000 0.000000 -v 1.000000 -1.000000 0.000000 -v 0.000000 1.000000 -0.000000 -usemtl Material.001 -s off -f 1 3 2 diff --git a/Orange/widgets/utils/plot/primitives/triangle_edges.obj b/Orange/widgets/utils/plot/primitives/triangle_edges.obj deleted file mode 100644 index f9aa37f1647..00000000000 --- a/Orange/widgets/utils/plot/primitives/triangle_edges.obj +++ /dev/null @@ -1,8 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -1.000000 -1.000000 0.000000 -v 1.000000 -1.000000 0.000000 -v 0.000000 1.000000 -0.000000 -f 1 2 -f 2 3 -f 1 3 diff --git a/Orange/widgets/utils/plot/primitives/wedge.obj b/Orange/widgets/utils/plot/primitives/wedge.obj deleted file mode 100644 index 1d050dca4a5..00000000000 --- a/Orange/widgets/utils/plot/primitives/wedge.obj +++ /dev/null @@ -1,18 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v 1.000000 -1.000000 -1.000000 -v 1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 -1.000000 -v 1.000000 1.000000 -1.000000 -v 0.999999 1.000000 1.000001 -usemtl Material -s off -f 2 6 3 -f 1 4 5 -f 3 6 5 -f 3 5 4 -f 1 5 2 -f 5 6 2 -f 1 2 3 -f 1 3 4 diff --git a/Orange/widgets/utils/plot/primitives/wedge_2d.obj b/Orange/widgets/utils/plot/primitives/wedge_2d.obj deleted file mode 100644 index 744dc82d50f..00000000000 --- a/Orange/widgets/utils/plot/primitives/wedge_2d.obj +++ /dev/null @@ -1,8 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -1.000000 -1.000000 0.000000 -v 1.000000 -1.000000 0.000000 -v -0.999999 1.000000 -0.000001 -usemtl Material.002 -s off -f 1 3 2 diff --git a/Orange/widgets/utils/plot/primitives/wedge_2d_edges.obj b/Orange/widgets/utils/plot/primitives/wedge_2d_edges.obj deleted file mode 100644 index c6272b5fd6f..00000000000 --- a/Orange/widgets/utils/plot/primitives/wedge_2d_edges.obj +++ /dev/null @@ -1,9 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -o Mesh_wedge_2d -v -1.000000 -1.000000 0.000000 -v -0.999999 1.000000 -0.000001 -v 1.000000 -1.000000 0.000000 -f 1 3 -f 2 3 -f 1 2 diff --git a/Orange/widgets/utils/plot/primitives/wedge_edges.obj b/Orange/widgets/utils/plot/primitives/wedge_edges.obj deleted file mode 100644 index 818d718aea0..00000000000 --- a/Orange/widgets/utils/plot/primitives/wedge_edges.obj +++ /dev/null @@ -1,18 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -o Mesh_wedge -v 1.000000 -1.000000 1.000000 -v 0.999999 1.000000 1.000001 -v -1.000000 -1.000000 1.000000 -v 1.000000 -1.000000 -1.000000 -v -1.000000 -1.000000 -1.000000 -v 1.000000 1.000000 -1.000000 -f 3 5 -f 2 3 -f 1 2 -f 1 3 -f 5 6 -f 1 4 -f 2 6 -f 4 6 -f 4 5 diff --git a/Orange/widgets/utils/plot/primitives/xcross.obj b/Orange/widgets/utils/plot/primitives/xcross.obj deleted file mode 100644 index f4718a9cc65..00000000000 --- a/Orange/widgets/utils/plot/primitives/xcross.obj +++ /dev/null @@ -1,72 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -0.919239 -0.494975 -0.446899 -v -0.919239 -0.494975 0.446899 -v 0.494975 0.919239 -0.446899 -v 0.494974 0.919239 0.446899 -v -0.496742 -0.917471 -0.446899 -v -0.496742 -0.917471 0.446899 -v 0.917471 0.496742 -0.446899 -v 0.917471 0.496743 0.446900 -v 0.919239 -0.494975 -0.446899 -v 0.919239 -0.494974 0.446900 -v -0.494975 0.919239 0.446899 -v -0.494975 0.919239 -0.446899 -v -0.000000 0.424264 0.446899 -v 0.000000 0.424264 -0.446899 -v 0.422496 0.001768 0.446899 -v 0.422497 0.001768 -0.446899 -v 0.496743 -0.917471 -0.446899 -v 0.496742 -0.917471 0.446899 -v -0.917471 0.496743 0.446899 -v -0.917471 0.496742 -0.446899 -v -0.422496 0.001768 0.446899 -v -0.422496 0.001768 -0.446899 -v -0.000000 -0.420728 0.446899 -v 0.000000 -0.420729 -0.446899 -usemtl Material -s off -f 17 18 24 -f 18 23 24 -f 5 24 23 -f 5 23 6 -f 1 2 22 -f 2 21 22 -f 19 20 21 -f 20 22 21 -f 3 14 13 -f 3 13 4 -f 11 13 12 -f 13 14 12 -f 7 8 15 -f 7 15 16 -f 9 16 10 -f 16 15 10 -f 22 14 24 -f 14 16 24 -f 1 22 24 -f 1 24 5 -f 24 16 17 -f 16 9 17 -f 21 2 23 -f 2 6 23 -f 13 21 15 -f 21 23 15 -f 15 23 10 -f 23 18 10 -f 20 12 14 -f 20 14 22 -f 11 19 13 -f 19 21 13 -f 19 11 12 -f 19 12 20 -f 17 9 18 -f 9 10 18 -f 14 3 7 -f 14 7 16 -f 4 13 8 -f 13 15 8 -f 7 3 8 -f 3 4 8 -f 1 5 6 -f 1 6 2 diff --git a/Orange/widgets/utils/plot/primitives/xcross_2d.obj b/Orange/widgets/utils/plot/primitives/xcross_2d.obj deleted file mode 100644 index 4367c72b9b5..00000000000 --- a/Orange/widgets/utils/plot/primitives/xcross_2d.obj +++ /dev/null @@ -1,26 +0,0 @@ -# Blender3D v249 OBJ File: -# www.blender3d.org -v -0.000000 -0.494975 0.919239 -v -0.000000 0.919239 -0.494974 -v -0.000000 -0.917471 0.496742 -v 0.000001 0.496743 -0.917471 -v 0.000001 -0.494974 -0.919239 -v -0.000000 0.919239 0.494975 -v -0.000000 0.424264 -0.000000 -v -0.000000 0.001768 -0.422496 -v -0.000000 -0.917471 -0.496742 -v -0.000000 0.496743 0.917471 -v -0.000000 0.001768 0.422496 -v -0.000000 -0.420728 -0.000000 -usemtl Material.001 -s off -f 11 1 12 -f 1 3 12 -f 7 11 8 -f 11 12 8 -f 8 12 5 -f 12 9 5 -f 6 10 7 -f 10 11 7 -f 2 7 4 -f 7 8 4 diff --git a/Orange/widgets/utils/plot/primitives/xcross_2d_edges.obj b/Orange/widgets/utils/plot/primitives/xcross_2d_edges.obj deleted file mode 100644 index f34ddde6102..00000000000 --- a/Orange/widgets/utils/plot/primitives/xcross_2d_edges.obj +++ /dev/null @@ -1,27 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -o Mesh_xcross_2d -v 0.000000 0.001768 0.422496 -v 0.000000 -0.494975 0.919239 -v 0.000000 -0.420728 0.000000 -v 0.000000 -0.917471 0.496742 -v 0.000000 0.424264 -0.000000 -v 0.000000 0.001768 -0.422496 -v 0.000001 -0.494974 -0.919239 -v 0.000000 -0.917471 -0.496742 -v 0.000000 0.919239 0.494975 -v 0.000000 0.496743 0.917471 -v 0.000000 0.919239 -0.494974 -v 0.000001 0.496743 -0.917471 -f 6 7 -f 2 4 -f 5 9 -f 11 12 -f 9 10 -f 1 10 -f 7 8 -f 5 11 -f 3 4 -f 3 8 -f 1 2 -f 6 12 diff --git a/Orange/widgets/utils/plot/primitives/xcross_edges.obj b/Orange/widgets/utils/plot/primitives/xcross_edges.obj deleted file mode 100644 index 6ad4ceafc3d..00000000000 --- a/Orange/widgets/utils/plot/primitives/xcross_edges.obj +++ /dev/null @@ -1,63 +0,0 @@ -# Blender v2.56 (sub 0) OBJ File: 'untitled.blend' -# www.blender.org -o Mesh_xcross -v 0.496743 -0.917471 -0.446899 -v 0.496742 -0.917471 0.446899 -v 0.000000 -0.420729 -0.446899 -v 0.000000 -0.420728 0.446899 -v -0.496742 -0.917471 -0.446899 -v -0.496742 -0.917471 0.446899 -v -0.919239 -0.494975 -0.446899 -v -0.919239 -0.494975 0.446899 -v -0.422496 0.001768 -0.446899 -v -0.422496 0.001768 0.446899 -v -0.917471 0.496743 0.446899 -v -0.917471 0.496742 -0.446899 -v 0.494975 0.919239 -0.446899 -v 0.000000 0.424264 -0.446899 -v 0.000000 0.424264 0.446899 -v 0.494974 0.919239 0.446899 -v -0.494975 0.919239 0.446899 -v -0.494975 0.919239 -0.446899 -v 0.917471 0.496742 -0.446899 -v 0.917471 0.496743 0.446900 -v 0.422496 0.001768 0.446899 -v 0.422497 0.001768 -0.446899 -v 0.919239 -0.494975 -0.446899 -v 0.919239 -0.494974 0.446900 -f 5 7 -f 22 23 -f 16 20 -f 12 18 -f 13 16 -f 8 10 -f 11 17 -f 5 6 -f 21 24 -f 2 24 -f 9 10 -f 2 4 -f 15 16 -f 3 5 -f 14 18 -f 7 8 -f 15 17 -f 17 18 -f 19 22 -f 1 23 -f 4 6 -f 7 9 -f 13 19 -f 3 4 -f 19 20 -f 21 22 -f 1 3 -f 6 8 -f 11 12 -f 20 21 -f 1 2 -f 10 11 -f 23 24 -f 13 14 -f 9 12 -f 14 15 diff --git a/Orange/widgets/utils/plot/symbol.fs b/Orange/widgets/utils/plot/symbol.fs deleted file mode 100644 index b8d2e5428da..00000000000 --- a/Orange/widgets/utils/plot/symbol.fs +++ /dev/null @@ -1,6 +0,0 @@ -varying vec4 var_color; - -void main(void) -{ - gl_FragColor = var_color; -} diff --git a/Orange/widgets/utils/plot/symbol.vs b/Orange/widgets/utils/plot/symbol.vs deleted file mode 100644 index 54cb15c65ae..00000000000 --- a/Orange/widgets/utils/plot/symbol.vs +++ /dev/null @@ -1,94 +0,0 @@ -// Each example is drawn using a symbol constructed out -// of triangles. Each vertex is specified by its offset -// from the center point, example's position, color, normal -// and index (stored in .w components of position and offset). -attribute vec4 position; -attribute vec4 offset; -attribute vec3 color; -attribute vec3 normal; - -varying vec4 var_color; - -uniform bool use_2d_symbols; -uniform bool encode_color; -uniform bool hide_outside; -uniform bool fade_outside; -uniform vec4 force_color; -uniform vec2 alpha_value; // vec2 instead of float, fixing a bug on windows - // (setUniformValue with float crashes) -uniform vec2 symbol_scale; - -uniform vec3 scale; -uniform vec3 translation; - -uniform mat4 modelview; -uniform mat4 projection; - -void main(void) { - vec3 offset_rotated = offset.xyz; - offset_rotated.x *= symbol_scale.x; - offset_rotated.y *= symbol_scale.x; - offset_rotated.z *= symbol_scale.x; - - if (use_2d_symbols) { - // Calculate inverse of rotations (in this case, inverse - // is actually just transpose), so that polygons face - // camera all the time. - mat3 invs; - - invs[0][0] = modelview[0][0]; - invs[0][1] = modelview[1][0]; - invs[0][2] = modelview[2][0]; - - invs[1][0] = modelview[0][1]; - invs[1][1] = modelview[1][1]; - invs[1][2] = modelview[2][1]; - - invs[2][0] = modelview[0][2]; - invs[2][1] = modelview[1][2]; - invs[2][2] = modelview[2][2]; - - offset_rotated = invs * offset_rotated; - } - - vec3 pos = position.xyz; - pos += translation; - pos *= scale; - vec4 off_pos = vec4(pos+offset_rotated, 1.); - - gl_Position = projection * modelview * off_pos; - - if (force_color.a > 0.) - { - var_color = force_color; - } - else if (encode_color) - { - var_color = vec4(position.w, offset.w, 0, 0); - } - else - { - pos = abs(pos); - float manhattan_distance = max(max(pos.x, pos.y), pos.z)+0.5; - float a = alpha_value.x; - - if (fade_outside) - a = min(pow(min(1., 1. / manhattan_distance), 5.), a); - - if (use_2d_symbols) - { - // No lighting for 2d symbols. - var_color = vec4(color, a); - } - else - { - // Calculate the amount of lighting this triangle receives (diffuse component only). - // The calculations are physically wrong, but look better. - vec3 light_direction = normalize(vec3(1., 1., 0.5)); - float diffuse = max(0., dot(normalize((modelview * vec4(normal, 0.)).xyz), light_direction)); - var_color = vec4(color+diffuse*0.7, a); - } - if (manhattan_distance > 1. && hide_outside) - var_color.a = 0.; - } -} diff --git a/Orange/widgets/utils/scaling.py b/Orange/widgets/utils/scaling.py index 62bda5c70d2..bf33f81e7b6 100644 --- a/Orange/widgets/utils/scaling.py +++ b/Orange/widgets/utils/scaling.py @@ -5,7 +5,6 @@ from Orange.data import Domain from Orange.statistics.basic_stats import DomainBasicStats from Orange.widgets.settings import Setting -from Orange.widgets.utils import checksum from Orange.widgets.utils.datacaching import getCached, setCached @@ -88,9 +87,7 @@ def _compute_jittered_data(self): col[above_1] = 2 - col[above_1] # noinspection PyAttributeOutsideInit - def set_data(self, data, skip_if_same=False, no_data=False): - if skip_if_same and checksum(data) == checksum(self.data): - return + def set_data(self, data, *, no_data=False): self._reset_data() if data is None: return diff --git a/Orange/widgets/utils/toolbar.py b/Orange/widgets/utils/toolbar.py deleted file mode 100644 index 7cdfee2aec4..00000000000 --- a/Orange/widgets/utils/toolbar.py +++ /dev/null @@ -1,159 +0,0 @@ -import os.path - -from AnyQt.QtCore import Qt -from AnyQt.QtGui import QIcon -from AnyQt.QtWidgets import QToolButton, QGroupBox, QHBoxLayout - -from Orange.widgets import gui -from Orange.widgets.settings import Setting - -SPACE = 0 -ZOOM = 1 -PAN = 2 -SELECT = 3 -RECTANGLE = 4 -POLYGON = 5 -REMOVE_LAST = 6 -REMOVE_ALL = 7 -SEND_SELECTION = 8 -ZOOM_EXTENT = 9 -ZOOM_SELECTION = 10 - -# attr name used to store toolbars on a widget -TOOLBARS_STORE = "__toolbars" - -dlg_zoom = gui.resource_filename("icons/Dlg_zoom.png") -dlg_zoom_selection = gui.resource_filename("icons/Dlg_zoom_selection.png") -dlg_pan = gui.resource_filename("icons/Dlg_pan_hand.png") -dlg_select = gui.resource_filename("icons/Dlg_arrow.png") -dlg_rect = gui.resource_filename("icons/Dlg_rect.png") -dlg_poly = gui.resource_filename("icons/Dlg_poly.png") -dlg_zoom_extent = gui.resource_filename("icons/Dlg_zoom_extent.png") -dlg_undo = gui.resource_filename("icons/Dlg_undo.png") -dlg_clear = gui.resource_filename("icons/Dlg_clear.png") -dlg_send = gui.resource_filename("icons/Dlg_send.png") -dlg_browseRectangle = gui.resource_filename("icons/Dlg_browseRectangle.png") -dlg_browseCircle = gui.resource_filename("icons/Dlg_browseCircle.png") - - -class ToolbarButton: - def __init__(self, text, attr_name, ext_attr_name, - icon=None, cursor=None, selectable=False): - self.text = text - self.attr_name = attr_name - self.ext_attr_name = ext_attr_name - self.icon = icon - self.cursor = cursor - self.selectable = selectable - - -class ZoomSelectToolbar(QGroupBox): - DefaultButtons = ZOOM, RECTANGLE, POLYGON, SPACE, REMOVE_LAST, REMOVE_ALL, SEND_SELECTION - SelectButtons = SELECT, RECTANGLE, POLYGON, SPACE, REMOVE_LAST, REMOVE_ALL, SEND_SELECTION - NavigateButtons = ZOOM, ZOOM_EXTENT, ZOOM_SELECTION, SPACE, PAN - - selected_button = Setting(0) - - @property - def builtin_functions(self): - if ZoomSelectToolbar._builtin_functions is None: - ZoomSelectToolbar._builtin_functions = ( - None, - ToolbarButton("Zooming", "buttonZoom", "activate_zooming", - QIcon(dlg_zoom), Qt.ArrowCursor, True), - ToolbarButton("Panning", "buttonPan", "activate_panning", - QIcon(dlg_pan), Qt.OpenHandCursor, True), - ToolbarButton("Selection", "buttonSelect", "activate_selection", - QIcon(dlg_select), Qt.CrossCursor, True), - ToolbarButton("Rectangle selection", "buttonSelectRect", "activate_rectangle_selection", - QIcon(dlg_rect), Qt.CrossCursor, True), - ToolbarButton("Polygon selection", "buttonSelectPoly", "activate_polygon_selection", - QIcon(dlg_poly), Qt.CrossCursor, True), - ToolbarButton("Remove last selection", "buttonRemoveLastSelection", "removeLastSelection", - QIcon(dlg_undo)), - ToolbarButton("Remove all selections", "buttonRemoveAllSelections", "removeAllSelections", - QIcon(dlg_clear)), - ToolbarButton("Send selections", "buttonSendSelections", "sendData", - QIcon(dlg_send)), - ToolbarButton("Zoom to extent", "buttonZoomExtent", "zoomExtent", - QIcon(dlg_zoom_extent)), - ToolbarButton("Zoom selection", "buttonZoomSelection", "zoomSelection", - QIcon(dlg_zoom_selection)) - ) - return ZoomSelectToolbar._builtin_functions - _builtin_functions = None - - def __init__(self, widget, parent, graph, auto_send=False, buttons=DefaultButtons, name="Zoom / Select"): - widget.settingsHandler.initialize(self) - QGroupBox.__init__(self, name, parent) - - self.widget_toolbars = self.register_toolbar(widget) - - self.widget = widget - self.graph = graph - self.auto_send = auto_send - - self.setup_toolbar(parent) - self.buttons = self.add_buttons(buttons) - - self.action(self.selected_button) - - def register_toolbar(self, widget): - if hasattr(widget, TOOLBARS_STORE): - getattr(widget, TOOLBARS_STORE).append(self) - else: - setattr(widget, TOOLBARS_STORE, [self]) - return getattr(widget, TOOLBARS_STORE) - - def setup_toolbar(self, parent): - self.setLayout(QHBoxLayout()) - self.layout().setContentsMargins(6, 6, 6, 6) - self.layout().setSpacing(4) - if parent.layout() is not None: - parent.layout().addWidget(self) - - def add_buttons(self, buttons): - buttons = [self.builtin_functions[f] if isinstance(f, int) else f for f in buttons] - for i, button in enumerate(buttons): - if not button: - self.add_spacing() - else: - self.add_button(button, action=lambda x=i: self.action(x)) - return buttons - - def add_spacing(self): - self.layout().addSpacing(10) - - def add_button(self, button: ToolbarButton, action=None): - btn = QToolButton(self) - btn.setMinimumSize(30, 30) - if self.layout() is not None: - self.layout().addWidget(btn) - btn.setCheckable(button.selectable) - if action: - btn.clicked.connect(action) - if button.icon: - btn.setIcon(button.icon) - btn.setToolTip(button.text) - - setattr(self, button.attr_name, btn) - if button.attr_name == "buttonSendSelections": - btn.setEnabled(not self.auto_send) - - return btn - - def action(self, button_idx): - button = self.buttons[button_idx] - if not isinstance(button, ToolbarButton): - return - - if button.selectable: - self.selected_button = button_idx - for toolbar in self.widget_toolbars: - for ti, tbutton in enumerate(toolbar.buttons): - if isinstance(tbutton, ToolbarButton) and tbutton.selectable: - getattr(toolbar, tbutton.attr_name).setChecked(self == toolbar and ti == button_idx) - getattr(self.graph, button.ext_attr_name)() - - if button.cursor is not None: - self.graph.setCursor(button.cursor) diff --git a/Orange/widgets/utils/webview.py b/Orange/widgets/utils/webview.py index b9a6396aa35..536ca1e8648 100644 --- a/Orange/widgets/utils/webview.py +++ b/Orange/widgets/utils/webview.py @@ -100,6 +100,7 @@ def __init__(self, parent=None, bridge=None, *, debug=False, **kwargs): sizeHint=QSize(500, 400), sizePolicy=QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding), + visible=False, **kwargs) self.bridge = bridge self.debug = debug @@ -139,6 +140,10 @@ def __init__(self, parent=None, bridge=None, *, debug=False, **kwargs): self.page().setWebChannel(channel) + # Delay showing the widget. Observation indicate this results in + # fewer window resizes. + QTimer.singleShot(1, self.show) + def _onloadJS(self, code, name='', injection_point=QWebEngineScript.DocumentReady): script = QWebEngineScript() script.setName(name or ('script_' + str(random())[2:])) diff --git a/Orange/widgets/visualize/owparallelcoordinates.py b/Orange/widgets/visualize/owparallelcoordinates.py deleted file mode 100644 index f3f9a45b4af..00000000000 --- a/Orange/widgets/visualize/owparallelcoordinates.py +++ /dev/null @@ -1,227 +0,0 @@ -import sys - -from AnyQt.QtCore import Qt - -from Orange.canvas.registry.description import Default -import Orange.data -from Orange.data import Table -from Orange.data.sql.table import SqlTable, LARGE_TABLE, DEFAULT_SAMPLE_TIME -from Orange.widgets.gui import attributeIconDict -from Orange.widgets.settings import DomainContextHandler, Setting, SettingProvider -from Orange.widgets.utils.plot import xBottom -from Orange.widgets.utils import checksum -from Orange.widgets.utils.toolbar import ZoomSelectToolbar, ZOOM, PAN, SPACE, REMOVE_ALL, SEND_SELECTION -from Orange.widgets.visualize.owparallelgraph import OWParallelGraph -from Orange.widgets.visualize.owviswidget import OWVisWidget -from Orange.widgets.widget import AttributeList -from Orange.widgets import gui, widget - - -class OWParallelCoordinates(OWVisWidget): - name = "Parallel Coordinates" - description = "Parallel coordinates display of multi-dimensional data." - icon = "icons/ParallelCoordinates.svg" - priority = 900 - inputs = [("Data", Orange.data.Table, 'set_data', Default), - ("Data Subset", Orange.data.Table, 'set_subset_data'), - ("Features", AttributeList, 'set_shown_attributes')] - outputs = [("Selected Data", Orange.data.Table, widget.Default), - ("Other Data", Orange.data.Table), - ("Features", AttributeList)] - - settingsHandler = DomainContextHandler() - - show_all_attributes = Setting(default=False) - auto_send_selection = Setting(default=True) - - jitterSizeNums = [0, 2, 5, 10, 15, 20, 30] - - graph = SettingProvider(OWParallelGraph) - zoom_select_toolbar = SettingProvider(ZoomSelectToolbar) - - __ignore_updates = True - - def __init__(self): - super().__init__() - #add a graph widget - self.graph = OWParallelGraph(self, self.mainArea) - self.mainArea.layout().addWidget(self.graph) - - self.data = None - self.subset_data = None - self.discrete_attribute_order = "Unordered" - self.continuous_attribute_order = "Unordered" - self.middle_labels = "Correlations" - - self.create_control_panel() - - self.resize(900, 700) - - self.__ignore_updates = False - - #noinspection PyAttributeOutsideInit - def create_control_panel(self): - self.add_attribute_selection_area(self.controlArea) - self.add_visual_settings(self.controlArea) - #self.add_annotation_settings(self. controlArea) - self.add_group_settings(self.controlArea) - self.add_zoom_select_toolbar(self.controlArea) - self.icons = attributeIconDict - - def add_attribute_selection_area(self, parent): - super().add_attribute_selection_area(parent) - self.shown_attributes_listbox.itemDoubleClicked.connect(self.flip_attribute) - - #noinspection PyAttributeOutsideInit - def add_zoom_select_toolbar(self, parent): - buttons = (ZOOM, PAN, SPACE, REMOVE_ALL, SEND_SELECTION) - self.zoom_select_toolbar = ZoomSelectToolbar(self, parent, self.graph, self.auto_send_selection, - buttons=buttons) - self.zoom_select_toolbar.buttonSendSelections.clicked.connect(self.sendSelections) - - def add_visual_settings(self, parent): - box = gui.vBox(parent, "Visual Settings") - gui.checkBox(box, self, 'graph.show_attr_values', 'Show attribute values', callback=self.update_graph) - gui.checkBox(box, self, 'graph.use_splines', 'Show splines', callback=self.update_graph, - tooltip="Show lines using splines") - self.graph.gui.show_legend_check_box(box) - - def add_annotation_settings(self, parent): - box = gui.vBox(parent, "Statistical Information") - gui.comboBox(box, self, "graph.show_statistics", label="Statistics: ", - orientation=Qt.Horizontal, labelWidth=90, - items=["No statistics", "Means, deviations", "Median, quartiles"], callback=self.update_graph, - sendSelectedValue=False, valueType=int) - gui.checkBox(box, self, 'graph.show_distributions', 'Show distributions', callback=self.update_graph, - tooltip="Show bars with distribution of class values " - "(only for categorical attributes)") - - def add_group_settings(self, parent): - box = gui.vBox(parent, "Groups") - box2 = gui.hBox(box) - gui.checkBox(box2, self, "graph.group_lines", "Group lines into", tooltip="Show clusters instead of lines", - callback=self.update_graph) - gui.spin(box2, self, "graph.number_of_groups", 0, 30, callback=self.update_graph) - gui.label(box2, self, "groups") - box2 = gui.hBox(box) - gui.spin(box2, self, "graph.number_of_steps", 0, 100, label="In no more than", callback=self.update_graph) - gui.label(box2, self, "steps") - - def flip_attribute(self, item): - if self.graph.flip_attribute(str(item.text())): - self.update_graph() - self.information() - else: - self.information("To flip a numeric feature, disable" - "'Global value scaling'") - - def update_graph(self): - self.graph.update_data(self.shown_attributes) - - def increase_axes_distance(self): - m, M = self.graph.bounds_for_axis(xBottom) - if (M - m) == 0: - return # we have not yet updated the axes (self.graph.updateAxes()) - self.graph.setAxisScale(xBottom, m, M - (M - m) / 10., 1) - self.graph.replot() - - def decrease_axes_distance(self): - m, M = self.graph.bounds_for_axis(xBottom) - if (M - m) == 0: - return # we have not yet updated the axes (self.graph.updateAxes()) - - self.graph.setAxisScale(xBottom, m, min(len(self.graph.attributes) - 1, M + (M - m) / 10.), 1) - self.graph.replot() - - # ------------- SIGNALS -------------------------- - # receive new data and update all fields - def set_data(self, data): - if type(data) == SqlTable and data.approx_len() > LARGE_TABLE: - data = data.sample_time(DEFAULT_SAMPLE_TIME) - - if data and (not bool(data) or len(data.domain) == 0): - data = None - if checksum(data) == checksum(self.data): - return # check if the new data set is the same as the old one - - self.__ignore_updates = True - self.closeContext() - same_domain = self.data and data and data.domain.checksum() == self.data.domain.checksum() # preserve attribute choice if the domain is the same - self.data = data - - if not same_domain: - self.shown_attributes = None - - self.openContext(self.data) - self.__ignore_updates = False - - def set_subset_data(self, subset_data): - self.subset_data = subset_data - - # attribute selection signal - list of attributes to show - def set_shown_attributes(self, shown_attributes): - self.new_shown_attributes = shown_attributes - - new_shown_attributes = None - - # this is called by OWBaseWidget after setData and setSubsetData are called. this way the graph is updated only once - def handleNewSignals(self): - self.__ignore_updates = True - self.graph.set_data(self.data, self.subset_data) - if self.new_shown_attributes: - self.shown_attributes = self.new_shown_attributes - self.new_shown_attributes = None - else: - self.shown_attributes = self._shown_attributes - # trust open context to take care of this? - # self.shown_attributes = None - self.update_graph() - self.sendSelections() - self.__ignore_updates = False - - def send_shown_attributes(self, attributes=None): - if attributes is None: - attributes = self.shown_attributes - self.send("Features", attributes) - - def selectionChanged(self): - self.zoom_select_toolbar.buttonSendSelections.setEnabled(not self.auto_send_selection) - if self.auto_send_selection: - self.sendSelections() - - # send signals with selected and unselected examples as two datasets - def sendSelections(self): - return - - # jittering options - def setJitteringSize(self): - self.graph.rescale_data() - self.update_graph() - - def attributes_changed(self): - if not self.__ignore_updates: - self.graph.removeAllSelections() - self.update_graph() - - self.send_shown_attributes() - - def send_report(self): - self.report_plot(self.graph) - - -#test widget appearance -if __name__ == "__main__": - from AnyQt.QtWidgets import QApplication - a = QApplication(sys.argv) - ow = OWParallelCoordinates() - ow.show() - ow.graph.group_lines = True - ow.graph.number_of_groups = 10 - ow.graph.number_of_steps = 30 - data = Orange.data.Table("iris") - ow.set_data(data) - ow.handleNewSignals() - - a.exec_() - - ow.saveSettings() diff --git a/Orange/widgets/visualize/owparallelgraph.py b/Orange/widgets/visualize/owparallelgraph.py deleted file mode 100644 index b64ae7b87fe..00000000000 --- a/Orange/widgets/visualize/owparallelgraph.py +++ /dev/null @@ -1,660 +0,0 @@ -# -# OWParallelGraph.py -# -import math -from collections import defaultdict - -import numpy as np - -from AnyQt.QtCore import QLineF, Qt, QEvent, QRect, QPoint, QPointF -from AnyQt.QtGui import QPixmap, QColor, QBrush, QPen, QPainterPath, QPolygonF -from AnyQt.QtWidgets import QToolTip, QGraphicsPathItem, QGraphicsPolygonItem - -from Orange.statistics.contingency import get_contingencies, get_contingency -from Orange.widgets import gui -from Orange.widgets.settings import Setting -from Orange.widgets.utils.colorpalette import ContinuousPaletteGenerator -from Orange.widgets.utils.plot import OWPlot, UserAxis, AxisStart, AxisEnd, OWCurve, OWPoint, PolygonCurve, \ - xBottom, yLeft, OWPlotItem -from Orange.widgets.utils.scaling import ScaleData -from Orange.widgets.utils import get_variable_values_sorted -from Orange.widgets.visualize.utils.lac import lac, create_contingencies - -NO_STATISTICS = 0 -MEANS = 1 -MEDIAN = 2 - -VISIBLE = 196 -TRANSPARENT = 64 -HIDDEN = 0 - - -class OWParallelGraph(OWPlot, ScaleData): - show_distributions = Setting(False) - show_attr_values = Setting(True) - show_statistics = Setting(default=False) - - group_lines = Setting(default=False) - number_of_groups = Setting(default=5) - number_of_steps = Setting(default=30) - - use_splines = Setting(False) - alpha_value = Setting(150) - alpha_value_2 = Setting(150) - - def __init__(self, widget, parent=None, name=None): - OWPlot.__init__(self, parent, name, axes=[], widget=widget) - ScaleData.__init__(self) - - self.update_antialiasing(False) - - self.widget = widget - self.last_selected_curve = None - self.enableGridXB(0) - self.enableGridYL(0) - self.domain_contingencies = None - self.auto_update_axes = 1 - self.old_legend_keys = [] - self.selection_conditions = {} - self.attributes = [] - self.visualized_mid_labels = [] - self.attribute_indices = [] - self.valid_data = [] - self.groups = {} - self.colors = None - - self.selected_examples = [] - self.unselected_examples = [] - self.bottom_pixmap = QPixmap(gui.resource_filename("icons/upgreenarrow.png")) - self.top_pixmap = QPixmap(gui.resource_filename("icons/downgreenarrow.png")) - - def set_data(self, data, subset_data=None, **args): - self.start_progress() - self.set_progress(1, 100) - self.data = data - self.have_data = True - self.domain_contingencies = None - self.groups = {} - OWPlot.setData(self, data) - ScaleData.set_data(self, data, no_data=True, **args) - self._compute_domain_data_stat() - self.end_progress() - - - def update_data(self, attributes, mid_labels=None): - old_selection_conditions = self.selection_conditions - - self.clear() - - if self.data is None: - return - if len(attributes) < 2: - return - - if self.show_statistics: - self.alpha_value = TRANSPARENT - self.alpha_value_2 = VISIBLE - else: - self.alpha_value = VISIBLE - self.alpha_value_2 = TRANSPARENT - - self.attributes = attributes - self.attribute_indices = [self.domain.index(name) - for name in self.attributes] - self.valid_data = self.get_valid_list(self.attribute_indices) - - self.visualized_mid_labels = mid_labels - self.add_relevant_selections(old_selection_conditions) - - class_var = self.domain.class_var - if not class_var: - self.colors = None - elif class_var.is_discrete: - self.colors = class_var.colors - elif class_var.is_continuous: - self.colors = ContinuousPaletteGenerator(*class_var.colors) - - if self.group_lines: - self.show_statistics = False - self.draw_groups() - else: - self.show_statistics = False - self.draw_curves() - self.draw_distributions() - self.draw_axes() - self.draw_statistics() - self.draw_mid_labels(mid_labels) - self.draw_legend() - - self.replot() - - def add_relevant_selections(self, old_selection_conditions): - """Keep only conditions related to the currently visualized attributes""" - for name, value in old_selection_conditions.items(): - if name in self.attributes: - self.selection_conditions[name] = value - - def draw_axes(self): - self.remove_all_axes() - for i in range(len(self.attributes)): - axis_id = UserAxis + i - a = self.add_axis(axis_id, line=QLineF(i, 0, i, 1), arrows=AxisStart | AxisEnd, - zoomable=True) - a.always_horizontal_text = True - a.max_text_width = 100 - a.title_margin = -10 - a.text_margin = 0 - a.setZValue(5) - self.set_axis_title(axis_id, self.domain[self.attributes[i]].name) - self.set_show_axis_title(axis_id, self.show_attr_values) - if self.show_attr_values: - attr = self.domain[self.attributes[i]] - if attr.is_continuous: - self.set_axis_scale(axis_id, self.attr_values[attr][0], - self.attr_values[attr][1]) - elif attr.is_discrete: - attribute_values = get_variable_values_sorted(self.domain[self.attributes[i]]) - attr_len = len(attribute_values) - values = [float(1.0 + 2.0 * j) / float(2 * attr_len) for j in range(len(attribute_values))] - a.set_bounds((0, 1)) - self.set_axis_labels(axis_id, labels=attribute_values, values=values) - - def draw_curves(self): - conditions = {name: self.attributes.index(name) for name in self.selection_conditions.keys()} - - def is_selected(example): - return all(self.selection_conditions[name][0] <= example[index] <= self.selection_conditions[name][1] - for (name, index) in list(conditions.items())) - - selected_curves = defaultdict(list) - background_curves = defaultdict(list) - - diff, mins = [], [] - for i in self.attribute_indices: - var = self.domain[i] - if var.is_discrete: - diff.append(len(var.values)) - mins.append(-0.5) - else: - diff.append(self.domain_data_stat[i].max - self.domain_data_stat[i].min or 1) - mins.append(self.domain_data_stat[i].min) - - def scale_row(row): - return [(x - m) / d for x, m, d in zip(row, mins, diff)] - - for row_idx, row in enumerate(self.data[:, self.attribute_indices]): - if any(np.isnan(v) for v in row.x): - continue - - color = tuple(self.select_color(row_idx)) - - if is_selected(row): - color += (self.alpha_value,) - selected_curves[color].extend(scale_row(row)) - self.selected_examples.append(row_idx) - else: - color += (self.alpha_value_2,) - background_curves[color].extend(row) - self.unselected_examples.append(row_idx) - - self._draw_curves(selected_curves) - self._draw_curves(background_curves) - - def select_color(self, row_index): - domain = self.data.domain - if domain.class_var is None: - return 0, 0, 0 - class_val = self.data[row_index, domain.index(domain.class_var)] - if domain.has_continuous_class: - return self.continuous_palette.getRGB(class_val) - else: - return self.colors[int(class_val)] - - def _draw_curves(self, selected_curves): - n_attr = len(self.attributes) - for color, y_values in sorted(selected_curves.items()): - n_rows = int(len(y_values) / n_attr) - x_values = list(range(n_attr)) * n_rows - curve = OWCurve() - curve.set_style(OWCurve.Lines) - curve.set_color(QColor(*color)) - curve.set_segment_length(n_attr) - curve.set_data(x_values, y_values) - curve.attach(self) - - def draw_groups(self): - phis, mus, sigmas = self.compute_groups() - - diff, mins = [], [] - for i in self.attribute_indices: - var = self.domain[i] - if var.is_discrete: - diff.append(len(var.values)) - mins.append(-0.5) - else: - diff.append(self.domain_data_stat[i].max - self.domain_data_stat[i].min or 1) - mins.append(self.domain_data_stat[i].min) - - for j, (phi, cluster_mus, cluster_sigma) in enumerate(zip(phis, mus, sigmas)): - for i, (mu1, sigma1, mu2, sigma2), in enumerate( - zip(cluster_mus, cluster_sigma, cluster_mus[1:], cluster_sigma[1:])): - nmu1 = (mu1 - mins[i]) / diff[i] - nmu2 = (mu2 - mins[i + 1]) / diff[i + 1] - nsigma1 = math.sqrt(sigma1) / diff[i] - nsigma2 = math.sqrt(sigma2) / diff[i + 1] - - polygon = ParallelCoordinatePolygon(i, nmu1, nmu2, nsigma1, nsigma2, phi, - tuple(self.colors[j]) if self.colors - else (0, 0, 0)) - polygon.attach(self) - - self.replot() - - def compute_groups(self): - key = (tuple(self.attributes), self.number_of_groups, self.number_of_steps) - if key not in self.groups: - def callback(i, n): - self.set_progress(i, 2*n) - - conts = create_contingencies(self.data[:, self.attribute_indices], callback=callback) - self.set_progress(50, 100) - w, mu, sigma, phi = lac(conts, self.number_of_groups, self.number_of_steps) - self.set_progress(100, 100) - self.groups[key] = list(map(np.nan_to_num, (phi, mu, sigma))) - return self.groups[key] - - def draw_legend(self): - domain = self.data.domain - class_var = domain.class_var - if class_var: - if class_var.is_discrete: - self.legend().clear() - values = get_variable_values_sorted(class_var) - for i, value in enumerate(values): - self.legend().add_item( - class_var.name, value, - OWPoint(OWPoint.Rect, QColor(*self.colors[i]), 10)) - else: - values = self.attr_values[class_var] - decimals = class_var.number_of_decimals - self.legend().add_color_gradient( - class_var.name, ["%%.%df" % decimals % v for v in values]) - else: - self.legend().clear() - self.old_legend_keys = [] - - def draw_mid_labels(self, mid_labels): - if mid_labels: - for j in range(len(mid_labels)): - self.addMarker(mid_labels[j], j + 0.5, 1.0, alignment=Qt.AlignCenter | Qt.AlignTop) - - def draw_statistics(self): - """Draw lines that represent standard deviation or quartiles""" - return # TODO: Implement using BasicStats - if self.show_statistics and self.data is not None: - data = [] - domain = self.data.domain - for attr_idx in self.attribute_indices: - if not self.domain[attr_idx].is_continuous: - data.append([()]) - continue # only for continuous attributes - - if not domain.class_var or domain.has_continuous_class: - if self.show_statistics == MEANS: - m = self.domain_data_stat[attr_idx].mean - dev = self.domain_data_stat[attr_idx].var - data.append([(m - dev, m, m + dev)]) - elif self.show_statistics == MEDIAN: - data.append([(0, 0, 0)]); continue - - sorted_array = np.sort(attr_values) - if len(sorted_array) > 0: - data.append([(sorted_array[int(len(sorted_array) / 4.0)], - sorted_array[int(len(sorted_array) / 2.0)], - sorted_array[int(len(sorted_array) * 0.75)])]) - else: - data.append([(0, 0, 0)]) - else: - curr = [] - class_values = get_variable_values_sorted(self.domain.class_var) - class_index = self.domain.index(self.domain.class_var) - - for c in range(len(class_values)): - attr_values = self.data[attr_idx, self.data[class_index] == c] - attr_values = attr_values[~np.isnan(attr_values)] - - if len(attr_values) == 0: - curr.append((0, 0, 0)) - continue - if self.show_statistics == MEANS: - m = attr_values.mean() - dev = attr_values.std() - curr.append((m - dev, m, m + dev)) - elif self.show_statistics == MEDIAN: - sorted_array = np.sort(attr_values) - curr.append((sorted_array[int(len(attr_values) / 4.0)], - sorted_array[int(len(attr_values) / 2.0)], - sorted_array[int(len(attr_values) * 0.75)])) - data.append(curr) - - # draw vertical lines - for i in range(len(data)): - for c in range(len(data[i])): - if data[i][c] == (): - continue - x = i - 0.03 * (len(data[i]) - 1) / 2.0 + c * 0.03 - col = QColor(self.discrete_palette[c]) - col.setAlpha(self.alpha_value_2) - self.add_curve("", col, col, 3, OWCurve.Lines, OWPoint.NoSymbol, xData=[x, x, x], - yData=[data[i][c][0], data[i][c][1], data[i][c][2]], lineWidth=4) - self.add_curve("", col, col, 1, OWCurve.Lines, OWPoint.NoSymbol, xData=[x - 0.03, x + 0.03], - yData=[data[i][c][0], data[i][c][0]], lineWidth=4) - self.add_curve("", col, col, 1, OWCurve.Lines, OWPoint.NoSymbol, xData=[x - 0.03, x + 0.03], - yData=[data[i][c][1], data[i][c][1]], lineWidth=4) - self.add_curve("", col, col, 1, OWCurve.Lines, OWPoint.NoSymbol, xData=[x - 0.03, x + 0.03], - yData=[data[i][c][2], data[i][c][2]], lineWidth=4) - - # draw lines with mean/median values - if not domain.class_var or domain.has_continuous_class: - class_count = 1 - else: - class_count = len(self.domain.class_var.values) - for c in range(class_count): - diff = - 0.03 * (class_count - 1) / 2.0 + c * 0.03 - ys = [] - xs = [] - for i in range(len(data)): - if data[i] != [()]: - ys.append(data[i][c][1]) - xs.append(i + diff) - else: - if len(xs) > 1: - col = QColor(self.discrete_palette[c]) - col.setAlpha(self.alpha_value_2) - self.add_curve("", col, col, 1, OWCurve.Lines, - OWPoint.NoSymbol, xData=xs, yData=ys, lineWidth=4) - xs = [] - ys = [] - col = QColor(self.discrete_palette[c]) - col.setAlpha(self.alpha_value_2) - self.add_curve("", col, col, 1, OWCurve.Lines, - OWPoint.NoSymbol, xData=xs, yData=ys, lineWidth=4) - - def draw_distributions(self): - """Draw distributions with discrete attributes""" - if not (self.show_distributions and self.data is not None and self.domain.has_discrete_class): - return - class_count = len(self.domain.class_var.values) - class_ = self.domain.class_var - - # we create a hash table of possible class values (happens only if we have a discrete class) - if self.domain_contingencies is None: - self.domain_contingencies = dict( - zip([attr for attr in self.domain if attr.is_discrete], - get_contingencies(self.data, skipContinuous=True))) - self.domain_contingencies[class_] = get_contingency(self.data, class_, class_) - - max_count = max([contingency.max() for contingency in self.domain_contingencies.values()] or [1]) - sorted_class_values = get_variable_values_sorted(self.domain.class_var) - - for axis_idx, attr_idx in enumerate(self.attribute_indices): - attr = self.domain[attr_idx] - if attr.is_discrete: - continue - - contingency = self.domain_contingencies[attr] - attr_len = len(attr.values) - - # we create a hash table of variable values and their indices - sorted_variable_values = get_variable_values_sorted(attr) - - # create bar curve - for j in range(attr_len): - attribute_value = sorted_variable_values[j] - value_count = contingency[:, attribute_value] - - for i in range(class_count): - class_value = sorted_class_values[i] - - color = QColor(*self.colors[i]) - color.setAlpha(self.alpha_value) - - width = float(value_count[class_value] * 0.5) / float(max_count) - y_off = float(1.0 + 2.0 * j) / float(2 * attr_len) - height = 0.7 / float(class_count * attr_len) - - y_low_bottom = y_off + float(class_count * height) / 2.0 - i * height - curve = PolygonCurve(QPen(color), - QBrush(color), - xData=[axis_idx, axis_idx + width, - axis_idx + width, axis_idx], - yData=[y_low_bottom, y_low_bottom, y_low_bottom - height, - y_low_bottom - height], - tooltip=attr.name) - curve.attach(self) - - # handle tooltip events - def event(self, ev): - if ev.type() == QEvent.ToolTip: - x = self.inv_transform(xBottom, ev.pos().x()) - y = self.inv_transform(yLeft, ev.pos().y()) - - canvas_position = self.mapToScene(ev.pos()) - x_float = self.inv_transform(xBottom, canvas_position.x()) - contact, (index, pos) = self.testArrowContact(int(round(x_float)), canvas_position.x(), - canvas_position.y()) - if contact: - attr = self.domain[self.attributes[index]] - if attr.is_continuous: - condition = self.selection_conditions.get(attr.name, [0, 1]) - val = self.attr_values[attr][0] + condition[pos] * ( - self.attr_values[attr][1] - self.attr_values[attr][0]) - str_val = attr.name + "= %%.%df" % attr.number_of_decimals % val - QToolTip.showText(ev.globalPos(), str_val) - else: - for curve in self.items(): - if type(curve) == PolygonCurve and \ - curve.boundingRect().contains(x, y) and \ - getattr(curve, "tooltip", None): - (name, value, total, dist) = curve.tooltip - count = sum([v[1] for v in dist]) - if count == 0: - continue - tooltip_text = "Attribute: %s
Value: %s
" \ - "Total instances: %i (%.1f%%)
" \ - "Class distribution:
" % ( - name, value, count, 100.0 * count / float(total)) - for (val, n) in dist: - tooltip_text += "    %s : %i (%.1f%%)
" % ( - val, n, 100.0 * float(n) / float(count)) - QToolTip.showText(ev.globalPos(), tooltip_text[:-4]) - - elif ev.type() == QEvent.MouseMove: - QToolTip.hideText() - - return OWPlot.event(self, ev) - - def testArrowContact(self, indices, x, y): - if type(indices) != list: indices = [indices] - for index in indices: - if index >= len(self.attributes) or index < 0: - continue - int_x = self.transform(xBottom, index) - bottom = self.transform(yLeft, - self.selection_conditions.get(self.attributes[index], [0, 1])[0]) - bottom_rect = QRect(int_x - self.bottom_pixmap.width() / 2, bottom, self.bottom_pixmap.width(), - self.bottom_pixmap.height()) - if bottom_rect.contains(QPoint(x, y)): - return 1, (index, 0) - top = self.transform(yLeft, - self.selection_conditions.get(self.attributes[index], [0, 1])[1]) - top_rect = QRect(int_x - self.top_pixmap.width() / 2, top - self.top_pixmap.height(), - self.top_pixmap.width(), - self.top_pixmap.height()) - if top_rect.contains(QPoint(x, y)): - return 1, (index, 1) - return 0, (0, 0) - - def mousePressEvent(self, e): - canvas_position = self.mapToScene(e.pos()) - x = self.inv_transform(xBottom, canvas_position.x()) - contact, info = self.testArrowContact(int(round(x)), canvas_position.x(), canvas_position.y()) - - if contact: - self.pressed_arrow = info - else: - OWPlot.mousePressEvent(self, e) - - def mouseMoveEvent(self, e): - if hasattr(self, "pressed_arrow"): - canvas_position = self.mapToScene(e.pos()) - y = min(1, max(0, self.inv_transform(yLeft, canvas_position.y()))) - index, pos = self.pressed_arrow - attr = self.domain[self.attributes[index]] - old_condition = self.selection_conditions.get(attr.name, [0, 1]) - old_condition[pos] = y - self.selection_conditions[attr.name] = old_condition - self.update_data(self.attributes, self.visualized_mid_labels) - - if attr.is_continuous: - val = self.attr_values[attr][0] + old_condition[pos] * ( - self.attr_values[attr][1] - self.attr_values[attr][0]) - strVal = attr.name + "= %.2f" % val - QToolTip.showText(e.globalPos(), strVal) - if self.sendSelectionOnUpdate and self.auto_send_selection_callback: - self.auto_send_selection_callback() - - else: - OWPlot.mouseMoveEvent(self, e) - - def mouseReleaseEvent(self, e): - if hasattr(self, "pressed_arrow"): - del self.pressed_arrow - else: - OWPlot.mouseReleaseEvent(self, e) - - def zoom_to_rect(self, r): - r.setTop(self.graph_area.top()) - r.setBottom(self.graph_area.bottom()) - super().zoom_to_rect(r) - - def removeAllSelections(self, send=1): - self.selection_conditions = {} - self.update_data(self.attributes, self.visualized_mid_labels) - - # draw the curves and the selection conditions - def drawCanvas(self, painter): - OWPlot.drawCanvas(self, painter) - for i in range( - int(max(0, math.floor(self.axisScaleDiv(xBottom).interval().minValue()))), - int(min(len(self.attributes), - math.ceil(self.axisScaleDiv(xBottom).interval().maxValue()) + 1))): - bottom, top = self.selection_conditions.get(self.attributes[i], (0, 1)) - painter.drawPixmap(self.transform(xBottom, i) - self.bottom_pixmap.width() / 2, - self.transform(yLeft, bottom), self.bottom_pixmap) - painter.drawPixmap(self.transform(xBottom, i) - self.top_pixmap.width() / 2, - self.transform(yLeft, top) - self.top_pixmap.height(), self.top_pixmap) - - def auto_send_selection_callback(self): - pass - - def clear(self): - super().clear() - - self.attributes = [] - self.visualized_mid_labels = [] - self.selected_examples = [] - self.unselected_examples = [] - self.selection_conditions = {} - - -# #################################################################### -# a curve that is able to draw several series of lines -class ParallelCoordinatesCurve(OWCurve): - def __init__(self, n_attributes, y_values, color, name=""): - OWCurve.__init__(self, tooltip=name) - self._item = QGraphicsPathItem(self) - self.path = QPainterPath() - self.fitted = False - - self.n_attributes = n_attributes - self.n_rows = int(len(y_values) / n_attributes) - - self.set_style(OWCurve.Lines) - if isinstance(color, tuple): - self.set_pen(QPen(QColor(*color))) - else: - self.set_pen(QPen(QColor(color))) - - x_values = list(range(n_attributes)) * self.n_rows - self.set_data(x_values, y_values) - - def update_properties(self): - self.redraw_path() - - def redraw_path(self): - self.path = QPainterPath() - for segment in self.segment(self.data()): - if self.fitted: - self.draw_cubic_path(segment) - else: - self.draw_normal_path(segment) - self._item.setPath(self.graph_transform().map(self.path)) - self._item.setPen(self.pen()) - - def segment(self, data): - for i in range(self.n_rows): - yield data[i * self.n_attributes:(i + 1) * self.n_attributes] - - def draw_cubic_path(self, segment): - for (x1, y1), (x2, y2) in zip(segment, segment[1:]): - self.path.moveTo(x1, y1) - self.path.cubicTo(QPointF(x1 + 0.5, y1), - QPointF(x2 - 0.5, y2), QPointF(x2, y2)) - - def draw_normal_path(self, segment): - if not segment: - return - - x, y = segment[0] - self.path.moveTo(x, y) - for x, y in segment[1:]: - self.path.lineTo(x, y) - - -class ParallelCoordinatePolygon(OWPlotItem): - def __init__(self, i, mu1, mu2, sigma1, sigma2, phi, color): - OWPlotItem.__init__(self) - self.outer_box = QGraphicsPolygonItem(self) - self.inner_box = QGraphicsPolygonItem(self) - - self.i = i - self.mu1 = mu1 - self.mu2 = mu2 - self.sigma1 = sigma1 - self.sigma2 = sigma2 - self.phi = phi - - self.twosigmapolygon = QPolygonF([ - QPointF(i, mu1 - sigma1), QPointF(i, mu1 + sigma1), - QPointF(i + 1, mu2 + sigma2), QPointF(i + 1, mu2 - sigma2), - QPointF(i, mu1 - sigma1) - ]) - - self.sigmapolygon = QPolygonF([ - QPointF(i, mu1 - .5 * sigma1), QPointF(i, mu1 + .5 * sigma1), - QPointF(i + 1, mu2 + .5 * sigma2), QPointF(i + 1, mu2 - .5 * sigma2), - QPointF(i, mu1 - .5 * sigma1) - ]) - - if isinstance(color, tuple): - color = QColor(*color) - color.setAlphaF(.3) - self.outer_box.setBrush(color) - self.outer_box.setPen(QColor(0, 0, 0, 0)) - self.inner_box.setBrush(color) - self.inner_box.setPen(color) - - def update_properties(self): - self.outer_box.setPolygon(self.graph_transform().map(self.twosigmapolygon)) - self.inner_box.setPolygon(self.graph_transform().map(self.sigmapolygon)) diff --git a/Orange/widgets/visualize/owviswidget.py b/Orange/widgets/visualize/owviswidget.py deleted file mode 100644 index 128df1e88a6..00000000000 --- a/Orange/widgets/visualize/owviswidget.py +++ /dev/null @@ -1,107 +0,0 @@ -import os - -from AnyQt.QtWidgets import QListWidget - -from Orange.widgets import gui -from Orange.widgets.settings import ContextSetting -from Orange.widgets.widget import OWWidget -from Orange.widgets.utils import vartype - -ICON_UP = gui.resource_filename("icons/Dlg_up3.png") -ICON_DOWN = gui.resource_filename("icons/Dlg_down3.png") - - -class OWVisWidget(OWWidget): - _shown_attributes = ContextSetting(default=[], required=ContextSetting.REQUIRED, - selected='selected_shown', reservoir="_hidden_attributes") - # Setting above will override these fields - _hidden_attributes = () - selected_shown = () - selected_hidden = () - - @property - def shown_attributes(self): - return [a[0] for a in self._shown_attributes] - - @shown_attributes.setter - def shown_attributes(self, value): - shown = [] - hidden = [] - - domain = self.get_data_domain() - attr_info = lambda a: (a.name, vartype(a)) - if domain: - if value: - shown = value if isinstance(value[0], tuple) else [attr_info(domain[a]) for a in value] - hidden = [x for x in [attr_info(domain[a]) for a in domain.attributes] if x not in shown] - else: - shown = [attr_info(a) for a in domain.attributes] - if not self.show_all_attributes: - hidden = shown[10:] - shown = shown[:10] - - if domain.class_var and attr_info(domain.class_var) not in shown: - hidden += [attr_info(domain.class_var)] - - self._shown_attributes = shown - self._hidden_attributes = hidden - self.selected_hidden = [] - self.selected_shown = [] - - self.trigger_attributes_changed() - - @property - def hidden_attributes(self): - return [a[0] for a in self._hidden_attributes] - - __attribute_selection_area_initialized = False - - #noinspection PyAttributeOutsideInit - def add_attribute_selection_area(self, parent): - self.add_shown_attributes(parent) - self.add_hidden_attributes(parent) - self.__attribute_selection_area_initialized = True - - self.trigger_attributes_changed() - - #noinspection PyAttributeOutsideInit - def add_shown_attributes(self, parent): - self.shown_attributes_area = gui.vBox(parent, " Shown attributes ") - self.shown_attributes_listbox = gui.listBox( - self.shown_attributes_area, self, "selected_shown", "_shown_attributes", - dragDropCallback=self.trigger_attributes_changed, - enableDragDrop=True, selectionMode=QListWidget.ExtendedSelection) - - #noinspection PyAttributeOutsideInit - def add_hidden_attributes(self, parent): - self.hidden_attributes_area = gui.vBox(parent, " Hidden attributes ") - self.hidden_attributes_listbox = gui.listBox( - self.hidden_attributes_area, self, "selected_hidden", - "_hidden_attributes", - dragDropCallback=self.trigger_attributes_changed, - enableDragDrop=True, selectionMode=QListWidget.ExtendedSelection) - - def get_data_domain(self): - if hasattr(self, "data") and self.data: - return self.data.domain - else: - return None - - def trigger_attributes_changed(self): - if not self.__attribute_selection_area_initialized: - # Some components trigger this event during the initialization. - # We ignore those requests, a separate event will be triggered - # manually when everything is initialized. - return - - self.attributes_changed() - - def closeContext(self): - super().closeContext() - - self.data = None - self.shown_attributes = None - - # "Events" - def attributes_changed(self): - pass diff --git a/setup.py b/setup.py index 587d4525c3c..5da7481f831 100755 --- a/setup.py +++ b/setup.py @@ -197,8 +197,6 @@ def configuration(parent_package='', top_path=None): "_owmap/*"], "Orange.widgets.unsupervised": ["icons/*.svg"], "Orange.widgets.utils": ["_webview/*.js"], - "Orange.widgets.utils.plot": ["*.fs", "*.gs", "*.vs"], - "Orange.widgets.utils.plot.primitives": ["*.obj"], "Orange.tests": ["xlsx_files/*.xlsx", "*.tab", "*.basket", "*.csv"] }