Thursday 30 September 2021

glb not showing up in three.js

So I have a glb that I just isnt showing up in the scene no matter where i position it maybe it's the code? (as i've tried glb's i know work with the same code and non appear) here are the relevant code parts:

var loader2 = new GLTFLoader();
   loader2.load( 'https://jokertattoo.co.uk/gun.glb', function ( gltfgame ) {
     
     hammer = gltfgame.scene;
     
        gltfgame.scene.traverse( function ( child ) {

            if ( child.isMesh ) {
                child.frustumCulled = false;
                child.castShadow = true;
                child.receiveShadow = true;
            }

        });

     hammer.position.y = 120;
                scene.add(hammer);
       });

and:

function hammerLoaded( geometry ) {
    geometry.computeVertexNormals();
                geometry.computeFaceNormals();
            


                hammer.position.y = 120;
                scene.add(hammer);

                var wood = new THREE.MeshPhongMaterial( { color: 0xffffff, map: THREE.ImageUtils.loadTexture( "284-v7.jpg"), shininess: 100, side: THREE.DoubleSide } );
                geometry.materials[0] = wood;

                var metal = new THREE.MeshPhongMaterial( { color: 0x222222, specular: 0x888888, shininess: 10, metal: true } );
                geometry.materials[1] = metal;

                
                var h = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial() );
                h.castShadow = true;
                h.receiveShadow = false;    
                
                var scale = 3.5;
                h.scale.set(scale,scale,scale);
                h.rotation.x = Math.PI/2;
                h.rotation.z = -Math.PI/2;              
                h.position.y = 12.0;
                h.position.z = -26.5;
                h.position.x = -6.2;
                hammer.add(h);
       
            }

heres a codepen also https://codepen.io/uiunicorn/pen/YzQjJmp

thanks for reading



from glb not showing up in three.js

photos.google.com/activities/photo_likes - call in javascript

https://photos.google.com/activities/photos_likes returns likes photos in shared albums. I would like to use the photoslibrary api or the activities api to find liked photos in shared albums.

However, I cannot find the documentation on how to do this using activities api or from the photoslibrary api. I have tried the photoslibrary api mediaitems.search, however, it does not seem to include photos_likes as a metadata value.

Which metadata value or tag from the photoslibrary api mediaitems.search can I use to find liked photos in shared albums? if this is the wrong api, which one should I use?



from photos.google.com/activities/photo_likes - call in javascript

RecyclerView swipe functionality breaks after orientation change

I created a RecyclerView that refreshes its list based on a database call. Each row has an options menu that is revealed when the user swipes. My original issue was that after an orientation change, the swipe gestures no longer revealed the menu. I hit all my expected breakpoints with onCreateViewHolder() and the onSwipe(). However, the row remained as the HIDE_MENU view type after swiping.

So I tried to introduce LiveData to persist the state of the list after orientation changes. The RecyclerView was still created and populated with items but now the swipe gesture crashes the application with an error:

java.lang.IndexOutOfBoundsException: Index: 0, Size: 0

Do I need to use LiveData to fix the original issue of preserving my swipe functionality after orientation changes? If not, please can someone explain why the item view types are no longer updated after orientation changes.

If I do need to use a ViewModel, what am I doing that is causing the list adapter not to receive the updated list?

HistoryFragment

class HistoryFragment : Fragment() {
    private val historyViewModel by activityViewModels<HistoryViewModel>()

    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        val root = inflater.inflate(R.layout.fragment_history, container, false)
        historyViewModel.getHistoryList().observe(viewLifecycleOwner, {
            refreshRecyclerView(it)
        })
        return root
    }
    
    private fun updateHistoryList() {
        val dbHandler = MySQLLiteDBHandler(requireContext(), null)
        val historyList = dbHandler.getHistoryList() as MutableList<HistoryObject>
        historyViewModel.setHistoryList(historyList)
    }

    private fun refreshRecyclerView(historyList: MutableList<HistoryObject>) {
        val historyListAdapter = HistoryListAdapter(historyList)
        val callback = HistorySwipeHelper(historyListAdapter)
        val helper = ItemTouchHelper(callback)
        history_list.adapter = historyListAdapter
        helper.attachToRecyclerView(history_list)
    }
    
    private fun setupSort() {
        val sortSpinner: Spinner = history_list_controls_sort
        sortSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onNothingSelected(parent: AdapterView<*>?) {}
            override fun onItemSelected(
                parent: AdapterView<*>?,
                view: View?,
                position: Int,
                id: Long
            ) {
                updateHistoryList()
            }
        }
    }

    override fun onViewCreated(
        view: View,
        savedInstanceState: Bundle?
    ) {
        setupSort()
    }
}

HistoryListAdapter

const val SHOW_MENU = 1
const val HIDE_MENU = 2

class HistoryListAdapter(private var historyData: MutableList<HistoryObject>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return if (viewType == SHOW_MENU) {
            val inflatedView = LayoutInflater.from(parent.context).inflate(R.layout.history_list_view_row_items_menu, parent, false)
            MenuViewHolder(inflatedView)
        } else {
            val inflatedView = LayoutInflater.from(parent.context).inflate(R.layout.history_list_view_row_items_description, parent, false)
            HistoryItemViewHolder(inflatedView)
        }
    }

    override fun getItemViewType(position: Int): Int {
        return if (historyData[position].showMenu) {
            SHOW_MENU
        } else {
            HIDE_MENU
        }
    }

    override fun getItemCount(): Int {
        return historyData.count()
    }

    fun showMenu(position: Int) {
        historyData.forEachIndexed { idx, it ->
            if (it.showMenu) {
                it.showMenu = false
                notifyItemChanged(idx)
            }
        }
        historyData[position].showMenu = true
        notifyItemChanged(position)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val item: HistoryObject = historyData[position]
        if (holder is HistoryItemViewHolder) {
            holder.bindItem(item)
            ...
        }

        if (holder is MenuViewHolder) {
            holder.bindItem(item)
            ...
        }
    }

    class HistoryItemViewHolder(v: View, private val clickHandler: (item: HistoryObject) -> Unit) : RecyclerView.ViewHolder(v) {
        private var view: View = v
        private var item: HistoryObject? = null

        fun bindItem(item: HistoryObject) {
            this.item = item
            ...
        }
    }

    class MenuViewHolder(v: View, private val deleteHandler: (item: HistoryObject) -> Unit) : RecyclerView.ViewHolder(v) {
        private var view: View = v
        private var item: HistoryObject? = null

        fun bindItem(item: HistoryObject) {
            this.item = item
            ...
        }
    }
}

HistorySwipeHelper

class HistorySwipeHelper(private val adapter: HistoryListAdapter) : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
    override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { return false }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        adapter.showMenu(viewHolder.adapterPosition)
    }

    override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float {
        return 0.1f
    }
}

HistoryViewModel

class HistoryViewModel(private var historyListHandle: SavedStateHandle) : ViewModel() {
    fun getHistoryList(): LiveData<MutableList<HistoryObject>> {
        return historyListHandle.getLiveData(HISTORY_LIST_KEY)
    }

    fun setHistoryList(newHistoryList: MutableList<HistoryObject>) {
        historyListHandle.set(HISTORY_LIST_KEY, newHistoryList)
    }

    companion object {
        const val HISTORY_LIST_KEY = "MY_HISTORY_LIST"
    }
}

Activity

class MainActivity : AppCompatActivity() {
    private val historyViewModel: HistoryViewModel by lazy {
        ViewModelProvider(this).get(HistoryViewModel::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        historyViewModel.setHistoryList(mutableListOf())
    }
}

Thanks in advance. If this question is too broad I can try again and decompose it.



from RecyclerView swipe functionality breaks after orientation change

Limit number of ads in Google custom search

I am considering using Google Custom Search for my site. It seem to be pretty customizable and I can match my site's style. The only thing that holds e back is obscene number of ads shown above my results - 4.

check out this similar Example and screenshot below:

screenshot

I found documentation that seems to indicate that I can control this

'maxTop' : 4

but I can't figure out for the world where I could use this setting since the only code I include looks like:

<script async src="https://cse.google.com/cse.js?cx=e14513e5dxxxxxx"></script>
<div class="gcse-search"></div>

Google documentation

Also, important fact is that the Custom Search is a part of Google Ads and I utilize Google Tag to include all-Google.



from Limit number of ads in Google custom search

drf-yasg cover all responses to following implemented standard api response

I did implement the standard API response such as this article in my app. And I also implement drf-yasg for my API documentation. As we know the schema are using directly serializer to present the response. How I can cover all these below response examples by using drf-yasg schema?

1. Success Single

{
   "status": 200,                       # int : http status, can be 201, 200, etc.
   "success": true,                     # bool: boolean to identify the response is success or failed.
   "message": "The success message",    # str : string success message or null
   "result": {}                         # dict: a dict response data
}

2. Success List

{
   "status": 200,      # int : http status
   "success": true,    # bool: boolean to identify the response is success or failed.
   "message": null,    # str : string message or null.
   "results": [],      # arr : a list/array

   "count": 2,                                                   # int: all total result items
   "page_size": 5,                                               # int: maximum items per-page
   "current_page": 1,                                            # int: your current page
   "next": "http://127.0.0.1:8000/api/page/?page=2&search=a",    # str: string link or null.
   "previous": null                                              # str: string link or null.
}

3. Error or Failed

{
   "status": 400,                      # int : http status, can be 403,404,500,etc..
   "success": false,                   # bool: boolean to identify the response is success or failed.
   "message": "The failed message",    # str : string failed message or null
   "result": {}                        # dict: a dict response data
}

Currently I just did implement the general schema from drf_yasg, and still don't know how to do that.

from django.urls import path
from django.conf import settings

from rest_framework import permissions, authentication
from rest_framework.settings import api_settings
from rest_framework.routers import SimpleRouter

from drf_yasg import openapi
from drf_yasg.views import get_schema_view

from myproject.users.api.views import UserViewSet, AuthLoginView
from myproject.storage.api.views import FileViewSet

router = SimpleRouter()
router.register('users', UserViewSet, basename='api_users')
router.register('files', FileViewSet, basename='api_files')

app_name = 'api'
urlpatterns = [
    path('auth/login/', AuthLoginView.as_view(), name='api_auth_login'),
] + router.urls

if settings.DEBUG:
    swagger_info = openapi.Info(
        title='My Project API',
        default_version=api_settings.DEFAULT_VERSION,
        description='This is documentation of My Project open API',
        terms_of_service='https://foobar.com/tos/',
        contact=openapi.Contact(email='help@foobar.com'),
        license=openapi.License(name='Proprietary and confidential'),
    )
    schema_view = get_schema_view(
        info=swagger_info,
        public=True,
        permission_classes=(permissions.IsAdminUser,),
        authentication_classes=(authentication.SessionAuthentication,)
    )
    urlpatterns += [
        path('', schema_view.with_ui('swagger', cache_timeout=None), name='schema-swagger-ui'),
    ]


from drf-yasg cover all responses to following implemented standard api response

Unpickled tensorflow model fails to make predictions

I've seen this question and this one, but neither actually explain what is going on, nor offer a solution to the problem I'm facing.

The code below is a snippet from what I'm trying to do in a larger context. Basically, I'm creating an object that contains a tensorflow.keras model, I'm saving it to a file with pickle using a trick adapted from this answer. See the code below ReproduceProblem.py:

import pickle
import numpy as np
import tempfile
import tensorflow as tf


def __getstate__(self):
    model_str = ""
    with tempfile.NamedTemporaryFile(suffix=".hdf5", delete=False) as fd:
        tf.keras.models.save_model(self, fd.name, overwrite=True)
        model_str = fd.read()
    d = {"model_str": model_str}
    return d


def __setstate__(self, state):
    with tempfile.NamedTemporaryFile(suffix=".hdf5", delete=False) as fd:
        fd.write(state["model_str"])
        fd.flush()
        model = tf.keras.models.load_model(fd.name)
    self.__dict__ = model.__dict__


class ContainsSequential:
    def __init__(self):
        self.model = tf.keras.models.Sequential()
        self.model.__getstate__ = lambda mdl=self.model: __getstate__(mdl)
        self.model.__setstate__ = __setstate__
        self.model.add(tf.keras.layers.Input(shape=(None, 3)))
        self.model.add(tf.keras.layers.LSTM(3, activation="relu", return_sequences=True))
        self.model.add(tf.keras.layers.Dense(3, activation="linear"))


# Now do the business:
tf.keras.backend.clear_session()
file_name = 'pickle_file.pckl'
instance = ContainsSequential()
instance.model.predict(np.random.rand(3, 1, 3))
with open(file_name, 'wb') as fid:
    pickle.dump(instance, fid)
with open(file_name, 'rb') as fid:
    restored_instance = pickle.load(fid)
restored_instance.model.predict(np.random.rand(3, 1, 3))
print('Done')

While is does not fail on the line instance.model.predict(np.random.rand(3, 1, 3)) it does fail on the line restored_instance.model.predict(np.random.rand(3, 1, 3)), the error message is:

  File "<path>\ReproduceProblem.py", line 52, in <module>
    restored_instance.model.predict(np.random.rand(3, 1, 3))
  File "<path>\Python\Python39\lib\site-packages\keras\engine\training.py", line 1693, in predict
    if self.distribute_strategy._should_use_with_coordinator:  # pylint: disable=protected-access
  File "<path>\Python\Python39\lib\site-packages\keras\engine\training.py", line 716, in distribute_strategy
    return self._distribution_strategy or tf.distribute.get_strategy()
AttributeError: 'Sequential' object has no attribute '_distribution_strategy'

I don't have the slightest idea of what _distribution_strategy should be, but in my workflow, once I've saved the file I don't need to train it anymore, just use it to make predictions or consult other attributes of the class. I've tried setting it to Noneand adding more attributes, but with no success.



from Unpickled tensorflow model fails to make predictions

How to queue requests in Django?

I manage a physical locker with Django (DRF). Users fill out a form, authenticate via link sent to their e-mail, authorize via a pin displayed on the locker.

My view should handle three cases:

  • If user authenticates and authorizes successfully, pin displayed on the locker is replaced with a generic message and the locker opens. (Already implemented)

  • If the user fails to authorize within 3 minutes, locker pin is replaced with a generic message.

  • If a new authorization request is made by user Foo, while authorization for user Bar is still incomplete, stack the request in a queue and wait for case 1. or case 2. to complete.

How can I:

  • Implement a request queue, such that the pin displayed on the locker does not get overridden/replaced when a new request comes in?
  • How can I wait 3 minutes for authorization to be completed before processing the next request?

View as is, in case it is useful:

   if request.method == 'POST':
        form = ConfirmationForm(request.POST)
        if form.is_valid():
            if pin == form.cleaned_data['pin']:
                open_bay(jwt_token=jwt[1], pin=pin)
                display_generic_message(jwt_token=jwt[1])
                lock_bay(jwt_token=jwt[1], pin=pin)
                return render(request, 'static/pages/request-success.html')
            else:
                pass
    else:
        form = ConfirmationForm()
    return render(request, 'static/pages/confirmation.html', {'form': form})


from How to queue requests in Django?

Download pixel art drawing as .png in PhaserJS

I need to download the created pixel drawing from this Phaser example as a .png image via FilesSaver.js but the canvas returns null.

Error:

Uncaught TypeError: Cannot read properties of null (reading 'toBlob')

This is the save function:

function save() { 
var canvasX = document.getElementById("canvas");
canvasX.toBlob(function(blob) { saveAs(blob, "image.png"); }); }

drawingArea: (PhaserJS 2)

function createDrawingArea() {

    game.create.grid('drawingGrid', 16 * canvasZoom, 16 * canvasZoom, canvasZoom, canvasZoom, 'rgba(0,191,243,0.8)');

    canvas = game.make.bitmapData(spriteWidth * canvasZoom, spriteHeight * canvasZoom);
    canvasBG = game.make.bitmapData(canvas.width + 2, canvas.height + 2);

    canvasBG.rect(0, 0, canvasBG.width, canvasBG.height, '#fff');
    canvasBG.rect(1, 1, canvasBG.width - 2, canvasBG.height - 2, '#3f5c67');

    var x = 10;
    var y = 64;

    canvasBG.addToWorld(x, y);
    canvasSprite = canvas.addToWorld(x + 1, y + 1);
    canvasGrid = game.add.sprite(x + 1, y + 1, 'drawingGrid');
    canvasGrid.crop(new Phaser.Rectangle(0, 0, spriteWidth * canvasZoom, spriteHeight * canvasZoom));

}

How to get the data of the drawing to create a .png out of it?



from Download pixel art drawing as .png in PhaserJS

Figuring the required Python modules and their versions of a Python process

I have a tool which follows the system calls of a process. That way I know all the files/areas that were using by a process. I have a Python script which being executed (creates a process). I know all the files that were used during the run, such as the script itself. I also know the files of the modules that were used. The modules are installed in /tmp/vendor.

Based on the files inside /tmp/vendor that I found, I'm trying to figure the module name and module version so I could create a requirements file for the pip and then install them using pip install (to some other directory). Basically, I want to be able to know all the module dependencies of a Python process. Those modules could come from different areas but let's focus on one (/tmp/vendor). The way I installed the modules into /tmp/vendor is just:

pip install --requirement requirements.txt --target /tmp/vendor

Now I want I to be able to build this requirements.txt file, based on the files in /tmp/vendor.

The solution could be dynamic or static. At first I tried to solve it in a static way - check the files in /tmp/vendor. I did an example - I installed requests:

pip install requests --target /tmp/vendor

As I understand, it installs the latest version. Inside vendor I have:

ls -la vendor/
total 52
drwxr-x--- 13 user group 4096 Sep 26 17:37 .
drwxr-x---  8 user group 4096 Sep 26 17:37 ..
drwxr-x---  2 user group 4096 Sep 26 17:37 bin
drwxr-x---  3 user group 4096 Sep 26 17:37 certifi
drwxr-x---  2 user group 4096 Sep 26 17:37 certifi-2021.5.30.dist-info
drwxr-x---  5 user group 4096 Sep 26 17:37 charset_normalizer
drwxr-x---  2 user group 4096 Sep 26 17:37 charset_normalizer-2.0.6.dist-info
drwxr-x---  3 user group 4096 Sep 26 17:37 idna
drwxr-x---  2 user group 4096 Sep 26 17:37 idna-3.2.dist-info
drwxr-x---  3 user group 4096 Sep 26 17:37 requests
drwxr-x---  2 user group 4096 Sep 26 17:37 requests-2.26.0.dist-info
drwxr-x---  6 user group 4096 Sep 26 17:37 urllib3
drwxr-x---  2 user group 4096 Sep 26 17:37 urllib3-1.26.7.dist-info

Now I can see that it also installs other modules that are needed, such as urllib3 and idna.
So my tool finds for example, that I were using:

/tmp/vendor/requests/utils.py

I also notice that each module is in format:

$NAME-(.*).dist-info

And the group is the version of the module. So at first I thought that I could parse for /tmp/vendor/(.*)/.* and get the module name ($NAME) and then look for $NAME-(.*).dist-info, but the problem is that I noticed that some module don't have this *.dist-info directory so I could not figure the version of the module, which made me leave this approach.

I also tried some dynamic approaches - I know which python version was used and I could run python and try to load the module. But I could not figure a way to find the version of the module.

To summarize - I'm looking for a robust way to figure the modules the are required for my Python process in order to run. The modules should come with their version. All of the modules were installed using pip so it should simplify the task. How can it be done?



from Figuring the required Python modules and their versions of a Python process

How to auto remove/kick members from telegram channel or group?

I have telegram channel/group where i add members manually and i remove/kick users manually after one/two/three month. Is there any way to do this task automatically, I mean can we remove/kick user from channel/group automatically or by scheduling?

Any sample python code will help for understanding. Thank you...!



from How to auto remove/kick members from telegram channel or group?

How to hint at number *types* (i.e. subclasses of Number) - not numbers themselves?

Assuming I want to write a function that accepts any type of number in Python, I can annotate it as follows:

from numbers import Number

def foo(bar: Number):
    print(bar)

Taking this concept one step further, I am writing functions which accept number types, i.e. int, float or numpy dtypes, as arguments. Currently, I am writing:

from typing import Type

def foo(bar: Type):
    assert issubclass(bar, Number)
    print(bar)

I thought I could substitute Type with something like NumberType (similar to NotImplementedType and friends, re-introduced in Python 3.10), because all number types are subclasses of Number:

from numbers import Number
import numpy as np

assert issubclass(int, Number)
assert issubclass(np.uint8, Number)

As it turns out (or at least as far as I can tell), there is no such thing as a generic NumberType in Python (3.9):

>>> type(Number)
abc.ABCMeta

Is there a clean way (i.e. without runtime checks) to achieve the desired kind of annotation?



from How to hint at number *types* (i.e. subclasses of Number) - not numbers themselves?

Calculating angles of body skeleton in video using OpenPose

Disclaimer: This question is regarding OpenPose but the key here is actually to figure how to use the output (coordinates stored in the JSON) and not how to use OpenPose, so please consider reading it to the end.

I have a video of a person from the side on a bike (profile of him sitting so we see the right side). I use the OpenPose to extract the coordinates of the skeleton. The OpenPose provides the coordinates in a JSON file looking like (see docs for explanation):

{
  "version": 1.3,
  "people": [
    {
      "person_id": [
        -1
      ],
      "pose_keypoints_2d": [
        594.071,
        214.017,
        0.917187,
        523.639,
        216.025,
        0.797579,
        519.661,
        212.063,
        0.856948,
        539.251,
        294.394,
        0.873084,
        619.546,
        304.215,
        0.897219,
        531.424,
        221.854,
        0.694434,
        550.986,
        310.036,
        0.787151,
        625.477,
        339.436,
        0.845077,
        423.656,
        319.878,
        0.660646,
        404.111,
        321.807,
        0.650697,
        484.434,
        437.41,
        0.85125,
        404.13,
        556.854,
        0.791542,
        443.261,
        319.801,
        0.601241,
        541.241,
        370.793,
        0.921286,
        502.02,
        494.141,
        0.799306,
        592.138,
        198.429,
        0.943879,
        0,
        0,
        0,
        562.742,
        182.698,
        0.914112,
        0,
        0,
        0,
        537.25,
        504.024,
        0.530087,
        535.323,
        500.073,
        0.526998,
        486.351,
        500.042,
        0.615485,
        449.168,
        594.093,
        0.700363,
        431.482,
        594.156,
        0.693443,
        386.46,
        560.803,
        0.803862
      ],
      "face_keypoints_2d": [],
      "hand_left_keypoints_2d": [],
      "hand_right_keypoints_2d": [],
      "pose_keypoints_3d": [],
      "face_keypoints_3d": [],
      "hand_left_keypoints_3d": [],
      "hand_right_keypoints_3d": []
    }
  ]
}

From what I understand, each JSON is a frame of the video.

My goal is to detect the angles of specific coordinates like right knee, right arm, etc. For example:

openpose_angles = [(9, 10, 11, "right_knee"),
                   (2, 3, 4,   "right_arm")]

This is based on the following OpenPose skeleton dummy:

enter image description here

What I did is to calculate the angle between three coordinates (using Python):

temp_df = json.load(open(os.path.join(jsons_dir, file)))
listPoints = list(zip(*[iter(temp_df['people'][person_number]['pose_keypoints_2d'])] * 3))

count = 0
lmList2 = {}
for x,y,c in listPoints:
    lmList2[count]=(x,y,c)
    count+=1

p1=angle_cords[0]
p2=angle_cords[1]
p3=angle_cords[2]
x1, y1 ,c1= lmList2[p1]
x2, y2, c2 = lmList2[p2]
x3, y3, c3 = lmList2[p3]

# Calculate the angle
angle = math.degrees(math.atan2(y3 - y2, x3 - x2) -
                     math.atan2(y1 - y2, x1 - x2))
if angle < 0:
    angle += 360

This method I saw on some blog (which I forgot where), but was related to OpenCV instead of OpenPose (not sure if makes the difference), but see angles that do not make sense. We showed it to our teach and he suggested us to use vectors to calculate the angles, instead of using math.atan2. But we got confued on how to implment this.

To summarize, here is the question - What will be the best way to calculate the angles? How to calculate them using vectors?



from Calculating angles of body skeleton in video using OpenPose

how to restrict drag elements in interact.js

hello I'm trying to stop my elements from overlapping using interact.js but I don't have any idea how to get the n elements to be able to do it, does anyone have an idea? or some other way I can validate it.

Try the solution of this question but I don't understand how to get the list of elements to go through it. enter link description here

window.dragMoveListener = dragMoveListener;

    interact('.signer-box')
        .draggable({
            onmove: dragMoveListener,
            inertia: true,
            autoScroll: true,
            restrict: {
                elementRect: {top: 0, left: 0, bottom: 1, right: 1}
            }
        })
        .resizable({
            onmove: resizeMoveListener,
            inertia: true,
            edges: {left: true, right: true, bottom: true, top: true}
        })

function dragMoveListener(event) {
    var target = event.target;
    var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
    var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
    target.style.webkitTransform = target.style.transform = 'translate(' + x + 'px, ' + y + 'px)';
    target.setAttribute('data-x', x);
    target.setAttribute('data-y', y);
    
    computeSignerBoxPosition();
}

function resizeMoveListener(event) {
    var target = event.target;
    var x = (parseFloat(target.getAttribute('data-x')) || 0);
    var y = (parseFloat(target.getAttribute('data-y')) || 0);
    x += event.deltaRect.left;
    y += event.deltaRect.top;

    target.style.width = event.rect.width + 'px';
    target.style.height = event.rect.height + 'px';
    target.style.webkitTransform = target.style.transform = 'translate(' + x + 'px,' + y + 'px)';
    target.setAttribute('data-x', x);
    target.setAttribute('data-y', y);
    
    computeSignerBoxPosition();
}

function computeSignerBoxPosition() {
    var $signatureBox = $('.signer-box');
    var sbDataX = parseFloat($signatureBox.attr('data-x'));
    var sbDataY = parseFloat($signatureBox.attr('data-y'));
    var sbOuterWidth = $signatureBox.outerWidth();
    var sbOuterHeight = $signatureBox.outerHeight();

    var w = $('#pdf-page').width();
    var h = $('#pdf-page').height();
    
    var top = sbDataX / w;
    var left = sbDataY / h;
    var width = sbOuterWidth / w;
    var height = sbOuterHeight / h;

    document.getElementById("widthValue").value = width;
    document.getElementById("heightValue").value = height;
    document.getElementById("coorX").value = top;
    document.getElementById("coorY").value = left;
}
@charset "UTF-8";

#content{
    text-align: center;
}

#pdf-container {
    display: inline-block;
    width: 100%;
    user-select: none;
}

#pdf-page {
    width: 100%;
}

.signer-box {
    background: url('../images/pen_icon.png') #29e no-repeat 50% 50%;
    background-size: 50%;
    color: white;
    font-size: 20px;
    font-family: sans-serif;
    border-radius: 8px;
    width: 180px;
    height: 150px;
    position:absolute;
    opacity: .8;
    box-sizing: border-box;
    box-shadow: rgb(0, 0, 0, 0.7) 0.2em 0.2em 0.5em;
    -ms-touch-action: none;
    touch-action: none; 
}
  
#signature-pad {
    position: relative;
    width: 100%;
    height: 160px;
    -moz-user-select: none;
    -webkit-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

#signatureImg{
    width: 100%;
}
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
   
        <div id="content">
            <div class="wrap">
                   <hr style="border:15px;"><hr style="border:2px;">
                    
                    <div id="wrapper">
                        <div id="content">
                            <div id="pdf-container" >                   
                                <div id="signers-list">
                                  <div id="signers-list">
                                   
                                <div id="signer-1" class="signer-box" data-x="379" data-y="279" style="position: absolute; transform: translate(379px, 279px); width: 148px; height: 90px; --content:&quot;Firma número 1&quot; ;"></div><div id="signer-2" class="signer-box" data-x="17" data-y="30" style="position: absolute; transform: translate(17px, 30px); width: 238px; height: 121px; --content:&quot;Firma número 2&quot; ;"></div></div>
                                </div>                  
                                <img id="pdf-page" src="data:image/png;base64,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WAIAACAsAQAAEJYAAAAISwAAABCWAAAACEsAAACEJQAAAMISAAAAhCUAAADCEgAAAGEJAACAsAQAAABhCQAAgLAEAABAWAIAACAsAQAAEJYAAAAgLAEAABCWAAAACEsAAACEJQAAAAhLAAAAhCUAAADCEgAAAGEJAAAAwhIAAABhCQAAgLAEAABAWAIAAICwBAAAQFgCAAAgLAEAABCWAAAAICwBAAAQlgAAAAhLAAAAhCUAAADCEgAAAIQlAAAAwhIAAABhCQAAgLAEAAAAYQkAAICwBAAAQFgCAAAgLAEAAEBYAgAAICwBAAAQlgAAAAhLAAAAEJYAAAAISwAAAIQlAAAAwhIAAABhCQAAAMISAAAAYQkAAICwBAAAQFgCAACAsAQAAEBYAgAAICwBAAAQlgAAACAsAQAAEJYAAAAISwAAAIQlAAAACEsAAACEJQAAAMISAAAAYQkAAICwBAAAAGEJAACAsAQAAEBYAgAAICwBAABAWAIAACAsAQAAEJYAAAAISwAAABCWAAAACEsAAACEJQAAAMISAAAAhCUAAADCEgAAAGEJAACAsAQAAABhCQAAgLAEAABAWAIAACAsAQAAEJYAAAAgLAEAABCWAAAACEsAAACEJQAAAAhLAAAAhCUAAADCEgAAAGEJAAAAwhIAAABhCQAAgLAEAABAWAIAAICwBAAAQFgCAAAgLAEAABCWAAAACEsAAAAQlgAAAAhLAAAAhCUAAADCEgAAAIQlAAAAwhIAAABhCQAAgLAEAAAAYQkAAICwBAAAQFgCAAAgLAEAAEBYAgAAICwBAAAQlgAAAAhLAAAAhKVDAAAAgLAEAABAWAIAACAsAQAAEJYAAAAgLAEAABCWAAAACEsAAACEJQAAAAhLAAAAhCUAAADCEgAAAGEJAAAAwhIAAABhCQAAgLAEAABAWAIAAICwBAAAQFgCAAAgLAEAABCWAAAACEsAAAAQlgAAAAhLAAAAhCUAAADCEgAAAIQlAAAAwhIAAABhCQAAgLAEAAAAYQkAAICwBAAAQFgCAAAgLAEAAEBYAgAAICwBAAAQlgAAAAhLAAAAhCUAAAAISwAAAIQlAAAAwhIAAABhCQAAAMISAAAAYQkAAICwBAAAQFgCAACAsAQAAEBYAgAAICwBAAAQlgAAACAsAQAAEJYAAAAISwAAAIQlAAAACEsAAACEJQAAAMISAAAAYQkAAICwBAAAAGEJAACAsAQAAEBYAgAAICwBAABAWAIAACAsAQAAEJYAAAAISwAAABCWAAAACEsAAACEJQAAAMISAAAAhCUAAADCEgAAAGEJAACAsAQAAEBYAgAAgLAEAABAWAIAACAsAQAAEJYAAAAgLAEAABCWAAAACEsAAACEJQAAAAhLAAAAhCUAAADCEgAAAGEJAAAAwhIAAABhCQAAgLAEAABAWAIAACAsAQAAQFgCAAAgLAEAABCWAAAACEsAAAAQlgAAAAhLAAAAhCUAAADCEgAAAIQlAAAAwhIAAABhCQAAgLAEAAAAYQkAAICwBAAAQFgCAAAgLAEAAEBYAgAAICwBAAAQlgAAAAhLAAAAhCUAAAAISwAAAIQlAAAAwhIAAABhCQAAAMISAAAAYQkAAICwBAAAQFgCAACAsAQAAEBYAgAAICwBAAAQlgAAACAsAQAAEJYAAAAISwAAAIQlAAAAwhIAAACEJQAAAMISAAAAYQkAAICwBAAAAGEJAACAsAQAAEBYAgAAICwBAABAWAIAACAsAQAAEJYAAAAISwAAABCWAAAACEsAAACEJQAAAMISAAAAYQkAAADCEgAAAGEJAACAsAQAAEBYAgAAgLAEAABAWAIAACAsAQAAEJYAAAAgLAEAABCWAAAACEsAAACEJQAAAAhLAAAAhCUAAADCEgAAAGEJAAAAwhIAAABhCQAAgLAEAABAWAIAACAsAQAAQFgCAAAgLAEAABCWAAAACEsAAAAQlgAAAAhLAAAAhCUAAADCEgAAAIQlAAAAwhIAAABhCQAAgLAEAAAAYQkAAICwBAAAQFgCAAAgLAEAABCWAAAAICwBAAAQlgAAAAhLAAAAhCUAAAAISwAAAIQlAAAAwhIAAABhCQAAAMISAAAAYQkAAICwBAAAQFgCAACAsAQAAEBYAgAAICwBAAAQlgAAAAhLAAAAEJYAAAAISwAAAIQlAAAAwhIAAACEJQAAAMISAAAAYQkAAICwBAAAAGEJAACAsAQAAEBYAgAAICwBAABAWAIAACAsAQAAEJYAAAAISwAAABCWAAAACEsAAACEJQAAAMISAAAAYQkAAADCEgAAAGEJAACAsAQAAEBYAgAAgLAEAABAWAIAACAsAQAAEJYAAAAgLAEAABCWAAAACEsAAACEJQAAAAhLAAAAhCUAAADCEgAAAGEJAACAsAQAAABhCQAAgLAEAABAWAIAACAsAQAAQFgCAAAgLAEAABCWAAAACEsAAAAQlgAAAAhLAAAAhCUAAADCEgAAAIQlAAAAwhIAAABhCQAAgLAEAABAWDoEAAAACEsAAACEJQAAAMISAAAAYQkAAADCEgAAAGEJAACAsAQAAEBYAgAAgLAEAABAWAIAACAsAQAAEJYAAAAgLAEAABCWAAAACEsAAACEJQAAAAhLAAAAhCUAAADCEgAAAGEJAACAsAQAAABhCQAAgLAEAABAWAIAACAsAQAAQFgCAAAgLAEAABCWAAAACEsAAAAQlgAAAAhLAAAAhCUAAADCEgAAAIQlAAAAwhIAAABhCQAAgLAEAABAWAIAAICwBAAAQFgCAAAgLAEAABCWAAAAICwBAAAQlgAAAAhLAAAAhCUAAAAISwAAAIQlAAAAwhIAAABhCQAAAMISAAAAYQkAAICwBAAAQFgCAACAsAQAAEBYAgAAICwBAAAQlgAAAAhLAAAAEJYAAAAISwAAAIQlAAAAwhIAAACEJQAAAMISAAAAYQkAAICwBAAAAGEJAACAsAQAAEBYAgAAICwBAABAWAIAACAsAQAAEJYAAAAISwAAAIQlAAAACEsAAACEJQAAAMISAAAAYQkAAADCEgAAAGEJAACAsAQAAEBYAgAAgLAEAABAWAIAACAsAQAAEJYAAAAgLAEAABCWAAAACEsAAACEJQAAAMISAAAAhCUAAADCEgAAAGEJAACAsAQAAABhCQAAgLAEAABAWAIAACAsAQAAQFgCAAAgLAEAABCWAAAACEsAAAAQlgAAAAhLAAAAhCUAAADCEgAAAIQlAAAAwhIAAABhCQAAgLAEAABAWAIAAICwBAAAQFgCAAAgLAEAABCWAAAAICwBAAAQlgAAAAhLAAAAhCUAAAAISwAAAIQlAAAAwhIAAABhCQAAAMISAAAAYQkAAICwBAAAQFgCAAAgLAEAAEBYAgAAICwBAAAQlgAAAAhLAAAAEJYAAAAISwAAAIQlAAAAwhIAAACEJQAAAMISAAAAYQkAAICwBAAAAGEJAACAsAQAAEBYAgAAICwBAAAQlgAAACAsAQAAEJYAAAAISwAAAIQlAAAACEsAAACEJQAAAMISAAAAYQkAAADCEgAAAGEJAACAsAQAAEBYAgAAgLAEAABAWAIAACAsAQAAEJYAAAAgLAEAABCWAAAACEsAAACEJQAAAMISAAAAhCUAAADCEgAAAGEJAACAsAQAAABhCQAAgLAEAABAWAIAACAsAQAAQFgCAAAgLAEAABCWAAAACEsAAAAQlgAAAAhLAAAAhCUAAADCEgAAAGEJAAAAwhIAAABhCQAAgLAEAABAWAIAAICwBAAAQFgCAAAgLAEAABCWAAAAICwBAAAQlgAAAAhLAAAAhCUAAAAISwAAAIQlAAAAwhIAAABhCQAAgLAEAAAAYQkAAICwBAAAQFgCAAAgLAEAAEBYAgAAICwBAAAQlgAAAAhLAAAAEJYAAAAISwAAAIQlAAAAwhIAAACEJQAAAMISAAAAYQkAAICwBAAAAGEJAACAsAQAAEBYAgAAICwBAAAQlgAAACAsAQAAEJYAAAAISwAAAIQlAAAACEsAAACEJQAAAMISAAAAYQkAAADCEgAAAGEJAACAsAQAAEBYAgAAgLAEAABAWAIAACAsAQAAEJYAAAAISwAAABCWAAAACEsAAACEJQAAAMISAAAAhCUAAADCEgAAAGEJAACAsAQAAABhCQAAgLAEAABAWAIAACAsAQAAQFgCAAAgLAEAABCWAAAACEsAAACEpUMAAACAsAQAAEBYAgAAICwBAAAQlgAAACAsAQAAEJYAAAAISwAAAIQlAAAACEsAAACEJQAAAMISAAAAYQkAAADCEgAAAGEJAACAsAQAAEBYAgAAgLAEAABAWAIAACAsAQAAEJYAAAAISwAAABCWAAAACEsAAACEJQAAAMISAAAAhCUAAADCEgAAAGEJAACAsAQAAABhCQAAgLAEAABAWAIAACAsAQAAQFgCAAAgLAEAABCWAAAACEsAAACEJQAAAAhLAAAAhCUAAADCEgAAAGEJAAAAwhIAAABhCQAAgLAEAABAWAIAAICwBAAAQFgCAAAgLAEAABCWAAAAICwBAAAQlgAAAAhLAAAAhCUAAAAISwAAAIQlAAAAwhIAAABhCQAAgLAEAAAAYQkAAICwBAAAQFgCAAAgLAEAAEBYAgAAICwBAAAQlgAAAAhLAAAAEJYAAAAISwAAAIQlAAAAwhIAAACEJQAAAMISAAAAYQkAAICwBAAAQFgCAACAsAQAAEBYAgAAICwBAAAQlgAAACAsAQAAEJYAAAAISwAAAIQlAAAACEsAAACEJQAAAMISAAAAYQkAAADCEgAAAGEJAACAsAQAAEBYAgAAICwBAOD/2q9jAgAAAIBg/VsLYovgA2AsAQAAMJYAAAAYSwAAAIwlAAAAGEsAAACMJQAAAMYSAAAAYwkAAADGEgAAAGMJAACAsQQAAMBYAgAAgLEEAADAWAIAAGAsAQAAMJYAAABgLAEAADCWAAAAGEsAAACMJQAAAMYSAAAAjCUAAADGEgAAAGMJAACAsQQAAABjCQAAgLEEAADAWAIAAGAsAQAAwFgCAABgLAEAADCWAAAAGEsAAAAwlgAAABhLAAAAjCUAAADGEgAAAGMJAAAAxhIAAABjCQAAgLEEAADAWAIAAICxBAAAwFgCAABgLAEAADCWAAAAYCwBAAAwlgAAABhLAAAAjCUAAAAYSwAAAIwlAAAAxhIAAABjCQAAgLEEAAAAYwkAAICxBAAAwFgCAABgLAEAAMBYAgAAYCwBAAAwlgAAABhLAAAAMJYAAAAYSwAAAIwlAAAAxhIAAACMJQAAAMYSAAAAYwkAAICxBAAAAGMJAACAsQQAAMBYAgAAYCwBAAAwlgAAAGAsAQAAMJYAAAAYSwAAAIwlAAAAGEsAAACMJQAAAMYSAAAAYwkAAADGEgAAAGMJAACAsQQAAMBYAgAAgLEEAADAWAIAAGAsAQAAMJYAAAAYSwAAADCWAAAAGEsAAACMJQAAAMYSAAAAjCUAAADGEgAAAGMJAACAsQQAAABjCQAAgLEEAADAWAIAAGAsAQAAwFgCAABgLAEAADCWAAAAGEsAAACMJQAAABhLAAAAjCUAAADGEgAAAGMJAAAAxhIAAABjCQAAgLEEAADAWAIAAICxBAAAwFgCAABgLAEAADCWAAAAYCwBAAAwlgAAABhLAAAAjCUAAAAYSwAAAIwlAAAAxhIAAABjCQAAgLEEAAAAYwkAAICxBAAAwFgCAABgLAEAAMBYAgAAYCwBAAAwlgAAABhLAAAAMJYAAAAYSwAAAIwlAAAAxhIAAACMJQAAAMYSAAAAYwkAAICxBAAAwFgCAACAsQQAAMBYAgAAYCwBAAAwlgAAAGAsAQAAMJYAAAAYSwAAAIwlAAAAGEsAAACMJQAAAMYSAAAAYwkAAADGEgAAAGMJAACAsQQAAMBYAgAAYCwlAAAAwFgCAABgLAEAADCWAAAAGEsAAAAwlgAAABhLAAAAjCUAAADGEgAAAIwlAAAAxhIAAABjCQAAgLEEAAAAYwkAAICxBAAAwFgCAABgLAEAAMBYAgAAYCwBAAAwlgAAABhLAAAAjCUAAAAYSwAAAIwlAAAAxhIAAABjCQAAAMYSAAAAYwkAAICxBAAAwFgCAACAsQQAAMBYAgAAYCwBAAAwlgAAAGAsAQAAMJYAAAAYSwAAAIwlAAAAxhIAAACMJQAAAMYSAAAAYwkAAICxBAAAAGMJAACAsQQAAMBYAgAAYCwBAADAWAIAAGAsAQAAMJYAAAAYSwAAADCWAAAAGEsAAACMJQAAAMYSAAAAjCUAAADGEgAAAGMJAACAsQQAAMBYAgAAgLEEAADAWAIAAGAsAQAAMJYAAABgLAEAADCWAAAAGEsAAACMJQAAABhLAAAAjCUAAADGEgAAAGMJAAAAxhIAAABjCQAAgLEEAADAWAIAAGAsAQAAwFgCAABgLAEAADCWAAAAGEsAAAAwlgAAABhLAAAAjCUAAADGEgAAAIwlAAAAxhIAAABjCQAAgLEEAAAAYwkAAICxBAAAwFgCAABgLAEAADCWAAAAYCwBAAAwlgAAABhLAAAAjCUAAAAYSwAAAIwlAAAAxhIAAABjCQAAAMYSAAAAYwkAAICxBAAAwFgCAACAsQQAAMBYAgAAYCwBAAAwlgAAAGAsAQAAMJYAAAAYSwAAAIwlAAAAxhIAAACMJQAAAMYSAAAAYwkAAICxBAAAAGMJAACAsQQAAMBYAgAAYCwBAADAWAIAAGAsAQAAMJYAAAAYSwAAADCWAAAAGEsAAACMJQAAAMYSAAAAYwkAAADGEgAAAGMJAACAsQQAAMBYAgAAgLEEAADAWAIAAGAsAQAAMJYAAABgLAEAADCWAAAAGEsAAACMJQAAABhLAAAAjCUAAADGEgAAAGMJAACAsQQAAABjCQAAgLEEAADAWAIAAGAsAQAAwFgCAABgLAEAADCWAAAAGEsAAAAwlgAAABhLAAAAjCUAAADGEgAAAIwlAAAAxhIAAABjCQAAgLEEAAAAYwkAAICxBAAAwFgCAABgLAEAADCWAAAAYCwBAAAwlgAAABhLAAAAjCUAAAAYSwAAAIwlAAAAxhIAAABjCQAAAMYSAAAAYwkAAICxBAAAwFgCAACAsQQAAMBYAgAAYCwBAAAwlgAAABhLAAAAMJYAAAAYSwAAAIwlAAAAxhIAAACMJQAAAMYSAAAAYwkAAICxBAAAAGMJAACAsQQAAMBYAgAAYCwBAADAWAIAAGAsAQAAMJYAAAAYSwAAAIwlAAAAGEsAAACMJQAAAMYSAAAAYwkAAADGEgAAAGMJAACAsQQAAMBYAgAAgLEEAADAWAIAAGAsAQAAMJYAAABgLAEAADCWAAAAGEsAAACMJQAAABhLAAAAjCUAAADGEgAAAGMJAACAsQQAAABjCQAAgLEEAADAWAIAAGAsAQAAwFgCAABgLAEAADCWAAAAGEsAAAAwlgAAABhLAAAAjCUAAADGEgAAAIwlAAAAxhIAAABjCQAAgLEEAADAWAIAAICxBAAAwFgCAABgLAEAADCWAAAAYCwBAAAwlgAAABhLAAAAjCUAAAAYSwAAAIwlAAAAxhIAAABjCQAAAMYSAAAAYwkAAICxBAAAwFgCAABgLCUAAADAWAIAAGAsAQAAMJYAAAAYSwAAADCWAAAAGEsAAACMJQAAAMYSAAAAjCUAAADGEgAAAGMJAACAsQQAAABjCQAAgLEEAADAWAIAAGAsAQAAwFgCAABgLAEAADCWAAAAGEsAAACMJQAAABhLAAAAjCUAAADGEgAAAGMJAAAAxhIAAABjCQAAgLEEAADAWAIAAICxBAAAwFgCAABgLAEAADCWAAAAYCwBAAAwlgAAABhLAAAAjCUAAADGEgAAAIwlAAAAxhIAAABjCQAAgLEEAAAAYwkAAICxBAAAwFgCAABgLAEAAMBYAgAAYCwBAAAwlgAAABhLAAAAMJYAAAAYSwAAAIwlAAAAxhIAAACMJQAAAMYSAAAAYwkAAICxBAAAwFgCAACAsQQAAMBYAgAAYCwBAAAwlgAAAGAsAQAAMJYAAAAYSwAAAIwlAAAAGEsAAACMJQAAAMYSAAAAYwkAAADGEgAAAGMJAACAsQQAAMBYAgAAYCwBAADAWAIAAGAsAQAAMJYAAAAYSwAAADCWAAAAGEsAAACMJQAAAMYSAAAAjCUAAADGEgAAAGMJAACAsQQAAABjCQAAgLEEAADAWAIAAGAsAQAAMJYAAABgLAEAADCWAAAAGEsAAACMJQAAABhLAAAAjCUAAADGEgAAAGMJAAAAxhIAAABjCQAAgLEEAADAWAIAAICxBAAAwFgCAABgLAEAADCWAAAAYCwBAAAwlgAAABhLAAAAjCUAAADGEgAAAIwlAAAAxhIAAABjCQAAgLEEAAAAYwkAAICxBAAAwFgCAABgLAEAAMBYAgAAYCwBAAAwlgAAABhLAAAAMJYAAAAYSwAAAIwlAAAAxhIAAABjCQAAAMYSAAAAYwkAAICxBAAAwFgCAACAsQQAAMBYAgAAYCwBAAAwlgAAAGAsAQAAMJYAAAAYSwAAAIwlAAAAGEsAAACMJQAAAMYSAAAAYwkAAICxBAAAAGMJAACAsQQAAMBYAgAAYCwBAADAWAIAAGAsAQAAMJYAAAAYSwAAADCWAAAAGEsAAACMJQAAAMYSAAAAjCUAAADGEgAAAGMJAACAsQQAAABjCQAAgLEEAADAWAIAAGAsAQAAMJYAAABgLAEAADCWAAAAGEsAAACMJQAAABhLAAAAjCUAAADGEgAAAGMJAAAAxhIAAABjCQAAgLEEAADAWAIAAICxBAAAwFgCAABgLAEAADCWAAAAGEsAAAAwlgAAABhLAAAAjCUAAADGEgAAAIwlAAAAxhIAAABjCQAAgLEEAAAAYwkAAICxBAAAwFgCAABgLAEAAMBYAgAAYCwBAAAwlgAAABhLAAAAjCUAAAAYSwAAAIwlAAAAxhIAAABjCQAAAMYSAAAAYwkAAICxBAAAwFgCAACAsQQAAMBYAgAAYCwBAAAwlgAAAGAsAQAAMJYAAAAYSwAAAIwlAAAAGEsAAACMJQAAAMYSAAAAYwkAAICxBAAAAGMJAACAsQQAAMBYAgAAsBF9Ip71RRoBHwAAAABJRU5ErkJggg==" />
                            </div>  
                        </div>
                    </div>
       
                   
                </table></form>

                    <hr style="border:15px;"><hr style="border:2px;">
                    <div class="content">

                        <table id="customers">
                            <tr>
                                <td>
                                     X:
                                </td>
                                <td>
                                    <input type="text" name="coorX" id="coorX" value="0" readonly="readonly">
                                </td>
                                <td>
                                     h:
                                </td>
                                <td>
                                    <input type="text" name="heightValue" id="heightValue" value="150" readonly="readonly">
                                </td>
                            </tr>
                            <tr>
                                <td>
                                     Y:
                                </td>
                                <td>
                                    <input type="text" name="coorY" id="coorY" value="0" readonly="readonly">
                                </td>
                                <td>
                                     w:
                                </td>
                                <td>
                                    <input type="text" name="widthValue" id="widthValue" value="180" readonly="readonly">
                                </td>
                            </tr>
                        </table>
                        <hr>
              </form>
            </div><!-- /.wrap -->
        </div><!-- /.content -->
         
            </div>
            <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.0.943/pdf.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/interact.js/1.2.9/interact.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.0.943/pdf.worker.min.js'></script>

    </body>
</html>


from how to restrict drag elements in interact.js

Webpack code splitting: ChunkLoadError - Loading chunk X failed, but the chunk exists

I've integrated Sentry with my website a few days ago and I noticed that sometimes users receive this error in their console:

ChunkLoadError: Loading chunk <CHUNK_NAME> failed.
(error: <WEBSITE_PATH>/<CHUNK_NAME>-<CHUNK_HASH>.js)

So I investigated the issue around the web and discovered some similar cases, but related to missing chunks caused by release updates during a session or caching issues.

The main difference between these cases and mine is that the failed chunks are actually reachable from the browser, so the loading error does not depend on the after-release refresh of the chunk hashes but (I guess), from some network related issue. This assumption is reinforced by this stat: around 90% of the devices involved are mobile.

Finally, I come to the question: Should I manage the issue in some way (e. g. retrying the chunk loading if failed) or it's better to simply ignore it and let the user refresh manually?


2021.09.28 edit:

A month later, the issue is still occurring but I have not received any report from users, also I'm constantly recording user sessions with Hotjar but nothing relevant has been noticed so far.

I recently had a chat with Sentry support that helped me excluding the network related hypotesis:

Our React SDK does not have offline cache by default, when an error is captured it will be sent at that point. If the app is not able to connect to Sentry to send the event, it will be discarded and the SDK will no try to send it again.

