File size: 7,572 Bytes
be02369
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import React, { useState, useMemo } from 'react';
import { PotRulerIcon } from '../components/icons';

type Units = 'cm' | 'in';
type BonsaiStyle = 'Upright' | 'Cascade' | 'Forest' | 'Literati';

const PotCalculatorView: React.FC = () => {
    const [units, setUnits] = useState<Units>('cm');
    const [height, setHeight] = useState(30);
    const [trunkDiameter, setTrunkDiameter] = useState(3);
    const [style, setStyle] = useState<BonsaiStyle>('Upright');

    const conversionFactor = units === 'in' ? 2.54 : 1;

    const results = useMemo(() => {
        let length, width, depth, rationale;
        const h = height;
        const d = trunkDiameter;

        switch (style) {
            case 'Cascade':
                length = h * 0.5;
                depth = h * 0.6;
                width = length;
                rationale = "Cascade pots are tall and often square or hexagonal to visually balance the downward-flowing trunk. The depth and stability are crucial.";
                break;
            case 'Forest':
                length = h * 1.5;
                depth = d * 0.75;
                width = length * 0.6;
                rationale = "Forest plantings require wide, very shallow oval or rectangular trays to create a sense of a landscape and accommodate many root systems.";
                break;
            case 'Literati':
                length = d * 3;
                depth = d * 1.5;
                width = length;
                rationale = "Literati pots are typically small, simple, and often round or unusually shaped. They are understated to emphasize the elegant, sparse trunk line.";
                break;
            case 'Upright':
            default:
                length = h * 0.66;
                depth = d;
                width = length * 0.8;
                rationale = "For upright styles, the pot length is typically 2/3 of the tree's height. The pot's depth should be equal to the trunk's diameter to provide visual stability.";
                break;
        }

        const format = (val: number) => {
            const converted = val / conversionFactor;
            return converted.toFixed(1);
        }

        return {
            length: format(length),
            width: format(width),
            depth: format(depth),
            rationale,
        };
    }, [height, trunkDiameter, style, units, conversionFactor]);

    return (
        <div className="space-y-8 max-w-4xl mx-auto">
            <header className="text-center">
                <h2 className="text-3xl font-bold tracking-tight text-stone-900 sm:text-4xl flex items-center justify-center gap-3">
                    <PotRulerIcon className="w-8 h-8 text-amber-800" />
                    Pot Ratio Calculator
                </h2>
                <p className="mt-4 text-lg leading-8 text-stone-600">
                    Find the perfect pot size for your bonsai based on traditional aesthetic guidelines.
                </p>
            </header>

            <div className="grid grid-cols-1 md:grid-cols-2 gap-8 items-start">
                <div className="bg-white p-6 rounded-xl shadow-lg border border-stone-200 space-y-6">
                    <div>
                        <div className="flex justify-between items-center mb-2">
                            <label htmlFor="units" className="font-semibold text-stone-800">Units</label>
                            <div className="flex gap-1 bg-stone-100 p-1 rounded-lg">
                                <button onClick={() => setUnits('cm')} className={`px-3 py-1 text-sm font-medium rounded-md ${units === 'cm' ? 'bg-white shadow' : ''}`}>cm</button>
                                <button onClick={() => setUnits('in')} className={`px-3 py-1 text-sm font-medium rounded-md ${units === 'in' ? 'bg-white shadow' : ''}`}>in</button>
                            </div>
                        </div>
                    </div>

                    <div>
                        <label htmlFor="height" className="font-semibold text-stone-800">Tree Height ({units})</label>
                        <div className="flex items-center gap-4 mt-2">
                            <input type="range" id="height" min="10" max="200" value={height} onChange={e => setHeight(Number(e.target.value))} className="w-full h-2 bg-stone-200 rounded-lg appearance-none cursor-pointer accent-amber-700" />
                            <input type="number" value={(height / conversionFactor).toFixed(1)} onChange={e => setHeight(Number(e.target.value) * conversionFactor)} className="w-20 p-2 border rounded-md" />
                        </div>
                    </div>

                    <div>
                        <label htmlFor="trunkDiameter" className="font-semibold text-stone-800">Trunk Diameter ({units})</label>
                        <div className="flex items-center gap-4 mt-2">
                            <input type="range" id="trunkDiameter" min="1" max="20" step="0.5" value={trunkDiameter} onChange={e => setTrunkDiameter(Number(e.target.value))} className="w-full h-2 bg-stone-200 rounded-lg appearance-none cursor-pointer accent-amber-700" />
                            <input type="number" value={(trunkDiameter / conversionFactor).toFixed(1)} onChange={e => setTrunkDiameter(Number(e.target.value) * conversionFactor)} className="w-20 p-2 border rounded-md" />
                        </div>
                    </div>

                    <div>
                        <label htmlFor="style" className="font-semibold text-stone-800">Bonsai Style</label>
                        <select id="style" value={style} onChange={e => setStyle(e.target.value as BonsaiStyle)} className="w-full mt-2 p-2 border rounded-md bg-white">
                            <option value="Upright">Formal/Informal Upright</option>
                            <option value="Cascade">Cascade/Semi-Cascade</option>
                            <option value="Forest">Forest/Group Planting</option>
                            <option value="Literati">Literati (Bunjin)</option>
                        </select>
                    </div>
                </div>

                <div className="bg-white p-6 rounded-xl shadow-lg border-2 border-amber-600 space-y-4">
                    <h3 className="text-xl font-bold text-center text-stone-900">Recommended Pot Size</h3>
                    <div className="flex justify-around text-center">
                        <div>
                            <p className="text-3xl font-bold text-amber-800">{results.length}</p>
                            <p className="text-sm text-stone-600">Length ({units})</p>
                        </div>
                         <div>
                            <p className="text-3xl font-bold text-amber-800">{results.width}</p>
                            <p className="text-sm text-stone-600">Width ({units})</p>
                        </div>
                        <div>
                            <p className="text-3xl font-bold text-amber-800">{results.depth}</p>
                            <p className="text-sm text-stone-600">Depth ({units})</p>
                        </div>
                    </div>
                    <div className="mt-4 pt-4 border-t-2 border-dashed">
                        <h4 className="font-semibold text-stone-800">Rationale</h4>
                        <p className="text-sm text-stone-700 mt-1">{results.rationale}</p>
                    </div>
                </div>
            </div>
        </div>
    );
};

export default PotCalculatorView;