# Copyright (c) 2024-2026 Karana Dynamics Pty Ltd. All rights reserved.
#
# NOTICE TO USER:
#
# This source code and/or documentation (the "Licensed Materials") is
# the confidential and proprietary information of Karana Dynamics Inc.
# Use of these Licensed Materials is governed by the terms and conditions
# of a separate software license agreement between Karana Dynamics and the
# Licensee ("License Agreement"). Unless expressly permitted under that
# agreement, any reproduction, modification, distribution, or disclosure
# of the Licensed Materials, in whole or in part, to any third party
# without the prior written consent of Karana Dynamics is strictly prohibited.
#
# THE LICENSED MATERIALS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
# KARANA DYNAMICS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
# BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND
# FITNESS FOR A PARTICULAR PURPOSE.
#
# IN NO EVENT SHALL KARANA DYNAMICS BE LIABLE FOR ANY DAMAGES WHATSOEVER,
# INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS, DATA, OR USE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, WHETHER IN CONTRACT, TORT,
# OR OTHERWISE ARISING OUT OF OR IN CONNECTION WITH THE LICENSED MATERIALS.
#
# U.S. Government End Users: The Licensed Materials are a "commercial item"
# as defined at 48 C.F.R. 2.101, and are provided to the U.S. Government
# only as a commercial end item under the terms of this license.
#
# Any use of the Licensed Materials in individual or commercial software must
# include, in the user documentation and internal source code comments,
# this Notice, Disclaimer, and U.S. Government Use Provision.
import Karana.Scene as ks
import Karana.Math as km
import Karana.Core as kc
[docs]
class Axes3d:
"""Create a 3d axes visualization."""
@property
def name(self) -> str:
"""Get the name of the Axes3d.
Returns
-------
str
The name
"""
return self._name
@property
def node(self) -> ks.ProxySceneNode:
"""Get the base node of the axes.
Returns
-------
ks.ProxySceneNode
The node
"""
if nd := self._node():
return nd
else:
raise ValueError("The underlying ProxySceneNode has been removed.")
def __init__(self, name: str, scene: ks.ProxyScene, length: float = 1.0, radius: float = 0.1):
"""Create an Axes3d.
Parameters
----------
name: str
Name of the Axes3d
scene: ks.ProxyScene
Scene to create the parts in.
length: float
Length of each axis. Defaults to 1.
radius: float
Radius of each axis. Defaults to 0.1.
"""
self._name = name
node = ks.ProxySceneNode(f"{name}_node", scene)
self._node = kc.CppWeakRef(node)
# Create geometries
cone_length = radius
cylinder_length = length - cone_length
cylinder = ks.CylinderGeometry(radius=0.5 * radius, height=cylinder_length)
cone = ks.ConeGeometry(radius=radius, height=cone_length)
# Create rgb emissive materials
mat_info = ks.PhongMaterialInfo()
mat_info.color = ks.Color.BLACK
mat_info.ambient_color = ks.Color.BLACK
# add some shininess
mat_info.specular_color = ks.Color.fromHex("444444")
mat_info.emissive_color = ks.Color.RED
red = ks.PhongMaterial(mat_info)
mat_info.emissive_color = ks.Color.GREEN
green = ks.PhongMaterial(mat_info)
mat_info.emissive_color = ks.Color.BLUE
blue = ks.PhongMaterial(mat_info)
# Quaternions to reorient parts
y2x = km.UnitQuaternion([0, 1, 0], [1, 0, 0])
y2z = km.UnitQuaternion([0, 1, 0], [0, 0, 1])
# Offsets to position part centers relative to parent
cyl_offset = cylinder_length / 2
cone_offset = cylinder_length + cone_length / 2
# x cylinder part
x_cyl = ks.ProxyScenePart(f"{name}_xcyl", scene, cylinder, red, layers=ks.LAYER_ORNAMENTAL)
x_cyl.attachTo(node)
x_cyl.setUnitQuaternion(y2x)
x_cyl.setTranslation([cyl_offset, 0, 0])
# x cone part
x_cone = ks.ProxyScenePart(f"{name}_xcone", scene, cone, red, layers=ks.LAYER_ORNAMENTAL)
x_cone.attachTo(node)
x_cone.setUnitQuaternion(y2x)
x_cone.setTranslation([cone_offset, 0, 0])
# y cylinder part
y_cyl = ks.ProxyScenePart(
f"{name}_ycyl", scene, cylinder, green, layers=ks.LAYER_ORNAMENTAL
)
y_cyl.attachTo(node)
y_cyl.setTranslation([0, cyl_offset, 0])
# y cone part
y_cone = ks.ProxyScenePart(f"{name}_ycone", scene, cone, green, layers=ks.LAYER_ORNAMENTAL)
y_cone.attachTo(node)
y_cone.setTranslation([0, cone_offset, 0])
# z cylinder part
z_cyl = ks.ProxyScenePart(f"{name}_zcyl", scene, cylinder, blue, layers=ks.LAYER_ORNAMENTAL)
z_cyl.attachTo(node)
z_cyl.setUnitQuaternion(y2z)
z_cyl.setTranslation([0, 0, cyl_offset])
# z cone part
z_cone = ks.ProxyScenePart(f"{name}_zcone", scene, cone, blue, layers=ks.LAYER_ORNAMENTAL)
z_cone.attachTo(node)
z_cone.setUnitQuaternion(y2z)
z_cone.setTranslation([0, 0, cone_offset])