Add cython modules.
parent
6352405206
commit
14e28827ee
|
@ -0,0 +1,40 @@
|
||||||
|
[![Build status](https://ci.appveyor.com/api/projects/status/b2o8jw7xnfqghqr5?svg=true)](https://ci.appveyor.com/project/KmolYuan/solvespace)
|
||||||
|
[![Build status](https://travis-ci.org/KmolYuan/solvespace.svg)](https://travis-ci.org/KmolYuan/solvespace)
|
||||||
|
![OS](https://img.shields.io/badge/OS-Windows%2C%20Mac%20OS%2C%20Ubuntu-blue.svg)
|
||||||
|
[![GitHub license](https://img.shields.io/badge/license-GPLv3+-blue.svg)](https://raw.githubusercontent.com/KmolYuan/solvespace/master/LICENSE)
|
||||||
|
|
||||||
|
python-solvespace
|
||||||
|
===
|
||||||
|
|
||||||
|
Python library from solver of SolveSpace.
|
||||||
|
|
||||||
|
Feature for CDemo and Python interface can see [here](https://github.com/KmolYuan/python-solvespace/blob/master/Cython/DOC.txt).
|
||||||
|
|
||||||
|
Build and Test
|
||||||
|
===
|
||||||
|
|
||||||
|
Requirement:
|
||||||
|
|
||||||
|
+ [Cython]
|
||||||
|
|
||||||
|
Build and install the module:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python setup.py install
|
||||||
|
python setup.py install --user # User mode
|
||||||
|
```
|
||||||
|
|
||||||
|
Run unit test:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python tests/test_slvs.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Uninstall the module:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip uninstall python_solvespace
|
||||||
|
```
|
||||||
|
|
||||||
|
[GNU Make]: https://sourceforge.net/projects/mingw-w64/files/latest/download?source=files
|
||||||
|
[Cython]: https://cython.org/
|
|
@ -0,0 +1,26 @@
|
||||||
|
--- cygwinccompiler.py
|
||||||
|
+++ cygwinccompiler.py
|
||||||
|
@@ -82,7 +82,25 @@ def get_msvcr():
|
||||||
|
elif msc_ver == '1600':
|
||||||
|
# VS2010 / MSVC 10.0
|
||||||
|
return ['msvcr100']
|
||||||
|
+ elif msc_ver == '1700':
|
||||||
|
+ # Visual Studio 2012 / Visual C++ 11.0
|
||||||
|
+ return ['msvcr110']
|
||||||
|
+ elif msc_ver == '1800':
|
||||||
|
+ # Visual Studio 2013 / Visual C++ 12.0
|
||||||
|
+ return ['msvcr120']
|
||||||
|
+ elif msc_ver == '1900':
|
||||||
|
+ # Visual Studio 2015 / Visual C++ 14.0
|
||||||
|
+ # "msvcr140.dll no longer exists" http://blogs.msdn.com/b/vcblog/archive/2014/06/03/visual-studio-14-ctp.aspx
|
||||||
|
+ return ['vcruntime140']
|
||||||
|
+ elif msc_ver == '1910':
|
||||||
|
+ return ['vcruntime140']
|
||||||
|
+ elif msc_ver == '1914':
|
||||||
|
+ return ['vcruntime140']
|
||||||
|
+ elif msc_ver == '1915':
|
||||||
|
+ return ['vcruntime140']
|
||||||
|
+ elif msc_ver == '1916':
|
||||||
|
+ return ['vcruntime140']
|
||||||
|
else:
|
||||||
|
raise ValueError("Unknown MS Compiler version %s " % msc_ver)
|
|
@ -0,0 +1,34 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""'python_solvespace' module is a wrapper of
|
||||||
|
Python binding Solvespace solver libraries.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__author__ = "Yuan Chang"
|
||||||
|
__copyright__ = "Copyright (C) 2016-2019"
|
||||||
|
__license__ = "GPLv3+"
|
||||||
|
__email__ = "pyslvs@gmail.com"
|
||||||
|
|
||||||
|
from .slvs import (
|
||||||
|
quaternion_u,
|
||||||
|
quaternion_v,
|
||||||
|
quaternion_n,
|
||||||
|
make_quaternion,
|
||||||
|
Constraint,
|
||||||
|
ResultFlag,
|
||||||
|
Params,
|
||||||
|
Entity,
|
||||||
|
SolverSystem,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'quaternion_u',
|
||||||
|
'quaternion_v',
|
||||||
|
'quaternion_n',
|
||||||
|
'make_quaternion',
|
||||||
|
'Constraint',
|
||||||
|
'ResultFlag',
|
||||||
|
'Params',
|
||||||
|
'Entity',
|
||||||
|
'SolverSystem',
|
||||||
|
]
|
|
@ -0,0 +1,325 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# cython: language_level=3
|
||||||
|
|
||||||
|
"""Wrapper header of Solvespace.
|
||||||
|
|
||||||
|
author: Yuan Chang
|
||||||
|
copyright: Copyright (C) 2016-2019
|
||||||
|
license: GPLv3+
|
||||||
|
email: pyslvs@gmail.com
|
||||||
|
"""
|
||||||
|
|
||||||
|
from libc.stdint cimport uint32_t
|
||||||
|
from libcpp.vector cimport vector
|
||||||
|
from libcpp.map cimport map as cmap
|
||||||
|
|
||||||
|
cdef extern from "slvs.h" nogil:
|
||||||
|
|
||||||
|
ctypedef uint32_t Slvs_hParam
|
||||||
|
ctypedef uint32_t Slvs_hEntity
|
||||||
|
ctypedef uint32_t Slvs_hConstraint
|
||||||
|
ctypedef uint32_t Slvs_hGroup
|
||||||
|
|
||||||
|
# Virtual work plane entity
|
||||||
|
Slvs_hEntity SLVS_FREE_IN_3D
|
||||||
|
|
||||||
|
ctypedef struct Slvs_Param:
|
||||||
|
Slvs_hParam h
|
||||||
|
Slvs_hGroup group
|
||||||
|
double val
|
||||||
|
|
||||||
|
# Entity type
|
||||||
|
int SLVS_E_POINT_IN_3D
|
||||||
|
int SLVS_E_POINT_IN_2D
|
||||||
|
|
||||||
|
int SLVS_E_NORMAL_IN_2D
|
||||||
|
int SLVS_E_NORMAL_IN_3D
|
||||||
|
|
||||||
|
int SLVS_E_DISTANCE
|
||||||
|
|
||||||
|
int SLVS_E_WORKPLANE
|
||||||
|
int SLVS_E_LINE_SEGMENT
|
||||||
|
int SLVS_E_CUBIC
|
||||||
|
int SLVS_E_CIRCLE
|
||||||
|
int SLVS_E_ARC_OF_CIRCLE
|
||||||
|
|
||||||
|
ctypedef struct Slvs_Entity:
|
||||||
|
Slvs_hEntity h
|
||||||
|
Slvs_hGroup group
|
||||||
|
int type
|
||||||
|
Slvs_hEntity wrkpl
|
||||||
|
Slvs_hEntity point[4]
|
||||||
|
Slvs_hEntity normal
|
||||||
|
Slvs_hEntity distance
|
||||||
|
Slvs_hParam param[4]
|
||||||
|
|
||||||
|
ctypedef struct Slvs_Constraint:
|
||||||
|
Slvs_hConstraint h
|
||||||
|
Slvs_hGroup group
|
||||||
|
int type
|
||||||
|
Slvs_hEntity wrkpl
|
||||||
|
double valA
|
||||||
|
Slvs_hEntity ptA
|
||||||
|
Slvs_hEntity ptB
|
||||||
|
Slvs_hEntity entityA
|
||||||
|
Slvs_hEntity entityB
|
||||||
|
Slvs_hEntity entityC
|
||||||
|
Slvs_hEntity entityD
|
||||||
|
int other
|
||||||
|
int other2
|
||||||
|
|
||||||
|
ctypedef struct Slvs_System:
|
||||||
|
Slvs_Param *param
|
||||||
|
int params
|
||||||
|
Slvs_Entity *entity
|
||||||
|
int entities
|
||||||
|
Slvs_Constraint *constraint
|
||||||
|
int constraints
|
||||||
|
Slvs_hParam dragged[4]
|
||||||
|
int calculateFaileds
|
||||||
|
Slvs_hConstraint *failed
|
||||||
|
int faileds
|
||||||
|
int dof
|
||||||
|
int result
|
||||||
|
|
||||||
|
void Slvs_Solve(Slvs_System *sys, Slvs_hGroup hg)
|
||||||
|
void Slvs_QuaternionU(
|
||||||
|
double qw, double qx, double qy, double qz,
|
||||||
|
double *x, double *y, double *z
|
||||||
|
)
|
||||||
|
void Slvs_QuaternionV(
|
||||||
|
double qw, double qx, double qy, double qz,
|
||||||
|
double *x, double *y, double *z
|
||||||
|
)
|
||||||
|
void Slvs_QuaternionN(
|
||||||
|
double qw, double qx, double qy, double qz,
|
||||||
|
double *x, double *y, double *z
|
||||||
|
)
|
||||||
|
void Slvs_MakeQuaternion(
|
||||||
|
double ux, double uy, double uz,
|
||||||
|
double vx, double vy, double vz,
|
||||||
|
double *qw, double *qx, double *qy, double *qz
|
||||||
|
)
|
||||||
|
Slvs_Param Slvs_MakeParam(Slvs_hParam h, Slvs_hGroup group, double val)
|
||||||
|
Slvs_Entity Slvs_MakePoint2d(
|
||||||
|
Slvs_hEntity h, Slvs_hGroup group,
|
||||||
|
Slvs_hEntity wrkpl,
|
||||||
|
Slvs_hParam u, Slvs_hParam v
|
||||||
|
)
|
||||||
|
Slvs_Entity Slvs_MakePoint3d(
|
||||||
|
Slvs_hEntity h, Slvs_hGroup group,
|
||||||
|
Slvs_hParam x, Slvs_hParam y, Slvs_hParam z
|
||||||
|
)
|
||||||
|
Slvs_Entity Slvs_MakeNormal3d(
|
||||||
|
Slvs_hEntity h, Slvs_hGroup group,
|
||||||
|
Slvs_hParam qw, Slvs_hParam qx,
|
||||||
|
Slvs_hParam qy, Slvs_hParam qz
|
||||||
|
)
|
||||||
|
Slvs_Entity Slvs_MakeNormal2d(
|
||||||
|
Slvs_hEntity h, Slvs_hGroup group,
|
||||||
|
Slvs_hEntity wrkpl
|
||||||
|
)
|
||||||
|
Slvs_Entity Slvs_MakeDistance(
|
||||||
|
Slvs_hEntity h, Slvs_hGroup group,
|
||||||
|
Slvs_hEntity wrkpl, Slvs_hParam d
|
||||||
|
)
|
||||||
|
Slvs_Entity Slvs_MakeLineSegment(
|
||||||
|
Slvs_hEntity h, Slvs_hGroup group,
|
||||||
|
Slvs_hEntity wrkpl,
|
||||||
|
Slvs_hEntity ptA, Slvs_hEntity ptB
|
||||||
|
)
|
||||||
|
Slvs_Entity Slvs_MakeCubic(
|
||||||
|
Slvs_hEntity h, Slvs_hGroup group,
|
||||||
|
Slvs_hEntity wrkpl,
|
||||||
|
Slvs_hEntity pt0, Slvs_hEntity pt1,
|
||||||
|
Slvs_hEntity pt2, Slvs_hEntity pt3
|
||||||
|
)
|
||||||
|
Slvs_Entity Slvs_MakeArcOfCircle(
|
||||||
|
Slvs_hEntity h, Slvs_hGroup group,
|
||||||
|
Slvs_hEntity wrkpl,
|
||||||
|
Slvs_hEntity normal,
|
||||||
|
Slvs_hEntity center,
|
||||||
|
Slvs_hEntity start, Slvs_hEntity end
|
||||||
|
)
|
||||||
|
Slvs_Entity Slvs_MakeCircle(
|
||||||
|
Slvs_hEntity h, Slvs_hGroup group,
|
||||||
|
Slvs_hEntity wrkpl,
|
||||||
|
Slvs_hEntity center,
|
||||||
|
Slvs_hEntity normal, Slvs_hEntity radius
|
||||||
|
)
|
||||||
|
Slvs_Entity Slvs_MakeWorkplane(
|
||||||
|
Slvs_hEntity h, Slvs_hGroup group,
|
||||||
|
Slvs_hEntity origin, Slvs_hEntity normal
|
||||||
|
)
|
||||||
|
Slvs_Constraint Slvs_MakeConstraint(
|
||||||
|
Slvs_hConstraint h,
|
||||||
|
Slvs_hGroup group,
|
||||||
|
int type,
|
||||||
|
Slvs_hEntity wrkpl,
|
||||||
|
double valA,
|
||||||
|
Slvs_hEntity ptA,
|
||||||
|
Slvs_hEntity ptB,
|
||||||
|
Slvs_hEntity entityA,
|
||||||
|
Slvs_hEntity entityB
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
cpdef enum Constraint:
|
||||||
|
# Expose macro of constraint types
|
||||||
|
POINTS_COINCIDENT = 100000
|
||||||
|
PT_PT_DISTANCE
|
||||||
|
PT_PLANE_DISTANCE
|
||||||
|
PT_LINE_DISTANCE
|
||||||
|
PT_FACE_DISTANCE
|
||||||
|
PT_IN_PLANE
|
||||||
|
PT_ON_LINE
|
||||||
|
PT_ON_FACE
|
||||||
|
EQUAL_LENGTH_LINES
|
||||||
|
LENGTH_RATIO
|
||||||
|
EQ_LEN_PT_LINE_D
|
||||||
|
EQ_PT_LN_DISTANCES
|
||||||
|
EQUAL_ANGLE
|
||||||
|
EQUAL_LINE_ARC_LEN
|
||||||
|
SYMMETRIC
|
||||||
|
SYMMETRIC_HORIZ
|
||||||
|
SYMMETRIC_VERT
|
||||||
|
SYMMETRIC_LINE
|
||||||
|
AT_MIDPOINT
|
||||||
|
HORIZONTAL
|
||||||
|
VERTICAL
|
||||||
|
DIAMETER
|
||||||
|
PT_ON_CIRCLE
|
||||||
|
SAME_ORIENTATION
|
||||||
|
ANGLE
|
||||||
|
PARALLEL
|
||||||
|
PERPENDICULAR
|
||||||
|
ARC_LINE_TANGENT
|
||||||
|
CUBIC_LINE_TANGENT
|
||||||
|
EQUAL_RADIUS
|
||||||
|
PROJ_PT_DISTANCE
|
||||||
|
WHERE_DRAGGED
|
||||||
|
CURVE_CURVE_TANGENT
|
||||||
|
LENGTH_DIFFERENCE
|
||||||
|
|
||||||
|
|
||||||
|
cpdef enum ResultFlag:
|
||||||
|
# Expose macro of result flags
|
||||||
|
OKAY
|
||||||
|
INCONSISTENT
|
||||||
|
DIDNT_CONVERGE
|
||||||
|
TOO_MANY_UNKNOWNS
|
||||||
|
|
||||||
|
|
||||||
|
cpdef tuple quaternion_u(double qw, double qx, double qy, double qz)
|
||||||
|
cpdef tuple quaternion_v(double qw, double qx, double qy, double qz)
|
||||||
|
cpdef tuple quaternion_n(double qw, double qx, double qy, double qz)
|
||||||
|
cpdef tuple make_quaternion(double ux, double uy, double uz, double vx, double vy, double vz)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Params:
|
||||||
|
|
||||||
|
cdef vector[Slvs_hParam] param_list
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
cdef Params create(Slvs_hParam *p, size_t count)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Entity:
|
||||||
|
|
||||||
|
cdef int t
|
||||||
|
cdef Slvs_hEntity h, wp
|
||||||
|
cdef Slvs_hGroup g
|
||||||
|
cdef readonly Params params
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
cdef Entity create(Slvs_Entity *e, size_t p_size)
|
||||||
|
|
||||||
|
cpdef bint is_3d(self)
|
||||||
|
cpdef bint is_none(self)
|
||||||
|
cpdef bint is_point_2d(self)
|
||||||
|
cpdef bint is_point_3d(self)
|
||||||
|
cpdef bint is_point(self)
|
||||||
|
cpdef bint is_normal_2d(self)
|
||||||
|
cpdef bint is_normal_3d(self)
|
||||||
|
cpdef bint is_normal(self)
|
||||||
|
cpdef bint is_distance(self)
|
||||||
|
cpdef bint is_work_plane(self)
|
||||||
|
cpdef bint is_line_2d(self)
|
||||||
|
cpdef bint is_line_3d(self)
|
||||||
|
cpdef bint is_line(self)
|
||||||
|
cpdef bint is_cubic(self)
|
||||||
|
cpdef bint is_circle(self)
|
||||||
|
cpdef bint is_arc(self)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class SolverSystem:
|
||||||
|
|
||||||
|
cdef Slvs_hGroup g
|
||||||
|
cdef Slvs_System sys
|
||||||
|
cdef cmap[Slvs_hParam, Slvs_Param] param_list
|
||||||
|
cdef vector[Slvs_Entity] entity_list
|
||||||
|
cdef vector[Slvs_Constraint] cons_list
|
||||||
|
cdef vector[Slvs_hConstraint] failed_list
|
||||||
|
|
||||||
|
cdef void copy_to_sys(self) nogil
|
||||||
|
cdef void copy_from_sys(self) nogil
|
||||||
|
cpdef void clear(self)
|
||||||
|
cdef void failed_collecting(self) nogil
|
||||||
|
cdef void free(self)
|
||||||
|
cpdef void set_group(self, size_t g)
|
||||||
|
cpdef int group(self)
|
||||||
|
cpdef tuple params(self, Params p)
|
||||||
|
cpdef int dof(self)
|
||||||
|
cpdef object constraints(self)
|
||||||
|
cpdef list faileds(self)
|
||||||
|
cpdef int solve(self)
|
||||||
|
cpdef Entity create_2d_base(self)
|
||||||
|
cdef Slvs_hParam new_param(self, double val) nogil
|
||||||
|
cdef Slvs_hEntity eh(self) nogil
|
||||||
|
|
||||||
|
cpdef Entity add_point_2d(self, double u, double v, Entity wp)
|
||||||
|
cpdef Entity add_point_3d(self, double x, double y, double z)
|
||||||
|
cpdef Entity add_normal_2d(self, Entity wp)
|
||||||
|
cpdef Entity add_normal_3d(self, double qw, double qx, double qy, double qz)
|
||||||
|
cpdef Entity add_distance(self, double d, Entity wp)
|
||||||
|
cpdef Entity add_line_2d(self, Entity p1, Entity p2, Entity wp)
|
||||||
|
cpdef Entity add_line_3d(self, Entity p1, Entity p2)
|
||||||
|
cpdef Entity add_cubic(self, Entity p1, Entity p2, Entity p3, Entity p4, Entity wp)
|
||||||
|
cpdef Entity add_arc(self, Entity nm, Entity ct, Entity start, Entity end, Entity wp)
|
||||||
|
cpdef Entity add_circle(self, Entity nm, Entity ct, Entity radius, Entity wp)
|
||||||
|
cpdef Entity add_work_plane(self, Entity origin, Entity nm)
|
||||||
|
cpdef void add_constraint(
|
||||||
|
self,
|
||||||
|
Constraint c_type,
|
||||||
|
Entity wp,
|
||||||
|
double v,
|
||||||
|
Entity p1,
|
||||||
|
Entity p2,
|
||||||
|
Entity e1,
|
||||||
|
Entity e2,
|
||||||
|
Entity e3 = *,
|
||||||
|
Entity e4 = *,
|
||||||
|
int other = *,
|
||||||
|
int other2 = *
|
||||||
|
)
|
||||||
|
|
||||||
|
cpdef void coincident(self, Entity e1, Entity e2, Entity wp = *)
|
||||||
|
cpdef void distance(self, Entity e1, Entity e2, double value, Entity wp = *)
|
||||||
|
cpdef void equal(self, Entity e1, Entity e2, Entity wp = *)
|
||||||
|
cpdef void equal_included_angle(self, Entity e1, Entity e2, Entity e3, Entity e4, Entity wp)
|
||||||
|
cpdef void equal_point_to_line(self, Entity e1, Entity e2, Entity e3, Entity e4, Entity wp)
|
||||||
|
cpdef void ratio(self, Entity e1, Entity e2, double value, Entity wp)
|
||||||
|
cpdef void symmetric(self, Entity e1, Entity e2, Entity e3 = *, Entity wp = *)
|
||||||
|
cpdef void symmetric_h(self, Entity e1, Entity e2, Entity wp)
|
||||||
|
cpdef void symmetric_v(self, Entity e1, Entity e2, Entity wp)
|
||||||
|
cpdef void midpoint(self, Entity e1, Entity e2, Entity wp = *)
|
||||||
|
cpdef void horizontal(self, Entity e1, Entity wp)
|
||||||
|
cpdef void vertical(self, Entity e1, Entity wp)
|
||||||
|
cpdef void diameter(self, Entity e1, double value, Entity wp)
|
||||||
|
cpdef void same_orientation(self, Entity e1, Entity e2)
|
||||||
|
cpdef void angle(self, Entity e1, Entity e2, double value, Entity wp, bint inverse = *)
|
||||||
|
cpdef void perpendicular(self, Entity e1, Entity e2, Entity wp, bint inverse = *)
|
||||||
|
cpdef void parallel(self, Entity e1, Entity e2, Entity wp = *)
|
||||||
|
cpdef void tangent(self, Entity e1, Entity e2, Entity wp = *)
|
||||||
|
cpdef void distance_proj(self, Entity e1, Entity e2, double value)
|
||||||
|
cpdef void dragged(self, Entity e1, Entity wp = *)
|
|
@ -0,0 +1,347 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Tuple, List, Counter
|
||||||
|
from enum import IntEnum, auto
|
||||||
|
|
||||||
|
|
||||||
|
def quaternion_u(
|
||||||
|
qw: float,
|
||||||
|
qx: float,
|
||||||
|
qy: float,
|
||||||
|
qz: float
|
||||||
|
) -> Tuple[float, float, float]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def quaternion_v(
|
||||||
|
qw: float,
|
||||||
|
qx: float,
|
||||||
|
qy: float,
|
||||||
|
qz: float
|
||||||
|
) -> Tuple[float, float, float]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def quaternion_n(
|
||||||
|
qw: float,
|
||||||
|
qx: float,
|
||||||
|
qy: float,
|
||||||
|
qz: float
|
||||||
|
) -> Tuple[float, float, float]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def make_quaternion(
|
||||||
|
ux: float,
|
||||||
|
uy: float,
|
||||||
|
uz: float,
|
||||||
|
vx: float,
|
||||||
|
vy: float,
|
||||||
|
vz: float
|
||||||
|
) -> Tuple[float, float, float, float]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Constraint(IntEnum):
|
||||||
|
# Expose macro of constraint types
|
||||||
|
POINTS_COINCIDENT = 100000
|
||||||
|
PT_PT_DISTANCE = auto()
|
||||||
|
PT_PLANE_DISTANCE = auto()
|
||||||
|
PT_LINE_DISTANCE = auto()
|
||||||
|
PT_FACE_DISTANCE = auto()
|
||||||
|
PT_IN_PLANE = auto()
|
||||||
|
PT_ON_LINE = auto()
|
||||||
|
PT_ON_FACE = auto()
|
||||||
|
EQUAL_LENGTH_LINES = auto()
|
||||||
|
LENGTH_RATIO = auto()
|
||||||
|
EQ_LEN_PT_LINE_D = auto()
|
||||||
|
EQ_PT_LN_DISTANCES = auto()
|
||||||
|
EQUAL_ANGLE = auto()
|
||||||
|
EQUAL_LINE_ARC_LEN = auto()
|
||||||
|
SYMMETRIC = auto()
|
||||||
|
SYMMETRIC_HORIZ = auto()
|
||||||
|
SYMMETRIC_VERT = auto()
|
||||||
|
SYMMETRIC_LINE = auto()
|
||||||
|
AT_MIDPOINT = auto()
|
||||||
|
HORIZONTAL = auto()
|
||||||
|
VERTICAL = auto()
|
||||||
|
DIAMETER = auto()
|
||||||
|
PT_ON_CIRCLE = auto()
|
||||||
|
SAME_ORIENTATION = auto()
|
||||||
|
ANGLE = auto()
|
||||||
|
PARALLEL = auto()
|
||||||
|
PERPENDICULAR = auto()
|
||||||
|
ARC_LINE_TANGENT = auto()
|
||||||
|
CUBIC_LINE_TANGENT = auto()
|
||||||
|
EQUAL_RADIUS = auto()
|
||||||
|
PROJ_PT_DISTANCE = auto()
|
||||||
|
WHERE_DRAGGED = auto()
|
||||||
|
CURVE_CURVE_TANGENT = auto()
|
||||||
|
LENGTH_DIFFERENCE = auto()
|
||||||
|
|
||||||
|
|
||||||
|
class ResultFlag(IntEnum):
|
||||||
|
# Expose macro of result flags
|
||||||
|
OKAY = 0
|
||||||
|
INCONSISTENT = auto()
|
||||||
|
DIDNT_CONVERGE = auto()
|
||||||
|
TOO_MANY_UNKNOWNS = auto()
|
||||||
|
|
||||||
|
|
||||||
|
class Params:
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Entity:
|
||||||
|
|
||||||
|
FREE_IN_3D: Entity
|
||||||
|
NONE: Entity
|
||||||
|
|
||||||
|
params: Params
|
||||||
|
|
||||||
|
def is_3d(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_none(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_point_2d(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_point_3d(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_point(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_normal_2d(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_normal_3d(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_normal(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_distance(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_work_plane(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_line_2d(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_line_3d(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_line(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_cubic(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_circle(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_arc(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class SolverSystem:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
def set_group(self, g: int) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
def group(self) -> int:
|
||||||
|
...
|
||||||
|
|
||||||
|
def params(self, p: Params) -> Tuple[float, ...]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def dof(self) -> int:
|
||||||
|
...
|
||||||
|
|
||||||
|
def constraints(self) -> Counter[str]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def faileds(self) -> List[int]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def solve(self) -> ResultFlag:
|
||||||
|
...
|
||||||
|
|
||||||
|
def create_2d_base(self) -> Entity:
|
||||||
|
...
|
||||||
|
|
||||||
|
def add_point_2d(self, u: float, v: float, wp: Entity) -> Entity:
|
||||||
|
...
|
||||||
|
|
||||||
|
def add_point_3d(self, x: float, y: float, z: float) -> Entity:
|
||||||
|
...
|
||||||
|
|
||||||
|
def add_normal_2d(self, wp: Entity) -> Entity:
|
||||||
|
...
|
||||||
|
|
||||||
|
def add_normal_3d(self, qw: float, qx: float, qy: float, qz: float) -> Entity:
|
||||||
|
...
|
||||||
|
|
||||||
|
def add_distance(self, d: float, wp: Entity) -> Entity:
|
||||||
|
...
|
||||||
|
|
||||||
|
def add_line_2d(self, p1: Entity, p2: Entity, wp: Entity) -> Entity:
|
||||||
|
...
|
||||||
|
|
||||||
|
def add_line_3d(self, p1: Entity, p2: Entity) -> Entity:
|
||||||
|
...
|
||||||
|
|
||||||
|
def add_cubic(self, p1: Entity, p2: Entity, p3: Entity, p4: Entity, wp: Entity) -> Entity:
|
||||||
|
...
|
||||||
|
|
||||||
|
def add_arc(self, nm: Entity, ct: Entity, start: Entity, end: Entity, wp: Entity) -> Entity:
|
||||||
|
...
|
||||||
|
|
||||||
|
def add_circle(self, nm: Entity, ct: Entity, radius: Entity, wp: Entity) -> Entity:
|
||||||
|
...
|
||||||
|
|
||||||
|
def add_work_plane(self, origin: Entity, nm: Entity) -> Entity:
|
||||||
|
...
|
||||||
|
|
||||||
|
def add_constraint(
|
||||||
|
self,
|
||||||
|
c_type: Constraint,
|
||||||
|
wp: Entity,
|
||||||
|
v: float,
|
||||||
|
p1: Entity,
|
||||||
|
p2: Entity,
|
||||||
|
e1: Entity,
|
||||||
|
e2: Entity,
|
||||||
|
e3: Entity = Entity.NONE,
|
||||||
|
e4: Entity = Entity.NONE,
|
||||||
|
other: int = 0,
|
||||||
|
other2: int = 0
|
||||||
|
) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
def coincident(self, e1: Entity, e2: Entity, wp: Entity = Entity.FREE_IN_3D) -> None:
|
||||||
|
"""Coincident two entities."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def distance(
|
||||||
|
self,
|
||||||
|
e1: Entity,
|
||||||
|
e2: Entity,
|
||||||
|
value: float,
|
||||||
|
wp: Entity = Entity.FREE_IN_3D
|
||||||
|
) -> None:
|
||||||
|
"""Distance constraint between two entities."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def equal(self, e1: Entity, e2: Entity, wp: Entity = Entity.FREE_IN_3D) -> None:
|
||||||
|
"""Equal constraint between two entities."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def equal_included_angle(
|
||||||
|
self,
|
||||||
|
e1: Entity,
|
||||||
|
e2: Entity,
|
||||||
|
e3: Entity,
|
||||||
|
e4: Entity,
|
||||||
|
wp: Entity
|
||||||
|
) -> None:
|
||||||
|
"""Constraint that point 1 and line 1, point 2 and line 2
|
||||||
|
must have same distance.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def equal_point_to_line(
|
||||||
|
self,
|
||||||
|
e1: Entity,
|
||||||
|
e2: Entity,
|
||||||
|
e3: Entity,
|
||||||
|
e4: Entity,
|
||||||
|
wp: Entity
|
||||||
|
) -> None:
|
||||||
|
"""Constraint that line 1 and line 2, line 3 and line 4
|
||||||
|
must have same included angle.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def ratio(self, e1: Entity, e2: Entity, value: float, wp: Entity) -> None:
|
||||||
|
"""The ratio constraint between two lines."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def symmetric(
|
||||||
|
self,
|
||||||
|
e1: Entity,
|
||||||
|
e2: Entity,
|
||||||
|
e3: Entity = Entity.NONE,
|
||||||
|
wp: Entity = Entity.FREE_IN_3D
|
||||||
|
) -> None:
|
||||||
|
"""Symmetric constraint between two points."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def symmetric_h(self, e1: Entity, e2: Entity, wp: Entity) -> None:
|
||||||
|
"""Symmetric constraint between two points with horizontal line."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def symmetric_v(self, e1: Entity, e2: Entity, wp: Entity) -> None:
|
||||||
|
"""Symmetric constraint between two points with vertical line."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def midpoint(
|
||||||
|
self,
|
||||||
|
e1: Entity,
|
||||||
|
e2: Entity,
|
||||||
|
wp: Entity = Entity.FREE_IN_3D
|
||||||
|
) -> None:
|
||||||
|
"""Midpoint constraint between a point and a line."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def horizontal(self, e1: Entity, wp: Entity) -> None:
|
||||||
|
"""Horizontal constraint of a 2d point."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def vertical(self, e1: Entity, wp: Entity) -> None:
|
||||||
|
"""Vertical constraint of a 2d point."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def diameter(self, e1: Entity, value: float, wp: Entity) -> None:
|
||||||
|
"""Diameter constraint of a circular entities."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def same_orientation(self, e1: Entity, e2: Entity) -> None:
|
||||||
|
"""Equal orientation constraint between two 3d normals."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def angle(self, e1: Entity, e2: Entity, value: float, wp: Entity, inverse: bool = False) -> None:
|
||||||
|
"""Degrees angle constraint between two 2d lines."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def perpendicular(self, e1: Entity, e2: Entity, wp: Entity, inverse: bool = False) -> None:
|
||||||
|
"""Perpendicular constraint between two 2d lines."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def parallel(self, e1: Entity, e2: Entity, wp: Entity = Entity.FREE_IN_3D) -> None:
|
||||||
|
"""Parallel constraint between two lines."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def tangent(self, e1: Entity, e2: Entity, wp: Entity = Entity.FREE_IN_3D) -> None:
|
||||||
|
"""Parallel constraint between two entities."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def distance_proj(self, e1: Entity, e2: Entity, value: float) -> None:
|
||||||
|
"""Projected distance constraint between two 3d points."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def dragged(self, e1: Entity, wp: Entity = Entity.FREE_IN_3D) -> None:
|
||||||
|
"""Dragged constraint of a point."""
|
||||||
|
...
|
|
@ -0,0 +1,796 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# cython: language_level=3, embedsignature=True, cdivision=True
|
||||||
|
|
||||||
|
"""Wrapper source code of Solvespace.
|
||||||
|
|
||||||
|
author: Yuan Chang
|
||||||
|
copyright: Copyright (C) 2016-2019
|
||||||
|
license: GPLv3+
|
||||||
|
email: pyslvs@gmail.com
|
||||||
|
"""
|
||||||
|
|
||||||
|
cimport cython
|
||||||
|
from cpython.mem cimport PyMem_Malloc, PyMem_Free
|
||||||
|
from cpython.object cimport Py_EQ, Py_NE
|
||||||
|
from libcpp.pair cimport pair
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
|
|
||||||
|
cpdef tuple quaternion_u(double qw, double qx, double qy, double qz):
|
||||||
|
cdef double x, y, z
|
||||||
|
Slvs_QuaternionV(qw, qx, qy, qz, &x, &y, &z)
|
||||||
|
return x, y, z
|
||||||
|
|
||||||
|
|
||||||
|
cpdef tuple quaternion_v(double qw, double qx, double qy, double qz):
|
||||||
|
cdef double x, y, z
|
||||||
|
Slvs_QuaternionV(qw, qx, qy, qz, &x, &y, &z)
|
||||||
|
return x, y, z
|
||||||
|
|
||||||
|
|
||||||
|
cpdef tuple quaternion_n(double qw, double qx, double qy, double qz):
|
||||||
|
cdef double x, y, z
|
||||||
|
Slvs_QuaternionN(qw, qx, qy, qz, &x, &y, &z)
|
||||||
|
return x, y, z
|
||||||
|
|
||||||
|
|
||||||
|
cpdef tuple make_quaternion(double ux, double uy, double uz, double vx, double vy, double vz):
|
||||||
|
cdef double qw, qx, qy, qz
|
||||||
|
Slvs_MakeQuaternion(ux, uy, uz, vx, vy, vz, &qw, &qx, &qy, &qz)
|
||||||
|
return qw, qx, qy, qz
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Params:
|
||||||
|
|
||||||
|
"""Python object to handle multiple parameter handles."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
cdef Params create(Slvs_hParam *p, size_t count):
|
||||||
|
"""Constructor."""
|
||||||
|
cdef Params params = Params.__new__(Params)
|
||||||
|
cdef size_t i
|
||||||
|
for i in range(count):
|
||||||
|
params.param_list.push_back(p[i])
|
||||||
|
return params
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
cdef str m = f"{self.__class__.__name__}(["
|
||||||
|
cdef size_t i
|
||||||
|
cdef size_t s = self.param_list.size()
|
||||||
|
for i in range(s):
|
||||||
|
m += str(<int>self.param_list[i])
|
||||||
|
if i != s - 1:
|
||||||
|
m += ", "
|
||||||
|
m += "])"
|
||||||
|
return m
|
||||||
|
|
||||||
|
# A virtual work plane that present 3D entity or constraint.
|
||||||
|
cdef Entity _E_FREE_IN_3D = Entity.__new__(Entity)
|
||||||
|
_E_FREE_IN_3D.t = SLVS_E_WORKPLANE
|
||||||
|
_E_FREE_IN_3D.h = SLVS_FREE_IN_3D
|
||||||
|
_E_FREE_IN_3D.g = 0
|
||||||
|
_E_FREE_IN_3D.params = Params.create(NULL, 0)
|
||||||
|
|
||||||
|
# A "None" entity used to fill in constraint option.
|
||||||
|
cdef Entity _E_NONE = Entity.__new__(Entity)
|
||||||
|
_E_NONE.t = 0
|
||||||
|
_E_NONE.h = 0
|
||||||
|
_E_NONE.g = 0
|
||||||
|
_E_NONE.params = Params.create(NULL, 0)
|
||||||
|
|
||||||
|
# Entity names
|
||||||
|
cdef dict _NAME_OF_ENTITIES = {
|
||||||
|
SLVS_E_POINT_IN_3D: "point 3d",
|
||||||
|
SLVS_E_POINT_IN_2D: "point 2d",
|
||||||
|
SLVS_E_NORMAL_IN_2D: "normal 2d",
|
||||||
|
SLVS_E_NORMAL_IN_3D: "normal 3d",
|
||||||
|
SLVS_E_DISTANCE: "distance",
|
||||||
|
SLVS_E_WORKPLANE: "work plane",
|
||||||
|
SLVS_E_LINE_SEGMENT: "line segment",
|
||||||
|
SLVS_E_CUBIC: "cubic",
|
||||||
|
SLVS_E_CIRCLE: "circle",
|
||||||
|
SLVS_E_ARC_OF_CIRCLE: "arc",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Constraint names
|
||||||
|
cdef dict _NAME_OF_CONSTRAINTS = {
|
||||||
|
POINTS_COINCIDENT: "points coincident",
|
||||||
|
PT_PT_DISTANCE: "point point distance",
|
||||||
|
PT_PLANE_DISTANCE: "point plane distance",
|
||||||
|
PT_LINE_DISTANCE: "point line distance",
|
||||||
|
PT_FACE_DISTANCE: "point face distance",
|
||||||
|
PT_IN_PLANE: "point in plane",
|
||||||
|
PT_ON_LINE: "point on line",
|
||||||
|
PT_ON_FACE: "point on face",
|
||||||
|
EQUAL_LENGTH_LINES: "equal length lines",
|
||||||
|
LENGTH_RATIO: "length ratio",
|
||||||
|
EQ_LEN_PT_LINE_D: "equal length point line distance",
|
||||||
|
EQ_PT_LN_DISTANCES: "equal point line distance",
|
||||||
|
EQUAL_ANGLE: "equal angle",
|
||||||
|
EQUAL_LINE_ARC_LEN: "equal line arc length",
|
||||||
|
SYMMETRIC: "symmetric",
|
||||||
|
SYMMETRIC_HORIZ: "symmetric horizontal",
|
||||||
|
SYMMETRIC_VERT: "symmetric vertical",
|
||||||
|
SYMMETRIC_LINE: "symmetric line",
|
||||||
|
AT_MIDPOINT: "at midpoint",
|
||||||
|
HORIZONTAL: "horizontal",
|
||||||
|
VERTICAL: "vertical",
|
||||||
|
DIAMETER: "diameter",
|
||||||
|
PT_ON_CIRCLE: "point on circle",
|
||||||
|
SAME_ORIENTATION: "same orientation",
|
||||||
|
ANGLE: "angle",
|
||||||
|
PARALLEL: "parallel",
|
||||||
|
PERPENDICULAR: "perpendicular",
|
||||||
|
ARC_LINE_TANGENT: "arc line tangent",
|
||||||
|
CUBIC_LINE_TANGENT: "cubic line tangent",
|
||||||
|
EQUAL_RADIUS: "equal radius",
|
||||||
|
PROJ_PT_DISTANCE: "project point distance",
|
||||||
|
WHERE_DRAGGED: "where dragged",
|
||||||
|
CURVE_CURVE_TANGENT: "curve curve tangent",
|
||||||
|
LENGTH_DIFFERENCE: "length difference",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Entity:
|
||||||
|
|
||||||
|
"""Python object to handle a pointer of 'Slvs_hEntity'."""
|
||||||
|
|
||||||
|
FREE_IN_3D = _E_FREE_IN_3D
|
||||||
|
NONE = _E_NONE
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
cdef Entity create(Slvs_Entity *e, size_t p_size):
|
||||||
|
"""Constructor."""
|
||||||
|
cdef Entity entity = Entity.__new__(Entity)
|
||||||
|
with nogil:
|
||||||
|
entity.t = e.type
|
||||||
|
entity.h = e.h
|
||||||
|
entity.wp = e.wrkpl
|
||||||
|
entity.g = e.group
|
||||||
|
entity.params = Params.create(e.param, p_size)
|
||||||
|
return entity
|
||||||
|
|
||||||
|
def __richcmp__(self, other: Entity, op: cython.int) -> bint:
|
||||||
|
"""Compare the entities."""
|
||||||
|
if op == Py_EQ:
|
||||||
|
return (
|
||||||
|
self.t == other.t and
|
||||||
|
self.h == other.h and
|
||||||
|
self.wp == other.wp and
|
||||||
|
self.g == other.g and
|
||||||
|
self.params == other.params
|
||||||
|
)
|
||||||
|
elif op == Py_NE:
|
||||||
|
return (
|
||||||
|
self.t != other.t or
|
||||||
|
self.h != other.h or
|
||||||
|
self.wp != other.wp or
|
||||||
|
self.g != other.g or
|
||||||
|
self.params != other.params
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise TypeError(
|
||||||
|
f"'{op}' not support between instances of "
|
||||||
|
f"{type(self)} and {type(other)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
cpdef bint is_3d(self):
|
||||||
|
return self.wp == SLVS_FREE_IN_3D
|
||||||
|
|
||||||
|
cpdef bint is_none(self):
|
||||||
|
return self.h == 0
|
||||||
|
|
||||||
|
cpdef bint is_point_2d(self):
|
||||||
|
return self.t == SLVS_E_POINT_IN_2D
|
||||||
|
|
||||||
|
cpdef bint is_point_3d(self):
|
||||||
|
return self.t == SLVS_E_POINT_IN_3D
|
||||||
|
|
||||||
|
cpdef bint is_point(self):
|
||||||
|
return self.is_point_2d() or self.is_point_3d()
|
||||||
|
|
||||||
|
cpdef bint is_normal_2d(self):
|
||||||
|
return self.t == SLVS_E_NORMAL_IN_2D
|
||||||
|
|
||||||
|
cpdef bint is_normal_3d(self):
|
||||||
|
return self.t == SLVS_E_NORMAL_IN_3D
|
||||||
|
|
||||||
|
cpdef bint is_normal(self):
|
||||||
|
return self.is_normal_2d() or self.is_normal_3d()
|
||||||
|
|
||||||
|
cpdef bint is_distance(self):
|
||||||
|
return self.t == SLVS_E_DISTANCE
|
||||||
|
|
||||||
|
cpdef bint is_work_plane(self):
|
||||||
|
return self.t == SLVS_E_WORKPLANE
|
||||||
|
|
||||||
|
cpdef bint is_line_2d(self):
|
||||||
|
return self.is_line() and not self.is_3d()
|
||||||
|
|
||||||
|
cpdef bint is_line_3d(self):
|
||||||
|
return self.is_line() and self.is_3d()
|
||||||
|
|
||||||
|
cpdef bint is_line(self):
|
||||||
|
return self.t == SLVS_E_LINE_SEGMENT
|
||||||
|
|
||||||
|
cpdef bint is_cubic(self):
|
||||||
|
return self.t == SLVS_E_CUBIC
|
||||||
|
|
||||||
|
cpdef bint is_circle(self):
|
||||||
|
return self.t == SLVS_E_CIRCLE
|
||||||
|
|
||||||
|
cpdef bint is_arc(self):
|
||||||
|
return self.t == SLVS_E_ARC_OF_CIRCLE
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
cdef int h = <int>self.h
|
||||||
|
cdef int g = <int>self.g
|
||||||
|
cdef str t = _NAME_OF_ENTITIES[<int>self.t]
|
||||||
|
return (
|
||||||
|
f"{self.__class__.__name__}"
|
||||||
|
f"(handle={h}, group={g}, type=<{t}>, is_3d={self.is_3d()}, params={self.params})"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class SolverSystem:
|
||||||
|
|
||||||
|
"""Python object of 'Slvs_System'."""
|
||||||
|
|
||||||
|
def __cinit__(self):
|
||||||
|
self.g = 0
|
||||||
|
self.sys.params = self.sys.entities = self.sys.constraints = 0
|
||||||
|
|
||||||
|
def __dealloc__(self):
|
||||||
|
self.free()
|
||||||
|
|
||||||
|
cdef inline void copy_to_sys(self) nogil:
|
||||||
|
"""Copy data from stack into system."""
|
||||||
|
cdef int i = 0
|
||||||
|
cdef pair[Slvs_hParam, Slvs_Param] param
|
||||||
|
for param in self.param_list:
|
||||||
|
self.sys.param[i] = param.second
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
cdef Slvs_Entity entity
|
||||||
|
for entity in self.entity_list:
|
||||||
|
self.sys.entity[i] = entity
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
cdef Slvs_Constraint con
|
||||||
|
for con in self.cons_list:
|
||||||
|
self.sys.constraint[i] = con
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
cdef inline void copy_from_sys(self) nogil:
|
||||||
|
"""Copy data from system into stack."""
|
||||||
|
self.param_list.clear()
|
||||||
|
self.entity_list.clear()
|
||||||
|
self.cons_list.clear()
|
||||||
|
cdef int i
|
||||||
|
for i in range(self.sys.params):
|
||||||
|
self.param_list[self.sys.param[i].h] = self.sys.param[i]
|
||||||
|
for i in range(self.sys.entities):
|
||||||
|
self.entity_list.push_back(self.sys.entity[i])
|
||||||
|
for i in range(self.sys.constraints):
|
||||||
|
self.cons_list.push_back(self.sys.constraint[i])
|
||||||
|
|
||||||
|
cpdef void clear(self):
|
||||||
|
self.g = 0
|
||||||
|
self.param_list.clear()
|
||||||
|
self.entity_list.clear()
|
||||||
|
self.cons_list.clear()
|
||||||
|
self.failed_list.clear()
|
||||||
|
self.free()
|
||||||
|
|
||||||
|
cdef inline void failed_collecting(self) nogil:
|
||||||
|
"""Collecting the failed constraints."""
|
||||||
|
cdef int i
|
||||||
|
for i in range(self.sys.faileds):
|
||||||
|
self.failed_list.push_back(self.sys.failed[i])
|
||||||
|
|
||||||
|
cdef inline void free(self):
|
||||||
|
PyMem_Free(self.sys.param)
|
||||||
|
PyMem_Free(self.sys.entity)
|
||||||
|
PyMem_Free(self.sys.constraint)
|
||||||
|
PyMem_Free(self.sys.failed)
|
||||||
|
self.sys.param = NULL
|
||||||
|
self.sys.entity = NULL
|
||||||
|
self.sys.constraint = NULL
|
||||||
|
self.sys.failed = NULL
|
||||||
|
self.sys.params = self.sys.entities = self.sys.constraints = 0
|
||||||
|
|
||||||
|
cpdef void set_group(self, size_t g):
|
||||||
|
"""Set the current group by integer."""
|
||||||
|
self.g = <Slvs_hGroup>g
|
||||||
|
|
||||||
|
cpdef int group(self):
|
||||||
|
"""Return the current group by integer."""
|
||||||
|
return <int>self.g
|
||||||
|
|
||||||
|
cpdef tuple params(self, Params p):
|
||||||
|
"""Get the parameters by Params object."""
|
||||||
|
cdef list param_list = []
|
||||||
|
cdef Slvs_hParam h
|
||||||
|
for h in p.param_list:
|
||||||
|
param_list.append(self.param_list[h].val)
|
||||||
|
return tuple(param_list)
|
||||||
|
|
||||||
|
cpdef int dof(self):
|
||||||
|
"""Return the DOF of system."""
|
||||||
|
return self.sys.dof
|
||||||
|
|
||||||
|
cpdef object constraints(self):
|
||||||
|
"""Return the list of all constraints."""
|
||||||
|
cons_list = []
|
||||||
|
cdef Slvs_Constraint con
|
||||||
|
for con in self.cons_list:
|
||||||
|
cons_list.append(_NAME_OF_CONSTRAINTS[con.type])
|
||||||
|
return Counter(cons_list)
|
||||||
|
|
||||||
|
cpdef list faileds(self):
|
||||||
|
"""Return the count of failed constraint."""
|
||||||
|
failed_list = []
|
||||||
|
cdef Slvs_hConstraint error
|
||||||
|
for error in self.failed_list:
|
||||||
|
failed_list.append(<int>error)
|
||||||
|
return failed_list
|
||||||
|
|
||||||
|
cpdef int solve(self):
|
||||||
|
"""Solve the system."""
|
||||||
|
# Parameters
|
||||||
|
self.sys.param = <Slvs_Param *>PyMem_Malloc(self.param_list.size() * sizeof(Slvs_Param))
|
||||||
|
# Entities
|
||||||
|
self.sys.entity = <Slvs_Entity *>PyMem_Malloc(self.entity_list.size() * sizeof(Slvs_Entity))
|
||||||
|
# Constraints
|
||||||
|
cdef size_t cons_size = self.cons_list.size()
|
||||||
|
self.sys.constraint = <Slvs_Constraint *>PyMem_Malloc(cons_size * sizeof(Slvs_Constraint))
|
||||||
|
self.sys.failed = <Slvs_hConstraint *>PyMem_Malloc(cons_size * sizeof(Slvs_hConstraint))
|
||||||
|
self.sys.faileds = cons_size
|
||||||
|
|
||||||
|
# Copy to system
|
||||||
|
self.copy_to_sys()
|
||||||
|
# Solve
|
||||||
|
Slvs_Solve(&self.sys, self.g)
|
||||||
|
# Failed constraints and free memory.
|
||||||
|
self.copy_from_sys()
|
||||||
|
self.failed_collecting()
|
||||||
|
self.free()
|
||||||
|
return self.sys.result
|
||||||
|
|
||||||
|
cpdef Entity create_2d_base(self):
|
||||||
|
"""Create a basic 2D system and return the work plane."""
|
||||||
|
cdef double qw, qx, qy, qz
|
||||||
|
qw, qx, qy, qz = make_quaternion(1, 0, 0, 0, 1, 0)
|
||||||
|
cdef Entity nm = self.add_normal_3d(qw, qx, qy, qz)
|
||||||
|
return self.add_work_plane(self.add_point_3d(0, 0, 0), nm)
|
||||||
|
|
||||||
|
cdef inline Slvs_hParam new_param(self, double val) nogil:
|
||||||
|
"""Add a parameter."""
|
||||||
|
self.sys.params += 1
|
||||||
|
cdef Slvs_hParam h = <Slvs_hParam>self.sys.params
|
||||||
|
self.param_list[h] = Slvs_MakeParam(h, self.g, val)
|
||||||
|
return h
|
||||||
|
|
||||||
|
cdef inline Slvs_hEntity eh(self) nogil:
|
||||||
|
"""Return new entity handle."""
|
||||||
|
self.sys.entities += 1
|
||||||
|
return <Slvs_hEntity>self.sys.entities
|
||||||
|
|
||||||
|
cpdef Entity add_point_2d(self, double u, double v, Entity wp):
|
||||||
|
"""Add 2D point."""
|
||||||
|
if wp is None or not wp.is_work_plane():
|
||||||
|
raise TypeError(f"{wp} is not a work plane")
|
||||||
|
|
||||||
|
cdef Slvs_hParam u_p = self.new_param(u)
|
||||||
|
cdef Slvs_hParam v_p = self.new_param(v)
|
||||||
|
cdef Slvs_Entity e = Slvs_MakePoint2d(self.eh(), self.g, wp.h, u_p, v_p)
|
||||||
|
self.entity_list.push_back(e)
|
||||||
|
|
||||||
|
return Entity.create(&e, 2)
|
||||||
|
|
||||||
|
cpdef Entity add_point_3d(self, double x, double y, double z):
|
||||||
|
"""Add 3D point."""
|
||||||
|
cdef Slvs_hParam x_p = self.new_param(x)
|
||||||
|
cdef Slvs_hParam y_p = self.new_param(y)
|
||||||
|
cdef Slvs_hParam z_p = self.new_param(z)
|
||||||
|
cdef Slvs_Entity e = Slvs_MakePoint3d(self.eh(), self.g, x_p, y_p, z_p)
|
||||||
|
self.entity_list.push_back(e)
|
||||||
|
|
||||||
|
return Entity.create(&e, 3)
|
||||||
|
|
||||||
|
cpdef Entity add_normal_2d(self, Entity wp):
|
||||||
|
"""Add a 2D normal."""
|
||||||
|
if wp is None or not wp.is_work_plane():
|
||||||
|
raise TypeError(f"{wp} is not a work plane")
|
||||||
|
|
||||||
|
cdef Slvs_Entity e = Slvs_MakeNormal2d(self.eh(), self.g, wp.h)
|
||||||
|
self.entity_list.push_back(e)
|
||||||
|
|
||||||
|
return Entity.create(&e, 0)
|
||||||
|
|
||||||
|
cpdef Entity add_normal_3d(self, double qw, double qx, double qy, double qz):
|
||||||
|
"""Add a 3D normal."""
|
||||||
|
cdef Slvs_hParam w_p = self.new_param(qw)
|
||||||
|
cdef Slvs_hParam x_p = self.new_param(qx)
|
||||||
|
cdef Slvs_hParam y_p = self.new_param(qy)
|
||||||
|
cdef Slvs_hParam z_p = self.new_param(qz)
|
||||||
|
self.entity_list.push_back(Slvs_MakeNormal3d(self.eh(), self.g, w_p, x_p, y_p, z_p))
|
||||||
|
return Entity.create(&self.entity_list.back(), 4)
|
||||||
|
|
||||||
|
cpdef Entity add_distance(self, double d, Entity wp):
|
||||||
|
"""Add a 2D distance."""
|
||||||
|
if wp is None or not wp.is_work_plane():
|
||||||
|
raise TypeError(f"{wp} is not a work plane")
|
||||||
|
|
||||||
|
cdef Slvs_hParam d_p = self.new_param(d)
|
||||||
|
self.entity_list.push_back(Slvs_MakeDistance(self.eh(), self.g, wp.h, d_p))
|
||||||
|
return Entity.create(&self.entity_list.back(), 1)
|
||||||
|
|
||||||
|
cpdef Entity add_line_2d(self, Entity p1, Entity p2, Entity wp):
|
||||||
|
"""Add a 2D line."""
|
||||||
|
if wp is None or not wp.is_work_plane():
|
||||||
|
raise TypeError(f"{wp} is not a work plane")
|
||||||
|
if p1 is None or not p1.is_point_2d():
|
||||||
|
raise TypeError(f"{p1} is not a 2d point")
|
||||||
|
if p2 is None or not p2.is_point_2d():
|
||||||
|
raise TypeError(f"{p2} is not a 2d point")
|
||||||
|
|
||||||
|
self.entity_list.push_back(Slvs_MakeLineSegment(self.eh(), self.g, wp.h, p1.h, p2.h))
|
||||||
|
return Entity.create(&self.entity_list.back(), 0)
|
||||||
|
|
||||||
|
cpdef Entity add_line_3d(self, Entity p1, Entity p2):
|
||||||
|
"""Add a 3D line."""
|
||||||
|
if p1 is None or not p1.is_point_3d():
|
||||||
|
raise TypeError(f"{p1} is not a 3d point")
|
||||||
|
if p2 is None or not p2.is_point_3d():
|
||||||
|
raise TypeError(f"{p2} is not a 3d point")
|
||||||
|
|
||||||
|
self.entity_list.push_back(Slvs_MakeLineSegment(self.eh(), self.g, SLVS_FREE_IN_3D, p1.h, p2.h))
|
||||||
|
return Entity.create(&self.entity_list.back(), 0)
|
||||||
|
|
||||||
|
cpdef Entity add_cubic(self, Entity p1, Entity p2, Entity p3, Entity p4, Entity wp):
|
||||||
|
"""Add a 2D cubic."""
|
||||||
|
if wp is None or not wp.is_work_plane():
|
||||||
|
raise TypeError(f"{wp} is not a work plane")
|
||||||
|
if p1 is None or not p1.is_point_2d():
|
||||||
|
raise TypeError(f"{p1} is not a 2d point")
|
||||||
|
if p2 is None or not p2.is_point_2d():
|
||||||
|
raise TypeError(f"{p2} is not a 2d point")
|
||||||
|
if p3 is None or not p3.is_point_2d():
|
||||||
|
raise TypeError(f"{p3} is not a 2d point")
|
||||||
|
if p4 is None or not p4.is_point_2d():
|
||||||
|
raise TypeError(f"{p4} is not a 2d point")
|
||||||
|
|
||||||
|
self.entity_list.push_back(Slvs_MakeCubic(self.eh(), self.g, wp.h, p1.h, p2.h, p3.h, p4.h))
|
||||||
|
return Entity.create(&self.entity_list.back(), 0)
|
||||||
|
|
||||||
|
cpdef Entity add_arc(self, Entity nm, Entity ct, Entity start, Entity end, Entity wp):
|
||||||
|
"""Add an 2D arc."""
|
||||||
|
if wp is None or not wp.is_work_plane():
|
||||||
|
raise TypeError(f"{wp} is not a work plane")
|
||||||
|
if nm is None or not nm.is_normal_3d():
|
||||||
|
raise TypeError(f"{nm} is not a 3d normal")
|
||||||
|
if ct is None or not ct.is_point_2d():
|
||||||
|
raise TypeError(f"{ct} is not a 2d point")
|
||||||
|
if start is None or not start.is_point_2d():
|
||||||
|
raise TypeError(f"{start} is not a 2d point")
|
||||||
|
if end is None or not end.is_point_2d():
|
||||||
|
raise TypeError(f"{end} is not a 2d point")
|
||||||
|
|
||||||
|
self.entity_list.push_back(Slvs_MakeArcOfCircle(self.eh(), self.g, wp.h, nm.h, ct.h, start.h, end.h))
|
||||||
|
return Entity.create(&self.entity_list.back(), 0)
|
||||||
|
|
||||||
|
cpdef Entity add_circle(self, Entity nm, Entity ct, Entity radius, Entity wp):
|
||||||
|
"""Add a 2D circle."""
|
||||||
|
if wp is None or not wp.is_work_plane():
|
||||||
|
raise TypeError(f"{wp} is not a work plane")
|
||||||
|
if nm is None or not nm.is_normal_3d():
|
||||||
|
raise TypeError(f"{nm} is not a 3d normal")
|
||||||
|
if ct is None or not ct.is_point_2d():
|
||||||
|
raise TypeError(f"{ct} is not a 2d point")
|
||||||
|
if radius is None or not radius.is_distance():
|
||||||
|
raise TypeError(f"{radius} is not a distance")
|
||||||
|
|
||||||
|
self.entity_list.push_back(Slvs_MakeCircle(self.eh(), self.g, wp.h, ct.h, nm.h, radius.h))
|
||||||
|
return Entity.create(&self.entity_list.back(), 0)
|
||||||
|
|
||||||
|
cpdef Entity add_work_plane(self, Entity origin, Entity nm):
|
||||||
|
"""Add a 3D work plane."""
|
||||||
|
if origin is None or origin.t != SLVS_E_POINT_IN_3D:
|
||||||
|
raise TypeError(f"{origin} is not a 3d point")
|
||||||
|
if nm is None or nm.t != SLVS_E_NORMAL_IN_3D:
|
||||||
|
raise TypeError(f"{nm} is not a 3d normal")
|
||||||
|
|
||||||
|
self.entity_list.push_back(Slvs_MakeWorkplane(self.eh(), self.g, origin.h, nm.h))
|
||||||
|
return Entity.create(&self.entity_list.back(), 0)
|
||||||
|
|
||||||
|
cpdef void add_constraint(
|
||||||
|
self,
|
||||||
|
Constraint c_type,
|
||||||
|
Entity wp,
|
||||||
|
double v,
|
||||||
|
Entity p1,
|
||||||
|
Entity p2,
|
||||||
|
Entity e1,
|
||||||
|
Entity e2,
|
||||||
|
Entity e3 = _E_NONE,
|
||||||
|
Entity e4 = _E_NONE,
|
||||||
|
int other = 0,
|
||||||
|
int other2 = 0
|
||||||
|
):
|
||||||
|
"""Add customized constraint."""
|
||||||
|
if wp is None or not wp.is_work_plane():
|
||||||
|
raise TypeError(f"{wp} is not a work plane")
|
||||||
|
|
||||||
|
cdef Entity e
|
||||||
|
for e in (p1, p2):
|
||||||
|
if e is None or not (e.is_none() or e.is_point()):
|
||||||
|
raise TypeError(f"{e} is not a point")
|
||||||
|
for e in (e1, e2, e3, e4):
|
||||||
|
if e is None:
|
||||||
|
raise TypeError(f"{e} is not a entity")
|
||||||
|
|
||||||
|
self.sys.constraints += 1
|
||||||
|
cdef Slvs_Constraint c
|
||||||
|
c.h = <Slvs_hConstraint>self.sys.constraints
|
||||||
|
c.group = self.g
|
||||||
|
c.type = c_type
|
||||||
|
c.wrkpl = wp.h
|
||||||
|
c.valA = v
|
||||||
|
c.ptA = p1.h
|
||||||
|
c.ptB = p2.h
|
||||||
|
c.entityA = e1.h
|
||||||
|
c.entityB = e2.h
|
||||||
|
c.entityC = e3.h
|
||||||
|
c.entityD = e4.h
|
||||||
|
c.other = other
|
||||||
|
c.other2 = other2
|
||||||
|
self.cons_list.push_back(c)
|
||||||
|
|
||||||
|
#####
|
||||||
|
# Constraint methods.
|
||||||
|
#####
|
||||||
|
|
||||||
|
cpdef void coincident(self, Entity e1, Entity e2, Entity wp = _E_FREE_IN_3D):
|
||||||
|
"""Coincident two entities."""
|
||||||
|
cdef Constraint t
|
||||||
|
if e1.is_point() and e2.is_point():
|
||||||
|
self.add_constraint(POINTS_COINCIDENT, wp, 0., e1, e2, _E_NONE, _E_NONE)
|
||||||
|
elif e1.is_point() and e2.is_work_plane() and wp is _E_FREE_IN_3D:
|
||||||
|
self.add_constraint(PT_IN_PLANE, e2, 0., e1, _E_NONE, e2, _E_NONE)
|
||||||
|
elif e1.is_point() and (e2.is_line() or e2.is_circle()):
|
||||||
|
if e2.is_line():
|
||||||
|
t = PT_ON_LINE
|
||||||
|
else:
|
||||||
|
t = PT_ON_CIRCLE
|
||||||
|
self.add_constraint(t, wp, 0., e1, _E_NONE, e2, _E_NONE)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {wp}")
|
||||||
|
|
||||||
|
cpdef void distance(
|
||||||
|
self,
|
||||||
|
Entity e1,
|
||||||
|
Entity e2,
|
||||||
|
double value,
|
||||||
|
Entity wp = _E_FREE_IN_3D
|
||||||
|
):
|
||||||
|
"""Distance constraint between two entities."""
|
||||||
|
if value == 0.:
|
||||||
|
self.coincident(e1, e2, wp)
|
||||||
|
return
|
||||||
|
|
||||||
|
if e1.is_point() and e2.is_point():
|
||||||
|
self.add_constraint(PT_PT_DISTANCE, wp, value, e1, e2, _E_NONE, _E_NONE)
|
||||||
|
elif e1.is_point() and e2.is_work_plane() and wp is _E_FREE_IN_3D:
|
||||||
|
self.add_constraint(PT_PLANE_DISTANCE, e2, value, e1, _E_NONE, e2, _E_NONE)
|
||||||
|
elif e1.is_point() and e2.is_line():
|
||||||
|
self.add_constraint(PT_LINE_DISTANCE, wp, value, e1, _E_NONE, e2, _E_NONE)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {wp}")
|
||||||
|
|
||||||
|
cpdef void equal(self, Entity e1, Entity e2, Entity wp = _E_FREE_IN_3D):
|
||||||
|
"""Equal constraint between two entities."""
|
||||||
|
if e1.is_line() and e2.is_line():
|
||||||
|
self.add_constraint(EQUAL_LENGTH_LINES, wp, 0., _E_NONE, _E_NONE, e1, e2)
|
||||||
|
elif e1.is_line() and (e2.is_arc() or e2.is_circle()):
|
||||||
|
self.add_constraint(EQUAL_LINE_ARC_LEN, wp, 0., _E_NONE, _E_NONE, e1, e2)
|
||||||
|
elif (e1.is_arc() or e1.is_circle()) and (e2.is_arc() or e2.is_circle()):
|
||||||
|
self.add_constraint(EQUAL_RADIUS, wp, 0., _E_NONE, _E_NONE, e1, e2)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {wp}")
|
||||||
|
|
||||||
|
cpdef void equal_included_angle(
|
||||||
|
self,
|
||||||
|
Entity e1,
|
||||||
|
Entity e2,
|
||||||
|
Entity e3,
|
||||||
|
Entity e4,
|
||||||
|
Entity wp
|
||||||
|
):
|
||||||
|
"""Constraint that line 1 and line 2, line 3 and line 4
|
||||||
|
must have same included angle.
|
||||||
|
"""
|
||||||
|
if wp is _E_FREE_IN_3D:
|
||||||
|
raise ValueError("this is a 2d constraint")
|
||||||
|
|
||||||
|
if e1.is_line_2d() and e2.is_line_2d() and e3.is_line_2d() and e4.is_line_2d():
|
||||||
|
self.add_constraint(EQUAL_ANGLE, wp, 0., _E_NONE, _E_NONE, e1, e2, e3, e4)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {e3}, {e4}, {wp}")
|
||||||
|
|
||||||
|
cpdef void equal_point_to_line(
|
||||||
|
self,
|
||||||
|
Entity e1,
|
||||||
|
Entity e2,
|
||||||
|
Entity e3,
|
||||||
|
Entity e4,
|
||||||
|
Entity wp
|
||||||
|
):
|
||||||
|
"""Constraint that point 1 and line 1, point 2 and line 2
|
||||||
|
must have same distance.
|
||||||
|
"""
|
||||||
|
if wp is _E_FREE_IN_3D:
|
||||||
|
raise ValueError("this is a 2d constraint")
|
||||||
|
|
||||||
|
if e1.is_point_2d() and e2.is_line_2d() and e3.is_point_2d() and e4.is_line_2d():
|
||||||
|
self.add_constraint(EQ_PT_LN_DISTANCES, wp, 0., e1, e3, e2, e4)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {e3}, {e4}, {wp}")
|
||||||
|
|
||||||
|
cpdef void ratio(self, Entity e1, Entity e2, double value, Entity wp):
|
||||||
|
"""The ratio constraint between two lines."""
|
||||||
|
if wp is _E_FREE_IN_3D:
|
||||||
|
raise ValueError("this is a 2d constraint")
|
||||||
|
|
||||||
|
if e1.is_line_2d() and e2.is_line_2d():
|
||||||
|
self.add_constraint(EQ_PT_LN_DISTANCES, wp, value, _E_NONE, _E_NONE, e1, e2)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {wp}")
|
||||||
|
|
||||||
|
cpdef void symmetric(
|
||||||
|
self,
|
||||||
|
Entity e1,
|
||||||
|
Entity e2,
|
||||||
|
Entity e3 = _E_NONE,
|
||||||
|
Entity wp = _E_FREE_IN_3D
|
||||||
|
):
|
||||||
|
"""Symmetric constraint between two points."""
|
||||||
|
if e1.is_point_3d() and e2.is_point_3d() and e3.is_work_plane() and wp is _E_FREE_IN_3D:
|
||||||
|
self.add_constraint(SYMMETRIC, wp, 0., e1, e2, e3, _E_NONE)
|
||||||
|
elif e1.is_point_2d() and e2.is_point_2d() and e3.is_work_plane() and wp is _E_FREE_IN_3D:
|
||||||
|
self.add_constraint(SYMMETRIC, e3, 0., e1, e2, e3, _E_NONE)
|
||||||
|
elif e1.is_point_2d() and e2.is_point_2d() and e3.is_line_2d():
|
||||||
|
if wp is _E_FREE_IN_3D:
|
||||||
|
raise ValueError("this is a 2d constraint")
|
||||||
|
self.add_constraint(SYMMETRIC_LINE, wp, 0., e1, e2, e3, _E_NONE)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {e3}, {wp}")
|
||||||
|
|
||||||
|
cpdef void symmetric_h(self, Entity e1, Entity e2, Entity wp):
|
||||||
|
"""Symmetric constraint between two points with horizontal line."""
|
||||||
|
if wp is _E_FREE_IN_3D:
|
||||||
|
raise ValueError("this is a 2d constraint")
|
||||||
|
|
||||||
|
if e1.is_point_2d() and e2.is_point_2d():
|
||||||
|
self.add_constraint(SYMMETRIC_HORIZ, wp, 0., e1, e2, _E_NONE, _E_NONE)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {wp}")
|
||||||
|
|
||||||
|
cpdef void symmetric_v(self, Entity e1, Entity e2, Entity wp):
|
||||||
|
"""Symmetric constraint between two points with vertical line."""
|
||||||
|
if wp is _E_FREE_IN_3D:
|
||||||
|
raise ValueError("this is a 2d constraint")
|
||||||
|
|
||||||
|
if e1.is_point_2d() and e2.is_point_2d():
|
||||||
|
self.add_constraint(SYMMETRIC_VERT, wp, 0., e1, e2, _E_NONE, _E_NONE)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {wp}")
|
||||||
|
|
||||||
|
cpdef void midpoint(
|
||||||
|
self,
|
||||||
|
Entity e1,
|
||||||
|
Entity e2,
|
||||||
|
Entity wp = _E_FREE_IN_3D
|
||||||
|
):
|
||||||
|
"""Midpoint constraint between a point and a line."""
|
||||||
|
if e1.is_point() and e2.is_line():
|
||||||
|
self.add_constraint(AT_MIDPOINT, wp, 0., e1, _E_NONE, e2, _E_NONE)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {wp}")
|
||||||
|
|
||||||
|
cpdef void horizontal(self, Entity e1, Entity wp):
|
||||||
|
"""Horizontal constraint of a 2d point."""
|
||||||
|
if wp is _E_FREE_IN_3D:
|
||||||
|
raise ValueError("this is a 2d constraint")
|
||||||
|
|
||||||
|
if e1.is_line_2d():
|
||||||
|
self.add_constraint(HORIZONTAL, wp, 0., _E_NONE, _E_NONE, e1, _E_NONE)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {wp}")
|
||||||
|
|
||||||
|
cpdef void vertical(self, Entity e1, Entity wp):
|
||||||
|
"""Vertical constraint of a 2d point."""
|
||||||
|
if wp is _E_FREE_IN_3D:
|
||||||
|
raise ValueError("this is a 2d constraint")
|
||||||
|
|
||||||
|
if e1.is_line_2d():
|
||||||
|
self.add_constraint(VERTICAL, wp, 0., _E_NONE, _E_NONE, e1, _E_NONE)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {wp}")
|
||||||
|
|
||||||
|
cpdef void diameter(self, Entity e1, double value, Entity wp):
|
||||||
|
"""Diameter constraint of a circular entities."""
|
||||||
|
if wp is _E_FREE_IN_3D:
|
||||||
|
raise ValueError("this is a 2d constraint")
|
||||||
|
|
||||||
|
if e1.is_arc() or e1.is_circle():
|
||||||
|
self.add_constraint(DIAMETER, wp, value, _E_NONE, _E_NONE, e1, _E_NONE)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {wp}")
|
||||||
|
|
||||||
|
cpdef void same_orientation(self, Entity e1, Entity e2):
|
||||||
|
"""Equal orientation constraint between two 3d normals."""
|
||||||
|
if e1.is_normal_3d() and e2.is_normal_3d():
|
||||||
|
self.add_constraint(SAME_ORIENTATION, _E_FREE_IN_3D, 0., _E_NONE, _E_NONE, e1, e2)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}")
|
||||||
|
|
||||||
|
cpdef void angle(self, Entity e1, Entity e2, double value, Entity wp, bint inverse = False):
|
||||||
|
"""Degrees angle constraint between two 2d lines."""
|
||||||
|
if wp is _E_FREE_IN_3D:
|
||||||
|
raise ValueError("this is a 2d constraint")
|
||||||
|
|
||||||
|
if e1.is_line_2d() and e2.is_line_2d():
|
||||||
|
self.add_constraint(ANGLE, wp, value, _E_NONE, _E_NONE,
|
||||||
|
e1, e2, _E_NONE, _E_NONE, inverse)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {wp}")
|
||||||
|
|
||||||
|
cpdef void perpendicular(self, Entity e1, Entity e2, Entity wp, bint inverse = False):
|
||||||
|
"""Perpendicular constraint between two 2d lines."""
|
||||||
|
if wp is _E_FREE_IN_3D:
|
||||||
|
raise ValueError("this is a 2d constraint")
|
||||||
|
|
||||||
|
if e1.is_line_2d() and e2.is_line_2d():
|
||||||
|
self.add_constraint(PERPENDICULAR, wp, 0., _E_NONE, _E_NONE,
|
||||||
|
e1, e2, _E_NONE, _E_NONE, inverse)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {wp}")
|
||||||
|
|
||||||
|
cpdef void parallel(self, Entity e1, Entity e2, Entity wp = _E_FREE_IN_3D):
|
||||||
|
"""Parallel constraint between two lines."""
|
||||||
|
if e1.is_line() and e2.is_line():
|
||||||
|
self.add_constraint(PARALLEL, wp, 0., _E_NONE, _E_NONE, e1, e2)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {wp}")
|
||||||
|
|
||||||
|
cpdef void tangent(self, Entity e1, Entity e2, Entity wp = _E_FREE_IN_3D):
|
||||||
|
"""Parallel constraint between two entities."""
|
||||||
|
if e1.is_arc() and e2.is_line_2d():
|
||||||
|
if wp is _E_FREE_IN_3D:
|
||||||
|
raise ValueError("this is a 2d constraint")
|
||||||
|
self.add_constraint(ARC_LINE_TANGENT, wp, 0., _E_NONE, _E_NONE, e1, e2)
|
||||||
|
elif e1.is_cubic() and e2.is_line_3d() and wp is _E_FREE_IN_3D:
|
||||||
|
self.add_constraint(CUBIC_LINE_TANGENT, wp, 0., _E_NONE, _E_NONE, e1, e2)
|
||||||
|
elif (e1.is_arc() or e1.is_cubic()) and (e2.is_arc() or e2.is_cubic()):
|
||||||
|
if (e1.is_arc() or e2.is_arc()) and wp is _E_FREE_IN_3D:
|
||||||
|
raise ValueError("this is a 2d constraint")
|
||||||
|
self.add_constraint(CURVE_CURVE_TANGENT, wp, 0., _E_NONE, _E_NONE, e1, e2)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}, {wp}")
|
||||||
|
|
||||||
|
cpdef void distance_proj(self, Entity e1, Entity e2, double value):
|
||||||
|
"""Projected distance constraint between two 3d points."""
|
||||||
|
if e1.is_point_3d() and e2.is_point_3d():
|
||||||
|
self.add_constraint(CURVE_CURVE_TANGENT, _E_FREE_IN_3D, value, e1, e2, _E_NONE, _E_NONE)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {e2}")
|
||||||
|
|
||||||
|
cpdef void dragged(self, Entity e1, Entity wp = _E_FREE_IN_3D):
|
||||||
|
"""Dragged constraint of a point."""
|
||||||
|
if e1.is_point():
|
||||||
|
self.add_constraint(WHERE_DRAGGED, wp, 0., e1, _E_NONE, _E_NONE, _E_NONE)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"unsupported entities: {e1}, {wp}")
|
|
@ -0,0 +1 @@
|
||||||
|
cython
|
|
@ -0,0 +1,116 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""Compile the Cython libraries of Python-Solvespace."""
|
||||||
|
|
||||||
|
__author__ = "Yuan Chang"
|
||||||
|
__copyright__ = "Copyright (C) 2016-2019"
|
||||||
|
__license__ = "GPLv3+"
|
||||||
|
__email__ = "pyslvs@gmail.com"
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import codecs
|
||||||
|
from setuptools import setup, Extension, find_packages
|
||||||
|
from platform import system
|
||||||
|
from distutils import sysconfig
|
||||||
|
|
||||||
|
here = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
include_path = '../include/'
|
||||||
|
src_path = '../src/'
|
||||||
|
platform_path = src_path + 'platform/'
|
||||||
|
ver = sysconfig.get_config_var('VERSION')
|
||||||
|
lib = sysconfig.get_config_var('BINDIR')
|
||||||
|
|
||||||
|
|
||||||
|
def read(*parts):
|
||||||
|
with codecs.open(os.path.join(here, *parts), 'r') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def get_version(*file_paths):
|
||||||
|
doc = read(*file_paths)
|
||||||
|
m1 = re.search(r"^set\(solvespace_VERSION_MAJOR (\d)\)", doc, re.M)
|
||||||
|
m2 = re.search(r"^set\(solvespace_VERSION_MINOR (\d)\)", doc, re.M)
|
||||||
|
if m1 and m2:
|
||||||
|
return f"{m1.group(1)}.{m2.group(1)}"
|
||||||
|
raise RuntimeError("Unable to find version string.")
|
||||||
|
|
||||||
|
|
||||||
|
macros = [
|
||||||
|
('_hypot', 'hypot'),
|
||||||
|
('M_PI', 'PI'), # C++ 11
|
||||||
|
('ISOLATION_AWARE_ENABLED', None),
|
||||||
|
('LIBRARY', None),
|
||||||
|
('EXPORT_DLL', None),
|
||||||
|
('_CRT_SECURE_NO_WARNINGS', None),
|
||||||
|
]
|
||||||
|
|
||||||
|
compile_args = [
|
||||||
|
'-O3',
|
||||||
|
'-Wno-cpp',
|
||||||
|
'-g',
|
||||||
|
'-Wno-write-strings',
|
||||||
|
'-fpermissive',
|
||||||
|
'-fPIC',
|
||||||
|
'-std=c++11',
|
||||||
|
]
|
||||||
|
|
||||||
|
sources = [
|
||||||
|
'python_solvespace/' + 'slvs.pyx',
|
||||||
|
src_path + 'util.cpp',
|
||||||
|
src_path + 'entity.cpp',
|
||||||
|
src_path + 'expr.cpp',
|
||||||
|
src_path + 'constrainteq.cpp',
|
||||||
|
src_path + 'constraint.cpp',
|
||||||
|
src_path + 'system.cpp',
|
||||||
|
src_path + 'lib.cpp',
|
||||||
|
]
|
||||||
|
|
||||||
|
if system() == 'Windows':
|
||||||
|
# Avoid compile error with CYTHON_USE_PYLONG_INTERNALS.
|
||||||
|
# https://github.com/cython/cython/issues/2670#issuecomment-432212671
|
||||||
|
macros.append(('MS_WIN64', None))
|
||||||
|
# Disable format warning
|
||||||
|
compile_args.append('-Wno-format')
|
||||||
|
|
||||||
|
# Solvespace arguments
|
||||||
|
macros.append(('WIN32', None))
|
||||||
|
|
||||||
|
# Platform sources
|
||||||
|
sources.append(platform_path + 'w32util.cpp')
|
||||||
|
sources.append(platform_path + 'platform.cpp')
|
||||||
|
else:
|
||||||
|
sources.append(platform_path + 'unixutil.cpp')
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="python_solvespace",
|
||||||
|
version=get_version('..', 'CMakeLists.txt'),
|
||||||
|
author=__author__,
|
||||||
|
author_email=__email__,
|
||||||
|
description="Python library of Solvespace",
|
||||||
|
long_description=read("README.md"),
|
||||||
|
url="https://github.com/solvespace/solvespace",
|
||||||
|
packages=find_packages(exclude=('tests',)),
|
||||||
|
ext_modules=[Extension(
|
||||||
|
"slvs",
|
||||||
|
sources,
|
||||||
|
language="c++",
|
||||||
|
include_dirs=[include_path, src_path, platform_path],
|
||||||
|
define_macros=macros,
|
||||||
|
extra_compile_args=compile_args
|
||||||
|
)],
|
||||||
|
python_requires=">=3.6",
|
||||||
|
setup_requires=[
|
||||||
|
'setuptools',
|
||||||
|
'wheel',
|
||||||
|
'cython',
|
||||||
|
],
|
||||||
|
classifiers=[
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"Programming Language :: Cython",
|
||||||
|
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
||||||
|
"Programming Language :: Python :: 3.6",
|
||||||
|
"Programming Language :: Python :: 3.7",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
]
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1,248 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""This module will test the functions of Python-Solvespace."""
|
||||||
|
|
||||||
|
__author__ = "Yuan Chang"
|
||||||
|
__copyright__ = "Copyright (C) 2016-2019"
|
||||||
|
__license__ = "GPLv3+"
|
||||||
|
__email__ = "pyslvs@gmail.com"
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from unittest import TestCase
|
||||||
|
from math import radians
|
||||||
|
from slvs import ResultFlag, SolverSystem, make_quaternion
|
||||||
|
|
||||||
|
|
||||||
|
class CoreTest(TestCase):
|
||||||
|
|
||||||
|
def test_crank_rocker(self):
|
||||||
|
"""Crank rocker example."""
|
||||||
|
sys = SolverSystem()
|
||||||
|
wp = sys.create_2d_base()
|
||||||
|
p0 = sys.add_point_2d(0, 0, wp)
|
||||||
|
sys.dragged(p0, wp)
|
||||||
|
p1 = sys.add_point_2d(90, 0, wp)
|
||||||
|
sys.dragged(p1, wp)
|
||||||
|
line0 = sys.add_line_2d(p0, p1, wp)
|
||||||
|
p2 = sys.add_point_2d(20, 20, wp)
|
||||||
|
p3 = sys.add_point_2d(0, 10, wp)
|
||||||
|
p4 = sys.add_point_2d(30, 20, wp)
|
||||||
|
sys.distance(p2, p3, 40, wp)
|
||||||
|
sys.distance(p2, p4, 40, wp)
|
||||||
|
sys.distance(p3, p4, 70, wp)
|
||||||
|
|
||||||
|
sys.distance(p0, p3, 35, wp)
|
||||||
|
sys.distance(p1, p4, 70, wp)
|
||||||
|
line1 = sys.add_line_2d(p0, p3, wp)
|
||||||
|
sys.angle(line0, line1, 45, wp)
|
||||||
|
|
||||||
|
result_flag = sys.solve()
|
||||||
|
self.assertEqual(result_flag, ResultFlag.OKAY)
|
||||||
|
x, y = sys.params(p2.params)
|
||||||
|
self.assertAlmostEqual(39.54852, x, 4)
|
||||||
|
self.assertAlmostEqual(61.91009, y, 4)
|
||||||
|
|
||||||
|
def test_involute(self):
|
||||||
|
"""Involute example."""
|
||||||
|
r = 10
|
||||||
|
angle = 45
|
||||||
|
|
||||||
|
sys = SolverSystem()
|
||||||
|
wp = sys.create_2d_base()
|
||||||
|
p0 = sys.add_point_2d(0, 0, wp)
|
||||||
|
sys.dragged(p0, wp)
|
||||||
|
|
||||||
|
p1 = sys.add_point_2d(0, 10, wp)
|
||||||
|
sys.distance(p0, p1, r, wp)
|
||||||
|
line0 = sys.add_line_2d(p0, p1, wp)
|
||||||
|
|
||||||
|
p2 = sys.add_point_2d(10, 10, wp)
|
||||||
|
line1 = sys.add_line_2d(p1, p2, wp)
|
||||||
|
sys.distance(p1, p2, r * radians(angle), wp)
|
||||||
|
sys.perpendicular(line0, line1, wp, False)
|
||||||
|
|
||||||
|
p3 = sys.add_point_2d(10, 0, wp)
|
||||||
|
sys.dragged(p3, wp)
|
||||||
|
line_base = sys.add_line_2d(p0, p3, wp)
|
||||||
|
sys.angle(line0, line_base, angle, wp)
|
||||||
|
|
||||||
|
result_flag = sys.solve()
|
||||||
|
self.assertEqual(result_flag, ResultFlag.OKAY)
|
||||||
|
x, y = sys.params(p2.params)
|
||||||
|
self.assertAlmostEqual(12.62467, x, 4)
|
||||||
|
self.assertAlmostEqual(1.51746, y, 4)
|
||||||
|
|
||||||
|
def test_jansen_linkage(self):
|
||||||
|
"""Jansen's linkage example."""
|
||||||
|
sys = SolverSystem()
|
||||||
|
wp = sys.create_2d_base()
|
||||||
|
p0 = sys.add_point_2d(0, 0, wp)
|
||||||
|
sys.dragged(p0, wp)
|
||||||
|
|
||||||
|
p1 = sys.add_point_2d(0, 20, wp)
|
||||||
|
sys.distance(p0, p1, 15, wp)
|
||||||
|
line0 = sys.add_line_2d(p0, p1, wp)
|
||||||
|
|
||||||
|
p2 = sys.add_point_2d(-38, -7.8, wp)
|
||||||
|
sys.dragged(p2, wp)
|
||||||
|
p3 = sys.add_point_2d(-50, 30, wp)
|
||||||
|
p4 = sys.add_point_2d(-70, -15, wp)
|
||||||
|
sys.distance(p2, p3, 41.5, wp)
|
||||||
|
sys.distance(p3, p4, 55.8, wp)
|
||||||
|
sys.distance(p2, p4, 40.1, wp)
|
||||||
|
|
||||||
|
p5 = sys.add_point_2d(-50, -50, wp)
|
||||||
|
p6 = sys.add_point_2d(-10, -90, wp)
|
||||||
|
p7 = sys.add_point_2d(-20, -40, wp)
|
||||||
|
sys.distance(p5, p6, 65.7, wp)
|
||||||
|
sys.distance(p6, p7, 49.0, wp)
|
||||||
|
sys.distance(p5, p7, 36.7, wp)
|
||||||
|
|
||||||
|
sys.distance(p1, p3, 50, wp)
|
||||||
|
sys.distance(p1, p7, 61.9, wp)
|
||||||
|
|
||||||
|
p8 = sys.add_point_2d(20, 0, wp)
|
||||||
|
line_base = sys.add_line_2d(p0, p8, wp)
|
||||||
|
sys.angle(line0, line_base, 45, wp)
|
||||||
|
|
||||||
|
result_flag = sys.solve()
|
||||||
|
self.assertEqual(result_flag, ResultFlag.OKAY)
|
||||||
|
x, y = sys.params(p2.params)
|
||||||
|
self.assertAlmostEqual(-38, x, 4)
|
||||||
|
self.assertAlmostEqual(-7.8, y, 4)
|
||||||
|
|
||||||
|
def test_nut_cracker(self):
|
||||||
|
"""Nut cracker example."""
|
||||||
|
h0 = 0.5
|
||||||
|
b0 = 0.75
|
||||||
|
r0 = 0.25
|
||||||
|
n1 = 1.5
|
||||||
|
n2 = 2.3
|
||||||
|
l0 = 3.25
|
||||||
|
|
||||||
|
sys = SolverSystem()
|
||||||
|
wp = sys.create_2d_base()
|
||||||
|
p0 = sys.add_point_2d(0, 0, wp)
|
||||||
|
sys.dragged(p0, wp)
|
||||||
|
|
||||||
|
p1 = sys.add_point_2d(2, 2, wp)
|
||||||
|
p2 = sys.add_point_2d(2, 0, wp)
|
||||||
|
line0 = sys.add_line_2d(p0, p2, wp)
|
||||||
|
sys.horizontal(line0, wp)
|
||||||
|
|
||||||
|
line1 = sys.add_line_2d(p1, p2, wp)
|
||||||
|
p3 = sys.add_point_2d(b0 / 2, h0, wp)
|
||||||
|
sys.dragged(p3, wp)
|
||||||
|
sys.distance(p3, line1, r0, wp)
|
||||||
|
sys.distance(p0, p1, n1, wp)
|
||||||
|
sys.distance(p1, p2, n2, wp)
|
||||||
|
|
||||||
|
result_flag = sys.solve()
|
||||||
|
self.assertEqual(result_flag, ResultFlag.OKAY)
|
||||||
|
x, _ = sys.params(p2.params)
|
||||||
|
ans_min = x - b0 / 2
|
||||||
|
ans_max = l0 - r0 - b0 / 2
|
||||||
|
self.assertAlmostEqual(1.01576, ans_min, 4)
|
||||||
|
self.assertAlmostEqual(2.625, ans_max, 4)
|
||||||
|
|
||||||
|
def test_pydemo(self):
|
||||||
|
"""
|
||||||
|
Some sample code for slvs.dll. We draw some geometric entities, provide
|
||||||
|
initial guesses for their positions, and then constrain them. The solver
|
||||||
|
calculates their new positions, in order to satisfy the constraints.
|
||||||
|
|
||||||
|
Copyright 2008-2013 Jonathan Westhues.
|
||||||
|
Copyright 2016-2017 Yuan Chang [pyslvs@gmail.com] Python-Solvespace bundled.
|
||||||
|
|
||||||
|
An example of a constraint in 2d. In our first group, we create a workplane
|
||||||
|
along the reference frame's xy plane. In a second group, we create some
|
||||||
|
entities in that group and dimension them.
|
||||||
|
"""
|
||||||
|
sys = SolverSystem()
|
||||||
|
sys.set_group(1)
|
||||||
|
|
||||||
|
# First, we create our workplane. Its origin corresponds to the origin
|
||||||
|
# of our base frame (x y z) = (0 0 0)
|
||||||
|
p101 = sys.add_point_3d(0, 0, 0)
|
||||||
|
# and it is parallel to the xy plane, so it has basis vectors (1 0 0)
|
||||||
|
# and (0 1 0).
|
||||||
|
qw, qx, qy, qz = make_quaternion(1, 0, 0, 0, 1, 0)
|
||||||
|
n102 = sys.add_normal_3d(qw, qx, qy, qz)
|
||||||
|
wp200 = sys.add_work_plane(p101, n102)
|
||||||
|
|
||||||
|
# Now create a second group. We'll solve group 2, while leaving group 1
|
||||||
|
# constant; so the workplane that we've created will be locked down,
|
||||||
|
# and the solver can't move it.
|
||||||
|
sys.set_group(2)
|
||||||
|
p301 = sys.add_point_2d(10, 20, wp200)
|
||||||
|
p302 = sys.add_point_2d(20, 10, wp200)
|
||||||
|
|
||||||
|
# And we create a line segment with those endpoints.
|
||||||
|
l400 = sys.add_line_2d(p301, p302, wp200)
|
||||||
|
|
||||||
|
# Now three more points.
|
||||||
|
p303 = sys.add_point_2d(100, 120, wp200)
|
||||||
|
p304 = sys.add_point_2d(120, 110, wp200)
|
||||||
|
p305 = sys.add_point_2d(115, 115, wp200)
|
||||||
|
|
||||||
|
# And arc, centered at point 303, starting at point 304, ending at
|
||||||
|
# point 305.
|
||||||
|
a401 = sys.add_arc(n102, p303, p304, p305, wp200)
|
||||||
|
|
||||||
|
# Now one more point, and a distance
|
||||||
|
p306 = sys.add_point_2d(200, 200, wp200)
|
||||||
|
d307 = sys.add_distance(30, wp200)
|
||||||
|
|
||||||
|
# And a complete circle, centered at point 306 with radius equal to
|
||||||
|
# distance 307. The normal is 102, the same as our workplane.
|
||||||
|
c402 = sys.add_circle(n102, p306, d307, wp200)
|
||||||
|
|
||||||
|
# The length of our line segment is 30.0 units.
|
||||||
|
sys.distance(p301, p302, 30, wp200)
|
||||||
|
|
||||||
|
# And the distance from our line segment to the origin is 10.0 units.
|
||||||
|
sys.distance(p101, l400, 10, wp200)
|
||||||
|
|
||||||
|
# And the line segment is vertical.
|
||||||
|
sys.vertical(l400, wp200)
|
||||||
|
|
||||||
|
# And the distance from one endpoint to the origin is 15.0 units.
|
||||||
|
sys.distance(p301, p101, 15, wp200)
|
||||||
|
|
||||||
|
# The arc and the circle have equal radius.
|
||||||
|
sys.equal(a401, c402, wp200)
|
||||||
|
|
||||||
|
# The arc has radius 17.0 units.
|
||||||
|
sys.diameter(a401, 17 * 2, wp200)
|
||||||
|
|
||||||
|
# If the solver fails, then ask it to report which constraints caused
|
||||||
|
# the problem.
|
||||||
|
|
||||||
|
# And solve.
|
||||||
|
result_flag = sys.solve()
|
||||||
|
self.assertEqual(result_flag, ResultFlag.OKAY)
|
||||||
|
x, y = sys.params(p301.params)
|
||||||
|
self.assertAlmostEqual(10, x, 4)
|
||||||
|
self.assertAlmostEqual(11.18030, y, 4)
|
||||||
|
x, y = sys.params(p302.params)
|
||||||
|
self.assertAlmostEqual(10, x, 4)
|
||||||
|
self.assertAlmostEqual(-18.81966, y, 4)
|
||||||
|
x, y = sys.params(p303.params)
|
||||||
|
self.assertAlmostEqual(101.11418, x, 4)
|
||||||
|
self.assertAlmostEqual(119.04153, y, 4)
|
||||||
|
x, y = sys.params(p304.params)
|
||||||
|
self.assertAlmostEqual(116.47661, x, 4)
|
||||||
|
self.assertAlmostEqual(111.76171, y, 4)
|
||||||
|
x, y = sys.params(p305.params)
|
||||||
|
self.assertAlmostEqual(117.40922, x, 4)
|
||||||
|
self.assertAlmostEqual(114.19676, y, 4)
|
||||||
|
x, y = sys.params(p306.params)
|
||||||
|
self.assertAlmostEqual(200, x, 4)
|
||||||
|
self.assertAlmostEqual(200, y, 4)
|
||||||
|
x, = sys.params(d307.params)
|
||||||
|
self.assertAlmostEqual(17, x, 4)
|
||||||
|
self.assertEqual(6, sys.dof())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue