app
Fusion - the Fusion 360 Bridge
Public Made by Adomby adom
Drive Autodesk Fusion 360 from the cloud via Adom Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.
master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
"""The efficiency report's rules, offline: findings() is pure, and measure() reads a small fake design."""
import importlib
from types import SimpleNamespace as NS
import unittest
import unittest.mock
from test_mechanical import adsk, Collection, Entity
adsk.fusion.DesignTypes = NS(ParametricDesignType=1, DirectDesignType=0)
adsk.fusion.FeatureHealthStates = NS(HealthyFeatureHealthState=0, WarningFeatureHealthState=1, ErrorFeatureHealthState=2)
adsk.fusion.PatternComputeOptions = NS(OptimizedPatternCompute=0, IdenticalPatternCompute=1, AdjustPatternCompute=2)
e = importlib.import_module('mechanical_test_commands.mechanical.efficiency')
def box(x, y, z):
return NS(minPoint=NS(x=0, y=0, z=0), maxPoint=NS(x=x, y=y, z=z))
def body(faces, size=(1, 1, 1)):
return Entity(name="Body1", faces=Collection([0] * faces), boundingBox=box(*size))
def features(moves=0, bases=0, threads=(), patterns=()):
return NS(moveFeatures=Collection([0] * moves), baseFeatures=Collection([0] * bases), threadFeatures=Collection([NS(isModeled=t) for t in threads]),
rectangularPatternFeatures=Collection(list(patterns)), circularPatternFeatures=Collection([]))
def component(name, bodies=(), **extra):
return Entity(name=name, bRepBodies=Collection(list(bodies)), meshBodies=Collection([]), sketches=Collection(extra.pop("sketches", [])),
occurrences=Collection(extra.pop("occurrences", [])), features=extra.pop("features", features()), **extra)
class Findings(unittest.TestCase):
def test_a_clean_design_has_no_findings(self):
self.assertEqual(e.findings({"snapshots": 0, "occurrences": 40, "components": {"all": 12}, "timeline": {"count": 30}}), [])
def test_each_rule_reports_what_it_measured_and_the_fix(self):
found = {f["rule"]: f for f in e.findings({
"snapshots": 66, "pendingSnapshot": True, "moveFeatures": 2, "designType": "parametric", "baseFeatures": 637,
"wires": 114, "occurrences": 4579, "components": {"all": 1249}, "timeline": {"count": 689, "errors": 1, "warnings": 3},
"duplicateDefinitions": {"extraDefinitions": 30}, "heavyDefinitions": [{"component": "Breaker", "weightedFaces": 5514}],
"bigSketches": [{"curves": 400}], "patterns": [{"computeOption": "adjust"}], "modeledThreads": 4, "meshBodies": 2,
"emptyComponents": 1})}
self.assertEqual(set(found), {"placement-snapshots", "pending-snapshot", "move-features", "duplicate-definitions", "design-size",
"timeline-length", "imported-history", "wires-in-assembly", "heavy-geometry", "sketch-size",
"pattern-compute", "timeline-health", "modeled-threads", "mesh-bodies", "empty-components"})
self.assertEqual((found["placement-snapshots"]["severity"], found["placement-snapshots"]["measured"]), ("high", 66))
self.assertEqual(found["design-size"]["limit"], 1000)
self.assertIn("occurrence_update", found["placement-snapshots"]["fix"])
def test_worst_first(self):
severities = [f["severity"] for f in e.findings({"snapshots": 3, "meshBodies": 1, "occurrences": 2000, "components": {"all": 10}})]
self.assertEqual(severities, ["high", "medium", "low"])
def test_direct_designs_are_not_told_to_drop_history(self):
rules = {f["rule"] for f in e.findings({"designType": "direct", "baseFeatures": 500, "wires": 100})}
self.assertFalse(rules & {"imported-history", "wires-in-assembly"})
class Measure(unittest.TestCase):
def test_counts_copies_snapshots_wires_and_heavy_definitions(self):
plate = component("Plate", [body(6, (0.4, 0.4, 0.05))])
copy = component("Plate (1)", [body(6, (0.4, 0.4, 0.05))])
wire = component("W001 18AWG red", [body(3)])
breaker = component("Breaker", [body(25000)])
empty = component("Empty")
threaded = component("Bolt", [body(40)], features=features(threads=(True, False), moves=1))
comps = [plate, copy, wire, breaker, empty, threaded]
tl_items = [NS(isGroup=False, entity=Entity(objectType="adsk::fusion::Snapshot"), healthState=0, isSuppressed=False),
NS(isGroup=False, entity=Entity(objectType="adsk::fusion::Occurrence"), healthState=1, isSuppressed=False),
NS(isGroup=True)]
for comp in comps:
comp.entityToken = comp.name
occurrences = [NS(component=c) for c in (plate, copy, wire, breaker, breaker, threaded)]
# Fusion's allComponents holds the root component too
root = component("Root", allOccurrences=Collection(occurrences), allJoints=Collection([]), allAsBuiltJoints=Collection([]),
allRigidGroups=Collection([]), occurrences=occurrences)
root.entityToken = "Root"
design = NS(rootComponent=root, designType=1, timeline=Collection(tl_items), snapshots=NS(count=1, hasPendingSnapshot=False),
allComponents=Collection([root] + comps))
for comp in [root] + comps:
comp.parentDesign = design
m = e.measure(None, design)
self.assertEqual((m["timeline"]["count"], m["timeline"]["groups"], m["timeline"]["warnings"], m["timeline"]["byType"]),
(3, 1, 1, {"Snapshot": 1, "Occurrence": 1}))
self.assertEqual((m["snapshots"], m["wires"], m["emptyComponents"], m["copyNames"], m["modeledThreads"], m["moveFeatures"]), (1, 1, 1, 1, 1, 1))
self.assertEqual(m["duplicateDefinitions"]["extraDefinitions"], 1)
self.assertEqual(m["heavyDefinitions"][0], {"component": "Breaker", "faces": 25000, "instances": 2, "weightedFaces": 50000})
self.assertEqual(m["skipped"], [])
def test_a_spent_budget_skips_the_costly_passes_and_says_so(self):
a = component("A", [body(6)])
root = component("Root", allOccurrences=Collection([NS(component=a)]), allJoints=Collection([]), allAsBuiltJoints=Collection([]),
allRigidGroups=Collection([]), occurrences=[NS(component=a)])
design = NS(rootComponent=root, designType=0, snapshots=NS(count=0, hasPendingSnapshot=False), allComponents=Collection([root, a]))
a.parentDesign = root.parentDesign = design
m = e.measure(None, design, budget_seconds=0)
self.assertEqual(m["skipped"], ["heavy geometry and duplicate definitions"])
a = importlib.import_module('mechanical_test_commands.mechanical.assemblies')
core = importlib.import_module('mechanical_test_commands.mechanical.core')
class Matrix:
def __init__(self, values):
self.values = list(values)
def asArray(self):
return list(self.values)
class Snapshots:
def __init__(self):
self.hasPendingSnapshot, self.added = False, 0
def add(self):
self.hasPendingSnapshot, self.added = False, self.added + 1
return object()
class Occurrence:
"""Moving it the old way leaves a pending snapshot, as Fusion does; editing its initial position does not."""
def __init__(self, snapshots, editable=None, refuse=False, timeline=None, index=0):
self.snapshots, self.refuse, self.timeline, self.index = snapshots, refuse, timeline, index
self.isGrounded = self.isGroundToParent = False
self._transform = Matrix([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
if editable is not None:
self.isVaildForEditInitialPosition = editable
@property
def transform2(self):
return self._transform
@transform2.setter
def transform2(self, matrix):
self._transform, self.snapshots.hasPendingSnapshot = matrix, True
@property
def initialTransform(self):
return self._transform
@initialTransform.setter
def initialTransform(self, matrix):
if self.refuse or (self.timeline and self.timeline.markerPosition != self.index + 1):
raise RuntimeError("Cannot be edited before rolling back" if self.timeline else "cannot find the occurrence path")
self._transform = matrix
@property
def timelineObject(self):
if not self.timeline:
raise RuntimeError("no timeline object")
occ = self
class Item:
index = occ.index
def rollTo(self, before):
occ.timeline.markerPosition = occ.index + (0 if before else 1)
occ.timeline.rolls += 1
return True
return Item()
def moved(x):
return Matrix([1, 0, 0, x / 10, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
class Placement(unittest.TestCase):
def place(self, occ, snapshots, **args):
design = NS(snapshots=snapshots, timeline=occ.timeline)
with unittest.mock.patch.object(a, "resolve", lambda *_: occ), \
unittest.mock.patch.object(a, "transform", lambda spec: moved(spec["position"][0])), \
unittest.mock.patch.object(a, "occurrence_data", lambda o, v: {"ref": "occ"}):
done = a.occurrence_update(design, {}, {"occurrence": "ref", "transform": {"position": [80, 0, 0]}, **args})
return done({})
def test_an_editable_occurrence_moves_without_a_snapshot(self):
snapshots = Snapshots()
occ = Occurrence(snapshots, editable=True)
self.assertEqual(self.place(occ, snapshots)["placement"], "initial_position")
self.assertEqual((snapshots.added, occ.transform2.asArray()[3]), (0, 8.0))
def test_an_earlier_occurrence_is_edited_rolled_back_and_the_marker_restored(self):
snapshots, timeline = Snapshots(), NS(markerPosition=7, rolls=0)
occ = Occurrence(snapshots, editable=True, timeline=timeline, index=3)
self.assertEqual(self.place(occ, snapshots)["placement"], "initial_position")
self.assertEqual((snapshots.added, timeline.rolls, timeline.markerPosition, occ.transform2.asArray()[3]), (0, 1, 7, 8.0))
def test_otherwise_the_move_is_captured(self):
for occ_args in ({"editable": False}, {}, {"editable": True, "refuse": True}):
snapshots = Snapshots()
self.assertEqual(self.place(Occurrence(snapshots, **occ_args), snapshots)["placement"], "capture", occ_args)
self.assertEqual(snapshots.added, 1, occ_args)
def test_the_caller_can_insist_either_way(self):
snapshots = Snapshots()
self.assertEqual(self.place(Occurrence(snapshots, editable=True), snapshots, method="capture")["placement"], "capture")
with self.assertRaises(core.MechanicalError) as error:
self.place(Occurrence(Snapshots(), editable=False), Snapshots(), method="initial_position")
self.assertEqual(error.exception.code, "initial_position_unavailable")
with self.assertRaises(core.MechanicalError) as error:
self.place(Occurrence(Snapshots(), editable=True, refuse=True), Snapshots(), method="initial_position")
self.assertEqual(error.exception.code, "initial_position_refused")
if __name__ == "__main__":
unittest.main()