CAD文件解析后按照特定规则将图形切割

该博客详细介绍了如何处理CAD文件,包括图形切割、框选、文件大小缩放等步骤。首先,通过检查点位是否在框内确定切割范围,然后根据指定颜色选取图形,使用递归遍历DOM结构来获取和处理图框。接着,生成新的DOM字符串,调整图形尺寸,并计算文件切割后的大小。最后,通过测试代码展示了整个流程,将每个切割部分保存为独立的SVG文件。
摘要由CSDN通过智能技术生成

本文主要对CAD文件解析后,若一份CAD文件中整个图有多个图形需要切割拆分,根据指定规则用独特颜色(内容中没有与其相同的颜色,确保不会因为颜色影响图形内容)的框将需要切割图形单独框起来,再做切割,切割后进行文件的大小缩放计算

直接上代码

一、图形计算相关代码

     * [检查一个path的点位是否在框内]
     * @author caiji
     * @param pathPoint  path点位
     * @param framePoint  矩形框的坐标点
     * @return boolean
     * @date 2022/4/21 17:21
     */
    public static boolean checkIsInFrame(List<PointEntity> pathPoint, List<PointEntity> framePoint){
        boolean flag = false;
        //统一排序后的顺序为 左上,左下,右上,右下
        framePoint = framePoint.stream().sorted(Comparator.comparing(PointEntity::getPointX).thenComparing(PointEntity::getPointY)).collect(Collectors.toList());
        for (PointEntity path : pathPoint){
            //判断点位是否在框的四个点范围内
            //与左上点比较,x和y都应该大于对应的x,y
            //与左下点比较,x大于对应x,y小于对应y
            //与右上点比较,x小于对应x,y大于对应y
            //与右下点比较,x小于对应x,y小于对应y
            if((path.getPointX()>framePoint.get(0).getPointX() && path.getPointY()>framePoint.get(0).getPointY()) &&
               (path.getPointX()>framePoint.get(1).getPointX() && path.getPointY()<framePoint.get(1).getPointY()) &&
               (path.getPointX()<framePoint.get(2).getPointX() && path.getPointY()>framePoint.get(2).getPointY()) &&
               (path.getPointX()<framePoint.get(3).getPointX() && path.getPointY()<framePoint.get(3).getPointY())) {
                flag = true;
            }
        }
        return flag;
    }


    /**
     * [根据path的data数据 获取对应x、y点位]
     * @author caiji
     * @param path
     * @return java.util.List<com.example.importexceldemo.common.PointEntity>
     * @date 2022/4/21 17:45
     */
    public static List<PointEntity> pathPointHandle(String path){
        List<PointEntity> result = new ArrayList<>();
        //切割M
        List<String> splitArray = Arrays.asList(path.split("M"));
        for (String o : splitArray) {
            if (!"".equals(o)) {
                //切割L
                List<String> pointArray = Arrays.asList(o.split("L"));
                for (int k = 0; k < pointArray.size(); k++) {
                    String strItem = pointArray.get(k);
                    //处理其他字母做结尾的坐标
                    if (!Character.isDigit(strItem.substring(strItem.length() - 1).charAt(0))) {
                        strItem = strItem.substring(0, strItem.length() - 1);
                    }
                    //0为x  1为y
                    PointEntity pointEntity = new PointEntity();
                    pointEntity.setPointX(Double.parseDouble(strItem.split(" ")[0]));
                    pointEntity.setPointY(Double.parseDouble(strItem.split(" ")[1]));
                    result.add(pointEntity);
                }
            }
        }
        return result;
    }


    /**
     * [获取图框的数量(根据颜色),以及点位]
     * 思路:按照一个矩形框有四条path属性计算,并且矩形的点位仅四个
     * @author caiji
     * @param node
     * @param framePoint
     * @return void
     * @date 2022/4/25 18:05
     */
    public static void getFrameCount(final Node node,List<PointEntity> framePoint){
        for (int i = 0;i<node.getChildNodes().getLength();i++) {// 遍历所有一级子节点
            NamedNodeMap map = node.getChildNodes().item(i).getAttributes();
            if(null != map){
                if(map.getNamedItem("stroke") != null && map.getNamedItem("stroke").getNodeValue().equals("#0000FF")) {
                    //如果为特定颜色,则为指定的矩形框
                    //取当前data数据,并且取第一个点,因为是矩形,所以存在点位重合,只需要取第一个点即可
                    String point = map.getNamedItem("d").getNodeValue();
                    PointEntity pointEntity = new PointEntity();
                    //取第一个点
                    String firstPoint = point.substring(point.indexOf("M")+1,point.indexOf("L"));
                    pointEntity.setPointX(Double.parseDouble(firstPoint.split(" ")[0]));
                    pointEntity.setPointY(Double.parseDouble(firstPoint.split(" ")[1]));
                    framePoint.add(pointEntity);
                }
            }
            getFrameCount(node.getChildNodes().item(i),framePoint);// 递归
        }
    }

  /**
     * [根据图框数量生成新的DOM格式字符串 -- 即SVG   (前提:CAD解析后并计算绝对坐标点位后再做这些处理)]
     * 思路:传入整个图的node,然后根据计算是否在框内范围将数据存入新的node
     * @author songxy
     * @param node
     * @param framePoint
     * @param newDoc
     * @return void
     * @date 2022/4/25 18:05
     */
    public static void generateNewDomByFrame(final Node node,List<PointEntity> framePoint,Node newDoc){
        for (int i = 0;i<node.getChildNodes().getLength();i++) {// 遍历所有一级子节点
            NamedNodeMap map = node.getChildNodes().item(i).getAttributes();
            if(null != map && null != map.getNamedItem("d")){
                String data = map.getNamedItem("d").getNodeValue();
                List<PointEntity> dataPoint = pathPointHandle(data);
                //取到本次path的data点位,并与框的点位比较,如果在范围内则保存
                boolean flag = checkIsInFrame(dataPoint,framePoint);
                if(flag){
                    Node newChild = node.getChildNodes().item(i).cloneNode(false);
                    newDoc.appendChild(newChild);
                }
            }
            generateNewDomByFrame(node.getChildNodes().item(i),framePoint,newDoc);// 递归
        }

    }

    /**
     * [处理文件切割后的尺寸计算]
     * 思路:CAD解析时存在大样图实际尺寸较大(几万+),所以解析时设置固定尺寸后,文件中的图也会相应压缩,切割后可能要处理显示上存在的位置以及尺寸问题
     * @author songxy
     * @param node
     * @return void
     * @date 2022/4/26 09:54
     */
    public static void handleDomSize(Node node){
        List<String> list = new ArrayList<>();
        List<Double> pointListX = new ArrayList<>();
        List<Double> pointListY = new ArrayList<>();
        getCadSizeData(node,list,pointListX,pointListY);
        Map<String,Double> map = calculationCadSize(pointListX,pointListY);
        Double x = map.get("width");
        Double y = map.get("height");
        Double minXnum = map.get("minXnum");
        Double minYnum = map.get("minYnum");
        //根据最大最小坐标值计算内容的高宽值
        //因为svg中y轴越往上越小,所以用高度相减
        //设置初始点位10,10 计算相对坐标值,不设置为0,0 留一点边距
        Double initX = 10d;
        Double initY = 10d;
        Double offsetX = minXnum - initX;
        Double offsetY = minYnum - initY;
        handleNodeForOffsetPoint(node,offsetX,offsetY);
        x = x+initX*2;
        y = y+initY*2;
        String viewBox = "0 0 "+x+" "+y;
        Node attr = node.getOwnerDocument().createAttribute("viewBox");
        attr.setNodeValue(viewBox);
        node.getAttributes().setNamedItem(attr);
    }

    /**
     * [将所有的框的点位平分,得出有几个框就有几个图]
     * 注意:此处必须是绘制的框是一次性绘制的,不能有框绘制到一半绘制另一个框的情况
     * @author songxy
     * @param pointEntities
     * @return java.util.List<java.util.List<com.example.importexceldemo.common.PointEntity>>
     * @date 2022/4/22 14:10
     */
    public static List<List<PointEntity>> averagePoint(List<PointEntity> pointEntities){
        List<List<PointEntity>> result = new ArrayList<>();
        //框的数量,也是图的数量、需要将pointEntities平分的份数
        int frameCount = pointEntities.size()/4;
        //余数
        int remainder = pointEntities.size()%frameCount;
        //商
        int number = pointEntities.size()/frameCount;
        int offset = 0;
        for (int i = 0;i<frameCount;i++){
            List<PointEntity> item = new ArrayList<>();
            if(remainder > 0){
                item = pointEntities.subList(i*number+offset,(i+1)*number+offset+1);
                remainder--;
                offset++;
            }else{
                item = pointEntities.subList(i*number+offset,(i+1)*number+offset);
            }
            result.add(item);
        }
        return result;
    }

    /**
     * 获取图形宽高尺寸
     * @author songxy
     * @param node
     * @return java.util.Map<java.lang.String,java.lang.Double>
     * @date 2021/11/5 20:33
     */
    public static void getCadSizeData(Node node, List<String> list,List<Double> pointListX,List<Double> pointListY){
        for (int i = 0;i<node.getChildNodes().getLength();i++){
            NamedNodeMap map = node.getChildNodes().item(i).getAttributes();
            if(null != map && map.getNamedItem("d") != null){
                list.add(map.getNamedItem("d").getNodeValue());
                String data = map.getNamedItem("d").getNodeValue().replace("[","").replace("]","");
                //切割M
                List<String> splitArray = Arrays.asList(data.split("M"));
                for (String o : splitArray){
                    if(!"".equals(o)) {
                        //切割L
                        List<String> pointArray = Arrays.asList(o.split("L"));
                        for (int k = 0; k < pointArray.size(); k++) {
                            String strItem = pointArray.get(k);
                            //处理其他字母做结尾的坐标
                            if(!Character.isDigit(strItem.substring(strItem.length()-1).charAt(0))){
                                strItem = strItem.substring(0,strItem.length()-1);
                            }
                            //0为x  1为y
                            Double numX = Double.parseDouble(strItem.split(" ")[0]);
                            Double numY = Double.parseDouble(strItem.split(" ")[1]);
                            pointListX.add(numX);
                            pointListY.add(numY);
                        }
                    }
                }
            }
            getCadSizeData(node.getChildNodes().item(i),list,pointListX,pointListY);
        }
    }

    /**
     * 计算图形高宽
     * @author songxy
     * @param pointListX
     * @param pointListY
     * @return java.util.Map<java.lang.String,java.lang.Double>
     * @date 2021/11/6 12:55
     */
    public static Map<String,Double> calculationCadSize(List<Double> pointListX, List<Double> pointListY){
        Double maxXnum = Double.parseDouble(Collections.max(pointListX).toString());
        Double minXnum = Double.parseDouble(Collections.min(pointListX).toString());
        Double maxYnum = Double.parseDouble(Collections.max(pointListY).toString());
        Double minYnum = Double.parseDouble(Collections.min(pointListY).toString());
        //根据最大最小坐标值计算内容的高宽值
        Double x = maxXnum-minXnum;
        Double y = maxYnum-minYnum;
        Map<String,Double> map = new HashMap<>();
        map.put("width",x);
        map.put("height",y);
        map.put("minXnum",minXnum);
        map.put("minYnum",minYnum);
        return map;
    }

    /**
     * 按公式计算d属性里面的坐标并且重新按偏移量计算赋值
     * @author songxy
     * @param node
     * @param offsetX
     * @param offsetY
     * @return void
     * @date 2021/11/4 10:18
     */
    public static void handleNodeForOffsetPoint(Node node, Double offsetX,Double offsetY){
        for (int i = 0;i<node.getChildNodes().getLength();i++){
            if(node.getChildNodes().item(i).hasAttributes() && node.getChildNodes().item(i).getAttributes().getNamedItem("d") != null){
                String data = node.getChildNodes().item(i).getAttributes().getNamedItem("d").getNodeValue().replace("[","").replace("]","");
                //切割M
                List<String> splitArray = Arrays.asList(data.split("M"));
                StringBuffer newData = new StringBuffer();
                for (String o : splitArray){
                    if(!"".equals(o)) {
                        //切割L
                        List<String> pointArray = Arrays.asList(o.split("L"));
                        for (int k = 0; k < pointArray.size(); k++) {
                            String lastChar = "";
                            String strItem = pointArray.get(k);
                            if(!Character.isDigit(strItem.substring(strItem.length()-1).charAt(0))){
                                lastChar = strItem.substring(strItem.length()-1);
                                strItem = strItem.substring(0,strItem.length()-1);
                            }
                            //0为x  1为y
                            Double numX = Double.parseDouble(strItem.split(" ")[0]);
                            Double numY = Double.parseDouble(strItem.split(" ")[1]);
                            //根据偏移量计算新点位
                            Double newX = numX-offsetX;
                            Double newY = numY-offsetY;
                            //拼接d属性值的新坐标
                            if (k == 0) {
                                newData.append("M");
                                newData.append(newX);
                                newData.append(" ");
                                newData.append(newY);
                            }else{
                                newData.append("L");
                                newData.append(newX);
                                newData.append(" ");
                                newData.append(newY);
                                newData.append(lastChar);
                            }
                        }
                    }
                }
                node.getChildNodes().item(i).getAttributes().getNamedItem("d").setNodeValue(newData.toString());
            }
            handleNodeForOffsetPoint(node.getChildNodes().item(i),offsetX,offsetY);
        }
    }

    /**
     * 将element转换成XML字符串
     * @param element
     * @return
     */
    public static String convertElemToSVG(Node element) {
        TransformerFactory transFactory = TransformerFactory.newInstance();
        Transformer transformer = null;
        try {
            transformer = transFactory.newTransformer();
        } catch (TransformerConfigurationException e) {
            e.printStackTrace();
        }
        StringWriter buffer = new StringWriter();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        try {
            transformer.transform(new DOMSource(element), new StreamResult(buffer));
        } catch (TransformerException e) {
            e.printStackTrace();
        }
        String elementStr = buffer.toString();
        return elementStr;
    }