Rodolfo from Sentry

I can confirm that the issue is quite unusual, I share with you another interesting stat: the user affected since the first occurrence are 882 out of 332.227 unique visitors (~0,26%), but I noticed that the 90% of the occurrences are from iOS (not generic mobile devices as I noticed a month ago), so if I calculate the same proportion with iOS users (794 (90% of 882) out of 128.444) we are near to a 0,62%. Still small but definitely more relevant on iOS.



from Webpack code splitting: ChunkLoadError - Loading chunk X failed, but the chunk exists

When do I need to add @Composable with Android Studio Compose?

The following code is from the project.

I find that fun MainScreen() add @Composable, and fun launchDetailsActivity doesn't add @Composable.

It make me confused. I think all function which apply to Compose should to add @Composable, why doesn't fun launchDetailsActivity add @Composable?

@AndroidEntryPoint
class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        setContent {
            ProvideWindowInsets {
                ProvideImageLoader {
                    CraneTheme {
                        MainScreen(
                            onExploreItemClicked = { launchDetailsActivity(context = this, item = it) }
                        )
                    }
                }
            }
        }
    }
}


@Composable
fun MainScreen(onExploreItemClicked: OnExploreItemClicked) {
  ...
}


fun launchDetailsActivity(context: Context, item: ExploreModel) {
    context.startActivity(createDetailsActivityIntent(context, item))
}


from When do I need to add @Composable with Android Studio Compose?

How is an event fired when the value of a State

Google Maps SDK for Android: Smoothly animating the camera to a new location, rendering all the tiles along the way

Background

Many similar questions seem to have been asked on SO before (most notably android google maps not loading the map when using GoogleMap.AnimateCamera() and How can I smoothly pan a GoogleMap in Android?), but none of the answers or comments posted throughout those threads have given me a firm idea of how to do this.

I initially thought that it would be as simple as just calling animateCamera(CameraUpdateFactory.newLatLng(), duration, callback) but like the OP of the first link above, all I get is a gray or very blurry map until the animation completes, even if I slow it down to tens of seconds long!

I've managed to find and implement this helper class that does a nice job of allowing the tiles to render along the way, but even with a delay of 0, there is a noticeable lag between each animation.

Code

OK, time for some code. Here's the (slightly-modified) helper class:

package com.coopmeisterfresh.googlemaps.NativeModules;

import android.os.Handler;

import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.GoogleMap;

import java.util.ArrayList;
import java.util.List;

public class CameraUpdateAnimator implements GoogleMap.OnCameraIdleListener {
    private final GoogleMap mMap;
    private final GoogleMap.OnCameraIdleListener mOnCameraIdleListener;

    private final List<Animation> cameraUpdates = new ArrayList<>();

    public CameraUpdateAnimator(GoogleMap map, GoogleMap.
        OnCameraIdleListener onCameraIdleListener) {
        mMap = map;
        mOnCameraIdleListener = onCameraIdleListener;
    }

    public void add(CameraUpdate cameraUpdate, boolean animate, long delay) {
        if (cameraUpdate != null) {
            cameraUpdates.add(new Animation(cameraUpdate, animate, delay));
        }
    }

    public void clear() {
        cameraUpdates.clear();
    }

    public void execute() {
        mMap.setOnCameraIdleListener(this);
        executeNext();
    }

    private void executeNext() {
        if (cameraUpdates.isEmpty()) {
            mOnCameraIdleListener.onCameraIdle();
        } else {
            final Animation animation = cameraUpdates.remove(0);

            new Handler().postDelayed(() -> {
                if (animation.mAnimate) {
                    mMap.animateCamera(animation.mCameraUpdate);
                } else {
                    mMap.moveCamera(animation.mCameraUpdate);
                }
            }, animation.mDelay);
        }
    }

    @Override
    public void onCameraIdle() {
        executeNext();
    }

    private static class Animation {
        private final CameraUpdate mCameraUpdate;
        private final boolean mAnimate;
        private final long mDelay;

        public Animation(CameraUpdate cameraUpdate, boolean animate, long delay) {
            mCameraUpdate = cameraUpdate;
            mAnimate = animate;
            mDelay = delay;
        }
    }
}

And my code to implement it:

// This is actually a React Native Component class, but I doubt that should matter...?
public class NativeGoogleMap extends SimpleViewManager<MapView> implements
    OnMapReadyCallback, OnRequestPermissionsResultCallback {

    // ...Other unrelated methods removed for brevity

    private void animateCameraToPosition(LatLng targetLatLng, float targetZoom) {
        // googleMap is my GoogleMap instance variable; it
        // gets properly initialised in another class method
        CameraPosition currPosition = googleMap.getCameraPosition();
        LatLng currLatLng = currPosition.target;
        float currZoom = currPosition.zoom;

        double latDelta = targetLatLng.latitude - currLatLng.latitude;
        double lngDelta = targetLatLng.longitude - currLatLng.longitude;

        double latInc = latDelta / 5;
        double lngInc = lngDelta / 5;

        float zoomInc = 0;
        float minZoom = googleMap.getMinZoomLevel();
        float maxZoom = googleMap.getMaxZoomLevel();

        if (lngInc > 15 && currZoom > minZoom) {
            zoomInc = (minZoom - currZoom) / 5;
        }

        CameraUpdateAnimator animator = new CameraUpdateAnimator(googleMap,
            () -> googleMap.animateCamera(CameraUpdateFactory.zoomTo(
            targetZoom), 5000, null));

        for (double nextLat = currLatLng.latitude, nextLng = currLatLng.
            longitude, nextZoom = currZoom; Math.abs(nextLng) < Math.abs(
            targetLatLng.longitude);) {
            nextLat += latInc;
            nextLng += lngInc;
            nextZoom += zoomInc;

            animator.add(CameraUpdateFactory.newLatLngZoom(new
                LatLng(nextLat, nextLng), (float)nextZoom), true);
        }

        animator.execute();
    }
}

Question

Is there a better way to accomplish this seemingly-simple task? I'm thinking that perhaps I need to move my animations to a worker thread or something; would that help?

Thanks for reading (I know it was an effort :P)!

Update 30/09/2021

I've updated the code above in line with Andy's suggestions in the comments and although it works (albeit with the same lag and rendering issues), the final algorithm will need to be a bit more complex since I want to zoom out to the longitudinal delta's half-way point, then back in as the journey continues.

Doing all these calculations at once, as well as smoothly rendering all the necessary tiles simultaneously, seems to be way too much for the cheap mobile phone that I'm testing on. Or is this a limitation of the API itself? In any case, how can I get all of this working smoothly, without any lag whatsoever between queued animations?



from Google Maps SDK for Android: Smoothly animating the camera to a new location, rendering all the tiles along the way

How to avoid using Chooser for Companion Device Pairing

I am using this example https://developer.android.com/guide/topics/connectivity/companion-device-pairing to avoid asking location permissions when performing BLE scans. Is there any way to avoid launching the Chooser and stay in the app?

Basically, need to intercept the IntentSender when onDeviceFound is called:

deviceManager.associate(pairingRequest, object: CompanionDeviceManager.Callback() {
    // Called when a device is found. Launch the IntentSender so the user
    // can select the device they want to pair with.
    override fun onDeviceFound(chooserLauncher: IntentSender) {
        startIntentSenderForResult(chooserLauncher,
            SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0)
    }

    override fun onFailure(error: CharSequence?) {
        // Handle the failure.
    }
}, null)

Thank you.



from How to avoid using Chooser for Companion Device Pairing

How to Create an Irregular 3D Polygon extruded shape React-Three-Fiber

So far, I've only created 3D rectangles with the useBox statement. Is there a way to create something like a "two by four" piece of wood that has the two ends cut at some angle, similar to the picture below?

enter image description here

Code/demo can be found here: https://codesandbox.io/s/ragdoll-physics-forked-bntr9?file=/src/index.js

I think the problem is tjat I'm mixing various samples from Three.js and React-Three-Fiber.

From this video https://youtu.be/3eGeh_aJxMI?t=509, I got the idea that I could probably create a 2D polygon shape, and then extrude it into 3D. If there is a better approach, please let me know. It uses a 'scene.add', but since I'm in react-three-fiber, I think I have to change that part to use my existing canvas.

The example below is not the shape I want, just a first demo with a triangle extruded to 3D (the video above used a heart shape).

I don't know what element to use in the return statement. When I try "" or "" I got errors (I can list the errors, but not sure those are the right elements to use, so won't include the errors for now.) Lowercase "" allows it to run without errors, but I cannot see any object representing my 3D extruded triangle on the display.

const Truss1 = () => {
  var trussPosition = [5, 5, 5];
  const trussBoard1FlatShap = new Shape();

  const x = 0;
  const y = 0;
  const moveSize = 40;
  trussBoard1FlatShap.moveTo(x - moveSize, y - moveSize);
  trussBoard1FlatShap.lineTo(x + moveSize, y - moveSize);
  trussBoard1FlatShap.lineTo(x, y + moveSize);

  var extrudeSettings = { amount: 30, bevelEnabled: false };
  var extrudeSettings2 = {steps: 2,
    depth: 16,
    bevelEnabled: true,
    bevelThickness: 1,
    bevelSize: 1,
    bevelOffset: 0,
    bevelSegments: 1}
  var material = new MeshLambertMaterial({ color: 'red'});

  var trussBoard1Extruded = new ExtrudeGeometry(trussBoard1FlatShap, extrudeSettings);  
   // I also tried extrudeSettings2 from above. 
  var trussBoard1Mesh = new Mesh(trussBoard1Extruded, material);
  trussBoard1Mesh.position.z = 55;
  
  const [cube] = useBox(() => ({ type: 'Static', position: [0, -3, -9.8], args: [0.25, 2, 0.25] }))

  return (
       <>
           <mesh scale={[8, 8, 8]} ref={trussBoard1Mesh} > 
           </mesh> 
           <Box scale={[1, 1, 1]} ref={cube} > 
           </Box> 
       </>
    )
}
    ...
    ReactDOM.render(
      <Canvas style= shadows orthographic camera=>
        <CameraControls />
        <color attach="background" args={['#171720']} />
        <fog attach="fog" args={['#171720', 20, 70]} />
        <ambientLight intensity={0.2} />
        <pointLight position={[-10, -10, -10]} color="red" intensity={1.5} />
        <Physics iterations={15} gravity={[0, -200, 0]} allowSleep={false}>
          <Plane position={[0, -5, 0]} rotation={[-Math.PI / 2, 0, 0]} />
          <Lamp />
          <Table />
          <DynTable>{/* <texture  attach="map" image={'/images/wood_andrey-haimin-q2Fyzn-KJOQ-unsplash.jpg'} /> */}</DynTable>
          <DynShed />
          <Truss1 />
        </Physics>
      </Canvas>,
      document.getElementById('root')
    )


from How to Create an Irregular 3D Polygon extruded shape React-Three-Fiber

Cannot inject context into cookie manager with Kodein

I have the MVVM app build with KMM. ViewModel contains several use cases. Each use case calls methods of Repository and Repository calls NetworkService to execute the API call. Use cases, Repository and NetworkService are in the shared module. I need to store all cookies in shared preferences which I receive from the server. For this purpose I created my own cookies storage and installed it in this way:

private val httpClient = HttpClient {
        install(HttpCookies) {
            storage = CookiesStorage(di)
        }
    }

Here's the code of cookies storage:

class CookiesStorage(val di: DI) : CookiesStorage {

    private val cookiesStorageImpl = CookiesStorageImpl(di)

    override suspend fun addCookie(requestUrl: Url, cookie: Cookie) {
        cookiesStorageImpl.addCookie(requestUrl, cookie)
    }

    override fun close() {
    }

    override suspend fun get(requestUrl: Url) = cookiesStorageImpl.getCookies()
}

expect class CookiesStorageImpl(di: DI) {
    val di: DI
    fun addCookie(requestUrl: Url, cookie: Cookie)
    fun getCookies(): MutableList<Cookie>
}

As work with storing key-value data in iOS and Android is different, I added expect class CookiesStorageImpl. Android implementation of this class is now the following:

actual class CookiesStorageImpl actual constructor(actual val di: DI) {

    private val cookieMap = mutableMapOf<String, String>()

    private val context: Context by di.instance()

    actual fun addCookie(requestUrl: Url, cookie: Cookie) {
        println("Set cookie name=${cookie.name}, value=${cookie.value}")
        cookieMap[cookie.name] = cookie.value
        context.getSharedPreferences("kmm_preferences", Context.MODE_PRIVATE)
    }

    actual fun getCookies() = mutableListOf<Cookie>().apply {
        cookieMap.forEach {
            this.add(Cookie(it.key, it.value))
        }
    }

}

As you can see, I initialize the context with di here.

Here's the di graph in android app:

val appModule = DI.Module("app module") {
    import(viewModelModule)
    bind<Context>() with multiton { app: App ->
        app.applicationContext
    }
}

val viewModelModule = DI.Module("view model module") {
    import(useCaseModule)

    bind<ViewModelProvider.Factory>() with singleton {
        ViewModelFactory(instance())
    }
    bind<LoginViewModel>() with provider {
        LoginViewModel(instance())
    }
}

And here's the DI of shared module:

val useCaseModule = DI.Module("use case module") {
    bind<LoginUseCase>() with singleton {
        LoginUseCase(di)
    }
}

So, as you can see, I just pass di from use case to CookiesStorageImpl. But when I run the app I got the following error while accessing to context:

org.kodein.di.DI$NotFoundException: No binding found for bind<Context> { ? { ? } }

So, as I understand, the problem is that UseCase doesn't know anything about the context, but I cannot understand how can I pass the binding to the use case module. Thanks in advance for any help!

UPD

Here's the way how I add graph in the Application class:

class App : Application(), DIAware {

    override val di by DI.lazy {
        import(appModule)
    }

}


from Cannot inject context into cookie manager with Kodein

Openlayers can't modify drawn features

I would like to make a chance for modifying my features in OpenLayers. So far I can only drag them across the map, but I can't change their shapes at all when drawn.

enter image description here

In the picture above you can spot the blue dot, which just moves along the shape border but is not able to modify it.

I tried to modify my code by using this example:

https://openlayers.org/en/latest/examples/draw-and-modify-geodesic.html

and my piece of code looks like this:

var modifyInteraction = new ol.interaction.Modify({
 features: selectInteraction.getFeatures({
   if (modifyPoint[0] === center[0] && modifyPoint[1] === center[1]) {
    first = transform(polygon[0], projection, 'EPSG:4326');
    last = transform(
      polygon[(polygon.length - 1) / 2],
      projection,
      'EPSG:4326'
    );
     radius = getDistance(first, last) / 2;
   } else {
     first = transform(center, projection, 'EPSG:4326');
     last = transform(modifyPoint, projection, 'EPSG:4326');
     radius = getDistance(first, last);
   }
   const circle = circular(
    transform(center, projection, 'EPSG:4326'),
    radius,
    128
   );
   circle.transform('EPSG:4326', projection);
   geometries[0].setCoordinates(circle.getCoordinates());
   // save changes to be applied at the end of the interaction
   modifyGeometry.setGeometries(geometries);
  ),
  });
   var translateInteraction = new ol.interaction.Translate({
   features: selectInteraction.getFeatures()
  });
  var setActiveEditing = function(active) {
  selectInteraction.getFeatures().clear();
  selectInteraction.setActive(active);
  modifyInteraction.setActive(active);
  translateInteraction.setActive(active);
};
setActiveEditing(true);

and the full fiddle is available here:

https://jsfiddle.net/2yj1ae04/

How can I make these features editable after drawing them in OpenLayers Map?



from Openlayers can't modify drawn features

Wednesday 29 September 2021

How to apply two tranformations one after the other in manim?

I wanted to apply two linear transformations with matrices one after the other. Here is the code for one transformation:

 from manim import *

class LT(LinearTransformationScene):
def __init__(self):
    LinearTransformationScene.__init__(
        self,
        show_coordinates=True,
        leave_ghost_vectors=True,
    )

def construct(self):
    matrix = [[1, 1], [0, 1]]
    self.apply_matrix(matrix)
    self.wait()

But I want to apply another transformation after this. I tried using the same code again with different matrix:

 from manim import *

class LT(LinearTransformationScene):
def __init__(self):
    LinearTransformationScene.__init__(
        self,
        show_coordinates=True,
        leave_ghost_vectors=True,
    )

def construct(self):
    matrix = [[1, 1], [0, 1]]
    self.apply_matrix(matrix)
    self.wait()

def construct(self):
    matrix = [[2, 1], [3, 1]]
    self.apply_matrix(matrix)
    self.wait()`

But that didnt work.. Gives the error:

 IndentationError: unindent does not match any outer indentation level

This must be very simple. But I have no idea- didnt found anything in the documentation or search results.Any help is greatly appreciated.


Update: I tried changing the names of the matrix:

from manim import *

class LT(LinearTransformationScene):
    def __init__(self):
        LinearTransformationScene.__init__(
            self,
            show_coordinates=True,
            leave_ghost_vectors=True,
        )

    def construct(self):
        matrix1 = [[1, 1], [0, 1]]
        self.apply_matrix(matrix1)
        self.wait()


    def construct(self):
        matrix2 = [[2, 1], [3, 1]]
        self.apply_matrix(matrix2)
        self.wait()

This time, no errors were given but the transformation didnt work correctly. Only the last matrix was rendered. What might have happened?



from How to apply two tranformations one after the other in manim?

Ionic Capacitor Android Build on Big Sur

I am able to add android to my project with:

$ ionic capacitor add android

however I am not able to run due to a java issue. When executing:

$ ionic capacitor run android

I get the following error:

[capacitor] ✔ Copying web assets from www to android/app/src/main/assets/public in 318.72ms
[capacitor] ✔ Creating capacitor.config.json in android/app/src/main/assets in 669.04μp
[capacitor] ✔ copy android in 340.12ms
[capacitor] ✔ Updating Android plugins in 4.06ms
[capacitor] [info] Found 4 Capacitor plugins for android:
[capacitor]        @capacitor/app@1.0.3
[capacitor]        @capacitor/haptics@1.0.3
[capacitor]        @capacitor/keyboard@1.0.3
[capacitor]        @capacitor/status-bar@1.0.3
[capacitor] ✔ update android in 40.03ms
[capacitor] ✖ Running Gradle build - failed!
[capacitor] [error] 
[capacitor]         FAILURE: Build failed with an exception.
[capacitor]         
[capacitor]         * What went wrong:
[capacitor]         Supplied javaHome must be a valid directory. You supplied: /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home
[capacitor]         
[capacitor]         * Try:
[capacitor]         Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
[capacitor]         
[capacitor]         * Get more help at https://help.gradle.org
[capacitor]         
[ERROR] An error occurred while running subprocess capacitor.
        
        capacitor run android --target emulator-5554 exited with exit code 1.
        
        Re-running this command with the --verbose flag may provide more information.

When I type

java --version
openjdk 11.0.12 2021-07-20
OpenJDK Runtime Environment Temurin-11.0.12+7 (build 11.0.12+7)
OpenJDK 64-Bit Server VM Temurin-11.0.12+7 (build 11.0.12+7, mixed mode)

I have also updated my ~/.zshrc file accordingly:

export JAVA_HOME=/Library/Java/JavaVirtualMachines/temurin-11.jdk/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH

export ANDROID_SDK_ROOT=$HOME/Library/Android/sdk

export PATH=$PATH:$ANDROID_SDK_ROOT/tools/bin
export PATH=$PATH:$ANDROID_SDK_ROOT/platform-tools
export PATH=$PATH:$ANDROID_SDK_ROOT/emulator
export PATH=$PATH:$ANDROID_SDK_ROOT/build-tools


from Ionic Capacitor Android Build on Big Sur

WebView change audio output device

How can I change the audio output of WebView from loudspeaker to earpiece? I tried this code, but it is not working

    private fun setupAudio() {
        val am = getSystemService(AUDIO_SERVICE) as AudioManager
        am.mode = AudioManager.MODE_IN_COMMUNICATION
        am.stopBluetoothSco()
        am.isSpeakerphoneOn=false
        Log.e("Foo","Current mode is ${am.mode}")
    }

Have permission

<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />


from WebView change audio output device

Properly scale random movement in simulation with variable time delta

I want to write a simulation of a particle moving in 1D. The simulation has the following parameters:

  • max_speed: the max speed the particle can obtain (in meters/second)
  • delta: the time interval between two simulation steps (in seconds)
  • iters: the number of steps to run the simulation

Below is an example of a working simulation. It returns two lists of values: one for the particle's position at each time step, and one for the corresponding seconds at each time step.

import random

def simulate_particle(max_speed, delta, iters):
    current_position = 0
    positions = [current_position]
    seconds = [0]
    for frame in range(1, iters+1):
        max_distance_possible = max_speed * delta
        current_position += max_distance_possible * random.uniform(-1, 1)
        positions.append(current_position)
        seconds.append(frame * delta)
    return positions, seconds

As you can see, the particle's position at the next frame is determined by:

  • calculating the max distance possible that the particle is able to travel between two frames (taking into account the max_speed and delta parameters)
  • multiplying this value by a value sampled from an uniform distribution over [-1, 1] (in order to add some randomness)

The issue here is that the particle movement becomes tied to the delta parameter. Since larger deltas allow for a larger max distance, it becomes easier for particles in simulations with large deltas to move further away from the starting position.

Here's a graphical representation of this issue:

import matplotlib.pyplot as plt

for delta, iters, color in zip(
    [600, 60, 6],  # decreasing deltas
    [10, 100, 1000],  # larger number of iterations to achieve same end time
    ['blue', 'orange', 'green'],
):
    for repeat in range(50):  # run simulations 50 times for each delta
        ys, xs = simulate_particle(max_speed=5, delta=delta, iters=iters)
        label = f'{delta=}, {iters=}' if repeat == 0 else None
        plt.plot(xs, ys, color=color, label=label)

plt.xlabel('Time (s)')
plt.ylabel('Position')
plt.legend()
plt.show()

output: enter image description here

I understand why this happens: It's much harder to reach extreme values when sampling many times from a narrow distribution, compared to sampling a few times from a broader one.

My question is how do I fix this? Is there some kind of normalization I can do? I tried changing the uniform distribution to other distributions (e.g. gaussian), but eventually the same effect happens.



from Properly scale random movement in simulation with variable time delta

Webpack Compression Plugin not compressing content (Vuejs)

I need help debugging Webpack's Compression Plugin.

SUMMARY OF PROBLEM

  • Goal is to enable asset compression and reduce my app's bundle size. Using the Brotli algorithm as the default, and gzip as a fallback for unsupported browsers.
  • I expected a content-encoding field within an asset's Response Headers. Instead, they're loaded without the field. I used the Chrome dev tools' network tab to confirm this. For context, see the following snippet: example asset request
  • No errors show in my browser or IDE when running locally.

WHAT I TRIED

  • Using different implementations for the compression plugin. See below list of approaches:
    1. (With Webpack Chain API)
config
 .plugin('brotliCompress')
     .use(CompressionWebpackPlugin, [{
       exclude: /.map$/,
       cache: true,
       algorithm: 'brotliCompress',
       test: /\.(js|css|html|svg)$/,
       threshold: 10240,
       minRatio: 0.8,
     }])
  1. (With Webpack Chain API)
config
  .plugin('gzip')
      .use(CompressionWebpackPlugin, [{
        algorithm: 'gzip',
        test: new RegExp('\\.(' + ['js', 'css'].join('|') + ')$'),
        threshold: 8192, // Assets larger than 8192 bytes are not processed
        minRatio: 0.8, // Assets compressing worse that this ratio are not processed
      }])
  1. (With Webpack Chain API)
config
  .plugin('CompressionPlugin')
      .use(CompressionWebpackPlugin)
  1. (Using vue-cli-plugin: compression) This fails due to a Missing generator error when I use vue invoke compression in response to an IDE console message after I run vue add compression as an alternative to using Webpack Chain API for compression configuration.
  pluginOptions: {
    compression: {
      brotli: {
        filename: '[file].br[query]',
        algorithm: 'brotliCompress',
        include: /\.(js|css|html|svg|json)(\?.*)?$/i,
        minRatio: 0.8,
      },
      gzip: {
        filename: '[file].gz[query]',
        algorithm: 'gzip',
        include: /\.(js|css|html|svg|json)(\?.*)?$/i,
        minRatio: 0.8
      }
    }
  },
  1. Lastly, I tried setting the threshold field to 0 as well as raising it larger than 10k bytes.

POINTS OF SIGNIFICANCE

  • The above attempts didn't achieve the goal I stated in the first summary bullet and were used in place of the previous approaches tested.
  • I prioritized my efforts with Webpack Chain API since it resulted in no errors when rebuilding and running the app.

REFERENCED LINKS/DOCS

CODE

vue.config.js

const path = require('path')
const CompressionWebpackPlugin = require('compression-webpack-plugin')

function resolve (dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  /* ....shortened for brevity */

  // Compress option VI (with vue cli plugin, generator bug when invoked)
  // pluginOptions: {
  //   compression: {
  //     brotli: {
  //       filename: '[file].br[query]',
  //       algorithm: 'brotliCompress',
  //       include: /\.(js|css|html|svg|json)(\?.*)?$/i,
  //       minRatio: 0.8,
  //     },
  //     gzip: {
  //       filename: '[file].gz[query]',
  //       algorithm: 'gzip',
  //       include: /\.(js|css|html|svg|json)(\?.*)?$/i,
  //       minRatio: 0.8
  //     }
  //   }
  // },

  chainWebpack: config => {
    config
      .resolve.alias
        .set('@', resolve('src'))

    config
      .plugins.delete('prefetch') 
        
    config
      .optimization.splitChunks()

    config
      .output
      .chunkFilename('[id].js')

    // The below configurations are recommeneded only in prod.
    // config.when(process.env.NODE_ENV === 'production', config => { config... })

    // Compress option VII
    // config
      // .plugin('gzip')
      // .use(CompressionWebpackPlugin, [{
      //   algorithm: 'gzip',
      //   test: new RegExp('\\.(' + ['js', 'css'].join('|') + ')$'),
      //   threshold: 8192, // Assets larger than 8192 bytes are not processed
      //   minRatio: 0.8, // Assets compressing worse that this ratio are not processed
      // }])

    // Compress option VIII
    // config
      // .plugin('CompressionPlugin')
      // .use(CompressionWebpackPlugin)

    config
      .plugin('brotliCompress')
      .use(CompressionWebpackPlugin, [{
        exclude: /.map$/,
        // deleteOriginalAssets: true,
        cache: true,
        algorithm: 'brotliCompress',
        test: /\.(js|css|html|svg)$/,
        threshold: 10240,
        minRatio: 0.8,
      }])
  },
}

package.json

"dependencies": {
    "@auth0/auth0-spa-js": "^1.15.0",
    "audio-recorder-polyfill": "^0.4.1",
    "compression-webpack-plugin": "^6.0.0",
    "core-js": "^3.6.5",
    "dotenv": "^8.2.0",
    "dotenv-expand": "^5.1.0",
    "moment": "^2.29.1",
    "register-service-worker": "^1.7.1",
    "uuid": "^3.4.0",
    "vue": "^2.6.11",
    "vue-loader": "^15.9.8",
    "vue-router": "^3.5.1",
    "vuex": "^3.6.2"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-pwa": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.2.2",
    "vue-cli-plugin-compression": "~1.1.5",
    "vue-template-compiler": "^2.6.11",
    "webpack": "^4.46.0"
  }

I appreciate all input. Thanks.



from Webpack Compression Plugin not compressing content (Vuejs)

Can I replace produceState with mutableStateOf in the Compose sample project?

The following Code A is from the project.

  1. uiState is created by the delegate produceState, can I use mutableStateOf instead of produceState? If so, how can I write code?

  2. Why can't I use Code B in the project?

Code A

@Composable
fun DetailsScreen(
    onErrorLoading: () -> Unit,
    modifier: Modifier = Modifier,
    viewModel: DetailsViewModel = viewModel()
) {
    val uiState by produceState(initialValue = DetailsUiState(isLoading = true)) {
        val cityDetailsResult = viewModel.cityDetails
        value = if (cityDetailsResult is Result.Success<ExploreModel>) {
            DetailsUiState(cityDetailsResult.data)
        } else {
            DetailsUiState(throwError = true)
        }
    }
   when {
         uiState.cityDetails != null -> {
    ...
}


@HiltViewModel
class DetailsViewModel @Inject constructor(
    private val destinationsRepository: DestinationsRepository,
    savedStateHandle: SavedStateHandle
) : ViewModel() {

    private val cityName = savedStateHandle.get<String>(KEY_ARG_DETAILS_CITY_NAME)!!

    val cityDetails: Result<ExploreModel>
        get() {
            val destination = destinationsRepository.getDestination(cityName)
            return if (destination != null) {
                Result.Success(destination)
            } else {
                Result.Error(IllegalArgumentException("City doesn't exist"))
            }
        }
}


data class DetailsUiState(
    val cityDetails: ExploreModel? = null,
    val isLoading: Boolean = false,
    val throwError: Boolean = false
)

Code B

@Composable
fun DetailsScreen(
    onErrorLoading: () -> Unit,
    modifier: Modifier = Modifier,
    viewModel: DetailsViewModel = viewModel()
) {

    val cityDetailsResult = viewModel.cityDetails
    val uiState=if (cityDetailsResult is Result.Success<ExploreModel>) {
        DetailsUiState(cityDetailsResult.data)
    } else {
        DetailsUiState(throwError = true)
    }

    ...


from Can I replace produceState with mutableStateOf in the Compose sample project?

Android Studio inexplicable error in debug APK

I faced with situation that version from built debug APK had inexplicable runtime errors which totally disappeared after rebuild. No code changes - just fresh build. Today it happened at least second time - it starts to worry me.

The first reason to worry is time waste to determine that error couldn't be repeated on my emulator and I just need to rebuild it. Here I can only build APKs one by one until I get two files equal to byte. Though I've never faced with same problem while installing directly through USB, it couldn't be solution as I don't have physical access to it all the time.

Update. Today such a thing happened through USB installation.

The second reason is main here. Can I be sure release build doesn't have same problem? Now I build AAB files which I can't install on device to check before update in Google Play.

Current Android Studio version:

Android Studio Arctic Fox | 2020.3.1 Patch 1 Build
#AI-203.7717.56.2031.7621141, built on August 7, 2021

Update. Repeated on Android Sutiod version:

Android Studio Arctic Fox | 2020.3.1 Patch 2 Build
#AI-203.7717.56.2031.7678000, built on August 27, 2021

Other build settings

buildToolsVersion '30.0.2'
gradle version 7.0.2

Why does it happen? Is there any workaround?



from Android Studio inexplicable error in debug APK