• Добро пожаловать на Форум пользователей ПО АСКОН. Пожалуйста, авторизуйтесь.
 

Уважаемые пользователи,

Хотим проинформировать вас о режиме работы регистрации на нашем сайте.

Регистрация будет доступна с 8:00 (мск) 12 января.

Благодарим вас за понимание и сотрудничество. Мы ценим ваше терпение и стремимся предоставить вам лучший опыт использования нашего сервиса.

С уважением,
Команда Ascon

Получение крайних точек на окружностях

Автор sel0_m0l0chn0e, 24.06.26, 10:14:56

« назад - далее »

0 Пользователи и 1 гость просматривают эту тему.

sel0_m0l0chn0e

Здравствуйте, есть алгоритм который ищет крайние точки на видах чертежа, после этого эти точки передаются в метод построения габаритных размеров для проведения боковых линий к этим точкам, проблема в том, что данный алгоритм плохо работает с окружностями, например, ставит крайнюю точку перед окружностью, хотя точка находиться на ней, как можно найти экстремумы на окружностях? код методов и скриншот с неправильным построением размеров прилагаю, версия компаса 22
    def _find_extreme_segments(self, view_dyn, axis: str):
        """Находит на виде два проекционных сегмента на краях габарита по оси.

        axis='x' → сегменты с минимальным и максимальным X-концом;
        axis='y' → с минимальным и максимальным Y-концом.
        Возвращает (lo_obj, lo_pt, hi_obj, hi_pt, perp_min, perp_max):
        *_pt — (x,y) концевой точки в экстремуме; perp_min/perp_max —
        габарит по перпендикулярной оси (фактическая кромка эскиза, от
        которой отодвигается размерная линия). None при неудаче.
        """
        try:
            container = self._qi(view_dyn, "IDrawingContainer")
            if container is None:
                log.info("[АССОЦ] IDrawingContainer недоступен")
                return None
            segs = container.LineSegments
            seg_count = segs.Count
        except Exception as e:
            log.info(f"[АССОЦ] LineSegments недоступны: {e}")
            return None

        use_x = (axis == "x")
        lo_obj = hi_obj = None
        lo_pt = hi_pt = None
        lo_val = float("inf")
        hi_val = float("-inf")
        # габарит по перпендикулярной оси — фактическая кромка эскиза,
        # от которой надо отступать размерной линией
        perp_min = float("inf")
        perp_max = float("-inf")

        for i in range(seg_count):
            try:
                s = segs.LineSegment(i)
            except Exception:
                continue
            for (sx, sy) in ((s.X1, s.Y1), (s.X2, s.Y2)):
                val  = sx if use_x else sy   # вдоль оси размера
                perp = sy if use_x else sx   # поперёк
                if val < lo_val:
                    lo_val, lo_obj, lo_pt = val, s, (sx, sy)
                if val > hi_val:
                    hi_val, hi_obj, hi_pt = val, s, (sx, sy)
                if perp < perp_min:
                    perp_min = perp
                if perp > perp_max:
                    perp_max = perp

        log.info(f"[АССОЦ] axis={axis} сегментов={seg_count} "
                 f"min={lo_val:.2f}@{tuple(round(v,2) for v in lo_pt) if lo_pt else None} "
                 f"max={hi_val:.2f}@{tuple(round(v,2) for v in hi_pt) if hi_pt else None} "
                 f"perp=[{perp_min:.2f}..{perp_max:.2f}]")

        if lo_obj is None or hi_obj is None:
            log.info("[АССОЦ] экстремальные сегменты не найдены")
            return None
        if lo_obj is hi_obj:
            log.info("[АССОЦ] min и max на одном сегменте — пропуск")
            return None
        return lo_obj, lo_pt, hi_obj, hi_pt, perp_min, perp_max
    def _add_overall_dimensions(self, view, h_lo, h_hi, v_lo, v_hi,
                                ref_p1=None, ref_p2=None,
                                with_vertical: bool = True,
                                with_horizontal: bool = True,
                                off: float = 30.0) -> None:
        """Проставляет габаритные размеры на виде.

        Выносные линии рисуются вручную от точных габаритных точек (ksLineSeg).
        Линия размера со стрелками и текстом — через ksLinDimension на уровне dim линии.
        """
        try:
            m7       = self._api7_mod
            view_dyn = dynamic.Dispatch(view._oleobj_)
            view_dyn.Current = True
            lin_dims = view_dyn.LineDimensions

            v_max = max(v_lo, v_hi)
            v_min = min(v_lo, v_hi)

            def _set_max_text(dim_obj, size: float):
                val = math.ceil(size) + 5
                try:
                    dt = m7.IDimensionText(dim_obj._oleobj_.QueryInterface(
                        m7.NamesToIIDMap["IDimensionText"], pythoncom.IID_IDispatch))
                    dt.AutoNominalValue = False
                    dt.NominalValue     = float(val)
                    try:
                        dt.Suffix.Str = " max"
                    except Exception:
                        pass
                except Exception as e:
                    log.warning(f"[РАЗМЕР] text: {e}")

            def _set_gap0(dim_obj):
                """Нулевой зазор у детали, длина выносной — до размерной линии."""
                try:
                    dp = m7.IDimensionParams(dim_obj._oleobj_.QueryInterface(
                        m7.NamesToIIDMap["IDimensionParams"], pythoncom.IID_IDispatch))
                    before_gap = getattr(dp, "Gap", "?")
                    before_val = getattr(dp, "GapValue", "?")
                    try:
                        dp.Gap = True
                    except Exception as e:
                        log.warning(f"[РАЗМЕР] set Gap: {e}")
                    try:
                        dp.GapValue = 0.0
                    except Exception as e:
                        log.warning(f"[РАЗМЕР] set GapValue: {e}")
                    after_gap = getattr(dp, "Gap", "?")
                    after_val = getattr(dp, "GapValue", "?")
                    log.info(f"[РАЗМЕР-GAP] было Gap={before_gap} GapValue={before_val} "
                             f"→ стало Gap={after_gap} GapValue={after_val}")
                except Exception as e:
                    log.warning(f"[РАЗМЕР] gap: {e}")

            if with_horizontal:
                extr = self._find_extreme_segments(view_dyn, "x")
                if extr is not None:
                    # точки размера берём С крайних сегментов (как в test.py),
                    # иначе Create() вернёт False
                    o1, p1, o2, p2, perp_min, perp_max = extr
                    x1, y1 = p1
                    x2, y2 = p2
                    # размерную линию отодвигаем от ФАКТИЧЕСКОЙ кромки эскиза
                    # (perp_min/perp_max по Y), а не от модельного габарита —
                    # иначе для другой номенклатуры линия попадает на деталь
                    dim_y = (perp_min - off) if v_lo > v_hi else (perp_max + off)
                else:
                    o1 = o2 = None
                    x1 = ref_p1[0] if ref_p1 else h_lo
                    y1 = ref_p1[1] if ref_p1 else v_max
                    x2 = ref_p2[0] if ref_p2 else h_hi
                    y2 = ref_p2[1] if ref_p2 else v_max
                    dim_y = (v_min - off) if v_lo > v_hi else (v_max + off)
                h = dynamic.Dispatch(lin_dims.Add()._oleobj_)
                h.X1, h.Y1 = x1, y1
                h.X2, h.Y2 = x2, y2
                h.X3, h.Y3 = (h_lo + h_hi) / 2.0, dim_y
                h.Orientation = 1
                _set_gap0(h)
                _set_max_text(h, abs(h_hi - h_lo))   # округление +5 и " max"
                h.Update()
                if o1 is not None:
                    self._associate_dim_to(h, o1, o2)  # привязка к рёбрам сохраняется
                    _set_max_text(h, abs(h_hi - h_lo))  # вернуть текст после ассоциации
                    h.Update()
                log.info(f"[РАЗМЕР-H] p1=({x1:.1f},{y1:.1f}) p2=({x2:.1f},{y2:.1f}) dim_y={dim_y:.1f}")

            if with_vertical:
                extr = self._find_extreme_segments(view_dyn, "y")
                if extr is not None:
                    o1, p1, o2, p2, perp_min, perp_max = extr
                    x1, y1 = p1
                    x2, y2 = p2
                    # вертикальный размер ставим слева от фактической кромки
                    dim_x = perp_min - off
                else:
                    o1 = o2 = None
                    x1 = ref_p1[0] if ref_p1 else 0.0
                    y1 = ref_p1[1] if ref_p1 else v_max
                    x2 = ref_p2[0] if ref_p2 else 0.0
                    y2 = ref_p2[1] if ref_p2 else v_min
                    dim_x = h_lo - off
                v = dynamic.Dispatch(lin_dims.Add()._oleobj_)
                v.X1, v.Y1 = x1, y1
                v.X2, v.Y2 = x2, y2
                v.X3, v.Y3 = dim_x, (v_max + v_min) / 2.0
                v.Orientation = 2
                _set_gap0(v)
                _set_max_text(v, abs(v_hi - v_lo))   # округление +5 и " max"
                v.Update()
                if o1 is not None:
                    self._associate_dim_to(v, o1, o2)  # привязка к рёбрам сохраняется
                    _set_max_text(v, abs(v_hi - v_lo))  # вернуть текст после ассоциации
                    v.Update()
                log.info(f"[РАЗМЕР-V] p1=({x1:.1f},{y1:.1f}) p2=({x2:.1f},{y2:.1f}) dim_x={dim_x:.1f}")

        except Exception as e:
            log.warning(f"[РАЗМЕР] {e}")

