Commit 16acdad6 authored by sumpfralle's avatar sumpfralle

separated some more OpenGL visualization items to separate plugins

fixed various small issues


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@1124 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent 0b5ac044
<?xml version="1.0"?>
<interface>
<!-- interface-requires gtk+ 2.12 -->
<!-- interface-naming-policy project-wide -->
<object class="GtkWindow" id="OpenGLWindow">
<property name="title" translatable="yes">PyCAM Visualization</property>
<property name="role">pycam-view</property>
<child>
<object class="GtkVBox" id="OpenGLBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">1</property>
<child>
<object class="GtkHBox" id="ViewControls">
<property name="visible">True</property>
<property name="spacing">4</property>
<child>
<object class="GtkToolbar" id="ViewItems">
<property name="visible">True</property>
<property name="toolbar_style">text</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVSeparator" id="vseparator13">
<property name="visible">True</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox38">
<property name="visible">True</property>
<child>
<object class="GtkToolButton" id="ResetView">
<property name="visible">True</property>
<property name="label" translatable="yes">Center</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="FrontView">
<property name="visible">True</property>
<property name="label" translatable="yes">Front</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="BackView">
<property name="visible">True</property>
<property name="label" translatable="yes">Back</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="LeftView">
<property name="visible">True</property>
<property name="label" translatable="yes">Left</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="RightView">
<property name="visible">True</property>
<property name="label" translatable="yes">Right</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="TopView">
<property name="visible">True</property>
<property name="label" translatable="yes">Top</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="BottomView">
<property name="visible">True</property>
<property name="label" translatable="yes">Bottom</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">6</property>
</packing>
</child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkLinkButton" id="3DViewHelp">
<property name="label">gtk-help</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
<property name="use_stock">True</property>
<property name="xalign">1</property>
<property name="uri">http://sourceforge.net/apps/mediawiki/pycam/index.php?title=3D_View</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator16">
<property name="visible">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment22">
<property name="visible">True</property>
<property name="left_padding">2</property>
<property name="right_padding">2</property>
<child>
<object class="GtkTable" id="view3ddimension">
<property name="visible">True</property>
<property name="n_rows">2</property>
<property name="n_columns">6</property>
<property name="column_spacing">3</property>
<child>
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Model corners:</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkVSeparator" id="vseparator16">
<property name="visible">True</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkVSeparator" id="vseparator17">
<property name="visible">True</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="left_attach">4</property>
<property name="right_attach">5</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox42">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="ModelCornerXMin">
<property name="visible">True</property>
<property name="label" translatable="yes">xmin</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ModelCornerXSpaces">
<property name="visible">True</property>
<property name="label" translatable="yes">..</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ModelCornerXMax">
<property name="visible">True</property>
<property name="label" translatable="yes">xmax</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox44">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="ModelCornerZMin">
<property name="visible">True</property>
<property name="label" translatable="yes">zmin</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ModelCornerZSpaces">
<property name="visible">True</property>
<property name="label" translatable="yes">..</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ModelCornerZMax">
<property name="visible">True</property>
<property name="label" translatable="yes">zmax</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">5</property>
<property name="right_attach">6</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="DimensionLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Bounding Box:</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox40">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="model_dim_x_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&#x394;x:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="model_dim_x">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">x</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox41">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="model_dim_z_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&#x394;z:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="model_dim_z">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">z</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">5</property>
<property name="right_attach">6</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox43">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="model_dim_y_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&#x394;y:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="model_dim_y">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">y</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox45">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="ModelCornerYMin">
<property name="visible">True</property>
<property name="label" translatable="yes">ymin</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ModelCornerYSpaces">
<property name="visible">True</property>
<property name="label" translatable="yes">..</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ModelCornerYMax">
<property name="visible">True</property>
<property name="label" translatable="yes">ymax</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
<object class="GtkToggleAction" id="Toggle3DView">
<property name="label">3D _View Window</property>
<property name="short_label">3D _View Window</property>
<property name="tooltip">Display the model in the 3D view window.</property>
</object>
</interface>
......@@ -98,463 +98,6 @@
</object>
</child>
</object>
<object class="GtkWindow" id="view3dwindow">
<property name="title" translatable="yes">PyCAM Visualization</property>
<property name="role">pycam-view</property>
<child>
<object class="GtkVBox" id="view3dbox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">1</property>
<child>
<object class="GtkHBox" id="ViewControls">
<property name="visible">True</property>
<property name="spacing">4</property>
<child>
<object class="GtkToolbar" id="ViewItems">
<property name="visible">True</property>
<property name="tooltip_text" translatable="yes">Configure the list of visible items.</property>
<property name="toolbar_style">text</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVSeparator" id="vseparator13">
<property name="visible">True</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox38">
<property name="visible">True</property>
<child>
<object class="GtkToolButton" id="Reset View">
<property name="visible">True</property>
<property name="label" translatable="yes">Center</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="Front View">
<property name="visible">True</property>
<property name="label" translatable="yes">Front</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="Back View">
<property name="visible">True</property>
<property name="label" translatable="yes">Back</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="Left View">
<property name="visible">True</property>
<property name="label" translatable="yes">Left</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="Right View">
<property name="visible">True</property>
<property name="label" translatable="yes">Right</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="Top View">
<property name="visible">True</property>
<property name="label" translatable="yes">Top</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="Bottom View">
<property name="visible">True</property>
<property name="label" translatable="yes">Bottom</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">6</property>
</packing>
</child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkLinkButton" id="3DViewHelp">
<property name="label">gtk-help</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="has_tooltip">True</property>
<property name="relief">none</property>
<property name="use_stock">True</property>
<property name="xalign">1</property>
<property name="uri">http://sourceforge.net/apps/mediawiki/pycam/index.php?title=3D_View</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator16">
<property name="visible">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment22">
<property name="visible">True</property>
<property name="left_padding">2</property>
<property name="right_padding">2</property>
<child>
<object class="GtkTable" id="view3ddimension">
<property name="visible">True</property>
<property name="n_rows">2</property>
<property name="n_columns">6</property>
<property name="column_spacing">3</property>
<child>
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Model corners:</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkVSeparator" id="vseparator16">
<property name="visible">True</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkVSeparator" id="vseparator17">
<property name="visible">True</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="left_attach">4</property>
<property name="right_attach">5</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox42">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="ModelCornerXMin">
<property name="visible">True</property>
<property name="label" translatable="yes">xmin</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ModelCornerXSpaces">
<property name="visible">True</property>
<property name="label" translatable="yes">..</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ModelCornerXMax">
<property name="visible">True</property>
<property name="label" translatable="yes">xmax</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox44">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="ModelCornerZMin">
<property name="visible">True</property>
<property name="label" translatable="yes">zmin</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ModelCornerZSpaces">
<property name="visible">True</property>
<property name="label" translatable="yes">..</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ModelCornerZMax">
<property name="visible">True</property>
<property name="label" translatable="yes">zmax</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">5</property>
<property name="right_attach">6</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="DimensionLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Bounding Box:</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox40">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="model_dim_x_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&#x394;x:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="model_dim_x">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">x</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox41">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="model_dim_z_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&#x394;z:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="model_dim_z">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">z</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">5</property>
<property name="right_attach">6</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox43">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="model_dim_y_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&#x394;y:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="model_dim_y">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">y</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox45">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="ModelCornerYMin">
<property name="visible">True</property>
<property name="label" translatable="yes">ymin</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ModelCornerYSpaces">
<property name="visible">True</property>
<property name="label" translatable="yes">..</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ModelCornerYMax">
<property name="visible">True</property>
<property name="label" translatable="yes">ymax</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
<object class="GtkAdjustment" id="DrillProgressMaxFPS">
<property name="lower">1</property>
<property name="upper">25</property>
......@@ -606,10 +149,6 @@
<object class="GtkAction" id="WindowMenu">
<property name="label">_Windows</property>
</object>
<object class="GtkToggleAction" id="Toggle3DView">
<property name="label">3D _View Window</property>
<property name="tooltip">Display the model in the 3D view window.</property>
</object>
<object class="GtkAction" id="SaveAsModel">
<property name="label">Save Model _as ...</property>
<property name="short_label">Save Model as ...</property>
......
......@@ -20,51 +20,21 @@ You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
# careful import - otherwise pycam.Gui.Project will throw an exception
from pycam.Geometry.Point import Point
from pycam.Geometry.utils import sqrt
import pycam.Geometry.Model
import pycam.Utils.log
# careful import
try:
import gtk.gtkgl
import OpenGL.GL as GL
import OpenGL.GLU as GLU
import OpenGL.GLUT as GLUT
GL_ENABLED = True
except (ImportError, RuntimeError):
GL_ENABLED = False
pass
from pycam.Geometry.Point import Point
import pycam.Geometry.Matrix as Matrix
from pycam.Geometry.utils import sqrt, number
import pycam.Utils.log
import gtk
import pango
import math
# buttons for rotating, moving and zooming the model view window
BUTTON_ROTATE = gtk.gdk.BUTTON1_MASK
BUTTON_MOVE = gtk.gdk.BUTTON2_MASK
BUTTON_ZOOM = gtk.gdk.BUTTON3_MASK
BUTTON_RIGHT = 3
GTK_COLOR_MAX = 65535.0
# The length of the distance vector does not matter - it will be normalized and
# multiplied later anyway.
VIEWS = {
"reset": {"distance": (-1.0, -1.0, 1.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 0.0, 1.0), "znear": 0.1, "zfar": 1000.0, "fovy": 30.0},
"top": {"distance": (0.0, 0.0, 1.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 1.0, 0.0), "znear": 0.1, "zfar": 1000.0, "fovy": 30.0},
"bottom": {"distance": (0.0, 0.0, -1.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 1.0, 0.0), "znear": 0.1, "zfar": 1000.0, "fovy": 30.0},
"left": {"distance": (-1.0, 0.0, 0.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 0.0, 1.0), "znear": 0.1, "zfar": 1000.0, "fovy": 30.0},
"right": {"distance": (1.0, 0.0, 0.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 0.0, 1.0), "znear": 0.1, "zfar": 1000.0, "fovy": 30.0},
"front": {"distance": (0.0, -1.0, 0.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 0.0, 1.0), "znear": 0.1, "zfar": 1000.0, "fovy": 30.0},
"back": {"distance": (0.0, 1.0, 0.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 0.0, 1.0), "znear": 0.1, "zfar": 1000.0, "fovy": 30.0},
}
log = pycam.Utils.log.get_logger()
......@@ -88,724 +58,6 @@ def connect_button_handlers(signal, original_button, derived_button):
original_button)
class Camera(object):
def __init__(self, settings, get_dim_func, view=None):
self.view = None
self.settings = settings
self._get_dim_func = get_dim_func
self.set_view(view)
def set_view(self, view=None):
if view is None:
self.view = VIEWS["reset"].copy()
else:
self.view = view.copy()
self.center_view()
self.auto_adjust_distance()
def center_view(self):
s = self.settings
# center the view on the object
self.view["center"] = ((s.get("maxx") + s.get("minx")) / 2,
(s.get("maxy") + s.get("miny")) / 2,
(s.get("maxz") + s.get("minz")) / 2)
def auto_adjust_distance(self):
s = self.settings
v = self.view
# adjust the distance to get a view of the whole object
dimx = s.get("maxx") - s.get("minx")
dimy = s.get("maxy") - s.get("miny")
dimz = s.get("maxz") - s.get("minz")
max_dim = max(max(dimx, dimy), dimz)
distv = Point(v["distance"][0], v["distance"][1],
v["distance"][2]).normalized()
# The multiplier "1.25" is based on experiments. 1.414 (sqrt(2)) should
# be roughly sufficient for showing the diagonal of any model.
distv = distv.mul((max_dim * 1.25) / number(math.sin(v["fovy"] / 2)))
self.view["distance"] = (distv.x, distv.y, distv.z)
# Adjust the "far" distance for the camera to make sure, that huge
# models (e.g. x=1000) are still visible.
self.view["zfar"] = 100 * max_dim
def scale_distance(self, scale):
if scale != 0:
scale = number(scale)
dist = self.view["distance"]
self.view["distance"] = (scale * dist[0], scale * dist[1],
scale * dist[2])
def get(self, key, default=None):
if (not self.view is None) and self.view.has_key(key):
return self.view[key]
else:
return default
def set(self, key, value):
self.view[key] = value
def move_camera_by_screen(self, x_move, y_move, max_model_shift):
""" move the camera acoording to a mouse movement
@type x_move: int
@value x_move: movement of the mouse along the x axis
@type y_move: int
@value y_move: movement of the mouse along the y axis
@type max_model_shift: float
@value max_model_shift: maximum shifting of the model view (e.g. for
x_move == screen width)
"""
factors_x, factors_y = self._get_axes_vectors()
width, height = self._get_screen_dimensions()
# relation of x/y movement to the respective screen dimension
win_x_rel = (-2 * x_move) / float(width) / math.sin(self.view["fovy"])
win_y_rel = (-2 * y_move) / float(height) / math.sin(self.view["fovy"])
# This code is completely arbitrarily based on trial-and-error for
# finding a nice movement speed for all distances.
# Anyone with a better approach should just fix this.
distance_vector = self.get("distance")
distance = float(sqrt(sum([dim ** 2 for dim in distance_vector])))
win_x_rel *= math.cos(win_x_rel / distance) ** 20
win_y_rel *= math.cos(win_y_rel / distance) ** 20
# update the model position that should be centered on the screen
old_center = self.view["center"]
new_center = []
for i in range(3):
new_center.append(old_center[i] \
+ max_model_shift * (number(win_x_rel) * factors_x[i] \
+ number(win_y_rel) * factors_y[i]))
self.view["center"] = tuple(new_center)
def rotate_camera_by_screen(self, start_x, start_y, end_x, end_y):
factors_x, factors_y = self._get_axes_vectors()
width, height = self._get_screen_dimensions()
# calculate rotation factors - based on the distance to the center
# (between -1 and 1)
rot_x_factor = (2.0 * start_x) / width - 1
rot_y_factor = (2.0 * start_y) / height - 1
# calculate rotation angles (between -90 and +90 degrees)
xdiff = end_x - start_x
ydiff = end_y - start_y
# compensate inverse rotation left/right side (around x axis) and
# top/bottom (around y axis)
if rot_x_factor < 0:
ydiff = -ydiff
if rot_y_factor > 0:
xdiff = -xdiff
rot_x_angle = rot_x_factor * math.pi * ydiff / height
rot_y_angle = rot_y_factor * math.pi * xdiff / width
# rotate around the "up" vector with the y-axis rotation
original_distance = self.view["distance"]
original_up = self.view["up"]
y_rot_matrix = Matrix.get_rotation_matrix_axis_angle(factors_y,
rot_y_angle)
new_distance = Matrix.multiply_vector_matrix(original_distance,
y_rot_matrix)
new_up = Matrix.multiply_vector_matrix(original_up, y_rot_matrix)
# rotate around the cross vector with the x-axis rotation
x_rot_matrix = Matrix.get_rotation_matrix_axis_angle(factors_x,
rot_x_angle)
new_distance = Matrix.multiply_vector_matrix(new_distance, x_rot_matrix)
new_up = Matrix.multiply_vector_matrix(new_up, x_rot_matrix)
self.view["distance"] = new_distance
self.view["up"] = new_up
def position_camera(self):
width, height = self._get_screen_dimensions()
prev_mode = GL.glGetIntegerv(GL.GL_MATRIX_MODE)
GL.glMatrixMode(GL.GL_PROJECTION)
GL.glLoadIdentity()
v = self.view
# position the light according to the current bounding box
light_pos = range(3)
values = {}
for model in self.settings.get("models").get_visible():
for key in ("minx", "miny", "minz", "maxx", "maxy", "maxz"):
if not key in values:
values[key] = []
values[key].append(getattr(model, key))
if values:
light_pos[0] = 2 * max(values["maxx"]) - min(values["minx"])
light_pos[1] = 2 * max(values["maxy"]) - min(values["miny"])
light_pos[2] = 2 * max(values["maxz"]) - min(values["minz"])
else:
light_pos = (0, 0, 0)
GL.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, (light_pos[0], light_pos[1],
light_pos[2], 0.0))
# position the camera
camera_position = (v["center"][0] + v["distance"][0],
v["center"][1] + v["distance"][1],
v["center"][2] + v["distance"][2])
# position a second light at camera position
GL.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, (camera_position[0],
camera_position[1], camera_position[2], 0.0))
if self.settings.get("view_perspective"):
# perspective view
GLU.gluPerspective(v["fovy"], (0.0 + width) / height, v["znear"],
v["zfar"])
else:
# parallel projection
# This distance calculation is completely based on trial-and-error.
distance = math.sqrt(sum([d ** 2 for d in v["distance"]]))
distance *= math.log(math.sqrt(width * height)) / math.log(10)
sin_factor = math.sin(v["fovy"] / 360.0 * math.pi) * distance
left = v["center"][0] - sin_factor
right = v["center"][0] + sin_factor
top = v["center"][1] + sin_factor
bottom = v["center"][1] - sin_factor
near = v["center"][2] - 2 * sin_factor
far = v["center"][2] + 2 * sin_factor
GL.glOrtho(left, right, bottom, top, near, far)
GLU.gluLookAt(camera_position[0], camera_position[1],
camera_position[2], v["center"][0], v["center"][1],
v["center"][2], v["up"][0], v["up"][1], v["up"][2])
GL.glMatrixMode(prev_mode)
def shift_view(self, x_dist=0, y_dist=0):
obj_dim = []
obj_dim.append(self.settings.get("maxx") - self.settings.get("minx"))
obj_dim.append(self.settings.get("maxy") - self.settings.get("miny"))
obj_dim.append(self.settings.get("maxz") - self.settings.get("minz"))
max_dim = max(obj_dim)
factor = 50
self.move_camera_by_screen(x_dist * factor, y_dist * factor, max_dim)
def zoom_in(self):
self.scale_distance(sqrt(0.5))
def zoom_out(self):
self.scale_distance(sqrt(2))
def _get_screen_dimensions(self):
return self._get_dim_func()
def _get_axes_vectors(self):
"""calculate the model vectors along the screen's x and y axes"""
# The "up" vector defines, in what proportion each axis of the model is
# in line with the screen's y axis.
v_up = self.view["up"]
factors_y = (number(v_up[0]), number(v_up[1]), number(v_up[2]))
# Calculate the proportion of each model axis according to the x axis of
# the screen.
distv = self.view["distance"]
distv = Point(distv[0], distv[1], distv[2]).normalized()
factors_x = distv.cross(Point(v_up[0], v_up[1], v_up[2])).normalized()
factors_x = (factors_x.x, factors_x.y, factors_x.z)
return (factors_x, factors_y)
class ModelViewWindowGL(object):
def __init__(self, gui, settings, notify_destroy=None, accel_group=None,
item_buttons=None, context_menu_actions=None):
# assume, that initialization will fail
self.gui = gui
self.window = self.gui.get_object("view3dwindow")
if not accel_group is None:
self.window.add_accel_group(accel_group)
self.initialized = False
self.busy = False
self.settings = settings
self.is_visible = False
# check if the 3D view is available
if GL_ENABLED:
self.enabled = True
else:
log.error("Failed to initialize the interactive 3D model view."
+ "\nPlease install 'python-gtkglext1' to enable it.")
self.enabled = False
return
self.mouse = {"start_pos": None, "button": None,
"event_timestamp": 0, "last_timestamp": 0,
"pressed_pos": None, "pressed_timestamp": 0,
"pressed_button": None}
self.notify_destroy_func = notify_destroy
self.window.connect("delete-event", self.destroy)
self.window.set_default_size(560, 400)
self._position = self.gui.get_object("ProjectWindow").get_position()
self._position = (self._position[0] + 100, self._position[1] + 100)
self.container = self.gui.get_object("view3dbox")
self.gui.get_object("Reset View").connect("clicked", self.rotate_view,
VIEWS["reset"])
self.gui.get_object("Left View").connect("clicked", self.rotate_view,
VIEWS["left"])
self.gui.get_object("Right View").connect("clicked", self.rotate_view,
VIEWS["right"])
self.gui.get_object("Front View").connect("clicked", self.rotate_view,
VIEWS["front"])
self.gui.get_object("Back View").connect("clicked", self.rotate_view,
VIEWS["back"])
self.gui.get_object("Top View").connect("clicked", self.rotate_view,
VIEWS["top"])
self.gui.get_object("Bottom View").connect("clicked", self.rotate_view,
VIEWS["bottom"])
# key binding
self.window.connect("key-press-event", self.key_handler)
# OpenGL stuff
glconfig = gtk.gdkgl.Config(mode=gtk.gdkgl.MODE_RGBA \
| gtk.gdkgl.MODE_DEPTH | gtk.gdkgl.MODE_DOUBLE)
self.area = gtk.gtkgl.DrawingArea(glconfig)
# first run; might also be important when doing other fancy
# gtk/gdk stuff
self.area.connect_after('realize', self.paint)
# called when a part of the screen is uncovered
self.area.connect('expose-event', self.paint)
# resize window
self.area.connect('configure-event', self._resize_window)
# catch mouse events
self.area.set_events(gtk.gdk.MOUSE | gtk.gdk.POINTER_MOTION_HINT_MASK \
| gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK \
| gtk.gdk.SCROLL_MASK)
self.area.connect("button-press-event", self.mouse_press_handler)
self.area.connect('motion-notify-event', self.mouse_handler)
self.area.connect("button-release-event", self.context_menu_handler)
self.area.connect("scroll-event", self.scroll_handler)
self.container.pack_end(self.area)
self.camera = Camera(self.settings, lambda: (self.area.allocation.width,
self.area.allocation.height))
# Color the dimension value according to the axes.
# For "y" axis: 100% green is too bright on light background - we
# reduce it a bit.
for color, names in (
(pango.AttrForeground(65535, 0, 0, 0, 100),
("model_dim_x_label", "model_dim_x", "ModelCornerXMax",
"ModelCornerXMin", "ModelCornerXSpaces")),
(pango.AttrForeground(0, 50000, 0, 0, 100),
("model_dim_y_label", "model_dim_y", "ModelCornerYMax",
"ModelCornerYMin", "ModelCornerYSpaces")),
(pango.AttrForeground(0, 0, 65535, 0, 100),
("model_dim_z_label", "model_dim_z", "ModelCornerZMax",
"ModelCornerZMin", "ModelCornerZSpaces"))):
attributes = pango.AttrList()
attributes.insert(color)
for name in names:
self.gui.get_object(name).set_attributes(attributes)
# add the items buttons (for configuring visible items)
if item_buttons:
items_button_container = self.gui.get_object("ViewItems")
for button in item_buttons:
new_checkbox = gtk.ToggleToolButton()
new_checkbox.set_label(button.get_label())
new_checkbox.set_active(button.get_active())
# Configure the two buttons (in "Preferences" and in the 3D view
# widget) to toggle each other. This is required for a
# consistent view of the setting.
connect_button_handlers("toggled", button, new_checkbox)
items_button_container.insert(new_checkbox, -1)
items_button_container.show_all()
# create the drop-down menu
if context_menu_actions:
action_group = gtk.ActionGroup("context")
for action in context_menu_actions:
action_group.add_action(action)
uimanager = gtk.UIManager()
# The "pos" parameter is optional since gtk 2.12 - we can
# remove it later.
uimanager.insert_action_group(action_group, pos=-1)
uimanager_template = '<ui><popup name="context">%s</popup></ui>'
uimanager_item_template = """<menuitem action="%s" />"""
uimanager_text = uimanager_template % "".join(
[uimanager_item_template % action.get_name()
for action in context_menu_actions])
uimanager.add_ui_from_string(uimanager_text)
self.context_menu = uimanager.get_widget("/context")
self.context_menu.insert(gtk.SeparatorMenuItem(), 0)
else:
self.context_menu = gtk.Menu()
for index, button in enumerate(item_buttons):
new_item = gtk.CheckMenuItem(button.get_label())
new_item.set_active(button.get_active())
connect_button_handlers("toggled", button, new_item)
self.context_menu.insert(new_item, index)
self.context_menu.show_all()
else:
self.context_menu = None
# show the window
self.area.show()
self.container.show()
self.show()
def show(self):
self.is_visible = True
self.window.move(*self._position)
self.window.show()
def hide(self):
self.is_visible = False
self._position = self.window.get_position()
self.window.hide()
def key_handler(self, widget=None, event=None):
if event is None:
return
try:
keyval = getattr(event, "keyval")
get_state = getattr(event, "get_state")
key_string = getattr(event, "string")
except AttributeError:
return
# define arrow keys and "vi"-like navigation keys
move_keys_dict = {
gtk.keysyms.Left: (1, 0),
gtk.keysyms.Down: (0, -1),
gtk.keysyms.Up: (0, 1),
gtk.keysyms.Right: (-1, 0),
ord("h"): (1, 0),
ord("j"): (0, -1),
ord("k"): (0, 1),
ord("l"): (-1, 0),
ord("H"): (1, 0),
ord("J"): (0, -1),
ord("K"): (0, 1),
ord("L"): (-1, 0),
}
if key_string and (key_string in '1234567'):
names = ["reset", "front", "back", "left", "right", "top", "bottom"]
index = '1234567'.index(key_string)
self.rotate_view(view=VIEWS[names[index]])
self._paint_ignore_busy()
elif key_string in ('i', 'm', 's', 'p'):
if key_string == 'i':
key = "view_light"
elif key_string == 'm':
key = "view_polygon"
elif key_string == 's':
key = "view_shadow"
elif key_string == 'p':
key = "view_perspective"
else:
key = None
# toggle setting
self.settings.set(key, not self.settings.get(key))
# re-init gl settings
self.glsetup()
self.paint()
elif key_string in ("+", "-"):
if key_string == "+":
self.camera.zoom_in()
else:
self.camera.zoom_out()
self._paint_ignore_busy()
elif keyval in move_keys_dict.keys():
move_x, move_y = move_keys_dict[keyval]
if get_state() & gtk.gdk.SHIFT_MASK:
# shift key pressed -> rotation
base = 0
factor = 10
self.camera.rotate_camera_by_screen(base, base,
base - factor * move_x, base - factor * move_y)
else:
# no shift key -> moving
self.camera.shift_view(x_dist=move_x, y_dist=move_y)
self._paint_ignore_busy()
else:
# see dir(gtk.keysyms)
#print "Key pressed: %s (%s)" % (keyval, get_state())
pass
def check_busy(func):
def check_busy_wrapper(self, *args, **kwargs):
if not self.enabled or self.busy:
return
self.busy = True
result = func(self, *args, **kwargs)
self.busy = False
return result
return check_busy_wrapper
def gtkgl_refresh(func):
def gtkgl_refresh_wrapper(self, *args, **kwargs):
prev_mode = GL.glGetIntegerv(GL.GL_MATRIX_MODE)
GL.glMatrixMode(GL.GL_MODELVIEW)
# clear the background with the configured color
bg_col = self.settings.get("color_background")
GL.glClearColor(bg_col[0], bg_col[1], bg_col[2], 0.0)
GL.glClear(GL.GL_COLOR_BUFFER_BIT|GL.GL_DEPTH_BUFFER_BIT)
result = func(self, *args, **kwargs)
self.camera.position_camera()
# adjust Light #2
v = self.camera.view
lightpos = (v["center"][0] + v["distance"][0],
v["center"][1] + v["distance"][1],
v["center"][2] + v["distance"][2])
GL.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, lightpos)
self._paint_raw()
GL.glMatrixMode(prev_mode)
GL.glFlush()
self.area.get_gl_drawable().swap_buffers()
return result
return gtkgl_refresh_wrapper
def glsetup(self):
GLUT.glutInit()
GLUT.glutInitDisplayMode(GLUT.GLUT_RGBA | GLUT.GLUT_DOUBLE | \
GLUT.GLUT_DEPTH | GLUT.GLUT_MULTISAMPLE | GLUT.GLUT_ALPHA | \
GLUT.GLUT_ACCUM)
if self.settings.get("view_shadow"):
# TODO: implement shadowing (or remove the setting)
pass
# use vertex normals for smooth rendering
GL.glShadeModel(GL.GL_SMOOTH)
bg_col = self.settings.get("color_background")
GL.glClearColor(bg_col[0], bg_col[1], bg_col[2], 0.0)
GL.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST)
GL.glMatrixMode(GL.GL_MODELVIEW)
# enable blending/transparency (alpha) for colors
GL.glEnable(GL.GL_BLEND)
# see http://wiki.delphigl.com/index.php/glBlendFunc
GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA)
GL.glEnable(GL.GL_DEPTH_TEST)
# "less" is OpenGL's default
GL.glDepthFunc(GL.GL_LESS)
# slightly improved performance: ignore all faces inside the objects
GL.glCullFace(GL.GL_BACK);
GL.glEnable(GL.GL_CULL_FACE);
# enable antialiasing
GL.glEnable(GL.GL_LINE_SMOOTH)
#GL.glEnable(GL.GL_POLYGON_SMOOTH)
GL.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST)
GL.glHint(GL.GL_POLYGON_SMOOTH_HINT, GL.GL_NICEST)
# TODO: move to toolpath drawing
GL.glLineWidth(0.8)
#GL.glEnable(GL.GL_MULTISAMPLE_ARB)
GL.glEnable(GL.GL_POLYGON_OFFSET_FILL)
GL.glPolygonOffset(1.0, 1.0)
GL.glMaterial(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE,
self.settings.get("color_model") )
GL.glMaterial(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR,
(1.0, 1.0, 1.0, 1.0))
GL.glMaterial(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, (100.0))
if self.settings.get("view_polygon"):
GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL)
else:
GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE)
GL.glMatrixMode(GL.GL_MODELVIEW)
GL.glLoadIdentity()
GL.glMatrixMode(GL.GL_PROJECTION)
GL.glLoadIdentity()
GL.glViewport(0, 0, self.area.allocation.width,
self.area.allocation.height)
# lighting
GL.glLightModeli(GL.GL_LIGHT_MODEL_LOCAL_VIEWER, GL.GL_TRUE)
# Light #1
# setup the ambient light
GL.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, (0.3, 0.3, 0.3, 1.0))
# setup the diffuse light
GL.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, (0.8, 0.8, 0.8, 1.0))
# setup the specular light
GL.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, (0.1, 0.1, 0.1, 1.0))
# enable Light #1
GL.glEnable(GL.GL_LIGHT0)
# Light #2
# spotlight with small light cone (like a desk lamp)
#GL.glLightfv(GL.GL_LIGHT1, GL.GL_SPOT_CUTOFF, 10.0)
# ... directed at the object
v = self.camera.view
GL.glLightfv(GL.GL_LIGHT1, GL.GL_SPOT_DIRECTION,
(v["center"][0], v["center"][1], v["center"][2]))
GL.glLightfv(GL.GL_LIGHT1, GL.GL_AMBIENT, (0.3, 0.3, 0.3, 1.0))
# and dark outside of the light cone
#GL.glLightfv(GL.GL_LIGHT1, GL.GL_SPOT_EXPONENT, 100.0)
#GL.glLightf(GL.GL_LIGHT1, GL.GL_QUADRATIC_ATTENUATION, 0.5)
# setup the diffuse light
GL.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE, (0.9, 0.9, 0.9, 1.0))
# setup the specular light
GL.glLightfv(GL.GL_LIGHT1, GL.GL_SPECULAR, (1.0, 1.0, 1.0, 1.0))
# enable Light #2
GL.glEnable(GL.GL_LIGHT1)
if self.settings.get("view_light"):
GL.glEnable(GL.GL_LIGHTING)
else:
GL.glDisable(GL.GL_LIGHTING)
GL.glEnable(GL.GL_NORMALIZE)
GL.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE)
GL.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR)
#GL.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_EMISSION)
GL.glEnable(GL.GL_COLOR_MATERIAL)
def destroy(self, widget=None, data=None):
if self.notify_destroy_func:
self.notify_destroy_func()
# don't close the window
return True
def gtkgl_functionwrapper(function):
def gtkgl_functionwrapper_function(self, *args, **kwords):
gldrawable = self.area.get_gl_drawable()
if not gldrawable:
return
glcontext = self.area.get_gl_context()
if not gldrawable.gl_begin(glcontext):
return
if not self.initialized:
self.glsetup()
self.initialized = True
result = function(self, *args, **kwords)
gldrawable.gl_end()
return result
return gtkgl_functionwrapper_function
@check_busy
@gtkgl_functionwrapper
def context_menu_handler(self, widget, event):
if (event.button == self.mouse["pressed_button"] == BUTTON_RIGHT) \
and self.context_menu \
and (event.get_time() - self.mouse["pressed_timestamp"] < 300) \
and (abs(event.x - self.mouse["pressed_pos"][0]) < 3) \
and (abs(event.y - self.mouse["pressed_pos"][1]) < 3):
# A quick press/release cycle with the right mouse button
# -> open the context menu.
self.context_menu.popup(None, None, None, event.button,
int(event.get_time()))
@check_busy
@gtkgl_functionwrapper
def scroll_handler(self, widget, event):
""" handle events of the scroll wheel
shift key: horizontal pan instead of vertical
control key: zoom
"""
try:
modifier_state = event.get_state()
except AttributeError:
# this should probably never happen
return
control_pressed = modifier_state & gtk.gdk.CONTROL_MASK
shift_pressed = modifier_state & gtk.gdk.SHIFT_MASK
if (event.direction == gtk.gdk.SCROLL_RIGHT) or \
((event.direction == gtk.gdk.SCROLL_UP) and shift_pressed):
# horizontal move right
self.camera.shift_view(x_dist=-1)
elif (event.direction == gtk.gdk.SCROLL_LEFT) or \
((event.direction == gtk.gdk.SCROLL_DOWN) and shift_pressed):
# horizontal move left
self.camera.shift_view(x_dist=1)
elif (event.direction == gtk.gdk.SCROLL_UP) and control_pressed:
# zoom in
self.camera.zoom_in()
elif event.direction == gtk.gdk.SCROLL_UP:
# vertical move up
self.camera.shift_view(y_dist=1)
elif (event.direction == gtk.gdk.SCROLL_DOWN) and control_pressed:
# zoom out
self.camera.zoom_out()
elif event.direction == gtk.gdk.SCROLL_DOWN:
# vertical move down
self.camera.shift_view(y_dist=-1)
else:
# no interesting event -> no re-painting
return
self._paint_ignore_busy()
def mouse_press_handler(self, widget, event):
self.mouse["pressed_timestamp"] = event.get_time()
self.mouse["pressed_button"] = event.button
self.mouse["pressed_pos"] = event.x, event.y
self.mouse_handler(widget, event)
@check_busy
@gtkgl_functionwrapper
def mouse_handler(self, widget, event):
x, y, state = event.x, event.y, event.state
if self.mouse["button"] is None:
if (state & BUTTON_ZOOM) or (state & BUTTON_ROTATE) \
or (state & BUTTON_MOVE):
self.mouse["button"] = state
self.mouse["start_pos"] = [x, y]
else:
# Don't try to create more than 25 frames per second (enough for
# a decent visualization).
if event.get_time() - self.mouse["event_timestamp"] < 40:
return
elif state & self.mouse["button"] & BUTTON_ZOOM:
# the start button is still active: update the view
start_x, start_y = self.mouse["start_pos"]
self.mouse["start_pos"] = [x, y]
# Move the mouse from lower left to top right corner for
# scaling up.
scale = 1 - 0.01 * ((x - start_x) + (start_y - y))
# do some sanity checks, scale no more than
# 1:100 on any given click+drag
if scale < 0.01:
scale = 0.01
elif scale > 100:
scale = 100
self.camera.scale_distance(scale)
self._paint_ignore_busy()
elif (state & self.mouse["button"] & BUTTON_MOVE) \
or (state & self.mouse["button"] & BUTTON_ROTATE):
start_x, start_y = self.mouse["start_pos"]
self.mouse["start_pos"] = [x, y]
if (state & BUTTON_MOVE):
# Determine the biggest dimension (x/y/z) for moving the
# screen's center in relation to this value.
obj_dim = []
obj_dim.append(self.settings.get("maxx") \
- self.settings.get("minx"))
obj_dim.append(self.settings.get("maxy") \
- self.settings.get("miny"))
obj_dim.append(self.settings.get("maxz") \
- self.settings.get("minz"))
max_dim = max(obj_dim)
self.camera.move_camera_by_screen(x - start_x, y - start_y,
max_dim)
else:
# BUTTON_ROTATE
# update the camera position according to the mouse movement
self.camera.rotate_camera_by_screen(start_x, start_y, x, y)
self._paint_ignore_busy()
else:
# button was released
self.mouse["button"] = None
self._paint_ignore_busy()
self.mouse["event_timestamp"] = event.get_time()
@check_busy
@gtkgl_functionwrapper
@gtkgl_refresh
def rotate_view(self, widget=None, view=None):
self.camera.set_view(view)
def reset_view(self):
self.rotate_view(None, None)
@check_busy
@gtkgl_functionwrapper
@gtkgl_refresh
def _resize_window(self, widget, data=None):
GL.glViewport(0, 0, self.area.allocation.width,
self.area.allocation.height)
@check_busy
@gtkgl_functionwrapper
@gtkgl_refresh
def paint(self, widget=None, data=None):
# the decorators take core for redraw
pass
@gtkgl_functionwrapper
@gtkgl_refresh
def _paint_ignore_busy(self, widget=None):
pass
def _paint_raw(self, widget=None):
# draw the model
draw_complete_model_view(self.settings)
# update the dimension display
s = self.settings
dimension_bar = self.gui.get_object("view3ddimension")
if s.get("show_dimensions"):
for name, size in (
("model_dim_x", s.get("maxx") - s.get("minx")),
("model_dim_y", s.get("maxy") - s.get("miny")),
("model_dim_z", s.get("maxz") - s.get("minz"))):
self.gui.get_object(name).set_text("%.3f %s" \
% (size, s.get("unit")))
dimension_bar.show()
else:
dimension_bar.hide()
def keep_gl_mode(func):
def keep_gl_mode_wrapper(*args, **kwargs):
prev_mode = GL.glGetIntegerv(GL.GL_MATRIX_MODE)
......@@ -864,106 +116,11 @@ def draw_direction_cone(p1, p2):
# draw the cone
GLUT.glutSolidCone(cone_radius, cone_length, 12, 1)
@keep_matrix
def draw_string(x, y, z, p, s, scale=.01):
GL.glPushMatrix()
GL.glTranslatef(x, y, z)
if p == 'xy':
GL.glRotatef(90, 1, 0, 0)
elif p == 'yz':
GL.glRotatef(90, 0, 1, 0)
GL.glRotatef(90, 0, 0, 1)
elif p == 'xz':
GL.glRotatef(90, 0, 1, 0)
GL.glRotatef(90, 0, 0, 1)
GL.glRotatef(45, 0, 1, 0)
GL.glScalef(scale, scale, scale)
for c in str(s):
GLUT.glutStrokeCharacter(GLUT.GLUT_STROKE_ROMAN, ord(c))
GL.glPopMatrix()
@keep_gl_mode
@keep_matrix
def draw_axes(settings):
GL.glMatrixMode(GL.GL_MODELVIEW)
GL.glLoadIdentity()
#GL.glTranslatef(0, 0, -2)
size_x = abs(settings.get("maxx"))
size_y = abs(settings.get("maxy"))
size_z = abs(settings.get("maxz"))
size = number(1.7) * max(size_x, size_y, size_z)
# the divider is just based on playing with numbers
scale = size / number(1500.0)
string_distance = number(1.1) * size
#GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
#GL.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, (0.0,0.0,0.0,1.0)); # no ambient light
#GL.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, (1.0,0.0,0.0,1.0)); # diffuse red
#GL.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, (1.0,1.0,1.0,1.0)); # highly specular
# otherwise plain colors like the next glColor4f wouldn't work
if settings.get("view_light"):
GL.glDisable(GL.GL_LIGHTING)
GL.glBegin(GL.GL_LINES)
GL.glColor4f(1, 0, 0, 1)
GL.glVertex3f(0, 0, 0)
GL.glVertex3f(size, 0, 0)
GL.glEnd()
draw_string(string_distance, 0, 0, 'xy', "X", scale=scale)
GL.glBegin(GL.GL_LINES)
GL.glColor3f(0, 1, 0)
GL.glVertex3f(0, 0, 0)
GL.glVertex3f(0, size, 0)
GL.glEnd()
draw_string(0, string_distance, 0, 'yz', "Y", scale=scale)
GL.glBegin(GL.GL_LINES)
GL.glColor3f(0, 0, 1)
GL.glVertex3f(0, 0, 0)
GL.glVertex3f(0, 0, size)
GL.glEnd()
draw_string(0, 0, string_distance, 'xz', "Z", scale=scale)
if settings.get("view_light"):
GL.glEnable(GL.GL_LIGHTING)
@keep_matrix
def draw_bounding_box(minx, miny, minz, maxx, maxy, maxz, color, lighting=True):
p1 = [minx, miny, minz]
p2 = [minx, maxy, minz]
p3 = [maxx, maxy, minz]
p4 = [maxx, miny, minz]
p5 = [minx, miny, maxz]
p6 = [minx, maxy, maxz]
p7 = [maxx, maxy, maxz]
p8 = [maxx, miny, maxz]
if lighting:
GL.glDisable(GL.GL_LIGHTING)
# lower rectangle
GL.glBegin(GL.GL_LINES)
GL.glColor4f(*color)
# all combinations of neighbouring corners
for corner_pair in [(p1, p2), (p1, p5), (p1, p4), (p2, p3),
(p2, p6), (p3, p4), (p3, p7), (p4, p8), (p5, p6),
(p6, p7), (p7, p8), (p8, p5)]:
GL.glVertex3f(*(corner_pair[0]))
GL.glVertex3f(*(corner_pair[1]))
GL.glEnd()
if lighting:
GL.glEnable(GL.GL_LIGHTING)
@keep_gl_mode
@keep_matrix
def draw_complete_model_view(settings):
GL.glMatrixMode(GL.GL_MODELVIEW)
GL.glLoadIdentity()
# axes
if settings.get("show_axes"):
draw_axes(settings)
# stock model
if settings.get("show_bounding_box"):
draw_bounding_box(
float(settings.get("minx")), float(settings.get("miny")),
float(settings.get("minz")), float(settings.get("maxx")),
float(settings.get("maxy")), float(settings.get("maxz")),
settings.get("color_bounding_box"),
lighting=settings.get("view_light"))
# draw the material (for simulation mode)
if settings.get("show_simulation"):
obj = settings.get("simulation_object")
......@@ -972,35 +129,6 @@ def draw_complete_model_view(settings):
# we need to wait until the color change is active
GL.glFinish()
obj.to_OpenGL()
# draw the model
if settings.get("show_model") \
and not (settings.get("show_simulation") \
and settings.get("simulation_toolpath_moves")):
for model in settings.get("models").get_visible():
color_str = settings.get("models").get_attr(model, "color")
alpha = settings.get("models").get_attr(model, "alpha")
col = gtk.gdk.color_parse(color_str)
GL.glColor4f(col.red / GTK_COLOR_MAX, col.green / GTK_COLOR_MAX,
col.blue / GTK_COLOR_MAX, alpha / GTK_COLOR_MAX)
# we need to wait until the color change is active
GL.glFinish()
"""
min_area = abs(model.maxx - model.minx) * abs(model.maxy - model.miny) / 100
# example for coloring specific triangles
groups = model.get_flat_areas(min_area)
all_flat_ids = []
for group in groups:
all_flat_ids.extend([t.id for t in group])
flat_color = (1.0, 0.0, 0.0, 1.0)
normal_color = settings.get("color_model")
def check_triangle_draw(triangle):
if triangle.id in all_flat_ids:
return True, flat_color
else:
return True, normal_color
model.to_OpenGL(visible_filter=check_triangle_draw)
"""
model.to_OpenGL(show_directions=settings.get("show_directions"))
# draw the support grid
if settings.get("show_support_grid") and settings.get("current_support_model"):
GL.glColor4f(*settings.get("color_support_grid"))
......@@ -1018,7 +146,7 @@ def draw_complete_model_view(settings):
# draw the toolpath
# don't do it, if a new toolpath is just being calculated
safety_height = settings.get("gcode_safety_height")
if settings.get("show_toolpath") \
if settings.get("toolpath") and settings.get("show_toolpath") \
and not settings.get("toolpath_in_progress") \
and not (settings.get("show_simulation") \
and settings.get("simulation_toolpath_moves")):
......@@ -1034,6 +162,7 @@ def draw_complete_model_view(settings):
cutter = settings.get("cutter")
if not cutter is None:
GL.glColor4f(*settings.get("color_cutter"))
GL.glFinish()
cutter.to_OpenGL()
if settings.get("show_drill_progress") \
and settings.get("toolpath_in_progress"):
......
......@@ -40,7 +40,6 @@ from pycam.Utils.locations import get_data_file_location, \
get_all_program_locations
import pycam.Utils
from pycam.Geometry.utils import sqrt
from pycam.Gui.OpenGLTools import ModelViewWindowGL
import pycam.Geometry.Model
from pycam.Toolpath import Bounds
import pycam.Plugins
......@@ -309,7 +308,6 @@ class ProjectGui(object):
def __init__(self, no_dialog=False):
self.settings = EventCore()
self.gui_is_active = False
self.view3d = None
# during initialization any dialog (e.g. "Unit change") is not allowed
# we set the final value later
self.no_dialog = True
......@@ -361,7 +359,6 @@ class ProjectGui(object):
("ExportEMCToolDefinition", self.export_emc_tools, None, None),
("Quit", self.destroy, None, "<Control>q"),
("GeneralSettings", self.toggle_preferences_window, None, "<Control>p"),
("Toggle3DView", self.toggle_3d_view, None, "<Control><Shift>v"),
("ToggleProcessPoolWindow", self.toggle_process_pool_window, None, None),
("UndoButton", self._restore_undo_state, None, "<Control>z"),
("HelpUserManual", self.show_help, "User_Manual", "F1"),
......@@ -385,7 +382,7 @@ class ProjectGui(object):
("BugTracker", self.show_help, "http://sourceforge.net/tracker/?group_id=237831&atid=1104176", None),
("FeatureRequest", self.show_help, "http://sourceforge.net/tracker/?group_id=237831&atid=1104179", None)):
item = self.gui.get_object(objname)
if objname in ("Toggle3DView", "ToggleProcessPoolWindow"):
if objname == "ToggleProcessPoolWindow":
action = "toggled"
else:
action = "activate"
......@@ -405,20 +402,23 @@ class ProjectGui(object):
gtk.link_button_set_uri_hook(open_url)
# no undo is allowed at the beginning
self.gui.get_object("UndoButton").set_sensitive(False)
self.settings.register_event("model-change-before", self._store_undo_state)
self.settings.register_event("model-change-after", self.update_model_dimensions)
self.settings.register_event("model-change-after", self.update_save_actions)
self.settings.register_event("model-change-after", self.update_view)
self.settings.register_event("visual-item-updated", self.update_view)
self.settings.register_event("visual-item-updated", self.update_model_dimensions)
self.settings.register_event("model-change-before",
self._store_undo_state)
self.settings.register_event("model-change-after",
self.update_save_actions)
self.settings.register_event("model-selection-changed",
self.update_save_actions)
self.settings.register_event("model-change-after",
lambda: self.settings.emit_event("visual-item-updated"))
def update_emc_tool_button():
tool_num = len(self.settings.get("tools"))
self.gui.get_object("ExportEMCToolDefinition").set_sensitive(tool_num > 0)
self.settings.register_event("tool-selection-changed", update_emc_tool_button)
self.settings.set("load_model", self.load_model)
self.settings.register_event("bounds-changed", self.update_view)
# configure drag-n-drop for config files and models
self.configure_drag_and_drop(self.window)
self.settings.set("configure-drag-drop-func",
self.configure_drag_and_drop)
self.settings.get("configure-drag-drop-func")(self.window)
# other events
self.window.connect("destroy", self.destroy)
# the settings window
......@@ -554,7 +554,8 @@ class ProjectGui(object):
obj = self.gui.get_object(objname)
self.settings.add_item(name, obj.get_active, obj.set_active)
# send "True" to trigger a re-setup of GL settings
obj.connect("toggled", self.update_view, True)
obj.connect("toggled", lambda widget: \
self.settings.emit_event("visual-item-updated"))
# color selectors
def get_color_wrapper(obj):
def gtk_color_to_float():
......@@ -588,7 +589,8 @@ class ProjectGui(object):
obj = self.gui.get_object(objname)
self.settings.add_item(name, get_color_wrapper(obj), set_color_wrapper(obj))
# repaint the 3d view after a color change
obj.connect("color-set", self.update_view)
obj.connect("color-set", lambda widget: \
self.settings.emit_event("visual-item-updated"))
# set the availability of ODE
self.enable_ode_control = self.gui.get_object("SettingEnableODE")
self.settings.add_item("enable_ode", self.enable_ode_control.get_active,
......@@ -652,7 +654,8 @@ class ProjectGui(object):
self.settings.add_item("touch_off_pause_execution",
touch_off_pause.get_active, touch_off_pause.set_active)
# redraw the toolpath if safety height changed
gcode_safety_height.connect("value-changed", self.update_view)
gcode_safety_height.connect("value-changed", lambda widget:
self.settings.emit_event("visual-item-updated"))
gcode_path_mode = self.gui.get_object("GCodeCornerStyleControl")
self.settings.add_item("gcode_path_mode", gcode_path_mode.get_active,
gcode_path_mode.set_active)
......@@ -801,12 +804,10 @@ class ProjectGui(object):
# register a logging handler for displaying error messages
pycam.Utils.log.add_gtk_gui(self.window, logging.ERROR)
self.window.show()
self.toggle_3d_view(value=True)
def update_all_controls(self):
self.update_save_actions()
self.update_unit_labels()
self.update_model_dimensions()
self.update_gcode_controls()
self.update_ode_settings()
self.update_parallel_processes_settings()
......@@ -906,12 +907,6 @@ class ProjectGui(object):
url = page
webbrowser.open(url)
def update_view(self, widget=None, data=None):
if self.view3d and self.view3d.is_visible and not self.no_dialog:
if data:
self.view3d.glsetup()
self.view3d.paint()
def set_model_filename(self, filename):
""" Store the given filename for a possible later "save model" action.
Additionally the window's title is adjusted and the "save" buttons are
......@@ -931,8 +926,8 @@ class ProjectGui(object):
bool(self.last_task_settings_uri and \
self.last_task_settings_uri.is_writable()))
# TODO: choose all models
model = self.settings.get("models") and self.settings.get("models")[0]
save_as_possible = (not model is None) and model.is_export_supported()
models = self.settings.get("models").get_selected()
save_as_possible = bool(models) and models[0].is_export_supported()
self.gui.get_object("SaveAsModel").set_sensitive(save_as_possible)
save_possible = bool(self.last_model_uri and save_as_possible and \
self.last_model_uri.is_writable())
......@@ -1153,68 +1148,6 @@ class ProjectGui(object):
# don't repeat, if the window is hidden
return self.gui.get_object("ToggleProcessPoolWindow").get_active()
@gui_activity_guard
def toggle_3d_view(self, widget=None, value=None):
toggle_3d_checkbox = self.gui.get_object("Toggle3DView")
# no interactive mode
if self.no_dialog:
return
if self.view3d and not self.view3d.enabled:
# initialization failed - don't do anything
return
if not self.settings.get("models"):
# no model loaded - don't enable the window
return
current_state = not ((self.view3d is None) or (not self.view3d.is_visible))
if value is None:
new_state = not current_state
else:
new_state = value
if new_state == current_state:
return
elif new_state:
if self.view3d is None:
# These buttons are replicated to appear in the 3D view - for
# easier configuration of visible items without the Preferences
# window.
item_buttons = self.gui.get_object(
"PreferencesVisibleItemsBox").get_children()
# do the gl initialization
self.view3d = ModelViewWindowGL(self.gui, self.settings,
notify_destroy=self.toggle_3d_view,
accel_group=self._accel_group,
item_buttons=item_buttons,
context_menu_actions=[self.gui.get_object(name)
for name in ("GeneralSettings", "Help3DView")])
if self.view3d.enabled:
self.view3d.reset_view()
# configure drag-and-drop for the 3D window
self.configure_drag_and_drop(self.view3d.window)
# disable the "toggle" button, if the 3D view does not work
toggle_3d_checkbox.set_sensitive(self.view3d.enabled)
else:
# the window is just hidden
self.view3d.show()
self.settings.emit_event("model-change-after")
else:
self.view3d.hide()
# enable the toggle button only, if the 3d view is available
# (e.g. disabled if no OpenGL support is available)
toggle_3d_checkbox.set_active(self.view3d.enabled and new_state)
def _treeview_get_active_index(self, table, datalist):
if len(datalist) == 0:
result = None
else:
treeselection = table.get_selection()
(model, iteration) = treeselection.get_selected()
# the first item in the model is the index within the list
try:
result = model[iteration][0]
except TypeError:
result = None
return result
def change_unit_init(self, widget=None):
new_unit = self.gui.get_object("unit_control").get_active_text()
if self._last_unit is None:
......@@ -1458,26 +1391,7 @@ class ProjectGui(object):
except IOError, err_msg:
log.warn("Failed to write preferences file (%s): %s" % (config_filename, err_msg))
@gui_activity_guard
def update_model_dimensions(self, widget=None):
models = self.settings.get("models").get_visible()
if not models:
return
# model corners in 3D view
values = {}
low, high = pycam.Geometry.Model.get_combined_bounds(models)
if None in low or None in high:
# all models are empty
return
for value, label_suffix in ((low[0], "XMin"), (low[1], "YMin"),
(low[2], "ZMin"), (high[0], "XMax"), (high[1], "YMax"),
(high[2], "ZMax")):
label_name = "ModelCorner%s" % label_suffix
value = "%.3f" % value
self.gui.get_object(label_name).set_label(value)
def destroy(self, widget=None, data=None):
self.update_view()
gtk.main_quit()
self.quit()
......@@ -1622,11 +1536,6 @@ class ProjectGui(object):
self.settings.emit_event("model-change-before")
self.settings.get("models").append(model)
self.last_model_uri = None
# do some initialization
self.settings.emit_event("model-change-after")
if self.view3d and self.view3d.enabled:
self.append_to_queue(self.view3d.reset_view)
self.append_to_queue(self.toggle_3d_view, value=True)
return True
else:
return False
......@@ -1696,7 +1605,8 @@ class ProjectGui(object):
self.func()
# break the loop if someone clicked the "cancel" button
return progress.update(text=text, percent=percent)
draw_callback = UpdateView(self.update_view,
draw_callback = UpdateView(
lambda: self.settings.emit_event("visual-item-updated"),
max_fps=self.settings.get("drill_progress_max_fps")).update
progress.update(text="Generating collision model")
......@@ -1751,7 +1661,6 @@ class ProjectGui(object):
# the tool id numbering should start with 1 instead of zero
self.toolpath.add_toolpath(toolpath, description, toolpath_settings)
self.update_toolpath_table()
self.update_view()
# return "False" if the action was cancelled
cancelled = progress.update()
result = not cancelled
......
......@@ -184,6 +184,34 @@ class Bounds(pycam.Plugins.ListPluginBase):
for not_found in remaining:
models.remove(not_found)
def get_bounds_limit(self, bounds):
default = (None, None, None), (None, None, None)
get_low_value = lambda axis: bounds["BoundaryLow%s" % "XYZ"[axis]]
get_high_value = lambda axis: bounds["BoundaryHigh%s" % "XYZ"[axis]]
if bounds["TypeRelativeMargin"]:
low_model, high_model = pycam.Geometry.Model.get_combined_bounds(
bounds["Models"])
if None in low_model or None in high_model:
# zero-sized models -> no action
return default
is_percent = self.RELATIVE_UNIT[bounds["RelativeUnit"]] == "%"
low, high = [], []
if is_percent:
for axis in range(3):
dim = high_model[axis] - low_model[axis]
low.append(low_model[axis] - (get_low_value(axis) / 100.0 * dim))
high.append(high_model[axis] + (get_high_value(axis) / 100.0 * dim))
else:
for axis in range(3):
low.append(low_model[axis] - get_low_value(axis))
high.append(high_model[axis] + get_high_value(axis))
else:
low, high = [], []
for axis in range(3):
low.append(get_low_value(axis))
high.append(get_high_value(axis))
return low, high
def _render_model_name(self, column, cell, model, m_iter):
path = model.get_path(m_iter)
all_models = self.core.get("models")
......
......@@ -78,17 +78,10 @@ class Fonts(pycam.Plugins.PluginBase):
self.update_font_dialog_preview)
# use global key accel map
self.font_dialog_window.add_accel_group(self.core.get("gtk-accel-group"))
# menu item and shortcut
actiongroup = self._gtk.ActionGroup("fonts")
font_action = self.gui.get_object("ShowFontDialog")
self.register_gtk_accelerator("fonts", font_action,
"<Control><Shift>t", "ShowFontDialog")
font_action.connect("activate", self.toggle_font_dialog_window)
key, mod = self._gtk.accelerator_parse("<Control><Shift>t")
# TODO: move the "<pycam>" accel path somewhere else
accel_path = "<pycam>/ShowFontDialog"
font_action.set_accel_path(accel_path)
self._gtk.accel_map_change_entry(accel_path, key, mod, True)
actiongroup.add_action(font_action)
self.core.get("gtk-uimanager").insert_action_group(actiongroup, pos=-1)
# store window position
self._font_dialog_window_visible = False
self._font_dialog_window_position = None
......
......@@ -95,6 +95,11 @@ class ModelPosition(pycam.Plugins.PluginBase):
self.core.emit_event("model-change-before")
dest = [self.gui.get_object("AlignPosition%s" % axis).get_value()
for axis in "XYZ"]
progress = self.core.get("progress")
progress.update(text="Shifting model")
progress.disable_cancel()
progress.set_multiple(len(models), "Model")
for model in models:
shift_values = []
for axis in "XYZ":
dest = self.gui.get_object("AlignPosition%s" % axis).get_value()
......@@ -111,11 +116,6 @@ class ModelPosition(pycam.Plugins.PluginBase):
else:
shift = dest - max_axis
shift_values.append(shift)
progress = self.core.get("progress")
progress.update(text="Shifting model")
progress.disable_cancel()
progress.set_multiple(len(models), "Model")
for model in models:
model.shift(shift_values[0], shift_values[1], shift_values[2],
callback=progress.update)
progress.update_multiple()
......
# -*- coding: utf-8 -*-
"""
$Id$
Copyright 2011 Lars Kruse <devel@sumpfralle.de>
This file is part of PyCAM.
PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PyCAM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
import pycam.Plugins
from pycam.Geometry.utils import number
class OpenGLViewAxes(pycam.Plugins.PluginBase):
DEPENDS = ["OpenGLWindow"]
def setup(self):
import OpenGL.GL
import OpenGL.GLUT
self._GL = OpenGL.GL
self._GLUT = OpenGL.GLUT
self.core.register_event("visualize-items", self.draw_axes)
return True
def teardown(self):
self.core.unregister_event("visualize-items", self.draw_axes)
def draw_axes(self):
if not self.core.get("show_axes"):
return
GL = self._GL
GL.glMatrixMode(GL.GL_MODELVIEW)
GL.glLoadIdentity()
low, high = pycam.Geometry.Model.get_combined_bounds(
self.core.get("models").get_visible())
if None in low or None in high:
low, high = (0, 0, 0), (1, 1, 1)
size_x = abs(high[0])
size_y = abs(high[1])
size_z = abs(high[2])
size = number(1.7) * max(size_x, size_y, size_z)
# the divider is just based on playing with numbers
scale = size / number(1500.0)
string_distance = number(1.1) * size
#GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
#GL.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, (0.0,0.0,0.0,1.0)); # no ambient light
#GL.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, (1.0,0.0,0.0,1.0)); # diffuse red
#GL.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, (1.0,1.0,1.0,1.0)); # highly specular
# otherwise plain colors like the next glColor4f wouldn't work
if self.core.get("view_light"):
GL.glDisable(GL.GL_LIGHTING)
GL.glBegin(GL.GL_LINES)
GL.glColor4f(1, 0, 0, 1)
GL.glVertex3f(0, 0, 0)
GL.glVertex3f(size, 0, 0)
GL.glEnd()
self.draw_string(string_distance, 0, 0, 'xy', "X", scale=scale)
GL.glBegin(GL.GL_LINES)
GL.glColor3f(0, 1, 0)
GL.glVertex3f(0, 0, 0)
GL.glVertex3f(0, size, 0)
GL.glEnd()
self.draw_string(0, string_distance, 0, 'yz', "Y", scale=scale)
GL.glBegin(GL.GL_LINES)
GL.glColor3f(0, 0, 1)
GL.glVertex3f(0, 0, 0)
GL.glVertex3f(0, 0, size)
GL.glEnd()
self.draw_string(0, 0, string_distance, 'xz', "Z", scale=scale)
if self.core.get("view_light"):
GL.glEnable(GL.GL_LIGHTING)
def draw_string(self, x, y, z, p, s, scale=.01):
GL = self._GL
GLUT = self._GLUT
GL.glPushMatrix()
GL.glTranslatef(x, y, z)
if p == 'xy':
GL.glRotatef(90, 1, 0, 0)
elif p == 'yz':
GL.glRotatef(90, 0, 1, 0)
GL.glRotatef(90, 0, 0, 1)
elif p == 'xz':
GL.glRotatef(90, 0, 1, 0)
GL.glRotatef(90, 0, 0, 1)
GL.glRotatef(45, 0, 1, 0)
GL.glScalef(scale, scale, scale)
for c in str(s):
GLUT.glutStrokeCharacter(GLUT.GLUT_STROKE_ROMAN, ord(c))
GL.glPopMatrix()
# -*- coding: utf-8 -*-
"""
$Id$
Copyright 2011 Lars Kruse <devel@sumpfralle.de>
This file is part of PyCAM.
PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PyCAM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
import pycam.Plugins
class OpenGLViewBounds(pycam.Plugins.PluginBase):
DEPENDS = ["OpenGLWindow", "Bounds"]
def setup(self):
import OpenGL.GL
self._GL = OpenGL.GL
self.core.register_event("visualize-items", self.draw_bounds)
return True
def teardown(self):
self.core.unregister_event("visualize-items", self.draw_bounds)
def draw_bounds(self):
GL = self._GL
if not self.core.get("show_bounding_box"):
return
bounds = self.core.get("bounds").get_selected()
if not bounds:
return
low, high = self.core.get("bounds").get_bounds_limit(bounds)
if None in low or None in high:
return
minx, miny, minz = low[0], low[1], low[2]
maxx, maxy, maxz = high[0], high[1], high[2]
p1 = [minx, miny, minz]
p2 = [minx, maxy, minz]
p3 = [maxx, maxy, minz]
p4 = [maxx, miny, minz]
p5 = [minx, miny, maxz]
p6 = [minx, maxy, maxz]
p7 = [maxx, maxy, maxz]
p8 = [maxx, miny, maxz]
if self.core.get("view_light"):
GL.glDisable(GL.GL_LIGHTING)
# lower rectangle
GL.glColor4f(*self.core.get("color_bounding_box"))
GL.glFinish()
GL.glBegin(GL.GL_LINES)
# all combinations of neighbouring corners
for corner_pair in [(p1, p2), (p1, p5), (p1, p4), (p2, p3),
(p2, p6), (p3, p4), (p3, p7), (p4, p8), (p5, p6),
(p6, p7), (p7, p8), (p8, p5)]:
GL.glVertex3f(*(corner_pair[0]))
GL.glVertex3f(*(corner_pair[1]))
GL.glEnd()
if self.core.get("view_light"):
GL.glEnable(GL.GL_LIGHTING)
# -*- coding: utf-8 -*-
"""
$Id$
Copyright 2011 Lars Kruse <devel@sumpfralle.de>
This file is part of PyCAM.
PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PyCAM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
import pycam.Plugins
GTK_COLOR_MAX = 65535.0
class OpenGLWindow(pycam.Plugins.PluginBase):
DEPENDS = ["OpenGLWindow", "Models"]
def setup(self):
import gtk
import OpenGL.GL
self._gtk = gtk
self._GL = OpenGL.GL
self.core.register_event("visualize-items", self.draw_model)
return True
def teardown(self):
self.core.unregister_event("visualize-items", self.draw_model)
return True
def draw_model(self):
if self.core.get("show_model") \
and not (self.core.get("show_simulation") \
and self.core.get("simulation_toolpath_moves")):
for model in self.core.get("models").get_visible():
color_str = self.core.get("models").get_attr(model, "color")
alpha = self.core.get("models").get_attr(model, "alpha")
col = self._gtk.gdk.color_parse(color_str)
self._GL.glColor4f(col.red / GTK_COLOR_MAX, col.green / GTK_COLOR_MAX,
col.blue / GTK_COLOR_MAX, alpha / GTK_COLOR_MAX)
# we need to wait until the color change is active
self._GL.glFinish()
model.to_OpenGL(show_directions=self.core.get("show_directions"))
# -*- coding: utf-8 -*-
"""
$Id$
Copyright 2011 Lars Kruse <devel@sumpfralle.de>
This file is part of PyCAM.
PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PyCAM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
# careful import
try:
import gtk.gtkgl
import OpenGL.GL as GL
import OpenGL.GLU as GLU
import OpenGL.GLUT as GLUT
import pango
GL_ENABLED = True
except (ImportError, RuntimeError):
GL_ENABLED = False
# imported later (on demand)
#import gtk
import math
from pycam.Gui.OpenGLTools import draw_complete_model_view
from pycam.Geometry.Point import Point
import pycam.Geometry.Matrix as Matrix
from pycam.Geometry.utils import sqrt, number
import pycam.Plugins
# The length of the distance vector does not matter - it will be normalized and
# multiplied later anyway.
VIEWS = {
"reset": {"distance": (-1.0, -1.0, 1.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 0.0, 1.0), "znear": 0.01, "zfar": 10000.0, "fovy": 30.0},
"top": {"distance": (0.0, 0.0, 1.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 1.0, 0.0), "znear": 0.01, "zfar": 10000.0, "fovy": 30.0},
"bottom": {"distance": (0.0, 0.0, -1.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 1.0, 0.0), "znear": 0.01, "zfar": 10000.0, "fovy": 30.0},
"left": {"distance": (-1.0, 0.0, 0.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 0.0, 1.0), "znear": 0.01, "zfar": 10000.0, "fovy": 30.0},
"right": {"distance": (1.0, 0.0, 0.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 0.0, 1.0), "znear": 0.01, "zfar": 10000.0, "fovy": 30.0},
"front": {"distance": (0.0, -1.0, 0.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 0.0, 1.0), "znear": 0.01, "zfar": 10000.0, "fovy": 30.0},
"back": {"distance": (0.0, 1.0, 0.0), "center": (0.0, 0.0, 0.0),
"up": (0.0, 0.0, 1.0), "znear": 0.01, "zfar": 10000.0, "fovy": 30.0},
}
# buttons for rotating, moving and zooming the model view window
BUTTON_ROTATE = gtk.gdk.BUTTON1_MASK
BUTTON_MOVE = gtk.gdk.BUTTON2_MASK
BUTTON_ZOOM = gtk.gdk.BUTTON3_MASK
BUTTON_RIGHT = 3
class OpenGLWindow(pycam.Plugins.PluginBase):
UI_FILE = "opengl.ui"
def setup(self):
if not GL_ENABLED:
self.log.error("Failed to initialize the interactive 3D model view."
+ "\nPlease install 'python-gtkglext1' to enable it.")
return False
if self.gui:
self.window = self.gui.get_object("OpenGLWindow")
self.core.get("configure-drag-drop-func")(self.window)
accel_group = self.core.get("gtk-accel-group")
if not accel_group is None:
self.window.add_accel_group(accel_group)
self.initialized = False
self.busy = False
self.is_visible = False
self._position = [100, 100]
toggle_3d = self.gui.get_object("Toggle3DView")
handler = toggle_3d.connect("toggled", self.toggle_3d_view)
self._toggle_action_handler = (toggle_3d, handler)
self.register_gtk_accelerator("opengl", toggle_3d,
"<Control><Shift>v", "ToggleOpenGLView")
self.mouse = {"start_pos": None, "button": None,
"event_timestamp": 0, "last_timestamp": 0,
"pressed_pos": None, "pressed_timestamp": 0,
"pressed_button": None}
self.window.connect("delete-event", self.destroy)
self.window.set_default_size(560, 400)
self.container = self.gui.get_object("OpenGLBox")
for obj_name, view in (("ResetView", "reset"),
("LeftView", "left"), ("RightView", "right"),
("FrontView", "front"), ("BackView", "back"),
("TopView", "top"), ("BottomView", "bottom")):
self.gui.get_object(obj_name).connect("clicked",
self.rotate_view, VIEWS[view])
# key binding
self.window.connect("key-press-event", self.key_handler)
# OpenGL stuff
glconfig = gtk.gdkgl.Config(mode=gtk.gdkgl.MODE_RGBA \
| gtk.gdkgl.MODE_DEPTH | gtk.gdkgl.MODE_DOUBLE)
self.area = gtk.gtkgl.DrawingArea(glconfig)
# first run; might also be important when doing other fancy
# gtk/gdk stuff
self.area.connect_after('realize', self.paint)
# called when a part of the screen is uncovered
self.area.connect('expose-event', self.paint)
# resize window
self.area.connect('configure-event', self._resize_window)
# catch mouse events
self.area.set_events(gtk.gdk.MOUSE | gtk.gdk.POINTER_MOTION_HINT_MASK \
| gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK \
| gtk.gdk.SCROLL_MASK)
self.area.connect("button-press-event", self.mouse_press_handler)
self.area.connect('motion-notify-event', self.mouse_handler)
self.area.connect("button-release-event", self.context_menu_handler)
self.area.connect("scroll-event", self.scroll_handler)
self.container.pack_end(self.area)
self.camera = Camera(self.core, lambda: (self.area.allocation.width,
self.area.allocation.height))
# Color the dimension value according to the axes.
# For "y" axis: 100% green is too bright on light background - we
# reduce it a bit.
for color, names in (
(pango.AttrForeground(65535, 0, 0, 0, 100),
("model_dim_x_label", "model_dim_x", "ModelCornerXMax",
"ModelCornerXMin", "ModelCornerXSpaces")),
(pango.AttrForeground(0, 50000, 0, 0, 100),
("model_dim_y_label", "model_dim_y", "ModelCornerYMax",
"ModelCornerYMin", "ModelCornerYSpaces")),
(pango.AttrForeground(0, 0, 65535, 0, 100),
("model_dim_z_label", "model_dim_z", "ModelCornerZMax",
"ModelCornerZMin", "ModelCornerZSpaces"))):
attributes = pango.AttrList()
attributes.insert(color)
for name in names:
self.gui.get_object(name).set_attributes(attributes)
# add the items buttons (for configuring visible items)
# TODO: enable the context menu
if False and item_buttons:
items_button_container = self.gui.get_object("ViewItems")
for button in item_buttons:
new_checkbox = gtk.ToggleToolButton()
new_checkbox.set_label(button.get_label())
new_checkbox.set_active(button.get_active())
# Configure the two buttons (in "Preferences" and in the 3D view
# widget) to toggle each other. This is required for a
# consistent view of the setting.
connect_button_handlers("toggled", button, new_checkbox)
items_button_container.insert(new_checkbox, -1)
items_button_container.show_all()
# create the drop-down menu
if context_menu_actions:
action_group = gtk.ActionGroup("context")
for action in context_menu_actions:
action_group.add_action(action)
uimanager = gtk.UIManager()
# The "pos" parameter is optional since gtk 2.12 - we can
# remove it later.
uimanager.insert_action_group(action_group, pos=-1)
uimanager_template = '<ui><popup name="context">%s</popup></ui>'
uimanager_item_template = """<menuitem action="%s" />"""
uimanager_text = uimanager_template % "".join(
[uimanager_item_template % action.get_name()
for action in context_menu_actions])
uimanager.add_ui_from_string(uimanager_text)
self.context_menu = uimanager.get_widget("/context")
self.context_menu.insert(gtk.SeparatorMenuItem(), 0)
else:
self.context_menu = gtk.Menu()
for index, button in enumerate(item_buttons):
new_item = gtk.CheckMenuItem(button.get_label())
new_item.set_active(button.get_active())
connect_button_handlers("toggled", button, new_item)
self.context_menu.insert(new_item, index)
self.context_menu.show_all()
else:
self.context_menu = None
self.core.register_event("model-change-after", self.update_model_dimensions)
self.core.register_event("visual-item-updated", self.update_model_dimensions)
self.core.register_event("visual-item-updated", self.update_view)
self.core.register_event("visualization-state-changed", self._update_widgets)
# show the window
self.area.show()
self.container.show()
self.show()
return True
def teardown(self):
return True
def update_view(self, widget=None, data=None):
if self.is_visible:
self.paint()
def _update_widgets(self):
obj, handler = self._toggle_action_handler
obj.handler_block(handler)
obj.set_active(self.is_visible)
obj.handler_unblock(handler)
def update_model_dimensions(self, widget=None):
models = self.core.get("models").get_visible()
low, high = pycam.Geometry.Model.get_combined_bounds(models)
if None in low or None in high:
low, high = (0, 0, 0), (0, 0, 0)
# model corners in 3D view
if None in low or None in high:
# all models are empty
return
for value, label_suffix in ((low[0], "XMin"), (low[1], "YMin"),
(low[2], "ZMin"), (high[0], "XMax"), (high[1], "YMax"),
(high[2], "ZMax")):
label_name = "ModelCorner%s" % label_suffix
value = "%.3f" % value
self.gui.get_object(label_name).set_label(value)
def toggle_3d_view(self, widget=None, value=None):
current_state = self.is_visible
if value is None:
new_state = not current_state
else:
new_state = value
if new_state == current_state:
return
elif new_state:
if self.is_visible:
self.reset_view()
else:
# the window is just hidden
self.view3d.show()
else:
self.hide()
def show(self):
self.is_visible = True
self.window.move(*self._position)
self.window.show()
def hide(self):
self.is_visible = False
self._position = self.window.get_position()
self.window.hide()
def key_handler(self, widget=None, event=None):
if event is None:
return
try:
keyval = getattr(event, "keyval")
get_state = getattr(event, "get_state")
key_string = getattr(event, "string")
except AttributeError:
return
# define arrow keys and "vi"-like navigation keys
move_keys_dict = {
gtk.keysyms.Left: (1, 0),
gtk.keysyms.Down: (0, -1),
gtk.keysyms.Up: (0, 1),
gtk.keysyms.Right: (-1, 0),
ord("h"): (1, 0),
ord("j"): (0, -1),
ord("k"): (0, 1),
ord("l"): (-1, 0),
ord("H"): (1, 0),
ord("J"): (0, -1),
ord("K"): (0, 1),
ord("L"): (-1, 0),
}
if key_string and (key_string in '1234567'):
names = ["reset", "front", "back", "left", "right", "top", "bottom"]
index = '1234567'.index(key_string)
self.rotate_view(view=VIEWS[names[index]])
self._paint_ignore_busy()
elif key_string in ('i', 'm', 's', 'p'):
if key_string == 'i':
key = "view_light"
elif key_string == 'm':
key = "view_polygon"
elif key_string == 's':
key = "view_shadow"
elif key_string == 'p':
key = "view_perspective"
else:
key = None
# toggle setting
self.core.set(key, not self.core.get(key))
# re-init gl settings
self.glsetup()
self.paint()
elif key_string in ("+", "-"):
if key_string == "+":
self.camera.zoom_in()
else:
self.camera.zoom_out()
self._paint_ignore_busy()
elif keyval in move_keys_dict.keys():
move_x, move_y = move_keys_dict[keyval]
if get_state() & gtk.gdk.SHIFT_MASK:
# shift key pressed -> rotation
base = 0
factor = 10
self.camera.rotate_camera_by_screen(base, base,
base - factor * move_x, base - factor * move_y)
else:
# no shift key -> moving
self.camera.shift_view(x_dist=move_x, y_dist=move_y)
self._paint_ignore_busy()
else:
# see dir(gtk.keysyms)
#print "Key pressed: %s (%s)" % (keyval, get_state())
pass
def check_busy(func):
def check_busy_wrapper(self, *args, **kwargs):
if self.busy:
return
self.busy = True
result = func(self, *args, **kwargs)
self.busy = False
return result
return check_busy_wrapper
def gtkgl_refresh(func):
def gtkgl_refresh_wrapper(self, *args, **kwargs):
prev_mode = GL.glGetIntegerv(GL.GL_MATRIX_MODE)
GL.glMatrixMode(GL.GL_MODELVIEW)
# clear the background with the configured color
bg_col = self.core.get("color_background")
GL.glClearColor(bg_col[0], bg_col[1], bg_col[2], 0.0)
GL.glClear(GL.GL_COLOR_BUFFER_BIT|GL.GL_DEPTH_BUFFER_BIT)
result = func(self, *args, **kwargs)
self.camera.position_camera()
# adjust Light #2
v = self.camera.view
lightpos = (v["center"][0] + v["distance"][0],
v["center"][1] + v["distance"][1],
v["center"][2] + v["distance"][2])
GL.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, lightpos)
self._paint_raw()
GL.glMatrixMode(prev_mode)
GL.glFlush()
self.area.get_gl_drawable().swap_buffers()
return result
return gtkgl_refresh_wrapper
def glsetup(self):
GLUT.glutInit()
GLUT.glutInitDisplayMode(GLUT.GLUT_RGBA | GLUT.GLUT_DOUBLE | \
GLUT.GLUT_DEPTH | GLUT.GLUT_MULTISAMPLE | GLUT.GLUT_ALPHA | \
GLUT.GLUT_ACCUM)
if self.core.get("view_shadow"):
# TODO: implement shadowing (or remove the setting)
pass
# use vertex normals for smooth rendering
GL.glShadeModel(GL.GL_SMOOTH)
bg_col = self.core.get("color_background")
GL.glClearColor(bg_col[0], bg_col[1], bg_col[2], 0.0)
GL.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST)
GL.glMatrixMode(GL.GL_MODELVIEW)
# enable blending/transparency (alpha) for colors
GL.glEnable(GL.GL_BLEND)
# see http://wiki.delphigl.com/index.php/glBlendFunc
GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA)
GL.glEnable(GL.GL_DEPTH_TEST)
# "less" is OpenGL's default
GL.glDepthFunc(GL.GL_LESS)
# slightly improved performance: ignore all faces inside the objects
GL.glCullFace(GL.GL_BACK);
GL.glEnable(GL.GL_CULL_FACE);
# enable antialiasing
GL.glEnable(GL.GL_LINE_SMOOTH)
#GL.glEnable(GL.GL_POLYGON_SMOOTH)
GL.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST)
GL.glHint(GL.GL_POLYGON_SMOOTH_HINT, GL.GL_NICEST)
# TODO: move to toolpath drawing
GL.glLineWidth(0.8)
#GL.glEnable(GL.GL_MULTISAMPLE_ARB)
GL.glEnable(GL.GL_POLYGON_OFFSET_FILL)
GL.glPolygonOffset(1.0, 1.0)
GL.glMaterial(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE,
self.core.get("color_model") )
GL.glMaterial(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR,
(1.0, 1.0, 1.0, 1.0))
GL.glMaterial(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, (100.0))
if self.core.get("view_polygon"):
GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL)
else:
GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE)
GL.glMatrixMode(GL.GL_MODELVIEW)
GL.glLoadIdentity()
GL.glMatrixMode(GL.GL_PROJECTION)
GL.glLoadIdentity()
GL.glViewport(0, 0, self.area.allocation.width,
self.area.allocation.height)
# lighting
GL.glLightModeli(GL.GL_LIGHT_MODEL_LOCAL_VIEWER, GL.GL_TRUE)
# Light #1
# setup the ambient light
GL.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, (0.3, 0.3, 0.3, 1.0))
# setup the diffuse light
GL.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, (0.8, 0.8, 0.8, 1.0))
# setup the specular light
GL.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, (0.1, 0.1, 0.1, 1.0))
# enable Light #1
GL.glEnable(GL.GL_LIGHT0)
# Light #2
# spotlight with small light cone (like a desk lamp)
#GL.glLightfv(GL.GL_LIGHT1, GL.GL_SPOT_CUTOFF, 10.0)
# ... directed at the object
v = self.camera.view
GL.glLightfv(GL.GL_LIGHT1, GL.GL_SPOT_DIRECTION,
(v["center"][0], v["center"][1], v["center"][2]))
GL.glLightfv(GL.GL_LIGHT1, GL.GL_AMBIENT, (0.3, 0.3, 0.3, 1.0))
# and dark outside of the light cone
#GL.glLightfv(GL.GL_LIGHT1, GL.GL_SPOT_EXPONENT, 100.0)
#GL.glLightf(GL.GL_LIGHT1, GL.GL_QUADRATIC_ATTENUATION, 0.5)
# setup the diffuse light
GL.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE, (0.9, 0.9, 0.9, 1.0))
# setup the specular light
GL.glLightfv(GL.GL_LIGHT1, GL.GL_SPECULAR, (1.0, 1.0, 1.0, 1.0))
# enable Light #2
GL.glEnable(GL.GL_LIGHT1)
if self.core.get("view_light"):
GL.glEnable(GL.GL_LIGHTING)
else:
GL.glDisable(GL.GL_LIGHTING)
GL.glEnable(GL.GL_NORMALIZE)
GL.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE)
GL.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR)
#GL.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_EMISSION)
GL.glEnable(GL.GL_COLOR_MATERIAL)
def destroy(self, widget=None, data=None):
self.hide()
self.core.emit_event("visualization-state-changed")
# don't close the window
return True
def gtkgl_functionwrapper(function):
def gtkgl_functionwrapper_function(self, *args, **kwords):
gldrawable = self.area.get_gl_drawable()
if not gldrawable:
return
glcontext = self.area.get_gl_context()
if not gldrawable.gl_begin(glcontext):
return
if not self.initialized:
self.glsetup()
self.initialized = True
result = function(self, *args, **kwords)
gldrawable.gl_end()
return result
return gtkgl_functionwrapper_function
@check_busy
@gtkgl_functionwrapper
def context_menu_handler(self, widget, event):
if (event.button == self.mouse["pressed_button"] == BUTTON_RIGHT) \
and self.context_menu \
and (event.get_time() - self.mouse["pressed_timestamp"] < 300) \
and (abs(event.x - self.mouse["pressed_pos"][0]) < 3) \
and (abs(event.y - self.mouse["pressed_pos"][1]) < 3):
# A quick press/release cycle with the right mouse button
# -> open the context menu.
self.context_menu.popup(None, None, None, event.button,
int(event.get_time()))
@check_busy
@gtkgl_functionwrapper
def scroll_handler(self, widget, event):
""" handle events of the scroll wheel
shift key: horizontal pan instead of vertical
control key: zoom
"""
try:
modifier_state = event.get_state()
except AttributeError:
# this should probably never happen
return
control_pressed = modifier_state & gtk.gdk.CONTROL_MASK
shift_pressed = modifier_state & gtk.gdk.SHIFT_MASK
if (event.direction == gtk.gdk.SCROLL_RIGHT) or \
((event.direction == gtk.gdk.SCROLL_UP) and shift_pressed):
# horizontal move right
self.camera.shift_view(x_dist=-1)
elif (event.direction == gtk.gdk.SCROLL_LEFT) or \
((event.direction == gtk.gdk.SCROLL_DOWN) and shift_pressed):
# horizontal move left
self.camera.shift_view(x_dist=1)
elif (event.direction == gtk.gdk.SCROLL_UP) and control_pressed:
# zoom in
self.camera.zoom_in()
elif event.direction == gtk.gdk.SCROLL_UP:
# vertical move up
self.camera.shift_view(y_dist=1)
elif (event.direction == gtk.gdk.SCROLL_DOWN) and control_pressed:
# zoom out
self.camera.zoom_out()
elif event.direction == gtk.gdk.SCROLL_DOWN:
# vertical move down
self.camera.shift_view(y_dist=-1)
else:
# no interesting event -> no re-painting
return
self._paint_ignore_busy()
def mouse_press_handler(self, widget, event):
self.mouse["pressed_timestamp"] = event.get_time()
self.mouse["pressed_button"] = event.button
self.mouse["pressed_pos"] = event.x, event.y
self.mouse_handler(widget, event)
@check_busy
@gtkgl_functionwrapper
def mouse_handler(self, widget, event):
x, y, state = event.x, event.y, event.state
if self.mouse["button"] is None:
if (state & BUTTON_ZOOM) or (state & BUTTON_ROTATE) \
or (state & BUTTON_MOVE):
self.mouse["button"] = state
self.mouse["start_pos"] = [x, y]
else:
# Don't try to create more than 25 frames per second (enough for
# a decent visualization).
if event.get_time() - self.mouse["event_timestamp"] < 40:
return
elif state & self.mouse["button"] & BUTTON_ZOOM:
# the start button is still active: update the view
start_x, start_y = self.mouse["start_pos"]
self.mouse["start_pos"] = [x, y]
# Move the mouse from lower left to top right corner for
# scaling up.
scale = 1 - 0.01 * ((x - start_x) + (start_y - y))
# do some sanity checks, scale no more than
# 1:100 on any given click+drag
if scale < 0.01:
scale = 0.01
elif scale > 100:
scale = 100
self.camera.scale_distance(scale)
self._paint_ignore_busy()
elif (state & self.mouse["button"] & BUTTON_MOVE) \
or (state & self.mouse["button"] & BUTTON_ROTATE):
start_x, start_y = self.mouse["start_pos"]
self.mouse["start_pos"] = [x, y]
if (state & BUTTON_MOVE):
# Determine the biggest dimension (x/y/z) for moving the
# screen's center in relation to this value.
obj_dim = []
low, high = pycam.Geometry.Model.get_combined_bounds(
self.core.get("models").get_visible())
for index in range(3):
obj_dim.append(high[index] - low[index])
max_dim = max(obj_dim)
self.camera.move_camera_by_screen(x - start_x, y - start_y,
max_dim)
else:
# BUTTON_ROTATE
# update the camera position according to the mouse movement
self.camera.rotate_camera_by_screen(start_x, start_y, x, y)
self._paint_ignore_busy()
else:
# button was released
self.mouse["button"] = None
self._paint_ignore_busy()
self.mouse["event_timestamp"] = event.get_time()
@check_busy
@gtkgl_functionwrapper
@gtkgl_refresh
def rotate_view(self, widget=None, view=None):
self.camera.set_view(view)
def reset_view(self):
self.rotate_view(None, None)
@check_busy
@gtkgl_functionwrapper
@gtkgl_refresh
def _resize_window(self, widget, data=None):
GL.glViewport(0, 0, self.area.allocation.width,
self.area.allocation.height)
@check_busy
@gtkgl_functionwrapper
@gtkgl_refresh
def paint(self, widget=None, data=None):
# the decorators take core for redraw
pass
@gtkgl_functionwrapper
@gtkgl_refresh
def _paint_ignore_busy(self, widget=None):
pass
def _paint_raw(self, widget=None):
# draw the model
draw_complete_model_view(self.core)
self.core.emit_event("visualize-items")
# update the dimension display
s = self.core
dimension_bar = self.gui.get_object("view3ddimension")
low, high = pycam.Geometry.Model.get_combined_bounds(
self.core.get("models").get_visible())
if not (None in low or None in high) and s.get("show_dimensions"):
for name, size in (
("model_dim_x", high[0] - low[0]),
("model_dim_y", high[1] - low[1]),
("model_dim_z", high[2] - low[2])):
self.gui.get_object(name).set_text("%.3f %s" \
% (size, s.get("unit")))
dimension_bar.show()
else:
dimension_bar.hide()
class Camera(object):
def __init__(self, core, get_dim_func, view=None):
self.view = None
self.core = core
self._get_dim_func = get_dim_func
self.set_view(view)
def set_view(self, view=None):
if view is None:
self.view = VIEWS["reset"].copy()
else:
self.view = view.copy()
self.center_view()
self.auto_adjust_distance()
def _get_low_high_dims(self):
# center the view on the object
models = self.core.get("models").get_visible()
return pycam.Geometry.Model.get_combined_bounds(models)
def center_view(self):
center = []
low, high = self._get_low_high_dims()
if None in low or None in high:
center = [0, 0, 0]
else:
for index in range(3):
center.append((low[index] + high[index]) / 2)
self.view["center"] = center
def auto_adjust_distance(self):
s = self.core
v = self.view
# adjust the distance to get a view of the whole object
low_high = zip(*self._get_low_high_dims())
if (None, None) in low_high:
return
max_dim = max([high - low for low, high in low_high])
distv = Point(v["distance"][0], v["distance"][1],
v["distance"][2]).normalized()
# The multiplier "1.25" is based on experiments. 1.414 (sqrt(2)) should
# be roughly sufficient for showing the diagonal of any model.
distv = distv.mul((max_dim * 1.25) / number(math.sin(v["fovy"] / 2)))
self.view["distance"] = (distv.x, distv.y, distv.z)
# Adjust the "far" distance for the camera to make sure, that huge
# models (e.g. x=1000) are still visible.
self.view["zfar"] = 100 * max_dim
def scale_distance(self, scale):
if scale != 0:
scale = number(scale)
dist = self.view["distance"]
self.view["distance"] = (scale * dist[0], scale * dist[1],
scale * dist[2])
def get(self, key, default=None):
if (not self.view is None) and self.view.has_key(key):
return self.view[key]
else:
return default
def set(self, key, value):
self.view[key] = value
def move_camera_by_screen(self, x_move, y_move, max_model_shift):
""" move the camera acoording to a mouse movement
@type x_move: int
@value x_move: movement of the mouse along the x axis
@type y_move: int
@value y_move: movement of the mouse along the y axis
@type max_model_shift: float
@value max_model_shift: maximum shifting of the model view (e.g. for
x_move == screen width)
"""
factors_x, factors_y = self._get_axes_vectors()
width, height = self._get_screen_dimensions()
# relation of x/y movement to the respective screen dimension
win_x_rel = (-2 * x_move) / float(width) / math.sin(self.view["fovy"])
win_y_rel = (-2 * y_move) / float(height) / math.sin(self.view["fovy"])
# This code is completely arbitrarily based on trial-and-error for
# finding a nice movement speed for all distances.
# Anyone with a better approach should just fix this.
distance_vector = self.get("distance")
distance = float(sqrt(sum([dim ** 2 for dim in distance_vector])))
win_x_rel *= math.cos(win_x_rel / distance) ** 20
win_y_rel *= math.cos(win_y_rel / distance) ** 20
# update the model position that should be centered on the screen
old_center = self.view["center"]
new_center = []
for i in range(3):
new_center.append(old_center[i] \
+ max_model_shift * (number(win_x_rel) * factors_x[i] \
+ number(win_y_rel) * factors_y[i]))
self.view["center"] = tuple(new_center)
def rotate_camera_by_screen(self, start_x, start_y, end_x, end_y):
factors_x, factors_y = self._get_axes_vectors()
width, height = self._get_screen_dimensions()
# calculate rotation factors - based on the distance to the center
# (between -1 and 1)
rot_x_factor = (2.0 * start_x) / width - 1
rot_y_factor = (2.0 * start_y) / height - 1
# calculate rotation angles (between -90 and +90 degrees)
xdiff = end_x - start_x
ydiff = end_y - start_y
# compensate inverse rotation left/right side (around x axis) and
# top/bottom (around y axis)
if rot_x_factor < 0:
ydiff = -ydiff
if rot_y_factor > 0:
xdiff = -xdiff
rot_x_angle = rot_x_factor * math.pi * ydiff / height
rot_y_angle = rot_y_factor * math.pi * xdiff / width
# rotate around the "up" vector with the y-axis rotation
original_distance = self.view["distance"]
original_up = self.view["up"]
y_rot_matrix = Matrix.get_rotation_matrix_axis_angle(factors_y,
rot_y_angle)
new_distance = Matrix.multiply_vector_matrix(original_distance,
y_rot_matrix)
new_up = Matrix.multiply_vector_matrix(original_up, y_rot_matrix)
# rotate around the cross vector with the x-axis rotation
x_rot_matrix = Matrix.get_rotation_matrix_axis_angle(factors_x,
rot_x_angle)
new_distance = Matrix.multiply_vector_matrix(new_distance, x_rot_matrix)
new_up = Matrix.multiply_vector_matrix(new_up, x_rot_matrix)
self.view["distance"] = new_distance
self.view["up"] = new_up
def position_camera(self):
width, height = self._get_screen_dimensions()
prev_mode = GL.glGetIntegerv(GL.GL_MATRIX_MODE)
GL.glMatrixMode(GL.GL_PROJECTION)
GL.glLoadIdentity()
v = self.view
# position the light according to the current bounding box
light_pos = [0, 0, 0]
low, high = self._get_low_high_dims()
if not None in low and not None in high:
for index in range(3):
light_pos[index] = 2 * (high[index] - low[index])
GL.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, (light_pos[0], light_pos[1],
light_pos[2], 0.0))
# position the camera
camera_position = (v["center"][0] + v["distance"][0],
v["center"][1] + v["distance"][1],
v["center"][2] + v["distance"][2])
# position a second light at camera position
GL.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, (camera_position[0],
camera_position[1], camera_position[2], 0.0))
if self.core.get("view_perspective"):
# perspective view
GLU.gluPerspective(v["fovy"], (0.0 + width) / height, v["znear"],
v["zfar"])
else:
# parallel projection
# This distance calculation is completely based on trial-and-error.
distance = math.sqrt(sum([d ** 2 for d in v["distance"]]))
distance *= math.log(math.sqrt(width * height)) / math.log(10)
sin_factor = math.sin(v["fovy"] / 360.0 * math.pi) * distance
left = v["center"][0] - sin_factor
right = v["center"][0] + sin_factor
top = v["center"][1] + sin_factor
bottom = v["center"][1] - sin_factor
near = v["center"][2] - 2 * sin_factor
far = v["center"][2] + 2 * sin_factor
GL.glOrtho(left, right, bottom, top, near, far)
GLU.gluLookAt(camera_position[0], camera_position[1],
camera_position[2], v["center"][0], v["center"][1],
v["center"][2], v["up"][0], v["up"][1], v["up"][2])
GL.glMatrixMode(prev_mode)
def shift_view(self, x_dist=0, y_dist=0):
obj_dim = []
low, high = self._get_low_high_dims()
for index in range(3):
obj_dim.append(high[index] - low[index])
max_dim = max(obj_dim)
factor = 50
self.move_camera_by_screen(x_dist * factor, y_dist * factor, max_dim)
def zoom_in(self):
self.scale_distance(sqrt(0.5))
def zoom_out(self):
self.scale_distance(sqrt(2))
def _get_screen_dimensions(self):
return self._get_dim_func()
def _get_axes_vectors(self):
"""calculate the model vectors along the screen's x and y axes"""
# The "up" vector defines, in what proportion each axis of the model is
# in line with the screen's y axis.
v_up = self.view["up"]
factors_y = (number(v_up[0]), number(v_up[1]), number(v_up[2]))
# Calculate the proportion of each model axis according to the x axis of
# the screen.
distv = self.view["distance"]
distv = Point(distv[0], distv[1], distv[2]).normalized()
factors_x = distv.cross(Point(v_up[0], v_up[1], v_up[2])).normalized()
factors_x = (factors_x.x, factors_x.y, factors_x.z)
return (factors_x, factors_y)
......@@ -76,8 +76,23 @@ class PluginBase(object):
"'setup'") % (self.name, __file__))
def teardown(self):
raise NotImplementedError("Module %s (%s) does not implement " + \
"'teardown'" % (self.name, __file__))
raise NotImplementedError(("Module %s (%s) does not implement " + \
"'teardown'") % (self.name, __file__))
def register_gtk_accelerator(self, groupname, action, accel_string,
accel_name):
# menu item and shortcut
try:
import gtk
except ImportError:
return
actiongroup = gtk.ActionGroup(groupname)
key, mod = gtk.accelerator_parse(accel_string)
accel_path = "<pycam>/%s" % accel_name
action.set_accel_path(accel_path)
gtk.accel_map_change_entry(accel_path, key, mod, True)
actiongroup.add_action(action)
self.core.get("gtk-uimanager").insert_action_group(actiongroup, pos=-1)
class PluginManager(object):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment