bx-865-apps #1
|
@ -392,6 +392,15 @@
|
|||
"detail": "unittest",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "io",
|
||||
"kind": 6,
|
||||
"isExtraImport": true,
|
||||
"importPath": "io",
|
||||
"description": "io",
|
||||
"detail": "io",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "StringIO",
|
||||
"importPath": "io",
|
||||
|
@ -1612,6 +1621,54 @@
|
|||
"detail": "django.db",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "migrations",
|
||||
"importPath": "django.db",
|
||||
"description": "django.db",
|
||||
"isExtraImport": true,
|
||||
"detail": "django.db",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "models",
|
||||
"importPath": "django.db",
|
||||
"description": "django.db",
|
||||
"isExtraImport": true,
|
||||
"detail": "django.db",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "migrations",
|
||||
"importPath": "django.db",
|
||||
"description": "django.db",
|
||||
"isExtraImport": true,
|
||||
"detail": "django.db",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "models",
|
||||
"importPath": "django.db",
|
||||
"description": "django.db",
|
||||
"isExtraImport": true,
|
||||
"detail": "django.db",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "migrations",
|
||||
"importPath": "django.db",
|
||||
"description": "django.db",
|
||||
"isExtraImport": true,
|
||||
"detail": "django.db",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "models",
|
||||
"importPath": "django.db",
|
||||
"description": "django.db",
|
||||
"isExtraImport": true,
|
||||
"detail": "django.db",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "models",
|
||||
"importPath": "django.db",
|
||||
|
@ -1677,35 +1734,30 @@
|
|||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "Image",
|
||||
"importPath": "PIL",
|
||||
"description": "PIL",
|
||||
"label": "base64",
|
||||
"kind": 6,
|
||||
"isExtraImport": true,
|
||||
"detail": "PIL",
|
||||
"importPath": "base64",
|
||||
"description": "base64",
|
||||
"detail": "base64",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "Bitmap",
|
||||
"importPath": "potrace",
|
||||
"description": "potrace",
|
||||
"label": "cv2",
|
||||
"kind": 6,
|
||||
"isExtraImport": true,
|
||||
"detail": "potrace",
|
||||
"importPath": "cv2",
|
||||
"description": "cv2",
|
||||
"detail": "cv2",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "POTRACE_TURNPOLICY_MINORITY",
|
||||
"importPath": "potrace",
|
||||
"description": "potrace",
|
||||
"label": "numpy",
|
||||
"kind": 6,
|
||||
"isExtraImport": true,
|
||||
"detail": "potrace",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "render",
|
||||
"importPath": "django.shortcuts",
|
||||
"description": "django.shortcuts",
|
||||
"isExtraImport": true,
|
||||
"detail": "django.shortcuts",
|
||||
"importPath": "numpy",
|
||||
"description": "numpy",
|
||||
"detail": "numpy",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
|
@ -1717,19 +1769,19 @@
|
|||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "csrf_exempt",
|
||||
"importPath": "django.views.decorators.csrf",
|
||||
"description": "django.views.decorators.csrf",
|
||||
"label": "MultiPartParser",
|
||||
"importPath": "rest_framework.parsers",
|
||||
"description": "rest_framework.parsers",
|
||||
"isExtraImport": true,
|
||||
"detail": "django.views.decorators.csrf",
|
||||
"detail": "rest_framework.parsers",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "HttpResponse",
|
||||
"importPath": "django.http",
|
||||
"description": "django.http",
|
||||
"label": "APIView",
|
||||
"importPath": "rest_framework.views",
|
||||
"description": "rest_framework.views",
|
||||
"isExtraImport": true,
|
||||
"detail": "django.http",
|
||||
"detail": "rest_framework.views",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
|
@ -1740,6 +1792,30 @@
|
|||
"detail": "django.http",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "parse_image",
|
||||
"importPath": "api.tracer",
|
||||
"description": "api.tracer",
|
||||
"isExtraImport": true,
|
||||
"detail": "api.tracer",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "read_image",
|
||||
"importPath": "api.tracer",
|
||||
"description": "api.tracer",
|
||||
"isExtraImport": true,
|
||||
"detail": "api.tracer",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "numpy_zip_str_to_arr",
|
||||
"importPath": "api.tracer",
|
||||
"description": "api.tracer",
|
||||
"isExtraImport": true,
|
||||
"detail": "api.tracer",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "get_asgi_application",
|
||||
"importPath": "django.core.asgi",
|
||||
|
@ -1788,6 +1864,14 @@
|
|||
"detail": "api",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "csrf_exempt",
|
||||
"importPath": "django.views.decorators.csrf",
|
||||
"description": "django.views.decorators.csrf",
|
||||
"isExtraImport": true,
|
||||
"detail": "django.views.decorators.csrf",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "get_wsgi_application",
|
||||
"importPath": "django.core.wsgi",
|
||||
|
@ -8344,10 +8428,37 @@
|
|||
"kind": 6,
|
||||
"importPath": "back.api.migrations.0007_floorplan_alter_product_image1_alter_product_image2_and_more",
|
||||
"description": "back.api.migrations.0007_floorplan_alter_product_image1_alter_product_image2_and_more",
|
||||
"peekOfCode": "class Migration(migrations.Migration):\n dependencies = [\n ('api', '0006_product_image2_product_image3'),\n ]\n operations = [\n migrations.CreateModel(\n name='FloorPlan',\n fields=[\n ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n ('title', models.CharField(max_length=200)),",
|
||||
"peekOfCode": "class Migration(migrations.Migration):\n dependencies = [\n ('api', '0006_product_image2_product_image3'),\n ]\n operations = [\n migrations.CreateModel(\n name='Floorplan',\n fields=[\n ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n ('title', models.CharField(max_length=200)),",
|
||||
"detail": "back.api.migrations.0007_floorplan_alter_product_image1_alter_product_image2_and_more",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "Migration",
|
||||
"kind": 6,
|
||||
"importPath": "back.api.migrations.0008_alter_floorplan_np_field",
|
||||
"description": "back.api.migrations.0008_alter_floorplan_np_field",
|
||||
"peekOfCode": "class Migration(migrations.Migration):\n dependencies = [\n ('api', '0007_floorplan_alter_product_image1_alter_product_image2_and_more'),\n ]\n operations = [\n migrations.AlterField(\n model_name='floorplan',\n name='np_field',\n field=models.TextField(),\n ),",
|
||||
"detail": "back.api.migrations.0008_alter_floorplan_np_field",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "Migration",
|
||||
"kind": 6,
|
||||
"importPath": "back.api.migrations.0009_alter_floorplan_np_field",
|
||||
"description": "back.api.migrations.0009_alter_floorplan_np_field",
|
||||
"peekOfCode": "class Migration(migrations.Migration):\n dependencies = [\n ('api', '0008_alter_floorplan_np_field'),\n ]\n operations = [\n migrations.AlterField(\n model_name='floorplan',\n name='np_field',\n field=models.BinaryField(),\n ),",
|
||||
"detail": "back.api.migrations.0009_alter_floorplan_np_field",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "Migration",
|
||||
"kind": 6,
|
||||
"importPath": "back.api.migrations.0010_alter_floorplan_np_field",
|
||||
"description": "back.api.migrations.0010_alter_floorplan_np_field",
|
||||
"peekOfCode": "class Migration(migrations.Migration):\n dependencies = [\n ('api', '0009_alter_floorplan_np_field'),\n ]\n operations = [\n migrations.AlterField(\n model_name='floorplan',\n name='np_field',\n field=models.TextField(),\n ),",
|
||||
"detail": "back.api.migrations.0010_alter_floorplan_np_field",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "ApiConfig",
|
||||
"kind": 6,
|
||||
|
@ -8366,6 +8477,15 @@
|
|||
"detail": "back.api.models",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "Floorplan",
|
||||
"kind": 6,
|
||||
"importPath": "back.api.models",
|
||||
"description": "back.api.models",
|
||||
"peekOfCode": "class Floorplan(models.Model):\n title = models.CharField(max_length=200)\n np_field = models.TextField()",
|
||||
"detail": "back.api.models",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "ProductSerializer",
|
||||
"kind": 6,
|
||||
|
@ -8376,20 +8496,74 @@
|
|||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "file_to_svg",
|
||||
"label": "FloorplanSerializer",
|
||||
"kind": 6,
|
||||
"importPath": "back.api.serializers",
|
||||
"description": "back.api.serializers",
|
||||
"peekOfCode": "class FloorplanSerializer(serializers.Serializer):\n title = serializers.CharField(max_length=200)\n np_field = serializers.CharField()\n def create(self,validated_data):\n return Floorplan.objects.create(**validated_data)",
|
||||
"detail": "back.api.serializers",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "numpy_arr_to_zip_str",
|
||||
"kind": 2,
|
||||
"importPath": "back.api.tracer",
|
||||
"description": "back.api.tracer",
|
||||
"peekOfCode": "def file_to_svg(filename: str):\n try:\n image = Image.open(filename)\n except IOError:\n print(\"Image (%s) could not be loaded.\" % filename)\n return\n bm = Bitmap(image, blacklevel=0.5)\n # bm.invert()\n plist = bm.trace(\n turdsize=2,",
|
||||
"peekOfCode": "def numpy_arr_to_zip_str(arr):\n f = io.BytesIO()\n np.savez_compressed(f, arr=arr)\n return base64.b64encode(f.getvalue())\ndef numpy_zip_str_to_arr(zip_str):\n f = io.BytesIO(base64.b64decode(zip_str))\n return np.load(f)['arr'].tolist()\ndef read_image(content: bytes) -> np.ndarray:\n \"\"\"\n Image bytes to OpenCV image",
|
||||
"detail": "back.api.tracer",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "products",
|
||||
"label": "numpy_zip_str_to_arr",
|
||||
"kind": 2,
|
||||
"importPath": "back.api.tracer",
|
||||
"description": "back.api.tracer",
|
||||
"peekOfCode": "def numpy_zip_str_to_arr(zip_str):\n f = io.BytesIO(base64.b64decode(zip_str))\n return np.load(f)['arr'].tolist()\ndef read_image(content: bytes) -> np.ndarray:\n \"\"\"\n Image bytes to OpenCV image\n :param content: Image bytes\n :returns OpenCV image\n :raises TypeError: If content is not bytes\n :raises ValueError: If content does not represent an image",
|
||||
"detail": "back.api.tracer",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "read_image",
|
||||
"kind": 2,
|
||||
"importPath": "back.api.tracer",
|
||||
"description": "back.api.tracer",
|
||||
"peekOfCode": "def read_image(content: bytes) -> np.ndarray:\n \"\"\"\n Image bytes to OpenCV image\n :param content: Image bytes\n :returns OpenCV image\n :raises TypeError: If content is not bytes\n :raises ValueError: If content does not represent an image\n \"\"\"\n if not isinstance(content, bytes):\n raise TypeError(f\"Expected 'content' to be bytes, received: {type(content)}\")",
|
||||
"detail": "back.api.tracer",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "parse_image",
|
||||
"kind": 2,
|
||||
"importPath": "back.api.tracer",
|
||||
"description": "back.api.tracer",
|
||||
"peekOfCode": "def parse_image(img):\n (img_h, img_w) = img.shape[:2]\n t = 1200\n w = t\n h = int((img_h / img_w) * t)\n img = cv2.resize(img, (w, h))\n gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n gray = 255 - gray\n gray = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY)[1]\n gray = cv2.blur(gray, (10, 5))",
|
||||
"detail": "back.api.tracer",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "Products",
|
||||
"kind": 6,
|
||||
"importPath": "back.api.views",
|
||||
"description": "back.api.views",
|
||||
"peekOfCode": "def products(request):\n \"\"\"\n List all task snippets\n \"\"\"\n if request.method == \"GET\":\n tasks = Product.objects.all()\n serializer = ProductSerializer(tasks, many=True)\n return JsonResponse(serializer.data, safe=False)\n elif request.method == \"POST\":\n data = JSONParser().parse(request)",
|
||||
"peekOfCode": "class Products(APIView):\n def get(self, request):\n tasks = Product.objects.all()\n serializer = ProductSerializer(tasks, many=True)\n return JsonResponse(serializer.data, safe=False)\n def post(self, request):\n data = JSONParser().parse(request)\n serializer = ProductSerializer(data=data)\n if serializer.is_valid():\n serializer.save()",
|
||||
"detail": "back.api.views",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "FloorplanView",
|
||||
"kind": 6,
|
||||
"importPath": "back.api.views",
|
||||
"description": "back.api.views",
|
||||
"peekOfCode": "class FloorplanView(APIView):\n parser_classes = (MultiPartParser,)\n def post(self, request):\n try:\n file = request.FILES[\"demo\"]\n logger.info(file.__dict__)\n res = parse_image(read_image(file.read()))\n serializer = FloorplanSerializer(\n data={\"title\": file.name, \"np_field\": res[\"b64\"].decode()}\n )",
|
||||
"detail": "back.api.views",
|
||||
"documentation": {}
|
||||
},
|
||||
{
|
||||
"label": "logger",
|
||||
"kind": 5,
|
||||
"importPath": "back.api.views",
|
||||
"description": "back.api.views",
|
||||
"peekOfCode": "logger = logging.getLogger(\"root\")\nclass Products(APIView):\n def get(self, request):\n tasks = Product.objects.all()\n serializer = ProductSerializer(tasks, many=True)\n return JsonResponse(serializer.data, safe=False)\n def post(self, request):\n data = JSONParser().parse(request)\n serializer = ProductSerializer(data=data)\n if serializer.is_valid():",
|
||||
"detail": "back.api.views",
|
||||
"documentation": {}
|
||||
},
|
||||
|
@ -8443,7 +8617,7 @@
|
|||
"kind": 5,
|
||||
"importPath": "back.back.settings",
|
||||
"description": "back.back.settings",
|
||||
"peekOfCode": "CSRF_TRUSTED_ORIGINS = (\n \"https://demo.kustarshina.ru\",\n \"http://localhost\",\n \"http://192.168.103.159\",\n)\nCORS_ORIGIN_ALLOW_ALL = False\nCORS_ORIGIN_WHITELIST = [\n \"null\",\n \"http://localhost\",\n \"http://localhost:4173\",",
|
||||
"peekOfCode": "CSRF_TRUSTED_ORIGINS = (\n \"https://demo.kustarshina.ru\",\n \"http://localhost\",\n \"http://localhost:3011\",\n \"http://192.168.103.159\",\n)\nCORS_ORIGIN_ALLOW_ALL = False\nCORS_ORIGIN_WHITELIST = [\n \"null\",\n \"http://localhost\",",
|
||||
"detail": "back.back.settings",
|
||||
"documentation": {}
|
||||
},
|
||||
|
@ -8452,7 +8626,7 @@
|
|||
"kind": 5,
|
||||
"importPath": "back.back.settings",
|
||||
"description": "back.back.settings",
|
||||
"peekOfCode": "CORS_ORIGIN_ALLOW_ALL = False\nCORS_ORIGIN_WHITELIST = [\n \"null\",\n \"http://localhost\",\n \"http://localhost:4173\",\n \"http://localhost:5173\",\n \"http://localhost:8000\",\n \"http://127.0.0.1\",\n \"http://192.168.103.159\",\n \"http://192.168.103.159:8000\",",
|
||||
"peekOfCode": "CORS_ORIGIN_ALLOW_ALL = False\nCORS_ORIGIN_WHITELIST = [\n \"null\",\n \"http://localhost\",\n \"http://localhost:3000\",\n \"http://localhost:3011\",\n \"http://localhost:4173\",\n \"http://localhost:5173\",\n \"http://localhost:8000\",\n \"http://127.0.0.1\",",
|
||||
"detail": "back.back.settings",
|
||||
"documentation": {}
|
||||
},
|
||||
|
@ -8461,7 +8635,7 @@
|
|||
"kind": 5,
|
||||
"importPath": "back.back.settings",
|
||||
"description": "back.back.settings",
|
||||
"peekOfCode": "CORS_ORIGIN_WHITELIST = [\n \"null\",\n \"http://localhost\",\n \"http://localhost:4173\",\n \"http://localhost:5173\",\n \"http://localhost:8000\",\n \"http://127.0.0.1\",\n \"http://192.168.103.159\",\n \"http://192.168.103.159:8000\",\n \"http://front:4173\",",
|
||||
"peekOfCode": "CORS_ORIGIN_WHITELIST = [\n \"null\",\n \"http://localhost\",\n \"http://localhost:3000\",\n \"http://localhost:3011\",\n \"http://localhost:4173\",\n \"http://localhost:5173\",\n \"http://localhost:8000\",\n \"http://127.0.0.1\",\n \"http://192.168.103.159\",",
|
||||
"detail": "back.back.settings",
|
||||
"documentation": {}
|
||||
},
|
||||
|
@ -8596,7 +8770,7 @@
|
|||
"kind": 5,
|
||||
"importPath": "back.back.urls",
|
||||
"description": "back.back.urls",
|
||||
"peekOfCode": "urlpatterns = [\n path(\"admin/\", admin.site.urls),\n path(\"api/products\", views.products),\n] + static('/files', document_root='files')",
|
||||
"peekOfCode": "urlpatterns = [\n path(\"admin/\", admin.site.urls),\n path(\"api/products\", csrf_exempt(views.Products.as_view())),\n path(\"api/floorplan\", csrf_exempt(views.FloorplanView.as_view())),\n] + static('/files', document_root='files')",
|
||||
"detail": "back.back.urls",
|
||||
"documentation": {}
|
||||
},
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from django.contrib import admin
|
||||
from .models import Product
|
||||
from .models import Product, Floorplan
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(Product)
|
||||
admin.site.register(Floorplan)
|
||||
|
|
|
@ -20,3 +20,8 @@ class Product(models.Model):
|
|||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class Floorplan(models.Model):
|
||||
title = models.CharField(max_length=200)
|
||||
np_field = models.TextField()
|
|
@ -1,5 +1,5 @@
|
|||
from rest_framework import routers, serializers, viewsets
|
||||
from .models import Product
|
||||
from .models import Product, Floorplan
|
||||
|
||||
|
||||
class ProductSerializer(serializers.HyperlinkedModelSerializer):
|
||||
|
@ -14,3 +14,11 @@ class ProductSerializer(serializers.HyperlinkedModelSerializer):
|
|||
"image2",
|
||||
"image3",
|
||||
]
|
||||
|
||||
|
||||
class FloorplanSerializer(serializers.Serializer):
|
||||
title = serializers.CharField(max_length=200)
|
||||
np_field = serializers.CharField()
|
||||
|
||||
def create(self,validated_data):
|
||||
return Floorplan.objects.create(**validated_data)
|
||||
|
|
|
@ -1,44 +1,66 @@
|
|||
from PIL import Image
|
||||
from potrace import Bitmap, POTRACE_TURNPOLICY_MINORITY # `potracer` library
|
||||
import io
|
||||
import base64
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
def numpy_arr_to_zip_str(arr):
|
||||
f = io.BytesIO()
|
||||
np.savez_compressed(f, arr=arr)
|
||||
return base64.b64encode(f.getvalue())
|
||||
|
||||
def numpy_zip_str_to_arr(zip_str):
|
||||
f = io.BytesIO(base64.b64decode(zip_str))
|
||||
return np.load(f)['arr'].tolist()
|
||||
|
||||
def read_image(content: bytes) -> np.ndarray:
|
||||
"""
|
||||
Image bytes to OpenCV image
|
||||
|
||||
:param content: Image bytes
|
||||
:returns OpenCV image
|
||||
:raises TypeError: If content is not bytes
|
||||
:raises ValueError: If content does not represent an image
|
||||
"""
|
||||
if not isinstance(content, bytes):
|
||||
raise TypeError(f"Expected 'content' to be bytes, received: {type(content)}")
|
||||
image = cv2.imdecode(np.frombuffer(content, dtype=np.uint8), cv2.IMREAD_COLOR)
|
||||
if image is None:
|
||||
raise ValueError(f"Expected 'content' to be image bytes")
|
||||
return image
|
||||
|
||||
|
||||
def file_to_svg(filename: str):
|
||||
try:
|
||||
def parse_image(img):
|
||||
(img_h, img_w) = img.shape[:2]
|
||||
t = 1200
|
||||
w = t
|
||||
h = int((img_h / img_w) * t)
|
||||
img = cv2.resize(img, (w, h))
|
||||
|
||||
image = Image.open(filename)
|
||||
except IOError:
|
||||
print("Image (%s) could not be loaded." % filename)
|
||||
return
|
||||
bm = Bitmap(image, blacklevel=0.5)
|
||||
# bm.invert()
|
||||
plist = bm.trace(
|
||||
turdsize=2,
|
||||
turnpolicy=POTRACE_TURNPOLICY_MINORITY,
|
||||
alphamax=1,
|
||||
opticurve=False,
|
||||
opttolerance=0.2,
|
||||
)
|
||||
with open(f"{filename}.svg", "w") as fp:
|
||||
fp.write(
|
||||
f'''<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{image.width}" height="{image.height}" viewBox="0 0 {image.width} {image.height}">''')
|
||||
parts = []
|
||||
for curve in plist:
|
||||
fs = curve.start_point
|
||||
parts.append(f"M{fs.x},{fs.y}")
|
||||
for segment in curve.segments:
|
||||
if segment.is_corner:
|
||||
a = segment.c
|
||||
b = segment.end_point
|
||||
parts.append(f"L{a.x},{a.y}L{b.x},{b.y}")
|
||||
else:
|
||||
a = segment.c1
|
||||
b = segment.c2
|
||||
c = segment.end_point
|
||||
parts.append(f"C{a.x},{a.y} {b.x},{b.y} {c.x},{c.y}")
|
||||
parts.append("z")
|
||||
fp.write(f'<path stroke="none" fill="black" fill-rule="evenodd" d="{"".join(parts)}"/>')
|
||||
fp.write("</svg>")
|
||||
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||
gray = 255 - gray
|
||||
gray = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY)[1]
|
||||
gray = cv2.blur(gray, (10, 5))
|
||||
contours, hierarchy = cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
|
||||
|
||||
for cnt in contours:
|
||||
area = cv2.contourArea(cnt)
|
||||
# if area > 150000 and area < 500000:
|
||||
cv2.drawContours(img, [cnt], 0, (255, 0, 0), 2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
file_to_svg('plan.png')
|
||||
svg_paths = []
|
||||
|
||||
for cnt in contours:
|
||||
if len(cnt) > 80:
|
||||
svg_path = "M"
|
||||
for i in range(len(cnt)):
|
||||
x, y = cnt[i][0]
|
||||
svg_path += f"{x} {y} "
|
||||
svg_paths.append(svg_path)
|
||||
|
||||
return {
|
||||
"width": w,
|
||||
"height": h,
|
||||
"paths": svg_paths,
|
||||
"array": gray.tolist(),
|
||||
"b64": numpy_arr_to_zip_str(gray),
|
||||
}
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
from django.shortcuts import render
|
||||
from rest_framework.parsers import JSONParser
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from .serializers import ProductSerializer
|
||||
from .models import Product
|
||||
|
||||
from rest_framework.parsers import JSONParser, MultiPartParser
|
||||
from rest_framework.views import APIView
|
||||
from django.http import JsonResponse
|
||||
|
||||
from api.tracer import parse_image, read_image, numpy_zip_str_to_arr
|
||||
from .serializers import FloorplanSerializer, ProductSerializer
|
||||
from .models import Floorplan, Product
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("root")
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def products(request):
|
||||
"""
|
||||
List all task snippets
|
||||
"""
|
||||
if request.method == "GET":
|
||||
class Products(APIView):
|
||||
def get(self, request):
|
||||
tasks = Product.objects.all()
|
||||
serializer = ProductSerializer(tasks, many=True)
|
||||
return JsonResponse(serializer.data, safe=False)
|
||||
elif request.method == "POST":
|
||||
|
||||
def post(self, request):
|
||||
data = JSONParser().parse(request)
|
||||
serializer = ProductSerializer(data=data)
|
||||
if serializer.is_valid():
|
||||
|
@ -23,3 +26,38 @@ def products(request):
|
|||
return JsonResponse(serializer.data, status=201)
|
||||
|
||||
return JsonResponse(serializer.errors, status=400)
|
||||
|
||||
|
||||
class FloorplanView(APIView):
|
||||
|
||||
parser_classes = (MultiPartParser,)
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
file = request.FILES["demo"]
|
||||
logger.info(file.__dict__)
|
||||
res = parse_image(read_image(file.read()))
|
||||
serializer = FloorplanSerializer(
|
||||
data={"title": file.name, "np_field": res["b64"].decode()}
|
||||
)
|
||||
logger.info(res["b64"])
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return JsonResponse(
|
||||
data={"response": {"array": res["array"]}}, status=201
|
||||
)
|
||||
return JsonResponse(serializer.errors, status=500)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
raise e
|
||||
|
||||
def get(self, request):
|
||||
try:
|
||||
item = Floorplan.objects.last()
|
||||
serializer = FloorplanSerializer(item, many=False)
|
||||
data = serializer.data
|
||||
data["np_field"] = numpy_zip_str_to_arr(data["np_field"])
|
||||
return JsonResponse(data, safe=False)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
raise e
|
||||
|
|
|
@ -13,6 +13,7 @@ https://docs.djangoproject.com/en/5.0/ref/settings/
|
|||
from pathlib import Path
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
|
@ -42,17 +43,21 @@ ALLOWED_HOSTS = [
|
|||
CSRF_TRUSTED_ORIGINS = (
|
||||
"https://demo.kustarshina.ru",
|
||||
"http://localhost",
|
||||
"http://localhost:3011",
|
||||
"http://192.168.103.159",
|
||||
)
|
||||
CORS_ORIGIN_ALLOW_ALL = False
|
||||
CORS_ORIGIN_WHITELIST = [
|
||||
"null",
|
||||
"http://localhost",
|
||||
"http://localhost:3000",
|
||||
"http://localhost:3011",
|
||||
"http://localhost:4173",
|
||||
"http://localhost:5173",
|
||||
"http://localhost:8000",
|
||||
"http://127.0.0.1",
|
||||
"http://192.168.103.159",
|
||||
"http://192.168.103.159:3000",
|
||||
"http://192.168.103.159:8000",
|
||||
"http://front:4173",
|
||||
"http://front:5173",
|
||||
|
|
|
@ -18,8 +18,10 @@ from django.contrib import admin
|
|||
from django.urls import path
|
||||
from django.conf.urls.static import static
|
||||
from api import views
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
urlpatterns = [
|
||||
path("admin/", admin.site.urls),
|
||||
path("api/products", views.products),
|
||||
path("api/products", csrf_exempt(views.Products.as_view())),
|
||||
path("api/floorplan", csrf_exempt(views.FloorplanView.as_view())),
|
||||
] + static('/files', document_root='files')
|
||||
|
|
|
@ -7,6 +7,8 @@ export {}
|
|||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
copy: typeof import('./src/components/Home copy.vue')['default']
|
||||
Floorplan: typeof import('./src/components/Floorplan.vue')['default']
|
||||
Game: typeof import('./src/components/Game.vue')['default']
|
||||
Home: typeof import('./src/components/Home.vue')['default']
|
||||
IMdi3dRotation: typeof import('~icons/mdi/3d-rotation')['default']
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue';
|
||||
import { useFloorplanStore } from '../stores/floorplan';
|
||||
|
||||
const floorplan = useFloorplanStore()
|
||||
onMounted(()=>{
|
||||
floorplan.getData()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div class="container">
|
||||
|
||||
</div>
|
||||
</template>
|
|
@ -15,6 +15,9 @@
|
|||
<li>
|
||||
<RouterLink to="game">Игра</RouterLink>
|
||||
</li>
|
||||
<li>
|
||||
<RouterLink to="floorplan">План помещения и маршрут</RouterLink>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://timesheet.kustarshina.ru/">Табель рабочего времени</a>
|
||||
</li>
|
||||
|
|
|
@ -8,11 +8,13 @@ import App from './App.vue'
|
|||
import Home from './components/Home.vue'
|
||||
import Projects from './components/Projects.vue'
|
||||
import Game from './components/Game.vue'
|
||||
import Floorplan from './components/Floorplan.vue'
|
||||
|
||||
const routes = [
|
||||
{ path: '/', component: Home },
|
||||
{ path: '/projects', component: Projects },
|
||||
{ path: '/game', component: Game },
|
||||
{ path: '/floorplan', component: Floorplan },
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { SERVER_URL } from '../constants'
|
||||
|
||||
export const useFloorplanStore = defineStore('floorplan', {
|
||||
state: () => {
|
||||
return {
|
||||
// for initially empty lists
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async getData() {
|
||||
try {
|
||||
const res = await fetch(`${SERVER_URL}/api/floorplan`)
|
||||
const data = await res.json()
|
||||
if (data.length) {
|
||||
// this.list = data
|
||||
}
|
||||
} catch (error) {
|
||||
// this.list = []
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
export interface ProductInfo {
|
||||
id: number
|
||||
title: string
|
||||
description: string
|
||||
model3d?: string
|
||||
image1?: string
|
||||
image2?: string
|
||||
image3?: string
|
||||
}
|
|
@ -3,13 +3,8 @@ name = "interactive-table"
|
|||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Your Name <you@example.com>"]
|
||||
<<<<<<< HEAD
|
||||
readme = "README.md"
|
||||
package-mode=false
|
||||
=======
|
||||
readme = "readme.md"
|
||||
package-mode = false
|
||||
>>>>>>> bx-434-game
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
|
@ -19,13 +14,9 @@ taskipy = "^1.12.2"
|
|||
django-cors-headers = "^4.3.1"
|
||||
pillow = "^10.3.0"
|
||||
python-dotenv = "^1.0.1"
|
||||
<<<<<<< HEAD
|
||||
psycopg2 = "^2.9.9"
|
||||
opencv-python = "^4.9.0.80"
|
||||
potracer = "^0.0.4"
|
||||
=======
|
||||
psycopg2-binary = "^2.9.9"
|
||||
>>>>>>> bx-434-game
|
||||
|
||||
|
||||
[build-system]
|
||||
|
|
Loading…
Reference in New Issue