├── sceneKit demo
├── 01_ship
│ ├── ship.scn
│ └── shipDemo.py
├── 02_Corvette-F3
│ ├── Corvette-F3.scn
│ ├── SF_Corvette-F3_bump.jpg
│ ├── SF_Corvette-F3_glow.jpg
│ ├── SF_Corvette-F3_diffuse.jpg
│ ├── SF_Corvette-F3_specular.jpg
│ └── Corvette-F3-Demo.py
├── 03_Feisar_ship
│ ├── Feisar_Ship.scn
│ └── Feisar_ShipDemo.py
├── 17_cube-puzzle
│ ├── resources
│ │ ├── wood.png
│ │ ├── marble.jpg
│ │ ├── Five_Piece-shapes.P
│ │ ├── Half_Hour-shapes.P
│ │ ├── Soma_Cube-shapes.P
│ │ ├── Five_Piece-solution.P
│ │ ├── Half_Hour-solution.P
│ │ ├── Soma_Cube-solution.P
│ │ ├── Diabolical_Cube-shapes.P
│ │ ├── Diabolical_Cube-solution.P
│ │ ├── Mikusinskis_Cube-shapes.P
│ │ └── Mikusinskis_Cube-solution.P
│ ├── README.md
│ ├── main.py
│ ├── selector.py
│ ├── data.py
│ ├── hud.py
│ ├── piece.py
│ ├── solver.py
│ ├── manual.py
│ └── setup.py
├── 16_AppleVehicleDemo
│ ├── resources
│ │ ├── ball.jpg
│ │ ├── click.caf
│ │ ├── icon.png
│ │ ├── smoke.png
│ │ ├── spark.png
│ │ ├── tire.jpg
│ │ ├── wall.jpg
│ │ ├── wheel.png
│ │ ├── wood.png
│ │ ├── carpet.jpg
│ │ ├── krStar.png
│ │ ├── launch.png
│ │ ├── needle.png
│ │ ├── reactor.scnp
│ │ ├── smoke.scnp
│ │ ├── WoodCubeA.jpg
│ │ ├── WoodCubeB.jpg
│ │ ├── WoodCubeC.jpg
│ │ ├── book_back.jpg
│ │ ├── book_front.jpg
│ │ ├── book_side.jpg
│ │ ├── carpetOld.jpg
│ │ ├── rc_car_PY.scn
│ │ ├── speedGauge.png
│ │ ├── starBurst.skn
│ │ ├── tex_smoke.png
│ │ ├── train_wood.jpg
│ │ ├── train_flat_PY.scn
│ │ ├── video_camera.png
│ │ ├── book_side_title.jpg
│ │ ├── rc_car_texture.png
│ │ └── video_camera@2x.png
│ └── OverlayScene.py
├── 07_textDemo.py
├── 04_Lucy
│ └── MDLDemo.py
├── 12_particlesDemo.py
├── 08_tetrahedron.py
├── 11_avoidOccluderDemo.py
├── 10_morpherDemo.py
├── 05_photoCube.py
├── 13_physicsDemo-1.py
├── 09_shapeDemo.py
├── 14_physicsDemo-2.py
└── 06_photoCubeWithRendererDelegate.py
├── .travis.yml
├── LICENSE
└── sceneKit
├── sceneKitAudio.py
├── sceneKitLight.py
├── sceneKitEnv.py
├── sceneKitMaterial.py
├── sceneKitGeometrySource.py
├── sceneKitConstraints.py
└── __init__.py
/sceneKit demo/01_ship/ship.scn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/01_ship/ship.scn
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | install: pip install flake8
3 | script: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
4 |
--------------------------------------------------------------------------------
/sceneKit demo/02_Corvette-F3/Corvette-F3.scn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/02_Corvette-F3/Corvette-F3.scn
--------------------------------------------------------------------------------
/sceneKit demo/03_Feisar_ship/Feisar_Ship.scn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/03_Feisar_ship/Feisar_Ship.scn
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/resources/wood.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/17_cube-puzzle/resources/wood.png
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/resources/marble.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/17_cube-puzzle/resources/marble.jpg
--------------------------------------------------------------------------------
/sceneKit demo/02_Corvette-F3/SF_Corvette-F3_bump.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/02_Corvette-F3/SF_Corvette-F3_bump.jpg
--------------------------------------------------------------------------------
/sceneKit demo/02_Corvette-F3/SF_Corvette-F3_glow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/02_Corvette-F3/SF_Corvette-F3_glow.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/ball.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/ball.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/click.caf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/click.caf
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/icon.png
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/smoke.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/smoke.png
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/spark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/spark.png
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/tire.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/tire.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/wall.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/wall.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/wheel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/wheel.png
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/wood.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/wood.png
--------------------------------------------------------------------------------
/sceneKit demo/02_Corvette-F3/SF_Corvette-F3_diffuse.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/02_Corvette-F3/SF_Corvette-F3_diffuse.jpg
--------------------------------------------------------------------------------
/sceneKit demo/02_Corvette-F3/SF_Corvette-F3_specular.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/02_Corvette-F3/SF_Corvette-F3_specular.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/carpet.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/carpet.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/krStar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/krStar.png
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/launch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/launch.png
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/needle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/needle.png
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/reactor.scnp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/reactor.scnp
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/smoke.scnp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/smoke.scnp
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/WoodCubeA.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/WoodCubeA.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/WoodCubeB.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/WoodCubeB.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/WoodCubeC.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/WoodCubeC.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/book_back.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/book_back.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/book_front.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/book_front.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/book_side.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/book_side.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/carpetOld.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/carpetOld.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/rc_car_PY.scn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/rc_car_PY.scn
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/speedGauge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/speedGauge.png
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/starBurst.skn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/starBurst.skn
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/tex_smoke.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/tex_smoke.png
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/train_wood.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/train_wood.jpg
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/resources/Five_Piece-shapes.P:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/17_cube-puzzle/resources/Five_Piece-shapes.P
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/resources/Half_Hour-shapes.P:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/17_cube-puzzle/resources/Half_Hour-shapes.P
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/resources/Soma_Cube-shapes.P:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/17_cube-puzzle/resources/Soma_Cube-shapes.P
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/train_flat_PY.scn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/train_flat_PY.scn
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/video_camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/video_camera.png
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/resources/Five_Piece-solution.P:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/17_cube-puzzle/resources/Five_Piece-solution.P
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/resources/Half_Hour-solution.P:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/17_cube-puzzle/resources/Half_Hour-solution.P
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/resources/Soma_Cube-solution.P:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/17_cube-puzzle/resources/Soma_Cube-solution.P
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/book_side_title.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/book_side_title.jpg
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/rc_car_texture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/rc_car_texture.png
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/resources/video_camera@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/16_AppleVehicleDemo/resources/video_camera@2x.png
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/resources/Diabolical_Cube-shapes.P:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/17_cube-puzzle/resources/Diabolical_Cube-shapes.P
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/resources/Diabolical_Cube-solution.P:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/17_cube-puzzle/resources/Diabolical_Cube-solution.P
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/resources/Mikusinskis_Cube-shapes.P:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/17_cube-puzzle/resources/Mikusinskis_Cube-shapes.P
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/resources/Mikusinskis_Cube-solution.P:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulbrich/sceneKit-wrapper-for-Pythonista/HEAD/sceneKit demo/17_cube-puzzle/resources/Mikusinskis_Cube-solution.P
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Peter Ulbrich
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/README.md:
--------------------------------------------------------------------------------
1 | #Objective
2 |
3 | Build a 3x3x3 sized cube from the available pieces. There are five typical configurations to choose from. Click on the selected puzzle box to start solving it.
4 |
5 | #Solver interface
6 | Click on a piece to move it to a position above the destination cube. Touch the red spheres to rotate and the yellow cones to shift the piece. Clicking on another piece will swap the one in the middle with the newly selected shape. Touching an empty (ghosty) location clears the manipulation area.
7 |
8 | Once a piece is in the desired posisiton in the manipulation area, tap the gray cube to drop it. If the piece fits, you can choose the next piece to work with, otherwise touching any of the spheres or cones will lift the piece back to furter adjust it. The piece last dropped can be lifted by touching the gray cube again (undo last drop), if the manipulation area is empty.
9 |
10 | In the bottom left corner there is a `restart` button. The `?` in the bottom right corner gives you a hint about the next possible moves. If the manipulation area is empty, possible continuation pieces will be highlighted green. If there is already a selected piece in the middle, you can circle through possible positions by repeatedly tapping the `?`.
11 |
12 | The top-right `back arrow` takes you back to the puzzle selection screen, whereas the top-left `x` closes the application.
13 |
14 | #Camera control
15 | You can look around the central area as well as the location of the pieces by the following gestures:
16 |
17 | - ```Pan with one finger``` to rotate the camera around the scene
18 |
19 | - ```Pan with two fingers``` to translate the camera on its local xy-plane
20 |
21 | - ```Pan with three fingers``` vertically to move the the camera forward backward
22 |
23 | - ```Double-tap``` to reset the camera
24 |
25 | - ```Rotate with two fingers``` to roll the camera (rotate on the camera node's z-axis)
26 |
27 | - ```Pinch``` to zoom in or zoom out (change the camera's fieldOfView)
28 |
29 |
30 |
31 | **All puzzles can be solved, so have fun!**
32 |
33 | (c) Peter Ulbrich, 2019
34 |
35 |
36 |
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/main.py:
--------------------------------------------------------------------------------
1 | '''Cube puzzles - five typical 3x3x3 3D puzzles.
2 |
3 | (c) Peter Ulbrich, 2019
4 |
5 | a demo script for the sceneKit wrapper package for Pythonista
6 |
7 | See the README.md for details.
8 |
9 | '''
10 |
11 | import sceneKit as scn
12 | import ui
13 | import Gestures
14 |
15 | from data import *
16 | from hud import *
17 | import manual
18 | import selector
19 | import setup
20 |
21 | class MainViewController:
22 |
23 | class MainView(ui.View):
24 | def will_close(self):
25 | data.gestures_instance.remove_all_gestures(data.main_view)
26 | for aSceneView in data.active_stage.scene_views():
27 | aSceneView.scene.paused = True
28 | aSceneView.removeFromSuperview()
29 | for aView in data.main_view.subviews:
30 | data.main_view.remove_subview(aView)
31 |
32 | def layout(self):
33 | _, _, w, h = data.main_view.frame
34 | data.horizontal = w > h
35 | data.hud_layer.layout()
36 | data.active_stage.layout()
37 |
38 |
39 | @classmethod
40 | def run(cls):
41 | scn.clearCache()
42 | cls().main()
43 |
44 | @on_main_thread
45 | def main(self):
46 | data.main_view = self.MainView()
47 | w, h = ui.get_window_size()
48 | data.main_view.frame = (0, 0, w, h)
49 |
50 | data.gestures_instance = Gestures.Gestures()
51 | data.gestures_instance.add_tap(data.main_view, self.simple_tap)
52 |
53 | data.hud_layer = Hud()
54 | self.next_stage(selector.SelectorStage())
55 |
56 | data.main_view.present(style='fullscreen', hide_title_bar=True)
57 |
58 | @classmethod
59 | def next_stage(cls, stage_instance):
60 | if data.active_stage is not None:
61 | for aSceneView in data.active_stage.scene_views():
62 | aSceneView.scene.paused = True
63 | aSceneView.removeFromSuperview()
64 | data.active_stage = stage_instance
65 | data.hud_layer.bring_to_front()
66 |
67 | def simple_tap(self, tap_data):
68 | data.active_stage.tap(tap_data.location)
69 |
70 |
71 | if __name__ == '__main__':
72 | MainViewController.run()
73 |
--------------------------------------------------------------------------------
/sceneKit demo/07_textDemo.py:
--------------------------------------------------------------------------------
1 | '''
2 | based on the code in the thread https://forum.omz-software.com/topic/1686/3d-in-pythonista by omz
3 | '''
4 | from objc_util import *
5 | import sceneKit as scn
6 | import ui
7 | import math
8 |
9 | @on_main_thread
10 | def demo():
11 | main_view = ui.View()
12 | w, h = ui.get_screen_size()
13 | main_view.frame = (0,0,w,h)
14 | main_view.name = 'textDemo'
15 |
16 | scene_view = scn.View(main_view.frame, superView=main_view)
17 | scene_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleRightMargin
18 | scene_view.antialiasingMode = scn.AntialiasingMode.Multisampling16X
19 |
20 | scene_view.allowsCameraControl = True
21 |
22 | scene_view.backgroundColor = 'white'
23 |
24 | scene_view.scene = scn.Scene()
25 |
26 | root_node = scene_view.scene.rootNode
27 | text_mesh = scn.Text.textWithString('Pythonista', 6.0)
28 | text_mesh.flatness = 0.2
29 | text_mesh.chamferRadius = 0.4
30 | text_mesh.font = ('HelveticaNeue-Bold', 18)
31 | bbox_min, bbox_max = text_mesh.boundingBox
32 | text_width = bbox_max.x - bbox_min.x
33 | text_node = scn.Node.nodeWithGeometry(text_mesh)
34 | text_node.castsShadow = True
35 | text_container = scn.Node.node()
36 | text_container.addChildNode(text_node)
37 | text_container.position = (0, 40, 0)
38 | text_node.position = (-text_width/2, 0, 0)
39 | box = scn.Box(width=150, height=4, length=150, chamferRadius=1)
40 | box_node = scn.Node.nodeWithGeometry(box)
41 | root_node.addChildNode(box_node)
42 | rotate_action = scn.Action.repeatActionForever(scn.Action.rotateBy(0, math.pi*2, math.pi*2, 10))
43 | text_container.runAction(rotate_action)
44 | root_node.addChildNode(text_container)
45 | light_node = scn.Node.node()
46 | light_node.position = (0, 105, 5)
47 | light_node.rotation = (1, 0, 0, -math.pi/2)
48 | light = scn.Light.light()
49 | light.type = 'spot'
50 | light.spotOuterAngle = 65
51 | light.castsShadow = True
52 | light.shadowSampleCount = 16
53 | light.color = 'cyan'
54 | light_node.light = light
55 | root_node.addChildNode(light_node)
56 |
57 | main_view.present(style='fullscreen', hide_title_bar=False)
58 |
59 | demo()
60 |
61 |
--------------------------------------------------------------------------------
/sceneKit demo/04_Lucy/MDLDemo.py:
--------------------------------------------------------------------------------
1 | """
2 | MDL import demo
3 | """
4 |
5 | from objc_util import *
6 | import sceneKit as scn
7 | import ui
8 | import math
9 |
10 | MDLAsset, MDLMesh, MDLSubmesh = ObjCClass('MDLAsset'), ObjCClass('MDLMesh'), ObjCClass('MDLSubmesh')
11 |
12 |
13 |
14 | class Demo:
15 |
16 | @classmethod
17 | def run(cls):
18 | cls().main()
19 |
20 | @on_main_thread
21 | def main(self):
22 | main_view = ui.View()
23 | w, h = ui.get_screen_size()
24 | main_view.frame = (0,0,w,h)
25 | main_view.name = 'MDL import demo'
26 |
27 | scene_view = scn.View(main_view.frame, superView=main_view)
28 | scene_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleWidth
29 | scene_view.allowsCameraControl = True
30 | scene_view.backgroundColor = 'white'
31 | scene_view.rendersContinuously = True
32 | scene_view.scene = scn.Scene()
33 |
34 | root_node = scene_view.scene.rootNode
35 |
36 | floor_geometry = scn.Floor()
37 | floor_node = scn.Node.nodeWithGeometry(floor_geometry)
38 | root_node.addChildNode(floor_node)
39 |
40 | asset = MDLAsset.alloc().initWithURL_(nsurl('Lucy.obj'))
41 | mesh = asset.objectAtIndex_(0)
42 | lucy_geometry = scn.Geometry.geometryWithMDLMesh(mesh)
43 |
44 | lucy_node_1 = scn.Node.nodeWithGeometry(lucy_geometry)
45 | root_node.addChildNode(lucy_node_1)
46 |
47 | lucy_node_2 = scn.Node.nodeWithMDLObject(mesh)
48 | lucy_node_2.position = (10., 0., 0.)
49 | root_node.addChildNode(lucy_node_2)
50 |
51 | camera_node = scn.Node()
52 | camera_node.camera = scn.Camera()
53 | camera_node.position = (10., 10., 10.)
54 | camera_node.lookAt(root_node.position)
55 | root_node.addChildNode(camera_node)
56 |
57 | light_node = scn.Node()
58 | light_node.position = (-20., 20., 20)
59 | light = scn.Light()
60 | light.type = scn.LightTypeDirectional
61 | light.castsShadow = True
62 | light.shadowSampleCount = 32
63 | light.color = (.99, 1.0, .86)
64 | light_node.light = light
65 | light_node.lookAt(root_node.position)
66 | root_node.addChildNode(light_node)
67 |
68 | main_view.present(style='fullscreen', hide_title_bar=False)
69 |
70 | Demo.run()
71 |
--------------------------------------------------------------------------------
/sceneKit demo/02_Corvette-F3/Corvette-F3-Demo.py:
--------------------------------------------------------------------------------
1 | """
2 | using the Corvette-F3.scn file
3 | """
4 |
5 | from objc_util import *
6 | import ctypes
7 | import sceneKit as scn
8 | import ui
9 | import math
10 |
11 |
12 | class Demo:
13 | def __init__(self):
14 | self.name = 'Corvette-F3'
15 | pass
16 |
17 | @classmethod
18 | def run(cls):
19 | cls().main()
20 |
21 | @on_main_thread
22 | def main(self):
23 | main_view = ui.View()
24 | w, h = ui.get_screen_size()
25 | main_view.frame = (0,0,w,h)
26 | main_view.name = 'Corvette-F3 demo'
27 |
28 | scene_view = scn.View(main_view.frame, superView=main_view)
29 | scene_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleWidth
30 | scene_view.allowsCameraControl = True
31 |
32 | scene_view.scene = scn.Scene.sceneWithURL(url='Corvette-F3.scn')
33 |
34 | root_node = scene_view.scene.rootNode
35 | corvette_node = root_node.childNodes[0]
36 |
37 | corvette_geometry = corvette_node.geometry
38 | corvette_material = corvette_geometry.firstMaterial
39 |
40 | corvette_material.diffuse.contents = ui.Image.named('SF_Corvette-F3_diffuse.jpg')
41 | corvette_material.emission.contents = ui.Image.named('SF_Corvette-F3_glow.jpg')
42 | corvette_material.specular.contents = ui.Image.named('SF_Corvette-F3_specular.jpg')
43 | corvette_material.normal.contents = ui.Image.named('SF_Corvette-F3_bump.jpg')
44 |
45 | constraint = scn.LookAtConstraint.lookAtConstraintWithTarget(corvette_node)
46 | constraint.gimbalLockEnabled = True
47 |
48 | light_node = scn.Node()
49 | light_node.position = (-800, -800, -2100)
50 | light = scn.Light()
51 | light.type = scn.LightTypeDirectional
52 | light.castsShadow = True
53 | light.color = (.89, .89, .89)
54 | light.intensity = 500
55 | light_node.light = light
56 | light_node.constraints = constraint
57 | root_node.addChildNode(light_node)
58 |
59 | ambient_light = scn.Light()
60 | ambient_light.type = scn.LightTypeAmbient
61 | ambient_light.name = 'ambient light'
62 | ambient_light.color = (.99, 1.0, .86)
63 | ambient_light.intensity = 300
64 | ambient_node = scn.Node()
65 | ambient_node.light = ambient_light
66 | root_node.addChildNode(ambient_node)
67 |
68 |
69 | main_view.present(style='fullscreen', hide_title_bar=False)
70 |
71 | Demo.run()
72 |
--------------------------------------------------------------------------------
/sceneKit demo/01_ship/shipDemo.py:
--------------------------------------------------------------------------------
1 | """
2 | using the ship.scn file from XCode
3 | """
4 |
5 | from objc_util import *
6 | import ctypes
7 | import sceneKit as scn
8 | import ui
9 | import math
10 |
11 |
12 | class Demo:
13 | def __init__(self):
14 | self.name = 'ship!'
15 | pass
16 |
17 | @classmethod
18 | def run(cls):
19 | cls().main()
20 |
21 | @on_main_thread
22 | def main(self):
23 | main_view = ui.View()
24 | w, h = ui.get_screen_size()
25 | main_view.frame = (0,0,w,h)
26 | main_view.name = 'ship demo'
27 |
28 | scene_view = scn.View(main_view.frame, superView=main_view)
29 | scene_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleWidth
30 | scene_view.allowsCameraControl = True
31 |
32 | scene_view.scene = scn.Scene.sceneWithURL(url='ship.scn')
33 |
34 | root_node = scene_view.scene.rootNode
35 |
36 | ship_node = root_node.childNodes[0]
37 | ship_mesh = ship_node.childNodes[0]
38 | ship_emitter = ship_mesh.childNodes[0]
39 | ship_emitter_light = ship_emitter.light
40 | ship_camera = ship_mesh.camera
41 | scrap_meshShape = ship_mesh.geometry
42 | lambert1 = scrap_meshShape.materials[0]
43 |
44 | camera_node = scn.Node()
45 | camera_node.camera = scn.Camera()
46 | camera_node.position = (0,0,5)
47 | root_node.addChildNode(camera_node)
48 |
49 | # Add a constraint to the camera to keep it pointing to the target geometry
50 | constraint = scn.LookAtConstraint.lookAtConstraintWithTarget(root_node)
51 | constraint.gimbalLockEnabled = True
52 | camera_node.constraints = constraint
53 |
54 | light_node = scn.Node()
55 | light_node.position = (100, 50, 100)
56 | light = scn.Light()
57 | light.type = scn.LightTypeDirectional
58 | light.castsShadow = True
59 | light.color = 'white'
60 | light_node.light = light
61 | light_node.constraints = constraint
62 | root_node.addChildNode(light_node)
63 |
64 | ambient_light = scn.Light()
65 | ambient_light.type = scn.LightTypeAmbient
66 | ambient_light.name = 'ambient light'
67 | ambient_light.color = (.99, 1.0, .86)
68 | ambient_light.intensity = 100
69 | ambient_node = scn.Node()
70 | ambient_node.light = ambient_light
71 | root_node.addChildNode(ambient_node)
72 |
73 | main_view.present(style='fullscreen', hide_title_bar=False)
74 |
75 | Demo.run()
76 |
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/selector.py:
--------------------------------------------------------------------------------
1 | '''Cube puzzles - five typical 3x3x3 3D puzzles.
2 |
3 | (c) Peter Ulbrich, 2019
4 |
5 | a demo script for the sceneKit wrapper package for Pythonista
6 |
7 | See the README.md for details.
8 |
9 | '''
10 |
11 | import math
12 |
13 | import sceneKit as scn
14 |
15 | from data import *
16 | import main
17 | import setup
18 | from piece import *
19 | import manual
20 |
21 | class SelectorStage:
22 | def __init__(self):
23 | self.selector_views = setup.setup_selector_views()
24 | self.animation = setup.setup_show_drawer_animation()
25 | self.animation.repeatCount = 1
26 | self.animation.animationDidStop = self.next_selector_animation
27 | self.animation_player = scn.AnimationPlayer(self.animation)
28 |
29 | self.selector_views[0].scene.background.addAnimationPlayer(self.animation_player, 'highlight')
30 | self.animation_player.play()
31 |
32 | self.animation_index = 0
33 |
34 | data.hud_layer.back_button.hidden = True
35 | data.hud_layer.restart_button.hidden = True
36 | data.hud_layer.set_title('Cube puzzles')
37 |
38 | def next_selector_animation(self, animation, receiver, completed):
39 | if completed:
40 | self.selector_views[self.animation_index].scene.background.removeAllAnimations()
41 | self.animation_index += 1
42 | if self.animation_index == len(self.selector_views):
43 | self.animation_index = 0
44 | self.selector_views[self.animation_index].scene.background.addAnimationPlayer(self.animation_player, 'highlight')
45 |
46 | def scene_views(self):
47 | return self.selector_views
48 |
49 | def tap(self, location):
50 | for index, aView in enumerate(self.selector_views):
51 | aFrame = aView.frame
52 | if aFrame[0] < location.x < aFrame[0] + aFrame[2] and aFrame[1] < location.y < aFrame[1] + aFrame[3]:
53 | data.hud_layer.back_button.hidden = False
54 | data.hud_layer.restart_button.hidden = False
55 | main.MainViewController.next_stage(manual.ManualStage(Puzzle_variant(index)))
56 |
57 | def hint(self):
58 | data.hud_layer.show_generic_help()
59 |
60 | def layout(self):
61 | _, _, w, h = data.main_view.frame
62 | rows = int(math.sqrt(len(self.selector_views)))
63 | columns = (len(self.selector_views)-1)//rows + 1
64 | missing = rows*columns - len(self.selector_views)
65 | if not data.horizontal:
66 | rows, columns = columns, rows
67 | width = int(w / (columns + 0.8 + 0.3*(columns-1)))
68 | height = int(h / (rows + 0.8 + 0.3*(rows-1)))
69 | for i in range(len(self.selector_views)):
70 | row = i // columns
71 | column = i % columns
72 | if row != rows - 1:
73 | self.selector_views[i].frame = (int(0.4*width + 1.3*column*width), int(0.4*height + 1.3*row*height), width, height)
74 | else:
75 | self.selector_views[i].frame = (int(0.4*width + missing/2*1.3*width + 1.3*column*width), int(0.4*height + 1.3*row*height), width, height)
76 | camera_node = self.selector_views[i].scene.rootNode.childNodeWithName('selector_camera')
77 | camera_node.position = (0., 7., 11.) if width/height > 0.8 else (0., 9.5, 14.5)
78 |
79 |
80 | if __name__ == '__main__':
81 | main.MainViewController.run()
82 |
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/data.py:
--------------------------------------------------------------------------------
1 | '''Cube puzzles - five typical 3x3x3 3D puzzles.
2 |
3 | (c) Peter Ulbrich, 2019
4 |
5 | a demo script for the sceneKit wrapper package for Pythonista
6 |
7 | See the README.md for details.
8 |
9 | '''
10 |
11 | from collections import namedtuple
12 | from enum import Enum
13 | import ui
14 |
15 | @ui.in_background
16 | def print_in_background(*text):
17 | print(*text)
18 |
19 | Cube = namedtuple('Cube', 'x y z')
20 |
21 | class Puzzle_variant(Enum):
22 | Half_Hour = 0
23 | Soma_Cube = 1
24 | Diabolical_Cube = 2
25 | Mikusinskis_Cube = 3
26 | Five_Piece = 4
27 |
28 | class Data:
29 | __slots__ = ('main_view', 'horizontal', 'gestures_instance', 'hud_layer', 'active_stage', 'reference_pieces')
30 |
31 | def __init__(self):
32 | self.reference_pieces = self.create_reference_pieces()
33 | self.active_stage = None
34 |
35 | def create_reference_pieces(self):
36 | piece_definitions = [
37 | [
38 | [((2., 0., 0.), (1., 1., 0.), (1., 1., -1.)), '1'],
39 | [((2., 0., 0.), (2., 0., -1.)), '2'],
40 | [((2., 0., 0.), (0., 0., -1.), (1., 1., 0.)), '3'],
41 | [((1., 1., 0.), (2., 1., 0.), (1., 0., -1.)), '4'],
42 | [((2., 0., 0.), (1., 0., -1.)), '5'],
43 | [((0., 0., -1.), (0., 1., -1.)), '6']
44 | ], [
45 | [((0., 0., -1.), (0., 1., -1.)), '1'],
46 | [((0., 1., 0.), (0., 0., -1.)), '2'],
47 | [((1., 1., 0.), (0., 0., -1.)), '3'],
48 | [((2., 0., 0.), (2., 0., -1.)), '4'],
49 | [((2., 0., 0.), (1., 0., -1.)), '5'],
50 | [((1., 0., -1.),), '6'],
51 | [((1., 0., -1.), (2., 0., -1.)), '7']
52 | ], [
53 | [tuple(), '1'],
54 | [((0., 0., -1.), (1., 0., -1.)), '2'],
55 | [((2., 0., 0.,), (0., 0., -1.), (1., 0., -1.), (0., 0., -2.)), '3'],
56 | [((0., 0., -1),), '4'],
57 | [((2., 0., 0.), (0., 0., -1.), (2., 0., -1.)), '5'],
58 | [((2., 0., 0.), (0., 0., -1.), (1., 0., -1.), (2., 0., -1.), (0., 0., -2.)), '6']
59 | ], [
60 | [((2., 0., 0.), (0., 0., -1.)), '1'],
61 | [((2., 0., 0.), (0., 0., -1.), (0., 1., -1.)), '2'],
62 | [((0., 0., -1.), (0., 1., -1.)), '3'],
63 | [((1., 0., -1.), (1., 1., -1.)), '4'],
64 | [((2., 0., 0.), (2., 1., 0.), (1., 0., -1.)), '5'],
65 | [((1., 0., -1.), (1., 1., -1.), (2., 1., -1.)), '6']
66 | ], [
67 | [((1., 0., -1.), (2., 0., 0.), (2., 1., 0.)), '1'],
68 | [((2., 0., 0.), (2., 1., 0.), (2., 1., -1.)), '2'],
69 | [((2., 0., 0.), (2., 0., -1.), (1., 1., 0.)), '3'],
70 | [((0., 1., 0.), (2., 0., 0.), (2., 0., -1.), (2., 1., -1.)), '4'],
71 | [((0., 1., 0.), (2., 0., 0.), (2., 1., 0.), (2., 1., -1.)), '5']
72 | ]
73 | ]
74 |
75 | reference_pieces = []
76 | for aDefinition in piece_definitions:
77 | pieces = {}
78 | for aPiece in aDefinition:
79 | name = aPiece[1]
80 | nodes = [Cube(0., 0., 0.)]
81 |
82 | nodes.append(Cube(1., 0., 0.))
83 |
84 | for aCubePosition in aPiece[0]:
85 | nodes.append(Cube._make(aCubePosition))
86 |
87 | pieces[name] = nodes
88 | reference_pieces.append(pieces)
89 | return reference_pieces
90 |
91 | data = Data()
92 |
93 | if __name__ == '__main__':
94 | import main
95 | main.MainViewController.run()
96 |
--------------------------------------------------------------------------------
/sceneKit demo/12_particlesDemo.py:
--------------------------------------------------------------------------------
1 | '''
2 | based on the code in the thread https://forum.omz-software.com/topic/1686/3d-in-pythonista by omz
3 | '''
4 | from objc_util import *
5 | import sceneKit as scn
6 | import ui
7 | import math
8 |
9 | @on_main_thread
10 | def demo():
11 | main_view = ui.View()
12 | w, h = ui.get_screen_size()
13 | main_view.frame = (0,0,w,h)
14 | main_view.name = 'particles demo'
15 |
16 | scene_view = scn.View(main_view.frame, superView=main_view)
17 | scene_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleWidth
18 | scene_view.antialiasingMode = scn.AntialiasingMode.Multisampling16X
19 |
20 | scene_view.allowsCameraControl = True
21 |
22 | scene_view.backgroundColor = 'black'
23 |
24 | scene_view.scene = scn.Scene()
25 |
26 | root_node = scene_view.scene.rootNode
27 | text_mesh = scn.Text.textWithString('Pythonista', 6.0)
28 | text_mesh.flatness = 0.2
29 | text_mesh.chamferRadius = 0.4
30 | text_mesh.font = ('HelveticaNeue-Bold', 18)
31 | bbox_min, bbox_max = text_mesh.boundingBox
32 | text_width = bbox_max.x - bbox_min.x
33 | text_node = scn.Node.nodeWithGeometry(text_mesh)
34 | text_node.castsShadow = False
35 | text_container = scn.Node.node()
36 | text_container.addChildNode(text_node)
37 | text_container.position = (0, 40, 0)
38 | text_node.position = (-text_width/2, 0, 0)
39 |
40 | fire = scn.ParticleSystem()
41 | fire.birthRate = 300000
42 | fire.loops = True
43 | fire.emissionDuration = 8
44 | fire.emissionDurationVariation = 4
45 | fire.idleDuration = 2
46 | fire.idleDurationVariation = 0.5
47 | fire.emittingDirection = (0, 1, 0)
48 | fire.spreadingAngle = 15
49 | fire.particleDiesOnCollision = False
50 | fire.particleLifeSpan = 0.4
51 | fire.particleLifeSpanVariation = 0.5
52 | fire.particleVelocity = 20
53 | fire.particleVelocityVariation = 30
54 | fire.particleImage = ui.Image.named('shp:WhitePuff05')
55 | fire.particleSize = 0.4
56 | fire.particleSizeVariation = 0.2
57 | fire.particleIntensity = 1.5
58 | fire.particleIntensityVariation = 2
59 | fire.stretchFactor = 0.02
60 | colorAnim = scn.CoreKeyframeAnimation()
61 | colorAnim.values = [(.99, 1.0, .71, 0.8), (1.0, .52, .0, 0.8), (1., .0, .1, 1.), (.78, .0, .0, 0.3)]
62 | colorAnim.keyTimes = (0., 0.1, 0.8, 1.)
63 | fire.timingFunctions = [scn.CoreMediaTimingFunction.functionWithName(aFunc) for aFunc in [scn.MediaTimingFunctionEaseOut, scn.MediaTimingFunctionEaseInEaseOut, scn.MediaTimingFunctionEaseIn]]
64 | prop_con = scn.ParticlePropertyController.controllerWithAnimation(colorAnim)
65 | fire.propertyControllers = {scn.SCNParticlePropertyColor:prop_con}
66 | fire.emitterShape = text_mesh
67 | fire.birthLocation = scn.ParticleBirthLocation.SCNParticleBirthLocationSurface
68 | text_node.addParticleSystem(fire)
69 |
70 | root_node.addChildNode(text_container)
71 |
72 | light_node = scn.Node.node()
73 | light_node.position = (0, 105, 5)
74 | light_node.rotation = (1, 0, 0, -math.pi/2)
75 | light = scn.Light.light()
76 | light.type = 'spot'
77 | light.spotOuterAngle = 90
78 | light.castsShadow = True
79 | light.shadowSampleCount = 16
80 | light.color = 'white'
81 | light_node.light = light
82 | root_node.addChildNode(light_node)
83 |
84 | main_view.present(style='fullscreen', hide_title_bar=False)
85 |
86 | demo()
87 |
88 |
--------------------------------------------------------------------------------
/sceneKit demo/16_AppleVehicleDemo/OverlayScene.py:
--------------------------------------------------------------------------------
1 | '''
2 | In the original version this was an SKScene. For easier integration it was rewritten with objects from the Phytonista ui module.
3 | '''
4 | import ui
5 | import io
6 | import math
7 | from PIL import Image
8 |
9 | import sceneKit as scn
10 |
11 | M_PI_2 = math.pi/2
12 |
13 | class SpeedNeedle:
14 | def __init__(self):
15 | self.view = ui.ImageView()
16 | self.view.content_mode = ui.CONTENT_CENTER
17 |
18 | self.image_base = Image.open("resources/needle.png")
19 | size = self.image_base.size
20 | self.image_base = self.image_base.resize((int(size[0]*0.55), int(size[1]*0.55)))
21 | size = self.image_base.size
22 | self.image = Image.new("RGBA", (2*size[1], 2*size[1]), None)
23 | self.image.paste(self.image_base, (size[1], 0))
24 | self.image = self.image.rotate(90)
25 |
26 | self.zRotation = 0.0
27 |
28 |
29 | def get_zRotation(self):
30 | return self._zRotation/180.*math.pi
31 | def set_zRotation(self, angle):
32 | self._zRotation = angle/math.pi*180.
33 | self.draw_needle()
34 | zRotation = property(get_zRotation, set_zRotation)
35 |
36 |
37 | def draw_needle(self):
38 | needle = self.image.rotate(self._zRotation)
39 | with io.BytesIO() as bIO:
40 | needle.save(bIO, 'PNG')
41 | ret_im = ui.Image.from_data(bIO.getvalue())
42 | self.view.image = ret_im
43 |
44 | class CameraButton:
45 | def __init__(self):
46 | self.view = ui.Button()
47 | self.image = ui.Image.named("resources/video_camera.png")
48 | self.view.background_image = self.image
49 | self.view.action = self.setClicked
50 | self._clicked = False
51 |
52 | self.sound = scn.AudioSource("resources/click.caf")
53 | self.clickAction = scn.Action.playAudioSource(self.sound, True)
54 |
55 | def setClicked(self, sender):
56 | self._clicked = True
57 |
58 | @property
59 | def clicked(self):
60 | ret = self._clicked
61 | self._clicked = False
62 | return ret
63 |
64 | class OverlayScene:
65 | def __init__(self, main_view):
66 |
67 | #add the speed gauge
68 |
69 | myImage = ui.Image.named("resources/speedGauge.png")
70 | self.myImage_view = ui.ImageView()
71 | self.myImage_view.frame = (20, int(main_view.frame[3]-1.5*myImage.size.height), myImage.size.width, myImage.size.height)
72 | self.myImage_view.image = myImage
73 | main_view.add_subview(self.myImage_view)
74 |
75 | #add the needle
76 |
77 | self.mySpeedNeedle = SpeedNeedle()
78 | self.mySpeedNeedle.view.frame = (self.myImage_view.frame[0], self.myImage_view.frame[1]+self.myImage_view.frame[3]//2-8, self.myImage_view.frame[2], self.myImage_view.frame[3])
79 | main_view.add_subview(self.mySpeedNeedle.view)
80 |
81 | #add the camera button
82 |
83 | self.myCameraButton = CameraButton()
84 | self.myCameraButton.view.frame = (main_view.frame[2]-20-self.myCameraButton.image.size.width, int(main_view.frame[3]-1.5*myImage.size.height+10), self.myCameraButton.image.size.width, self.myCameraButton.image.size.height)
85 | main_view.add_subview(self.myCameraButton.view)
86 |
87 | #add a close button
88 | self.close_button = ui.Button()
89 | self.close_button.action = self.closeClick
90 | self.close_button.frame = (20, 40, 40, 40)
91 | self.close_button.background_image = ui.Image.named('emj:No_Entry_2')
92 | self.close = False
93 | main_view.add_subview(self.close_button)
94 |
95 | def closeClick(self, sender):
96 | self.close = True
97 |
98 |
--------------------------------------------------------------------------------
/sceneKit demo/08_tetrahedron.py:
--------------------------------------------------------------------------------
1 | """
2 | based on:
3 | a tetrahedron by cvp at https://forum.omz-software.com/topic/3922/for-the-fun-a-photos-cube
4 |
5 | """
6 |
7 | from objc_util import *
8 | import ctypes
9 | import sceneKit as scn
10 | import ui
11 | import math
12 |
13 | @on_main_thread
14 | def demo():
15 | main_view = ui.View()
16 | w, h = ui.get_screen_size()
17 | main_view.frame = (0,0,w,h)
18 | main_view.name = 'Tetrahedron'
19 |
20 | scene_view = scn.View(main_view.frame, superView=main_view)
21 | scene_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleRightMargin
22 | scene_view.allowsCameraControl = True
23 |
24 | scene_view.scene = scn.Scene()
25 |
26 | root_node = scene_view.scene.rootNode
27 |
28 | camera_node = scn.Node()
29 | camera_node.camera = scn.Camera()
30 | camera_node.position = (0,0,5)
31 | root_node.addChildNode(camera_node)
32 |
33 | verts = [
34 | scn.Vector3(0, 1, 0),
35 | scn.Vector3(-0.5, 0, 0.5),
36 | scn.Vector3(0.5, 0, 0.5),
37 | scn.Vector3(0.5, 0, -0.5),
38 | scn.Vector3(-0.5, 0, -0.5),
39 | scn.Vector3(0, -1, 0)]
40 |
41 | source = scn.GeometrySource.geometrySourceWithVertices(verts)
42 |
43 | indexes = [3,3,3,3,3,3,3,3,
44 | 0, 1, 2,
45 | 2, 3, 0,
46 | 3, 4, 0,
47 | 4, 1, 0,
48 | 1, 5, 2,
49 | 2, 5, 3,
50 | 3, 5, 4,
51 | 4, 5, 1]
52 |
53 | elements = scn.GeometryElement.geometryElementWithData(indexes, scn.GeometryPrimitiveType.Polygon)
54 |
55 | geometry = scn.Geometry.geometryWithSources(source, elements)
56 |
57 | material = scn.Material()
58 | material.contents = scn.RGBA(1,0.9,0.9,1.0)
59 | geometry.materials = material
60 |
61 | geometry_node = scn.Node.nodeWithGeometry(geometry)
62 | root_node.addChildNode(geometry_node)
63 |
64 | elements2 = []
65 | materials = []
66 | colors = ['red','blue','green','yellow','orange','pink','cyan','orchid']
67 | for i in range(8):
68 | j = 8 + i*3
69 | indexes2 = [indexes[j],indexes[j+1],indexes[j+2]]
70 | element = scn.GeometryElement.geometryElementWithData(indexes2, scn.GeometryPrimitiveType.Triangles)
71 | elements2.append(element)
72 | material = scn.Material()
73 | material.contents= colors[i]
74 | materials.append(material)
75 | geometry2 = scn.Geometry.geometryWithSources(source, elements2)
76 | geometry2.materials = materials
77 | geometry2_node = scn.Node.nodeWithGeometry(geometry2)
78 | tx,ty,tz = (-1.5,0,0)
79 | translation = (1,0,0,0, 0,1,0,0, 0,0,1,0, tx,ty,tz,1)
80 | geometry2_node.pivot = translation
81 | root_node.addChildNode(geometry2_node)
82 |
83 | constraint = scn.LookAtConstraint.lookAtConstraintWithTarget(geometry_node)
84 | constraint.gimbalLockEnabled = True
85 | camera_node.constraints = constraint
86 |
87 | light_node = scn.Node()
88 | light_node.position = (100, 0, -10)
89 | light = scn.Light()
90 | light.type = scn.LightTypeDirectional
91 | light.castsShadow = True
92 | light.color = 'white'
93 | light_node.light = light
94 | root_node.addChildNode(light_node)
95 |
96 | rotate_action = scn.Action.repeatActionForever(scn.Action.rotateBy(0, math.pi*2, 0, 10))
97 | geometry_node.runAction(rotate_action)
98 | geometry2_node.runAction(rotate_action)
99 |
100 | main_view.present(style='fullscreen', hide_title_bar=False)
101 |
102 |
103 | demo()
104 |
--------------------------------------------------------------------------------
/sceneKit demo/11_avoidOccluderDemo.py:
--------------------------------------------------------------------------------
1 | """
2 | avoid occluder demo
3 | """
4 |
5 | from objc_util import *
6 | import sceneKit as scn
7 | import ui
8 | import math
9 |
10 | def dot(v1, v2):
11 | return sum(x*y for x,y in zip(list(v1),list(v2)))
12 |
13 | def det2(v1, v2):
14 | return v1[0]*v2[1] - v1[1]*v2[0]
15 |
16 | class Demo:
17 |
18 | @classmethod
19 | def run(cls):
20 | cls().main()
21 |
22 | @on_main_thread
23 | def main(self):
24 | main_view = ui.View()
25 | w, h = ui.get_screen_size()
26 | main_view.frame = (0,0,w,h)
27 | main_view.name = 'avoid occluder demo'
28 |
29 | scene_view = scn.View(main_view.frame, superView=main_view)
30 | scene_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleWidth
31 | scene_view.allowsCameraControl = True
32 | scene_view.delegate = self
33 | scene_view.backgroundColor = 'white'
34 | scene_view.rendersContinuously = True
35 | scene_view.scene = scn.Scene()
36 |
37 | root_node = scene_view.scene.rootNode
38 |
39 | floor_geometry = scn.Floor()
40 | floor_node = scn.Node.nodeWithGeometry(floor_geometry)
41 | root_node.addChildNode(floor_node)
42 |
43 | ball_radius = 0.2
44 | ball_geometry = scn.Sphere(radius=ball_radius)
45 | ball_geometry.firstMaterial.diffuse.contents = (.48, .48, .48)
46 | ball_geometry.firstMaterial.specular.contents = (.88, .88, .88)
47 | self.ball_node_1 = scn.Node.nodeWithGeometry(ball_geometry)
48 | self.ball_node_2 = scn.Node.nodeWithGeometry(ball_geometry)
49 |
50 | root_node.addChildNode(self.ball_node_1)
51 | root_node.addChildNode(self.ball_node_2)
52 |
53 | occluder_geometry = scn.Box(0.3, 2., 15., 0.2)
54 | occluder_geometry.firstMaterial.diffuse.contents = (.91, .91, .91)
55 | occluder_node = scn.Node.nodeWithGeometry(occluder_geometry)
56 | occluder_node.position = (0., 0.8, 0.)
57 | root_node.addChildNode(occluder_node)
58 |
59 | self.orbit_r = 10
60 | self.omega_speed_1 = math.pi/1500
61 | self.omega_speed_2 = 1.5*self.omega_speed_1
62 | self.ball_node_1.position = (self.orbit_r, 0.5, 0.)
63 | self.ball_node_2.position = (0., 0.5, self.orbit_r)
64 |
65 | constraint = scn.AvoidOccluderConstraint.avoidOccluderConstraintWithTarget(self.ball_node_1)
66 | self.ball_node_2.constraints = [constraint]
67 |
68 | camera_node = scn.Node()
69 | camera_node.camera = scn.Camera()
70 | camera_node.position = (0.5*self.orbit_r , 0.5*self.orbit_r, 1.5*self.orbit_r)
71 | camera_node.lookAt(root_node.position)
72 | root_node.addChildNode(camera_node)
73 |
74 | light_node = scn.Node()
75 | light_node.position = (self.orbit_r, self.orbit_r, self.orbit_r)
76 | light = scn.Light()
77 | light.type = scn.LightTypeDirectional
78 | light.castsShadow = True
79 | light.shadowSampleCount = 32
80 | light.color = (.99, 1.0, .86)
81 | light_node.light = light
82 | light_node.lookAt(root_node.position)
83 | root_node.addChildNode(light_node)
84 |
85 | main_view.present(style='fullscreen', hide_title_bar=False)
86 |
87 | def update(self, view, atTime):
88 | pos_1 = self.ball_node_1.presentationNode.position
89 | pos_2 = self.ball_node_2.presentationNode.position
90 | self.omega_1 = -math.atan2(det2((pos_1.x, pos_1.z), (1., 0.)), dot((pos_1.x, pos_1.z), (1., 0.)))
91 | self.omega_2 = -math.atan2(det2((pos_2.x, pos_2.z), (1., 0.)), dot((pos_2.x, pos_2.z), (1., 0.)))
92 | self.omega_1 += self.omega_speed_1
93 | self.omega_2 += self.omega_speed_2
94 | self.ball_node_1.position = (self.orbit_r*math.cos(self.omega_1), 0.5, self.orbit_r*math.sin(self.omega_1))
95 | self.ball_node_2.position = (self.orbit_r*math.cos(self.omega_2), 0.5, self.orbit_r*math.sin(self.omega_2))
96 |
97 | Demo.run()
98 |
--------------------------------------------------------------------------------
/sceneKit demo/03_Feisar_ship/Feisar_ShipDemo.py:
--------------------------------------------------------------------------------
1 | """
2 | using the Feisar_Ship.scn file
3 | """
4 |
5 | from objc_util import *
6 | import ctypes
7 | import sceneKit as scn
8 | import ui
9 | import math
10 |
11 |
12 | class Demo:
13 | def __init__(self):
14 | self.name = 'Feisar_Ship'
15 | pass
16 |
17 | @classmethod
18 | def run(cls):
19 | cls().main()
20 |
21 | @on_main_thread
22 | def main(self):
23 | main_view = ui.View()
24 | w, h = ui.get_screen_size()
25 | main_view.frame = (0,0,w,h)
26 | main_view.name = 'Feisar_Ship demo'
27 |
28 | scene_view = scn.View(main_view.frame, superView=main_view)
29 | scene_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleWidth
30 | scene_view.allowsCameraControl = True
31 |
32 | scene_view.backgroundColor = 'black'
33 |
34 | scene_view.scene = scn.Scene.sceneWithURL(url='Feisar_Ship.scn')
35 |
36 | root_node = scene_view.scene.rootNode
37 |
38 | feisar_node = root_node.childNodes[0]
39 |
40 | feisar_node.light = scn.nil
41 | root_node.light = scn.nil
42 |
43 | feisar_geometry = feisar_node.geometry
44 | geometry_sources = feisar_geometry.geometrySources
45 |
46 | feisar_material = feisar_geometry.firstMaterial
47 | feisar_material.emission.intensity = 0.05
48 | feisar_material.roughness.contents = (.79, .79, .79)
49 | feisar_material.metalness.contents = (.11, .11, .11)
50 |
51 | pulse_action = scn.Action.repeatActionForever(scn.Action.sequence([scn.Action.fadeInWithDuration(1.2), scn.Action.fadeOutWithDuration(0.5)]))
52 |
53 | blue_emitter = scn.Material()
54 | blue_emitter.diffuse.contents = 'black'
55 | blue_emitter.emission.contents = (.0, .35, 1.0)
56 | white_emitter = scn.Material()
57 | white_emitter.diffuse.contents = 'black'
58 | white_emitter.emission.contents = (.86, .98, 1.0)
59 |
60 | nose_geometry = scn.Sphere(radius=1.5)
61 | nose_geometry.firstMaterial = blue_emitter
62 | nose_node = scn.Node.nodeWithGeometry(nose_geometry)
63 | nose_node.position = (0., 4.35, 161.)
64 | nose_node.castsShadow = False
65 | nose_node.runAction(pulse_action)
66 | feisar_node.addChildNode(nose_node)
67 |
68 | front_grille_p0 = scn.vector3Make((0., 10., 137.6))
69 | front_grille_p1 = scn.vector3Make((0., 20., 153.5))
70 | front_grille_ds1 = scn.vector3Make((-0.25, 0.7, -0.25))
71 | grille_nr = 15
72 | dt = 0.8/grille_nr
73 | front_grille_node = scn.Node()
74 | feisar_node.addChildNode(front_grille_node)
75 |
76 | front_grille_geometry = scn.Cylinder(0.45, 13.7)
77 | front_grille_geometry.firstMaterial = white_emitter
78 |
79 | for i in range(grille_nr):
80 | aNode = scn.Node.nodeWithGeometry(front_grille_geometry)
81 | aNode.position = (0., front_grille_p0.y+(front_grille_p1.y-front_grille_p0.y)*i/grille_nr, front_grille_p0.z+(front_grille_p1.z-front_grille_p0.z)*i/grille_nr)
82 | aNode.scale = (1.+front_grille_ds1.x*i/grille_nr, 1.+front_grille_ds1.y*i/grille_nr, 1.+front_grille_ds1.z*i/grille_nr)
83 | aNode.rotation = (0., 0., 1., math.pi/2)
84 | grille_action = scn.Action.sequence([scn.Action.waitForDuration(i*dt), scn.Action.fadeOutWithDuration(0.7*dt), scn.Action.waitForDuration(dt), scn.Action.fadeInWithDuration(0.3*dt), scn.Action.waitForDuration((grille_nr-2-i)*dt)])
85 | aNode.runAction(scn.Action.repeatActionForever(scn.Action.sequence([grille_action, grille_action.reversedAction()])))
86 | front_grille_node.addChildNode(aNode)
87 |
88 | constraint = scn.LookAtConstraint.lookAtConstraintWithTarget(feisar_node)
89 | constraint.gimbalLockEnabled = True
90 |
91 |
92 | light_node = scn.Node()
93 | light_node.position = (150, 60, -20)
94 |
95 | light = scn.Light()
96 | light.type = scn.LightTypeDirectional
97 | light.castsShadow = True
98 | light.color = (.99, 1.0, .86)
99 | light_node.light = light
100 | light_node.constraints = constraint
101 | root_node.addChildNode(light_node)
102 |
103 | main_view.present(style='fullscreen', hide_title_bar=False)
104 |
105 | Demo.run()
106 |
--------------------------------------------------------------------------------
/sceneKit demo/10_morpherDemo.py:
--------------------------------------------------------------------------------
1 | """
2 | based on:
3 | a tetrahedron by cvp at https://forum.omz-software.com/topic/3922/for-the-fun-a-photos-cube
4 |
5 | """
6 |
7 | from objc_util import *
8 | import ctypes
9 | import sceneKit as scn
10 | import ui
11 | import math
12 |
13 |
14 | class Demo:
15 | def __init__(self):
16 | self.name = 'my name is Demo'
17 | pass
18 |
19 | @classmethod
20 | def run(cls):
21 | cls().main()
22 |
23 | @on_main_thread
24 | def main(self):
25 | # actually you need only to preserve those properties that are needed after the main_view.present call,
26 | # in this case the self.morpher. All the other self. prefixes are not needed for the same functionality
27 | self.main_view = ui.View()
28 | w, h = ui.get_screen_size()
29 | self.main_view.frame = (0,0,w,h)
30 | self.main_view.name = 'Morpher demo'
31 |
32 | self.scene_view = scn.View(self.main_view.frame, superView=self.main_view)
33 | self.scene_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleRightMargin
34 | self.scene_view.allowsCameraControl = True
35 |
36 | self.scene_view.scene = scn.Scene()
37 |
38 | self.scene_view.delegate = self
39 |
40 | self.root_node = self.scene_view.scene.rootNode
41 |
42 | self.camera_node = scn.Node()
43 | self.camera_node.camera = scn.Camera()
44 | self.camera_node.position = (0,0,5)
45 | self.root_node.addChildNode(self.camera_node)
46 |
47 | verts = [
48 | scn.Vector3(0, 1, 0),
49 | scn.Vector3(-0.5, 0, 0.5),
50 | scn.Vector3(0.5, 0, 0.5),
51 | scn.Vector3(0.5, 0, -0.5),
52 | scn.Vector3(-0.5, 0, -0.5),
53 | scn.Vector3(0, -1, 0)]
54 |
55 | verts_2 = [
56 | scn.Vector3(0, 2.5, 0),
57 | scn.Vector3(-0.4, 0, 0.4),
58 | scn.Vector3(0.4, 0, 0.4),
59 | scn.Vector3(0.4, 0, -0.4),
60 | scn.Vector3(-0.4, 0, -0.4),
61 | scn.Vector3(0, -1.5, 0)]
62 |
63 | self.source = scn.GeometrySource.geometrySourceWithVertices(verts)
64 | self.source_2 = scn.GeometrySource.geometrySourceWithVertices(verts_2)
65 |
66 | indexes = [
67 | 0, 1, 2,
68 | 2, 3, 0,
69 | 3, 4, 0,
70 | 4, 1, 0,
71 | 1, 5, 2,
72 | 2, 5, 3,
73 | 3, 5, 4,
74 | 4, 5, 1]
75 |
76 | self.elements = scn.GeometryElement.geometryElementWithData(indexes, scn.GeometryPrimitiveType.Triangles)
77 |
78 | self.geometry = scn.Geometry.geometryWithSources(self.source, self.elements)
79 | self.geometry_2 = scn.Geometry.geometryWithSources(self.source_2, self.elements)
80 |
81 | self.material = scn.Material()
82 | self.material.contents = scn.RGBA(1, 0.9, 0.9, 1.0)
83 | self.geometry.materials = self.material
84 |
85 | self.morpher = scn.Morpher()
86 | self.morpher.targets = [self.geometry_2]
87 | self.morpher.setWeightForTargetAtIndex(0.0, 0)
88 |
89 | self.geometry_node = scn.Node.nodeWithGeometry(self.geometry)
90 | self.geometry_node.name = 'hero'
91 | self.geometry_node.morpher = self.morpher
92 | self.root_node.addChildNode(self.geometry_node)
93 |
94 | # Add a constraint to the camera to keep it pointing to the target geometry
95 | constraint = scn.LookAtConstraint.lookAtConstraintWithTarget(self.geometry_node)
96 | constraint.gimbalLockEnabled = True
97 | self.camera_node.constraints = constraint
98 |
99 | self.light_node = scn.Node()
100 | self.light_node.position = (100, 0, -10)
101 | self.light = scn.Light()
102 | self.light.type = scn.LightTypeDirectional
103 | self.light.castsShadow = True
104 | self.light.color = 'white'
105 | self.light_node.light = self.light
106 | self.root_node.addChildNode(self.light_node)
107 |
108 | self.rotate_action = scn.Action.repeatActionForever(scn.Action.rotateBy(0, math.pi*2, 0, 10))
109 | self.geometry_node.runAction(self.rotate_action)
110 |
111 | self.main_view.present(style='fullscreen', hide_title_bar=False)
112 |
113 | def update(self, view, atTime):
114 | tick = (int(atTime*1000) % 314)/100.
115 | weight = math.sin(tick)
116 | self.morpher.setWeightForTargetAtIndex(weight, 0)
117 |
118 | # also correct:
119 | # hero_node = view.scene.rootNode.childNodeWithName('hero')
120 | # morpher = hero_node.morpher
121 | # morpher.setWeightForTargetAtIndex(weight, 0)
122 |
123 |
124 |
125 | Demo.run()
126 |
--------------------------------------------------------------------------------
/sceneKit demo/05_photoCube.py:
--------------------------------------------------------------------------------
1 | """
2 | based on:
3 | photoCube at https://forum.omz-software.com/topic/3922/for-the-fun-a-photos-cube
4 | For the fun, a Photos cube by cvp at https://forum.omz-software.com/topic/3922/for-the-fun-a-photos-cube
5 | """
6 |
7 | from objc_util import *
8 | import sceneKit as scn
9 | import ui
10 |
11 | cube_image_names = ['emj:Airplane', 'emj:Cat_Face', 'emj:Anchor', 'emj:Basketball', 'emj:Aubergine', 'emj:Baby_Chick_1']
12 | cube_images_orig = [ui.Image.named(cube_image_names[i]) for i in range(6)]
13 |
14 | (image_width_orig, image_height_orig) = cube_images_orig[0].size
15 | (image_width, image_height) = (image_width_orig * 1.5, image_height_orig * 1.5)
16 | i_x = (image_width - image_width_orig)/2
17 | i_y = (image_height - image_height_orig)/2
18 |
19 | cube_images = []
20 | for i in range(6):
21 | with ui.ImageContext(image_width, image_height) as ctx:
22 | cube_images_orig[i].draw(i_x, i_y, image_width_orig, image_height_orig)
23 | cube_images.append(ctx.get_image())
24 |
25 | with ui.ImageContext(image_width, image_height) as ctx:
26 | rrect = ui.Path.rounded_rect(0, 0, image_width, image_height, 2)
27 | rrect.line_width = 4
28 | ui.set_color((.91, .91, .91))
29 | rrect.fill()
30 | ui.set_color('black')
31 | rrect.stroke()
32 | d = 2
33 | rrect = ui.Path.rounded_rect(0+d, 0+d, image_width-2*d, image_height-2*d, 2)
34 | rrect.line_width = 2
35 | ui.set_color((.6, .6, .6))
36 | rrect.stroke()
37 | inside_image = ctx.get_image()
38 |
39 | @on_main_thread
40 | def main():
41 | main_view = ui.View()
42 | w, h = ui.get_screen_size()
43 | main_view.frame = (0,0,w,h)
44 | main_view.name = 'Photos cube'
45 |
46 | scene_view = scn.View(main_view.frame)
47 | scene_view.autoresizingMask = (scn.ViewAutoresizing.FlexibleHeight, scn.ViewAutoresizing.FlexibleRightMargin)
48 |
49 | scene_view.allowsCameraControl = True
50 |
51 | scene_view.backgroundColor = 'white'
52 |
53 | scene_view.addToSuperview(main_view)
54 |
55 | scene = scn.Scene()
56 | scene_view.scene = scene
57 |
58 | root_node = scene.rootNode
59 |
60 | cube_geometry = scn.Box(width=1, height=1, length=1, chamferRadius=0.05)
61 | cube_geometry_inside = scn.Box(width=1-0.001, height=1-0.001, length=1-0.001, chamferRadius=0.04)
62 |
63 | Material_inside = scn.Material()
64 | Material_inside.diffuse.contents = inside_image
65 |
66 | cube_geometry_inside.materials =[Material_inside for i in range(6)]
67 |
68 | cube_geometry_materials = [scn.Material() for i in range(6)]
69 | for i in range(6):
70 | cube_geometry_materials[i].diffuse.contents = cube_images[i]
71 |
72 |
73 | cube_geometry.materials = cube_geometry_materials
74 |
75 | cube_node = scn.Node.nodeWithGeometry(cube_geometry)
76 | cube_node_inside = scn.Node.nodeWithGeometry(cube_geometry_inside)
77 | cube_node.addChildNode(cube_node_inside)
78 |
79 | cube_action = scn.Action.scaleTo(1.5, 3.0)
80 | cube_reversed_action = scn.Action.scaleTo(1.0, 1.0)
81 | cube_wait_action = scn.Action.waitForDuration(0.8)
82 | cube_y_rot_action1 = scn.Action.rotateBy(0, 0.2, 0, 0.3)
83 | cube_y_rot_action2 = scn.Action.rotateBy(0, -0.4, 0, 0.3)
84 | cube_y_rot_action3 = scn.Action.rotateBy(0, 0.2, 0, 0.3)
85 | cube_sequence = scn.Action.sequence([cube_action, cube_reversed_action, cube_wait_action, cube_y_rot_action1, cube_y_rot_action2, cube_y_rot_action3, cube_wait_action])
86 | cube_forever = scn.Action.repeatActionForever(cube_sequence)
87 |
88 | cube_node.runAction(cube_forever)
89 |
90 | constraint = scn.LookAtConstraint.lookAtConstraintWithTarget(cube_node)
91 | constraint.gimbalLockEnabled = True
92 |
93 | camera = scn.Camera()
94 | camera_node = scn.Node()
95 | camera_node.name = 'camera!'
96 | camera_node.camera = camera
97 | camera_node.position = (-3.0,3.0,3.0)
98 | camera_node.constraints = [constraint]
99 |
100 | lights_node = scn.Node()
101 |
102 | ambient_light = scn.Light()
103 | ambient_light.type = scn.LightTypeAmbient
104 | ambient_light.name = 'ambient light'
105 | ambient_light.color = (.99, 1.0, .86)
106 | ambient_node = scn.Node()
107 | ambient_node.light = ambient_light
108 | lights_node.addChildNode(ambient_node)
109 |
110 | directional_light = scn.Light()
111 | directional_light.type = scn.LightTypeDirectional
112 | directional_light.name = 'directional light'
113 | directional_light.color = 'white'
114 | directional_node = scn.Node()
115 | directional_node.light = directional_light
116 | directional_node.position = camera_node.position
117 | directional_node.constraints = [constraint]
118 |
119 | lights_node.addChildNode(directional_node)
120 |
121 | root_node.addChildNode(camera_node)
122 | root_node.addChildNode(lights_node)
123 | root_node.addChildNode(cube_node)
124 | scene_view.pointOfView = camera_node
125 |
126 |
127 | scn.Transaction.setAnimationDuration(10.0)
128 | directional_light.color = 'yellow'
129 |
130 |
131 | main_view.present(style='fullscreen', hide_title_bar=False)
132 |
133 | if __name__ == '__main__':
134 | main()
135 |
--------------------------------------------------------------------------------
/sceneKit demo/13_physicsDemo-1.py:
--------------------------------------------------------------------------------
1 | """
2 | physics experiments Nr. 1
3 | """
4 |
5 | from objc_util import *
6 | import ctypes
7 | import sceneKit as scn
8 | import ui
9 | import math
10 |
11 |
12 | class Demo:
13 |
14 | @classmethod
15 | def run(cls):
16 | cls().main()
17 |
18 | @on_main_thread
19 | def main(self):
20 | main_view = ui.View()
21 | w, h = ui.get_screen_size()
22 | main_view.frame = (0,0,w,h)
23 | main_view.name = 'physics experiment demo - 1'
24 |
25 | scene_view = scn.View(main_view.frame, superView=main_view)
26 | scene_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleRightMargin
27 | scene_view.allowsCameraControl = True
28 |
29 | scene_view.backgroundColor = 'white'
30 | scene_view.scene = scn.Scene()
31 |
32 | physics_world = scene_view.scene.physicsWorld
33 |
34 | root_node = scene_view.scene.rootNode
35 |
36 | floor_geometry = scn.Floor()
37 | floor_geometry.reflectivity = 0.1
38 | floor_node = scn.Node.nodeWithGeometry(floor_geometry)
39 | root_node.addChildNode(floor_node)
40 |
41 | ball_radius = 0.2
42 | ball_geometry = scn.Sphere(radius=ball_radius)
43 | ball_geometry.firstMaterial.diffuse.contents = (.48, .48, .48)
44 | ball_geometry.firstMaterial.specular.contents = (.88, .88, .88)
45 | ball_node = scn.Node.nodeWithGeometry(ball_geometry)
46 |
47 | rope_seg_nr = 45
48 | rope_length = 5.0
49 | rope_radius = 0.025
50 | rope_seg_length = rope_length/rope_seg_nr
51 |
52 | rope_element_geometry = scn.Capsule(rope_radius, rope_seg_length)
53 | rope_element_geometry.firstMaterial.diffuse.content = (.77, .67, .09)
54 |
55 | rope_nodes = []
56 | for i in range(rope_seg_nr):
57 | rope_nodes.append(scn.Node.nodeWithGeometry(rope_element_geometry))
58 | if i > 0:
59 | rope_nodes[-1].position = (0., -rope_seg_length, 0.)
60 | rope_nodes[-2].addChildNode(rope_nodes[-1])
61 | aConstraint = scn.DistanceConstraint.distanceConstraintWithTarget(rope_nodes[-2])
62 | aConstraint.maximumDistance = rope_seg_length
63 | aConstraint.minimumDistance = rope_seg_length
64 | rope_nodes[-1].constraints = aConstraint
65 | else:
66 | rope_nodes[0].position = (0, rope_length+1.0-rope_seg_length/2, 0)
67 | aConstraint = scn.DistanceConstraint.distanceConstraintWithTarget(root_node)
68 | aConstraint.maximumDistance = rope_length+1.0-rope_seg_length/2
69 | aConstraint.minimumDistance = rope_length+1.0-rope_seg_length/2
70 | rope_nodes[0].constraints = aConstraint
71 |
72 | ball_node.position = (0, -rope_seg_length/2-rope_radius-ball_radius, 0)
73 | aConstraint = scn.DistanceConstraint.distanceConstraintWithTarget(rope_nodes[-1])
74 | aConstraint.maximumDistance = rope_seg_length/2 + rope_radius + ball_radius
75 | aConstraint.minimumDistance = rope_seg_length/2 + rope_radius + ball_radius
76 | ball_node.constraints = aConstraint
77 |
78 | rope_nodes[-1].addChildNode(ball_node)
79 | root_node.addChildNode(rope_nodes[0])
80 |
81 | ball_physicsBody = scn.PhysicsBody.dynamicBody()
82 | ball_physicsBody.physicsShape = scn.PhysicsShape.shapeWithGeometry(ball_geometry)
83 | ball_physicsBody.mass = 5.0
84 | ball_physicsBody.damping = 0.005
85 | ball_physicsBody.angularDamping = 0.00
86 | ball_node.physicsBody = ball_physicsBody
87 |
88 | rope_element_physicsShape = scn.PhysicsShape.shapeWithGeometry(rope_element_geometry)
89 |
90 | for i in range(rope_seg_nr):
91 | rope_nodes[i].physicsBody = scn.PhysicsBody.dynamicBody()
92 | rope_nodes[i].physicsBody.physicsShape = rope_element_physicsShape
93 | rope_nodes[i].physicsBody.mass = 1.1
94 | rope_nodes[i].physicsBody.damping = 0.00
95 | rope_nodes[i].physicsBody.angularDamping = 0.00
96 | if i > 0:
97 | physics_world.addBehavior(scn.PhysicsBallSocketJoint.joint(rope_nodes[i-1].physicsBody, (0, -rope_seg_length/2, 0), rope_nodes[i].physicsBody, (0, rope_seg_length/2, 0)))
98 |
99 | physics_world.addBehavior(scn.PhysicsBallSocketJoint.joint(rope_nodes[0].physicsBody, (0, rope_seg_length/2, 0)))
100 | physics_world.addBehavior(scn.PhysicsBallSocketJoint.joint(rope_nodes[-1].physicsBody, (0, -rope_seg_length/2, 0), ball_node.physicsBody, (0, ball_radius, 0)))
101 |
102 | ball_node.physicsBody.applyForce((45.0, 0.0, 0.0), True)
103 |
104 | camera_node = scn.Node()
105 | camera_node.camera = scn.Camera()
106 | camera_node.position = (0 , rope_length+1.0 ,rope_length)
107 | camera_node.lookAt((0, rope_length/2+1.0, 0))
108 | root_node.addChildNode(camera_node)
109 |
110 | light_node = scn.Node()
111 | light_node.position = (rope_length, rope_length, rope_length)
112 |
113 | light = scn.Light()
114 | light.type = scn.LightTypeDirectional
115 | light.castsShadow = True
116 | light.shadowSampleCount = 32
117 | light.color = (.99, 1.0, .86)
118 | light_node.light = light
119 | light_node.lookAt((0, rope_length/2+1.0, 0))
120 | root_node.addChildNode(light_node)
121 |
122 | main_view.present(style='fullscreen', hide_title_bar=False)
123 |
124 | Demo.run()
125 |
--------------------------------------------------------------------------------
/sceneKit/sceneKitAudio.py:
--------------------------------------------------------------------------------
1 | '''audio modul, to be included in sceneKit'''
2 |
3 | from objc_util import nsurl
4 | import os
5 |
6 | import sceneKit
7 | from .sceneKitEnv import *
8 | from .sceneKitNode import *
9 |
10 | class AudioSource(CInst):
11 | def __init__(self, url=None, fileName=None, named=None, ID=None):
12 | if named is not None: fileName=named
13 | if url is not None:
14 | self.ID = SCNAudioSource.alloc().initWithURL_(nsurl(self.convertURL(url)))
15 | elif fileName is not None:
16 | self.ID = SCNAudioSource.audioSourceNamed_(fileName)
17 | elif ID is not None:
18 | self.ID = ID
19 | else:
20 | self.ID = SCNAudioSource.alloc()
21 |
22 | # for Pythonista built-in sounds
23 | def convertURL(self, url):
24 | pythonistaSoundDir = os.path.abspath(os.path.join(os.__file__, '../../../..')+'/Media/Sounds/')
25 | soundFileExtention = '.caf'
26 | (root, ext) = os.path.splitext(url)
27 | if len(ext) == 0:
28 | root = root.replace(':', '/')
29 | url = pythonistaSoundDir+'/'+root+soundFileExtention
30 | return url
31 |
32 | @classmethod
33 | def audioSourceNamed(cls, fileName=None, named=None):
34 | return cls(fileName=fileName, named=named)
35 |
36 | def initWithFileNamed(self, name=None, fileNamed=None):
37 | if fileNamed is not None: name=fileNamed
38 | self.ID.initWithFileNamed_(name)
39 | initWithFile = initWithFileNamed
40 |
41 | def initWithURL(self, url=None):
42 | return self.ID.initWithURL_(nsurl(self.convertURL(url)))
43 |
44 | def setPositional(self, aBool):
45 | self.ID.setPositional_(aBool)
46 | def isPositional(self):
47 | return self.ID.positional()
48 | positional = property(isPositional, setPositional)
49 |
50 | def load(self):
51 | self.ID.load()
52 |
53 | def setVolume(self, aVolume):
54 | self.ID.setVolume_(aVolume)
55 | def getVolume(self):
56 | return self.ID.volume()
57 | volume = property(getVolume, setVolume)
58 |
59 | def setRate(self, aRate):
60 | self.ID.setRate_(aRate)
61 | def getRate(self):
62 | return self.ID.rate()
63 | rate = property(getRate, setRate)
64 |
65 | def setReverbBlend(self, aReverbBlend):
66 | self.ID.setReverbBlend_(aReverbBlend)
67 | def getReverbBlend(self):
68 | return self.ID.reverbBlend()
69 | reverbBlend = property(getReverbBlend, setReverbBlend)
70 |
71 | def setLoops(self, aBool):
72 | self.ID.setLoops_(aBool)
73 | def getLoops(self):
74 | return self.ID.loops()
75 | loops = property(getLoops, setLoops)
76 |
77 | def setShouldStream(self, aBool):
78 | self.ID.setShouldStream_(aBool)
79 | def getShouldStream(self):
80 | return self.ID.shouldStream()
81 | shouldStream = property(getShouldStream, setShouldStream)
82 |
83 | class AudioPlayer(CInst):
84 | def __init__(self, source=None, audioNode=None, ID=None):
85 | if source is not None:
86 | self.ID = SCNAudioPlayer.audioPlayerWithSource_(source.ID)
87 | elif audioNode is not None:
88 | self.ID = SCNAudioPlayer.audioPlayerWithAVAudioNode_(audioNode)
89 | elif ID is not None:
90 | self.ID = ID
91 | else:
92 | self.ID = SCNAudioPlayer.alloc()
93 |
94 | @classmethod
95 | def audioPlayerWithSource(cls, source=None):
96 | return cls(source=source)
97 |
98 | @classmethod
99 | def audioPlayerWithAVAudioNode(cls, audioNode=None, AVAudioNode=None, avAudioNode=None):
100 | if AVAudioNode is not None: audioNode = AVAudioNode
101 | if avAudioNode is not none: audioNode = avAudioNode
102 | return cls(audioNode=audioNode)
103 |
104 | def initWithSource(self, source=None):
105 | self.ID.initWithSource_(source.ID)
106 |
107 | def initWithAVAudioNode(self, audioNode=None, AVAudioNode=None, avAudioNode=None):
108 | if AVAudioNode is not None: audioNode = AVAudioNode
109 | if avAudioNode is not None: audioNode = avAudioNode
110 | self.ID.initWithAVAudioNode_(audioNode)
111 |
112 | def getAudioSource(self):
113 | return sceneKit.AudioSource.outof(self.ID.audioSource())
114 | audioSource = property(getAudioSource, None)
115 |
116 | def getAudioNode(self):
117 | return self.ID.audioNode()
118 | audioNode = property(getAudioNode, None)
119 |
120 | def setWillStartPlayback(self, aBlock):
121 | self.audioSourceWillStartPlayback = AudioSourceWillStartStopPlaybackBlock(aBlock)
122 | self.ID.setWillStartPlayback_(self.audioSourceWillStartPlayback.blockCode)
123 | def getWillStartPlayback(self):
124 | return self.audioSourceWillStartPlayback.pCode
125 | willStartPlayback = property(getWillStartPlayback, setWillStartPlayback)
126 |
127 | def setDidFinishPlayback(self, aBlock):
128 | self.audioSourceDidFinishPlayback = AudioSourceWillStartStopPlaybackBlock(aBlock)
129 | self.ID.setDidFinishPlayback_(self.audioSourceDidFinishPlayback.blockCode)
130 | def getDidFinishPlayback(self):
131 | return self.audioSourceDidFinishPlayback.pCode
132 | didFinishPlayback = property(getDidFinishPlayback, setDidFinishPlayback)
133 |
134 |
135 | class AudioSourceWillStartStopPlaybackBlock:
136 | def __init__(self, block):
137 | self.blockCode = ObjCBlock(self.blockInterface, restype=None, argtypes=[c_void_p])
138 | self.pCode = block
139 |
140 | def blockInterface(self, _cmd):
141 | self.pCode()
142 |
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/hud.py:
--------------------------------------------------------------------------------
1 | '''Cube puzzles - five typical 3x3x3 3D puzzles.
2 |
3 | (c) Peter Ulbrich, 2019
4 |
5 | a demo script for the sceneKit wrapper package for Pythonista
6 |
7 | See the README.md for details.
8 |
9 | '''
10 |
11 | from objc_util import *
12 | import ui
13 | from markdown2 import markdown
14 |
15 | from data import *
16 | import main
17 | import selector
18 |
19 | TEMPLATE = '''
20 |
21 |
22 |
23 |
24 |
25 | Preview
26 |
33 |
34 | {{CONTENT}}
35 |
36 | '''
37 |
38 | class Hud:
39 | def __init__(self):
40 | self.views = []
41 |
42 | _, _, w, h = data.main_view.frame
43 |
44 | self.close_button = ui.Button()
45 | self.close_button.action = self.closeClick
46 | self.close_button.image = ui.Image.named('iob:close_round_24')
47 | data.main_view.add_subview(self.close_button)
48 | self.views.append(self.close_button)
49 |
50 | self.back_button = ui.Button()
51 | self.back_button.action = self.backClick
52 | self.back_button.image = ui.Image.named('iob:arrow_left_a_24')
53 | self.back_button.hidden = True
54 | data.main_view.add_subview(self.back_button)
55 | self.views.append(self.back_button)
56 |
57 | self.restart_button = ui.Button()
58 | self.restart_button.action = self.restartClick
59 | self.restart_button.image = ui.Image.named('iob:refresh_24')
60 | self.restart_button.hidden = True
61 | data.main_view.add_subview(self.restart_button)
62 | self.views.append(self.restart_button)
63 |
64 | self.hint_button = ui.Button()
65 | self.hint_button.action = self.hintClick
66 | self.hint_button.image = ui.Image.named('iob:help_24')
67 | data.main_view.add_subview(self.hint_button)
68 | self.views.append(self.hint_button)
69 |
70 | self.alert_label = ui.Label()
71 | self.alert_label.background_color = 'transparent'
72 | self.alert_label.text = ''
73 | self.alert_label.font = ('', 96)
74 | self.alert_label.alignment = ui.ALIGN_CENTER
75 | self.alert_label.hidden = True
76 | self.alert_label.alpha = 0.0
77 | data.main_view.add_subview(self.alert_label)
78 | self.views.append(self.alert_label)
79 |
80 | self.status_label = ui.Label()
81 | self.status_label.background_color = 'transparent'
82 | self.status_label.alignment = ui.ALIGN_CENTER
83 | self.status_label.line_break_mode = ui.LB_CHAR_WRAP
84 | self.status_label.number_of_lines = 2
85 | self.status_label.text = ''
86 | self.status_label.font = ('', 8)
87 | data.main_view.add_subview(self.status_label)
88 | self.views.append(self.status_label)
89 |
90 | self.title_label = ui.Label()
91 | self.title_label.background_color = 'transparent'
92 | self.title_label.alignment = ui.ALIGN_CENTER
93 | self.title_label.number_of_lines = 1
94 | self.title_label.text = ''
95 | self.title_label.font = ('', 22)
96 | data.main_view.add_subview(self.title_label)
97 | self.views.append(self.title_label)
98 |
99 | @on_main_thread
100 | def show_alert(self, message_symbol):
101 | def animation_fade_in():
102 | self.alert_label.alpha = 1.0
103 | def animation_fade_out():
104 | self.alert_label.alpha = 0.0
105 | def animate_fade_out():
106 | ui.animate(animation_fade_out, duration=2.0)
107 | def hide_alert():
108 | self.alert_label.hidden = True
109 |
110 | ui.cancel_delays()
111 | self.alert_label.text = message_symbol
112 | self.alert_label.hidden = False
113 | ui.animate(animation_fade_in, duration=0.8)
114 | ui.delay(animate_fade_out, 2)
115 | ui.delay(hide_alert, 5)
116 |
117 | def set_status(self, text):
118 | self.status_label.text = text
119 |
120 | def set_title(self, text):
121 | self.title_label.text = text
122 |
123 | def bring_to_front(self):
124 | for aView in self.views:
125 | aView.bring_to_front()
126 |
127 | @on_main_thread
128 | def hintClick(self, sender):
129 | data.active_stage.hint()
130 |
131 | @on_main_thread
132 | def restartClick(self, sender):
133 | data.active_stage.restart()
134 |
135 | @on_main_thread
136 | def backClick(self, sender):
137 | data.hud_layer.set_title('')
138 | main.MainViewController.next_stage(selector.SelectorStage())
139 |
140 | @on_main_thread
141 | def closeClick(self, sender):
142 | data.main_view.close()
143 | ui.delay(self.exit, 2.0)
144 |
145 | def exit(self):
146 | raise SystemExit()
147 |
148 | def show_generic_help(self):
149 | with open('README.md', 'r') as fp:
150 | text = fp.read()
151 | converted = markdown(text)
152 | html = TEMPLATE.replace('{{CONTENT}}', converted)
153 | webview = ui.WebView(name='Read me')
154 | webview.load_html(html)
155 | webview.present()
156 |
157 | def layout(self):
158 | _, _, w, h = data.main_view.frame
159 | self.close_button.frame = (0, 12, 40, 40)
160 | self.back_button.frame = (w - 40, 12, 40, 40)
161 | self.restart_button.frame = (0, h - 12 - 40, 40, 40)
162 | self.hint_button.frame = (w - 40, h - 12 - 40, 40, 40)
163 | self.title_label.frame = ((w - w // 2) // 2, 12, w // 2, 40)
164 | self.status_label.frame = ((w - w // 2) // 2, h - 12 - 40, w // 2, 40)
165 | self.alert_label.frame = ((w - 160) / 2, (h - 160) / 2, 160, 160)
166 |
167 |
168 | if __name__ == '__main__':
169 | main.MainViewController.run()
170 |
171 |
--------------------------------------------------------------------------------
/sceneKit demo/09_shapeDemo.py:
--------------------------------------------------------------------------------
1 | '''
2 | shape: A geometry based on a two-dimensional path, optionally extruded to create a three-dimensional object
3 | '''
4 | from objc_util import *
5 | import sceneKit as scn
6 | import ui
7 | from math import *
8 |
9 | w, h = ui.get_screen_size()
10 | title_bar = 88+44 # you should get these programatically in a real app
11 | if w > h:
12 | image_frame = (0, 0, w/2, h-title_bar)
13 | scene_frame = (w/2, 0, w/2, h)
14 | box_bubble = 1.4
15 | else:
16 | image_frame = (0, 0, w, (h-title_bar)/2)
17 | scene_frame = (0, (h-title_bar)/2, w, (h+title_bar)/2)
18 | box_bubble = 1.0
19 |
20 | image_size = CGSize(image_frame[2], image_frame[3])
21 | image_center = CGPoint(image_size.width/2, image_size.height/2)
22 | leaves = 20
23 | r0 = min(image_size.width, image_size.height)/2*0.1
24 | r1 = min(image_size.width, image_size.height)/2*0.8
25 | c1 = r0+(r1-r0)*0.5
26 | c2 = r0+(r1-r0)*0.95
27 | dalpha = 2*pi/leaves/2
28 |
29 | path = ui.Path()
30 | for i in range(leaves):
31 | alpha = i*2*pi/leaves
32 | x0, y0 = image_center.x+r0*cos(alpha), image_center.y+r0*sin(alpha)
33 | x1, y1 = image_center.x+r1*cos(alpha), image_center.y+r1*sin(alpha)
34 | cx11, cy11 = image_center.x+c2*cos(alpha+dalpha), image_center.y+c2*sin(alpha+dalpha)
35 | cx21, cy21 = image_center.x+c1*cos(alpha+dalpha), image_center.y+c1*sin(alpha+dalpha)
36 | cx12, cy12 = image_center.x+c1*cos(alpha+dalpha), image_center.y+c1*sin(alpha+dalpha)
37 | cx22, cy22 = image_center.x+c2*cos(alpha-dalpha), image_center.y+c2*sin(alpha-dalpha)
38 | path.move_to(x0, y0)
39 | path.add_curve(x1, y1, cx11, cy11, cx21, cy21)
40 | path.add_curve(x0, y0, cx12, cy12, cx22, cy22)
41 |
42 | x0, y0 = image_center.x+r0*cos(0), image_center.y+r0*sin(0)
43 | path.move_to(x0, y0)
44 | path.add_arc(image_center.x, image_center.y, r0, 0, 2*pi)
45 | path.close()
46 |
47 | with ui.ImageContext(image_size.width, image_size.height) as ctx:
48 | ui.set_color('red')
49 | path.stroke()
50 | img = ctx.get_image()
51 |
52 | @on_main_thread
53 | def demo():
54 | main_view = ui.View()
55 | main_view.frame = (0,0,w,h)
56 | main_view.name = 'shapeDemo'
57 | main_view.background_color = 'white'
58 |
59 | image_view = ui.ImageView()
60 | image_view.frame = image_frame
61 | image_view.background_color = 'white'
62 | image_view.content_mode = ui.CONTENT_CENTER
63 | image_view.image = img
64 |
65 | main_view.add_subview(image_view)
66 |
67 | scene_view = scn.View(scene_frame, superView=main_view)
68 | scene_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleRightMargin
69 | scene_view.antialiasingMode = scn.AntialiasingMode.Multisampling16X
70 | scene_view.allowsCameraControl = True
71 | scene_view.backgroundColor = 'white'
72 | scene_view.scene = scn.Scene()
73 |
74 | root_node = scene_view.scene.rootNode
75 |
76 | shape = scn.Shape.shapeWithPath(path, 40)
77 | shape.chamferRadius = 8
78 | shape.firstMaterial.contents = 'yellow'
79 |
80 | shape_node = scn.Node.nodeWithGeometry(shape)
81 | shape_node.castsShadow = True
82 | bbox_min, bbox_max = shape.boundingBox
83 | shape_width = bbox_max.x - bbox_min.x
84 | shape_height = bbox_max.y - bbox_min.y
85 | shape_tr = list(scn.Matrix4Identity)
86 | shape_tr[13] = bbox_min.y+0.5*shape_height
87 | shape_node.pivot = shape_tr
88 | shape_node.position = (-bbox_min.x-0.5*shape_width, 0, 0)
89 |
90 | shape_container = scn.Node()
91 | shape_container.addChildNode(shape_node)
92 |
93 | scale = image_size.width/shape_width
94 | shape_container.scale = (scale, scale, 1)
95 | shape_width *= scale
96 | shape_height *= scale
97 |
98 | shape_container.position = (0, 0.2*shape_height, 0.5*shape_height)
99 | shape_container.rotation = (0, 1, 0, pi) # compensates for the differen coordinate systems of drawing vs. scene
100 |
101 | rotate_action = scn.Action.rotateBy(0, 0, 2*pi, 10)
102 | rotate_action.timingMode = scn.ActionTimingMode.EaseInEaseOut
103 | invers_action = rotate_action.reversedAction()
104 | combined_action = scn.Action.sequence([rotate_action, invers_action])
105 | shape_container.runAction(scn.Action.repeatActionForever(combined_action))
106 |
107 | box = scn.Box(width=2*shape_width, height=4, length=1.6*shape_width, chamferRadius=1)
108 | box.firstMaterial.contents = (.76, .91, 1.0)
109 |
110 | boxBoxMin, boxBoxMax = box.boundingBox
111 | box.boundingBox = ((box_bubble*boxBoxMin.x, boxBoxMin.y, boxBoxMin.z), (box_bubble*boxBoxMax.x, boxBoxMax.y, boxBoxMax.z))
112 | box_node = scn.Node.nodeWithGeometry(box)
113 | box_node.rotation = (1, 0, 0, pi/4)
114 |
115 | root_node.addChildNode(box_node)
116 | root_node.addChildNode(shape_container)
117 |
118 | constraint = scn.LookAtConstraint.lookAtConstraintWithTarget(shape_container)
119 | constraint.gimbalLockEnabled = True
120 |
121 | light_node = scn.Node()
122 | light_z = boxBoxMax.z + 0.0*boxBoxMax.z
123 | light_y = (shape_container.position.y+0.5*shape_height/2)/shape_container.position.z*light_z
124 | light_node.position = (20, light_y, light_z)
125 | light_node.constraints = [constraint]
126 | light = scn.Light()
127 | light.type = 'spot'
128 | light.spotOuterAngle = 90
129 | light.castsShadow = True
130 | light.zFar = 1500
131 | light.shadowSampleCount = 16
132 | light.color = 'white'
133 | light_node.light = light
134 | root_node.addChildNode(light_node)
135 |
136 | ambient_light = scn.Light()
137 | ambient_light.type = scn.LightTypeAmbient
138 | ambient_light.color = (.41, .32, .0)
139 | ambient_node = scn.Node()
140 | ambient_node.light = ambient_light
141 | root_node.addChildNode(ambient_node)
142 |
143 | main_view.present(style='fullscreen', hide_title_bar=False)
144 |
145 | demo()
146 |
--------------------------------------------------------------------------------
/sceneKit demo/14_physicsDemo-2.py:
--------------------------------------------------------------------------------
1 | """
2 | physics experiments Nr. 2
3 | """
4 |
5 | from objc_util import *
6 | import ctypes
7 | import sceneKit as scn
8 | import ui
9 | import math
10 | import random
11 | from scene import *
12 |
13 | class Counter(Scene):
14 | def setup(self):
15 | self.background_color = 'midnightblue'
16 | self.counter = -1
17 | self.counter_node = LabelNode(str(self.counter))
18 | self.counter_node.position = (self.size.w/2, self.size.h/2)
19 | self.add_child(self.counter_node)
20 |
21 | def update(self):
22 | self.counter_node.text = str(self.counter)
23 |
24 | class Demo:
25 |
26 | @classmethod
27 | def run(cls):
28 | cls().main()
29 |
30 | @on_main_thread
31 | def main(self):
32 | main_view = ui.View()
33 | w, h = ui.get_screen_size()
34 | main_view.frame = (0,0,w,h)
35 | main_view.name = 'physics experiment demo - 2'
36 |
37 | scene_view = scn.View(main_view.frame, superView=main_view)
38 | scene_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleRightMargin
39 | scene_view.allowsCameraControl = True
40 |
41 | self.counter_scene = Counter()
42 | counter_view = SceneView()
43 | counter_view.frame = (0., 0., 100., 50.)
44 | counter_view.scene = self.counter_scene
45 | main_view.add_subview(counter_view)
46 |
47 | # scene_view.debugOptions = scn.DebugOption.ShowBoundingBoxes | scn.DebugOption.ShowCameras | scn.DebugOption.ShowLightInfluences
48 |
49 | # scene_view.debugOptions = scn.DebugOption.ShowPhysicsShapes
50 |
51 | scene_view.backgroundColor = 'white'
52 | scene_view.scene = scn.Scene()
53 |
54 | physics_world = scene_view.scene.physicsWorld
55 | physics_world.contactDelegate = self
56 |
57 | root_node = scene_view.scene.rootNode
58 |
59 | box_size = 10
60 | box = scn.Box(box_size, box_size, box_size, 0.0)
61 | box.firstMaterial.diffuse.contents = (.86, .86, .86)
62 | box.firstMaterial.transparency = 0.15
63 | box.firstMaterial.cullMode = scn.CullMode.Front
64 | box_node = scn.Node.nodeWithGeometry(box)
65 | root_node.addChildNode(box_node)
66 |
67 | self.particle_number = 25
68 | particle_max_r = box_size/20
69 | particle_colors = ['black', 'blue', 'green', 'pink', 'yellow', 'red', 'cyan', 'gray', 'magenta', 'brown', 'crimson', 'gold', 'indigo', 'olive']
70 | particles_node = scn.Node()
71 | particles_node.physicsField = scn.PhysicsField.electricField()
72 | particles_node.physicsField.strength = 5.0
73 |
74 | root_node.addChildNode(particles_node)
75 | for i in range(self.particle_number):
76 | r = random.uniform(0.35*particle_max_r, particle_max_r)
77 | particle = scn.Sphere(r)
78 | particle.firstMaterial.diffuse.contents = random.choice(particle_colors)
79 | particle.firstMaterial.specular.contents = (.86, .94, 1.0, 0.8)
80 | particle_node = scn.Node.nodeWithGeometry(particle)
81 | particle_node.position = (random.uniform(-(box_size/2-particle_max_r)*0.95, (box_size/2-particle_max_r)*0.95), random.uniform(-(box_size/2-particle_max_r)*0.95, (box_size/2-particle_max_r)*0.95), random.uniform(-(box_size/2-particle_max_r)*0.95, (box_size/2-particle_max_r)*0.95))
82 | particle_node.physicsBody = scn.PhysicsBody.dynamicBody()
83 | particle_node.physicsBody.mass = 1.2*r
84 | particle_node.physicsBody.charge = random.uniform(-10*r, +0*r)
85 | particle_node.physicsBody.restitution = 1.0
86 | particle_node.physicsBody.damping = 0.0
87 | particle_node.physicsBody.angularDamping = 0.0
88 | particle_node.physicsBody.continuousCollisionDetectionThreshold = 1*r
89 | particle_node.physicsBody.affectedByGravity = False
90 | particle_node.physicsBody.contactTestBitMask = scn.PhysicsCollisionCategory.Default.value
91 | particle_node.physicsBody.velocity = (random.uniform(-box_size, box_size), random.uniform(-box_size, box_size), random.uniform(-box_size, box_size))
92 | particles_node.addChildNode(particle_node)
93 |
94 | box_nodes = scn.Node()
95 | d = box_size/2
96 | for pos in [(d,0,0), (-d,0,0), (0,d,0), (0,-d,0), (0,0,d), (0,0,-d)]:
97 | aNode = scn.Node()
98 | aNode.position = pos
99 | aNode.lookAt((0,0,0))
100 | aNode.physicsBody = scn.PhysicsBody.staticBody()
101 | aNode.physicsBody.physicsShape = scn.PhysicsShape.shapeWithGeometry(scn.Plane(box_size, box_size))
102 | box_nodes.addChildNode(aNode)
103 | root_node.addChildNode(box_nodes)
104 |
105 | constraint = scn.LookAtConstraint.lookAtConstraintWithTarget(root_node)
106 | constraint.gimbalLockEnabled = True
107 |
108 | camera_node = scn.Node()
109 | camera_node.camera = scn.Camera()
110 | camera_node.position = (-2.5*box_size, 1.5*box_size, 2*box_size)
111 | camera_node.constraints = constraint
112 | root_node.addChildNode(camera_node)
113 |
114 | light_node = scn.Node()
115 | light_node.position = (box_size, box_size, box_size)
116 | light = scn.Light()
117 | light.type = scn.LightTypeDirectional
118 | light.castsShadow = True
119 | light.shadowSampleCount = 32
120 | light.color = (.95, 1.0, .98)
121 | light_node.light = light
122 | light_node.constraints = constraint
123 | root_node.addChildNode(light_node)
124 |
125 | main_view.present(style='fullscreen', hide_title_bar=False)
126 |
127 | def didEndContact(self, aWorld, aContact):
128 | big, small = (aContact.nodeA, aContact.nodeB) if aContact.nodeA.physicsBody.mass > aContact.nodeB.physicsBody.mass else (aContact.nodeB, aContact.nodeA)
129 | m1, m2 = big.physicsBody.mass, small.physicsBody.mass
130 | v1, v2 = big.physicsBody.velocity, small.physicsBody.velocity
131 | m = m1 + m2
132 | big.physicsBody.mass = m
133 | big.physicsBody.charge += small.physicsBody.charge
134 | big.geometry.radius += 0.4*math.log1p(small.geometry.radius)
135 | big.physicsBody.physicsShape = scn.PhysicsShape.shapeWithGeometry(big.geometry)
136 | big.physicsBody.continuousCollisionDetectionThreshold = 1*big.geometry.radius
137 | big.physicsBody.velocity = ((m1*v1.x+m2*v2.x)/(m), (m1*v1.y+m2*v2.y)/(m), (m1*v1.z+m2*v2.z)/(m))
138 | small.removeFromParentNode()
139 | self.particle_number -= 1
140 | self.counter_scene.counter = self.particle_number
141 |
142 | Demo.run()
143 |
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/piece.py:
--------------------------------------------------------------------------------
1 | '''Cube puzzles - five typical 3x3x3 3D puzzles.
2 |
3 | (c) Peter Ulbrich, 2019
4 |
5 | a demo script for the sceneKit wrapper package for Pythonista
6 |
7 | See the README.md for details.
8 |
9 | '''
10 |
11 | from enum import Enum, auto
12 | import math
13 | import weakref
14 |
15 | import sceneKit as scn
16 |
17 | from data import *
18 | import setup
19 |
20 | class Piece_location(Enum):
21 | Drawer = auto()
22 | Tray = auto()
23 | Puzzle = auto()
24 | Solver = auto()
25 |
26 | class Piece_set:
27 | def __init__(self, puzzle_variant):
28 | self.pieces = setup.setup_pieces(self, puzzle_variant)
29 | self.puzzle_variant = puzzle_variant
30 | self.previous_piece_in_tray = []
31 |
32 | def solved(self):
33 | unplaced_pieces = sum(1 for aPiece in self.pieces if aPiece.location != Piece_location.Puzzle)
34 | return unplaced_pieces == 0
35 |
36 | def reset_pieces(self):
37 | for aPiece in self.pieces:
38 | aPiece.reset()
39 |
40 | class Piece:
41 | def __init__(self, piece_set, name=None, handle_node=None):
42 | self.piece_set = weakref.ref(piece_set)()
43 | self.name = name
44 | self.drawer_handle_node = handle_node
45 | self.location = Piece_location.Drawer
46 | self.piece_handle_node = handle_node.clone()
47 |
48 | self.tray_handle_node = scn.Node()
49 | self.tray_handle_node.addChildNode(self.piece_handle_node)
50 | self.drawer_view = None
51 | self.tray_handle_node.opacity = 0.
52 |
53 | def __str__(self):
54 | return 'piece ' + self.name + ' ' + str(self.location)
55 |
56 | def reset(self):
57 | self.location = Piece_location.Drawer
58 | self.tray_handle_node.opacity = 0.
59 | self.drawer_handle_node.opacity = 1.0
60 | self.piece_handle_node.eulerAngles = (0, 0, 0)
61 | self.piece_handle_node.position = (0, 0, 0)
62 |
63 | def from_drawer_to_tray(self):
64 | self.drawer_handle_node.opacity = 0.5
65 | self.tray_handle_node.opacity = 1.0
66 | self.piece_handle_node.eulerAngles = (0, 0, 0)
67 | self.piece_handle_node.position = (0, 0, 0)
68 | self.location = Piece_location.Tray
69 |
70 | def from_tray_to_drawer(self):
71 | self.drawer_handle_node.opacity = 1.0
72 | self.tray_handle_node.opacity = 0.0
73 | self.location = Piece_location.Drawer
74 |
75 | def rotate(self, axis, constraint=True, zero_align=False):
76 | thetaOver2 = math.pi / 4
77 | if axis == 'x' and data.horizontal or axis == 'y' and not data.horizontal:
78 | new_rotation = (math.sin(thetaOver2), 0, 0, math.cos(thetaOver2))
79 | elif axis == 'y' and data.horizontal or axis == 'x' and not data.horizontal:
80 | if not data.horizontal: thetaOver2 *= -1
81 | new_rotation = (0, math.sin(thetaOver2), 0, math.cos(thetaOver2))
82 | elif axis == 'z':
83 | new_rotation = (0, 0, math.sin(thetaOver2), math.cos(thetaOver2))
84 | self.piece_handle_node.rotateBy(new_rotation, self.piece_handle_node.worldPosition)
85 | if constraint:
86 | self.constraint(zero_align)
87 |
88 | def shift(self, axis, distance):
89 | pos = self.piece_handle_node.position
90 | if axis == 'x': new_shift = scn.Vector3(distance, 0, 0)
91 | elif axis == 'y': new_shift = scn.Vector3(0, distance, 0)
92 | elif axis == 'z': new_shift = scn.Vector3(0, 0, distance)
93 | self.piece_handle_node.position = (pos.x + new_shift.x, pos.y + new_shift.y, pos.z + new_shift.z)
94 | self.constraint()
95 |
96 | def constraint(self, zero_align=False):
97 | pos = self.piece_handle_node.position
98 | min_box, max_box = self.tray_handle_node.boundingBox
99 | min_box = scn.Vector3(round(min_box.x + 0.5), round(min_box.y + 0.5), round(min_box.z + 0.5))
100 | max_box = scn.Vector3(round(max_box.x - 0.5), round(max_box.y - 0.5), round(max_box.z - 0.5))
101 | new_pos = [round(pos.x), round(pos.y), round(pos.z)]
102 |
103 | if not zero_align:
104 | if min_box.x < 0: new_pos[0] = pos.x - min_box.x
105 | if max_box.x > 2: new_pos[0] = pos.x - (max_box.x - 2)
106 | if min_box.y < -2: new_pos[1] = pos.y - (min_box.y + 2)
107 | if max_box.y > 0: new_pos[1] = pos.y - (max_box.y - 0)
108 | if min_box.z < -2: new_pos[2] = pos.z - (min_box.z + 2)
109 | if max_box.z > 0: new_pos[2] = pos.z - max_box.z
110 | else:
111 | new_pos[0] = pos.x - min_box.x
112 | new_pos[1] = pos.y - min_box.y
113 | new_pos[2] = pos.z - max_box.z
114 |
115 | self.piece_handle_node.position = new_pos
116 |
117 | def drop(self):
118 | tray_handle = self.tray_handle_node.parentNode
119 | starting_positions = [self.piece_handle_node.convertPosition(aNode.position, toNode=tray_handle) for aNode in self.piece_handle_node.childNodes]
120 |
121 | pieces_resting_set = set()
122 | for aPiece in (_ for _ in self.piece_set.pieces if _.location == Piece_location.Puzzle):
123 | for aNode in aPiece.piece_handle_node.childNodes:
124 | aPos = aPiece.piece_handle_node.convertPosition(aNode.position, toNode=tray_handle)
125 | pieces_resting_set.add(scn.Vector3(round(aPos.x), round(aPos.y), round(aPos.z)))
126 |
127 | for downShift in range(16):
128 | new_positions = {scn.Vector3(round(aStartPos.x), round(aStartPos.y - downShift), round(aStartPos.z)) for aStartPos in starting_positions}
129 | break_value = min(aPos.y for aPos in new_positions)
130 |
131 | if break_value < -6: break
132 | if not new_positions.isdisjoint(pieces_resting_set): break
133 |
134 | downShift -= 1
135 | current_position = self.piece_handle_node.position
136 |
137 | self.piece_handle_node.position = scn.Vector3(current_position.x, current_position.y - downShift, current_position.z)
138 |
139 | top_value = max(aPos.y for aPos in new_positions) + 1
140 | if top_value <= -4:
141 | self.location = Piece_location.Puzzle
142 | self.piece_set.previous_piece_in_tray.append(self)
143 | return True
144 | else: return False
145 |
146 | def lift(self):
147 | pos = self.piece_handle_node.position
148 | self.piece_handle_node.position = (pos.x, 0, pos.z)
149 | self.constraint()
150 | self.location = Piece_location.Tray
151 |
152 |
153 | if __name__ == '__main__':
154 | import main
155 | main.MainViewController.run()
156 |
--------------------------------------------------------------------------------
/sceneKit demo/06_photoCubeWithRendererDelegate.py:
--------------------------------------------------------------------------------
1 | """
2 | based on:
3 | For the fun, a Photos cube by cvp at https://forum.omz-software.com/topic/3922/for-the-fun-a-photos-cube
4 |
5 | includes a renderer delegate demo
6 | """
7 | import random
8 | import math
9 | from objc_util import *
10 | import sceneKit as scn
11 | import ui
12 |
13 | cube_image_names = ['emj:Airplane', 'emj:Cat_Face', 'emj:Anchor', 'emj:Basketball', 'emj:Aubergine', 'emj:Baby_Chick_1', 'emj:Alarm_Clock', 'emj:Ambulance', 'emj:American_Football', 'emj:Artist_Palette', 'emj:Athletic_Shoe', 'emj:Baby_Chick_2', 'emj:Baby_Chick_3', 'emj:Bactrian_Camel', 'emj:Balloon', 'emj:Banana', 'emj:Baseball', 'emj:Bird', 'emj:Blue_Book']
14 |
15 | cube_images_orig = [ui.Image.named(cube_image_names[i]) for i in range(len(cube_image_names))]
16 |
17 | (image_width_orig, image_height_orig) = cube_images_orig[0].size
18 | (image_width, image_height) = (image_width_orig * 1.5, image_height_orig * 1.5)
19 | i_x = (image_width - image_width_orig)/2
20 | i_y = (image_height - image_height_orig)/2
21 |
22 | cube_images = []
23 | for i in range(len(cube_image_names)):
24 | with ui.ImageContext(image_width, image_height) as ctx:
25 | cube_images_orig[i].draw(i_x, i_y, image_width_orig, image_height_orig)
26 | cube_images.append(ctx.get_image())
27 |
28 | with ui.ImageContext(image_width, image_height) as ctx:
29 | rrect = ui.Path.rounded_rect(0, 0, image_width, image_height, 2)
30 | rrect.line_width = 4
31 | ui.set_color((.91, .91, .91))
32 | rrect.fill()
33 | ui.set_color('black')
34 | rrect.stroke()
35 | d = 2
36 | rrect = ui.Path.rounded_rect(0+d, 0+d, image_width-2*d, image_height-2*d, 2)
37 | rrect.line_width = 2
38 | ui.set_color((.6, .6, .6))
39 | rrect.stroke()
40 | inside_image = ctx.get_image()
41 |
42 | tile_image = ui.Image.named('plf:Tile_Water')
43 | tile_number = 15
44 | tile_factor = scn.Matrix4(tile_number, 0.0, 0.0, 0.0, 0.0, tile_number, 0.0, 0.0, 0.0, 0.0, tile_number, 0.0, 0.0, 0.0, 0.0, 1.0)
45 |
46 | class RendererDelegateForMyScene:
47 | # delete unnecessary methods for performance improvement
48 | def update(self, view, atTime):
49 | tick = int(atTime*10) % 50
50 | if tick == 0:
51 | cube_node = view.scene.rootNode.childNodeWithName('cube node')
52 | cube_geometry_materials = cube_node.geometry.materials
53 | for i in range(6):
54 | cube_geometry_materials[i].diffuse.contents = cube_images[random.randrange(len(cube_image_names))]
55 | cube_node.geometry.materials = cube_geometry_materials
56 | else:
57 | pass
58 |
59 | def didApplyAnimations(self, view, atTime):
60 | pass
61 |
62 | def didSimulatePhysics(self, view, atTime):
63 | pass
64 |
65 | def didApplyConstraints(self, view, atTime):
66 | pass
67 |
68 | def willRenderScene(self, view, scene, atTime):
69 | pass
70 |
71 | def didRenderScene(self, view, scene, atTime):
72 | pass
73 |
74 | @on_main_thread
75 | def main():
76 | main_view = ui.View()
77 | w, h = ui.get_screen_size()
78 | main_view.frame = (0,0,w,h)
79 | main_view.name = 'Photos cube'
80 |
81 | scene_view = scn.View(main_view.frame, superView=main_view)
82 | scene_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleRightMargin
83 | scene_view.antialiasingMode = scn.AntialiasingMode.Multisampling16X
84 |
85 |
86 | scene_view.allowsCameraControl = True
87 |
88 | scene_view.backgroundColor = 'white'
89 |
90 | scene_view.delegate = RendererDelegateForMyScene()
91 |
92 | scene = scn.Scene()
93 | scene_view.scene = scene
94 |
95 | scn.Transaction.disableActions = True
96 |
97 | scene.background.contents = tile_image
98 | scene.background.contentsTransform = tile_factor
99 | scene.background.wrapS, scene.background.wrapT = scn.WrapMode.Repeat, scn.WrapMode.Repeat
100 |
101 | root_node = scene.rootNode
102 |
103 | cube_geometry = scn.Box(width=1, height=1, length=1, chamferRadius=0.05)
104 | cube_geometry_inside = scn.Box(width=1-0.001, height=1-0.001, length=1-0.001, chamferRadius=0.04)
105 |
106 | material_inside = scn.Material()
107 | material_inside.diffuse.contents = inside_image
108 |
109 | cube_geometry_inside.materials =[material_inside for i in range(6)]
110 |
111 | cube_geometry_materials = [scn.Material() for i in range(6)]
112 | for i in range(6):
113 | cube_geometry_materials[i].diffuse.contents = cube_images[random.randrange(len(cube_image_names))]
114 | cube_geometry.materials = cube_geometry_materials
115 |
116 | cube_node = scn.Node.nodeWithGeometry(cube_geometry)
117 | cube_node.name = 'cube node'
118 | cube_node_inside = scn.Node.nodeWithGeometry(cube_geometry_inside)
119 | cube_node.addChildNode(cube_node_inside)
120 |
121 | cube_node.position = (0., 0., -5.)
122 |
123 | cube_action = scn.Action.scaleTo(1.5, 3.0)
124 | cube_reversed_action = scn.Action.scaleTo(1.0, 1.0)
125 | cube_wait_action = scn.Action.waitForDuration(0.8)
126 | cube_y_rot_action1 = scn.Action.rotateBy(0, 0.2, 0, 0.3)
127 | cube_y_rot_action2 = scn.Action.rotateBy(0, -0.4, 0, 0.3)
128 | cube_y_rot_action3 = scn.Action.rotateBy(0, 0.2, 0, 0.3)
129 |
130 | cube_sequence = scn.Action.sequence([cube_action, cube_reversed_action, cube_wait_action, cube_y_rot_action1, cube_y_rot_action2, cube_y_rot_action3, cube_wait_action])
131 | cube_forever = scn.Action.repeatActionForever(cube_sequence)
132 |
133 | cube_node.runAction(cube_forever)
134 |
135 |
136 | constraint = scn.LookAtConstraint.lookAtConstraintWithTarget(cube_node)
137 | constraint.gimbalLockEnabled = True
138 |
139 | camera = scn.Camera()
140 | camera_node = scn.Node()
141 | camera_node.name = 'camera!'
142 | camera_node.camera = camera
143 | camera_node.position = (-3.0,3.0,3.0)
144 | camera_node.constraints = [constraint]
145 |
146 | lights_node = scn.Node()
147 |
148 | ambient_light = scn.Light()
149 | ambient_light.type = scn.LightTypeAmbient
150 | ambient_light.name = 'ambient light'
151 | ambient_light.color = (.99, 1.0, .86)
152 | ambient_node = scn.Node()
153 | ambient_node.light = ambient_light
154 | lights_node.addChildNode(ambient_node)
155 |
156 | directional_light = scn.Light()
157 | directional_light.type = scn.LightTypeDirectional
158 | directional_light.name = 'directional light'
159 | directional_light.color = 'white'
160 | directional_node = scn.Node()
161 | directional_node.light = directional_light
162 | directional_node.position = camera_node.position
163 | directional_node.constraints = [constraint]
164 |
165 | lights_node.addChildNode(directional_node)
166 |
167 | root_node.addChildNode(camera_node)
168 | root_node.addChildNode(lights_node)
169 | root_node.addChildNode(cube_node)
170 | scene_view.pointOfView = camera_node
171 |
172 |
173 | scn.Transaction.begin()
174 | scn.Transaction.disableActions = False
175 | scn.Transaction.animationDuration = 10.0
176 | directional_light.color = 'yellow'
177 | scn.Transaction.commit()
178 |
179 |
180 | scene_view.playing = True
181 |
182 | main_view.present(style='fullscreen', hide_title_bar=False)
183 |
184 | if __name__ == '__main__':
185 | main()
186 |
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/solver.py:
--------------------------------------------------------------------------------
1 | '''Cube puzzles - five typical 3x3x3 3D puzzles.
2 |
3 | (c) Peter Ulbrich, 2019
4 |
5 | a demo script for the sceneKit wrapper package for Pythonista
6 |
7 | See the README.md for details.
8 |
9 | '''
10 |
11 | import math
12 | import pickle
13 |
14 | try:
15 | from pyrr import Vector3, Quaternion
16 | except ImportError:
17 | Vector3 = Quaternion = None
18 |
19 | from data import *
20 |
21 | class Piece:
22 | def __init__(self, name, shapes):
23 | self.name = name
24 | self.puzzle_shape = None
25 | self.shapes = shapes
26 |
27 | class Solver:
28 | def __init__(self, puzzle_variant):
29 | self.puzzle_cubes = sum(len(nodes) for nodes in data.reference_pieces[puzzle_variant.value].values())
30 |
31 | try:
32 | with open('resources/'+puzzle_variant.name+'-shapes.P', 'rb') as fp:
33 | self.shapes_dict = pickle.load(fp)
34 | for name, nodes in data.reference_pieces[puzzle_variant.value].items():
35 | if len(list(self.shapes_dict[name].keys())[0]) != len(nodes):
36 | raise pickle.UnpicklingError
37 | except (FileNotFoundError, pickle.UnpicklingError, KeyError):
38 | if Vector3 is None:
39 | raise ImportError('Module Pyrr is needed to recreate the shapes.P file. Ether re-download the shapes.P file or run "pip install pyrr".')
40 | self.shapes_dict = {}
41 | for name, nodes in data.reference_pieces[puzzle_variant.value].items():
42 | self.shapes_dict[name] = self.generate_shapes(nodes)
43 | with open('resources/'+puzzle_variant.name+'-shapes.P', 'wb') as fp:
44 | pickle.dump(self.shapes_dict, fp)
45 |
46 | self.pieces = [Piece(name, self.shapes_dict[name]) for name in data.reference_pieces[puzzle_variant.value].keys()]
47 |
48 | try:
49 | with open('resources/'+puzzle_variant.name+'-solution.P', 'rb') as fs:
50 | self.empty_solutions = pickle.load(fs)
51 | if self.empty_solutions:
52 | if len(data.reference_pieces[puzzle_variant.value][self.empty_solutions[0][0][0][0]]) != len(self.empty_solutions[0][0][0][1]):
53 | raise pickle.UnpicklingError
54 | except (FileNotFoundError, pickle.UnpicklingError, KeyError):
55 | self.generate_solutions()
56 | self.empty_solutions = self.path
57 | with open('resources/'+puzzle_variant.name+'-solution.P', 'wb') as fs:
58 | pickle.dump(self.empty_solutions, fs)
59 | self.path = []
60 | self.solution = []
61 |
62 | def solve_it(self, input):
63 | for aPiece in self.pieces:
64 | aPiece.puzzle_shape = None
65 | aPiece.shapes = self.shapes_dict[aPiece.name]
66 |
67 | for aPiece_name in input:
68 | aPiece = [_ for _ in self.pieces if _.name == aPiece_name][0]
69 | shape = input[aPiece_name]
70 | aPiece.puzzle_shape = frozenset([Cube(int(round(aNode.x)), int(round(aNode.y)), int(round(aNode.z))) for aNode in shape])
71 | aPiece.shapes = {aPiece.puzzle_shape:self.shapes_dict[aPiece.name][aPiece.puzzle_shape]}
72 |
73 | self.solution = []
74 | for aPath in self.empty_solutions:
75 | aSolution = []
76 | valid_path = True
77 | distance = 0
78 | for aStep in aPath:
79 | next_step_items = []
80 | for aPiece_name, aShape in aStep:
81 | aPiece = [_ for _ in self.pieces if _.name == aPiece_name][0]
82 | if aPiece_name in input and aShape != aPiece.puzzle_shape:
83 | valid_path = False
84 | break
85 | if aPiece_name not in input:
86 | next_step_items.append((aPiece_name, aPiece.shapes[aShape]))
87 | elif distance < len(input):
88 | pass
89 | else:
90 | valid_path = False
91 | break
92 | else:
93 | distance += len(aStep)
94 | if not valid_path:
95 | break
96 | elif next_step_items:
97 | aSolution.append(next_step_items)
98 | if aSolution and valid_path:
99 | self.solution.append(aSolution)
100 |
101 | return self.solution
102 |
103 | def generate_solutions(self):
104 | for aPiece in self.pieces:
105 | aPiece.puzzle_shape = None
106 | aPiece.shapes = self.shapes_dict[aPiece.name]
107 | self.path = []
108 | self.solve(set(), len(self.pieces) - 1)
109 |
110 | def solve(self, cubes_used, piece_ind):
111 | if piece_ind < 0:
112 | if len(cubes_used) != self.puzzle_cubes:
113 | return
114 | else:
115 | new_path = []
116 | solver_pieces = self.pieces[:]
117 | while solver_pieces:
118 | next_removals = []
119 | for aPiece in solver_pieces:
120 | larger_shape = set()
121 | for aNode in aPiece.puzzle_shape:
122 | larger_shape.add(Cube(aNode.x, aNode.y + 1, aNode.z))
123 | larger_shape.add(Cube(aNode.x, aNode.y + 2, aNode.z))
124 |
125 | remainder_cubes = set()
126 | for aRemainder_piece in (_ for _ in solver_pieces if _ != aPiece):
127 | remainder_cubes.update(aRemainder_piece.puzzle_shape)
128 |
129 | if remainder_cubes.isdisjoint(larger_shape):
130 | next_removals.append(aPiece)
131 |
132 | if next_removals:
133 | path_entry = []
134 | for aPiece in next_removals:
135 | path_entry.append((aPiece.name, aPiece.puzzle_shape))
136 | solver_pieces.remove(aPiece)
137 | new_path.append(path_entry)
138 |
139 | else:
140 | new_path = []
141 | break
142 |
143 | if not solver_pieces:
144 | new_path.reverse()
145 | self.path.append(new_path)
146 | return
147 |
148 | for next_shape in self.pieces[piece_ind].shapes:
149 | if cubes_used.isdisjoint(next_shape):
150 | self.pieces[piece_ind].puzzle_shape = next_shape
151 | solved = self.solve(cubes_used | next_shape, piece_ind - 1)
152 | self.pieces[piece_ind].puzzle_shape = None
153 |
154 | return
155 |
156 | def generate_shapes(self, nodes):
157 | nodes = [Vector3(aNode) for aNode in nodes]
158 | theta = math.pi / 2
159 | quaternions = {}
160 | for i in range(4):
161 | quaternions[(i, 0, 0)] = Quaternion.from_x_rotation(i * theta)
162 | quaternions[(0, i, 0)] = Quaternion.from_y_rotation(i * theta)
163 | quaternions[(0, 0, i)] = Quaternion.from_z_rotation(i * theta)
164 | shapes = {}
165 | for x_rot in (3, 2, 1, 0):
166 | new_rotation = quaternions[(x_rot, 0, 0)]
167 | rotated_nodes_x = self.shape_constrained([new_rotation * aNode for aNode in nodes], zero_align=True)
168 | for y_rot in (3, 2, 1, 0):
169 | new_rotation = quaternions[(0, y_rot, 0)]
170 | rotated_nodes_y = self.shape_constrained([new_rotation * aNode for aNode in rotated_nodes_x], zero_align=True)
171 | for z_rot in (3, 2, 1, 0):
172 | new_rotation = quaternions[(0, 0, z_rot)]
173 | rotated_nodes = self.shape_constrained([new_rotation * aNode for aNode in rotated_nodes_y], zero_align=True)
174 | for x_shift in (2, 1, 0):
175 | for y_shift in (2, 1, 0):
176 | for z_shift in (-2, -1, 0):
177 | new_shift = Vector3([float(x_shift), float(y_shift), float(z_shift)])
178 | shifted_nodes = [new_shift + aNode for aNode in rotated_nodes]
179 | shape = self.shape_constrained(shifted_nodes)
180 | shape = frozenset([Cube(int(round(aNode.x)), int(round(aNode.y)), int(round(aNode.z))) for aNode in shape])
181 | shapes[shape] = (x_rot, y_rot, z_rot, x_shift, y_shift, z_shift)
182 | return shapes
183 |
184 | def shape_constrained(self, shape_nodes, zero_align=False):
185 | min_box = Vector3([100., 100., 100.])
186 | max_box = Vector3([-100., -100., -100.])
187 | for aNode in shape_nodes:
188 | min_box.x = round(min(min_box.x, aNode.x))
189 | min_box.y = round(min(min_box.y, aNode.y))
190 | min_box.z = round(min(min_box.z, aNode.z))
191 | max_box.x = round(max(max_box.x, aNode.x))
192 | max_box.y = round(max(max_box.y, aNode.y))
193 | max_box.z = round(max(max_box.z, aNode.z))
194 |
195 | offset = Vector3()
196 | if not zero_align:
197 | if min_box.x < 0: offset.x = float(min_box.x)
198 | elif max_box.x > 2: offset.x = float(max_box.x - 2.)
199 | if min_box.y < -6: offset.y = float(min_box.y + 6.)
200 | elif max_box.y > -4: offset.y = float(max_box.y + 4.)
201 | if min_box.z < -2: offset.z = float(min_box.z + 2.)
202 | elif max_box.z > 0: offset.z = float(max_box.z)
203 | else:
204 | offset.x = float(min_box.x)
205 | offset.y = float(min_box.y + 6.)
206 | offset.z = float(max_box.z)
207 |
208 | return [aNode - offset for aNode in shape_nodes]
209 |
210 | if __name__ == '__main__':
211 | import main
212 | main.MainViewController.run()
213 |
214 |
--------------------------------------------------------------------------------
/sceneKit/sceneKitLight.py:
--------------------------------------------------------------------------------
1 | '''light modul, to be included in sceneKit'''
2 |
3 | from ui import parse_color
4 |
5 | import sceneKit
6 | from .sceneKitEnv import *
7 | from .sceneKitMaterial import *
8 | from .sceneKitAnimation import *
9 |
10 | _lightTypes = []
11 | for aLightType in ['SCNLightTypeIES', 'SCNLightTypeAmbient', 'SCNLightTypeDirectional', 'SCNLightTypeOmni', 'SCNLightTypeProbe', 'SCNLightTypeSpot']:
12 | _lightTypes.append(str(ObjCInstance(c_void_p.in_dll(c, aLightType))))
13 | _lightTypes = tuple(_lightTypes)
14 |
15 | SCNLightTypeIES, SCNLightTypeAmbient, SCNLightTypeDirectional, \
16 | SCNLightTypeOmni, SCNLightTypeProbe, SCNLightTypeSpot = _lightTypes
17 | LightTypeIES, LightTypeAmbient, LightTypeDirectional, \
18 | LightTypeOmni, LightTypeProbe, LightTypeSpot = _lightTypes
19 |
20 | class ShadowMode(Enum):
21 | Forward = 0
22 | Deferred = 1
23 | Modulated = 2
24 | SCNShadowModeForward = 0
25 | SCNShadowModeDeferred = 1
26 | SCNShadowModeModulated = 2
27 |
28 |
29 | class Light(Animatable, CInst):
30 | def __init__(self, mdlLight=None, ID=None):
31 | if ID is not None:
32 | self.ID = ID
33 | elif mdlLight is not None:
34 | self.ID = SCNLight.lightWithMDLLight_(mdlLight)
35 | if self.ID is None:
36 | raise RuntimeError('lightWithMDLLight failed. Wrong MDL asset?')
37 | else:
38 | self.ID = SCNLight.light()
39 |
40 | @classmethod
41 | def light(cls):
42 | return cls()
43 |
44 | @classmethod
45 | def lightWithMDLLight(cls, mdlLight=None):
46 | return cls(mdlLight=mdlLight)
47 |
48 | def setName(self, aString):
49 | self.ID.setName_(aString)
50 | def getName(self):
51 | return str(self.ID.name())
52 | name = property(getName, setName)
53 |
54 | def setType(self, aLightType):
55 | self.ID.setType_(aLightType)
56 | def getType(self):
57 | return self.ID.type()
58 | type = property(getType, setType)
59 |
60 | def setColor(self, aColor):
61 | r, g, b, a = parse_color(aColor)
62 | self.ID.setColor_(UIColor.color(red=r, green=g, blue=b, alpha=a))
63 | def getColor(self):
64 | color = self.ID.color()
65 | return RGBA(color.red(), color.green(), color.blue(), color.alpha())
66 | color = property(getColor, setColor)
67 |
68 | def setTemperature(self, aTemp):
69 | self.ID.setTemperature_(aTemp)
70 | def getTemperature(self):
71 | return self.ID.temperature()
72 | temperature = property(getTemperature, setTemperature)
73 |
74 | def setIntensity(self, anIntensity):
75 | self.ID.setIntensity_(anIntensity)
76 | def getIntensity(self):
77 | return self.ID.intensity()
78 | intensity = property(getIntensity, setIntensity)
79 |
80 | def getSphericalHarmonicsCoefficients(self):
81 | return self.ID.sphericalHarmonicsCoefficients()
82 | sphericalHarmonicsCoefficients = property(getSphericalHarmonicsCoefficients, None)
83 |
84 | def setAttenuationStartDistance(self, aDistance):
85 | self.ID.setAttenuationStartDistance_(aDistance)
86 | def getAttenuationStartDistance(self):
87 | return self.ID.attenuationStartDistance()
88 | attenuationStartDistance = property(getAttenuationStartDistance, setAttenuationStartDistance)
89 |
90 | def setAttenuationEndDistance(self, aDistance):
91 | self.ID.setAttenuationEndDistance_(aDistance)
92 | def getAttenuationEndDistance(self):
93 | return self.ID.attenuationEndDistance()
94 | attenuationEndDistance = property(getAttenuationEndDistance, setAttenuationEndDistance)
95 |
96 | def setAttenuationFalloffExponent(self, aFallOff):
97 | self.ID.setAttenuationFalloffExponent_(aFallOff)
98 | def getAttenuationFalloffExponent(self):
99 | return self.ID.attenuationFalloffExponent()
100 | attenuationFalloffExponent = property(getAttenuationFalloffExponent, setAttenuationFalloffExponent)
101 |
102 | def setSpotInnerAngle(self, anAngle):
103 | self.ID.setSpotInnerAngle_(anAngle)
104 | def getSpotInnerAngle(self):
105 | return self.ID.spotInnerAngle()
106 | spotInnerAngle = property(getSpotInnerAngle, setSpotInnerAngle)
107 |
108 | def setSpotOuterAngle(self, anAngle):
109 | self.ID.setSpotOuterAngle_(anAngle)
110 | def getSpotOuterAngle(self):
111 | return self.ID.spotOuterAngle()
112 | spotOuterAngle = property(getSpotOuterAngle, setSpotOuterAngle)
113 |
114 | def setCastsShadow(self, aBool):
115 | self.ID.setCastsShadow_(aBool)
116 | def getCastsShadow(self):
117 | return self.ID.castsShadow()
118 | castsShadow = property(getCastsShadow, setCastsShadow)
119 |
120 | def setShadowRadius(self, aRadius):
121 | self.ID.setShadowRadius_(aRadius)
122 | def getShadowRadius(self):
123 | return self.ID.shadowRadius()
124 | shadowRadius = property(getShadowRadius, setShadowRadius)
125 |
126 | def setShadowColor(self, aColor):
127 | r, g, b, a = parse_color(aColor)
128 | self.ID.setShadowColor_(ObjCClass('UIColor').color(red=r, green=g, blue=b, alpha=a))
129 | def getShadowColor(self):
130 | color = self.ID.shadowColor()
131 | return RGBA(color.red(), color.green(), color.blue(), color.alpha())
132 | shadowColor = property(getShadowColor, setShadowColor)
133 |
134 | def setShadowMapSize(self, aGCSize):
135 | self.ID.setShadowMapSize_(aGCSize)
136 | def getShadowMapSize(self):
137 | map = self.ID.shadowMapSize()
138 | return Size(map.width, map.height)
139 | shadowMapSize = property(getShadowMapSize, setShadowMapSize)
140 |
141 | def setShadowSampleCount(self, aCount):
142 | self.ID.setShadowSampleCount_(aCount)
143 | def getShadowSampleCount(self):
144 | return self.ID.shadowSampleCount()
145 | shadowSampleCount = property(getShadowSampleCount, setShadowSampleCount)
146 |
147 | def setShadowMode(self, aMode):
148 | self.ID.setShadowMode_(aMode.value)
149 | def getShadowMode(self):
150 | return ShadowMode(self.ID.shadowMode())
151 | shadowMode = property(getShadowMode, setShadowMode)
152 |
153 | def setShadowBias(self, aBias):
154 | self.ID.setShadowBias_(aBias)
155 | def getShadowBias(self):
156 | return self.ID.shadowBias()
157 | shadowBias = property(getShadowBias, setShadowBias)
158 |
159 | def setOrthographicScale(self, aScale):
160 | self.ID.setOrthographicScale_(aScale)
161 | def getOrthographicScale(self):
162 | return self.ID.orthographicScale()
163 | orthographicScale = property(getOrthographicScale, setOrthographicScale)
164 |
165 | def setZFar(self, aDistance):
166 | self.ID.setZFar_(aDistance)
167 | def getZFar(self):
168 | return self.ID.zFar()
169 | zFar = property(getZFar, setZFar)
170 |
171 | def setZNear(self, aDistance):
172 | self.ID.setZNear_(aDistance)
173 | def getZNear(self):
174 | return self.ID.zNear()
175 | zNear = property(getZNear, setZNear)
176 |
177 | def setCategoryBitMask(self, aBitMask):
178 | self.ID.setCategoryBitMask_(aBitMask)
179 | def getCategoryBitMask(self):
180 | return self.ID.categoryBitMask()
181 | categoryBitMask = property(getCategoryBitMask, setCategoryBitMask)
182 |
183 | def setIESProfileURL(self, anURL):
184 | self.ID.setIESProfileURL_(nsurl(anURL))
185 | def getIESProfileURL(self):
186 | return str(self.ID.IESProfileURL().absoluteString())
187 | IESProfileURL = property(getIESProfileURL, setIESProfileURL)
188 |
189 | def setAutomaticallyAdjustsShadowProjection(self, aBool):
190 | self.ID.setAutomaticallyAdjustsShadowProjection_(aBool)
191 | def getAutomaticallyAdjustsShadowProjection(self):
192 | return self.ID.automaticallyAdjustsShadowProjection()
193 | automaticallyAdjustsShadowProjection = property(getAutomaticallyAdjustsShadowProjection, setAutomaticallyAdjustsShadowProjection)
194 |
195 | def setForcesBackFaceCasters(self, aBool):
196 | self.ID.setForcesBackFaceCasters_(aBool)
197 | def getForcesBackFaceCasters(self):
198 | return self.ID.forcesBackFaceCasters()
199 | forcesBackFaceCasters = property(getForcesBackFaceCasters, setForcesBackFaceCasters)
200 |
201 | def setMaximumShadowDistance(self, aDistance):
202 | self.ID.setMaximumShadowDistance_(aDistance)
203 | def getMaximumShadowDistance(self):
204 | return self.ID.maximumShadowDistance()
205 | maximumShadowDistance = property(getMaximumShadowDistance, setMaximumShadowDistance)
206 |
207 | def setSampleDistributedShadowMaps(self, aBool):
208 | self.ID.setSampleDistributedShadowMaps_(aBool)
209 | def getSampleDistributedShadowMaps(self):
210 | return self.ID.sampleDistributedShadowMaps()
211 | sampleDistributedShadowMaps = property(getSampleDistributedShadowMaps, setSampleDistributedShadowMaps)
212 |
213 | def setShadowCascadeCount(self, aCount):
214 | self.ID.setShadowCascadeCount_(aCount)
215 | def getShadowCascadeCount(self):
216 | return self.ID.shadowCascadeCount()
217 | shadowCascadeCount = property(getShadowCascadeCount, setShadowCascadeCount)
218 |
219 | def setShadowCascadeSplittingFactor(self, aFactor):
220 | self.ID.setShadowCascadeSplittingFactor_(aFactor)
221 | def getShadowCascadeSplittingFactor(self):
222 | return self.ID.shadowCascadeSplittingFactor()
223 | shadowCascadeSplittingFactor = property(getShadowCascadeSplittingFactor, setShadowCascadeSplittingFactor)
224 |
225 | def setGobo(self, aMaterialProperty):
226 | self.ID.setGobo_(aMaterialProperty._materialProperty)
227 | def getGobo(self):
228 | return sceneKit.MaterialProperty.outof(self.ID.gobo())
229 | gobo = property(getGobo, setGobo)
230 |
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/manual.py:
--------------------------------------------------------------------------------
1 | '''Cube puzzles - five typical 3x3x3 3D puzzles.
2 |
3 | (c) Peter Ulbrich, 2019
4 |
5 | a demo script for the sceneKit wrapper package for Pythonista
6 |
7 | See the README.md for details.
8 |
9 | '''
10 |
11 | import math
12 |
13 | import sceneKit as scn
14 |
15 | from data import *
16 | import main
17 | import setup
18 | from piece import *
19 | import solver
20 |
21 | class ManualStage:
22 | def __init__(self, puzzle_variant):
23 | self.piece_set = Piece_set(puzzle_variant)
24 | self.center_view = setup.setup_center_view(self.piece_set)
25 | setup.setup_drawer_views(self.piece_set)
26 | self.solver = solver.Solver(puzzle_variant)
27 |
28 | self.show_drawer_animation = setup.setup_show_drawer_animation()
29 |
30 | self.solved_tray_piece = None
31 | self.tray_piece_transforms = []
32 | self.tray_piece_transforms_index = -1
33 |
34 | data.hud_layer.set_title(str(puzzle_variant.name).replace('_', ' '))
35 |
36 | def restart(self):
37 | scn.Transaction.begin()
38 | scn.Transaction.animationDuration = 1.
39 | self.piece_set.reset_pieces()
40 | self.piece_set.previous_piece_in_tray = []
41 | scn.Transaction.commit()
42 | self.reset_show_next_tray_position()
43 |
44 | def scene_views(self):
45 | return [self.center_view] + [aPiece.drawer_view for aPiece in self.piece_set.pieces]
46 |
47 | def tap(self, location):
48 | for aPiece in self.piece_set.pieces:
49 | drawer_frame = aPiece.drawer_view.frame
50 | if drawer_frame[0] < location.x < drawer_frame[0] + drawer_frame[2] and drawer_frame[1] < location.y < drawer_frame[1] + drawer_frame[3]:
51 | self.drawer_tapped(aPiece)
52 | return
53 | if self.center_view.frame[0] < location.x < self.center_view.frame[0] + self.center_view.frame[2] and self.center_view.frame[1] < location.y < self.center_view.frame[1] + self.center_view.frame[3]:
54 | self.center_view_tapped(location)
55 | return
56 |
57 | def center_view_tapped(self, location):
58 | x = location.x - self.center_view.frame[0]
59 | y = location.y - self.center_view.frame[1]
60 | hit_list = self.center_view.hitTest((x, y), {scn.HitTestOptionSearchMode:scn.HitTestSearchMode.Closest, scn.HitTestOptionCategoryBitMask:2})
61 | if not hit_list: return
62 |
63 | hit_name = hit_list[0].node.name
64 | tray_piece = next((aCandidate for aCandidate in self.piece_set.pieces if aCandidate.location == Piece_location.Tray), None)
65 | scn.Transaction.begin()
66 | scn.Transaction.animationDuration = 1.
67 | try:
68 | if hit_name[0] == 'r':
69 | tray_piece.rotate(hit_name[1])
70 | elif hit_name[0] == 's':
71 | shift = 1 if hit_name[2] == '1' else -1
72 | tray_piece.shift(hit_name[1], shift)
73 | except (IndexError, AttributeError): pass
74 | scn.Transaction.commit()
75 |
76 | if hit_name == 'box':
77 | scn.Transaction.begin()
78 | scn.Transaction.animationDuration = 1.5
79 | if tray_piece is not None:
80 | tray_piece.drop()
81 | # if len([_ for _ in self.piece_set.pieces if _.location != Piece_location.Puzzle]) == 0:
82 | if self.piece_set.solved():
83 | self.show_finished()
84 | else:
85 | try:
86 | tray_piece = self.piece_set.previous_piece_in_tray.pop()
87 | tray_piece.lift()
88 | tray_piece.constraint()
89 | except IndexError: pass
90 | scn.Transaction.commit()
91 | self.reset_show_next_tray_position()
92 |
93 | def drawer_tapped(self, aPiece):
94 | self.reset_show_next_tray_position()
95 | scn.Transaction.begin()
96 | scn.Transaction.animationDuration = 0.3
97 | onTray = next((aCandidate for aCandidate in self.piece_set.pieces if aCandidate.location == Piece_location.Tray), None)
98 | if onTray is not None:
99 | onTray.from_tray_to_drawer()
100 | if aPiece.location == Piece_location.Drawer and aPiece != onTray:
101 | aPiece.from_drawer_to_tray()
102 | scn.Transaction.commit()
103 |
104 | def hint(self):
105 | if self.piece_set.solved():
106 | self.show_finished()
107 | return
108 |
109 | tray_pieces = [aPiece for aPiece in self.piece_set.pieces if aPiece.location == Piece_location.Tray]
110 | if tray_pieces and tray_pieces[0] == self.solved_tray_piece:
111 | self.show_next_tray_position()
112 | return
113 |
114 | solver_input = {}
115 | for aPiece in self.piece_set.pieces:
116 | if aPiece.location == Piece_location.Puzzle:
117 | tray_handle = aPiece.tray_handle_node.parentNode
118 | positions = []
119 | for aNode in aPiece.piece_handle_node.childNodes:
120 | aPos = aPiece.piece_handle_node.convertPosition(aNode.position, toNode=tray_handle)
121 | positions.append(aPos)
122 |
123 | solver_input[aPiece.name] = tuple(positions)
124 |
125 | solution = self.solver.solve_it(solver_input)
126 |
127 | if not solution:
128 | self.show_dead_end()
129 | return
130 |
131 | candidate_names = set()
132 | for aSolution in solution:
133 | for anOption in aSolution[0]:
134 | candidate_names.add(anOption[0])
135 |
136 | candidate_pieces = [aPiece for aPiece in self.piece_set.pieces if aPiece.name in candidate_names]
137 |
138 |
139 | if not tray_pieces or tray_pieces[0] not in candidate_pieces:
140 | self.show_candidates(candidate_pieces)
141 | return
142 |
143 | if tray_pieces and tray_pieces[0] in candidate_pieces:
144 | tray_piece = tray_pieces[0]
145 | tray_piece_transforms = set()
146 | for aSolution in self.solver.solution:
147 | for aStepItem in aSolution[0]:
148 | if aStepItem[0] == tray_piece.name:
149 | tray_piece_transforms.add(aStepItem[1])
150 | self.setup_show_next_tray_position(tray_piece, list(tray_piece_transforms))
151 | self.show_next_tray_position()
152 | return
153 |
154 |
155 | def show_dead_end(self):
156 | message_symbol = '🤷🏻♀️'
157 | data.hud_layer.show_alert(message_symbol)
158 |
159 | def show_finished(self):
160 | message_symbol = '👍'
161 | data.hud_layer.show_alert(message_symbol)
162 |
163 |
164 | def show_candidates(self, candidate_pieces):
165 | for aPiece in candidate_pieces:
166 | aPiece.drawer_view.scene.background.removeAllAnimations()
167 | aPiece.drawer_view.scene.background.addAnimation(self.show_drawer_animation)
168 |
169 | def setup_show_next_tray_position(self, tray_piece, tray_piece_transforms):
170 | self.solved_tray_piece = tray_piece
171 | self.tray_piece_transforms = tray_piece_transforms
172 | self.tray_piece_transforms_index = -1
173 | data.hud_layer.set_status('')
174 |
175 | def reset_show_next_tray_position(self):
176 | self.solved_tray_piece = None
177 | self.tray_piece_transforms = []
178 | self.tray_piece_transforms_index = -1
179 | data.hud_layer.set_status('')
180 |
181 | def show_next_tray_position(self):
182 | self.tray_piece_transforms_index += 1
183 | if self.tray_piece_transforms_index == len(self.tray_piece_transforms):
184 | self.tray_piece_transforms_index = 0
185 |
186 | tray_piece = self.solved_tray_piece
187 | transform = self.tray_piece_transforms[self.tray_piece_transforms_index]
188 |
189 | original_transform = tray_piece.piece_handle_node.transform
190 |
191 | tray_piece.from_drawer_to_tray()
192 | for ind, axis in enumerate(['x', 'y', 'z']):
193 | for _ in range(transform[ind]):
194 | tray_piece.rotate(axis, constraint=False)
195 | tray_piece.constraint(zero_align=True)
196 | tray_piece.shift('x', transform[3])
197 | tray_piece.shift('y', transform[4])
198 | tray_piece.shift('z', transform[5])
199 |
200 | new_transform = tray_piece.piece_handle_node.transform
201 | tray_piece.piece_handle_node.transform = original_transform
202 |
203 | scn.Transaction.begin()
204 | scn.Transaction.animationDuration = 1.
205 | tray_piece.piece_handle_node.transform = new_transform
206 | scn.Transaction.commit()
207 |
208 | lead = "".join('⚫️' for i in range(self.tray_piece_transforms_index))
209 | tail = "".join('⚫️' for i in range(len(self.tray_piece_transforms) - self.tray_piece_transforms_index - 1))
210 | data.hud_layer.set_status(lead + '🔵' + tail)
211 |
212 | def layout(self):
213 | _, _, w, h = data.main_view.frame
214 | half = (len(self.piece_set.pieces) + 1) // 2
215 | odd = 2 * half - len(self.piece_set.pieces)
216 | side_with_gap = min(int(min(w, h) / (half+0.2)), int(min(w, h) / 3.5))
217 | side = int(0.8 * side_with_gap)
218 | gap = side_with_gap - side
219 | for i in range(len(self.piece_set.pieces)):
220 | tile = i % half
221 | left_side = top_side = i < half
222 | if data.horizontal:
223 | x = int(2 * gap) if left_side else w - int(2*gap) - side
224 | y = h - (odd * side_with_gap if not left_side else 0)//2 - (h - half*side - (half-1) * gap)//2 - side - tile*side_with_gap
225 | else:
226 | x = (odd * side_with_gap if not top_side else 0)//2 + (w - half*side - (half-1) * gap)//2 + tile*side_with_gap
227 | y = 20 + side//2 if top_side else h - 20 - side//2 - side
228 |
229 | self.piece_set.pieces[i].drawer_view.frame = (x, y, (side), (side))
230 |
231 | x = 2*gap + side_with_gap if data.horizontal else gap
232 | y = (h - half*side - (half-1)*gap)//2 + 10 if data.horizontal else 20 + side//2 + side + gap
233 |
234 | self.center_view.frame = (x, y, w - 2*x, h - 2*y) if data.horizontal else (x, y, w - 2*x, h - 2*y)
235 |
236 | box_node = self.center_view.scene.rootNode.childNodeWithName('box')
237 | box_node.rotation = (0, 0, 1, 0) if data.horizontal else (0, 0, 1, -math.pi/2)
238 | box_node.position = (1., 1., -1.) if data.horizontal else (-0.5, 3, -1)
239 | box_node.scale = (1., 1., 1.) if data.horizontal else (1.3, 1.3, 1.3)
240 |
241 | camera_node = self.center_view.scene.rootNode.childNodeWithName('center_camera')
242 | camera_node.position = (6., 8.5, 9.5) if data.horizontal else (9.2, 10., 13.)
243 |
244 | if __name__ == '__main__':
245 | main.MainViewController.run()
246 |
--------------------------------------------------------------------------------
/sceneKit demo/17_cube-puzzle/setup.py:
--------------------------------------------------------------------------------
1 | '''Cube puzzles - five typical 3x3x3 3D puzzles.
2 |
3 | (c) Peter Ulbrich, 2019
4 |
5 | a demo script for the sceneKit wrapper package for Pythonista
6 |
7 | See the README.md for details.
8 |
9 | '''
10 |
11 | import math
12 | import sceneKit as scn
13 |
14 | import main
15 | from data import *
16 | from piece import *
17 |
18 | def setup_elementary_cube_geometry():
19 | geometry = scn.Box(1., 1., 1., 0.)
20 | material = scn.Material()
21 | material.diffuse.contents = 'resources/wood.png'
22 | geometry.firstMaterial = material
23 | return geometry
24 |
25 | def setup_pieces(piece_set, puzzle_variant):
26 | pieces = []
27 | for name, cubes in data.reference_pieces[puzzle_variant.value].items():
28 | handle_node = scn.Node()
29 | handle_node.name = name
30 | nodes = []
31 | for aCubePosition in cubes:
32 | nodes.append(scn.Node.nodeWithGeometry(setup_elementary_cube_geometry()))
33 | nodes[-1].position = aCubePosition
34 |
35 | for aNode in nodes:
36 | aNode.geometry.firstMaterial.diffuse.intensity = (0.7 - len(pieces)/len(data.reference_pieces[puzzle_variant.value])) + 0.3
37 | handle_node.addChildNode(aNode)
38 |
39 | pieces.append(Piece(piece_set, name, handle_node))
40 |
41 | return pieces
42 |
43 | rotate_action = scn.Action.repeatActionForever(scn.Action.rotateBy(0, math.pi*2, 0, 25.))
44 |
45 | def setup_selector_views():
46 | selector_views = []
47 | for aVariant in Puzzle_variant:
48 | selector_view = scn.View(None, superView=data.main_view)
49 | selector_view.preferredFramesPerSecond = 30
50 | selector_view.rendersContinuously = False
51 | selector_view.autoenablesDefaultLighting = True
52 | selector_view.allowsCameraControl = False
53 |
54 | selector_view.scene = scn.Scene()
55 | selector_view.scene.background.contents = 'beige'
56 |
57 | title_geometry = scn.Text(str(aVariant.name).replace('_', ' '), 0.3)
58 | title_geometry.font = ('Chalkboard SE', 4.)
59 | title_geometry.firstMaterial.contents = (.48, .14, .14)
60 | title_geometry.flatness = 0.1
61 | title_geometry.chamferRadius = 0.08
62 | bbox_min, bbox_max = title_geometry.boundingBox
63 | title_size = bbox_max.x - bbox_min.x
64 | title_node = scn.Node.nodeWithGeometry(title_geometry)
65 | title_node.position = (-title_size/2, -1., -25.)
66 | selector_view.scene.rootNode.addChildNode(title_node)
67 |
68 | puzzle_node = scn.Node()
69 |
70 | table_geometry = scn.Cylinder(6., 0.5)
71 | table_geometry.firstMaterial.diffuse.contents = 'resources/marble.jpg'
72 | table_node = scn.Node.nodeWithGeometry(table_geometry)
73 | table_node.position = (0., -0.75, 0.)
74 | puzzle_node.addChildNode(table_node)
75 |
76 | puzzle_node.position = (0., -0.25, 0.)
77 | piece_set = Piece_set(aVariant)
78 | pieces = len(piece_set.pieces)
79 | for index, aPiece in enumerate(piece_set.pieces):
80 | show_piece = aPiece.drawer_handle_node
81 | show_piece.rotation = (0, 1, 0, index/pieces*2*math.pi - math.pi/4.)
82 | show_piece.position = scn.Vector3(4.*math.sin(index/pieces*2*math.pi), 0., 4.*math.cos(index/pieces*2*math.pi))
83 | puzzle_node.addChildNode(show_piece)
84 |
85 | puzzle_node.runAction(rotate_action)
86 | selector_view.scene.rootNode.addChildNode(puzzle_node)
87 |
88 | camera_node = scn.Node()
89 | camera_node.name = 'selector_camera'
90 | camera_node.position = (0., 7., 11.)
91 | camera_node.lookAt((0., 0., 0.))
92 | camera_node.camera = scn.Camera()
93 | selector_view.scene.rootNode.addChildNode(camera_node)
94 |
95 | selector_views.append(selector_view)
96 | return selector_views
97 |
98 | def setup_center_view(piece_set):
99 | center_view = scn.View(None, superView=data.main_view)
100 | center_view.preferredFramesPerSecond = 30
101 | center_view.rendersContinuously = False
102 | center_view.autoenablesDefaultLighting = True
103 | center_view.autoresizingMask = scn.ViewAutoresizing.FlexibleHeight | scn.ViewAutoresizing.FlexibleWidth
104 | center_view.allowsCameraControl = True
105 | center_view.jitteringEnabled = True
106 |
107 | center_view.backgroundColor = 'beige'
108 |
109 | box_size = 3.0
110 | box = scn.Box(box_size, box_size, box_size, 0.0)
111 | box.firstMaterial.diffuse.contents = (.86, .86, .86)
112 | box.firstMaterial.transparency = 0.15
113 | box.firstMaterial.cullMode = scn.CullMode.Front
114 | box_node = scn.Node.nodeWithGeometry(box)
115 | box_node.name = 'box'
116 | box_node.categoryBitMask = 2
117 |
118 | tray_handle = setup_tray(piece_set)
119 | axis_handle = setup_axes()
120 | tray_handle.addChildNode(axis_handle)
121 | box_node.addChildNode(tray_handle)
122 |
123 | scene = scn.Scene()
124 | scene.rootNode.addChildNode(box_node)
125 |
126 | camera_node = scn.Node()
127 | camera_node.name = 'center_camera'
128 | camera_node.position = (8., 10., 12)
129 | camera_node.lookAt((1., 3., -1.5))
130 | camera_node.camera = scn.Camera()
131 | camera_node.camera.fieldOfView = 50
132 | scene.rootNode.addChildNode(camera_node)
133 |
134 | center_view.scene = scene
135 | return center_view
136 |
137 | def setup_drawer_views(piece_set):
138 | for aPiece in piece_set.pieces:
139 | drawer_view = scn.View(None, superView=data.main_view)
140 | drawer_view.preferredFramesPerSecond = 30
141 | drawer_view.rendersContinuously = False
142 | drawer_view.autoenablesDefaultLighting = True
143 | drawer_view.allowsCameraControl = True
144 |
145 | drawer_view.scene = scn.Scene()
146 | drawer_view.scene.background.contents = 'beige'
147 | show_piece = aPiece.drawer_handle_node
148 | show_piece.rotation = (0, 1, 0, math.pi/2.5)
149 | drawer_view.scene.rootNode.addChildNode(show_piece)
150 |
151 | min, max = drawer_view.scene.rootNode.boundingBox
152 |
153 | camera_node = scn.Node()
154 | camera_node.position = (2.5, 3., 2.5)
155 | camera_node.lookAt(((min.x+max.x)/2+1., (min.y+max.y)/2+0.5, 0.))
156 | camera_node.camera = scn.Camera()
157 | drawer_view.scene.rootNode.addChildNode(camera_node)
158 |
159 | aPiece.drawer_view = drawer_view
160 |
161 | def setup_show_drawer_animation():
162 | core_animation = scn.CoreBasicAnimation('contents')
163 | core_animation.toValue = (.4, 1.0, .0, 0.9)
164 | core_animation.removedOnCompletion = False
165 |
166 | animation = scn.Animation.animationWithCAAnimation(core_animation)
167 | animation.removedOnCompletion = False
168 | animation.autoreverses = True
169 | animation.duration = 1.
170 | animation.repeatCount = 3
171 |
172 | return animation
173 |
174 | def setup_tray(piece_set):
175 | tray_handle = scn.Node()
176 | tray_handle.position = (-1, 5, 1)
177 | tray_handle.name = 'tray'
178 |
179 | for aPiece in piece_set.pieces:
180 | tray_handle.addChildNode(aPiece.tray_handle_node)
181 |
182 | return tray_handle
183 |
184 | def setup_axes():
185 | axis_rode = scn.Cylinder(0.015, 7.5)
186 | axis_rode.firstMaterial.contents = 'blue'
187 |
188 | vertical_axis_rode = scn.Cylinder(0.015, 11.)
189 | vertical_axis_rode.firstMaterial.contents = 'blue'
190 |
191 | rotate_control_geometry = scn.Sphere(0.35)
192 | rotate_control_geometry.firstMaterial.contents = 'red'
193 |
194 | cube_rotate_control_geometry = scn.Sphere(0.35)
195 | cube_rotate_control_geometry.firstMaterial.contents = 'green'
196 |
197 | shift_control_geometry = scn.Cone(0, 0.38, 0.7)
198 | shift_control_geometry.firstMaterial.contents = 'orange'
199 |
200 | axis_handle = scn.Node()
201 | axis_handle.name = 'origo'
202 | axis_handle.position = (-0.5, -0.5, 0.5)
203 |
204 | x_axis_node = scn.Node.nodeWithGeometry(axis_rode)
205 | x_axis_node.rotation = (0, 0, 1, math.pi/2)
206 | x_axis_node.position = (1.5, 0, 0)
207 |
208 | x_axis_rotate_control_node_1 = scn.Node.nodeWithGeometry(rotate_control_geometry)
209 | x_axis_rotate_control_node_1.position = (0., -axis_rode.height / 2 + 1, 0.)
210 | x_axis_rotate_control_node_1.name = 'rx1'
211 | x_axis_rotate_control_node_1.categoryBitMask = 2
212 | x_axis_node.addChildNode(x_axis_rotate_control_node_1)
213 | x_axis_rotate_control_node_2 = scn.Node.nodeWithGeometry(rotate_control_geometry)
214 | x_axis_rotate_control_node_2.position = (0., axis_rode.height / 2 - 1, 0.)
215 | x_axis_rotate_control_node_2.name = 'rx2'
216 | x_axis_rotate_control_node_2.categoryBitMask = 2
217 | x_axis_node.addChildNode(x_axis_rotate_control_node_2)
218 |
219 | x_axis_shift_control_node_1 = scn.Node.nodeWithGeometry(shift_control_geometry)
220 | x_axis_shift_control_node_1.position = (0., -axis_rode.height / 2, 0.)
221 | x_axis_shift_control_node_1.rotation = (0., 0., 1., -math.pi)
222 | x_axis_shift_control_node_1.name = 'sx1'
223 | x_axis_shift_control_node_1.categoryBitMask = 2
224 | x_axis_node.addChildNode(x_axis_shift_control_node_1)
225 | x_axis_shift_control_node_2 = scn.Node.nodeWithGeometry(shift_control_geometry)
226 | x_axis_shift_control_node_2.position = (0., axis_rode.height / 2, 0.)
227 | x_axis_shift_control_node_2.name = 'sx2'
228 | x_axis_shift_control_node_2.categoryBitMask = 2
229 | x_axis_node.addChildNode(x_axis_shift_control_node_2)
230 |
231 | axis_handle.addChildNode(x_axis_node)
232 |
233 | y_axis_node = scn.Node.nodeWithGeometry(vertical_axis_rode)
234 | y_axis_node.rotation = (1, 0, 0, -math.pi)
235 | y_axis_node.position = (0, -3, 0)
236 | y_axis_rotate_control_node_1 = scn.Node.nodeWithGeometry(rotate_control_geometry)
237 | y_axis_rotate_control_node_1.position = (0., -vertical_axis_rode.height / 2, 0.)
238 | y_axis_rotate_control_node_1.name = 'ry1'
239 | y_axis_rotate_control_node_1.categoryBitMask = 2
240 | y_axis_node.addChildNode(y_axis_rotate_control_node_1)
241 | y_axis_rotate_control_node_2 = scn.Node.nodeWithGeometry(rotate_control_geometry)
242 | y_axis_rotate_control_node_2.position = (0., vertical_axis_rode.height / 2, 0.)
243 | y_axis_rotate_control_node_2.name = 'ry2'
244 | y_axis_rotate_control_node_2.categoryBitMask = 2
245 | y_axis_node.addChildNode(y_axis_rotate_control_node_2)
246 |
247 | axis_handle.addChildNode(y_axis_node)
248 |
249 | z_axis_node = scn.Node.nodeWithGeometry(axis_rode)
250 | z_axis_node.rotation = (1, 0, 0, -math.pi/2)
251 | z_axis_node.position = (0, 0, -1.5)
252 | z_axis_rotate_control_node_1 = scn.Node.nodeWithGeometry(rotate_control_geometry)
253 | z_axis_rotate_control_node_1.position = (0., -axis_rode.height / 2 + 1, 0.)
254 | z_axis_rotate_control_node_1.name = 'rz1'
255 | z_axis_rotate_control_node_1.categoryBitMask = 2
256 | z_axis_node.addChildNode(z_axis_rotate_control_node_1)
257 | z_axis_rotate_control_node_2 = scn.Node.nodeWithGeometry(rotate_control_geometry)
258 | z_axis_rotate_control_node_2.position = (0., axis_rode.height / 2 - 1, 0.)
259 | z_axis_rotate_control_node_2.name = 'rz2'
260 | z_axis_rotate_control_node_2.categoryBitMask = 2
261 | z_axis_node.addChildNode(z_axis_rotate_control_node_2)
262 |
263 | z_axis_shift_control_node_1 = scn.Node.nodeWithGeometry(shift_control_geometry)
264 | z_axis_shift_control_node_1.position = (0., -axis_rode.height / 2, 0.)
265 | z_axis_shift_control_node_1.rotation = (0., 0., 1., math.pi)
266 | z_axis_shift_control_node_1.name = 'sz1'
267 | z_axis_shift_control_node_1.categoryBitMask = 2
268 | z_axis_node.addChildNode(z_axis_shift_control_node_1)
269 | z_axis_shift_control_node_2 = scn.Node.nodeWithGeometry(shift_control_geometry)
270 | z_axis_shift_control_node_2.position = (0., axis_rode.height / 2, 0.)
271 | z_axis_shift_control_node_2.name = 'sz2'
272 | z_axis_shift_control_node_2.categoryBitMask = 2
273 | z_axis_node.addChildNode(z_axis_shift_control_node_2)
274 |
275 | axis_handle.addChildNode(z_axis_node)
276 |
277 | return axis_handle
278 |
279 | if __name__ == '__main__':
280 | main.MainViewController.run()
281 |
--------------------------------------------------------------------------------
/sceneKit/sceneKitEnv.py:
--------------------------------------------------------------------------------
1 | '''environment for sceneKit'''
2 |
3 | from objc_util import *
4 | from collections import namedtuple, Iterable
5 | from ui import parse_color, Image
6 | import ui
7 | from types import MethodType
8 | from enum import Enum, Flag
9 | import weakref
10 |
11 | import sceneKit
12 |
13 | load_framework('SceneKit')
14 | load_framework('SpriteKit')
15 |
16 | NSValue = ObjCClass('NSValue')
17 | NSError = ObjCClass('NSError')
18 | UIImage = ObjCClass('UIImage')
19 | UIFont = ObjCClass('UIFont')
20 |
21 |
22 | CAMediaTimingFunction, CAValueFunction, CAAnimation, CABasicAnimation, CAKeyframeAnimation, CAAnimationGroup = map(ObjCClass, ['CAMediaTimingFunction', 'CAValueFunction', 'CAAnimation', 'CABasicAnimation', 'CAKeyframeAnimation', 'CAAnimationGroup'])
23 |
24 | SCNView, SCNScene, SCNBox, SCNNode, SCNSceneSource, SCNMaterial, SCNCamera, SCNLight, \
25 | SCNGeometry, SCNFloor, SCNPlane, SCNBox, SCNCapsule, SCNCone, \
26 | SCNCylinder, SCNPyramid, SCNSphere, SCNTorus, SCNTube, SCNMaterialProperty, SCNConstraint, \
27 | SCNBillboardConstraint, SCNLookAtConstraint, SCNDistanceConstraint, SCNAvoidOccluderConstraint, \
28 | SCNAccelerationConstraint, SCNSliderConstraint, SCNReplicatorConstraint, SCNIKConstraint, \
29 | SCNAction, SCNTransaction, SCNAnimation, SCNAnimationPlayer, SCNAnimationEvent, \
30 | SCNTimingFunction, SKTransition, SCNMorpher, SCNReferenceNode, SCNGeometrySource, SCNGeometryElement, \
31 | SCNText, SCNShape, SCNLevelOfDetail, SCNGeometryTessellator, SCNSkinner, SCNPhysicsShape, \
32 | SCNPhysicsBody, SCNPhysicsContact, SCNPhysicsWorld, SCNPhysicsField, SCNPhysicsBehavior, SCNPhysicsHingeJoint, \
33 | SCNPhysicsSliderJoint, SCNPhysicsBallSocketJoint, SCNPhysicsConeTwistJoint, SCNPhysicsVehicle, SCNPhysicsVehicleWheel, \
34 | SCNParticleSystem, SCNParticlePropertyController, SCNAnimationPlayer, SCNAudioSource, SCNAudioPlayer, SCNSceneSource \
35 | = map(ObjCClass, ['SCNView', 'SCNScene', 'SCNBox', 'SCNNode', 'SCNSceneSource', 'SCNMaterial', 'SCNCamera', 'SCNLight', 'SCNGeometry', 'SCNFloor', 'SCNPlane', 'SCNBox', 'SCNCapsule', 'SCNCone', 'SCNCylinder', 'SCNPyramid', 'SCNSphere', 'SCNTorus', 'SCNTube', 'SCNMaterialProperty', 'SCNConstraint', 'SCNBillboardConstraint', 'SCNLookAtConstraint', 'SCNDistanceConstraint', 'SCNAvoidOccluderConstraint', 'SCNAccelerationConstraint', 'SCNSliderConstraint', 'SCNReplicatorConstraint', 'SCNIKConstraint', 'SCNAction', 'SCNTransaction', 'SCNAnimation', 'SCNAnimationPlayer', 'SCNAnimationEvent', 'SCNTimingFunction', 'SKTransition', 'SCNMorpher', 'SCNReferenceNode', 'SCNGeometrySource', 'SCNGeometryElement', 'SCNText', 'SCNShape', 'SCNLevelOfDetail', 'SCNGeometryTessellator', 'SCNSkinner', 'SCNPhysicsShape', 'SCNPhysicsBody', 'SCNPhysicsContact', 'SCNPhysicsWorld', 'SCNPhysicsField', 'SCNPhysicsBehavior', 'SCNPhysicsHingeJoint', 'SCNPhysicsSliderJoint', 'SCNPhysicsBallSocketJoint', 'SCNPhysicsConeTwistJoint', 'SCNPhysicsVehicle', 'SCNPhysicsVehicleWheel', 'SCNParticleSystem', 'SCNParticlePropertyController', 'SCNAnimationPlayer', 'SCNAudioSource', 'SCNAudioPlayer', 'SCNSceneSource'])
36 |
37 | _hitTestOptions = []
38 | for anOption in ['SCNHitTestBackFaceCullingKey', 'SCNHitTestBoundingBoxOnlyKey', 'SCNHitTestOptionCategoryBitMask', \
39 | 'SCNHitTestClipToZRangeKey', 'SCNHitTestIgnoreChildNodesKey', 'SCNHitTestIgnoreHiddenNodesKey', \
40 | 'SCNHitTestRootNodeKey', 'SCNHitTestOptionSearchMode']:
41 | _hitTestOptions.append(str(ObjCInstance(c_void_p.in_dll(c, anOption))))
42 | _hitTestOptions = tuple(_hitTestOptions)
43 |
44 | SCNHitTestBackFaceCullingKey, SCNHitTestBoundingBoxOnlyKey, SCNHitTestOptionCategoryBitMask, \
45 | SCNHitTestClipToZRangeKey, SCNHitTestIgnoreChildNodesKey, SCNHitTestIgnoreHiddenNodesKey, \
46 | SCNHitTestRootNodeKey, SCNHitTestOptionSearchMode = _hitTestOptions
47 | HitTestBackFaceCullingKey, HitTestBoundingBoxOnlyKey, HitTestOptionCategoryBitMask, \
48 | HitTestClipToZRangeKey, HitTestIgnoreChildNodesKey, HitTestIgnoreHiddenNodesKey, \
49 | HitTestRootNodeKey, HitTestOptionSearchMode = _hitTestOptions
50 |
51 | class HitTestSearchMode(Enum):
52 | Closest = 0
53 | All = 1
54 | Any = 2
55 | SCNHitTestSearchModeClosest = 0
56 | SCNHitTestSearchModeAll = 1
57 | SCNHitTestSearchModeAny =2
58 |
59 | def hitTestOptions(inDict):
60 | outDict = {}
61 | for k, v in inDict.items():
62 | if k == HitTestBackFaceCullingKey:
63 | outDict[k] = bool(v)
64 | elif k == HitTestBoundingBoxOnlyKey:
65 | outDict[k] = bool(v)
66 | elif k == HitTestOptionCategoryBitMask:
67 | outDict[k] = int(v)
68 | elif k == HitTestClipToZRangeKey:
69 | outDict[k] = bool(v)
70 | elif k == HitTestIgnoreChildNodesKey:
71 | outDict[k] = bool(v)
72 | elif k == HitTestIgnoreHiddenNodesKey:
73 | outDict[k] = bool(v)
74 | elif k == HitTestRootNodeKey:
75 | outDict[k] = v.ID
76 | elif k == HitTestOptionSearchMode:
77 | outDict[k] = v.value
78 | return outDict
79 |
80 | Point = namedtuple('Point', 'x y')
81 | Vector2 = namedtuple('Vector2', 'x y')
82 | Vector3 = namedtuple('Vector3', 'x y z')
83 | class SCNVector3(Structure):
84 | _fields_ = [("x", c_float), ("y", c_float), ("z", c_float)]
85 |
86 | Vector4 = namedtuple('Vector4', 'x y z w')
87 | Quaternion = namedtuple('Quaternion', 'x y z w')
88 | class SCNVector4(Structure):
89 | _fields_ = [("x", c_float), ("y", c_float), ("z", c_float), ("w", c_float)]
90 |
91 | Matrix4 = namedtuple('Matrix4', 'm11 m12 m13 m14 m21 m22 m23 m24 m31 m32 m33 m34 m41 m42 m43 m44')
92 | Matrix4Identity = Matrix4(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0)
93 |
94 | class SCNMatrix4(Structure):
95 | _fields_ = [('m11', c_float), ('m12', c_float), ('m13', c_float), ('m14', c_float), ('m21', c_float), ('m22', c_float), ('m23', c_float), ('m24', c_float), ('m31', c_float), ('m32', c_float), ('m33', c_float), ('m34', c_float), ('m41', c_float), ('m42', c_float), ('m43', c_float), ('m44', c_float)]
96 |
97 |
98 | RGBA = namedtuple('RGBA', 'red green blue alpha')
99 |
100 | Size = namedtuple('Size', 'width height')
101 |
102 | def getCStructValuesAsList(aCStruct):
103 | fieldList = [aField[0] for aField in aCStruct._fields_]
104 | return [getattr(aCStruct, aField) for aField in fieldList]
105 |
106 | def vector2Make(iter):
107 | return Vector2._make(iter)
108 |
109 | def vector3Make(iter):
110 | return Vector3._make(iter)
111 |
112 | def quaternionMake(iter):
113 | return Quaternion._make(iter)
114 |
115 | def vector4Make(iter):
116 | return Vector4._make(iter)
117 |
118 | def matrix4Make(iter):
119 | return Matrix4._make(iter)
120 |
121 | def RGBAMake(color):
122 | r, g, b, a = parse_color(color)
123 | return RGBA(r, g, b, a)
124 |
125 | class BoundingVolume:
126 | def setBoundingBox(self, minMax):
127 | (minV, maxV) = minMax
128 | bbox_min, bbox_max = SCNVector3(), SCNVector3()
129 | bbox_min.x, bbox_min.y, bbox_min.z = minV[0], minV[1], minV[2]
130 | bbox_max.x, bbox_max.y, bbox_max.z = maxV[0], maxV[1], maxV[2]
131 | self.ID.setBoundingBoxMin_max_(byref(bbox_min), byref(bbox_max), restype=None, argtypes=[POINTER(SCNVector3), POINTER(SCNVector3)])
132 | def getBoundingBox(self):
133 | bbox_min, bbox_max = SCNVector3(), SCNVector3()
134 | retFlag = self.ID.getBoundingBoxMin_max_(byref(bbox_min), byref(bbox_max), restype=c_bool, argtypes=[POINTER(SCNVector3), POINTER(SCNVector3)])
135 | if retFlag:
136 | return (vector3Make(getCStructValuesAsList(bbox_min)), vector3Make(getCStructValuesAsList(bbox_max)))
137 | else:
138 | return None
139 | def getBoundingBoxMin(self):
140 | return self.getBoundingBox(self)
141 | boundingBox = property(getBoundingBox, setBoundingBox)
142 |
143 | def getBoundingSphere(self):
144 | center = SCNVector3()
145 | radius = c_double(0.0)
146 | retFlag = self.ID.getBoundingSphereCenter_radius_(byref(center), byref(radius), restype=c_bool, argtypes=[POINTER(SCNVector3), POINTER(c_double)])
147 | if retFlag:
148 | return (vector3Make(getCStructValuesAsList(center)), float(radius.value))
149 | else:
150 | return None
151 | boundingSphere = property(getBoundingSphere, None)
152 |
153 | class _NoneClass:
154 | ID = None
155 | nil = _NoneClass()
156 | null = nil
157 | Nil = nil
158 | Null = nil
159 |
160 | _CInstCache = dict()
161 |
162 | def printCache():
163 | print('_CInstCache')
164 | for anItem in list(_CInstCache.items()):
165 | print('-----', '\n', anItem)
166 | print('&&&&&&&&&END')
167 |
168 | def cacheSize():
169 | return len(_CInstCache)
170 |
171 | def clearCache():
172 | _CInstCache.clear()
173 |
174 | class CInst:
175 | def setID(self, aCInst):
176 | self._ID = aCInst
177 | _CInstCache[aCInst] = self
178 | def getID(self):
179 | return self._ID
180 | ID = property(getID, setID)
181 |
182 | @classmethod
183 | def outof(cls, fromC):
184 | if fromC is None:
185 | return nil
186 | elif fromC in _CInstCache:
187 | return _CInstCache[fromC]
188 | else:
189 | try:
190 | return cls.subclassOutof(ID=fromC)
191 | except AttributeError:
192 | return cls(ID=fromC)
193 |
194 | def __str__(self):
195 | return 'CInst instance with ' + str(self.ID)
196 |
197 |
198 | class CInst_NoCache:
199 | def setID(self, aCInst):
200 | self._ID = aCInst
201 | def getID(self):
202 | return self._ID
203 | ID = property(getID, setID)
204 |
205 | @classmethod
206 | def outof(cls, fromC):
207 | if fromC is None:
208 | return nil
209 | else:
210 | try:
211 | return cls.subclassOutof(ID=fromC)
212 | except AttributeError:
213 | return cls(ID=fromC)
214 |
215 | def __str__(self):
216 | return 'CInst_NoCache instance with ' + str(self.ID)
217 |
218 |
219 | def contentFromPy(aPContent):
220 | def isImage(aPContent):
221 | return isinstance(aPContent, Image)
222 | def isImageArray(aPContent):
223 | if isinstance(aPContent, Iterable):
224 | for aPContentElement in aPContent:
225 | if not isImage(aPContentElement): return False
226 | return True
227 | return False
228 | def isNumber(aPContent):
229 | return isinstance(aPContent, float) or isinstance(aPContent, int)
230 | def isURL(aPContent):
231 | if isinstance(aPContent, str):
232 | if ' ' in aPContent: return False
233 | if len(aPContent) > 8 or ':' in aPContent or '/' in aPContent or '.' in aPContent: return True
234 | return False
235 | def isColor(aPContent):
236 | try:
237 | r, g, b, a = parse_color(aPContent)
238 | return True
239 | except:
240 | return False
241 |
242 | if isImage(aPContent):
243 | aPng = aPContent.to_png()
244 | return ObjCClass('UIImage').imageWithData_(aPng)
245 | elif isImageArray(aPContent):
246 | imageList = []
247 | for anImage in aPContent:
248 | aPng = anImage.to_png()
249 | imageList.append(ObjCClass('UIImage').imageWithData_(aPng))
250 | return imageList
251 | elif isNumber(aPContent):
252 | return ns(aPContent)
253 | elif isURL(aPContent):
254 | return nsurl(aPContent)
255 | elif isColor(aPContent):
256 | r, g, b, a = parse_color(aPContent) #parse_color accepts anything, might result in random colors
257 | return UIColor.color(red=r, green=g, blue=b, alpha=a)
258 | else:
259 | return aPContent
260 |
261 | def contentFromC(aCContent):
262 | if aCContent is None: return None
263 | elif aCContent.isKindOfClass(UIColor):
264 | return RGBA(aCContent.red(), aCContent.green(), aCContent.blue(), aCContent.alpha())
265 | elif aCContent.isKindOfClass(ObjCClass('UIImage')):
266 | c.UIImagePNGRepresentation.argtypes = [c_void_p]
267 | c.UIImagePNGRepresentation.restype = c_void_p
268 | data = ObjCInstance(c.UIImagePNGRepresentation(aCContent.ptr))
269 | return Image.from_data(nsdata_to_bytes(data),2.0)
270 | elif aCContent.isKindOfClass(NSArray):
271 | if aCContent.objectAtIndex(0).isKindOfClass(ObjCClass('UIImage')):
272 | imageArray = []
273 | c.UIImagePNGRepresentation.argtypes = [c_void_p]
274 | c.UIImagePNGRepresentation.restype = c_void_p
275 | for i in range(aCContent.count()):
276 | aCImg = aCContent.objectAtIndex(i)
277 | if aCImg.isKindOfClass(ObjCClass('UIImage')):
278 | data = ObjCInstance(c.UIImagePNGRepresentation(aCImg.ptr))
279 | imageArray.append(Image.from_data(nsdata_to_bytes(data),2.0))
280 | return imageArray
281 | elif aCContent.isKindOfClass(NSURL): return str(aCContent.absoluteString())
282 | else: pass
283 | return aCContent
284 |
--------------------------------------------------------------------------------
/sceneKit/sceneKitMaterial.py:
--------------------------------------------------------------------------------
1 | '''material modul, to be included in sceneKit'''
2 |
3 | from ui import parse_color, Image
4 | from collections import OrderedDict, Iterable
5 |
6 | import sceneKit
7 | from .sceneKitEnv import *
8 | from .sceneKitAnimation import *
9 |
10 | _lightingModels = []
11 | for aModel in ['SCNLightingModelBlinn', 'SCNLightingModelConstant', 'SCNLightingModelLambert', 'SCNLightingModelPhong', 'SCNLightingModelPhysicallyBased']:
12 | _lightingModels.append(str(ObjCInstance(c_void_p.in_dll(c, aModel))))
13 |
14 | SCNLightingModelBlinn, SCNLightingModelConstant, SCNLightingModelLambert, SCNLightingModelPhong, SCNLightingModelPhysicallyBased = _lightingModels
15 | LightingModelBlinn, LightingModelConstant, LightingModelLambert, LightingModelPhong, \
16 | LightingModelPhysicallyBased = _lightingModels
17 |
18 | class WrapMode(Enum):
19 | Clamp = 1
20 | Repeat = 2
21 | ClampToBorder = 3
22 | Mirror = 4
23 | SCNWrapModeClamp = 1
24 | SCNWrapModeRepeat = 2
25 | SCNWrapModeClampToBorder = 3
26 | SCNWrapModeMirror = 4
27 |
28 | Clamp = WrapMode.Clamp
29 | Repeat = WrapMode.Repeat
30 | ClampToBorder = WrapMode.ClampToBorder
31 | Mirror = WrapMode.Mirror
32 |
33 | class FilterMode(Enum):
34 | ModeNone = 0
35 | Nearest = 1
36 | Linear = 2
37 | SCNFilterModeNone = 0
38 | SCNFilterModeNearest = 1
39 | SCNFilterModeLinear = 2
40 |
41 | class ColorMask(Flag):
42 | MaskNone = 0
43 | Alpha = (0x1 << 0)
44 | Blue = (0x1 << 1)
45 | Green = (0x1 << 2)
46 | Red = (0x1 << 3)
47 | All = Alpha | Blue | Green | Red
48 | SCNColorMaskNone = 0
49 | SCNColorMaskAlpha = (0x1 << 0)
50 | SCNColorMaskBlue = (0x1 << 1)
51 | SCNColorMaskGreen = (0x1 << 2)
52 | SCNColorMaskRed = (0x1 << 3)
53 | SCNColorMaskAll = Alpha | Blue | Green | Red
54 |
55 | class TransparencyMode(Enum):
56 | AOne = 0
57 | Default = 0
58 | RGBZero = 1
59 | SingleLayer = 2
60 | DualLayer = 3
61 | SCNTransparencyModeAOne = 0
62 | SCNTransparencyModeDefault = 0
63 | SCNTransparencyModeRGBZero = 1
64 | SCNTransparencyModeSingleLayer = 2
65 | SCNTransparencyModeDualLayer = 3
66 |
67 | class BlendMode(Enum):
68 | Alpha = 0
69 | Add = 1
70 | Subtract = 2
71 | Multiply = 3
72 | Screen = 4
73 | Replace = 5
74 | Max = 6
75 | SCNBlendModeAlpha = 0
76 | SCNBlendModeAdd = 1
77 | SCNBlendModeSubtract = 2
78 | SCNBlendModeMultiply = 3
79 | SCNBlendModeScreen = 4
80 | SCNBlendModeReplace = 5
81 | SCNBlendModeMax = 6
82 |
83 | class CullMode(Enum):
84 | Back = 0
85 | Front = 1
86 | SCNCullModeBack = 0
87 | SCNCullModeFront = 1
88 |
89 | CullBack = CullMode.Back
90 | CullFront = CullMode.Front
91 |
92 | class FillMode(Enum):
93 | Fill = 0
94 | Lines = 1
95 | SCNFillModeFill = 0
96 | SCNFillModeLines = 1
97 |
98 | class Material(Animatable, CInst):
99 | def __init__(self, mdlMaterial=None, ID=None):
100 | if ID is not None:
101 | self.ID = ID
102 | elif mdlMaterial is not None:
103 | self.ID = SCNMaterial.materialWithMDLMaterial_(mdlMaterial)
104 | if self.ID is None:
105 | raise RuntimeError('materialWithMDLMaterial failed. Wrong MDL asset?')
106 | else:
107 | self.ID = SCNMaterial.material()
108 |
109 | @classmethod
110 | def material(cls):
111 | return cls()
112 |
113 | @classmethod
114 | def materialWithMDLMaterial(cls, mdlMaterial=None):
115 | return cls(mdlMaterial=mdlMaterial)
116 |
117 | def setName(self, aString):
118 | self.ID.setName_(aString)
119 | def getName(self):
120 | return str(self.ID.name())
121 | name = property(getName, setName)
122 |
123 | def setContents(self, aContent):
124 | self.diffuse.contents = aContent
125 | def getContents(self):
126 | return self.diffuse.contents
127 | contents = property(getContents, setContents)
128 |
129 | def setLightingModelName(self, aName):
130 | if aName[:3] != 'SCN': aName = 'SCN'+aName
131 | self.ID.setLightingModelName_(aName)
132 | def getLightingModelName(self):
133 | return str(self.ID.lightingModelName())[3:]
134 | lightingModelName = property(getLightingModelName, setLightingModelName)
135 |
136 | def getDiffuse(self):
137 | return sceneKit.MaterialProperty.outof(self.ID.diffuse())
138 | diffuse = property(getDiffuse, None)
139 |
140 | def getMetalness(self):
141 | return sceneKit.MaterialProperty.outof(self.ID.metalness())
142 | metalness = property(getMetalness, None)
143 |
144 | def getRoughness(self):
145 | return sceneKit.MaterialProperty.outof(self.ID.roughness())
146 | roughness = property(getRoughness, None)
147 |
148 | def getNormal(self):
149 | return sceneKit.MaterialProperty.outof(self.ID.normal())
150 | normal = property(getNormal, None)
151 |
152 | def getDisplacement(self):
153 | return sceneKit.MaterialProperty.outof(self.ID.displacement())
154 | displacement = property(getDisplacement, None)
155 |
156 | def getEmission(self):
157 | return sceneKit.MaterialProperty.outof(self.ID.emission())
158 | emission = property(getEmission, None)
159 |
160 | def getSelfIllumination(self):
161 | return sceneKit.MaterialProperty.outof(self.ID.selfIllumination())
162 | selfIllumination = property(getSelfIllumination, None)
163 |
164 | def getAmbientOcclusion(self):
165 | return sceneKit.MaterialProperty.outof(self.ID.ambientOcclusion())
166 | ambientOcclusion = property(getAmbientOcclusion, None)
167 |
168 | def getDiffuse(self):
169 | return sceneKit.MaterialProperty.outof(self.ID.diffuse())
170 | diffuse = property(getDiffuse, None)
171 |
172 | def getAmbient(self):
173 | return sceneKit.MaterialProperty.outof(self.ID.ambient())
174 | ambient = property(getAmbient, None)
175 |
176 | def getSpecular(self):
177 | return sceneKit.MaterialProperty.outof(self.ID.specular())
178 | specular = property(getSpecular, None)
179 |
180 | def getReflective(self):
181 | return scenekit.MaterialProperty.outof(self.ID.reflective())
182 | reflective = property(getReflective, None)
183 |
184 | def getMultiply(self):
185 | return sceneKit.MaterialProperty.outof(self.ID.multiply())
186 | multiply = property(getMultiply, None)
187 |
188 | def getTransparent(self):
189 | return sceneKit.MaterialProperty.outof(self.ID.transparent())
190 | transparent = property(getTransparent, None)
191 |
192 | def setShininess(self, aShineniness):
193 | self.ID.setShininess_(aShineniness)
194 | def getShininess(self):
195 | return self.ID.shininess()
196 | shininess = property(getShininess, setShininess)
197 |
198 | def setFresnelExponent(self, anExponent):
199 | self.ID.setFresnelExponent_(anExponent)
200 | def getFresnelExponent(self):
201 | return self.ID.fresnelExponent()
202 | fresnelExponent = property(getFresnelExponent, setFresnelExponent)
203 |
204 | def setLocksAmbientWithDiffuse(self, aBool):
205 | self.ID.setLocksAmbientWithDiffuse_(aBool)
206 | def getLocksAmbientWithDiffuse(self):
207 | return self.ID.locksAmbientWithDiffuse()
208 | locksAmbientWithDiffuse = property(getLocksAmbientWithDiffuse, setLocksAmbientWithDiffuse)
209 |
210 | def setTransparency(self, aTransparency):
211 | self.ID.setTransparency_(aTransparency)
212 | def getTransparency(self):
213 | return self.ID.transparency()
214 | transparency = property(getTransparency, setTransparency)
215 |
216 | def setTransparencyMode(self, aTransparencyMode):
217 | self.ID.setTransparencyMode_(aTransparencyMode.value)
218 | def getTransparencyMode(self):
219 | return TransparencyMode(self.ID.transparencyMode())
220 | transparencyMode = property(getTransparencyMode, setTransparencyMode)
221 |
222 | def setBlendMode(self, aBlendMode):
223 | self.ID.setBlendMode_(aBlendMode.value)
224 | def getBlendMode(self):
225 | return BlendMode(self.ID.blendMode())
226 | blendMode = property(getBlendMode, setBlendMode)
227 |
228 | def setLitPerPixel(self, aBool):
229 | self.ID.setLitPerPixel_(aBool)
230 | def getLitPerPixel(self):
231 | return self.ID.litPerPixel()
232 | litPerPixel = property(getLitPerPixel, setLitPerPixel)
233 |
234 | def setDoubleSided(self, aBool):
235 | self.ID.setDoubleSided_(aBool)
236 | def isDoubleSided(self):
237 | return self.ID.doubleSided()
238 | doubleSided = property(isDoubleSided, setDoubleSided)
239 |
240 | def setCullMode(self, aCullMode):
241 | self.ID.setCullMode_(aCullMode.value)
242 | def getCullMode(self):
243 | return CullMode(self.ID.cullMode())
244 | cullMode = property(getCullMode, setCullMode)
245 |
246 | def setFillMode(self, aFillMode):
247 | self.ID.setFillMode_(aFillMode.value)
248 | def getFillMode(self):
249 | return FillMode(self.ID.fillMode())
250 | fillMode = property(getFillMode, setFillMode)
251 |
252 | def setWritesToDepthBuffer(self, aBool):
253 | self.ID.setWritesToDepthBuffer_(aBool)
254 | def getWritesToDepthBuffer(self):
255 | return self.ID.writesToDepthBuffer()
256 | writesToDepthBuffer = property(getWritesToDepthBuffer, setWritesToDepthBuffer)
257 |
258 | def setReadsFromDepthBuffer(self, aBool):
259 | self.ID.setReadsFromDepthBuffer_(aBool)
260 | def getReadsFromDepthBuffer(self):
261 | return self.ID.readsFromDepthBuffer()
262 | readsFromDepthBuffer = property(getReadsFromDepthBuffer, setReadsFromDepthBuffer)
263 |
264 | def setColorBufferWriteMask(self, aColorBufferWriteMask):
265 | self.ID.setColorBufferWriteMask_(_colorMasks.index(aColorBufferWriteMask))
266 | def getColorBufferWriteMask(self):
267 | return _colorMasks[self.ID.colorBufferWriteMask()]
268 | colorBufferWriteMask = property(getColorBufferWriteMask, setColorBufferWriteMask)
269 |
270 | class MaterialProperty(Animatable, CInst):
271 | def __init__(self, contents=None, ID=None):
272 | if contents is not None:
273 | self.ID = SCNMaterialProperty.materialPropertyWithContents_(contents)
274 | elif ID is not None:
275 | self.ID = ID
276 | else:
277 | self.ID = None
278 |
279 | @classmethod
280 | def materialPropertyWithContents(cls, contents=None):
281 | return cls(contents=contents)
282 |
283 | def setContents(self, aContent):
284 | self.ID.setContents_(contentFromPy(aContent))
285 | def getContents(self):
286 | contents = self.ID.contents()
287 | return contentFromC(contents)
288 | contents = property(getContents, setContents)
289 |
290 | def setIntensity(self, anIntensity):
291 | self.ID.setIntensity_(anIntensity)
292 | def getIntensity(self):
293 | return self.ID.intensity()
294 | intensity = property(getIntensity, setIntensity)
295 |
296 | def setContentsTransform(self, aTransform):
297 | self.ID.setContentsTransform_(matrix4Make(aTransform))
298 | def getContentsTransform(self):
299 | trans = self.ID.contentsTransform()
300 | return matrix4Make(getCStructValuesAsList(trans))
301 | contentsTransform = property(getContentsTransform, setContentsTransform)
302 |
303 | def setWrapS(self, aWrapMode):
304 | self.ID.setWrapS_(aWrapMode.value)
305 | def getWrapS(self):
306 | return WrapMode(self.ID.wrapS())
307 | wrapS = property(getWrapS, setWrapS)
308 |
309 | def setWrapT(self, aWrapMode):
310 | self.ID.setWrapT_(aWrapMode.value)
311 | def getWrapT(self):
312 | return WrapMode(self.ID.wrapT())
313 | wrapT = property(getWrapT, setWrapT)
314 |
315 | def setMinificationFilter(self, aFilter):
316 | self.ID.setMinificationFilter_(aFilter.value)
317 | def getMinificationFilter(self):
318 | return FilterMode(self.ID.minificationFilter())
319 | minificationFilter = property(getMinificationFilter, setMinificationFilter)
320 |
321 | def setMagnificationFilter(self, aFilter):
322 | self.ID.setMagnificationFilter_(aFilter.value)
323 | def getMagnificationFilter(self):
324 | return FilterMode(self.ID.magnificationFilter())
325 | magnificationFilter = property(getMagnificationFilter, setMagnificationFilter)
326 |
327 | def setMipFilter(self, aFilter):
328 | self.ID.setMipFilter_(aFilter.value)
329 | def getMipFilter(self):
330 | return FilterMode(self.ID.mipFilter())
331 | mipFilter = property(getMipFilter, setMipFilter)
332 |
333 | def setMaxAnisotropy(self, anAnisotropy):
334 | self.ID.setMaxAnisotropy_(anAnisotropy)
335 | def getMaxAnisotropy(self):
336 | return self.ID.maxAnisotropy()
337 | maxAnisotropy = property(getMaxAnisotropy, setMaxAnisotropy)
338 |
339 | def setMappingChannel(self, aChannel):
340 | self.ID.setMappingChannel_(aChannel)
341 | def getMappingChannel(self):
342 | return self.ID.mappingChannel()
343 | mappingChannel = property(getMappingChannel, setMappingChannel)
344 |
345 | def setTextureComponents(self, flags):
346 | if not isinstance(flags, Iterable):
347 | flags = [flags]
348 | mask = 0
349 | for aFlag in flags:
350 | mask = mask | aFlag.value
351 | self.ID.setTextureComponents_(mask)
352 | def getTextureComponents(self):
353 | mask = self.ID.textureComponents()
354 | flags = []
355 | for aFlag in ColorMask:
356 | if (mask & aFlag.value) != 0: flags.append(aFlag)
357 | if len(flags) == 5: flags = [ColorMask.All]
358 | elif len(flags) == 0: flags = [ColorMask.MaskNone]
359 | return tuple(flags)
360 | textureComponents = property(getTextureComponents, setTextureComponents)
361 |
--------------------------------------------------------------------------------
/sceneKit/sceneKitGeometrySource.py:
--------------------------------------------------------------------------------
1 | '''
2 | geometrySource module for sceneKit
3 | includes class GeometrySource and class GeometryElement
4 | '''
5 |
6 | from ctypes import *
7 | from objc_util import *
8 | import sys
9 |
10 | import sceneKit
11 | from .sceneKitEnv import *
12 |
13 | class GeometryPrimitiveType(Enum):
14 | Triangles = 0
15 | TriangleStrip = 1
16 | Line = 2
17 | Point = 3
18 | Polygon = 4
19 | SCNGeometryPrimitiveTypeTriangles = 0
20 | SCNGeometryPrimitiveTypeTriangleStrip = 1
21 | SCNGeometryPrimitiveTypeLine = 2
22 | SCNGeometryPrimitiveTypePoint = 3
23 | SCNGeometryPrimitiveTypePolygon = 4
24 |
25 | _geometrySourceSemantics = []
26 | for aSemantics in ['SCNGeometrySourceSemanticVertex', 'SCNGeometrySourceSemanticNormal', 'SCNGeometrySourceSemanticTexcoord', 'SCNGeometrySourceSemanticColor', 'SCNGeometrySourceSemanticTangent', 'SCNGeometrySourceSemanticEdgeCrease', 'SCNGeometrySourceSemanticVertexCrease', 'SCNGeometrySourceSemanticBoneIndices', 'SCNGeometrySourceSemanticBoneWeights']:
27 | _geometrySourceSemantics.append(str(ObjCInstance(c_void_p.in_dll(c, aSemantics))))
28 | _geometrySourceSemantics = tuple(_geometrySourceSemantics)
29 |
30 | SCNGeometrySourceSemanticVertex, SCNGeometrySourceSemanticNormal, SCNGeometrySourceSemanticTexcoord, SCNGeometrySourceSemanticColor, SCNGeometrySourceSemanticTangent, SCNGeometrySourceSemanticEdgeCrease, SCNGeometrySourceSemanticVertexCrease, SCNGeometrySourceSemanticBoneIndices, SCNGeometrySourceSemanticBoneWeights = _geometrySourceSemantics
31 | GeometrySourceSemanticVertex, GeometrySourceSemanticNormal, GeometrySourceSemanticTexcoord, GeometrySourceSemanticColor, GeometrySourceSemanticTangent, GeometrySourceSemanticEdgeCrease, GeometrySourceSemanticVertexCrease, GeometrySourceSemanticBoneIndices, GeometrySourceSemanticBoneWeights = _geometrySourceSemantics
32 |
33 | class GeometrySource(CInst):
34 | def __init__(self, count=None, data=None, semantic=None, vectorCount=None, floatComponents=None, componentsPerVector=None, bytesPerComponent=None, dataOffset=None, dataStride=None, geometrySourceWithVertices=None, geometrySourceWithNormals=None, geometrySourceWithTextureCoordinates=None, geometrySourceWithData=None, geometrySourceWithCData=None, ID=None):
35 | if geometrySourceWithVertices is not None:
36 | try:
37 | iterator = iter(geometrySourceWithVertices)
38 | except TypeError:
39 | geometrySourceWithVertices = [geometrySourceWithVertices]
40 | SCNVector3ArrayN = SCNVector3 * len(geometrySourceWithVertices)
41 | verts = [SCNVector3(*aVertice) for aVertice in geometrySourceWithVertices]
42 | verts_array = SCNVector3ArrayN(*verts)
43 | self.ID = SCNGeometrySource.geometrySourceWithVertices_count_(byref(verts_array), len(verts), restype=c_void_p, argtypes=[POINTER(SCNVector3ArrayN), c_long])
44 | elif geometrySourceWithNormals is not None:
45 | try:
46 | iterator = iter(geometrySourceWithNormals)
47 | except TypeError:
48 | geometrySourceWithNormals = [geometrySourceWithNormals]
49 | SCNVector3ArrayN = SCNVector3 * len(geometrySourceWithNormals)
50 | norms = [SCNVector3(*aNorm) for aNorm in geometrySourceWithNormals]
51 | norms_array = SCNVector3ArrayN(*norms)
52 | self.ID = SCNGeometrySource.geometrySourceWithNormals_count_(byref(norms_array), len(norms), restype=c_void_p, argtypes=[POINTER(SCNVector3ArrayN), c_long])
53 | elif geometrySourceWithTextureCoordinates is not None:
54 | try:
55 | iterator = iter(geometrySourceWithTextureCoordinates)
56 | except TypeError:
57 | geometrySourceWithTextureCoordinates = [geometrySourceWithTextureCoordinates]
58 | CGPointArrayN = CGPoint * len(geometrySourceWithTextureCoordinates)
59 | coords = [CGPoint(*aCoord) for aCoord in geometrySourceWithTextureCoordinates]
60 | coords_array = CGPointArrayN(*coords)
61 | self.ID = SCNGeometrySource.geometrySourceWithTextureCoordinates_count_(byref(coords_array), len(coords), restype=c_void_p, argtypes=[POINTER(CGPointArrayN), c_long])
62 | elif geometrySourceWithData is not None:
63 | dataC = ns(bytes(geometrySourceWithData))
64 | self.ID = SCNGeometrySource.geometrySourceWithData_semantic_vectorCount_floatComponents_componentsPerVector_bytesPerComponent_dataOffset_dataStride_(dataC, semantic, vectorCount, floatComponents, componentsPerVector, bytesPerComponent, dataOffset, dataStride)
65 | elif ID is not None:
66 | self.ID = ID
67 | else:
68 | self.ID = None
69 |
70 | @classmethod
71 | def geometrySourceWithData(cls, data=None, semantic=None, vectorCount=None, floatComponents=None, componentsPerVector=None, bytesPerComponent=None, dataOffset=None, dataStride=None):
72 | return cls(geometrySourceWithData=data, semantic=semantic, vectorCount=vectorCount, floatComponents=floatComponents, componentsPerVector=componentsPerVector, bytesPerComponent=bytesPerComponent, dataOffset=dataOffset, dataStride=dataStride)
73 |
74 | @classmethod
75 | def geometrySourceWithVertices(cls, vertices=None, count=None):
76 | return cls(geometrySourceWithVertices=vertices, count=count)
77 |
78 | @classmethod
79 | def geometrySourceWithNormals(cls, normals=None, count=None):
80 | return cls(geometrySourceWithNormals=normals, count=count)
81 |
82 | @classmethod
83 | def geometrySourceWithTextureCoordinates(cls, textureCoordinates=None, count=None):
84 | return cls(geometrySourceWithTextureCoordinates=textureCoordinates, count=count)
85 |
86 | @classmethod
87 | def interleavedGeometrySourceWithVectors(cls, sourceList, semanticList):
88 | returnSources, sourceParams, vertexStructFields = [], [], []
89 | vectorCount = len(sourceList[0])
90 | dataOffset = 0
91 | for i in range(len(sourceList)):
92 | vectors = sourceList[i]
93 | floatComponents = isinstance(vectors[0][0], float)
94 | componentsPerVector = len(vectors[0])
95 | bytesPerComponent = sizeof(c_float) if floatComponents else sizeof(c_int)
96 | sourceParams.append((floatComponents, componentsPerVector, bytesPerComponent, dataOffset))
97 | for j in range(componentsPerVector):
98 | field = 'f'+str(i)+str(j)
99 | if floatComponents:
100 | vertexStructFields.append((field, c_float))
101 | dataOffset += sizeof(c_float)
102 | else:
103 | vertexStructFields.append((field, c_int))
104 | dataOffset += sizeof(c_int)
105 | dataStride = dataOffset
106 | class vertexStruct(Structure):
107 | _fields_ = vertexStructFields
108 | vertexStructArrayType = vertexStruct * vectorCount
109 | vertexStructArray = vertexStructArrayType()
110 | for i in range(vectorCount):
111 | element = []
112 | for j in range(len(sourceList)):
113 | element += sourceList[j][i]
114 | vertexStructArray[i] = vertexStruct(*element)
115 | data = NSData.dataWithBytes_length_(byref(vertexStructArray), sizeof(vertexStructArray))
116 | for i in range(len(sourceList)):
117 | returnSources.append( cls.outof( SCNGeometrySource.geometrySourceWithData_semantic_vectorCount_floatComponents_componentsPerVector_bytesPerComponent_dataOffset_dataStride_(data, semanticList[i], vectorCount, sourceParams[i][0], sourceParams[i][1], sourceParams[i][2], sourceParams[i][3], dataStride)))
118 |
119 | return tuple(returnSources)
120 |
121 | @classmethod
122 | def geometrySourceWithVectors(cls, source, semantic):
123 | return cls.interleavedGeometrySourceWithVectors([source], [semantic])[0]
124 |
125 | def getVectors(self):
126 | data = nsdata_to_bytes(self.ID.data())
127 | dataRet = []
128 | for i in range(self.vectorCount):
129 | components = []
130 | for j in range(self.componentsPerVector):
131 | if self.floatComponents:
132 | dataC = create_string_buffer(data[(i*self.dataStride+self.dataOffset)+j*self.bytesPerComponent : (i*self.dataStride+self.dataOffset)+j*self.bytesPerComponent+self.bytesPerComponent], size=self.bytesPerComponent)
133 | components.append(c_float.from_buffer(dataC).value)
134 | else:
135 | components.append(int.from_bytes(data[(i*self.dataStride+self.dataOffset)+j*self.bytesPerComponent : (i*self.dataStride+self.dataOffset)+j*self.bytesPerComponent+self.bytesPerComponent], sys.byteorder))
136 | if self.componentsPerVector == 2:
137 | dataRet.append(vector2Make(components))
138 | elif self.componentsPerVector == 3:
139 | dataRet.append(vector3Make(components))
140 | elif self.componentsPerVector == 4:
141 | dataRet.append(vector4Make(components))
142 | else:
143 | dataRet.append(tuple(components))
144 | return tuple(dataRet)
145 | vectors = property(getVectors, None)
146 |
147 | def getData(self):
148 | return nsdata_to_bytes(self.ID.data())
149 | data = property(getData, None)
150 |
151 | def getSemantic(self):
152 | return self.ID.semantic()
153 | semantic = property(getSemantic, None)
154 |
155 | def getVectorCount(self):
156 | return self.ID.vectorCount()
157 | vectorCount = property(getVectorCount, None)
158 |
159 | def getFloatComponents(self):
160 | return self.ID.floatComponents()
161 | floatComponents = property(getFloatComponents, None)
162 |
163 | def getComponentsPerVector(self):
164 | return self.ID.componentsPerVector()
165 | componentsPerVector = property(getComponentsPerVector, None)
166 |
167 | def getBytesPerComponent(self):
168 | return self.ID.bytesPerComponent()
169 | bytesPerComponent = property(getBytesPerComponent, None)
170 |
171 | def getDataOffset(self):
172 | return self.ID.dataOffset()
173 | dataOffset = property(getDataOffset, None)
174 |
175 | def getDataStride(self):
176 | return self.ID.dataStride()
177 | dataStride = property(getDataStride, None)
178 |
179 |
180 | class GeometryElement(CInst):
181 | def __init__(self, geometryElementWithData=None, primitiveType=None, mdlSubMesh=None, ID=None):
182 | if geometryElementWithData is not None:
183 | if primitiveType == GeometryPrimitiveType.Triangles:
184 | count = len(geometryElementWithData) // 3
185 | elif primitiveType == GeometryPrimitiveType.TriangleStrip:
186 | count = len(geometryElementWithData) - 2
187 | elif primitiveType == GeometryPrimitiveType.Line:
188 | count = len(geometryElementWithData) // 2
189 | elif primitiveType == GeometryPrimitiveType.Point:
190 | count = len(geometryElementWithData)
191 | elif primitiveType == GeometryPrimitiveType.Polygon:
192 | count, remainder = 0, len(geometryElementWithData)
193 | for i in range(len(geometryElementWithData)):
194 | if remainder > 0:
195 | count += 1
196 | remainder -= (geometryElementWithData[i]+1)
197 | else:
198 | break
199 | else: count = 0
200 | bytesPerIndex = (max(geometryElementWithData).bit_length() // 8) + 1
201 | indexes_array = bytearray(len(geometryElementWithData) * bytesPerIndex)
202 | for i in range(len(geometryElementWithData)):
203 | indexes_array[i*bytesPerIndex : (i+1)*bytesPerIndex] = geometryElementWithData[i].to_bytes(bytesPerIndex, sys.byteorder)
204 | datIndexes = ns(bytes(indexes_array))
205 | self.ID = SCNGeometryElement.geometryElementWithData_primitiveType_primitiveCount_bytesPerIndex_(datIndexes, primitiveType.value, count, bytesPerIndex)
206 | elif mdlSubMesh is not None:
207 | self.ID = SCNGeometryElement.geometryElementWithMDLSubmesh_(mdlSubMesh)
208 | if self.ID is None:
209 | raise RuntimeError('geometryElementWithMDLSubmesh failed. Wrong MDL asset?')
210 | elif ID is not None:
211 | self.ID = ID
212 | else:
213 | self.ID = None
214 |
215 | @classmethod
216 | def geometryElementWithData(cls, data=None, primitiveType=None, primitiveCount=None, bytesPerIndex=None):
217 | return cls(geometryElementWithData=data, primitiveType=primitiveType)
218 |
219 | @classmethod
220 | def geometryElementWithMDLSubmesh(cls, mdlSubMesh=None):
221 | return cls(mdlSubMesh=mdlSubMesh)
222 |
223 | def getData(self):
224 | data = nsdata_to_bytes(self.ID.data())
225 | bytesPerIndex = self.bytesPerIndex
226 | dataRet = []
227 | for i in range(0, len(data)//bytesPerIndex):
228 | dataRet.append(int.from_bytes(data[i*bytesPerIndex : (i+1)*bytesPerIndex], sys.byteorder))
229 | return tuple(dataRet)
230 | data = property(getData, None)
231 |
232 | def getBytesPerIndex(self):
233 | return self.ID.bytesPerIndex()
234 | bytesPerIndex = property(getBytesPerIndex, None)
235 |
236 | def getPrimitiveType(self):
237 | return GeometryPrimitiveType(self.ID.primitiveType())
238 | primitiveType = property(getPrimitiveType, None)
239 |
240 | def getPrimitiveCount(self):
241 | return self.ID.primitiveCount()
242 | primitiveCount = property(getPrimitiveCount, None)
243 |
244 | def setPrimitiveRange(self, aRange):
245 | anNSRange = NSRange(aRange[0], aRange[1])
246 | self.ID.setPrimitiveRange_(anNSRange)
247 | def getPrimitiveRange(self):
248 | aRange = self.ID.primitiveRange()
249 | location, length = aRange.location, aRange.length
250 | if location > self.primitiveCount:
251 | location, length = 0, self.primitiveCount
252 | return (location, length)
253 | primitiveRange = property(getPrimitiveRange, setPrimitiveRange)
254 |
255 | def setPointSize(self, aSize):
256 | self.ID.setPointSize_(aSize)
257 | def getPointSize(self):
258 | return self.ID.pointSize()
259 | pointSize = property(getPointSize, setPointSize)
260 |
261 | def setMinimumPointScreenSpaceRadius(self, aRadius):
262 | self.ID.setMinimumPointScreenSpaceRadius_(aRadius)
263 | def getMinimumPointScreenSpaceRadius(self):
264 | return self.ID.minimumPointScreenSpaceRadius()
265 | minimumPointScreenSpaceRadius = property(getMinimumPointScreenSpaceRadius, setMinimumPointScreenSpaceRadius)
266 |
267 | def setMaximumPointScreenSpaceRadius(self, aRadius):
268 | self.ID.setMaximumPointScreenSpaceRadius_(aRadius)
269 | def getMaximumPointScreenSpaceRadius(self):
270 | return self.ID.maximumPointScreenSpaceRadius()
271 | maximumPointScreenSpaceRadius = property(getMaximumPointScreenSpaceRadius, setMaximumPointScreenSpaceRadius)
272 |
--------------------------------------------------------------------------------
/sceneKit/sceneKitConstraints.py:
--------------------------------------------------------------------------------
1 | '''constraints modul, to be included in sceneKit'''
2 |
3 | from collections import OrderedDict, Iterable
4 |
5 | import sceneKit
6 | from .sceneKitEnv import *
7 | from .sceneKitNode import *
8 |
9 | class BillboardAxis(Flag):
10 | X = (0x1 << 0)
11 | Y = (0x1 << 1)
12 | Z = (0x1 << 2)
13 | All = X | Y | Z
14 | SCNBillboardAxisX = (0x1 << 0)
15 | SCNBillboardAxisY = (0x1 << 1)
16 | SCNBillboardAxisZ = (0x1 << 2)
17 | SCNBillboardAxisAll = X | Y | Z
18 |
19 |
20 | class Constraint(CInst):
21 | def __init__(self, ID=None):
22 | if ID is not None:
23 | self.ID = ID
24 | else:
25 | self.ID = None
26 |
27 | @classmethod
28 | def subclassOutof(cls, ID=None):
29 | desc = str(ID.description())
30 | kind = desc[4:desc.index(':')]
31 | if kind == 'BillboardConstraint':
32 | return BillboardConstraint(ID=ID)
33 | elif kind == 'LookAtConstraint':
34 | return LookAtConstraint(ID=ID)
35 | elif kind == 'DistanceConstraint':
36 | return DistanceConstraint(ID=ID)
37 | elif kind == 'AvoidOccluderConstraint':
38 | return AvoidOccluderConstraint(ID=ID)
39 | elif kind == 'AccelerationConstraint':
40 | return AccelerationConstraint(ID=ID)
41 | elif kind == 'IKConstraint':
42 | return IKConstraint(ID=ID)
43 | elif kind == 'ReplicatorConstraint':
44 | return ReplicatorConstraint(ID=ID)
45 | else:
46 | return cls(ID=ID)
47 |
48 | def setInfluenceFactor(self, anInfluenceFactor):
49 | self.ID.setInfluenceFactor(anInfluenceFactor)
50 | def getInfluenceFactor(self):
51 | return self.ID.influenceFactor()
52 | influenceFactor = property(getInfluenceFactor, setInfluenceFactor)
53 |
54 | def setEnabled(self, aBool):
55 | self.ID.setEnabled(aBool)
56 | def getEnabled(self):
57 | return self.ID.enabled()
58 | enabled = property(getEnabled, setEnabled)
59 |
60 | def setIncremental(self, aBool):
61 | self.ID.setIncremental(aBool)
62 | def getIncremental(self):
63 | return self.ID.incremental()
64 | incremental = property(getIncremental, setIncremental)
65 |
66 | class _Target:
67 | def setTarget(self, aTargetNode):
68 | self.ID.setTarget(aTargetNode.ID)
69 | def getTarget(self):
70 | return sceneKit.Node.outof(self.ID.target())
71 | target = property(getTarget, setTarget)
72 |
73 | class BillboardConstraint(Constraint):
74 | def __init__(self, ID=None):
75 | if ID is not None:
76 | self.ID = ID
77 | else:
78 | self.ID = SCNBillboardConstraint.billboardConstraint()
79 |
80 | @classmethod
81 | def billboardConstraint(cls):
82 | return cls()
83 |
84 | def setFreeAxes(self, axes):
85 | if not isinstance(axes, Iterable):
86 | axes = [axes]
87 | mask = 0
88 | for anAxis in axes:
89 | mask = mask | anAxis.value
90 | self.ID.setFreeAxes(mask)
91 | def getFreeAxes(self):
92 | mask = self.ID.freeAxes()
93 | axes = []
94 | for anAxis in BillboardAxis:
95 | if (mask & anAxis.value) != 0: axes.append(anAxis)
96 | if len(axes) == 4: axes = [BillboardAxis.All]
97 | return tuple(axes)
98 | freeAxes = property(getFreeAxes, setFreeAxes)
99 |
100 | class LookAtConstraint(Constraint, _Target):
101 | def __init__(self, target=None, ID=None):
102 | if ID is not None:
103 | self.ID = ID
104 | else:
105 | self.ID = SCNLookAtConstraint.lookAtConstraintWithTarget_(target.ID)
106 |
107 | @classmethod
108 | def lookAtConstraintWithTarget(cls, target=None):
109 | return cls(target=target)
110 |
111 | def setGimbalLockEnabled(self, aBool):
112 | self.ID.setGimbalLockEnabled(aBool)
113 | def getGimbalLockEnabled(self):
114 | return self.ID.gimbalLockEnabled()
115 | gimbalLockEnabled = property(getGimbalLockEnabled, setGimbalLockEnabled)
116 |
117 | def setLocalFront(self, aLocalFront):
118 | self.ID.setLocalFront(vector3Make(aLocalFront))
119 | def getLocalFront(self):
120 | front = self.ID.localFront()
121 | return Vector3(front.a, front.b, front.c)
122 | localFront = property(getLocalFront, setLocalFront)
123 |
124 | def setTargetOffset(self, aTargetOffset):
125 | self.ID.setTargetOffset(vector3Make(aTargetOffset))
126 | def getTargetOffset(self):
127 | offset = self.ID.targetOffset()
128 | return Vector3(offset.a, offset.b, offset.c)
129 | targetOffset = property(getTargetOffset, setTargetOffset)
130 |
131 | def setWorldUp(self, aWorldUp):
132 | self.ID.setWorldUp(vector3Make(aWorldUp))
133 | def getWorldUp(self):
134 | up = self.ID.worldUp()
135 | return Vector3(up.a, up.b, up.c)
136 | worldUp = property(getWorldUp, setWorldUp)
137 |
138 | class DistanceConstraint(Constraint, _Target):
139 | def __init__(self, target=None, ID=None):
140 | if ID is not None:
141 | self.ID = ID
142 | else:
143 | self.ID = SCNDistanceConstraint.distanceConstraintWithTarget_(target.ID)
144 |
145 | @classmethod
146 | def distanceConstraintWithTarget(cls, target=None):
147 | return cls(target=target)
148 |
149 | def setMaximumDistance(self, aMaximumDistance):
150 | self.ID.setMaximumDistance(aMaximumDistance)
151 | def getMaximumDistance(self):
152 | return self.ID.maximumDistance()
153 | maximumDistance = property(getMaximumDistance, setMaximumDistance)
154 |
155 | def setMinimumDistance(self, aMinimumDistance):
156 | self.ID.setMinimumDistance(aMinimumDistance)
157 | def getMinimumDistance(self):
158 | return self.ID.minimumDistance()
159 | minimumDistance = property(getMinimumDistance, setMinimumDistance)
160 |
161 | class AvoidOccluderConstraint(Constraint, _Target):
162 | def __init__(self, target=None, ID=None):
163 | if ID is not None:
164 | self.ID = ID
165 | else:
166 | self.ID = SCNAvoidOccluderConstraint.avoidOccluderConstraintWithTarget_(target.ID)
167 |
168 | @classmethod
169 | def avoidOccluderConstraintWithTarget(cls, target=None):
170 | return cls(target=target)
171 |
172 | def setBias(self, aBias):
173 | self.ID.setBias(aBias)
174 | def getBias(self):
175 | return self.ID.bias()
176 | bias = property(getBias, setBias)
177 |
178 | def setOccluderCategoryBitMask(self, aBitMask):
179 | self.ID.setOccluderCategoryBitMask(aBitMask)
180 | def getOccluderCategoryBitMask(self):
181 | return self.ID.occluderCategoryBitMask()
182 | occluderCategoryBitMask = property(getOccluderCategoryBitMask, setOccluderCategoryBitMask)
183 |
184 | def setDelegate(self, aDelegate):
185 | anAvoidOccluderConstraintDelegate = AvoidOccluderConstraintDelegate(aDelegate)
186 | self.ID.setDelegate_(anAvoidOccluderConstraintDelegate.ID)
187 | def getDelegate(self):
188 | anAvoidOccluderConstraintDelegate = sceneKit.AvoidOccluderConstraintDelegate.outof(self.ID.delegate())
189 | return anAvoidOccluderConstraintDelegate.delegate if anAvoidOccluderConstraintDelegate is not None else None
190 | delegate = property(getDelegate, setDelegate)
191 |
192 | class AccelerationConstraint(Constraint):
193 | def __init__(self, ID=None):
194 | if ID is not None:
195 | self.ID = ID
196 | else:
197 | self.ID = SCNAccelerationConstraint.accelerationConstraint()
198 |
199 | @classmethod
200 | def accelerationConstraint(cls):
201 | return cls()
202 |
203 | def setDamping(self, aDamping):
204 | self.ID.setDamping(aDamping)
205 | def getDamping(self):
206 | return self.ID.damping()
207 | damping = property(getDamping, setDamping)
208 |
209 | def setDecelerationDistance(self, aDist):
210 | self.ID.setDecelerationDistance(aDist)
211 | def getDecelerationDistance(self):
212 | return self.ID.decelerationDistance()
213 | decelerationDistance = property(getDecelerationDistance, setDecelerationDistance)
214 |
215 | def setMaximumLinearAcceleration(self, aMaximumLinearAcceleration):
216 | self.ID.setMaximumLinearAcceleration(aMaximumLinearAcceleration)
217 | def getMaximumLinearAcceleration(self):
218 | return self.ID.maximumLinearAcceleration()
219 | maximumLinearAcceleration = property(getMaximumLinearAcceleration, setMaximumLinearAcceleration)
220 |
221 | def setMaximumLinearVelocity(self, aMaximumLinearVelocity):
222 | self.ID.setMaximumLinearVelocity(aMaximumLinearVelocity)
223 | def getMaximumLinearVelocity(self):
224 | return self.ID.maximumLinearVelocity()
225 | maximumLinearVelocity = property(getMaximumLinearVelocity, setMaximumLinearVelocity)
226 |
227 | class SliderConstraint(Constraint):
228 | def __init__(self, ID=None):
229 | if ID is not None:
230 | self.ID = ID
231 | else:
232 | self.ID = SCNSliderConstraint.sliderConstraint()
233 |
234 | @classmethod
235 | def sliderConstraint(cls):
236 | return cls()
237 |
238 | def setCollisionCategoryBitMask(self, aMask):
239 | self.ID.setCollisionCategoryBitMask(aMask)
240 | def getCollisionCategoryBitMask(self):
241 | return self.ID.collisionCategoryBitMask()
242 | collisionCategoryBitMask = property(getCollisionCategoryBitMask, setCollisionCategoryBitMask)
243 |
244 | def setOffset(self, anOffset):
245 | self.ID.setOffset(vector3Make(anOffset))
246 | def getOffset(self):
247 | offset = self.ID.offset()
248 | return Vector3(offset.a, offset.b, offset.c)
249 | offset = property(getOffset, setOffset)
250 |
251 | def setRadius(self, aRadius):
252 | self.ID.setRadius(aRadius)
253 | def getRadius(self):
254 | return self.ID.radius()
255 | radius = property(getRadius, setRadius)
256 |
257 | class ReplicatorConstraint(Constraint, _Target):
258 | def __init__(self, target=None, ID=None):
259 | if ID is not None:
260 | self.ID = ID
261 | else:
262 | self.ID = SCNReplicatorConstraint.replicatorConstraintWithTarget_(target.ID)
263 |
264 | @classmethod
265 | def replicatorConstraintWithTarget(cls, target=None):
266 | return cls(target=target)
267 |
268 | def setOrientationOffset(self, anOrientationOffset):
269 | self.ID.setOrientationOffset(quaternionMake(anOrientationOffset))
270 | def getOrientationOffset(self):
271 | anOffset = self.ID.orientationOffset()
272 | return Quaternion(anOffset.a, anOffset.b, anOffset.c, anOffset.d)
273 | orientationOffset = property(getOrientationOffset, setOrientationOffset)
274 |
275 | def setPositionOffset(self, anOffset):
276 | self.ID.setPositionOffset(vector3Make(anOffset))
277 | def getPositionOffset(self):
278 | offset = self.ID.positionOffset()
279 | return Vector3(offset.a, offset.b, offset.c)
280 | positionOffset = property(getPositionOffset, setPositionOffset)
281 |
282 | def setReplicatesOrientation(self, aBool):
283 | self.ID.setReplicatesOrientation(aBool)
284 | def getReplicatesOrientation(self):
285 | return self.ID.replicatesOrientation()
286 | replicatesOrientation = property(getReplicatesOrientation, setReplicatesOrientation)
287 |
288 | def setReplicatesPosition(self, aBool):
289 | self.ID.setReplicatesPosition(aBool)
290 | def getReplicatesPosition(self):
291 | return self.ID.replicatesPosition()
292 | replicatesPosition = property(getReplicatesPosition, setReplicatesPosition)
293 |
294 | def setReplicatesScale(self, aBool):
295 | self.ID.setReplicatesScale(aBool)
296 | def getReplicatesScale(self):
297 | return self.ID.replicatesScale()
298 | replicatesScale = property(getReplicatesScale, setReplicatesScale)
299 |
300 | def setScaleOffset(self, aScaleOffset):
301 | self.ID.setScaleOffset(vector3Make(aScaleOffset))
302 | def getScaleOffset(self):
303 | anOffset = self.ID.scaleOffset()
304 | return Vector3(offset.a, offset.b, offset.c)
305 | scaleOffset = property(getScaleOffset, setScaleOffset)
306 |
307 | class IKConstraint(Constraint):
308 | def __init__(self, chainRootNode=None, ID=None):
309 | if ID is not None:
310 | self.ID = ID
311 | elif chainRootNode is not None:
312 | self.ID = SCNIKConstraint.inverseKinematicsConstraintWithChainRootNode_(chainRootNode.ID)
313 | else:
314 | self.ID = SCNIKConstraint.alloc()
315 |
316 | @classmethod
317 | def inverseKinematicsConstraintWithChainRootNode(cls, chainRootNode=None):
318 | return cls(chainRootNode=chainRootNode)
319 |
320 | def initWithChainRootNode(self, chainRootNode=None):
321 | self.ID.initWithChainRootNode_(chainRootNode.ID)
322 |
323 | def setChainRootNode(self, aChainRootNode):
324 | self.ID.setChainRootNode(aChainRootNode.ID)
325 | def getChainRootNode(self):
326 | return sceneKit.Node.outof(self.ID.chainRootNode())
327 | chainRootNode = property(getChainRootNode, setChainRootNode)
328 |
329 | def setMaxAllowedRotationAngleForJoint(self, aNode):
330 | self.ID.setMaxAllowedRotationAngleForJoint(aNode.ID)
331 | def getMaxAllowedRotationAngleForJoint(self):
332 | return self.ID.maxAllowedRotationAngleForJoint()
333 | maxAllowedRotationAngleForJoint = property(getMaxAllowedRotationAngleForJoint, setMaxAllowedRotationAngleForJoint)
334 |
335 | def setTargetPosition(self, aTargetPosition):
336 | self.ID.setTargetPosition(vector3Make(aTargetPosition))
337 | def getTargetPosition(self):
338 | aPos = self.ID.targetPosition()
339 | return Vector3(aPos.a, aPos.b, aPos.c)
340 | targetPosition = property(getTargetPosition, setTargetPosition)
341 |
342 | def avoidOccluderConstraint_didAvoidOccluder_forNode_(_self, _cmd, constraint, occluder, node):
343 | anAvoidOccluderConstraintDelegate = sceneKit.AvoidOccluderConstraintDelegate.outof(ObjCInstance(_self))
344 | if anAvoidOccluderConstraintDelegate.methods[0]:
345 | constraint = sceneKit.AvoidOccluderConstraint.outof(ObjCInstance(constraint))
346 | occluder = sceneKit.Node.outof(ObjCInstance(occluder))
347 | node = sceneKit.Node.outof(ObjCInstance(node))
348 | anAvoidOccluderConstraintDelegate.delegate.didAvoidOccluder(constraint, occluder, node)
349 |
350 | def avoidOccluderConstraint_shouldAvoidOccluder_forNode_(_self, _cmd, constraint, occluder, node):
351 | anAvoidOccluderConstraintDelegate = sceneKit.AvoidOccluderConstraintDelegate.outof(ObjCInstance(_self))
352 | if anAvoidOccluderConstraintDelegate.methods[1]:
353 | constraint = sceneKit.AvoidOccluderConstraint.outof(ObjCInstance(constraint))
354 | occluder = sceneKit.Node.outof(ObjCInstance(occluder))
355 | node = sceneKit.Node.outof(ObjCInstance(node))
356 | ret = anAvoidOccluderConstraintDelegate.delegate.shouldAvoidOccluder(constraint, occluder, node)
357 | return ret if ret is not None else False
358 |
359 | class AvoidOccluderConstraintDelegate(CInst):
360 | _actions = ('didAvoidOccluder', 'shouldAvoidOccluder')
361 | _newCClassName = 'SCNPYAvoidOccluderConstraintDelegateClass'
362 | _cMethodList = [avoidOccluderConstraint_didAvoidOccluder_forNode_, avoidOccluderConstraint_shouldAvoidOccluder_forNode_]
363 | avoidOccluderConstraint_didAvoidOccluder_forNode_.restype = None
364 | avoidOccluderConstraint_didAvoidOccluder_forNode_.argtypes = [c_void_p, c_void_p, c_void_p]
365 | avoidOccluderConstraint_shouldAvoidOccluder_forNode_.restype = c_bool
366 | avoidOccluderConstraint_shouldAvoidOccluder_forNode_.argtypes = [c_void_p, c_void_p, c_void_p]
367 | _protocols =['SCNAvoidOccluderConstraintDelegate']
368 | DelegateCClass = create_objc_class(_newCClassName, NSObject, methods=_cMethodList, protocols=_protocols)
369 |
370 | def __init__(self, aDelegate=None, ID=None):
371 | if ID is not None:
372 | self.ID = ID
373 | else:
374 | self.delegate = aDelegate
375 | self.methods = [callable(_a) for _a in [getattr(aDelegate, anAction, False) for anAction in AvoidOccluderConstraintDelegate._actions]]
376 | self.ID = AvoidOccluderConstraintDelegate.DelegateCClass.alloc().init().autorelease()
377 |
378 |
--------------------------------------------------------------------------------
/sceneKit/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | sceneKit top module for the sceneKit wrapper under Pythonista
3 |
4 | (c) Peter Ulbrich, 2019, peter.ulbrich@gmail.com
5 |
6 | Use of this source code is governed by the MIT license:
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
11 |
12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13 |
14 | '''
15 |
16 | from objc_util import *
17 | import ctypes
18 | import ui
19 | from collections import namedtuple, OrderedDict
20 | import os
21 |
22 | from .sceneKitEnv import *
23 | from .sceneKitNode import *
24 | from .sceneKitMaterial import *
25 | from .sceneKitCamera import *
26 | from .sceneKitGeometry import *
27 | from .sceneKitLight import *
28 | from .sceneKitConstraints import *
29 | from .sceneKitAnimation import *
30 | from .sceneKitSceneRenderer import *
31 | from .sceneKitGeometrySource import *
32 | from .sceneKitPhysics import *
33 | from .sceneKitParticleSystem import *
34 | from .sceneKitAudio import *
35 | # from .sceneKitSceneSource import * #not implemented due to limited use under runtime environment
36 |
37 |
38 | class ViewAutoresizing(Flag):
39 | AutoresizingNone = 0
40 | FlexibleLeftMargin = (1 << 0)
41 | FlexibleWidth = (1 << 1)
42 | FlexibleRightMargin = (1 << 2)
43 | FlexibleTopMargin = (1 << 3)
44 | FlexibleHeight = (1 << 4)
45 | FlexibleBottomMargin = (1 << 5)
46 |
47 | UIViewAutoresizingNone = 0
48 | UIViewAutoresizingFlexibleLeftMargin = (1 << 0)
49 | UIViewAutoresizingFlexibleWidth = (1 << 1)
50 | UIViewAutoresizingFlexibleRightMargin = (1 << 2)
51 | UIViewAutoresizingFlexibleTopMargin = (1 << 3)
52 | UIViewAutoresizingFlexibleHeight = (1 << 4)
53 | UIViewAutoresizingFlexibleBottomMargin = (1 << 5)
54 |
55 | _sceneAttributes = []
56 | for anAttribute in ['SCNSceneEndTimeAttributeKey', 'SCNSceneFrameRateAttributeKey', 'SCNSceneStartTimeAttributeKey', 'SCNSceneUpAxisAttributeKey']:
57 | _sceneAttributes.append(str(ObjCInstance(c_void_p.in_dll(c, anAttribute))))
58 | _sceneAttributes = tuple(_sceneAttributes)
59 |
60 | SCNSceneEndTimeAttributeKey, SCNSceneFrameRateAttributeKey, SCNSceneStartTimeAttributeKey, \
61 | SCNSceneUpAxisAttributeKey = _sceneAttributes
62 | SceneEndTimeAttributeKey, SceneFrameRateAttributeKey, SceneStartTimeAttributeKey, \
63 | SceneUpAxisAttributeKey = _sceneAttributes
64 |
65 | class AntialiasingMode(Enum):
66 | ModeNone = 0
67 | Multisampling2X = 1
68 | Multisampling4X = 2
69 | Multisampling8X = 3
70 | Multisampling16X = 4
71 | SCNAntialiasingModeNone = 0
72 | SCNAntialiasingModeMultisampling2X = 1
73 | SCNAntialiasingModeMultisampling4X = 2
74 | SCNAntialiasingModeMultisampling8X = 3
75 | SCNAntialiasingModeMultisampling16X = 4
76 |
77 |
78 | class View(SceneRenderer, CInst):
79 | def __init__(self, frame=None, superView=None, ID=None):
80 | if frame is not None:
81 | try:
82 | if len(frame) == 4:
83 | x, y, w, h = frame
84 | elif len(frame) == 2:
85 | (x, y), (w, h) = frame
86 | frame = CGRect((x, y),(w, h))
87 | except TypeError:
88 | pass
89 | self.ID = SCNView.alloc().initWithFrame_options_(frame, None).autorelease()
90 | if superView is not None:
91 | self.addToSuperview(superView)
92 | elif ID is not None:
93 | self.ID = ID
94 | else:
95 | self.ID = SCNView.alloc().init()
96 | if superView is not None:
97 | self.addToSuperview(superView)
98 |
99 | def addToSuperview(self, aUIView):
100 | aUIView.objc_instance.addSubview_(self.ID)
101 |
102 | def removeFromSuperview(self):
103 | self.ID.removeFromSuperview()
104 |
105 | def initWithFrame(self, frame=None):
106 | if frame is not None:
107 | try:
108 | if len(frame) == 4:
109 | x, y, w, h = frame
110 | elif len(frame) == 2:
111 | (x, y), (w, h) = frame
112 | frame = CGRect((x, y),(w, h))
113 | except TypeError:
114 | pass
115 | self.ID.initWithFrame_options_(frame, None)
116 |
117 | def setBackgroundColor(self, color):
118 | r, g, b, a = parse_color(color)
119 | self.ID.setBackgroundColor_(UIColor.color(red=r, green=g, blue=b, alpha=a))
120 | def getBackgroundColor(self):
121 | backgroundColor = self.ID.backgroundColor()
122 | return RGBA(backgroundColor.red(), backgroundColor.green(), backgroundColor.blue(), backgroundColor.alpha())
123 | backgroundColor = property(getBackgroundColor, setBackgroundColor)
124 | background_color = property(getBackgroundColor, setBackgroundColor)
125 |
126 | def setPreferredFramesPerSecond(self, aRate):
127 | self.ID.setPreferredFramesPerSecond(aRate)
128 | def getPreferredFramesPerSecond(self):
129 | return self.ID.preferredFramesPerSecond()
130 | preferredFramesPerSecond = property(getPreferredFramesPerSecond, setPreferredFramesPerSecond)
131 |
132 | def setRendersContinuously(self, aBool):
133 | self.ID.setRendersContinuously(aBool)
134 | def getRendersContinuously(self):
135 | return self.ID.rendersContinuously()
136 | rendersContinuously = property(getRendersContinuously, setRendersContinuously)
137 |
138 | def setAntialiasingMode(self, anAntialiasingMode):
139 | self.ID.setAntialiasingMode(anAntialiasingMode.value)
140 | def getAntialiasingMode(self):
141 | return AntialiasingMode(self.ID.antialiasingMode())
142 | antialiasingMode = property(getAntialiasingMode, setAntialiasingMode)
143 |
144 | def setAllowsCameraControl(self, control=True):
145 | self.ID.setAllowsCameraControl_(control)
146 | def getAllowsCameraControl(self):
147 | return self.ID.allowsCameraControl()
148 | allowsCameraControl = property(getAllowsCameraControl, setAllowsCameraControl)
149 |
150 | def getCameraControlConfiguration(self):
151 | return sceneKit.CameraControlConfiguration.outof(self.ID.cameraControlConfiguration())
152 | cameraControlConfiguration = property(getCameraControlConfiguration, None)
153 |
154 | def getDefaultCameraController(self):
155 | return sceneKit.CameraController.outof(self.ID.defaultCameraController())
156 | defaultCameraController = property(getDefaultCameraController, None)
157 |
158 | def pause(self, sender=None):
159 | self.ID.pause_(sender)
160 |
161 | def play(self, sender=None):
162 | self.ID.play_(sender)
163 |
164 | def stop(self, sender=None):
165 | self.ID.stop_(sender)
166 |
167 | def snapshot(self):
168 | uiImage = self.ID.snapshot()
169 | c.UIImagePNGRepresentation.argtypes = [c_void_p]
170 | c.UIImagePNGRepresentation.restype = c_void_p
171 | data = ObjCInstance(c.UIImagePNGRepresentation(uiImage.ptr))
172 | return Image.from_data(nsdata_to_bytes(data),2.0)
173 |
174 | def setAutoresizingMask(self, flags):
175 | try:
176 | iterator = iter(flags)
177 | except TypeError:
178 | flags = [flags]
179 | mask = 0
180 | for aFlag in flags:
181 | mask = mask | aFlag.value
182 | self.ID.setAutoresizingMask_(mask)
183 | def getAutoresizingMask(self):
184 | mask = self.ID.autoresizingMask()
185 | flags = []
186 | for aFlag in ViewAutoresizing:
187 | if (mask & aFlag.value) != 0: flags.append(aFlag)
188 | return tuple(flags)
189 | autoresizingMask = property(getAutoresizingMask, setAutoresizingMask)
190 |
191 | def setDrawableResizesAsynchronously(self, aBool):
192 | self.ID.setDrawableResizesAsynchronously_(aBool)
193 | def getDrawableResizesAsynchronously(self):
194 | return self.ID.drawableResizesAsynchronously()
195 | drawableResizesAsynchronously = property(getDrawableResizesAsynchronously, setDrawableResizesAsynchronously)
196 |
197 | def setFrame(self, aFrame):
198 | aFrame = CGRect(CGPoint(aFrame[0], aFrame[1]), CGSize(aFrame[2], aFrame[3]))
199 | self.ID.setFrame_(aFrame)
200 | def getFrame(self):
201 | ret = self.ID.frame()
202 | return (ret.origin.x, ret.origin.y, ret.size.width, ret.size.height)
203 | frame = property(getFrame, setFrame)
204 |
205 |
206 | class Scene(Animatable, CInst):
207 | def __init__(self, name=None, inDirectory= None, options=None, url=None, mdlAsset=None, ID=None):
208 | if name is not None and inDirectory is None:
209 | self.ID = SCNScene.sceneNamed_(name)
210 | elif name is not None and inDirectory is not None:
211 | self.ID = SCNScene.sceneNamed_inDirectory_options_(name, inDirectory, options)
212 | elif url is not None:
213 | error = c_void_p(0)
214 | ret = SCNScene.sceneWithURL_options_error_(nsurl(url), self.convertOptions(options), byref(error), restype=c_void_p, argtypes=[c_void_p, c_void_p, c_void_p])
215 | if ret is None:
216 | self.ID = None
217 | try:
218 | error = ObjCInstance(error)
219 | message = "sceneWithURL failed. Error: " + str(int(error.code())) + ' ' + str(error.localizedDescription())
220 | except AttributeError:
221 | message = "sceneWithURL failed. Wrong URL?"
222 | raise RuntimeError(message)
223 | else:
224 | self.ID = ret
225 | elif mdlAsset is not None:
226 | self.ID = SCNScene.sceneWithMDLAsset_(mdlAsset)
227 | if self.ID is None:
228 | raise RuntimeError('sceneWithMDLAsset failed. Wrong asset?')
229 | elif ID is not None:
230 | self.ID = ID
231 | else:
232 | self.ID = SCNScene.scene()
233 |
234 | @classmethod
235 | def scene(cls):
236 | return cls()
237 |
238 | @classmethod
239 | def sceneNamed(cls, name=None, inDiectory=None, options=None):
240 | return cls(name=name, inDiectory=inDiectory, options=options)
241 |
242 | @classmethod
243 | def sceneWithURL(cls, url=None, options=None):
244 | return cls(url=url, options=options)
245 |
246 | @classmethod
247 | def sceneWithMDLAsset(cls, mdlAsset=None):
248 | return cls(mdlAsset=mdlAsset)
249 |
250 | def convertOptions(self, options):
251 | if options is None: return None
252 | retOptions = {}
253 | for key, value in options.items():
254 | i = _sceneSourceLoadingOptions.index(key)
255 | if key == SceneSourceAnimationImportPolicyKey:
256 | i = _sceneSourceAnimationImportPolicies.index(value)
257 | elif key == SCNSceneSourceAssetDirectoryURLsKey:
258 | value = [nsurl(aValue) for aValue in value]
259 | retOptions[key] = value
260 | return retOptions
261 |
262 | def setPaused(self, aBool):
263 | self.ID.setPaused(aBool)
264 | def getPaused(self):
265 | return self.ID.paused()
266 | paused = property(getPaused, setPaused)
267 |
268 | def getRootNode(self):
269 | return Node.outof(self.ID.rootNode())
270 | rootNode = property(getRootNode, None)
271 |
272 | def getBackground(self):
273 | return MaterialProperty.outof(self.ID.background())
274 | background = property(getBackground, None)
275 |
276 | def getLightingEnvironment(self):
277 | return MaterialProperty.outof(self.ID.lightingEnvironment())
278 | lightingEnvironment = property(getLightingEnvironment, None)
279 |
280 | def attributeForKey(self, key):
281 | attribute = self.ID.attributeForKey_(key)
282 | if key == SCNSceneUpAxisAttributeKey and attribute is not None:
283 | attribute = attribute.SCNVector3Value()
284 | return Vector3(attribute.a, attribute.b, attribute.c)
285 | else:
286 | return attribute
287 |
288 | def setAttribute(self, attribute, key):
289 | if key == SCNSceneUpAxisAttributeKey:
290 | attribute = vector3Make(attribute)
291 | attribute = NSValue.valueWithSCNVector3_(attribute)
292 | self.ID.setAttribute_forKey_(attribute, key)
293 | def setAttributeForKey(self, attribute, key):
294 | self.setAttribute(attribute, key)
295 |
296 | def writeToURL(self, url=None, options=None, delegate=None, progressHandler=None):
297 | if delegate is not None:
298 | sceneWriteToUrlDelegate = SceneWriteToUrlDelegate(delegate)
299 | delegate = sceneWriteToUrlDelegate.ID
300 | if progressHandler is not None:
301 | self.sceneWriteToUrlProgressHandler = sceneWriteToUrlProgressHandlerBlock(progressHandler)
302 | progressHandler = self.sceneWriteToUrlProgressHandler.blockCode
303 | return self.ID.writeToURL_options_delegate_progressHandler_(nsurl(url), self.convertOptions(options), delegate, progressHandler)
304 |
305 | def setFogStartDistance(self, aFogStartDistance):
306 | self.ID.setFogStartDistance(aFogStartDistance)
307 | def getFogStartDistance(self):
308 | return self.ID.fogStartDistance()
309 | fogStartDistance = property(getFogStartDistance, setFogStartDistance)
310 |
311 | def setFogEndDistance(self, aFogEndDistance):
312 | self.ID.setFogEndDistance(aFogEndDistance)
313 | def getFogEndDistance(self):
314 | return self.ID.fogEndDistance()
315 | fogEndDistance = property(getFogEndDistance, setFogEndDistance)
316 |
317 | def setFogDensityExponent(self, aFogDensityExponent):
318 | self.ID.setFogDensityExponent(aFogDensityExponent)
319 | def getFogDensityExponent(self):
320 | return self.ID.fogDensityExponent()
321 | fogDensityExponent = property(getFogDensityExponent, setFogDensityExponent)
322 |
323 | def setFogColor(self, aFogColor):
324 | r, g, b, a = parse_color(aFogColor)
325 | self.ID.setFogColor_(ObjCClass('UIColor').color(red=r, green=g, blue=b, alpha=a))
326 | def getFogColor(self):
327 | aFogColor = self.ID.fogColor()
328 | return RGBA(aFogColor.red(), aFogColor.green(), aFogColor.blue(), aFogColor.alpha())
329 | fogColor = property(getFogColor, setFogColor)
330 |
331 | def getPhysicsWorld(self):
332 | return PhysicsWorld.outof(self.ID.physicsWorld())
333 | physicsWorld = property(getPhysicsWorld, None)
334 |
335 | def addParticleSystem(self, system=None, transform=Matrix4Identity, withTransform=None):
336 | if withTransform is not None: transform = withTransform
337 | self.ID.addParticleSystem_withTransform_(system.ID, matrix4Make(transform))
338 |
339 | def getParticleSystems(self):
340 | return tuple([sceneKit.ParticleSystem.outof(aSystem) for aSystem in self.ID.particleSystems()])
341 | particleSystems = property(getParticleSystems, None)
342 |
343 | def removeParticleSystem(self, system=None):
344 | self.ID.removeParticleSystem_(system.ID)
345 |
346 | def removeAllParticleSystems(self):
347 | self.ID.removeAllParticleSystems()
348 |
349 | def writeImage_withSceneDocumentURL_originalImageURL_(_self, _cmd, ximage, xdocumentURL, xoriginalImageURL):
350 | aWriteToUrlDelegate = sceneKit.SceneWriteToUrlDelegate.outof(ObjCInstance(_self))
351 | if aWriteToUrlDelegate.methods[0]:
352 | if ximage is not None:
353 | ximage = ObjCInstance(ximage)
354 | ximage = contentFromC(ximage)
355 | if xdocumentURL is not None:
356 | xdocumentURL = ObjCInstance(xdocumentURL)
357 | xdocumentURL = contentFromC(xdocumentURL)
358 | if xoriginalImageURL is not None:
359 | xoriginalImageURL = ObjCInstance(xoriginalImageURL)
360 | xoriginalImageURL = contentFromC(xoriginalImageURL)
361 | ret =aWriteToUrlDelegate.delegate.writeImage(ximage, xdocumentURL, xoriginalImageURL)
362 | return nsurl(ret) if ret is not None else None
363 |
364 | class SceneWriteToUrlDelegate(CInst):
365 | _actions = ('writeImage',)
366 | _newCClassName = 'SCNPYWriteToUrlDelegateClass'
367 | _cMethodList = [writeImage_withSceneDocumentURL_originalImageURL_]
368 | writeImage_withSceneDocumentURL_originalImageURL_.restype = c_void_p
369 | writeImage_withSceneDocumentURL_originalImageURL_.argtypes = [c_void_p, c_void_p, c_void_p]
370 | _protocols = ['SCNSceneExportDelegate']
371 | DelegateCClass = create_objc_class(_newCClassName, NSObject, methods=_cMethodList, protocols=_protocols)
372 |
373 | def __init__(self, aDelegate=None, ID=None):
374 | if ID is not None:
375 | self.ID = ID
376 | else:
377 | self.delegate = aDelegate
378 | self.methods = [callable(_a) for _a in [getattr(aDelegate, anAction, False) for anAction in WriteToUrlDelegate._actions]]
379 | self.ID = WriteToUrlDelegate.DelegateCClass.alloc().init()
380 |
381 | class sceneWriteToUrlProgressHandlerBlock:
382 | def __init__(self, block):
383 | self.blockCode = ObjCBlock(self.blockInterface, restype=None, argtypes=[c_void_p, c_float, c_void_p, POINTER(c_bool)])
384 | self.pCode = block
385 |
386 | def blockInterface(self, _cmd, totalProgress, xError, xStop):
387 | totalProgress = float(totalProgress)
388 | try:
389 | xError = ObjCInstance(xError)
390 | code = xError.code()
391 | except AttributeError:
392 | xError = None
393 | stop = self.pCode(totalProgress, xError)
394 | if stop is None: stop = False
395 | xStop[0] = stop
396 |
--------------------------------------------------------------------------------