Compare commits

..

12 Commits

Author SHA1 Message Date
dc080a8737 견적서에 계산기추가 2025-12-23 15:30:27 +09:00
59b65df1d6 abs() 수정 2025-12-23 15:21:29 +09:00
0ee7f6c590 계산기 2025-12-23 14:19:35 +09:00
3f9bc960b3 좌표삭제 2025-12-23 13:31:38 +09:00
f8cc93d54e 할당완료 2025-12-22 18:20:54 +09:00
54ac757ced 계산기 투입 2025-12-22 17:54:07 +09:00
2e3ae74a82 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2025-12-22 17:33:44 +09:00
6fdb0df00c 0.이하 처리 2025-12-22 17:31:45 +09:00
Jaeyoung Lee
83769c55ab Merge branch 'feature/design-remake' into dev 2025-12-22 14:45:58 +09:00
Jaeyoung Lee
8f1874c3cd 하단레벨 지붕선 작성로직 B타입 추가. 2025-12-22 14:45:23 +09:00
ab44be17fd 특이사항 제거 2025-12-22 13:19:09 +09:00
e3ce0dea3f 오브젝트 등록수정 2025-12-22 10:50:43 +09:00
8 changed files with 550 additions and 230 deletions

View File

@ -3,7 +3,7 @@ import { createCalculator } from '@/util/calc-utils'
import '@/styles/calc.scss' import '@/styles/calc.scss'
export const CalculatorInput = forwardRef( export const CalculatorInput = forwardRef(
({ value, onChange, label, options = {}, id, className = 'calculator-input', readOnly = false, placeholder, name='', disabled = false }, ref) => { ({ value, onChange, label, options = {}, id, className = 'calculator-input', readOnly = false, placeholder, name='', disabled = false, maxLength = 6 }, ref) => {
const [showKeypad, setShowKeypad] = useState(false) const [showKeypad, setShowKeypad] = useState(false)
const [displayValue, setDisplayValue] = useState(value || '0') const [displayValue, setDisplayValue] = useState(value || '0')
const [hasOperation, setHasOperation] = useState(false) const [hasOperation, setHasOperation] = useState(false)
@ -48,28 +48,56 @@ export const CalculatorInput = forwardRef(
const calculator = calculatorRef.current const calculator = calculatorRef.current
let newDisplayValue = '' let newDisplayValue = ''
// maxLength
if (maxLength > 0) {
const currentLength = (calculator.currentOperand || '').length + (calculator.previousOperand || '').length + (calculator.operation || '').length
if (currentLength >= maxLength) {
return
}
}
// 2 // 2
const shouldPreventInput = (value) => { const shouldPreventInput = (value) => {
const decimalParts = (value || '').split('.') if (!value) return false
const decimalParts = value.toString().split('.')
return decimalParts.length > 1 && decimalParts[1].length >= 2 return decimalParts.length > 1 && decimalParts[1].length >= 2
} }
//
const appendNumber = (current, num) => {
// maxLength
if (maxLength > 0 && (current + num).length > maxLength) {
return current
}
// 0 0
if (current === '0' && num !== '.' && !current.includes('.')) {
return num.toString()
}
// 0. 0
if (current === '0' && num === '0') {
return '0.'
}
return current + num
}
if (hasOperation) { if (hasOperation) {
// //
if (calculator.currentOperand === '0' || calculator.shouldResetDisplay) { if (calculator.shouldResetDisplay) {
calculator.currentOperand = num.toString() calculator.currentOperand = num.toString()
calculator.shouldResetDisplay = false calculator.shouldResetDisplay = false
}else if (!shouldPreventInput(calculator.currentOperand)) { // 2 } else if (num === '.') {
calculator.currentOperand = (calculator.currentOperand || '') + num if (!calculator.currentOperand.includes('.')) {
calculator.currentOperand = calculator.currentOperand || '0' + '.'
} }
// else { } else if (!shouldPreventInput(calculator.currentOperand)) {
// calculator.currentOperand = (calculator.currentOperand || '') + num calculator.currentOperand = appendNumber(calculator.currentOperand || '0', num)
// } }
newDisplayValue = calculator.previousOperand + calculator.operation + calculator.currentOperand newDisplayValue = calculator.previousOperand + calculator.operation + calculator.currentOperand
setDisplayValue(newDisplayValue) setDisplayValue(newDisplayValue)
} else { } else {
// //
if (displayValue === '0' || calculator.shouldResetDisplay) { if (calculator.shouldResetDisplay) {
calculator.currentOperand = num.toString() calculator.currentOperand = num.toString()
calculator.shouldResetDisplay = false calculator.shouldResetDisplay = false
newDisplayValue = calculator.currentOperand newDisplayValue = calculator.currentOperand
@ -77,8 +105,17 @@ export const CalculatorInput = forwardRef(
if (!hasOperation) { if (!hasOperation) {
onChange(calculator.currentOperand) onChange(calculator.currentOperand)
} }
} else if (!shouldPreventInput(calculator.currentOperand)) { // 2 } else if (num === '.') {
calculator.currentOperand = (calculator.currentOperand || '') + num if (!calculator.currentOperand.includes('.')) {
calculator.currentOperand = (calculator.currentOperand || '0') + '.'
newDisplayValue = calculator.currentOperand
setDisplayValue(newDisplayValue)
if (!hasOperation) {
onChange(newDisplayValue)
}
}
} else if (!shouldPreventInput(calculator.currentOperand)) {
calculator.currentOperand = appendNumber(calculator.currentOperand || '0', num)
newDisplayValue = calculator.currentOperand newDisplayValue = calculator.currentOperand
setDisplayValue(newDisplayValue) setDisplayValue(newDisplayValue)
if (!hasOperation) { if (!hasOperation) {
@ -382,6 +419,7 @@ export const CalculatorInput = forwardRef(
placeholder={placeholder} placeholder={placeholder}
autoComplete={'off'} autoComplete={'off'}
disabled={disabled} disabled={disabled}
maxLength={maxLength}
/> />
{showKeypad && !readOnly && ( {showKeypad && !readOnly && (

View File

@ -24,6 +24,7 @@ import { useSwal } from '@/hooks/useSwal'
import { QcastContext } from '@/app/QcastProvider' import { QcastContext } from '@/app/QcastProvider'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import {normalizeDigits, normalizeDecimal} from '@/util/input-utils' import {normalizeDigits, normalizeDecimal} from '@/util/input-utils'
import { CalculatorInput } from '@/components/common/input/CalcInput'
export default function Estimate({}) { export default function Estimate({}) {
const [uniqueData, setUniqueData] = useState([]) const [uniqueData, setUniqueData] = useState([])
const [handlePricingFlag, setHandlePricingFlag] = useState(false) const [handlePricingFlag, setHandlePricingFlag] = useState(false)
@ -1693,13 +1694,13 @@ export default function Estimate({}) {
{/* 파일첨부 끝 */} {/* 파일첨부 끝 */}
{/* 견적특이사항 시작 */} {/* 견적특이사항 시작 */}
<div className="table-box-title-wrap"> <div className="table-box-title-wrap">
<div className="title-wrap"> {/*<div className="title-wrap">*/}
<h3 className="product">{getMessage('estimate.detail.header.specialEstimate')}</h3> {/* <h3 className="product">{getMessage('estimate.detail.header.specialEstimate')}</h3>*/}
<div className="estimate-check-btn"> {/* <div className="estimate-check-btn">*/}
<button className={`estimate-arr-btn down mr5 ${hidden ? '' : 'on'}`} onClick={() => setHidden(false)}></button> {/* <button className={`estimate-arr-btn down mr5 ${hidden ? '' : 'on'}`} onClick={() => setHidden(false)}></button>*/}
<button className={`estimate-arr-btn up ${hidden ? 'on' : ''}`} onClick={() => setHidden(true)}></button> {/* <button className={`estimate-arr-btn up ${hidden ? 'on' : ''}`} onClick={() => setHidden(true)}></button>*/}
</div> {/* </div>*/}
</div> {/*</div>*/}
</div> </div>
{/* 견적 특이사항 코드영역시작 */} {/* 견적 특이사항 코드영역시작 */}
<div className={`estimate-check-wrap ${hidden ? 'hide' : ''}`}> <div className={`estimate-check-wrap ${hidden ? 'hide' : ''}`}>
@ -2106,15 +2107,27 @@ export default function Estimate({}) {
</td> </td>
<td> <td>
<div className="input-wrap" style={{ width: '100%' }}> <div className="input-wrap" style={{ width: '100%' }}>
<input {/*<input*/}
type="text" {/* type="text"*/}
className="input-light al-r" {/* className="input-light al-r"*/}
{/* value={convertNumberToPriceDecimal(item?.amount?.replaceAll(',', ''))}*/}
{/* disabled={item.itemId === '' || !!item?.paDispOrder}*/}
{/* onChange={(e) => {*/}
{/* onChangeAmount(e.target.value, item.dispOrder, index)*/}
{/* }}*/}
{/* maxLength={6}*/}
{/*/>*/}
<CalculatorInput
className={"input-light al-r"}
value={convertNumberToPriceDecimal(item?.amount?.replaceAll(',', ''))} value={convertNumberToPriceDecimal(item?.amount?.replaceAll(',', ''))}
disabled={item.itemId === '' || !!item?.paDispOrder} disabled={item.itemId === '' || !!item?.paDispOrder}
onChange={(e) => { onChange={(value) =>{
onChangeAmount(e.target.value, item.dispOrder, index) onChangeAmount(value, item.dispOrder, index)
}}
options={{
allowNegative: false,
allowDecimal: false
}} }}
maxLength={6}
/> />
</div> </div>
</td> </td>
@ -2122,9 +2135,32 @@ export default function Estimate({}) {
<td> <td>
<div className="form-flex-wrap"> <div className="form-flex-wrap">
<div className="input-wrap mr5"> <div className="input-wrap mr5">
<input {/*<input*/}
type="text" {/* type="text"*/}
className="input-light al-r" {/* className="input-light al-r"*/}
{/* value={*/}
{/* item.openFlg === '1'*/}
{/* ? 'OPEN'*/}
{/* : convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))*/}
{/* }*/}
{/* disabled={*/}
{/* item.openFlg === '1'*/}
{/* ? true*/}
{/* : estimateContextState?.estimateType === 'YJSS'*/}
{/* ? item?.paDispOrder*/}
{/* ? true*/}
{/* : item.pkgMaterialFlg !== '1'*/}
{/* : item.itemId === '' || !!item?.paDispOrder*/}
{/* ? true*/}
{/* : item.openFlg === '1'*/}
{/* }*/}
{/* onChange={(e) => {*/}
{/* onChangeSalePrice(e.target.value, item.dispOrder, index)*/}
{/* }}*/}
{/* maxLength={12}*/}
{/*/>*/}
<CalculatorInput
className={"input-light al-r"}
value={ value={
item.openFlg === '1' item.openFlg === '1'
? 'OPEN' ? 'OPEN'
@ -2140,13 +2176,15 @@ export default function Estimate({}) {
: item.itemId === '' || !!item?.paDispOrder : item.itemId === '' || !!item?.paDispOrder
? true ? true
: item.openFlg === '1' : item.openFlg === '1'
? true
: false
} }
onChange={(e) => { onChange={(value) =>{
onChangeSalePrice(e.target.value, item.dispOrder, index) onChangeSalePrice(value, item.dispOrder, index)
}} }}
maxLength={12} maxLength={12}
options={{
allowNegative: false,
allowDecimal: false
}}
/> />
</div> </div>
{item.openFlg === '1' && ( {item.openFlg === '1' && (

View File

@ -235,11 +235,23 @@ export default function Module({ setTabNum }) {
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="grid-select mr10"> <div className="grid-select mr10">
<input {/*<input*/}
type="text" {/* type="text"*/}
{/* className="input-origin block"*/}
{/* value={inputVerticalSnowCover}*/}
{/* onChange={(e) => setInputVerticalSnowCover(normalizeDecimal(e.target.value))}*/}
{/*/>*/}
<CalculatorInput
id=""
name=""
label=""
className="input-origin block" className="input-origin block"
value={inputVerticalSnowCover} value={inputVerticalSnowCover}
onChange={(e) => setInputVerticalSnowCover(normalizeDecimal(e.target.value))} onChange={(value) => setInputVerticalSnowCover(value)}
options={{
allowNegative: false,
allowDecimal: false
}}
/> />
</div> </div>
<span className="thin">cm</span> <span className="thin">cm</span>

View File

@ -638,7 +638,7 @@ export const Orientation = forwardRef((props, ref) => {
name="" name=""
label="" label=""
className="input-origin block" className="input-origin block"
value={inputInstallHeight} value={inputVerticalSnowCover}
onChange={(value) => handleChangeVerticalSnowCover(value)} onChange={(value) => handleChangeVerticalSnowCover(value)}
options={{ options={{
allowNegative: false, allowNegative: false,

View File

@ -2125,12 +2125,12 @@ export default function StuffDetail() {
{/* {...register('verticalSnowCover')}*/} {/* {...register('verticalSnowCover')}*/}
{/*/>*/} {/*/>*/}
<CalculatorInput <CalculatorInput
id="" id="verticalSnowCover"
name="" name="verticalSnowCover"
label="" label=""
className="input-light" className="input-light"
value={form.watch('verticalSnowCover') || ''} value={form.watch('verticalSnowCover') || ''}
onChange={()=>{}} onChange={(value) => form.setValue('verticalSnowCover', value)}
options={{ options={{
allowNegative: false, allowNegative: false,
allowDecimal: false allowDecimal: false
@ -2197,12 +2197,12 @@ export default function StuffDetail() {
{/* {...register('installHeight')}*/} {/* {...register('installHeight')}*/}
{/*/>*/} {/*/>*/}
<CalculatorInput <CalculatorInput
id="" id="installHeight"
name="" name="installHeight"
label="" label=""
className="input-light" className="input-light"
value={form.watch('installHeight') || ''} value={form.watch('installHeight') || ''}
onChange={()=>{}} onChange={(value) => form.setValue('installHeight', value)}
options={{ options={{
allowNegative: false, allowNegative: false,
allowDecimal: false allowDecimal: false
@ -2721,12 +2721,12 @@ export default function StuffDetail() {
{/* {...register('verticalSnowCover')}*/} {/* {...register('verticalSnowCover')}*/}
{/*/>*/} {/*/>*/}
<CalculatorInput <CalculatorInput
id="" id="verticalSnowCover"
name="" name="verticalSnowCover"
label="" label=""
className="input-light" className="input-light"
value={form.watch('verticalSnowCover') || ''} value={form.watch('verticalSnowCover') || ''}
onChange={()=>{}} onChange={(value) => form.setValue('verticalSnowCover', value)}
options={{ options={{
allowNegative: false, allowNegative: false,
allowDecimal: false allowDecimal: false
@ -2797,12 +2797,12 @@ export default function StuffDetail() {
{/*/>*/} {/*/>*/}
<CalculatorInput <CalculatorInput
id="" id="installHeight"
name="" name="installHeight"
label="" label=""
className="input-light" className="input-light"
value={form.watch('installHeight') || ''} value={form.watch('installHeight') || ''}
onChange={()=>{}} onChange={(value) => form.setValue('installHeight', value)}
options={{ options={{
allowNegative: false, allowNegative: false,
allowDecimal: false allowDecimal: false

View File

@ -473,7 +473,20 @@ export function useRoofAllocationSetting(id) {
// Filter out any eaveHelpLines that are already in lines to avoid duplicates // Filter out any eaveHelpLines that are already in lines to avoid duplicates
const existingEaveLineIds = new Set(roofBase.lines.map((line) => line.id)) const existingEaveLineIds = new Set(roofBase.lines.map((line) => line.id))
const newEaveLines = roofEaveHelpLines.filter((line) => !existingEaveLineIds.has(line.id)) const newEaveLines = roofEaveHelpLines.filter((line) => !existingEaveLineIds.has(line.id))
roofBase.lines = [...newEaveLines] // Filter out lines from roofBase.lines that share any points with newEaveLines
const linesToKeep = roofBase.lines.filter(roofLine => {
return !newEaveLines.some(eaveLine => {
// Check if any endpoint of roofLine matches any endpoint of eaveLine
return (
// Check if any endpoint of roofLine matches any endpoint of eaveLine
(Math.abs(roofLine.x1 - eaveLine.x1) < 0.1 && Math.abs(roofLine.y1 - eaveLine.y1) < 0.1) || // p1 matches p1
(Math.abs(roofLine.x2 - eaveLine.x2) < 0.1 && Math.abs(roofLine.y2 - eaveLine.y2) < 0.1) // p2 matches p2
);
});
});
// Combine remaining lines with newEaveLines
roofBase.lines = [...linesToKeep, ...newEaveLines];
} else { } else {
roofBase.lines = [...roofEaveHelpLines] roofBase.lines = [...roofEaveHelpLines]
} }

View File

@ -4848,6 +4848,9 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
const prevLineVector = { x: Math.sign(prevLine.x1 - prevLine.x2), y: Math.sign(prevLine.y1 - prevLine.y2) } const prevLineVector = { x: Math.sign(prevLine.x1 - prevLine.x2), y: Math.sign(prevLine.y1 - prevLine.y2) }
const nextLineVector = { x: Math.sign(nextLine.x1 - nextLine.x2), y: Math.sign(nextLine.y1 - nextLine.y2) } const nextLineVector = { x: Math.sign(nextLine.x1 - nextLine.x2), y: Math.sign(nextLine.y1 - nextLine.y2) }
const midX = (baseLine.x1 + baseLine.x2) / 2
const midY = (baseLine.y1 + baseLine.y2) / 2
const checkPoint = { x: midX + nextLineVector.x, y: midY + nextLineVector.y }
//반절마루 생성불가이므로 지붕선 분기를 해야하는지 확인 후 처리. //반절마루 생성불가이므로 지붕선 분기를 해야하는지 확인 후 처리.
if ( if (
prevLineVector.x === nextLineVector.x && prevLineVector.x === nextLineVector.x &&
@ -4855,12 +4858,24 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
baseLine.attributes.type === LINE_TYPE.WALLLINE.EAVES && baseLine.attributes.type === LINE_TYPE.WALLLINE.EAVES &&
(prevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE || nextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) (prevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE || nextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE)
) { ) {
downRoofGable.push({ currLine: baseLine, currIndex: index }) downRoofGable.push({ currLine: baseLine, currIndex: index, type: 'A' })
}
if (
(prevLineVector.x !== nextLineVector.x || prevLineVector.y !== nextLineVector.y) &&
checkWallPolygon.inPolygon(checkPoint) &&
prevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE &&
nextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE
) {
downRoofGable.push({ currLine: baseLine, currIndex: index, type: 'B' })
} }
}) })
const downRoofLines = [] // 하단지붕 파생 라인 처리후 innerLines에 추가. const downRoofLines = [] // 하단지붕 파생 라인 처리후 innerLines에 추가.
downRoofGable.forEach(({ currLine, currIndex }) => { //A타입 하단 지붕 prevLine과 nextLine이 같은 방향으로 가는 경우
downRoofGable
.filter((l) => l.type === 'A')
.forEach(({ currLine, currIndex }) => {
// 라인의 방향 // 라인의 방향
const currVector = { x: Math.sign(clamp01(currLine.x1 - currLine.x2)), y: Math.sign(clamp01(currLine.y1 - currLine.y2)) } const currVector = { x: Math.sign(clamp01(currLine.x1 - currLine.x2)), y: Math.sign(clamp01(currLine.y1 - currLine.y2)) }
@ -5048,6 +5063,204 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
} }
}) })
// B타입 하단지붕 prevLine과 nextLine의 방향이 반대인데 현재 라인이 지붕 안쪽으로 들어가는 모양.
downRoofGable
.filter((l) => l.type === 'B')
.forEach(({ currLine, currIndex }) => {
const checkLine = new fabric.Line([currLine.x1, currLine.y1, currLine.x2, currLine.y2], {
stroke: 'red',
strokeWidth: 4,
parentId: roofId,
name: 'check',
})
canvas.add(checkLine).renderAll()
// 라인의 방향
const currVector = { x: Math.sign(clamp01(currLine.x1 - currLine.x2)), y: Math.sign(clamp01(currLine.y1 - currLine.y2)) }
//어느쪽이 기준인지 확인.
//대각선 제외
if (currVector.x !== 0 && currVector.y !== 0) return
const prevLine = baseLines[(currIndex - 1 + baseLines.length) % baseLines.length]
const nextLine = baseLines[(currIndex + 1) % baseLines.length]
const prevVector = { x: Math.sign(clamp01(prevLine.x1 - prevLine.x2)), y: Math.sign(clamp01(prevLine.y1 - prevLine.y2)) }
const nextVector = { x: Math.sign(clamp01(nextLine.x1 - nextLine.x2)), y: Math.sign(clamp01(nextLine.y1 - nextLine.y2)) }
const minX = Math.min(currLine.x1, currLine.x2)
const maxX = Math.max(currLine.x1, currLine.x2)
const minY = Math.min(currLine.y1, currLine.y2)
const maxY = Math.max(currLine.y1, currLine.y2)
const midX = (currLine.x1 + currLine.x2) / 2
const midY = (currLine.y1 + currLine.y2) / 2
let ridgeFindVector = { x: nextVector.x, y: nextVector.y }
if (!checkWallPolygon.inPolygon({ x: midX, y: midY })) {
ridgeFindVector = { x: prevVector.x, y: prevVector.y }
}
// 마루를 따라 생성되어야 하는 지붕선 추가.
const oppLines = innerLines
.filter((l) => l.name === LINE_TYPE.SUBLINE.RIDGE)
.filter((line) => {
const lineVector = { x: Math.sign(clamp01(line.x1 - line.x2)), y: Math.sign(clamp01(line.y1 - line.y2)) }
let oppVector
if (currVector.x === 0) {
if (currVector.y === 1) {
oppVector = { x: Math.sign(clamp01(currLine.x1 - line.x1)), y: 0 }
} else if (currVector.y === -1) {
oppVector = { x: Math.sign(clamp01(line.x1 - currLine.x1)), y: 0 }
}
} else if (currVector.y === 0) {
if (currVector.x === 1) {
oppVector = { x: 0, y: Math.sign(clamp01(line.y1 - currLine.y1)) }
} else if (currVector.x === -1) {
oppVector = { x: 0, y: Math.sign(clamp01(currLine.y1 - line.y1)) }
}
}
const lineMinX = Math.min(line.x1, line.x2)
const lineMaxX = Math.max(line.x1, line.x2)
const lineMinY = Math.min(line.y1, line.y2)
const lineMaxY = Math.max(line.y1, line.y2)
const isInside = lineVector.y === 0 ? minX <= lineMinX && lineMaxX <= maxX : minY <= lineMinY && lineMaxY <= maxY
const rightOpp = ridgeFindVector.x === oppVector.x && ridgeFindVector.y === oppVector.y
return rightOpp && isInside
})
// 현재 라인의 지붕선 추가.
const currOffset = currLine.attributes.offset
const roofPoint = [
currLine.x1 + -nextVector.x * currOffset,
currLine.y1 + -nextVector.y * currOffset,
currLine.x2 + -nextVector.x * currOffset,
currLine.y2 + -nextVector.y * currOffset,
]
downRoofLines.push(drawRoofLine(roofPoint, canvas, roof, textMode))
// 현재 라인 좌표의 시작과 끝 포인트에서 직교하는 포인트와 라인을 찾는다
let orthogonalStartPoint,
orthogonalStartDistance = Infinity,
orthogonalStartLine
let orthogonalEndPoint,
orthogonalEndDistance = Infinity,
orthogonalEndLine
oppLines.forEach((line) => {
const checkLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { stroke: 'red', strokeWidth: 4, parentId: roofId, name: 'check' })
canvas.add(checkLine).renderAll()
if (currVector.x === 0) {
//세로선
// 시작포인트와 가까운 포인트의 길이
const lineStartDist =
Math.abs(currLine.y1 - line.y1) < Math.abs(currLine.y1 - line.y2) ? Math.abs(currLine.y1 - line.y1) : Math.abs(currLine.y1 - line.y2)
const lineStartPoint =
Math.abs(currLine.y1 - line.y1) < Math.abs(currLine.y1 - line.y2) ? { x: line.x1, y: currLine.y1 } : { x: line.x2, y: currLine.y1 }
if (lineStartDist < orthogonalStartDistance) {
orthogonalStartDistance = lineStartDist
orthogonalStartPoint = lineStartPoint
orthogonalStartLine = line
}
// 종료포인트와 가까운 포인트의 길이
const lineEndDist =
Math.abs(currLine.y2 - line.y1) < Math.abs(currLine.y2 - line.y2) ? Math.abs(currLine.y2 - line.y2) : Math.abs(currLine.y2 - line.y1)
const lineEndPoint =
Math.abs(currLine.y2 - line.y1) < Math.abs(currLine.y2 - line.y2) ? { x: line.x2, y: currLine.y2 } : { x: line.x1, y: currLine.y2 }
if (lineEndDist < orthogonalEndDistance) {
orthogonalEndDistance = lineEndDist
orthogonalEndPoint = lineEndPoint
orthogonalEndLine = line
}
} else if (currVector.y === 0) {
//가로선
// 시작포인트와 가까운 포인트의 길이
const lineStartDist =
Math.abs(currLine.x1 - line.x1) < Math.abs(currLine.x1 - line.x2) ? Math.abs(currLine.x1 - line.x1) : Math.abs(currLine.x1 - line.x2)
const lineStartPoint =
Math.abs(currLine.x1 - line.x1) < Math.abs(currLine.x1 - line.x2) ? { x: currLine.x1, y: line.y1 } : { x: currLine.x1, y: line.y2 }
if (lineStartDist < orthogonalStartDistance) {
orthogonalStartDistance = lineStartDist
orthogonalStartPoint = lineStartPoint
orthogonalStartLine = line
}
//종료포인트와 가까운 포인트의 길이
const lineEndDist =
Math.abs(currLine.x2 - line.x1) < Math.abs(currLine.x2 - line.x2) ? Math.abs(currLine.x2 - line.x1) : Math.abs(currLine.x2 - line.x2)
const lineEndPoint =
Math.abs(currLine.x2 - line.x1) < Math.abs(currLine.x2 - line.x2) ? { x: currLine.x2, y: line.y1 } : { x: currLine.x2, y: line.y2 }
if (lineEndDist < orthogonalEndDistance) {
orthogonalEndDistance = lineEndDist
orthogonalEndPoint = lineEndPoint
orthogonalEndLine = line
}
}
})
//직교 라인이 있는 경우
if (orthogonalStartLine !== undefined && orthogonalEndLine !== undefined) {
if (orthogonalStartLine === orthogonalEndLine) {
//직교 라인이 1개일때 처리
downRoofLines.push(
drawRoofLine([orthogonalStartPoint.x, orthogonalStartPoint.y, orthogonalEndPoint.x, orthogonalEndPoint.y], canvas, roof, textMode),
)
} else {
//직교 라인이 2개일때 처리
// 시작 라인 처리
const startDist1 = Math.sqrt(
Math.pow(orthogonalStartPoint.x - orthogonalStartLine.x1, 2) + Math.pow(orthogonalStartPoint.y - orthogonalStartLine.y1, 2),
)
const startDist2 = Math.sqrt(
Math.pow(orthogonalStartPoint.x - orthogonalStartLine.x2, 2) + Math.pow(orthogonalStartPoint.y - orthogonalStartLine.y2, 2),
)
const otherStartPoint =
startDist1 > startDist2
? { x: orthogonalStartLine.x1, y: orthogonalStartLine.y1 }
: { x: orthogonalStartLine.x2, y: orthogonalStartLine.y2 }
downRoofLines.push(
drawRoofLine([orthogonalStartPoint.x, orthogonalStartPoint.y, otherStartPoint.x, otherStartPoint.y], canvas, roof, textMode),
)
const endDist1 = Math.sqrt(
Math.pow(orthogonalEndPoint.x - orthogonalEndLine.x1, 2) + Math.pow(orthogonalEndPoint.y - orthogonalEndLine.y1, 2),
)
const endDist2 = Math.sqrt(
Math.pow(orthogonalEndPoint.x - orthogonalEndLine.x2, 2) + Math.pow(orthogonalEndPoint.y - orthogonalEndLine.y2, 2),
)
const otherEndPoint =
endDist1 > endDist2 ? { x: orthogonalEndLine.x1, y: orthogonalEndLine.y1 } : { x: orthogonalEndLine.x2, y: orthogonalEndLine.y2 }
downRoofLines.push(drawRoofLine([orthogonalEndPoint.x, orthogonalEndPoint.y, otherEndPoint.x, otherEndPoint.y], canvas, roof, textMode))
}
//지붕선(roofPoint)에서 직교포인트까지 연결하는 라인을 추가한다.
const orthogonalPoint1 = [roofPoint[0], roofPoint[1], orthogonalStartPoint.x, orthogonalStartPoint.y]
const orthogonalPoint2 = [roofPoint[2], roofPoint[3], orthogonalEndPoint.x, orthogonalEndPoint.y]
downRoofLines.push(
drawHipLine(
orthogonalPoint1,
canvas,
roof,
textMode,
null,
getDegreeByChon(currLine.attributes.pitch),
getDegreeByChon(currLine.attributes.pitch),
),
)
downRoofLines.push(
drawHipLine(
orthogonalPoint2,
canvas,
roof,
textMode,
null,
getDegreeByChon(currLine.attributes.pitch),
getDegreeByChon(currLine.attributes.pitch),
),
)
}
})
//추가된 하단 지붕 라인 innerLines에 추가. //추가된 하단 지붕 라인 innerLines에 추가.
innerLines.push(...downRoofLines) innerLines.push(...downRoofLines)
@ -5107,7 +5320,13 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
} }
startPoint = point startPoint = point
} }
innerLines.push(drawHipLine([startPoint.x, startPoint.y, currentLine.x2, currentLine.y2], canvas, roof, textMode, null, nextDegree, nextDegree)) if (splitPoint.length === 1) {
innerLines.push(drawRoofLine([startPoint.x, startPoint.y, currentLine.x2, currentLine.y2], canvas, roof, textMode))
} else {
innerLines.push(
drawHipLine([startPoint.x, startPoint.y, currentLine.x2, currentLine.y2], canvas, roof, textMode, null, nextDegree, nextDegree),
)
}
} else { } else {
innerLines.push(drawRoofLine([currentLine.x1, currentLine.y1, currentLine.x2, currentLine.y2], canvas, roof, textMode)) innerLines.push(drawRoofLine([currentLine.x1, currentLine.y1, currentLine.x2, currentLine.y2], canvas, roof, textMode))
} }

View File

@ -729,7 +729,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
}, },
}) })
coordinateText(line) //coordinateText(line)
canvas.add(line) canvas.add(line)
line.bringToFront() line.bringToFront()
canvas.renderAll() canvas.renderAll()
@ -844,7 +844,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
const nextIndex = (index + 1) % sortRoofLines.length const nextIndex = (index + 1) % sortRoofLines.length
const newLine = sortRoofLines[prevIndex] const newLine = sortRoofLines[nextIndex]
if (Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) { if (Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) {
if (inLine) { if (inLine) {
@ -871,7 +871,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
//newPStart.y = wallLine.y1; //newPStart.y = wallLine.y1;
//외곽 라인 그리기 //외곽 라인 그리기
const rLineM = Big(wallBaseLine.x2).minus(roofLine.x2).abs().toNumber() const rLineM = Big(wallBaseLine.x2).minus(roofLine.x2).abs().toNumber()
newPStart.y = Big(wallBaseLine.y1).minus(rLineM).abs().toNumber() newPStart.y = Big(wallBaseLine.y1).minus(rLineM).toNumber()
const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x })
if (inLine) { if (inLine) {
if (inLine.x2 > inLine.x1) { if (inLine.x2 > inLine.x1) {
@ -898,7 +898,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length; const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
const nextIndex = (index + 1) % sortRoofLines.length; const nextIndex = (index + 1) % sortRoofLines.length;
const newLine = sortRoofLines[nextIndex] const newLine = sortRoofLines[prevIndex]
if (Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) { if (Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) {
@ -927,7 +927,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
//외곽 라인 그리기 //외곽 라인 그리기
const rLineM = Big(wallBaseLine.x2).minus(roofLine.x2).abs().toNumber() const rLineM = Big(wallBaseLine.x2).minus(roofLine.x2).abs().toNumber()
newPEnd.y = Big(wallBaseLine.y2).plus(rLineM).abs().toNumber() newPEnd.y = Big(wallBaseLine.y2).plus(rLineM).toNumber()
const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x })
if (inLine) { if (inLine) {
if (inLine.x2 > inLine.x1) { if (inLine.x2 > inLine.x1) {
@ -1014,7 +1014,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
const nextIndex = (index + 1) % sortRoofLines.length const nextIndex = (index + 1) % sortRoofLines.length
const newLine = sortRoofLines[prevIndex] const newLine = sortRoofLines[nextIndex]
if (Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) { if (Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) {
if (inLine) { if (inLine) {
@ -1041,7 +1041,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
//newPStart.y = wallLine.y1; //newPStart.y = wallLine.y1;
//외곽 라인 그리기 //외곽 라인 그리기
const rLineM = Big(wallBaseLine.x1).minus(roofLine.x1).abs().toNumber() const rLineM = Big(wallBaseLine.x1).minus(roofLine.x1).abs().toNumber()
newPStart.y = Big(wallBaseLine.y1).plus(rLineM).abs().toNumber() newPStart.y = Big(wallBaseLine.y1).plus(rLineM).toNumber()
const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x })
if (inLine) { if (inLine) {
if (inLine.x2 > inLine.x1) { if (inLine.x2 > inLine.x1) {
@ -1068,7 +1068,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length; const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
const nextIndex = (index + 1) % sortRoofLines.length; const nextIndex = (index + 1) % sortRoofLines.length;
const newLine = sortRoofLines[nextIndex] const newLine = sortRoofLines[prevIndex]
if (inLine) { if (inLine) {
if (inLine.x2 < inLine.x1) { if (inLine.x2 < inLine.x1) {
@ -1096,7 +1096,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
//외곽 라인 그리기 //외곽 라인 그리기
const rLineM = Big(wallBaseLine.x2).minus(roofLine.x2).abs().toNumber() const rLineM = Big(wallBaseLine.x2).minus(roofLine.x2).abs().toNumber()
newPEnd.y = Big(wallBaseLine.y2).minus(rLineM).abs().toNumber() newPEnd.y = Big(wallBaseLine.y2).minus(rLineM).toNumber()
const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x })
if (inLine) { if (inLine) {
if (inLine.x2 > inLine.x1) { if (inLine.x2 > inLine.x1) {
@ -1203,7 +1203,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length; const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
const nextIndex = (index + 1) % sortRoofLines.length; const nextIndex = (index + 1) % sortRoofLines.length;
const newLine = sortRoofLines[prevIndex] const newLine = sortRoofLines[nextIndex]
if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) { if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) {
if (inLine) { if (inLine) {
@ -1229,7 +1229,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
} else { } else {
//외곽 라인 그리기 //외곽 라인 그리기
const rLineM = Big(wallBaseLine.y1).minus(roofLine.y1).abs().toNumber() const rLineM = Big(wallBaseLine.y1).minus(roofLine.y1).abs().toNumber()
newPStart.x = Big(wallBaseLine.x1).plus(rLineM).abs().toNumber() newPStart.x = Big(wallBaseLine.x1).plus(rLineM).toNumber()
const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x })
if (inLine) { if (inLine) {
if (inLine.y2 > inLine.y1) { if (inLine.y2 > inLine.y1) {
@ -1255,7 +1255,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length; const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
const nextIndex = (index + 1) % sortRoofLines.length; const nextIndex = (index + 1) % sortRoofLines.length;
const newLine = sortRoofLines[nextIndex] const newLine = sortRoofLines[prevIndex]
if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) { if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) {
if (inLine) { if (inLine) {
@ -1282,7 +1282,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
//newPEnd.x = wallLine.x2; //newPEnd.x = wallLine.x2;
//외곽 라인 그리기 //외곽 라인 그리기
const rLineM = Big(wallBaseLine.y2).minus(roofLine.y2).abs().toNumber() const rLineM = Big(wallBaseLine.y2).minus(roofLine.y2).abs().toNumber()
newPEnd.x = Big(wallBaseLine.x2).minus(rLineM).abs().toNumber() newPEnd.x = Big(wallBaseLine.x2).minus(rLineM).toNumber()
const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x })
if (inLine) { if (inLine) {
if (inLine.y1 > inLine.y2) { if (inLine.y1 > inLine.y2) {
@ -1365,7 +1365,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length; const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
const nextIndex = (index + 1) % sortRoofLines.length; const nextIndex = (index + 1) % sortRoofLines.length;
const newLine = sortRoofLines[prevIndex] const newLine = sortRoofLines[nextIndex]
if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) { if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) {
if (inLine) { if (inLine) {
@ -1392,7 +1392,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
//newPStart.x = wallLine.x1; //newPStart.x = wallLine.x1;
//외곽 라인 그리기 //외곽 라인 그리기
const rLineM = Big(wallBaseLine.y1).minus(roofLine.y1).abs().toNumber() const rLineM = Big(wallBaseLine.y1).minus(roofLine.y1).abs().toNumber()
newPStart.x = Big(wallBaseLine.x1).minus(rLineM).abs().toNumber() newPStart.x = Big(wallBaseLine.x1).minus(rLineM).toNumber()
const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x })
if (inLine) { if (inLine) {
if (inLine.y2 > inLine.y1) { if (inLine.y2 > inLine.y1) {
@ -1419,7 +1419,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length; const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
const nextIndex = (index + 1) % sortRoofLines.length; const nextIndex = (index + 1) % sortRoofLines.length;
const newLine = sortRoofLines[nextIndex] const newLine = sortRoofLines[prevIndex]
if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) { if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) {
if (inLine) { if (inLine) {
@ -1446,7 +1446,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
//newPEnd.x = wallLine.x2; //newPEnd.x = wallLine.x2;
//외곽 라인 그리기 //외곽 라인 그리기
const rLineM = Big(wallBaseLine.y2).minus(roofLine.y2).abs().toNumber() const rLineM = Big(wallBaseLine.y2).minus(roofLine.y2).abs().toNumber()
newPEnd.x = Big(wallBaseLine.x2).plus(rLineM).abs().toNumber() newPEnd.x = Big(wallBaseLine.x2).plus(rLineM).toNumber()
const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x })
if (inLine) { if (inLine) {
if (inLine.y1 > inLine.y2) { if (inLine.y1 > inLine.y2) {