6 from __future__ import absolute_import
8 from fabmetheus_utilities.geometry.creation import lineation
9 from fabmetheus_utilities.geometry.geometry_tools import path
10 from fabmetheus_utilities.geometry.geometry_utilities import evaluate
11 from fabmetheus_utilities import euclidean
16 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
17 __credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
18 __date__ = '$Date: 2008/02/05 $'
19 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
22 def addGridRow(diameter, gridPath, loopsComplex, maximumComplex, rowIndex, x, y, zigzag):
25 while x < maximumComplex.real:
27 if euclidean.getIsInFilledRegion(loopsComplex, point):
30 if zigzag and rowIndex % 2 == 1:
34 def getGeometryOutput(elementNode):
35 'Get vector3 vertexes from attribute dictionary.'
36 derivation = GridDerivation(elementNode)
37 diameter = derivation.radius + derivation.radius
38 typeStringTwoCharacters = derivation.typeString.lower()[: 2]
39 typeStringFirstCharacter = typeStringTwoCharacters[: 1]
40 topRight = complex(derivation.demiwidth, derivation.demiheight)
41 loopsComplex = [euclidean.getSquareLoopWiddershins(-topRight, topRight)]
42 if len(derivation.target) > 0:
43 loopsComplex = euclidean.getComplexPaths(derivation.target)
44 maximumComplex = euclidean.getMaximumByComplexPaths(loopsComplex)
45 minimumComplex = euclidean.getMinimumByComplexPaths(loopsComplex)
47 if typeStringTwoCharacters == 'he':
48 gridPath = getHexagonalGrid(diameter, loopsComplex, maximumComplex, minimumComplex, derivation.zigzag)
49 elif typeStringTwoCharacters == 'ra' or typeStringFirstCharacter == 'a':
50 gridPath = getRandomGrid(derivation, diameter, elementNode, loopsComplex, maximumComplex, minimumComplex)
51 elif typeStringTwoCharacters == 're' or typeStringFirstCharacter == 'e':
52 gridPath = getRectangularGrid(diameter, loopsComplex, maximumComplex, minimumComplex, derivation.zigzag)
54 print('Warning, the step type was not one of (hexagonal, random or rectangular) in getGeometryOutput in grid for:')
55 print(derivation.typeString)
58 loop = euclidean.getVector3Path(gridPath)
59 elementNode.attributes['closed'] = 'false'
60 return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop, 0.5 * math.pi))
62 def getGeometryOutputByArguments(arguments, elementNode):
63 'Get vector3 vertexes from attribute dictionary by arguments.'
64 if len(arguments) < 1:
65 return getGeometryOutput(elementNode)
66 inradius = 0.5 * euclidean.getFloatFromValue(arguments[0])
67 elementNode.attributes['inradius.x'] = str(inradius)
68 if len(arguments) > 1:
69 inradius = 0.5 * euclidean.getFloatFromValue(arguments[1])
70 elementNode.attributes['inradius.y'] = str(inradius)
71 return getGeometryOutput(elementNode)
73 def getHexagonalGrid(diameter, loopsComplex, maximumComplex, minimumComplex, zigzag):
75 diameter = complex(diameter.real, math.sqrt(0.75) * diameter.imag)
76 demiradius = 0.25 * diameter
77 xRadius = 0.5 * diameter.real
78 xStart = minimumComplex.real - demiradius.real
79 y = minimumComplex.imag - demiradius.imag
82 while y < maximumComplex.imag:
86 addGridRow(diameter, gridPath, loopsComplex, maximumComplex, rowIndex, x, y, zigzag)
91 def getIsPointInsideZoneAwayOthers(diameterReciprocal, loopsComplex, point, pixelDictionary):
92 'Determine if the point is inside the loops zone and and away from the other points.'
93 if not euclidean.getIsInFilledRegion(loopsComplex, point):
95 pointOverDiameter = complex(point.real * diameterReciprocal.real, point.imag * diameterReciprocal.imag)
96 squareValues = euclidean.getSquareValuesFromPoint(pixelDictionary, pointOverDiameter)
97 for squareValue in squareValues:
98 if abs(squareValue - pointOverDiameter) < 1.0:
100 euclidean.addElementToPixelListFromPoint(pointOverDiameter, pixelDictionary, pointOverDiameter)
103 def getNewDerivation(elementNode):
104 'Get new derivation.'
105 return GridDerivation(elementNode)
107 def getRandomGrid(derivation, diameter, elementNode, loopsComplex, maximumComplex, minimumComplex):
108 'Get rectangular grid.'
110 diameterReciprocal = complex(1.0 / diameter.real, 1.0 / diameter.imag)
111 diameterSquared = diameter.real * diameter.real + diameter.imag * diameter.imag
112 elements = int(math.ceil(derivation.density * euclidean.getAreaLoops(loopsComplex) / diameterSquared / math.sqrt(0.75)))
113 elements = evaluate.getEvaluatedInt(elements, elementNode, 'elements')
114 failedPlacementAttempts = 0
116 if derivation.seed != None:
117 random.seed(derivation.seed)
118 successfulPlacementAttempts = 0
119 while failedPlacementAttempts < 100:
120 point = euclidean.getRandomComplex(minimumComplex, maximumComplex)
121 if getIsPointInsideZoneAwayOthers(diameterReciprocal, loopsComplex, point, pixelDictionary):
122 gridPath.append(point)
123 euclidean.addElementToPixelListFromPoint(point, pixelDictionary, point)
124 successfulPlacementAttempts += 1
126 failedPlacementAttempts += 1
127 if successfulPlacementAttempts >= elements:
131 def getRectangularGrid(diameter, loopsComplex, maximumComplex, minimumComplex, zigzag):
132 'Get rectangular grid.'
133 demiradius = 0.25 * diameter
134 xStart = minimumComplex.real - demiradius.real
135 y = minimumComplex.imag - demiradius.imag
138 while y < maximumComplex.imag:
139 addGridRow(diameter, gridPath, loopsComplex, maximumComplex, rowIndex, xStart, y, zigzag)
144 def processElementNode(elementNode):
145 'Process the xml element.'
146 path.convertElementNode(elementNode, getGeometryOutput(elementNode))
149 class GridDerivation(object):
150 'Class to hold grid variables.'
151 def __init__(self, elementNode):
153 self.inradius = lineation.getInradius(complex(10.0, 10.0), elementNode)
154 self.demiwidth = lineation.getFloatByPrefixBeginEnd(elementNode, 'demiwidth', 'width', self.inradius.real)
155 self.demiheight = lineation.getFloatByPrefixBeginEnd(elementNode, 'demiheight', 'height', self.inradius.imag)
156 self.density = evaluate.getEvaluatedFloat(0.2, elementNode, 'density')
157 self.radius = lineation.getComplexByPrefixBeginEnd(elementNode, 'elementRadius', 'elementDiameter', complex(1.0, 1.0))
158 self.radius = lineation.getComplexByPrefixBeginEnd(elementNode, 'radius', 'diameter', self.radius)
159 self.seed = evaluate.getEvaluatedInt(None, elementNode, 'seed')
160 self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target')
161 self.typeMenuRadioStrings = 'hexagonal random rectangular'.split()
162 self.typeString = evaluate.getEvaluatedString('rectangular', elementNode, 'type')
163 self.zigzag = evaluate.getEvaluatedBoolean(True, elementNode, 'zigzag')