Student2025

А что мешает от опорной точки (вашем случае справа) по найденным ключевым точкам окружности найти наибольшую длину между ними ? А в вашем случае надо высчитывать по проекциям точек на ось X.

Валерий Изранов

sel0_m0l0chn0e, если вы будете нажимать кнопку Код,
то тексты ваших программ будут смотреться намного красивее.

"sel0_m0l0chn0e", а почему именно так?
+ Благодарностей: 1

abvgdeyka

Здравствуйте, писал нечто подобное, если я правильно понял задачу, то вот код (возможно немного нужно его подчистить).
Documents = application.Documents
#  Получим активный документ
iDocument2D = kompas_object.ActiveDocument2D()




def get_object_bounding_rect(obj):
    """
    Получение габаритного прямоугольника объекта
    :param obj: Указатель на объект
    :return: Кортеж (x1, y1, x2, y2) или None в случае ошибки
    """
    try:
        # Создаем структуру параметров прямоугольника
        rect_param = kompas_object.GetParamStruct(kompas6_constants.ko_RectParam)

        # Вызываем функцию получения габаритного прямоугольника
        result = kompas_object.ksGetObjGabaritRect(obj, rect_param)

        if result == 1:
            # Получаем нижнюю левую точку
            p_bottom = rect_param.GetpBot()
            math_point_param = p_bottom.QueryInterface(kompas6_api5_module.ksMathPointParam.CLSID)
            x1 = math_point_param.x
            y1 = math_point_param.y

            # Получаем верхнюю правую точку
            p_top = rect_param.GetpTop()
            math_point_param = p_top.QueryInterface(kompas6_api5_module.ksMathPointParam.CLSID)
            x2 = math_point_param.x
            y2 = math_point_param.y

            return (x1, y1, x2, y2)

        return None
    except Exception as e:
        print ("Ошибка при получении габаритного прямоугольника:", str(e))
        return None

# Пример использования:
if __name__ == "__main__":
        # Получаем активный 2D документ
        doc = kompas_object.ActiveDocument2D()

        # Получаем активный документ и контейнер таблиц
        kompas_document = application.ActiveDocument
        kompas_document_2d = kompas_api7_module.IKompasDocument2D(kompas_document)

        iViewsAndLayersManager = kompas_document_2d.ViewsAndLayersManager
        iView = iViewsAndLayersManager.Views
        iW = iView.View(1)
        print(iW.Name)


            # Создаем структуру параметров прямоугольника
        rect_param = kompas_object.GetParamStruct(kompas6_constants.ko_RectParam)


        # Вызываем функцию получения габаритного прямоугольника
        print(doc.ksGetObjGabaritRect(iW.Reference, rect_param))

        GTx = (rect_param.GetpTop().x - iW.X)/1
        GTy = (rect_param.GetpTop().y - iW.Y)/1

        GBx = (rect_param.GetpBot().x - iW.X)/1
        GBy = (rect_param.GetpBot().y - iW.Y)/1

        iRectangleParam = kompas6_api5_module.ksRectangleParam(kompas_object.GetParamStruct(kompas6_constants.ko_RectangleParam))
        iRectangleParam.Init()
        iRectangleParam.x = GBx
        iRectangleParam.y = GBy
        iRectangleParam.ang = 0
        iRectangleParam.height = GTy - GBy
        iRectangleParam.width = GTx - GBx
        iRectangleParam.style = 2
        obj = iDocument2D.ksRectangle(iRectangleParam)

KrissKross

может вам пойти от задачи

далее для просторы
на примере горизонтальных размеров

Задача:
проставить габаритные размеры
- на требуемых видах чертежа
- Виды созданы по 3Д модели

Перед решением проведем подготовительные работы
1. Предварительно получить:
- габариты 3Д модели
- габариты требуемых видов чертежа

2. Предварительные расчеты:
для каждого вида в 3Д модели - проекции-типа ХУ и т.д. - рассчитать смещение от начала координат до габарита влево и вправо
назовем: Хнач. и Хкон. - для проекции ХУ

Решение №1:
зная габариты 3Д модели - заходим в вид чертежа
определяем какая проекция 3Д модели соответствует виду чертежа
и ищем или сверх вниз или наоборот - первую точку
равную - Хнач. или Хкон. - для конкретной проекции 3Д модели

Усложним задачу
если на видах есть произвольная геометрия - нарисованная вручную
- значит берем габариты видов и от них пляшем

примерно так видится решение в первом приближении

Gruden


Student2025

Ого как заплутали в трех соснах, не надо не чего придумывать есть готовые функции в API они сами и высчитают габариты и координаты выдадут еще и центр тяжести как бонус!