/* * ******************************************************************************** * This file is part of the LibreCAD project, a 2D CAD program * * Copyright (C) 2025 LibreCAD.org * Copyright (C) 2025 sand1024 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ******************************************************************************** */ #include "lc_dimarrowregistry.h" #include "lc_arrow_box.h" #include "lc_arrow_circle.h" #include "lc_arrow_datum.h" #include "lc_arrow_dot.h" #include "lc_arrow_headclosed.h" #include "lc_arrow_headclosed_blank.h" #include "lc_arrow_headopen.h" #include "lc_arrow_integral.h" #include "lc_arrow_none.h" #include "lc_arrow_tick.h" #include "rs_arc.h" #include "rs_block.h" #include "rs_blocklist.h" #include "rs_circle.h" #include "rs_creation.h" #include "rs_entitycontainer.h" #include "rs_graphic.h" #include "rs_insert.h" #include "rs_line.h" #include "rs_math.h" #include "rs_solid.h" #include "rs_vector.h" LC_DimArrowRegistry::LC_DimArrowRegistry() { init(); } std::vector LC_DimArrowRegistry::m_defaultArrowsInfo; QString LC_DimArrowRegistry::ArrowInfo::ARROW_TYPE_OBLIQUE = "_OBLIQUE"; QString LC_DimArrowRegistry::ArrowInfo::ARROW_TYPE_ARCHTICK = "_ARCHTICK"; bool LC_DimArrowRegistry::isStandardBlockName(const QString& blockName) { init(); for (const auto& info : m_defaultArrowsInfo) { if (info.blockName.compare(blockName, Qt::CaseInsensitive) == 0) { return true; } } return false; } bool LC_DimArrowRegistry::getArrowInfoByBlockName(const QString& blockName, ArrowInfo& found) { init(); for (const auto& info : m_defaultArrowsInfo) { if (info.blockName.compare(blockName, Qt::CaseInsensitive) == 0) { found = info; return true; } } return false; } bool LC_DimArrowRegistry::getArrowInfoByType(const ArrowType type, ArrowInfo& found) { init(); for (const auto& info : m_defaultArrowsInfo) { if (info.type == type) { found = info; return true; } } return false; } std::pair LC_DimArrowRegistry::createArrowBlock(RS_EntityContainer* container, const QString& blockName, const RS_Vector& point, double directionAngle, double arrowSize) { auto graphic = container->getGraphic(); ArrowInfo info; if (graphic != nullptr) { RS_BlockList* blocksList = graphic->getBlockList(); auto customBlock = blocksList->findCaseInsensitive(blockName); if (customBlock != nullptr) { double dimLineExtension = 0.0; // check whether this is a block for standard arrows - and whether we need to adjust dimline points // to address AutoCAD-like rendering of such special built-in arrow types if (getArrowInfoByBlockName(blockName, info)) { dimLineExtension = info.dimLineCorrection; } auto blockEntity = createCustomArrowBlock(container, customBlock->getName(), point, directionAngle, arrowSize); return {blockEntity, dimLineExtension}; } } bool hasArrowType = getArrowInfoByBlockName(blockName, info); if (!hasArrowType) { info = m_defaultArrowsInfo[0]; } auto blockEntity = createDefaultArrowBlock(container, info.type, point, directionAngle, arrowSize); return {blockEntity, 0.0}; } RS_Entity* LC_DimArrowRegistry::createDefaultArrowBlock(RS_EntityContainer* container, ArrowType type, const RS_Vector& point, double directionAngle, double arrowSize) { switch (type) { case (closed_filled): { return new LC_ArrowHeadClosed(container, point, directionAngle, arrowSize, 0.165, true); } case (dot): { return new LC_ArrowCircle(container, point, directionAngle, arrowSize, LC_ArrowCircle::dot); } case (dot_small): { return new LC_ArrowDot(container, point, directionAngle, arrowSize, LC_ArrowDot::blank); } case (dot_blank): { return new LC_ArrowCircle(container, point, directionAngle, arrowSize, LC_ArrowCircle::dot_blank); } case (origin_indicator): { return new LC_ArrowCircle(container, point, directionAngle, arrowSize, LC_ArrowCircle::origin_indicator); } case (origin_indicator_2): { return new LC_ArrowCircle(container, point, directionAngle, arrowSize, LC_ArrowCircle::origin_indicator2); } case (open): { return new LC_ArrowHeadOpen(container, point, directionAngle, arrowSize, 0.165); } case (right_angle): { return new LC_ArrowHeadOpen(container, point, directionAngle, arrowSize, 0.785398); } case (open_30): { return new LC_ArrowHeadOpen(container, point, directionAngle, arrowSize, 0.261799); } case (closed): { return new LC_ArrowHeadClosed(container, point, directionAngle, arrowSize, 0.165, false); } case (dot_small_blank): { return new LC_ArrowDot(container, point, directionAngle, arrowSize, LC_ArrowDot::blank); } case (none): { return new LC_ArrowNone(container, point, directionAngle, arrowSize); } case (oblique): { return new LC_ArrowTick(container, point, directionAngle, arrowSize, false); } case (box_filled): { return new LC_ArrowBox(container, point, directionAngle, arrowSize, true); } case (box): { return new LC_ArrowBox(container, point, directionAngle, arrowSize, false); } case (closed_blank): { return new LC_ArrowHeadClosedBlank(container, point, directionAngle, arrowSize, 0.165); } case (datum_triangle_filled): { return new LC_ArrowDatum(container, point, directionAngle, arrowSize, true); } case (datum_triangle): { return new LC_ArrowDatum(container, point, directionAngle, arrowSize, false); } case (integral): { return new LC_ArrowIntegral(container, point, directionAngle, arrowSize); } case (architectural_tick): { return new LC_ArrowTick(container, point, directionAngle, arrowSize, true); } default: break; } return nullptr; } RS_Entity* LC_DimArrowRegistry::createCustomArrowBlock(RS_EntityContainer* container, QString blockName, const RS_Vector& point, double direction_angle, double arrowSize) { auto insertData = new RS_InsertData(blockName, point, RS_Vector(arrowSize, arrowSize), direction_angle, 1, 1, RS_Vector(0, 0), nullptr, RS2::Update); auto ins = new RS_Insert(container, *insertData); return ins; } void LC_DimArrowRegistry::init() { if (m_defaultArrowsInfo.empty()) { m_defaultArrowsInfo = { {"", closed_filled, tr("Closed Filled"), 0.0}, // todo - sand - dot is supported by ACAD for setting default arrow block. Think about adding such support too // {".", closed_filled, tr("Closed Filled")}, {"_DOT", dot, tr("Dot"), 0.0}, {"_DOTSMALL", dot_small, tr("Dot Small"), 1.0}, {"_DOTBLANK", dot_blank, tr("Dot Blank"), 0.0}, {"_ORIGIN", origin_indicator, tr("Origin Indicator"), 0.0}, {"_ORIGIN2", origin_indicator_2, tr("Origin Indicator 2"), 0.0}, {"_OPEN", open, tr("Open"), 0.0}, {"_OPEN90", right_angle, tr("Right Angle"), 0.0}, {"_OPEN30", open_30, tr("Open 30"), 0.0}, {"_CLOSED", closed, tr("Closed"), 0.0}, {"_SMALL", dot_small_blank, tr("Dot Small Blank"), 1}, {"_NONE", none, tr("None"), 1.0}, {ArrowInfo::ARROW_TYPE_OBLIQUE, oblique, tr("Oblique"), 1.0}, {"_BOXFILLED", box_filled, tr("Box Filled"), 0.0}, {"_BOXBLANK", box, tr("Box Blank"), 0.0}, {"_CLOSEDBLANK", closed_blank, tr("Closed Blank"), 0.0}, {"_DATUMFILLED", datum_triangle_filled, tr("Datum Filled"), 0.0}, {"_DATUMBLANK", datum_triangle, tr("Datum Blank"), 0.0}, {"_INTEGRAL", integral, tr("Integral"), 1.0}, {ArrowInfo::ARROW_TYPE_ARCHTICK, architectural_tick, tr("Architecture Tick"), 1.0} }; } } void LC_DimArrowRegistry::insertStandardArrowBlocks(RS_EntityContainer* container) { auto graphic = container->getGraphic(); ArrowInfo info; if (graphic != nullptr) { RS_BlockList* blocksList = graphic->getBlockList(); for (auto arrowInfo : m_defaultArrowsInfo) { insertStandardArrowBlock(container, blocksList, arrowInfo); } } } void LC_DimArrowRegistry::fillArrowBlockByEntities(RS_Block* block, ArrowType arrow) { switch (arrow) { case closed_filled: { block->addByBlockEntity(new RS_Solid({{0.0, 0.0}, {-1.0, 0.1667}, {-1.0, -0.1667}})); break; } case dot: { block->addByBlockLine({-1.0, 0.0}, {-0.5, 0.0}); block->addByBlockEntity(new RS_Circle({{0.0, 0.0}, 0.25})); break; } case dot_small: { block->addByBlockEntity(new RS_Circle({{0.0, 0.0}, 0.0625})); break; } case dot_blank: { block->addByBlockLine({-1.0, 0.0}, {-0.5, 0.0}); block->addByBlockEntity(new RS_Circle({{0.0, 0.0}, 0.5})); break; } case origin_indicator: { block->addByBlockLine({0.0, 0.0}, {-1.0, 0.0}); block->addByBlockEntity(new RS_Circle({{0.0, 0.0}, 0.5})); break; } case origin_indicator_2: { block->addByBlockLine({-1.0, 0.0}, {-0.5, 0.0}); block->addByBlockEntity(new RS_Circle({{0.0, 0.0}, 0.5})); block->addByBlockEntity(new RS_Circle({{0.0, 0.0}, 0.25})); break; } case open: { block->addByBlockLine({-1.0, 0.1667}, {0.0, 0.0}); block->addByBlockLine({-1.0, 0.0}, {0.0, 0.0}); block->addByBlockLine({-1.0, -0.1667}, {0.0, 0.0}); break; } case right_angle: { block->addByBlockLine({-0.5, 0.5}, {0.0, 0.0}); block->addByBlockLine({-1.0, 0.0}, {0.0, 0.0}); block->addByBlockLine({-0.5, -0.5}, {0.0, 0.0}); break; } case open_30: { block->addByBlockLine({-1.0, 0.2679}, {0.0, 0.0}); block->addByBlockLine({-1.0, 0.0}, {0.0, 0.0}); block->addByBlockLine({-1.0, -0.2679}, {0.0, 0.0}); break; } case closed: { block->addByBlockLine({0.0, 0.0}, {-1.0, 0.0}); block->addByBlockLine({0.0, 0.0}, {-1.0, 0.1667}); block->addByBlockLine({-1.0, 0.1667}, {-1.0, -0.1667}); block->addByBlockLine({-1.0, -0.1667}, {0.0, 0.0}); break; } case dot_small_blank: { block->addByBlockEntity(new RS_Circle({{0.0, 0.0}, 0.25})); break; } case none: { break; } case oblique: { block->addByBlockLine({-0.5, -0.5}, {0.5, 0.5}); break; } case box_filled: { // last 2 vertexes are switched by solid! block->addByBlockEntity(new RS_Solid({{-0.5, -0.5}, {-0.5, 0.5}, {0.5, -0.5}, {0.5, 0.5}})); block->addByBlockLine({-0.5, 0.0}, {-1.0, 0.0}); break; } case box: { block->addByBlockLine({0.5, -0.5}, {0.5, 0.5}); block->addByBlockLine({0.5, 0.5}, {-0.5, 0.5}); block->addByBlockLine({-0.5, 0.5}, {-0.5, -0.5}); block->addByBlockLine({-0.5, -0.5}, {0.5, -0.5}); block->addByBlockLine({-0.5, 0.0}, {-1.0, 0.0}); break; } case closed_blank: { block->addByBlockLine({0.0, 0.0}, {-1.0, 0.1667}); block->addByBlockLine({-1.0, 0.1667}, {-1.0, -0.1667}); block->addByBlockLine({-1.0, -0.1667}, {0.0, 0.0}); break; } case datum_triangle_filled: { block->addByBlockEntity(new RS_Solid({{0.0, -0.5774}, {0.0, 0.5774}, {-1.0, 0}})); break; } case datum_triangle: { block->addByBlockLine({0.0, -0.5774}, {0.0, 0.5774}); block->addByBlockLine({0.0, 0.5774}, {-1.0, 0}); block->addByBlockLine({-1.0, 0.0}, {0.0, -0.5774}); break; } case integral: { RS_ArcData data1({-0.4449, 0.0913}, 0.4542, RS_Math::deg2rad(282), RS_Math::deg2rad(348), false); block->addByBlockEntity(new RS_Arc(data1)); RS_ArcData data2({0.4449, -0.0913}, 0.4542, RS_Math::deg2rad(66), RS_Math::deg2rad(168), false); block->addByBlockEntity(new RS_Arc(data2)); break; } case architectural_tick: { block->addByBlockLine({-0.5, -0.5}, {0.5, 0.5}); break; } default: break; } } void LC_DimArrowRegistry::insertStandardArrowBlock(RS_EntityContainer* container, RS_BlockList* blocksList, LC_DimArrowRegistry::ArrowInfo arrowInfo) { QString blockName = arrowInfo.blockName; auto customBlock = blocksList->findCaseInsensitive(blockName); if (customBlock == nullptr) { RS_BlockData d = RS_BlockData(blockName, RS_Vector(0.0, 0.0), false); customBlock = new RS_Block(container, d); customBlock->setAutoUpdateBorders(false); fillArrowBlockByEntities(customBlock, arrowInfo.type); blocksList->add(customBlock, true); } } void LC_DimArrowRegistry::insertStandardArrowBlock(RS_EntityContainer* container, RS_BlockList* blocksList, LC_DimArrowRegistry::ArrowInfo* arrowInfo) { QString blockName = arrowInfo->name; RS_BlockData d = RS_BlockData(blockName, RS_Vector(0.0, 0.0), false); auto customBlock = new RS_Block(container, d); customBlock->setAutoUpdateBorders(false); fillArrowBlockByEntities(customBlock, arrowInfo->type); blocksList->add(customBlock, false); } void LC_DimArrowRegistry::insertStandardArrowBlocks(RS_Graphic* graphic, const QList& styles) { auto blockList = graphic->getBlockList(); auto container = graphic->getDocument(); QSet uniqueArrowBlockNames; collectUsedArrowTypes(styles, uniqueArrowBlockNames); for (const auto& blockName : uniqueArrowBlockNames) { if (blockList->findCaseInsensitive(blockName) == nullptr) { ArrowInfo info; if (getArrowInfoByBlockName(blockName, info)) { insertStandardArrowBlock(container, blockList, info); } } } } void LC_DimArrowRegistry::collectUsedArrowTypes(const QList& list, QSet& uniqueArrowBlockNames) { for (auto dimStyle : list) { auto arrowhead = dimStyle->arrowhead(); QString firstArrowBlockName = arrowhead->arrowHeadBlockNameFirst(); QString secondArrowBlockName = arrowhead->arrowHeadBlockNameSecond(); QString sameArrowBlockName = arrowhead->sameBlockName(); QString leaderName = dimStyle->leader()->arrowBlockName(); if (!firstArrowBlockName.isEmpty()) { uniqueArrowBlockNames << firstArrowBlockName; } if (!secondArrowBlockName.isEmpty()) { uniqueArrowBlockNames << secondArrowBlockName; } if (!sameArrowBlockName.isEmpty()) { uniqueArrowBlockNames << sameArrowBlockName; } if (!leaderName.isEmpty()) { uniqueArrowBlockNames << leaderName; } } // no need for default arrow if (uniqueArrowBlockNames.contains("_CLOSEDFILLED")) { uniqueArrowBlockNames.remove("_CLOSEDFILLED"); } } void LC_DimArrowRegistry::insertStandardArrowBlock(RS_EntityContainer* container, RS_BlockList* blocksList, ArrowType arrowType) { ArrowInfo arrowInfo; if (getArrowInfoByType(arrowType, arrowInfo)) { insertStandardArrowBlock(container, blocksList, arrowInfo); } } void LC_DimArrowRegistry::fillDefaultArrowTypes(std::vector& arrowTypes) { init(); for (auto at : m_defaultArrowsInfo) { QString blockName = at.blockName; if (blockName.isEmpty()) { blockName = "_CLOSEDFILLED"; } ArrowInfo arrowType(blockName, at.type, at.name, at.dimLineCorrection); arrowTypes.push_back(arrowType); } } bool LC_DimArrowRegistry::isObliqueOrArchArrow(const QString& blockName) { return blockName.compare(ArrowInfo::ARROW_TYPE_OBLIQUE, Qt::CaseInsensitive) == 0 || blockName.compare(ArrowInfo::ARROW_TYPE_ARCHTICK, Qt::CaseInsensitive) == 0; }