1a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk/*
2a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * Copyright (C) 2011 The Android Open Source Project
3a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk *
4a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * Licensed under the Apache License, Version 2.0 (the "License");
5a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * you may not use this file except in compliance with the License.
6a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * You may obtain a copy of the License at
7a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk *
8a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk *      http://www.apache.org/licenses/LICENSE-2.0
9a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk *
10a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * Unless required by applicable law or agreed to in writing, software
11a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * distributed under the License is distributed on an "AS IS" BASIS,
12a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * See the License for the specific language governing permissions and
14a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * limitations under the License.
15a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk */
16a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
17a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukpackage com.android.scenegraph;
18a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
19a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukimport java.lang.Math;
20a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukimport java.util.ArrayList;
21a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
22e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchoukimport com.android.scenegraph.SceneManager;
23e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk
24a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukimport android.renderscript.*;
25a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukimport android.renderscript.Float3;
26a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukimport android.renderscript.Matrix4f;
27a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukimport android.util.Log;
28a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
29a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk/**
30a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * @hide
31a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk */
32a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukpublic class CompoundTransform extends Transform {
33a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
344bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk    public static abstract class Component {
35a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk        String mName;
364bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        CompoundTransform mParent;
37e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        int mParentIndex;
38e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        protected ScriptField_TransformComponent_s.Item mData;
39e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk
40e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk        Component(int type, String name) {
41e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mData = new ScriptField_TransformComponent_s.Item();
42e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk            mData.type = type;
43e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mName = name;
44e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        }
45a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
46e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        void setNameAlloc() {
47e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            RenderScriptGL rs = SceneManager.getRS();
48e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            if (mData.name != null)  {
49e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk                return;
50e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            }
51e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mData.name = SceneManager.getCachedAlloc(getName());
52e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            if (mData.name == null) {
53e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk                mData.name = SceneManager.getStringAsAllocation(rs, getName());
54e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk                SceneManager.cacheAlloc(getName(), mData.name);
55e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            }
56e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        }
57e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk
58e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk        ScriptField_TransformComponent_s.Item getRSData() {
59e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk            setNameAlloc();
60e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk            return mData;
61e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk        }
62e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk
63e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        protected void update() {
64e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            if (mParent != null) {
65e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk                mParent.updateRSComponent(this);
66e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk            }
67e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk        }
68e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk
69a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk        public String getName() {
70a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk            return mName;
71a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk        }
72a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    }
73a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
74a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    public static class TranslateComponent extends Component {
75a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk        public TranslateComponent(String name, Float3 translate) {
76e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk            super(ScriptC_export.const_Transform_TRANSLATE, name);
77e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            setValue(translate);
78a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk        }
794bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        public Float3 getValue() {
80e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            return new Float3(mData.value.x, mData.value.y, mData.value.z);
814bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        }
824bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        public void setValue(Float3 val) {
83e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mData.value.x = val.x;
84e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mData.value.y = val.y;
85e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mData.value.z = val.z;
86e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            update();
874bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        }
88a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    }
89a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
90a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    public static class RotateComponent extends Component {
91a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk        public RotateComponent(String name, Float3 axis, float angle) {
92e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk            super(ScriptC_export.const_Transform_ROTATE, name);
93e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            setAxis(axis);
94e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            setAngle(angle);
95a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk        }
964bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        public Float3 getAxis() {
97e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            return new Float3(mData.value.x, mData.value.y, mData.value.z);
984bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        }
994bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        public float getAngle() {
100e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            return mData.value.w;
1014bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        }
1024bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        public void setAxis(Float3 val) {
103e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mData.value.x = val.x;
104e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mData.value.y = val.y;
105e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mData.value.z = val.z;
106e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            update();
1074bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        }
1084bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        public void setAngle(float val) {
109e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mData.value.w = val;
110e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            update();
1114bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        }
112a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    }
113a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
114a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    public static class ScaleComponent extends Component {
115a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk        public ScaleComponent(String name, Float3 scale) {
116e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk            super(ScriptC_export.const_Transform_SCALE, name);
117e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            setValue(scale);
118a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk        }
1194bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        public Float3 getValue() {
120e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            return new Float3(mData.value.x, mData.value.y, mData.value.z);
1214bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        }
1224bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        public void setValue(Float3 val) {
123e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mData.value.x = val.x;
124e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mData.value.y = val.y;
125e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mData.value.z = val.z;
126e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            update();
1274bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        }
128a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    }
129a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
130e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk    ScriptField_TransformComponent_s mComponentField;
131a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    public ArrayList<Component> mTransformComponents;
132a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
133a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    public CompoundTransform() {
134a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk        mTransformComponents = new ArrayList<Component>();
135a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    }
136a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
13757fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk    public TranslateComponent addTranslate(String name, Float3 translate) {
13857fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk        TranslateComponent c = new TranslateComponent(name, translate);
13957fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk        addComponent(c);
14057fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk        return c;
14157fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk    }
14257fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk
14357fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk    public RotateComponent addRotate(String name, Float3 axis, float angle) {
14457fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk        RotateComponent c = new RotateComponent(name, axis, angle);
14557fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk        addComponent(c);
14657fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk        return c;
14757fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk    }
14857fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk
14957fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk    public ScaleComponent addScale(String name, Float3 scale) {
15057fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk        ScaleComponent c = new ScaleComponent(name, scale);
15157fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk        addComponent(c);
15257fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk        return c;
15357fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk    }
15457fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk
155a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    public void addComponent(Component c) {
1564bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        if (c.mParent != null) {
1574bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk            throw new IllegalArgumentException("Transform components may not be shared");
1584bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        }
1594bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        c.mParent = this;
160e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        c.mParentIndex = mTransformComponents.size();
161a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk        mTransformComponents.add(c);
162e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        updateRSComponentAllocation();
163a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    }
164a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
165a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    public void setComponent(int index, Component c) {
1664bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        if (c.mParent != null) {
1674bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk            throw new IllegalArgumentException("Transform components may not be shared");
1684bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        }
169e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        if (index >= mTransformComponents.size()) {
170e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            throw new IllegalArgumentException("Invalid component index");
171e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        }
1724bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        c.mParent = this;
173e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        c.mParentIndex = index;
174a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk        mTransformComponents.set(index, c);
175e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        updateRSComponent(c);
176a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    }
177a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
178e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk    void updateRSComponent(Component c) {
179e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        if (mField == null || mComponentField == null) {
1804bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk            return;
1814bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk        }
182e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        mComponentField.set(c.getRSData(), c.mParentIndex, true);
183e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        mField.set_isDirty(0, 1, true);
184e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk    }
185e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk
186e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk    void updateRSComponentAllocation() {
187e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        if (mField == null) {
188e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            return;
189e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk        }
190e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        initLocalData();
191e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk
192e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        mField.set_components(0, mTransformData.components, false);
193e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        mField.set_isDirty(0, 1, true);
194e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk    }
195e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk
196e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk    void initLocalData() {
197e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        RenderScriptGL rs = SceneManager.getRS();
198e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        int numComponenets = mTransformComponents.size();
199e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        if (numComponenets > 0) {
200e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mComponentField = new ScriptField_TransformComponent_s(rs, numComponenets);
201e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            for (int i = 0; i < numComponenets; i ++) {
202e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk                Component ith = mTransformComponents.get(i);
203e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk                mComponentField.set(ith.getRSData(), i, false);
204e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            }
205e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mComponentField.copyAll();
206e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk
207e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk            mTransformData.components = mComponentField.getAllocation();
208e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk        }
209a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk    }
210a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk}
211a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
212a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
213a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
214a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
215a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk
216