{"action":"created","comment":{"url":"https://api.github.com/repos/astropy/astropy/pulls/comments/34948659","id":34948659,"diff_hunk":"@@ -493,3 +493,190 @@ def combine_labels(left, right):\n \n     return left + right\n \n+\n+def ellipse_extent(a, b, theta):\n+    \"\"\"\n+    Calculates the extent of a box encapsulating a rotated 2D ellipse.\n+\n+    Parameters\n+    ----------\n+    a : float\n+        Major axis\n+    b : float\n+        Minor axis\n+    theta: float\n+        Rotation angle in radians.\n+\n+    Returns\n+    -------\n+    image : array\n+            The evaluated model on the input image or coordinates. If \n+            no image or coordinates are input, the returned array is \n+            limited to the bounding_box of the model. The the bounding_box\n+            is not defined, an image or coordinates must be passed.\n+\n+    Examples\n+    --------\n+    .. plot::\n+        :include-source:\n+\n+        import numpy as np\n+        import matplotlib.pyplot as plt\n+        from astropy.modeling.models import Ellipse2D\n+        from astropy.modeling.utils import ellipse_extent\n+\n+        amplitude = 1\n+        x0 = 50\n+        y0 = 50\n+        a = 30\n+        b = 10\n+        theta = np.pi/4\n+\n+        model = Ellipse2D(amplitude, x0, y0, a, b, theta)\n+\n+\n+        dx, dy = np.around( ellipse_extent(a, b, theta) )\n+\n+        limits = [slice(y0 - dy, y0 + dy + 1, 1),\n+                  slice(x0 - dx, x0 + dx + 1, 1)]\n+\n+        coords = np.mgrid[limits][::-1]\n+\n+        plt.imshow(model(*coords),cmap='binary',interpolation='nearest',alpha=.5,\n+                  extent = [x0 - dx, x0 + dx, y0 - dy, y0 + dy])\n+        plt.show()\n+    \"\"\"\n+\n+    t = np.arctan2(-b * np.tan(theta), a)\n+    dx = a * np.cos(t) * np.cos(theta) - b * np.sin(t) * np.sin(theta)\n+\n+    t = np.arctan2(b, a * np.tan(theta))\n+    dy = b * np.sin(t) * np.cos(theta) + a * np.cos(t) * np.sin(theta)\n+    return np.abs([dx, dy])\n+\n+\n+def render_model(model, image=None, coords=None, copy=True):\n+    \"\"\"\n+    Evaluates a model on an input array. Evaluation is limited to\n+    a bounding box if the `bounding_box` property is defined for\n+    the model.\n+\n+    Parameters\n+    ----------\n+    model : Model\n+            Model to be evaluated\n+    image : array\n+            Array on which the model is evaluated.\n+    coords: list\n+            List of coordinate arrays mapping to the image array,\n+            such that `image[coords] == image`.\n+    copy : bool, optional\n+           Default is true. If False, image will be modified in place. \n+\n+    Returns\n+    -------\n+    image : array\n+            The evaluated model on the input image or coordinates. If \n+            no image or coordinates are input, the returned array is \n+            limited to the bounding_box of the model. The the bounding_box\n+            is not defined, an image or coordinates must be passed.\n+\n+    Examples\n+    --------\n+    .. plot::\n+        :include-source:\n+\n+            import numpy as np\n+        import matplotlib.pyplot as plt\n+\n+        from astropy.modeling.models import Gaussian2D\n+        from astropy.modeling.utils import render_model\n+\n+        imshape = (450, 600)\n+        nsrc = 50\n+        models = {}\n+        models['x_mean'] = np.random.rand(nsrc) * imshape[1]\n+        models['y_mean'] = np.random.rand(nsrc) * imshape[0]\n+        models['amplitude'] = np.abs(np.random.randn(nsrc) + 10.)\n+        models['x_stddev'] = np.abs(np.random.rand(nsrc) + 5)\n+        models['y_stddev'] = models['x_stddev'] * (np.random.rand(nsrc) + 1)\n+        models['theta'] = np.random.rand(nsrc) * np.pi\n+\n+        model_list = [Gaussian2D(x_mean=models['x_mean'][i], y_mean=models['y_mean'][i], \n+                                x_stddev=models['x_stddev'][i], y_stddev=models['y_stddev'][i],\n+                                amplitude=models['amplitude'][i], theta=models['theta'][i])\n+                                for i in range(nsrc)]\n+\n+\n+        bb_image = np.zeros(imshape)\n+        full_image = np.zeros(imshape)\n+\n+        for model in model_list:\n+            render_model(model, bb_image, copy = False)\n+            model.bounding_box = None\n+            render_model(model, full_image, copy = False)\n+\n+        plt.figure(figsize=(12,8))\n+        plt.imshow(bb_image, cmap='binary')\n+        plt.colorbar()\n+        plt.figure(figsize=(12,8))\n+        plt.imshow(full_image - bb_image, cmap='binary', vmin=-1e-5, vmax=1e-5)\n+        plt.colorbar(format='%.0e')\n+        plt.show()\n+    \"\"\"\n+\n+    from .. nddata.utils import add_array, extract_array","path":"astropy/modeling/utils.py","position":154,"original_position":154,"commit_id":"5d8e4d681605d82eda43ef3d29c0d5ca1ae1fde5","original_commit_id":"5d8e4d681605d82eda43ef3d29c0d5ca1ae1fde5","user":{"login":"cdeil","id":852409,"avatar_url":"https://avatars.githubusercontent.com/u/852409?v=3","gravatar_id":"","url":"https://api.github.com/users/cdeil","html_url":"https://github.com/cdeil","followers_url":"https://api.github.com/users/cdeil/followers","following_url":"https://api.github.com/users/cdeil/following{/other_user}","gists_url":"https://api.github.com/users/cdeil/gists{/gist_id}","starred_url":"https://api.github.com/users/cdeil/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cdeil/subscriptions","organizations_url":"https://api.github.com/users/cdeil/orgs","repos_url":"https://api.github.com/users/cdeil/repos","events_url":"https://api.github.com/users/cdeil/events{/privacy}","received_events_url":"https://api.github.com/users/cdeil/received_events","type":"User","site_admin":false},"body":"Move import to the top of the file.","created_at":"2015-07-18T15:26:09Z","updated_at":"2015-07-18T15:26:09Z","html_url":"https://github.com/astropy/astropy/pull/3909#discussion_r34948659","pull_request_url":"https://api.github.com/repos/astropy/astropy/pulls/3909","_links":{"self":{"href":"https://api.github.com/repos/astropy/astropy/pulls/comments/34948659"},"html":{"href":"https://github.com/astropy/astropy/pull/3909#discussion_r34948659"},"pull_request":{"href":"https://api.github.com/repos/astropy/astropy/pulls/3909"}}},"pull_request":{"url":"https://api.github.com/repos/astropy/astropy/pulls/3909","id":39056593,"html_url":"https://github.com/astropy/astropy/pull/3909","diff_url":"https://github.com/astropy/astropy/pull/3909.diff","patch_url":"https://github.com/astropy/astropy/pull/3909.patch","issue_url":"https://api.github.com/repos/astropy/astropy/issues/3909","number":3909,"state":"open","locked":false,"title":"WIP: Implement `bounding_box` model property and `render_model` function ","user":{"login":"patti","id":4968596,"avatar_url":"https://avatars.githubusercontent.com/u/4968596?v=3","gravatar_id":"","url":"https://api.github.com/users/patti","html_url":"https://github.com/patti","followers_url":"https://api.github.com/users/patti/followers","following_url":"https://api.github.com/users/patti/following{/other_user}","gists_url":"https://api.github.com/users/patti/gists{/gist_id}","starred_url":"https://api.github.com/users/patti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/patti/subscriptions","organizations_url":"https://api.github.com/users/patti/orgs","repos_url":"https://api.github.com/users/patti/repos","events_url":"https://api.github.com/users/patti/events{/privacy}","received_events_url":"https://api.github.com/users/patti/received_events","type":"User","site_admin":false},"body":"Implementation of a `bounding_box` property for models to improve efficiency by constraining model evaluation to relevant limits. Currently defaults are defined for Gaussian2D, Ellipse2D, and Gaussian1D. Some tests have been written for the 2D case. Other tests will be added shortly. \r\n\r\nThe `render_model` function and associated `ellipse_extent` function currently live in `modeling.utils.py` although this likely isn't going to be its permanent home. I could be mistaken, but it doesn't look like docs are actually built for this module. High-level docs will address this, but this should be discussed.\r\n\r\nRemaining tasks and open questions are listed here:\r\n\r\nDocs\r\n- [ ] Clean up and fill out doc-strings\r\n- [ ] Write high level docs for render_model. Suggested adding subpage `astropy/docs/modeling/bounding_boxes.rst`. This will demo the large sky-model use-case. \r\n\r\nTests\r\n- [x] Add test for 1D bounding box in `Fittable1DModelTester` class\r\n- [x] Add test functions for `render_model` (edge cases, floating position, various optional inputs) in `test_functional_models.py`.\r\n- [ ] Add test for N-dim bounding box ... where? how?\r\n- [ ] Is it ok to add `bbox_parameters` to the `test_parameters` ? If not, I need to re-work `render_model` to handle negative indices.\r\n\r\n`bounding_box` property\r\n- [x] Need to handle models without defined defaults. Will add a try/except in the setter and set `_bounding_box = None` for these.\r\n- [ ] How to document defaults so that they are accessible to the user? Currently these are defined in the private function `_bounding_box_default` in each model class that is called by the property if `_bounding_box == 'auto' `\r\n- [ ] Later: add defaults for other well-defined models (`Box1D`, `Box2D`, `Ring2D`, `Disk2D`, etc.)\r\n\r\n`render_model` function\r\n- [x] Clean up try/except and if/else statements\r\n\r\n","created_at":"2015-07-02T07:54:18Z","updated_at":"2015-07-18T15:26:09Z","closed_at":null,"merged_at":null,"merge_commit_sha":"469afa4c189de69b2c9323a7aa9613602b5754ba","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/astropy/astropy/pulls/3909/commits","review_comments_url":"https://api.github.com/repos/astropy/astropy/pulls/3909/comments","review_comment_url":"https://api.github.com/repos/astropy/astropy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/astropy/astropy/issues/3909/comments","statuses_url":"https://api.github.com/repos/astropy/astropy/statuses/5d8e4d681605d82eda43ef3d29c0d5ca1ae1fde5","head":{"label":"patti:boundingboxes","ref":"boundingboxes","sha":"5d8e4d681605d82eda43ef3d29c0d5ca1ae1fde5","user":{"login":"patti","id":4968596,"avatar_url":"https://avatars.githubusercontent.com/u/4968596?v=3","gravatar_id":"","url":"https://api.github.com/users/patti","html_url":"https://github.com/patti","followers_url":"https://api.github.com/users/patti/followers","following_url":"https://api.github.com/users/patti/following{/other_user}","gists_url":"https://api.github.com/users/patti/gists{/gist_id}","starred_url":"https://api.github.com/users/patti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/patti/subscriptions","organizations_url":"https://api.github.com/users/patti/orgs","repos_url":"https://api.github.com/users/patti/repos","events_url":"https://api.github.com/users/patti/events{/privacy}","received_events_url":"https://api.github.com/users/patti/received_events","type":"User","site_admin":false},"repo":{"id":32838725,"name":"astropy","full_name":"patti/astropy","owner":{"login":"patti","id":4968596,"avatar_url":"https://avatars.githubusercontent.com/u/4968596?v=3","gravatar_id":"","url":"https://api.github.com/users/patti","html_url":"https://github.com/patti","followers_url":"https://api.github.com/users/patti/followers","following_url":"https://api.github.com/users/patti/following{/other_user}","gists_url":"https://api.github.com/users/patti/gists{/gist_id}","starred_url":"https://api.github.com/users/patti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/patti/subscriptions","organizations_url":"https://api.github.com/users/patti/orgs","repos_url":"https://api.github.com/users/patti/repos","events_url":"https://api.github.com/users/patti/events{/privacy}","received_events_url":"https://api.github.com/users/patti/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/patti/astropy","description":"Repository for the Astropy core package","fork":true,"url":"https://api.github.com/repos/patti/astropy","forks_url":"https://api.github.com/repos/patti/astropy/forks","keys_url":"https://api.github.com/repos/patti/astropy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/patti/astropy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/patti/astropy/teams","hooks_url":"https://api.github.com/repos/patti/astropy/hooks","issue_events_url":"https://api.github.com/repos/patti/astropy/issues/events{/number}","events_url":"https://api.github.com/repos/patti/astropy/events","assignees_url":"https://api.github.com/repos/patti/astropy/assignees{/user}","branches_url":"https://api.github.com/repos/patti/astropy/branches{/branch}","tags_url":"https://api.github.com/repos/patti/astropy/tags","blobs_url":"https://api.github.com/repos/patti/astropy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/patti/astropy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/patti/astropy/git/refs{/sha}","trees_url":"https://api.github.com/repos/patti/astropy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/patti/astropy/statuses/{sha}","languages_url":"https://api.github.com/repos/patti/astropy/languages","stargazers_url":"https://api.github.com/repos/patti/astropy/stargazers","contributors_url":"https://api.github.com/repos/patti/astropy/contributors","subscribers_url":"https://api.github.com/repos/patti/astropy/subscribers","subscription_url":"https://api.github.com/repos/patti/astropy/subscription","commits_url":"https://api.github.com/repos/patti/astropy/commits{/sha}","git_commits_url":"https://api.github.com/repos/patti/astropy/git/commits{/sha}","comments_url":"https://api.github.com/repos/patti/astropy/comments{/number}","issue_comment_url":"https://api.github.com/repos/patti/astropy/issues/comments{/number}","contents_url":"https://api.github.com/repos/patti/astropy/contents/{+path}","compare_url":"https://api.github.com/repos/patti/astropy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/patti/astropy/merges","archive_url":"https://api.github.com/repos/patti/astropy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/patti/astropy/downloads","issues_url":"https://api.github.com/repos/patti/astropy/issues{/number}","pulls_url":"https://api.github.com/repos/patti/astropy/pulls{/number}","milestones_url":"https://api.github.com/repos/patti/astropy/milestones{/number}","notifications_url":"https://api.github.com/repos/patti/astropy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/patti/astropy/labels{/name}","releases_url":"https://api.github.com/repos/patti/astropy/releases{/id}","created_at":"2015-03-25T02:49:38Z","updated_at":"2015-03-25T02:49:43Z","pushed_at":"2015-07-18T00:24:35Z","git_url":"git://github.com/patti/astropy.git","ssh_url":"git@github.com:patti/astropy.git","clone_url":"https://github.com/patti/astropy.git","svn_url":"https://github.com/patti/astropy","homepage":"www.astropy.org","size":37960,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"astropy:master","ref":"master","sha":"561a6dd5ba0ade701db0b738bf9032c58c387246","user":{"login":"astropy","id":847984,"avatar_url":"https://avatars.githubusercontent.com/u/847984?v=3","gravatar_id":"","url":"https://api.github.com/users/astropy","html_url":"https://github.com/astropy","followers_url":"https://api.github.com/users/astropy/followers","following_url":"https://api.github.com/users/astropy/following{/other_user}","gists_url":"https://api.github.com/users/astropy/gists{/gist_id}","starred_url":"https://api.github.com/users/astropy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/astropy/subscriptions","organizations_url":"https://api.github.com/users/astropy/orgs","repos_url":"https://api.github.com/users/astropy/repos","events_url":"https://api.github.com/users/astropy/events{/privacy}","received_events_url":"https://api.github.com/users/astropy/received_events","type":"Organization","site_admin":false},"repo":{"id":2081289,"name":"astropy","full_name":"astropy/astropy","owner":{"login":"astropy","id":847984,"avatar_url":"https://avatars.githubusercontent.com/u/847984?v=3","gravatar_id":"","url":"https://api.github.com/users/astropy","html_url":"https://github.com/astropy","followers_url":"https://api.github.com/users/astropy/followers","following_url":"https://api.github.com/users/astropy/following{/other_user}","gists_url":"https://api.github.com/users/astropy/gists{/gist_id}","starred_url":"https://api.github.com/users/astropy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/astropy/subscriptions","organizations_url":"https://api.github.com/users/astropy/orgs","repos_url":"https://api.github.com/users/astropy/repos","events_url":"https://api.github.com/users/astropy/events{/privacy}","received_events_url":"https://api.github.com/users/astropy/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/astropy/astropy","description":"Repository for the Astropy core package","fork":false,"url":"https://api.github.com/repos/astropy/astropy","forks_url":"https://api.github.com/repos/astropy/astropy/forks","keys_url":"https://api.github.com/repos/astropy/astropy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/astropy/astropy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/astropy/astropy/teams","hooks_url":"https://api.github.com/repos/astropy/astropy/hooks","issue_events_url":"https://api.github.com/repos/astropy/astropy/issues/events{/number}","events_url":"https://api.github.com/repos/astropy/astropy/events","assignees_url":"https://api.github.com/repos/astropy/astropy/assignees{/user}","branches_url":"https://api.github.com/repos/astropy/astropy/branches{/branch}","tags_url":"https://api.github.com/repos/astropy/astropy/tags","blobs_url":"https://api.github.com/repos/astropy/astropy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/astropy/astropy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/astropy/astropy/git/refs{/sha}","trees_url":"https://api.github.com/repos/astropy/astropy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/astropy/astropy/statuses/{sha}","languages_url":"https://api.github.com/repos/astropy/astropy/languages","stargazers_url":"https://api.github.com/repos/astropy/astropy/stargazers","contributors_url":"https://api.github.com/repos/astropy/astropy/contributors","subscribers_url":"https://api.github.com/repos/astropy/astropy/subscribers","subscription_url":"https://api.github.com/repos/astropy/astropy/subscription","commits_url":"https://api.github.com/repos/astropy/astropy/commits{/sha}","git_commits_url":"https://api.github.com/repos/astropy/astropy/git/commits{/sha}","comments_url":"https://api.github.com/repos/astropy/astropy/comments{/number}","issue_comment_url":"https://api.github.com/repos/astropy/astropy/issues/comments{/number}","contents_url":"https://api.github.com/repos/astropy/astropy/contents/{+path}","compare_url":"https://api.github.com/repos/astropy/astropy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/astropy/astropy/merges","archive_url":"https://api.github.com/repos/astropy/astropy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/astropy/astropy/downloads","issues_url":"https://api.github.com/repos/astropy/astropy/issues{/number}","pulls_url":"https://api.github.com/repos/astropy/astropy/pulls{/number}","milestones_url":"https://api.github.com/repos/astropy/astropy/milestones{/number}","notifications_url":"https://api.github.com/repos/astropy/astropy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/astropy/astropy/labels{/name}","releases_url":"https://api.github.com/repos/astropy/astropy/releases{/id}","created_at":"2011-07-21T01:33:49Z","updated_at":"2015-07-17T16:12:33Z","pushed_at":"2015-07-18T04:06:47Z","git_url":"git://github.com/astropy/astropy.git","ssh_url":"git@github.com:astropy/astropy.git","clone_url":"https://github.com/astropy/astropy.git","svn_url":"https://github.com/astropy/astropy","homepage":"www.astropy.org","size":64110,"stargazers_count":612,"watchers_count":612,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":413,"mirror_url":null,"open_issues_count":574,"forks":413,"open_issues":574,"watchers":612,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/astropy/astropy/pulls/3909"},"html":{"href":"https://github.com/astropy/astropy/pull/3909"},"issue":{"href":"https://api.github.com/repos/astropy/astropy/issues/3909"},"comments":{"href":"https://api.github.com/repos/astropy/astropy/issues/3909/comments"},"review_comments":{"href":"https://api.github.com/repos/astropy/astropy/pulls/3909/comments"},"review_comment":{"href":"https://api.github.com/repos/astropy/astropy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/astropy/astropy/pulls/3909/commits"},"statuses":{"href":"https://api.github.com/repos/astropy/astropy/statuses/5d8e4d681605d82eda43ef3d29c0d5ca1ae1fde5"}}}}