archipack: remove 2d to 3d module (shapely dep issue)
This commit is contained in:
parent
5bf39c3cfb
commit
d1b57c76ad
|
@ -27,14 +27,14 @@
|
|||
|
||||
bl_info = {
|
||||
'name': 'Archipack',
|
||||
'description': 'Architectural objects and 2d polygons detection from unordered splines',
|
||||
'description': 'Architectural objects',
|
||||
'author': 's-leger',
|
||||
'license': 'GPL',
|
||||
'deps': 'shapely',
|
||||
'deps': '',
|
||||
'version': (1, 2, 6),
|
||||
'blender': (2, 7, 8),
|
||||
'location': 'View3D > Tools > Create > Archipack',
|
||||
'warning': '2d to 3d require shapely python module (see setup in documentation)',
|
||||
'warning': '',
|
||||
'wiki_url': 'https://github.com/s-leger/archipack/wiki',
|
||||
'tracker_url': 'https://github.com/s-leger/archipack/issues',
|
||||
'link': 'https://github.com/s-leger/archipack',
|
||||
|
@ -53,19 +53,12 @@ if "bpy" in locals():
|
|||
imp.reload(archipack_door)
|
||||
imp.reload(archipack_window)
|
||||
imp.reload(archipack_stair)
|
||||
imp.reload(archipack_wall)
|
||||
imp.reload(archipack_wall2)
|
||||
imp.reload(archipack_slab)
|
||||
imp.reload(archipack_fence)
|
||||
imp.reload(archipack_truss)
|
||||
imp.reload(archipack_floor)
|
||||
imp.reload(archipack_rendering)
|
||||
try:
|
||||
imp.reload(archipack_polylib)
|
||||
HAS_POLYLIB = True
|
||||
except:
|
||||
HAS_POLYLIB = False
|
||||
pass
|
||||
|
||||
print("archipack: reload ready")
|
||||
else:
|
||||
|
@ -76,24 +69,12 @@ else:
|
|||
from . import archipack_door
|
||||
from . import archipack_window
|
||||
from . import archipack_stair
|
||||
from . import archipack_wall
|
||||
from . import archipack_wall2
|
||||
from . import archipack_slab
|
||||
from . import archipack_fence
|
||||
from . import archipack_truss
|
||||
from . import archipack_floor
|
||||
from . import archipack_rendering
|
||||
try:
|
||||
"""
|
||||
polylib depends on shapely
|
||||
raise ImportError when not meet
|
||||
"""
|
||||
from . import archipack_polylib
|
||||
HAS_POLYLIB = True
|
||||
except:
|
||||
print("archipack: shapely not found, using built in modules only")
|
||||
HAS_POLYLIB = False
|
||||
pass
|
||||
|
||||
print("archipack: ready")
|
||||
|
||||
|
@ -121,14 +102,11 @@ icons_collection = {}
|
|||
|
||||
def update_panel(self, context):
|
||||
try:
|
||||
bpy.utils.unregister_class(TOOLS_PT_Archipack_PolyLib)
|
||||
bpy.utils.unregister_class(TOOLS_PT_Archipack_Tools)
|
||||
bpy.utils.unregister_class(TOOLS_PT_Archipack_Create)
|
||||
except:
|
||||
pass
|
||||
prefs = context.user_preferences.addons[__name__].preferences
|
||||
TOOLS_PT_Archipack_PolyLib.bl_category = prefs.tools_category
|
||||
bpy.utils.register_class(TOOLS_PT_Archipack_PolyLib)
|
||||
TOOLS_PT_Archipack_Tools.bl_category = prefs.tools_category
|
||||
bpy.utils.register_class(TOOLS_PT_Archipack_Tools)
|
||||
TOOLS_PT_Archipack_Create.bl_category = prefs.create_category
|
||||
|
@ -155,11 +133,6 @@ class Archipack_Pref(AddonPreferences):
|
|||
description="Put Achipack's object into a sub menu (shift+a)",
|
||||
default=True
|
||||
)
|
||||
enable_2d_to_3d = BoolProperty(
|
||||
name="Enable 2d to 3d",
|
||||
description="Enable 2d to 3d module",
|
||||
default=False
|
||||
)
|
||||
max_style_draw_tool = BoolProperty(
|
||||
name="Draw a wall use 3dsmax style",
|
||||
description="Reverse clic / release cycle for Draw a wall",
|
||||
|
@ -251,12 +224,6 @@ class Archipack_Pref(AddonPreferences):
|
|||
box.label("Features")
|
||||
box.prop(self, "max_style_draw_tool")
|
||||
box = layout.box()
|
||||
box.label("2d to 3d")
|
||||
if not HAS_POLYLIB:
|
||||
box.label(text="WARNING Shapely python module not found", icon="ERROR")
|
||||
box.label(text="2d to 3d tools are disabled, see setup in documentation")
|
||||
box.prop(self, "enable_2d_to_3d")
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
split = row.split(percentage=0.5)
|
||||
col = split.column()
|
||||
|
@ -286,114 +253,6 @@ class Archipack_Pref(AddonPreferences):
|
|||
# ----------------------------------------------------
|
||||
|
||||
|
||||
class TOOLS_PT_Archipack_PolyLib(Panel):
|
||||
bl_label = "Archipack 2d to 3d"
|
||||
bl_idname = "TOOLS_PT_Archipack_PolyLib"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "TOOLS"
|
||||
bl_category = "Tools"
|
||||
bl_context = "objectmode"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
|
||||
global archipack_polylib
|
||||
return (HAS_POLYLIB and
|
||||
context.user_preferences.addons[__name__].preferences.enable_2d_to_3d and
|
||||
((archipack_polylib.vars_dict['select_polygons'] is not None) or
|
||||
(context.object is not None and context.object.type == 'CURVE')))
|
||||
|
||||
def draw(self, context):
|
||||
global icons_collection
|
||||
icons = icons_collection["main"]
|
||||
layout = self.layout
|
||||
row = layout.row(align=True)
|
||||
box = row.box()
|
||||
row = box.row(align=True)
|
||||
row.operator(
|
||||
"archipack.polylib_detect",
|
||||
icon_value=icons["detect"].icon_id,
|
||||
text='Detect'
|
||||
).extend = context.window_manager.archipack_polylib.extend
|
||||
row.prop(context.window_manager.archipack_polylib, "extend")
|
||||
row = box.row(align=True)
|
||||
row.prop(context.window_manager.archipack_polylib, "resolution")
|
||||
row = box.row(align=True)
|
||||
row.label(text="Polygons")
|
||||
row = box.row(align=True)
|
||||
row.operator(
|
||||
"archipack.polylib_pick_2d_polygons",
|
||||
icon_value=icons["selection"].icon_id,
|
||||
text='Select'
|
||||
).action = 'select'
|
||||
row.operator(
|
||||
"archipack.polylib_pick_2d_polygons",
|
||||
icon_value=icons["union"].icon_id,
|
||||
text='Union'
|
||||
).action = 'union'
|
||||
row.operator(
|
||||
"archipack.polylib_output_polygons",
|
||||
icon_value=icons["polygons"].icon_id,
|
||||
text='All')
|
||||
row = box.row(align=True)
|
||||
row.operator(
|
||||
"archipack.polylib_pick_2d_polygons",
|
||||
text='Wall',
|
||||
icon_value=icons["wall"].icon_id).action = 'wall'
|
||||
row.prop(context.window_manager.archipack_polylib, "solidify_thickness")
|
||||
row = box.row(align=True)
|
||||
row.operator("archipack.polylib_pick_2d_polygons",
|
||||
text='Window',
|
||||
icon_value=icons["window"].icon_id).action = 'window'
|
||||
row.operator("archipack.polylib_pick_2d_polygons",
|
||||
text='Door',
|
||||
icon_value=icons["door"].icon_id).action = 'door'
|
||||
row.operator("archipack.polylib_pick_2d_polygons", text='Rectangle').action = 'rectangle'
|
||||
row = box.row(align=True)
|
||||
row.label(text="Lines")
|
||||
row = box.row(align=True)
|
||||
row.operator(
|
||||
"archipack.polylib_pick_2d_lines",
|
||||
icon_value=icons["selection"].icon_id,
|
||||
text='Lines').action = 'select'
|
||||
row.operator(
|
||||
"archipack.polylib_pick_2d_lines",
|
||||
icon_value=icons["union"].icon_id,
|
||||
text='Union').action = 'union'
|
||||
row.operator(
|
||||
"archipack.polylib_output_lines",
|
||||
icon_value=icons["polygons"].icon_id,
|
||||
text='All')
|
||||
row = box.row(align=True)
|
||||
row.label(text="Points")
|
||||
row = box.row(align=True)
|
||||
row.operator(
|
||||
"archipack.polylib_pick_2d_points",
|
||||
icon_value=icons["selection"].icon_id,
|
||||
text='Points').action = 'select'
|
||||
row = layout.row(align=True)
|
||||
box = row.box()
|
||||
row = box.row(align=True)
|
||||
row.operator("archipack.polylib_simplify")
|
||||
row.prop(context.window_manager.archipack_polylib, "simplify_tolerance")
|
||||
row = box.row(align=True)
|
||||
row.prop(context.window_manager.archipack_polylib, "simplify_preserve_topology")
|
||||
row = layout.row(align=True)
|
||||
box = row.box()
|
||||
row = box.row(align=True)
|
||||
row.operator("archipack.polylib_offset")
|
||||
row = box.row(align=True)
|
||||
row.prop(context.window_manager.archipack_polylib, "offset_distance")
|
||||
row = box.row(align=True)
|
||||
row.prop(context.window_manager.archipack_polylib, "offset_side")
|
||||
row = box.row(align=True)
|
||||
row.prop(context.window_manager.archipack_polylib, "offset_resolution")
|
||||
row = box.row(align=True)
|
||||
row.prop(context.window_manager.archipack_polylib, "offset_join_style")
|
||||
row = box.row(align=True)
|
||||
row.prop(context.window_manager.archipack_polylib, "offset_mitre_limit")
|
||||
|
||||
|
||||
class TOOLS_PT_Archipack_Tools(Panel):
|
||||
bl_label = "Archipack Tools"
|
||||
bl_idname = "TOOLS_PT_Archipack_Tools"
|
||||
|
@ -602,7 +461,6 @@ def register():
|
|||
archipack_door.register()
|
||||
archipack_window.register()
|
||||
archipack_stair.register()
|
||||
archipack_wall.register()
|
||||
archipack_wall2.register()
|
||||
archipack_slab.register()
|
||||
archipack_fence.register()
|
||||
|
@ -610,9 +468,6 @@ def register():
|
|||
archipack_floor.register()
|
||||
archipack_rendering.register()
|
||||
|
||||
if HAS_POLYLIB:
|
||||
archipack_polylib.register()
|
||||
|
||||
bpy.utils.register_class(archipack_data)
|
||||
WindowManager.archipack = PointerProperty(type=archipack_data)
|
||||
bpy.utils.register_class(Archipack_Pref)
|
||||
|
@ -626,7 +481,6 @@ def unregister():
|
|||
bpy.types.INFO_MT_mesh_add.remove(menu_func)
|
||||
bpy.utils.unregister_class(ARCHIPACK_create_menu)
|
||||
|
||||
bpy.utils.unregister_class(TOOLS_PT_Archipack_PolyLib)
|
||||
bpy.utils.unregister_class(TOOLS_PT_Archipack_Tools)
|
||||
bpy.utils.unregister_class(TOOLS_PT_Archipack_Create)
|
||||
bpy.utils.unregister_class(Archipack_Pref)
|
||||
|
@ -639,7 +493,6 @@ def unregister():
|
|||
archipack_door.unregister()
|
||||
archipack_window.unregister()
|
||||
archipack_stair.unregister()
|
||||
archipack_wall.unregister()
|
||||
archipack_wall2.unregister()
|
||||
archipack_slab.unregister()
|
||||
archipack_fence.unregister()
|
||||
|
@ -647,9 +500,6 @@ def unregister():
|
|||
archipack_floor.unregister()
|
||||
archipack_rendering.unregister()
|
||||
|
||||
if HAS_POLYLIB:
|
||||
archipack_polylib.unregister()
|
||||
|
||||
bpy.utils.unregister_class(archipack_data)
|
||||
del WindowManager.archipack
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,137 +0,0 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110- 1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# Author: Stephen Leger (s-leger)
|
||||
#
|
||||
# ----------------------------------------------------------
|
||||
import bpy
|
||||
import bmesh
|
||||
from bpy.types import Operator, PropertyGroup, Mesh, Panel
|
||||
from bpy.props import FloatProperty, CollectionProperty
|
||||
from .archipack_object import ArchipackObject
|
||||
|
||||
|
||||
def update_wall(self, context):
|
||||
self.update(context)
|
||||
|
||||
|
||||
class archipack_wall(ArchipackObject, PropertyGroup):
|
||||
z = FloatProperty(
|
||||
name='height',
|
||||
min=0.1, max=10000,
|
||||
default=2.7, precision=2,
|
||||
description='height', update=update_wall,
|
||||
)
|
||||
|
||||
def update(self, context):
|
||||
# update height via bmesh to avoid loosing material ids
|
||||
# this should be the rule for other simple objects
|
||||
# as long as there is no topologic changes
|
||||
o = context.active_object
|
||||
if archipack_wall.datablock(o) != self:
|
||||
return
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
me = o.data
|
||||
bm = bmesh.from_edit_mesh(me)
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.faces.ensure_lookup_table()
|
||||
new_z = self.z
|
||||
last_z = list(v.co.z for v in bm.verts)
|
||||
max_z = max(last_z)
|
||||
for v in bm.verts:
|
||||
if v.co.z == max_z:
|
||||
v.co.z = new_z
|
||||
bmesh.update_edit_mesh(me, True)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
|
||||
class ARCHIPACK_PT_wall(Panel):
|
||||
bl_idname = "ARCHIPACK_PT_wall"
|
||||
bl_label = "Wall"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = 'ArchiPack'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return archipack_wall.filter(context.active_object)
|
||||
|
||||
def draw(self, context):
|
||||
|
||||
prop = archipack_wall.datablock(context.active_object)
|
||||
if prop is None:
|
||||
return
|
||||
layout = self.layout
|
||||
layout.prop(prop, 'z')
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Define operator class to create object
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
class ARCHIPACK_OT_wall(Operator):
|
||||
bl_idname = "archipack.wall"
|
||||
bl_label = "Wall"
|
||||
bl_description = "Add wall parameters to active object"
|
||||
bl_category = 'Archipack'
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
z = FloatProperty(
|
||||
name="z",
|
||||
default=2.7
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object is not None
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
row = layout.row()
|
||||
row.label("Use Properties panel (N) to define parms", icon='INFO')
|
||||
|
||||
def execute(self, context):
|
||||
if context.mode == "OBJECT":
|
||||
o = context.active_object
|
||||
if archipack_wall.filter(o):
|
||||
return {'CANCELLED'}
|
||||
params = o.data.archipack_wall.add()
|
||||
params.z = self.z
|
||||
return {'FINISHED'}
|
||||
else:
|
||||
self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
|
||||
return {'CANCELLED'}
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(archipack_wall)
|
||||
Mesh.archipack_wall = CollectionProperty(type=archipack_wall)
|
||||
bpy.utils.register_class(ARCHIPACK_PT_wall)
|
||||
bpy.utils.register_class(ARCHIPACK_OT_wall)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(archipack_wall)
|
||||
del Mesh.archipack_wall
|
||||
bpy.utils.unregister_class(ARCHIPACK_PT_wall)
|
||||
bpy.utils.unregister_class(ARCHIPACK_OT_wall)
|
|
@ -1,97 +0,0 @@
|
|||
|
||||
import array
|
||||
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110- 1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# Author: Stephen Leger (s-leger)
|
||||
#
|
||||
# ----------------------------------------------------------
|
||||
|
||||
|
||||
class BitArray():
|
||||
|
||||
def __init__(self, bitSize, fill=0):
|
||||
self.size = bitSize
|
||||
intSize = bitSize >> 5
|
||||
if (bitSize & 31):
|
||||
intSize += 1
|
||||
if fill == 1:
|
||||
fill = 4294967295
|
||||
else:
|
||||
fill = 0
|
||||
self.bitArray = array.array('I')
|
||||
self.bitArray.extend((fill,) * intSize)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.list)
|
||||
|
||||
def bit_location(self, bit_num):
|
||||
return bit_num >> 5, bit_num & 31
|
||||
|
||||
def test(self, bit_num):
|
||||
record, offset = self.bit_location(bit_num)
|
||||
mask = 1 << offset
|
||||
return(self.bitArray[record] & mask)
|
||||
|
||||
def set(self, bit_num):
|
||||
record, offset = self.bit_location(bit_num)
|
||||
mask = 1 << offset
|
||||
self.bitArray[record] |= mask
|
||||
|
||||
def clear(self, bit_num):
|
||||
record, offset = self.bit_location(bit_num)
|
||||
mask = ~(1 << offset)
|
||||
self.bitArray[record] &= mask
|
||||
|
||||
def toggle(self, bit_num):
|
||||
record, offset = self.bit_location(bit_num)
|
||||
mask = 1 << offset
|
||||
self.bitArray[record] ^= mask
|
||||
|
||||
@property
|
||||
def len(self):
|
||||
return len(self.bitArray)
|
||||
|
||||
@property
|
||||
def copy(self):
|
||||
copy = BitArray(self.size)
|
||||
for i in range(self.len):
|
||||
copy.bitArray[i] = self.bitArray[i]
|
||||
return copy
|
||||
|
||||
@property
|
||||
def list(self):
|
||||
return [x for x in range(self.size) if self.test(x) > 0]
|
||||
|
||||
def none(self):
|
||||
for i in range(self.len):
|
||||
self.bitArray[i] = 0
|
||||
|
||||
def reverse(self):
|
||||
for i in range(self.len):
|
||||
self.bitArray[i] = 4294967295 ^ self.bitArray[i]
|
||||
|
||||
def all(self):
|
||||
for i in range(self.len):
|
||||
self.bitArray[i] = 4294967295
|
|
@ -1,187 +0,0 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
"""
|
||||
# Pyqtree
|
||||
|
||||
Pyqtree is a pure Python spatial index for GIS or rendering usage.
|
||||
It stores and quickly retrieves items from a 2x2 rectangular grid area,
|
||||
and grows in depth and detail as more items are added.
|
||||
The actual quad tree implementation is adapted from
|
||||
[Matt Rasmussen's compbio library](https://github.com/mdrasmus/compbio/blob/master/rasmus/quadtree.py)
|
||||
and extended for geospatial use.
|
||||
|
||||
|
||||
## Platforms
|
||||
|
||||
Python 2 and 3.
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
Pyqtree is written in pure Python and has no dependencies.
|
||||
|
||||
|
||||
## Installing It
|
||||
|
||||
Installing Pyqtree can be done by opening your terminal or commandline and typing:
|
||||
|
||||
pip install pyqtree
|
||||
|
||||
Alternatively, you can simply download the "pyqtree.py" file and place
|
||||
it anywhere Python can import it, such as the Python site-packages folder.
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
Start your script by importing the quad tree.
|
||||
|
||||
from pyqtree import Index
|
||||
|
||||
Setup the spatial index, giving it a bounding box area to keep track of.
|
||||
The bounding box being in a four-tuple: (xmin, ymin, xmax, ymax).
|
||||
|
||||
spindex = Index(bbox=(0, 0, 100, 100))
|
||||
|
||||
Populate the index with items that you want to be retrieved at a later point,
|
||||
along with each item's geographic bbox.
|
||||
|
||||
# this example assumes you have a list of items with bbox attribute
|
||||
for item in items:
|
||||
spindex.insert(item, item.bbox)
|
||||
|
||||
Then when you have a region of interest and you wish to retrieve items from that region,
|
||||
just use the index's intersect method. This quickly gives you a list of the stored items
|
||||
whose bboxes intersects your region of interests.
|
||||
|
||||
overlapbbox = (51, 51, 86, 86)
|
||||
matches = spindex.intersect(overlapbbox)
|
||||
|
||||
There are other things that can be done as well, but that's it for the main usage!
|
||||
|
||||
|
||||
## More Information:
|
||||
|
||||
- [Home Page](http://github.com/karimbahgat/Pyqtree)
|
||||
- [API Documentation](http://pythonhosted.org/Pyqtree)
|
||||
|
||||
|
||||
## License:
|
||||
|
||||
This code is free to share, use, reuse, and modify according to the MIT license, see LICENSE.txt.
|
||||
|
||||
|
||||
## Credits:
|
||||
|
||||
- Karim Bahgat (2015)
|
||||
- Joschua Gandert (2016)
|
||||
|
||||
"""
|
||||
|
||||
|
||||
__version__ = "0.25.0"
|
||||
|
||||
# PYTHON VERSION CHECK
|
||||
import sys
|
||||
|
||||
|
||||
PYTHON3 = int(sys.version[0]) == 3
|
||||
if PYTHON3:
|
||||
xrange = range
|
||||
|
||||
|
||||
class _QuadNode(object):
|
||||
def __init__(self, item, rect):
|
||||
self.item = item
|
||||
self.rect = rect
|
||||
|
||||
|
||||
class _QuadTree(object):
|
||||
"""
|
||||
Internal backend version of the index.
|
||||
The index being used behind the scenes. Has all the same methods as the user
|
||||
index, but requires more technical arguments when initiating it than the
|
||||
user-friendly version.
|
||||
"""
|
||||
def __init__(self, x, y, width, height, max_items, max_depth, _depth=0):
|
||||
self.nodes = []
|
||||
self.children = []
|
||||
self.center = (x, y)
|
||||
self.width, self.height = width, height
|
||||
self.max_items = max_items
|
||||
self.max_depth = max_depth
|
||||
self._depth = _depth
|
||||
|
||||
def _insert(self, item, bbox):
|
||||
if len(self.children) == 0:
|
||||
node = _QuadNode(item, bbox)
|
||||
self.nodes.append(node)
|
||||
if len(self.nodes) > self.max_items and self._depth < self.max_depth:
|
||||
self._split()
|
||||
else:
|
||||
self._insert_into_children(item, bbox)
|
||||
|
||||
def _intersect(self, rect, results=None):
|
||||
if results is None:
|
||||
results = set()
|
||||
# search children
|
||||
if self.children:
|
||||
if rect[0] <= self.center[0]:
|
||||
if rect[1] <= self.center[1]:
|
||||
self.children[0]._intersect(rect, results)
|
||||
if rect[3] >= self.center[1]:
|
||||
self.children[1]._intersect(rect, results)
|
||||
if rect[2] >= self.center[0]:
|
||||
if rect[1] <= self.center[1]:
|
||||
self.children[2]._intersect(rect, results)
|
||||
if rect[3] >= self.center[1]:
|
||||
self.children[3]._intersect(rect, results)
|
||||
# search node at this level
|
||||
for node in self.nodes:
|
||||
if (node.rect[2] >= rect[0] and node.rect[0] <= rect[2] and
|
||||
node.rect[3] >= rect[1] and node.rect[1] <= rect[3]):
|
||||
results.add(node.item)
|
||||
return results
|
||||
|
||||
def _insert_into_children(self, item, rect):
|
||||
# if rect spans center then insert here
|
||||
if (rect[0] <= self.center[0] and rect[2] >= self.center[0] and
|
||||
rect[1] <= self.center[1] and rect[3] >= self.center[1]):
|
||||
node = _QuadNode(item, rect)
|
||||
self.nodes.append(node)
|
||||
else:
|
||||
# try to insert into children
|
||||
if rect[0] <= self.center[0]:
|
||||
if rect[1] <= self.center[1]:
|
||||
self.children[0]._insert(item, rect)
|
||||
if rect[3] >= self.center[1]:
|
||||
self.children[1]._insert(item, rect)
|
||||
if rect[2] > self.center[0]:
|
||||
if rect[1] <= self.center[1]:
|
||||
self.children[2]._insert(item, rect)
|
||||
if rect[3] >= self.center[1]:
|
||||
self.children[3]._insert(item, rect)
|
||||
|
||||
def _split(self):
|
||||
quartwidth = self.width / 4.0
|
||||
quartheight = self.height / 4.0
|
||||
halfwidth = self.width / 2.0
|
||||
halfheight = self.height / 2.0
|
||||
x1 = self.center[0] - quartwidth
|
||||
x2 = self.center[0] + quartwidth
|
||||
y1 = self.center[1] - quartheight
|
||||
y2 = self.center[1] + quartheight
|
||||
new_depth = self._depth + 1
|
||||
self.children = [_QuadTree(x1, y1, halfwidth, halfheight,
|
||||
self.max_items, self.max_depth, new_depth),
|
||||
_QuadTree(x1, y2, halfwidth, halfheight,
|
||||
self.max_items, self.max_depth, new_depth),
|
||||
_QuadTree(x2, y1, halfwidth, halfheight,
|
||||
self.max_items, self.max_depth, new_depth),
|
||||
_QuadTree(x2, y2, halfwidth, halfheight,
|
||||
self.max_items, self.max_depth, new_depth)]
|
||||
nodes = self.nodes
|
||||
self.nodes = []
|
||||
for node in nodes:
|
||||
self._insert_into_children(node.item, node.rect)
|
Loading…
Reference in New Issue