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"]
}