#!/usr/bin/env python # coding: utf-8 # In[414]: from IPython.core.display import HTML import numpy as np import math # [x3d](https://pypi.org/project/x3d) is a [PyPI](https://pypi.org) distributed Python package maintained by the # [Web3D Consortium](https://web3d.org). See also a local help file [x3d.x3d](x3d.x3d.html) explaining the import statement # # `from x3d import x3d` . # In[415]: from x3d import x3d # [common_geometry](common_geometry.html) is a Python package, local to this Jupyter notebook folder, which defines convenience classes for generating X3D elements with the geometric definitition pertinent to this notebook. See [common_geometry.py](common_geometry.py) for Python source code; [common_geometry](common_geometry.html) for documentation autogenerated by the [pydoc](https://docs.python.org/3/library/pydoc.html) documentation system. # # In[416]: import common_geometry # In[417]: alpha_degrees = 30.0 # the half angle of the cone beta_degrees = 30.0 # the angle the intersecting plane makes with horizontal a = 1.0 # distance of intersecting plane to cone vertex f = 2.5 # total height of cone if (alpha_degrees + beta_degrees >= 90.0): raise AssertionError("choice of alpha , beta angles geometrically invalid") # In[418]: class InscribedSphere: """ data structure with named fields describing sphere inscribed (internally tangent) to a cone with intersection with plane """ def __init__(self): self.center = np.array((0.,0.,0.)) self.radius = 0.0 # focus is the point of tangency with the plane self.focus = np.array((0.,0.,0.)) # contact_circle is the circular section where the sphere is in tangent contact with the cone self.contact_circle_center = ((0.,0.,0.)) self.contact_circle_radius = 0. def contact_circle_point(self, theta): """ returns a (3,) array of a point on the circle, theta a cclockwise rotation (relative to y axis) from the x axis """ return self.contact_circle_center + self.contact_circle_radius * np.array((math.cos(theta),0.0, math.sin(theta))) # In[419]: # convert angles to radians alpha = math.radians(alpha_degrees) beta = math.radians(beta_degrees) # In[420]: # coordinates of points and direction vectors pointC = np.array((0.,0.,0.)) axis = np.array((0.,-1.,0.)) # axis of cone, pointing to open end pointB = pointC - a * axis z = np.array((0.,0.,1.0)) # global z axis normal = np.array((math.sin(beta), math.cos(beta),0.0)) ua = np.cross(z, normal) # a unit vector in inclined plane, largely in -x direction for beta > 0 # will be along semimajor axis of ellipse ub = np.cross(normal, ua) # will be along semiminor axis # ### Evaluation of geometry of the upper inscribed sphere # # ∡CBA = alpha # # ∡ACB = pi/2 - beta # # ∣AD is bisector of ∡BAC # # DE ⊥ AC # # # # # # # ![Drawing of upper sphere](top_drawing.png) # In[421]: angCBA=alpha # alpha is half angle of cone angBAC= math.pi/2 + beta - alpha # angles in triangle sum to pi angDAC= angBAC/2 # AD segment is angle bisector of angle BAC angACD= math.pi/2 - beta # beta is angle that segment AC makes with horizontal angCDA= math.pi - angDAC - angACD # angles in triangle sum to pi # now apply law of sines disBC = a # definition of cone dimension a disAC = math.sin(angCBA) * disBC / math.sin(angBAC) # law of sines disCD = math.sin(angDAC) * disAC / math.sin(angCDA) # law of sines disBD = disBC - disCD disDH = disBD * math.sin(angCBA) disDF = disDH * math.sin(angCBA) disFH = disDH * math.cos(angCBA) disDE = disCD * math.sin(angACD) disCE = disCD * math.cos(angACD) # In[ ]: # In[422]: upperSphere = InscribedSphere() upperSphere.radius = disDE upperSphere.center = pointC - disCD * axis upperSphere.focus = pointC + disCE * ua upperSphere.contact_circle_center = upperSphere.center - disDF * axis upperSphere.contact_circle_radius = disFH # In[ ]: # #### Evaluation of geometry of the lower inscribed sphere # # ∡CBA = alpha # # ∡ACB = pi/2 - beta # # ∣AD is bisector of ∡BAC # # DE ⊥ AC # # In[423]: disBC = a angCBA=alpha angACB= math.pi/2 + beta angBAC= math.pi - angCBA - angACB angCAD = (math.pi-angBAC)/2 angDCA = math.pi/2 - beta angADC = math.pi - angCAD - angDCA disAC = math.sin(angCBA) * disBC/math.sin(angBAC) disCD = math.sin(angCAD) * disAC/math.sin(angADC) disAD = math.sin(angDCA) * disAC/math.sin(angADC) disDE = disAD * math.sin(angCAD) disCE = disCD * math.cos(angDCA) disBD = disBC + disCD disDH = disBD * math.sin(angCBA) disDF = disDH * math.sin(angCBA) disFH = disDH * math.cos(angCBA) # In[424]: lowerSphere = InscribedSphere() lowerSphere.radius = disDE lowerSphere.center = pointC + disCD * axis lowerSphere.focus = pointC - disCE * ua lowerSphere.contact_circle_center = lowerSphere.center - disDF * axis lowerSphere.contact_circle_radius = disFH # In[425]: sc = x3d.Scene() sc.children.append( x3d.Background(skyColor = [(1,1,1)]) ) cone_shape = common_geometry.Cone( vertex_height = a, total_height = f, half_angle = alpha, appearance = x3d.Appearance( material = x3d.Material( diffuseColor=(0.0,0.5,0.5),transparency=0.75) ) ) plane_shape = common_geometry.TiltedPlane( length = 2.5, width = 2.5, beta = beta, appearance= x3d.Appearance( material = x3d.Material( emissiveColor=(0.8,0.6,0.6), diffuseColor=(0.8,0.6,0.6),transparency=0.2)) ) upper_sphere = common_geometry.Sphere( center = upperSphere.center, radius = upperSphere.radius * 0.995 , appearance = x3d.Appearance( material = x3d.Material( diffuseColor=(1,1,0), transparency=0.0) ) ) upper_focus = common_geometry.Sphere( center = upperSphere.focus, radius = 0.02 , appearance = x3d.Appearance( material = x3d.Material( diffuseColor=(0,0,0), transparency=0.0) ) ) contact_circle_appearance = x3d.Appearance( lineProperties = x3d.LineProperties( linewidthScaleFactor=1 ), material= x3d.Material( emissiveColor = (0,0,1)) ) upper_contact_circle = common_geometry.Circle( center = upperSphere.contact_circle_center, radius = upperSphere.contact_circle_radius, appearance = contact_circle_appearance ) lower_sphere = common_geometry.Sphere( center = lowerSphere.center, radius = lowerSphere.radius * 0.995, appearance = x3d.Appearance( material = x3d.Material( diffuseColor=(1,1,0.0)) ) ) lower_focus = common_geometry.Sphere( center = lowerSphere.focus, radius = 0.02 , appearance = x3d.Appearance( material = x3d.Material( diffuseColor=(0,0,0), transparency=0.0) ) ) lower_contact_circle = common_geometry.Circle( center = lowerSphere.contact_circle_center, radius = lowerSphere.contact_circle_radius, appearance = contact_circle_appearance ) figure = x3d.Transform( rotation=(0, -1, 0 ,math.radians(20.0)), children = [ cone_shape, plane_shape, upper_sphere, upper_contact_circle, upper_focus, lower_sphere, lower_contact_circle, lower_focus ] ) sc.children.append(figure) # Explicitly construct the ellipse which is the intersection between the plane and the cone. # # Will start with a circle, then stretch it by different scales in x and z axis to be the appropriate # ellipse, then rotate it around the z axis # In[426]: ellipse_center = 0.5 * (lowerSphere.focus + upperSphere.focus) # c is 1/2 distance between foci c = 0.5 * np.sqrt( np.square( lowerSphere.focus - upperSphere.focus).sum()) semimajor = 0.5 * np.sqrt( np.square( lowerSphere.contact_circle_center - upperSphere.contact_circle_center).sum())/ math.cos(alpha) semiminor = np.sqrt( np.square(semimajor) - np.square(c)) print("semiminor axis: %.5f" % semiminor) print("semimajor axis: %.5f" % semimajor) def ellipse_point(theta): """ for theta an angle in radians, returns point on ellipse that makes angle theta measured from center relative to major axis """ return ellipse_center + \ math.cos(theta) * semimajor * ua + \ math.sin(theta) * semiminor * ub # In[427]: ellipse_line_properties = x3d.LineProperties( applied=True, linewidthScaleFactor=3) ellipse_material = x3d.Material(emissiveColor=(0,0,0)) appearance = x3d.Appearance( lineProperties = ellipse_line_properties, material=ellipse_material ) circular_shape = x3d.Transform( rotation = (1.0, 0.0, 0.0, math.pi/2), children=[ x3d.Shape( geometry = x3d.Circle2D(radius=1.0), appearance = appearance ) ] ) ellipse_shape = x3d.Transform( translation = tuple(ellipse_center), children=[x3d.Transform( rotation=(0 , 0, -1, beta), children=[ x3d.Transform( scale = (semimajor,1,semiminor), children = [circular_shape] ) ] ) ] ) figure.children.append(ellipse_shape) # In[428]: # create polyline: upper focus to ellipse to upper contact circle theta = math.pi * 0.75 ep = ellipse_point(theta) phi = math.atan2(ep[2], ep[0]) print("phi %.3f" % phi) polyline_vertices = x3d.Coordinate( [tuple(p) for p in \ [ upperSphere.focus, ellipse_point(theta), upperSphere.contact_circle_point(phi), lowerSphere.focus, ellipse_point(theta), lowerSphere.contact_circle_point(phi), ] ] ) polyline_vertices.DEF='foci_segments' polyline_indices = [0,1,2,-1,3,4,5,-1] polyline_colors = x3d.Color(color=[(0,0,1),(1,0,0)]) polyline = x3d.Shape( geometry = x3d.IndexedLineSet( colorPerVertex=False, coord = polyline_vertices, color = polyline_colors, coordIndex = polyline_indices, colorIndex = [0,1], ), appearance = x3d.Appearance( lineProperties = ellipse_line_properties, material=ellipse_material ) ) figure.children.append(polyline) # In[429]: NumKeyFrame = 32 keys = np.linspace(0.0, 1.0, NumKeyFrame+1, endpoint=True) key_values = list() for frac in keys: theta = 2. * math.pi * frac ep = ellipse_point(theta) phi = math.atan2(ep[2], ep[0]) value = [tuple(p) for p in \ [ upperSphere.focus, ep, upperSphere.contact_circle_point(phi), lowerSphere.focus, ep, lowerSphere.contact_circle_point(phi), ] ] key_values.extend( value ) print(key_values) interpolator = x3d.CoordinateInterpolator( key = list(keys), keyValue = key_values, DEF = 'foci_segments_interpolator' ) sc.children.append(interpolator) route1 = x3d.ROUTE( fromNode = 'foci_segments_interpolator', fromField = 'value_changed', toNode = 'foci_segments', toField = 'point' ) sc.children.append(route1) time_sensor=x3d.TimeSensor( DEF = 'timer1', cycleInterval = 5.0, enabled=True, loop = True, ) sc.children.append(time_sensor) sc.children.append( x3d.ROUTE( fromNode="timer1", fromField='fraction_changed', toNode = 'foci_segments_interpolator', toField = 'set_fraction' ) ) # In[430]: get_ipython().run_cell_magic('html', '', ' \n \n\n') # In[431]: x3dnode = """\ %s """ % sc.HTML5() #print(x3dnode) HTML(x3dnode) # In[432]: if (True): print(sc.XML()) # In[ ]: # In[ ]: