8 |
Basic Work Flow
9 |
Use File menu to read in the image and various region definitions which define what to render in 3D.
10 |
11 |
Use View-Mesh View to show/hide the 3D renderered view.
12 |
13 |
Stages toggle the various processing stages.
14 |
15 |
By default, processing occurs automatically. This can be toggled on/off through Preferences->Auto Reprocess
16 |
17 |
There are a number of basic image manipulation key commands available. The image display is based on ginga.
18 | The key commands can be found in the ginga documentation
19 |
20 |
To create new regions, right-click on "Regions".
21 | To delete, hide, or edit existing regions, click on or right-click on the desired region.
22 |
23 |
24 |
When complete, use File->Save to save all regions and the STL model files.
25 |
26 | #
27 | # Shape editor
28 | #
29 | shape_editor:
30 | default: |
31 | To create new regions, right-click on "Regions".
32 |
33 | To delete, hide, or edit existing regions, click on or right-click on the desired region.
34 | draw: |
35 | Select the shape desired.
36 |
37 | To create/modify a pixel mask, select "paint".
38 | edit: |
39 | Click-drag to move the region.
40 |
41 | Click-drag any of the "control points" to to change the size or shape of the region.
42 |
43 | Enter an angle and hit RETURN to change region rotation.
44 |
45 | Enter a scale factor and hit RETURN to change region size.
46 | edit_select: |
47 | Click-drag to move the region.
48 |
49 | Click-drag any of the "control points" to change the size or shape of the region.
50 |
51 | Enter an angle and hit RETURN to change region rotation.
52 |
53 | Enter a scale factor and hit RETURN to change region size.
54 | paint: |
55 | Click-drag to either add to or erase a mask.
56 |
57 | Choose whether you are "Paint"ing or "Erase"ing.
58 |
59 | Brush size determines how much area is affected while painting.
60 | paint_edit: |
61 | Click-drag to either add to or erase a mask.
62 |
63 | Choose whether you are "Paint"ing or "Erase"ing.
64 |
65 | Brush size determines how much area is affected while painting.
66 |
--------------------------------------------------------------------------------
/astro3d/core/texture_examples.py:
--------------------------------------------------------------------------------
1 | """
2 | This module provides tools to produce texture samples.
3 | """
4 | from __future__ import (absolute_import, division, print_function,
5 | unicode_literals)
6 | import numpy as np
7 | from astropy.io import fits
8 | from astropy import log
9 | from . import textures
10 |
11 |
12 | __doctest_skip__ = ['textures_to_jpeg']
13 |
14 |
15 | def save_image(data, filename):
16 | """
17 | Save an image to a bitmap (e.g. TIFF, JPEG, PNG) or a FITS file.
18 |
19 | The type of file is determined by the filename suffix.
20 |
21 | Parameters
22 | ----------
23 | data : `~numpy.ndarray`
24 | The data array to save.
25 |
26 | filename : str
27 | The output filename.
28 | """
29 |
30 | if isinstance(data, np.ma.core.MaskedArray):
31 | data = data.data
32 |
33 | suffix = filename.split('.')[-1].lower()
34 | if suffix in ('fit', 'fits'):
35 | fits.writeto(filename, data, clobber=True)
36 | else:
37 | from scipy.misc import imsave # uses PIL/Pillow
38 | imsave(filename, np.flipud(data)) # flip to make origin lower-left
39 | log.info('Saved {0}.'.format(filename))
40 |
41 |
42 | def textures_to_jpeg():
43 | """
44 | Generate some textures and save them to JPEG images.
45 |
46 | Examples
47 | --------
48 | >>> from astro3d import texture_examples
49 | >>> texture_examples.textures_to_jpeg()
50 | """
51 |
52 | shape = (200, 200)
53 | size = [15, 10, 6, 3] # line thickness or dot diameter
54 | spacing = [25, 15, 10, 5] # line spacing or dot grid spacing
55 |
56 | for sz, sp in zip(size, spacing):
57 | log.info('{0} {1}'.format(sz, sp))
58 | for profile in ['spherical', 'linear']:
59 | log.info('\t{0}'.format(profile))
60 |
61 | lim = textures.lines_texture(shape, profile, sz, 1., sp,
62 | orientation=0.)
63 | fn = ('lines_{0}_thickness{1}_spacing{2}'
64 | '.jpg'.format(profile, sz, sp))
65 | save_image(lim, fn)
66 |
67 | rlim = lim.transpose()
68 | lim[rlim > lim] = rlim[rlim > lim]
69 | fn = ('hatch_{0}_thickness{1}_spacing{2}'
70 | '.jpg'.format(profile, sz, sp))
71 | save_image(lim, fn)
72 |
73 | sdim = textures.dots_texture(shape, profile, sz, 1.,
74 | textures.square_grid(shape, sp))
75 | fn = ('dots_squaregrid_{0}_diameter{1}_spacing{2}'
76 | '.jpg'.format(profile, sz, sp))
77 | save_image(sdim, fn)
78 |
79 | hdim = textures.dots_texture(shape, profile, sz, 1,
80 | textures.hexagonal_grid(shape, sp))
81 | fn = ('dots_hexagonalgrid_{0}_diameter{1}_spacing{2}'
82 | '.jpg'.format(profile, sz, sp))
83 | save_image(hdim, fn)
84 |
--------------------------------------------------------------------------------
/astro3d/gui/qt/layer_manager.py:
--------------------------------------------------------------------------------
1 | """Layer Manager"""
2 |
3 | from qtpy import QtWidgets
4 |
5 | from ...util.logger import make_null_logger
6 | from .items import LayerItem, Action
7 | from .. import signaldb
8 |
9 | # Configure logging
10 | logger = make_null_logger(__name__)
11 |
12 | __all__ = ['LayerManager']
13 |
14 |
15 | class LayerManager(QtWidgets.QTreeView):
16 | """Manager the various layers"""
17 |
18 | def __init__(self, *args, **kwargs):
19 | super(LayerManager, self).__init__(*args, **kwargs)
20 | self.setHeaderHidden(True)
21 |
22 | def selectionChanged(self, selected, deselected):
23 | """QT builtin slot called when a selection is changed"""
24 |
25 | def get_selected_item(itemselection):
26 | try:
27 | index = itemselection.indexes()[0]
28 | item = self.model().itemFromIndex(index)
29 | if not item.is_available:
30 | item = None
31 | except IndexError:
32 | item = None
33 | return item
34 |
35 | selected_layer = get_selected_item(selected)
36 | deselected_layer = get_selected_item(deselected)
37 | logger.debug('selected="{}" deselected="{}"'.format(
38 | selected_layer,
39 | deselected_layer
40 | ))
41 | signaldb.LayerSelected(
42 | selected_item=selected_layer,
43 | deselected_item=deselected_layer,
44 | source='layermanager')
45 |
46 | def select_from_object(self,
47 | selected_item=None,
48 | deselected_item=None,
49 | source=None):
50 | # If from the layer manager, there is nothing that need be
51 | # done.
52 | if source == 'layermanager':
53 | return
54 |
55 | try:
56 | self.setCurrentIndex(selected_item.index())
57 | except AttributeError:
58 | """IF cannot select, doesn't matter"""
59 | pass
60 |
61 | def contextMenuEvent(self, event):
62 | logger.debug('event = "{}"'.format(event))
63 |
64 | indexes = self.selectedIndexes()
65 | if len(indexes) > 0:
66 | index = indexes[0]
67 | item = self.model().itemFromIndex(index)
68 | menu = QtWidgets.QMenu()
69 | for action_def in item._actions:
70 | if isinstance(action_def, Action):
71 | action = menu.addAction(action_def.text)
72 | action.setData(action_def)
73 | else:
74 | menu.addAction(action_def)
75 |
76 | taken = menu.exec_(event.globalPos())
77 | if taken:
78 | logger.debug(
79 | 'taken action = "{}", data="{}"'.format(
80 | taken,
81 | taken.data()
82 | )
83 | )
84 | action_def = taken.data()
85 | action_def.func(*action_def.args)
86 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist =
3 | py{37,38,39}-test{,-alldeps,-devdeps}{,-cov}
4 | py{37,38,39}-test-numpy{117,118,119,120}
5 | py{37,38,39}-test-astropy{40,lts}
6 | build_docs
7 | linkcheck
8 | codestyle
9 | requires =
10 | setuptools >= 30.3.0
11 | pip >= 19.3.1
12 | isolated_build = true
13 |
14 | [testenv]
15 | # Suppress display of matplotlib plots generated during docs build
16 | setenv =
17 | MPLBACKEND=agg
18 | devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/scientific-python-nightly-wheels/simple
19 |
20 | # Pass through the following environment variables which may be needed
21 | # for the CI
22 | passenv = HOME,WINDIR,LC_ALL,LC_CTYPE,CC,CI
23 |
24 | # Run the tests in a temporary directory to make sure that we don't import
25 | # this package from the source tree
26 | changedir = .tmp/{envname}
27 |
28 | # tox environments are constructed with so-called 'factors' (or terms)
29 | # separated by hyphens, e.g., test-devdeps-cov. Lines below starting
30 | # with factor: will only take effect if that factor is included in the
31 | # environment name. To see a list of example environments that can be run,
32 | # along with a description, run:
33 | #
34 | # tox -l -v
35 | #
36 | description =
37 | run tests
38 | alldeps: with all optional dependencies
39 | devdeps: with the latest developer version of key dependencies
40 | oldestdeps: with the oldest supported version of key dependencies
41 | cov: and test coverage
42 | numpy116: with numpy 1.16.*
43 | numpy117: with numpy 1.17.*
44 | numpy118: with numpy 1.18.*
45 | numpy119: with numpy 1.19.*
46 | numpy120: with numpy 1.20.*
47 | astropy40: with astropy 4.0.*
48 | astropylts: with the latest astropy LTS
49 |
50 | # The following provides some specific pinnings for key packages
51 | deps =
52 | numpy117: numpy==1.17.*
53 | numpy118: numpy==1.18.*
54 | numpy119: numpy==1.19.*
55 | numpy120: numpy==1.20.*
56 |
57 | astropy40: astropy==4.0.*
58 | astropylts: astropy==4.0.*
59 |
60 | devdeps: numpy>=0.0.dev0
61 |
62 | # The following indicates which extras_require from setup.cfg will be installed
63 | extras =
64 | test
65 | alldeps: all
66 |
67 | commands =
68 | devdeps: pip install -U -i https://pypi.anaconda.org/astropy/simple astropy --pre
69 | pip freeze
70 |
71 | pytest --pyargs astro3d {toxinidir}/docs \
72 | cov: --cov astro3d --cov-config={toxinidir}/setup.cfg --cov-report xml:{toxinidir}/coverage.xml \
73 | {posargs}
74 |
75 | [testenv:build_docs]
76 | changedir = docs
77 | description = invoke sphinx-build to build the HTML docs
78 | extras = docs
79 | commands =
80 | pip freeze
81 | sphinx-build -W -b html . _build/html
82 |
83 | [testenv:linkcheck]
84 | changedir = docs
85 | description = check the links in the HTML docs
86 | extras = docs
87 | commands =
88 | pip freeze
89 | sphinx-build -W -b linkcheck . _build/html
90 |
91 | [testenv:codestyle]
92 | skip_install = true
93 | changedir = .
94 | description = check code style with flake8
95 | deps = flake8
96 | commands = flake8 astro3d --count --max-line-length=100
97 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | Astro3d
2 | =======
3 |
4 | Astro3d provides a GUI and engine to create a 3D model from an
5 | astronomical image. The 3D model is saved in a `STL`_ file, which can
6 | then be sent to a 3D printer.
7 |
8 | .. _STL: https://en.wikipedia.org/wiki/STL_(file_format)
9 |
10 | Requirements
11 | ------------
12 | - Python 3
13 | - Qt5
14 |
15 | Installation
16 | ------------
17 |
18 | Start a terminal window
19 | ^^^^^^^^^^^^^^^^^^^^^^^
20 |
21 | In whatever system you have, start a terminal window that will get you
22 | to the command line.
23 |
24 | Remember, you need to be in the ``bash`` shell. If not sure, start
25 | ``bash``::
26 |
27 | $ bash
28 |
29 | Install Anaconda
30 | ^^^^^^^^^^^^^^^^
31 | If you have never installed anaconda before, or want to start fresh,
32 | this is the place to start. First, remove any previous instance of
33 | anaconda::
34 |
35 | $ cd
36 | $ rm -rf anaconda3
37 |
38 | Go to the Anaconda download site, https://www.anaconda.com/download/,
39 | and download the command line installer appropriate for your system.
40 | Choose the Python3.x version.
41 |
42 | After downloading, perform the installation::
43 |
44 | $ cd