Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion src/fscad/fscad.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
__all__ = ['app', 'root', 'ui', 'brep', 'design', 'Translation', 'Place', 'BoundedEntity', 'BRepEntity', 'Body', 'Loop',
'Face', 'Edge', 'Point', 'BoundingBox', 'Component', 'ComponentWithChildren', 'Shape', 'BRepComponent',
'PlanarShape', 'Box', 'Cylinder', 'Sphere', 'Torus', 'Rect', 'Circle', 'Builder2D', 'Polygon',
'RegularPolygon', 'import_fusion_archive', 'import_dxf', 'Combination', 'Union', 'Difference',
'RegularPolygon', 'Text', 'import_fusion_archive', 'import_dxf', 'Combination', 'Union', 'Difference',
'Intersection', 'Group', 'Loft', 'Revolve', 'Sweep', 'ExtrudeBase', 'Extrude', 'ExtrudeTo', 'OffsetEdges',
'SplitFace', 'Silhouette', 'Hull', 'RawThreads', 'Threads', 'ConicalThreads', 'Fillet', 'Chamfer', 'Scale',
'Thicken', 'MemoizableDesign', 'setup_document', 'run_design', 'relative_import']
Expand Down Expand Up @@ -2326,6 +2326,74 @@ def __init__(self, sides: int, radius: float, is_outer_radius: bool = True, name
super().__init__(*points, name=name)


class Text(Shape):
"""Defines 2D text geometry suitable for extrusion.

Args:
text: The text string to create
height: The height of the text in cm
font: The font name to use. If None, uses the default font.
name: The name of the component
"""
def __init__(self, text: str, height: float = 1.0, font: str = None, name: str = None):
root_comp = root()
occ = root_comp.occurrences.addNewComponent(Matrix3D.create())
sketch = occ.component.sketches.add(occ.component.xYConstructionPlane)

text_input = sketch.sketchTexts.createInput(text, height, Point3D.create(0, 0, 0))
if font is not None:
text_input.fontName = font
sketch_text = sketch.sketchTexts.add(text_input)
sketch_text.explode()

profiles = sketch.profiles
if profiles.count == 0:
occ.deleteMe()
super().__init__(name=name)
return

thin_depth = 0.01
extent = adsk.fusion.DistanceExtentDefinition.create(
adsk.core.ValueInput.createByReal(thin_depth))
for i in range(profiles.count):
profile = profiles.item(i)
extrude_input = occ.component.features.extrudeFeatures.createInput(
profile, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
extrude_input.setOneSideExtent(
extent, adsk.fusion.ExtentDirections.PositiveExtentDirection,
adsk.core.ValueInput.createByReal(0))
occ.component.features.extrudeFeatures.add(extrude_input)

all_bodies = sorted(occ.bRepBodies, key=lambda b: b.volume, reverse=True)
kept_bodies = []
for body in all_bodies:
centroid = body.physicalProperties.centerOfMass
is_inner = False
for larger in kept_bodies:
bb = larger.boundingBox
if (bb.minPoint.x < centroid.x < bb.maxPoint.x and
bb.minPoint.y < centroid.y < bb.maxPoint.y):
is_inner = True
break
if not is_inner:
kept_bodies.append(body)

planar_bodies = []
for body in kept_bodies:
for face in body.faces:
if isinstance(face.geometry, adsk.core.Plane) and face.geometry.normal.isParallelTo(self._pos_z):
planar_bodies.append(brep().copy(face))
break

occ.deleteMe()
super().__init__(*planar_bodies, name=name)

def get_plane(self) -> Optional[adsk.core.Plane]:
if not self._bodies:
return None
return adsk.core.Plane.create(Point3D.create(0, 0, 0), Vector3D.create(0, 0, 1))


def import_fusion_archive(filename, name="import"):
"""Imports the given fusion archive as a new Component

Expand Down
Binary file added tests/TextTest/text.f3d
Binary file not shown.
Binary file added tests/TextTest/text_empty.f3d
Binary file not shown.
Binary file added tests/TextTest/text_extrude.f3d
Binary file not shown.
Binary file added tests/TextTest/text_font.f3d
Binary file not shown.
Binary file added tests/TextTest/text_get_plane.f3d
Binary file not shown.
Binary file added tests/TextTest/text_multiple_chars.f3d
Binary file not shown.
Binary file added tests/TextTest/text_with_holes.f3d
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/edge_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def test_component_edges(self):
~box2 == ~box1,
~box2 == ~box1)

self.assertEquals(len(Group([box1, box2]).edges), 24)
self.assertEqual(len(Group([box1, box2]).edges), 24)

def test_edge_after_translate(self):
box = Box(1, 1, 1)
Expand Down
2 changes: 1 addition & 1 deletion tests/face_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def test_component_faces(self):
~box2 == ~box1,
~box2 == ~box1)

self.assertEquals(len(Group([box1, box2]).faces), 12)
self.assertEqual(len(Group([box1, box2]).faces), 12)

def test_face_after_translate(self):
box = Box(1, 1, 1)
Expand Down
10 changes: 5 additions & 5 deletions tests/memoizable_design_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def test_design(self):
box2.tx(5)
box1.create_occurrence()
box2.create_occurrence()
self.assertEquals(times_called, 1)
self.assertEqual(times_called, 1)

def test_called_twice_with_name_as_keyword(self):

Expand All @@ -62,7 +62,7 @@ def test_design(self, name):
box2.tx(5)
box1.create_occurrence()
box2.create_occurrence()
self.assertEquals(times_called, 1)
self.assertEqual(times_called, 1)

def test_called_twice_with_name_as_positional(self):

Expand All @@ -83,7 +83,7 @@ def test_design(self, name):
box2.tx(5)
box1.create_occurrence()
box2.create_occurrence()
self.assertEquals(times_called, 1)
self.assertEqual(times_called, 1)

def test_with_arguments(self):

Expand All @@ -107,7 +107,7 @@ def test_design(self, tx, name):
box1.create_occurrence()
box2.create_occurrence()
box3.create_occurrence()
self.assertEquals(times_called, 2)
self.assertEqual(times_called, 2)

def test_different_instances(self):

Expand Down Expand Up @@ -138,7 +138,7 @@ def test_design(self, tx, name):
box2.create_occurrence()
box3.create_occurrence()
box4.create_occurrence()
self.assertEquals(times_called, 2)
self.assertEqual(times_called, 2)


def run(context):
Expand Down
1 change: 1 addition & 0 deletions tests/misc_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

# note: load_tests is required for the "pattern" test filtering functionality in loadTestsFromModule in run()
from fscad.test_utils import FscadTestCase, load_tests
import fscad.fscad
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow. does this need to be imported before the "from fscad.fscad import *" or something? otherwise, it's already being imported on line 26?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

misc_test seems to run fine without this change

from fscad.fscad import *
import fscad.fscad

Expand Down
14 changes: 7 additions & 7 deletions tests/revolve_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def test_full_revolve(self):
revolve = Revolve(rect, adsk.core.Line3D.create(Point3D.create(0, 0, 0), Point3D.create(0, 1, 0)))
revolve.create_occurrence(True)

self.assertEquals(len(revolve.faces), 4)
self.assertEqual(len(revolve.faces), 4)

def test_infinite_line(self):
rect = Rect(1, 1)
Expand All @@ -41,7 +41,7 @@ def test_infinite_line(self):
adsk.core.Line3D.create(Point3D.create(0, 0, 0), Point3D.create(0, 1, 0)).asInfiniteLine())
revolve.create_occurrence(True)

self.assertEquals(len(revolve.faces), 4)
self.assertEqual(len(revolve.faces), 4)

def test_partial_revolve(self):
rect = Rect(1, 1)
Expand All @@ -50,7 +50,7 @@ def test_partial_revolve(self):
revolve = Revolve(rect, adsk.core.Line3D.create(Point3D.create(0, 0, 0), Point3D.create(0, 1, 0)), 180)
revolve.create_occurrence(True)

self.assertEquals(len(revolve.faces), 6)
self.assertEqual(len(revolve.faces), 6)

def test_partial_revolve_negative(self):
rect = Rect(1, 1)
Expand All @@ -59,7 +59,7 @@ def test_partial_revolve_negative(self):
revolve = Revolve(rect, adsk.core.Line3D.create(Point3D.create(0, 0, 0), Point3D.create(0, 1, 0)), -180)
revolve.create_occurrence(True)

self.assertEquals(len(revolve.faces), 6)
self.assertEqual(len(revolve.faces), 6)

def test_revolve_off_axis(self):
rect = Rect(1, 1)
Expand All @@ -68,7 +68,7 @@ def test_revolve_off_axis(self):
revolve = Revolve(rect, adsk.core.Line3D.create(Point3D.create(0, 0, 0), Point3D.create(0, 1, 1)))
revolve.create_occurrence(True)

self.assertEquals(len(revolve.faces), 4)
self.assertEqual(len(revolve.faces), 4)

def test_revolve_around_edge(self):
rect = Rect(1, 1)
Expand All @@ -77,7 +77,7 @@ def test_revolve_around_edge(self):

revolve.create_occurrence(True)

self.assertEquals(len(revolve.faces), 3)
self.assertEqual(len(revolve.faces), 3)

def test_revolve_face(self):
box = Box(1, 1, 1)
Expand Down Expand Up @@ -123,7 +123,7 @@ def test_revolve_with_edge_axis(self):
revolve = Revolve(box.top, upper_right_edge, 180)
revolve.create_occurrence(True)

self.assertEquals(revolve.size().asArray(), (box.size().x * 2, box.size().y, box.size().z*2))
self.assertEqual(revolve.size().asArray(), (box.size().x * 2, box.size().y, box.size().z*2))


def run(context):
Expand Down
38 changes: 19 additions & 19 deletions tests/silhouette_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def test_orthogonal_face_silhouette(self):
Vector3D.create(0, 0, 1)))
silhouette.create_occurrence(True)

self.assertEquals(silhouette.size().asArray(), rect.size().asArray())
self.assertEqual(silhouette.size().asArray(), rect.size().asArray())

def test_non_orthogonal_face_silhouette(self):
rect = Rect(1, 1)
Expand All @@ -40,7 +40,7 @@ def test_non_orthogonal_face_silhouette(self):
Vector3D.create(0, 0, 1)))
silhouette.create_occurrence(True)

self.assertEquals(silhouette.size().asArray(), (rect.size().x, rect.size().y, 0))
self.assertEqual(silhouette.size().asArray(), (rect.size().x, rect.size().y, 0))

def test_parallel_face_silhouette(self):
rect = Rect(1, 1)
Expand All @@ -50,7 +50,7 @@ def test_parallel_face_silhouette(self):
Vector3D.create(0, 0, 1)))
silhouette.create_occurrence(True)

self.assertEquals(silhouette.size().asArray(), (0, 0, 0))
self.assertEqual(silhouette.size().asArray(), (0, 0, 0))

def test_body_silhouette(self):
box = Box(1, 1, 1)
Expand All @@ -60,7 +60,7 @@ def test_body_silhouette(self):
Vector3D.create(0, 0, 1)))
silhouette.create_occurrence(True)

self.assertEquals(silhouette.size().asArray(), (box.size().x, box.size().y, 0))
self.assertEqual(silhouette.size().asArray(), (box.size().x, box.size().y, 0))

def test_component_silhouette(self):
rect = Rect(1, 1)
Expand All @@ -70,7 +70,7 @@ def test_component_silhouette(self):
Vector3D.create(0, 0, 1)))
silhouette.create_occurrence(True)

self.assertEquals(silhouette.size().asArray(), (rect.size().x, rect.size().y, 0))
self.assertEqual(silhouette.size().asArray(), (rect.size().x, rect.size().y, 0))

def test_multiple_disjoint_faces_silhouette(self):
rect1 = Rect(1, 1)
Expand All @@ -88,7 +88,7 @@ def test_multiple_disjoint_faces_silhouette(self):

self.assertTrue(abs(silhouette.size().x - assembly.size().x) < app().pointTolerance)
self.assertTrue(abs(silhouette.size().y - assembly.size().y) < app().pointTolerance)
self.assertEquals(silhouette.size().z, 0)
self.assertEqual(silhouette.size().z, 0)

def test_multiple_overlapping_faces_silhouette(self):
rect1 = Rect(1, 1)
Expand All @@ -106,7 +106,7 @@ def test_multiple_overlapping_faces_silhouette(self):

self.assertTrue(abs(silhouette.size().x - assembly.size().x) < app().pointTolerance)
self.assertTrue(abs(silhouette.size().y - assembly.size().y) < app().pointTolerance)
self.assertEquals(silhouette.size().z, 0)
self.assertEqual(silhouette.size().z, 0)

def test_cylinder_silhouette(self):
cyl = Cylinder(1, 1)
Expand All @@ -115,7 +115,7 @@ def test_cylinder_silhouette(self):
Vector3D.create(0, 0, 1)))
silhouette.create_occurrence(True)

self.assertEquals(silhouette.size().asArray(), (cyl.size().x, cyl.size().y, 0))
self.assertEqual(silhouette.size().asArray(), (cyl.size().x, cyl.size().y, 0))

def test_single_edge(self):
circle = Circle(1)
Expand All @@ -125,7 +125,7 @@ def test_single_edge(self):
Vector3D.create(0, 0, 1)))
silhouette.create_occurrence(True)

self.assertEquals(silhouette.size().asArray(), circle.size().asArray())
self.assertEqual(silhouette.size().asArray(), circle.size().asArray())

def test_multiple_edges(self):
rect = Rect(1, 1)
Expand All @@ -149,8 +149,8 @@ def test_multiple_edges(self):

silhouette.create_occurrence(True)

self.assertEquals(silhouette.size().asArray(), rect.size().asArray())
self.assertEquals(len(silhouette.edges), 4)
self.assertEqual(silhouette.size().asArray(), rect.size().asArray())
self.assertEqual(len(silhouette.edges), 4)

def test_named_edges(self):
box = Box(1, 1, 1)
Expand All @@ -173,35 +173,35 @@ def test_named_edges(self):
~edge_finder == ~silhouette)
found_edges = silhouette.find_edges(edge_finder)
named_edges = silhouette.named_edges("front")
self.assertEquals(len(found_edges), 1)
self.assertEquals(found_edges, named_edges)
self.assertEqual(len(found_edges), 1)
self.assertEqual(found_edges, named_edges)

edge_finder.place(
~edge_finder == ~silhouette,
+edge_finder == +silhouette,
~edge_finder == ~silhouette)
found_edges = silhouette.find_edges(edge_finder)
named_edges = silhouette.named_edges("back")
self.assertEquals(len(found_edges), 1)
self.assertEquals(found_edges, named_edges)
self.assertEqual(len(found_edges), 1)
self.assertEqual(found_edges, named_edges)

edge_finder.place(
+edge_finder == +silhouette,
~edge_finder == ~silhouette,
~edge_finder == ~silhouette)
found_edges = silhouette.find_edges(edge_finder)
named_edges = silhouette.named_edges("right")
self.assertEquals(len(found_edges), 1)
self.assertEquals(found_edges, named_edges)
self.assertEqual(len(found_edges), 1)
self.assertEqual(found_edges, named_edges)

edge_finder.place(
-edge_finder == -silhouette,
~edge_finder == ~silhouette,
~edge_finder == ~silhouette)
found_edges = silhouette.find_edges(edge_finder)
named_edges = silhouette.named_edges("left")
self.assertEquals(len(found_edges), 1)
self.assertEquals(found_edges, named_edges)
self.assertEqual(len(found_edges), 1)
self.assertEqual(found_edges, named_edges)



Expand Down
1 change: 1 addition & 0 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"thicken_test",
"thread_test",
"transform_test",
"text_test",
"union_test",
]

Expand Down
Loading