diff --git a/src/app/api/image/cad/route.js b/src/app/api/image/cad/route.js index a0e60a4c..e124408c 100644 --- a/src/app/api/image/cad/route.js +++ b/src/app/api/image/cad/route.js @@ -11,7 +11,7 @@ const s3 = new S3Client({ }) const uploadImage = async (file) => { - console.log('๐Ÿš€ ~ uploadImage ~ file:', file) + // console.log('๐Ÿš€ ~ uploadImage ~ file:', file) const Body = Buffer.from(await file.arrayBuffer()) const Key = `cads/${file.name}` const ContentType = 'image/png' @@ -49,7 +49,7 @@ export async function DELETE(req) { try { const searchParams = req.nextUrl.searchParams const Key = `cads/${searchParams.get('fileName')}` - console.log('๐Ÿš€ ~ DELETE ~ Key:', Key) + // console.log('๐Ÿš€ ~ DELETE ~ Key:', Key) if (!Key) { return NextResponse.json({ error: 'fileName parameter is required' }, { status: 400 }) diff --git a/src/app/api/image/map/route.js b/src/app/api/image/map/route.js index 0cc76c02..96df259b 100644 --- a/src/app/api/image/map/route.js +++ b/src/app/api/image/map/route.js @@ -57,7 +57,7 @@ export async function DELETE(req) { try { const searchParams = req.nextUrl.searchParams const Key = `maps/${searchParams.get('fileName')}` - console.log('๐Ÿš€ ~ DELETE ~ Key:', Key) + // console.log('๐Ÿš€ ~ DELETE ~ Key:', Key) if (!Key) { return NextResponse.json({ error: 'fileName parameter is required' }, { status: 400 }) diff --git a/src/app/api/image/upload/route.js b/src/app/api/image/upload/route.js index 4d875257..4d802e75 100644 --- a/src/app/api/image/upload/route.js +++ b/src/app/api/image/upload/route.js @@ -55,7 +55,7 @@ export async function DELETE(req) { } const Key = `upload/${fileName}` - console.log('๐Ÿš€ ~ DELETE ~ Key:', Key) + // console.log('๐Ÿš€ ~ DELETE ~ Key:', Key) await s3.send( new DeleteObjectCommand({ diff --git a/src/app/layout.js b/src/app/layout.js index 40386d49..59b1ddcc 100644 --- a/src/app/layout.js +++ b/src/app/layout.js @@ -10,6 +10,8 @@ import Header from '@/components/header/Header' import QModal from '@/components/common/modal/QModal' import PopupManager from '@/components/common/popupManager/PopupManager' import ErrorBoundary from '@/components/common/ErrorBoundary' +import PageTracker from '@/components/common/PageTracker' // [PAGE-TRACKER 2026-05-14] ๋ผ์šฐํŠธ ๋ณ€๊ฒฝ ์‹œ ์ฝ˜์†”/ํƒญํƒ€์ดํ‹€์— ๊ฒฝ๋กœ ํ‘œ์‹œ +import ModalTracker from '@/components/common/ModalTracker' // [MODAL-TRACKER 2026-05-14] ๋ชจ๋‹ฌ/ํŒ์—… openยทclose ์ถ”์  import './globals.css' import '../styles/style.scss' @@ -73,6 +75,8 @@ export default async function RootLayout({ children }) { + + {headerPathname === '/login' || headerPathname === '/join' ? ( {children} ) : ( diff --git a/src/components/common/ModalTracker.jsx b/src/components/common/ModalTracker.jsx new file mode 100644 index 00000000..4ea2957c --- /dev/null +++ b/src/components/common/ModalTracker.jsx @@ -0,0 +1,88 @@ +'use client' + +// [MODAL-TRACKER 2026-05-14] QModal / PopupManager / contextPopup ์˜ atom ์„ ๊ตฌ๋…ํ•ด +// ๋ชจ๋‹ฌยทํŒ์—…์ด ์—ด๋ฆฌ๊ณ  ๋‹ซํž ๋•Œ ์ฝ˜์†”์— ์ปดํฌ๋„ŒํŠธ๋ช…/id ๋ฅผ ์ถœ๋ ฅ. ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋ณ„ ์ˆ˜์ • ์—†์Œ. +import { useEffect, useRef } from 'react' +import { useRecoilValue } from 'recoil' +import { modalState, modalContent } from '@/store/modalAtom' +import { popupState, contextPopupState } from '@/store/popupAtom' +import { logger } from '@/util/logger' + +const TRACK_ENABLED = process.env.NEXT_PUBLIC_ENABLE_LOGGING === 'true' + +const BADGE_OPEN = 'background:#7b1fa2;color:#fff;padding:2px 8px;border-radius:3px;font-weight:bold' +const BADGE_CLOSE = 'background:#9e9e9e;color:#fff;padding:2px 8px;border-radius:3px' +const BADGE_POPUP = 'background:#00897b;color:#fff;padding:2px 8px;border-radius:3px;font-weight:bold' +const BADGE_CTX = 'background:#ef6c00;color:#fff;padding:2px 8px;border-radius:3px;font-weight:bold' + +function describeNode(node) { + if (!node) return 'null' + if (Array.isArray(node)) return `Array(${node.length})` + const t = node?.type + if (!t) return typeof node + return typeof t === 'string' ? t : t.displayName || t.name || 'Anonymous' +} + +export default function ModalTracker() { + const open = useRecoilValue(modalState) + const content = useRecoilValue(modalContent) + const popup = useRecoilValue(popupState) + const ctx = useRecoilValue(contextPopupState) + + const firstModal = useRef(true) + const prevPopupIds = useRef({ config: [], other: [] }) + const prevCtx = useRef(null) + + // QModal open/close + useEffect(() => { + if (!TRACK_ENABLED) return + if (firstModal.current) { + firstModal.current = false + return + } + if (open) { + logger.info(`%c[MODAL OPEN] ${describeNode(content)}`, BADGE_OPEN) + } else { + logger.info(`%c[MODAL CLOSE]`, BADGE_CLOSE) + } + }, [open]) + + // PopupManager push/pop (config + other ๋‘ ๋ฒ„ํ‚ท) + useEffect(() => { + if (!TRACK_ENABLED) return + const prev = prevPopupIds.current + for (const bucket of ['config', 'other']) { + const cur = popup?.[bucket] || [] + const curIds = cur.map((p) => p.id) + const prevIds = prev[bucket] || [] + curIds + .filter((id) => !prevIds.includes(id)) + .forEach((id) => { + const item = cur.find((p) => p.id === id) + logger.info(`%c[POPUP+ ${bucket}] ${describeNode(item?.component)} (id=${id})`, BADGE_POPUP) + }) + prevIds + .filter((id) => !curIds.includes(id)) + .forEach((id) => { + logger.info(`%c[POPUP- ${bucket}] (id=${id})`, BADGE_CLOSE) + }) + } + prevPopupIds.current = { + config: (popup?.config || []).map((p) => p.id), + other: (popup?.other || []).map((p) => p.id), + } + }, [popup]) + + // contextPopup (์šฐํด๋ฆญ ๋ฉ”๋‰ด ๋“ฑ) + useEffect(() => { + if (!TRACK_ENABLED) return + if (ctx && !prevCtx.current) { + logger.info(`%c[CTX POPUP+] ${describeNode(ctx)}`, BADGE_CTX) + } else if (!ctx && prevCtx.current) { + logger.info(`%c[CTX POPUP-]`, BADGE_CLOSE) + } + prevCtx.current = ctx + }, [ctx]) + + return null +} diff --git a/src/components/common/PageTracker.jsx b/src/components/common/PageTracker.jsx new file mode 100644 index 00000000..19eb99d9 --- /dev/null +++ b/src/components/common/PageTracker.jsx @@ -0,0 +1,28 @@ +'use client' + +// [PAGE-TRACKER 2026-05-14] ๋ผ์šฐํŠธ ๋ณ€๊ฒฝ๋งˆ๋‹ค ์ฝ˜์†” + document.title ์— ํ˜„์žฌ ๊ฒฝ๋กœ ํ‘œ์‹œ. +// production ์—์„œ๋Š” NEXT_PUBLIC_ENABLE_LOGGING=false ๋ผ logger.info ๊ฐ€ ๋ฌด์Œ. +import { useEffect } from 'react' +import { usePathname } from 'next/navigation' +import { logger } from '@/util/logger' + +const TRACK_ENABLED = process.env.NEXT_PUBLIC_ENABLE_LOGGING === 'true' + +export default function PageTracker() { + const pathname = usePathname() + + useEffect(() => { + if (!TRACK_ENABLED || !pathname) return + + logger.info( + `%c[PAGE] ${pathname}`, + 'background:#1e88e5;color:#fff;padding:2px 8px;border-radius:3px;font-weight:bold', + ) + + if (typeof document !== 'undefined') { + document.title = `${pathname} ยท HANASYS DESIGN` + } + }, [pathname]) + + return null +} diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 29868836..a0fcb57f 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -2,7 +2,7 @@ import { fabric } from 'fabric' import { v4 as uuidv4 } from 'uuid' import { QLine } from '@/components/fabric/QLine' import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util' -import { calculateAngle, drawGableRoof, drawRoofByAttribute, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils' +import { calculateAngle, drawGableRoof, drawRoofByAttribute, drawShedRoof, snapNearAxisEdges, toGeoJSON } from '@/util/qpolygon-utils' import * as turf from '@turf/turf' import { LINE_TYPE, POLYGON_TYPE } from '@/common/common' import Big from 'big.js' @@ -120,6 +120,20 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { this.adjustRoofLines = [] // this.colorLines = [] + // [1956-RACK-TILT 2026-05-13] ๋นจ๊ฐ• ์ ์„  ํด๋ฆฌ๊ณค๊ตฐ(๊ฐ€๋Œ€์„ /๋ชจ๋“ˆ์„ค์น˜๋ฉด) FP drift ๋ณด์ •. + // - trestle, dormerTrestle: createRoofRack ์ถœ๋ ฅ + // - trestlePolygon: useMode.handleOuterlinesTest ์ถœ๋ ฅ + // - moduleSetupSurface: useModuleBasicSetting (eavesMargin/ridgeMargin/kerabaMargin ์ ์šฉ) + // ๊ฐ€๋กœ/์„ธ๋กœ edge ์˜ FP drift ๊ฐ€ ์ €์žฅ๋œ JSON ์— ๋ฐ•ํ˜€์žˆ๋Š” ๊ฒฝ์šฐ ๋ณต์› ์‹œ ๋ผ์ธ์ด ๊ธฐ์šธ์–ด ๋ณด์ด๋Š” ๋ฌธ์ œ. + if ( + options.name === 'trestle' || + options.name === 'dormerTrestle' || + options.name === 'trestlePolygon' || + options.name === 'moduleSetupSurface' + ) { + points = snapNearAxisEdges(points, 2) + } + // ์†Œ์ˆ˜์  ์ „๋ถ€ ์ œ๊ฑฐ points.forEach((point) => { point.x = Number(point.x.toFixed(this.toFixed)) diff --git a/src/components/main/ChangePasswordPop.jsx b/src/components/main/ChangePasswordPop.jsx index 58c22d69..3367506b 100644 --- a/src/components/main/ChangePasswordPop.jsx +++ b/src/components/main/ChangePasswordPop.jsx @@ -197,7 +197,7 @@ export default function ChangePasswordPop(props) {
{getMessage('main.popup.login.guide1')} - {getMessage('main.popup.login.guide2')} + {/*{getMessage('main.popup.login.guide2')}*/}
diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 0d6f12a8..68129345 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -23,6 +23,7 @@ import { QcastContext } from '@/app/QcastProvider' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useSwal } from '@/hooks/useSwal' import { sanitizeIntegerInputEvent } from '@/util/input-utils' +import { logger } from '@/util/logger' import { CalculatorInput } from '@/components/common/input/CalcInput' import Image from 'next/image' @@ -1008,81 +1009,115 @@ export default function StuffDetail() { //ํŒ์—…์—์„œ ๋„˜์–ด์˜จ ์„ค๊ณ„์˜๋ขฐ ์ •๋ณด๋กœ ๋ฐ”๊พธ๊ธฐ const setPlanReqInfo = (info) => { - form.setValue('planReqNo', info.planReqNo) - - form.setValue('objectStatusId', info.building) - setSelectObjectStatusId(info.building) - - form.setValue('objectName', info.title) - form.setValue('zipNo', info.zipNo) - form.setValue('address', info.address2) - - prefCodeList.map((row) => { - if (row.prefName == info.address1) { - setPrefValue(row.prefId) - form.setValue('prefId', row.prefId) - form.setValue('prefName', info.address1) - } + // [PLANREQ-DEBUG 2026-05-15] import ํ๋ฆ„ ์ง„๋‹จ + logger.debug('[PLANREQ-DEBUG] setPlanReqInfo entry', { + session: { storeId: session?.storeId, storeLvl: session?.storeLvl }, + info: { planReqNo: info?.planReqNo, saleStoreId: info?.saleStoreId, saleStoreLevel: info?.saleStoreLevel, saleStoreName: info?.saleStoreName }, + saleStoreList: saleStoreList.map((s) => s.saleStoreId), + otherSaleStoreList: otherSaleStoreList.map((o) => o.saleStoreId), }) - //์„ค๊ณ„์˜๋ขฐ ํŒ์—…์—์„  WL_์•ˆ๋ถ™์–ด์„œ ์˜ด - if (info.windSpeed !== '') { - form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`) - } else { - form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`) + // [PLANREQ-MATCH 2026-05-15] all-or-nothing: saleStore ๋งคํ•‘ ๊ฒ€์ฆ ํ›„์—๋งŒ ๋ชจ๋“  ํ•„๋“œ ์ ์šฉ + const applyFields = () => { + form.setValue('planReqNo', info.planReqNo) + form.setValue('objectStatusId', info.building) + setSelectObjectStatusId(info.building) + form.setValue('objectName', info.title) + form.setValue('zipNo', info.zipNo) + form.setValue('address', info.address2) + prefCodeList.map((row) => { + if (row.prefName == info.address1) { + setPrefValue(row.prefId) + form.setValue('prefId', row.prefId) + form.setValue('prefName', info.address1) + } + }) + //์„ค๊ณ„์˜๋ขฐ ํŒ์—…์—์„  WL_์•ˆ๋ถ™์–ด์„œ ์˜ด + if (info.windSpeed !== '') { + form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`) + } else { + form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`) + } + form.setValue('verticalSnowCover', info.verticalSnowCover) + form.setValue('surfaceType', info.surfaceType) + if (info.surfaceType === 'โ…ก') { + form.setValue('saltAreaFlg', true) + } else { + form.setValue('saltAreaFlg', false) + } + const installHeight = info.installHeight ? info.installHeight.split('.')[0] : '' + form.setValue('installHeight', installHeight) + form.setValue('remarks', info.remarks) } - form.setValue('verticalSnowCover', info.verticalSnowCover) - form.setValue('surfaceType', info.surfaceType) - - if (info.surfaceType === 'โ…ก') { - form.setValue('saltAreaFlg', true) - } else { - form.setValue('saltAreaFlg', false) - } - - let installHeight = info.installHeight ? info.installHeight.split('.')[0] : '' - - form.setValue('installHeight', installHeight) - form.setValue('remarks', info.remarks) if (info.saleStoreLevel === '1') { + // 1์ฐจ ID: ๋งคํ•‘ ์‹คํŒจ ์‚ฌ์‹ค์ƒ ์—†์Œ (์กฐํšŒ ์ž์ฒด๊ฐ€ ๊ถŒํ•œ ํ•„ํ„ฐ) โ†’ ์ฆ‰์‹œ ์ ์šฉ + applyFields() setSelOptions(info.saleStoreId) form.setValue('saleStoreId', info.saleStoreId) form.setValue('saleStoreName', info.saleStoreName) form.setValue('saleStoreLevel', info.saleStoreLevel) - } else { + return + } + + // info.saleStoreLevel !== '1' (2์ฐจ ID) + if (session?.storeLvl === '2') { + const matched = otherSaleStoreList.some((o) => o.saleStoreId === info.saleStoreId) + logger.debug('[PLANREQ-DEBUG] 2์ฐจ user match check', { matched, target: info.saleStoreId }) + if (!matched) { + // ๋งคํ•‘ ์‹คํŒจ โ€” ์•„๋ฌด๊ฒƒ๋„ ์ ์šฉ ์•ˆ ํ•จ + ์‚ฌ์šฉ์ž์—๊ฒŒ ์•Œ๋ฆผ + swalFire({ + title: getMessage('stuff.detail.planReq.message.notMatch'), + type: 'alert', + icon: 'warning', + }) + return + } + applyFields() setOtherSelOptions(info.saleStoreId) form.setValue('otherSaleStoreId', info.saleStoreId) form.setValue('otherSaleStoreName', info.saleStoreName) form.setValue('otherSaleStoreLevel', info.saleStoreLevel) - get({ url: `/api/object/saleStore/${info.saleStoreId}/firstAgent` }).then((res) => { - if (res?.firstAgentId) { - const firstAgent = { saleStoreId: res.firstAgentId, saleStoreName: res.firstAgentName } - setSaleStoreList((prev) => { - const exists = prev.some((s) => s.saleStoreId === res.firstAgentId) - return exists ? prev : [...prev, firstAgent] - }) - setShowSaleStoreList((prev) => { - const exists = prev.some((s) => s.saleStoreId === res.firstAgentId) - return exists ? prev : [...prev, firstAgent] - }) - setSelOptions(res.firstAgentId) - form.setValue('saleStoreId', res.firstAgentId) - form.setValue('saleStoreName', res.firstAgentName) - form.setValue('saleStoreLevel', '1') - } else { - setSelOptions('') - form.setValue('saleStoreId', '') - form.setValue('saleStoreName', '') - form.setValue('saleStoreLevel', '') - } - }).catch(() => { - setSelOptions('') - form.setValue('saleStoreId', '') - form.setValue('saleStoreName', '') - form.setValue('saleStoreLevel', '') - }) + return } + + // T01 / 1์ฐจ user + 2์ฐจ ID: firstAgent ๊ฒ€์ฆ ํ›„์—๋งŒ ์ ์šฉ (์‹คํŒจ ์‹œ ๋ฌด๋ฐ˜์˜) + get({ url: `/api/object/saleStore/${info.saleStoreId}/firstAgent` }).then((res) => { + logger.debug('[PLANREQ-DEBUG] firstAgent result', { firstAgentId: res?.firstAgentId }) + if (!res?.firstAgentId) { + swalFire({ + title: getMessage('stuff.detail.planReq.message.notMatch'), + type: 'alert', + icon: 'warning', + }) + return + } + applyFields() + setOtherSelOptions(info.saleStoreId) + form.setValue('otherSaleStoreId', info.saleStoreId) + form.setValue('otherSaleStoreName', info.saleStoreName) + form.setValue('otherSaleStoreLevel', info.saleStoreLevel) + const firstAgent = { saleStoreId: res.firstAgentId, saleStoreName: res.firstAgentName } + setSaleStoreList((prev) => { + const exists = prev.some((s) => s.saleStoreId === res.firstAgentId) + return exists ? prev : [...prev, firstAgent] + }) + setShowSaleStoreList((prev) => { + const exists = prev.some((s) => s.saleStoreId === res.firstAgentId) + return exists ? prev : [...prev, firstAgent] + }) + setSelOptions(res.firstAgentId) + form.setValue('saleStoreId', res.firstAgentId) + form.setValue('saleStoreName', res.firstAgentName) + form.setValue('saleStoreLevel', '1') + }).catch(() => { + // ๋งคํ•‘ ์‹คํŒจ โ€” ์•„๋ฌด๊ฒƒ๋„ ์ ์šฉ ์•ˆ ํ•จ + ์‚ฌ์šฉ์ž์—๊ฒŒ ์•Œ๋ฆผ + swalFire({ + title: getMessage('stuff.detail.planReq.message.notMatch'), + type: 'alert', + icon: 'warning', + }) + }) } //ํ’์†์„ ํƒ ํŒ์—…์—์„œ ๋„˜์–ด์˜จ ๋ฐ”๋žŒ์ •๋ณด @@ -2051,13 +2086,12 @@ export default function StuffDetail() { getOptionLabel={(x) => x.saleStoreName} getOptionValue={(x) => x.saleStoreId} isDisabled={ + // [PLANREQ-MATCH 2026-05-15] 2์ฐจ user ๋Š” ํ•˜์œ„ store ์„ ํƒ์ด ๊ฐ€๋Šฅํ•ด์•ผ ํ•˜๋ฏ€๋กœ ํ•ญ์ƒ enable session?.storeLvl === '1' ? otherSaleStoreList.length > 0 ? false : true - : otherSaleStoreList.length === 1 - ? true - : false + : false } isClearable={true} value={otherSaleStoreList.filter(function (option) { @@ -2650,15 +2684,14 @@ export default function StuffDetail() { getOptionLabel={(x) => x.saleStoreName} getOptionValue={(x) => x.saleStoreId} isDisabled={ + // [PLANREQ-MATCH 2026-05-15] 2์ฐจ user ๋Š” ํ•˜์œ„ store ์„ ํƒ์ด ๊ฐ€๋Šฅํ•ด์•ผ ํ•˜๋ฏ€๋กœ ํ•ญ์ƒ enable (tempFlg='0' ์ž ๊ธˆ๋งŒ ์œ ์ง€) managementState?.tempFlg === '0' ? true : session?.storeLvl === '1' ? otherSaleStoreList.length > 0 ? false : true - : otherSaleStoreList.length === 1 - ? true - : false + : false } isClearable={managementState?.tempFlg === '0' ? false : true} value={otherSaleStoreList.filter(function (option) { diff --git a/src/config/config.export.js b/src/config/config.export.js index 620bd65c..142d4026 100644 --- a/src/config/config.export.js +++ b/src/config/config.export.js @@ -5,7 +5,7 @@ import configProduction from './config.production' // ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” ์ด ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ config ๊ฐ’์„ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค. const Config = () => { - console.log('๐Ÿš€ ~ Config ~ process.env.NEXT_PUBLIC_RUN_MODE:', process.env.NEXT_PUBLIC_RUN_MODE) + // console.log('๐Ÿš€ ~ Config ~ process.env.NEXT_PUBLIC_RUN_MODE:', process.env.NEXT_PUBLIC_RUN_MODE) switch (process.env.NEXT_PUBLIC_RUN_MODE) { case 'local': return configLocal diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index e99c122b..87c6ae06 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -36,6 +36,7 @@ import { calcLineActualSize2 } from '@/util/qpolygon-utils' import { notifyLowPitchRestrictionForRoofs, isLowPitchRestricted, LOW_PITCH_RESTRICTED_ROOF_IDS } from '@/util/roof-pitch-warning' // [LOW-PITCH-DIAG 2026-05-06 TEMP] ์ง„๋‹จ์šฉ โ€” ๊ฒ€์ฆ ํ›„ import + ํ˜ธ์ถœ ํ•จ๊ป˜ ์ œ๊ฑฐ import { debugCapture } from '@/util/debugCapture' +import { logger } from '@/util/logger' export function useRoofAllocationSetting(id) { const canvas = useRecoilValue(canvasState) @@ -217,7 +218,7 @@ export function useRoofAllocationSetting(id) { })) setCurrentRoofList(normalizedRoofs) } catch (error) { - console.error('Data fetching error:', error) + logger.error('Data fetching error:', error) } } @@ -521,7 +522,7 @@ export function useRoofAllocationSetting(id) { const extLines = roofBase.lines.filter((l) => l.lineName === 'extensionLine') if (extLines.length === 0) { - console.log(`[INTEGRATE] roofBase.id=${roofBase.id} extensionLine ์—†์Œ โ†’ skip`) + logger.log(`[INTEGRATE] roofBase.id=${roofBase.id} extensionLine ์—†์Œ โ†’ skip`) return } @@ -563,7 +564,7 @@ export function useRoofAllocationSetting(id) { }) if (!sk) { - console.log( + logger.log( `[INTEGRATE] ext ์ง์—†์Œ ` + `(${extP1.x.toFixed(1)},${extP1.y.toFixed(1)})โ†’(${extP2.x.toFixed(1)},${extP2.y.toFixed(1)})` ) @@ -571,8 +572,9 @@ export function useRoofAllocationSetting(id) { } // [2026-04-30] sk ๊ฐ€ ์ด๋ฏธ SK ๋นŒ๋“œ ๋‹จ๊ณ„์—์„œ ์—ฐ์žฅ๋œ ๊ฒฝ์šฐ โ†’ merge ์Šคํ‚ต. - if (sk.__extended) { - console.log( + // [save/load ๋ณด์กด 2026-05-13] runtime __extended ๋Š” ์ €์žฅ ์‹œ ์‚ฌ๋ผ์ง€๋ฏ€๋กœ attributes.extended ๋„ ํ•จ๊ป˜ ๊ฒ€์‚ฌ. + if (sk.__extended || sk.attributes?.extended) { + logger.log( `[INTEGRATE] sk ์ด๋ฏธ ์—ฐ์žฅ๋จ(id=${sk.id}) โ†’ merge ์Šคํ‚ต, ext ๋Š” lines ์—์„œ๋งŒ ์ œ๊ฑฐ(canvas ์œ ์ง€)` ) extLinesOnly.push(ext) @@ -617,7 +619,7 @@ export function useRoofAllocationSetting(id) { }) mergedLine.length = totalLen - console.log( + logger.log( `[INTEGRATE] merge ` + `ext=(${extP1.x.toFixed(1)},${extP1.y.toFixed(1)})โ†’(${extP2.x.toFixed(1)},${extP2.y.toFixed(1)}) len=${extLen.toFixed(1)} ` + `sk[${sk.lineName || sk.name}]=(${skP1.x.toFixed(1)},${skP1.y.toFixed(1)})โ†’(${skP2.x.toFixed(1)},${skP2.y.toFixed(1)}) len=${skLen.toFixed(1)} ` + @@ -630,7 +632,7 @@ export function useRoofAllocationSetting(id) { }) if (merged.length === 0 && extLinesOnly.length === 0) { - console.log(`[INTEGRATE] ํ†ตํ•ฉ ๋Œ€์ƒ ์—†์Œ ext=${extLines.length}`) + logger.log(`[INTEGRATE] ํ†ตํ•ฉ ๋Œ€์ƒ ์—†์Œ ext=${extLines.length}`) return } @@ -646,7 +648,7 @@ export function useRoofAllocationSetting(id) { removedExt.forEach((l) => canvas.remove(l)) removedSk.forEach((l) => canvas.remove(l)) - console.log( + logger.log( `[INTEGRATE] ์™„๋ฃŒ roofBase.id=${roofBase.id} ` + `ext์ œ๊ฑฐ=${removedExt.length} sk์ œ๊ฑฐ=${removedSk.length} merged=${merged.length} ` + `extKeptInCanvas=${extLinesOnly.length} ` + @@ -660,7 +662,7 @@ export function useRoofAllocationSetting(id) { const apply = () => { const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.roofMaterial) const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL) - console.log(`[ALLOC] apply() ์ง„์ž…. roofBases=${roofBases.length}`) + logger.log(`[ALLOC] apply() ์ง„์ž…. roofBases=${roofBases.length}`) roofBases.forEach((roofBase) => { try { // ์ง€๋ถ• ํ• ๋‹น ๋กœ์ง์— extensionLine ์ถ”๊ฐ€ @@ -668,31 +670,9 @@ export function useRoofAllocationSetting(id) { (obj.lineName === 'eaveHelpLine' || obj.lineName === 'extensionLine') && obj.roofId === roofBase.id ) - // [ALLOC-DEBUG] canvas ํ”ฝ์—… ์Šค๋ƒ…์ƒท (์‚ญ์ œ ์ง์ „ ์ƒํƒœ) - const snapByLineName = {} - const snapByName = {} - roofEaveHelpLines.forEach((o) => { - snapByLineName[o.lineName] = (snapByLineName[o.lineName] || 0) + 1 - snapByName[o.name || '?'] = (snapByName[o.name || '?'] || 0) + 1 - }) - console.log( - `[ALLOC-DEBUG] roofBase.id=${roofBase.id} ` + - `roofBase.lines=${roofBase.lines?.length || 0} ` + - `roofBase.innerLines=${roofBase.innerLines?.length || 0} ` + - `canvasํ”ฝ์—…=${roofEaveHelpLines.length} ` + - `byLineName=${JSON.stringify(snapByLineName)} ` + - `byName=${JSON.stringify(snapByName)}` - ) - roofEaveHelpLines.forEach((o, i) => { - console.log( - ` [ALLOC-DEBUG] picked[${i}] name=${o.name} lineName=${o.lineName} ` + - `(${o.x1?.toFixed(1)},${o.y1?.toFixed(1)})โ†’(${o.x2?.toFixed(1)},${o.y2?.toFixed(1)}) ` + - `len=${Math.hypot((o.x2 || 0) - (o.x1 || 0), (o.y2 || 0) - (o.y1 || 0)).toFixed(1)}` - ) - }) - // console.log('roofBase.id:', roofBase.id) - // console.log('roofEaveHelpLines:', roofEaveHelpLines) - // console.log('extensionLines found:', roofEaveHelpLines.filter(l => l.lineName === 'extensionLine')) + // logger.log('roofBase.id:', roofBase.id) + // logger.log('roofEaveHelpLines:', roofEaveHelpLines) + // logger.log('extensionLines found:', roofEaveHelpLines.filter(l => l.lineName === 'extensionLine')) if (roofEaveHelpLines.length > 0) { if (roofBase.lines) { // Filter out any eaveHelpLines that are already in lines to avoid duplicates @@ -703,8 +683,8 @@ export function useRoofAllocationSetting(id) { // extensionLine๊ณผ ์ผ๋ฐ˜ eaveHelpLine ๋ถ„๋ฆฌ const extensionLines = newEaveLines.filter(line => line.lineName === 'extensionLine') const normalEaveLines = newEaveLines.filter(line => line.lineName === 'eaveHelpLine') - // console.log('extensionLines count:', extensionLines.length) - // console.log('normalEaveLines count:', normalEaveLines.length) + // logger.log('extensionLines count:', extensionLines.length) + // logger.log('normalEaveLines count:', normalEaveLines.length) // ์ผ๋ฐ˜ eaveHelpLine๋งŒ Overlap ํŒ๋‹จ์— ์‚ฌ์šฉ const linesToKeep = roofBase.lines.filter(roofLine => { @@ -747,7 +727,7 @@ export function useRoofAllocationSetting(id) { isPointInside(eX2, eY2, rX1, rY1, rX2, rY2); if (isOverlapping) { - console.log('Removing overlapping line:', roofLine); + logger.log('Removing overlapping line:', roofLine); return true; // ํฌ๊ฐœ์ง€๋Š” ๊ฒฝ์šฐ์—๋งŒ ์‚ญ์ œ } } @@ -758,20 +738,6 @@ export function useRoofAllocationSetting(id) { }); // Combine remaining lines with newEaveLines roofBase.lines = [...linesToKeep, ...normalEaveLines, ...extensionLines]; - // [ALLOC-DEBUG] ๋ณ‘ํ•ฉ ๊ฒฐ๊ณผ breakdown - console.log( - `[ALLOC-DEBUG] ๋ณ‘ํ•ฉ์™„๋ฃŒ roofBase.id=${roofBase.id} ` + - `linesToKeep=${linesToKeep.length} ` + - `normalEave=${normalEaveLines.length} ` + - `extension=${extensionLines.length} ` + - `total=${roofBase.lines.length}` - ) - roofBase.lines.forEach((ln, i) => { - console.log( - ` [ALLOC-DEBUG] merged[${i}] name=${ln.name || '?'} lineName=${ln.lineName || '?'} ` + - `(${ln.x1?.toFixed(1)},${ln.y1?.toFixed(1)})โ†’(${ln.x2?.toFixed(1)},${ln.y2?.toFixed(1)})` - ) - }) } else { roofBase.lines = [...roofEaveHelpLines] } @@ -811,22 +777,13 @@ export function useRoofAllocationSetting(id) { // extensionLine + ๋™์ผ์ง์„  SK 1:1 ํ†ตํ•ฉ (๋Œ€๊ฐ์„  ๋‹จ์ผ๊ธธ์ด/๊ฐ๋„ ๋ฉด์  ์‚ฐ์ถœ) integrateExtensionLines(roofBase) - // [ALLOC-DEBUG] split ์ง์ „ ์ตœ์ข… ์ž…๋ ฅ - console.log( - `[ALLOC-DEBUG] split ์ง์ „ roofBase.id=${roofBase.id} ` + - `separatePolygon=${roofBase.separatePolygon?.length || 0} ` + - `lines=${roofBase.lines?.length || 0} ` + - `innerLines=${roofBase.innerLines?.length || 0} ` + - `points=${roofBase.points?.length || 0} ` + - `โ†’ ${roofBase.separatePolygon?.length > 0 ? 'splitPolygonWithSeparate' : 'splitPolygonWithLines'}` - ) if (roofBase.separatePolygon.length > 0) { splitPolygonWithSeparate(roofBase.separatePolygon) } else { splitPolygonWithLines(roofBase) } } catch (e) { - console.log(e) + logger.log(e) canvas.discardActiveObject() return } diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index c1b5524c..af134ece 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -33,7 +33,7 @@ import { import { QLine } from '@/components/fabric/QLine' import { fabric } from 'fabric' import { QPolygon } from '@/components/fabric/QPolygon' -import offsetPolygon, { calculateAngle } from '@/util/qpolygon-utils' +import offsetPolygon, { calculateAngle, snapNearAxisEdges } from '@/util/qpolygon-utils' import { isObjectNotEmpty } from '@/util/common-utils' import * as turf from '@turf/turf' import { INPUT_TYPE, LINE_TYPE, Mode, POLYGON_TYPE } from '@/common/common' @@ -1505,7 +1505,16 @@ export function useMode() { offsetPoints.push(offsetPoint) } - return makePolygon(offsetPoints, false) + // [1956-RACK-TILT 2026-05-13] trestlePolygon(ใƒขใ‚ธใƒฅใƒผใƒซ้…็ฝฎ้ ˜ๅŸŸ ๋นจ๊ฐ•์ ์„ ) FP drift ๋ณด์ •. + // ์ž…๋ ฅ polygon.lines ์˜ ๋ฏธ์„ธ drift ๊ฐ€ ํ‰๊ท ๋ฒ•์„  offset ์œผ๋กœ ์ „ํŒŒ๋˜์–ด ๊ฐ€๋กœ/์„ธ๋กœ ๋ผ์ธ์ด + // ๊ธฐ์šธ์–ด ๋ณด์ด๋Š” ๋ฌธ์ œ ์ฐจ๋‹จ. tolerance 0.5mm ๋ณด๋‹ค ์ž‘์€ dy/dx ๋งŒ ์ถ•์œผ๋กœ ์Šค๋ƒ…. + const _snapped = snapNearAxisEdges( + offsetPoints.map((p) => ({ x: p.x1, y: p.y1 })), + 2, + ) + const snappedOffsetPoints = _snapped.map((p) => ({ x1: p.x, y1: p.y })) + + return makePolygon(snappedOffsetPoints, false) } /** @@ -1730,10 +1739,25 @@ export function useMode() { // edge[i] ๊ฐ€ tiny ์ด๋ฉด ์‹œ์ž‘ ์ •์  vertex[i] ๋ฅผ ์ œ๊ฑฐํ•ด ์•ž์ชฝ edge ์™€ ๋ณ‘ํ•ฉํ•œ๋‹ค. // ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‚จ๋Š” cleaned edge ์˜ line ๋งคํ•‘(cleanedLines[k]=lines[kept[k]]) ์ด // edge[i+1] (์‹ค์ œ ๊ธด edge) ์˜ line ์„ ์“ฐ๊ฒŒ ๋˜๋ฏ€๋กœ offset ๊ณ„์‚ฐ์ด ์•ˆ์ •ํ•ด์ง„๋‹ค. + // [tiny edge axis guard 2026-05-13] ๋‹จ, ์ œ๊ฑฐ ํ›„ ์ƒˆ๋กœ ์ƒ๊ธฐ๋Š” ์—ฃ์ง€๊ฐ€ axis-aligned ์ธ ๊ฒฝ์šฐ์—๋งŒ ์•ˆ์ „. + // ์ง๊ฐ ๋…ธ์น˜(์˜ˆ: H 91mm + V 24.5mm ์ฝ”๋„ˆ) ์˜ 24.5mm edge ๋ฅผ ๋‹จ์ˆœ ์ œ๊ฑฐํ•˜๋ฉด v[i-1]โ†’v[i+1] ๊ฐ€ + // 15ยฐ ๋Œ€๊ฐ์„ ์ด ๋˜์–ด inset+clipOffsetToOriginal ํ›„ mm ๋‹จ์œ„ zigzag ํ† ๋ง‰์œผ๋กœ ๋ณด์ž„ (RACK-TILT). + // ์›๋ณธ ๋ฐ์ดํ„ฐ๊ฐ€ wallBaseLine ์˜ axis-aligned ์ง๊ฐ ๋…ธ์น˜๋ฅผ ๋ณด์กดํ•ด์•ผ ํ•˜๋ฏ€๋กœ, ๋Œ€๊ฐ์„  ๊ฒฐ๊ณผ๊ฐ€ ๋˜๋ฉด skip. const TINY_EDGE_THRESHOLD = 30 + const AXIS_TOL = 0.5 for (let i = 0; i < n; i++) { if (edges[i].length < TINY_EDGE_THRESHOLD) { - removeIndices.add(i) + const prev = (i + n - 1) % n + const nxt = (i + 1) % n + const vPrev = vertices[prev] + const vNext = vertices[nxt] + // ์ƒˆ ์—ฃ์ง€ v[prev]โ†’v[nxt] ๊ฐ€ ์ˆ˜ํ‰ ๋˜๋Š” ์ˆ˜์ง์ธ ๊ฒฝ์šฐ๋งŒ ๋จธ์ง€ (drift artifact ํก์ˆ˜). + // ๊ทธ ์™ธ(์ง๊ฐ ๋…ธ์น˜ ์‚ฌ์ด์˜ short step) ๋Š” ์ง„์งœ geometry ์ด๋ฏ€๋กœ ๋ณด์กด. + const isAxisAligned = + Math.abs(vPrev.y - vNext.y) < AXIS_TOL || Math.abs(vPrev.x - vNext.x) < AXIS_TOL + if (isAxisAligned) { + removeIndices.add(i) + } } } @@ -5100,6 +5124,7 @@ export function useMode() { roofs.forEach((roof, index) => { // const offsetPolygonPoint = offsetPolygon(roof.points(), -20) //์ด๋™๋˜์„œ ์ฐ์„๋ผ๊ณ  ๋ฐ”๊ฟˆ + // [1956-RACK-TILT 2026-05-13] trestle name ์œผ๋กœ QPolygon.initialize ๊ฐ€ ์ถ•-์Šค๋ƒ… ์ฒ˜๋ฆฌ const offsetPolygonPoint = offsetPolygon(roof.getCurrentPoints(), -20) const trestlePoly = new QPolygon(offsetPolygonPoint, { diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 8b2f7c37..595d63cd 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -3,7 +3,7 @@ import { useRecoilValue } from 'recoil' import { fabric } from 'fabric' import { calculateIntersection, findAndRemoveClosestPoint, getDegreeByChon, isPointOnLine } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' -import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils' +import { calcLinePlaneSize, isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils' import { basicSettingState, flowDisplaySelector } from '@/store/settingAtom' import { fontSelector } from '@/store/fontAtom' import { QLine } from '@/components/fabric/QLine' @@ -1028,7 +1028,10 @@ export const usePolygon = () => { const { intersections, startPoint, endPoint } = line // ์›๋ณธ ๋ผ์ธ์˜ ๊ธฐํ•˜ํ•™์  ๊ธธ์ด (๋น„์œจ ๊ณ„์‚ฐ์šฉ) - const originalGeomLength = Math.round(Math.hypot(line.x2 - line.x1, line.y2 - line.y1)) * 10 + // [ROUND-PRECISION 2026-05-14] Math.round(hypot)*10 โ†’ calcLinePlaneSize (Big.js, ร—10 ํ›„ round). + // ๊ธฐ์กด: 45.6 โ†’ Math.round(45.6)*10 = 460. ไผใ›ๅ›ณๅ…ฅๅŠ› ์˜ Big.js ๊ฒฝ๋กœ๋Š” 456. + // ๋ฐฐ์น˜๋ฉด split ๊ฒฐ๊ณผ์™€ ไผใ›ๅ›ณๅ…ฅๅŠ› SK ๋นŒ๋”์˜ 0.5mm boundary ๊ฐ€ ๊ฐˆ๋ฆฌ๋Š” ๊ทผ๋ณธ ์›์ธ. + const originalGeomLength = calcLinePlaneSize({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }) if (intersections.length === 1) { const newLinePoint1 = [line.x1, line.y1, intersections[0].x, intersections[0].y] @@ -1050,8 +1053,9 @@ export const usePolygon = () => { }) // ๋ถ„ํ• ๋œ ๊ฐ ์„ธ๊ทธ๋จผํŠธ์˜ ๊ธฐํ•˜ํ•™์  ๊ธธ์ด - const length1 = Math.round(Math.hypot(newLine1.x1 - newLine1.x2, newLine1.y1 - newLine1.y2)) * 10 - const length2 = Math.round(Math.hypot(newLine2.x1 - newLine2.x2, newLine2.y1 - newLine2.y2)) * 10 + // [ROUND-PRECISION 2026-05-14] calcLinePlaneSize ์‚ฌ์šฉ โ€” ์œ„ originalGeomLength ์™€ ๋™์ผ ์ด์œ . + const length1 = calcLinePlaneSize({ x1: newLine1.x1, y1: newLine1.y1, x2: newLine1.x2, y2: newLine1.y2 }) + const length2 = calcLinePlaneSize({ x1: newLine2.x1, y1: newLine2.y1, x2: newLine2.x2, y2: newLine2.y2 }) // ๋ถ„ํ•  ์‹œ ์ƒˆ sub-segment ์˜ planeSize/actualSize ๋Š” ์ƒˆ ๊ธฐํ•˜ํ•™์œผ๋กœ ์ง์ ‘ ๊ณ„์‚ฐ. // ๋ถ€๋ชจ ๋น„์œจ์„ ์“ฐ๋ฉด ๋ถ€๋ชจ ์ขŒํ‘œ drift ๊ฐ€ ๊ทธ๋Œ€๋กœ ์ „ํŒŒ๋˜์–ด ์‚ฌ์šฉ์ž ๊ธฐ๋Œ€ round ๊ฐ’๊ณผ ์–ด๊ธ‹๋‚จ. @@ -1131,7 +1135,8 @@ export const usePolygon = () => { name: 'newLine', }) - const calcLength = Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10 + // [ROUND-PRECISION 2026-05-14] calcLinePlaneSize ์‚ฌ์šฉ โ€” Big.js ร—10 ํ›„ round. + const calcLength = calcLinePlaneSize({ x1: newLine.x1, y1: newLine.y1, x2: newLine.x2, y2: newLine.y2 }) let segPlaneSize, segActualSize if (line.attributes.planeSize && originalGeomLength > 0) { @@ -1164,7 +1169,8 @@ export const usePolygon = () => { attributes: line.attributes, name: 'newLine', }) - const lastCalcLength = Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10 + // [ROUND-PRECISION 2026-05-14] calcLinePlaneSize ์‚ฌ์šฉ โ€” Big.js ร—10 ํ›„ round. + const lastCalcLength = calcLinePlaneSize({ x1: newLine.x1, y1: newLine.y1, x2: newLine.x2, y2: newLine.y2 }) let lastPlaneSize, lastActualSize if (line.attributes.planeSize && originalGeomLength > 0) { diff --git a/src/lib/cadAction.js b/src/lib/cadAction.js index b2feafff..aa15d9dd 100644 --- a/src/lib/cadAction.js +++ b/src/lib/cadAction.js @@ -10,7 +10,7 @@ import fs from 'fs/promises' const imageSavePath = 'public/cadImages' const convertDwgToPng = async (fileName, data) => { - console.log('fileName', fileName) + // console.log('fileName', fileName) try { await fs.readdir(imageSavePath) } catch { diff --git a/src/locales/ja.json b/src/locales/ja.json index 443b1d8f..2bc8f273 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -782,6 +782,7 @@ "stuff.detail.tempSave.message2": "ๆ‹…ๅฝ“่€…ๅใฏๅ…จ่ง’20ๆ–‡ๅญ—๏ผˆๅŠ่ง’40ๆ–‡ๅญ—๏ผ‰ไปฅไธ‹ใงๅ…ฅๅŠ›ใ—ใฆใใ ใ•ใ„.", "stuff.detail.tempSave.message3": "ไบŒๆฌก่ฒฉๅฃฒๅบ—ใ‚’้ธๆŠžใ—ใฆใใ ใ•ใ„ใ€‚", "stuff.detail.confirm.message1": "่ฒฉๅฃฒๅบ—ๆƒ…ๅ ฑใ‚’ๅค‰ๆ›ดใ™ใ‚‹ใจใ€่จญ่จˆไพ้ ผๆ–‡ๆ›ธ็•ชๅทใŒๅ‰Š้™คใ•ใ‚Œใพใ™ใ€‚ๅค‰ๆ›ดใ—ใพใ™ใ‹๏ผŸ", + "stuff.detail.planReq.message.notMatch": "่จญ่จˆไพ้ ผใฎ่ฒฉๅฃฒๅบ—ๆƒ…ๅ ฑใŒไธ€่‡ดใ—ใชใ„ใŸใ‚ใ€ใ‚คใƒณใƒใƒผใƒˆใงใใพใ›ใ‚“ใ€‚", "stuff.detail.delete.message1": "ไป•ๆง˜ใŒ็ขบๅฎšใ—ใŸใ‚‚ใฎใฏๅ‰Š้™คใงใใพใ›ใ‚“ใ€‚", "stuff.detail.planList.title": "ใƒ—ใƒฉใƒณใƒชใ‚นใƒˆ", "stuff.detail.planList.cnt": "ๅ…จไฝ“", @@ -936,7 +937,7 @@ "main.popup.login.newPassword1": "ๆ–ฐใ—ใ„ใƒ‘ใ‚นใƒฏใƒผใƒ‰ใ‚’ๅ…ฅๅŠ›", "main.popup.login.newPassword2": "ๆ–ฐใ—ใ„ใƒ‘ใ‚นใƒฏใƒผใƒ‰ใฎๅ†ๅ…ฅๅŠ›", "main.popup.login.placeholder": "ๅŠ่ง’10ๆ–‡ๅญ—ไปฅๅ†…", - "main.popup.login.guide1": "ๅˆๆœŸๅŒ–ใ•ใ‚ŒใŸใƒ‘ใ‚นใƒฏใƒผใƒ‰ใงใƒญใ‚ฐใ‚คใƒณใ—ใŸๅ ดๅˆใ€ใƒ‘ใ‚นใƒฏใƒผใƒ‰ใ‚’ๅค‰ๆ›ดใ—ใชใ‘ใ‚Œใฐใ‚ตใ‚คใƒˆๅˆฉ็”จใŒๅฏ่ƒฝใงใ™ใ€‚", + "main.popup.login.guide1": "ๅˆๅ›žใƒญใ‚ฐใ‚คใƒณๆ™‚ใฏใ€ใƒ‘ใ‚นใƒฏใƒผใƒ‰ๅค‰ๆ›ดๅพŒใซใ‚ตใ‚คใƒˆใ‚’ใ”ๅˆฉ็”จใ„ใŸใ ใ‘ใพใ™ใ€‚", "main.popup.login.guide2": "ใƒ‘ใ‚นใƒฏใƒผใƒ‰ใ‚’ๅค‰ๆ›ดใ—ใชใ„ๅ ดๅˆใฏใ€ใƒญใ‚ฐใ‚คใƒณ็”ป้ขใซ้€ฒใฟใพใ™ใ€‚", "main.popup.login.btn1": "ๅค‰ๆ›ด", "main.popup.login.btn2": "ๅค‰ๆ›ดใ—ใชใ„", diff --git a/src/locales/ko.json b/src/locales/ko.json index 3e5ed786..5d9a5474 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -782,6 +782,7 @@ "stuff.detail.tempSave.message2": "๋‹ด๋‹น์ž์ด๋ฆ„์€ ์ „๊ฐ20์ž(๋ฐ˜๊ฐ40์ž) ์ดํ•˜๋กœ ์ž…๋ ฅํ•ด ์ฃผ์‹ญ์‹œ์˜ค.", "stuff.detail.tempSave.message3": "2์ฐจ ํŒ๋งค์ ์„ ์„ ํƒํ•ด์ฃผ์„ธ์š”.", "stuff.detail.confirm.message1": "ํŒ๋งค์  ์ •๋ณด๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ์„ค๊ณ„์˜๋ขฐ ๋ฌธ์„œ๋ฒˆํ˜ธ๊ฐ€ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค. ๋ณ€๊ฒฝํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?", + "stuff.detail.planReq.message.notMatch": "์„ค๊ณ„์˜๋ขฐ์˜ ํŒ๋งค์  ์ •๋ณด๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์•„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.", "stuff.detail.delete.message1": "์‚ฌ์–‘์ด ํ™•์ •๋œ ๋ฌผ๊ฑด์€ ์‚ญ์ œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.", "stuff.detail.planList.title": "ํ”Œ๋žœ๋ชฉ๋ก", "stuff.detail.planList.cnt": "์ „์ฒด", @@ -936,7 +937,7 @@ "main.popup.login.newPassword1": "์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ", "main.popup.login.newPassword2": "์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์ž…๋ ฅ", "main.popup.login.placeholder": "๋ฐ˜๊ฐ 10์ž ์ด๋‚ด", - "main.popup.login.guide1": "์ดˆ๊ธฐํ™”๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ๋กœ๊ทธ์ธํ•œ ๊ฒฝ์šฐ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ์‚ฌ์ดํŠธ ์ด์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.", + "main.popup.login.guide1": "์ฒซ ๋กœ๊ทธ์ธ ์‹œ์—๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ํ›„ ์‚ฌ์ดํŠธ๋ฅผ ์ด์šฉํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹คใ€‚", "main.popup.login.guide2": "๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ๋กœ๊ทธ์ธ ํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.", "main.popup.login.btn1": "๋ณ€๊ฒฝ", "main.popup.login.btn2": "๋ณ€๊ฒฝ์•ˆํ•จ", diff --git a/src/util/logger.js b/src/util/logger.js index 5830c8db..c096298c 100644 --- a/src/util/logger.js +++ b/src/util/logger.js @@ -1,30 +1,90 @@ // utils/logger.js -const isLoggingEnabled = process.env.NEXT_PUBLIC_ENABLE_LOGGING; +// [LOGGER-GUARD 2026-05-14] string 'false' ๊ฐ€ truthy ๋ผ production ์—์„œ๋„ ๊ฐ€๋“œ๋ฅผ ํ†ต๊ณผํ•˜๋˜ ๋ฒ„๊ทธ ์ˆ˜์ •. +// ๋ช…์‹œ์  === 'true' ๋น„๊ต โ†’ Next.js inline replacement + dead code elimination ์œผ๋กœ production ๋นŒ๋“œ์—์„œ emit ํ˜ธ์ถœ ์ œ๊ฑฐ. +const isLoggingEnabled = process.env.NEXT_PUBLIC_ENABLE_LOGGING === 'true'; + +// [LOGGER-CALLER 2026-05-14] stack ์—์„œ logger.js ์ด์™ธ์˜ ์ฒซ frame ์„ ์ถ”์ถœํ•ด prefix ๋กœ ๋ถ™์ž„. +// ํŽ˜์ด์ง€/hook/์ปดํฌ๋„ŒํŠธ ์–ด๋””์„œ ์ฐํžŒ ๋กœ๊ทธ์ธ์ง€ ์ฝ˜์†”์—์„œ ์ฆ‰์‹œ ์‹๋ณ„ ๊ฐ€๋Šฅ. webpack chunk ๋งค์นญ ์œ„ํ•ด ํŒŒ์ผ๋ช…๋งŒ ์ถ”์ถœ. +const FILE_LINE_RE = /([\w\-\.]+\.(?:jsx?|tsx?)):(\d+)/ + +// [LOGGER-FORMAT 2026-05-14] ํ†ต์ผ ํฌ๋งท: [HH:mm:ss.SSS] [LEVEL] [caller] +// ๋ ˆ๋ฒจ๋ณ„ ์ƒ‰์ƒ ๋ฐฐ์ง€๋กœ ์ฝ˜์†”์—์„œ ํ•œ๋ˆˆ์— ๊ตฌ๋ถ„. +const STYLE_TS = 'color:#888' +const STYLE_CALLER = 'color:#888;font-style:italic' +const STYLE_LEVEL = { + log: 'background:#616161;color:#fff;padding:1px 6px;border-radius:2px;font-weight:bold', + info: 'background:#1e88e5;color:#fff;padding:1px 6px;border-radius:2px;font-weight:bold', + warn: 'background:#fb8c00;color:#fff;padding:1px 6px;border-radius:2px;font-weight:bold', + error: 'background:#e53935;color:#fff;padding:1px 6px;border-radius:2px;font-weight:bold', + debug: 'background:#8e24aa;color:#fff;padding:1px 6px;border-radius:2px;font-weight:bold', +} + +function formatTime() { + const d = new Date() + const hh = String(d.getHours()).padStart(2, '0') + const mm = String(d.getMinutes()).padStart(2, '0') + const ss = String(d.getSeconds()).padStart(2, '0') + const ms = String(d.getMilliseconds()).padStart(3, '0') + return `${hh}:${mm}:${ss}.${ms}` +} + +function getCallerTag() { + try { + const stack = new Error().stack + if (!stack) return '' + const lines = stack.split('\n') + for (let i = 1; i < lines.length; i++) { + const m = lines[i].match(FILE_LINE_RE) + if (m && m[1] !== 'logger.js') return `${m[1]}:${m[2]}` + } + } catch (_) {} + return '' +} + +function emit(level, args) { + const ts = formatTime() + const caller = getCallerTag() + const levelStyle = STYLE_LEVEL[level] || STYLE_LEVEL.log + const levelLabel = level.toUpperCase() + + // prefix: '[ts] %cLEVEL%c [caller] ' โ†’ styled level + dimmed caller + // user ์ฒซ ์ธ์ž๊ฐ€ string ์ด๋ฉด prefix ์™€ ํ•ฉ์ณ %c ํ˜ธํ™˜ ์œ ์ง€. + const prefixText = caller + ? `%c[${ts}] %c${levelLabel}%c [${caller}]` + : `%c[${ts}] %c${levelLabel}%c` + const prefixStyles = [STYLE_TS, levelStyle, STYLE_CALLER] + + if (typeof args[0] === 'string') { + console[level](`${prefixText} ${args[0]}`, ...prefixStyles, ...args.slice(1)) + } else { + console[level](prefixText, ...prefixStyles, ...args) + } +} export const logger = { log: (...args) => { if (isLoggingEnabled) { - console.log(...args); + emit('log', args); } }, error: (...args) => { // ์—๋Ÿฌ๋Š” ํ•ญ์ƒ ๋กœ๊น…ํ•˜๊ฑฐ๋‚˜, ๋˜๋Š” ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ - console.error(...args); + emit('error', args); // ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์„œ๋ฒ„๋กœ ์—๋Ÿฌ๋ฅผ ๋ณด๋‚ด๋Š” ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜๋„ ์žˆ์Œ }, warn: (...args) => { if (isLoggingEnabled) { - console.warn(...args); + emit('warn', args); } }, info: (...args) => { if (isLoggingEnabled) { - console.info(...args); + emit('info', args); } }, debug: (...args) => { if (isLoggingEnabled) { - console.debug(...args); + emit('debug', args); } } -}; \ No newline at end of file +}; diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index dccd3414..5fc1a2e7 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -405,6 +405,57 @@ export function cleanSelfIntersectingPolygon(vertices) { return vertices } +/** + * [1956-RACK-TILT 2026-05-13] ๊ฑฐ์˜ ์ถ•์— ํ‰ํ–‰ํ•œ edge ๋ฅผ ์ •ํ™•ํžˆ ์ˆ˜ํ‰/์ˆ˜์ง์œผ๋กœ ์Šค๋ƒ…. + * ๋นจ๊ฐ• ์ ์„ (๊ฐ€๋Œ€์„ /๋ชจ๋“ˆ์„ค์น˜๋ฉด) edge ๊ฐ€ FP drift ๋ˆ„์ ์œผ๋กœ ๋ฏธ์„ธ ๊ธฐ์šธ์–ด์ ธ ๋ณด์ด๋Š” ๋ฌธ์ œ ๋ณด์ •. + * **๊ฐ๋„ ๊ธฐ๋ฐ˜ ํŒ์ •**: |dy|/len < sin(angleTolDeg) ๋ฉด ์ˆ˜ํ‰์œผ๋กœ ๊ฐ„์ฃผ. + * ์ ˆ๋Œ€๊ฐ’ tolerance ๊ฐ€ ์•„๋‹Œ ๊ฐ๋„ ์ž„๊ณ„๋ฅผ ์“ฐ๋Š” ์ด์œ  โ€” ๊ธด edge(์˜ˆ: 576mm) ์— ์ž‘์€ ๋ˆ„์  + * drift(15mm) ๊ฐ€ ์‹ค๋ ค closing edge ๊ฐ€ ํก์ˆ˜ํ•˜๋ฉด ์ ˆ๋Œ€๊ฐ’์€ ํฌ์ง€๋งŒ ๊ฐ๋„๋Š” ์ž‘์Œ(1.5ยฐ). + * vertex ๋‹จ์œ„๋กœ ์ธ์ ‘ edge ์˜ ์ถ• ๋ชฉํ‘œ๊ฐ’์„ ์ ์šฉํ•ด ์ฝ”๋„ˆ ์–ด๊ธ‹๋‚จ ๋ฐฉ์ง€. + * ๋Œ€๊ฐ(hip/ridge, 45ยฐ ๋“ฑ) ์€ ์ž„๊ณ„ 2ยฐ ๋ณด๋‹ค ํ›จ์”ฌ ํฌ๋‹ˆ ๋ฌด์˜ํ–ฅ. + */ +export function snapNearAxisEdges(vertices, angleTolDeg = 2) { + if (!vertices || vertices.length < 3) return vertices + const n = vertices.length + const sinTol = Math.sin((angleTolDeg * Math.PI) / 180) + + const edgeY = new Array(n).fill(null) // ๊ฑฐ์˜ ์ˆ˜ํ‰ โ†’ ๋ชฉํ‘œ y + const edgeX = new Array(n).fill(null) // ๊ฑฐ์˜ ์ˆ˜์ง โ†’ ๋ชฉํ‘œ x + + for (let i = 0; i < n; i++) { + const a = vertices[i] + const b = vertices[(i + 1) % n] + const dx = b.x - a.x + const dy = b.y - a.y + const len = Math.sqrt(dx * dx + dy * dy) + if (len < 1) continue // 0-length edge ์•ˆ์ „์žฅ์น˜ + const absSinDy = Math.abs(dy) / len // sin(angle from horizontal) + const absSinDx = Math.abs(dx) / len // sin(angle from vertical) + if (absSinDy < sinTol && absSinDx >= sinTol) { + edgeY[i] = (a.y + b.y) / 2 // ๊ฑฐ์˜ ์ˆ˜ํ‰ + } else if (absSinDx < sinTol && absSinDy >= sinTol) { + edgeX[i] = (a.x + b.x) / 2 // ๊ฑฐ์˜ ์ˆ˜์ง + } + } + + return vertices.map((v, i) => { + const prev = (i - 1 + n) % n + let nx = v.x + let ny = v.y + const yPrev = edgeY[prev] + const yCur = edgeY[i] + if (yPrev !== null && yCur !== null) ny = (yPrev + yCur) / 2 + else if (yPrev !== null) ny = yPrev + else if (yCur !== null) ny = yCur + const xPrev = edgeX[prev] + const xCur = edgeX[i] + if (xPrev !== null && xCur !== null) nx = (xPrev + xCur) / 2 + else if (xPrev !== null) nx = xPrev + else if (xCur !== null) nx = xCur + return { x: nx, y: ny } + }) +} + export default function offsetPolygon(vertices, offset) { const polygon = createPolygon(vertices) const arcSegments = 0 diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js index 5a3e89d1..2329cc92 100644 --- a/src/util/skeleton-utils.js +++ b/src/util/skeleton-utils.js @@ -6,6 +6,7 @@ import { getDegreeByChon } from '@/util/canvas-util' import Big from 'big.js' import { QPolygon } from '@/components/fabric/QPolygon' import wallLine from '@/components/floor-plan/modal/wallLineOffset/type/WallLine' +import { logger } from '@/util/logger' /** * ์ง€๋ถ• ํด๋ฆฌ๊ณค์˜ ์Šค์ผˆ๋ ˆํ†ค(์ค‘์‹ฌ์„ )์„ ์ƒ์„ฑํ•˜๊ณ  ์บ”๋ฒ„์Šค์— ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. @@ -95,7 +96,7 @@ const movingLineFromSkeleton = (roofId, canvas) => { const _lp = canvas?.skeleton?.lastPoints const _src = _lp ? 'lastPoints' : 'orgRoofPoints' const _p7 = oldPoints?.[7] - console.log(`[LP-TRACE] movingLineFromSkeleton.in src=${_src} oldPoints[7]=(${_p7?.x?.toFixed(1)},${_p7?.y?.toFixed(1)}) pos=${movePosition} dir=${moveDirection}`) + // logger.log(`[LP-TRACE] movingLineFromSkeleton.in src=${_src} oldPoints[7]=(${_p7?.x?.toFixed(1)},${_p7?.y?.toFixed(1)}) pos=${movePosition} dir=${moveDirection}`) } catch (_e) {} const oppositeLine = findOppositeLine(canvas.skeleton.Edges, startPoint, endPoint, oldPoints); @@ -107,18 +108,18 @@ const movingLineFromSkeleton = (roofId, canvas) => { const skeletonLines = canvas.getObjects().filter((object) => object.skeletonType === 'line' && object.parentId === roofId) if (oppositeLine) { - console.log('Opposite line found:', oppositeLine); + // logger.log('Opposite line found:', oppositeLine); } else { - console.log('No opposite line found'); + // logger.log('No opposite line found'); } if(moveFlowLine !== 0) { return oldPoints.map((point, index) => { - console.log('Point:', point); + // logger.log('Point:', point); const newPoint = { ...point }; const absMove = Big(moveFlowLine).times(2).div(10); - console.log('skeletonBuilder moveDirection:', moveDirection); + // logger.log('skeletonBuilder moveDirection:', moveDirection); switch (moveDirection) { case 'left': @@ -179,7 +180,7 @@ const movingLineFromSkeleton = (roofId, canvas) => { if (line.position === 'bottom') { - console.log('oldPoint:', point); + // logger.log('oldPoint:', point); if (isSamePoint(newPoint, line.start)) { newPoint.y = Big(line.start.y).minus(absMove).toNumber(); @@ -198,7 +199,7 @@ const movingLineFromSkeleton = (roofId, canvas) => { // ์‚ฌ์šฉ ์˜ˆ์‹œ } - console.log('newPoint:', newPoint); + // logger.log('newPoint:', newPoint); //baseline ๋ณ€๊ฒฝ return newPoint; }) @@ -207,14 +208,14 @@ const movingLineFromSkeleton = (roofId, canvas) => { // const selectLine = getSelectLine(); // - // console.log("wall::::", wall.points) - // console.log("์ €์žฅ๋œ 3333moveSelectLine:", roof.moveSelectLine); - // console.log("์ €์žฅ๋œ 3moveSelectLine:", selectLine); + // logger.log("wall::::", wall.points) + // logger.log("์ €์žฅ๋œ 3333moveSelectLine:", roof.moveSelectLine); + // logger.log("์ €์žฅ๋œ 3moveSelectLine:", selectLine); // const result = getSelectLinePosition(wall, selectLine, { // testDistance: 5, // ํ…Œ์ŠคํŠธ ๊ฑฐ๋ฆฌ // debug: true // ๋””๋ฒ„๊น… ๋กœ๊ทธ ์ถœ๋ ฅ // }); - // console.log("3333linePosition:::::", result.position); + // logger.log("3333linePosition:::::", result.position); const position = movePosition //result.position; const moveUpDownLength = Big(moveUpDown).times(1).div(10); @@ -255,10 +256,10 @@ const movingLineFromSkeleton = (roofId, canvas) => { affected.add(k); affected.add((k + 1) % nPts); }); - console.log('absMove::', moveUpDownLength); + // logger.log('absMove::', moveUpDownLength); // [BR-TRACE local] movingLineFromSkeleton position/direction ๋ถ„๊ธฐ const __isLocalMS = process.env.NEXT_PUBLIC_RUN_MODE === 'local' - if (__isLocalMS) console.log(`[BR-TRACE] movingLineFromSkeleton position=${position} dir=${moveDirection} affected=${[...affected].join(',')}`) + // if (__isLocalMS) logger.log(`[BR-TRACE] movingLineFromSkeleton position=${position} dir=${moveDirection} affected=${[...affected].join(',')}`) affected.forEach((i) => { const point = newPoints[i]; if (!point) return; @@ -266,21 +267,21 @@ const movingLineFromSkeleton = (roofId, canvas) => { if (position === 'bottom') { if (moveDirection === 'in') point.y = Big(point.y).minus(moveUpDownLength).toNumber(); else if (moveDirection === 'out') point.y = Big(point.y).plus(moveUpDownLength).toNumber(); - if (__isLocalMS) console.log(`[BR-TRACE] i=${i} bottom/${moveDirection} y ${__by}โ†’${point.y}`) + // if (__isLocalMS) logger.log(`[BR-TRACE] i=${i} bottom/${moveDirection} y ${__by}โ†’${point.y}`) } else if (position === 'top') { if (moveDirection === 'in') point.y = Big(point.y).plus(moveUpDownLength).toNumber(); else if (moveDirection === 'out') point.y = Big(point.y).minus(moveUpDownLength).toNumber(); - if (__isLocalMS) console.log(`[BR-TRACE] i=${i} top/${moveDirection} y ${__by}โ†’${point.y}`) + // if (__isLocalMS) logger.log(`[BR-TRACE] i=${i} top/${moveDirection} y ${__by}โ†’${point.y}`) } else if (position === 'left') { if (moveDirection === 'in') point.x = Big(point.x).plus(moveUpDownLength).toNumber(); else if (moveDirection === 'out') point.x = Big(point.x).minus(moveUpDownLength).toNumber(); - if (__isLocalMS) console.log(`[BR-TRACE] i=${i} left/${moveDirection} x ${__bx}โ†’${point.x}`) + // if (__isLocalMS) logger.log(`[BR-TRACE] i=${i} left/${moveDirection} x ${__bx}โ†’${point.x}`) } else if (position === 'right') { if (moveDirection === 'in') point.x = Big(point.x).minus(moveUpDownLength).toNumber(); else if (moveDirection === 'out') point.x = Big(point.x).plus(moveUpDownLength).toNumber(); - if (__isLocalMS) console.log(`[BR-TRACE] i=${i} right/${moveDirection} x ${__bx}โ†’${point.x}`) + // if (__isLocalMS) logger.log(`[BR-TRACE] i=${i} right/${moveDirection} x ${__bx}โ†’${point.x}`) } else if (__isLocalMS) { - console.warn(`[BR-TRACE] i=${i} position=${position} ๋งค์นญ ๋ถ„๊ธฐ ์—†์Œ (์ด๋™ ์•ˆ ๋จ)`) + logger.warn(`[BR-TRACE] i=${i} position=${position} ๋งค์นญ ๋ถ„๊ธฐ ์—†์Œ (์ด๋™ ์•ˆ ๋จ)`) } }); @@ -343,7 +344,7 @@ const movingLineFromSkeleton = (roofId, canvas) => { const cleaned = removeNonOrthogonalPoints(newPoints); - // console.log(cleaned) + // logger.log(cleaned) return cleaned; } @@ -408,7 +409,7 @@ const buildRawMovedPoints = (roofId, canvas) => { // [LP-TRACE] buildRawMovedPoints ์ง„์ž… โ€” prevLast ์œ ๋ฌด ๋ฐ [7] ํ˜„์žฌ ๊ฐ’ try { const _pl7 = prevLast?.[7] - console.log(`[LP-TRACE] buildRaw.in prevLast=${prevLast ? 'Y' : 'N'} prevLast[7]=(${_pl7?.x?.toFixed(1)},${_pl7?.y?.toFixed(1)}) oldPoints[7]=(${oldPoints[7]?.x?.toFixed(1)},${oldPoints[7]?.y?.toFixed(1)})`) + // logger.log(`[LP-TRACE] buildRaw.in prevLast=${prevLast ? 'Y' : 'N'} prevLast[7]=(${_pl7?.x?.toFixed(1)},${_pl7?.y?.toFixed(1)}) oldPoints[7]=(${oldPoints[7]?.x?.toFixed(1)},${oldPoints[7]?.y?.toFixed(1)})`) } catch (_e) {} const selectLine = roof.moveSelectLine @@ -446,7 +447,7 @@ const buildRawMovedPoints = (roofId, canvas) => { }) // [BR-TRACE local] buildRawMovedPoints position/direction ๋ถ„๊ธฐ const __isLocalBR = process.env.NEXT_PUBLIC_RUN_MODE === 'local' - if (__isLocalBR) console.log(`[BR-TRACE] buildRawMovedPoints position=${position} dir=${moveDirection} affected=${[...affected].join(',')}`) + // if (__isLocalBR) logger.log(`[BR-TRACE] buildRawMovedPoints position=${position} dir=${moveDirection} affected=${[...affected].join(',')}`) affected.forEach((i) => { const point = newPoints[i] if (!point) return @@ -454,21 +455,21 @@ const buildRawMovedPoints = (roofId, canvas) => { if (position === 'bottom') { if (moveDirection === 'out') point.y = Big(point.y).plus(moveUpDownLength).toNumber() else if (moveDirection === 'in') point.y = Big(point.y).minus(moveUpDownLength).toNumber() - if (__isLocalBR) console.log(`[BR-TRACE] i=${i} bottom/${moveDirection} y ${__by}โ†’${point.y}`) + // if (__isLocalBR) logger.log(`[BR-TRACE] i=${i} bottom/${moveDirection} y ${__by}โ†’${point.y}`) } else if (position === 'top') { if (moveDirection === 'out') point.y = Big(point.y).minus(moveUpDownLength).toNumber() else if (moveDirection === 'in') point.y = Big(point.y).plus(moveUpDownLength).toNumber() - if (__isLocalBR) console.log(`[BR-TRACE] i=${i} top/${moveDirection} y ${__by}โ†’${point.y}`) + // if (__isLocalBR) logger.log(`[BR-TRACE] i=${i} top/${moveDirection} y ${__by}โ†’${point.y}`) } else if (position === 'left') { if (moveDirection === 'out') point.x = Big(point.x).minus(moveUpDownLength).toNumber() else if (moveDirection === 'in') point.x = Big(point.x).plus(moveUpDownLength).toNumber() - if (__isLocalBR) console.log(`[BR-TRACE] i=${i} left/${moveDirection} x ${__bx}โ†’${point.x}`) + // if (__isLocalBR) logger.log(`[BR-TRACE] i=${i} left/${moveDirection} x ${__bx}โ†’${point.x}`) } else if (position === 'right') { if (moveDirection === 'out') point.x = Big(point.x).plus(moveUpDownLength).toNumber() else if (moveDirection === 'in') point.x = Big(point.x).minus(moveUpDownLength).toNumber() - if (__isLocalBR) console.log(`[BR-TRACE] i=${i} right/${moveDirection} x ${__bx}โ†’${point.x}`) + // if (__isLocalBR) logger.log(`[BR-TRACE] i=${i} right/${moveDirection} x ${__bx}โ†’${point.x}`) } else if (__isLocalBR) { - console.warn(`[BR-TRACE] i=${i} position=${position} ๋งค์นญ ๋ถ„๊ธฐ ์—†์Œ (์ด๋™ ์•ˆ ๋จ)`) + logger.warn(`[BR-TRACE] i=${i} position=${position} ๋งค์นญ ๋ถ„๊ธฐ ์—†์Œ (์ด๋™ ์•ˆ ๋จ)`) } }) @@ -477,12 +478,12 @@ const buildRawMovedPoints = (roofId, canvas) => { const _sel = selectLine const _sp = _sel?.startPoint const _ep = _sel?.endPoint - console.log(`[LP-TRACE] buildRaw.out matchingLines=${matchingLines.length} selSP=(${_sp?.x?.toFixed(1)},${_sp?.y?.toFixed(1)}) selEP=(${_ep?.x?.toFixed(1)},${_ep?.y?.toFixed(1)}) bp7=(${basePoints[7]?.x?.toFixed(1)},${basePoints[7]?.y?.toFixed(1)}) newPoints[7]=(${newPoints[7]?.x?.toFixed(1)},${newPoints[7]?.y?.toFixed(1)})`) + // logger.log(`[LP-TRACE] buildRaw.out matchingLines=${matchingLines.length} selSP=(${_sp?.x?.toFixed(1)},${_sp?.y?.toFixed(1)}) selEP=(${_ep?.x?.toFixed(1)},${_ep?.y?.toFixed(1)}) bp7=(${basePoints[7]?.x?.toFixed(1)},${basePoints[7]?.y?.toFixed(1)}) newPoints[7]=(${newPoints[7]?.x?.toFixed(1)},${newPoints[7]?.y?.toFixed(1)})`) } catch (_e) {} return newPoints } catch (e) { - console.warn('[buildRawMovedPoints] ์‹คํŒจ โ†’ null fallback:', e) + logger.warn('[buildRawMovedPoints] ์‹คํŒจ โ†’ null fallback:', e) return null } } @@ -511,7 +512,7 @@ export const verifyMoveBoundary = (roofId, canvas) => { try { const roof = canvas?.getObjects().find((o) => o.id === roofId) if (!roof || !Array.isArray(roof.points)) { - console.log('[verifyMoveBoundary] roof/points ์—†์Œ โ†’ unknown') + // logger.log('[verifyMoveBoundary] roof/points ์—†์Œ โ†’ unknown') return 'unknown' } @@ -519,20 +520,20 @@ export const verifyMoveBoundary = (roofId, canvas) => { const moveUpDown = roof.moveUpDown ?? 0 const position = roof.movePosition const direction = roof.moveDirect - console.log( - `[verifyMoveBoundary] ์ง„์ž… position=${position} direction=${direction} moveUpDown=${moveUpDown} moveFlowLine=${moveFlowLine}` - ) + // logger.log( + // `[verifyMoveBoundary] ์ง„์ž… position=${position} direction=${direction} moveUpDown=${moveUpDown} moveFlowLine=${moveFlowLine}` + // ) if (moveFlowLine === 0 && moveUpDown === 0) { - console.log('[verifyMoveBoundary] ์ด๋™ ์—†์Œ โ†’ ok') + // logger.log('[verifyMoveBoundary] ์ด๋™ ์—†์Œ โ†’ ok') return 'ok' } const proposed = buildRawMovedPoints(roofId, canvas) const orig = roof.points if (!Array.isArray(proposed) || proposed.length !== orig.length) { - console.log( - `[verifyMoveBoundary] proposed ๋น„์ •์ƒ (len=${proposed?.length}, orig.len=${orig.length}) โ†’ unknown` - ) + // logger.log( + // `[verifyMoveBoundary] proposed ๋น„์ •์ƒ (len=${proposed?.length}, orig.len=${orig.length}) โ†’ unknown` + // ) return 'unknown' } @@ -555,9 +556,9 @@ export const verifyMoveBoundary = (roofId, canvas) => { const crossOrig = getTurnDirection(orig[prev], orig[i], orig[next]) const crossNew = getTurnDirection(proposed[prev], proposed[i], proposed[next]) - console.log( - `[verifyMoveBoundary] [${i}] orig=(${Math.round(orig[i].x)},${Math.round(orig[i].y)}) โ†’ proposed=(${Math.round(proposed[i].x)},${Math.round(proposed[i].y)}) crossOrig=${crossOrig.toFixed(1)} crossNew=${crossNew.toFixed(1)}` - ) + // logger.log( + // `[verifyMoveBoundary] [${i}] orig=(${Math.round(orig[i].x)},${Math.round(orig[i].y)}) โ†’ proposed=(${Math.round(proposed[i].x)},${Math.round(proposed[i].y)}) crossOrig=${crossOrig.toFixed(1)} crossNew=${crossNew.toFixed(1)}` + // ) // ์›๋ž˜ ๊ณจ์งœ๊ธฐ(>0) ์˜€๋˜ ๊ผญ์ง“์ ๋งŒ ๊ฒฝ๊ณ„ ํŒ์ • ๋Œ€์ƒ if (crossOrig > 0) { @@ -565,27 +566,27 @@ export const verifyMoveBoundary = (roofId, canvas) => { const boundaryTol = Math.max(1.0, Math.abs(crossOrig) * 0.05) if (crossNew < -boundaryTol) { - console.warn( + logger.warn( `[verifyMoveBoundary] ๊ผญ์ง“์ [${i}] ๊ณจ์งœ๊ธฐ โ†’ ๋ณผ๋ก ๋’ค์ง‘ํž˜ (CROSSED): ${crossOrig.toFixed(1)} โ†’ ${crossNew.toFixed(1)}` ) return 'crossed' } if (Math.abs(crossNew) <= boundaryTol) { - console.log( - `[verifyMoveBoundary] ๊ผญ์ง“์ [${i}] ๊ฒฝ๊ณ„ ๋„๋‹ฌ (on-boundary): cross โ‰ˆ 0 (${crossNew.toFixed(1)})` - ) + // logger.log( + // `[verifyMoveBoundary] ๊ผญ์ง“์ [${i}] ๊ฒฝ๊ณ„ ๋„๋‹ฌ (on-boundary): cross โ‰ˆ 0 (${crossNew.toFixed(1)})` + // ) if (worst === 'ok') worst = 'on-boundary' } } } if (movedIdx.length === 0) { - console.log('[verifyMoveBoundary] ์ด๋™๋œ ๊ผญ์ง“์  0๊ฐœ (buildRawMovedPoints ๋งค์นญ ์‹คํŒจ ๊ฐ€๋Šฅ์„ฑ)') + // logger.log('[verifyMoveBoundary] ์ด๋™๋œ ๊ผญ์ง“์  0๊ฐœ (buildRawMovedPoints ๋งค์นญ ์‹คํŒจ ๊ฐ€๋Šฅ์„ฑ)') } - console.log(`[verifyMoveBoundary] ์ตœ์ข… ํŒ์ •: ${worst} (moved indices=[${movedIdx.join(',')}])`) + // logger.log(`[verifyMoveBoundary] ์ตœ์ข… ํŒ์ •: ${worst} (moved indices=[${movedIdx.join(',')}])`) return worst } catch (e) { - console.warn('[verifyMoveBoundary] ํŒ์ • ์‹คํŒจ โ†’ unknown:', e) + logger.warn('[verifyMoveBoundary] ํŒ์ • ์‹คํŒจ โ†’ unknown:', e) return 'unknown' } } @@ -600,7 +601,7 @@ const safeMovedPointsWithFallback = (roofId, canvas) => { const movedPoints = movingLineFromSkeleton(roofId, canvas) if (!Array.isArray(movedPoints) || movedPoints.length === 0) { - console.warn('[safeMovedPointsWithFallback] movedPoints ๋น„์ •์ƒ โ†’ bail out') + logger.warn('[safeMovedPointsWithFallback] movedPoints ๋น„์ •์ƒ โ†’ bail out') return null } @@ -650,7 +651,7 @@ const safeMovedPointsWithFallback = (roofId, canvas) => { } if (zeroLenEdges > 0 || severeReduction || selfIntersect) { - console.warn('[safeMovedPointsWithFallback] ๋ถ•๊ดด/๊ต์ฐจ ๊ฐ์ง€ (๋กœ๊ทธ๋งŒ)', { + logger.warn('[safeMovedPointsWithFallback] ๋ถ•๊ดด/๊ต์ฐจ ๊ฐ์ง€ (๋กœ๊ทธ๋งŒ)', { movedLen: movedPoints.length, oldLen: oldPoints.length, zeroLenEdges, @@ -734,7 +735,7 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi if (!il || !isFinite(il.x1) || !isFinite(il.y1) || !isFinite(il.x2) || !isFinite(il.y2)) return false return il.lineName === 'hip' || il.name === 'hip' }) - console.log(`[v1 ext] HIP ์ถ”์ถœ ${hipLines.length}๊ฐœ / ์ „์ฒด innerLines ${roof.innerLines.length}`) + // logger.log(`[v1 ext] HIP ์ถ”์ถœ ${hipLines.length}๊ฐœ / ์ „์ฒด innerLines ${roof.innerLines.length}`) // ์ ˆ์‚ญ ๋Œ€์ƒ: roofLine seg + canvas ์˜ ๋‹ค๋ฅธ ๋ณด์กฐ๋ผ์ธ (eaveHelpLine ๋“ฑ). // ๊ธฐ์กด helper ์ž์ฒด(BASELINE_TO_ROOFLINE_HELPER_TYPE) / wall/roof ๋ณธ์ฒด / ํ…์ŠคํŠธ ๋“ฑ์€ ์ œ์™ธ. @@ -764,7 +765,7 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi const s = segs[k] auxList.push(`${s.__name || '?'}=(${Math.round(s.A.x)},${Math.round(s.A.y)})โ†’(${Math.round(s.B.x)},${Math.round(s.B.y)}) len=${Math.round(Math.hypot(s.B.x - s.A.x, s.B.y - s.A.y))}`) } - console.log(`[BR-SEGS] roofLine=${m} aux=${segs.length - m}`, auxList) + // logger.log(`[BR-SEGS] roofLine=${m} aux=${segs.length - m}`, auxList) } // [v1 ext ์ •์ •] ext ๋Š” wallbaseLine ์˜ ๊ผญ์ง€์ (corner) ์— outer ๋์ ์ด ์ผ์น˜ํ•˜๋Š” hip ๋งŒ ๋Œ€์ƒ. @@ -783,16 +784,16 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi // outer ๋์ด wallbaseLine corner ์— ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด ext ๋Œ€์ƒ ์•„๋‹˜. if (outerCornerDist > CORNER_EPS) { - console.log( - `[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) ` + - `corner ๊ฑฐ๋ฆฌ=${outerCornerDist.toFixed(2)} > ${CORNER_EPS} โ†’ ๋‚ด๋ถ€ hip skip` - ) + // logger.log( + // `[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) ` + + // `corner ๊ฑฐ๋ฆฌ=${outerCornerDist.toFixed(2)} > ${CORNER_EPS} โ†’ ๋‚ด๋ถ€ hip skip` + // ) continue } // ์ด๋ฏธ ์ฒ˜๋งˆ(roofLine) ์œ„์— ์žˆ์œผ๋ฉด ext ๋ถˆํ•„์š”. if (isPointOnRoofLine(outerEnd, 1.0)) { - console.log(`[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) ์ด๋ฏธ roofLine ์œ„ โ†’ skip`) + // logger.log(`[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) ์ด๋ฏธ roofLine ์œ„ โ†’ skip`) continue } @@ -814,7 +815,7 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi // [BR-RAY] local only โ€” finite t ์ธ ๋ชจ๋“  ํ›„๋ณด hit ์ฐ๊ธฐ (์–ด๋–ค seg ๊ฐ€ ๊ฐ€์žฅ ๋น ๋ฅธ์ง€ ๋น„๊ต์šฉ) if (__isLocalRayLog && isFinite(t)) { const __label = (k < m) ? `roofLine#${k}` : (segs[k].__name || `aux#${k - m}`) - console.log(`[BR-RAY] hip outer=(${Math.round(outerEnd.x)},${Math.round(outerEnd.y)}) ${__label} t=${t.toFixed(2)} seg=(${Math.round(A.x)},${Math.round(A.y)})โ†’(${Math.round(B.x)},${Math.round(B.y)})`) + // logger.log(`[BR-RAY] hip outer=(${Math.round(outerEnd.x)},${Math.round(outerEnd.y)}) ${__label} t=${t.toFixed(2)} seg=(${Math.round(A.x)},${Math.round(A.y)})โ†’(${Math.round(B.x)},${Math.round(B.y)})`) } if (t < bestT) { bestT = t @@ -823,7 +824,7 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi } } if (!isFinite(bestT) || bestT < 0.5) { - console.log(`[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) NO_HIT or ๋„ˆ๋ฌด ๊ฐ€๊นŒ์›€`) + // logger.log(`[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) NO_HIT or ๋„ˆ๋ฌด ๊ฐ€๊นŒ์›€`) continue } @@ -832,18 +833,18 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi // eaveHelpLine ์ด ray ๊ฒฝ๋กœ๋ฅผ ๊ฐ€๋กœ๋ง‰์œผ๋ฉด ์ด๋ฏธ eave/aux ์˜์—ญ์— ๋„๋‹ฌํ•œ ๊ฒƒ์ด๋ฏ€๋กœ ๋” ๊ทธ๋ฆด ํ•„์š” ์—†์Œ. // roofLine ์Šน์ž๋งŒ ์ง„์งœ "์ฒ˜๋งˆ ๋„๋‹ฌ" ๋กœ ์ธ์ • โ†’ ext ๊ทธ๋ฆผ. if (bestSegName === 'eaveHelpLine') { - console.log(`[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) ์Šน์ž=eaveHelpLine bestT=${bestT.toFixed(2)} โ†’ skip`) + // logger.log(`[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) ์Šน์ž=eaveHelpLine bestT=${bestT.toFixed(2)} โ†’ skip`) continue } const hitX = outerEnd.x + ux * bestT const hitY = outerEnd.y + uy * bestT - console.log( - `[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) ` + - `dir=(${ux.toFixed(3)},${uy.toFixed(3)}) bestT=${bestT.toFixed(2)} src=${bestSrc} ` + - `โ†’ ext=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)})โ†’(${hitX.toFixed(1)},${hitY.toFixed(1)})` - ) + // logger.log( + // `[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) ` + + // `dir=(${ux.toFixed(3)},${uy.toFixed(3)}) bestT=${bestT.toFixed(2)} src=${bestSrc} ` + + // `โ†’ ext=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)})โ†’(${hitX.toFixed(1)},${hitY.toFixed(1)})` + // ) // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ // [hip ์ขŒํ‘œ ์—ฐ์žฅ 2026-04-30] @@ -871,17 +872,22 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi hip.attributes.actualSize = Math.round(oldActual * ratio * 100) / 100 // integrateExtensionLines ๊ฐ€ ์ด hip ์˜ ์ขŒํ‘œ๋ฅผ ๋˜๋Œ๋ฆฌ์ง€ ์•Š๊ฒŒ ๊ฐ€๋“œ. + // [save/load ๋ณด์กด 2026-05-13] runtime ํ”Œ๋ž˜๊ทธ(__extended) ์™ธ์— attributes.extended ๋„ ๋™๊ธฐํ™”. + // attributes ๋Š” SAVE_KEY ์— ํฌํ•จ โ†’ JSON ์ €์žฅ/๋กœ๋“œ ํ›„์—๋„ ์‚ด์•„๋‚จ์Œ. + // ์ €์žฅ ํ›„ ์žฌ๋กœ๋“œ ์‹œ sk.__extended ๊ฐ€ ์‚ฌ๋ผ์ ธ INTEGRATE merge ๊ฐ€ ๋‹ค์‹œ ์‹คํ–‰๋˜์–ด + // ํ™•์žฅ๋œ hip ๋์ ์ด ์˜› ์ขŒํ‘œ๋กœ ๋˜๋Œ์•„๊ฐ€๋Š” ํšŒ๊ท€ ์ฐจ๋‹จ. hip.__extended = true + hip.attributes.extended = true // Fabric bounding/length/text ๊ฐฑ์‹ . if (typeof hip.setCoords === 'function') hip.setCoords() if (typeof hip.setLength === 'function') hip.setLength() if (typeof hip.addLengthText === 'function') hip.addLengthText() - console.log( - `[v1 ext] hip ์—ฐ์žฅ oldLen=${oldLen.toFixed(1)} +extLen=${extLen.toFixed(1)} ratio=${ratio.toFixed(3)} ` + - `(plane ${oldPlane}โ†’${hip.attributes.planeSize}, actual ${oldActual}โ†’${hip.attributes.actualSize})` - ) + // logger.log( + // `[v1 ext] hip ์—ฐ์žฅ oldLen=${oldLen.toFixed(1)} +extLen=${extLen.toFixed(1)} ratio=${ratio.toFixed(3)} ` + + // `(plane ${oldPlane}โ†’${hip.attributes.planeSize}, actual ${oldActual}โ†’${hip.attributes.actualSize})` + // ) // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ // [ํ™•์žฅ์„  ์Šคํƒ€์ผ] local: ๋นจ๊ฐ• ์ ์„ (๋””๋ฒ„๊ทธ ๊ฐ€์‹œํ™”), dev/prd: innerLine ๋™์ผ์ƒ‰ยท์‹ค์„ (์—ฐ๊ฒฐ๋œ ๊ฒƒ์ฒ˜๋Ÿผ). @@ -947,16 +953,16 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { // // ๋””๋ฒ„๊ทธ: baseLines์˜ offset/type/์ขŒํ‘œ ํ™•์ธ // baseLines.forEach((bl, i) => { - // console.log(`[skeleton] baseLine[${i}] type=${bl.attributes?.type} offset=${bl.attributes?.offset} (${Math.round(bl.x1)},${Math.round(bl.y1)})โ†’(${Math.round(bl.x2)},${Math.round(bl.y2)})`) + // logger.log(`[skeleton] baseLine[${i}] type=${bl.attributes?.type} offset=${bl.attributes?.offset} (${Math.round(bl.x1)},${Math.round(bl.y1)})โ†’(${Math.round(bl.x2)},${Math.round(bl.y2)})`) // }) // roof.points.forEach((p, i) => { - // console.log(`[skeleton] roof.points[${i}] (${Math.round(p.x)},${Math.round(p.y)})`) + // logger.log(`[skeleton] roof.points[${i}] (${Math.round(p.x)},${Math.round(p.y)})`) // }) // roof.points ์ˆœ์„œ์™€ ๋งž์ถฐ์•ผ offset ๋งคํ•‘์ด ์ •ํ™•ํ•จ const baseLinePoints = createOrderedBasePoints(roof.points, baseLines) // baseLinePoints.forEach((p, i) => { - // console.log(`[skeleton] orderedBase[${i}] (${Math.round(p.x)},${Math.round(p.y)})`) + // logger.log(`[skeleton] orderedBase[${i}] (${Math.round(p.x)},${Math.round(p.y)})`) // }) @@ -969,7 +975,7 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { // 1. ์ง€๋ถ• ํด๋ฆฌ๊ณค ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ const coordinates = preprocessPolygonCoordinates(roof.points) if (coordinates.length < 3) { - console.warn('Polygon has less than 3 unique points. Cannot generate skeleton.') + logger.warn('Polygon has less than 3 unique points. Cannot generate skeleton.') return } @@ -977,10 +983,10 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { const moveUpDown = roof.moveUpDown || 0 // ๋””๋ฒ„๊ทธ: offset ๋ณ€๊ฒฝ + moveLine ์ƒํƒœ ํ™•์ธ - console.log('[DEBUG] moveFlowLine:', moveFlowLine, 'moveUpDown:', moveUpDown) - console.log('[DEBUG] wall.lines:', wall.lines.map((l, i) => `[${i}] (${Math.round(l.x1)},${Math.round(l.y1)})โ†’(${Math.round(l.x2)},${Math.round(l.y2)})`)) - console.log('[DEBUG] wall.baseLines:', wall.baseLines.map((l, i) => `[${i}] (${Math.round(l.x1)},${Math.round(l.y1)})โ†’(${Math.round(l.x2)},${Math.round(l.y2)})`)) - console.log('[DEBUG] roof.points:', roof.points.map((p, i) => `[${i}] (${Math.round(p.x)},${Math.round(p.y)})`)) + // logger.log('[DEBUG] moveFlowLine:', moveFlowLine, 'moveUpDown:', moveUpDown) + // logger.log('[DEBUG] wall.lines:', wall.lines.map((l, i) => `[${i}] (${Math.round(l.x1)},${Math.round(l.y1)})โ†’(${Math.round(l.x2)},${Math.round(l.y2)})`)) + // logger.log('[DEBUG] wall.baseLines:', wall.baseLines.map((l, i) => `[${i}] (${Math.round(l.x1)},${Math.round(l.y1)})โ†’(${Math.round(l.x2)},${Math.round(l.y2)})`)) + // logger.log('[DEBUG] roof.points:', roof.points.map((p, i) => `[${i}] (${Math.round(p.x)},${Math.round(p.y)})`)) // ๋‹ซํžŒ ํด๋ฆฌ๊ณค(์ฒซ์  = ๋์ )์ธ ๊ฒฝ์šฐ ๋งˆ์ง€๋ง‰ ์ค‘๋ณต ์  ์ œ๊ฑฐ const isClosedPolygon = (points) => @@ -1039,10 +1045,10 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { // - 45๋„ ๋Œ€๊ฐ ๋ฐฉํ–ฅ(signDx, signDy ๊ฐ ยฑ1)์ด๋ฏ€๋กœ ์‹ค์ œ xยทy ์ด๋™๋Ÿ‰์€ ๊ฐ ์ถ•์œผ๋กœ maxStep const maxContactDistance = Math.hypot(maxStep, maxStep) - // console.log("orderedBaseLinePoints",orderedBaseLinePoints) - // console.log("contactData",contactData) - // console.log("roofLineContactPoints",roofLineContactPoints) - // console.log("maxContactDistance",maxContactDistance) + // logger.log("orderedBaseLinePoints",orderedBaseLinePoints) + // logger.log("contactData",contactData) + // logger.log("roofLineContactPoints",roofLineContactPoints) + // logger.log("maxContactDistance",maxContactDistance) let changRoofLinePoints = orderedBaseLinePoints.map((point, index) => { const contactPoint = roofLineContactPoints[index] @@ -1059,7 +1065,7 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { } }) - // console.log("changRoofLinePoints1:::", changRoofLinePoints) + // logger.log("changRoofLinePoints1:::", changRoofLinePoints) // 6. ๋งˆ๋ฃจ์ด๋™(์šฉ๋งˆ๋ฃจ ์œ„์น˜ ์ˆ˜๋™ ์กฐ์ •)์ด ์„ค์ •๋œ ๊ฒฝ์šฐ movingLineFromSkeleton ๊ฒฐ๊ณผ๋กœ ๋Œ€์ฒด if (moveFlowLine !== 0 || moveUpDown !== 0) { @@ -1070,14 +1076,14 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { try { const __moveVerdict = verifyMoveBoundary(roofId, canvas) if (__moveVerdict === 'crossed') { - console.warn('[skeleton] ๊ฒฝ๊ณ„ ๋„˜์Œ(crossed) โ†’ ์˜ค๋ฒ„๋œ wall.baseLine ์ขŒํ‘œ ๊ธฐ๋ฐ˜ ์žฌ๋นŒ๋“œ ์ง„ํ–‰') + logger.warn('[skeleton] ๊ฒฝ๊ณ„ ๋„˜์Œ(crossed) โ†’ ์˜ค๋ฒ„๋œ wall.baseLine ์ขŒํ‘œ ๊ธฐ๋ฐ˜ ์žฌ๋นŒ๋“œ ์ง„ํ–‰') } } catch (e) { - console.warn('[skeleton] verifyMoveBoundary ์˜ˆ์™ธ โ†’ ๊ธฐ์กด ํ๋ฆ„ ์ง„ํ–‰:', e) + logger.warn('[skeleton] verifyMoveBoundary ์˜ˆ์™ธ โ†’ ๊ธฐ์กด ํ๋ฆ„ ์ง„ํ–‰:', e) } const movedPoints = safeMovedPointsWithFallback(roofId, canvas) - // console.log("movedPoints:::", movedPoints); + // logger.log("movedPoints:::", movedPoints); // movedPoints๋ฅผ roof ๊ฒฝ๊ณ„๋กœ ํ™•์žฅ // ์ด๋ฒˆ ์ด๋™์—์„œ ์‹ค์ œ๋กœ ๋ณ€๊ฒฝ๋œ ํฌ์ธํŠธ๋งŒ ๊ตฌ๋ถ„ํ•˜์—ฌ, ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ์ถ•๋งŒ roof ๊ฒฝ๊ณ„๋กœ ํ™•์žฅ @@ -1122,8 +1128,8 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { if (SK_INPUT_USE_WALL_BASELINE_DIRECT) { changRoofLinePoints = orderedBaseLinePoints.map((p) => ({ x: p.x, y: p.y })) roofLineContactPoints = orderedBaseLinePoints.map((p) => ({ x: p.x, y: p.y })) - console.log('[v1 SK_INPUT] wall.baseLines ์ง์ ‘ ์‚ฌ์šฉ (45ยฐ ํ™•์žฅ/movedPoints ๋ฌด์‹œ):', - changRoofLinePoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`).join(' ')) + // logger.log('[v1 SK_INPUT] wall.baseLines ์ง์ ‘ ์‚ฌ์šฉ (45ยฐ ํ™•์žฅ/movedPoints ๋ฌด์‹œ):', + // changRoofLinePoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`).join(' ')) // [v1 ํ‰ํ–‰์˜ค๋ฒ„ corner ํก์ˆ˜ 2026-04-29] // ํ‰ํ–‰์˜ค๋ฒ„ ์‹œ OVER_GUARD ๊ฐ€ ์ธ์ ‘ baseLine ๊ธธ์ด๋ฅผ OVER_EPS(0.5) ๋งŒํผ๋งŒ ๋‚จ๊น€. @@ -1156,23 +1162,23 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { kept = next } if (absorbedAll.length > 0 && kept.length >= 3) { - console.log(`[v1 SK_INPUT ํ‰ํ–‰ํก์ˆ˜] ${absorbedAll.length}๊ฐœ corner ์ œ๊ฑฐ:\n ${absorbedAll.join('\n ')}`) - console.log(`[v1 SK_INPUT ํ‰ํ–‰ํก์ˆ˜] ์ตœ์ข… ${kept.length}๊ฐœ corner:`, - kept.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`).join(' ')) + // logger.log(`[v1 SK_INPUT ํ‰ํ–‰ํก์ˆ˜] ${absorbedAll.length}๊ฐœ corner ์ œ๊ฑฐ:\n ${absorbedAll.join('\n ')}`) + // logger.log(`[v1 SK_INPUT ํ‰ํ–‰ํก์ˆ˜] ์ตœ์ข… ${kept.length}๊ฐœ corner:`, + // kept.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`).join(' ')) changRoofLinePoints = kept roofLineContactPoints = kept.map((p) => ({ x: p.x, y: p.y })) } } - console.log('[DEBUG] changRoofLinePoints(์ตœ์ข… SK์ž…๋ ฅ):', changRoofLinePoints.map((p, i) => `[${i}] (${Math.round(p.x)},${Math.round(p.y)})`)) + // logger.log('[DEBUG] changRoofLinePoints(์ตœ์ข… SK์ž…๋ ฅ):', changRoofLinePoints.map((p, i) => `[${i}] (${Math.round(p.x)},${Math.round(p.y)})`)) // ์ขŒํ‘œ ์œ ํšจ์„ฑ ๊ฒ€์ฆ const invalidPoints = changRoofLinePoints.filter((p, i) => p == null || isNaN(p.x) || isNaN(p.y) || !isFinite(p.x) || !isFinite(p.y) ) if (invalidPoints.length > 0) { - console.error('[SK_ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ์ขŒํ‘œ ๋ฐœ๊ฒฌ:', invalidPoints) - console.error('[SK_ERROR] ์ „์ฒด ์ขŒํ‘œ:', JSON.stringify(changRoofLinePoints)) + logger.error('[SK_ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ์ขŒํ‘œ ๋ฐœ๊ฒฌ:', invalidPoints) + logger.error('[SK_ERROR] ์ „์ฒด ์ขŒํ‘œ:', JSON.stringify(changRoofLinePoints)) } // ์ธ์ ‘ ์ค‘๋ณต์  ๊ฒ€์ถœ (๊ฑฐ๋ฆฌ < 0.5) @@ -1181,7 +1187,7 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { const next = changRoofLinePoints[(i + 1) % changRoofLinePoints.length] const dist = Math.hypot(next.x - curr.x, next.y - curr.y) if (dist < 0.5) { - console.warn(`[SK_WARN] ์ธ์ ‘ ์ค‘๋ณต์ : [${i}](${Math.round(curr.x)},${Math.round(curr.y)}) โ†” [${(i + 1) % changRoofLinePoints.length}](${Math.round(next.x)},${Math.round(next.y)}) dist=${dist.toFixed(3)}`) + logger.warn(`[SK_WARN] ์ธ์ ‘ ์ค‘๋ณต์ : [${i}](${Math.round(curr.x)},${Math.round(curr.y)}) โ†” [${(i + 1) % changRoofLinePoints.length}](${Math.round(next.x)},${Math.round(next.y)}) dist=${dist.toFixed(3)}`) } } @@ -1192,7 +1198,7 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { const next = changRoofLinePoints[(i + 1) % changRoofLinePoints.length] const cross = (curr.x - prev.x) * (next.y - prev.y) - (curr.y - prev.y) * (next.x - prev.x) if (Math.abs(cross) < 1.0) { - console.warn(`[SK_WARN] ์ผ์ง์„  ์ : [${i}](${Math.round(curr.x)},${Math.round(curr.y)}) cross=${cross.toFixed(3)}`) + logger.warn(`[SK_WARN] ์ผ์ง์„  ์ : [${i}](${Math.round(curr.x)},${Math.round(curr.y)}) cross=${cross.toFixed(3)}`) } } @@ -1200,14 +1206,14 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { let isOverDetected = false const origPoints = roof.points || [] const n = changRoofLinePoints.length - console.log('[SK_OVER_DEBUG] origPoints:', origPoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) - console.log('[SK_OVER_DEBUG] changRoofLinePoints:', changRoofLinePoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) - console.log('[SK_OVER_DEBUG] ๋ณ€๊ฒฝ๋œ ์ :', changRoofLinePoints.map((p, i) => { - const o = origPoints[i] - if (!o) return null - const dist = Math.hypot(p.x - o.x, p.y - o.y) - return dist > 1 ? `[${i}] dx=${Math.round(p.x - o.x)} dy=${Math.round(p.y - o.y)}` : null - }).filter(Boolean)) + // logger.log('[SK_OVER_DEBUG] origPoints:', origPoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) + // logger.log('[SK_OVER_DEBUG] changRoofLinePoints:', changRoofLinePoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) + // logger.log('[SK_OVER_DEBUG] ๋ณ€๊ฒฝ๋œ ์ :', changRoofLinePoints.map((p, i) => { + // const o = origPoints[i] + // if (!o) return null + // const dist = Math.hypot(p.x - o.x, p.y - o.y) + // return dist > 1 ? `[${i}] dx=${Math.round(p.x - o.x)} dy=${Math.round(p.y - o.y)}` : null + // }).filter(Boolean)) if (origPoints.length === n && n >= 3) { for (let i = 0; i < n; i++) { const prevOrig = origPoints[(i - 1 + n) % n] @@ -1222,9 +1228,9 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { if (crossOrig * crossNew < 0) { isOverDetected = true - console.error(`[SK_OVER] ๊ผญ์ง“์ [${i}] ๋ฐฉํ–ฅ ๋’ค์ง‘ํž˜! cross: ${crossOrig.toFixed(1)} โ†’ ${crossNew.toFixed(1)}`) - console.error(`[SK_OVER] ์›๋ณธ: [${(i-1+n)%n}](${Math.round(prevOrig.x)},${Math.round(prevOrig.y)}) โ†’ [${i}](${Math.round(currOrig.x)},${Math.round(currOrig.y)}) โ†’ [${(i+1)%n}](${Math.round(nextOrig.x)},${Math.round(nextOrig.y)})`) - console.error(`[SK_OVER] ๋ณ€๊ฒฝ: [${(i-1+n)%n}](${Math.round(prevNew.x)},${Math.round(prevNew.y)}) โ†’ [${i}](${Math.round(currNew.x)},${Math.round(currNew.y)}) โ†’ [${(i+1)%n}](${Math.round(nextNew.x)},${Math.round(nextNew.y)})`) + logger.error(`[SK_OVER] ๊ผญ์ง“์ [${i}] ๋ฐฉํ–ฅ ๋’ค์ง‘ํž˜! cross: ${crossOrig.toFixed(1)} โ†’ ${crossNew.toFixed(1)}`) + logger.error(`[SK_OVER] ์›๋ณธ: [${(i-1+n)%n}](${Math.round(prevOrig.x)},${Math.round(prevOrig.y)}) โ†’ [${i}](${Math.round(currOrig.x)},${Math.round(currOrig.y)}) โ†’ [${(i+1)%n}](${Math.round(nextOrig.x)},${Math.round(nextOrig.y)})`) + logger.error(`[SK_OVER] ๋ณ€๊ฒฝ: [${(i-1+n)%n}](${Math.round(prevNew.x)},${Math.round(prevNew.y)}) โ†’ [${i}](${Math.round(currNew.x)},${Math.round(currNew.y)}) โ†’ [${(i+1)%n}](${Math.round(nextNew.x)},${Math.round(nextNew.y)})`) } } } @@ -1251,10 +1257,10 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { ) if (corrected && corrected.length >= 3) { skeletonInputPoints = corrected - console.log('[SK_OVER] ๋ณด์ • ํฌ์ธํŠธ ์ ์šฉ:', skeletonInputPoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) + // logger.log('[SK_OVER] ๋ณด์ • ํฌ์ธํŠธ ์ ์šฉ:', skeletonInputPoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) } } catch (e) { - console.error('[SK_OVER] ๋ณด์ • ์‹คํŒจ โ†’ fallback:', e) + logger.error('[SK_OVER] ๋ณด์ • ์‹คํŒจ โ†’ fallback:', e) } } @@ -1264,14 +1270,14 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { try { // SkeletonBuilder๋Š” ๋‹ซํžˆ์ง€ ์•Š์€ ํด๋ฆฌ๊ณค์„ ๊ธฐ๋Œ€ํ•˜๋ฏ€๋กœ ๋งˆ์ง€๋ง‰ ์  ์ œ๊ฑฐ geoJSONPolygon.pop() - console.log('[SkeletonBuilder] geoJSONPolygon:', JSON.stringify(geoJSONPolygon, null, 2)) - console.log('[SkeletonBuilder] ๊ผญ์ง“์  ์ˆ˜:', geoJSONPolygon.length) + // logger.log('[SkeletonBuilder] geoJSONPolygon:', JSON.stringify(geoJSONPolygon, null, 2)) + // logger.log('[SkeletonBuilder] ๊ผญ์ง“์  ์ˆ˜:', geoJSONPolygon.length) const skeleton = SkeletonBuilder.BuildFromGeoJSON([[geoJSONPolygon]]) // ์Šค์ผˆ๋ ˆํ†ค ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‚ด๋ถ€์„  ์ƒ์„ฑ roof.innerLines = roof.innerLines || [] roof.innerLines = createInnerLinesFromSkeleton(roofId, canvas, skeleton, textMode, isOverDetected, changRoofLinePoints) - //console.log("roofInnerLines:::", roof.innerLines); + //logger.log("roofInnerLines:::", roof.innerLines); // [v1 helper 2026-04-29 B ๊ฒฝ๋กœ] SK ์ž…๋ ฅ (offset ํ™•์žฅ baseLine ๋˜๋Š” movedPoints) ๊ณผ roofLinePoints ๊ฐ„ // gap ์„ ์‹œ๊ฐํ™”. innerLines ์™€ ๋ณ„๊ฐœ collection. ๊ธฐ์กด working code (processInBoth ๋“ฑ) ๋ฌด์˜ํ–ฅ. @@ -1318,20 +1324,20 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { // [LP-TRACE] ์‹ค์ œ ์ €์žฅ๋˜๋Š” lastPoints[7] โ€” ์ด ๊ฐ’์ด ๋‹ค์Œ ์ด๋™์˜ prevLast[7] try { const _p7 = rawMovedFull[7] - console.log(`[LP-TRACE] lastPoints.save [7]=(${_p7?.x?.toFixed(1)},${_p7?.y?.toFixed(1)}) len=${rawMovedFull.length}`) + // logger.log(`[LP-TRACE] lastPoints.save [7]=(${_p7?.x?.toFixed(1)},${_p7?.y?.toFixed(1)}) len=${rawMovedFull.length}`) } catch (_e) {} - console.log('[skeleton] lastPoints ์ €์žฅ: raw ํ’€ ๊ธธ์ด', rawMovedFull.length, '์ ') + // logger.log('[skeleton] lastPoints ์ €์žฅ: raw ํ’€ ๊ธธ์ด', rawMovedFull.length, '์ ') } else { canvas.skeleton.lastPoints = roofLineContactPoints } canvas.set('skeleton', cleanSkeleton) canvas.renderAll() - //console.log('skeleton rendered.', canvas) + //logger.log('skeleton rendered.', canvas) } catch (e) { - console.error('[SK_ERROR] ์Šค์ผˆ๋ ˆํ†ค ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:', e) - console.error('[SK_ERROR] ์ž…๋ ฅ ์ขŒํ‘œ:', changRoofLinePoints.map((p, i) => `[${i}] (${p.x.toFixed(2)},${p.y.toFixed(2)})`)) - console.error('[SK_ERROR] geoJSON:', JSON.stringify(geoJSONPolygon)) + logger.error('[SK_ERROR] ์Šค์ผˆ๋ ˆํ†ค ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:', e) + logger.error('[SK_ERROR] ์ž…๋ ฅ ์ขŒํ‘œ:', changRoofLinePoints.map((p, i) => `[${i}] (${p.x.toFixed(2)},${p.y.toFixed(2)})`)) + logger.error('[SK_ERROR] geoJSON:', JSON.stringify(geoJSONPolygon)) // [๋ณดํ˜ธ] ์˜ˆ์™ธ ์‹œ ์ƒํƒœ ํ”Œ๋ž˜๊ทธ๋งŒ ๋ณต๊ตฌํ•˜๊ณ , ๊ธฐ์กด SK/innerLines๋Š” ์œ ์ง€ํ•œ๋‹ค. // โ†’ ์‚ฌ์šฉ์ž๊ฐ€ ํž˜๋“ค๊ฒŒ ์ถ”๊ฐ€ํ•œ ๋ผ์ธ๋“ค์ด ์ˆœ๊ฐ„์ ์œผ๋กœ ์‚ฌ๋ผ์ง€๋Š” ํ˜„์ƒ ๋ฐฉ์ง€. if (canvas.skeletonStates) { @@ -1357,12 +1363,12 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { export const drawSkeletonRidgeRoofFromBaseLines = (roofId, canvas, textMode) => { const roof = canvas?.getObjects().find((o) => o.id === roofId) if (!roof) { - console.warn('[SK_OVER_FN] roof ์—†์Œ โ†’ ์ค‘๋‹จ') + logger.warn('[SK_OVER_FN] roof ์—†์Œ โ†’ ์ค‘๋‹จ') return } const wall = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.WALL && obj.attributes?.roofId === roofId) if (!wall || !wall.baseLines?.length) { - console.warn('[SK_OVER_FN] wall/baseLines ์—†์Œ โ†’ ์ค‘๋‹จ') + logger.warn('[SK_OVER_FN] wall/baseLines ์—†์Œ โ†’ ์ค‘๋‹จ') return } @@ -1372,22 +1378,22 @@ export const drawSkeletonRidgeRoofFromBaseLines = (roofId, canvas, textMode) => const bl = wall.baseLines[i] const planeSize = bl?.attributes?.planeSize ?? Infinity if (planeSize < 1) { - console.log(`[SK_OVER_FN] baseLines[${i}] SHOULDER_ABSORBED skip (planeSize=${planeSize})`) + // logger.log(`[SK_OVER_FN] baseLines[${i}] SHOULDER_ABSORBED skip (planeSize=${planeSize})`) continue } if (bl == null || !isFinite(bl.x1) || !isFinite(bl.y1)) { - console.warn(`[SK_OVER_FN] baseLines[${i}] invalid coord โ†’ skip`) + logger.warn(`[SK_OVER_FN] baseLines[${i}] invalid coord โ†’ skip`) continue } rawPoints.push({ x: bl.x1, y: bl.y1 }) } if (rawPoints.length < 3) { - console.warn(`[SK_OVER_FN] ์œ ํšจ baseLines ๋์  ${rawPoints.length} < 3 โ†’ ์ค‘๋‹จ`) + logger.warn(`[SK_OVER_FN] ์œ ํšจ baseLines ๋์  ${rawPoints.length} < 3 โ†’ ์ค‘๋‹จ`) return } - console.log('[SK_OVER_FN] wall.baseLines polygon:', rawPoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) + // logger.log('[SK_OVER_FN] wall.baseLines polygon:', rawPoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) // skeletonPoints ๋งˆํ‚น (์˜ค๋ฒ„ ์ขŒํ‘œ ๊ทธ๋Œ€๋กœ) roof.skeletonPoints = rawPoints.map((p) => ({ x: p.x, y: p.y })) @@ -1397,8 +1403,8 @@ export const drawSkeletonRidgeRoofFromBaseLines = (roofId, canvas, textMode) => try { geoJSONPolygon.pop() - console.log('[SK_OVER_FN] geoJSONPolygon:', JSON.stringify(geoJSONPolygon, null, 2)) - console.log('[SK_OVER_FN] ๊ผญ์ง“์  ์ˆ˜:', geoJSONPolygon.length) + // logger.log('[SK_OVER_FN] geoJSONPolygon:', JSON.stringify(geoJSONPolygon, null, 2)) + // logger.log('[SK_OVER_FN] ๊ผญ์ง“์  ์ˆ˜:', geoJSONPolygon.length) const skeleton = SkeletonBuilder.BuildFromGeoJSON([[geoJSONPolygon]]) roof.innerLines = roof.innerLines || [] @@ -1431,8 +1437,8 @@ export const drawSkeletonRidgeRoofFromBaseLines = (roofId, canvas, textMode) => canvas.set('skeleton', cleanSkeleton) canvas.renderAll() } catch (e) { - console.error('[SK_OVER_FN] ์Šค์ผˆ๋ ˆํ†ค ์ƒ์„ฑ ์˜ค๋ฅ˜:', e) - console.error('[SK_OVER_FN] ์ž…๋ ฅ ์ขŒํ‘œ:', rawPoints.map((p, i) => `[${i}] (${p.x.toFixed(2)},${p.y.toFixed(2)})`)) + logger.error('[SK_OVER_FN] ์Šค์ผˆ๋ ˆํ†ค ์ƒ์„ฑ ์˜ค๋ฅ˜:', e) + logger.error('[SK_OVER_FN] ์ž…๋ ฅ ์ขŒํ‘œ:', rawPoints.map((p, i) => `[${i}] (${p.x.toFixed(2)},${p.y.toFixed(2)})`)) if (canvas.skeletonStates) canvas.skeletonStates[roofId] = false } } @@ -1571,7 +1577,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver if (rotIdx > 0) { wallLines = [...wallLines.slice(rotIdx), ...wallLines.slice(0, rotIdx)] if (process.env.NEXT_PUBLIC_RUN_MODE === 'local') { - console.log('[WL-ROT-FIX] wall.lines rotated by', -rotIdx, 'to align with wall.baseLines[0].startPoint') + // logger.log('[WL-ROT-FIX] wall.lines rotated by', -rotIdx, 'to align with wall.baseLines[0].startPoint') } } } @@ -1717,33 +1723,33 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver // [PAIR-DIAG] local only โ€” top in ์‹œ ํŽ˜์–ด ์ธ๋ฑ์Šค ์–ด๊ธ‹๋‚จ ์ง„๋‹จ (2026-04-30) // 1) ๊ธธ์ด ๋ฏธ์Šค๋งค์น˜ / 2) _keyOfEdge ์ถฉ๋Œ / 3) raw ๋‹จ๊ณ„ 1:1 ๋ฌด๋„ˆ์ง ๊ฐ€๋ ค๋‚ด๊ธฐ ์œ„ํ•จ if (process.env.NEXT_PUBLIC_RUN_MODE === 'local') { - console.log('[PAIR-DIAG] lengths', { - roofLines: roofLines.length, - wallLines: wallLines.length, - wallBaseLines: wall.baseLines.length, - }) + // logger.log('[PAIR-DIAG] lengths', { + // roofLines: roofLines.length, + // wallLines: wallLines.length, + // wallBaseLines: wall.baseLines.length, + // }) const _seen = new Map() roofLines.forEach((l, i) => { const k = _keyOfEdge(l) - if (_seen.has(k)) console.warn(`[PAIR-DIAG] key collision: idx ${_seen.get(k)} vs ${i} key=${k}`) + if (_seen.has(k)) logger.warn(`[PAIR-DIAG] key collision: idx ${_seen.get(k)} vs ${i} key=${k}`) else _seen.set(k, i) }) roofLines.forEach((rl, i) => { const wl = wallLines[i] const wb = wall.baseLines[i] - console.log(`[PAIR-DIAG] raw#${i}`, - `roof=(${Math.round(rl.x1)},${Math.round(rl.y1)})โ†’(${Math.round(rl.x2)},${Math.round(rl.y2)})`, - wl ? `wall=(${Math.round(wl.x1)},${Math.round(wl.y1)})โ†’(${Math.round(wl.x2)},${Math.round(wl.y2)})` : 'wall=โˆ…', - wb ? `base=(${Math.round(wb.x1)},${Math.round(wb.y1)})โ†’(${Math.round(wb.x2)},${Math.round(wb.y2)})` : 'base=โˆ…') + // logger.log(`[PAIR-DIAG] raw#${i}`, + // `roof=(${Math.round(rl.x1)},${Math.round(rl.y1)})โ†’(${Math.round(rl.x2)},${Math.round(rl.y2)})`, + // wl ? `wall=(${Math.round(wl.x1)},${Math.round(wl.y1)})โ†’(${Math.round(wl.x2)},${Math.round(wl.y2)})` : 'wall=โˆ…', + // wb ? `base=(${Math.round(wb.x1)},${Math.round(wb.y1)})โ†’(${Math.round(wb.x2)},${Math.round(wb.y2)})` : 'base=โˆ…') }) } // [MOVE-TRACE] ์ง„์ž… ์Šค๋ƒ…์ƒท (ํ•„์š” ์‹œ ์ฃผ์„ ํ•ด์ œ) - // console.log('[MOVE-TRACE] === getMoveUpDownLine entry ===', + // logger.log('[MOVE-TRACE] === getMoveUpDownLine entry ===', // 'moveUpDown=', roof.moveUpDown, // 'moveFlowLine=', roof.moveFlowLine) // sortWallBaseLines.forEach((bl, i) => { - // console.log(`[MOVE-TRACE] sortWallBaseLines[${i}]`, + // logger.log(`[MOVE-TRACE] sortWallBaseLines[${i}]`, // `(${Math.round(bl?.x1)},${Math.round(bl?.y1)})โ†’(${Math.round(bl?.x2)},${Math.round(bl?.y2)})`, // 'vs sortWallLines', // `(${Math.round(sortWallLines[i]?.x1)},${Math.round(sortWallLines[i]?.y1)})โ†’(${Math.round(sortWallLines[i]?.x2)},${Math.round(sortWallLines[i]?.y2)})`, @@ -1768,11 +1774,11 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const l = `${lineAxis}${k}` // y1 or x1 const otherL = `${lineAxis}${otherK}` // y2 or x2 - console.log(`${condition}::::isStartEnd:::::`) + // logger.log(`${condition}::::isStartEnd:::::`) // [MOVE-TRACE] index 0 ์ „์šฉ (ํ•„์š” ์‹œ ์ฃผ์„ ํ•ด์ œ) // if (index === 0) { - // console.log('[MOVE-TRACE] processInStartEnd idx=0', + // logger.log('[MOVE-TRACE] processInStartEnd idx=0', // 'condition=', condition, // 'isStart=', isStart, // 'inSign=', inSign, @@ -1820,7 +1826,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const wbL = wallBaseLine[l] const wL = wallLine[l] if (Math.abs(wbL - wL) < 0.1) { - console.warn(`โš ๏ธ [${condition.toUpperCase()}_${isStart ? 'START' : 'END'}] Line crossing detected! newPoint:`, newPointM, 'pLine:', pLineM) + logger.warn(`โš ๏ธ [${condition.toUpperCase()}_${isStart ? 'START' : 'END'}] Line crossing detected! newPoint:`, newPointM, 'pLine:', pLineM) if (moveAxis === 'x') { getAddLine({ x: pLineM, y: pLineL }, { x: newPointM, y: pLineL }, 'green') getAddLine({ x: newPointM, y: pLineL }, ePoint, 'pink') @@ -1852,12 +1858,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const prevAbsorbed = (prevBL?.attributes?.planeSize ?? Infinity) < 10 const nextAbsorbed = (nextBL?.attributes?.planeSize ?? Infinity) < 10 if (prevAbsorbed || nextAbsorbed) { - console.log(`โญ๏ธ [pair#${index}] ${condition} HIP_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) + // logger.log(`โญ๏ธ [pair#${index}] ${condition} HIP_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) return } } - console.log(`${condition}::::isStartEnd (both):::::`) + // logger.log(`${condition}::::isStartEnd (both):::::`) // ์›๋ณธ ๊ธฐ์ค€: moveDistY = abs(roofLine.y - wallBaseLine.y), moveDistX = abs(roofLine.x - wallBaseLine.x) const moveDistY1 = Math.abs(Big(roofLine.y1).minus(wallBaseLine.y1).toNumber()) @@ -1883,7 +1889,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const createHipLine = (fromPoint, toPoint) => { const createdLine = getAddLine(fromPoint, toPoint, 'orange', 'extensionLine') - console.log('extensionLine created - length:', createdLine.length, 'coords:', { x1: createdLine.x1, y1: createdLine.y1, x2: createdLine.x2, y2: createdLine.y2 }) + // logger.log('extensionLine created - length:', createdLine.length, 'coords:', { x1: createdLine.x1, y1: createdLine.y1, x2: createdLine.x2, y2: createdLine.y2 }) createdLine.set({ name: LINE_TYPE.SUBLINE.HIP, lineName: LINE_TYPE.SUBLINE.HIP, stroke: '#FF0000', strokeWidth: 2 }) createdLine.attributes = { ...createdLine.attributes, type: LINE_TYPE.SUBLINE.HIP } innerLines.push(createdLine) @@ -1909,7 +1915,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver target = { x: roofLine.x1 + xSignStart * dist, y: roofLine.y1 } } if (!__isNearSkVertex(target)) { - console.log(`โญ๏ธ [pair#${index}] ${condition} HIP_SKIP_VERTEX_MISS start target=(${target.x.toFixed(1)},${target.y.toFixed(1)})`) + // logger.log(`โญ๏ธ [pair#${index}] ${condition} HIP_SKIP_VERTEX_MISS start target=(${target.x.toFixed(1)},${target.y.toFixed(1)})`) } else { createHipLine(sPoint, target) } @@ -1925,7 +1931,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver target = { x: roofLine.x2 + xSignEnd * dist, y: roofLine.y2 } } if (!__isNearSkVertex(target)) { - console.log(`โญ๏ธ [pair#${index}] ${condition} HIP_SKIP_VERTEX_MISS end target=(${target.x.toFixed(1)},${target.y.toFixed(1)})`) + // logger.log(`โญ๏ธ [pair#${index}] ${condition} HIP_SKIP_VERTEX_MISS end target=(${target.x.toFixed(1)},${target.y.toFixed(1)})`) } else { createHipLine(ePoint, target) } @@ -1982,17 +1988,17 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver // }) // } - console.log('wallBaseLines', wall.baseLines) + // logger.log('wallBaseLines', wall.baseLines) //wall.baseLine์€ ์›€์ง์ธ๋ผ์ธ let movedLines = [] // ์กฐ๊ฑด์— ๋งž๋Š” ๋ผ์ธ๋“ค๋งŒ ํ•„ํ„ฐ๋ง - const validWallLines = [...wallLines].sort((a, b) => a.idx - b.idx).filter((wallLine, index) => wallLine.idx - 1 === index) + const validWallLines = [...wallLines].sort((a, b) => a.idx - b.idx).filter((wallLine, index) => wallLine.idx - 1 === index); // [ASI-FIX 2026-05-15] ์„ธ๋ฏธ์ฝœ๋ก  ์—†์œผ๋ฉด ๋‹ค์Œ ์ค„ `(` ๊ฐ€ ํ•จ์ˆ˜ํ˜ธ์ถœ๋กœ ๋ฌถ์—ฌ .filter(...)(...) โ†’ "is not a function" โ†’ SK ๋นŒ๋“œ ์‹คํŒจ + ํ™•์žฅ์„  ๋ˆ„๋ฝ - console.log('', sortRoofLines, sortWallLines, sortWallBaseLines); + // logger.log('', sortRoofLines, sortWallLines, sortWallBaseLines); - (sortWallLines.length === sortWallBaseLines.length && sortWallBaseLines.length > 3) && + ;(sortWallLines.length === sortWallBaseLines.length && sortWallBaseLines.length > 3) && sortWallLines.forEach((wallLine, index) => { const roofLine = sortRoofLines[index] @@ -2003,7 +2009,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver // ์ž„๊ณ„ = 10: useMovementSetting OVER_EPS=0.5 (canvas unit) ์‹œ ์ž”์—ฌ ๊ธธ์ด=0.5 โ†’ planeSize=5 (calcLinePlaneSize ร— 10). // ์•ˆ์ „๋งˆ์ง„ ๋‘๊ณ  < 10 ์œผ๋กœ ์ฐจ๋‹จ. ์ •์ƒ baseLine ์€ ๋ณดํ†ต 50+ ์ด๋ฏ€๋กœ false positive ์—†์Œ. if (!wallBaseLine || (wallBaseLine.attributes?.planeSize ?? Infinity) < 10) { - console.log(`โญ๏ธ [pair#${index}] SHOULDER_ABSORBED skip: wb=(${Math.round(wallBaseLine?.x1)},${Math.round(wallBaseLine?.y1)})โ†’(${Math.round(wallBaseLine?.x2)},${Math.round(wallBaseLine?.y2)}) planeSize=${wallBaseLine?.attributes?.planeSize}`) + // logger.log(`โญ๏ธ [pair#${index}] SHOULDER_ABSORBED skip: wb=(${Math.round(wallBaseLine?.x1)},${Math.round(wallBaseLine?.y1)})โ†’(${Math.round(wallBaseLine?.x2)},${Math.round(wallBaseLine?.y2)}) planeSize=${wallBaseLine?.attributes?.planeSize}`) return } @@ -2016,16 +2022,16 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const wallIdWl = wallLine.attributes?.wallId ?? wallLine.attributes?.idx ?? '?' const wallIdWb = wallBaseLine.attributes?.wallId ?? wallBaseLine.attributes?.idx ?? '?' const dirMatch = wlDir === wbDir - console.log(`๐Ÿ”— [pair#${index}] wallId wl=${wallIdWl} wb=${wallIdWb} ${dirMatch ? '' : 'โš ๏ธDIR_MISMATCH '}wlDir=${wlDir} wbDir=${wbDir} wl=(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})โ†’(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)}) wb=(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})โ†’(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`) + // logger.log(`๐Ÿ”— [pair#${index}] wallId wl=${wallIdWl} wb=${wallIdWb} ${dirMatch ? '' : 'โš ๏ธDIR_MISMATCH '}wlDir=${wlDir} wbDir=${wbDir} wl=(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})โ†’(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)}) wb=(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})โ†’(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`) } //roofline ์™ธ๊ณฝ์„  ์„ค์ • - // console.log('index::::', index) - // console.log('roofLine:', roofLine.x1, roofLine.y1, roofLine.x2, roofLine.y2) - // console.log('wallLine:', wallLine.x1, wallLine.y1, wallLine.x2, wallLine.y2) - // console.log('wallBaseLine:', wallBaseLine.x1, wallBaseLine.y1, wallBaseLine.x2, wallBaseLine.y2) - // console.log('isSamePoint result:', isSameLine2(wallBaseLine, wallLine)) + // logger.log('index::::', index) + // logger.log('roofLine:', roofLine.x1, roofLine.y1, roofLine.x2, roofLine.y2) + // logger.log('wallLine:', wallLine.x1, wallLine.y1, wallLine.x2, wallLine.y2) + // logger.log('wallBaseLine:', wallBaseLine.x1, wallBaseLine.y1, wallBaseLine.x2, wallBaseLine.y2) + // logger.log('isSamePoint result:', isSameLine2(wallBaseLine, wallLine)) const isCollinear = (l1, l2, tolerance = 0.1) => { const slope1 = Math.abs(l1.x2 - l1.x1) < tolerance ? Infinity : (l1.y2 - l1.y1) / (l1.x2 - l1.x1) @@ -2060,7 +2066,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver } for (const [p1, p2] of __cgPairs) { if (Math.hypot(p2.x - p1.x, p2.y - p1.y) < 0.5) continue - console.log(`๐ŸŽฏ [corner-gap eaveHelpLine] index=${index} p1=(${Math.round(p1.x)},${Math.round(p1.y)})โ†’p2=(${Math.round(p2.x)},${Math.round(p2.y)}) wallLine=(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})โ†’(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)}) wallBaseLine=(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})โ†’(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`) + // logger.log(`๐ŸŽฏ [corner-gap eaveHelpLine] index=${index} p1=(${Math.round(p1.x)},${Math.round(p1.y)})โ†’p2=(${Math.round(p2.x)},${Math.round(p2.y)}) wallLine=(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})โ†’(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)}) wallBaseLine=(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})โ†’(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`) const __cgLine = new QLine([p1.x, p1.y, p2.x, p2.y], { parentId: roof.id, fontSize: roof.fontSize, @@ -2106,21 +2112,21 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver movedLines.push({ index, p1, p2 }) // [์ง„๋‹จ] eaveHelpLine ์ƒ์„ฑ ์ถ”์ : ์–ด๋А index/์–ด๋А wallBaseLine-wallLine ์Œ์—์„œ ๋งŒ๋“ค์–ด์ง€๋Š”์ง€ - console.log('๐ŸŽฏ [getAddLine]', { - index, - lineType, - p1: { x: Math.round(p1.x), y: Math.round(p1.y) }, - p2: { x: Math.round(p2.x), y: Math.round(p2.y) }, - wallLine: `(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})โ†’(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)})`, - wallBaseLine: `(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})โ†’(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`, - wallBaseLen: Math.round(Math.hypot(wallBaseLine.x2 - wallBaseLine.x1, wallBaseLine.y2 - wallBaseLine.y1)), - }) + // logger.log('๐ŸŽฏ [getAddLine]', { + // index, + // lineType, + // p1: { x: Math.round(p1.x), y: Math.round(p1.y) }, + // p2: { x: Math.round(p2.x), y: Math.round(p2.y) }, + // wallLine: `(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})โ†’(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)})`, + // wallBaseLine: `(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})โ†’(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`, + // wallBaseLen: Math.round(Math.hypot(wallBaseLine.x2 - wallBaseLine.x1, wallBaseLine.y2 - wallBaseLine.y1)), + // }) const dx = Math.abs(p2.x - p1.x); const dy = Math.abs(p2.y - p1.y); const isDiagonal = dx > 0.5 && dy > 0.5; // x, y ๋ณ€ํ™”๊ฐ€ ๋ชจ๋‘ ์žˆ์œผ๋ฉด ๋Œ€๊ฐ์„  - //console.log("mergeLines:::::::", mergeLines); + //logger.log("mergeLines:::::::", mergeLines); const line = new QLine([p1.x, p1.y, p2.x, p2.y], { parentId: roof.id, fontSize: roof.fontSize, @@ -2184,7 +2190,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const midY = (wallBaseLine.y1 + wallBaseLine.y2) / 2 const isH = Math.abs(wallBaseLine.y1 - wallBaseLine.y2) < 0.5 const isV = Math.abs(wallBaseLine.x1 - wallBaseLine.x2) < 0.5 - console.log(`๐Ÿงญ [getSelectLinePosition ๊ฒฐ๊ณผ] idx=${index} position=${mLine.position} orient=${mLine.orientation} mid=(${Math.round(midX)},${Math.round(midY)}) isH=${isH} isV=${isV} wallLine=(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})โ†’(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)}) wallBaseLine=(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})โ†’(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`) + // logger.log(`๐Ÿงญ [getSelectLinePosition ๊ฒฐ๊ณผ] idx=${index} position=${mLine.position} orient=${mLine.orientation} mid=(${Math.round(midX)},${Math.round(midY)}) isH=${isH} isV=${isV} wallLine=(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})โ†’(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)}) wallBaseLine=(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})โ†’(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`) } if (getOrientation(roofLine) === 'vertical') { @@ -2241,7 +2247,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const prevAbsorbed = (prevBL?.attributes?.planeSize ?? Infinity) < 10 const nextAbsorbed = (nextBL?.attributes?.planeSize ?? Infinity) < 10 if (prevAbsorbed || nextAbsorbed) { - console.log(`โญ๏ธ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) + // logger.log(`โญ๏ธ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) return } } @@ -2300,7 +2306,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver } if (isStartEnd.end) { - console.log('left_out::::isStartEnd:::::', isStartEnd) + // logger.log('left_out::::isStartEnd:::::', isStartEnd) const moveDist = Big(wallLine.x1).minus(wallBaseLine.x1).abs().toNumber() const aStartY = Big(roofLine.y2).plus(moveDist).toNumber() const bStartY = Big(wallLine.y2).plus(moveDist).toNumber() @@ -2365,13 +2371,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const prevAbsorbed = (prevBL?.attributes?.planeSize ?? Infinity) < 10 const nextAbsorbed = (nextBL?.attributes?.planeSize ?? Infinity) < 10 if (prevAbsorbed || nextAbsorbed) { - console.log(`โญ๏ธ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) + // logger.log(`โญ๏ธ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) return } } if (isStartEnd.start) { - console.log('right_out::::isStartEnd:::::', isStartEnd) + // logger.log('right_out::::isStartEnd:::::', isStartEnd) const moveDist = Big(wallLine.x1).minus(wallBaseLine.x1).abs().toNumber() const aStartY = Big(roofLine.y1).plus(moveDist).toNumber() const bStartY = Big(wallLine.y1).plus(moveDist).toNumber() @@ -2526,13 +2532,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const prevAbsorbed = (prevBL?.attributes?.planeSize ?? Infinity) < 10 const nextAbsorbed = (nextBL?.attributes?.planeSize ?? Infinity) < 10 if (prevAbsorbed || nextAbsorbed) { - console.log(`โญ๏ธ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) + // logger.log(`โญ๏ธ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) return } } if (isStartEnd.start) { - console.log('top_out isStartEnd:::::::', isStartEnd) + // logger.log('top_out isStartEnd:::::::', isStartEnd) const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() const aStartX = Big(roofLine.x1).plus(moveDist).toNumber() const bStartX = Big(wallLine.x1).plus(moveDist).toNumber() @@ -2585,7 +2591,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver getAddLine(newPStart, newPEnd, 'red') } if (isStartEnd.end) { - console.log('isStartEnd:::::', isStartEnd) + // logger.log('isStartEnd:::::', isStartEnd) const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() const aStartX = Big(roofLine.x2).minus(moveDist).toNumber() const bStartX = Big(wallLine.x2).minus(moveDist).toNumber() @@ -2637,7 +2643,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver getAddLine(newPStart, newPEnd, 'red') } } else if (condition === 'bottom_out') { - console.log('bottom_out isStartEnd:::::::', isStartEnd) + // logger.log('bottom_out isStartEnd:::::::', isStartEnd) // [SHOULDER-ADJACENT _out] ์ธ์ ‘ pair ํก์ˆ˜ ์‹œ ํ˜„์žฌ pair ์˜ ๋์ ์€ ์ƒˆ SK ํ† ํด๋กœ์ง€์—์„œ dissolve ๋จ. // roof.points ์˜› ์ฝ”๋„ˆ ์ฐธ์กฐ โ†’ updateAndAddLine NOT FOUND + phantom eaveHelpLine ์™ธ๋ถ€ ์ถœํ˜„. @@ -2649,13 +2655,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const prevAbsorbed = (prevBL?.attributes?.planeSize ?? Infinity) < 10 const nextAbsorbed = (nextBL?.attributes?.planeSize ?? Infinity) < 10 if (prevAbsorbed || nextAbsorbed) { - console.log(`โญ๏ธ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) + // logger.log(`โญ๏ธ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) return } } if (isStartEnd.start) { - console.log('isStartEnd:::::::', isStartEnd) + // logger.log('isStartEnd:::::::', isStartEnd) const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() const aStartX = Big(roofLine.x1).minus(moveDist).toNumber() const bStartX = Big(wallLine.x1).minus(moveDist).toNumber() @@ -2708,7 +2714,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver } if (isStartEnd.end) { - console.log('isStartEnd:::::', isStartEnd) + // logger.log('isStartEnd:::::', isStartEnd) const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() const aStartX = Big(roofLine.x2).plus(moveDist).toNumber() const bStartX = Big(wallLine.x2).plus(moveDist).toNumber() @@ -2845,12 +2851,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver if (toRemove.length > 0) { if (process.env.NEXT_PUBLIC_RUN_MODE === 'local') { - console.log( - `[B์•ˆ cull] dead-end ridge ์ œ๊ฑฐ ${toRemove.length}๊ฐœ`, - toRemove.map( - (l) => `(${Math.round(l.x1)},${Math.round(l.y1)})โ†’(${Math.round(l.x2)},${Math.round(l.y2)})` - ) - ) + // logger.log( + // `[B์•ˆ cull] dead-end ridge ์ œ๊ฑฐ ${toRemove.length}๊ฐœ`, + // toRemove.map( + // (l) => `(${Math.round(l.x1)},${Math.round(l.y1)})โ†’(${Math.round(l.x2)},${Math.round(l.y2)})` + // ) + // ) } toRemove.forEach((line) => { canvas.remove(line) @@ -2909,7 +2915,7 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) { if(!outerLine) { outerLine = findMatchingLine(edgeResult.Polygon, roof, roof.points); - console.log('Has matching line:', outerLine); + // logger.log('Has matching line:', outerLine); //if(outerLine === null) return } // [hip pitch fallback 2026-04-30] @@ -2956,7 +2962,7 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) { return false } - // console.log('๐Ÿ“ [processEavesEdge] face ๋ถ„์„:', { + // logger.log('๐Ÿ“ [processEavesEdge] face ๋ถ„์„:', { // hasOuterLine: !!outerLine, // outerLineType: outerLine?.attributes?.type, // pitch, @@ -2976,19 +2982,19 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) { // ํ™•์žฅ๋œ ์™ธ๊ณฝ์„ ์— ํ•ด๋‹นํ•˜๋Š” edge๋Š” ์Šคํ‚ต if (_isSkipOuter) { - // console.log(' [edge', i, '] SKIP(outerEdge)', { p1: {x: Math.round(p1.x), y: Math.round(p1.y)}, p2: {x: Math.round(p2.x), y: Math.round(p2.y)} }) + // logger.log(' [edge', i, '] SKIP(outerEdge)', { p1: {x: Math.round(p1.x), y: Math.round(p1.y)}, p2: {x: Math.round(p2.x), y: Math.round(p2.y)} }) continue } // ์ง€๋ถ• ๊ฒฝ๊ณ„์„ ๊ณผ ๊ต์ฐจ ํ™•์ธ ๋ฐ ํด๋ฆฌํ•‘ const clippedLine = clipLineToRoofBoundary(p1, p2, roof.lines, roof.moveSelectLine); - //console.log('clipped line', clippedLine.p1, clippedLine.p2); + //logger.log('clipped line', clippedLine.p1, clippedLine.p2); const isOuterLine = isOuterEdge(clippedLine.p1, clippedLine.p2, [edgeResult.Edge]) // const _dx = Math.abs(clippedLine.p2.x - clippedLine.p1.x) // const _dy = Math.abs(clippedLine.p2.y - clippedLine.p1.y) // const _lineType = (_dx < 0.5 && _dy > 0.5) ? 'โš ๏ธ์ˆ˜์ง' : (_dy < 0.5 && _dx > 0.5) ? '์ˆ˜ํ‰' : '๋Œ€๊ฐ' - // console.log(` [edge ${i}] ${_lineType} ${isOuterLine ? 'OUTER' : 'INNER'} (${Math.round(clippedLine.p1.x)},${Math.round(clippedLine.p1.y)})โ†’(${Math.round(clippedLine.p2.x)},${Math.round(clippedLine.p2.y)}) dx:${Math.round(_dx)} dy:${Math.round(_dy)}`) + // logger.log(` [edge ${i}] ${_lineType} ${isOuterLine ? 'OUTER' : 'INNER'} (${Math.round(clippedLine.p1.x)},${Math.round(clippedLine.p1.y)})โ†’(${Math.round(clippedLine.p2.x)},${Math.round(clippedLine.p2.y)}) dx:${Math.round(_dx)} dy:${Math.round(_dy)}`) addRawLine(roof.id, skeletonLines, clippedLine.p1, clippedLine.p2, 'ridge', '#1083E3', 4, pitch, isOuterLine, targetWallId); // } @@ -3068,7 +3074,7 @@ function logDeadEndLines(skeletonLines, roof) { const dy = Math.abs(line.p2.y - line.p1.y); if (dx < 0.5 && dy < 0.5) { - // console.log('โš ๏ธ [logDeadEndLines] ์ œ๋กœ ๊ธธ์ด:', { idx, p1: { x: Math.round(line.p1.x), y: Math.round(line.p1.y) } }); + // logger.log('โš ๏ธ [logDeadEndLines] ์ œ๋กœ ๊ธธ์ด:', { idx, p1: { x: Math.round(line.p1.x), y: Math.round(line.p1.y) } }); return; } @@ -3078,16 +3084,16 @@ function logDeadEndLines(skeletonLines, roof) { const p2Dead = deadEndVertices.has(k2); if (p1Dead || p2Dead) { - console.log('โš ๏ธ [logDeadEndLines] ์‚ญ์ œ ํ›„๋ณด:', { - idx, - p1: { x: Math.round(line.p1.x), y: Math.round(line.p1.y), deadEnd: p1Dead, isRoof: isRoofVertex(line.p1) }, - p2: { x: Math.round(line.p2.x), y: Math.round(line.p2.y), deadEnd: p2Dead, isRoof: isRoofVertex(line.p2) }, - dx: Math.round(dx), dy: Math.round(dy), - }); + // logger.log('โš ๏ธ [logDeadEndLines] ์‚ญ์ œ ํ›„๋ณด:', { + // idx, + // p1: { x: Math.round(line.p1.x), y: Math.round(line.p1.y), deadEnd: p1Dead, isRoof: isRoofVertex(line.p1) }, + // p2: { x: Math.round(line.p2.x), y: Math.round(line.p2.y), deadEnd: p2Dead, isRoof: isRoofVertex(line.p2) }, + // dx: Math.round(dx), dy: Math.round(dy), + // }); } }); - // console.log(`๐Ÿ” [logDeadEndLines] ์ „์ฒด: ${skeletonLines.length}, ์œ ๋‹ˆํฌ: ${uniqueLines.length}, dead end ๊ผญ์ง“์ : ${deadEndVertices.size}`); + // logger.log(`๐Ÿ” [logDeadEndLines] ์ „์ฒด: ${skeletonLines.length}, ์œ ๋‹ˆํฌ: ${uniqueLines.length}, dead end ๊ผญ์ง“์ : ${deadEndVertices.size}`); } function findMatchingLine(edgePolygon, roof, roofPoints) { @@ -3126,7 +3132,7 @@ function findMatchingLine(edgePolygon, roof, roofPoints) { function processGableEdge(edgeResult, baseLines, skeletonLines, selectBaseLine, lastSkeletonLines) { const edgePoints = edgeResult.Polygon.map(p => ({ x: p.X, y: p.Y })); //const polygons = createPolygonsFromSkeletonLines(skeletonLines, selectBaseLine); - //console.log("edgePoints::::::", edgePoints) + //logger.log("edgePoints::::::", edgePoints) // 1. Initialize processedLines with a deep copy of lastSkeletonLines let processedLines = [] // 1. ์ผ€๋ผ๋ฐ” ๋ฉด๊ณผ ๊ด€๋ จ๋œ ๋ถˆํ•„์š”ํ•œ ์Šค์ผˆ๋ ˆํ†ค ์„ ์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. @@ -3141,8 +3147,8 @@ function processGableEdge(edgeResult, baseLines, skeletonLines, selectBaseLine, } } - //console.log("skeletonLines::::::", skeletonLines) - //console.log("lastSkeletonLines", lastSkeletonLines) + //logger.log("skeletonLines::::::", skeletonLines) + //logger.log("lastSkeletonLines", lastSkeletonLines) // 2. Find common lines between skeletonLines and lastSkeletonLines skeletonLines.forEach(line => { @@ -3168,9 +3174,9 @@ function processGableEdge(edgeResult, baseLines, skeletonLines, selectBaseLine, // return !isEdgeLine; // }); - //console.log("skeletonLines::::::", skeletonLines); - //console.log("lastSkeletonLines", lastSkeletonLines); - //console.log("processedLines after filtering", processedLines); + //logger.log("skeletonLines::::::", skeletonLines); + //logger.log("lastSkeletonLines", lastSkeletonLines); + //logger.log("processedLines after filtering", processedLines); return processedLines; @@ -3242,8 +3248,8 @@ const calcOverCorrectedPointsSafe = (points, origPoints, lastPoints) => { changedNow[i] ? op : (lastPoints[i] ?? op) ) - console.log('[calcOverCorrectedPointsSafe] changedNow:', - changedNow.map((v, i) => v ? i : null).filter(v => v !== null)) + // logger.log('[calcOverCorrectedPointsSafe] changedNow:', + // changedNow.map((v, i) => v ? i : null).filter(v => v !== null)) return calcOverCorrectedPoints(points, virtualOrig) } @@ -3265,7 +3271,7 @@ const calcOverCorrectedPoints = (points, origPoints) => { const origDir = origPoints[movedIdx].x - origPoints[fixedIdx].x const newDir = skPoints[movedIdx].x - skPoints[fixedIdx].x if (origDir * newDir < 0) { - console.log(`[SK_OVER] ํด๋žจํ•‘: [${movedIdx}].x ${Math.round(skPoints[movedIdx].x)} โ†’ ${Math.round(skPoints[fixedIdx].x)}`) + // logger.log(`[SK_OVER] ํด๋žจํ•‘: [${movedIdx}].x ${Math.round(skPoints[movedIdx].x)} โ†’ ${Math.round(skPoints[fixedIdx].x)}`) skPoints[movedIdx].x = skPoints[fixedIdx].x didClamp = true } @@ -3275,7 +3281,7 @@ const calcOverCorrectedPoints = (points, origPoints) => { const origDir = origPoints[movedIdx].y - origPoints[fixedIdx].y const newDir = skPoints[movedIdx].y - skPoints[fixedIdx].y if (origDir * newDir < 0) { - console.log(`[SK_OVER] ํด๋žจํ•‘: [${movedIdx}].y ${Math.round(skPoints[movedIdx].y)} โ†’ ${Math.round(skPoints[fixedIdx].y)}`) + // logger.log(`[SK_OVER] ํด๋žจํ•‘: [${movedIdx}].y ${Math.round(skPoints[movedIdx].y)} โ†’ ${Math.round(skPoints[fixedIdx].y)}`) skPoints[movedIdx].y = skPoints[fixedIdx].y didClamp = true } @@ -3336,7 +3342,7 @@ const calcOverCorrectedPoints = (points, origPoints) => { // ๋ณด์ • ์‹คํŒจ ์‹œ(๊ผญ์ง“์  ๋ถ€์กฑ) ์›๋ณธ ๋ฐ˜ํ™˜ โ†’ ๊ธฐ์กด ๋™์ž‘ ๋ณด์žฅ if (cleaned.length >= 3) return cleaned if (deduped.length >= 3) return deduped - console.warn('[SK_OVER] calcOverCorrectedPoints: ๋ณด์ • ์‹คํŒจ โ†’ ์›๋ณธ ๋ฐ˜ํ™˜') + logger.warn('[SK_OVER] calcOverCorrectedPoints: ๋ณด์ • ์‹คํŒจ โ†’ ์›๋ณธ ๋ฐ˜ํ™˜') return points } @@ -3419,7 +3425,7 @@ function addRawLine(id, skeletonLines, p1, p2, lineType, color, width, pitch, is }; skeletonLines.push(newLine); - //console.log('skeletonLines', skeletonLines); + //logger.log('skeletonLines', skeletonLines); } /** @@ -4214,14 +4220,14 @@ function clipLineToRoofBoundary(p1, p2, roofLines, selectLine) { // p2๊ฐ€ ๋‹ค๊ฐํ˜• ๋‚ด๋ถ€์— ์žˆ๋Š”์ง€ ํ™•์ธ const p2Inside = isPointInsidePolygon(p2, roofLines); - //console.log('p1Inside:', p1Inside, 'p2Inside:', p2Inside); + //logger.log('p1Inside:', p1Inside, 'p2Inside:', p2Inside); // ๋‘ ์  ๋ชจ๋‘ ๋‚ด๋ถ€์— ์žˆ์œผ๋ฉด ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ if (p1Inside && p2Inside) { if(!selectLine || isDiagonal){ return { p1: clippedP1, p2: clippedP2 }; } - //console.log('ํ‰ํ–‰์„ ::', clippedP1, clippedP2) + //logger.log('ํ‰ํ–‰์„ ::', clippedP1, clippedP2) return { p1: clippedP1, p2: clippedP2 }; } @@ -4246,7 +4252,7 @@ function clipLineToRoofBoundary(p1, p2, roofLines, selectLine) { } } - //console.log('Found intersections:', intersections.length); + //logger.log('Found intersections:', intersections.length); // ๊ต์ฐจ์ ๋“ค์„ t ๊ฐ’์œผ๋กœ ์ •๋ ฌ intersections.sort((a, b) => a.t - b.t); @@ -4254,20 +4260,20 @@ function clipLineToRoofBoundary(p1, p2, roofLines, selectLine) { if (!p1Inside && !p2Inside) { // ๋‘ ์  ๋ชจ๋‘ ์™ธ๋ถ€์— ์žˆ๋Š” ๊ฒฝ์šฐ if (intersections.length >= 2) { - //console.log('Both outside, using intersection points'); + //logger.log('Both outside, using intersection points'); clippedP1.x = intersections[0].point.x; clippedP1.y = intersections[0].point.y; clippedP2.x = intersections[1].point.x; clippedP2.y = intersections[1].point.y; } else { - //console.log('Both outside, no valid intersections - returning original'); + //logger.log('Both outside, no valid intersections - returning original'); // ๊ต์ฐจ์ ์ด ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์œผ๋ฉด ์›๋ณธ ๋ฐ˜ํ™˜ return { p1: clippedP1, p2: clippedP2 }; } } else if (!p1Inside && p2Inside) { // p1์ด ์™ธ๋ถ€, p2๊ฐ€ ๋‚ด๋ถ€ if (intersections.length > 0) { - //console.log('p1 outside, p2 inside - moving p1 to intersection'); + //logger.log('p1 outside, p2 inside - moving p1 to intersection'); clippedP1.x = intersections[0].point.x; clippedP1.y = intersections[0].point.y; // p2๋Š” ์ด๋ฏธ ๋‚ด๋ถ€์— ์žˆ์œผ๋ฏ€๋กœ ์›๋ณธ ์œ ์ง€ @@ -4277,7 +4283,7 @@ function clipLineToRoofBoundary(p1, p2, roofLines, selectLine) { } else if (p1Inside && !p2Inside) { // p1์ด ๋‚ด๋ถ€, p2๊ฐ€ ์™ธ๋ถ€ if (intersections.length > 0) { - //console.log('p1 inside, p2 outside - moving p2 to intersection'); + //logger.log('p1 inside, p2 outside - moving p2 to intersection'); // p1์€ ์ด๋ฏธ ๋‚ด๋ถ€์— ์žˆ์œผ๋ฏ€๋กœ ์›๋ณธ ์œ ์ง€ clippedP1.x = p1.x; clippedP1.y = p1.y; @@ -4528,32 +4534,32 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => { const { testDistance = 10, epsilon = 0.5, debug = false } = options; if (!wall || !selectLine) { - if (debug) console.log('ERROR: wall ๋˜๋Š” selectLine์ด ์—†์Œ'); + // if (debug) logger.log('ERROR: wall ๋˜๋Š” selectLine์ด ์—†์Œ'); return { position: 'unknown', orientation: 'unknown', error: 'invalid_input' }; } // selectLine์˜ ์ขŒํ‘œ ์ถ”์ถœ const lineCoords = extractLineCoords(selectLine); if (!lineCoords.valid) { - if (debug) console.log('ERROR: selectLine ์ขŒํ‘œ๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Œ'); + // if (debug) logger.log('ERROR: selectLine ์ขŒํ‘œ๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Œ'); return { position: 'unknown', orientation: 'unknown', error: 'invalid_coords' }; } const { x1, y1, x2, y2 } = lineCoords; - //console.log('wall.points', wall.baseLines); + //logger.log('wall.points', wall.baseLines); for(const line of wall.baseLines) { - //console.log('line', line); + //logger.log('line', line); const basePoint = extractLineCoords(line); const { x1: bx1, y1: by1, x2: bx2, y2: by2 } = basePoint; - //console.log('x1, y1, x2, y2', bx1, by1, bx2, by2); + //logger.log('x1, y1, x2, y2', bx1, by1, bx2, by2); // ๊ฐ์ฒด ๋น„๊ต ๋Œ€์‹  ์ขŒํ‘œ๊ฐ’ ๋น„๊ต if (Math.abs(bx1 - x1) < 0.1 && Math.abs(by1 - y1) < 0.1 && Math.abs(bx2 - x2) < 0.1 && Math.abs(by2 - y2) < 0.1) { - //console.log('basePoint ์ผ์น˜!!!', basePoint); + //logger.log('basePoint ์ผ์น˜!!!', basePoint); } } @@ -4562,9 +4568,9 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => { const lineInfo = analyzeLineOrientation(x1, y1, x2, y2, epsilon); // if (debug) { - // console.log('=== getSelectLinePosition ==='); - // console.log('selectLine ์ขŒํ‘œ:', lineCoords); - // console.log('๋ผ์ธ ๋ฐฉํ–ฅ:', lineInfo.orientation); + // logger.log('=== getSelectLinePosition ==='); + // logger.log('selectLine ์ขŒํ‘œ:', lineCoords); + // logger.log('๋ผ์ธ ๋ฐฉํ–ฅ:', lineInfo.orientation); // } // ๋ผ์ธ์˜ ์ค‘์  @@ -4585,9 +4591,9 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => { const bottomIsInside = checkPointInPolygon(bottomTestPoint, wall); // if (debug) { - // console.log('์ˆ˜ํ‰์„  ํ…Œ์ŠคํŠธ:'); - // console.log(' ์œ„์ชฝ ํฌ์ธํŠธ:', topTestPoint, '-> ๋‚ด๋ถ€:', topIsInside); - // console.log(' ์•„๋ž˜์ชฝ ํฌ์ธํŠธ:', bottomTestPoint, '-> ๋‚ด๋ถ€:', bottomIsInside); + // logger.log('์ˆ˜ํ‰์„  ํ…Œ์ŠคํŠธ:'); + // logger.log(' ์œ„์ชฝ ํฌ์ธํŠธ:', topTestPoint, '-> ๋‚ด๋ถ€:', topIsInside); + // logger.log(' ์•„๋ž˜์ชฝ ํฌ์ธํŠธ:', bottomTestPoint, '-> ๋‚ด๋ถ€:', bottomIsInside); // } // top ์กฐ๊ฑด: ์œ„์ชฝ์ด ์™ธ๋ถ€, ์•„๋ž˜์ชฝ์ด ๋‚ด๋ถ€ @@ -4611,9 +4617,9 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => { const rightIsInside = checkPointInPolygon(rightTestPoint, wall); // if (debug) { - // console.log('์ˆ˜์ง์„  ํ…Œ์ŠคํŠธ:'); - // console.log(' ์™ผ์ชฝ ํฌ์ธํŠธ:', leftTestPoint, '-> ๋‚ด๋ถ€:', leftIsInside); - // console.log(' ์˜ค๋ฅธ์ชฝ ํฌ์ธํŠธ:', rightTestPoint, '-> ๋‚ด๋ถ€:', rightIsInside); + // logger.log('์ˆ˜์ง์„  ํ…Œ์ŠคํŠธ:'); + // logger.log(' ์™ผ์ชฝ ํฌ์ธํŠธ:', leftTestPoint, '-> ๋‚ด๋ถ€:', leftIsInside); + // logger.log(' ์˜ค๋ฅธ์ชฝ ํฌ์ธํŠธ:', rightTestPoint, '-> ๋‚ด๋ถ€:', rightIsInside); // } // left ์กฐ๊ฑด: ์™ผ์ชฝ์ด ์™ธ๋ถ€, ์˜ค๋ฅธ์ชฝ์ด ๋‚ด๋ถ€ @@ -4627,7 +4633,7 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => { } else { // ๋Œ€๊ฐ์„  - if (debug) console.log('๋Œ€๊ฐ์„ ์€ ์ง€์›ํ•˜์ง€ ์•Š์Œ'); + // if (debug) logger.log('๋Œ€๊ฐ์„ ์€ ์ง€์›ํ•˜์ง€ ์•Š์Œ'); return { position: 'unknown', orientation: 'diagonal', error: 'not_supported' }; } @@ -4647,7 +4653,7 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => { }; // if (debug) { - // console.log('์ตœ์ข… ๊ฒฐ๊ณผ:', result); + // logger.log('์ตœ์ข… ๊ฒฐ๊ณผ:', result); // } return result; @@ -4658,7 +4664,7 @@ const checkPointInPolygon = (point, wall) => { // 2. wall.baseLines๋ฅผ ์ด์šฉํ•œ Ray Casting Algorithm if (!wall.baseLines || !Array.isArray(wall.baseLines)) { - console.warn('wall.baseLines๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค'); + logger.warn('wall.baseLines๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค'); return false; } @@ -4801,7 +4807,7 @@ function pointToLineDistance(point, lineP1, lineP2) { const getOrientation = (line, eps = 0.1) => { if (!line) { - console.error('line ๊ฐ์ฒด๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค:', line); + logger.error('line ๊ฐ์ฒด๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค:', line); return null; // ๋˜๋Š” ์ ์ ˆํ•œ ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜ } @@ -4823,7 +4829,7 @@ const getOrientation = (line, eps = 0.1) => { if (dx < eps && dy < eps) return 'point'; return 'diagonal'; } catch (e) { - console.error('๋ฐฉํ–ฅ ๊ณ„์‚ฐ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:', e); + logger.error('๋ฐฉํ–ฅ ๊ณ„์‚ฐ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:', e); return null; } } @@ -4842,9 +4848,9 @@ export const processEaveHelpLines = (lines) => { const mergedHorizontal = mergeLines(horizontalLines, 'horizontal'); // ๊ฒฐ๊ณผ ํ™•์ธ์šฉ ๋กœ๊ทธ - console.log('Original lines:', lines.length); - console.log('Merged vertical:', mergedVertical.length); - console.log('Merged horizontal:', mergedHorizontal.length); + // logger.log('Original lines:', lines.length); + // logger.log('Merged vertical:', mergedVertical.length); + // logger.log('Merged horizontal:', mergedHorizontal.length); return [...mergedVertical, ...mergedHorizontal]; }; @@ -4887,7 +4893,7 @@ const mergeLines = (lines, direction) => { merged.push(current); // ๋ณ‘ํ•ฉ ๊ฒฐ๊ณผ ๋กœ๊ทธ - console.log(`Merged ${direction} lines:`, merged); + // logger.log(`Merged ${direction} lines:`, merged); return merged; }; @@ -4953,11 +4959,11 @@ function updateAndAddLine(innerLines, targetPoint) { if (!foundLine) { foundLine = findLineContainingPoint(innerLines, targetPoint); if (foundLine) { - console.log(`[MOVE-TRACE] HIP fallback matched: position=${targetPoint.position} target=(${targetPoint.x.toFixed(2)},${targetPoint.y.toFixed(2)}) โ†’ (${foundLine.x1.toFixed(1)},${foundLine.y1.toFixed(1)})โ†’(${foundLine.x2.toFixed(1)},${foundLine.y2.toFixed(1)}) type=${foundLine.attributes?.type||'?'}`) + // logger.log(`[MOVE-TRACE] HIP fallback matched: position=${targetPoint.position} target=(${targetPoint.x.toFixed(2)},${targetPoint.y.toFixed(2)}) โ†’ (${foundLine.x1.toFixed(1)},${foundLine.y1.toFixed(1)})โ†’(${foundLine.x2.toFixed(1)},${foundLine.y2.toFixed(1)}) type=${foundLine.attributes?.type||'?'}`) } } if (!foundLine) { - console.warn(`[updateAndAddLine] NOT FOUND position=${targetPoint.position} point=(${targetPoint.x.toFixed(2)},${targetPoint.y.toFixed(2)})`); + logger.warn(`[updateAndAddLine] NOT FOUND position=${targetPoint.position} point=(${targetPoint.x.toFixed(2)},${targetPoint.y.toFixed(2)})`); return [...innerLines]; } @@ -5110,7 +5116,7 @@ function isPointOnLineSegment2(point, lineStart, lineEnd, tolerance = 0.1) { const isOnSegment = Math.abs((dist1 + dist2) - lineLength) <= tolerance; if (isOnSegment) { - console.log(`์  (${px}, ${py})์€ ์„ ๋ถ„ [(${x1}, ${y1}), (${x2}, ${y2})] ์œ„์— ์žˆ์Šต๋‹ˆ๋‹ค.`); + // logger.log(`์  (${px}, ${py})์€ ์„ ๋ถ„ [(${x1}, ${y1}), (${x2}, ${y2})] ์œ„์— ์žˆ์Šต๋‹ˆ๋‹ค.`); } return isOnSegment; @@ -5147,6 +5153,12 @@ function isValleyVertex(targetPoint, connectedLine, allLines, isStartVertex) { let neighborLine = null; + // [valley degenerate skip 2026-05-13] + // SHOULDER_ABSORBED ๋กœ ์ธ์ ‘ baseLine ์ด zero-length ๊ฐ€ ๋˜๋ฉด (p1==p2) + // cross product ๊ฐ€ ๋…ธ์ด์ฆˆ ์ž‘์€ ๊ฐ’์ด ๋˜์–ด valley ํŒ์ •์ด ์šฐ์—ฐ์— ์ขŒ์šฐ๋จ + // (notch ์ผ€์ด์Šค: cross=59.15 โ†’ valley=true ์˜คํŒ โ†’ eaveHelpLine 4๊ฐœ ์ž˜๋ชป ์ƒ์„ฑ). + // length < 1.0 (๊ด€์ธก์น˜ 0.10, 0.50) ์ด๋ฉด skip ํ•˜๊ณ  ๊ทธ ๋„ˆ๋จธ ์ง„์งœ ์ธ์ ‘ ๋ผ์ธ ํƒ์ƒ‰. + const DEGEN_LEN = 1.0; if (isStartVertex) { neighborLine = allLines.find(l => { if (l === connectedLine) return false; @@ -5154,6 +5166,7 @@ function isValleyVertex(targetPoint, connectedLine, allLines, isStartVertex) { const ly1 = l.y1 ?? l.get?.('y1'); const lx2 = l.x2 ?? l.get?.('x2'); const ly2 = l.y2 ?? l.get?.('y2'); + if (Math.hypot(lx2 - lx1, ly2 - ly1) < DEGEN_LEN) return false; // [valley degenerate skip 2026-05-13] const end = l.endPoint || { x: lx2, y: ly2 }; return isSamePoint(end, targetPoint, tolerance); }); @@ -5164,6 +5177,7 @@ function isValleyVertex(targetPoint, connectedLine, allLines, isStartVertex) { const ly1 = l.y1 ?? l.get?.('y1'); const lx2 = l.x2 ?? l.get?.('x2'); const ly2 = l.y2 ?? l.get?.('y2'); + if (Math.hypot(lx2 - lx1, ly2 - ly1) < DEGEN_LEN) return false; // [valley degenerate skip 2026-05-13] const start = l.startPoint || { x: lx1, y: ly1 }; return isSamePoint(start, targetPoint, tolerance); }); @@ -5194,7 +5208,29 @@ function isValleyVertex(targetPoint, connectedLine, allLines, isStartVertex) { } const crossProduct = getTurnDirection(p1, p2, p3); - console.log('crossProduct:', crossProduct); + // [collinear continuation 2026-05-13] + // neighโ†’targetโ†’conn ์ด ๊ฑฐ์˜ ์ง์„ ์ด๋ฉด (๋…ธ์น˜ ๊ผฌ๋ฆฌ/์—ฐ์† segment) + // drift (round 0.1) ๋กœ cross ๊ฐ€ ๋ฏธ์„ธํ•œ ์–‘/์Œ์ˆ˜ ๋…ธ์ด์ฆˆ โ†’ valley ์˜คํŒ. + // |cross| / (|v1|*|v2|) = |sin(theta)|. < 0.05 (โ‰ˆ 2.9ยฐ) ๋ฉด ์ง์„  ์—ฐ์†์œผ๋กœ ๊ฐ„์ฃผ. + const v1len = Math.hypot(p2.x - p1.x, p2.y - p1.y); + const v2len = Math.hypot(p3.x - p2.x, p3.y - p2.y); + let collinearSkip = false; + if (v1len > 0 && v2len > 0) { + const sinTheta = Math.abs(crossProduct) / (v1len * v2len); + if (sinTheta < 0.05) collinearSkip = true; + } + // [valley diag 2026-05-13] L์ž(์ •์ƒ) vs notch(E-1~E-4 ๋ฐœ์ƒ) ๋น„๊ต์šฉ + const fmt = (p) => `(${Math.round(p.x)},${Math.round(p.y)})`; + const cLen = Math.hypot(clx2 - clx1, cly2 - cly1); + const nLen = Math.hypot(nlx2 - nlx1, nly2 - nly1); + // logger.log( + // `[VALLEY] ${isStartVertex ? 'START' : 'END'} target=${fmt(targetPoint)} ` + + // `conn=${fmt({x:clx1,y:cly1})}โ†’${fmt({x:clx2,y:cly2})}[len=${cLen.toFixed(2)}] ` + + // `neigh=${fmt({x:nlx1,y:nly1})}โ†’${fmt({x:nlx2,y:nly2})}[len=${nLen.toFixed(2)}] ` + + // `p1=${fmt(p1)} p2=${fmt(p2)} p3=${fmt(p3)} cross=${crossProduct.toFixed(2)} ` + + // `valley=${collinearSkip ? false : (crossProduct > 0)}${collinearSkip ? ' (collinear-skip)' : ''}` + // ); + if (collinearSkip) return false; return crossProduct > 0; }