You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Triangles can only be centered in their centroid or barycenter. Would be nice to have more options.
Examples of working modified Triangle class, in object_sketch.py, and test snippet:
classTriangle(BaseSketchObject):
"""Sketch Object: Triangle Add any triangle to the sketch by specifying the length of any side and any two other side lengths or interior angles. Note that the interior angles are opposite the side with the same designation (i.e. side 'a' is opposite angle 'A'). Args: a (float, optional): side 'a' length. Defaults to None. b (float, optional): side 'b' length. Defaults to None. c (float, optional): side 'c' length. Defaults to None. A (float, optional): interior angle 'A' in degrees. Defaults to None. B (float, optional): interior angle 'B' in degrees. Defaults to None. C (float, optional): interior angle 'C' in degrees. Defaults to None. rotation (float, optional): angles to rotate objects. Defaults to 0. center (str, optional): Where to center the triangle. Defaults to 'centroid'. Choices are vertices 'A', 'B' or 'C' and 'orthocenter', 'circumcenter', 'incenter' or 'centroid'. align (Union[Align, tuple[Align, Align]], optional): align min, center, or max of object. Defaults to None. mode (Mode, optional): combination mode. Defaults to Mode.ADD. Raises: ValueError: One length and two other values were not provided """_applies_to= [BuildSketch._tag]
def__init__(
self,
*,
a: float|None=None,
b: float|None=None,
c: float|None=None,
A: float|None=None,
B: float|None=None,
C: float|None=None,
center: str="centroid",
align: Align|tuple[Align, Align] |None=None,
rotation: float=0,
mode: Mode=Mode.ADD,
):
context: BuildSketch|None=BuildSketch._get_context(self)
validate_inputs(context, self)
if [visNoneforvin [a, b, c]].count(True) ==3or [
visNoneforvin [a, b, c, A, B, C]
].count(True) !=3:
raiseValueError("One length and two other values must be provided")
A, B, C= (radians(angle) ifangleisnotNoneelseNoneforanglein [A, B, C])
ar, br, cr, Ar, Br, Cr=trianglesolver.solve(a, b, c, A, B, C)
self.a=ar#: length of side 'a'self.b=br#: length of side 'b'self.c=cr#: length of side 'c'self.A=degrees(Ar) #: interior angle 'A' in degreesself.B=degrees(Br) #: interior angle 'B' in degreesself.C=degrees(Cr) #: interior angle 'C' in degreestriangle=Face(
Wire.make_polygon(
[Vector(0, 0), Vector(ar, 0), Vector(cr, 0).rotate(Axis.Z, self.B)]
)
)
# Center the trianglevertices=triangle.vertices()
ax, ay, _=vertices[0] # Vertex Abx, by, _=vertices[1] # Vertex Bcx, cy, _=vertices[2] # Vertex Cifcenter=="A":
pass# Already centered at Aelifcenter=="B":
triangle.move(Location(-Vector(bx, by)))
elifcenter=="C":
triangle.move(Location(-Vector(cx, cy)))
elifcenter=="centroid":
centroid=sum((Vector(v) forvinvertices), Vector(0, 0, 0)) /3triangle.move(Location(-centroid))
elifcenter=="orthocenter":
# Compute perpendicular slopes for altitudesdefperp_bisector(x1, y1, x2, y2):
"""Returns midpoint and perpendicular slope"""mid_x, mid_y= (x1+x2) /2, (y1+y2) /2slope=Noneifx1==x2else-(x2-x1) / (y2-y1)
returnmid_x, mid_y, slopemid_bc, _, slope_a=perp_bisector(bx, by, cx, cy)
mid_ac, _, slope_b=perp_bisector(ax, ay, cx, cy)
# Solve for intersection of two altitudesifslope_aisNone: # Vertical line casehx, hy=ax, slope_b*ax+ (by-slope_b*bx)
elifslope_bisNone:
hx, hy=bx, slope_a*bx+ (ay-slope_a*ax)
else:
hx= (by-ay+slope_a*ax-slope_b*bx) / (slope_a-slope_b)
hy=slope_a*hx+ (ay-slope_a*ax)
triangle.move(Location(-Vector(hx, hy)))
elifcenter=="circumcenter":
d=2* (ax* (by-cy) +bx* (cy-ay) +cx* (ay-by))
ux= ((ax**2+ay**2) * (by-cy) + (bx**2+by**2) * (cy-ay) + (cx**2+cy**2) * (ay-by)) /duy= ((ax**2+ay**2) * (cx-bx) + (bx**2+by**2) * (ax-cx) + (cx**2+cy**2) * (bx-ax)) /dtriangle.move(Location(-Vector(ux, uy)))
elifcenter=="incenter":
# Compute side lengthsa= ((bx-cx)**2+ (by-cy)**2) **0.5# Opposite Ab= ((ax-cx)**2+ (ay-cy)**2) **0.5# Opposite Bc= ((ax-bx)**2+ (ay-by)**2) **0.5# Opposite C# Compute incenterix= (a*ax+b*bx+c*cx) / (a+b+c)
iy= (a*ay+b*by+c*cy) / (a+b+c)
triangle.move(Location(-Vector(ix, iy)))
else:
raiseValueError("Invalid center value")
alignment=NoneifalignisNoneelsetuplify(align, 2)
super().__init__(obj=triangle, rotation=rotation, align=alignment, mode=mode)
self.edge_a=self.edges().filter_by(lambdae: abs(e.length-ar) <TOLERANCE)[
0
] #: edge 'a'self.edge_b=self.edges().filter_by(
lambdae: abs(e.length-br) <TOLERANCEandenotin [self.edge_a]
)[
0
] #: edge 'b'self.edge_c=self.edges().filter_by(
lambdae: enotin [self.edge_a, self.edge_b]
)[
0
] #: edge 'c'self.vertex_A=topo_explore_common_vertex(
self.edge_b, self.edge_c
) #: vertex 'A'self.vertex_B=topo_explore_common_vertex(
self.edge_a, self.edge_c
) #: vertex 'B'self.vertex_C=topo_explore_common_vertex(
self.edge_a, self.edge_b
) #: vertex 'C'
The implementation shown here would violate make of the build123d conventions - having a center parameter, a parameter with a string type, etc. This type of positioning functionality is currently provided by align which also be enhanced (align=Align.CENTROID). It also seems totally reasonable to add a set of properties like orthocenter, circumcenter, etc. such that a user could do:
t=Triangle(...)
t.locate(t.orthocenter)
or something similar (doesn't work as well in builder mode).
Keep in mind that there already are edge_a, edge_b, edge_c, vertex_A, vertex_B, and vertex_C attributes of the Triangle class so all of the positioning calculations can be done fairly easily.
Triangles can only be centered in their centroid or barycenter. Would be nice to have more options.
Examples of working modified Triangle class, in object_sketch.py, and test snippet:
The text was updated successfully, but these errors were encountered: