Skip to main content

ANIMASI GARIS
CREATED by MARVELOUSLY HELP ME of DEEP SEEK
*atur screen zoom-in dan zoom-out utk TEPAT Readability

Click here for My DeepSeek Session of building this App


Aliran Banyak Panah multi‑arah + kedip

150 px

 Ruas garis & sudut belok

⚡ aliran
✨ Bobot total: 0 (dinormalkan ke 100%)
panah menghadap arah gerak · berkedip · jumlah panah dapat diatur


S O U R C E  C O D E

<style>
    #apw-wrap {
        font-family: "Segoe UI", Roboto, Arial, sans-serif;
        background: #eef2f5;
        padding: 12px;
        display: flex;
        justify-content: center;
        align-items: flex-start;
    }
    #apw-wrap .apw-container {
        background: white;
        border-radius: 28px;
        box-shadow: 0 15px 35px rgba(0, 0, 0, 0.15);
        padding: 28px;
        max-width: 820px;
        width: 100%;
    }
    #apw-wrap h2 {
        margin: 0 0 12px 0;
        color: #1e2b3a;
        font-weight: 500;
        display: flex;
        align-items: center;
        gap: 8px;
        border-bottom: 2px solid #cfdee9;
        padding-bottom: 16px;
    }
    #apw-wrap h2 span {
        background: #0066cc;
        color: white;
        font-size: 0.9rem;
        padding: 4px 14px;
        border-radius: 40px;
        font-weight: 400;
    }
    #apw-wrap canvas {
        display: block;
        margin: 0 auto 22px auto;
        border: 2px solid #a0c0d4;
        border-radius: 20px;
        background: #fafeff;
        width: 100%;
        height: auto;
        cursor: crosshair;
        box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.02);
    }
    #apw-wrap .apw-control-panel {
        display: flex;
        flex-direction: column;
        gap: 24px;
    }
    #apw-wrap .apw-slider-group {
        display: flex;
        flex-direction: column;
        gap: 8px;
    }
    #apw-wrap .apw-slider-group label {
        font-weight: 600;
        color: #144a6f;
        font-size: 1rem;
        letter-spacing: 0.3px;
    }
    #apw-wrap .apw-slider-row {
        display: flex;
        align-items: center;
        gap: 16px;
    }
    #apw-wrap .apw-slider-row input[type="range"] {
        flex: 1;
        height: 7px;
        border-radius: 20px;
        background: #d3e2ed;
    }
    #apw-wrap .apw-slider-row span {
        min-width: 60px;
        text-align: right;
        font-weight: 700;
        color: #0b4770;
    }
    #apw-wrap .apw-segmen-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin: 6px 0 10px 0;
    }
    #apw-wrap .apw-segmen-header h3 {
        margin: 0;
        font-size: 1.15rem;
        font-weight: 500;
        color: #1d3e5e;
    }
    #apw-wrap .apw-btn-tambah {
        background: #28a745;
        color: white;
        border: none;
        padding: 8px 20px;
        border-radius: 40px;
        font-weight: 600;
        font-size: 0.9rem;
        cursor: pointer;
        transition: 0.15s;
        box-shadow: 0 3px 8px rgba(40, 167, 69, 0.25);
    }
    #apw-wrap .apw-btn-tambah:hover {
        background: #218838;
        transform: scale(1.02);
    }
    #apw-wrap .apw-btn-hapus {
        background: #dc3545;
        color: white;
        border: none;
        padding: 8px 20px;
        border-radius: 40px;
        font-weight: 600;
        font-size: 0.9rem;
        cursor: pointer;
        transition: 0.15s;
        box-shadow: 0 3px 8px rgba(220, 53, 69, 0.25);
    }
    #apw-wrap .apw-btn-hapus:hover {
        background: #c82333;
        transform: scale(1.02);
    }
    #apw-wrap .apw-btn-hapus:disabled {
        background: #b3b3b3;
        pointer-events: none;
        opacity: 0.5;
    }
    #apw-segmenList {
        background: #f2f8ff;
        border-radius: 24px;
        padding: 18px 14px;
        border: 1px solid #cbdbe9;
        display: flex;
        flex-direction: column;
        gap: 20px;
    }
    #apw-wrap .apw-segmen-item {
        background: white;
        border-radius: 20px;
        padding: 16px 18px;
        box-shadow: 0 3px 8px rgba(0, 40, 70, 0.08);
        border-left: 6px solid #4b8fc8;
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 16px;
    }
    #apw-wrap .apw-segmen-index {
        font-weight: 700;
        background: #e1effb;
        color: #004c99;
        padding: 6px 14px;
        border-radius: 30px;
        font-size: 0.9rem;
    }
    #apw-wrap .apw-bobot-control {
        display: flex;
        align-items: center;
        gap: 10px;
        flex: 2 1 200px;
    }
    #apw-wrap .apw-bobot-control label {
        font-weight: 600;
        color: #2b4d6a;
        min-width: 55px;
    }
    #apw-wrap .apw-bobot-control input[type="number"] {
        width: 85px;
        padding: 8px;
        border: 2px solid #bcd4e6;
        border-radius: 26px;
        text-align: center;
        font-weight: 600;
        color: #003366;
        background: #f5faff;
        transition: 0.15s;
    }
    #apw-wrap .apw-bobot-control input[type="number"]:focus {
        border-color: #0077be;
        outline: none;
        background: white;
    }
    #apw-wrap .apw-sudut-control {
        display: flex;
        align-items: center;
        gap: 12px;
        flex: 3 1 260px;
    }
    #apw-wrap .apw-sudut-control label {
        font-weight: 600;
        color: #2b4d6a;
        min-width: 40px;
    }
    #apw-wrap .apw-sudut-control input[type="range"] {
        flex: 1;
        min-width: 140px;
    }
    #apw-wrap .apw-sudut-control span {
        min-width: 45px;
        text-align: right;
        font-weight: 700;
        color: #004182;
    }
    #apw-wrap .apw-radio-group {
        display: flex;
        gap: 30px;
        align-items: center;
        flex-wrap: wrap;
        background: #e9f0f7;
        padding: 15px 20px;
        border-radius: 60px;
    }
    #apw-wrap .apw-radio-group label {
        font-weight: 550;
        color: #1e3b58;
        display: flex;
        align-items: center;
        gap: 9px;
        cursor: pointer;
    }
    #apw-wrap .apw-radio-group input[type="radio"] {
        accent-color: #0066cc;
        width: 18px;
        height: 18px;
    }
    #apw-wrap .apw-panah-control {
        background: #e9f0f7;
        border-radius: 60px;
        padding: 12px 24px;
        display: flex;
        align-items: center;
        gap: 24px;
        flex-wrap: wrap;
    }
    #apw-wrap .apw-panah-control label {
        font-weight: 600;
        color: #144a6f;
        display: flex;
        align-items: center;
        gap: 10px;
    }
    #apw-wrap .apw-panah-control input[type="range"] {
        width: 200px;
    }
    #apw-wrap .apw-panah-control span {
        font-weight: 700;
        color: #0066cc;
        min-width: 30px;
        text-align: center;
    }
    #apw-wrap .apw-button-bar {
        display: flex;
        gap: 12px;
        flex-wrap: wrap;
        justify-content: center;
        margin-top: 8px;
    }
    #apw-wrap .apw-button-bar button {
        background: #0066cc;
        border: 1px solid rgba(255, 255, 255, 0.25);
        color: white;
        padding: 12px 26px;
        border-radius: 60px;
        font-size: 1rem;
        font-weight: 600;
        cursor: pointer;
        transition: 0.2s;
        box-shadow: 0 5px 12px rgba(0, 102, 204, 0.35);
        flex: 1 0 140px;
    }
    #apw-wrap .apw-button-bar button:hover {
        background: #004c99;
        transform: translateY(-3px);
        box-shadow: 0 9px 18px rgba(0, 102, 204, 0.45);
    }
    #apw-wrap .apw-button-bar button:active {
        transform: translateY(1px);
    }
    #apw-wrap #apwStopBtn {
        background: #6c757d;
        box-shadow: 0 5px 10px rgba(108, 117, 125, 0.3);
    }
    #apw-wrap #apwStopBtn:hover {
        background: #5a6268;
    }
    #apw-wrap #apwResetBtn {
        background: #28a745;
        box-shadow: 0 5px 10px rgba(40, 167, 69, 0.3);
    }
    #apw-wrap #apwResetBtn:hover {
        background: #218838;
    }
    #apw-wrap .apw-info-panel {
        background: #e2eaf2;
        border-radius: 60px;
        padding: 12px 22px;
        text-align: center;
        color: #113750;
        font-size: 0.95rem;
        border: 1px solid #b8cfe4;
        display: flex;
        justify-content: space-between;
        flex-wrap: wrap;
    }
    #apw-wrap .apw-total-bobot {
        background: #004a7c;
        color: white;
        border-radius: 40px;
        padding: 4px 16px;
    }
    #apw-wrap footer {
        text-align: center;
        font-size: 0.8rem;
        color: #6a7e94;
        margin-top: 24px;
    }
