diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js index 59d82c56..7017943f 100644 --- a/src/util/skeleton-utils.js +++ b/src/util/skeleton-utils.js @@ -11,1071 +11,7 @@ import { QLine } from '@/components/fabric/QLine' * @param {Array} existingSkeletonLines - 기존에 생성된 스켈레톤 라인 */ -/* -canvas = { - "version": "5.4.2", - "objects": [ - { - "type": "circle", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 467, - "top": 45.2, - "width": 0, - "height": 0, - "fill": "rgb(0,0,0)", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": false, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "radius": 0, - "startAngle": 0, - "endAngle": 360 - }, - { - "type": "circle", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 467, - "top": 443.2, - "width": 0, - "height": 0, - "fill": "rgb(0,0,0)", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": false, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "radius": 0, - "startAngle": 0, - "endAngle": 360 - }, - { - "type": "circle", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 1110, - "top": 443.2, - "width": 0, - "height": 0, - "fill": "rgb(0,0,0)", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": false, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "radius": 0, - "startAngle": 0, - "endAngle": 360 - }, - { - "type": "circle", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 1110, - "top": 45.2, - "width": 0, - "height": 0, - "fill": "rgb(0,0,0)", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": false, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "radius": 0, - "startAngle": 0, - "endAngle": 360 - }, - { - "type": "circle", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 467, - "top": 45.2, - "width": 0, - "height": 0, - "fill": "rgb(0,0,0)", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": false, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "radius": 0, - "startAngle": 0, - "endAngle": 360 - }, - { - "type": "QLine", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 467, - "top": 45.2, - "width": 0, - "height": 398, - "fill": "rgb(0,0,0)", - "stroke": "black", - "strokeWidth": 3, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": true, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "key": true, - "attributes": { - "offset": 50, - "pitch": 4, - "type": "eaves", - "onlyOffset": false, - "roofId": "75c4a7ae-5053-4a4f-8241-2462906dae0e", - "wallId": "ce69d453-989a-49d9-88f6-201086ebc5fe", - "planeSize": 3980, - "actualSize": 3980, - "originPoint": { - "x1": 467, - "y1": 45.2, - "x2": 467, - "y2": 443.2 - } - } - }, - { - "type": "QLine", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 467, - "top": 443.2, - "width": 643, - "height": 0, - "fill": "rgb(0,0,0)", - "stroke": "black", - "strokeWidth": 3, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": true, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "key": true, - "attributes": { - "offset": 50, - "pitch": 4, - "type": "eaves", - "onlyOffset": false, - "roofId": "75c4a7ae-5053-4a4f-8241-2462906dae0e", - "wallId": "ce69d453-989a-49d9-88f6-201086ebc5fe", - "planeSize": 6430, - "actualSize": 6430, - "originPoint": { - "x1": 467, - "y1": 443.2, - "x2": 1110, - "y2": 443.2 - } - } - }, - { - "type": "QLine", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 1110, - "top": 45.2, - "width": 0, - "height": 398, - "fill": "rgb(0,0,0)", - "stroke": "black", - "strokeWidth": 3, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": true, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "key": true, - "attributes": { - "offset": 50, - "pitch": 4, - "type": "eaves", - "onlyOffset": false, - "roofId": "75c4a7ae-5053-4a4f-8241-2462906dae0e", - "wallId": "ce69d453-989a-49d9-88f6-201086ebc5fe", - "planeSize": 3980, - "actualSize": 3980, - "originPoint": { - "x1": 1110, - "y1": 443.2, - "x2": 1110, - "y2": 45.2 - } - } - }, - { - "type": "QLine", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 467, - "top": 45.2, - "width": 643, - "height": 0, - "fill": "rgb(0,0,0)", - "stroke": "black", - "strokeWidth": 3, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": true, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "key": true, - "attributes": { - "offset": 50, - "pitch": 4, - "type": "eaves", - "onlyOffset": false, - "roofId": "75c4a7ae-5053-4a4f-8241-2462906dae0e", - "wallId": "ce69d453-989a-49d9-88f6-201086ebc5fe", - "planeSize": 6430, - "actualSize": 6430, - "originPoint": { - "x1": 1110, - "y1": 45.2, - "x2": 467, - "y2": 45.2 - } - } - }, - { - "type": "QPolygon", - "version": "5.4.2", - "originX": "center", - "originY": "center", - "left": 788.5, - "top": 244.2, - "width": 643, - "height": 398, - "fill": "transparent", - "stroke": "#000000", - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": true, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "points": [ - { - "x": 467, - "y": 45.2, - "x1": 467, - "y1": 45.2, - "x2": 467, - "y2": 443.2 - }, - { - "x": 467, - "y": 443.2, - "x1": 467, - "y1": 443.2, - "x2": 1110, - "y2": 443.2 - }, - { - "x": 1110, - "y": 443.2, - "x1": 1110, - "y1": 443.2, - "x2": 1110, - "y2": 45.2 - }, - { - "x": 1110, - "y": 45.2, - "x1": 1110, - "y1": 45.2, - "x2": 467, - "y2": 45.2 - } - ], - "key": true - }, - { - "type": "text", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 467, - "top": 194.2, - "width": 30, - "height": 22.6, - "fill": "black", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": true, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "fontFamily": "Times New Roman", - "fontWeight": "normal", - "fontSize": 20, - "text": "500", - "underline": false, - "overline": false, - "linethrough": false, - "textAlign": "left", - "fontStyle": "normal", - "lineHeight": 1.16, - "textBackgroundColor": "", - "charSpacing": 0, - "styles": [], - "direction": "ltr", - "path": null, - "pathStartOffset": 0, - "pathSide": "left", - "pathAlign": "baseline" - }, - { - "type": "text", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 838.5, - "top": 413.2, - "width": 30, - "height": 22.6, - "fill": "black", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": true, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "fontFamily": "Times New Roman", - "fontWeight": "normal", - "fontSize": 20, - "text": "500", - "underline": false, - "overline": false, - "linethrough": false, - "textAlign": "left", - "fontStyle": "normal", - "lineHeight": 1.16, - "textBackgroundColor": "", - "charSpacing": 0, - "styles": [], - "direction": "ltr", - "path": null, - "pathStartOffset": 0, - "pathSide": "left", - "pathAlign": "baseline" - }, - { - "type": "text", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 1110, - "top": 194.2, - "width": 30, - "height": 22.6, - "fill": "black", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": true, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "fontFamily": "Times New Roman", - "fontWeight": "normal", - "fontSize": 20, - "text": "500", - "underline": false, - "overline": false, - "linethrough": false, - "textAlign": "left", - "fontStyle": "normal", - "lineHeight": 1.16, - "textBackgroundColor": "", - "charSpacing": 0, - "styles": [], - "direction": "ltr", - "path": null, - "pathStartOffset": 0, - "pathSide": "left", - "pathAlign": "baseline" - }, - { - "type": "text", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 838.5, - "top": 15.2, - "width": 30, - "height": 22.6, - "fill": "black", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": true, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "fontFamily": "Times New Roman", - "fontWeight": "normal", - "fontSize": 20, - "text": "500", - "underline": false, - "overline": false, - "linethrough": false, - "textAlign": "left", - "fontStyle": "normal", - "lineHeight": 1.16, - "textBackgroundColor": "", - "charSpacing": 0, - "styles": [], - "direction": "ltr", - "path": null, - "pathStartOffset": 0, - "pathSide": "left", - "pathAlign": "baseline" - }, - { - "type": "QPolygon", - "version": "5.4.2", - "originX": "center", - "originY": "center", - "left": 788.5, - "top": 244.2, - "width": 743, - "height": 498, - "fill": "transparent", - "stroke": "#1083E3", - "strokeWidth": 2, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": true, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "points": [ - { - "x": 417, - "y": -4.8 - }, - { - "x": 417, - "y": 493.2 - }, - { - "x": 1160, - "y": 493.2 - }, - { - "x": 1160, - "y": -4.8 - } - ], - "key": true - }, - { - "type": "textbox", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 417, - "top": 244.2, - "width": 36.56, - "height": 18.08, - "fill": "black", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": false, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "fontFamily": "MS PGothic", - "fontWeight": "normal", - "fontSize": 16, - "text": "3980", - "underline": false, - "overline": false, - "linethrough": false, - "textAlign": "left", - "fontStyle": "normal", - "lineHeight": 1.16, - "textBackgroundColor": "", - "charSpacing": 0, - "styles": [], - "direction": "ltr", - "path": null, - "pathStartOffset": 0, - "pathSide": "left", - "pathAlign": "baseline", - "minWidth": 20, - "splitByGrapheme": false - }, - { - "type": "textbox", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 788.5, - "top": 453.2, - "width": 36.4, - "height": 18.08, - "fill": "black", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": false, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "fontFamily": "MS PGothic", - "fontWeight": "normal", - "fontSize": 16, - "text": "6430", - "underline": false, - "overline": false, - "linethrough": false, - "textAlign": "left", - "fontStyle": "normal", - "lineHeight": 1.16, - "textBackgroundColor": "", - "charSpacing": 0, - "styles": [], - "direction": "ltr", - "path": null, - "pathStartOffset": 0, - "pathSide": "left", - "pathAlign": "baseline", - "minWidth": 20, - "splitByGrapheme": false - }, - { - "type": "textbox", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 1120, - "top": 244.2, - "width": 36.56, - "height": 18.08, - "fill": "black", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": false, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "fontFamily": "MS PGothic", - "fontWeight": "normal", - "fontSize": 16, - "text": "3980", - "underline": false, - "overline": false, - "linethrough": false, - "textAlign": "left", - "fontStyle": "normal", - "lineHeight": 1.16, - "textBackgroundColor": "", - "charSpacing": 0, - "styles": [], - "direction": "ltr", - "path": null, - "pathStartOffset": 0, - "pathSide": "left", - "pathAlign": "baseline", - "minWidth": 20, - "splitByGrapheme": false - }, - { - "type": "textbox", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 788.5, - "top": 15.2, - "width": 36.4, - "height": 18.08, - "fill": "black", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": false, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "fontFamily": "MS PGothic", - "fontWeight": "normal", - "fontSize": 16, - "text": "6430", - "underline": false, - "overline": false, - "linethrough": false, - "textAlign": "left", - "fontStyle": "normal", - "lineHeight": 1.16, - "textBackgroundColor": "", - "charSpacing": 0, - "styles": [], - "direction": "ltr", - "path": null, - "pathStartOffset": 0, - "pathSide": "left", - "pathAlign": "baseline", - "minWidth": 20, - "splitByGrapheme": false - }, - { - "type": "line", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 467, - "top": 45.2, - "width": 0, - "height": 398, - "fill": "rgb(0,0,0)", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": false, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "key": true - }, - { - "type": "line", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 467, - "top": 443.2, - "width": 643, - "height": 0, - "fill": "rgb(0,0,0)", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": false, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "key": true - }, - { - "type": "line", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 1110, - "top": 45.2, - "width": 0, - "height": 398, - "fill": "rgb(0,0,0)", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": false, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "key": true - }, - { - "type": "line", - "version": "5.4.2", - "originX": "left", - "originY": "top", - "left": 467, - "top": 45.2, - "width": 643, - "height": 0, - "fill": "rgb(0,0,0)", - "stroke": null, - "strokeWidth": 1, - "strokeDashArray": null, - "strokeLineCap": "butt", - "strokeDashOffset": 0, - "strokeLineJoin": "miter", - "strokeUniform": false, - "strokeMiterLimit": 4, - "scaleX": 1, - "scaleY": 1, - "angle": 0, - "flipX": false, - "flipY": false, - "opacity": 1, - "shadow": null, - "visible": false, - "backgroundColor": "", - "fillRule": "nonzero", - "paintFirst": "fill", - "globalCompositeOperation": "source-over", - "skewX": 0, - "skewY": 0, - "key": true - } - ] -} -, -roofId = "75c4a7ae-5053-4a4f-8241-2462906dae0e" -textMode = "plane" - - - */ export const drawSkeletonRidgeRoof = (roofId, canvas, textMode) => { const roof = canvas?.getObjects().find((object) => object.id === roofId) if (!roof) { @@ -1268,8 +204,8 @@ function processGableEdge(edgeResult, baseLines, skeletonLines, selectBaseLine, for (let i = skeletonLines.length - 1; i >= 0; i--) { const line = skeletonLines[i]; const isEdgeLine = line.p1 && line.p2 && - edgePoints.some(ep => Math.abs(ep.x - line.p1.x) < 0.001 && Math.abs(ep.y - line.p1.y) < 0.001) && - edgePoints.some(ep => Math.abs(ep.x - line.p2.x) < 0.001 && Math.abs(ep.y - line.p2.y) < 0.001); + edgePoints.some(ep => Math.abs(ep.x - line.p1.x) < 0.001 && Math.abs(ep.y - line.p1.y) < 0.001) && + edgePoints.some(ep => Math.abs(ep.x - line.p2.x) < 0.001 && Math.abs(ep.y - line.p2.y) < 0.001); if (isEdgeLine) { skeletonLines.splice(i, 1); @@ -1533,37 +469,81 @@ export const findDisconnectedSkeletonLines = (skeletonLines, baseLines) => { let extendedLine = null; if (!p1Connected) { extendedLine = extendFromP2TowardP1(line.p1, line.p2, baseLines, skeletonLines, index); + + // [수정] 1차 연장 시도(Raycast) 실패 시, 수직 투영(Projection) 대신 모든 선분과의 교차점을 찾는 방식으로 변경 if (!extendedLine) { - let closestBaseLine = null; + let closestIntersection = null; let minDistance = Infinity; - let projection = null; - baseLines.forEach(base => { - const p = getProjectionPoint(line.p1, base); - const d = Math.sqrt(Math.pow(line.p1.x - p.x, 2) + Math.pow(line.p1.y - p.y, 2)); - if (d < minDistance) { - minDistance = d; - closestBaseLine = base; - projection = p; + + // 모든 외벽선과 다른 내부선을 타겟으로 설정 + const allTargetLines = [ + ...baseLines.map(l => ({ p1: {x: l.x1, y: l.y1}, p2: {x: l.x2, y: l.y2} })), + ...skeletonLines.filter((_, i) => i !== index) + ]; + + allTargetLines.forEach(targetLine => { + // 무한 직선 간의 교차점을 찾음 + const intersection = getInfiniteLineIntersection(line.p1, line.p2, targetLine.p1, targetLine.p2); + + // 교차점이 존재하고, 타겟 '선분' 위에 있는지 확인 + if (intersection && isPointOnSegmentForExtension(intersection, targetLine.p1, targetLine.p2)) { + // 연장 방향이 올바른지 확인 (뒤로 가지 않도록) + const lineVec = { x: line.p1.x - line.p2.x, y: line.p1.y - line.p2.y }; + const intersectVec = { x: intersection.x - line.p1.x, y: intersection.y - line.p1.y }; + const dotProduct = lineVec.x * intersectVec.x + lineVec.y * intersectVec.y; + + if (dotProduct >= -1e-6) { // 교차점이 p1 기준으로 '앞'에 있을 경우 + const dist = Math.sqrt(Math.pow(line.p1.x - intersection.x, 2) + Math.pow(line.p1.y - intersection.y, 2)); + if (dist > 0.1 && dist < minDistance) { // 자기 자신이 아니고, 가장 가까운 교차점 갱신 + minDistance = dist; + closestIntersection = intersection; + } + } } }); - if(projection) extendedLine = { point: projection }; + + if (closestIntersection) { + extendedLine = { point: closestIntersection }; + } } } else if (!p2Connected) { extendedLine = extendFromP2TowardP1(line.p2, line.p1, baseLines, skeletonLines, index); + + // [수정] 1차 연장 시도(Raycast) 실패 시, 수직 투영(Projection) 대신 모든 선분과의 교차점을 찾는 방식으로 변경 if (!extendedLine) { - let closestBaseLine = null; + let closestIntersection = null; let minDistance = Infinity; - let projection = null; - baseLines.forEach(base => { - const p = getProjectionPoint(line.p2, base); - const d = Math.sqrt(Math.pow(line.p2.x - p.x, 2) + Math.pow(line.p2.y - p.y, 2)); - if (d < minDistance) { - minDistance = d; - closestBaseLine = base; - projection = p; + + // 모든 외벽선과 다른 내부선을 타겟으로 설정 + const allTargetLines = [ + ...baseLines.map(l => ({ p1: {x: l.x1, y: l.y1}, p2: {x: l.x2, y: l.y2} })), + ...skeletonLines.filter((_, i) => i !== index) + ]; + + allTargetLines.forEach(targetLine => { + // 무한 직선 간의 교차점을 찾음 + const intersection = getInfiniteLineIntersection(line.p2, line.p1, targetLine.p1, targetLine.p2); + + // 교차점이 존재하고, 타겟 '선분' 위에 있는지 확인 + if (intersection && isPointOnSegmentForExtension(intersection, targetLine.p1, targetLine.p2)) { + // 연장 방향이 올바른지 확인 (뒤로 가지 않도록) + const lineVec = { x: line.p2.x - line.p1.x, y: line.p2.y - line.p1.y }; + const intersectVec = { x: intersection.x - line.p2.x, y: intersection.y - line.p2.y }; + const dotProduct = lineVec.x * intersectVec.x + lineVec.y * intersectVec.y; + + if (dotProduct >= -1e-6) { // 교차점이 p2 기준으로 '앞'에 있을 경우 + const dist = Math.sqrt(Math.pow(line.p2.x - intersection.x, 2) + Math.pow(line.p2.y - intersection.y, 2)); + if (dist > 0.1 && dist < minDistance) { // 자기 자신이 아니고, 가장 가까운 교차점 갱신 + minDistance = dist; + closestIntersection = intersection; + } + } } }); - if(projection) extendedLine = { point: projection }; + + if (closestIntersection) { + extendedLine = { point: closestIntersection }; + } } } @@ -1603,6 +583,46 @@ const createPolygonsFromSkeletonLines = (skeletonLines, selectBaseLine) => { }; +/** + * 두 무한 직선의 교차점을 찾습니다. (선분X) + * @param {object} p1 - 직선1의 점1 + * @param {object} p2 - 직선1의 점2 + * @param {object} p3 - 직선2의 점1 + * @param {object} p4 - 직선2의 점2 + * @returns {object|null} 교차점 좌표 또는 null (평행/동일선) + */ +const getInfiniteLineIntersection = (p1, p2, p3, p4) => { + const x1 = p1.x, y1 = p1.y; + const x2 = p2.x, y2 = p2.y; + const x3 = p3.x, y3 = p3.y; + const x4 = p4.x, y4 = p4.y; + + const denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); + if (Math.abs(denom) < 1e-10) return null; // 평행 또는 동일선 + + const t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom; + + return { + x: x1 + t * (x2 - x1), + y: y1 + t * (y2 - y1) + }; +}; + +/** + * 점이 선분 위에 있는지 확인합니다. (연장 로직용) + * @param {object} point - 확인할 점 + * @param {object} segStart - 선분 시작점 + * @param {object} segEnd - 선분 끝점 + * @param {number} tolerance - 허용 오차 + * @returns {boolean} 선분 위 여부 + */ +const isPointOnSegmentForExtension = (point, segStart, segEnd, tolerance = 0.1) => { + const dist = Math.sqrt(Math.pow(segEnd.x - segStart.x, 2) + Math.pow(segEnd.y - segStart.y, 2)); + const dist1 = Math.sqrt(Math.pow(point.x - segStart.x, 2) + Math.pow(point.y - segStart.y, 2)); + const dist2 = Math.sqrt(Math.pow(point.x - segEnd.x, 2) + Math.pow(point.y - segEnd.y, 2)); + return Math.abs(dist - (dist1 + dist2)) < tolerance; +}; + /** * 스켈레톤 라인들 간의 모든 교차점을 찾습니다. * @param {Array} skeletonLines - 스켈레톤 라인 배열 (각 요소는 {p1: {x, y}, p2: {x, y}} 형태)