Upload files to "/"
This commit is contained in:
commit
5ad717f132
69
config.json
Normal file
69
config.json
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"board": {
|
||||||
|
"name": "Naturheld 140",
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6
|
||||||
|
},
|
||||||
|
"walls": [
|
||||||
|
{
|
||||||
|
"name": "AZ 1",
|
||||||
|
"shape": "rectangular",
|
||||||
|
"width": 2.55,
|
||||||
|
"height": 2.65,
|
||||||
|
"heightFirstRow": 0.3,
|
||||||
|
"windows": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AZ 2",
|
||||||
|
"shape": "rectangular",
|
||||||
|
"width": 3.62,
|
||||||
|
"height": 2.5,
|
||||||
|
"heightFirstRow": 0.3,
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"left": 1.0,
|
||||||
|
"bottom": 0.72,
|
||||||
|
"width": 1.52,
|
||||||
|
"height": 1.57
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "GZ 1",
|
||||||
|
"shape": "rectangular",
|
||||||
|
"width": 3.60,
|
||||||
|
"height": 2.5,
|
||||||
|
"heightFirstRow": 0.3,
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"left": 1.06,
|
||||||
|
"bottom": 0.75,
|
||||||
|
"width": 1.52,
|
||||||
|
"height": 1.57
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "GZ 2",
|
||||||
|
"shape": "rectangular",
|
||||||
|
"width": 4.0,
|
||||||
|
"height": 2.65,
|
||||||
|
"heightFirstRow": 0.3,
|
||||||
|
"windows": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "KZ",
|
||||||
|
"shape": "triangular",
|
||||||
|
"width": 7.2,
|
||||||
|
"height": 4.22,
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"left": 2.83,
|
||||||
|
"bottom": 0.95,
|
||||||
|
"width": 1.6,
|
||||||
|
"height": 1.4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
742
visualization_data.js
Normal file
742
visualization_data.js
Normal file
@ -0,0 +1,742 @@
|
|||||||
|
const wallsData = [
|
||||||
|
{
|
||||||
|
"wall": {
|
||||||
|
"name": "AZ 1",
|
||||||
|
"width": 2.55,
|
||||||
|
"height": 2.65,
|
||||||
|
"shape": "rectangular",
|
||||||
|
"windows": []
|
||||||
|
},
|
||||||
|
"board": {
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6
|
||||||
|
},
|
||||||
|
"boards": [
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.3,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.3,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 0,
|
||||||
|
"width": 0.04999999999999982,
|
||||||
|
"height": 0.3,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0.3,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 0.3,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 0.3,
|
||||||
|
"width": 0.04999999999999982,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0.8999999999999999,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 0.8999999999999999,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 0.8999999999999999,
|
||||||
|
"width": 0.04999999999999982,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 1.5,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 1.5,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 1.5,
|
||||||
|
"width": 0.04999999999999982,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 2.0999999999999996,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.5500000000000003,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 2.0999999999999996,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.5500000000000003,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 2.0999999999999996,
|
||||||
|
"width": 0.04999999999999982,
|
||||||
|
"height": 0.5500000000000003,
|
||||||
|
"is_cut": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wall": {
|
||||||
|
"name": "AZ 2",
|
||||||
|
"width": 3.62,
|
||||||
|
"height": 2.5,
|
||||||
|
"shape": "rectangular",
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"left": 1.0,
|
||||||
|
"bottom": 0.72,
|
||||||
|
"width": 1.52,
|
||||||
|
"height": 1.57
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"board": {
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6
|
||||||
|
},
|
||||||
|
"boards": [
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.3,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.3,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.12,
|
||||||
|
"height": 0.3,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0.3,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 0.3,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 0.3,
|
||||||
|
"width": 1.12,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0.8999999999999999,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 0.8999999999999999,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 0.8999999999999999,
|
||||||
|
"width": 1.12,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 1.5,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 1.5,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 1.5,
|
||||||
|
"width": 1.12,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 2.0999999999999996,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.40000000000000036,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 2.0999999999999996,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.40000000000000036,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 2.0999999999999996,
|
||||||
|
"width": 1.12,
|
||||||
|
"height": 0.40000000000000036,
|
||||||
|
"is_cut": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wall": {
|
||||||
|
"name": "GZ 1",
|
||||||
|
"width": 3.6,
|
||||||
|
"height": 2.5,
|
||||||
|
"shape": "rectangular",
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"left": 1.06,
|
||||||
|
"bottom": 0.75,
|
||||||
|
"width": 1.52,
|
||||||
|
"height": 1.57
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"board": {
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6
|
||||||
|
},
|
||||||
|
"boards": [
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.3,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.3,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.1,
|
||||||
|
"height": 0.3,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0.3,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 0.3,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 0.3,
|
||||||
|
"width": 1.1,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0.8999999999999999,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 0.8999999999999999,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 0.8999999999999999,
|
||||||
|
"width": 1.1,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 1.5,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 1.5,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 1.5,
|
||||||
|
"width": 1.1,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 2.0999999999999996,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.40000000000000036,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 2.0999999999999996,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.40000000000000036,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 2.0999999999999996,
|
||||||
|
"width": 1.1,
|
||||||
|
"height": 0.40000000000000036,
|
||||||
|
"is_cut": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wall": {
|
||||||
|
"name": "GZ 2",
|
||||||
|
"width": 4.0,
|
||||||
|
"height": 2.65,
|
||||||
|
"shape": "rectangular",
|
||||||
|
"windows": []
|
||||||
|
},
|
||||||
|
"board": {
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6
|
||||||
|
},
|
||||||
|
"boards": [
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.3,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.3,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.3,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 3.75,
|
||||||
|
"y": 0,
|
||||||
|
"width": 0.25,
|
||||||
|
"height": 0.3,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0.3,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 0.3,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 0.3,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 3.75,
|
||||||
|
"y": 0.3,
|
||||||
|
"width": 0.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0.8999999999999999,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 0.8999999999999999,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 0.8999999999999999,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 3.75,
|
||||||
|
"y": 0.8999999999999999,
|
||||||
|
"width": 0.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 1.5,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 1.5,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 1.5,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 3.75,
|
||||||
|
"y": 1.5,
|
||||||
|
"width": 0.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 2.0999999999999996,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.5500000000000003,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 2.0999999999999996,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.5500000000000003,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 2.0999999999999996,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.5500000000000003,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 3.75,
|
||||||
|
"y": 2.0999999999999996,
|
||||||
|
"width": 0.25,
|
||||||
|
"height": 0.5500000000000003,
|
||||||
|
"is_cut": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wall": {
|
||||||
|
"name": "KZ",
|
||||||
|
"width": 7.2,
|
||||||
|
"height": 4.22,
|
||||||
|
"shape": "triangular",
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"left": 2.83,
|
||||||
|
"bottom": 0.95,
|
||||||
|
"width": 1.6,
|
||||||
|
"height": 1.4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"board": {
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6
|
||||||
|
},
|
||||||
|
"boards": [
|
||||||
|
{
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.25,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 3.75,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 5.0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 6.25,
|
||||||
|
"y": 0,
|
||||||
|
"width": 0.9500000000000002,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 0.5118483412322274,
|
||||||
|
"y": 0.6,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.7618483412322274,
|
||||||
|
"y": 0.6,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 3.0118483412322274,
|
||||||
|
"y": 0.6,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 4.261848341232227,
|
||||||
|
"y": 0.6,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 5.511848341232227,
|
||||||
|
"y": 0.6,
|
||||||
|
"width": 1.1763033175355453,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.0236966824644549,
|
||||||
|
"y": 1.2,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.273696682464455,
|
||||||
|
"y": 1.2,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 3.523696682464455,
|
||||||
|
"y": 1.2,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 4.773696682464455,
|
||||||
|
"y": 1.2,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 6.023696682464455,
|
||||||
|
"y": 1.2,
|
||||||
|
"width": 0.1526066350710895,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1.5355450236966823,
|
||||||
|
"y": 1.7999999999999998,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.7855450236966823,
|
||||||
|
"y": 1.7999999999999998,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 4.035545023696683,
|
||||||
|
"y": 1.7999999999999998,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 5.285545023696683,
|
||||||
|
"y": 1.7999999999999998,
|
||||||
|
"width": 0.3789099526066355,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.0473933649289098,
|
||||||
|
"y": 2.4,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 3.2973933649289098,
|
||||||
|
"y": 2.4,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 4.54739336492891,
|
||||||
|
"y": 2.4,
|
||||||
|
"width": 0.6052132701421802,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2.5592417061611377,
|
||||||
|
"y": 3.0,
|
||||||
|
"width": 1.25,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 3.8092417061611377,
|
||||||
|
"y": 3.0,
|
||||||
|
"width": 0.8315165876777253,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 3.071090047393365,
|
||||||
|
"y": 3.6,
|
||||||
|
"width": 1.05781990521327,
|
||||||
|
"height": 0.6,
|
||||||
|
"is_cut": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 3.582938388625592,
|
||||||
|
"y": 4.199999999999999,
|
||||||
|
"width": 0.034123222748816316,
|
||||||
|
"height": 0.020000000000000018,
|
||||||
|
"is_cut": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
259
wall_calculator.py
Normal file
259
wall_calculator.py
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
import json
|
||||||
|
import math
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List, Tuple, Dict
|
||||||
|
import webbrowser
|
||||||
|
import os
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Window:
|
||||||
|
left: float # position from left edge
|
||||||
|
bottom: float # position from bottom edge
|
||||||
|
width: float
|
||||||
|
height: float
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Dimensions:
|
||||||
|
name: str
|
||||||
|
width: float
|
||||||
|
height: float
|
||||||
|
shape: str = "rectangular"
|
||||||
|
windows: List[Window] = None
|
||||||
|
heightFirstRow: float = None
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
if self.windows is None:
|
||||||
|
self.windows = []
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Board:
|
||||||
|
x: float # x position
|
||||||
|
y: float # y position
|
||||||
|
width: float
|
||||||
|
height: float
|
||||||
|
is_cut: bool
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BoardUsage:
|
||||||
|
full_boards: int
|
||||||
|
cut_boards: List[float] # List of cut board lengths in meters
|
||||||
|
last_row_height: float # Height of the last row in meters
|
||||||
|
waste: float # Total waste in square meters
|
||||||
|
board_positions: List[Board] # List of all boards with their positions
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class WallResult:
|
||||||
|
wall: Dimensions
|
||||||
|
usage: BoardUsage
|
||||||
|
|
||||||
|
def load_config(filename: str) -> Tuple[List[Dimensions], Dimensions]:
|
||||||
|
"""Load walls and board dimensions from config file."""
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
|
||||||
|
walls = []
|
||||||
|
for wall_config in config['walls']:
|
||||||
|
windows = []
|
||||||
|
if 'windows' in wall_config:
|
||||||
|
for w in wall_config['windows']:
|
||||||
|
windows.append(Window(
|
||||||
|
left=w['left'],
|
||||||
|
bottom=w['bottom'],
|
||||||
|
width=w['width'],
|
||||||
|
height=w['height']
|
||||||
|
))
|
||||||
|
|
||||||
|
wall = Dimensions(
|
||||||
|
name=wall_config['name'],
|
||||||
|
width=wall_config['width'],
|
||||||
|
height=wall_config['height'],
|
||||||
|
shape=wall_config.get('shape', 'rectangular'),
|
||||||
|
windows=windows,
|
||||||
|
heightFirstRow=wall_config.get('heightFirstRow', None)
|
||||||
|
)
|
||||||
|
walls.append(wall)
|
||||||
|
|
||||||
|
board = Dimensions(**config['board'])
|
||||||
|
return walls, board
|
||||||
|
|
||||||
|
def get_row_width(y: float, wall: Dimensions) -> float:
|
||||||
|
"""Calculate the width of the wall at a given height."""
|
||||||
|
if wall.shape == "rectangular":
|
||||||
|
return wall.width
|
||||||
|
elif wall.shape == "triangular":
|
||||||
|
# For equilateral triangle, calculate width at given height
|
||||||
|
# At y=0 (bottom), width is wall.width
|
||||||
|
# At y=wall.height (top), width is 0
|
||||||
|
return wall.width * (1 - y/wall.height)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def get_row_offset(y: float, wall: Dimensions) -> float:
|
||||||
|
"""Calculate the x-offset for a row at given height in triangular walls."""
|
||||||
|
if wall.shape != "triangular":
|
||||||
|
return 0
|
||||||
|
# For triangular shape, calculate offset based on similar triangles
|
||||||
|
# At y=0 (bottom), offset is 0
|
||||||
|
# At y=wall.height (top), offset is wall.width/2
|
||||||
|
return (y / wall.height) * (wall.width / 2)
|
||||||
|
|
||||||
|
def calculate_board_usage(wall: Dimensions, board: Dimensions) -> BoardUsage:
|
||||||
|
"""Calculate how many boards are needed to cover the wall."""
|
||||||
|
# Calculate the height of the first row if specified
|
||||||
|
first_row_height = wall.heightFirstRow if wall.heightFirstRow is not None else board.height
|
||||||
|
|
||||||
|
# Calculate remaining height after first row
|
||||||
|
remaining_height = wall.height - first_row_height
|
||||||
|
|
||||||
|
# Calculate number of full-height rows needed for remaining height
|
||||||
|
full_rows = math.floor(remaining_height / board.height)
|
||||||
|
|
||||||
|
# Calculate the height of the last row
|
||||||
|
last_row_height = remaining_height - (full_rows * board.height)
|
||||||
|
if last_row_height < 0.01: # If less than 1cm remaining, ignore it
|
||||||
|
last_row_height = 0
|
||||||
|
full_rows = math.floor(remaining_height / board.height)
|
||||||
|
|
||||||
|
rows = 1 + full_rows + (1 if last_row_height > 0 else 0) # First row + full rows + last row if needed
|
||||||
|
|
||||||
|
full_boards = 0
|
||||||
|
cut_boards = []
|
||||||
|
waste = 0
|
||||||
|
board_positions = []
|
||||||
|
|
||||||
|
# Process each row
|
||||||
|
for row in range(rows):
|
||||||
|
# Calculate y position from bottom
|
||||||
|
if row == 0:
|
||||||
|
current_y = 0
|
||||||
|
current_row_height = first_row_height
|
||||||
|
else:
|
||||||
|
current_y = first_row_height + ((row - 1) * board.height)
|
||||||
|
current_row_height = last_row_height if row == rows - 1 else board.height
|
||||||
|
|
||||||
|
# Calculate the width of the wall at this height
|
||||||
|
row_width = get_row_width(current_y, wall)
|
||||||
|
next_row_width = get_row_width(current_y + current_row_height, wall)
|
||||||
|
|
||||||
|
# For triangular shape, we need to handle trapezoidal sections
|
||||||
|
if wall.shape == "triangular":
|
||||||
|
# Use the wider base for board calculations to ensure coverage
|
||||||
|
row_width = max(row_width, next_row_width)
|
||||||
|
|
||||||
|
# Calculate x offset for triangular walls
|
||||||
|
x_offset = get_row_offset(current_y, wall)
|
||||||
|
current_x = x_offset
|
||||||
|
remaining_width = row_width
|
||||||
|
|
||||||
|
while remaining_width > 0:
|
||||||
|
if remaining_width >= board.width:
|
||||||
|
full_boards += 1
|
||||||
|
board_positions.append(Board(
|
||||||
|
x=current_x,
|
||||||
|
y=current_y,
|
||||||
|
width=board.width,
|
||||||
|
height=current_row_height,
|
||||||
|
is_cut=current_row_height != board.height
|
||||||
|
))
|
||||||
|
current_x += board.width
|
||||||
|
remaining_width -= board.width
|
||||||
|
# Calculate waste if board height is cut
|
||||||
|
if current_row_height != board.height:
|
||||||
|
waste += (board.width * (board.height - current_row_height))
|
||||||
|
else:
|
||||||
|
if remaining_width > 0:
|
||||||
|
cut_boards.append(remaining_width)
|
||||||
|
board_positions.append(Board(
|
||||||
|
x=current_x,
|
||||||
|
y=current_y,
|
||||||
|
width=remaining_width,
|
||||||
|
height=current_row_height,
|
||||||
|
is_cut=True
|
||||||
|
))
|
||||||
|
# Calculate waste considering both width and height cuts
|
||||||
|
width_waste = board.width - remaining_width
|
||||||
|
height_waste = board.height - current_row_height if current_row_height != board.height else 0
|
||||||
|
waste += (width_waste * current_row_height) + (remaining_width * height_waste)
|
||||||
|
remaining_width = 0
|
||||||
|
|
||||||
|
return BoardUsage(full_boards, cut_boards, last_row_height, waste, board_positions)
|
||||||
|
|
||||||
|
def generate_visualization_data(wall_results: List[WallResult], board: Dimensions) -> List[Dict]:
|
||||||
|
"""Generate data for the visualization of multiple walls."""
|
||||||
|
return [{
|
||||||
|
"wall": {
|
||||||
|
"name": result.wall.name,
|
||||||
|
"width": result.wall.width,
|
||||||
|
"height": result.wall.height,
|
||||||
|
"shape": result.wall.shape,
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"left": w.left,
|
||||||
|
"bottom": w.bottom,
|
||||||
|
"width": w.width,
|
||||||
|
"height": w.height
|
||||||
|
}
|
||||||
|
for w in result.wall.windows
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"board": {
|
||||||
|
"width": board.width,
|
||||||
|
"height": board.height
|
||||||
|
},
|
||||||
|
"boards": [
|
||||||
|
{
|
||||||
|
"x": b.x,
|
||||||
|
"y": b.y,
|
||||||
|
"width": b.width,
|
||||||
|
"height": b.height,
|
||||||
|
"is_cut": b.is_cut
|
||||||
|
}
|
||||||
|
for b in result.usage.board_positions
|
||||||
|
]
|
||||||
|
} for result in wall_results]
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Load configuration
|
||||||
|
walls, board = load_config('config.json')
|
||||||
|
|
||||||
|
# Calculate board usage for each wall
|
||||||
|
wall_results = []
|
||||||
|
total_full_boards = 0
|
||||||
|
total_cut_boards = 0
|
||||||
|
total_waste = 0
|
||||||
|
|
||||||
|
for wall in walls:
|
||||||
|
usage = calculate_board_usage(wall, board)
|
||||||
|
wall_results.append(WallResult(wall, usage))
|
||||||
|
|
||||||
|
total_full_boards += usage.full_boards
|
||||||
|
total_cut_boards += len(usage.cut_boards)
|
||||||
|
total_waste += usage.waste
|
||||||
|
|
||||||
|
# Print results for each wall
|
||||||
|
for result in wall_results:
|
||||||
|
print(f"\n{result.wall.name}:")
|
||||||
|
print(f"Wall dimensions: {result.wall.width}m x {result.wall.height}m")
|
||||||
|
print(f"Wall shape: {result.wall.shape}")
|
||||||
|
print(f"Full boards needed: {result.usage.full_boards}")
|
||||||
|
print(f"Cut boards needed: {len(result.usage.cut_boards)}")
|
||||||
|
print("Cut board lengths:", [f"{length:.2f}m" for length in result.usage.cut_boards])
|
||||||
|
print(f"Last row height: {result.usage.last_row_height:.2f}m")
|
||||||
|
print(f"Waste: {result.usage.waste:.2f} square meters")
|
||||||
|
|
||||||
|
# Print total summary
|
||||||
|
print("\nTotal Summary:")
|
||||||
|
print(f"Total full boards needed: {total_full_boards}")
|
||||||
|
print(f"Total cut boards needed: {total_cut_boards}")
|
||||||
|
print(f"Total waste: {total_waste:.2f} square meters")
|
||||||
|
print(f"Total boards needed: {total_full_boards + total_cut_boards}")
|
||||||
|
|
||||||
|
# Generate visualization data
|
||||||
|
viz_data = generate_visualization_data(wall_results, board)
|
||||||
|
with open('visualization_data.js', 'w') as f:
|
||||||
|
f.write(f"const wallsData = {json.dumps(viz_data, indent=2)};")
|
||||||
|
|
||||||
|
# Open visualization in browser
|
||||||
|
webbrowser.open('file://' + os.path.abspath('wall_visualization.html'))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
490
wall_visualization.html
Normal file
490
wall_visualization.html
Normal file
@ -0,0 +1,490 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Wall Board Coverage Visualization</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 20px;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.wall-section {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.canvas-container {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
#boardInfo {
|
||||||
|
position: absolute;
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
color: white;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
pointer-events: none;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.legend {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
.legend-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.legend-color {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border: 1px solid #000;
|
||||||
|
}
|
||||||
|
.dimensions {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.stats {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.total-summary {
|
||||||
|
margin-top: 40px;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #e8f5e9;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
color: #2e7d32;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Wall Board Coverage Visualization</h1>
|
||||||
|
<div id="wallSections"></div>
|
||||||
|
<div class="legend">
|
||||||
|
<div class="legend-item">
|
||||||
|
<div class="legend-color" style="background-color: #4CAF50;"></div>
|
||||||
|
<span>Full Boards</span>
|
||||||
|
</div>
|
||||||
|
<div class="legend-item">
|
||||||
|
<div class="legend-color" style="background-color: #FFA726;"></div>
|
||||||
|
<span>Cut Boards</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="total-summary" id="totalSummary"></div>
|
||||||
|
<div id="boardInfo"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="visualization_data.js"></script>
|
||||||
|
<script>
|
||||||
|
console.log('Starting visualization script...');
|
||||||
|
|
||||||
|
// Add error handling and debugging for data
|
||||||
|
if (typeof wallsData === 'undefined') {
|
||||||
|
console.error('Visualization data not loaded!');
|
||||||
|
document.getElementById('wallSections').innerHTML =
|
||||||
|
'<div style="color: red; padding: 20px;">Error: Visualization data not loaded. Please run the Python script first.</div>';
|
||||||
|
} else {
|
||||||
|
console.log('Loaded wall data:', wallsData);
|
||||||
|
console.log('Number of walls:', wallsData.length);
|
||||||
|
|
||||||
|
const PADDING = 40;
|
||||||
|
const SCALE_FACTOR = 100; // pixels per meter
|
||||||
|
|
||||||
|
function drawWall(ctx, wall) {
|
||||||
|
ctx.strokeStyle = '#000';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
|
||||||
|
if (wall.shape === 'rectangular') {
|
||||||
|
ctx.strokeRect(
|
||||||
|
PADDING,
|
||||||
|
PADDING,
|
||||||
|
wall.width * SCALE_FACTOR,
|
||||||
|
wall.height * SCALE_FACTOR
|
||||||
|
);
|
||||||
|
} else if (wall.shape === 'triangular') {
|
||||||
|
const baseWidth = wall.width * SCALE_FACTOR;
|
||||||
|
const height = wall.height * SCALE_FACTOR;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(PADDING, PADDING + height); // Bottom left
|
||||||
|
ctx.lineTo(PADDING + baseWidth, PADDING + height); // Bottom right
|
||||||
|
ctx.lineTo(PADDING + (baseWidth / 2), PADDING); // Top middle
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawWindows(ctx, wall) {
|
||||||
|
if (!wall.windows || wall.windows.length === 0) return;
|
||||||
|
|
||||||
|
// Save the current context state
|
||||||
|
ctx.save();
|
||||||
|
|
||||||
|
// Set window style
|
||||||
|
ctx.strokeStyle = '#000';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.fillStyle = 'rgba(135, 206, 235, 0.5)'; // Light blue with 50% transparency
|
||||||
|
|
||||||
|
// Draw each window
|
||||||
|
wall.windows.forEach(window => {
|
||||||
|
const x = PADDING + (window.left * SCALE_FACTOR);
|
||||||
|
const y = PADDING + ((wall.height - window.bottom - window.height) * SCALE_FACTOR);
|
||||||
|
const width = window.width * SCALE_FACTOR;
|
||||||
|
const height = window.height * SCALE_FACTOR;
|
||||||
|
|
||||||
|
// Draw window with semi-transparent fill
|
||||||
|
ctx.fillRect(x, y, width, height);
|
||||||
|
ctx.strokeRect(x, y, width, height);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Restore the context state
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPointInTriangle(x, y, wall) {
|
||||||
|
if (wall.shape !== 'triangular') return true;
|
||||||
|
|
||||||
|
const baseWidth = wall.width * SCALE_FACTOR;
|
||||||
|
const height = wall.height * SCALE_FACTOR;
|
||||||
|
const topX = PADDING + (baseWidth / 2);
|
||||||
|
const topY = PADDING;
|
||||||
|
const bottomLeftX = PADDING;
|
||||||
|
const bottomLeftY = PADDING + height;
|
||||||
|
const bottomRightX = PADDING + baseWidth;
|
||||||
|
const bottomRightY = PADDING + height;
|
||||||
|
|
||||||
|
// Calculate barycentric coordinates
|
||||||
|
const denominator = (bottomLeftY - bottomRightY) * (topX - bottomRightX) +
|
||||||
|
(bottomRightX - bottomLeftX) * (topY - bottomRightY);
|
||||||
|
|
||||||
|
const a = ((bottomLeftY - bottomRightY) * (x - bottomRightX) +
|
||||||
|
(bottomRightX - bottomLeftX) * (y - bottomRightY)) / denominator;
|
||||||
|
const b = ((bottomRightY - topY) * (x - bottomRightX) +
|
||||||
|
(topX - bottomRightX) * (y - bottomRightY)) / denominator;
|
||||||
|
const c = 1 - a - b;
|
||||||
|
|
||||||
|
return a >= 0 && a <= 1 && b >= 0 && b <= 1 && c >= 0 && c <= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawBoards(ctx, wallData) {
|
||||||
|
wallData.boardRects = [];
|
||||||
|
let cutBoardsInfo = []; // Store info about cut boards for later
|
||||||
|
|
||||||
|
// For triangular walls, create clipping path once
|
||||||
|
if (wallData.wall.shape === 'triangular') {
|
||||||
|
ctx.save();
|
||||||
|
ctx.beginPath();
|
||||||
|
const baseWidth = wallData.wall.width * SCALE_FACTOR;
|
||||||
|
const wallHeight = wallData.wall.height * SCALE_FACTOR;
|
||||||
|
ctx.moveTo(PADDING, PADDING + wallHeight);
|
||||||
|
ctx.lineTo(PADDING + baseWidth, PADDING + wallHeight);
|
||||||
|
ctx.lineTo(PADDING + (baseWidth / 2), PADDING);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.clip();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw all boards
|
||||||
|
wallData.boards.forEach((board, index) => {
|
||||||
|
ctx.fillStyle = board.is_cut ? '#FFA726' : '#4CAF50';
|
||||||
|
ctx.strokeStyle = '#000';
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
|
||||||
|
const x = PADDING + (board.x * SCALE_FACTOR);
|
||||||
|
const y = PADDING + ((wallData.wall.height - board.y - board.height) * SCALE_FACTOR);
|
||||||
|
const width = board.width * SCALE_FACTOR;
|
||||||
|
const height = board.height * SCALE_FACTOR;
|
||||||
|
|
||||||
|
// Store board position and dimensions for hover detection
|
||||||
|
wallData.boardRects.push({
|
||||||
|
x, y, width, height,
|
||||||
|
boardData: {
|
||||||
|
width: board.width,
|
||||||
|
height: board.height,
|
||||||
|
is_cut: board.is_cut,
|
||||||
|
y_position: board.y,
|
||||||
|
x_position: board.x
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Draw board
|
||||||
|
ctx.fillRect(x, y, width, height);
|
||||||
|
ctx.strokeRect(x, y, width, height);
|
||||||
|
|
||||||
|
// Store cut board info for later drawing
|
||||||
|
if (board.is_cut) {
|
||||||
|
cutBoardsInfo.push({
|
||||||
|
x, y, width, height,
|
||||||
|
board: board,
|
||||||
|
wallData: wallData
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw dimensions if space allows
|
||||||
|
if (isPointInTriangle(x + width/2, y + height/2, wallData.wall)) {
|
||||||
|
ctx.fillStyle = '#000';
|
||||||
|
ctx.font = '10px Arial';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
|
||||||
|
// Format dimensions
|
||||||
|
const widthText = board.width.toFixed(2) + 'm';
|
||||||
|
const heightText = board.height.toFixed(2) + 'm';
|
||||||
|
|
||||||
|
// Draw width dimension if board is wide enough
|
||||||
|
if (width > 40) {
|
||||||
|
ctx.fillText(widthText, x + width/2, y + height/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw height dimension if board is tall enough and board.x matches the calculated offset
|
||||||
|
if (height > 30 &&
|
||||||
|
(wallData.wall.shape === 'rectangular' ? board.x === 0 :
|
||||||
|
Math.abs(board.x - (board.y / wallData.wall.height) * (wallData.wall.width / 2)) < 0.01)) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(x - 5, y + height/2);
|
||||||
|
ctx.rotate(-Math.PI/2);
|
||||||
|
ctx.fillText(heightText, 0, 0);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Restore context if we used clipping
|
||||||
|
if (wallData.wall.shape === 'triangular') {
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw dotted lines for cut boards after clipping is restored
|
||||||
|
cutBoardsInfo.forEach(info => {
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle = '#666';
|
||||||
|
ctx.setLineDash([5, 5]); // Create dotted line pattern
|
||||||
|
ctx.beginPath();
|
||||||
|
|
||||||
|
// Draw vertical dotted line at the end of the cut board
|
||||||
|
if (info.board.width < info.wallData.board.width) {
|
||||||
|
const fullWidth = info.wallData.board.width * SCALE_FACTOR;
|
||||||
|
ctx.moveTo(info.x + info.width, info.y);
|
||||||
|
ctx.lineTo(info.x + fullWidth, info.y);
|
||||||
|
ctx.moveTo(info.x + info.width, info.y + info.height);
|
||||||
|
ctx.lineTo(info.x + fullWidth, info.y + info.height);
|
||||||
|
ctx.moveTo(info.x + fullWidth, info.y);
|
||||||
|
ctx.lineTo(info.x + fullWidth, info.y + info.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw horizontal dotted line at the top of cut board for height cuts
|
||||||
|
if (info.board.height < info.wallData.board.height) {
|
||||||
|
const fullHeight = info.wallData.board.height * SCALE_FACTOR;
|
||||||
|
ctx.moveTo(info.x, info.y);
|
||||||
|
ctx.lineTo(info.x, info.y - (fullHeight - info.height));
|
||||||
|
ctx.moveTo(info.x + info.width, info.y);
|
||||||
|
ctx.lineTo(info.x + info.width, info.y - (fullHeight - info.height));
|
||||||
|
ctx.moveTo(info.x, info.y - (fullHeight - info.height));
|
||||||
|
ctx.lineTo(info.x + info.width, info.y - (fullHeight - info.height));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.restore();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createWallSection(wallData, index) {
|
||||||
|
const section = document.createElement('div');
|
||||||
|
section.className = 'wall-section';
|
||||||
|
|
||||||
|
// Create wall title
|
||||||
|
const title = document.createElement('h2');
|
||||||
|
title.textContent = wallData.wall.name;
|
||||||
|
section.appendChild(title);
|
||||||
|
|
||||||
|
// Create dimensions div
|
||||||
|
const dimensions = document.createElement('div');
|
||||||
|
dimensions.className = 'dimensions';
|
||||||
|
dimensions.id = `dimensions-${index}`;
|
||||||
|
section.appendChild(dimensions);
|
||||||
|
|
||||||
|
// Create stats div
|
||||||
|
const stats = document.createElement('div');
|
||||||
|
stats.className = 'stats';
|
||||||
|
section.appendChild(stats);
|
||||||
|
|
||||||
|
// Create canvas container
|
||||||
|
const canvasContainer = document.createElement('div');
|
||||||
|
canvasContainer.className = 'canvas-container';
|
||||||
|
section.appendChild(canvasContainer);
|
||||||
|
|
||||||
|
// Create canvas element
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 800;
|
||||||
|
canvas.height = 600;
|
||||||
|
canvasContainer.appendChild(canvas);
|
||||||
|
|
||||||
|
// Add mouse event listeners for board info
|
||||||
|
const boardInfo = document.getElementById('boardInfo');
|
||||||
|
canvas.addEventListener('mousemove', (event) => {
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const x = event.clientX - rect.left;
|
||||||
|
const y = event.clientY - rect.top;
|
||||||
|
|
||||||
|
// Check if mouse is over any board
|
||||||
|
let found = false;
|
||||||
|
for (const boardRect of wallData.boardRects) {
|
||||||
|
if (x >= boardRect.x && x <= boardRect.x + boardRect.width &&
|
||||||
|
y >= boardRect.y && y <= boardRect.y + boardRect.height) {
|
||||||
|
const data = boardRect.boardData;
|
||||||
|
boardInfo.style.display = 'block';
|
||||||
|
boardInfo.style.left = (event.pageX + 10) + 'px';
|
||||||
|
boardInfo.style.top = (event.pageY + 10) + 'px';
|
||||||
|
boardInfo.innerHTML = `
|
||||||
|
Position: (${data.x_position.toFixed(2)}m, ${data.y_position.toFixed(2)}m)<br>
|
||||||
|
Size: ${data.width.toFixed(2)}m × ${data.height.toFixed(2)}m<br>
|
||||||
|
${data.is_cut ? 'Cut board' : 'Full board'}
|
||||||
|
`;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
boardInfo.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.addEventListener('mouseleave', () => {
|
||||||
|
boardInfo.style.display = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get the canvas context
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// Draw wall
|
||||||
|
drawWall(ctx, wallData.wall);
|
||||||
|
|
||||||
|
// Draw boards
|
||||||
|
drawBoards(ctx, wallData);
|
||||||
|
|
||||||
|
// Draw windows (moved after boards)
|
||||||
|
drawWindows(ctx, wallData.wall);
|
||||||
|
|
||||||
|
// Add dimensions text first
|
||||||
|
let windowsInfo = '';
|
||||||
|
if (wallData.wall.windows && wallData.wall.windows.length > 0) {
|
||||||
|
windowsInfo = '<br>Windows:<br>' + wallData.wall.windows.map((w, i) =>
|
||||||
|
`Window ${i + 1}: Position (${w.left}m from left, ${w.bottom}m from bottom), Size: ${w.width}m × ${w.height}m`
|
||||||
|
).join('<br>');
|
||||||
|
}
|
||||||
|
|
||||||
|
dimensions.innerHTML = `
|
||||||
|
Wall dimensions: ${wallData.wall.width}m × ${wallData.wall.height}m<br>
|
||||||
|
Wall shape: ${wallData.wall.shape}<br>
|
||||||
|
Board dimensions: ${wallData.board.width}m × ${wallData.board.height}m
|
||||||
|
${windowsInfo}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Draw dimension lines and labels
|
||||||
|
ctx.strokeStyle = '#666';
|
||||||
|
ctx.fillStyle = '#666';
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.font = '12px Arial';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
|
||||||
|
// Width dimension
|
||||||
|
const y = wallData.wall.height * SCALE_FACTOR + PADDING + 20;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(PADDING, y);
|
||||||
|
ctx.lineTo(PADDING + wallData.wall.width * SCALE_FACTOR, y);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.fillText(
|
||||||
|
`${wallData.wall.width}m`,
|
||||||
|
PADDING + (wallData.wall.width * SCALE_FACTOR) / 2,
|
||||||
|
y + 15
|
||||||
|
);
|
||||||
|
|
||||||
|
// Height dimension
|
||||||
|
const x = PADDING - 20;
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(x, PADDING + (wallData.wall.height * SCALE_FACTOR) / 2);
|
||||||
|
ctx.rotate(-Math.PI / 2);
|
||||||
|
ctx.fillText(`${wallData.wall.height}m`, 0, 0);
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
// Add stats to the stats div
|
||||||
|
stats.innerHTML = `
|
||||||
|
Total boards: ${wallData.boards.length}<br>
|
||||||
|
Full boards: ${wallData.boards.filter(b => !b.is_cut).length}<br>
|
||||||
|
Cut boards: ${wallData.boards.filter(b => b.is_cut).length}
|
||||||
|
`;
|
||||||
|
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderWallSections() {
|
||||||
|
console.log('Starting renderWallSections...');
|
||||||
|
const wallSections = document.getElementById('wallSections');
|
||||||
|
if (!wallSections) {
|
||||||
|
console.error('Could not find wallSections element!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wallSections.innerHTML = '';
|
||||||
|
|
||||||
|
// Calculate totals across all walls
|
||||||
|
console.log('Calculating totals...');
|
||||||
|
const totalBoards = wallsData.reduce((sum, wall) => sum + wall.boards.length, 0);
|
||||||
|
const totalFullBoards = wallsData.reduce((sum, wall) => sum + wall.boards.filter(b => !b.is_cut).length, 0);
|
||||||
|
const totalCutBoards = wallsData.reduce((sum, wall) => sum + wall.boards.filter(b => b.is_cut).length, 0);
|
||||||
|
console.log('Totals calculated:', { totalBoards, totalFullBoards, totalCutBoards });
|
||||||
|
|
||||||
|
// Render each wall section
|
||||||
|
console.log('Rendering wall sections...');
|
||||||
|
wallsData.forEach((wallData, index) => {
|
||||||
|
console.log(`Creating wall section ${index}:`, wallData.wall.name);
|
||||||
|
const section = createWallSection(wallData, index);
|
||||||
|
wallSections.appendChild(section);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update total summary
|
||||||
|
console.log('Updating total summary...');
|
||||||
|
const totalSummary = document.getElementById('totalSummary');
|
||||||
|
if (!totalSummary) {
|
||||||
|
console.error('Could not find totalSummary element!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
totalSummary.innerHTML = `
|
||||||
|
<h2>Total Summary</h2>
|
||||||
|
Total boards across all walls: ${totalBoards}<br>
|
||||||
|
Full boards: ${totalFullBoards}<br>
|
||||||
|
Cut boards: ${totalCutBoards}
|
||||||
|
`;
|
||||||
|
console.log('Rendering complete.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only render if we have data
|
||||||
|
if (typeof wallsData !== 'undefined') {
|
||||||
|
console.log('Starting render with data...');
|
||||||
|
renderWallSections();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user