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 @@ ...@@ -98,463 +98,6 @@
</object> </object>
</child> </child>
</object> </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"> <object class="GtkAdjustment" id="DrillProgressMaxFPS">
<property name="lower">1</property> <property name="lower">1</property>
<property name="upper">25</property> <property name="upper">25</property>
...@@ -606,10 +149,6 @@ ...@@ -606,10 +149,6 @@
<object class="GtkAction" id="WindowMenu"> <object class="GtkAction" id="WindowMenu">
<property name="label">_Windows</property> <property name="label">_Windows</property>
</object> </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"> <object class="GtkAction" id="SaveAsModel">
<property name="label">Save Model _as ...</property> <property name="label">Save Model _as ...</property>
<property name="short_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 ...@@ -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/>. 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: try:
import gtk.gtkgl
import OpenGL.GL as GL import OpenGL.GL as GL
import OpenGL.GLU as GLU
import OpenGL.GLUT as GLUT import OpenGL.GLUT as GLUT
GL_ENABLED = True
except (ImportError, RuntimeError): 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 gtk
import pango
import math 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() log = pycam.Utils.log.get_logger()
...@@ -88,724 +58,6 @@ def connect_button_handlers(signal, original_button, derived_button): ...@@ -88,724 +58,6 @@ def connect_button_handlers(signal, original_button, derived_button):
original_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(func):
def keep_gl_mode_wrapper(*args, **kwargs): def keep_gl_mode_wrapper(*args, **kwargs):
prev_mode = GL.glGetIntegerv(GL.GL_MATRIX_MODE) prev_mode = GL.glGetIntegerv(GL.GL_MATRIX_MODE)
...@@ -864,106 +116,11 @@ def draw_direction_cone(p1, p2): ...@@ -864,106 +116,11 @@ def draw_direction_cone(p1, p2):
# draw the cone # draw the cone
GLUT.glutSolidCone(cone_radius, cone_length, 12, 1) 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_gl_mode
@keep_matrix @keep_matrix
def draw_complete_model_view(settings): def draw_complete_model_view(settings):
GL.glMatrixMode(GL.GL_MODELVIEW) GL.glMatrixMode(GL.GL_MODELVIEW)
GL.glLoadIdentity() 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) # draw the material (for simulation mode)
if settings.get("show_simulation"): if settings.get("show_simulation"):
obj = settings.get("simulation_object") obj = settings.get("simulation_object")
...@@ -972,35 +129,6 @@ def draw_complete_model_view(settings): ...@@ -972,35 +129,6 @@ def draw_complete_model_view(settings):
# we need to wait until the color change is active # we need to wait until the color change is active
GL.glFinish() GL.glFinish()
obj.to_OpenGL() 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 # draw the support grid
if settings.get("show_support_grid") and settings.get("current_support_model"): if settings.get("show_support_grid") and settings.get("current_support_model"):
GL.glColor4f(*settings.get("color_support_grid")) GL.glColor4f(*settings.get("color_support_grid"))
...@@ -1018,7 +146,7 @@ def draw_complete_model_view(settings): ...@@ -1018,7 +146,7 @@ def draw_complete_model_view(settings):
# draw the toolpath # draw the toolpath
# don't do it, if a new toolpath is just being calculated # don't do it, if a new toolpath is just being calculated
safety_height = settings.get("gcode_safety_height") 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("toolpath_in_progress") \
and not (settings.get("show_simulation") \ and not (settings.get("show_simulation") \
and settings.get("simulation_toolpath_moves")): and settings.get("simulation_toolpath_moves")):
...@@ -1034,6 +162,7 @@ def draw_complete_model_view(settings): ...@@ -1034,6 +162,7 @@ def draw_complete_model_view(settings):
cutter = settings.get("cutter") cutter = settings.get("cutter")
if not cutter is None: if not cutter is None:
GL.glColor4f(*settings.get("color_cutter")) GL.glColor4f(*settings.get("color_cutter"))
GL.glFinish()
cutter.to_OpenGL() cutter.to_OpenGL()
if settings.get("show_drill_progress") \ if settings.get("show_drill_progress") \
and settings.get("toolpath_in_progress"): and settings.get("toolpath_in_progress"):
......
...@@ -40,7 +40,6 @@ from pycam.Utils.locations import get_data_file_location, \ ...@@ -40,7 +40,6 @@ from pycam.Utils.locations import get_data_file_location, \
get_all_program_locations get_all_program_locations
import pycam.Utils import pycam.Utils
from pycam.Geometry.utils import sqrt from pycam.Geometry.utils import sqrt
from pycam.Gui.OpenGLTools import ModelViewWindowGL
import pycam.Geometry.Model import pycam.Geometry.Model
from pycam.Toolpath import Bounds from pycam.Toolpath import Bounds
import pycam.Plugins import pycam.Plugins
...@@ -309,7 +308,6 @@ class ProjectGui(object): ...@@ -309,7 +308,6 @@ class ProjectGui(object):
def __init__(self, no_dialog=False): def __init__(self, no_dialog=False):
self.settings = EventCore() self.settings = EventCore()
self.gui_is_active = False self.gui_is_active = False
self.view3d = None
# during initialization any dialog (e.g. "Unit change") is not allowed # during initialization any dialog (e.g. "Unit change") is not allowed
# we set the final value later # we set the final value later
self.no_dialog = True self.no_dialog = True
...@@ -361,7 +359,6 @@ class ProjectGui(object): ...@@ -361,7 +359,6 @@ class ProjectGui(object):
("ExportEMCToolDefinition", self.export_emc_tools, None, None), ("ExportEMCToolDefinition", self.export_emc_tools, None, None),
("Quit", self.destroy, None, "<Control>q"), ("Quit", self.destroy, None, "<Control>q"),
("GeneralSettings", self.toggle_preferences_window, None, "<Control>p"), ("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), ("ToggleProcessPoolWindow", self.toggle_process_pool_window, None, None),
("UndoButton", self._restore_undo_state, None, "<Control>z"), ("UndoButton", self._restore_undo_state, None, "<Control>z"),
("HelpUserManual", self.show_help, "User_Manual", "F1"), ("HelpUserManual", self.show_help, "User_Manual", "F1"),
...@@ -385,7 +382,7 @@ class ProjectGui(object): ...@@ -385,7 +382,7 @@ class ProjectGui(object):
("BugTracker", self.show_help, "http://sourceforge.net/tracker/?group_id=237831&atid=1104176", None), ("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)): ("FeatureRequest", self.show_help, "http://sourceforge.net/tracker/?group_id=237831&atid=1104179", None)):
item = self.gui.get_object(objname) item = self.gui.get_object(objname)
if objname in ("Toggle3DView", "ToggleProcessPoolWindow"): if objname == "ToggleProcessPoolWindow":
action = "toggled" action = "toggled"
else: else:
action = "activate" action = "activate"
...@@ -405,20 +402,23 @@ class ProjectGui(object): ...@@ -405,20 +402,23 @@ class ProjectGui(object):
gtk.link_button_set_uri_hook(open_url) gtk.link_button_set_uri_hook(open_url)
# no undo is allowed at the beginning # no undo is allowed at the beginning
self.gui.get_object("UndoButton").set_sensitive(False) 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-before",
self.settings.register_event("model-change-after", self.update_model_dimensions) self._store_undo_state)
self.settings.register_event("model-change-after", self.update_save_actions) self.settings.register_event("model-change-after",
self.settings.register_event("model-change-after", self.update_view) self.update_save_actions)
self.settings.register_event("visual-item-updated", self.update_view) self.settings.register_event("model-selection-changed",
self.settings.register_event("visual-item-updated", self.update_model_dimensions) self.update_save_actions)
self.settings.register_event("model-change-after",
lambda: self.settings.emit_event("visual-item-updated"))
def update_emc_tool_button(): def update_emc_tool_button():
tool_num = len(self.settings.get("tools")) tool_num = len(self.settings.get("tools"))
self.gui.get_object("ExportEMCToolDefinition").set_sensitive(tool_num > 0) self.gui.get_object("ExportEMCToolDefinition").set_sensitive(tool_num > 0)
self.settings.register_event("tool-selection-changed", update_emc_tool_button) self.settings.register_event("tool-selection-changed", update_emc_tool_button)
self.settings.set("load_model", self.load_model) 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 # 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 # other events
self.window.connect("destroy", self.destroy) self.window.connect("destroy", self.destroy)
# the settings window # the settings window
...@@ -554,7 +554,8 @@ class ProjectGui(object): ...@@ -554,7 +554,8 @@ class ProjectGui(object):
obj = self.gui.get_object(objname) obj = self.gui.get_object(objname)
self.settings.add_item(name, obj.get_active, obj.set_active) self.settings.add_item(name, obj.get_active, obj.set_active)
# send "True" to trigger a re-setup of GL settings # 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 # color selectors
def get_color_wrapper(obj): def get_color_wrapper(obj):
def gtk_color_to_float(): def gtk_color_to_float():
...@@ -588,7 +589,8 @@ class ProjectGui(object): ...@@ -588,7 +589,8 @@ class ProjectGui(object):
obj = self.gui.get_object(objname) obj = self.gui.get_object(objname)
self.settings.add_item(name, get_color_wrapper(obj), set_color_wrapper(obj)) self.settings.add_item(name, get_color_wrapper(obj), set_color_wrapper(obj))
# repaint the 3d view after a color change # 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 # set the availability of ODE
self.enable_ode_control = self.gui.get_object("SettingEnableODE") self.enable_ode_control = self.gui.get_object("SettingEnableODE")
self.settings.add_item("enable_ode", self.enable_ode_control.get_active, self.settings.add_item("enable_ode", self.enable_ode_control.get_active,
...@@ -652,7 +654,8 @@ class ProjectGui(object): ...@@ -652,7 +654,8 @@ class ProjectGui(object):
self.settings.add_item("touch_off_pause_execution", self.settings.add_item("touch_off_pause_execution",
touch_off_pause.get_active, touch_off_pause.set_active) touch_off_pause.get_active, touch_off_pause.set_active)
# redraw the toolpath if safety height changed # 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") gcode_path_mode = self.gui.get_object("GCodeCornerStyleControl")
self.settings.add_item("gcode_path_mode", gcode_path_mode.get_active, self.settings.add_item("gcode_path_mode", gcode_path_mode.get_active,
gcode_path_mode.set_active) gcode_path_mode.set_active)
...@@ -801,12 +804,10 @@ class ProjectGui(object): ...@@ -801,12 +804,10 @@ class ProjectGui(object):
# register a logging handler for displaying error messages # register a logging handler for displaying error messages
pycam.Utils.log.add_gtk_gui(self.window, logging.ERROR) pycam.Utils.log.add_gtk_gui(self.window, logging.ERROR)
self.window.show() self.window.show()
self.toggle_3d_view(value=True)
def update_all_controls(self): def update_all_controls(self):
self.update_save_actions() self.update_save_actions()
self.update_unit_labels() self.update_unit_labels()
self.update_model_dimensions()
self.update_gcode_controls() self.update_gcode_controls()
self.update_ode_settings() self.update_ode_settings()
self.update_parallel_processes_settings() self.update_parallel_processes_settings()
...@@ -906,12 +907,6 @@ class ProjectGui(object): ...@@ -906,12 +907,6 @@ class ProjectGui(object):
url = page url = page
webbrowser.open(url) 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): def set_model_filename(self, filename):
""" Store the given filename for a possible later "save model" action. """ Store the given filename for a possible later "save model" action.
Additionally the window's title is adjusted and the "save" buttons are Additionally the window's title is adjusted and the "save" buttons are
...@@ -931,8 +926,8 @@ class ProjectGui(object): ...@@ -931,8 +926,8 @@ class ProjectGui(object):
bool(self.last_task_settings_uri and \ bool(self.last_task_settings_uri and \
self.last_task_settings_uri.is_writable())) self.last_task_settings_uri.is_writable()))
# TODO: choose all models # TODO: choose all models
model = self.settings.get("models") and self.settings.get("models")[0] models = self.settings.get("models").get_selected()
save_as_possible = (not model is None) and model.is_export_supported() save_as_possible = bool(models) and models[0].is_export_supported()
self.gui.get_object("SaveAsModel").set_sensitive(save_as_possible) self.gui.get_object("SaveAsModel").set_sensitive(save_as_possible)
save_possible = bool(self.last_model_uri and save_as_possible and \ save_possible = bool(self.last_model_uri and save_as_possible and \
self.last_model_uri.is_writable()) self.last_model_uri.is_writable())
...@@ -1153,68 +1148,6 @@ class ProjectGui(object): ...@@ -1153,68 +1148,6 @@ class ProjectGui(object):
# don't repeat, if the window is hidden # don't repeat, if the window is hidden
return self.gui.get_object("ToggleProcessPoolWindow").get_active() 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): def change_unit_init(self, widget=None):
new_unit = self.gui.get_object("unit_control").get_active_text() new_unit = self.gui.get_object("unit_control").get_active_text()
if self._last_unit is None: if self._last_unit is None:
...@@ -1458,26 +1391,7 @@ class ProjectGui(object): ...@@ -1458,26 +1391,7 @@ class ProjectGui(object):
except IOError, err_msg: except IOError, err_msg:
log.warn("Failed to write preferences file (%s): %s" % (config_filename, 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): def destroy(self, widget=None, data=None):
self.update_view()
gtk.main_quit() gtk.main_quit()
self.quit() self.quit()
...@@ -1622,11 +1536,6 @@ class ProjectGui(object): ...@@ -1622,11 +1536,6 @@ class ProjectGui(object):
self.settings.emit_event("model-change-before") self.settings.emit_event("model-change-before")
self.settings.get("models").append(model) self.settings.get("models").append(model)
self.last_model_uri = None 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 return True
else: else:
return False return False
...@@ -1696,7 +1605,8 @@ class ProjectGui(object): ...@@ -1696,7 +1605,8 @@ class ProjectGui(object):
self.func() self.func()
# break the loop if someone clicked the "cancel" button # break the loop if someone clicked the "cancel" button
return progress.update(text=text, percent=percent) 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 max_fps=self.settings.get("drill_progress_max_fps")).update
progress.update(text="Generating collision model") progress.update(text="Generating collision model")
...@@ -1751,7 +1661,6 @@ class ProjectGui(object): ...@@ -1751,7 +1661,6 @@ class ProjectGui(object):
# the tool id numbering should start with 1 instead of zero # the tool id numbering should start with 1 instead of zero
self.toolpath.add_toolpath(toolpath, description, toolpath_settings) self.toolpath.add_toolpath(toolpath, description, toolpath_settings)
self.update_toolpath_table() self.update_toolpath_table()
self.update_view()
# return "False" if the action was cancelled # return "False" if the action was cancelled
cancelled = progress.update() cancelled = progress.update()
result = not cancelled result = not cancelled
......
...@@ -184,6 +184,34 @@ class Bounds(pycam.Plugins.ListPluginBase): ...@@ -184,6 +184,34 @@ class Bounds(pycam.Plugins.ListPluginBase):
for not_found in remaining: for not_found in remaining:
models.remove(not_found) 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): def _render_model_name(self, column, cell, model, m_iter):
path = model.get_path(m_iter) path = model.get_path(m_iter)
all_models = self.core.get("models") all_models = self.core.get("models")
......
...@@ -78,17 +78,10 @@ class Fonts(pycam.Plugins.PluginBase): ...@@ -78,17 +78,10 @@ class Fonts(pycam.Plugins.PluginBase):
self.update_font_dialog_preview) self.update_font_dialog_preview)
# use global key accel map # use global key accel map
self.font_dialog_window.add_accel_group(self.core.get("gtk-accel-group")) 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") 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) 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 # store window position
self._font_dialog_window_visible = False self._font_dialog_window_visible = False
self._font_dialog_window_position = None self._font_dialog_window_position = None
......
...@@ -95,27 +95,27 @@ class ModelPosition(pycam.Plugins.PluginBase): ...@@ -95,27 +95,27 @@ class ModelPosition(pycam.Plugins.PluginBase):
self.core.emit_event("model-change-before") self.core.emit_event("model-change-before")
dest = [self.gui.get_object("AlignPosition%s" % axis).get_value() dest = [self.gui.get_object("AlignPosition%s" % axis).get_value()
for axis in "XYZ"] for axis in "XYZ"]
shift_values = []
for axis in "XYZ":
dest = self.gui.get_object("AlignPosition%s" % axis).get_value()
alignments = ("Min", "Center", "Max")
for alignment in alignments:
objname = "AlignPosition%s%s" % (axis, alignment)
min_axis = getattr(model, "min%s" % axis.lower())
max_axis = getattr(model, "max%s" % axis.lower())
if self.gui.get_object(objname).get_active():
if alignment == "Min":
shift = dest - min_axis
elif alignment == "Center":
shift = dest - (min_axis + max_axis) / 2.0
else:
shift = dest - max_axis
shift_values.append(shift)
progress = self.core.get("progress") progress = self.core.get("progress")
progress.update(text="Shifting model") progress.update(text="Shifting model")
progress.disable_cancel() progress.disable_cancel()
progress.set_multiple(len(models), "Model") progress.set_multiple(len(models), "Model")
for model in models: for model in models:
shift_values = []
for axis in "XYZ":
dest = self.gui.get_object("AlignPosition%s" % axis).get_value()
alignments = ("Min", "Center", "Max")
for alignment in alignments:
objname = "AlignPosition%s%s" % (axis, alignment)
min_axis = getattr(model, "min%s" % axis.lower())
max_axis = getattr(model, "max%s" % axis.lower())
if self.gui.get_object(objname).get_active():
if alignment == "Min":
shift = dest - min_axis
elif alignment == "Center":
shift = dest - (min_axis + max_axis) / 2.0
else:
shift = dest - max_axis
shift_values.append(shift)
model.shift(shift_values[0], shift_values[1], shift_values[2], model.shift(shift_values[0], shift_values[1], shift_values[2],
callback=progress.update) callback=progress.update)
progress.update_multiple() 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): ...@@ -76,8 +76,23 @@ class PluginBase(object):
"'setup'") % (self.name, __file__)) "'setup'") % (self.name, __file__))
def teardown(self): def teardown(self):
raise NotImplementedError("Module %s (%s) does not implement " + \ raise NotImplementedError(("Module %s (%s) does not implement " + \
"'teardown'" % (self.name, __file__)) "'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): 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