## EllipseLayout.py Version 1.00

##  Copyright (c) 2006 Bruce Vaughan, BV Detailing & Design, Inc.

############################################################################

"""

Revision History:

Version 1.00 (11/12/06) - Development version

Version 1.01 (11/12/06) - Removed print statements used for debugging

material coordinates instead of member coordinates

Select three points in your current plane in a counter-clockwise direction:

1. The ellipse center point (Point 1)

2. A point representing an axis end point in the ellipse 'x' direction (Point 2)

From this point the 'x' semi-axis default dimension and default ellipse rotation is calculated.

3. Any point in the current plane that is not collinear with the other two (Point 3)

From this point the ellipse 'y' semi-axis default dimension is calculated.

The third point should be picked counter-clockwise with respect to the other two points.

Three non-collinear points are required to define a plane.

Dialog box selections:

1. The quadrant to layout the ellipse (one quadrant is laid out at a time)

2. Number of layout spaces between points (ellipse resolution)

3. Axis dimensions and rotation

Algorithim:

Add a miscellaneous member representing the unit vector of the defined plane (WP)

Divide the quadrant (90 degrees) by resolution

Resolution rays originate at member WP (0,0,0) (Point 1)

Calculate member WP -z (ellipse x) and y (ellipse y) coordinates of the intersection of each

resolution ray with the ellipse

Rotate each calculated point about member WP by the rotation angle of the ellipse

Add miscellaneous member at each rotated resolution point (RP)

"""

def run_script():

import macrolib.pickle

from macrolib.FileDefaults import import_data, export_data, data_Defaults_path

from macrolib.angle import rtod, dtor

from macrolib.P3D import Plane3D

from macrolib.PointRotate import PointRotate3D

from math import atan2, tan, cos, sin, pi

from param import yes_or_no, ResponseNotOK, Units, Dialog, dim_print, Warning, dim

import os

import sys

Units("feet")

from point import Point, PointLocate

from member import Member

from cons_line import ConsLine

# from rnd_bar import RndBar

#######################################################

## Variables Section

# system path for default values files

# auto save path

default_file_path = data_Defaults_path()

# default values file name

def_file = "EllipseLayout_v1_01.txt"

# default to enable or disable the automatic importing and exporting of dialog dictionary variables

# ("Enable", "Disable")

enable_default_import_export = "Enable"

image_path = os.path.join(os.getcwd(), "macro", "Images")

image_name1 = os.path.join(image_path, "EllipseLayout1.gif")

image_name2 = os.path.join(image_path, "EllipseLayout2.gif")

## Defaults Section

resEllipse = 6

quadEllipse = "0-90"        # ("0-90", "90-180", "180-270", "270-360")

"""

# Default values are calculated from user input:

axis1Ellipse

axis2Ellipse

rotEllipse

"""

#######################################################

# cannot assign existing = "Yes"

# cannot assign sequence to miscellaneous member

# description is ignored

#######################################################

while 1:

# Auto import default values file

if enable_default_import_export == "Enable":

dd1 = import_data(os.path.join(default_file_path, def_file))

if dd1:

for key, value in dd1.items():

exec "%s = %s" % (key, repr(value)) in None

else:

Warning("Invalid data or defaults file does not exist - Reverting to original defaults")

###################################################

ptWP = PointLocate ("Pick the ellipse center point                                   ")

ptAxis = PointLocate("Pick an ellipse 'X' direction axis WP")

ptPlane = PointLocate("Pick another point counter-clockwise to define the current plane")

if None not in (ptWP, ptAxis, ptPlane):

# create class instance 'a'

a = Plane3D(ptWP, ptAxis, ptPlane)

mem1 = add_RP(ptWP, ptWP + a.N_uv, "WP")

# calculate rotation of ellipse with respect to mem1.main_mtrl()

ptTem = mem1.main_mtrl().to_local(ptAxis - ptWP)

if round(ptTem.z, 4) == 0.0:

if ptTem.y > 0.0:

rotEllipse = pi/2

else:

rotEllipse = -pi/2

else:

rotEllipse = atan2(ptTem.y, -ptTem.z)

###############################################################

##  DIALOG BOX 1 ---------------------------------------------#

###############################################################

dlg1 = Dialog("Ellipse Layout")

dlg1.menu("print_doc", ("Yes", "No"), "No", "Print parametric script documentation only         ")

dlg1.tabset_begin()

dlg1.tab("General Information")

dlg1.entry("axis1Ellipse", dim_print(ptWP.dist(ptAxis)), "Ellipse semi-axis dimension 'X'                       ")

dlg1.entry("axis2Ellipse", dim_print(ptWP.dist(ptPlane)), "Ellipse semi-axis dimension 'Y'")

dlg1.entry("resEllipse", resEllipse, "Ellipse resolution")

dlg1.entry("rotEllipse", rtod(rotEllipse), "Ellipse rotation with respect to current plane")

dlg1.tab("Image 1")

dlg1.group_title("Ellipse laid out in plane of beam top flange")

dlg1.image(image_name1)

dlg1.tab("Image 2")

dlg1.group_title("Five 3 point construction circles approximate the ellipse")

dlg1.image(image_name2)

dlg1.group_title_end

try:

dd1 = dlg1.done()

except ResponseNotOK:

break

# Update the local namespace

for key, value in dd1.items():

exec "%s = %s" % (key, repr(value)) in None

###############################################################

## END DIALOG BOX 1 ------------------------------------------#

###############################################################

rotEllipse = dtor(rotEllipse)

if print_doc == "Yes":

print __doc__

break

# Auto export default values to disk if enabled

if enable_default_import_export == "Enable":

export_data(os.path.join(default_file_path, def_file), dd1)

"""

Calculate points along ellipse in mem1.main_mtrl() coordinates before rotating

Equation of an ellipse: (x**2/b**2) + (y**2/h**2) = 1

Equation of resolution ray: mx-y = 0 where m = tan(A)

Substituting and solving for 'x': x=(b**2*h**2/(h**2+b*m**2))**0.5

Solving for 'y': y=(((b**2*h**2)-(h**2*x**2))/b**2)**0.5

"""

# initialize local point list

localRP_list = []

rayIncrement = (pi/2.0)/resEllipse

for i in range(resEllipse + 1):

xAxis = axis1Ellipse            # b

yAxis = axis2Ellipse            # h

xOff = (xAxis**2*yAxis**2/(yAxis**2+(xAxis*tan(rayIncrement * (i)))**2))**0.5

yOff = (((xAxis**2*yAxis**2)-(yAxis**2*xOff**2))/xAxis**2)**0.5

localRP_list.append(Point(0.0, yOff, -xOff))

for pt in localRP_list:

pt.z = -pt.z

for pt in localRP_list:

pt.y = -pt.y

pt.z = -pt.z

for pt in localRP_list:

pt.y = -pt.y

else: # quadEllipse == "0-90", do nothing

pass

# rotate points into position

# initialize a new list to store rotated points

localRP_rot = []

for pt in localRP_list:

localRP_rot.append(PointRotate3D(Point(0.0, 0.0, 0.0), Point(1.0, 0.0, 0.0), pt, rotEllipse))

for pt in localRP_rot:

pt1 = ptWP + mem1.main_mtrl().translate(pt.x, pt.y, pt.z)

if not yes_or_no("Layout another ellipse quadrant?"):

break

## End run_script() #################################################

if __name__ == '__main__':

try:

run_script()

finally:

del run_script