Source code for declaracad.occ.impl.occ_circuit

"""
Copyright (c) 2022, CodeLV.

Distributed under the terms of the GPL v3 License.

The full license is in the file LICENSE, distributed with this software.

Created on Jan 27, 2022
"""

from typing import Optional

from atom.api import Typed
from OCCT.BRepAdaptor import BRepAdaptor_CompCurve
from OCCT.BRepBuilderAPI import (
    BRepBuilderAPI_MakeEdge,
    BRepBuilderAPI_MakePolygon,
    BRepBuilderAPI_MakeWire,
)
from OCCT.GccAna import GccAna_Lin2d2Tan
from OCCT.GccEnt import GccEnt
from OCCT.gce import gce_MakePln
from OCCT.Geom import Geom_Circle
from OCCT.Geom2d import Geom2d_Line
from OCCT.GeomAPI import GeomAPI
from OCCT.gp import gp_Ax2, gp_Circ, gp_Pln, gp_Pnt2d

from declaracad.occ.draw import ProxyCircuit

from .occ_wire import OccWire
from .topology import Topology


[docs] class OccCircuit(OccWire, ProxyCircuit): curve = Typed(BRepAdaptor_CompCurve) def calculate_parameters( self, circles: list, pln: gp_Pln, clockwise: bool, offset: float ): normal = pln.Position().Direction() enclosing = GccEnt.Enclosing_ outside = GccEnt.Outside_ if clockwise: enclosing, outside = outside, enclosing for circle in circles: axis = gp_Ax2(circle.position.proxy, normal) if offset < 0: dr = offset if circle.radius < 0 else 0 else: dr = 0 if circle.radius < 0 else offset r = abs(circle.radius + dr) geom_circle = Geom_Circle(gp_Circ(axis, r)) c = GeomAPI.To2d_(geom_circle, pln).Circ2d() reverse = circle.radius < 0 if reverse: if circle.reverse: param = outside(c) else: param = enclosing(c) else: if circle.reverse: param = enclosing(c) else: param = outside(c) yield (param, geom_circle, reverse) def create_shape(self): d = self.declaration circles = d.circles[:] n = len(circles) if n < 2: raise ValueError("At least 2 circles are required") # Determine direction if n > 2: polygon = BRepBuilderAPI_MakePolygon() for circle in circles: polygon.Add(circle.position.proxy) clockwise = Topology.is_clockwise(polygon.Wire()) # Normal plane builder = gce_MakePln(*(c.position.proxy for c in circles[0:3])) else: c = circles[0] builder = gce_MakePln(c.position.proxy, c.direction.proxy) clockwise = True if not builder.IsDone(): raise ValueError(f"Could not build {d}: Circles must be on the same plane") pln = builder.Value() if d.reverse: clockwise = not clockwise tol = d.tolerance shape = BRepBuilderAPI_MakeWire() #: List of parameters parameters = list(self.calculate_parameters(circles, pln, clockwise, d.offset)) # Solution of last intersection a0: Optional[float] = None # Solution of first arc intersection afirst: Optional[float] = None n = len(parameters) for i in range(n): a, geom_circle, reverse = parameters[i] b, next_circle, next_reverse = parameters[(i + 1) % n] r = GccAna_Lin2d2Tan(a, b, tol) if not r.IsDone(): raise ValueError(f"Could not solve {d}") p1, p2 = gp_Pnt2d(), gp_Pnt2d() t1, a1 = r.Tangency1(1, 0, 0, p1) t2, a2 = r.Tangency2(1, 0, 0, p2) if a0 is not None: if reverse: a0, a1 = a1, a0 edge = BRepBuilderAPI_MakeEdge(geom_circle, a0, a1).Edge() shape.Add(edge) else: afirst = a1 # Add line line = Geom2d_Line(r.ThisSolution(1)) geom_line = GeomAPI.To3d_(line, pln) edge = BRepBuilderAPI_MakeEdge(geom_line, t1, t2).Edge() shape.Add(edge) a0 = a2 # Add first with initial solution if next_reverse: a2, afirst = afirst, a2 edge = BRepBuilderAPI_MakeEdge(next_circle, a2, afirst).Edge() shape.Add(edge) wire = shape.Wire() self.curve = BRepAdaptor_CompCurve(wire) self.shape = wire def init_layout(self): # This does not depened on children pass def update_shape(self, change=None): self.create_shape()