二、测试代码 万年main方法调试

    public static void main(String[] args) {
        String svgFilePath = "/CAD文件/04253.svg";
        try {
            File file = new File(svgFilePath);
            String parser = XMLResourceDescriptor.getXMLParserClassName();
            SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
            Document doc = f.createDocument(file.toURI().toString());
            List<PointEntity> pointEntities = new ArrayList<>();
            //获取所有特定框的点位
            getFrameCount(doc.getDocumentElement(),pointEntities);
            //处理点位数据,得到有多少个框,即多少个图形和图形的框的四个点位
            List<List<PointEntity>> frameList = averagePoint(pointEntities);
            //根据框数将图形全部生成新的文件
            for (int i = 1;i<=frameList.size();i++){
                Node newDoc = doc.getDocumentElement().cloneNode(false);
                //生成新的dom
                generateNewDomByFrame(doc.getDocumentElement(),frameList.get(i-1),newDoc);
                //切割后图形所在的位置不会变更,所以需要处理dom尺寸
                handleDomSize(newDoc);
                String str = convertElemToSVG(newDoc);
                OutputStream outputStream = new FileOutputStream("/CAD文件/0455"+i+".svg");
                OutputStreamWriter os = new OutputStreamWriter(outputStream,"utf-8");
                os.write(str);
                os.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值