</style>

<div id="apw-wrap">
    <div class="apw-container">
        <h2> Aliran Banyak Panah <span>multi‑arah + kedip</span></h2>

        <canvas height="360" id="apwCanvas" width="640"></canvas>

        <div class="apw-control-panel">
            <div class="apw-slider-group">
                <label> Panjang total garis (piksel)</label>
                <div class="apw-slider-row">
                    <input id="apwPanjangSlider" max="280" type="range" value="150" />
                    <span id="apwPanjangDisplay">150 px</span>
                </div>
            </div>

            <div class="apw-segmen-header">
                <h3> Ruas garis &amp; sudut belok</h3>
                <div style="display: flex; gap: 8px">
                    <button class="apw-btn-tambah" id="apwTambahSegmenBtn">➕ Tambah ruas</button>
                    <button class="apw-btn-hapus" disabled="disabled" id="apwHapusSegmenBtn">✖ Hapus terakhir</button>
                </div>
            </div>

            <div id="apw-segmenList"></div>

            <div class="apw-radio-group">
                <label
                    ><input checked="checked" id="apwForwardRadio" name="apwArahPanah" type="radio" value="forward" />
                    ▶ Forward (awal → akhir)</label
                >
                <label
                    ><input id="apwBackwardRadio" name="apwArahPanah" type="radio" value="backward" /> ◀ Backward
                    (akhir → awal)</label
                >
            </div>

            <div class="apw-panah-control">
                <label> Jumlah panah <span id="apwJmlPanahValue">3</span></label>
                <input id="apwJmlPanahSlider" max="10" type="range" value="3" />
                <span>⚡ aliran</span>
            </div>

            <div class="apw-button-bar">
                <button id="apwStartBtn">▶ Mulai Aliran</button>
                <button id="apwStopBtn">⏸ Berhenti</button>
                <button id="apwResetBtn">↻ Reset ke Awal</button>
            </div>

            <div class="apw-info-panel">
                <span>✨ Bobot total: <span id="apwTotalBobotSpan">0</span></span>
                <span class="apw-total-bobot" id="apwBobotNormalInfo">(dinormalkan ke 100%)</span>
            </div>
        </div>

        <footer>panah menghadap arah gerak · berkedip · jumlah panah dapat diatur</footer>
    </div>
</div>

<script>
    (function () {
        var canvas = document.getElementById("apwCanvas");
        var ctx = canvas.getContext("2d");
        var panjangSlider = document.getElementById("apwPanjangSlider");
        var panjangDisplay = document.getElementById("apwPanjangDisplay");
        var forwardRadio = document.getElementById("apwForwardRadio");
        var backwardRadio = document.getElementById("apwBackwardRadio");
        var jmlPanahSlider = document.getElementById("apwJmlPanahSlider");
        var jmlPanahValue = document.getElementById("apwJmlPanahValue");
        var startBtn = document.getElementById("apwStartBtn");
        var stopBtn = document.getElementById("apwStopBtn");
        var resetBtn = document.getElementById("apwResetBtn");
        var tambahSegmen = document.getElementById("apwTambahSegmenBtn");
        var hapusSegmen = document.getElementById("apwHapusSegmenBtn");
        var segmenListDiv = document.getElementById("apw-segmenList");
        var totalBobotSpan = document.getElementById("apwTotalBobotSpan");
        var panjangTotal = 150;
        var arahPanah = 1;
        var progressUtama = 0.0;
        var jumlahPanah = 3;
        var animActive = false;
        var animId = null;
        var SPEED = 0.004;
        var segmen = [
            { bobot: 50, sudut: 0 },
            { bobot: 30, sudut: 45 },
            { bobot: 20, sudut: -30 },
        ];
        var MAX_SEGMEN = 8;

        function renderSegmenList() {
            var html = "";
            for (var i = 0; i < segmen.length; i++) {
                var s = segmen[i];
                var isFirst = i === 0;
                html += '<div class="apw-segmen-item" data-index="' + i + '">';
                html += '<div class="apw-segmen-index">Ruas ' + (i + 1) + "</div>";
                html += '<div class="apw-bobot-control"><label>Bobot</label>';
                html +=
                    '<input type="number" min="1" max="500" step="1" value="' +
                    s.bobot +
                    '" class="apw-bobot-input" data-index="' +
                    i +
                    '"></div>';
                if (!isFirst) {
                    html += '<div class="apw-sudut-control"><label>Sudut</label>';
                    html +=
                        '<input type="range" min="-180" max="180" value="' +
                        s.sudut +
                        '" step="1" class="apw-sudut-slider" data-index="' +
                        i +
                        '">';
                    html += '<span class="apw-sudut-value" data-index="' + i + '">' + s.sudut + "&deg;</span></div>";
                } else {
                    html +=
                        '<div style="min-width:140px;color:#2f5e8a;font-weight:500;background:#e2f0fa;padding:6px 18px;border-radius:30px;">&#8614; arah awal 0&deg;</div>';
                }
                html += "</div>";
            }
            segmenListDiv.innerHTML = html;
            document.querySelectorAll(".apw-bobot-input").forEach(function (inp) {
                inp.addEventListener("input", function () {
                    var idx = parseInt(this.dataset.index);
                    var val = parseInt(this.value);
                    if (isNaN(val) || val < 1) val = 1;
                    if (val > 1000) val = 1000;
                    segmen[idx].bobot = val;
                    updateTotalBobot();
                    drawScene();
                });
            });
            document.querySelectorAll(".apw-sudut-slider").forEach(function (slider) {
                slider.addEventListener("input", function () {
                    var idx = parseInt(this.dataset.index);
                    var val = parseInt(this.value);
                    segmen[idx].sudut = val;
                    var sp = document.querySelector('.apw-sudut-value[data-index="' + idx + '"]');
                    if (sp) sp.innerText = val + "\u00b0";
                    drawScene();
                });
            });
            updateTotalBobot();
            hapusSegmen.disabled = segmen.length <= 1;
        }

        function updateTotalBobot() {
            var total = segmen.reduce(function (acc, s) {
                return acc + s.bobot;
            }, 0);
            totalBobotSpan.innerText = total;
        }

        function tambahRuas() {
            if (segmen.length >= MAX_SEGMEN) {
                alert("Maksimal " + MAX_SEGMEN + " ruas.");
                return;
            }
            segmen.push({ bobot: 30, sudut: 0 });
            renderSegmenList();
            drawScene();
        }

        function hapusRuasTerakhir() {
            if (segmen.length <= 1) return;
            segmen.pop();
            renderSegmenList();
            drawScene();
        }

        function hitungTitik(startX, startY) {
            var totalBobot = segmen.reduce(function (acc, s) {
                return acc + s.bobot;
            }, 0);
            if (totalBobot === 0) return [{ x: startX, y: startY }];
            var points = [{ x: startX, y: startY }];
            var arahKum = 0;
            var currentX = startX;
            var currentY = startY;
            for (var i = 0; i < segmen.length; i++) {
                var s = segmen[i];
                var panjangRuas = panjangTotal * (s.bobot / totalBobot);
                if (i > 0) arahKum += (s.sudut * Math.PI) / 180;
                currentX += panjangRuas * Math.cos(arahKum);
                currentY += panjangRuas * Math.sin(arahKum);
                points.push({ x: currentX, y: currentY });
            }
            return points;
        }

        function hitungPanjangLintasan() {
            var totalBobot = segmen.reduce(function (acc, s) {
                return acc + s.bobot;
            }, 0);
            return totalBobot > 0 ? panjangTotal : 0;
        }

        function getPointAndAngleAtProgress(progress) {
            var startX = 240,
                startY = 180;
            var points = hitungTitik(startX, startY);
            if (points.length < 2) return { x: startX, y: startY, angle: 0 };
            var totalBobot = segmen.reduce(function (acc, s) {
                return acc + s.bobot;
            }, 0);
            if (totalBobot === 0) return { x: startX, y: startY, angle: 0 };
            var panjangRuasList = segmen.map(function (s) {
                return panjangTotal * (s.bobot / totalBobot);
            });
            var totalPanjang = panjangRuasList.reduce(function (a, b) {
                return a + b;
            }, 0);
            var targetPanjang = progress * totalPanjang;
            if (targetPanjang < 0) return { x: points[0].x, y: points[0].y, angle: 0 };
            if (targetPanjang >= totalPanjang) {
                var last = points.length - 1;
                return { x: points[last].x, y: points[last].y, angle: 0 };
            }
            var accum = 0;
            for (var i = 0; i < panjangRuasList.length; i++) {
                var pj = panjangRuasList[i];
                if (targetPanjang <= accum + pj || i === panjangRuasList.length - 1) {
                    var sisa = targetPanjang - accum;
                    var t = pj === 0 ? 0 : sisa / pj;
                    var x = points[i].x + (points[i + 1].x - points[i].x) * t;
                    var y = points[i].y + (points[i + 1].y - points[i].y) * t;
                    var angle = Math.atan2(points[i + 1].y - points[i].y, points[i + 1].x - points[i].x);
                    return { x: x, y: y, angle: angle };
                }
                accum += pj;
            }
            return { x: points[points.length - 1].x, y: points[points.length - 1].y, angle: 0 };
        }

        function drawScene() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            var startX = 240,
                startY = 180;
            var points = hitungTitik(startX, startY);
            var blink = 0.4 + 0.6 * Math.sin(Date.now() / 200);
            ctx.save();
            ctx.globalAlpha = blink;
            if (points.length >= 2) {
                ctx.beginPath();
                ctx.moveTo(points[0].x, points[0].y);
                for (var i = 1; i < points.length; i++) ctx.lineTo(points[i].x, points[i].y);
                ctx.strokeStyle = "#c2185b";
                ctx.lineWidth = 4.5;
                ctx.lineJoin = "round";
                ctx.lineCap = "round";
                ctx.shadowColor = "#b0bec5";
                ctx.shadowBlur = 8;
                ctx.stroke();
                ctx.shadowBlur = 0;
            }
            for (var i = 0; i < points.length; i++) {
                ctx.beginPath();
                ctx.arc(points[i].x, points[i].y, i === 0 || i === points.length - 1 ? 6 : 5, 0, 2 * Math.PI);
                ctx.fillStyle = i === 0 ? "#0b5e42" : i === points.length - 1 ? "#b03e3e" : "#1976d2";
                ctx.shadowBlur = 8;
                ctx.shadowColor = "#333";
                ctx.fill();
            }
            ctx.shadowBlur = 0;
            ctx.restore();
            var totalPanjang = hitungPanjangLintasan();
            if (totalPanjang === 0) return;
            var step = 1 / jumlahPanah;
            for (var i = 0; i < jumlahPanah; i++) {
                var p = progressUtama - i * step;
                p = p - Math.floor(p);
                var pt = getPointAndAngleAtProgress(p);
                var orientasi = arahPanah === 1 ? pt.angle : pt.angle + Math.PI;
                ctx.save();
                ctx.translate(pt.x, pt.y);
                ctx.rotate(orientasi);
                ctx.fillStyle = "#FFD966";
                ctx.shadowColor = "black";
                ctx.shadowBlur = 8;
                ctx.beginPath();
                ctx.moveTo(12, 0);
                ctx.lineTo(-8, -6);
                ctx.lineTo(-8, 6);
                ctx.closePath();
                ctx.fill();
                ctx.restore();
            }
            ctx.font = 'bold 12px "Segoe UI",monospace';
            ctx.fillStyle = "#1a2c3f";
            ctx.fillText("progress: " + (progressUtama * 100).toFixed(1) + "%", 12, 30);
            ctx.fillText("arah: " + (arahPanah === 1 ? "\u2192" : "\u2190"), 12, 55);
            ctx.fillText("panah: " + jumlahPanah, 12, 80);
        }

        function animationStep() {
            if (!animActive) return;
            progressUtama += SPEED * arahPanah;
            if (progressUtama > 1.0) progressUtama -= 1.0;
            else if (progressUtama < 0.0) progressUtama += 1.0;
            drawScene();
            animId = requestAnimationFrame(animationStep);
        }

        function startAnimation() {
            if (!animActive) {
                animActive = true;
                if (animId) cancelAnimationFrame(animId);
                animId = requestAnimationFrame(animationStep);
            }
        }

        function stopAnimation() {
            if (animActive) {
                animActive = false;
                if (animId) {
                    cancelAnimationFrame(animId);
                    animId = null;
                }
                drawScene();
            }
        }

        panjangSlider.addEventListener("input", function () {
            panjangTotal = parseInt(this.value);
            panjangDisplay.innerText = panjangTotal + " px";
            drawScene();
        });
        forwardRadio.addEventListener("change", function () {
            if (forwardRadio.checked) arahPanah = 1;
            drawScene();
        });
        backwardRadio.addEventListener("change", function () {
            if (backwardRadio.checked) arahPanah = -1;
            drawScene();
        });
        jmlPanahSlider.addEventListener("input", function () {
            jumlahPanah = parseInt(this.value);
            jmlPanahValue.innerText = jumlahPanah;
            drawScene();
        });
        startBtn.addEventListener("click", function () {
            arahPanah = forwardRadio.checked ? 1 : -1;
            startAnimation();
        });
        stopBtn.addEventListener("click", stopAnimation);
        resetBtn.addEventListener("click", function () {
            progressUtama = 0.0;
            if (!animActive) drawScene();
        });
        tambahSegmen.addEventListener("click", tambahRuas);
        hapusSegmen.addEventListener("click", hapusRuasTerakhir);

        renderSegmenList();
        panjangDisplay.innerText = panjangTotal + " px";
        jmlPanahValue.innerText = jumlahPanah;
        drawScene();
    })();
</script>

Comments

Popular posts from this blog

Utk yg mo Bantu2 Keuangan saya
..monggo ke Bank Central Asia BCA 5520166779 a.n. Andreas Tparlaungan Manurung (Indonesia)


For those who would like to help support my finances
..please feel free to send it to Bank Central Asia (BCA) account number 5520166779 under the name Andreas Tparlaungan Manurung (Indonesia)

ANDREAS TOMMY PARLAUNGAN MANURUNG SHARED POOLING ACCOUNT MY ANDROID APKs PAGE please download here! REFRESH PAGE aka CHECK LATEST UPDATE! DOWNLOAD "SHOWING" POOL OF MY ANDROID-APK(s) aka APK CONTAINING LIST OF ALL MY ANDROID-APK(s) APP CLICK HERE FOR ALWAYS BEING UPDATED FOR MY LATEST APK! CONTOH HASIL "PROGRAM" App: Prompts' Guide aka TEMPLATE-HELPERs click here to download! Youtube and Instagram EMBEDded to Blogger/Blogspot.com SOURCE CODE Click this box to download 📥 TikTok EMBEDded to Blogger/Blogspot.com SOURCE CODE Input: BrowserLINK (mandatory) Click this box to download SHORTCUT-APPs note :  "precise" click to download R8: ronin1985.blogspot.com R2M: ronin-manu.blogspot.com Helping Download(ing) OnlineVIDEO! ...

[ERROR BUG]
ChatGPT+Gemini: TikTok → Blogger Embed Converter using Cloudflare/Online Server

🔄 Refresh Page ERROR BUG: The connection is blocked because it was initiated by a public page to connect to devices or servers on your local network. Planning: Revise Program CODE Code USING Javascript/Online Server Code NOT USING Javascript Sample Working Code aka Already Repaired! Temporary Solution is by Asking AI Assistant to do REPAIR CODE of (Not yet Repaired) Current Conversion Program Code-Output TikTok Archive – Embedded Preview TikTok Embed ▶ View this video on TikTok ⚠️ DISCLAIMER: INPUT URL LIMITATION This program is currently restricted to processing Full Browser URLs only. It does not support TikTok’s mobile "short-link" format (e.g., vt.tiktok.com ). Required Action: Users must open the video in a web browser and copy the expanded URL from the address bar before pasting it into this program. URL Conversion Example ❌ UNSUPPORTED: https://vt.tiktok.com/ZSaXoFyov/ ✅ REQ...

REPOST: Studying WATER PUMP by ROMAN ENGINEERING

*^ Ini yg Asli Gan! Mekanisme pada Concrete Pump: Kok ky Sistem Mekanik Romawi ya?! Tapi malah bisa HANYA pake PER aka bukan "MATA BOR look a like" Mekanisme Drill yg Cost Pembuatan bikin REPOT aka harus Tool SUPER Khusus Dari Material Besi yg digunakan terlihat langsung secara kasat mata Jauh Lebih Banyak drpd Per Biasa seperti yg ditunjukkan pd Video Alternatif dgn Penggunaan PER Video dr Instagram: Source: YouTube Rome's drainage machines #history #romanempire #engineering